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