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