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-2011 Wayne Stambaugh <stambaughw@verizon.net>
6  * Copyright (C) 1992-2019 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_i.h>
27 #include <hotkeys_basic.h>
28 #include <id.h>
29 #include <kicad_string.h>
30 #include <eda_base_frame.h>
31 #include <eda_draw_frame.h>
33 
34 #include <tool/tool_manager.h>
36 #include <wx/apptrait.h>
37 #include <wx/stdpaths.h>
38 #include <wx/tokenzr.h>
39 #include <tool/tool_action.h>
40 
41 
42 /*
43  * class to handle the printable name and the keycode
44  */
46 {
47  const wxChar* m_Name;
48  int m_KeyCode;
49 };
50 
51 /* table giving the hotkey name from the hotkey code, for special keys
52  * Note : when modifiers (ATL, SHIFT, CTRL) do not modify
53  * the code of the key, do need to enter the modified key code
54  * For instance wxT( "F1" ), WXK_F1 handle F1, AltF1, CtrlF1 ...
55  * Key names are:
56  * "Space","Ctrl+Space","Alt+Space" or
57  * "Alt+A","Ctrl+F1", ...
58  */
59 #define KEY_NON_FOUND -1
61 {
62  { wxT( "F1" ), WXK_F1 },
63  { wxT( "F2" ), WXK_F2 },
64  { wxT( "F3" ), WXK_F3 },
65  { wxT( "F4" ), WXK_F4 },
66  { wxT( "F5" ), WXK_F5 },
67  { wxT( "F6" ), WXK_F6 },
68  { wxT( "F7" ), WXK_F7 },
69  { wxT( "F8" ), WXK_F8 },
70  { wxT( "F9" ), WXK_F9 },
71  { wxT( "F10" ), WXK_F10 },
72  { wxT( "F11" ), WXK_F11 },
73  { wxT( "F12" ), WXK_F12 },
74 
75  { wxT( "Esc" ), WXK_ESCAPE },
76  { wxT( "Del" ), WXK_DELETE },
77  { wxT( "Tab" ), WXK_TAB },
78  { wxT( "Back" ), WXK_BACK },
79  { wxT( "Ins" ), WXK_INSERT },
80 
81  { wxT( "Home" ), WXK_HOME },
82  { wxT( "End" ), WXK_END },
83  { wxT( "PgUp" ), WXK_PAGEUP },
84  { wxT( "PgDn" ), WXK_PAGEDOWN },
85 
86  { wxT( "Up" ), WXK_UP },
87  { wxT( "Down" ), WXK_DOWN },
88  { wxT( "Left" ), WXK_LEFT },
89  { wxT( "Right" ), WXK_RIGHT },
90 
91  { wxT( "Return" ), WXK_RETURN },
92 
93  { wxT( "Space" ), WXK_SPACE },
94 
95  { wxT( "Num Pad 0" ), WXK_NUMPAD0 },
96  { wxT( "Num Pad 1" ), WXK_NUMPAD1 },
97  { wxT( "Num Pad 2" ), WXK_NUMPAD2 },
98  { wxT( "Num Pad 3" ), WXK_NUMPAD3 },
99  { wxT( "Num Pad 4" ), WXK_NUMPAD4 },
100  { wxT( "Num Pad 5" ), WXK_NUMPAD5 },
101  { wxT( "Num Pad 6" ), WXK_NUMPAD6 },
102  { wxT( "Num Pad 7" ), WXK_NUMPAD7 },
103  { wxT( "Num Pad 8" ), WXK_NUMPAD8 },
104  { wxT( "Num Pad 9" ), WXK_NUMPAD9 },
105  { wxT( "Num Pad +" ), WXK_NUMPAD_ADD },
106  { wxT( "Num Pad -" ), WXK_NUMPAD_SUBTRACT },
107  { wxT( "Num Pad *" ), WXK_NUMPAD_MULTIPLY },
108  { wxT( "Num Pad /" ), WXK_NUMPAD_DIVIDE },
109  { wxT( "Num Pad ." ), WXK_NUMPAD_SEPARATOR },
110 
111  { wxT( "" ), 0 },
112 
113  { wxT( "Click" ), PSEUDO_WXK_CLICK },
114  { wxT( "DblClick" ), PSEUDO_WXK_DBLCLICK },
115  { wxT( "Wheel" ), PSEUDO_WXK_WHEEL },
116 
117  // Do not change this line: end of list
118  { wxT( "" ), KEY_NON_FOUND }
119 };
120 
121 // name of modifier keys.
122 // Note: the Ctrl key is Cmd key on Mac OS X.
123 // However, in wxWidgets defs, the key WXK_CONTROL is the Cmd key,
124 // so the code using WXK_CONTROL should be ok on any system.
125 // (on Mac OS X the actual Ctrl key code is WXK_RAW_CONTROL)
126 #ifdef __WXMAC__
127 #define USING_MAC_CMD
128 #endif
129 
130 #ifdef USING_MAC_CMD
131 #define MODIFIER_CTRL wxT( "Cmd+" )
132 #else
133 #define MODIFIER_CTRL wxT( "Ctrl+" )
134 #endif
135 #define MODIFIER_CMD_MAC wxT( "Cmd+" )
136 #define MODIFIER_CTRL_BASE wxT( "Ctrl+" )
137 #define MODIFIER_ALT wxT( "Alt+" )
138 #define MODIFIER_SHIFT wxT( "Shift+" )
139 
140 
150 wxString KeyNameFromKeyCode( int aKeycode, bool* aIsFound )
151 {
152  wxString keyname, modifier, fullkeyname;
153  int ii;
154  bool found = false;
155 
156  // Assume keycode of 0 is "unassigned"
157  if( (aKeycode & MD_CTRL) != 0 )
158  modifier << MODIFIER_CTRL;
159 
160  if( (aKeycode & MD_ALT) != 0 )
161  modifier << MODIFIER_ALT;
162 
163  if( (aKeycode & MD_SHIFT) != 0 )
164  modifier << MODIFIER_SHIFT;
165 
166  aKeycode &= ~( MD_CTRL | MD_ALT | MD_SHIFT );
167 
168  if( (aKeycode > ' ') && (aKeycode < 0x7F ) )
169  {
170  found = true;
171  keyname.Append( (wxChar)aKeycode );
172  }
173  else
174  {
175  for( ii = 0; ; ii++ )
176  {
177  if( hotkeyNameList[ii].m_KeyCode == KEY_NON_FOUND ) // End of list
178  {
179  keyname = wxT( "<unknown>" );
180  break;
181  }
182 
183  if( hotkeyNameList[ii].m_KeyCode == aKeycode )
184  {
185  keyname = hotkeyNameList[ii].m_Name;
186  found = true;
187  break;
188  }
189  }
190  }
191 
192  if( aIsFound )
193  *aIsFound = found;
194 
195  fullkeyname = modifier + keyname;
196  return fullkeyname;
197 }
198 
199 
207 wxString AddHotkeyName( const wxString& aText, int aHotKey, HOTKEY_ACTION_TYPE aStyle )
208 {
209  wxString msg = aText;
210  wxString keyname = KeyNameFromKeyCode( aHotKey );
211 
212  if( !keyname.IsEmpty() )
213  {
214  switch( aStyle )
215  {
216  case IS_HOTKEY:
217  {
218  // Don't add a suffix for unassigned hotkeys:
219  // WX spews debug from wxAcceleratorEntry::ParseAccel if it doesn't
220  // recognise the keyname, which is the case for <unassigned>.
221  if( aHotKey != 0 )
222  {
223  msg << wxT( "\t" ) << keyname;
224  }
225  break;
226  }
227  case IS_COMMENT:
228  {
229  msg << wxT( " (" ) << keyname << wxT( ")" );
230  break;
231  }
232  }
233  }
234 
235 #ifdef USING_MAC_CMD
236  // On OSX, the modifier equivalent to the Ctrl key of PCs
237  // is the Cmd key, but in code we should use Ctrl as prefix in menus
238  msg.Replace( MODIFIER_CMD_MAC, MODIFIER_CTRL_BASE );
239 #endif
240 
241  return msg;
242 }
243 
244 
249 int KeyCodeFromKeyName( const wxString& keyname )
250 {
251  int ii, keycode = KEY_NON_FOUND;
252 
253  // Search for modifiers: Ctrl+ Alt+ and Shift+
254  // Note: on Mac OSX, the Cmd key is equiv here to Ctrl
255  wxString key = keyname;
256  wxString prefix;
257  int modifier = 0;
258 
259  while( true )
260  {
261  prefix.Empty();
262 
263  if( key.StartsWith( MODIFIER_CTRL_BASE ) )
264  {
265  modifier |= MD_CTRL;
266  prefix = MODIFIER_CTRL_BASE;
267  }
268  else if( key.StartsWith( MODIFIER_CMD_MAC ) )
269  {
270  modifier |= MD_CTRL;
271  prefix = MODIFIER_CMD_MAC;
272  }
273  else if( key.StartsWith( MODIFIER_ALT ) )
274  {
275  modifier |= MD_ALT;
276  prefix = MODIFIER_ALT;
277  }
278  else if( key.StartsWith( MODIFIER_SHIFT ) )
279  {
280  modifier |= MD_SHIFT;
281  prefix = MODIFIER_SHIFT;
282  }
283  else
284  {
285  break;
286  }
287 
288  if( !prefix.IsEmpty() )
289  key.Remove( 0, prefix.Len() );
290  }
291 
292  if( (key.length() == 1) && (key[0] > ' ') && (key[0] < 0x7F) )
293  {
294  keycode = key[0];
295  keycode += modifier;
296  return keycode;
297  }
298 
299  for( ii = 0; hotkeyNameList[ii].m_KeyCode != KEY_NON_FOUND; ii++ )
300  {
301  if( key.CmpNoCase( hotkeyNameList[ii].m_Name ) == 0 )
302  {
303  keycode = hotkeyNameList[ii].m_KeyCode + modifier;
304  break;
305  }
306  }
307 
308  return keycode;
309 }
310 
311 
312 /*
313  * DisplayHotkeyList
314  * Displays the hotkeys registered with the given tool manager.
315  */
316 void DisplayHotkeyList( EDA_BASE_FRAME* aParent, TOOL_MANAGER* aToolManager )
317 {
318  DIALOG_LIST_HOTKEYS dlg( aParent, aToolManager );
319  dlg.ShowModal();
320 }
321 
322 
323 void ReadHotKeyConfig( wxString fileName, std::map<std::string, int>& aHotKeys )
324 {
325  if( fileName.IsEmpty() )
326  {
327  wxFileName fn( "user" );
328  fn.SetExt( DEFAULT_HOTKEY_FILENAME_EXT );
330  fileName = fn.GetFullPath();
331  }
332 
333  if( !wxFile::Exists( fileName ) )
334  return;
335 
336  wxFile file( fileName, wxFile::OpenMode::read );
337 
338  if( !file.IsOpened() ) // There is a problem to open file
339  return;
340 
341  wxString input;
342  file.ReadAll( &input );
343  input.Replace( "\r\n", "\n" ); // Convert Windows files to Unix line-ends
344  wxStringTokenizer fileTokenizer( input, "\n", wxTOKEN_STRTOK );
345 
346  while( fileTokenizer.HasMoreTokens() )
347  {
348  wxStringTokenizer lineTokenizer( fileTokenizer.GetNextToken(), "\t" );
349 
350  wxString cmdName = lineTokenizer.GetNextToken();
351  wxString keyName = lineTokenizer.GetNextToken();
352 
353  if( !cmdName.IsEmpty() )
354  aHotKeys[ cmdName.ToStdString() ] = KeyCodeFromKeyName( keyName );
355  }
356 }
357 
358 
359 int WriteHotKeyConfig( const std::map<std::string, TOOL_ACTION*>& aActionMap )
360 {
361  std::map<std::string, int> hotkeys;
362  wxFileName fn( "user" );
363 
364  fn.SetExt( DEFAULT_HOTKEY_FILENAME_EXT );
366 
367  // Read the existing config (all hotkeys)
368  //
369  ReadHotKeyConfig( fn.GetFullPath(), hotkeys );
370 
371  // Overlay the current app's hotkey definitions onto the map
372  //
373  for( const auto& ii : aActionMap )
374  hotkeys[ ii.first ] = ii.second->GetHotKey();
375 
376  // Write entire hotkey set
377  //
378  wxFile file( fn.GetFullPath(), wxFile::OpenMode::write );
379 
380  for( const auto& ii : hotkeys )
381  file.Write( wxString::Format( "%s\t%s\n", ii.first, KeyNameFromKeyCode( ii.second ) ) );
382 
383  return 1;
384 }
385 
386 
387 int ReadLegacyHotkeyConfig( const wxString& aAppname, std::map<std::string, int>& aMap )
388 {
389  // For Eeschema and Pcbnew frames, we read the new combined file.
390  // For other kifaces, we read the frame-based file
391  if( aAppname == LIB_EDIT_FRAME_NAME || aAppname == SCH_EDIT_FRAME_NAME )
392  {
394  }
395  else if( aAppname == PCB_EDIT_FRAME_NAME || aAppname == FOOTPRINT_EDIT_FRAME_NAME )
396  {
398  }
399 
400  return ReadLegacyHotkeyConfigFile( aAppname, aMap );
401 }
402 
403 
404 int ReadLegacyHotkeyConfigFile( const wxString& aFilename, std::map<std::string, int>& aMap )
405 {
406  wxFileName fn( aFilename );
407 
408  fn.SetExt( DEFAULT_HOTKEY_FILENAME_EXT );
410 
411  if( !wxFile::Exists( fn.GetFullPath() ) )
412  return 0;
413 
414  wxFile cfgfile( fn.GetFullPath() );
415 
416  if( !cfgfile.IsOpened() ) // There is a problem to open file
417  return 0;
418 
419  // get length
420  cfgfile.SeekEnd();
421  wxFileOffset size = cfgfile.Tell();
422  cfgfile.Seek( 0 );
423 
424  // read data
425  std::vector<char> buffer( size );
426  cfgfile.Read( buffer.data(), size );
427  wxString data( buffer.data(), wxConvUTF8, size );
428 
429  // Is this the wxConfig format? If so, remove "Keys=" and parse the newlines.
430  if( data.StartsWith( wxT("Keys="), &data ) )
431  data.Replace( "\\n", "\n", true );
432 
433  // parse
434  wxStringTokenizer tokenizer( data, L"\r\n", wxTOKEN_STRTOK );
435 
436  while( tokenizer.HasMoreTokens() )
437  {
438  wxString line = tokenizer.GetNextToken();
439  wxStringTokenizer lineTokenizer( line );
440 
441  wxString line_type = lineTokenizer.GetNextToken();
442 
443  if( line_type[0] == '#' ) // comment
444  continue;
445 
446  if( line_type[0] == '[' ) // tags ignored reading legacy hotkeys
447  continue;
448 
449  if( line_type == wxT( "$Endlist" ) )
450  break;
451 
452  if( line_type != wxT( "shortcut" ) )
453  continue;
454 
455  // Get the key name
456  lineTokenizer.SetString( lineTokenizer.GetString(), L"\"\r\n\t ", wxTOKEN_STRTOK );
457  wxString keyname = lineTokenizer.GetNextToken();
458 
459  wxString remainder = lineTokenizer.GetString();
460 
461  // Get the command name
462  wxString fctname = remainder.AfterFirst( '\"' ).BeforeFirst( '\"' );
463 
464  // Add the pair to the map
465  aMap[ fctname.ToStdString() ] = KeyCodeFromKeyName( keyname );
466  }
467 
468  // cleanup
469  cfgfile.Close();
470  return 1;
471 }
472 
473 
const wxChar * m_Name
#define MODIFIER_CTRL_BASE
#define KEY_NON_FOUND
Hotkey list dialog (as opposed to editor)
A dialog that presents the user with a read-only list of hotkeys and their current bindings.
wxString AddHotkeyName(const wxString &aText, int aHotKey, HOTKEY_ACTION_TYPE aStyle)
AddHotkeyName.
#define PSEUDO_WXK_WHEEL
Definition: hotkeys_basic.h:52
void ReadHotKeyConfig(wxString fileName, std::map< std::string, int > &aHotKeys)
Function ReadotKeyConfig Reads a hotkey config file into a map.
int ReadLegacyHotkeyConfigFile(const wxString &aFilename, std::map< std::string, int > &aMap)
Function ReadLegacyHotkeyConfigFile Read hotkey configuration for a given app, possibly before the fr...
#define LIB_EDIT_FRAME_NAME
TOOL_MANAGER.
Definition: tool_manager.h:51
#define MODIFIER_CTRL
#define SCH_EDIT_FRAME_NAME
int ReadLegacyHotkeyConfig(const wxString &aAppname, std::map< std::string, int > &aMap)
Function ReadLegacyHotkeyConfig Read configuration data and fill the current hotkey list with hotkeys...
void DisplayHotkeyList(EDA_BASE_FRAME *aParent, TOOL_MANAGER *aToolManager)
Function DisplayHotkeyList Displays the current hotkey list.
#define PSEUDO_WXK_CLICK
Definition: hotkeys_basic.h:50
int KeyCodeFromKeyName(const wxString &keyname)
Function KeyCodeFromKeyName return the key code from its user-friendly key name (ie: "Ctrl+M")
#define MODIFIER_SHIFT
Base window classes and related definitions.
#define FOOTPRINT_EDIT_FRAME_NAME
static wxString GetUserSettingsPath()
Return the user configuration path used to store KiCad's configuration files.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:201
The base frame for deriving all KiCad main window classes.
#define MODIFIER_ALT
#define MODIFIER_CMD_MAC
#define EESCHEMA_HOTKEY_NAME
Definition: hotkeys_basic.h:31
wxString KeyNameFromKeyCode(int aKeycode, bool *aIsFound)
Function KeyNameFromKeyCode return the key name from the key code Only some wxWidgets key values are ...
#define PCB_EDIT_FRAME_NAME
#define DEFAULT_HOTKEY_FILENAME_EXT
Definition: hotkeys_basic.h:30
#define PSEUDO_WXK_DBLCLICK
Definition: hotkeys_basic.h:51
HOTKEY_ACTION_TYPE
An helper enum for AddHotkeyName function In menus we can add a hot key, or an accelerator ,...
Definition: hotkeys_basic.h:76
#define PCBNEW_HOTKEY_NAME
Definition: hotkeys_basic.h:32
int WriteHotKeyConfig(const std::map< std::string, TOOL_ACTION * > &aActionMap)
Function WriteHotKeyConfig Updates the hotkeys config file with the hotkeys from the given actions ma...
static struct hotkey_name_descr hotkeyNameList[]