Merge "Property - Allow the custom property to be INTEGER type" into tizen
[platform/core/uifw/dali-core.git] / dali / public-api / signals / base-signal.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/public-api/signals/base-signal.h>
20
21 // EXTERNAL INCLUDES
22 #include <algorithm> // remove_if
23
24 // INTERNAL INCLUDES
25 #include <dali/integration-api/debug.h>
26
27 namespace
28 {
29
30 const int INVALID_CALLBACK_INDEX = -1;
31
32 // Predicate for std::remove_if algorithm
33 bool IsNullPredicate(void* ptr)
34 {
35   return ptr == NULL;
36 }
37
38 } // unnamed namespace
39
40 namespace Dali
41 {
42
43 BaseSignal::BaseSignal()
44 : mEmittingFlag( false )
45 {
46 }
47
48 BaseSignal::~BaseSignal()
49 {
50   // We can't assert in a destructor
51   if( mEmittingFlag )
52   {
53     DALI_LOG_ERROR( "Invalid destruction of Signal during Emit()\n" );
54   }
55
56   // The signal is being destroyed. We have to inform any slots
57   // that are connected, that the signal is dead.
58   const std::size_t count( mSignalConnections.Count() );
59   for( std::size_t i=0; i < count; i++ )
60   {
61     SignalConnection* connection = mSignalConnections[ i ];
62
63     // Note that values are set to NULL in DeleteConnection
64     if( connection )
65     {
66       connection->Disconnect( this );
67       delete connection;
68     }
69   }
70
71   mSignalConnections.Clear();
72 }
73
74 bool BaseSignal::Empty() const
75 {
76   return ( 0 == GetConnectionCount() );
77 }
78
79 std::size_t BaseSignal::GetConnectionCount() const
80 {
81   std::size_t count( 0 );
82
83   const std::size_t size( mSignalConnections.Count() );
84   for( std::size_t i = 0; i < size; ++i )
85   {
86     // Note that values are set to NULL in DeleteConnection
87     if ( NULL != mSignalConnections[i] )
88     {
89       ++count;
90     }
91   }
92
93   return count;
94 }
95
96 void BaseSignal::Emit()
97 {
98   // Guards against nested Emit() calls
99   EmitGuard guard( mEmittingFlag ); // Guards against nested Emit() calls
100   if( guard.ErrorOccurred() )
101   {
102     return;
103   }
104
105   // If more connections are added by callbacks, these are ignore until the next Emit()
106   // Note that mSignalConnections.Count() count cannot be reduced while iterating
107   const std::size_t initialCount( mSignalConnections.Count() );
108
109   for( std::size_t i = 0; i < initialCount; ++i )
110   {
111     CallbackBase* callback( GetCallback(i) );
112
113     // Note that connections will be set to NULL when disconnected
114     // This is preferable to reducing the connection count while iterating
115     if( callback )
116     {
117       CallbackBase::Execute( *callback );
118     }
119   }
120
121   // Cleanup NULL values from Connection container
122   CleanupConnections();
123 }
124
125 void BaseSignal::OnConnect( CallbackBase* callback )
126 {
127   DALI_ASSERT_ALWAYS( NULL != callback && "Invalid member function pointer passed to Connect()" );
128
129   int index = FindCallback( callback );
130
131   // Don't double-connect the same callback
132   if( INVALID_CALLBACK_INDEX == index )
133   {
134     // create a new signal connection object, to allow the signal to track the connection.
135     SignalConnection* connection = new SignalConnection( callback );
136
137     mSignalConnections.PushBack( connection );
138   }
139   else
140   {
141     // clean-up required
142     delete callback;
143   }
144 }
145
146 void BaseSignal::OnDisconnect( CallbackBase* callback )
147 {
148   DALI_ASSERT_ALWAYS( NULL != callback && "Invalid member function pointer passed to Disconnect()" );
149
150   int index = FindCallback( callback );
151
152   if( index > INVALID_CALLBACK_INDEX )
153   {
154     DeleteConnection( index );
155   }
156
157   // call back is a temporary created to find which slot should be disconnected.
158   delete callback;
159 }
160
161 void BaseSignal::OnConnect( ConnectionTrackerInterface* tracker, CallbackBase* callback )
162 {
163   DALI_ASSERT_ALWAYS( NULL != tracker  && "Invalid ConnectionTrackerInterface pointer passed to Connect()" );
164   DALI_ASSERT_ALWAYS( NULL != callback && "Invalid member function pointer passed to Connect()" );
165
166   int index = FindCallback( callback );
167
168   // Don't double-connect the same callback
169   if( INVALID_CALLBACK_INDEX == index )
170   {
171     // create a new signal connection object, to allow the signal to track the connection.
172     SignalConnection* connection = new SignalConnection( tracker, callback );
173
174     mSignalConnections.PushBack( connection );
175
176     // Let the connection tracker know that a connection between a signal and a slot has been made.
177     tracker->SignalConnected( this, callback );
178   }
179   else
180   {
181     // clean-up required
182     delete callback;
183   }
184 }
185
186 void BaseSignal::OnDisconnect( ConnectionTrackerInterface* tracker, CallbackBase* callback )
187 {
188   DALI_ASSERT_ALWAYS( NULL != tracker  && "Invalid ConnectionTrackerInterface pointer passed to Disconnect()" );
189   DALI_ASSERT_ALWAYS( NULL != callback && "Invalid member function pointer passed to Disconnect()" );
190
191   int index = FindCallback( callback );
192
193   if( index > INVALID_CALLBACK_INDEX )
194   {
195     // temporary pointer to disconnected callback
196     CallbackBase* disconnectedCallback = mSignalConnections[index]->GetCallback();
197
198     // close the signal side connection first.
199     DeleteConnection( index );
200
201     // close the slot side connection
202     tracker->SignalDisconnected( this, disconnectedCallback );
203   }
204
205   // call back is a temporary created to find which slot should be disconnected.
206   delete callback;
207 }
208
209 // for SlotObserver::SlotDisconnected
210 void BaseSignal::SlotDisconnected( CallbackBase* callback )
211 {
212   const std::size_t count( mSignalConnections.Count() );
213   for( std::size_t i=0; i < count; ++i )
214   {
215     const CallbackBase* connectionCallback = GetCallback( i );
216
217     // Pointer comparison i.e. SignalConnection contains pointer to same callback instance
218     if( connectionCallback &&
219         connectionCallback == callback )
220     {
221       DeleteConnection( i );
222
223       // Disconnection complete
224       return;
225     }
226   }
227
228   DALI_ASSERT_ALWAYS( false && "Callback lost in SlotDisconnected()" );
229 }
230
231 CallbackBase* BaseSignal::GetCallback( std::size_t connectionIndex ) const
232 {
233   DALI_ASSERT_ALWAYS( connectionIndex < mSignalConnections.Count() && "GetCallback called with invalid index" );
234
235   CallbackBase* callback( NULL );
236
237   SignalConnection* connection( mSignalConnections[ connectionIndex ] );
238
239   // Note that values are set to NULL in DeleteConnection
240   if( connection )
241   {
242     callback = connection->GetCallback();
243   }
244
245   return callback;
246 }
247
248 int BaseSignal::FindCallback( CallbackBase* callback )
249 {
250   int index( INVALID_CALLBACK_INDEX );
251
252   // A signal can have multiple slots connected to it.
253   // We need to search for the slot which has the same call back function (if it's static)
254   // Or the same object / member function (for non-static)
255   const std::size_t count( mSignalConnections.Count() );
256   for( std::size_t i=0; i < count; ++i )
257   {
258     const CallbackBase* connectionCallback = GetCallback( i );
259
260     // Note that values are set to NULL in DeleteConnection
261     if( connectionCallback &&
262         ( *connectionCallback == *callback ) )
263     {
264       index = i;
265       break;
266     }
267   }
268
269   return index;
270 }
271
272 void BaseSignal::DeleteConnection( std::size_t connectionIndex )
273 {
274   DALI_ASSERT_ALWAYS( connectionIndex < mSignalConnections.Count() && "DeleteConnection called with invalid index" );
275
276   // delete the object
277   SignalConnection* connection( mSignalConnections[ connectionIndex ] );
278   delete connection;
279
280   // IMPORTANT - do not remove from items from mSignalConnections, set to NULL instead.
281   // Signal Emit() methods require that connection count is not reduced while iterating
282   // i.e. DeleteConnection can be called from within callbacks, while iterating through mSignalConnections.
283   mSignalConnections[ connectionIndex ] = NULL;
284 }
285
286 void BaseSignal::CleanupConnections()
287 {
288   const std::size_t total = mSignalConnections.Count();
289   // only do something if there are items
290   if( total > 0 )
291   {
292     std::size_t index = 0;
293     // process the whole vector
294     for( std::size_t i = 0; i < total; ++i )
295     {
296       if( mSignalConnections[ index ] == NULL )
297       {
298         // items will be moved so don't increase index (erase will decrease the count of vector)
299         mSignalConnections.Erase( mSignalConnections.Begin() + index );
300       }
301       else
302       {
303         // increase to next element
304         ++index;
305       }
306     }
307   }
308 }
309
310 // BaseSignal::EmitGuard
311
312 BaseSignal::EmitGuard::EmitGuard( bool& flag )
313 : mFlag( NULL )
314 {
315   if( !flag )
316   {
317     mFlag = &flag;
318     flag = true;
319   }
320   else
321   {
322     // mFlag is NULL when Emit() is called during Emit()
323     DALI_LOG_ERROR( "Cannot call Emit() from inside Emit()" );
324   }
325 }
326
327 BaseSignal::EmitGuard::~EmitGuard()
328 {
329   if( mFlag )
330   {
331     *mFlag = false;
332   }
333 }
334
335 bool BaseSignal::EmitGuard::ErrorOccurred()
336 {
337   // mFlag is NULL when Emit() is called during Emit()
338   return (NULL == mFlag);
339 }
340
341 } // namespace Dali