Hot keys specific to a tool have priority over global hot keys (TOOL_ACTION scope: AS_GLOBAL/AS_CONTEXT is finally handled properly).

This commit is contained in:
Maciej Suminski 2014-04-09 17:05:05 +02:00
parent 371b2f7c0e
commit 50b202fe99
7 changed files with 114 additions and 32 deletions

View File

@ -26,6 +26,7 @@
#include <tool/tool_manager.h>
#include <tool/tool_event.h>
#include <tool/tool_action.h>
#include <boost/foreach.hpp>
#include <cassert>
ACTION_MANAGER::ACTION_MANAGER( TOOL_MANAGER* aToolManager ) :
@ -43,7 +44,12 @@ ACTION_MANAGER::~ACTION_MANAGER()
void ACTION_MANAGER::RegisterAction( TOOL_ACTION* aAction )
{
assert( aAction->GetId() == -1 ); // Check if the TOOL_ACTION was not registered before
// Check if the TOOL_ACTION was not registered before
assert( aAction->GetId() == -1 );
// TOOL_ACTIONs are supposed to be named [appName.]toolName.actionName (with dots between)
// action name without specifying at least toolName is not valid
assert( aAction->GetName().find( '.', 0 ) != std::string::npos );
assert( m_actionNameIndex.find( aAction->m_name ) == m_actionNameIndex.end() );
assert( m_actionIdIndex.find( aAction->m_id ) == m_actionIdIndex.end() );
@ -53,13 +59,7 @@ void ACTION_MANAGER::RegisterAction( TOOL_ACTION* aAction )
m_actionIdIndex[aAction->m_id] = aAction;
if( aAction->HasHotKey() )
{
// Duplication of hot keys leads to unexpected behaviour
// The right way to change a hotkey is to use ACTION_MANAGER::ClearHotKey() first
assert( m_actionHotKeys.find( aAction->m_currentHotKey ) == m_actionHotKeys.end() );
m_actionHotKeys[aAction->m_currentHotKey] = aAction;
}
m_actionHotKeys[aAction->m_currentHotKey].push_back( aAction );
}
@ -72,7 +72,15 @@ void ACTION_MANAGER::UnregisterAction( TOOL_ACTION* aAction )
aAction->setId( -1 );
if( aAction->HasHotKey() )
m_actionHotKeys.erase( aAction->m_currentHotKey );
{
std::list<TOOL_ACTION*>& actions = m_actionHotKeys[aAction->m_currentHotKey];
std::list<TOOL_ACTION*>::iterator action = std::find( actions.begin(), actions.end(), aAction );
if( action != actions.end() )
actions.erase( action );
else
assert( false );
}
}
@ -107,18 +115,52 @@ void ACTION_MANAGER::RunAction( const TOOL_ACTION* aAction ) const
bool ACTION_MANAGER::RunHotKey( int aHotKey ) const
{
std::map<int, TOOL_ACTION*>::const_iterator it = m_actionHotKeys.find( aHotKey );
HOTKEY_LIST::const_iterator it = m_actionHotKeys.find( aHotKey );
if( it == m_actionHotKeys.end() )
return false; // no appropriate action found for the hotkey
RunAction( it->second );
const std::list<TOOL_ACTION*>& actions = it->second;
// Choose the action that has the highest priority on the active tools stack
// If there is none, run the global action associated with the hot key
int highestPriority = -1, priority = -1;
const TOOL_ACTION* context = NULL; // pointer to context action of the highest priority tool
const TOOL_ACTION* global = NULL; // pointer to global action, if there is no context action
BOOST_FOREACH( const TOOL_ACTION* action, actions )
{
if( action->GetScope() == AS_GLOBAL )
{
// Store the global action for the hot key in case there was no possible
// context actions to run
assert( global == NULL ); // there should be only one global action per hot key
global = action;
continue;
}
TOOL_BASE* tool = m_toolMgr->FindTool( action->GetToolName() );
if( tool )
{
priority = m_toolMgr->GetPriority( tool->GetId() );
if( priority >= 0 && priority > highestPriority )
{
highestPriority = priority;
context = action;
}
}
}
if( !global && !context ) // currently there is no valid action to run
return false;
if( context )
RunAction( context );
else if( global )
RunAction( global );
return true;
}
void ACTION_MANAGER::ClearHotKey( int aHotKey )
{
m_actionHotKeys.erase( aHotKey );
}

View File

