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 <eda_draw_frame.h>
27 #include <tool/action_manager.h>
28 #include <tool/tool_action.h>
29 #include <tool/tool_manager.h>
30 #include <trace_helpers.h>
31 #include <wx/log.h>
32 
33 #include <hotkeys_basic.h>
34 #include <cctype>
35 
37  m_toolMgr( aToolManager )
38 {
39  // Register known actions
40  std::list<TOOL_ACTION*>& actionList = GetActionList();
41 
42  for( TOOL_ACTION* action : actionList )
43  {
44  if( action->m_id == -1 )
45  action->m_id = MakeActionId( action->m_name );
46 
47  RegisterAction( action );
48  }
49 }
50 
51 
53 {
54 }
55 
56 
58 {
59  // TOOL_ACTIONs are supposed to be named [appName.]toolName.actionName (with dots between)
60  // action name without specifying at least toolName is not valid
61  wxASSERT( aAction->GetName().find( '.', 0 ) != std::string::npos );
62 
63  // TOOL_ACTIONs must have unique names & ids
64  wxASSERT( m_actionNameIndex.find( aAction->m_name ) == m_actionNameIndex.end() );
65 
66  m_actionNameIndex[aAction->m_name] = aAction;
67 }
68 
69 
70 void ACTION_MANAGER::SetConditions( const TOOL_ACTION& aAction, const ACTION_CONDITIONS& aConditions )
71 {
72  // Remove any existing handlers with the old conditions to ensure the UI layer doesn't have stale data
73  if( m_toolMgr )
75 
76  m_uiConditions[aAction.GetId()] = aConditions;
77 
78  wxLogTrace( kicadTraceToolStack,
79  "ACTION_MANAGER::SetConditions: Registering conditions for ID %d - %s",
80  aAction.GetId(), aAction.GetName() );
81 
82  // Register a new handler with the new conditions
83  if( m_toolMgr )
84  m_toolMgr->GetToolHolder()->RegisterUIUpdateHandler( aAction, aConditions );
85 }
86 
87 
89 {
90  const auto it = m_uiConditions.find( aAction.GetId() );
91 
92  // If the action doesn't have something registered, then return null
93  if( it == m_uiConditions.end() )
94  return nullptr;
95  else
96  return &it->second;
97 }
98 
99 
100 int ACTION_MANAGER::MakeActionId( const std::string& aActionName )
101 {
102  static int currentActionId = 1;
103 
104  return currentActionId++;
105 }
106 
107 
108 TOOL_ACTION* ACTION_MANAGER::FindAction( const std::string& aActionName ) const
109 {
110  std::map<std::string, TOOL_ACTION*>::const_iterator it = m_actionNameIndex.find( aActionName );
111 
112  if( it != m_actionNameIndex.end() )
113  return it->second;
114 
115  return NULL;
116 }
117 
118 
119 bool ACTION_MANAGER::RunHotKey( int aHotKey ) const
120 {
121  int key = aHotKey & ~MD_MODIFIER_MASK;
122  int mod = aHotKey & MD_MODIFIER_MASK;
123 
124  if( key >= 'a' && key <= 'z' )
125  key = std::toupper( key );
126 
127  wxLogTrace( kicadTraceToolStack, "ACTION_MANAGER::RunHotKey Key: %s",
128  KeyNameFromKeyCode( aHotKey ) );
129 
130  HOTKEY_LIST::const_iterator it = m_actionHotKeys.find( key | mod );
131 
132  // If no luck, try without Shift, to handle keys that require it
133  // e.g. to get ? you need to press Shift+/ without US keyboard layout
134  // Hardcoding ? as Shift+/ is a bad idea, as on another layout you may need to press a
135  // different combination
136  if( it == m_actionHotKeys.end() )
137  {
138  wxLogTrace( kicadTraceToolStack,
139  "ACTION_MANAGER::RunHotKey No actions found, searching with key: %s",
140  KeyNameFromKeyCode( key | ( mod & ~MD_SHIFT ) ) );
141 
142  it = m_actionHotKeys.find( key | ( mod & ~MD_SHIFT ) );
143 
144  if( it == m_actionHotKeys.end() )
145  return false; // no appropriate action found for the hotkey
146  }
147 
148  const std::list<TOOL_ACTION*>& actions = it->second;
149 
150  // Choose the action that has the highest priority on the active tools stack
151  // If there is none, run the global action associated with the hot key
152  int highestPriority = -1, priority = -1;
153  const TOOL_ACTION* context = NULL; // pointer to context action of the highest priority tool
154  std::vector<const TOOL_ACTION*> global; // pointers to global actions
155  // if there is no context action
156 
157  for( const TOOL_ACTION* action : actions )
158  {
159  if( action->GetScope() == AS_GLOBAL )
160  {
161  // Store the global action in case there are no context actions to run
162  global.emplace_back( action );
163  continue;
164  }
165 
166  TOOL_BASE* tool = m_toolMgr->FindTool( action->GetToolName() );
167 
168  if( tool )
169  {
170  // Choose the action that goes to the tool with highest priority
171  // (i.e. is on the top of active tools stack)
172  priority = m_toolMgr->GetPriority( tool->GetId() );
173 
174  if( priority >= 0 && priority > highestPriority )
175  {
176  highestPriority = priority;
177  context = action;
178  }
179  }
180  }
181 
182  // Get the selection to use to test if the action is enabled
184 
185  if( context )
186  {
187  bool runAction = true;
188 
189  if( const ACTION_CONDITIONS* aCond = GetCondition( *context ) )
190  runAction = aCond->enableCondition( sel );
191 
192  wxLogTrace( kicadTraceToolStack,
193  "ACTION_MANAGER::RunHotKey %s context action: %s for hotkey %s",
194  runAction ? "Running" : "Not running",
195  context->GetName(),
196  KeyNameFromKeyCode( aHotKey ) );
197 
198  if( runAction )
199  return m_toolMgr->RunAction( *context, true );
200  }
201  else if( !global.empty() )
202  {
203  for( auto act : global )
204  {
205  bool runAction = true;
206 
207  if( const ACTION_CONDITIONS* aCond = GetCondition( *act ) )
208  runAction = aCond->enableCondition( sel );
209 
210  wxLogTrace( kicadTraceToolStack,
211  "ACTION_MANAGER::RunHotKey %s global action: %s for hotkey %s",
212  runAction ? "Running" : "Not running",
213  act->GetName(),
214  KeyNameFromKeyCode( aHotKey ) );
215 
216  if( runAction && m_toolMgr->RunAction( *act, true ) )
217  return true;
218  }
219  }
220 
221  wxLogTrace( kicadTraceToolStack,
222  "ACTION_MANAGER::RunHotKey No action found for key %s",
223  KeyNameFromKeyCode( aHotKey ) );
224 
225  return false;
226 }
227 
228 
229 const std::map<std::string, TOOL_ACTION*>& ACTION_MANAGER::GetActions() const
230 {
231  return m_actionNameIndex;
232 }
233 
234 
235 int ACTION_MANAGER::GetHotKey( const TOOL_ACTION& aAction ) const
236 {
237  std::map<int, int>::const_iterator it = m_hotkeys.find( aAction.GetId() );
238 
239  if( it == m_hotkeys.end() )
240  return 0;
241 
242  return it->second;
243 }
244 
245 
246 void ACTION_MANAGER::UpdateHotKeys( bool aFullUpdate )
247 {
248  std::map<std::string, int> legacyHotKeyMap;
249  std::map<std::string, int> userHotKeyMap;
250 
251  m_actionHotKeys.clear();
252  m_hotkeys.clear();
253 
254  if( aFullUpdate && m_toolMgr->GetToolHolder() )
255  {
257  ReadHotKeyConfig( wxEmptyString, userHotKeyMap );
258  }
259 
260  for( const auto& ii : m_actionNameIndex )
261  {
262  TOOL_ACTION* action = ii.second;
263  int hotkey = 0;
264 
265  if( aFullUpdate )
266  hotkey = processHotKey( action, legacyHotKeyMap, userHotKeyMap );
267  else
268  hotkey = action->GetHotKey();
269 
270  if( hotkey > 0 )
271  m_actionHotKeys[hotkey].push_back( action );
272 
273  m_hotkeys[action->GetId()] = hotkey;
274  }
275 }
276 
277 
278 int ACTION_MANAGER::processHotKey( TOOL_ACTION* aAction, std::map<std::string, int> aLegacyMap,
279  std::map<std::string, int> aHotKeyMap )
280 {
281  aAction->m_hotKey = aAction->m_defaultHotKey;
282 
283  if( !aAction->m_legacyName.empty() && aLegacyMap.count( aAction->m_legacyName ) )
284  aAction->SetHotKey( aLegacyMap[ aAction->m_legacyName ] );
285 
286  if( aHotKeyMap.count( aAction->m_name ) )
287  aAction->SetHotKey( aHotKeyMap[ aAction->m_name ] );
288 
289  return aAction->m_hotKey;
290 }
std::map< int, int > m_hotkeys
const std::map< std::string, TOOL_ACTION * > & GetActions() const
Get a list of currently-registered actions mapped by their name.
int GetPriority(int aToolId) const
Return priority of a given tool.
static int MakeActionId(const std::string &aActionName)
Generate an unique ID from for an action with given name.
TOOL_ID GetId() const
Return the unique identifier of the tool.
Definition: tool_base.h:120
virtual void UnregisterUIUpdateHandler(const TOOL_ACTION &aAction)
Unregister a UI handler for an action that was registered using RegisterUIUpdateHandler.
Definition: tools_holder.h:83
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:143
virtual wxString ConfigBaseName()
Definition: tools_holder.h:153
void UpdateHotKeys(bool aFullUpdate)
Optionally read the hotkey config files and then rebuilds the internal hotkey maps.
void ReadHotKeyConfig(const wxString &aFileName, std::map< std::string, int > &aHotKeys)
Reads a hotkey config file into a map.
~ACTION_MANAGER()
Unregister every registered action.
TOOL_BASE * FindTool(int aId) const
Search for a tool with given ID.
static std::list< TOOL_ACTION * > & GetActionList()
Return list of TOOL_ACTIONs.
std::string m_name
Definition: tool_action.h:193
TOOL_ACTION * FindAction(const std::string &aActionName) const
Find an action with a given name (if there is one available).
bool RunHotKey(int aHotKey) const
Run an action associated with a hotkey (if there is one available).
Master controller class:
Definition: tool_manager.h:54
const std::string m_legacyName
Definition: tool_action.h:198
#define NULL
int ReadLegacyHotkeyConfig(const wxString &aAppname, std::map< std::string, int > &aMap)
Read configuration data and fill the current hotkey list with hotkeys.
int GetHotKey(const TOOL_ACTION &aAction) const
Return the hot key associated with a given action or 0 if there is none.
ACTION_MANAGER(TOOL_MANAGER *aToolManager)
wxLogTrace helper definitions.
void SetHotKey(int aKeycode)
std::map< std::string, TOOL_ACTION * > m_actionNameIndex
Map for indexing actions by their hotkeys.
HOTKEY_LIST m_actionHotKeys
Quick action<->hot key lookup.
void SetConditions(const TOOL_ACTION &aAction, const ACTION_CONDITIONS &aConditions)
Set the conditions the UI elements for activating a specific tool action should use for determining t...
TOOL_MANAGER * m_toolMgr
Map for indexing actions by their names.
TOOLS_HOLDER * GetToolHolder() const
Definition: tool_manager.h:294
void RegisterAction(TOOL_ACTION *aAction)
Add a tool action to the manager and sets it up.
virtual SELECTION & GetCurrentSelection()
Get the current selection from the canvas area.
Definition: tools_holder.h:101
int GetId() const
Return the unique id of the TOOL_ACTION object.
Definition: tool_action.h:121
std::map< int, ACTION_CONDITIONS > m_uiConditions
Map the command ID that wx uses for the action to the UI conditions for the menu/toolbar items.
int processHotKey(TOOL_ACTION *aAction, std::map< std::string, int > aLegacyMap, std::map< std::string, int > aHotKeyMap)
Tool manager needed to run actions.
Global action (toolbar/main menu event, global shortcut)
Functors that can be used to figure out how the action controls should be displayed in the UI and if ...
Base abstract interface for all kinds of tools.
Definition: tool_base.h:65
Represent a single user action.
Definition: tool_action.h:67
const wxChar *const kicadTraceToolStack
Flag to enable tracing of the tool handling stack.
const ACTION_CONDITIONS * GetCondition(const TOOL_ACTION &aAction) const
Get the conditions to use for a specific tool action.
wxString KeyNameFromKeyCode(int aKeycode, bool *aIsFound)
Return the key name from the key code.
virtual void RegisterUIUpdateHandler(const TOOL_ACTION &aAction, const ACTION_CONDITIONS &aConditions)
Register an action's update conditions with the UI layer to allow the UI to appropriately display the...
Definition: tools_holder.h:63
int GetHotKey() const
Return the hotkey keycode which initiates the action.
Definition: tool_action.h:111
const int m_defaultHotKey
Definition: tool_action.h:196
const std::string & GetName() const
Return name of the action.
Definition: tool_action.h:101