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