diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 7ccb9764c2..5b690eca51 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -162,6 +162,7 @@ set(COMMON_SRCS tool/tool_dispatcher.cpp tool/tool_event.cpp tool/tool_interactive.cpp + tool/action_manager.cpp tool/context_menu.cpp geometry/seg.cpp diff --git a/common/tool/tool_event.cpp b/common/tool/tool_event.cpp index 4c5a0c2f98..cf40310534 100644 --- a/common/tool/tool_event.cpp +++ b/common/tool/tool_event.cpp @@ -83,9 +83,9 @@ const std::string TOOL_EVENT::Format() const { TA_ViewDirty, "view-dirty" }, { TA_ChangeLayer, "change-layer" }, { TA_CancelTool, "cancel-tool" }, - { TA_ActivateTool, "activate-tool" }, { TA_ContextMenuUpdate, "context-menu-update" }, { TA_ContextMenuChoice, "context-menu-choice" }, + { TA_Action, "action" }, { 0, "" } }; diff --git a/common/tool/tool_manager.cpp b/common/tool/tool_manager.cpp index 9e0b4ac621..1fd03b62e2 100644 --- a/common/tool/tool_manager.cpp +++ b/common/tool/tool_manager.cpp @@ -82,9 +82,8 @@ struct TOOL_MANAGER::TOOL_STATE TOOL_MANAGER::TOOL_MANAGER() : - m_model( NULL ), m_view( NULL ) + m_actionMgr( this ), m_model( NULL ), m_view( NULL ) { - } @@ -128,38 +127,79 @@ 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 ); - - // Save the tool on the front of the processing queue - m_activeTools.push_front( aToolId ); - - return true; - } + return invokeTool( tool ); return false; } -bool TOOL_MANAGER::InvokeTool( const std::string& aName ) +bool TOOL_MANAGER::InvokeTool( const std::string& aToolName ) { - TOOL_BASE* tool = FindTool( aName ); + TOOL_BASE* tool = FindTool( aToolName ); - if( tool ) - return InvokeTool( tool->GetId() ); + if( tool && tool->GetType() == TOOL_Interactive ) + return invokeTool( tool ); return false; } +bool TOOL_MANAGER::invokeTool( TOOL_BASE* aTool ) +{ + wxASSERT( aTool != NULL ); + + TOOL_EVENT evt( TC_Command, TA_Action, aTool->GetName() ); + ProcessEvent( evt ); + + return true; +} + + +bool TOOL_MANAGER::runTool( TOOL_ID aToolId ) +{ + TOOL_BASE* tool = FindTool( aToolId ); + + if( tool && tool->GetType() == TOOL_Interactive ) + return runTool( tool ); + + return false; +} + + +bool TOOL_MANAGER::runTool( const std::string& aToolName ) +{ + TOOL_BASE* tool = FindTool( aToolName ); + + if( tool && tool->GetType() == TOOL_Interactive ) + return runTool( tool ); + + return false; +} + + +bool TOOL_MANAGER::runTool( TOOL_BASE* aTool ) +{ + wxASSERT( aTool != NULL ); + + if( !isRegistered( aTool ) ) + return false; + + TOOL_STATE* state = m_toolState[aTool]; + + // If the tool is already active, do not invoke it again + if( state->idle == false ) + return false; + state->idle = false; + + static_cast( aTool )->Reset(); + + // 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::const_iterator it = m_toolIdIndex.find( aId ); @@ -228,7 +268,8 @@ void TOOL_MANAGER::dispatchInternal( TOOL_EVENT& aEvent ) finishTool( st ); } - // The tool requested to stop propagating event to other tools + // If the tool did not request to propagate + // the event to other tools, we should stop it now if( !m_passEvent ) break; } @@ -269,6 +310,21 @@ void TOOL_MANAGER::dispatchInternal( TOOL_EVENT& aEvent ) } +bool TOOL_MANAGER::dispatchActivation( TOOL_EVENT& aEvent ) +{ + BOOST_FOREACH( TOOL_STATE* st, m_toolState | boost::adaptors::map_values ) + { + if( st->theTool->GetName() == aEvent.m_commandStr ) + { + runTool( st->theTool ); + return true; + } + } + + return false; +} + + void TOOL_MANAGER::finishTool( TOOL_STATE* aState ) { wxASSERT( m_activeTools.front() == aState->theTool->GetId() ); @@ -286,8 +342,18 @@ bool TOOL_MANAGER::ProcessEvent( TOOL_EVENT& aEvent ) { // wxLogDebug( "event: %s", aEvent.Format().c_str() ); + if( aEvent.Action() == TA_KeyUp ) + { + // 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 + } else if( aEvent.Category() == TC_Command ) // it may be a tool activation event + { + dispatchActivation( aEvent ); + } + dispatchInternal( aEvent ); - + BOOST_FOREACH( TOOL_ID toolId, m_activeTools ) { TOOL_STATE* st = m_toolIdIndex[toolId]; @@ -359,3 +425,12 @@ void TOOL_MANAGER::SetEnvironment( EDA_ITEM* aModel, KiGfx::VIEW* aView, static_cast( tool )->Reset(); } } + + +bool TOOL_MANAGER::isActive( TOOL_BASE* aTool ) +{ + if( !isRegistered( aTool ) ) + return false; + + return !m_toolState[aTool]->idle; +} diff --git a/include/tool/tool_action.h b/include/tool/tool_action.h index e2eced3359..61ef7ad886 100644 --- a/include/tool/tool_action.h +++ b/include/tool/tool_action.h @@ -1,3 +1,27 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * 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 + */ + #ifndef __TOOL_ACTION_H #define __TOOL_ACTION_H @@ -5,82 +29,157 @@ #include #include - -///> Scope of tool actions -enum TOOL_ActionScope { - SCOPE_CONTEXT = 1, ///> Action belongs to a particular tool (i.e. a part of a pop-up menu) - SCOPE_GLOBAL ///> Global action (toolbar/main menu event, global shortcut) -}; +#include // TOOL_ACTION - represents a single action. For instance: // - changing layer to top by pressing PgUp // - running the DRC from the menu // and so on, and so forth.... -class TOOL_ACTION +class TOOL_ACTION { - public: - +public: + TOOL_ACTION( const std::string& aName, TOOL_ActionScope aScope = AS_CONTEXT, int aDefaultHotKey = 0, + const wxString& aMenuItem = wxT( "" ), const wxString& aMenuDesc = wxT( "" ) ) : + m_name( aName ), m_scope( aScope ), m_defaultHotKey( aDefaultHotKey ), + m_currentHotKey( aDefaultHotKey ), m_menuItem( aMenuItem ), + m_menuDescription( aMenuDesc ), m_id( -1 ), m_actionMgr( NULL ) + { + } - TOOL_ACTION - ( - const std::string& name, - TOOL_ActionScope scope = SCOPE_GLOBAL, - int aDefaultHotKey = 0, - const wxString& menuItem = wxT(""), - const wxString& menuDesc = wxT("") - ) : - m_name(name), - m_scope(scope), - m_defaultHotKey(aDefaultHotKey), - m_currentHotKey(aDefaultHotKey), - m_menuItem(menuItem), - m_menuDescription(menuDesc) {} + ~TOOL_ACTION() + { + if( m_actionMgr ) + m_actionMgr->UnregisterAction( this ); + } - bool operator == ( const TOOL_ACTION& rhs ) const - { - return m_id == rhs.m_id; - } + bool operator==( const TOOL_ACTION& aRhs ) const + { + return m_id == aRhs.m_id; + } - bool operator != ( const TOOL_ACTION& rhs ) const - { - return m_id != rhs.m_id; - } + bool operator!=( const TOOL_ACTION& aRhs ) const + { + return m_id != aRhs.m_id; + } - bool hasHotKey() const - { - return m_currentHotKey > 0; - } + /** + * Function GetName() + * Returns name of the action. It is the same one that is contained in TOOL_EVENT that is + * sent by activating the TOOL_ACTION. + * + * @return Name of the action. + */ + const std::string& GetName() const + { + return m_name; + } - private: - friend class TOOL_MANAGER; + /** + * Function GetId() + * Returns the unique id of the TOOL_ACTION object. It is valid only after registering the + * TOOL_ACTION by ACTION_MANAGER. + * + * @return The unique identification number. If the number is negative, then it is not valid. + */ + int GetId() const + { + return m_id; + } - void setId ( int aId ) - { - m_id = aId; - } + /** + * Function ChangeHotKey() + * Assigns a new hot key. + * + * @param aNewHotKey is the new hot key. + */ + void ChangeHotKey( int aNewHotKey ) + { + wxASSERT_MSG( false, wxT( "It is not fully implemented yet") ); + // I mean it has to be changed in the ACTION_MANAGER, or change the implementation + m_currentHotKey = aNewHotKey; + } + /** + * Function RestoreHotKey() + * Changes the assigned hot key to the default one. + */ + void RestoreHotKey() + { + wxASSERT_MSG( false, wxT( "It is not fully implemented yet") ); + // I mean it has to be changed in the ACTION_MANAGER, or change the implementation + m_currentHotKey = m_defaultHotKey; + } - // name of the action (convention is: app.[tool.]action.name) - std::string m_name; - TOOL_ActionScope m_scope; - int m_defaultHotKey; - int m_currentHotKey; - - // Menu item text - wxString m_menuItem; - // Pop-up help - wxString m_menuDescription; - - //KiBitmap m_bitmap; + /** + * Function HasHotKey() + * Checks if the action has a hot key assigned. + * + * @return True if there is a hot key assigned, false otherwise. + * + */ + bool HasHotKey() const + { + return m_currentHotKey > 0; + } - // Unique ID for fast matching. Assigned by TOOL_MANAGER - int m_id; + /** + * Function GetEvent() + * Returns the event associated with the action (ie. the event that will be sent after + * activating the action). + * + * @return The event associated with the action. + */ + TOOL_EVENT GetEvent() const + { + return TOOL_EVENT( TC_Command, TA_Action, m_name, m_scope ); + } - // Origin of the action - TOOL_BASE *m_origin; +private: + friend class ACTION_MANAGER; - // Originating UI object - wxWindow *m_uiOrigin; + /// Assigns an unique identifier. It is given by an instance of ACTION_MANAGER. + void setId( int aId ) + { + m_id = aId; + } + + /// Assigns ACTION_MANAGER object that handles the TOOL_ACTION. + void setActionMgr( ACTION_MANAGER* aManager ) + { + m_actionMgr = aManager; + } + + /// Name of the action (convention is: app.[tool.]action.name) + std::string m_name; + + /// Scope of the action (ie. the event that is issued after activation). + TOOL_ActionScope m_scope; + + /// Default hot key that activates the action. + const int m_defaultHotKey; + + /// Custom assigned hot key that activates the action. + int m_currentHotKey; + + /// Menu entry text + wxString m_menuItem; + + /// Pop-up help + wxString m_menuDescription; + + // Icon for menu entry + //KiBitmap m_bitmap; + + /// Unique ID for fast matching. Assigned by ACTION_MANAGER. + int m_id; + + /// Action manager that handles this TOOL_ACTION. + ACTION_MANAGER* m_actionMgr; + + /// Origin of the action +// const TOOL_BASE* m_origin; + + /// Originating UI object +// wxWindow* m_uiOrigin; }; - #endif diff --git a/include/tool/tool_event.h b/include/tool/tool_event.h index 592624c321..60c8eba326 100644 --- a/include/tool/tool_event.h +++ b/include/tool/tool_event.h @@ -74,15 +74,16 @@ enum TOOL_Actions // Tool cancel event. Issued automagically when the user hits escape or selects End Tool from the context menu. TA_CancelTool = 0x2000, - // Tool activation event. Issued by the GUI upon pressing a button/menu selection. - TA_ActivateTool = 0x4000, - // Context menu update. Issued whenever context menu is open and the user hovers the mouse over one of choices. // Used in dynamic highligting in disambiguation menu - TA_ContextMenuUpdate = 0x8000, + TA_ContextMenuUpdate = 0x4000, // Context menu choice. Sent if the user picked something from the context menu or closed it without selecting anything. - TA_ContextMenuChoice = 0x10000, + TA_ContextMenuChoice = 0x8000, + + // Tool action + TA_Action = 0x10000, + TA_Any = 0xffffffff }; @@ -104,7 +105,15 @@ enum TOOL_Modifiers MD_ModifierMask = MD_ModShift | MD_ModCtrl | MD_ModAlt, }; -// Defines when a context menu is opened. +/// Scope of tool actions +enum TOOL_ActionScope +{ + AS_CONTEXT = 1, ///> Action belongs to a particular tool (i.e. a part of a pop-up menu) + AS_ACTIVE, ///> All active tools + AS_GLOBAL ///> Global action (toolbar/main menu event, global shortcut) +}; + +/// Defines when a context menu is opened. enum CONTEXT_MENU_TRIGGER { CMENU_BUTTON = 0, // On the right button @@ -122,16 +131,19 @@ class TOOL_EVENT public: const std::string Format() const; - TOOL_EVENT( TOOL_EventCategory aCategory = TC_None, TOOL_Actions aAction = TA_None ) : + TOOL_EVENT( TOOL_EventCategory aCategory = TC_None, TOOL_Actions aAction = TA_None, + TOOL_ActionScope aScope = AS_GLOBAL ) : m_category( aCategory ), m_actions( aAction ), + m_scope( aScope ), m_mouseButtons( 0 ), m_keyCode( 0 ), m_modifiers( 0 ) {} - TOOL_EVENT( TOOL_EventCategory aCategory, TOOL_Actions aAction, int aExtraParam ) : + TOOL_EVENT( TOOL_EventCategory aCategory, TOOL_Actions aAction, int aExtraParam, TOOL_ActionScope aScope = AS_GLOBAL ) : m_category( aCategory ), - m_actions( aAction ) + m_actions( aAction ), + m_scope( aScope ) { if( aCategory == TC_Mouse ) { @@ -153,16 +165,16 @@ public: } TOOL_EVENT( TOOL_EventCategory aCategory, TOOL_Actions aAction, - const std::string& aExtraParam ) : + const std::string& aExtraParam, TOOL_ActionScope aScope = AS_GLOBAL ) : m_category( aCategory ), m_actions( aAction ), + m_scope( aScope ), m_mouseButtons( 0 ) { if( aCategory == TC_Command ) m_commandStr = aExtraParam; } - TOOL_EventCategory Category() const { return m_category; @@ -285,6 +297,7 @@ private: TOOL_EventCategory m_category; TOOL_Actions m_actions; + TOOL_ActionScope m_scope; VECTOR2D m_mouseDelta; VECTOR2D m_mousePos; @@ -314,9 +327,15 @@ public: TOOL_EVENT_LIST() {}; TOOL_EVENT_LIST( const TOOL_EVENT& aSingleEvent ) { - m_events.push_back(aSingleEvent); + m_events.push_back( aSingleEvent ); } + /** + * Function Format() + * Returns information about event in form of a human-readable string. + * + * @return Event information. + */ const std::string Format() const; boost::optional Matches( const TOOL_EVENT &b ) const diff --git a/include/tool/tool_manager.h b/include/tool/tool_manager.h index cfafb609c0..c2a8310f5c 100644 --- a/include/tool/tool_manager.h +++ b/include/tool/tool_manager.h @@ -25,17 +25,14 @@ #ifndef __TOOL_MANAGER_H #define __TOOL_MANAGER_H -#include #include -#include #include -#include - #include #include #include +#include class TOOL_BASE; class CONTEXT_MENU; @@ -69,24 +66,61 @@ public: /** * Function InvokeTool() - * 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 + * Calls a tool by sending a tool activation event to tool of given ID. * + * @param aToolId is the ID number of the requested tool. * @return True if the requested tool was invoked successfully. */ bool InvokeTool( TOOL_ID aToolId ); - bool InvokeTool( const std::string& aName ); - template - void InvokeTool( const std::string& aName, const Parameters& aToolParams ); + /** + * Function InvokeTool() + * Calls a tool by sending a tool activation event to tool of given name. + * + * @param aToolName is the name of the requested tool. + * @return True if the requested tool was invoked successfully. + */ + bool InvokeTool( const std::string& aToolName ); + + /** + * Function RegisterAction() + * Registers an action that can be used to control tools (eg. invoke, trigger specific + * behaviours). + * + * @param aAction is the action to be registered. + */ + void RegisterAction( TOOL_ACTION* aAction ) + { + m_actionMgr.RegisterAction( aAction ); + } + + /** + * Function UnregisterAction() + * Unregisters an action, so it is no longer active. + * + * @param aAction is the action to be unregistered. + */ + void UnregisterAction( TOOL_ACTION* aAction ) + { + m_actionMgr.UnregisterAction( aAction ); + } /** * Function FindTool() - * Searches for a tool with given name or ID + * Searches for a tool with given ID. * - * @return Pointer to the request tool of NULL in case of failure. + * @param aId is the ID number of the requested tool. + * @return Pointer to the requested tool or NULL in case of failure. */ TOOL_BASE* FindTool( int aId ) const; + + /** + * Function FindTool() + * Searches for a tool with given name. + * + * @param aName is the name of the requested tool. + * @return Pointer to the requested tool or NULL in case of failure. + */ TOOL_BASE* FindTool( const std::string& aName ) const; /** @@ -170,19 +204,103 @@ private: typedef std::pair TRANSITION; void dispatchInternal( TOOL_EVENT& aEvent ); + + /** + * Function dispatchActivation() + * Checks if it is a valid activation event and invokes a proper tool. + * @param aEvent is an event to be tested. + * @return True if a tool was invoked, false otherwise. + */ + bool dispatchActivation( TOOL_EVENT& aEvent ); + + /** + * Function invokeTool() + * Invokes a tool by sending a proper event. + * @param aTool is the tool to be invoked. + */ + bool invokeTool( TOOL_BASE* aTool ); + + /** + * Function runTool() + * Makes a tool active, so it can receive events and react to them. Activated tool is pushed + * on the active tools stack, so the last activated tool receives events first. + * + * @param aToolId is the ID number of tool to be run. + */ + bool runTool( TOOL_ID aToolId ); + + /** + * Function runTool() + * Makes a tool active, so it can receive events and react to them. Activated tool is pushed + * on the active tools stack, so the last activated tool receives events first. + * + * @param aToolId is the name of tool to be run. + */ + bool runTool( const std::string& aName ); + + /** + * Function runTool() + * Makes a tool active, so it can receive events and react to them. Activated tool is pushed + * on the active tools stack, so the last activated tool receives events first. + * + * @param aToolId is the tool to be run. + */ + bool runTool( TOOL_BASE* aTool ); + + template + void invokeTool( const std::string& aName, const Parameters& aToolParams ); + + /** + * Function finishTool() + * Deactivates a tool and does the necessary clean up. + * + * @param aState is the state variable of the tool to be stopped. + */ void finishTool( TOOL_STATE* aState ); + /** + * Function isRegistered() + * Returns information about a tool registration status. + * + * @param aTool is the tool to be checked. + * @return True if the tool is in the registered tools list, false otherwise. + */ + bool isRegistered( TOOL_BASE* aTool ) const + { + return ( m_toolState.count( aTool ) > 0 ); + } + + /** + * Function isActive() + * Returns information about a tool activation status. + * + * @param aTool is the tool to be checked. + * @return True if the tool is on the active tools stack, false otherwise. + */ + bool isActive( TOOL_BASE* aTool ); + + /// Index of registered tools current states, associated by tools' objects. std::map m_toolState; + + /// Index of the registered tools current states, associated by tools' names. std::map m_toolNameIndex; + + /// Index of the registered tools current states, associated by tools' ID numbers. std::map m_toolIdIndex; + + /// Stack of the active tools std::deque m_activeTools; + ACTION_MANAGER m_actionMgr; EDA_ITEM* m_model; KiGfx::VIEW* m_view; KiGfx::VIEW_CONTROLS* m_viewControls; wxWindow* m_editFrame; + + /// Flag saying if the currently processed event should be passed to other tools. bool m_passEvent; + /// Pointer to the tool on the top of the active tools stack. TOOL_STATE* m_currentTool; }; diff --git a/include/view/view_group.h b/include/view/view_group.h index 7664060560..54dbd16521 100644 --- a/include/view/view_group.h +++ b/include/view/view_group.h @@ -42,7 +42,7 @@ namespace KiGfx class VIEW_GROUP : public VIEW_ITEM { public: - VIEW_GROUP( VIEW* aView ); + VIEW_GROUP( VIEW* aView = NULL ); virtual ~VIEW_GROUP(); /** diff --git a/pcbnew/router/router_tool.cpp b/pcbnew/router/router_tool.cpp index 7583dce6ce..5bc84708ac 100644 --- a/pcbnew/router/router_tool.cpp +++ b/pcbnew/router/router_tool.cpp @@ -42,11 +42,11 @@ using namespace KiGfx; using namespace std; using boost::optional; -static TOOL_ACTION ACT_AutoEndRoute ( "AutoEndRoute", SCOPE_CONTEXT, 'F' ); -static TOOL_ACTION ACT_PlaceVia ( "PlaceVia", SCOPE_CONTEXT, 'V' ); -static TOOL_ACTION ACT_OpenRouteOptions ( "OpenRouterOptions", SCOPE_CONTEXT, 'E' ); -static TOOL_ACTION ACT_SwitchPosture ( "SwitchPosture", SCOPE_CONTEXT, '/' ); -static TOOL_ACTION ACT_EndTrack ( "SwitchPosture", SCOPE_CONTEXT, WXK_END ); +static TOOL_ACTION ACT_AutoEndRoute ( "AutoEndRoute", AS_CONTEXT, 'F' ); +static TOOL_ACTION ACT_PlaceVia ( "PlaceVia", AS_CONTEXT, 'V' ); +static TOOL_ACTION ACT_OpenRouteOptions ( "OpenRouterOptions", AS_CONTEXT, 'E' ); +static TOOL_ACTION ACT_SwitchPosture ( "SwitchPosture", AS_CONTEXT, '/' ); +static TOOL_ACTION ACT_EndTrack ( "SwitchPosture", AS_CONTEXT, WXK_END ); ROUTER_TOOL::ROUTER_TOOL() : TOOL_INTERACTIVE( "pcbnew.InteractiveRouter" ) @@ -88,7 +88,7 @@ void ROUTER_TOOL::Reset() if(getView()) m_router->SetView( getView() ); - Go( &ROUTER_TOOL::Main, TOOL_EVENT( TC_Command, TA_ActivateTool, GetName() ) ); + Go( &ROUTER_TOOL::Main, TOOL_EVENT( TC_Command, TA_Action, GetName() ) ); } int ROUTER_TOOL::getDefaultWidth( int aNetCode ) diff --git a/pcbnew/tools/move_tool.cpp b/pcbnew/tools/move_tool.cpp index f7839211c1..5143f5b3a0 100644 --- a/pcbnew/tools/move_tool.cpp +++ b/pcbnew/tools/move_tool.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include "selection_tool.h" @@ -35,7 +35,8 @@ using namespace KiGfx; using boost::optional; MOVE_TOOL::MOVE_TOOL() : - TOOL_INTERACTIVE( "pcbnew.InteractiveMove" ), m_selectionTool( NULL ) + TOOL_INTERACTIVE( "pcbnew.InteractiveMove" ), m_selectionTool( NULL ), + m_activate( m_toolName, AS_GLOBAL, 'M', "Move", "Moves the selected item(s)" ) { } @@ -47,6 +48,8 @@ MOVE_TOOL::~MOVE_TOOL() void MOVE_TOOL::Reset() { + m_toolMgr->RegisterAction( &m_activate ); + // Find the selection tool, so they can cooperate TOOL_BASE* selectionTool = m_toolMgr->FindTool( std::string( "pcbnew.InteractiveSelection" ) ); @@ -60,8 +63,8 @@ void MOVE_TOOL::Reset() return; } - // the tool launches upon reception of activate ("pcbnew.InteractiveMove") - Go( &MOVE_TOOL::Main, TOOL_EVENT( TC_Command, TA_ActivateTool, GetName() ) ); + // the tool launches upon reception of action event ("pcbnew.InteractiveMove") + Go( &MOVE_TOOL::Main, m_activate.GetEvent() ); } @@ -69,13 +72,12 @@ int MOVE_TOOL::Main( TOOL_EVENT& aEvent ) { VECTOR2D dragPosition; bool dragging = false; - bool restore = false; + bool restore = false; // Should items' state be restored when finishing the tool? VIEW* view = m_toolMgr->GetView(); - std::set selection; - VIEW_GROUP items( view ); - view->Add( &items ); - m_toolMgr->GetViewControls()->SetSnapping( true ); + view->Add( &m_items ); + getViewControls()->SetSnapping( true ); + getViewControls()->SetAutoPan( true ); // Main loop: keep receiving events while( OPT_TOOL_EVENT evt = Wait() ) @@ -83,73 +85,68 @@ int MOVE_TOOL::Main( TOOL_EVENT& aEvent ) if( evt->IsCancel() ) { restore = true; - m_toolMgr->PassEvent(); break; // Finish } - if( evt->IsDrag( MB_Left ) ) + if( evt->IsMotion() || evt->IsDrag( MB_Left ) ) { if( dragging ) { - // Dragging is alre + // Dragging is already active VECTOR2D movement = ( evt->Position() - dragPosition ); - std::set::iterator it, it_end; - for( it = selection.begin(), it_end = selection.end(); it != it_end; ++it ) - { + + // so move all the selected items + for( it = m_selection.begin(), it_end = m_selection.end(); it != it_end; ++it ) (*it)->Move( wxPoint( movement.x, movement.y ) ); - } - items.ViewUpdate( VIEW_ITEM::GEOMETRY ); } else { - // Begin dragging - selection = m_selectionTool->GetSelection(); + // Prepare to drag + m_selection = m_selectionTool->GetSelection(); + if( m_selection.empty() ) + break; // there are no items to operate on std::set::iterator it; - for( it = selection.begin(); it != selection.end(); ++it ) + for( it = m_selection.begin(); it != m_selection.end(); ++it ) { - viewGroupAdd( *it, &items ); + // Gather all selected items into one VIEW_GROUP + viewGroupAdd( *it, &m_items ); - // but if a MODULE was selected, then we need to redraw all of it's parts + // Modules are treated in a special way - when they are moved, we have to + // move all the parts that make the module, not the module itself if( (*it)->Type() == PCB_MODULE_T ) { MODULE* module = static_cast( *it ); - // Move everything that belongs to the module + // Add everything that belongs to the module (besides the module itself) for( D_PAD* pad = module->Pads().GetFirst(); pad; pad = pad->Next() ) - viewGroupAdd( pad, &items ); + viewGroupAdd( pad, &m_items ); for( BOARD_ITEM* drawing = module->GraphicalItems().GetFirst(); drawing; drawing = drawing->Next() ) - viewGroupAdd( drawing, &items ); + viewGroupAdd( drawing, &m_items ); - viewGroupAdd( &module->Reference(), &items ); - viewGroupAdd( &module->Value(), &items ); + viewGroupAdd( &module->Reference(), &m_items ); + viewGroupAdd( &module->Value(), &m_items ); } - } - items.ViewUpdate( VIEW_ITEM::GEOMETRY ); dragging = true; } + m_items.ViewUpdate( VIEW_ITEM::GEOMETRY ); dragPosition = evt->Position(); } - else if( evt->Category() == TC_Mouse ) // Filter out other events - { - if( dragging ) - { - break; // Finish - } - } + else if( evt->IsMouseUp( MB_Left ) || evt->IsClick( MB_Left ) ) + break; // Finish } // Clean-up after movement std::deque::iterator it, it_end; if( restore ) { - // Movement has to be rollbacked, so restore previous state of items + // Movement has to be rollbacked, so restore the previous state of items for( it = m_itemsState.begin(), it_end = m_itemsState.end(); it != it_end; ++it ) it->Restore(); } @@ -164,9 +161,10 @@ int MOVE_TOOL::Main( TOOL_EVENT& aEvent ) } m_itemsState.clear(); - items.Clear(); - view->Remove( &items ); - m_toolMgr->GetViewControls()->SetSnapping( false ); + m_items.Clear(); + view->Remove( &m_items ); + getViewControls()->SetSnapping( false ); + getViewControls()->SetAutoPan( false ); return 0; } diff --git a/pcbnew/tools/move_tool.h b/pcbnew/tools/move_tool.h index 1645237527..2a3c4f7981 100644 --- a/pcbnew/tools/move_tool.h +++ b/pcbnew/tools/move_tool.h @@ -27,6 +27,7 @@ #include #include +#include class BOARD_ITEM; class SELECTION_TOOL; @@ -38,13 +39,8 @@ class VIEW_GROUP; /** * Class MOVE_TOOL - * /// TODO DOCS!! - * Our sample move tool: currently supports: - * - pick single objects (click LMB) - * - add objects to existing move (Shift+LMB) - * - draw move box (drag LMB) * - * WORK IN PROGRESS. CONSIDER AS A DEMO! + * Our sample move tool. Allows to move items selected by pcbnew.InteractiveSelection. */ class MOVE_TOOL : public TOOL_INTERACTIVE @@ -68,6 +64,7 @@ public: int Main( TOOL_EVENT& aEvent ); private: + /// Adds an item to the VIEW_GROUP that holds all moved items and displays them on the overlay void viewGroupAdd( BOARD_ITEM* aItem, KiGfx::VIEW_GROUP* aGroup ); /// Structure for (re)storing BOARD_ITEM state @@ -108,7 +105,17 @@ private: /// Selection tool used for obtaining selected items SELECTION_TOOL* m_selectionTool; + /// Stores the initial state of moved items (so it is possible to rollback changes) std::deque m_itemsState; + + /// Set of selected items (obtained from pcbnew. + std::set m_selection; + + /// VIEW_GROUP that helds currently moved items + KiGfx::VIEW_GROUP m_items; + + /// Register hotkey fot activation of the move tool + TOOL_ACTION m_activate; }; #endif diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp index 8559e81f6a..fd970863f9 100644 --- a/pcbnew/tools/selection_tool.cpp +++ b/pcbnew/tools/selection_tool.cpp @@ -3,6 +3,7 @@ * * Copyright (C) 2013 CERN * @author Tomasz Wlostowski + * @author Maciej Suminski * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -48,7 +49,8 @@ using namespace KiGfx; using boost::optional; SELECTION_TOOL::SELECTION_TOOL() : - TOOL_INTERACTIVE( "pcbnew.InteractiveSelection" ), m_multiple( false ) + TOOL_INTERACTIVE( "pcbnew.InteractiveSelection" ), m_multiple( false ), + m_activate( m_toolName, AS_GLOBAL, 'S', "Selection tool", "Allows to select items" ) { m_selArea = new SELECTION_AREA; } @@ -63,93 +65,63 @@ SELECTION_TOOL::~SELECTION_TOOL() void SELECTION_TOOL::Reset() { + m_toolMgr->RegisterAction( &m_activate ); m_selectedItems.clear(); - // The tool launches upon reception of activate ("pcbnew.InteractiveSelection") - Go( &SELECTION_TOOL::Main, TOOL_EVENT( TC_Command, TA_ActivateTool, GetName() ) ); + // The tool launches upon reception of action event ("pcbnew.InteractiveSelection") + Go( &SELECTION_TOOL::Main, m_activate.GetEvent() ); } int SELECTION_TOOL::Main( TOOL_EVENT& aEvent ) { - bool dragging = false; - bool allowMultiple = true; BOARD* board = getModel( PCB_T ); - if( !board ) - return 0; + wxASSERT( board != NULL ); // Main loop: keep receiving events while( OPT_TOOL_EVENT evt = Wait() ) { + // Should selected items be added to the current selection or + // become the new selection (discarding previously selected items) m_additive = evt->Modifier( MD_ModShift ); if( evt->IsCancel() ) { - if( !m_selectedItems.empty() ) + if( !m_selectedItems.empty() ) // Cancel event deselects items... clearSelection(); - else - break; // Finish + else // ...unless there is nothing selected + break; } // single click? Select single object if( evt->IsClick( MB_Left ) ) selectSingle( evt->Position() ); - // unlock the multiple selection box - if( evt->IsMouseUp( MB_Left ) ) - allowMultiple = true; - // drag with LMB? Select multiple objects (or at least draw a selection box) or drag them if( evt->IsDrag( MB_Left ) ) { - dragging = true; - getViewControls()->SetAutoPan( true ); - if( m_selectedItems.empty() || m_additive ) { // If nothings has been selected or user wants to select more // draw the selection box - if( allowMultiple ) - allowMultiple = !selectMultiple(); + selectMultiple(); } else { - bool runTool = false; - - // Check if dragging event started within the currently selected items bounding box - std::set::iterator it, it_end; - for( it = m_selectedItems.begin(), it_end = m_selectedItems.end(); - it != it_end; ++it ) + if( containsSelected( evt->Position() ) ) { - BOX2I itemBox = (*it)->ViewBBox(); - itemBox.Inflate( 500000 ); // Give some margin for gripping an item - - if( itemBox.Contains( evt->Position() ) ) - { - // Click event occurred within a selected item bounding box - // -> user wants to drag selected items - runTool = true; - break; - } - } - - if( runTool ) m_toolMgr->InvokeTool( "pcbnew.InteractiveMove" ); + Wait(); + } else + { clearSelection(); + } } } - else if( dragging ) - { - dragging = false; - getViewControls()->SetAutoPan( false ); - } } - // Restore the default settings - getViewControls()->SetAutoPan( false ); - return 0; } @@ -166,7 +138,7 @@ void SELECTION_TOOL::toggleSelection( BOARD_ITEM* aItem ) if( !m_additive ) clearSelection(); - // Prevent selection of invisible items + // Prevent selection of invisible or inactive items if( selectable( aItem ) ) { aItem->SetSelected(); @@ -272,17 +244,17 @@ BOARD_ITEM* SELECTION_TOOL::pickSmallestComponent( GENERAL_COLLECTOR* aCollector bool SELECTION_TOOL::selectMultiple() { - OPT_TOOL_EVENT evt; - VIEW* v = getView(); + VIEW* view = getView(); bool cancelled = false; - m_multiple = true; + m_multiple = true; // Multiple selection mode is active + getViewControls()->SetAutoPan( true ); // Those 2 lines remove the blink-in-the-random-place effect m_selArea->SetOrigin( VECTOR2I( 0, 0 ) ); m_selArea->SetEnd( VECTOR2I( 0, 0 ) ); - v->Add( m_selArea ); + view->Add( m_selArea ); - while( evt = Wait() ) + while( OPT_TOOL_EVENT evt = Wait() ) { if( evt->IsCancel() ) { @@ -307,17 +279,17 @@ bool SELECTION_TOOL::selectMultiple() // End drawing a selection box m_selArea->ViewSetVisible( false ); - // Mark items within a box as selected + // Mark items within the selection box as selected std::vector selectedItems; BOX2I selectionBox = m_selArea->ViewBBox(); + view->Query( selectionBox, selectedItems ); // Get the list of selected items - v->Query( selectionBox, selectedItems ); std::vector::iterator it, it_end; for( it = selectedItems.begin(), it_end = selectedItems.end(); it != it_end; ++it ) { BOARD_ITEM* item = static_cast( it->first ); - // Add only those items which are visible and fully within the selection box + // Add only those items that are visible and fully within the selection box if( selectable( item ) && selectionBox.Contains( item->ViewBBox() ) ) { item->SetSelected(); @@ -328,8 +300,9 @@ bool SELECTION_TOOL::selectMultiple() } } - v->Remove( m_selArea ); - m_multiple = false; + view->Remove( m_selArea ); + m_multiple = false; // Multiple selection mode is inactive + getViewControls()->SetAutoPan( false ); return cancelled; } @@ -359,12 +332,12 @@ BOARD_ITEM* SELECTION_TOOL::disambiguationMenu( GENERAL_COLLECTOR* aCollector ) { if( evt->Action() == TA_ContextMenuUpdate ) { - // User has pointed an item, so show it in a different way if( current ) current->ClearBrightened(); int id = *evt->GetCommandId(); + // User has pointed an item, so show it in a different way if( id >= 0 ) { current = ( *aCollector )[id]; @@ -389,6 +362,7 @@ BOARD_ITEM* SELECTION_TOOL::disambiguationMenu( GENERAL_COLLECTOR* aCollector ) break; } + // Draw a mark to show which item is available to be selected if( current && current->IsBrightened() ) { brightBox.reset( new BRIGHT_BOX( current ) ); @@ -474,3 +448,21 @@ bool SELECTION_TOOL::selectable( const BOARD_ITEM* aItem ) // All other items are selected only if the layer on which they exist is visible return board->IsLayerVisible( aItem->GetLayer() ); } + + +bool SELECTION_TOOL::containsSelected( const VECTOR2I& aPoint ) const +{ + // Check if the point is located within any of the currently selected items bounding boxes + std::set::iterator it, it_end; + for( it = m_selectedItems.begin(), it_end = m_selectedItems.end(); + it != it_end; ++it ) + { + BOX2I itemBox = (*it)->ViewBBox(); + itemBox.Inflate( 500000 ); // Give some margin for gripping an item + + if( itemBox.Contains( aPoint ) ) + return true; + } + + return false; +} diff --git a/pcbnew/tools/selection_tool.h b/pcbnew/tools/selection_tool.h index c76743d266..622a9bcfa6 100644 --- a/pcbnew/tools/selection_tool.h +++ b/pcbnew/tools/selection_tool.h @@ -3,6 +3,7 @@ * * Copyright (C) 2013 CERN * @author Tomasz Wlostowski + * @author Maciej Suminski * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,6 +31,7 @@ #include #include +#include class SELECTION_AREA; class BOARD_ITEM; @@ -42,6 +44,9 @@ class GENERAL_COLLECTOR; * - pick single objects (click LMB) * - add objects to existing selection (Shift+LMB) * - draw selection box (drag LMB) + * - handles MODULEs properly (ie. selects either MODULE or its PADs, TEXTs, etc.) + * - takes into account high-contrast & layer visibility settings + * - invokes InteractiveMove tool when user starts to drag selected items */ class SELECTION_TOOL : public TOOL_INTERACTIVE @@ -131,10 +136,18 @@ private: */ bool selectable( const BOARD_ITEM* aItem ); + /** + * Function containsSelected() + * Checks if the given point is placed within any of selected items' bounding box. + * + * @return True if the given point is contained in any of selected items' bouding box. + */ + bool containsSelected( const VECTOR2I& aPoint ) const; + /// Container storing currently selected items std::set m_selectedItems; - /// Visual representation of selection area + /// Visual representation of selection box SELECTION_AREA* m_selArea; /// Menu shown in case of selection ambiguity @@ -145,6 +158,9 @@ private: /// Flag saying if multiple selection mode is active bool m_multiple; + + /// Register hotkey fot activation of the selection tool + TOOL_ACTION m_activate; }; #endif