2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/public-api/signals/base-signal.h>
22 #include <algorithm> // remove_if
25 #include <dali/integration-api/debug.h>
30 const int INVALID_CALLBACK_INDEX = -1;
32 } // unnamed namespace
37 BaseSignal::BaseSignal()
38 : mEmittingFlag( false )
42 BaseSignal::~BaseSignal()
44 // We can't assert in a destructor
47 DALI_LOG_ERROR( "Invalid destruction of Signal during Emit()\n" );
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++ )
55 SignalConnection* connection = mSignalConnections[ i ];
57 // Note that values are set to NULL in DeleteConnection
60 connection->Disconnect( this );
65 mSignalConnections.Clear();
68 bool BaseSignal::Empty() const
70 return ( 0 == GetConnectionCount() );
73 std::size_t BaseSignal::GetConnectionCount() const
75 std::size_t count( 0 );
77 const std::size_t size( mSignalConnections.Count() );
78 for( std::size_t i = 0; i < size; ++i )
80 // Note that values are set to NULL in DeleteConnection
81 if ( NULL != mSignalConnections[i] )
90 void BaseSignal::Emit()
92 // Guards against nested Emit() calls
93 EmitGuard guard( mEmittingFlag ); // Guards against nested Emit() calls
94 if( guard.ErrorOccurred() )
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() );
103 for( std::size_t i = 0; i < initialCount; ++i )
105 CallbackBase* callback( GetCallback(i) );
107 // Note that connections will be set to NULL when disconnected
108 // This is preferable to reducing the connection count while iterating
111 CallbackBase::Execute( *callback );
115 // Cleanup NULL values from Connection container
116 CleanupConnections();
119 void BaseSignal::OnConnect( CallbackBase* callback )
121 DALI_ASSERT_ALWAYS( NULL != callback && "Invalid member function pointer passed to Connect()" );
123 int index = FindCallback( callback );
125 // Don't double-connect the same callback
126 if( INVALID_CALLBACK_INDEX == index )
128 // create a new signal connection object, to allow the signal to track the connection.
129 SignalConnection* connection = new SignalConnection( callback );
131 mSignalConnections.PushBack( connection );
140 void BaseSignal::OnDisconnect( CallbackBase* callback )
142 DALI_ASSERT_ALWAYS( NULL != callback && "Invalid member function pointer passed to Disconnect()" );
144 int index = FindCallback( callback );
146 if( index > INVALID_CALLBACK_INDEX )
148 DeleteConnection( index );
151 // call back is a temporary created to find which slot should be disconnected.
155 void BaseSignal::OnConnect( ConnectionTrackerInterface* tracker, CallbackBase* callback )
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()" );
160 int index = FindCallback( callback );
162 // Don't double-connect the same callback
163 if( INVALID_CALLBACK_INDEX == index )
165 // create a new signal connection object, to allow the signal to track the connection.
166 SignalConnection* connection = new SignalConnection( tracker, callback );
168 mSignalConnections.PushBack( connection );
170 // Let the connection tracker know that a connection between a signal and a slot has been made.
171 tracker->SignalConnected( this, callback );
180 void BaseSignal::OnDisconnect( ConnectionTrackerInterface* tracker, CallbackBase* callback )
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()" );
185 int index = FindCallback( callback );
187 if( index > INVALID_CALLBACK_INDEX )
189 // temporary pointer to disconnected callback
190 CallbackBase* disconnectedCallback = mSignalConnections[index]->GetCallback();
192 // close the signal side connection first.
193 DeleteConnection( index );
195 // close the slot side connection
196 tracker->SignalDisconnected( this, disconnectedCallback );
199 // call back is a temporary created to find which slot should be disconnected.
203 // for SlotObserver::SlotDisconnected
204 void BaseSignal::SlotDisconnected( CallbackBase* callback )
206 const std::size_t count( mSignalConnections.Count() );
207 for( std::size_t i=0; i < count; ++i )
209 const CallbackBase* connectionCallback = GetCallback( i );
211 // Pointer comparison i.e. SignalConnection contains pointer to same callback instance
212 if( connectionCallback &&
213 connectionCallback == callback )
215 DeleteConnection( i );
217 // Disconnection complete
222 DALI_ASSERT_ALWAYS( false && "Callback lost in SlotDisconnected()" );
225 CallbackBase* BaseSignal::GetCallback( std::size_t connectionIndex ) const
227 DALI_ASSERT_ALWAYS( connectionIndex < mSignalConnections.Count() && "GetCallback called with invalid index" );
229 CallbackBase* callback( NULL );
231 SignalConnection* connection( mSignalConnections[ connectionIndex ] );
233 // Note that values are set to NULL in DeleteConnection
236 callback = connection->GetCallback();
242 int BaseSignal::FindCallback( CallbackBase* callback )
244 int index( INVALID_CALLBACK_INDEX );
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 )
252 const CallbackBase* connectionCallback = GetCallback( i );
254 // Note that values are set to NULL in DeleteConnection
255 if( connectionCallback &&
256 ( *connectionCallback == *callback ) )
266 void BaseSignal::DeleteConnection( std::size_t connectionIndex )
268 DALI_ASSERT_ALWAYS( connectionIndex < mSignalConnections.Count() && "DeleteConnection called with invalid index" );
271 SignalConnection* connection( mSignalConnections[ connectionIndex ] );
274 // IMPORTANT - do not remove from items from mSignalConnections, set to NULL instead.
275 // Signal Emit() methods require that connection count is not reduced while iterating
276 // i.e. DeleteConnection can be called from within callbacks, while iterating through mSignalConnections.
277 mSignalConnections[ connectionIndex ] = NULL;
280 void BaseSignal::CleanupConnections()
282 const std::size_t total = mSignalConnections.Count();
283 // only do something if there are items
286 std::size_t index = 0;
287 // process the whole vector
288 for( std::size_t i = 0; i < total; ++i )
290 if( mSignalConnections[ index ] == NULL )
292 // items will be moved so don't increase index (erase will decrease the count of vector)
293 mSignalConnections.Erase( mSignalConnections.Begin() + index );
297 // increase to next element
304 // BaseSignal::EmitGuard
306 BaseSignal::EmitGuard::EmitGuard( bool& flag )
316 // mFlag is NULL when Emit() is called during Emit()
317 DALI_LOG_ERROR( "Cannot call Emit() from inside Emit()\n" );
321 BaseSignal::EmitGuard::~EmitGuard()
329 bool BaseSignal::EmitGuard::ErrorOccurred()
331 // mFlag is NULL when Emit() is called during Emit()
332 return (NULL == mFlag);