diff --git a/common/tool/tool_interactive.cpp b/common/tool/tool_interactive.cpp index 3c04d81cb1..c69030090a 100644 --- a/common/tool/tool_interactive.cpp +++ b/common/tool/tool_interactive.cpp @@ -56,11 +56,6 @@ void TOOL_INTERACTIVE::goInternal( TOOL_STATE_FUNC& aState, const TOOL_EVENT_LIS } -void TOOL_INTERACTIVE::Reset() -{ -} - - void TOOL_INTERACTIVE::SetContextMenu( CONTEXT_MENU* aMenu, TOOL_ContextMenuTrigger aTrigger ) { aMenu->setTool( this ); diff --git a/common/tool/tool_manager.cpp b/common/tool/tool_manager.cpp index 2d1ce65bb7..c200031930 100644 --- a/common/tool/tool_manager.cpp +++ b/common/tool/tool_manager.cpp @@ -92,24 +92,40 @@ void TOOL_MANAGER::RegisterTool( TOOL_BASE* aTool ) } -void TOOL_MANAGER::InvokeTool( TOOL_ID aToolId ) +bool TOOL_MANAGER::InvokeTool( TOOL_ID aToolId ) { TOOL_BASE* tool = FindTool( aToolId ); if( tool && tool->GetType() == TOOL_Interactive ) + { + // If the tool is already active, do not invoke it again + if( m_toolIdIndex[aToolId]->idle == false ) + return false; + + m_toolIdIndex[aToolId]->idle = false; static_cast( tool )->Reset(); - TOOL_EVENT evt( TC_Command, TA_ActivateTool, tool->GetName() ); - ProcessEvent( evt ); + TOOL_EVENT evt( TC_Command, TA_ActivateTool, tool->GetName() ); + ProcessEvent( evt ); + + // Save the tool on the front of the processing queue + m_activeTools.push_front( aToolId ); + + return true; + } + + return false; } -void TOOL_MANAGER::InvokeTool( const std::string& aName ) +bool TOOL_MANAGER::InvokeTool( const std::string& aName ) { TOOL_BASE* tool = FindTool( aName ); if( tool ) - InvokeTool( tool->GetId() ); + return InvokeTool( tool->GetId() ); + + return false; } @@ -159,13 +175,18 @@ optional TOOL_MANAGER::ScheduleWait( TOOL_BASE* aTool, void TOOL_MANAGER::dispatchInternal( TOOL_EVENT& aEvent ) { // iterate over all registered tools - BOOST_FOREACH( ToolState* st, m_toolState | boost::adaptors::map_values ) - { + BOOST_FOREACH( TOOL_ID toolId, m_activeTools ) + { + ToolState* st = m_toolIdIndex[toolId]; + // the tool state handler is waiting for events (i.e. called Wait() method) if( st->pendingWait ) { if( st->waitEvents.Matches( aEvent ) ) { + // By default, already processed events are not passed further + m_passEvent = false; + // got matching event? clear wait list and wake up the coroutine st->wakeupEvent = aEvent; st->pendingWait = false; @@ -173,16 +194,22 @@ void TOOL_MANAGER::dispatchInternal( TOOL_EVENT& aEvent ) st->cofunc->Resume(); if( !st->cofunc->Running() ) { - delete st->cofunc; - st->cofunc = NULL; + finishTool( st ); } + + // The tool requested to stop propagating event to other tools + if( !m_passEvent ) + break; } } - else + } + + BOOST_FOREACH( ToolState* st, m_toolState | boost::adaptors::map_values ) + { + if( !st->pendingWait ) { // no state handler in progress - check if there are any transitions (defined by // Go() method that match the event. - if( st->transitions.size() ) { BOOST_FOREACH( Transition tr, st->transitions ) @@ -201,8 +228,7 @@ void TOOL_MANAGER::dispatchInternal( TOOL_EVENT& aEvent ) if( !st->cofunc->Running() ) { - delete st->cofunc; - st->cofunc = NULL; + finishTool( st ); } } } @@ -212,14 +238,28 @@ void TOOL_MANAGER::dispatchInternal( TOOL_EVENT& aEvent ) } +void TOOL_MANAGER::finishTool( ToolState* aState ) +{ + wxASSERT( m_activeTools.front() == aState->theTool->GetId() ); + + aState->idle = true; + m_activeTools.erase( m_activeTools.begin() ); + + delete aState->cofunc; + aState->cofunc = NULL; +} + + bool TOOL_MANAGER::ProcessEvent( TOOL_EVENT& aEvent ) { wxLogDebug( "event: %s", aEvent.Format().c_str() ); dispatchInternal( aEvent ); - BOOST_FOREACH( ToolState* st, m_toolState | boost::adaptors::map_values ) - { + BOOST_FOREACH( TOOL_ID toolId, m_activeTools ) + { + ToolState* st = m_toolIdIndex[toolId]; + if( st->contextMenuTrigger == CMENU_NOW ) { st->pendingWait = true; @@ -273,8 +313,10 @@ void TOOL_MANAGER::SetEnvironment( EDA_ITEM* aModel, KiGfx::VIEW* aView, m_editFrame = aFrame; // Reset state of the registered tools - BOOST_FOREACH( TOOL_BASE* tool, m_toolState | boost::adaptors::map_keys ) + BOOST_FOREACH( TOOL_ID toolId, m_activeTools ) { + TOOL_BASE* tool = m_toolIdIndex[toolId]->theTool; + if( tool->GetType() == TOOL_Interactive ) static_cast( tool )->Reset(); } diff --git a/include/tool/tool_manager.h b/include/tool/tool_manager.h index 36599977a0..771427812d 100644 --- a/include/tool/tool_manager.h +++ b/include/tool/tool_manager.h @@ -72,8 +72,8 @@ public: * Calls a tool by sending a tool activation event to tool of given ID or name. * An user-defined parameter object can be also passed */ - void InvokeTool( TOOL_ID aToolId ); - void InvokeTool( const std::string& aName ); + bool InvokeTool( TOOL_ID aToolId ); + bool InvokeTool( const std::string& aName ); template void InvokeTool( const std::string& aName, const Parameters& aToolParams ); @@ -153,20 +153,31 @@ public: void ScheduleContextMenu( TOOL_BASE* aTool, CONTEXT_MENU* aMenu, TOOL_ContextMenuTrigger aTrigger ); -private: - void dispatchInternal( TOOL_EVENT& aEvent ); + /** + * Allows a tool pass the already handled event to be passed to the next tool on the stack. + */ + void PassEvent() + { + m_passEvent = true; + } +private: struct ToolState; typedef std::pair Transition; + void dispatchInternal( TOOL_EVENT& aEvent ); + void finishTool( ToolState* aState ); + std::map m_toolState; std::map m_toolNameIndex; std::map m_toolIdIndex; + std::deque m_activeTools; EDA_ITEM* m_model; KiGfx::VIEW* m_view; KiGfx::VIEW_CONTROLS* m_viewControls; wxWindow* m_editFrame; + bool m_passEvent; ToolState* m_currentTool; };