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