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