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()
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
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.
TOOL_ID GetId() const
Function GetId() Returns 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:105
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Function RunAction() Runs the specified action.
Definition: tool_manager.h:141
virtual wxString ConfigBaseName()
Definition: tools_holder.h:173
void UpdateHotKeys(bool aFullUpdate)
Optionally reads the hotkey config files and then rebuilds the internal hotkey maps.
~ACTION_MANAGER()
Unregisters every registered action.
TOOL_BASE * FindTool(int aId) const
Function FindTool() Searches for a tool with given ID.
static std::list< TOOL_ACTION * > & GetActionList()
Returns list of TOOL_ACTIONs.
std::string m_name
Name of the action (convention is "app.tool.actionName")
Definition: tool_action.h:169
const std::map< std::string, TOOL_ACTION * > & GetActions()
Get a list of currently-registered actions mapped by their name.
TOOL_ACTION * FindAction(const std::string &aActionName) const
Finds an action with a given name (if there is one available).
bool RunHotKey(int aHotKey) const
Runs an action associated with a hotkey (if there is one available).
void ReadHotKeyConfig(wxString fileName, std::map< std::string, int > &aHotKeys)
Function ReadotKeyConfig Reads a hotkey config file into a map.
TOOL_MANAGER.
Definition: tool_manager.h:51
const std::string m_legacyName
Definition: tool_action.h:174
#define NULL
int ReadLegacyHotkeyConfig(const wxString &aAppname, std::map< std::string, int > &aMap)
Function ReadLegacyHotkeyConfig Read configuration data and fill the current hotkey list with hotkeys...
int GetHotKey(const TOOL_ACTION &aAction) const
Returns 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
All active tools
Definition: tool_event.h:147
std::map< std::string, TOOL_ACTION * > m_actionNameIndex
Map for indexing actions by their names
HOTKEY_LIST m_actionHotKeys
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
Tool manager needed to run actions
TOOLS_HOLDER * GetToolHolder() const
Definition: tool_manager.h:301
void RegisterAction(TOOL_ACTION *aAction)
Adds 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:123
int GetId() const
Returns the unique id of the TOOL_ACTION object.
Definition: tool_action.h:94
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)
Functors that can be used to figure out how the action controls should be displayed in the UI and if ...
TOOL_BASE.
Definition: tool_base.h:67
Represents a single user action.
Definition: tool_action.h:44
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)
Function KeyNameFromKeyCode return the key name from the key code Only some wxWidgets key values are ...
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:85
int GetHotKey() const
Returns the hotkey keycode which initiates the action.
Definition: tool_action.h:85
const int m_defaultHotKey
Definition: tool_action.h:172
const std::string & GetName() const
Returns name of the action.
Definition: tool_action.h:75