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