← Back to team overview

kicad-developers team mailing list archive

Final? version of hotkeys patch

 

Hi,

Jesus, here be dragons. Finally got the hotkeys stuff working properly 
on all platforms - this has been tested on Linux, Win10, and OSX. Thanks 
to JP for a push in the right direction (and even that required more 
work!).

Quick summary of the problems:

    - On Windows, there is a bug/quirk somewhere, where if a Tab 
      keypress occurs in a dialog with nothing in the tab order, this 
      must NOT generate the corresponding wxEVT_CHAR. If this happens, 
      the entire application freezes solid. This has nothing to do with 
      wxTAB_TRAVERSAL, so disabling this style property does not help.

    - wxEVT_CHAR_HOOK can be used to catch this event early and block 
      the tab bug. It also has the benefit of catching other 'special' 
      keys that wxEVT_CHAR misses (again, on Windows. wxEVT_CHAR has no 
      problem receiving them on Linux).

    - .../however/, wxEVT_CHAR_HOOK reports some keys incorrectly. Any 
      shifted symbol keys are reported as shift+(the unshifted key), so 
      on a US keyboard for example, when you type <?>, it sees 
      <Shift>+</>. There's no easy way to map these to the "correct" 
      keys, particularly considering international keyboards.

    - When wxEvent::DoAllowNextEvent() is called (see below), 
      wxEvent::Skip MUST be called on Linux and OSX, and must NOT be 
      called on Windows. No... I don't know why.

In the end, I implemented separate OnChar and OnCharHook handlers. 
OnCharHook does the following:

    1. If the keypress is a pure modifier (wxEVT_CHAR_HOOK generates 
    events for things like Ctrl by itself), do nothing.

    2. If the keypress is for a printable character **that is not 
    whitespace** (to avoid tripping the Tab bug), call 
    wxEvent::DoAllowNextEvent to cause the wxEVT_CHAR for the same key 
    to be generated. Call or do not call wxEvent::Skip depending on 
    platform, as above. This causes the "correct" key to be looked up 
    (e.g.  <?> instead of <Shift></>) and this progresses to the OnChar 
    handler.

    3. For all other keys, do not allow the wxEVT_CHAR to be generated, 
    but instead pass the event object directly to OnChar.


Then OnChar handles returning the keycode to caller.

Please, help me test this. Wayne, if this works, and you don't mind, I'd 
really like to get it merged. Even if there are still minor GUI quirks 
and whatnot, the current top of the devel branch has the hotkey bugs 
from earlier that I'd like to get fixed. Any further minor issues can be 
resolved in further minor commits.

--
Exasperatedly,
Chris
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 9d8b71f..675c565 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}
@@ -138,6 +139,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
@@ -154,6 +158,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
@@ -167,6 +175,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 a3c2742..96a8d3b 100644
--- a/common/dialogs/dialog_hotkeys_editor.cpp
+++ b/common/dialogs/dialog_hotkeys_editor.cpp
@@ -21,321 +21,14 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
-#include <algorithm>
-
-#include <fctsys.h>
-#include <pgm_base.h>
-#include <common.h>
-#include <confirm.h>
-
 #include <dialog_hotkeys_editor.h>
 
-
-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 < 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();
-                HOTKEYS_EDITOR_DIALOG* parent = static_cast<HOTKEYS_EDITOR_DIALOG*>( m_parent );
-                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<DIALOG_HOTKEY_CLIENT_DATA*>( 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();
@@ -344,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;
+    m_hotkeyListCtrl = new WIDGET_HOTKEY_LIST( m_panelHotkeys,
+            WIDGET_HOTKEY_LIST::GenSections( aHotkeys ) );
+    m_hotkeyListCtrl->InstallOnPanel( m_panelHotkeys );
 
-    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_sdbSizerOK->SetDefault();
 
-    m_hotkeyListCtrl = new HOTKEY_LIST_CTRL( this, sections );
-    m_mainSizer->Insert( 1, m_hotkeyListCtrl, wxSizerFlags( 1 ).Expand().Border( wxALL, 5 ) );
     Layout();
-
-    m_sdbSizerOK->SetDefault();
     Center();
 }
 
@@ -389,7 +74,7 @@ bool HOTKEYS_EDITOR_DIALOG::TransferDataFromWindow()
         return false;
 
     // save the hotkeys
-    m_parent->WriteHotkeyConfig( m_hotkeys );
+    GetParent()->WriteHotkeyConfig( m_hotkeys );
 
     return true;
 }
@@ -399,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 1293cb4..5373ac5 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 814b3ac..6a096fa 100644
--- a/common/dialogs/dialog_hotkeys_editor_base.fbp
+++ b/common/dialogs/dialog_hotkeys_editor_base.fbp
@@ -125,7 +125,7 @@
                         <property name="gripper">0</property>
                         <property name="hidden">0</property>
                         <property name="id">wxID_ANY</property>
