2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
8 // http://floralicense.org/license/
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.
18 #include <dali/public-api/signals/base-signal.h>
21 #include <algorithm> // remove_if
24 #include <dali/integration-api/debug.h>
29 const int INVALID_CALLBACK_INDEX = -1;
31 // Predicate for std::remove_if algorithm
32 bool IsNullPredicate(void* ptr)
37 } // unnamed namespace
42 BaseSignal::BaseSignal()
43 : mEmittingFlag( false )
47 BaseSignal::~BaseSignal()
49 // We can't assert in a destructor
52 DALI_LOG_ERROR( "Invalid destruction of Signal during Emit()\n" );
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++ )
59 SignalConnection* connection = mSignalConnections[ i ];
61 // Note that values are set to NULL in DeleteConnection
64 connection->Disconnect( this );
69 mSignalConnections.clear();
72 bool BaseSignal::Empty() const
74 return ( 0 == GetConnectionCount() );
77 std::size_t BaseSignal::GetConnectionCount() const
79 std::size_t count( 0 );
81 const std::size_t size( mSignalConnections.size() );
82 for( std::size_t i = 0; i < size; ++i )
84 // Note that values are set to NULL in DeleteConnection
85 if ( NULL != mSignalConnections[i] )
94 void BaseSignal::Emit()
96 // Guards against nested Emit() calls
97 EmitGuard guard( mEmittingFlag ); // Guards against nested Emit() calls
98 if( guard.ErrorOccurred() )
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() );
107 for( std::size_t i = 0; i < initialCount; ++i )
109 CallbackBase* callback( GetCallback(i) );
111 // Note that connections will be set to NULL when disconnected
112 // This is preferable to reducing the connection count while iterating
115 CallbackBase::Execute( *callback );
119 // Cleanup NULL values from Connection container
120 CleanupConnections();
123 void BaseSignal::OnConnect( CallbackBase* callback )
125 DALI_ASSERT_ALWAYS( NULL != callback && "Invalid member function pointer passed to Connect()" );
127 int index = FindCallback( callback );
129 // Don't double-connect the same callback
130 if( INVALID_CALLBACK_INDEX == index )
132 // create a new signal connection object, to allow the signal to track the connection.
133 SignalConnection* connection = new SignalConnection( callback );
135 mSignalConnections.push_back( connection );
144 void BaseSignal::OnDisconnect( CallbackBase* callback )
146 DALI_ASSERT_ALWAYS( NULL != callback && "Invalid member function pointer passed to Disconnect()" );
148 int index = FindCallback( callback );
150 if( index > INVALID_CALLBACK_INDEX )
152 DeleteConnection( index );
155 // call back is a temporary created to find which slot should be disconnected.
159 void BaseSignal::OnConnect( ConnectionTrackerInterface* tracker, CallbackBase* callback )
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()" );
164 int index = FindCallback( callback );
166 // Don't double-connect the same callback
167 if( INVALID_CALLBACK_INDEX == index )
169 // create a new signal connection object, to allow the signal to track the connection.
170 SignalConnection* connection = new SignalConnection( tracker, callback );
172 mSignalConnections.push_back( connection );
174 // Let the connection tracker know that a connection between a signal and a slot has been made.
175 tracker->SignalConnected( this, callback );
184 void BaseSignal::OnDisconnect( ConnectionTrackerInterface* tracker, CallbackBase* callback )
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()" );
189 int index = FindCallback( callback );
191 if( index > INVALID_CALLBACK_INDEX )
193 // temporary pointer to disconnected callback
194 CallbackBase* disconnectedCallback = mSignalConnections[index]->GetCallback();
196 // close the signal side connection first.
197 DeleteConnection( index );
199 // close the slot side connection
200 tracker->SignalDisconnected( this, disconnectedCallback );
203 // call back is a temporary created to find which slot should be disconnected.
207 // for SlotObserver::SlotDisconnected
208 void BaseSignal::SlotDisconnected( CallbackBase* callback )
210 for( std::size_t i=0; i < mSignalConnections.size(); ++i )
212 const CallbackBase* connectionCallback = GetCallback( i );
214 // Pointer comparison i.e. SignalConnection contains pointer to same callback instance
215 if( connectionCallback &&
216 connectionCallback == callback )
218 DeleteConnection( i );
220 // Disconnection complete
225 DALI_ASSERT_ALWAYS( false && "Callback lost in SlotDisconnected()" );
228 CallbackBase* BaseSignal::GetCallback( std::size_t connectionIndex ) const
230 DALI_ASSERT_ALWAYS( connectionIndex < mSignalConnections.size() && "GetCallback called with invalid index" );
232 CallbackBase* callback( NULL );
234 SignalConnection* connection( mSignalConnections[ connectionIndex ] );
236 // Note that values are set to NULL in DeleteConnection
239 callback = connection->GetCallback();
245 int BaseSignal::FindCallback( CallbackBase* callback )
247 int index( INVALID_CALLBACK_INDEX );
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)
253 for( std::size_t i=0; i < mSignalConnections.size(); ++i )
255 const CallbackBase* connectionCallback = GetCallback( i );
257 // Note that values are set to NULL in DeleteConnection
258 if( connectionCallback &&
259 ( *connectionCallback == *callback ) )
269 void BaseSignal::DeleteConnection( std::size_t connectionIndex )
271 DALI_ASSERT_ALWAYS( connectionIndex < mSignalConnections.size() && "DeleteConnection called with invalid index" );
274 SignalConnection* connection( mSignalConnections[ connectionIndex ] );
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;
283 void BaseSignal::CleanupConnections()
285 // Move NULL pointers to the end...
286 std::vector< SignalConnection* >::iterator endIter = remove_if( mSignalConnections.begin(), mSignalConnections.end(), IsNullPredicate );
288 // ...and remove them
289 mSignalConnections.erase( endIter, mSignalConnections.end() );
292 // BaseSignal::EmitGuard
294 BaseSignal::EmitGuard::EmitGuard( bool& flag )
304 // mFlag is NULL when Emit() is called during Emit()
305 DALI_LOG_ERROR( "Cannot call Emit() from inside Emit()" );
309 BaseSignal::EmitGuard::~EmitGuard()
317 bool BaseSignal::EmitGuard::ErrorOccurred()
319 // mFlag is NULL when Emit() is called during Emit()
320 return (NULL == mFlag);