@ -301,6 +301,25 @@ void TOOL_MANAGER::ResetTools( TOOL_BASE::RESET_REASON aReason )
}
int TOOL_MANAGER::GetPriority( int aToolId ) const
{
int priority = 0;
for( std::deque<int>::const_iterator it = m_activeTools.begin(),
itEnd = m_activeTools.end(); it != itEnd; ++it )
{
std::cout << FindTool( *it )->GetName() << std::endl;
if( *it == aToolId )
return priority;
++priority;
}
return -1;
}
void TOOL_MANAGER::ScheduleNextState( TOOL_BASE* aTool, TOOL_STATE_FUNC& aHandler,
const TOOL_EVENT_LIST& aConditions )
{

View File

@ -25,6 +25,7 @@
#ifndef ACTION_MANAGER_H_
#define ACTION_MANAGER_H_
#include <list>
#include <map>
#include <string>
@ -97,13 +98,6 @@ public:
*/
bool RunHotKey( int aHotKey ) const;
/**
* Function ClearHotKey()
* Removes an action associated with a hotkey.
* @param aHotKey is the hotkey to be cleared.
*/
void ClearHotKey( int aHotKey );
private:
///> Tool manager needed to run actions
TOOL_MANAGER* m_toolMgr;
@ -115,7 +109,8 @@ private:
std::map<std::string, TOOL_ACTION*> m_actionNameIndex;
///> Map for indexing actions by their hotkeys
std::map<int, TOOL_ACTION*> m_actionHotKeys;
typedef std::map<int, std::list<TOOL_ACTION*> > HOTKEY_LIST;
HOTKEY_LIST m_actionHotKeys;
};
#endif /* ACTION_MANAGER_H_ */

View File

@ -171,6 +171,21 @@ public:
m_menuDescription = aDescription;
}
TOOL_ACTION_SCOPE GetScope() const
{
return m_scope;
}
/**
* Returns name of the tool associated with the action. It is basically the action name
* stripped of the last part (e.g. for "pcbnew.InteractiveDrawing.drawCircle" it is
* "pcbnew.InteractiveDrawing").
*/
std::string GetToolName() const
{
return m_name.substr( 0, m_name.rfind( '.' ) );
}
private:
friend class ACTION_MANAGER;

View File

@ -206,6 +206,16 @@ public:
return FindTool( GetCurrentToolId() );
}
/**
* Returns priority of a given tool. Higher number means that the tool is closer to the
* beginning of the active tools queue (i.e. receives events earlier, tools with lower
* priority receive events later).
* @param aToolId is the id of queried tool.
* @return The priority of a given tool. If returned number is negative, then it means that
* the tool id is invalid or the tool is not active.
*/
int GetPriority( int aToolId ) const;
/**
* Defines a state transition - the events that cause a given handler method in the tool
* to be called. Called by TOOL_INTERACTIVE::Go(). May be called from a coroutine context.

View File

@ -45,11 +45,11 @@
using namespace KIGFX;
using boost::optional;
static TOOL_ACTION ACT_AutoEndRoute( "AutoEndRoute", AS_CONTEXT, 'G' );
static TOOL_ACTION ACT_PlaceVia( "PlaceVia", AS_CONTEXT, 'V' );
static TOOL_ACTION ACT_OpenRouteOptions( "OpenRouterOptions", AS_CONTEXT, 'Y' );
static TOOL_ACTION ACT_SwitchPosture( "SwitchPosture", AS_CONTEXT, '/' );
static TOOL_ACTION ACT_EndTrack( "EndTrack", AS_CONTEXT, WXK_END );
static TOOL_ACTION ACT_AutoEndRoute( "pcbnew.InteractiveRouter.AutoEndRoute", AS_CONTEXT, 'G' );
static TOOL_ACTION ACT_PlaceVia( "pcbnew.InteractiveRouter.PlaceVia", AS_CONTEXT, 'V' );
static TOOL_ACTION ACT_OpenRouteOptions( "pcbnew.InteractiveRouter.OpenRouterOptions", AS_CONTEXT, 'T' );
static TOOL_ACTION ACT_SwitchPosture( "pcbnew.InteractiveRouter.SwitchPosture", AS_CONTEXT, '/' );
static TOOL_ACTION ACT_EndTrack( "pcbnew.InteractiveRouter.EndTrack", AS_CONTEXT, WXK_END );
ROUTER_TOOL::ROUTER_TOOL() :
TOOL_INTERACTIVE( "pcbnew.InteractiveRouter" )

View File

@ -43,11 +43,11 @@ TOOL_ACTION COMMON_ACTIONS::editActivate( "pcbnew.InteractiveEdit",
"Move", "Moves the selected item(s)" );
TOOL_ACTION COMMON_ACTIONS::rotate( "pcbnew.rotate",
AS_CONTEXT, 'R',
AS_GLOBAL, 'R',
"Rotate", "Rotates selected item(s)" );
TOOL_ACTION COMMON_ACTIONS::flip( "pcbnew.flip",
AS_CONTEXT, 'F',
AS_GLOBAL, 'F',
"Flip", "Flips selected item(s)" );
TOOL_ACTION COMMON_ACTIONS::remove( "pcbnew.InteractiveEdit.remove",
@ -58,6 +58,7 @@ TOOL_ACTION COMMON_ACTIONS::properties( "pcbnew.InteractiveEdit.properties",
AS_GLOBAL, 'E',
"Properties...", "Displays properties window" );
// Drawing tool actions
TOOL_ACTION COMMON_ACTIONS::drawLine( "pcbnew.InteractiveDrawing.line",
AS_GLOBAL, 0,