-                        <property name="label">Select a row and press a new key combination to alter the binding.</property>
+                        <property name="label">Double-click to edit</property>
                         <property name="max_size"></property>
                         <property name="maximize_button">0</property>
                         <property name="maximum_size"></property>
@@ -178,6 +178,86 @@
                 </object>
                 <object class="sizeritem" expanded="1">
                     <property name="border">5</property>
+                    <property name="flag">wxEXPAND | wxALL</property>
+                    <property name="proportion">1</property>
+                    <object class="wxPanel" expanded="1">
+                        <property name="BottomDockable">1</property>
+                        <property name="LeftDockable">1</property>
+                        <property name="RightDockable">1</property>
+                        <property name="TopDockable">1</property>
+                        <property name="aui_layer"></property>
+                        <property name="aui_name"></property>
+                        <property name="aui_position"></property>
+                        <property name="aui_row"></property>
+                        <property name="best_size"></property>
+                        <property name="bg"></property>
+                        <property name="caption"></property>
+                        <property name="caption_visible">1</property>
+                        <property name="center_pane">0</property>
+                        <property name="close_button">1</property>
+                        <property name="context_help"></property>
+                        <property name="context_menu">1</property>
+                        <property name="default_pane">0</property>
+                        <property name="dock">Dock</property>
+                        <property name="dock_fixed">0</property>
+                        <property name="docking">Left</property>
+                        <property name="enabled">1</property>
+                        <property name="fg"></property>
+                        <property name="floatable">1</property>
+                        <property name="font"></property>
+                        <property name="gripper">0</property>
+                        <property name="hidden">0</property>
+                        <property name="id">wxID_ANY</property>
+                        <property name="max_size"></property>
+                        <property name="maximize_button">0</property>
+                        <property name="maximum_size"></property>
+                        <property name="min_size"></property>
+                        <property name="minimize_button">0</property>
+                        <property name="minimum_size"></property>
+                        <property name="moveable">1</property>
+                        <property name="name">m_panelHotkeys</property>
+                        <property name="pane_border">1</property>
+                        <property name="pane_position"></property>
+                        <property name="pane_size"></property>
+                        <property name="permission">protected</property>
+                        <property name="pin_button">1</property>
+                        <property name="pos"></property>
+                        <property name="resize">Resizable</property>
+                        <property name="show">1</property>
+                        <property name="size"></property>
+                        <property name="subclass"></property>
+                        <property name="toolbar_pane">0</property>
+                        <property name="tooltip"></property>
+                        <property name="window_extra_style"></property>
+                        <property name="window_name"></property>
+                        <property name="window_style">wxTAB_TRAVERSAL</property>
+                        <event name="OnChar"></event>
+                        <event name="OnEnterWindow"></event>
+                        <event name="OnEraseBackground"></event>
+                        <event name="OnKeyDown"></event>
+                        <event name="OnKeyUp"></event>
+                        <event name="OnKillFocus"></event>
+                        <event name="OnLeaveWindow"></event>
+                        <event name="OnLeftDClick"></event>
+                        <event name="OnLeftDown"></event>
+                        <event name="OnLeftUp"></event>
+                        <event name="OnMiddleDClick"></event>
+                        <event name="OnMiddleDown"></event>
+                        <event name="OnMiddleUp"></event>
+                        <event name="OnMotion"></event>
+                        <event name="OnMouseEvents"></event>
+                        <event name="OnMouseWheel"></event>
+                        <event name="OnPaint"></event>
+                        <event name="OnRightDClick"></event>
+                        <event name="OnRightDown"></event>
+                        <event name="OnRightUp"></event>
+                        <event name="OnSetFocus"></event>
+                        <event name="OnSize"></event>
+                        <event name="OnUpdateUI"></event>
+                    </object>
+                </object>
+                <object class="sizeritem" expanded="1">
+                    <property name="border">5</property>
                     <property name="flag">wxALIGN_RIGHT|wxEXPAND</property>
                     <property name="proportion">0</property>
                     <object class="wxBoxSizer" expanded="1">
diff --git a/common/dialogs/dialog_hotkeys_editor_base.h b/common/dialogs/dialog_hotkeys_editor_base.h
index 9c58421..a59f560 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 <wx/font.h>
 #include <wx/colour.h>
 #include <wx/settings.h>
+#include <wx/panel.h>
 #include <wx/button.h>
 #include <wx/sizer.h>
 #include <wx/dialog.h>
