2 * Copyright (c) 2014 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 // Predicate for std::remove_if algorithm
33 bool IsNullPredicate(void* ptr)
38 } // unnamed namespace
43 BaseSignal::BaseSignal()
44 : mEmittingFlag( false )
48 BaseSignal::~BaseSignal()
50 // We can't assert in a destructor
53 DALI_LOG_ERROR( "Invalid destruction of Signal during Emit()\n" );
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++ )
60 SignalConnection* connection = mSignalConnections[ i ];
62 // Note that values are set to NULL in DeleteConnection
65 connection->Disconnect( this );
70 mSignalConnections.clear();
73 bool BaseSignal::Empty() const
75 return ( 0 == GetConnectionCount() );
78 std::size_t BaseSignal::GetConnectionCount() const
80 std::size_t count( 0 );
82 const std::size_t size( mSignalConnections.size() );
83 for( std::size_t i = 0; i < size; ++i )
85 // Note that values are set to NULL in DeleteConnection
86 if ( NULL != mSignalConnections[i] )
95 void BaseSignal::Emit()
97 // Guards against nested Emit() calls
98 EmitGuard guard( mEmittingFlag ); // Guards against nested Emit() calls
99 if( guard.ErrorOccurred() )
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() );
108 for( std::size_t i = 0; i < initialCount; ++i )
110 CallbackBase* callback( GetCallback(i) );
112 // Note that connections will be set to NULL when disconnected
113 // This is preferable to reducing the connection count while iterating
116 CallbackBase::Execute( *callback );
120 // Cleanup NULL values from Connection container
121 CleanupConnections();
124 void BaseSignal::OnConnect( CallbackBase* callback )
126 DALI_ASSERT_ALWAYS( NULL != callback && "Invalid member function pointer passed to Connect()" );
128 int index = FindCallback( callback );
130 // Don't double-connect the same callback
131 if( INVALID_CALLBACK_INDEX == index )
133 // create a new signal connection object, to allow the signal to track the connection.
134 SignalConnection* connection = new SignalConnection( callback );
136 mSignalConnections.push_back( connection );
145 void BaseSignal::OnDisconnect( CallbackBase* callback )
147 DALI_ASSERT_ALWAYS( NULL != callback && "Invalid member function pointer passed to Disconnect()" );
149 int index = FindCallback( callback );
151 if( index > INVALID_CALLBACK_INDEX )
153 DeleteConnection( index );
156 // call back is a temporary created to find which slot should be disconnected.
160 void BaseSignal::OnConnect( ConnectionTrackerInterface* tracker, CallbackBase* callback )
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()" );
165 int index = FindCallback( callback );
167 // Don't double-connect the same callback
168 if( INVALID_CALLBACK_INDEX == index )
170 // create a new signal connection object, to allow the signal to track the connection.
171 SignalConnection* connection = new SignalConnection( tracker, callback );
173 mSignalConnections.push_back( connection );
175 // Let the connection tracker know that a connection between a signal and a slot has been made.
176 tracker->SignalConnected( this, callback );
185 void BaseSignal::OnDisconnect( ConnectionTrackerInterface* tracker, CallbackBase* callback )
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()" );
190 int index = FindCallback( callback );
192 if( index > INVALID_CALLBACK_INDEX )
194 // temporary pointer to disconnected callback
195 CallbackBase* disconnectedCallback = mSignalConnections[index]->GetCallback();
197 // close the signal side connection first.
198 DeleteConnection( index );
200 // close the slot side connection
201 tracker->SignalDisconnected( this, disconnectedCallback );
204 // call back is a temporary created to find which slot should be disconnected.
208 // for SlotObserver::SlotDisconnected
209 void BaseSignal::SlotDisconnected( CallbackBase* callback )
211 for( std::size_t i=0; i < mSignalConnections.size(); ++i )
213 const CallbackBase* connectionCallback = GetCallback( i );
215 // Pointer comparison i.e. SignalConnection contains pointer to same callback instance
216 if( connectionCallback &&
217 connectionCallback == callback )
219 DeleteConnection( i );
221 // Disconnection complete
226 DALI_ASSERT_ALWAYS( false && "Callback lost in SlotDisconnected()" );
229 CallbackBase* BaseSignal::GetCallback( std::size_t connectionIndex ) const
231 DALI_ASSERT_ALWAYS( connectionIndex < mSignalConnections.size() && "GetCallback called with invalid index" );
233 CallbackBase* callback( NULL );
235 SignalConnection* connection( mSignalConnections[ connectionIndex ] );
237 // Note that values are set to NULL in DeleteConnection
240 callback = connection->GetCallback();
246 int BaseSignal::FindCallback( CallbackBase* callback )
248 int index( INVALID_CALLBACK_INDEX );
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)
254 for( std::size_t i=0; i < mSignalConnections.size(); ++i )
256 const CallbackBase* connectionCallback = GetCallback( i );
258 // Note that values are set to NULL in DeleteConnection
259 if( connectionCallback &&
260 ( *connectionCallback == *callback ) )
270 void BaseSignal::DeleteConnection( std::size_t connectionIndex )
272 DALI_ASSERT_ALWAYS( connectionIndex < mSignalConnections.size() && "DeleteConnection called with invalid index" );
275 SignalConnection* connection( mSignalConnections[ connectionIndex ] );
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;
284 void BaseSignal::CleanupConnections()
286 // Move NULL pointers to the end...
287 std::vector< SignalConnection* >::iterator endIter = remove_if( mSignalConnections.begin(), mSignalConnections.end(), IsNullPredicate );
289 // ...and remove them
290 mSignalConnections.erase( endIter, mSignalConnections.end() );
293 // BaseSignal::EmitGuard
295 BaseSignal::EmitGuard::EmitGuard( bool& flag )
305 // mFlag is NULL when Emit() is called during Emit()
306 DALI_LOG_ERROR( "Cannot call Emit() from inside Emit()" );
310 BaseSignal::EmitGuard::~EmitGuard()
318 bool BaseSignal::EmitGuard::ErrorOccurred()
320 // mFlag is NULL when Emit() is called during Emit()
321 return (NULL == mFlag);