KiCad PCB EDA Suite
Loading...
Searching...
No Matches
hotkeys_basic.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) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2010 Wayne Stambaugh <[email protected]>
6 * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
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 <kiface_base.h>
27#include <hotkeys_basic.h>
28#include <id.h>
29#include <string_utils.h>
30#include <eda_base_frame.h>
31#include <eda_draw_frame.h>
33#include <paths.h>
34
35#include <tool/tool_manager.h>
37#include <wx/apptrait.h>
38#include <wx/stdpaths.h>
39#include <wx/tokenzr.h>
40#include <wx/txtstrm.h>
41#include <wx/wfstream.h>
42#include <tool/tool_action.h>
43
44
45/*
46 * class to handle the printable name and the keycode
47 */
49{
50 const wxChar* m_Name;
52};
53
54
55/* table giving the hotkey name from the hotkey code, for special keys
56 * Note : when modifiers (ATL, SHIFT, CTRL) do not modify
57 * the code of the key, do need to enter the modified key code
58 * For instance wxT( "F1" ), WXK_F1 handle F1, AltF1, CtrlF1 ...
59 * Key names are:
60 * "Space","Ctrl+Space","Alt+Space" or
61 * "Alt+A","Ctrl+F1", ...
62 */
63#define KEY_NON_FOUND -1
65{
66 { wxT( "F1" ), WXK_F1 },
67 { wxT( "F2" ), WXK_F2 },
68 { wxT( "F3" ), WXK_F3 },
69 { wxT( "F4" ), WXK_F4 },
70 { wxT( "F5" ), WXK_F5 },
71 { wxT( "F6" ), WXK_F6 },
72 { wxT( "F7" ), WXK_F7 },
73 { wxT( "F8" ), WXK_F8 },
74 { wxT( "F9" ), WXK_F9 },
75 { wxT( "F10" ), WXK_F10 },
76 { wxT( "F11" ), WXK_F11 },
77 { wxT( "F12" ), WXK_F12 },
78 { wxT( "F13" ), WXK_F13 },
79 { wxT( "F14" ), WXK_F14 },
80 { wxT( "F15" ), WXK_F15 },
81 { wxT( "F16" ), WXK_F16 },
82 { wxT( "F17" ), WXK_F17 },
83 { wxT( "F18" ), WXK_F18 },
84 { wxT( "F19" ), WXK_F19 },
85 { wxT( "F20" ), WXK_F20 },
86 { wxT( "F21" ), WXK_F21 },
87 { wxT( "F22" ), WXK_F22 },
88 { wxT( "F23" ), WXK_F23 },
89 { wxT( "F24" ), WXK_F24 },
90
91 { wxT( "Esc" ), WXK_ESCAPE },
92 { wxT( "Del" ), WXK_DELETE },
93 { wxT( "Tab" ), WXK_TAB },
94 { wxT( "Back" ), WXK_BACK },
95 { wxT( "Ins" ), WXK_INSERT },
96
97 { wxT( "Home" ), WXK_HOME },
98 { wxT( "End" ), WXK_END },
99 { wxT( "PgUp" ), WXK_PAGEUP },
100 { wxT( "PgDn" ), WXK_PAGEDOWN },
101
102 { wxT( "Up" ), WXK_UP },
103 { wxT( "Down" ), WXK_DOWN },
104 { wxT( "Left" ), WXK_LEFT },
105 { wxT( "Right" ), WXK_RIGHT },
106
107 { wxT( "Return" ), WXK_RETURN },
108
109 { wxT( "Space" ), WXK_SPACE },
110
111 { wxT( "Num Pad 0" ), WXK_NUMPAD0 },
112 { wxT( "Num Pad 1" ), WXK_NUMPAD1 },
113 { wxT( "Num Pad 2" ), WXK_NUMPAD2 },
114 { wxT( "Num Pad 3" ), WXK_NUMPAD3 },
115 { wxT( "Num Pad 4" ), WXK_NUMPAD4 },
116 { wxT( "Num Pad 5" ), WXK_NUMPAD5 },
117 { wxT( "Num Pad 6" ), WXK_NUMPAD6 },
118 { wxT( "Num Pad 7" ), WXK_NUMPAD7 },
119 { wxT( "Num Pad 8" ), WXK_NUMPAD8 },
120 { wxT( "Num Pad 9" ), WXK_NUMPAD9 },
121 { wxT( "Num Pad +" ), WXK_NUMPAD_ADD },
122 { wxT( "Num Pad -" ), WXK_NUMPAD_SUBTRACT },
123 { wxT( "Num Pad *" ), WXK_NUMPAD_MULTIPLY },
124 { wxT( "Num Pad /" ), WXK_NUMPAD_DIVIDE },
125 { wxT( "Num Pad ." ), WXK_NUMPAD_SEPARATOR },
126 { wxT( "Num Pad Enter" ), WXK_NUMPAD_ENTER },
127 { wxT( "Num Pad F1"), WXK_NUMPAD_F1 },
128 { wxT( "Num Pad F2"), WXK_NUMPAD_F2 },
129 { wxT( "Num Pad F3"), WXK_NUMPAD_F3 },
130 { wxT( "Num Pad F4"), WXK_NUMPAD_F4 },
131
132 { wxT( "" ), 0 },
133
134 { wxT( "Click" ), PSEUDO_WXK_CLICK },
135 { wxT( "DblClick" ), PSEUDO_WXK_DBLCLICK },
136 { wxT( "Wheel" ), PSEUDO_WXK_WHEEL },
137
138 // Do not change this line: end of list
139 { wxT( "" ), KEY_NON_FOUND }
140};
141
142
143// name of modifier keys.
144// Note: the Ctrl key is Cmd key on Mac OS X.
145// However, in wxWidgets defs, the key WXK_CONTROL is the Cmd key,
146// so the code using WXK_CONTROL should be ok on any system.
147// (on Mac OS X the actual Ctrl key code is WXK_RAW_CONTROL)
148#ifdef __WXMAC__
149#define USING_MAC_CMD
150#endif
151
152#ifdef USING_MAC_CMD
153#define MODIFIER_CTRL wxT( "Cmd+" )
154#define MODIFIER_ALT wxT( "Option+" )
155#else
156#define MODIFIER_CTRL wxT( "Ctrl+" )
157#define MODIFIER_ALT wxT( "Alt+" )
158#endif
159#define MODIFIER_CMD_MAC wxT( "Cmd+" )
160#define MODIFIER_CTRL_BASE wxT( "Ctrl+" )
161#define MODIFIER_SHIFT wxT( "Shift+" )
162
163
173wxString KeyNameFromKeyCode( int aKeycode, bool* aIsFound )
174{
175 wxString keyname, modifier, fullkeyname;
176 int ii;
177 bool found = false;
178
179 if( aKeycode == WXK_CONTROL )
180 return wxString( MODIFIER_CTRL ).BeforeFirst( '+' );
181 else if( aKeycode == WXK_RAW_CONTROL )
182 return wxString( MODIFIER_CTRL_BASE ).BeforeFirst( '+' );
183 else if( aKeycode == WXK_SHIFT )
184 return wxString( MODIFIER_SHIFT ).BeforeFirst( '+' );
185 else if( aKeycode == WXK_ALT )
186 return wxString( MODIFIER_ALT ).BeforeFirst( '+' );
187
188 // Assume keycode of 0 is "unassigned"
189 if( (aKeycode & MD_CTRL) != 0 )
190 modifier << MODIFIER_CTRL;
191
192 if( (aKeycode & MD_ALT) != 0 )
193 modifier << MODIFIER_ALT;
194
195 if( (aKeycode & MD_SHIFT) != 0 )
196 modifier << MODIFIER_SHIFT;
197
198 aKeycode &= ~( MD_CTRL | MD_ALT | MD_SHIFT );
199
200 if( (aKeycode > ' ') && (aKeycode < 0x7F ) )
201 {
202 found = true;
203 keyname.Append( (wxChar)aKeycode );
204 }
205 else
206 {
207 for( ii = 0; ; ii++ )
208 {
209 if( hotkeyNameList[ii].m_KeyCode == KEY_NON_FOUND ) // End of list
210 {
211 keyname = wxT( "<unknown>" );
212 break;
213 }
214
215 if( hotkeyNameList[ii].m_KeyCode == aKeycode )
216 {
217 keyname = hotkeyNameList[ii].m_Name;
218 found = true;
219 break;
220 }
221 }
222 }
223
224 if( aIsFound )
225 *aIsFound = found;
226
227 fullkeyname = modifier + keyname;
228 return fullkeyname;
229}
230
231
238wxString AddHotkeyName( const wxString& aText, int aHotKey, HOTKEY_ACTION_TYPE aStyle )
239{
240 wxString msg = aText;
241 wxString keyname = KeyNameFromKeyCode( aHotKey );
242
243 if( !keyname.IsEmpty() )
244 {
245 switch( aStyle )
246 {
247 case IS_HOTKEY:
248 {
249 // Don't add a suffix for unassigned hotkeys:
250 // WX spews debug from wxAcceleratorEntry::ParseAccel if it doesn't
251 // recognize the keyname, which is the case for <unassigned>.
252 if( aHotKey != 0 )
253 {
254 msg << wxT( "\t" ) << keyname;
255 }
256 break;
257 }
258 case IS_COMMENT:
259 {
260 msg << wxT( " (" ) << keyname << wxT( ")" );
261 break;
262 }
263 }
264 }
265
266#ifdef USING_MAC_CMD
267 // On OSX, the modifier equivalent to the Ctrl key of PCs
268 // is the Cmd key, but in code we should use Ctrl as prefix in menus
269 msg.Replace( MODIFIER_CMD_MAC, MODIFIER_CTRL_BASE );
270#endif
271
272 return msg;
273}
274
275
279int KeyCodeFromKeyName( const wxString& keyname )
280{
281 int ii, keycode = KEY_NON_FOUND;
282
283 // Search for modifiers: Ctrl+ Alt+ and Shift+
284 // Note: on Mac OSX, the Cmd key is equiv here to Ctrl
285 wxString key = keyname;
286 wxString prefix;
287 int modifier = 0;
288
289 while( true )
290 {
291 prefix.Empty();
292
293 if( key.StartsWith( MODIFIER_CTRL_BASE ) )
294 {
295 modifier |= MD_CTRL;
296 prefix = MODIFIER_CTRL_BASE;
297 }
298 else if( key.StartsWith( MODIFIER_CMD_MAC ) )
299 {
300 modifier |= MD_CTRL;
301 prefix = MODIFIER_CMD_MAC;
302 }
303 else if( key.StartsWith( MODIFIER_ALT ) )
304 {
305 modifier |= MD_ALT;
306 prefix = MODIFIER_ALT;
307 }
308 else if( key.StartsWith( MODIFIER_SHIFT ) )
309 {
310 modifier |= MD_SHIFT;
311 prefix = MODIFIER_SHIFT;
312 }
313 else
314 {
315 break;
316 }
317
318 if( !prefix.IsEmpty() )
319 key.Remove( 0, prefix.Len() );
320 }
321
322 if( (key.length() == 1) && (key[0] > ' ') && (key[0] < 0x7F) )
323 {
324 keycode = key[0];
325 keycode += modifier;
326 return keycode;
327 }
328
329 for( ii = 0; hotkeyNameList[ii].m_KeyCode != KEY_NON_FOUND; ii++ )
330 {
331 if( key.CmpNoCase( hotkeyNameList[ii].m_Name ) == 0 )
332 {
333 keycode = hotkeyNameList[ii].m_KeyCode + modifier;
334 break;
335 }
336 }
337
338 return keycode;
339}
340
341
342/*
343 * Displays the hotkeys registered with the given tool manager.
344 */
346{
347 DIALOG_LIST_HOTKEYS dlg( aParent );
348 dlg.ShowModal();
349}
350
351
352void ReadHotKeyConfig( const wxString& aFileName,
353 std::map<std::string, std::pair<int, int>>& aHotKeys )
354{
355 wxString fileName = aFileName;
356
357 if( fileName.IsEmpty() )
358 {
359 wxFileName fn( wxS( "user" ) );
360 fn.SetExt( FILEEXT::HotkeyFileExtension );
361 fn.SetPath( PATHS::GetUserSettingsPath() );
362 fileName = fn.GetFullPath();
363 }
364
365 if( !wxFile::Exists( fileName ) )
366 return;
367
368 wxFFile file( fileName, "rb" );
369
370 if( !file.IsOpened() ) // There is a problem to open file
371 return;
372
373 wxString input;
374 file.ReadAll( &input );
375 input.Replace( "\r\n", "\n" ); // Convert Windows files to Unix line-ends
376 wxStringTokenizer fileTokenizer( input, wxS( "\n" ), wxTOKEN_STRTOK );
377
378 while( fileTokenizer.HasMoreTokens() )
379 {
380 wxStringTokenizer lineTokenizer( fileTokenizer.GetNextToken(), wxS( "\t" ) );
381
382 wxString cmdName = lineTokenizer.GetNextToken();
383 wxString primary = lineTokenizer.GetNextToken();
384 wxString secondary = lineTokenizer.GetNextToken();
385
386 if( !cmdName.IsEmpty() )
387 aHotKeys[cmdName.ToStdString()] = std::pair<int, int>(
388 KeyCodeFromKeyName( primary ), KeyCodeFromKeyName( secondary ) );
389 }
390}
391
392
393void ReadHotKeyConfigIntoActions( const wxString& aFileName, std::vector<TOOL_ACTION*>& aActions )
394{
395 std::map<std::string, std::pair<int, int>> hotkeys;
396
397 // Read the existing config (all hotkeys)
398 ReadHotKeyConfig( aFileName, hotkeys );
399
400 // Set each tool action hotkey to the config file hotkey if present
401 for( TOOL_ACTION* action : aActions )
402 if( hotkeys.find( action->GetName() ) != hotkeys.end() )
403 {
404 std::pair<int, int> keys = hotkeys[action->GetName()];
405 action->SetHotKey( keys.first, keys.second );
406 }
407}
408
409
410int WriteHotKeyConfig( const std::vector<TOOL_ACTION*>& aActions )
411{
412 std::map<std::string, std::pair<int, int>> hotkeys;
413 wxFileName fn( "user" );
414
415 fn.SetExt( FILEEXT::HotkeyFileExtension );
416 fn.SetPath( PATHS::GetUserSettingsPath() );
417
418 // Read the existing config (all hotkeys)
419 ReadHotKeyConfig( fn.GetFullPath(), hotkeys );
420
421 // Overlay the current app's hotkey definitions onto the map
422 for( const TOOL_ACTION* action : aActions )
423 hotkeys[ action->GetName() ] = std::pair<int, int>( action->GetHotKey(), action->GetHotKeyAlt() );
424
425 // Write entire hotkey set
426 wxFFileOutputStream outStream( fn.GetFullPath() );
427 wxTextOutputStream txtStream( outStream, wxEOL_UNIX );
428
429 for( const std::pair<const std::string, std::pair<int, int>>& entry : hotkeys )
430 txtStream << entry.first
431 << "\t" << KeyNameFromKeyCode( entry.second.first )
432 << "\t" << KeyNameFromKeyCode( entry.second.second ) << endl;
433
434 txtStream.Flush();
435 outStream.Close();
436
437 return 1;
438}
439
440
441int ReadLegacyHotkeyConfig( const wxString& aAppname, std::map<std::string, int>& aMap )
442{
443 // For Eeschema and Pcbnew frames, we read the new combined file.
444 // For other kifaces, we read the frame-based file
445 if( aAppname == LIB_EDIT_FRAME_NAME || aAppname == SCH_EDIT_FRAME_NAME )
446 {
448 }
449 else if( aAppname == PCB_EDIT_FRAME_NAME || aAppname == FOOTPRINT_EDIT_FRAME_NAME )
450 {
452 }
453
454 return ReadLegacyHotkeyConfigFile( aAppname, aMap );
455}
456
457
458int ReadLegacyHotkeyConfigFile( const wxString& aFilename, std::map<std::string, int>& aMap )
459{
460 wxFileName fn( aFilename );
461
462 fn.SetExt( FILEEXT::HotkeyFileExtension );
463 fn.SetPath( PATHS::GetUserSettingsPath() );
464
465 if( !wxFile::Exists( fn.GetFullPath() ) )
466 return 0;
467
468 wxFFile cfgfile( fn.GetFullPath(), "rb" );
469
470 if( !cfgfile.IsOpened() ) // There is a problem to open file
471 return 0;
472
473 // get length
474 wxFileOffset size = cfgfile.Length();
475
476 // read data
477 std::vector<char> buffer( size );
478 cfgfile.Read( buffer.data(), size );
479 wxString data( buffer.data(), wxConvUTF8, size );
480
481 // Is this the wxConfig format? If so, remove "Keys=" and parse the newlines.
482 if( data.StartsWith( wxT("Keys="), &data ) )
483 data.Replace( "\\n", "\n", true );
484
485 // parse
486 wxStringTokenizer tokenizer( data, L"\r\n", wxTOKEN_STRTOK );
487
488 while( tokenizer.HasMoreTokens() )
489 {
490 wxString line = tokenizer.GetNextToken();
491 wxStringTokenizer lineTokenizer( line );
492
493 wxString line_type = lineTokenizer.GetNextToken();
494
495 if( line_type[0] == '#' ) // comment
496 continue;
497
498 if( line_type[0] == '[' ) // tags ignored reading legacy hotkeys
499 continue;
500
501 if( line_type == wxT( "$Endlist" ) )
502 break;
503
504 if( line_type != wxT( "shortcut" ) )
505 continue;
506
507 // Get the key name
508 lineTokenizer.SetString( lineTokenizer.GetString(), L"\"\r\n\t ", wxTOKEN_STRTOK );
509 wxString keyname = lineTokenizer.GetNextToken();
510
511 wxString remainder = lineTokenizer.GetString();
512
513 // Get the command name
514 wxString fctname = remainder.AfterFirst( '\"' ).BeforeFirst( '\"' );
515
516 // Add the pair to the map
517 aMap[ fctname.ToStdString() ] = KeyCodeFromKeyName( keyname );
518 }
519
520 // cleanup
521 cfgfile.Close();
522 return 1;
523}
524
525
A dialog that presents the user with a read-only list of hotkeys and their current bindings.
The base frame for deriving all KiCad main window classes.
static wxString GetUserSettingsPath()
Return the user configuration path used to store KiCad's configuration files.
Definition: paths.cpp:510
Represent a single user action.
Definition: tool_action.h:269
Hotkey list dialog (as opposed to editor)
Base window classes and related definitions.
#define LIB_EDIT_FRAME_NAME
#define FOOTPRINT_EDIT_FRAME_NAME
#define SCH_EDIT_FRAME_NAME
#define PCB_EDIT_FRAME_NAME
static const std::string HotkeyFileExtension
int ReadLegacyHotkeyConfigFile(const wxString &aFilename, std::map< std::string, int > &aMap)
Read hotkey configuration for a given app.
wxString AddHotkeyName(const wxString &aText, int aHotKey, HOTKEY_ACTION_TYPE aStyle)
void ReadHotKeyConfig(const wxString &aFileName, std::map< std::string, std::pair< int, int > > &aHotKeys)
Reads a hotkey config file into a map.
#define MODIFIER_ALT
#define MODIFIER_SHIFT
void DisplayHotkeyList(EDA_BASE_FRAME *aParent)
Display the current hotkey list.
int ReadLegacyHotkeyConfig(const wxString &aAppname, std::map< std::string, int > &aMap)
Read configuration data and fill the current hotkey list with hotkeys.
#define MODIFIER_CMD_MAC
wxString KeyNameFromKeyCode(int aKeycode, bool *aIsFound)
Return the key name from the key code.
int WriteHotKeyConfig(const std::vector< TOOL_ACTION * > &aActions)
Update the hotkeys config file with the hotkeys from the given actions map.
#define MODIFIER_CTRL_BASE
static struct hotkey_name_descr hotkeyNameList[]
int KeyCodeFromKeyName(const wxString &keyname)
Return the key code from its user-friendly key name (ie: "Ctrl+M").
void ReadHotKeyConfigIntoActions(const wxString &aFileName, std::vector< TOOL_ACTION * > &aActions)
Reads a hotkey config file into a list of actions.
#define KEY_NON_FOUND
#define MODIFIER_CTRL
#define EESCHEMA_HOTKEY_NAME
Definition: hotkeys_basic.h:31
#define PCBNEW_HOTKEY_NAME
Definition: hotkeys_basic.h:32
#define PSEUDO_WXK_WHEEL
Definition: hotkeys_basic.h:52
HOTKEY_ACTION_TYPE
In menus we can add a hot key, or an accelerator, or sometimes just a comment.
Definition: hotkeys_basic.h:76
@ IS_HOTKEY
Definition: hotkeys_basic.h:77
@ IS_COMMENT
Definition: hotkeys_basic.h:78
#define PSEUDO_WXK_DBLCLICK
Definition: hotkeys_basic.h:51
#define PSEUDO_WXK_CLICK
Definition: hotkeys_basic.h:50
const wxChar * m_Name
@ MD_ALT
Definition: tool_event.h:144
@ MD_CTRL
Definition: tool_event.h:143
@ MD_SHIFT
Definition: tool_event.h:142
Definition of file extensions used in Kicad.