diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 12f823ed9b..048ddf01a6 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,6 +1,7 @@ include_directories( BEFORE ${INC_BEFORE} ) include_directories( ./dialogs + ./widgets ./dialog_about ${CAIRO_INCLUDE_DIR} ${GLEW_INCLUDE_DIR} @@ -144,6 +145,9 @@ set( COMMON_ABOUT_DLG_SRCS dialog_about/AboutDialog_main.cpp dialog_about/dialog_about.cpp dialog_about/dialog_about_base.cpp + ) + +set( COMMON_DLG_SRCS dialogs/dialog_display_info_HTML_base.cpp dialogs/dialog_exit_base.cpp dialogs/dialog_image_editor.cpp @@ -160,6 +164,10 @@ set( COMMON_ABOUT_DLG_SRCS dialogs/wx_html_report_panel.cpp ) +set( COMMON_WIDGET_SRCS + widgets/widget_hotkey_list.cpp + ) + set( COMMON_PAGE_LAYOUT_SRCS page_layout/title_block_shapes.cpp page_layout/class_worksheet_dataitem.cpp @@ -173,6 +181,8 @@ set( COMMON_PAGE_LAYOUT_SRCS set( COMMON_SRCS ${LIB_KICAD_SRCS} ${COMMON_ABOUT_DLG_SRCS} + ${COMMON_DLG_SRCS} + ${COMMON_WIDGET_SRCS} ${COMMON_PAGE_LAYOUT_SRCS} base_struct.cpp basicframe.cpp diff --git a/common/dialogs/dialog_hotkeys_editor.cpp b/common/dialogs/dialog_hotkeys_editor.cpp index ad0952e4d9..96a8d3b5d1 100644 --- a/common/dialogs/dialog_hotkeys_editor.cpp +++ b/common/dialogs/dialog_hotkeys_editor.cpp @@ -21,320 +21,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include - -#include -#include -#include -#include - #include - -class DIALOG_HOTKEY_CLIENT_DATA : public wxClientData -{ - EDA_HOTKEY m_hotkey; - wxString m_section_tag; - -public: - DIALOG_HOTKEY_CLIENT_DATA( const EDA_HOTKEY& aHotkey, const wxString& aSectionTag ) - : m_hotkey( aHotkey ), m_section_tag( aSectionTag ) {} - - EDA_HOTKEY& GetHotkey() { return m_hotkey; } - wxString GetSectionTag() const { return m_section_tag; } -}; - - -HOTKEY_LIST_CTRL::HOTKEY_LIST_CTRL( wxWindow *aParent, const HOTKEYS_SECTIONS& aSections ) : - wxTreeListCtrl( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTL_SINGLE ), - m_sections( aSections ) -{ - AppendColumn( _( "Command" ) ); - AppendColumn( _( "Hotkey" ) ); - - SetColumnWidth( 1, 100 ); - - Bind( wxEVT_CHAR, &HOTKEY_LIST_CTRL::OnChar, this ); -} - - -void HOTKEY_LIST_CTRL::DeselectRow( int aRow ) -{ - wxASSERT( aRow >= 0 && aRow < (int)m_items.size() ); - Unselect( m_items[aRow] ); -} - - -void HOTKEY_LIST_CTRL::OnChar( wxKeyEvent& aEvent ) -{ - DIALOG_HOTKEY_CLIENT_DATA* data = GetSelHKClientData(); - - if( data ) - { - long key = aEvent.GetKeyCode(); - - switch( key ) - { - case WXK_ESCAPE: - UnselectAll(); - break; - - default: - if( key >= 'a' && key <= 'z' ) // convert to uppercase - key = key + ('A' - 'a'); - - // Remap Ctrl A (=1+GR_KB_CTRL) to Ctrl Z(=26+GR_KB_CTRL) - // to GR_KB_CTRL+'A' .. GR_KB_CTRL+'Z' - if( aEvent.ControlDown() && key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z ) - key += 'A' - 1; - - /* Disallow shift for keys that have two keycodes on them (e.g. number and - * punctuation keys) leaving only the "letter keys" of A-Z. - * Then, you can have, e.g. Ctrl-5 and Ctrl-% (GB layout) - * and Ctrl-( and Ctrl-5 (FR layout). - * Otherwise, you'd have to have to say Ctrl-Shift-5 on a FR layout - */ - bool keyIsLetter = key >= 'A' && key <= 'Z'; - - if( aEvent.ShiftDown() && ( keyIsLetter || key > 256 ) ) - key |= GR_KB_SHIFT; - - if( aEvent.ControlDown() ) - key |= GR_KB_CTRL; - - if( aEvent.AltDown() ) - key |= GR_KB_ALT; - - // See if this key code is handled in hotkeys names list - bool exists; - KeyNameFromKeyCode( key, &exists ); - - if( exists && data->GetHotkey().m_KeyCode != key ) - { - wxString tag = data->GetSectionTag(); - bool canUpdate = ResolveKeyConflicts( key, tag ); - - if( canUpdate ) - { - data->GetHotkey().m_KeyCode = key; - } - - // Remove selection - UnselectAll(); - } - } - } - UpdateFromClientData(); -} - - -DIALOG_HOTKEY_CLIENT_DATA* HOTKEY_LIST_CTRL::GetSelHKClientData() -{ - return GetHKClientData( GetSelection() ); -} - - -DIALOG_HOTKEY_CLIENT_DATA* HOTKEY_LIST_CTRL::GetHKClientData( wxTreeListItem aItem ) -{ - if( aItem.IsOk() ) - { - wxClientData* data = GetItemData( aItem ); - if( !data ) - return NULL; - - DIALOG_HOTKEY_CLIENT_DATA* hkdata = static_cast( data ); - return hkdata; - } - else - { - return NULL; - } -} - - -void HOTKEY_LIST_CTRL::LoadSection( struct EDA_HOTKEY_CONFIG* aSection ) -{ - HOTKEY_LIST list; - EDA_HOTKEY** info_ptr; - - for( info_ptr = aSection->m_HK_InfoList; *info_ptr; info_ptr++ ) - { - EDA_HOTKEY info = **info_ptr; - list.push_back( info ); - } - - m_hotkeys.push_back( list ); -} - - -void HOTKEY_LIST_CTRL::UpdateFromClientData() -{ - for( wxTreeListItem i = GetFirstItem(); i.IsOk(); i = GetNextItem( i ) ) - { - DIALOG_HOTKEY_CLIENT_DATA* hkdata = GetHKClientData( i ); - if( !hkdata ) - continue; - - EDA_HOTKEY& hk = hkdata->GetHotkey(); - - wxString name = wxGetTranslation( hk.m_InfoMsg ); - wxString key = KeyNameFromKeyCode( hk.m_KeyCode ); - - SetItemText( i, 0, name ); - SetItemText( i, 1, key ); - } -} - - -bool HOTKEY_LIST_CTRL::TransferDataToControl() -{ - Freeze(); - DeleteAllItems(); - m_items.clear(); - m_hotkeys.clear(); - - HOTKEYS_SECTIONS::iterator sec_it; - size_t sec_index = 0; - for( sec_it = m_sections.begin(); sec_it != m_sections.end(); ++sec_it, ++sec_index ) - { - LoadSection( sec_it->second ); - wxString section_tag = *( sec_it->second->m_SectionTag ); - - // Create parent item - wxTreeListItem parent = AppendItem( GetRootItem(), sec_it->first ); - - HOTKEY_LIST& each_list = m_hotkeys[sec_index]; - HOTKEY_LIST::iterator hk_it; - for( hk_it = each_list.begin(); hk_it != each_list.end(); ++hk_it ) - { - wxTreeListItem item = AppendItem( parent, wxEmptyString ); - SetItemData( item, new DIALOG_HOTKEY_CLIENT_DATA( &*hk_it, section_tag ) ); - m_items.push_back( item ); - } - - Expand( parent ); - } - - UpdateFromClientData(); - Thaw(); - - return true; -} - - -bool HOTKEY_LIST_CTRL::TransferDataFromControl() -{ - for( size_t i_sec = 0; i_sec < m_sections.size(); ++i_sec ) - { - struct EDA_HOTKEY_CONFIG* section = m_sections[i_sec].second; - for( EDA_HOTKEY** info_ptr = section->m_HK_InfoList; *info_ptr; ++info_ptr ) - { - EDA_HOTKEY* info = *info_ptr; - for( wxTreeListItem item = GetFirstItem(); item.IsOk(); item = GetNextItem( item ) ) - { - DIALOG_HOTKEY_CLIENT_DATA* hkdata = GetHKClientData( item ); - if( !hkdata ) - continue; - - EDA_HOTKEY& hk = hkdata->GetHotkey(); - if( hk.m_Idcommand == info->m_Idcommand ) - { - info->m_KeyCode = hk.m_KeyCode; - break; - } - } - } - } - return true; -} - - -bool HOTKEY_LIST_CTRL::ResolveKeyConflicts( long aKey, const wxString& aSectionTag ) -{ - EDA_HOTKEY* conflictingKey = NULL; - EDA_HOTKEY_CONFIG* conflictingSection = NULL; - - CheckKeyConflicts( aKey, aSectionTag, &conflictingKey, &conflictingSection ); - - if( conflictingKey != NULL ) - { - wxString info = wxGetTranslation( conflictingKey->m_InfoMsg ); - wxString msg = wxString::Format( - _( "<%s> is already assigned to \"%s\" in section \"%s\". Are you sure you want " - "to change its assignment?" ), - KeyNameFromKeyCode( aKey ), GetChars( info ), - *(conflictingSection->m_Title) ); - - wxMessageDialog dlg( m_parent, msg, _( "Confirm change" ), wxYES_NO | wxNO_DEFAULT ); - - if( dlg.ShowModal() == wxID_YES ) - { - conflictingKey->m_KeyCode = 0; - UpdateFromClientData(); - return true; - } - else - { - return false; - } - } - - return true; -} - - -bool HOTKEY_LIST_CTRL::CheckKeyConflicts( long aKey, const wxString& aSectionTag, - EDA_HOTKEY** aConfKey, EDA_HOTKEY_CONFIG** aConfSect ) -{ - EDA_HOTKEY* conflictingKey = NULL; - struct EDA_HOTKEY_CONFIG* conflictingSection = NULL; - - for( wxTreeListItem item = GetFirstItem(); item.IsOk(); item = GetNextItem( item ) ) - { - DIALOG_HOTKEY_CLIENT_DATA* hkdata = GetHKClientData( item ); - if( !hkdata ) - continue; - - EDA_HOTKEY& hk = hkdata->GetHotkey(); - wxString tag = hkdata->GetSectionTag(); - - if( aSectionTag != g_CommonSectionTag - && tag != g_CommonSectionTag - && tag != aSectionTag ) - continue; - - if( aKey == hk.m_KeyCode ) - { - conflictingKey = &hk; - - // Find the section - HOTKEYS_SECTIONS::iterator it; - for( it = m_sections.begin(); it != m_sections.end(); ++it ) - { - if( *it->second->m_SectionTag == tag ) - { - conflictingSection = it->second; - break; - } - } - } - } - - if( aConfKey ) - *aConfKey = conflictingKey; - - if( aConfSect ) - *aConfSect = conflictingSection; - - return conflictingKey == NULL; -} - - void InstallHotkeyFrame( EDA_BASE_FRAME* aParent, EDA_HOTKEY_CONFIG* aHotkeys ) { HOTKEYS_EDITOR_DIALOG dialog( aParent, aHotkeys ); int diag = dialog.ShowModal(); + if( diag == wxID_OK ) { aParent->ReCreateMenuBar(); @@ -343,26 +37,18 @@ void InstallHotkeyFrame( EDA_BASE_FRAME* aParent, EDA_HOTKEY_CONFIG* aHotkeys ) } -HOTKEYS_EDITOR_DIALOG::HOTKEYS_EDITOR_DIALOG( EDA_BASE_FRAME* aParent, - EDA_HOTKEY_CONFIG* aHotkeys ) : +HOTKEYS_EDITOR_DIALOG::HOTKEYS_EDITOR_DIALOG( EDA_BASE_FRAME* aParent, + EDA_HOTKEY_CONFIG* aHotkeys ) : HOTKEYS_EDITOR_DIALOG_BASE( aParent ), - m_parent( aParent ), m_hotkeys( aHotkeys ) { - EDA_HOTKEY_CONFIG* section; - - HOTKEYS_SECTIONS sections; - for( section = m_hotkeys; section->m_HK_InfoList; section++ ) - { - HOTKEYS_SECTION sec( wxGetTranslation( *section->m_Title ), section ); - sections.push_back( sec ); - } - - m_hotkeyListCtrl = new HOTKEY_LIST_CTRL( this, sections ); - m_mainSizer->Insert( 1, m_hotkeyListCtrl, wxSizerFlags( 1 ).Expand().Border( wxALL, 5 ) ); - Layout(); + m_hotkeyListCtrl = new WIDGET_HOTKEY_LIST( m_panelHotkeys, + WIDGET_HOTKEY_LIST::GenSections( aHotkeys ) ); + m_hotkeyListCtrl->InstallOnPanel( m_panelHotkeys ); m_sdbSizerOK->SetDefault(); + + Layout(); Center(); } @@ -388,7 +74,7 @@ bool HOTKEYS_EDITOR_DIALOG::TransferDataFromWindow() return false; // save the hotkeys - m_parent->WriteHotkeyConfig( m_hotkeys ); + GetParent()->WriteHotkeyConfig( m_hotkeys ); return true; } @@ -398,4 +84,3 @@ void HOTKEYS_EDITOR_DIALOG::ResetClicked( wxCommandEvent& aEvent ) { m_hotkeyListCtrl->TransferDataToControl(); } - diff --git a/common/dialogs/dialog_hotkeys_editor_base.cpp b/common/dialogs/dialog_hotkeys_editor_base.cpp index 1293cb4b95..5373ac504a 100644 --- a/common/dialogs/dialog_hotkeys_editor_base.cpp +++ b/common/dialogs/dialog_hotkeys_editor_base.cpp @@ -15,10 +15,13 @@ HOTKEYS_EDITOR_DIALOG_BASE::HOTKEYS_EDITOR_DIALOG_BASE( wxWindow* parent, wxWind m_mainSizer = new wxBoxSizer( wxVERTICAL ); - m_staticText1 = new wxStaticText( this, wxID_ANY, _("Select a row and press a new key combination to alter the binding."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText1 = new wxStaticText( this, wxID_ANY, _("Double-click to edit"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText1->Wrap( 400 ); m_mainSizer->Add( m_staticText1, 0, wxALL|wxEXPAND, 5 ); + m_panelHotkeys = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_mainSizer->Add( m_panelHotkeys, 1, wxEXPAND | wxALL, 5 ); + wxBoxSizer* b_buttonsSizer; b_buttonsSizer = new wxBoxSizer( wxHORIZONTAL ); diff --git a/common/dialogs/dialog_hotkeys_editor_base.fbp b/common/dialogs/dialog_hotkeys_editor_base.fbp index 814b3acc7b..6a096faede 100644 --- a/common/dialogs/dialog_hotkeys_editor_base.fbp +++ b/common/dialogs/dialog_hotkeys_editor_base.fbp @@ -125,7 +125,7 @@ 0 0 wxID_ANY - Select a row and press a new key combination to alter the binding. + Double-click to edit 0 @@ -176,6 +176,86 @@ + + 5 + wxEXPAND | wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_panelHotkeys + 1 + + + protected + 1 + + Resizable + 1 + + + 0 + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + 5 wxALIGN_RIGHT|wxEXPAND diff --git a/common/dialogs/dialog_hotkeys_editor_base.h b/common/dialogs/dialog_hotkeys_editor_base.h index 9c58421872..a59f560d4f 100644 --- a/common/dialogs/dialog_hotkeys_editor_base.h +++ b/common/dialogs/dialog_hotkeys_editor_base.h @@ -20,6 +20,7 @@ class DIALOG_SHIM; #include #include #include +#include #include #include #include @@ -37,6 +38,7 @@ class HOTKEYS_EDITOR_DIALOG_BASE : public DIALOG_SHIM protected: wxBoxSizer* m_mainSizer; wxStaticText* m_staticText1; + wxPanel* m_panelHotkeys; wxButton* m_resetButton; wxStdDialogButtonSizer* m_sdbSizer; wxButton* m_sdbSizerOK; diff --git a/common/widgets/widget_hotkey_list.cpp b/common/widgets/widget_hotkey_list.cpp new file mode 100644 index 0000000000..381367020d --- /dev/null +++ b/common/widgets/widget_hotkey_list.cpp @@ -0,0 +1,674 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2016 Chris Pavlina + * Copyright (C) 2016 KiCad Developers, see CHANGELOG.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 3 + * 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 + */ + +#include + +#include + +#include +#include + +#include +#include + + +/** + * Minimum width of the hotkey column + */ +static const int HOTKEY_MIN_WIDTH = 100; + + +/** + * Extra margin to compensate for vertical scrollbar + */ +static const int HORIZ_MARGIN = 30; + + +/** + * Menu IDs for the hotkey context menu + */ +enum ID_WHKL_MENU_IDS +{ + ID_EDIT = 2001, + ID_RESET, + ID_RESET_ALL, +}; + + +/** + * Class WIDGET_HOTKEY_CLIENT_DATA + * Stores the hotkey and section tag associated with each row. To change a + * hotkey, edit it in the row's client data, then call WIDGET_HOTKEY_LIST::UpdateFromClientData(). + */ +class WIDGET_HOTKEY_CLIENT_DATA : public wxClientData +{ + EDA_HOTKEY m_hotkey; + wxString m_section_tag; + +public: + WIDGET_HOTKEY_CLIENT_DATA( const EDA_HOTKEY& aHotkey, const wxString& aSectionTag ) + : m_hotkey( aHotkey ), m_section_tag( aSectionTag ) + {} + + + EDA_HOTKEY& GetHotkey() { return m_hotkey; } + const wxString& GetSectionTag() const { return m_section_tag; } +}; + + +/** + * Class HK_PROMPT_DIALOG + * Dialog to prompt the user to enter a key. + */ +class HK_PROMPT_DIALOG : public DIALOG_SHIM +{ + wxKeyEvent m_event; + +public: + HK_PROMPT_DIALOG( wxWindow* aParent, wxWindowID aId, const wxString& aTitle, + const wxString& aName, const wxString& aCurrentKey ) + : DIALOG_SHIM( aParent, aId, aTitle, wxDefaultPosition, wxDefaultSize ) + { + wxPanel* panel = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize ); + wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL ); + + /* Dialog layout: + * + * inst_label........................ + * ---------------------------------- + * + * cmd_label_0 cmd_label_1 \ + * | fgsizer + * key_label_0 key_label_1 / + */ + + wxStaticText* inst_label = new wxStaticText( panel, wxID_ANY, wxEmptyString, + wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL ); + + inst_label->SetLabelText( _( "Press a new hotkey, or press Esc to cancel..." ) ); + sizer->Add( inst_label, 0, wxALL, 5 ); + + sizer->Add( new wxStaticLine( panel ), 0, wxALL | wxEXPAND, 2 ); + + wxFlexGridSizer* fgsizer = new wxFlexGridSizer( 2 ); + + wxStaticText* cmd_label_0 = new wxStaticText( panel, wxID_ANY, _( "Command:" ) ); + fgsizer->Add( cmd_label_0, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 ); + + wxStaticText* cmd_label_1 = new wxStaticText( panel, wxID_ANY, wxEmptyString ); + cmd_label_1->SetFont( cmd_label_1->GetFont().Bold() ); + cmd_label_1->SetLabel( aName ); + fgsizer->Add( cmd_label_1, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 ); + + wxStaticText* key_label_0 = new wxStaticText( panel, wxID_ANY, _( "Current key:" ) ); + fgsizer->Add( key_label_0, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 ); + + wxStaticText* key_label_1 = new wxStaticText( panel, wxID_ANY, wxEmptyString ); + key_label_1->SetFont( key_label_1->GetFont().Bold() ); + key_label_1->SetLabel( aCurrentKey ); + fgsizer->Add( key_label_1, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 ); + + sizer->Add( fgsizer, 1, wxEXPAND ); + + // Wrap the sizer in a second to give a larger border around the whole dialog + wxBoxSizer* outer_sizer = new wxBoxSizer( wxVERTICAL ); + outer_sizer->Add( sizer, 0, wxALL | wxEXPAND, 10 ); + panel->SetSizer( outer_sizer ); + + Layout(); + outer_sizer->Fit( this ); + Center(); + + SetMinClientSize( GetClientSize() ); + + // Binding both EVT_CHAR and EVT_CHAR_HOOK ensures that all key events, + // including specials like Tab and Return, are received, particularly + // on MSW. + panel->Bind( wxEVT_CHAR, &HK_PROMPT_DIALOG::OnChar, this ); + panel->Bind( wxEVT_CHAR_HOOK, &HK_PROMPT_DIALOG::OnCharHook, this ); + } + + + void OnCharHook( wxKeyEvent& aEvent ) + { + // On certain platforms, EVT_CHAR_HOOK is the only handler that receives + // certain "special" keys. However, it doesn't always receive "normal" + // keys correctly. For example, with a US keyboard, it sees ? as shift+/. + // + // Untangling these incorrect keys would be too much trouble, so we bind + // both events, and simply skip the EVT_CHAR_HOOK if it receives a + // "normal" key. + + const enum wxKeyCode skipped_keys[] = + { + WXK_NONE, WXK_SHIFT, WXK_ALT, WXK_CONTROL, WXK_CAPITAL, + WXK_NUMLOCK, WXK_SCROLL, WXK_RAW_CONTROL + }; + + int key = aEvent.GetKeyCode(); + + for( size_t i = 0; i < sizeof( skipped_keys ) / sizeof( skipped_keys[0] ); ++i ) + { + if( key == skipped_keys[i] ) + return; + } + + if( key <= 255 && isprint( key ) && !isspace( key ) ) + { + // Let EVT_CHAR handle this one + aEvent.DoAllowNextEvent(); + + // On Windows, wxEvent::Skip must NOT be called. + // On Linux and OSX, wxEvent::Skip MUST be called. + // No, I don't know why. +#ifndef __WXMSW__ + aEvent.Skip(); +#endif + } + else + { + OnChar( aEvent ); + } + } + + + void OnChar( wxKeyEvent& aEvent ) + { + m_event = aEvent; + EndFlexible( wxID_OK ); + } + + + /** + * End the dialog whether modal or quasimodal + */ + void EndFlexible( int aRtnCode ) + { + if( IsQuasiModal() ) + EndQuasiModal( aRtnCode ); + else + EndModal( aRtnCode ); + } + + + static wxKeyEvent PromptForKey( wxWindow* aParent, const wxString& aName, + const wxString& aCurrentKey ) + { + HK_PROMPT_DIALOG dialog( aParent, wxID_ANY, _( "Set Hotkey" ), aName, aCurrentKey ); + + if( dialog.ShowModal() == wxID_OK ) + { + return dialog.m_event; + } + else + { + wxKeyEvent dummy; + return dummy; + } + } +}; + + +WIDGET_HOTKEY_CLIENT_DATA* WIDGET_HOTKEY_LIST::GetHKClientData( wxTreeListItem aItem ) +{ + if( aItem.IsOk() ) + { + wxClientData* data = GetItemData( aItem ); + + if( !data ) + { + return NULL; + } + else + { + return static_cast( data ); + } + } + else + { + return NULL; + } +} + + +WIDGET_HOTKEY_CLIENT_DATA* WIDGET_HOTKEY_LIST::GetSelHKClientData() +{ + return GetHKClientData( GetSelection() ); +} + + +void WIDGET_HOTKEY_LIST::UpdateFromClientData() +{ + for( wxTreeListItem i = GetFirstItem(); i.IsOk(); i = GetNextItem( i ) ) + { + WIDGET_HOTKEY_CLIENT_DATA* hkdata = GetHKClientData( i ); + + if( hkdata ) + { + EDA_HOTKEY& hk = hkdata->GetHotkey(); + + SetItemText( i, 0, wxGetTranslation( hk.m_InfoMsg ) ); + SetItemText( i, 1, KeyNameFromKeyCode( hk.m_KeyCode ) ); + } + } +} + + +void WIDGET_HOTKEY_LIST::LoadSection( EDA_HOTKEY_CONFIG* aSection ) +{ + HOTKEY_LIST list; + + for( EDA_HOTKEY** info_ptr = aSection->m_HK_InfoList; *info_ptr; ++info_ptr ) + { + list.push_back( **info_ptr ); + } + + m_hotkeys.push_back( list ); +} + + +void WIDGET_HOTKEY_LIST::EditItem( wxTreeListItem aItem ) +{ + WIDGET_HOTKEY_CLIENT_DATA* hkdata = GetHKClientData( aItem ); + + if( !hkdata ) + { + // Activated item was not a hotkey row + return; + } + + wxString name = GetItemText( aItem, 0 ); + wxString current_key = GetItemText( aItem, 1 ); + + wxKeyEvent key_event = HK_PROMPT_DIALOG::PromptForKey( GetParent(), name, current_key ); + long key = MapKeypressToKeycode( key_event ); + + if( hkdata && key ) + { + // See if this key code is handled in hotkeys names list + bool exists; + KeyNameFromKeyCode( key, &exists ); + + if( exists && hkdata->GetHotkey().m_KeyCode != key ) + { + wxString tag = hkdata->GetSectionTag(); + bool canUpdate = ResolveKeyConflicts( key, tag ); + + if( canUpdate ) + { + hkdata->GetHotkey().m_KeyCode = key; + } + } + + UpdateFromClientData(); + } +} + + +void WIDGET_HOTKEY_LIST::ResetItem( wxTreeListItem aItem ) +{ + WIDGET_HOTKEY_CLIENT_DATA* hkdata = GetHKClientData( aItem ); + EDA_HOTKEY* hk = &hkdata->GetHotkey(); + + for( size_t sec_index = 0; sec_index < m_sections.size(); ++sec_index ) + { + wxString& section_tag = *( m_sections[sec_index].m_section->m_SectionTag ); + + if( section_tag != hkdata->GetSectionTag() ) + continue; + + HOTKEY_LIST& each_list = m_hotkeys[sec_index]; + HOTKEY_LIST::iterator hk_it; + + for( hk_it = each_list.begin(); hk_it != each_list.end(); ++hk_it ) + { + if( hk_it->m_Idcommand == hk->m_Idcommand ) + { + hk->m_KeyCode = hk_it->m_KeyCode; + break; + } + } + } + + UpdateFromClientData(); +} + + +void WIDGET_HOTKEY_LIST::OnActivated( wxTreeListEvent& aEvent ) +{ + EditItem( aEvent.GetItem() ); +} + + +void WIDGET_HOTKEY_LIST::OnContextMenu( wxTreeListEvent& aEvent ) +{ + // Save the active event for use in OnMenu + m_context_menu_item = aEvent.GetItem(); + + wxMenu menu; + + menu.Append( ID_EDIT, _( "Edit..." ) ); + menu.Append( ID_RESET, _( "Reset" ) ); + menu.Append( wxID_SEPARATOR ); + menu.Append( ID_RESET_ALL, _( "Reset all" ) ); + + PopupMenu( &menu ); +} + + +void WIDGET_HOTKEY_LIST::OnMenu( wxCommandEvent& aEvent ) +{ + switch( aEvent.GetId() ) + { + case ID_EDIT: + EditItem( m_context_menu_item ); + break; + + case ID_RESET: + ResetItem( m_context_menu_item ); + break; + + case ID_RESET_ALL: + TransferDataToControl(); + break; + + default: + wxFAIL_MSG( wxT( "Unknown ID in context menu event" ) ); + } +} + + +void WIDGET_HOTKEY_LIST::OnSize( wxSizeEvent& aEvent ) +{ + // Handle this manually - wxTreeListCtrl screws up the width of the first column + wxDataViewCtrl* view = GetDataView(); + + if( !view ) + return; + + wxRect rect = GetClientRect(); + view->SetSize( rect ); + +#ifdef wxHAS_GENERIC_DATAVIEWCTRL + { + wxWindow* view = GetView(); + view->Refresh(); + view->Update(); + } +#endif + + // Find the maximum width of the hotkey column + int hk_column_width = 0; + + for( wxTreeListItem item = GetFirstItem(); item.IsOk(); item = GetNextItem( item ) ) + { + const wxString& text = GetItemText( item, 1 ); + int width = WidthFor( text ); + + if( width > hk_column_width ) + hk_column_width = width; + } + + if( hk_column_width < HOTKEY_MIN_WIDTH ) + hk_column_width = HOTKEY_MIN_WIDTH; + + SetColumnWidth( 1, hk_column_width ); + SetColumnWidth( 0, rect.width - hk_column_width - HORIZ_MARGIN ); +} + + +bool WIDGET_HOTKEY_LIST::CheckKeyConflicts( long aKey, const wxString& aSectionTag, + EDA_HOTKEY** aConfKey, EDA_HOTKEY_CONFIG** aConfSect ) +{ + EDA_HOTKEY* conflicting_key = NULL; + struct EDA_HOTKEY_CONFIG* conflicting_section = NULL; + + for( wxTreeListItem item = GetFirstItem(); item.IsOk(); item = GetNextItem( item ) ) + { + WIDGET_HOTKEY_CLIENT_DATA* hkdata = GetHKClientData( item ); + + if( !hkdata ) + continue; + + EDA_HOTKEY& hk = hkdata->GetHotkey(); + wxString tag = hkdata->GetSectionTag(); + + if( aSectionTag != g_CommonSectionTag + && tag != g_CommonSectionTag + && tag != aSectionTag ) + { + // This key and its conflict candidate are in orthogonal sections, so skip. + continue; + } + + if( aKey == hk.m_KeyCode ) + { + conflicting_key = &hk; + + // Find the section + HOTKEY_SECTIONS::iterator it; + + for( it = m_sections.begin(); it != m_sections.end(); ++it ) + { + if( *it->m_section->m_SectionTag == tag ) + { + conflicting_section = it->m_section; + break; + } + } + } + } + + // Write the outparams + if( aConfKey ) + *aConfKey = conflicting_key; + + if( aConfSect ) + *aConfSect = conflicting_section; + + return conflicting_key == NULL; +} + + +bool WIDGET_HOTKEY_LIST::ResolveKeyConflicts( long aKey, const wxString& aSectionTag ) +{ + EDA_HOTKEY* conflicting_key = NULL; + EDA_HOTKEY_CONFIG* conflicting_section = NULL; + + CheckKeyConflicts( aKey, aSectionTag, &conflicting_key, &conflicting_section ); + + if( conflicting_key != NULL ) + { + wxString info = wxGetTranslation( conflicting_key->m_InfoMsg ); + wxString msg = wxString::Format( + _( "<%s> is already assigned to \"%s\" in section \"%s\". Are you sure you want " + "to change its assignment?" ), + KeyNameFromKeyCode( aKey ), GetChars( info ), + *(conflicting_section->m_Title) ); + + wxMessageDialog dlg( GetParent(), msg, _( "Confirm change" ), wxYES_NO | wxNO_DEFAULT ); + + if( dlg.ShowModal() == wxID_YES ) + { + conflicting_key->m_KeyCode = 0; + UpdateFromClientData(); + return true; + } + else + { + return false; + } + } + else + { + return true; + } +} + + +WIDGET_HOTKEY_LIST::WIDGET_HOTKEY_LIST( wxWindow* aParent, const HOTKEY_SECTIONS& aSections ) + : wxTreeListCtrl( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTL_SINGLE ), + m_sections( aSections ) +{ + AppendColumn( _( "Command" ) ); + AppendColumn( _( "Hotkey" ) ); + + Bind( wxEVT_TREELIST_ITEM_ACTIVATED, &WIDGET_HOTKEY_LIST::OnActivated, this ); + Bind( wxEVT_TREELIST_ITEM_CONTEXT_MENU, &WIDGET_HOTKEY_LIST::OnContextMenu, this ); + Bind( wxEVT_MENU, &WIDGET_HOTKEY_LIST::OnMenu, this ); + Bind( wxEVT_SIZE, &WIDGET_HOTKEY_LIST::OnSize, this ); +} + + +HOTKEY_SECTIONS WIDGET_HOTKEY_LIST::GenSections( EDA_HOTKEY_CONFIG* aHotkeys ) +{ + HOTKEY_SECTIONS sections; + + for( EDA_HOTKEY_CONFIG* section = aHotkeys; section->m_HK_InfoList; ++section ) + { + HOTKEY_SECTION sec; + sec.m_name = wxGetTranslation( *section->m_Title ); + sec.m_section = section; + sections.push_back( sec ); + } + + return sections; +} + + +void WIDGET_HOTKEY_LIST::InstallOnPanel( wxPanel* aPanel ) +{ + wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL ); + + sizer->Add( this, 1, wxALL | wxEXPAND, 0 ); + aPanel->SetSizer( sizer ); +} + + +bool WIDGET_HOTKEY_LIST::TransferDataToControl() +{ + Freeze(); + DeleteAllItems(); + m_hotkeys.clear(); + + for( size_t sec_index = 0; sec_index < m_sections.size(); ++sec_index ) + { + // LoadSection pushes into m_hotkeys + LoadSection( m_sections[sec_index].m_section ); + wxASSERT( m_hotkeys.size() == sec_index + 1 ); + + wxString section_tag = *( m_sections[sec_index].m_section->m_SectionTag ); + + // Create parent tree item + wxTreeListItem parent = AppendItem( GetRootItem(), m_sections[sec_index].m_name ); + + HOTKEY_LIST& each_list = m_hotkeys[sec_index]; + HOTKEY_LIST::iterator hk_it; + + for( hk_it = each_list.begin(); hk_it != each_list.end(); ++hk_it ) + { + wxTreeListItem item = AppendItem( parent, wxEmptyString ); + SetItemData( item, new WIDGET_HOTKEY_CLIENT_DATA( &*hk_it, section_tag ) ); + } + + Expand( parent ); + } + + UpdateFromClientData(); + Thaw(); + + return true; +} + + +bool WIDGET_HOTKEY_LIST::TransferDataFromControl() +{ + for( size_t sec_index = 0; sec_index < m_sections.size(); ++sec_index ) + { + EDA_HOTKEY_CONFIG* section = m_sections[sec_index].m_section; + + for( EDA_HOTKEY** info_ptr = section->m_HK_InfoList; *info_ptr; ++info_ptr ) + { + EDA_HOTKEY* info = *info_ptr; + + for( wxTreeListItem item = GetFirstItem(); item.IsOk(); item = GetNextItem( item ) ) + { + WIDGET_HOTKEY_CLIENT_DATA* hkdata = GetHKClientData( item ); + + if( !hkdata ) + continue; + + EDA_HOTKEY& hk = hkdata->GetHotkey(); + + if( hk.m_Idcommand == info->m_Idcommand ) + { + info->m_KeyCode = hk.m_KeyCode; + break; + } + } + } + } + + return true; +} + + +long WIDGET_HOTKEY_LIST::MapKeypressToKeycode( const wxKeyEvent& aEvent ) +{ + long key = aEvent.GetKeyCode(); + + if( key == WXK_ESCAPE ) + { + return 0; + } + else + { + if( key >= 'a' && key <= 'z' ) // convert to uppercase + key = key + ('A' - 'a'); + + // Remap Ctrl A (=1+GR_KB_CTRL) to Ctrl Z(=26+GR_KB_CTRL) + // to GR_KB_CTRL+'A' .. GR_KB_CTRL+'Z' + if( aEvent.ControlDown() && key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z ) + key += 'A' - 1; + + /* Disallow shift for keys that have two keycodes on them (e.g. number and + * punctuation keys) leaving only the "letter keys" of A-Z. + * Then, you can have, e.g. Ctrl-5 and Ctrl-% (GB layout) + * and Ctrl-( and Ctrl-5 (FR layout). + * Otherwise, you'd have to have to say Ctrl-Shift-5 on a FR layout + */ + bool keyIsLetter = key >= 'A' && key <= 'Z'; + + if( aEvent.ShiftDown() && ( keyIsLetter || key > 256 ) ) + key |= GR_KB_SHIFT; + + if( aEvent.ControlDown() ) + key |= GR_KB_CTRL; + + if( aEvent.AltDown() ) + key |= GR_KB_ALT; + + return key; + } +} diff --git a/eeschema/dialogs/dialog_eeschema_options.cpp b/eeschema/dialogs/dialog_eeschema_options.cpp index c6b0f34c87..5ce1477ebb 100644 --- a/eeschema/dialogs/dialog_eeschema_options.cpp +++ b/eeschema/dialogs/dialog_eeschema_options.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2009 Wayne Stambaugh - * Copyright (C) 1992-2011 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2016 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 @@ -30,10 +30,14 @@ #include #include +#include +#include +#include -#include "wx/settings.h" +#include -DIALOG_EESCHEMA_OPTIONS::DIALOG_EESCHEMA_OPTIONS( wxWindow* parent ) : + +DIALOG_EESCHEMA_OPTIONS::DIALOG_EESCHEMA_OPTIONS( SCH_EDIT_FRAME* parent ) : DIALOG_EESCHEMA_OPTIONS_BASE( parent ) { m_choiceUnits->SetFocus(); @@ -50,9 +54,29 @@ DIALOG_EESCHEMA_OPTIONS::DIALOG_EESCHEMA_OPTIONS( wxWindow* parent ) : m_fieldGrid->AutoSizeColLabelSize( i ); } + // Embed the hotkeys list + HOTKEY_SECTIONS sections = WIDGET_HOTKEY_LIST::GenSections( g_Eeschema_Hokeys_Descr ); + m_hotkeyListCtrl = new WIDGET_HOTKEY_LIST( m_panelHotkeys, sections ); + m_hotkeyListCtrl->InstallOnPanel( m_panelHotkeys ); + // Make sure we select the first tab of the options tab page m_notebook->SetSelection( 0 ); + // Lay out all child pages + // No, I don't know why this->Layout() doesn't propagate through to these, + // but at least on MSW, it does not. + for( size_t i = 0; i < m_notebook->GetPageCount(); ++i ) + { + m_notebook->GetPage( i )->Layout(); + } + + Layout(); +} + + +SCH_EDIT_FRAME* DIALOG_EESCHEMA_OPTIONS::GetParent() +{ + return static_cast( DIALOG_EESCHEMA_OPTIONS_BASE::GetParent() ); } @@ -147,11 +171,13 @@ void DIALOG_EESCHEMA_OPTIONS::OnAddButtonClick( wxCommandEvent& event ) for( int row = 0; row < m_fieldGrid->GetNumberRows(); ++row ) { bool this_row_selected = false; + for( int col = 0; col < m_fieldGrid->GetNumberCols(); ++col ) { if( m_fieldGrid->IsInSelection( row, col ) ) this_row_selected = true; } + if( this_row_selected ) { selected_row = row; @@ -198,10 +224,12 @@ void DIALOG_EESCHEMA_OPTIONS::OnDeleteButtonClick( wxCommandEvent& event ) TransferDataFromWindow(); int n_rows = m_fieldGrid->GetNumberRows(); + for( int count = 0; count < n_rows; ++count ) { // Iterate backwards, unsigned-friendly way for future int row = n_rows - count - 1; + if( rows_to_delete[row] ) { templateFields.erase( templateFields.begin() + row ); @@ -217,9 +245,14 @@ bool DIALOG_EESCHEMA_OPTIONS::TransferDataToWindow() if( !wxDialog::TransferDataToWindow() ) return false; + if( !m_hotkeyListCtrl->TransferDataToControl() ) + return false; + m_fieldGrid->Freeze(); + if( m_fieldGrid->GetNumberRows() ) m_fieldGrid->DeleteRows( 0, m_fieldGrid->GetNumberRows() ); + m_fieldGrid->AppendRows( templateFields.size() ); for( int row = 0; row < m_fieldGrid->GetNumberRows(); ++row ) @@ -238,21 +271,34 @@ bool DIALOG_EESCHEMA_OPTIONS::TransferDataToWindow() m_fieldGrid->SetCellRenderer( row, 2, new wxGridCellBoolRenderer() ); m_fieldGrid->SetCellAlignment( row, 2, wxALIGN_CENTRE, wxALIGN_CENTRE ); } + m_fieldGrid->AutoSizeRows(); m_fieldGrid->Thaw(); + Layout(); return true; } bool DIALOG_EESCHEMA_OPTIONS::TransferDataFromWindow() { + if( !wxDialog::TransferDataFromWindow() ) + return false; + + if( !m_hotkeyListCtrl->TransferDataFromControl() ) + return false; + + // Refresh hotkeys + GetParent()->ReCreateMenuBar(); + GetParent()->Refresh(); + for( int row = 0; row < m_fieldGrid->GetNumberRows(); ++row ) { - templateFields[row].m_Name = m_fieldGrid->GetCellValue( row, 0 ); + templateFields[row].m_Name = m_fieldGrid->GetCellValue( row, 0 ); templateFields[row].m_Value = m_fieldGrid->GetCellValue( row, 1 ); templateFields[row].m_Visible = ( m_fieldGrid->GetCellValue( row, 2 ) != wxEmptyString ); } + return true; } @@ -271,4 +317,3 @@ TEMPLATE_FIELDNAMES DIALOG_EESCHEMA_OPTIONS::GetTemplateFields( void ) { return templateFields; } - diff --git a/eeschema/dialogs/dialog_eeschema_options.h b/eeschema/dialogs/dialog_eeschema_options.h index 828d1b04bd..d5daca2f80 100644 --- a/eeschema/dialogs/dialog_eeschema_options.h +++ b/eeschema/dialogs/dialog_eeschema_options.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2009 Wayne Stambaugh - * Copyright (C) 1992-2011 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2016 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 @@ -34,9 +34,14 @@ #include #include +class WIDGET_HOTKEY_LIST; +class SCH_EDIT_FRAME; + class DIALOG_EESCHEMA_OPTIONS : public DIALOG_EESCHEMA_OPTIONS_BASE { protected: + WIDGET_HOTKEY_LIST* m_hotkeyListCtrl; + /** @brief The template fieldnames for this dialog */ TEMPLATE_FIELDNAMES templateFields; @@ -80,7 +85,9 @@ public: * * @param parent The dialog's parent */ - DIALOG_EESCHEMA_OPTIONS( wxWindow* parent ); + DIALOG_EESCHEMA_OPTIONS( SCH_EDIT_FRAME* parent ); + + virtual SCH_EDIT_FRAME* GetParent(); /** * Function GetUnitsSelection diff --git a/eeschema/dialogs/dialog_eeschema_options_base.cpp b/eeschema/dialogs/dialog_eeschema_options_base.cpp index b744d8dd39..9c85f9ddb6 100644 --- a/eeschema/dialogs/dialog_eeschema_options_base.cpp +++ b/eeschema/dialogs/dialog_eeschema_options_base.cpp @@ -233,7 +233,7 @@ DIALOG_EESCHEMA_OPTIONS_BASE::DIALOG_EESCHEMA_OPTIONS_BASE( wxWindow* parent, wx m_panel3->Layout(); bSizer8->Fit( m_panel3 ); m_notebook->AddPage( m_panel3, _("Editing"), false ); - m_panel4 = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_controlsPanel = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); wxBoxSizer* bSizer81; bSizer81 = new wxBoxSizer( wxVERTICAL ); @@ -248,35 +248,51 @@ DIALOG_EESCHEMA_OPTIONS_BASE::DIALOG_EESCHEMA_OPTIONS_BASE( wxWindow* parent, wx bSizer81->Add( fgSizer31, 0, wxALL|wxEXPAND, 5 ); - wxBoxSizer* bSizer91; - bSizer91 = new wxBoxSizer( wxVERTICAL ); + m_controlsSizer = new wxBoxSizer( wxVERTICAL ); - m_checkEnableZoomCenter = new wxCheckBox( m_panel4, wxID_ANY, _("Cen&ter and warp cursor on zoom"), wxDefaultPosition, wxDefaultSize, 0 ); + wxBoxSizer* bSizer13; + bSizer13 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText20 = new wxStaticText( m_controlsPanel, wxID_ANY, _("Hotkeys:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText20->Wrap( -1 ); + bSizer13->Add( m_staticText20, 1, wxALL, 5 ); + + m_staticText21 = new wxStaticText( m_controlsPanel, wxID_ANY, _("Double-click to edit"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText21->Wrap( -1 ); + bSizer13->Add( m_staticText21, 0, wxALL, 5 ); + + + m_controlsSizer->Add( bSizer13, 0, wxEXPAND, 5 ); + + m_panelHotkeys = new wxPanel( m_controlsPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_controlsSizer->Add( m_panelHotkeys, 1, wxEXPAND | wxALL, 5 ); + + m_checkEnableZoomCenter = new wxCheckBox( m_controlsPanel, wxID_ANY, _("Cen&ter and warp cursor on zoom"), wxDefaultPosition, wxDefaultSize, 0 ); m_checkEnableZoomCenter->SetToolTip( _("Keep the cursor at its current location when zooming") ); - bSizer91->Add( m_checkEnableZoomCenter, 0, wxTOP|wxRIGHT|wxLEFT, 3 ); + m_controlsSizer->Add( m_checkEnableZoomCenter, 0, wxTOP|wxRIGHT|wxLEFT, 3 ); - m_checkEnableMiddleButtonPan = new wxCheckBox( m_panel4, xwID_ANY, _("&Use middle mouse button to pan"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkEnableMiddleButtonPan = new wxCheckBox( m_controlsPanel, xwID_ANY, _("&Use middle mouse button to pan"), wxDefaultPosition, wxDefaultSize, 0 ); m_checkEnableMiddleButtonPan->SetToolTip( _("Use middle mouse button dragging to pan") ); - bSizer91->Add( m_checkEnableMiddleButtonPan, 0, wxTOP|wxRIGHT|wxLEFT, 3 ); + m_controlsSizer->Add( m_checkEnableMiddleButtonPan, 0, wxTOP|wxRIGHT|wxLEFT, 3 ); - m_checkMiddleButtonPanLimited = new wxCheckBox( m_panel4, wxID_ANY, _("&Limit panning to scroll size"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkMiddleButtonPanLimited = new wxCheckBox( m_controlsPanel, wxID_ANY, _("&Limit panning to scroll size"), wxDefaultPosition, wxDefaultSize, 0 ); m_checkMiddleButtonPanLimited->SetToolTip( _("Middle mouse button panning limited by current scrollbar size") ); - bSizer91->Add( m_checkMiddleButtonPanLimited, 0, wxTOP|wxRIGHT|wxLEFT, 3 ); + m_controlsSizer->Add( m_checkMiddleButtonPanLimited, 0, wxTOP|wxRIGHT|wxLEFT, 3 ); - m_checkAutoPan = new wxCheckBox( m_panel4, wxID_ANY, _("&Pan while moving object"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer91->Add( m_checkAutoPan, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 3 ); + m_checkAutoPan = new wxCheckBox( m_controlsPanel, wxID_ANY, _("&Pan while moving object"), wxDefaultPosition, wxDefaultSize, 0 ); + m_controlsSizer->Add( m_checkAutoPan, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 3 ); - bSizer81->Add( bSizer91, 0, wxALL|wxEXPAND, 5 ); + bSizer81->Add( m_controlsSizer, 1, wxALL|wxEXPAND, 5 ); - m_panel4->SetSizer( bSizer81 ); - m_panel4->Layout(); - bSizer81->Fit( m_panel4 ); - m_notebook->AddPage( m_panel4, _("Co&ntrols"), false ); + m_controlsPanel->SetSizer( bSizer81 ); + m_controlsPanel->Layout(); + bSizer81->Fit( m_controlsPanel ); + m_notebook->AddPage( m_controlsPanel, _("Controls"), false ); m_panel2 = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); m_panel2->SetToolTip( _("User defined field names for schematic components. ") ); @@ -342,10 +358,13 @@ DIALOG_EESCHEMA_OPTIONS_BASE::DIALOG_EESCHEMA_OPTIONS_BASE( wxWindow* parent, wx m_panel2->SetSizer( bSizer6 ); m_panel2->Layout(); bSizer6->Fit( m_panel2 ); - m_notebook->AddPage( m_panel2, _("Default &Fields"), false ); + m_notebook->AddPage( m_panel2, _("Default Fields"), false ); bOptionsSizer->Add( m_notebook, 1, wxALL|wxEXPAND, 5 ); + wxBoxSizer* bSizer12; + bSizer12 = new wxBoxSizer( wxHORIZONTAL ); + m_sdbSizer = new wxStdDialogButtonSizer(); m_sdbSizerOK = new wxButton( this, wxID_OK ); m_sdbSizer->AddButton( m_sdbSizerOK ); @@ -353,7 +372,10 @@ DIALOG_EESCHEMA_OPTIONS_BASE::DIALOG_EESCHEMA_OPTIONS_BASE( wxWindow* parent, wx m_sdbSizer->AddButton( m_sdbSizerCancel ); m_sdbSizer->Realize(); - bOptionsSizer->Add( m_sdbSizer, 0, wxALL|wxEXPAND, 6 ); + bSizer12->Add( m_sdbSizer, 1, wxALL|wxEXPAND, 5 ); + + + bOptionsSizer->Add( bSizer12, 0, wxBOTTOM|wxEXPAND, 5 ); mainSizer->Add( bOptionsSizer, 1, wxEXPAND, 12 ); diff --git a/eeschema/dialogs/dialog_eeschema_options_base.fbp b/eeschema/dialogs/dialog_eeschema_options_base.fbp index 7a24be7c2f..668ac9f496 100644 --- a/eeschema/dialogs/dialog_eeschema_options_base.fbp +++ b/eeschema/dialogs/dialog_eeschema_options_base.fbp @@ -184,11 +184,11 @@ - + Display 1 - + 1 1 1 @@ -262,16 +262,16 @@ - + bSizer82 wxVERTICAL none - + 5 wxALL|wxEXPAND 0 - + 3 wxBOTH 0,1,2 @@ -1675,11 +1675,11 @@ - + Editing 0 - + 1 1 1 @@ -1753,16 +1753,16 @@ - + bSizer8 wxVERTICAL none - + 5 wxALL|wxEXPAND 0 - + 3 wxBOTH 0,1,2 @@ -3069,11 +3069,11 @@ - + 3 wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT 0 - + 1 1 1 @@ -3769,7 +3769,7 @@ - Co&ntrols + Controls 0 1 @@ -3806,7 +3806,7 @@ 0 1 - m_panel4 + m_controlsPanel 1 @@ -3850,11 +3850,11 @@ bSizer81 wxVERTICAL none - + 5 wxALL|wxEXPAND 0 - + 3 wxBOTH 0,1,2 @@ -3868,15 +3868,272 @@ 0 - + 5 wxALL|wxEXPAND - 0 - + 1 + - bSizer91 + m_controlsSizer wxVERTICAL - none + protected + + 5 + wxEXPAND + 0 + + + bSizer13 + wxHORIZONTAL + none + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Hotkeys: + + 0 + + + 0 + + 1 + m_staticText20 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Double-click to edit + + 0 + + + 0 + + 1 + m_staticText21 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND | wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_panelHotkeys + 1 + + + protected + 1 + + Resizable + 1 + + + 0 + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + 3 wxTOP|wxRIGHT|wxLEFT @@ -4234,11 +4491,11 @@ - + - Default &Fields + Default Fields 0 - + 1 1 1 @@ -4312,25 +4569,25 @@ - + bSizer6 wxHORIZONTAL none - + 5 wxEXPAND 1 - + bSizer11 wxVERTICAL none - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -4471,7 +4728,7 @@ - + 5 wxEXPAND 0 @@ -4673,30 +4930,41 @@ - - 6 - wxALL|wxEXPAND + + 5 + wxBOTTOM|wxEXPAND 0 - - 0 - 1 - 0 - 0 - 0 - 1 - 0 - 0 + - m_sdbSizer - protected - - - - - - - - + bSizer12 + wxHORIZONTAL + none + + 5 + wxALL|wxEXPAND + 1 + + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + + m_sdbSizer + protected + + + + + + + + + + diff --git a/eeschema/dialogs/dialog_eeschema_options_base.h b/eeschema/dialogs/dialog_eeschema_options_base.h index 585d4425c4..5b20e1c9f4 100644 --- a/eeschema/dialogs/dialog_eeschema_options_base.h +++ b/eeschema/dialogs/dialog_eeschema_options_base.h @@ -103,7 +103,11 @@ class DIALOG_EESCHEMA_OPTIONS_BASE : public DIALOG_SHIM wxCheckBox* m_checkAutoplaceFields; wxCheckBox* m_checkAutoplaceJustify; wxCheckBox* m_checkAutoplaceAlign; - wxPanel* m_panel4; + wxPanel* m_controlsPanel; + wxBoxSizer* m_controlsSizer; + wxStaticText* m_staticText20; + wxStaticText* m_staticText21; + wxPanel* m_panelHotkeys; wxCheckBox* m_checkEnableZoomCenter; wxCheckBox* m_checkEnableMiddleButtonPan; wxCheckBox* m_checkMiddleButtonPanLimited; diff --git a/eeschema/menubar.cpp b/eeschema/menubar.cpp index 0baaa1e5b3..2e8047dada 100644 --- a/eeschema/menubar.cpp +++ b/eeschema/menubar.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2009-2014 Wayne Stambaugh - * Copyright (C) 1992-2014 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2016 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 @@ -398,23 +398,38 @@ void SCH_EDIT_FRAME::ReCreateMenuBar() // Language submenu Pgm().AddMenuLanguageList( preferencesMenu ); - // Hotkey submenu - AddHotkeyConfigMenu( preferencesMenu ); + // Import/export + wxMenu* importExportSubmenu = new wxMenu(); - // Separator - preferencesMenu->AppendSeparator(); - - AddMenuItem( preferencesMenu, + AddMenuItem( importExportSubmenu, ID_CONFIG_SAVE, _( "&Save Preferences" ), _( "Save application preferences" ), - KiBitmap( save_setup_xpm ) ); + wxNullBitmap ); - AddMenuItem( preferencesMenu, + AddMenuItem( importExportSubmenu, ID_CONFIG_READ, _( "Load Prefe&rences" ), _( "Load application preferences" ), - KiBitmap( read_setup_xpm ) ); + wxNullBitmap ); + + AddMenuItem( importExportSubmenu, + ID_PREFERENCES_HOTKEY_EXPORT_CONFIG, + _( "E&xport Hotkeys" ), + _( "Create a hotkey configuration file to export the current hotkeys" ), + wxNullBitmap ); + + AddMenuItem( importExportSubmenu, + ID_PREFERENCES_HOTKEY_IMPORT_CONFIG, + _( "&Import Hotkeys" ), + _( "Load an existing hotkey configuration file" ), + wxNullBitmap ); + + AddMenuItem( preferencesMenu, importExportSubmenu, + wxID_ANY, + _( "&Import and export" ), + _( "Import and export settings" ), + KiBitmap( save_setup_xpm ) ); // Menu Tools: wxMenu* toolsMenu = new wxMenu; diff --git a/include/dialog_hotkeys_editor.h b/include/dialog_hotkeys_editor.h index c0da68277d..801bea9aea 100644 --- a/include/dialog_hotkeys_editor.h +++ b/include/dialog_hotkeys_editor.h @@ -5,7 +5,7 @@ * * 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 + * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -28,151 +28,9 @@ #ifndef __dialog_hotkeys_editor__ #define __dialog_hotkeys_editor__ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - #include -#include #include <../common/dialogs/dialog_hotkeys_editor_base.h> - -typedef std::pair HOTKEYS_SECTION; -typedef std::vector HOTKEYS_SECTIONS; - -typedef std::vector HOTKEY_LIST; - -class HOTKEYS_EDITOR_DIALOG; -class DIALOG_HOTKEY_CLIENT_DATA; - -/** - * Class HOTKEY_LIST_CTRL - * is a class to contain the contents of a hotkey editor tab page. - */ -class HOTKEY_LIST_CTRL : public wxTreeListCtrl -{ -public: - HOTKEY_LIST_CTRL( wxWindow* aParent, const HOTKEYS_SECTIONS& aSections ); - ~HOTKEY_LIST_CTRL() {}; - - /** - * Function DeselectRow - * Deselect the given row - * - * @param aRow is the row to deselect - */ - void DeselectRow( int aRow ); - - /** - * Function TransferDataToControl - * Load the hotkey data into the control. - * @return true iff the operation was successful - */ - bool TransferDataToControl(); - - /** - * Function TransferDataFromControl - * Save the hotkey data from the control. - * @return true iff the operation was successful - */ - bool TransferDataFromControl(); - - /** - * Function ResolveKeyConflicts - * Check if we can set a hotkey, this will prompt the user if there - * is a conflict between keys. The key code should have already been - * checked that it's not for the same entry as its currently in or else - * it'll prompt the change on itself. - * The function will do conflict detection depending on aSectionTag. - * g_CommonSectionTag means the key code must be checked with all sections. - * While other tags means the key code only must be checked with the aSectionTag - * section and g_CommonSectionTag section. - * - * @param aKey is the key code that wants to be set - * @param aSectionTag is the section tag that the key code came from - * - * @return True if the user accepted the overwrite or no conflict existed - */ - bool ResolveKeyConflicts( long aKey, const wxString& aSectionTag ); - - - /** - * Function CheckKeyConflicts - * Check whether the given key conflicts with anything in this HOTKEY_LIST_CTRL. - * - * @param aKey - key to check - * @param aSectionTag - section tag of the key - * @param aConfKey - if not NULL, outparam holding the key this one conflicts with - * @param aConfSect - if not NULL, outparam holding the section this one conflicts with - */ - bool CheckKeyConflicts( long aKey, const wxString& aSectionTag, - EDA_HOTKEY** aConfKey, EDA_HOTKEY_CONFIG** aConfSect ); - - /** - * Function UpdateFromClientData - * Update all visible items from the data stored in their client data objects. - */ - void UpdateFromClientData(); - -private: - HOTKEYS_SECTIONS m_sections; - std::vector< HOTKEY_LIST > m_hotkeys; - std::vector< wxTreeListItem > m_items; - - /** - * Function GetSelHKClientData - * Return the DIALOG_HOTKEY_CLIENT_DATA for the item being edited, or NULL if none is selected. - */ - DIALOG_HOTKEY_CLIENT_DATA* GetSelHKClientData(); - - /** - * Function GetHKClientData - * Return the DIALOG_HOTKEY_CLIENT_DATA for the given item, or NULL if invalid. - */ - DIALOG_HOTKEY_CLIENT_DATA* GetHKClientData( wxTreeListItem aItem ); - -protected: - /** - * Function LoadSection - * Generates a HOTKEY_LIST from the given hotkey configuration array and - * pushes it to m_hotkeys. - * - * @param aSection is a pointer to the hotkey configuration array - */ - void LoadSection( struct EDA_HOTKEY_CONFIG* aSection ); - - /** - * Function OnGetItemText - * Returns the requested row, column data to the list control. - * - * @param aRow is the row of the data which matches our hotkeys vector as a index - * @param aColumn is the column of the data which is either Command(0) or KeyCode(1) - * - * @return String containing the text for the specified row, column combination - */ - wxString OnGetItemText( long aRow, long aColumn ) const; - - /** - * Function OnChar - * Decoded key press handler which is used to set key codes in the list control - * - * @param aEvent is the key press event, the keycode is retrieved from it - */ - void OnChar( wxKeyEvent& aEvent ); -}; - +#include /** * Class HOTKEYS_EDITOR_DIALOG @@ -182,14 +40,18 @@ protected: class HOTKEYS_EDITOR_DIALOG : public HOTKEYS_EDITOR_DIALOG_BASE { protected: - EDA_BASE_FRAME* m_parent; struct EDA_HOTKEY_CONFIG* m_hotkeys; - HOTKEY_LIST_CTRL* m_hotkeyListCtrl; + WIDGET_HOTKEY_LIST* m_hotkeyListCtrl; bool TransferDataToWindow(); bool TransferDataFromWindow(); + virtual EDA_BASE_FRAME* GetParent() + { + return static_cast( HOTKEYS_EDITOR_DIALOG_BASE::GetParent() ); + } + public: HOTKEYS_EDITOR_DIALOG( EDA_BASE_FRAME* aParent, EDA_HOTKEY_CONFIG* aHotkeys ); diff --git a/include/hotkeys_basic.h b/include/hotkeys_basic.h index a2dc77302d..089fcce479 100644 --- a/include/hotkeys_basic.h +++ b/include/hotkeys_basic.h @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2004-2011 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2004-2016 KiCad Developers, see change_log.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 @@ -29,6 +29,8 @@ #ifndef HOTKEYS_BASIC_H #define HOTKEYS_BASIC_H +#include + #define DEFAULT_HOTKEY_FILENAME_EXT wxT( "hotkeys" ) // A define to allow translation of Hot Key message Info in hotkey help menu diff --git a/include/widgets/widget_hotkey_list.h b/include/widgets/widget_hotkey_list.h new file mode 100644 index 0000000000..bfaf010b63 --- /dev/null +++ b/include/widgets/widget_hotkey_list.h @@ -0,0 +1,202 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2016 Chris Pavlina + * Copyright (C) 2016 KiCad Developers, see CHANGELOG.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 3 + * 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 widget_hotkey_list + */ + +#ifndef __widget_hotkey_list__ +#define __widget_hotkey_list__ + +#include +#include + +#include + +#include + +/** + * struct HOTKEY_SECTION + * Associates a hotkey configuration with a name. + */ +struct HOTKEY_SECTION +{ + wxString m_name; + EDA_HOTKEY_CONFIG* m_section; +}; + +typedef std::vector HOTKEY_SECTIONS; +typedef std::vector HOTKEY_LIST; + +class WIDGET_HOTKEY_CLIENT_DATA; + +class WIDGET_HOTKEY_LIST : public wxTreeListCtrl +{ + HOTKEY_SECTIONS m_sections; + std::vector m_hotkeys; + wxTreeListItem m_context_menu_item; + + /** + * Method GetHKClientData + * Return the WIDGET_HOTKEY_CLIENT_DATA for the given item, or NULL if the + * item is invalid. + */ + WIDGET_HOTKEY_CLIENT_DATA* GetHKClientData( wxTreeListItem aItem ); + + /** + * Method GetSelHKClientData + * Return the WIDGET_HOTKEY_CLIENT_DATA for the item being edited, or NULL if + * none is selected. + */ + WIDGET_HOTKEY_CLIENT_DATA* GetSelHKClientData(); + + /** + * Method UpdateFromClientData + * Refresh the visible text on the widget from the rows' client data objects. + */ + void UpdateFromClientData(); + +protected: + /** + * Method LoadSection + * Generates a HOTKEY_LIST from the given hotkey configuration array and pushes + * it to m_hotkeys. + */ + void LoadSection( EDA_HOTKEY_CONFIG* aSection ); + + /** + * Method EditItem + * Prompt the user for a new hotkey given a list item. + */ + void EditItem( wxTreeListItem aItem ); + + /** + * Method ResetItem + * Reset the item to the original from the dialog was created. + */ + void ResetItem( wxTreeListItem aItem ); + + /** + * Method OnActivated + * Handle activation of a row. + */ + void OnActivated( wxTreeListEvent& aEvent ); + + /** + * Method OnContextMenu + * Handle right-click on a row. + */ + void OnContextMenu( wxTreeListEvent& aEvent ); + + /** + * Method OnMenu + * Handle activation of a context menu item. + */ + void OnMenu( wxCommandEvent& aEvent ); + + /** + * Function OnSize + * Handle resizing of the control. Overrides the buggy wxTreeListCtrl::OnSize. + */ + void OnSize( wxSizeEvent& aEvent ); + + /** + * Method CheckKeyConflicts + * Check whether the given key conflicts with anything in this WIDGET_HOTKEY_LIST. + * + * @param aKey - key to check + * @param aSectionTag - section tag into which the key is proposed to be installed + * @param aConfKey - if not NULL, outparam getting the key this one conflicts with + * @param aConfSect - if not NULL, outparam getting the section this one conflicts with + */ + bool CheckKeyConflicts( long aKey, const wxString& aSectionTag, + EDA_HOTKEY** aConfKey, EDA_HOTKEY_CONFIG** aConfSect ); + + /** + * Method ResolveKeyConflicts + * Check if we can set a hotkey, and prompt the user if there is a conflict between + * keys. The key code should already have been checked that it's not for the same + * entry as it's current in, or else this method will prompt for the self-change. + * + * The method will do conflict resolution depending on aSectionTag. + * g_CommonSectionTag means the key code must only be checkd with the aSectionTag + * section and g_CommonSectionTag section. + * + * @param aKey - key to check + * @param aSectionTag - section tag into which the key is proposed to be installed + * + * @return true iff the user accepted the overwrite or no conflict existed + */ + bool ResolveKeyConflicts( long aKey, const wxString& aSectionTag ); + +public: + /** + * Constructor WIDGET_HOTKEY_LIST + * Create a WIDGET_HOTKEY_LIST. + * + * @param aParent - parent widget + * @param aSections - list of the hotkey sections to display and their names. + * See WIDGET_HOTKEY_LIST::GenSections for a way to generate these easily + * from an EDA_HOTKEY_CONFIG*. + */ + WIDGET_HOTKEY_LIST( wxWindow* aParent, const HOTKEY_SECTIONS& aSections ); + + /** + * Static method GenSections + * Generate a list of sections and names from an EDA_HOTKEY_CONFIG*. Titles + * will be looked up from translations. + */ + static HOTKEY_SECTIONS GenSections( EDA_HOTKEY_CONFIG* aHotkeys ); + + /** + * Method InstallOnPanel + * Install this WIDGET_HOTKEY_LIST onto an empty panel. This is useful + * when combining with wxFormBuilder, as an empty panel can be left as a + * placeholder in the layout. + */ + void InstallOnPanel( wxPanel* aPanel ); + + /** + * Method TransferDataToControl + * Load the hotkey data into the control. It is safe to call this multiple times, + * for example to reset the control. + * @return true iff the operation was successful + */ + bool TransferDataToControl(); + + /** + * Method TransferDataFromControl + * Save the hotkey data from the control. + * @return true iff the operation was successful + */ + bool TransferDataFromControl(); + + /** + * Static method MapKeypressToKeycode + * Map a keypress event to the correct key code for use as a hotkey. + */ + static long MapKeypressToKeycode( const wxKeyEvent& aEvent ); +}; + +#endif // __widget_hotkey_list__