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-2021 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 
71  const ACTION_CONDITIONS& aConditions )
72 {
73  // Remove any existing handlers with the old conditions to ensure the UI layer doesn't have
74  // stale data.
75  if( m_toolMgr )
77 
78  m_uiConditions[aAction.GetId()] = aConditions;
79 
80  wxLogTrace( kicadTraceToolStack,
81  "ACTION_MANAGER::SetConditions: Registering conditions for ID %d - %s",
82  aAction.GetId(), aAction.GetName() );
83 
84  // Register a new handler with the new conditions
85  if( m_toolMgr )
86  m_toolMgr->GetToolHolder()->RegisterUIUpdateHandler( aAction, aConditions );
87 }
88 
89 
91 {
92  const auto it = m_uiConditions.find( aAction.GetId() );
93 
94  // If the action doesn't have something registered, then return null
95  if( it == m_uiConditions.end() )
96  return nullptr;
97  else
98  return &it->second;
99 }
100 
101 
102 int ACTION_MANAGER::MakeActionId( const std::string& aActionName )
103 {
104  static int currentActionId = 1;
105 
106  return currentActionId++;
107 }
108 
109 
110 TOOL_ACTION* ACTION_MANAGER::FindAction( const std::string& aActionName ) const
111 {
112  std::map<std::string, TOOL_ACTION*>::const_iterator it = m_actionNameIndex.find( aActionName );
113 
114  if( it != m_actionNameIndex.end() )
115  return it->second;
116 
117  return nullptr;
118 }
119 
120 
121 bool ACTION_MANAGER::RunHotKey( int aHotKey ) const
122 {
123  int key = aHotKey & ~MD_MODIFIER_MASK;
124  int mod = aHotKey & MD_MODIFIER_MASK;
125 
126  if( key >= 'a' && key <= 'z' )
127  key = std::toupper( key );
128 
129  wxLogTrace( kicadTraceToolStack, "ACTION_MANAGER::RunHotKey Key: %s",
130  KeyNameFromKeyCode( aHotKey ) );
131 
132  HOTKEY_LIST::const_iterator it = m_actionHotKeys.find( key | mod );
133 
134  // If no luck, try without Shift, to handle keys that require it
135  // e.g. to get ? you need to press Shift+/ without US keyboard layout
136  // Hardcoding ? as Shift+/ is a bad idea, as on another layout you may need to press a
137  // different combination
138  if( it == m_actionHotKeys.end() )
139  {
140  wxLogTrace( kicadTraceToolStack,
141  "ACTION_MANAGER::RunHotKey No actions found, searching with key: %s",
142  KeyNameFromKeyCode( key | ( mod & ~MD_SHIFT ) ) );
143 
144  it = m_actionHotKeys.find( key | ( mod & ~MD_SHIFT ) );
145 
146  if( it == m_actionHotKeys.end() )
147  return false; // no appropriate action found for the hotkey
148  }
149 
150  const std::list<TOOL_ACTION*>& actions = it->second;
151 
152  // Choose the action that has the highest priority on the active tools stack
153  // If there is none, run the global action associated with the hot key
154  int highestPriority = -1, priority = -1;
155  const TOOL_ACTION* context = nullptr; // pointer to context action of the highest priority tool
156  std::vector<const TOOL_ACTION*> global; // pointers to global actions
157  // if there is no context action
158 
159  for( const TOOL_ACTION* action : actions )
160  {
161  if( action->GetScope() == AS_GLOBAL )
162  {
163  // Store the global action in case there are no context actions to run
164  global.emplace_back( action );
165  continue;
166  }
167 
168  TOOL_BASE* tool = m_toolMgr->FindTool( action->GetToolName() );
169 
170  if( tool )
171  {
172  // Choose the action that goes to the tool with highest priority
173  // (i.e. is on the top of active tools stack)
174  priority = m_toolMgr->GetPriority( tool->GetId() );
175 
176  if( priority >= 0 && priority > highestPriority )
177  {
178  highestPriority = priority;
179  context = action;
180  }
181  }
182  }
183 
184  // Get the selection to use to test if the action is enabled
186 
187  if( context )
188  {
189  bool runAction = true;
190 
191  if( const ACTION_CONDITIONS* aCond = GetCondition( *context ) )
192  runAction = aCond->enableCondition( sel );
193 
194  wxLogTrace( kicadTraceToolStack,
195  "ACTION_MANAGER::RunHotKey %s context action: %s for hotkey %s",
196  runAction ? "Running" : "Not running",
197  context->GetName(),
198  KeyNameFromKeyCode( aHotKey ) );
199 
200  if( runAction )
201  return m_toolMgr->RunAction( *context, true );
202  }
203  else if( !global.empty() )
204  {
205  for( auto act : global )
206  {
207  bool runAction = true;
208 
209  if( const ACTION_CONDITIONS* aCond = GetCondition( *act ) )
210  runAction = aCond->enableCondition( sel );
211 
212  wxLogTrace( kicadTraceToolStack,
213  "ACTION_MANAGER::RunHotKey %s global action: %s for hotkey %s",
214  runAction ? "Running" : "Not running",
215  act->GetName(),
216  KeyNameFromKeyCode( aHotKey ) );
217 
218  if( runAction && m_toolMgr->RunAction( *act, true ) )
219  return true;
220  }
221  }
222 
223  wxLogTrace( kicadTraceToolStack,
224  "ACTION_MANAGER::RunHotKey No action found for key %s",
225  KeyNameFromKeyCode( aHotKey ) );
226 
227  return false;
228 }
229 
230 
231 const std::map<std::string, TOOL_ACTION*>& ACTION_MANAGER::GetActions() const
232 {
233  return m_actionNameIndex;
234 }
235 
236 
237 int ACTION_MANAGER::GetHotKey( const TOOL_ACTION& aAction ) const
238 {
239  std::map<int, int>::const_iterator it = m_hotkeys.find( aAction.GetId() );
240 
241  if( it == m_hotkeys.end() )
242  return 0;
243 
244  return it->second;
245 }
246 
247 
248 void ACTION_MANAGER::UpdateHotKeys( bool aFullUpdate )
249 {
250  std::map<std::string, int> legacyHotKeyMap;
251  std::map<std::string, int> userHotKeyMap;
252 
253  m_actionHotKeys.clear();
254  m_hotkeys.clear();
255 
256  if( aFullUpdate && m_toolMgr->GetToolHolder() )
257  {
259  ReadHotKeyConfig( wxEmptyString, userHotKeyMap );
260  }
261 
262  for( const auto& ii : m_actionNameIndex )
263  {
264  TOOL_ACTION* action = ii.second;
265  int hotkey = 0;
266 
267  if( aFullUpdate )
268  hotkey = processHotKey( action, legacyHotKeyMap, userHotKeyMap );
269  else
270  hotkey = action->GetHotKey();
271 
272  if( hotkey > 0 )
273  m_actionHotKeys[hotkey].push_back( action );
274 
275  m_hotkeys[action->GetId()] = hotkey;
276  }
277 }
278 
279 
280 int ACTION_MANAGER::processHotKey( TOOL_ACTION* aAction, std::map<std::string, int> aLegacyMap,
281  std::map<std::string, int> aHotKeyMap )
282 {
283  aAction->m_hotKey = aAction->m_defaultHotKey;
284 
285  if( !aAction->m_legacyName.empty() && aLegacyMap.count( aAction->m_legacyName ) )
286  aAction->SetHotKey( aLegacyMap[ aAction->m_legacyName ] );
287 
288  if( aHotKeyMap.count( aAction->m_name ) )
289  aAction->SetHotKey( aHotKeyMap[ aAction->m_name ] );
290 
291  return aAction->m_hotKey;
292 }
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:155
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
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