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