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 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,
352 std::map<std::string, std::pair<int, int>>& aHotKeys )
353{
354 wxString fileName = aFileName;
355
356 if( fileName.IsEmpty() )
357 {
358 wxFileName fn( wxS( "user" ) );
359 fn.SetExt( FILEEXT::HotkeyFileExtension );
360 fn.SetPath( PATHS::GetUserSettingsPath() );
361 fileName = fn.GetFullPath();
362 }
363
364 if( !wxFile::Exists( fileName ) )
365 return;
366
367 wxFFile file( fileName, "rb" );
368
369 if( !file.IsOpened() ) // There is a problem to open file
370 return;
371
372 wxString input;
373 file.ReadAll( &input );
374 input.Replace( "\r\n", "\n" ); // Convert Windows files to Unix line-ends
375 wxStringTokenizer fileTokenizer( input, wxS( "\n" ), wxTOKEN_STRTOK );
376
377 while( fileTokenizer.HasMoreTokens() )
378 {
379 wxStringTokenizer lineTokenizer( fileTokenizer.GetNextToken(), wxS( "\t" ) );
380
381 wxString cmdName = lineTokenizer.GetNextToken();
382 wxString primary = lineTokenizer.GetNextToken();
383 wxString secondary = lineTokenizer.GetNextToken();
384
385 if( !cmdName.IsEmpty() )
386 aHotKeys[cmdName.ToStdString()] = std::pair<int, int>(
387 KeyCodeFromKeyName( primary ), KeyCodeFromKeyName( secondary ) );
388 }
389}
390
391
392void ReadHotKeyConfigIntoActions( const wxString& aFileName, std::vector<TOOL_ACTION*>& aActions )
393{
394 std::map<std::string, std::pair<int, int>> hotkeys;
395
396 // Read the existing config (all hotkeys)
397 ReadHotKeyConfig( aFileName, hotkeys );
398
399 // Set each tool action hotkey to the config file hotkey if present
400 for( TOOL_ACTION* action : aActions )
401 if( hotkeys.find( action->GetName() ) != hotkeys.end() )
402 {
403 std::pair<int, int> keys = hotkeys[action->GetName()];
404 action->SetHotKey( keys.first, keys.second );
405 }
406}
407
408
409int WriteHotKeyConfig( const std::vector<TOOL_ACTION*>& aActions )
410{
411 std::map<std::string, std::pair<int, int>> hotkeys;
412 wxFileName fn( "user" );
413
414 fn.SetExt( FILEEXT::HotkeyFileExtension );
415 fn.SetPath( PATHS::GetUserSettingsPath() );
416
417 // Read the existing config (all hotkeys)
418 ReadHotKeyConfig( fn.GetFullPath(), hotkeys );
419
420 // Overlay the current app's hotkey definitions onto the map
421 for( const TOOL_ACTION* action : aActions )
422 hotkeys[ action->GetName() ] = std::pair<int, int>( action->GetHotKey(), action->GetHotKeyAlt() );
423
424 // Write entire hotkey set
425 wxFFileOutputStream outStream( fn.GetFullPath() );
426 wxTextOutputStream txtStream( outStream, wxEOL_UNIX );
427
428 for( const std::pair<const std::string, std::pair<int, int>>& entry : hotkeys )
429 txtStream << entry.first
430 << "\t" << KeyNameFromKeyCode( entry.second.first )
431 << "\t" << KeyNameFromKeyCode( entry.second.second ) << endl;
432
433 txtStream.Flush();
434 outStream.Close();
435
436 return 1;
437}
438
439
440int ReadLegacyHotkeyConfig( const wxString& aAppname, std::map<std::string, int>& aMap )
441{
442 // For Eeschema and Pcbnew frames, we read the new combined file.
443 // For other kifaces, we read the frame-based file
444 if( aAppname == LIB_EDIT_FRAME_NAME || aAppname == SCH_EDIT_FRAME_NAME )
445 {
447 }
448 else if( aAppname == PCB_EDIT_FRAME_NAME || aAppname == FOOTPRINT_EDIT_FRAME_NAME )
449 {
451 }
452
453 return ReadLegacyHotkeyConfigFile( aAppname, aMap );
454}
455
456
457int ReadLegacyHotkeyConfigFile( const wxString& aFilename, std::map<std::string, int>& aMap )
458{
459 wxFileName fn( aFilename );
460
461 fn.SetExt( FILEEXT::HotkeyFileExtension );
462 fn.SetPath( PATHS::GetUserSettingsPath() );
463
464 if( !wxFile::Exists( fn.GetFullPath() ) )
465 return 0;
466
467 wxFFile cfgfile( fn.GetFullPath(), "rb" );
468
469 if( !cfgfile.IsOpened() ) // There is a problem to open file
470 return 0;
471
472 // get length
473 wxFileOffset size = cfgfile.Length();
474
475 // read data
476 std::vector<char> buffer( size );
477 cfgfile.Read( buffer.data(), size );
478 wxString data( buffer.data(), wxConvUTF8, size );
479
480 // Is this the wxConfig format? If so, remove "Keys=" and parse the newlines.
481 if( data.StartsWith( wxT("Keys="), &data ) )
482 data.Replace( "\\n", "\n", true );
483
484 // parse
485 wxStringTokenizer tokenizer( data, L"\r\n", wxTOKEN_STRTOK );
486
487 while( tokenizer.HasMoreTokens() )
488 {
489 wxString line = tokenizer.GetNextToken();
490 wxStringTokenizer lineTokenizer( line );
491
492 wxString line_type = lineTokenizer.GetNextToken();
493
494 if( line_type[0] == '#' ) // comment
495 continue;
496
497 if( line_type[0] == '[' ) // tags ignored reading legacy hotkeys
498 continue;
499
500 if( line_type == wxT( "$Endlist" ) )
501 break;
502
503 if( line_type != wxT( "shortcut" ) )
504 continue;
505
506 // Get the key name
507 lineTokenizer.SetString( lineTokenizer.GetString(), L"\"\r\n\t ", wxTOKEN_STRTOK );
508 wxString keyname = lineTokenizer.GetNextToken();
509
510 wxString remainder = lineTokenizer.GetString();
511
512 // Get the command name
513 wxString fctname = remainder.AfterFirst( '\"' ).BeforeFirst( '\"' );
514
515 // Add the pair to the map
516 aMap[ fctname.ToStdString() ] = KeyCodeFromKeyName( keyname );
517 }
518
519 // cleanup
520 cfgfile.Close();
521 return 1;
522}
523
524
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:506
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.