KiCad PCB EDA Suite
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>
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 F1"), WXK_NUMPAD_F1 },
127 { wxT( "Num Pad F2"), WXK_NUMPAD_F2 },
128 { wxT( "Num Pad F3"), WXK_NUMPAD_F3 },
129 { wxT( "Num Pad F4"), WXK_NUMPAD_F4 },
130
131 { wxT( "" ), 0 },
132
133 { wxT( "Click" ), PSEUDO_WXK_CLICK },
134 { wxT( "DblClick" ), PSEUDO_WXK_DBLCLICK },
135 { wxT( "Wheel" ), PSEUDO_WXK_WHEEL },
136
137 // Do not change this line: end of list
138 { wxT( "" ), KEY_NON_FOUND }
139};
140
141
142// name of modifier keys.
143// Note: the Ctrl key is Cmd key on Mac OS X.
144// However, in wxWidgets defs, the key WXK_CONTROL is the Cmd key,
145// so the code using WXK_CONTROL should be ok on any system.
146// (on Mac OS X the actual Ctrl key code is WXK_RAW_CONTROL)
147#ifdef __WXMAC__
148#define USING_MAC_CMD
149#endif
150
151#ifdef USING_MAC_CMD
152#define MODIFIER_CTRL wxT( "Cmd+" )
153#define MODIFIER_ALT wxT( "Option+" )
154#else
155#define MODIFIER_CTRL wxT( "Ctrl+" )
156#define MODIFIER_ALT wxT( "Alt+" )
157#endif
158#define MODIFIER_CMD_MAC wxT( "Cmd+" )
159#define MODIFIER_CTRL_BASE wxT( "Ctrl+" )
160#define MODIFIER_SHIFT wxT( "Shift+" )
161
162
172wxString KeyNameFromKeyCode( int aKeycode, bool* aIsFound )
173{
174 wxString keyname, modifier, fullkeyname;
175 int ii;
176 bool found = false;
177
178 if( aKeycode == WXK_CONTROL )
179 return wxString( MODIFIER_CTRL ).BeforeFirst( '+' );
180 else if( aKeycode == WXK_RAW_CONTROL )
181 return wxString( MODIFIER_CTRL_BASE ).BeforeFirst( '+' );
182 else if( aKeycode == WXK_SHIFT )
183 return wxString( MODIFIER_SHIFT ).BeforeFirst( '+' );
184 else if( aKeycode == WXK_ALT )
185 return wxString( MODIFIER_ALT ).BeforeFirst( '+' );
186
187 // Assume keycode of 0 is "unassigned"
188 if( (aKeycode & MD_CTRL) != 0 )
189 modifier << MODIFIER_CTRL;
190
191 if( (aKeycode & MD_ALT) != 0 )
192 modifier << MODIFIER_ALT;
193
194 if( (aKeycode & MD_SHIFT) != 0 )
195 modifier << MODIFIER_SHIFT;
196
197 aKeycode &= ~( MD_CTRL | MD_ALT | MD_SHIFT );
198
199 if( (aKeycode > ' ') && (aKeycode < 0x7F ) )
200 {
201 found = true;
202 keyname.Append( (wxChar)aKeycode );
203 }
204 else
205 {
206 for( ii = 0; ; ii++ )
207 {
208 if( hotkeyNameList[ii].m_KeyCode == KEY_NON_FOUND ) // End of list
209 {
210 keyname = wxT( "<unknown>" );
211 break;
212 }
213
214 if( hotkeyNameList[ii].m_KeyCode == aKeycode )
215 {
216 keyname = hotkeyNameList[ii].m_Name;
217 found = true;
218 break;
219 }
220 }
221 }
222
223 if( aIsFound )
224 *aIsFound = found;
225
226 fullkeyname = modifier + keyname;
227 return fullkeyname;
228}
229
230
237wxString AddHotkeyName( const wxString& aText, int aHotKey, HOTKEY_ACTION_TYPE aStyle )
238{
239 wxString msg = aText;
240 wxString keyname = KeyNameFromKeyCode( aHotKey );
241
242 if( !keyname.IsEmpty() )
243 {
244 switch( aStyle )
245 {
246 case IS_HOTKEY:
247 {
248 // Don't add a suffix for unassigned hotkeys:
249 // WX spews debug from wxAcceleratorEntry::ParseAccel if it doesn't
250 // recognize the keyname, which is the case for <unassigned>.
251 if( aHotKey != 0 )
252 {
253 msg << wxT( "\t" ) << keyname;
254 }
255 break;
256 }
257 case IS_COMMENT:
258 {
259 msg << wxT( " (" ) << keyname << wxT( ")" );
260 break;
261 }
262 }
263 }
264
265#ifdef USING_MAC_CMD
266 // On OSX, the modifier equivalent to the Ctrl key of PCs
267 // is the Cmd key, but in code we should use Ctrl as prefix in menus
268 msg.Replace( MODIFIER_CMD_MAC, MODIFIER_CTRL_BASE );
269#endif
270
271 return msg;
272}
273
274
278int KeyCodeFromKeyName( const wxString& keyname )
279{
280 int ii, keycode = KEY_NON_FOUND;
281
282 // Search for modifiers: Ctrl+ Alt+ and Shift+
283 // Note: on Mac OSX, the Cmd key is equiv here to Ctrl
284 wxString key = keyname;
285 wxString prefix;
286 int modifier = 0;
287
288 while( true )
289 {
290 prefix.Empty();
291
292 if( key.StartsWith( MODIFIER_CTRL_BASE ) )
293 {
294 modifier |= MD_CTRL;
295 prefix = MODIFIER_CTRL_BASE;
296 }
297 else if( key.StartsWith( MODIFIER_CMD_MAC ) )
298 {
299 modifier |= MD_CTRL;
300 prefix = MODIFIER_CMD_MAC;
301 }
302 else if( key.StartsWith( MODIFIER_ALT ) )
303 {
304 modifier |= MD_ALT;
305 prefix = MODIFIER_ALT;
306 }
307 else if( key.StartsWith( MODIFIER_SHIFT ) )
308 {
309 modifier |= MD_SHIFT;
310 prefix = MODIFIER_SHIFT;
311 }
312 else
313 {
314 break;
315 }
316
317 if( !prefix.IsEmpty() )
318 key.Remove( 0, prefix.Len() );
319 }
320
321 if( (key.length() == 1) && (key[0] > ' ') && (key[0] < 0x7F) )
322 {
323 keycode = key[0];
324 keycode += modifier;
325 return keycode;
326 }
327
328 for( ii = 0; hotkeyNameList[ii].m_KeyCode != KEY_NON_FOUND; ii++ )
329 {
330 if( key.CmpNoCase( hotkeyNameList[ii].m_Name ) == 0 )
331 {
332 keycode = hotkeyNameList[ii].m_KeyCode + modifier;
333 break;
334 }
335 }
336
337 return keycode;
338}
339
340
341/*
342 * Displays the hotkeys registered with the given tool manager.
343 */
345{
346 DIALOG_LIST_HOTKEYS dlg( aParent );
347 dlg.ShowModal();
348}
349
350
351void ReadHotKeyConfig( const wxString& aFileName, std::map<std::string, int>& aHotKeys )
352{
353 wxString fileName = aFileName;
354
355 if( fileName.IsEmpty() )
356 {
357 wxFileName fn( "user" );
358 fn.SetExt( HotkeyFileExtension );
360 fileName = fn.GetFullPath();
361 }
362
363 if( !wxFile::Exists( fileName ) )
364 return;
365
366 wxFFile file( fileName, "rb" );
367
368 if( !file.IsOpened() ) // There is a problem to open file
369 return;
370
371 wxString input;
372 file.ReadAll( &input );
373 input.Replace( "\r\n", "\n" ); // Convert Windows files to Unix line-ends
374 wxStringTokenizer fileTokenizer( input, "\n", wxTOKEN_STRTOK );
375
376 while( fileTokenizer.HasMoreTokens() )
377 {
378 wxStringTokenizer lineTokenizer( fileTokenizer.GetNextToken(), "\t" );
379
380 wxString cmdName = lineTokenizer.GetNextToken();
381 wxString keyName = lineTokenizer.GetNextToken();
382
383 if( !cmdName.IsEmpty() )
384 aHotKeys[ cmdName.ToStdString() ] = KeyCodeFromKeyName( keyName );
385 }
386}
387
388
389void ReadHotKeyConfigIntoActions( const wxString& aFileName, std::vector<TOOL_ACTION*>& aActions )
390{
391 std::map<std::string, int> hotkeys;
392
393 // Read the existing config (all hotkeys)
394 ReadHotKeyConfig( aFileName, hotkeys );
395
396 // Set each tool action hotkey to the config file hotkey if present
397 for( TOOL_ACTION* action : aActions )
398 if( hotkeys.find( action->GetName() ) != hotkeys.end() )
399 action->SetHotKey( hotkeys[action->GetName()] );
400}
401
402
403int WriteHotKeyConfig( const std::vector<TOOL_ACTION*>& aActions )
404{
405 std::map<std::string, int> hotkeys;
406 wxFileName fn( "user" );
407
408 fn.SetExt( HotkeyFileExtension );
410
411 // Read the existing config (all hotkeys)
412 ReadHotKeyConfig( fn.GetFullPath(), hotkeys );
413
414 // Overlay the current app's hotkey definitions onto the map
415 for( const TOOL_ACTION* action : aActions )
416 hotkeys[ action->GetName() ] = action->GetHotKey();
417
418 // Write entire hotkey set
419 wxFFileOutputStream outStream( fn.GetFullPath() );
420 wxTextOutputStream txtStream( outStream, wxEOL_UNIX );
421
422 for( const std::pair<const std::string, int>& entry : hotkeys )
423 txtStream << entry.first << "\t" << KeyNameFromKeyCode( entry.second ) << endl;
424
425 txtStream.Flush();
426 outStream.Close();
427
428 return 1;
429}
430
431
432int ReadLegacyHotkeyConfig( const wxString& aAppname, std::map<std::string, int>& aMap )
433{
434 // For Eeschema and Pcbnew frames, we read the new combined file.
435 // For other kifaces, we read the frame-based file
436 if( aAppname == LIB_EDIT_FRAME_NAME || aAppname == SCH_EDIT_FRAME_NAME )
437 {
439 }
440 else if( aAppname == PCB_EDIT_FRAME_NAME || aAppname == FOOTPRINT_EDIT_FRAME_NAME )
441 {
443 }
444
445 return ReadLegacyHotkeyConfigFile( aAppname, aMap );
446}
447
448
449int ReadLegacyHotkeyConfigFile( const wxString& aFilename, std::map<std::string, int>& aMap )
450{
451 wxFileName fn( aFilename );
452
453 fn.SetExt( HotkeyFileExtension );
455
456 if( !wxFile::Exists( fn.GetFullPath() ) )
457 return 0;
458
459 wxFFile cfgfile( fn.GetFullPath(), "rb" );
460
461 if( !cfgfile.IsOpened() ) // There is a problem to open file
462 return 0;
463
464 // get length
465 wxFileOffset size = cfgfile.Length();
466
467 // read data
468 std::vector<char> buffer( size );
469 cfgfile.Read( buffer.data(), size );
470 wxString data( buffer.data(), wxConvUTF8, size );
471
472 // Is this the wxConfig format? If so, remove "Keys=" and parse the newlines.
473 if( data.StartsWith( wxT("Keys="), &data ) )
474 data.Replace( "\\n", "\n", true );
475
476 // parse
477 wxStringTokenizer tokenizer( data, L"\r\n", wxTOKEN_STRTOK );
478
479 while( tokenizer.HasMoreTokens() )
480 {
481 wxString line = tokenizer.GetNextToken();
482 wxStringTokenizer lineTokenizer( line );
483
484 wxString line_type = lineTokenizer.GetNextToken();
485
486 if( line_type[0] == '#' ) // comment
487 continue;
488
489 if( line_type[0] == '[' ) // tags ignored reading legacy hotkeys
490 continue;
491
492 if( line_type == wxT( "$Endlist" ) )
493 break;
494
495 if( line_type != wxT( "shortcut" ) )
496 continue;
497
498 // Get the key name
499 lineTokenizer.SetString( lineTokenizer.GetString(), L"\"\r\n\t ", wxTOKEN_STRTOK );
500 wxString keyname = lineTokenizer.GetNextToken();
501
502 wxString remainder = lineTokenizer.GetString();
503
504 // Get the command name
505 wxString fctname = remainder.AfterFirst( '\"' ).BeforeFirst( '\"' );
506
507 // Add the pair to the map
508 aMap[ fctname.ToStdString() ] = KeyCodeFromKeyName( keyname );
509 }
510
511 // cleanup
512 cfgfile.Close();
513 return 1;
514}
515
516
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.
Represent a single user action.
Definition: tool_action.h:68
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
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)
#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.
void ReadHotKeyConfig(const wxString &aFileName, std::map< std::string, int > &aHotKeys)
Reads a hotkey config file into a 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:140
@ MD_CTRL
Definition: tool_event.h:139
@ MD_SHIFT
Definition: tool_event.h:138
Definition of file extensions used in Kicad.