@@ -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 0000000..a188968
--- /dev/null
+++ b/common/widgets/widget_hotkey_list.cpp
@@ -0,0 +1,672 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2016 Chris Pavlina <pavlina.chris@xxxxxxxxx>
+ * 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 <cctype>
+
+#include <widgets/widget_hotkey_list.h>
+
+#include <wx/dataview.h>
+#include <wx/statline.h>
+
+#include <draw_frame.h>
+#include <dialog_shim.h>
+
+
+/**
+ * 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, aName );
+        cmd_label_1->SetFont( cmd_label_1->GetFont().Bold().MakeLarger() );
+        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, aCurrentKey );
+        key_label_1->SetFont( key_label_1->GetFont().Bold().MakeLarger() );
+        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<WIDGET_HOTKEY_CLIENT_DATA*>( 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 c6b0f34..5ce1477 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 <stambaughw@xxxxxxxxxxx>
- * 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 <class_base_screen.h>
 
 #include <dialog_eeschema_options.h>
+#include <widgets/widget_hotkey_list.h>
+#include <schframe.h>
+#include <hotkeys.h>
 
-#include "wx/settings.h"
+#include <wx/settings.h>
 
-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<SCH_EDIT_FRAME*>( 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 828d1b0..d5daca2 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 <stambaughw@xxxxxxxxxxx>
- * 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 <dialog_eeschema_options_base.h>
 #include <template_fieldnames.h>
 
+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 b744d8d..9c85f9d 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 7a24be7..668ac9f 100644
--- a/eeschema/dialogs/dialog_eeschema_options_base.fbp
+++ b/eeschema/dialogs/dialog_eeschema_options_base.fbp
@@ -184,11 +184,11 @@
                                 <event name="OnSetFocus"></event>
                                 <event name="OnSize"></event>
                                 <event name="OnUpdateUI"></event>
-                                <object class="notebookpage" expanded="1">
+                                <object class="notebookpage" expanded="0">
                                     <property name="bitmap"></property>
                                     <property name="label">Display</property>
                                     <property name="select">1</property>
-                                    <object class="wxPanel" expanded="1">
+                                    <object class="wxPanel" expanded="0">
                                         <property name="BottomDockable">1</property>
                                         <property name="LeftDockable">1</property>
                                         <property name="RightDockable">1</property>
@@ -262,16 +262,16 @@
                                         <event name="OnSetFocus"></event>
                                         <event name="OnSize"></event>
                                         <event name="OnUpdateUI"></event>
-                                        <object class="wxBoxSizer" expanded="1">
+                                        <object class="wxBoxSizer" expanded="0">
                                             <property name="minimum_size"></property>
                                             <property name="name">bSizer82</property>
                                             <property name="orient">wxVERTICAL</property>
                                             <property name="permission">none</property>
-                                            <object class="sizeritem" expanded="1">
+                                            <object class="sizeritem" expanded="0">
                                                 <property name="border">5</property>
                                                 <property name="flag">wxALL|wxEXPAND</property>
                                                 <property name="proportion">0</property>
-                                                <object class="wxFlexGridSizer" expanded="1">
+                                                <object class="wxFlexGridSizer" expanded="0">
                                                     <property name="cols">3</property>
                                                     <property name="flexible_direction">wxBOTH</property>
                                                     <property name="growablecols">0,1,2</property>
@@ -1675,11 +1675,11 @@
                                         </object>
                                     </object>
                                 </object>
-                                <object class="notebookpage" expanded="1">
+                                <object class="notebookpage" expanded="0">
                                     <property name="bitmap"></property>
                                     <property name="label">Editing</property>
                                     <property name="select">0</property>
-                                    <object class="wxPanel" expanded="1">
+                                    <object class="wxPanel" expanded="0">
                                         <property name="BottomDockable">1</property>
                                         <property name="LeftDockable">1</property>
                                         <property name="RightDockable">1</property>
@@ -1753,16 +1753,16 @@
                                         <event name="OnSetFocus"></event>
                                         <event name="OnSize"></event>
                                         <event name="OnUpdateUI"></event>
-                                        <object class="wxBoxSizer" expanded="1">
+                                        <object class="wxBoxSizer" expanded="0">
                                             <property name="minimum_size"></property>
                                             <property name="name">bSizer8</property>
                                             <property name="orient">wxVERTICAL</property>
                                             <property name="permission">none</property>
-                                            <object class="sizeritem" expanded="1">
+                                            <object class="sizeritem" expanded="0">
                                                 <property name="border">5</property>
                                                 <property name="flag">wxALL|wxEXPAND</property>
                                                 <property name="proportion">0</property>
-                                                <object class="wxFlexGridSizer" expanded="1">
+                                                <object class="wxFlexGridSizer" expanded="0">
                                                     <property name="cols">3</property>
                                                     <property name="flexible_direction">wxBOTH</property>
                                                     <property name="growablecols">0,1,2</property>
@@ -3069,11 +3069,11 @@
                                                             <event name="OnUpdateUI"></event>
                                                         </object>
                                                     </object>
-                                                    <object class="sizeritem" expanded="1">
+                                                    <object class="sizeritem" expanded="0">
                                                         <property name="border">3</property>
                                                         <property name="flag">wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT</property>
                                                         <property name="proportion">0</property>
-                                                        <object class="wxStaticText" expanded="1">
+                                                        <object class="wxStaticText" expanded="0">
                                                             <property name="BottomDockable">1</property>
                                                             <property name="LeftDockable">1</property>
                                                             <property name="RightDockable">1</property>
@@ -3769,7 +3769,7 @@
                                 </object>
                                 <object class="notebookpage" expanded="1">
                                     <property name="bitmap"></property>
-                                    <property name="label">Co&amp;ntrols</property>
+                                    <property name="label">Controls</property>
                                     <property name="select">0</property>
                                     <object class="wxPanel" expanded="1">
                                         <property name="BottomDockable">1</property>
@@ -3806,7 +3806,7 @@
                                         <property name="minimize_button">0</property>
                                         <property name="minimum_size"></property>
                                         <property name="moveable">1</property>
-                                        <property name="name">m_panel4</property>
+                                        <property name="name">m_controlsPanel</property>
                                         <property name="pane_border">1</property>
                                         <property name="pane_position"></property>
                                         <property name="pane_size"></property>
@@ -3850,11 +3850,11 @@
                                             <property name="name">bSizer81</property>
                                             <property name="orient">wxVERTICAL</property>
                                             <property name="permission">none</property>
-                                            <object class="sizeritem" expanded="1">
+                                            <object class="sizeritem" expanded="0">
                                                 <property name="border">5</property>
                                                 <property name="flag">wxALL|wxEXPAND</property>
                                                 <property name="proportion">0</property>
-                                                <object class="wxFlexGridSizer" expanded="1">
+                                                <object class="wxFlexGridSizer" expanded="0">
                                                     <property name="cols">3</property>
                                                     <property name="flexible_direction">wxBOTH</property>
                                                     <property name="growablecols">0,1,2</property>
@@ -3868,15 +3868,272 @@
                                                     <property name="vgap">0</property>
                                                 </object>
                                             </object>
-                                            <object class="sizeritem" expanded="0">
+                                            <object class="sizeritem" expanded="1">
                                                 <property name="border">5</property>
                                                 <property name="flag">wxALL|wxEXPAND</property>
-                                                <property name="proportion">0</property>
-                                                <object class="wxBoxSizer" expanded="0">
+                                                <property name="proportion">1</property>
+                                                <object class="wxBoxSizer" expanded="1">
                                                     <property name="minimum_size"></property>
-                                                    <property name="name">bSizer91</property>
+                                                    <property name="name">m_controlsSizer</property>
                                                     <property name="orient">wxVERTICAL</property>
-                                                    <property name="permission">none</property>
+                                                    <property name="permission">protected</property>
+                                                    <object class="sizeritem" expanded="1">
+                                                        <property name="border">5</property>
+                                                        <property name="flag">wxEXPAND</property>
+                                                        <property name="proportion">0</property>
+                                                        <object class="wxBoxSizer" expanded="1">
+                                                            <property name="minimum_size"></property>
+                                                            <property name="name">bSizer13</property>
+                                                            <property name="orient">wxHORIZONTAL</property>
+                                                            <property name="permission">none</property>
+                                                            <object class="sizeritem" expanded="0">
+                                                                <property name="border">5</property>
+                                                                <property name="flag">wxALL</property>
+                                                                <property name="proportion">1</property>
+                                                                <object class="wxStaticText" expanded="0">
+                                                                    <property name="BottomDockable">1</property>
+                                                                    <property name="LeftDockable">1</property>
+                                                                    <property name="RightDockable">1</property>
+                                                                    <property name="TopDockable">1</property>
+                                                                    <property name="aui_layer"></property>
+                                                                    <property name="aui_name"></property>
+                                                                    <property name="aui_position"></property>
+                                                                    <property name="aui_row"></property>
+                                                                    <property name="best_size"></property>
+                                                                    <property name="bg"></property>
+                                                                    <property name="caption"></property>
+                                                                    <property name="caption_visible">1</property>
+                                                                    <property name="center_pane">0</property>
+                                                                    <property name="close_button">1</property>
+                                                                    <property name="context_help"></property>
+                                                                    <property name="context_menu">1</property>
+                                                                    <property name="default_pane">0</property>
+                                                                    <property name="dock">Dock</property>
+                                                                    <property name="dock_fixed">0</property>
+                                                                    <property name="docking">Left</property>
+                                                                    <property name="enabled">1</property>
+                                                                    <property name="fg"></property>
+                                                                    <property name="floatable">1</property>
+                                                                    <property name="font"></property>
+                                                                    <property name="gripper">0</property>
+                                                                    <property name="hidden">0</property>
+                                                                    <property name="id">wxID_ANY</property>
+                                                                    <property name="label">Hotkeys:</property>
+                                                                    <property name="max_size"></property>
+                                                                    <property name="maximize_button">0</property>
+                                                                    <property name="maximum_size"></property>
+                                                                    <property name="min_size"></property>
+                                                                    <property name="minimize_button">0</property>
+                                                                    <property name="minimum_size"></property>
+                                                                    <property name="moveable">1</property>
+                                                                    <property name="name">m_staticText20</property>
+                                                                    <property name="pane_border">1</property>
+                                                                    <property name="pane_position"></property>
+                                                                    <property name="pane_size"></property>
+                                                                    <property name="permission">protected</property>
+                                                                    <property name="pin_button">1</property>
+                                                                    <property name="pos"></property>
+                                                                    <property name="resize">Resizable</property>
+                                                                    <property name="show">1</property>
+                                                                    <property name="size"></property>
+                                                                    <property name="style"></property>
+                                                                    <property name="subclass"></property>
+                                                                    <property name="toolbar_pane">0</property>
+                                                                    <property name="tooltip"></property>
+                                                                    <property name="window_extra_style"></property>
+                                                                    <property name="window_name"></property>
+                                                                    <property name="window_style"></property>
+                                                                    <property name="wrap">-1</property>
+                                                                    <event name="OnChar"></event>
+                                                                    <event name="OnEnterWindow"></event>
+                                                                    <event name="OnEraseBackground"></event>
+                                                                    <event name="OnKeyDown"></event>
+                                                                    <event name="OnKeyUp"></event>
+                                                                    <event name="OnKillFocus"></event>
+                                                                    <event name="OnLeaveWindow"></event>
+                                                                    <event name="OnLeftDClick"></event>
+                                                                    <event name="OnLeftDown"></event>
+                                                                    <event name="OnLeftUp"></event>
+                                                                    <event name="OnMiddleDClick"></event>
+                                                                    <event name="OnMiddleDown"></event>
+                                                                    <event name="OnMiddleUp"></event>
+                                                                    <event name="OnMotion"></event>
+                                                                    <event name="OnMouseEvents"></event>
+                                                                    <event name="OnMouseWheel"></event>
+                                                                    <event name="OnPaint"></event>
+                                                                    <event name="OnRightDClick"></event>
+                                                                    <event name="OnRightDown"></event>
+                                                                    <event name="OnRightUp"></event>
+                                                                    <event name="OnSetFocus"></event>
+                                                                    <event name="OnSize"></event>
+                                                                    <event name="OnUpdateUI"></event>
+                                                                </object>
+                                                            </object>
+                                                            <object class="sizeritem" expanded="0">
+                                                                <property name="border">5</property>
+                                                                <property name="flag">wxALL</property>
+                                                                <property name="proportion">0</property>
+                                                                <object class="wxStaticText" expanded="0">
+                                                                    <property name="BottomDockable">1</property>
+                                                                    <property name="LeftDockable">1</property>
+                                                                    <property name="RightDockable">1</property>
+                                                                    <property name="TopDockable">1</property>
+                                                                    <property name="aui_layer"></property>
+                                                                    <property name="aui_name"></property>
+                                                                    <property name="aui_position"></property>
+                                                                    <property name="aui_row"></property>
+                                                                    <property name="best_size"></property>
+                                                                    <property name="bg"></property>
+                                                                    <property name="caption"></property>
+                                                                    <property name="caption_visible">1</property>
+                                                                    <property name="center_pane">0</property>
+                                                                    <property name="close_button">1</property>
+                                                                    <property name="context_help"></property>
+                                                                    <property name="context_menu">1</property>
+                                                                    <property name="default_pane">0</property>
+                                                                    <property name="dock">Dock</property>
+                                                                    <property name="dock_fixed">0</property>
+                                                                    <property name="docking">Left</property>
+                                                                    <property name="enabled">1</property>
+                                                                    <property name="fg"></property>
+                                                                    <property name="floatable">1</property>
+                                                                    <property name="font"></property>
+                                                                    <property name="gripper">0</property>
+                                                                    <property name="hidden">0</property>
+                                                                    <property name="id">wxID_ANY</property>
+                                                                    <property name="label">Double-click to edit</property>
+                                                                    <property name="max_size"></property>
+                                                                    <property name="maximize_button">0</property>
+                                                                    <property name="maximum_size"></property>
+                                                                    <property name="min_size"></property>
+                                                                    <property name="minimize_button">0</property>
+                                                                    <property name="minimum_size"></property>
+                                                                    <property name="moveable">1</property>
+                                                                    <property name="name">m_staticText21</property>
+                                                                    <property name="pane_border">1</property>
+                                                                    <property name="pane_position"></property>
+                                                                    <property name="pane_size"></property>
+                                                                    <property name="permission">protected</property>
+                                                                    <property name="pin_button">1</property>
+                                                                    <property name="pos"></property>
+                                                                    <property name="resize">Resizable</property>
+                                                                    <property name="show">1</property>
+                                                                    <property name="size"></property>
+                                                                    <property name="style"></property>
+                                                                    <property name="subclass"></property>
+                                                                    <property name="toolbar_pane">0</property>
+                                                                    <property name="tooltip"></property>
+                                                                    <property name="window_extra_style"></property>
+                                                                    <property name="window_name"></property>
+                                                                    <property name="window_style"></property>
+                                                                    <property name="wrap">-1</property>
+                                                                    <event name="OnChar"></event>
+                                                                    <event name="OnEnterWindow"></event>
+                                                                    <event name="OnEraseBackground"></event>
+                                                                    <event name="OnKeyDown"></event>
+                                                                    <event name="OnKeyUp"></event>
+                                                                    <event name="OnKillFocus"></event>
+                                                                    <event name="OnLeaveWindow"></event>
+                                                                    <event name="OnLeftDClick"></event>
+                                                                    <event name="OnLeftDown"></event>
+                                                                    <event name="OnLeftUp"></event>
+                                                                    <event name="OnMiddleDClick"></event>
+                                                                    <event name="OnMiddleDown"></event>
+                                                                    <event name="OnMiddleUp"></event>
+                                                                    <event name="OnMotion"></event>
+                                                                    <event name="OnMouseEvents"></event>
+                                                                    <event name="OnMouseWheel"></event>
+                                                                    <event name="OnPaint"></event>
+                                                                    <event name="OnRightDClick"></event>
+                                                                    <event name="OnRightDown"></event>
+                                                                    <event name="OnRightUp"></event>
+                                                                    <event name="OnSetFocus"></event>
+                                                                    <event name="OnSize"></event>
+                                                                    <event name="OnUpdateUI"></event>
+                                                                </object>
+                                                            </object>
+                                                        </object>
+                                                    </object>
+                                                    <object class="sizeritem" expanded="1">
+                                                        <property name="border">5</property>
+                                                        <property name="flag">wxEXPAND | wxALL</property>
+                                                        <property name="proportion">1</property>
+                                                        <object class="wxPanel" expanded="1">
+                                                            <property name="BottomDockable">1</property>
+                                                            <property name="LeftDockable">1</property>
+                                                            <property name="RightDockable">1</property>
+                                                            <property name="TopDockable">1</property>
+                                                            <property name="aui_layer"></property>
+                                                            <property name="aui_name"></property>
+                                                            <property name="aui_position"></property>
+                                                            <property name="aui_row"></property>
+                                                            <property name="best_size"></property>
+                                                            <property name="bg"></property>
+                                                            <property name="caption"></property>
+                                                            <property name="caption_visible">1</property>
+                                                            <property name="center_pane">0</property>
+                                                            <property name="close_button">1</property>
+                                                            <property name="context_help"></property>
+                                                            <property name="context_menu">1</property>
+                                                            <property name="default_pane">0</property>
+                                                            <property name="dock">Dock</property>
+                                                            <property name="dock_fixed">0</property>
+                                                            <property name="docking">Left</property>
+                                                            <property name="enabled">1</property>
+                                                            <property name="fg"></property>
+                                                            <property name="floatable">1</property>
+                                                            <property name="font"></property>
+                                                            <property name="gripper">0</property>
+                                                            <property name="hidden">0</property>
+                                                            <property name="id">wxID_ANY</property>
+                                                            <property name="max_size"></property>
+                                                            <property name="maximize_button">0</property>
+                                                            <property name="maximum_size"></property>
+                                                            <property name="min_size"></property>
+                                                            <property name="minimize_button">0</property>
+                                                            <property name="minimum_size"></property>
+                                                            <property name="moveable">1</property>
+                                                            <property name="name">m_panelHotkeys</property>
+                                                            <property name="pane_border">1</property>
+                                                            <property name="pane_position"></property>
+                                                            <property name="pane_size"></property>
+                                                            <property name="permission">protected</property>
+                                                            <property name="pin_button">1</property>
+                                                            <property name="pos"></property>
+                                                            <property name="resize">Resizable</property>
+                                                            <property name="show">1</property>
+                                                            <property name="size"></property>
+                                                            <property name="subclass"></property>
+                                                            <property name="toolbar_pane">0</property>
+                                                            <property name="tooltip"></property>
+                                                            <property name="window_extra_style"></property>
+                                                            <property name="window_name"></property>
+                                                            <property name="window_style">wxTAB_TRAVERSAL</property>
+                                                            <event name="OnChar"></event>
+                                                            <event name="OnEnterWindow"></event>
+                                                            <event name="OnEraseBackground"></event>
+                                                            <event name="OnKeyDown"></event>
+                                                            <event name="OnKeyUp"></event>
+                                                            <event name="OnKillFocus"></event>
+                                                            <event name="OnLeaveWindow"></event>
+                                                            <event name="OnLeftDClick"></event>
+                                                            <event name="OnLeftDown"></event>
+                                                            <event name="OnLeftUp"></event>
+                                                            <event name="OnMiddleDClick"></event>
+                                                            <event name="OnMiddleDown"></event>
+                                                            <event name="OnMiddleUp"></event>
+                                                            <event name="OnMotion"></event>
+                                                            <event name="OnMouseEvents"></event>
+                                                            <event name="OnMouseWheel"></event>
+                                                            <event name="OnPaint"></event>
+                                                            <event name="OnRightDClick"></event>
+                                                            <event name="OnRightDown"></event>
+                                                            <event name="OnRightUp"></event>
+                                                            <event name="OnSetFocus"></event>
+                                                            <event name="OnSize"></event>
+                                                            <event name="OnUpdateUI"></event>
+                                                        </object>
+                                                    </object>
                                                     <object class="sizeritem" expanded="0">
                                                         <property name="border">3</property>
                                                         <property name="flag">wxTOP|wxRIGHT|wxLEFT</property>
@@ -4234,11 +4491,11 @@
                                         </object>
                                     </object>
                                 </object>
-                                <object class="notebookpage" expanded="1">
+                                <object class="notebookpage" expanded="0">
                                     <property name="bitmap"></property>
-                                    <property name="label">Default &amp;Fields</property>
+                                    <property name="label">Default Fields</property>
                                     <property name="select">0</property>
-                                    <object class="wxPanel" expanded="1">
+                                    <object class="wxPanel" expanded="0">
                                         <property name="BottomDockable">1</property>
                                         <property name="LeftDockable">1</property>
                                         <property name="RightDockable">1</property>
@@ -4312,25 +4569,25 @@
                                         <event name="OnSetFocus"></event>
                                         <event name="OnSize"></event>
                                         <event name="OnUpdateUI"></event>
-                                        <object class="wxBoxSizer" expanded="1">
+                                        <object class="wxBoxSizer" expanded="0">
                                             <property name="minimum_size"></property>
                                             <property name="name">bSizer6</property>
                                             <property name="orient">wxHORIZONTAL</property>
                                             <property name="permission">none</property>
-                                            <object class="sizeritem" expanded="1">
+                                            <object class="sizeritem" expanded="0">
                                                 <property name="border">5</property>
                                                 <property name="flag">wxEXPAND</property>
                                                 <property name="proportion">1</property>
-                                                <object class="wxBoxSizer" expanded="1">
+                                                <object class="wxBoxSizer" expanded="0">
                                                     <property name="minimum_size"></property>
                                                     <property name="name">bSizer11</property>
                                                     <property name="orient">wxVERTICAL</property>
                                                     <property name="permission">none</property>
-                                                    <object class="sizeritem" expanded="1">
+                                                    <object class="sizeritem" expanded="0">
                                                         <property name="border">5</property>
                                                         <property name="flag">wxALL|wxEXPAND</property>
                                                         <property name="proportion">1</property>
-                                                        <object class="wxGrid" expanded="1">
+                                                        <object class="wxGrid" expanded="0">
                                                             <property name="BottomDockable">1</property>
                                                             <property name="LeftDockable">1</property>
                                                             <property name="RightDockable">1</property>
@@ -4471,7 +4728,7 @@
                                                     </object>
                                                 </object>
                                             </object>
-                                            <object class="sizeritem" expanded="1">
+                                            <object class="sizeritem" expanded="0">
                                                 <property name="border">5</property>
                                                 <property name="flag">wxEXPAND</property>
                                                 <property name="proportion">0</property>
@@ -4673,30 +4930,41 @@
                                 </object>
                             </object>
                         </object>
-                        <object class="sizeritem" expanded="0">
-                            <property name="border">6</property>
-                            <property name="flag">wxALL|wxEXPAND</property>
+                        <object class="sizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="flag">wxBOTTOM|wxEXPAND</property>
                             <property name="proportion">0</property>
-                            <object class="wxStdDialogButtonSizer" expanded="0">
-                                <property name="Apply">0</property>
-                                <property name="Cancel">1</property>
-                                <property name="ContextHelp">0</property>
-                                <property name="Help">0</property>
-                                <property name="No">0</property>
-                                <property name="OK">1</property>
-                                <property name="Save">0</property>
-                                <property name="Yes">0</property>
+                            <object class="wxBoxSizer" expanded="1">
                                 <property name="minimum_size"></property>
-                                <property name="name">m_sdbSizer</property>
-                                <property name="permission">protected</property>
-                                <event name="OnApplyButtonClick"></event>
-                                <event name="OnCancelButtonClick"></event>
-                                <event name="OnContextHelpButtonClick"></event>
-                                <event name="OnHelpButtonClick"></event>
-                                <event name="OnNoButtonClick"></event>
-                                <event name="OnOKButtonClick"></event>
-                                <event name="OnSaveButtonClick"></event>
-                                <event name="OnYesButtonClick"></event>
+                                <property name="name">bSizer12</property>
+                                <property name="orient">wxHORIZONTAL</property>
+                                <property name="permission">none</property>
+                                <object class="sizeritem" expanded="0">
+                                    <property name="border">5</property>
+                                    <property name="flag">wxALL|wxEXPAND</property>
+                                    <property name="proportion">1</property>
+                                    <object class="wxStdDialogButtonSizer" expanded="0">
+                                        <property name="Apply">0</property>
+                                        <property name="Cancel">1</property>
+                                        <property name="ContextHelp">0</property>
+                                        <property name="Help">0</property>
+                                        <property name="No">0</property>
+                                        <property name="OK">1</property>
+                                        <property name="Save">0</property>
+                                        <property name="Yes">0</property>
+                                        <property name="minimum_size"></property>
+                                        <property name="name">m_sdbSizer</property>
+                                        <property name="permission">protected</property>
+                                        <event name="OnApplyButtonClick"></event>
+                                        <event name="OnCancelButtonClick"></event>
+                                        <event name="OnContextHelpButtonClick"></event>
+                                        <event name="OnHelpButtonClick"></event>
+                                        <event name="OnNoButtonClick"></event>
+                                        <event name="OnOKButtonClick"></event>
+                                        <event name="OnSaveButtonClick"></event>
+                                        <event name="OnYesButtonClick"></event>
+                                    </object>
+                                </object>
                             </object>
                         </object>
                     </object>
diff --git a/eeschema/dialogs/dialog_eeschema_options_base.h b/eeschema/dialogs/dialog_eeschema_options_base.h
index 585d442..5b20e1c 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 0baaa1e..2e8047d 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 <stambaughw@xxxxxxxxxxx>
- * 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 c0da682..801bea9 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 <wx/intl.h>
-
-#include <wx/string.h>
-#include <wx/choice.h>
-#include <wx/gdicmn.h>
-#include <wx/font.h>
-#include <wx/settings.h>
-#include <wx/textctrl.h>
-#include <wx/stattext.h>
-#include <wx/button.h>
-#include <wx/treelist.h>
-#include <wx/dialog.h>
-#include <wx/grid.h>
-
-#include <vector>
-#include <utility>
-
 #include <hotkeys_basic.h>
-#include <draw_frame.h>
 #include <../common/dialogs/dialog_hotkeys_editor_base.h>
-
-typedef std::pair<wxString, struct EDA_HOTKEY_CONFIG*> HOTKEYS_SECTION;
-typedef std::vector<HOTKEYS_SECTION> HOTKEYS_SECTIONS;
-
-typedef std::vector<EDA_HOTKEY> 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 <widgets/widget_hotkey_list.h>
 
 /**
  * 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<EDA_BASE_FRAME*>( 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 a2dc773..089fcce 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 <common.h>
+
 #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 0000000..bfaf010
--- /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 <pavlina.chris@xxxxxxxxx>
+ * 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 <utility>
+#include <vector>
+
+#include <wx/treelist.h>
+
+#include <hotkeys_basic.h>
+
+/**
+ * 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_SECTION> HOTKEY_SECTIONS;
+typedef std::vector<EDA_HOTKEY>     HOTKEY_LIST;
+
+class WIDGET_HOTKEY_CLIENT_DATA;
+
+class WIDGET_HOTKEY_LIST : public wxTreeListCtrl
+{
+    HOTKEY_SECTIONS             m_sections;
+    std::vector<HOTKEY_LIST>    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__

Follow ups