summaryrefslogtreecommitdiff
path: root/common/tool/tool_manager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'common/tool/tool_manager.cpp')
-rw-r--r--common/tool/tool_manager.cpp777
1 files changed, 777 insertions, 0 deletions
diff --git a/common/tool/tool_manager.cpp b/common/tool/tool_manager.cpp
new file mode 100644
index 0000000..447ab99
--- /dev/null
+++ b/common/tool/tool_manager.cpp
@@ -0,0 +1,777 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 CERN
+ * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ * @author Maciej Suminski <maciej.suminski@cern.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <map>
+#include <list>
+#include <stack>
+#include <algorithm>
+
+#include <boost/foreach.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/optional.hpp>
+#include <boost/range/adaptor/map.hpp>
+
+#include <wx/event.h>
+#include <wx/clipbrd.h>
+
+#include <view/view.h>
+#include <view/view_controls.h>
+
+#include <tool/tool_base.h>
+#include <tool/tool_interactive.h>
+#include <tool/tool_manager.h>
+#include <tool/context_menu.h>
+#include <tool/coroutine.h>
+#include <tool/action_manager.h>
+
+#include <wxPcbStruct.h>
+#include <confirm.h>
+#include <class_draw_panel_gal.h>
+
+using boost::optional;
+
+/// Struct describing the current execution state of a TOOL
+struct TOOL_MANAGER::TOOL_STATE
+{
+ TOOL_STATE( TOOL_BASE* aTool ) :
+ theTool( aTool )
+ {
+ clear();
+ }
+
+ TOOL_STATE( const TOOL_STATE& aState )
+ {
+ theTool = aState.theTool;
+ idle = aState.idle;
+ pendingWait = aState.pendingWait;
+ pendingContextMenu = aState.pendingContextMenu;
+ contextMenu = aState.contextMenu;
+ contextMenuTrigger = aState.contextMenuTrigger;
+ cofunc = aState.cofunc;
+ wakeupEvent = aState.wakeupEvent;
+ waitEvents = aState.waitEvents;
+ transitions = aState.transitions;
+ // do not copy stateStack
+ }
+
+ ~TOOL_STATE()
+ {
+ assert( stateStack.empty() );
+ }
+
+ /// The tool itself
+ TOOL_BASE* theTool;
+
+ /// Is the tool active (pending execution) or disabled at the moment
+ bool idle;
+
+ /// Flag defining if the tool is waiting for any event (i.e. if it
+ /// issued a Wait() call).
+ bool pendingWait;
+
+ /// Is there a context menu being displayed
+ bool pendingContextMenu;
+
+ /// Context menu currently used by the tool
+ CONTEXT_MENU* contextMenu;
+
+ /// Defines when the context menu is opened
+ CONTEXT_MENU_TRIGGER contextMenuTrigger;
+
+ /// Tool execution context
+ COROUTINE<int, const TOOL_EVENT&>* cofunc;
+
+ /// The event that triggered the execution/wakeup of the tool after Wait() call
+ TOOL_EVENT wakeupEvent;
+
+ /// List of events the tool is currently waiting for
+ TOOL_EVENT_LIST waitEvents;
+
+ /// List of possible transitions (ie. association of events and state handlers that are executed
+ /// upon the event reception
+ std::vector<TRANSITION> transitions;
+
+ void operator=( const TOOL_STATE& aState )
+ {
+ theTool = aState.theTool;
+ idle = aState.idle;
+ pendingWait = aState.pendingWait;
+ pendingContextMenu = aState.pendingContextMenu;
+ contextMenu = aState.contextMenu;
+ contextMenuTrigger = aState.contextMenuTrigger;
+ cofunc = aState.cofunc;
+ wakeupEvent = aState.wakeupEvent;
+ waitEvents = aState.waitEvents;
+ transitions = aState.transitions;
+ // do not copy stateStack
+ }
+
+ bool operator==( const TOOL_MANAGER::TOOL_STATE& aRhs ) const
+ {
+ return aRhs.theTool == this->theTool;
+ }
+
+ bool operator!=( const TOOL_MANAGER::TOOL_STATE& aRhs ) const
+ {
+ return aRhs.theTool != this->theTool;
+ }
+
+ /**
+ * Function Push()
+ * Stores the current state of the tool on stack. Stacks are stored internally and are not
+ * shared between different TOOL_STATE objects.
+ */
+ void Push()
+ {
+ stateStack.push( new TOOL_STATE( *this ) );
+
+ clear();
+ }
+
+ /**
+ * Function Pop()
+ * Restores state of the tool from stack. Stacks are stored internally and are not
+ * shared between different TOOL_STATE objects.
+ * @return True if state was restored, false if the stack was empty.
+ */
+ bool Pop()
+ {
+ delete cofunc;
+
+ if( !stateStack.empty() )
+ {
+ *this = *stateStack.top();
+ delete stateStack.top();
+ stateStack.pop();
+
+ return true;
+ }
+ else
+ {
+ cofunc = NULL;
+
+ return false;
+ }
+ }
+
+private:
+ ///> Stack preserving previous states of a TOOL.
+ std::stack<TOOL_STATE*> stateStack;
+
+ ///> Restores the initial state.
+ void clear()
+ {
+ idle = true;
+ pendingWait = false;
+ pendingContextMenu = false;
+ cofunc = NULL;
+ contextMenu = NULL;
+ contextMenuTrigger = CMENU_OFF;
+ transitions.clear();
+ }
+};
+
+
+TOOL_MANAGER::TOOL_MANAGER() :
+ m_model( NULL ),
+ m_view( NULL ),
+ m_viewControls( NULL ),
+ m_editFrame( NULL ),
+ m_passEvent( false )
+{
+ m_actionMgr = new ACTION_MANAGER( this );
+}
+
+
+TOOL_MANAGER::~TOOL_MANAGER()
+{
+ std::map<TOOL_BASE*, TOOL_STATE*>::iterator it, it_end;
+
+ for( it = m_toolState.begin(), it_end = m_toolState.end(); it != it_end; ++it )
+ {
+ delete it->second->cofunc; // delete cofunction
+ delete it->second; // delete TOOL_STATE
+ delete it->first; // delete the tool itself
+ }
+
+ delete m_actionMgr;
+}
+
+
+void TOOL_MANAGER::RegisterTool( TOOL_BASE* aTool )
+{
+ wxASSERT_MSG( m_toolNameIndex.find( aTool->GetName() ) == m_toolNameIndex.end(),
+ wxT( "Adding two tools with the same name may result in unexpected behaviour.") );
+ wxASSERT_MSG( m_toolIdIndex.find( aTool->GetId() ) == m_toolIdIndex.end(),
+ wxT( "Adding two tools with the same ID may result in unexpected behaviour.") );
+ wxASSERT_MSG( m_toolTypes.find( typeid( *aTool ).name() ) == m_toolTypes.end(),
+ wxT( "Adding two tools of the same type may result in unexpected behaviour.") );
+
+ TOOL_STATE* st = new TOOL_STATE( aTool );
+
+ m_toolState[aTool] = st;
+ m_toolNameIndex[aTool->GetName()] = st;
+ m_toolIdIndex[aTool->GetId()] = st;
+ m_toolTypes[typeid( *aTool ).name()] = st->theTool;
+
+ aTool->attachManager( this );
+
+ if( !aTool->Init() )
+ {
+ std::string msg = StrPrintf( "Initialization of the %s tool failed",
+ aTool->GetName().c_str() );
+
+ DisplayError( NULL, wxString::FromUTF8( msg.c_str() ) );
+
+ // Unregister the tool
+ m_toolState.erase( aTool );
+ m_toolNameIndex.erase( aTool->GetName() );
+ m_toolIdIndex.erase( aTool->GetId() );
+ m_toolTypes.erase( typeid( *aTool ).name() );
+
+ delete st;
+ delete aTool;
+ }
+}
+
+
+bool TOOL_MANAGER::InvokeTool( TOOL_ID aToolId )
+{
+ TOOL_BASE* tool = FindTool( aToolId );
+
+ if( tool && tool->GetType() == INTERACTIVE )
+ return invokeTool( tool );
+
+ return false; // there is no tool with the given id
+}
+
+
+bool TOOL_MANAGER::InvokeTool( const std::string& aToolName )
+{
+ TOOL_BASE* tool = FindTool( aToolName );
+
+ if( tool && tool->GetType() == INTERACTIVE )
+ return invokeTool( tool );
+
+ return false; // there is no tool with the given name
+}
+
+
+void TOOL_MANAGER::RegisterAction( TOOL_ACTION* aAction )
+{
+ m_actionMgr->RegisterAction( aAction );
+}
+
+
+void TOOL_MANAGER::UnregisterAction( TOOL_ACTION* aAction )
+{
+ m_actionMgr->UnregisterAction( aAction );
+}
+
+
+bool TOOL_MANAGER::RunAction( const std::string& aActionName, bool aNow, void* aParam )
+{
+ TOOL_ACTION* action = m_actionMgr->FindAction( aActionName );
+
+ if( action )
+ {
+ TOOL_EVENT event = action->MakeEvent();
+
+ // Allow to override the action parameter
+ if( aParam )
+ event.SetParameter( aParam );
+
+ if( aNow )
+ ProcessEvent( event );
+ else
+ PostEvent( event );
+
+ return true;
+ }
+
+ wxASSERT_MSG( action != NULL, wxString::Format( wxT( "Could not find action %s." ), aActionName ) );
+
+ return false;
+}
+
+
+void TOOL_MANAGER::RunAction( const TOOL_ACTION& aAction, bool aNow, void* aParam )
+{
+ TOOL_EVENT event = aAction.MakeEvent();
+
+ // Allow to override the action parameter
+ if( aParam )
+ event.SetParameter( aParam );
+
+ if( aNow )
+ ProcessEvent( event );
+ else
+ PostEvent( event );
+}
+
+
+int TOOL_MANAGER::GetHotKey( const TOOL_ACTION& aAction )
+{
+ return m_actionMgr->GetHotKey( aAction );
+}
+
+
+void TOOL_MANAGER::UpdateHotKeys()
+{
+ m_actionMgr->UpdateHotKeys();
+}
+
+
+bool TOOL_MANAGER::invokeTool( TOOL_BASE* aTool )
+{
+ wxASSERT( aTool != NULL );
+
+ TOOL_EVENT evt( TC_COMMAND, TA_ACTIVATE, aTool->GetName() );
+ ProcessEvent( evt );
+
+ return true;
+}
+
+
+bool TOOL_MANAGER::runTool( TOOL_ID aToolId )
+{
+ TOOL_BASE* tool = FindTool( aToolId );
+
+ if( tool && tool->GetType() == INTERACTIVE )
+ return runTool( tool );
+
+ return false; // there is no tool with the given id
+}
+
+
+bool TOOL_MANAGER::runTool( const std::string& aToolName )
+{
+ TOOL_BASE* tool = FindTool( aToolName );
+
+ if( tool && tool->GetType() == INTERACTIVE )
+ return runTool( tool );
+
+ return false; // there is no tool with the given name
+}
+
+
+bool TOOL_MANAGER::runTool( TOOL_BASE* aTool )
+{
+ wxASSERT( aTool != NULL );
+
+ if( !isRegistered( aTool ) )
+ {
+ wxASSERT_MSG( false, wxT( "You cannot run unregistered tools" ) );
+ return false;
+ }
+
+ // If the tool is already active, bring it to the top of the active tools stack
+ if( isActive( aTool ) )
+ {
+ m_activeTools.erase( std::find( m_activeTools.begin(), m_activeTools.end(),
+ aTool->GetId() ) );
+ m_activeTools.push_front( aTool->GetId() );
+
+ return false;
+ }
+
+ aTool->Reset( TOOL_INTERACTIVE::RUN );
+ aTool->SetTransitions();
+
+ // Add the tool on the front of the processing queue (it gets events first)
+ m_activeTools.push_front( aTool->GetId() );
+
+ return true;
+}
+
+
+TOOL_BASE* TOOL_MANAGER::FindTool( int aId ) const
+{
+ std::map<TOOL_ID, TOOL_STATE*>::const_iterator it = m_toolIdIndex.find( aId );
+
+ if( it != m_toolIdIndex.end() )
+ return it->second->theTool;
+
+ return NULL;
+}
+
+
+TOOL_BASE* TOOL_MANAGER::FindTool( const std::string& aName ) const
+{
+ std::map<std::string, TOOL_STATE*>::const_iterator it = m_toolNameIndex.find( aName );
+
+ if( it != m_toolNameIndex.end() )
+ return it->second->theTool;
+
+ return NULL;
+}
+
+void TOOL_MANAGER::DeactivateTool()
+{
+ TOOL_EVENT evt( TC_COMMAND, TA_ACTIVATE, "" ); // deactivate the active tool
+ ProcessEvent( evt );
+}
+
+void TOOL_MANAGER::ResetTools( TOOL_BASE::RESET_REASON aReason )
+{
+ DeactivateTool();
+
+ BOOST_FOREACH( TOOL_BASE* tool, m_toolState | boost::adaptors::map_keys )
+ {
+ tool->Reset( aReason );
+ tool->SetTransitions();
+ }
+}
+
+
+int TOOL_MANAGER::GetPriority( int aToolId ) const
+{
+ int priority = 0;
+
+ for( std::list<TOOL_ID>::const_iterator it = m_activeTools.begin(),
+ itEnd = m_activeTools.end(); it != itEnd; ++it )
+ {
+ if( *it == aToolId )
+ return priority;
+
+ ++priority;
+ }
+
+ return -1;
+}
+
+
+void TOOL_MANAGER::ScheduleNextState( TOOL_BASE* aTool, TOOL_STATE_FUNC& aHandler,
+ const TOOL_EVENT_LIST& aConditions )
+{
+ TOOL_STATE* st = m_toolState[aTool];
+
+ st->transitions.push_back( TRANSITION( aConditions, aHandler ) );
+}
+
+
+optional<TOOL_EVENT> TOOL_MANAGER::ScheduleWait( TOOL_BASE* aTool,
+ const TOOL_EVENT_LIST& aConditions )
+{
+ TOOL_STATE* st = m_toolState[aTool];
+
+ assert( !st->pendingWait ); // everything collapses on two Yield() in a row
+
+ // indicate to the manager that we are going to sleep and we shall be
+ // woken up when an event matching aConditions arrive
+ st->pendingWait = true;
+ st->waitEvents = aConditions;
+
+ // switch context back to event dispatcher loop
+ st->cofunc->Yield();
+
+ return st->wakeupEvent;
+}
+
+
+void TOOL_MANAGER::dispatchInternal( const TOOL_EVENT& aEvent )
+{
+ // iterate over all registered tools
+ for( std::list<TOOL_ID>::iterator it = m_activeTools.begin();
+ it != m_activeTools.end(); /* iteration is done inside */)
+ {
+ std::list<TOOL_ID>::iterator curIt = it;
+ TOOL_STATE* st = m_toolIdIndex[*it];
+ ++it; // it might be overwritten, if the tool is removed the m_activeTools list
+
+ // the tool state handler is waiting for events (i.e. called Wait() method)
+ if( st->pendingWait )
+ {
+ if( st->waitEvents.Matches( aEvent ) )
+ {
+ // By default, only messages are passed further
+ m_passEvent = ( aEvent.Category() == TC_MESSAGE );
+
+ // got matching event? clear wait list and wake up the coroutine
+ st->wakeupEvent = aEvent;
+ st->pendingWait = false;
+ st->waitEvents.clear();
+
+ if( st->cofunc && !st->cofunc->Resume() )
+ {
+ if( finishTool( st, false ) ) // The couroutine has finished
+ it = m_activeTools.erase( curIt );
+ }
+
+ // If the tool did not request to propagate
+ // the event to other tools, we should stop it now
+ if( !m_passEvent )
+ break;
+ }
+ }
+ }
+
+ BOOST_FOREACH( TOOL_STATE* st, m_toolState | boost::adaptors::map_values )
+ {
+ // no state handler in progress - check if there are any transitions (defined by
+ // Go() method that match the event.
+ if( !st->pendingWait && !st->transitions.empty() )
+ {
+ BOOST_FOREACH( TRANSITION& tr, st->transitions )
+ {
+ if( tr.first.Matches( aEvent ) )
+ {
+ // if there is already a context, then store it
+ if( st->cofunc )
+ st->Push();
+
+ // as the state changes, the transition table has to be set up again
+ st->transitions.clear();
+
+ st->cofunc = new COROUTINE<int, const TOOL_EVENT&>( tr.second );
+
+ // got match? Run the handler.
+ st->cofunc->Call( aEvent );
+
+ if( !st->cofunc->Running() )
+ finishTool( st ); // The couroutine has finished immediately?
+
+ // there is no point in further checking, as transitions got cleared
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+bool TOOL_MANAGER::dispatchStandardEvents( const TOOL_EVENT& aEvent )
+{
+ if( aEvent.Action() == TA_KEY_PRESSED )
+ {
+ // Check if there is a hotkey associated
+ if( m_actionMgr->RunHotKey( aEvent.Modifier() | aEvent.KeyCode() ) )
+ return false; // hotkey event was handled so it does not go any further
+ }
+
+ return true;
+}
+
+
+bool TOOL_MANAGER::dispatchActivation( const TOOL_EVENT& aEvent )
+{
+ if( aEvent.IsActivate() )
+ {
+ std::map<std::string, TOOL_STATE*>::iterator tool = m_toolNameIndex.find( *aEvent.GetCommandStr() );
+
+ if( tool != m_toolNameIndex.end() )
+ {
+ runTool( tool->second->theTool );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+void TOOL_MANAGER::dispatchContextMenu( const TOOL_EVENT& aEvent )
+{
+ BOOST_FOREACH( TOOL_ID toolId, m_activeTools )
+ {
+ TOOL_STATE* st = m_toolIdIndex[toolId];
+
+ // the tool requested a context menu. The menu is activated on RMB click (CMENU_BUTTON mode)
+ // or immediately (CMENU_NOW) mode. The latter is used for clarification lists.
+ if( st->contextMenuTrigger != CMENU_OFF )
+ {
+ if( st->contextMenuTrigger == CMENU_BUTTON && !aEvent.IsClick( BUT_RIGHT ) )
+ break;
+
+ st->pendingWait = true;
+ st->waitEvents = TOOL_EVENT( TC_ANY, TA_ANY );
+
+ // Store the menu pointer in case it is changed by the TOOL when handling menu events
+ CONTEXT_MENU* m = st->contextMenu;
+
+ if( st->contextMenuTrigger == CMENU_NOW )
+ st->contextMenuTrigger = CMENU_OFF;
+
+ // Temporarily store the cursor position, so the tools could execute actions
+ // using the point where the user has invoked a context menu
+ bool forcedCursor = m_viewControls->IsCursorPositionForced();
+ VECTOR2D cursorPos = m_viewControls->GetCursorPosition();
+ m_viewControls->ForceCursorPosition( true, m_viewControls->GetCursorPosition() );
+
+ // Run update handlers
+ m->UpdateAll();
+
+ boost::scoped_ptr<CONTEXT_MENU> menu( new CONTEXT_MENU( *m ) );
+ GetEditFrame()->PopupMenu( menu.get() );
+
+ // If nothing was chosen from the context menu, we must notify the tool as well
+ if( menu->GetSelected() < 0 )
+ {
+ TOOL_EVENT evt( TC_COMMAND, TA_CONTEXT_MENU_CHOICE, -1 );
+ evt.SetParameter( m );
+ dispatchInternal( evt );
+ }
+
+ TOOL_EVENT evt( TC_COMMAND, TA_CONTEXT_MENU_CLOSED );
+ evt.SetParameter( m );
+ dispatchInternal( evt );
+
+ m_viewControls->ForceCursorPosition( forcedCursor, cursorPos );
+
+ break;
+ }
+ }
+}
+
+
+bool TOOL_MANAGER::finishTool( TOOL_STATE* aState, bool aDeactivate )
+{
+ bool shouldDeactivate = false;
+
+ // Reset VIEW_CONTROLS only if the most recent tool is finished
+ if( m_activeTools.empty() || m_activeTools.front() == aState->theTool->GetId() )
+ m_viewControls->Reset();
+
+ if( !aState->Pop() ) // if there are no other contexts saved on the stack
+ {
+ // find the tool and deactivate it
+ std::list<TOOL_ID>::iterator tool = std::find( m_activeTools.begin(), m_activeTools.end(),
+ aState->theTool->GetId() );
+
+ if( tool != m_activeTools.end() )
+ {
+ shouldDeactivate = true;
+
+ if( aDeactivate )
+ m_activeTools.erase( tool );
+ }
+ }
+
+ aState->theTool->SetTransitions();
+
+ return shouldDeactivate;
+}
+
+
+bool TOOL_MANAGER::ProcessEvent( const TOOL_EVENT& aEvent )
+{
+ // Early dispatch of events destined for the TOOL_MANAGER
+ if( !dispatchStandardEvents( aEvent ) )
+ return false;
+
+ dispatchInternal( aEvent );
+ dispatchActivation( aEvent );
+ dispatchContextMenu( aEvent );
+
+ // Dispatch queue
+ while( !m_eventQueue.empty() )
+ {
+ TOOL_EVENT event = m_eventQueue.front();
+ m_eventQueue.pop_front();
+ ProcessEvent( event );
+ }
+
+ if( m_view->IsDirty() )
+ {
+ EDA_DRAW_FRAME* f = static_cast<EDA_DRAW_FRAME*>( GetEditFrame() );
+ f->GetGalCanvas()->Refresh(); // fixme: ugly hack, provide a method in TOOL_DISPATCHER.
+ }
+
+ return false;
+}
+
+
+void TOOL_MANAGER::ScheduleContextMenu( TOOL_BASE* aTool, CONTEXT_MENU* aMenu,
+ CONTEXT_MENU_TRIGGER aTrigger )
+{
+ TOOL_STATE* st = m_toolState[aTool];
+
+ st->contextMenu = aMenu;
+ st->contextMenuTrigger = aTrigger;
+}
+
+
+bool TOOL_MANAGER::SaveClipboard( const std::string& aText )
+{
+ if( wxTheClipboard->Open() )
+ {
+ wxTheClipboard->SetData( new wxTextDataObject( wxString( aText.c_str(), wxConvUTF8 ) ) );
+ wxTheClipboard->Close();
+
+ return true;
+ }
+
+ return false;
+}
+
+
+std::string TOOL_MANAGER::GetClipboard() const
+{
+ std::string result;
+
+ if( wxTheClipboard->Open() )
+ {
+ if( wxTheClipboard->IsSupported( wxDF_TEXT ) )
+ {
+ wxTextDataObject data;
+ wxTheClipboard->GetData( data );
+
+ result = data.GetText().mb_str();
+ }
+
+ wxTheClipboard->Close();
+ }
+
+ return result;
+}
+
+
+TOOL_ID TOOL_MANAGER::MakeToolId( const std::string& aToolName )
+{
+ static int currentId;
+
+ return currentId++;
+}
+
+
+void TOOL_MANAGER::SetEnvironment( EDA_ITEM* aModel, KIGFX::VIEW* aView,
+ KIGFX::VIEW_CONTROLS* aViewControls, wxWindow* aFrame )
+{
+ m_model = aModel;
+ m_view = aView;
+ m_viewControls = aViewControls;
+ m_editFrame = aFrame;
+ m_actionMgr->UpdateHotKeys();
+}
+
+
+bool TOOL_MANAGER::isActive( TOOL_BASE* aTool )
+{
+ if( !isRegistered( aTool ) )
+ return false;
+
+ // Just check if the tool is on the active tools stack
+ return std::find( m_activeTools.begin(), m_activeTools.end(), aTool->GetId() ) != m_activeTools.end();
+}