KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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-2023 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
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
36
38 m_toolMgr( aToolManager )
39{
40 // Register known actions
41 std::list<TOOL_ACTION*>& actionList = GetActionList();
42
43 for( TOOL_ACTION* action : actionList )
44 {
45 if( action->m_id == -1 )
46 action->m_id = MakeActionId( action->m_name );
47
48 int groupID = 0;
49 std::string groupName = "none";
50
51 std::optional<TOOL_ACTION_GROUP> group = action->GetActionGroup();
52
53 if( group.has_value() )
54 {
55 groupID = group.value().GetGroupID();
56 groupName = group.value().GetName();
57 }
58
59 wxLogTrace( kicadTraceToolStack,
60 "ACTION_MANAGER::ACTION_MANAGER: Registering action %s with ID %d, UI ID %d, "
61 "and group %s(%d)",
62 action->m_name, action->m_id, action->GetUIId(), groupName, groupID );
63
64 RegisterAction( action );
65 }
66}
67
68
70{
71}
72
73
75{
76 // TOOL_ACTIONs are supposed to be named [appName.]toolName.actionName (with dots between)
77 // action name without specifying at least toolName is not valid
78 wxASSERT( aAction->GetName().find( '.', 0 ) != std::string::npos );
79
80 // TOOL_ACTIONs must have unique names & ids
81 wxASSERT( m_actionNameIndex.find( aAction->m_name ) == m_actionNameIndex.end() );
82
83 m_actionNameIndex[aAction->m_name] = aAction;
84
85 if( aAction->HasCustomUIId() )
86 m_customUIIdIndex[aAction->GetUIId()] = aAction;
87}
88
89
91 const ACTION_CONDITIONS& aConditions )
92{
93 // Remove any existing handlers with the old conditions to ensure the UI layer doesn't have
94 // stale data.
95 if( m_toolMgr )
97
98 m_uiConditions[aAction.GetId()] = aConditions;
99
100 wxLogTrace( kicadTraceToolStack,
101 wxS( "ACTION_MANAGER::SetConditions: Registering conditions for ID %d - %s" ),
102 aAction.GetId(), aAction.GetName() );
103
104 // Register a new handler with the new conditions
105 if( m_toolMgr )
106 m_toolMgr->GetToolHolder()->RegisterUIUpdateHandler( aAction, aConditions );
107}
108
109
111{
112 const auto it = m_uiConditions.find( aAction.GetId() );
113
114 // If the action doesn't have something registered, then return null
115 if( it == m_uiConditions.end() )
116 return nullptr;
117 else
118 return &it->second;
119}
120
121
122int ACTION_MANAGER::MakeActionId( const std::string& aActionName )
123{
124 static int currentActionId = 1;
125
126 return currentActionId++;
127}
128
129
130TOOL_ACTION* ACTION_MANAGER::FindAction( const std::string& aActionName ) const
131{
132 std::map<std::string, TOOL_ACTION*>::const_iterator it = m_actionNameIndex.find( aActionName );
133
134 if( it != m_actionNameIndex.end() )
135 return it->second;
136
137 return nullptr;
138}
139
140
141bool ACTION_MANAGER::RunHotKey( int aHotKey ) const
142{
143 int key = aHotKey & ~MD_MODIFIER_MASK;
144 int mod = aHotKey & MD_MODIFIER_MASK;
145
146 if( key >= 'a' && key <= 'z' )
147 key = std::toupper( key );
148
149 wxLogTrace( kicadTraceToolStack, wxS( "ACTION_MANAGER::RunHotKey Key: %s" ),
150 KeyNameFromKeyCode( aHotKey ) );
151
152 HOTKEY_LIST::const_iterator it = m_actionHotKeys.find( key | mod );
153
154 // If no luck, try without Shift, to handle keys that require it
155 // e.g. to get ? you need to press Shift+/ without US keyboard layout
156 // Hardcoding ? as Shift+/ is a bad idea, as on another layout you may need to press a
157 // different combination.
158 // This doesn't apply for letters, as we already handled case normalisation.
159 if( it == m_actionHotKeys.end() && !std::isalpha( key ) )
160 {
161 wxLogTrace( kicadTraceToolStack,
162 wxS( "ACTION_MANAGER::RunHotKey No actions found, searching with key: %s" ),
163 KeyNameFromKeyCode( key | ( mod & ~MD_SHIFT ) ) );
164
165 it = m_actionHotKeys.find( key | ( mod & ~MD_SHIFT ) );
166 }
167
168 // Still no luck, we're done without a match
169 if( it == m_actionHotKeys.end() )
170 return false; // no appropriate action found for the hotkey
171
172 const std::list<TOOL_ACTION*>& actions = it->second;
173
174 // Choose the action that has the highest priority on the active tools stack
175 // If there is none, run the global action associated with the hot key
176 int highestPriority = -1, priority = -1;
177 const TOOL_ACTION* context = nullptr; // pointer to context action of the highest priority tool
178 std::vector<const TOOL_ACTION*> global; // pointers to global actions
179 // if there is no context action
180
181 for( const TOOL_ACTION* action : actions )
182 {
183 if( action->GetScope() == AS_GLOBAL )
184 {
185 // Store the global action in case there are no context actions to run
186 global.emplace_back( action );
187 continue;
188 }
189
190 TOOL_BASE* tool = m_toolMgr->FindTool( action->GetToolName() );
191
192 if( tool )
193 {
194 // Choose the action that goes to the tool with highest priority
195 // (i.e. is on the top of active tools stack)
196 priority = m_toolMgr->GetPriority( tool->GetId() );
197
198 if( priority >= 0 && priority > highestPriority )
199 {
200 highestPriority = priority;
201 context = action;
202 }
203 }
204 }
205
206 // Get the selection to use to test if the action is enabled
208
209 if( context )
210 {
211 bool runAction = true;
212
213 if( const ACTION_CONDITIONS* aCond = GetCondition( *context ) )
214 runAction = aCond->enableCondition( sel );
215
216 wxLogTrace( kicadTraceToolStack,
217 wxS( "ACTION_MANAGER::RunHotKey %s context action: %s for hotkey %s" ),
218 runAction ? wxS( "Running" ) : wxS( "Not running" ),
219 context->GetName(),
220 KeyNameFromKeyCode( aHotKey ) );
221
222 if( runAction )
223 return m_toolMgr->RunAction( *context );
224 }
225 else if( !global.empty() )
226 {
227 for( const TOOL_ACTION* act : global )
228 {
229 bool runAction = true;
230
231 if( const ACTION_CONDITIONS* aCond = GetCondition( *act ) )
232 runAction = aCond->enableCondition( sel );
233
234 wxLogTrace( kicadTraceToolStack,
235 wxS( "ACTION_MANAGER::RunHotKey %s global action: %s for hotkey %s" ),
236 runAction ? wxS( "Running" ) : wxS( "Not running" ),
237 act->GetName(),
238 KeyNameFromKeyCode( aHotKey ) );
239
240 if( runAction && m_toolMgr->RunAction( *act ) )
241 return true;
242 }
243 }
244
245 wxLogTrace( kicadTraceToolStack,
246 wxS( "ACTION_MANAGER::RunHotKey No action found for key %s" ),
247 KeyNameFromKeyCode( aHotKey ) );
248
249 return false;
250}
251
252
253bool ACTION_MANAGER::IsActionUIId( int aId ) const
254{
255 // Automatically assigned IDs are always in this range
256 if( aId >= TOOL_ACTION::GetBaseUIId() )
257 return true;
258
259 // Search the custom assigned UI IDs
260 auto it = m_customUIIdIndex.find( aId );
261
262 return ( it != m_customUIIdIndex.end() );
263}
264
265
266const std::map<std::string, TOOL_ACTION*>& ACTION_MANAGER::GetActions() const
267{
268 return m_actionNameIndex;
269}
270
271
272int ACTION_MANAGER::GetHotKey( const TOOL_ACTION& aAction ) const
273{
274 std::map<int, int>::const_iterator it = m_hotkeys.find( aAction.GetId() );
275
276 if( it == m_hotkeys.end() )
277 return 0;
278
279 return it->second;
280}
281
282
283void ACTION_MANAGER::UpdateHotKeys( bool aFullUpdate )
284{
285 static std::map<std::string, int> legacyHotKeyMap;
286 static std::map<std::string, std::pair<int, int>> userHotKeyMap;
287 static bool mapsInitialized = false;
288
289 m_actionHotKeys.clear();
290 m_hotkeys.clear();
291
292 if( aFullUpdate && !mapsInitialized && m_toolMgr->GetToolHolder() )
293 {
295 ReadHotKeyConfig( wxEmptyString, userHotKeyMap );
296 mapsInitialized = true;
297 }
298
299 for( const auto& ii : m_actionNameIndex )
300 {
301 TOOL_ACTION* action = ii.second;
302 int hotkey = 0;
303 int alt = 0;
304
305 if( aFullUpdate )
306 processHotKey( action, legacyHotKeyMap, userHotKeyMap );
307
308 hotkey = action->GetHotKey();
309 alt = action->GetHotKeyAlt();
310
311 if( hotkey > 0 )
312 m_actionHotKeys[hotkey].push_back( action );
313
314 if( alt > 0 )
315 m_actionHotKeys[alt].push_back( action );
316
317 m_hotkeys[action->GetId()] = hotkey;
318 }
319}
320
321
323 const std::map<std::string, int>& aLegacyMap,
324 const std::map<std::string, std::pair<int, int>>& aHotKeyMap )
325{
326 aAction->m_hotKey = aAction->m_defaultHotKey;
327
328 if( !aAction->m_legacyName.empty() && aLegacyMap.count( aAction->m_legacyName ) )
329 aAction->SetHotKey( aLegacyMap.at( aAction->m_legacyName ) );
330
331 if( aHotKeyMap.count( aAction->m_name ) )
332 {
333 std::pair<int, int> keys = aHotKeyMap.at( aAction->m_name );
334 aAction->SetHotKey( keys.first, keys.second );
335 }
336}
~ACTION_MANAGER()
Unregister every registered action.
HOTKEY_LIST m_actionHotKeys
Quick action<->hot key lookup.
const ACTION_CONDITIONS * GetCondition(const TOOL_ACTION &aAction) const
Get the conditions to use for a specific tool action.
std::map< int, TOOL_ACTION * > m_customUIIdIndex
Map for indexing actions by their hotkeys.
TOOL_MANAGER * m_toolMgr
Map for indexing actions by their names.
void processHotKey(TOOL_ACTION *aAction, const std::map< std::string, int > &aLegacyMap, const std::map< std::string, std::pair< int, int > > &aHotKeyMap)
Tool manager needed to run actions.
const std::map< std::string, TOOL_ACTION * > & GetActions() const
Get a list of currently-registered actions mapped by their name.
std::map< std::string, TOOL_ACTION * > m_actionNameIndex
Map for recording actions that have custom UI IDs.
std::map< int, int > m_hotkeys
bool RunHotKey(int aHotKey) const
Run an action associated with a hotkey (if there is one available).
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...
void RegisterAction(TOOL_ACTION *aAction)
Add a tool action to the manager and sets it up.
TOOL_ACTION * FindAction(const std::string &aActionName) const
Find an action with a given name (if there is one available).
static std::list< TOOL_ACTION * > & GetActionList()
Return list of TOOL_ACTIONs.
bool IsActionUIId(int aId) const
Test if a UI ID corresponds to an action ID in our system.
int GetHotKey(const TOOL_ACTION &aAction) const
Return the hot key associated with a given action or 0 if there is none.
static int MakeActionId(const std::string &aActionName)
Generate an unique ID from for an action with given name.
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.
void UpdateHotKeys(bool aFullUpdate)
Optionally read the hotkey config files and then rebuilds the internal hotkey maps.
ACTION_MANAGER(TOOL_MANAGER *aToolManager)
virtual wxString ConfigBaseName()
Definition: tools_holder.h:164
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...
virtual SELECTION & GetCurrentSelection()
Get the current selection from the canvas area.
Definition: tools_holder.h:98
virtual void UnregisterUIUpdateHandler(const TOOL_ACTION &aAction)
Unregister a UI handler for an action that was registered using RegisterUIUpdateHandler.
Represent a single user action.
Definition: tool_action.h:269
bool HasCustomUIId() const
Return true if this action has a custom UI ID set.
Definition: tool_action.h:329
static int GetBaseUIId()
Get the base value used to offset the user interface IDs for the actions.
Definition: tool_action.h:344
void SetHotKey(int aKeycode, int aKeycodeAlt=0)
int GetId() const
Return the unique id of the TOOL_ACTION object.
Definition: tool_action.h:324
const std::string & GetName() const
Return name of the action.
Definition: tool_action.h:302
int GetHotKeyAlt() const
Definition: tool_action.h:314
int GetHotKey() const
Return the hotkey keycode which initiates the action.
Definition: tool_action.h:313
const int m_defaultHotKey
Default hot key.
Definition: tool_action.h:443
int m_hotKey
The current hotkey (post-user-settings-application).
Definition: tool_action.h:445
const std::string m_legacyName
Name for reading legacy hotkey settings.
Definition: tool_action.h:449
std::string m_name
Name of the action (convention is "app.tool.actionName")
Definition: tool_action.h:438
int GetUIId() const
Get the unique ID for this action in the user interface system.
Definition: tool_action.h:339
Base abstract interface for all kinds of tools.
Definition: tool_base.h:66
TOOL_ID GetId() const
Return the unique identifier of the tool.
Definition: tool_base.h:123
Master controller class:
Definition: tool_manager.h:62
int GetPriority(int aToolId) const
Return priority of a given tool.
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:150
TOOLS_HOLDER * GetToolHolder() const
Definition: tool_manager.h:402
TOOL_BASE * FindTool(int aId) const
Search for a tool with given ID.
const wxChar *const kicadTraceToolStack
Flag to enable tracing of the tool handling stack.
void ReadHotKeyConfig(const wxString &aFileName, std::map< std::string, std::pair< int, int > > &aHotKeys)
Read a hotkey config file into a map.
int ReadLegacyHotkeyConfig(const wxString &aAppname, std::map< std::string, int > &aMap)
Read configuration data and fill the current hotkey list with hotkeys.
wxString KeyNameFromKeyCode(int aKeycode, bool *aIsFound)
Return the key name from the key code.
Functors that can be used to figure out how the action controls should be displayed in the UI and if ...
@ AS_GLOBAL
Global action (toolbar/main menu event, global shortcut)
Definition: tool_action.h:48
@ MD_MODIFIER_MASK
Definition: tool_event.h:146
@ MD_SHIFT
Definition: tool_event.h:143
wxLogTrace helper definitions.