/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2013 Jean-Pierre Charras, j-p.charras at wanadoo.fr * Copyright (C) 2010-2011 Wayne Stambaugh * Copyright (C) 1992-2011 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ /** * @file hotkeys_basic.cpp * @brief Some functions to handle hotkeys in KiCad */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define HOTKEYS_CONFIG_KEY wxT( "Keys" ) wxString g_CommonSectionTag( wxT( "[common]" ) ); wxString g_SchematicSectionTag( wxT( "[eeschema]" ) ); wxString g_LibEditSectionTag( wxT( "[libedit]" ) ); wxString g_BoardEditorSectionTag( wxT( "[pcbnew]" ) ); wxString g_ModuleEditSectionTag( wxT( "[footprinteditor]" ) ); /* Class to handle hotkey commnands. hotkeys have a default value * This class allows the real key code changed by user from a key code list * file. */ EDA_HOTKEY::EDA_HOTKEY( const wxChar* infomsg, int idcommand, int keycode, int idmenuevent ) { m_KeyCode = keycode; // Key code (ascii value for ascii keys // or wxWidgets code for function key m_InfoMsg = infomsg; // info message. m_Idcommand = idcommand; // internal id for the corresponding // command (see hotkey_id_commnand list) m_IdMenuEvent = idmenuevent; // id to call the corresponding event // (if any) (see id.h) } EDA_HOTKEY::EDA_HOTKEY( const EDA_HOTKEY* base ) { m_KeyCode = base->m_KeyCode; m_InfoMsg = base->m_InfoMsg; m_Idcommand = base->m_Idcommand; m_IdMenuEvent = base->m_IdMenuEvent; } EDA_HOTKEY_CLIENT_DATA::~EDA_HOTKEY_CLIENT_DATA() { } /* class to handle the printable name and the keycode */ struct hotkey_name_descr { const wxChar* m_Name; int m_KeyCode; }; /* table giving the hotkey name from the hotkey code, for special keys * Note : when modifiers (ATL, SHIFT, CTRL) do not modify * the code of the key, do need to enter the modified key code * For instance wxT( "F1" ), WXK_F1 handle F1, AltF1, CtrlF1 ... * Key names are: * "Space","Ctrl+Space","Alt+Space" or * "Alt+A","Ctrl+F1", ... */ static struct hotkey_name_descr s_Hotkey_Name_List[] = { { wxT( "F1" ), WXK_F1 }, { wxT( "F2" ), WXK_F2 }, { wxT( "F3" ), WXK_F3 }, { wxT( "F4" ), WXK_F4 }, { wxT( "F5" ), WXK_F5 }, { wxT( "F6" ), WXK_F6 }, { wxT( "F7" ), WXK_F7 }, { wxT( "F8" ), WXK_F8 }, { wxT( "F9" ), WXK_F9 }, { wxT( "F10" ), WXK_F10 }, { wxT( "F11" ), WXK_F11 }, { wxT( "F12" ), WXK_F12 }, { wxT( "Esc" ), WXK_ESCAPE }, { wxT( "Del" ), WXK_DELETE }, { wxT( "Tab" ), WXK_TAB }, { wxT( "BkSp" ), WXK_BACK }, { wxT( "Ins" ), WXK_INSERT }, { wxT( "Home" ), WXK_HOME }, { wxT( "End" ), WXK_END }, { wxT( "PgUp" ), WXK_PAGEUP }, { wxT( "PgDn" ), WXK_PAGEDOWN }, { wxT( "Up" ), WXK_UP }, { wxT( "Down" ), WXK_DOWN }, { wxT( "Left" ), WXK_LEFT }, { wxT( "Right" ), WXK_RIGHT }, { wxT( "Space" ), WXK_SPACE }, // Do not change this line: end of list { wxT( "" ), 0 } }; #define MODIFIER_CTRL wxT( "Ctrl+" ) #define MODIFIER_ALT wxT( "Alt+" ) #define MODIFIER_SHIFT wxT( "Shift+" ) /** * Function ReturnKeyNameFromKeyCode * return the key name from the key code * Only some wxWidgets key values are handled for function key ( see * s_Hotkey_Name_List[] ) * @param aKeycode = key code (ascii value, or wxWidgets value for function keys) * @param aIsFound = a pointer to a bool to return true if found, or false. an be NULL default) * @return the key name in a wxString */ wxString ReturnKeyNameFromKeyCode( int aKeycode, bool* aIsFound ) { wxString keyname, modifier, fullkeyname; int ii; bool found = false; if( (aKeycode & GR_KB_CTRL) != 0 ) modifier << MODIFIER_CTRL; if( (aKeycode & GR_KB_ALT) != 0 ) modifier << MODIFIER_ALT; if( (aKeycode & GR_KB_SHIFT) != 0 ) modifier << MODIFIER_SHIFT; aKeycode &= ~( GR_KB_CTRL | GR_KB_ALT | GR_KB_SHIFT ); if( (aKeycode > ' ') && (aKeycode < 0x7F ) ) { found = true; keyname.Append( (wxChar)aKeycode ); } else { for( ii = 0; ; ii++ ) { if( s_Hotkey_Name_List[ii].m_KeyCode == 0 ) // End of list { keyname = wxT( "" ); break; } if( s_Hotkey_Name_List[ii].m_KeyCode == aKeycode ) { keyname = s_Hotkey_Name_List[ii].m_Name; found = true; break; } } } if( aIsFound ) *aIsFound = found; fullkeyname = modifier + keyname; return fullkeyname; } /* * helper function use in AddHotkeyName to calculate an accelerator string * In some menus, accelerators do not perform exactly the same action as * the hotkey that perform a similar action. * this is usually the case when this action uses the current mouse position * for instance zoom action is ran from the F1 key or the Zoom menu. * a zoom uses the mouse position from a hot key and not from the menu * In this case, the accelerator if Shift+ * But for many keys, the Shift modifier is not usable, and the accelerator is Alt+ */ static void AddModifierToKey( wxString& aFullKey, const wxString & aKey ) { if( (aKey.Length() == 1) && (aKey[0] >= 'A') && (aKey[0] <= 'Z')) // We can use Shift+ as accelerator and for hot key aFullKey << wxT( "\t" ) << MODIFIER_SHIFT << aKey; else // We must use Alt+ as accelerator ans for hot key aFullKey << wxT( "\t" ) << MODIFIER_ALT << aKey; } /* AddHotkeyName * Add the key name from the Command id value ( m_Idcommand member value) * aText = a wxString. returns aText + key name * aList = pointer to a EDA_HOTKEY list of commands * aCommandId = Command Id value * aShortCutType = IS_HOTKEY to add (shortcuts in menus, same as hotkeys) * IS_ACCELERATOR to add (accelerators in menus, not hotkeys) * IS_COMMENT to add <(keyname)> mainly in tool tips * Return a wxString (aTest + key name) if key found or aText without modification */ wxString AddHotkeyName( const wxString& aText, EDA_HOTKEY** aList, int aCommandId, HOTKEY_ACTION_TYPE aShortCutType ) { wxString msg = aText; wxString keyname; if( aList ) keyname = ReturnKeyNameFromCommandId( aList, aCommandId ); if( !keyname.IsEmpty() ) { switch( aShortCutType ) { case IS_HOTKEY: msg << wxT( "\t" ) << keyname; break; case IS_ACCELERATOR: AddModifierToKey( msg, keyname ); break; case IS_COMMENT: msg << wxT( " (" ) << keyname << wxT( ")" ); break; } } return msg; } /* AddHotkeyName * Add the key name from the Command id value ( m_Idcommand member value) * aText = a wxString. returns aText + key name * aList = pointer to a EDA_HOTKEY_CONFIG DescrList of commands * aCommandId = Command Id value * aShortCutType = IS_HOTKEY to add (active shortcuts in menus) * IS_ACCELERATOR to add (active accelerators in menus) * IS_COMMENT to add <(keyname)> * Return a wxString (aText + key name) if key found or aText without modification */ wxString AddHotkeyName( const wxString& aText, struct EDA_HOTKEY_CONFIG* aDescList, int aCommandId, HOTKEY_ACTION_TYPE aShortCutType ) { wxString msg = aText; wxString keyname; EDA_HOTKEY** List; if( aDescList ) { for( ; aDescList->m_HK_InfoList != NULL; aDescList++ ) { List = aDescList->m_HK_InfoList; keyname = ReturnKeyNameFromCommandId( List, aCommandId ); if( !keyname.IsEmpty() ) { switch( aShortCutType ) { case IS_HOTKEY: msg << wxT( "\t" ) << keyname; break; case IS_ACCELERATOR: AddModifierToKey( msg, keyname ); break; case IS_COMMENT: msg << wxT( " (" ) << keyname << wxT( ")" ); break; } break; } } } return msg; } /** * Function ReturnKeyNameFromCommandId * return the key name from the Command id value ( m_Idcommand member value) * @param aList = pointer to a EDA_HOTKEY list of commands * @param aCommandId = Command Id value * @return the key name in a wxString */ wxString ReturnKeyNameFromCommandId( EDA_HOTKEY** aList, int aCommandId ) { wxString keyname; for( ; *aList != NULL; aList++ ) { EDA_HOTKEY* hk_decr = *aList; if( hk_decr->m_Idcommand == aCommandId ) { keyname = ReturnKeyNameFromKeyCode( hk_decr->m_KeyCode ); break; } } return keyname; } /** * Function ReturnKeyCodeFromKeyName * return the key code from its key name * Only some wxWidgets key values are handled for function key * @param keyname = wxString key name to find in s_Hotkey_Name_List[], * like F2 or space or an usual (ascii) char. * @return the key code */ int ReturnKeyCodeFromKeyName( const wxString& keyname ) { int ii, keycode = 0; // Search for modifiers: Ctrl+ Alt+ and Shift+ wxString key = keyname; int modifier = 0; while( 1 ) { if( key.StartsWith( MODIFIER_CTRL ) ) { modifier |= GR_KB_CTRL; key.Remove( 0, 5 ); } else if( key.StartsWith( MODIFIER_ALT ) ) { modifier |= GR_KB_ALT; key.Remove( 0, 4 ); } else if( key.StartsWith( MODIFIER_SHIFT ) ) { modifier |= GR_KB_SHIFT; key.Remove( 0, 6 ); } else { break; } } if( (key.length() == 1) && (key[0] > ' ') && (key[0] < 0x7F) ) { keycode = key[0]; keycode += modifier; return keycode; } for( ii = 0; ; ii++ ) { if( s_Hotkey_Name_List[ii].m_KeyCode == 0 ) // End of list reached break; if( key.CmpNoCase( s_Hotkey_Name_List[ii].m_Name ) == 0 ) { keycode = s_Hotkey_Name_List[ii].m_KeyCode + modifier; break; } } return keycode; } /* DisplayHotkeyList * Displays the current hotkey list * aList = a EDA_HOTKEY_CONFIG list(Null terminated) */ void DisplayHotkeyList( EDA_DRAW_FRAME* aFrame, struct EDA_HOTKEY_CONFIG* aDescList ) { wxString keyname; EDA_HOTKEY** List; wxString msg = wxT( "" ); msg += wxT( "

"); msg += _("Hotkeys List"); msg += wxT("

"); for( ; aDescList->m_HK_InfoList != NULL; aDescList++ ) { List = aDescList->m_HK_InfoList; for( ; *List != NULL; List++ ) { EDA_HOTKEY* hk_decr = *List; if( !hk_decr->m_InfoMsg.Contains( wxT( "Macros" ) ) ) { keyname = ReturnKeyNameFromKeyCode( hk_decr->m_KeyCode ); // Some chars should be modified, using html encoding, to be // displayed by DisplayHtmlInfoMessage() keyname.Replace( wxT("<"), wxT("<") ); keyname.Replace( wxT(">"), wxT(">") ); msg += wxT( ""); msg += wxT("" ); } } } msg += wxT("
" ) + hk_decr->m_InfoMsg + wxT("  ") + keyname + wxT( "
"); DisplayHtmlInfoMessage( aFrame, _("Hotkeys List"), msg, wxSize(340, 750)); } /** * Function GetDescriptorFromHotkey * Return a EDA_HOTKEY * pointer from a key code for OnHotKey() function * @param aKey = key code (ascii value, or wxWidgets value for function keys * @param aList = pointer to a EDA_HOTKEY list of commands * @return the corresponding EDA_HOTKEY pointer from the EDA_HOTKEY List */ EDA_HOTKEY* GetDescriptorFromHotkey( int aKey, EDA_HOTKEY** aList ) { for( ; *aList != NULL; aList++ ) { EDA_HOTKEY* hk_decr = *aList; if( hk_decr->m_KeyCode == aKey ) return hk_decr; } return NULL; } /** * Function WriteHotkeyConfig * Store the current hotkey list * It is stored using the standard wxConfig mechanism or a file. * * @param aDescList = pointer to the current hotkey list. * @param aFullFileName = a wxString pointer to a full file name. * if NULL, use the standard wxConfig mechanism (default) * the output format is: shortcut "key" "function" * lines starting with # are comments */ int EDA_BASE_FRAME::WriteHotkeyConfig( struct EDA_HOTKEY_CONFIG* aDescList, wxString* aFullFileName ) { wxString msg; wxString keyname, infokey; msg = wxT( "$hotkey list\n" ); /* Print the current hotkey list */ EDA_HOTKEY** List; for( ; aDescList->m_HK_InfoList != NULL; aDescList++ ) { if( aDescList->m_Comment ) { msg += wxT( "# " ); msg += wxString( aDescList->m_Comment ); msg += wxT( "\n" ); } msg += *aDescList->m_SectionTag; msg += wxT( "\n" ); List = aDescList->m_HK_InfoList; for( ; *List != NULL; List++ ) { EDA_HOTKEY* hk_decr = *List; msg += wxT( "shortcut " ); keyname = ReturnKeyNameFromKeyCode( hk_decr->m_KeyCode ); AddDelimiterString( keyname ); infokey = hk_decr->m_InfoMsg; AddDelimiterString( infokey ); msg += keyname + wxT( ": " ) + infokey + wxT( "\n" ); } } msg += wxT( "$Endlist\n" ); if( aFullFileName ) { FILE* file = wxFopen( *aFullFileName, wxT( "wt" ) ); if( file ) { fputs( TO_UTF8( msg ), file ); fclose( file ); } else { msg.Printf( wxT( "Unable to write file %s" ), GetChars( *aFullFileName ) ); return 0; } } else { wxConfig config( m_FrameName ); config.Write( HOTKEYS_CONFIG_KEY, msg ); } return 1; } /** * Function ReadHotkeyConfigFile * Read an old configuration file (<file>.key) and fill the current hotkey list * with hotkeys * @param aFilename = file name to read. * @param aDescList = current hotkey list descr. to initialise. */ int EDA_BASE_FRAME::ReadHotkeyConfigFile( const wxString& aFilename, struct EDA_HOTKEY_CONFIG* aDescList ) { wxFile cfgfile( aFilename ); /* get length */ cfgfile.SeekEnd(); wxFileOffset size = cfgfile.Tell(); cfgfile.Seek( 0 ); /* read data */ char* buffer = new char[size]; cfgfile.Read( buffer, size ); wxString data( buffer, wxConvUTF8 ); /* parse */ ParseHotkeyConfig( data, aDescList ); /* cleanup */ delete[] buffer; cfgfile.Close(); return 1; } void ReadHotkeyConfig( const wxString& Appname, struct EDA_HOTKEY_CONFIG* aDescList ) { wxConfig config( Appname ); if( !config.HasEntry( HOTKEYS_CONFIG_KEY ) ) { // assume defaults are ok return; } wxString data; config.Read( HOTKEYS_CONFIG_KEY, &data ); ParseHotkeyConfig( data, aDescList ); } /* Function ReadHotkeyConfig * Read configuration data and fill the current hotkey list with hotkeys * aDescList is the current hotkey list descr. to initialize. */ int EDA_BASE_FRAME::ReadHotkeyConfig( struct EDA_HOTKEY_CONFIG* aDescList ) { ::ReadHotkeyConfig( m_FrameName, aDescList ); return 1; } /* Function ParseHotkeyConfig * the input format is: shortcut "key" "function" * lines starting by # are ignored (comments) * lines like [xxx] are tags (example: [common] or [libedit] which identify sections */ void ParseHotkeyConfig( const wxString& data, struct EDA_HOTKEY_CONFIG* aDescList ) { /* Read the config */ wxStringTokenizer tokenizer( data, L"\r\n", wxTOKEN_STRTOK ); EDA_HOTKEY** CurrentHotkeyList = 0; while( tokenizer.HasMoreTokens() ) { wxString line = tokenizer.GetNextToken(); wxStringTokenizer lineTokenizer( line ); wxString line_type = lineTokenizer.GetNextToken(); if( line_type[0] == '#' ) //comment continue; if( line_type[0] == '[' ) // A tag is found. search infos in list { CurrentHotkeyList = 0; EDA_HOTKEY_CONFIG* DList = aDescList; for( ; DList->m_HK_InfoList; DList++ ) { if( *DList->m_SectionTag == line_type ) { CurrentHotkeyList = DList->m_HK_InfoList; break; } } continue; } if( line_type == wxT( "$Endlist" ) ) break; if( line_type != wxT( "shortcut" ) ) continue; if( CurrentHotkeyList == NULL ) continue; /* Get the key name */ lineTokenizer.SetString( lineTokenizer.GetString(), L"\"\r\n\t ", wxTOKEN_STRTOK ); wxString keyname = lineTokenizer.GetNextToken(); wxString remainder = lineTokenizer.GetString(); /* Get the command name */ wxString fctname = remainder.AfterFirst( '\"' ).BeforeFirst( '\"' ); /* search the hotkey in current hotkey list */ for( EDA_HOTKEY** List = CurrentHotkeyList; *List != NULL; List++ ) { EDA_HOTKEY* hk_decr = *List; if( hk_decr->m_InfoMsg == fctname ) { int code = ReturnKeyCodeFromKeyName( keyname ); if( code ) hk_decr->m_KeyCode = code; break; } } } } /** * Function ImportHotkeyConfigFromFile * Prompt the user for an old hotkey file to read, and read it. * @param aDescList = current hotkey list descr. to initialize. */ void EDA_BASE_FRAME::ImportHotkeyConfigFromFile( struct EDA_HOTKEY_CONFIG* aDescList ) { wxString ext = DEFAULT_HOTKEY_FILENAME_EXT; wxString mask = wxT( "*." ) + ext; wxString path = wxGetCwd(); wxString filename = wxGetApp().GetAppName() + wxT( "." ) + ext; filename = EDA_FileSelector( _( "Read Hotkey Configuration File:" ), path, filename, ext, mask, this, wxFD_OPEN, true ); if( filename.IsEmpty() ) return; ReadHotkeyConfigFile( filename, aDescList ); } /** * Function ExportHotkeyConfigToFile * Prompt the user for an old hotkey file to read, and read it. * @param aDescList = current hotkey list descr. to initialize. */ void EDA_BASE_FRAME::ExportHotkeyConfigToFile( struct EDA_HOTKEY_CONFIG* aDescList ) { wxString ext = DEFAULT_HOTKEY_FILENAME_EXT; wxString mask = wxT( "*." ) + ext; wxString path = wxGetCwd(); wxString filename = wxGetApp().GetAppName() + wxT( "." ) + ext; filename = EDA_FileSelector( _( "Write Hotkey Configuration File:" ), path, filename, ext, mask, this, wxFD_OPEN | wxFD_SAVE, true ); if( filename.IsEmpty() ) return; WriteHotkeyConfig( aDescList, &filename ); } /* add hotkey config options submenu to aMenu */ void AddHotkeyConfigMenu( wxMenu* aMenu ) { if( aMenu == NULL ) return; wxMenu* HotkeySubmenu = new wxMenu(); /* List existing hotkey menu*/ AddMenuItem( HotkeySubmenu, ID_PREFERENCES_HOTKEY_SHOW_CURRENT_LIST, _( "&List Current Keys" ), _( "Displays the current hotkeys list and corresponding commands" ), KiBitmap( info_xpm ) ); /* Call hotkeys editor*/ AddMenuItem( HotkeySubmenu, ID_PREFERENCES_HOTKEY_SHOW_EDITOR, _( "&Edit Hotkeys" ), _( "Call the hotkeys editor" ), KiBitmap( editor_xpm ) ); HotkeySubmenu->AppendSeparator(); /* create hotkey file to export current hotkeys config */ AddMenuItem( HotkeySubmenu, ID_PREFERENCES_HOTKEY_EXPORT_CONFIG, _( "E&xport Hotkeys" ), _( "Create a hotkey configuration file to export the current hotkeys" ), KiBitmap( save_setup_xpm ) ); /* Reload hotkey file */ AddMenuItem( HotkeySubmenu, ID_PREFERENCES_HOTKEY_IMPORT_CONFIG, _( "&Import Hotkeys" ), _( "Load an existing hotkey configuration file" ), KiBitmap( reload_xpm ) ); /* Append HotkeySubmenu to menu */ AddMenuItem( aMenu, HotkeySubmenu, ID_PREFERENCES_HOTKEY_SUBMENU, _( "&Hotkeys" ), _( "Hotkeys configuration and preferences" ), KiBitmap( hotkeys_xpm ) ); }