KiCad PCB EDA Suite
action_manager.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2013 CERN
5  * Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors.
6  * @author Maciej Suminski <maciej.suminski@cern.ch>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 #include <tool/action_manager.h>
27 #include <tool/tool_manager.h>
28 #include <tool/tool_action.h>
29 #include <draw_frame.h>
30 
31 #include <hotkeys_basic.h>
32 #include <cctype>
33 
35  m_toolMgr( aToolManager )
36 {
37  // Register known actions
38  std::list<TOOL_ACTION*>& actionList = GetActionList();
39 
40  for( TOOL_ACTION* action : actionList )
41  {
42  if( action->m_id == -1 )
43  action->m_id = MakeActionId( action->m_name );
44 
45  RegisterAction( new TOOL_ACTION( *action ) );
46  }
47 }
48 
49 
51 {
52  while( !m_actionNameIndex.empty() )
53  {
54  TOOL_ACTION* action = m_actionNameIndex.begin()->second;
55  UnregisterAction( action );
56  delete action;
57  }
58 }
59 
60 
62 {
63  // TOOL_ACTIONs are supposed to be named [appName.]toolName.actionName (with dots between)
64  // action name without specifying at least toolName is not valid
65  wxASSERT( aAction->GetName().find( '.', 0 ) != std::string::npos );
66 
67  // TOOL_ACTIONs must have unique names & ids
68  wxASSERT( m_actionNameIndex.find( aAction->m_name ) == m_actionNameIndex.end() );
69 
70  m_actionNameIndex[aAction->m_name] = aAction;
71 }
72 
73 
75 {
76  m_actionNameIndex.erase( aAction->m_name );
77  int hotkey = GetHotKey( *aAction );
78 
79  if( hotkey )
80  {
81  std::list<TOOL_ACTION*>& actions = m_actionHotKeys[hotkey];
82  auto action = std::find( actions.begin(), actions.end(), aAction );
83 
84  if( action != actions.end() )
85  actions.erase( action );
86  }
87 }
88 
89 
90 int ACTION_MANAGER::MakeActionId( const std::string& aActionName )
91 {
92  static int currentActionId = 1;
93 
94  return currentActionId++;
95 }
96 
97 
98 TOOL_ACTION* ACTION_MANAGER::FindAction( const std::string& aActionName ) const
99 {
100  std::map<std::string, TOOL_ACTION*>::const_iterator it = m_actionNameIndex.find( aActionName );
101 
102  if( it != m_actionNameIndex.end() )
103  return it->second;
104 
105  return NULL;
106 }
107 
108 
109 bool ACTION_MANAGER::RunHotKey( int aHotKey ) const
110 {
111  int key = aHotKey & ~MD_MODIFIER_MASK;
112  int mod = aHotKey & MD_MODIFIER_MASK;
113 
114  if( key >= 'a' && key <= 'z' )
115  key = std::toupper( key );
116 
117  HOTKEY_LIST::const_iterator it = m_actionHotKeys.find( key | mod );
118 
119  // If no luck, try without Shift, to handle keys that require it
120  // e.g. to get ? you need to press Shift+/ without US keyboard layout
121  // Hardcoding ? as Shift+/ is a bad idea, as on another layout you may need to press a
122  // different combination
123  if( it == m_actionHotKeys.end() )
124  {
125  it = m_actionHotKeys.find( key | ( mod & ~MD_SHIFT ) );
126 
127  if( it == m_actionHotKeys.end() )
128  return false; // no appropriate action found for the hotkey
129  }
130 
131  const std::list<TOOL_ACTION*>& actions = it->second;
132 
133  // Choose the action that has the highest priority on the active tools stack
134  // If there is none, run the global action associated with the hot key
135  int highestPriority = -1, priority = -1;
136  const TOOL_ACTION* context = NULL; // pointer to context action of the highest priority tool
137  const TOOL_ACTION* global = NULL; // pointer to global action, if there is no context action
138 
139  for( const TOOL_ACTION* action : actions )
140  {
141  if( action->GetScope() == AS_GLOBAL )
142  {
143  // Store the global action for the hot key in case there was no possible
144  // context actions to run
145  wxASSERT( global == NULL ); // there should be only one global action per hot key
146  global = action;
147  continue;
148  }
149 
150  TOOL_BASE* tool = m_toolMgr->FindTool( action->GetToolName() );
151 
152  if( tool )
153  {
154  // Choose the action that goes to the tool with highest priority
155  // (i.e. is on the top of active tools stack)
156  priority = m_toolMgr->GetPriority( tool->GetId() );
157 
158  if( priority >= 0 && priority > highestPriority )
159  {
160  highestPriority = priority;
161  context = action;
162  }
163  }
164  }
165 
166  if( context )
167  {
168  m_toolMgr->RunAction( *context, true );
169  return true;
170  }
171  else if( global )
172  {
173  m_toolMgr->RunAction( *global, true );
174  return true;
175  }
176 
177  return false;
178 }
179 
180 
181 int ACTION_MANAGER::GetHotKey( const TOOL_ACTION& aAction ) const
182 {
183  std::map<int, int>::const_iterator it = m_hotkeys.find( aAction.GetId() );
184 
185  if( it == m_hotkeys.end() )
186  return 0;
187 
188  return it->second;
189 }
190 
191 
193 {
194  m_actionHotKeys.clear();
195  m_hotkeys.clear();
196 
197  for( const auto& actionName : m_actionNameIndex )
198  {
199  TOOL_ACTION* action = actionName.second;
200  int hotkey = processHotKey( action );
201 
202  if( hotkey <= 0 )
203  continue;
204 
205  // Second hotkey takes priority as defaults are loaded first and updates
206  // are loaded after
207  if( action->GetScope() == AS_GLOBAL && m_actionHotKeys.count( hotkey ) )
208  {
209  for( auto it = m_actionHotKeys[hotkey].begin();
210  it != m_actionHotKeys[hotkey].end(); )
211  {
212  if( (*it)->GetScope() == AS_GLOBAL )
213  {
214  // Users are free to define colliding hotkeys, but we want to know if
215  // our default set has any collisions.
216  wxMessageBox( wxString::Format(
217  "Duplicate hotkey definitions for '%s': %s and %s",
218  KeyNameFromKeyCode( hotkey ),
219  actionName.first,
220  m_actionHotKeys[hotkey].front()->GetName() ) );
221 
222  it = m_actionHotKeys[hotkey].erase( it );
223  }
224  else
225  it++;
226  }
227  }
228 
229  m_actionHotKeys[hotkey].push_back( action );
230  m_hotkeys[action->GetId()] = hotkey;
231 
232  }
233 }
234 
235 
237 {
238  int hotkey = aAction->getDefaultHotKey();
239 
240  if( ( hotkey & TOOL_ACTION::LEGACY_HK ) )
241  {
242  hotkey = hotkey & ~TOOL_ACTION::LEGACY_HK; // it leaves only HK_xxx identifier
243 
244  auto frame = dynamic_cast<EDA_DRAW_FRAME*>( m_toolMgr->GetEditFrame() );
245  EDA_HOTKEY* hk_desc = nullptr;
246 
247  if( frame )
248  hk_desc = frame->GetHotKeyDescription( hotkey );
249 
250  if( hk_desc )
251  {
252  hotkey = hk_desc->m_KeyCode;
253 
254  // Convert modifiers to the ones used by the Tool Framework
255  if( hotkey & GR_KB_CTRL )
256  {
257  hotkey &= ~GR_KB_CTRL;
258  hotkey |= MD_CTRL;
259  }
260 
261  if( hotkey & GR_KB_ALT )
262  {
263  hotkey &= ~GR_KB_ALT;
264  hotkey |= MD_ALT;
265  }
266 
267  if( hotkey & GR_KB_SHIFT )
268  {
269  hotkey &= ~GR_KB_SHIFT;
270  hotkey |= MD_SHIFT;
271  }
272  }
273  else
274  {
275  hotkey = 0;
276  }
277  }
278 
279  return hotkey;
280 }
#define GR_KB_ALT
std::map< int, int > m_hotkeys
Quick action<->hot key lookup
int GetPriority(int aToolId) const
Returns priority of a given tool.
static int MakeActionId(const std::string &aActionName)
Generates an unique ID from for an action with given name.
void UpdateHotKeys()
Function UpdateHotKeys() Updates TOOL_ACTIONs hot key assignment according to the current frame's Hot...
TOOL_ID GetId() const
Function GetId() Returns the unique identifier of the tool.
Definition: tool_base.h:122
EDA_DRAW_FRAME * GetEditFrame() const
Definition: tool_manager.h:267
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Function RunAction() Runs the specified action.
Definition: tool_manager.h:125
#define GR_KB_CTRL
~ACTION_MANAGER()
Destructor.
TOOL_BASE * FindTool(int aId) const
Function FindTool() Searches for a tool with given ID.
static std::list< TOOL_ACTION * > & GetActionList()
Function GetActionList() Returns list of TOOL_ACTIONs.
std::string m_name
Name of the action (convention is: app.[tool.]action.name)
Definition: tool_action.h:183
TOOL_ACTION * FindAction(const std::string &aActionName) const
Function FindAction() Finds an action with a given name (if there is one available).
bool RunHotKey(int aHotKey) const
Function RunHotKey() Runs an action associated with a hotkey (if there is one available).
Class TOOL_MANAGER.
Definition: tool_manager.h:49
TOOL_ACTION_SCOPE GetScope() const
Definition: tool_action.h:125
int GetHotKey(const TOOL_ACTION &aAction) const
Function GetHotKey() Returns the hot key associated with a given action or 0 if there is none.
static const int LEGACY_HK
Flag to determine the hot key settings is not a particular key, but a reference to legacy hot key set...
Definition: tool_action.h:211
ACTION_MANAGER(TOOL_MANAGER *aToolManager)
Constructor.
#define GR_KB_SHIFT
All active tools
Definition: tool_event.h:143
void UnregisterAction(TOOL_ACTION *aAction)
Function UnregisterAction() Removes a tool action from the manager and makes it unavailable for furth...
std::map< std::string, TOOL_ACTION * > m_actionNameIndex
Map for indexing actions by their names
int processHotKey(TOOL_ACTION *aAction)
Resolves a reference to legacy hot key settings to a particular hot key.
HOTKEY_LIST m_actionHotKeys
TOOL_MANAGER * m_toolMgr
Tool manager needed to run actions
void RegisterAction(TOOL_ACTION *aAction)
Function RegisterAction() Adds a tool action to the manager and sets it up.
int GetId() const
Function GetId() Returns the unique id of the TOOL_ACTION object.
Definition: tool_action.h:85
class EDA_HOTKEY is a class to handle hot key commands.
Definition: hotkeys_basic.h:59
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
Class TOOL_BASE.
Definition: tool_base.h:68
Class TOOL_ACTION.
Definition: tool_action.h:46
wxString KeyNameFromKeyCode(int aKeycode, bool *aIsFound)
Function KeyNameFromKeyCode return the key name from the key code Only some wxWidgets key values are ...
Some functions to handle hotkeys in KiCad.
int getDefaultHotKey()
Returns the hot key assigned in the object definition.
Definition: tool_action.h:177
#define mod(a, n)
Definition: greymap.cpp:24
const std::string & GetName() const
Function GetName() Returns name of the action.
Definition: tool_action.h:73