← Back to team overview

kicad-developers team mailing list archive

[PATCH] Hotkey list dialog

 

Hi,

Here is a patch sequence for using the hotkey editor widget in a
read-only mode to provide a filterable (and slightly prettier) hotkey
list. This is a 5.1 milestone.

Major details other than the main aim of the list widget:

* There are some "common UI" elements introduced here that should be
generally re-usable:
** BUTTON_ROW_PANEL provides an easy way to construct a row of buttons
spaced out nicely
** A place to put "generic" UI stuff like the 5px constant very often
used for margins
* The editor/list panel is no longer a wxFB project, as I couldn't get
it to play nice with optional elements: undoing half the construction
in the derived class, unbinding events and changing various stuff in
response to editabilty was not very tidy. It's substantially less code
now, partly due to the re-usable widgets above.

Also adds tooltips for the HK editor buttons via the new BUTTON_ROW_PANEL class.

Cheers,

John
From 2e27dd083faef63b043e7f791014ec7ee089a45b Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Wed, 3 Oct 2018 11:02:43 +0100
Subject: [PATCH 4/5] Centralise some UI constants, use in hotkey lists

There are lot of places where constants are used in the KiCad UI
as "magic numbers". The most common one is "5", used in many
wxFormBuilder and manual UI constructions as the margin.

This commit provides a place for all UI to look up shared
constants and other functions, to help create a consistent UI using
functions that provide meaning and intent to these magic numbers.
---
 common/CMakeLists.txt                   |  1 +
 common/dialogs/dialog_hotkey_list.cpp   |  7 +++--
 common/dialogs/panel_hotkeys_editor.cpp | 11 ++++---
 common/widgets/button_row_panel.cpp     |  7 ++---
 common/widgets/ui_common.cpp            | 27 ++++++++++++++++
 include/widgets/ui_common.h             | 41 +++++++++++++++++++++++++
 6 files changed, 83 insertions(+), 11 deletions(-)
 create mode 100644 common/widgets/ui_common.cpp
 create mode 100644 include/widgets/ui_common.h

diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 223225060..a2b9f2adf 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -196,6 +196,7 @@ set( COMMON_WIDGET_SRCS
     widgets/stepped_slider.cpp
     widgets/text_ctrl_eval.cpp
     widgets/two_column_tree_list.cpp
+    widgets/ui_common.cpp
     widgets/unit_binder.cpp
     widgets/widget_hotkey_list.cpp
     widgets/wx_grid.cpp
diff --git a/common/dialogs/dialog_hotkey_list.cpp b/common/dialogs/dialog_hotkey_list.cpp
index e5a451cb9..340031172 100644
--- a/common/dialogs/dialog_hotkey_list.cpp
+++ b/common/dialogs/dialog_hotkey_list.cpp
@@ -24,6 +24,7 @@
 #include <dialog_hotkey_list.h>
 
 #include <panel_hotkeys_editor.h>
+#include <widgets/ui_common.h>
 
 #include <wx/sizer.h>
 #include <wx/button.h>
@@ -32,18 +33,20 @@
 DIALOG_LIST_HOTKEYS::DIALOG_LIST_HOTKEYS( EDA_BASE_FRAME* aParent, EDA_HOTKEY_CONFIG* aDescList ):
     DIALOG_SHIM( aParent, wxID_ANY, _( "Hotkey List" ) )
 {
+    const auto margin = KIUI::GetStdMargin();
+
     auto main_sizer = new wxBoxSizer( wxVERTICAL );
 
     m_hk_list = new PANEL_HOTKEYS_EDITOR( aParent, this, true,
         aDescList, aDescList, {} );
 
-    main_sizer->Add( m_hk_list, 1, wxTOP | wxLEFT | wxRIGHT | wxEXPAND, 5 );
+    main_sizer->Add( m_hk_list, 1, wxTOP | wxLEFT | wxRIGHT | wxEXPAND, margin );
 
     auto sdb_sizer = new wxStdDialogButtonSizer;
     sdb_sizer->AddButton( new wxButton( this, wxID_OK ) );
     sdb_sizer->Realize();
 
-    main_sizer->Add( sdb_sizer, 0, wxEXPAND | wxALL, 5 );
+    main_sizer->Add( sdb_sizer, 0, wxEXPAND | wxALL, margin );
 
     SetSizer( main_sizer );
 
diff --git a/common/dialogs/panel_hotkeys_editor.cpp b/common/dialogs/panel_hotkeys_editor.cpp
index 4fd103d17..544d03153 100644
--- a/common/dialogs/panel_hotkeys_editor.cpp
+++ b/common/dialogs/panel_hotkeys_editor.cpp
@@ -30,12 +30,11 @@
 #include <wx/sizer.h>
 
 #include <widgets/button_row_panel.h>
+#include <widgets/ui_common.h>
 
 
 static const wxSize default_dialog_size { 500, 350 };
 static const wxSize min_dialog_size { -1, 350 };
-static const int widget_margins = 5;
-static const int side_margins = 10;
 
 /**
  * Helper function to add a filter box to a panel, with some
@@ -70,18 +69,20 @@ PANEL_HOTKEYS_EDITOR::PANEL_HOTKEYS_EDITOR( EDA_BASE_FRAME* aFrame, wxWindow* aW
         m_nickname( aNickname ),
         m_hotkeyStore( aShowHotkeys )
 {
+    const auto margin = KIUI::GetStdMargin();
     auto mainSizer = new wxBoxSizer( wxVERTICAL );
 
     // Sub-sizer for setting a wider side margin
     // TODO: Can this be set by the parent widget- doesn't seem to be
     // this panel's responsibility?
+    const int side_margins = 10; // seems to be hardcoded in wxFB
     auto bMargins = new wxBoxSizer( wxVERTICAL );
 
     auto filterSearch = CreateTextFilterBox( this, _( "Type filter text" ) );
-    bMargins->Add( filterSearch, 0, wxBOTTOM | wxEXPAND | wxTOP, widget_margins );
+    bMargins->Add( filterSearch, 0, wxBOTTOM | wxEXPAND | wxTOP, margin );
 
     m_hotkeyListCtrl = new WIDGET_HOTKEY_LIST( this, m_hotkeyStore, m_readOnly );
-    bMargins->Add( m_hotkeyListCtrl, 1, wxALL | wxEXPAND, widget_margins );
+    bMargins->Add( m_hotkeyListCtrl, 1, wxALL | wxEXPAND, margin );
 
     if( !m_readOnly )
         installButtons( bMargins );
@@ -139,7 +140,7 @@ void PANEL_HOTKEYS_EDITOR::installButtons( wxSizer* aSizer )
 
     auto btnPanel = new BUTTON_ROW_PANEL( this, l_btn_defs, r_btn_defs );
 
-    aSizer->Add( btnPanel, 0, wxEXPAND | wxTOP, widget_margins );
+    aSizer->Add( btnPanel, 0, wxEXPAND | wxTOP, KIUI::GetStdMargin() );
 }
 
 
diff --git a/common/widgets/button_row_panel.cpp b/common/widgets/button_row_panel.cpp
index 78d9e4cdc..3b5fae2d4 100644
--- a/common/widgets/button_row_panel.cpp
+++ b/common/widgets/button_row_panel.cpp
@@ -22,6 +22,7 @@
  */
 
 #include <widgets/button_row_panel.h>
+#include <widgets/ui_common.h>
 
 #include <wx/button.h>
 #include <wx/sizer.h>
@@ -37,7 +38,7 @@ BUTTON_ROW_PANEL::BUTTON_ROW_PANEL( wxWindow* aWindow,
     addButtons( true, aLeftBtns );
 
     // add the spacer
-    m_sizer->Add( 0, 0, 1, wxEXPAND, 5 );
+    m_sizer->Add( 0, 0, 1, wxEXPAND, KIUI::GetStdMargin() );
 
     addButtons( false, aRightBtns );
 
@@ -48,9 +49,7 @@ BUTTON_ROW_PANEL::BUTTON_ROW_PANEL( wxWindow* aWindow,
 
 void BUTTON_ROW_PANEL::addButtons( bool aLeft, const BTN_DEF_LIST& aDefs )
 {
-    // The "normal" KiCad margin magic number
-    // TODO: Get this from somewhere
-    const int btn_margin = 5;
+    const int btn_margin = KIUI::GetStdMargin();
     // No button expands to fill horizontally
     const int btn_proportion = 0;
 
diff --git a/common/widgets/ui_common.cpp b/common/widgets/ui_common.cpp
new file mode 100644
index 000000000..7cb0ca04c
--- /dev/null
+++ b/common/widgets/ui_common.cpp
@@ -0,0 +1,27 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2018 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <widgets/ui_common.h>
+
+
+int KIUI::GetStdMargin()
+{
+    // This is the value used in (most) wxFB dialogs
+    return 5;
+}
\ No newline at end of file
diff --git a/include/widgets/ui_common.h b/include/widgets/ui_common.h
new file mode 100644
index 000000000..132b3cc66
--- /dev/null
+++ b/include/widgets/ui_common.h
@@ -0,0 +1,41 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2018 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @file ui_common.h
+ * Functions to provide common constants and other functions to assist
+ * in making a consistent UI
+ */
+
+#ifndef UI_COMMON_H
+#define UI_COMMON_H
+
+namespace KIUI
+{
+
+/**
+ * Get the standard margin around a widget in the KiCad UI
+ * @return margin in pixels
+ */
+int GetStdMargin();
+
+}
+
+
+#endif // UI_COMMON_H
\ No newline at end of file
-- 
2.19.0

From 2fb3672df5cf5b7810d72a10b33090f4818838e4 Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Tue, 2 Oct 2018 22:37:15 +0100
Subject: [PATCH 3/5] Add read-only option for hotkey list, use for list dialog

This replaces the flat HTML list dialog with a read-only
version of the editor dialog in a pared-down dialog.

Fixes: lp:1778374
* https://bugs.launchpad.net/kicad/+bug/1778374
---
 common/CMakeLists.txt                   |  1 +
 common/dialogs/dialog_hotkey_list.cpp   | 57 ++++++++++++++++++++
 common/dialogs/dialog_hotkey_list.h     | 69 +++++++++++++++++++++++++
 common/dialogs/panel_hotkeys_editor.cpp |  8 +--
 common/eda_base_frame.cpp               |  3 +-
 common/hotkeys_basic.cpp                | 50 ++----------------
 common/widgets/widget_hotkey_list.cpp   | 23 ++++++---
 include/panel_hotkeys_editor.h          | 10 ++--
 include/widgets/widget_hotkey_list.h    |  3 +-
 9 files changed, 163 insertions(+), 61 deletions(-)
 create mode 100644 common/dialogs/dialog_hotkey_list.cpp
 create mode 100644 common/dialogs/dialog_hotkey_list.h

diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index aebd48038..223225060 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -163,6 +163,7 @@ set( COMMON_DLG_SRCS
     dialogs/dialog_edit_library_tables.cpp
     dialogs/dialog_exit_base.cpp
     dialogs/dialog_file_dir_picker.cpp
+    dialogs/dialog_hotkey_list.cpp
     dialogs/dialog_image_editor.cpp
     dialogs/dialog_image_editor_base.cpp
     dialogs/dialog_list_selector_base.cpp
diff --git a/common/dialogs/dialog_hotkey_list.cpp b/common/dialogs/dialog_hotkey_list.cpp
new file mode 100644
index 000000000..e5a451cb9
--- /dev/null
+++ b/common/dialogs/dialog_hotkey_list.cpp
@@ -0,0 +1,57 @@
+/*
+ * This program source code file is part of KICAD, a free EDA CAD application.
+ *
+ * Copyright (C) 2018 Kicad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#include <dialog_hotkey_list.h>
+
+#include <panel_hotkeys_editor.h>
+
+#include <wx/sizer.h>
+#include <wx/button.h>
+
+
+DIALOG_LIST_HOTKEYS::DIALOG_LIST_HOTKEYS( EDA_BASE_FRAME* aParent, EDA_HOTKEY_CONFIG* aDescList ):
+    DIALOG_SHIM( aParent, wxID_ANY, _( "Hotkey List" ) )
+{
+    auto main_sizer = new wxBoxSizer( wxVERTICAL );
+
+    m_hk_list = new PANEL_HOTKEYS_EDITOR( aParent, this, true,
+        aDescList, aDescList, {} );
+
+    main_sizer->Add( m_hk_list, 1, wxTOP | wxLEFT | wxRIGHT | wxEXPAND, 5 );
+
+    auto sdb_sizer = new wxStdDialogButtonSizer;
+    sdb_sizer->AddButton( new wxButton( this, wxID_OK ) );
+    sdb_sizer->Realize();
+
+    main_sizer->Add( sdb_sizer, 0, wxEXPAND | wxALL, 5 );
+
+    SetSizer( main_sizer );
+
+    FinishDialogSettings();
+}
+
+
+bool DIALOG_LIST_HOTKEYS::TransferDataToWindow()
+{
+    return m_hk_list->TransferDataToWindow();
+}
\ No newline at end of file
diff --git a/common/dialogs/dialog_hotkey_list.h b/common/dialogs/dialog_hotkey_list.h
new file mode 100644
index 000000000..e9c01bb29
--- /dev/null
+++ b/common/dialogs/dialog_hotkey_list.h
@@ -0,0 +1,69 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2018 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 dialog_hotkey_list.h
+ * Hotkey list dialog (as opposed to editor)
+ */
+
+#ifndef DIALOG_HOTKEYS_LIST_H
+#define DIALOG_HOTKEYS_LIST_H
+
+
+#include <dialog_shim.h>
+
+// Private forwards
+class PANEL_HOTKEYS_EDITOR;
+
+
+/**
+ * A dialog that presents the user with a read-only list of hotkeys and
+ * their current bindings.
+ */
+class DIALOG_LIST_HOTKEYS: public DIALOG_SHIM
+{
+public:
+
+    /**
+     * Construct a hotkey list dialog on the given frame, with a set of hotkeys
+     *
+     * @param aParent the parent frame
+     * @param aDescList the list of hotkey sections (each of which has a list
+     * of hotkeys) to display
+     */
+    DIALOG_LIST_HOTKEYS( EDA_BASE_FRAME* aParent,
+        EDA_HOTKEY_CONFIG* aDescList );
+
+protected:
+
+    /**
+     * Called on dialog initialisation - inits the dialog's own widgets
+     */
+    bool TransferDataToWindow() override;
+
+private:
+
+    PANEL_HOTKEYS_EDITOR* m_hk_list;
+};
+
+#endif // DIALOG_HOTKEYS_LIST_H
\ No newline at end of file
diff --git a/common/dialogs/panel_hotkeys_editor.cpp b/common/dialogs/panel_hotkeys_editor.cpp
index 21a0b99dd..4fd103d17 100644
--- a/common/dialogs/panel_hotkeys_editor.cpp
+++ b/common/dialogs/panel_hotkeys_editor.cpp
@@ -59,13 +59,14 @@ static wxSearchCtrl* CreateTextFilterBox( wxWindow* aParent, const wxString& aDe
 
 
 PANEL_HOTKEYS_EDITOR::PANEL_HOTKEYS_EDITOR( EDA_BASE_FRAME* aFrame, wxWindow* aWindow,
+                                            bool aReadOnly,
                                             EDA_HOTKEY_CONFIG* aHotkeys,
                                             EDA_HOTKEY_CONFIG* aShowHotkeys,
                                             const wxString& aNickname ) :
         wxPanel( aWindow, wxID_ANY, wxDefaultPosition, default_dialog_size ),
         m_frame( aFrame ),
+        m_readOnly( aReadOnly ),
         m_hotkeys( aHotkeys ),
-        m_showHotkeys( aShowHotkeys ),
         m_nickname( aNickname ),
         m_hotkeyStore( aShowHotkeys )
 {
@@ -79,10 +80,11 @@ PANEL_HOTKEYS_EDITOR::PANEL_HOTKEYS_EDITOR( EDA_BASE_FRAME* aFrame, wxWindow* aW
     auto filterSearch = CreateTextFilterBox( this, _( "Type filter text" ) );
     bMargins->Add( filterSearch, 0, wxBOTTOM | wxEXPAND | wxTOP, widget_margins );
 
-    m_hotkeyListCtrl = new WIDGET_HOTKEY_LIST( this, m_hotkeyStore );
+    m_hotkeyListCtrl = new WIDGET_HOTKEY_LIST( this, m_hotkeyStore, m_readOnly );
     bMargins->Add( m_hotkeyListCtrl, 1, wxALL | wxEXPAND, widget_margins );
 
-    installButtons( bMargins );
+    if( !m_readOnly )
+        installButtons( bMargins );
 
     mainSizer->Add( bMargins, 1, wxEXPAND | wxRIGHT | wxLEFT, side_margins );
 
diff --git a/common/eda_base_frame.cpp b/common/eda_base_frame.cpp
index dd74ec4ce..4dd4f3730 100644
--- a/common/eda_base_frame.cpp
+++ b/common/eda_base_frame.cpp
@@ -517,7 +517,8 @@ bool EDA_BASE_FRAME::ShowPreferences( EDA_HOTKEY_CONFIG* aHotkeys, EDA_HOTKEY_CO
     wxTreebook* book = dlg.GetTreebook();
 
     book->AddPage( new PANEL_COMMON_SETTINGS( &dlg, book ), _( "Common" ) );
-    book->AddPage( new PANEL_HOTKEYS_EDITOR( this, book, aHotkeys, aShowHotkeys, aHotkeysNickname ), _( "Hotkeys" ) );
+    book->AddPage( new PANEL_HOTKEYS_EDITOR( this, book, false,
+        aHotkeys, aShowHotkeys, aHotkeysNickname ), _( "Hotkeys" ) );
 
     for( unsigned i = 0; i < KIWAY_PLAYER_COUNT;  ++i )
     {
diff --git a/common/hotkeys_basic.cpp b/common/hotkeys_basic.cpp
index aca3aa9fa..0827ac84f 100644
--- a/common/hotkeys_basic.cpp
+++ b/common/hotkeys_basic.cpp
@@ -37,11 +37,13 @@
 #include <gestfich.h>
 #include <eda_base_frame.h>
 #include <macros.h>
-#include <panel_hotkeys_editor.h>
 #include <menus_helpers.h>
 #include <draw_frame.h>
+
 #include <tool/tool_manager.h>
 
+#include "dialogs/dialog_hotkey_list.h"
+
 #include <wx/apptrait.h>
 #include <wx/stdpaths.h>
 #include <wx/tokenzr.h>
@@ -442,52 +444,10 @@ int KeyCodeFromKeyName( const wxString& keyname )
  * Displays the current hotkey list
  * aList = a EDA_HOTKEY_CONFIG list(Null terminated)
  */
-#include <html_messagebox.h>
-
 void DisplayHotkeyList( EDA_BASE_FRAME* aFrame, struct EDA_HOTKEY_CONFIG* aDescList )
 {
-    wxString     keyname;
-    wxString     keymessage;
-    EDA_HOTKEY** list;
-
-    wxString     msg = wxT( "<html><body bgcolor=\"#E2E2E2\">" );
-
-    msg += wxT( "<H3>" );
-    msg += _( "Hotkeys List" );
-    msg += wxT( "</H3> <table cellpadding=\"0\">" );
-
-    for( ; aDescList->m_HK_InfoList != nullptr; aDescList++ )
-    {
-        list = aDescList->m_HK_InfoList;
-
-        for( ; *list != nullptr; list++ )
-        {
-            EDA_HOTKEY* hk_decr = *list;
-
-            if( !hk_decr->m_InfoMsg.Contains( wxT( "Macros" ) ) )
-            {
-                keyname = KeyNameFromKeyCode( hk_decr->m_KeyCode );
-                keymessage = wxGetTranslation( hk_decr->m_InfoMsg );
-
-                // Some chars are modified, using html encoding, to be
-                // displayed by DisplayHtmlInfoMessage()
-                keyname.Replace( wxT( "<" ), wxT( "&lt;" ) );
-                keyname.Replace( wxT( ">" ), wxT( "&gt;" ) );
-                msg    += wxT( "<tr><td>" ) + keymessage + wxT( "</td>" );
-                msg    += wxT( "<td><b>&nbsp;&nbsp;" ) + keyname + wxT( "</b></td></tr>" );
-            }
-        }
-    }
-
-    msg += wxT( "</table></html></body>" );
-
-    // Create a non modal dialog, which shows the list of hotkeys until dismissed
-    // but does not block the parent window
-    HTML_MESSAGE_BOX *dlg = new HTML_MESSAGE_BOX( aFrame, _( "Hotkeys List" ) );
-    dlg->SetDialogSizeInDU( 300, 250 );
-
-    dlg->AddHTML_Text( msg );
-    dlg->Show( true );
+    DIALOG_LIST_HOTKEYS dlg( aFrame, aDescList );
+    dlg.ShowModal();
 }
 
 
diff --git a/common/widgets/widget_hotkey_list.cpp b/common/widgets/widget_hotkey_list.cpp
index 122bd46ad..d89b005b4 100644
--- a/common/widgets/widget_hotkey_list.cpp
+++ b/common/widgets/widget_hotkey_list.cpp
@@ -506,18 +506,29 @@ bool WIDGET_HOTKEY_LIST::ResolveKeyConflicts( long aKey, const wxString& aSectio
 }
 
 
-WIDGET_HOTKEY_LIST::WIDGET_HOTKEY_LIST( wxWindow* aParent, HOTKEY_STORE& aHotkeyStore )
+WIDGET_HOTKEY_LIST::WIDGET_HOTKEY_LIST( wxWindow* aParent, HOTKEY_STORE& aHotkeyStore,
+            bool aReadOnly )
     :   TWO_COLUMN_TREE_LIST( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTL_SINGLE ),
-        m_hk_store( aHotkeyStore )
+        m_hk_store( aHotkeyStore ),
+        m_readOnly( aReadOnly )
 {
-    AppendColumn( _( "Command (double-click to edit)" ) );
+    wxString command_header = _( "Command" );
+
+    if( !m_readOnly )
+        command_header << " " << _( "(double-click to edit)" );
+
+    AppendColumn( command_header );
     AppendColumn( _( "Hotkey" ) );
     SetRubberBandColumn( 0 );
     SetClampedMinWidth( HOTKEY_MIN_WIDTH );
 
-    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 );
+    if( !m_readOnly )
+    {
+        // The event only apply if the widget is in editable mode
+        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 );
+    }
 }
 
 
diff --git a/include/panel_hotkeys_editor.h b/include/panel_hotkeys_editor.h
index b4cc78302..91e40df90 100644
--- a/include/panel_hotkeys_editor.h
+++ b/include/panel_hotkeys_editor.h
@@ -40,21 +40,21 @@ class PANEL_HOTKEYS_EDITOR : public wxPanel
 {
 protected:
     EDA_BASE_FRAME*           m_frame;
+    bool                      m_readOnly;
     struct EDA_HOTKEY_CONFIG* m_hotkeys;
-    struct EDA_HOTKEY_CONFIG* m_showHotkeys;
     wxString                  m_nickname;
 
     HOTKEY_STORE              m_hotkeyStore;
     WIDGET_HOTKEY_LIST*       m_hotkeyListCtrl;
 
-    bool TransferDataToWindow() override;
-    bool TransferDataFromWindow() override;
-
 public:
-    PANEL_HOTKEYS_EDITOR( EDA_BASE_FRAME* aFrame, wxWindow* aWindow,
+    PANEL_HOTKEYS_EDITOR( EDA_BASE_FRAME* aFrame, wxWindow* aWindow, bool aReadOnly,
                           EDA_HOTKEY_CONFIG* aHotkeys, EDA_HOTKEY_CONFIG* aShowHotkeys,
                           const wxString& aNickname );
 
+    bool TransferDataToWindow() override;
+    bool TransferDataFromWindow() override;
+
 private:
 
     /**
diff --git a/include/widgets/widget_hotkey_list.h b/include/widgets/widget_hotkey_list.h
index 413ab5bb0..6c1065aa9 100644
--- a/include/widgets/widget_hotkey_list.h
+++ b/include/widgets/widget_hotkey_list.h
@@ -44,6 +44,7 @@ class WIDGET_HOTKEY_CLIENT_DATA;
 class WIDGET_HOTKEY_LIST : public TWO_COLUMN_TREE_LIST
 {
     HOTKEY_STORE&               m_hk_store;
+    bool                        m_readOnly;
 
     wxTreeListItem              m_context_menu_item;
 
@@ -164,7 +165,7 @@ public:
      * @param aHotkeys - EDA_HOTKEY_CONFIG data - a hotkey store is constructed
      * from this.
      */
-    WIDGET_HOTKEY_LIST( wxWindow* aParent, HOTKEY_STORE& aHotkeyStore );
+    WIDGET_HOTKEY_LIST( wxWindow* aParent, HOTKEY_STORE& aHotkeyStore, bool aReadOnly );
 
     /**
      * Method ApplyFilterString
-- 
2.19.0

From 2ef8b4f0798504d3c35536b1a6cf583a4e6e08ce Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Tue, 2 Oct 2018 13:41:16 +0100
Subject: [PATCH 2/5] Add tooltips to hotkey dialog buttons

THis is done in the generic widget, so other users can
benefit from it.
---
 common/dialogs/panel_hotkeys_editor.cpp | 4 ++++
 common/widgets/button_row_panel.cpp     | 2 ++
 include/widgets/button_row_panel.h      | 5 +++++
 3 files changed, 11 insertions(+)

diff --git a/common/dialogs/panel_hotkeys_editor.cpp b/common/dialogs/panel_hotkeys_editor.cpp
index 48838f699..21a0b99dd 100644
--- a/common/dialogs/panel_hotkeys_editor.cpp
+++ b/common/dialogs/panel_hotkeys_editor.cpp
@@ -101,6 +101,7 @@ void PANEL_HOTKEYS_EDITOR::installButtons( wxSizer* aSizer )
         {
             wxID_RESET,
             _( "Reset Hotkeys" ),
+            _( "Undo all changes made so far in this dialog" ),
             [this]( wxCommandEvent ){
                 m_hotkeyListCtrl->ResetAllHotkeys( false );
             }
@@ -108,6 +109,7 @@ void PANEL_HOTKEYS_EDITOR::installButtons( wxSizer* aSizer )
         {
             wxID_ANY,
             _( "Set to Defaults" ),
+            _( "Set all hotkeys to the built-in KiCad defaults" ),
             [this]( wxCommandEvent ){
                 m_hotkeyListCtrl->ResetAllHotkeys( true );
             }
@@ -118,6 +120,7 @@ void PANEL_HOTKEYS_EDITOR::installButtons( wxSizer* aSizer )
         {
             wxID_ANY,
             _( "Import..." ),
+            _( "Import hotkey definitions from an external file, replacing the current values" ),
             [this]( wxCommandEvent ){
                 m_frame->ImportHotkeyConfigFromFile( m_hotkeys, m_nickname );
             }
@@ -125,6 +128,7 @@ void PANEL_HOTKEYS_EDITOR::installButtons( wxSizer* aSizer )
         {
             wxID_ANY,
             _( "Export..." ),
+            _( "Export these hotkey definitions to an external file" ),
             [this]( wxCommandEvent ){
                 m_frame->ExportHotkeyConfigToFile( m_hotkeys, m_nickname );
             }
diff --git a/common/widgets/button_row_panel.cpp b/common/widgets/button_row_panel.cpp
index a4ca5f508..78d9e4cdc 100644
--- a/common/widgets/button_row_panel.cpp
+++ b/common/widgets/button_row_panel.cpp
@@ -68,6 +68,8 @@ void BUTTON_ROW_PANEL::addButtons( bool aLeft, const BTN_DEF_LIST& aDefs )
         if( ( aLeft ) || ( !aLeft && i < aDefs.size() - 1 ) )
             this_style |= wxRIGHT;
 
+        btn->SetToolTip( def.m_tooltip );
+
         m_sizer->Add( btn, btn_proportion, this_style, btn_margin );
 
         btn->Bind( wxEVT_COMMAND_BUTTON_CLICKED, def.m_callback );
diff --git a/include/widgets/button_row_panel.h b/include/widgets/button_row_panel.h
index 8abfe834a..4a2b32795 100644
--- a/include/widgets/button_row_panel.h
+++ b/include/widgets/button_row_panel.h
@@ -63,6 +63,11 @@ public:
          */
         wxString        m_text;
 
+        /**
+         * Button tooltip text - empty string for no tooltip
+         */
+        wxString        m_tooltip;
+
         /**
          * The callback fired when the button is clicked. Can be nullptr,
          * but then the button is useless.
-- 
2.19.0

From af92b5ac6b074929649bdb213f9e2212aba2cdea Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Mon, 1 Oct 2018 17:18:29 +0100
Subject: [PATCH 1/5] Simplify hotkey editor widget

This is in preparation for making this widget optionally read-only.

Major changes:

* Construct panel in code, not with wxFormBuilder. This make's it
  easier to conditionally construct elements that won't be used
  in a read-only mode (e.g. the buttons).
* Use a generic "button row panel" widget for the buttons, as the
  sizing and layout logic is reusable in nearly all dialogs, and
  it's simplifies layout in the higher-level dialog widget. This
  widget is one example of many possible "reuable widgets".
---
 common/CMakeLists.txt                        |   2 +-
 common/dialogs/panel_hotkeys_editor.cpp      | 127 +++-
 common/dialogs/panel_hotkeys_editor_base.cpp |  76 ---
 common/dialogs/panel_hotkeys_editor_base.fbp | 650 -------------------
 common/dialogs/panel_hotkeys_editor_base.h   |  58 --
 common/widgets/button_row_panel.cpp          |  75 +++
 common/widgets/widget_hotkey_list.cpp        |  12 -
 include/panel_hotkeys_editor.h               |  32 +-
 include/widgets/button_row_panel.h           | 102 +++
 include/widgets/widget_hotkey_list.h         |   8 -
 10 files changed, 289 insertions(+), 853 deletions(-)
 delete mode 100644 common/dialogs/panel_hotkeys_editor_base.cpp
 delete mode 100644 common/dialogs/panel_hotkeys_editor_base.fbp
 delete mode 100644 common/dialogs/panel_hotkeys_editor_base.h
 create mode 100644 common/widgets/button_row_panel.cpp
 create mode 100644 include/widgets/button_row_panel.h

diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 0f1722b6b..aebd48038 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -172,12 +172,12 @@ set( COMMON_DLG_SRCS
     dialogs/panel_common_settings.cpp
     dialogs/panel_common_settings_base.cpp
     dialogs/panel_hotkeys_editor.cpp
-    dialogs/panel_hotkeys_editor_base.cpp
     dialogs/wx_html_report_panel.cpp
     dialogs/wx_html_report_panel_base.cpp
     )
 
 set( COMMON_WIDGET_SRCS
+    widgets/button_row_panel.cpp
     widgets/color_swatch.cpp
     widgets/footprint_choice.cpp
     widgets/footprint_preview_widget.cpp
diff --git a/common/dialogs/panel_hotkeys_editor.cpp b/common/dialogs/panel_hotkeys_editor.cpp
index e74700777..48838f699 100644
--- a/common/dialogs/panel_hotkeys_editor.cpp
+++ b/common/dialogs/panel_hotkeys_editor.cpp
@@ -21,67 +21,140 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
-#include <eda_base_frame.h>
 #include <panel_hotkeys_editor.h>
+#include <eda_base_frame.h>
+
+#include <wx/srchctrl.h>
+#include <wx/panel.h>
+#include <wx/button.h>
+#include <wx/sizer.h>
+
+#include <widgets/button_row_panel.h>
+
+
+static const wxSize default_dialog_size { 500, 350 };
+static const wxSize min_dialog_size { -1, 350 };
+static const int widget_margins = 5;
+static const int side_margins = 10;
+
+/**
+ * Helper function to add a filter box to a panel, with some
+ * sensible defaults for that purpose.
+ *
+ * @param  aParent          The panrent widget/panel
+ * @param  aDescriptiveText The text to show when the box is empty.
+ * @return                  A newly constructed filter box - the caller owns it
+ */
+static wxSearchCtrl* CreateTextFilterBox( wxWindow* aParent, const wxString& aDescriptiveText )
+{
+    auto search_widget = new wxSearchCtrl( aParent, wxID_ANY );
+
+    search_widget->ShowSearchButton( false );
+    search_widget->ShowCancelButton( true );
+
+    search_widget->SetDescriptiveText( aDescriptiveText);
+
+    return search_widget;
+}
+
 
 PANEL_HOTKEYS_EDITOR::PANEL_HOTKEYS_EDITOR( EDA_BASE_FRAME* aFrame, wxWindow* aWindow,
                                             EDA_HOTKEY_CONFIG* aHotkeys,
                                             EDA_HOTKEY_CONFIG* aShowHotkeys,
                                             const wxString& aNickname ) :
-        PANEL_HOTKEYS_EDITOR_BASE( aWindow ),
+        wxPanel( aWindow, wxID_ANY, wxDefaultPosition, default_dialog_size ),
         m_frame( aFrame ),
         m_hotkeys( aHotkeys ),
         m_showHotkeys( aShowHotkeys ),
         m_nickname( aNickname ),
         m_hotkeyStore( aShowHotkeys )
 {
-    m_filterSearch->SetDescriptiveText( _("Type filter text") );
+    auto mainSizer = new wxBoxSizer( wxVERTICAL );
 
-    m_hotkeyListCtrl = new WIDGET_HOTKEY_LIST( m_panelHotkeys, m_hotkeyStore );
-    m_hotkeyListCtrl->InstallOnPanel( m_panelHotkeys );
-}
+    // Sub-sizer for setting a wider side margin
+    // TODO: Can this be set by the parent widget- doesn't seem to be
+    // this panel's responsibility?
+    auto bMargins = new wxBoxSizer( wxVERTICAL );
 
+    auto filterSearch = CreateTextFilterBox( this, _( "Type filter text" ) );
+    bMargins->Add( filterSearch, 0, wxBOTTOM | wxEXPAND | wxTOP, widget_margins );
 
-bool PANEL_HOTKEYS_EDITOR::TransferDataToWindow()
-{
-    return m_hotkeyListCtrl->TransferDataToControl();
-}
+    m_hotkeyListCtrl = new WIDGET_HOTKEY_LIST( this, m_hotkeyStore );
+    bMargins->Add( m_hotkeyListCtrl, 1, wxALL | wxEXPAND, widget_margins );
 
+    installButtons( bMargins );
 
-bool PANEL_HOTKEYS_EDITOR::TransferDataFromWindow()
-{
-    if( !m_hotkeyListCtrl->TransferDataFromControl() )
-        return false;
+    mainSizer->Add( bMargins, 1, wxEXPAND | wxRIGHT | wxLEFT, side_margins );
 
-    // save the hotkeys
-    m_frame->WriteHotkeyConfig( m_hotkeys );
+    this->SetSizer( mainSizer );
+    this->Layout();
 
-    return true;
+    // Connect Events
+    filterSearch->Bind( wxEVT_COMMAND_TEXT_UPDATED,
+        &PANEL_HOTKEYS_EDITOR::OnFilterSearch, this );
 }
 
 
-void PANEL_HOTKEYS_EDITOR::ResetClicked( wxCommandEvent& aEvent )
+void PANEL_HOTKEYS_EDITOR::installButtons( wxSizer* aSizer )
 {
-    m_hotkeyListCtrl->ResetAllHotkeys( false );
+    const BUTTON_ROW_PANEL::BTN_DEF_LIST l_btn_defs = {
+        {
+            wxID_RESET,
+            _( "Reset Hotkeys" ),
+            [this]( wxCommandEvent ){
+                m_hotkeyListCtrl->ResetAllHotkeys( false );
+            }
+        },
+        {
+            wxID_ANY,
+            _( "Set to Defaults" ),
+            [this]( wxCommandEvent ){
+                m_hotkeyListCtrl->ResetAllHotkeys( true );
+            }
+        }
+    };
+
+    const BUTTON_ROW_PANEL::BTN_DEF_LIST r_btn_defs = {
+        {
+            wxID_ANY,
+            _( "Import..." ),
+            [this]( wxCommandEvent ){
+                m_frame->ImportHotkeyConfigFromFile( m_hotkeys, m_nickname );
+            }
+        },
+        {
+            wxID_ANY,
+            _( "Export..." ),
+            [this]( wxCommandEvent ){
+                m_frame->ExportHotkeyConfigToFile( m_hotkeys, m_nickname );
+            }
+        },
+    };
+
+    auto btnPanel = new BUTTON_ROW_PANEL( this, l_btn_defs, r_btn_defs );
+
+    aSizer->Add( btnPanel, 0, wxEXPAND | wxTOP, widget_margins );
 }
 
-void PANEL_HOTKEYS_EDITOR::DefaultsClicked( wxCommandEvent& aEvent )
+
+bool PANEL_HOTKEYS_EDITOR::TransferDataToWindow()
 {
-    m_hotkeyListCtrl->ResetAllHotkeys( true );
+    return m_hotkeyListCtrl->TransferDataToControl();
 }
 
 
-void PANEL_HOTKEYS_EDITOR::OnExport( wxCommandEvent& aEvent )
+bool PANEL_HOTKEYS_EDITOR::TransferDataFromWindow()
 {
-    m_frame->ExportHotkeyConfigToFile( m_hotkeys, m_nickname );
-}
+    if( !m_hotkeyListCtrl->TransferDataFromControl() )
+        return false;
 
+    // save the hotkeys
+    m_frame->WriteHotkeyConfig( m_hotkeys );
 
-void PANEL_HOTKEYS_EDITOR::OnImport( wxCommandEvent& aEvent )
-{
-    m_frame->ImportHotkeyConfigFromFile( m_hotkeys, m_nickname );
+    return true;
 }
 
+
 void PANEL_HOTKEYS_EDITOR::OnFilterSearch( wxCommandEvent& aEvent )
 {
     const auto searchStr = aEvent.GetString();
diff --git a/common/dialogs/panel_hotkeys_editor_base.cpp b/common/dialogs/panel_hotkeys_editor_base.cpp
deleted file mode 100644
index 2f00fd549..000000000
--- a/common/dialogs/panel_hotkeys_editor_base.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Jun 18 2018)
-// http://www.wxformbuilder.org/
-//
-// PLEASE DO *NOT* EDIT THIS FILE!
-///////////////////////////////////////////////////////////////////////////
-
-#include "panel_hotkeys_editor_base.h"
-
-///////////////////////////////////////////////////////////////////////////
-
-PANEL_HOTKEYS_EDITOR_BASE::PANEL_HOTKEYS_EDITOR_BASE( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style )
-{
-	m_mainSizer = new wxBoxSizer( wxHORIZONTAL );
-	
-	wxBoxSizer* bMargins;
-	bMargins = new wxBoxSizer( wxVERTICAL );
-	
-	m_filterSearch = new wxSearchCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
-	#ifndef __WXMAC__
-	m_filterSearch->ShowSearchButton( false );
-	#endif
-	m_filterSearch->ShowCancelButton( true );
-	bMargins->Add( m_filterSearch, 0, wxBOTTOM|wxEXPAND|wxTOP, 5 );
-	
-	m_panelHotkeys = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
-	m_panelHotkeys->SetMinSize( wxSize( -1,350 ) );
-	
-	bMargins->Add( m_panelHotkeys, 1, wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT, 2 );
-	
-	wxBoxSizer* b_buttonsSizer;
-	b_buttonsSizer = new wxBoxSizer( wxHORIZONTAL );
-	
-	m_resetButton = new wxButton( this, wxID_RESET, _("Reset Hotkeys"), wxDefaultPosition, wxDefaultSize, 0 );
-	b_buttonsSizer->Add( m_resetButton, 0, wxEXPAND|wxTOP|wxRIGHT, 5 );
-	
-	m_defaultButton = new wxButton( this, wxID_ANY, _("Set to Defaults"), wxDefaultPosition, wxDefaultSize, 0 );
-	b_buttonsSizer->Add( m_defaultButton, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 );
-	
-	
-	b_buttonsSizer->Add( 0, 0, 1, wxEXPAND, 5 );
-	
-	btnImport = new wxButton( this, wxID_ANY, _("Import..."), wxDefaultPosition, wxDefaultSize, 0 );
-	b_buttonsSizer->Add( btnImport, 0, wxTOP|wxRIGHT|wxLEFT, 5 );
-	
-	btnExport = new wxButton( this, wxID_ANY, _("Export..."), wxDefaultPosition, wxDefaultSize, 0 );
-	b_buttonsSizer->Add( btnExport, 0, wxTOP|wxLEFT, 5 );
-	
-	
-	bMargins->Add( b_buttonsSizer, 0, wxEXPAND, 5 );
-	
-	
-	m_mainSizer->Add( bMargins, 1, wxEXPAND|wxRIGHT|wxLEFT, 10 );
-	
-	
-	this->SetSizer( m_mainSizer );
-	this->Layout();
-	
-	// Connect Events
-	m_filterSearch->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( PANEL_HOTKEYS_EDITOR_BASE::OnFilterSearch ), NULL, this );
-	m_resetButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_HOTKEYS_EDITOR_BASE::ResetClicked ), NULL, this );
-	m_defaultButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_HOTKEYS_EDITOR_BASE::DefaultsClicked ), NULL, this );
-	btnImport->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_HOTKEYS_EDITOR_BASE::OnImport ), NULL, this );
-	btnExport->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_HOTKEYS_EDITOR_BASE::OnExport ), NULL, this );
-}
-
-PANEL_HOTKEYS_EDITOR_BASE::~PANEL_HOTKEYS_EDITOR_BASE()
-{
-	// Disconnect Events
-	m_filterSearch->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( PANEL_HOTKEYS_EDITOR_BASE::OnFilterSearch ), NULL, this );
-	m_resetButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_HOTKEYS_EDITOR_BASE::ResetClicked ), NULL, this );
-	m_defaultButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_HOTKEYS_EDITOR_BASE::DefaultsClicked ), NULL, this );
-	btnImport->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_HOTKEYS_EDITOR_BASE::OnImport ), NULL, this );
-	btnExport->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_HOTKEYS_EDITOR_BASE::OnExport ), NULL, this );
-	
-}
diff --git a/common/dialogs/panel_hotkeys_editor_base.fbp b/common/dialogs/panel_hotkeys_editor_base.fbp
deleted file mode 100644
index f54a83ad6..000000000
--- a/common/dialogs/panel_hotkeys_editor_base.fbp
+++ /dev/null
@@ -1,650 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
-<wxFormBuilder_Project>
-    <FileVersion major="1" minor="13" />
-    <object class="Project" expanded="1">
-        <property name="class_decoration"></property>
-        <property name="code_generation">C++</property>
-        <property name="disconnect_events">1</property>
-        <property name="disconnect_mode">source_name</property>
-        <property name="disconnect_php_events">0</property>
-        <property name="disconnect_python_events">0</property>
-        <property name="embedded_files_path">res</property>
-        <property name="encoding">UTF-8</property>
-        <property name="event_generation">connect</property>
-        <property name="file">panel_hotkeys_editor_base</property>
-        <property name="first_id">1000</property>
-        <property name="help_provider">none</property>
-        <property name="indent_with_spaces"></property>
-        <property name="internationalize">1</property>
-        <property name="name">PanelHotkeysEditorBase</property>
-        <property name="namespace"></property>
-        <property name="path">.</property>
-        <property name="precompiled_header"></property>
-        <property name="relative_path">1</property>
-        <property name="skip_lua_events">1</property>
-        <property name="skip_php_events">1</property>
-        <property name="skip_python_events">1</property>
-        <property name="ui_table">UI</property>
-        <property name="use_enum">0</property>
-        <property name="use_microsoft_bom">0</property>
-        <object class="Panel" expanded="1">
-            <property name="aui_managed">0</property>
-            <property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
-            <property name="bg"></property>
-            <property name="context_help"></property>
-            <property name="context_menu">1</property>
-            <property name="enabled">1</property>
-            <property name="event_handler">impl_virtual</property>
-            <property name="fg"></property>
-            <property name="font"></property>
-            <property name="hidden">0</property>
-            <property name="id">wxID_ANY</property>
-            <property name="maximum_size"></property>
-            <property name="minimum_size"></property>
-            <property name="name">PANEL_HOTKEYS_EDITOR_BASE</property>
-            <property name="pos"></property>
-            <property name="size">500,300</property>
-            <property name="subclass">; forward_declare</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="OnAuiFindManager"></event>
-            <event name="OnAuiPaneButton"></event>
-            <event name="OnAuiPaneClose"></event>
-            <event name="OnAuiPaneMaximize"></event>
-            <event name="OnAuiPaneRestore"></event>
-            <event name="OnAuiRender"></event>
-            <event name="OnChar"></event>
-            <event name="OnEnterWindow"></event>
-            <event name="OnEraseBackground"></event>
-            <event name="OnInitDialog"></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 class="wxBoxSizer" expanded="1">
-                <property name="minimum_size"></property>
-                <property name="name">m_mainSizer</property>
-                <property name="orient">wxHORIZONTAL</property>
-                <property name="permission">protected</property>
-                <object class="sizeritem" expanded="1">
-                    <property name="border">10</property>
-                    <property name="flag">wxEXPAND|wxRIGHT|wxLEFT</property>
-                    <property name="proportion">1</property>
-                    <object class="wxBoxSizer" expanded="1">
-                        <property name="minimum_size"></property>
-                        <property name="name">bMargins</property>
-                        <property name="orient">wxVERTICAL</property>
-                        <property name="permission">none</property>
-                        <object class="sizeritem" expanded="1">
-                            <property name="border">5</property>
-                            <property name="flag">wxBOTTOM|wxEXPAND|wxTOP</property>
-                            <property name="proportion">0</property>
-                            <object class="wxSearchCtrl" 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="cancel_button">1</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_filterSearch</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="search_button">0</property>
-                                <property name="show">1</property>
-                                <property name="size"></property>
-                                <property name="style"></property>
-                                <property name="subclass">; forward_declare</property>
-                                <property name="toolbar_pane">0</property>
-                                <property name="tooltip"></property>
-                                <property name="validator_data_type"></property>
-                                <property name="validator_style">wxFILTER_NONE</property>
-                                <property name="validator_type">wxDefaultValidator</property>
-                                <property name="validator_variable"></property>
-                                <property name="value"></property>
-                                <property name="window_extra_style"></property>
-                                <property name="window_name"></property>
-                                <property name="window_style"></property>
-                                <event name="OnCancelButton"></event>
-                                <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="OnSearchButton"></event>
-                                <event name="OnSetFocus"></event>
-                                <event name="OnSize"></event>
-                                <event name="OnText">OnFilterSearch</event>
-                                <event name="OnTextEnter"></event>
-                                <event name="OnUpdateUI"></event>
-                            </object>
-                        </object>
-                        <object class="sizeritem" expanded="1">
-                            <property name="border">2</property>
-                            <property name="flag">wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT</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">-1,350</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">wxEXPAND</property>
-                            <property name="proportion">0</property>
-                            <object class="wxBoxSizer" expanded="1">
-                                <property name="minimum_size"></property>
-                                <property name="name">b_buttonsSizer</property>
-                                <property name="orient">wxHORIZONTAL</property>
-                                <property name="permission">none</property>
-                                <object class="sizeritem" expanded="1">
-                                    <property name="border">5</property>
-                                    <property name="flag">wxEXPAND|wxTOP|wxRIGHT</property>
-                                    <property name="proportion">0</property>
-                                    <object class="wxButton" 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">0</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_RESET</property>
-                                        <property name="label">Reset Hotkeys</property>
-                                        <property name="markup">0</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_resetButton</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="validator_data_type"></property>
-                                        <property name="validator_style">wxFILTER_NONE</property>
-                                        <property name="validator_type">wxDefaultValidator</property>
-                                        <property name="validator_variable"></property>
-                                        <property name="window_extra_style"></property>
-                                        <property name="window_name"></property>
-                                        <property name="window_style"></property>
-                                        <event name="OnButtonClick">ResetClicked</event>
-                                        <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">wxEXPAND|wxTOP|wxRIGHT|wxLEFT</property>
-                                    <property name="proportion">0</property>
-                                    <object class="wxButton" 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">0</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">Set to Defaults</property>
-                                        <property name="markup">0</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_defaultButton</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="validator_data_type"></property>
-                                        <property name="validator_style">wxFILTER_NONE</property>
-                                        <property name="validator_type">wxDefaultValidator</property>
-                                        <property name="validator_variable"></property>
-                                        <property name="window_extra_style"></property>
-                                        <property name="window_name"></property>
-                                        <property name="window_style"></property>
-                                        <event name="OnButtonClick">DefaultsClicked</event>
-                                        <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">wxEXPAND</property>
-                                    <property name="proportion">1</property>
-                                    <object class="spacer" expanded="1">
-                                        <property name="height">0</property>
-                                        <property name="permission">protected</property>
-                                        <property name="width">0</property>
-                                    </object>
-                                </object>
-                                <object class="sizeritem" expanded="1">
-                                    <property name="border">5</property>
-                                    <property name="flag">wxTOP|wxRIGHT|wxLEFT</property>
-                                    <property name="proportion">0</property>
-                                    <object class="wxButton" 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">0</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">Import...</property>
-                                        <property name="markup">0</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">btnImport</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">; forward_declare</property>
-                                        <property name="toolbar_pane">0</property>
-                                        <property name="tooltip"></property>
-                                        <property name="validator_data_type"></property>
-                                        <property name="validator_style">wxFILTER_NONE</property>
-                                        <property name="validator_type">wxDefaultValidator</property>
-                                        <property name="validator_variable"></property>
-                                        <property name="window_extra_style"></property>
-                                        <property name="window_name"></property>
-                                        <property name="window_style"></property>
-                                        <event name="OnButtonClick">OnImport</event>
-                                        <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">wxTOP|wxLEFT</property>
-                                    <property name="proportion">0</property>
-                                    <object class="wxButton" 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">0</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">Export...</property>
-                                        <property name="markup">0</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">btnExport</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">; forward_declare</property>
-                                        <property name="toolbar_pane">0</property>
-                                        <property name="tooltip"></property>
-                                        <property name="validator_data_type"></property>
-                                        <property name="validator_style">wxFILTER_NONE</property>
-                                        <property name="validator_type">wxDefaultValidator</property>
-                                        <property name="validator_variable"></property>
-                                        <property name="window_extra_style"></property>
-                                        <property name="window_name"></property>
-                                        <property name="window_style"></property>
-                                        <event name="OnButtonClick">OnExport</event>
-                                        <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>
-                </object>
-            </object>
-        </object>
-    </object>
-</wxFormBuilder_Project>
diff --git a/common/dialogs/panel_hotkeys_editor_base.h b/common/dialogs/panel_hotkeys_editor_base.h
deleted file mode 100644
index c199f8146..000000000
--- a/common/dialogs/panel_hotkeys_editor_base.h
+++ /dev/null
@@ -1,58 +0,0 @@
-///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Jun 18 2018)
-// http://www.wxformbuilder.org/
-//
-// PLEASE DO *NOT* EDIT THIS FILE!
-///////////////////////////////////////////////////////////////////////////
-
-#ifndef __PANEL_HOTKEYS_EDITOR_BASE_H__
-#define __PANEL_HOTKEYS_EDITOR_BASE_H__
-
-#include <wx/artprov.h>
-#include <wx/xrc/xmlres.h>
-#include <wx/intl.h>
-#include <wx/string.h>
-#include <wx/srchctrl.h>
-#include <wx/gdicmn.h>
-#include <wx/font.h>
-#include <wx/colour.h>
-#include <wx/settings.h>
-#include <wx/panel.h>
-#include <wx/button.h>
-#include <wx/sizer.h>
-
-///////////////////////////////////////////////////////////////////////////
-
-
-///////////////////////////////////////////////////////////////////////////////
-/// Class PANEL_HOTKEYS_EDITOR_BASE
-///////////////////////////////////////////////////////////////////////////////
-class PANEL_HOTKEYS_EDITOR_BASE : public wxPanel 
-{
-	private:
-	
-	protected:
-		wxBoxSizer* m_mainSizer;
-		wxSearchCtrl* m_filterSearch;
-		wxPanel* m_panelHotkeys;
-		wxButton* m_resetButton;
-		wxButton* m_defaultButton;
-		wxButton* btnImport;
-		wxButton* btnExport;
-		
-		// Virtual event handlers, overide them in your derived class
-		virtual void OnFilterSearch( wxCommandEvent& event ) { event.Skip(); }
-		virtual void ResetClicked( wxCommandEvent& event ) { event.Skip(); }
-		virtual void DefaultsClicked( wxCommandEvent& event ) { event.Skip(); }
-		virtual void OnImport( wxCommandEvent& event ) { event.Skip(); }
-		virtual void OnExport( wxCommandEvent& event ) { event.Skip(); }
-		
-	
-	public:
-		
-		PANEL_HOTKEYS_EDITOR_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 500,300 ), long style = wxTAB_TRAVERSAL ); 
-		~PANEL_HOTKEYS_EDITOR_BASE();
-	
-};
-
-#endif //__PANEL_HOTKEYS_EDITOR_BASE_H__
diff --git a/common/widgets/button_row_panel.cpp b/common/widgets/button_row_panel.cpp
new file mode 100644
index 000000000..a4ca5f508
--- /dev/null
+++ b/common/widgets/button_row_panel.cpp
@@ -0,0 +1,75 @@
+/*
+ * This program source code file is part of KICAD, a free EDA CAD application.
+ *
+ * Copyright (C) 1992-2018 Kicad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#include <widgets/button_row_panel.h>
+
+#include <wx/button.h>
+#include <wx/sizer.h>
+
+
+BUTTON_ROW_PANEL::BUTTON_ROW_PANEL( wxWindow* aWindow,
+    const BTN_DEF_LIST& aLeftBtns,
+    const BTN_DEF_LIST& aRightBtns ):
+    wxPanel( aWindow, wxID_ANY )
+{
+    m_sizer = new wxBoxSizer( wxHORIZONTAL );
+
+    addButtons( true, aLeftBtns );
+
+    // add the spacer
+    m_sizer->Add( 0, 0, 1, wxEXPAND, 5 );
+
+    addButtons( false, aRightBtns );
+
+    this->SetSizer( m_sizer );
+    this->Layout();
+}
+
+
+void BUTTON_ROW_PANEL::addButtons( bool aLeft, const BTN_DEF_LIST& aDefs )
+{
+    // The "normal" KiCad margin magic number
+    // TODO: Get this from somewhere
+    const int btn_margin = 5;
+    // No button expands to fill horizontally
+    const int btn_proportion = 0;
+
+    for( size_t i = 0; i < aDefs.size(); ++i )
+    {
+        const auto& def = aDefs[i];
+        wxButton* btn = new wxButton( this, def.m_id, def.m_text );
+
+        // Buttons expand to fill the size vertically
+        long this_style = wxEXPAND;
+
+        if( ( aLeft && i > 0 ) || ( !aLeft ) )
+            this_style |= wxLEFT;
+
+        if( ( aLeft ) || ( !aLeft && i < aDefs.size() - 1 ) )
+            this_style |= wxRIGHT;
+
+        m_sizer->Add( btn, btn_proportion, this_style, btn_margin );
+
+        btn->Bind( wxEVT_COMMAND_BUTTON_CLICKED, def.m_callback );
+    }
+}
\ No newline at end of file
diff --git a/common/widgets/widget_hotkey_list.cpp b/common/widgets/widget_hotkey_list.cpp
index cb7f31def..122bd46ad 100644
--- a/common/widgets/widget_hotkey_list.cpp
+++ b/common/widgets/widget_hotkey_list.cpp
@@ -51,9 +51,6 @@ enum ID_WHKL_MENU_IDS
 };
 
 
-
-
-
 /**
  * Class WIDGET_HOTKEY_CLIENT_DATA
  * Stores the hotkey change data associated with each row. To change a
@@ -524,15 +521,6 @@ WIDGET_HOTKEY_LIST::WIDGET_HOTKEY_LIST( wxWindow* aParent, HOTKEY_STORE& aHotkey
 }
 
 
-void WIDGET_HOTKEY_LIST::InstallOnPanel( wxPanel* aPanel )
-{
-    wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
-
-    sizer->Add( this, 1, wxALL | wxEXPAND, 0 );
-    aPanel->SetSizer( sizer );
-}
-
-
 void WIDGET_HOTKEY_LIST::ApplyFilterString( const wxString& aFilterStr )
 {
     updateShownItems( aFilterStr );
diff --git a/include/panel_hotkeys_editor.h b/include/panel_hotkeys_editor.h
index fd4776c49..b4cc78302 100644
--- a/include/panel_hotkeys_editor.h
+++ b/include/panel_hotkeys_editor.h
@@ -27,11 +27,16 @@
 #include <hotkeys_basic.h>
 #include <hotkey_store.h>
 
-#include "../common/dialogs/panel_hotkeys_editor_base.h"
 #include <widgets/widget_hotkey_list.h>
 
+#include "wx/panel.h"
 
-class PANEL_HOTKEYS_EDITOR : public PANEL_HOTKEYS_EDITOR_BASE
+
+class wxPanel;
+class wxSizer;
+
+
+class PANEL_HOTKEYS_EDITOR : public wxPanel
 {
 protected:
     EDA_BASE_FRAME*           m_frame;
@@ -50,28 +55,13 @@ public:
                           EDA_HOTKEY_CONFIG* aHotkeys, EDA_HOTKEY_CONFIG* aShowHotkeys,
                           const wxString& aNickname );
 
-    ~PANEL_HOTKEYS_EDITOR() {};
-
 private:
 
     /**
-     * Function ResetClicked
-     * Reinit the hotkeys to the initial state (removes all pending changes)
-     *
-     * @param aEvent is the button press event, unused
-     */
-    void ResetClicked( wxCommandEvent& aEvent ) override;
-
-    /**
-     * Function DefaultsClicked
-     * Set the hotkeys to the default values (values after installation)
-     *
-     * @param aEvent is the button press event, unused
+     * Install the button panel (global reset/default, import/export)
+     * @param aSizer the dialog to install on
      */
-    void DefaultsClicked( wxCommandEvent& aEvent ) override;
-
-    void OnExport( wxCommandEvent& aEvent ) override;
-    void OnImport( wxCommandEvent& aEvent ) override;
+    void installButtons( wxSizer* aSizer );
 
     /**
      * Function OnFilterSearch
@@ -79,7 +69,7 @@ private:
      *
      * @param aEvent: the search event, used to get the search query
      */
-    void OnFilterSearch( wxCommandEvent& aEvent ) override;
+    void OnFilterSearch( wxCommandEvent& aEvent );
 };
 
 
diff --git a/include/widgets/button_row_panel.h b/include/widgets/button_row_panel.h
new file mode 100644
index 000000000..8abfe834a
--- /dev/null
+++ b/include/widgets/button_row_panel.h
@@ -0,0 +1,102 @@
+/*
+ * This program source code file is part of KICAD, a free EDA CAD application.
+ *
+ * Copyright (C) 1992-2018 Kicad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#ifndef SIMPLE_BUTTON_PANEL_H
+#define SIMPLE_BUTTON_PANEL_H
+
+#include "wx/panel.h"
+
+#include <vector>
+#include <functional>
+
+
+// Forward defs for private-only classes
+class wxBoxSizer;
+
+
+/**
+ * A panel that contains buttons, arranged on the left and/or right sides.
+ */
+class BUTTON_ROW_PANEL: public wxPanel
+{
+public:
+
+    /**
+     * Callback function definition. A callback of this type can be registered
+     * to handle the button click event.
+     */
+    using BTN_CALLBACK = std::function< void( wxCommandEvent& ) >;
+
+    /**
+     * The information needed to instantiate a button on a BUTTON_ROW_PANEL.
+     */
+    struct BTN_DEF
+    {
+        /**
+         * The button ID. Can be wxID_ANY, but should be unique if you
+         * want to work out which button this was from an event handler.
+         */
+        wxWindowID      m_id;
+
+        /**
+         * The button display text.
+         */
+        wxString        m_text;
+
+        /**
+         * The callback fired when the button is clicked. Can be nullptr,
+         * but then the button is useless.
+         */
+        BTN_CALLBACK    m_callback;
+    };
+
+    /**
+     * A list of BTN_DEFs, used to group buttons into the left/right groups.
+     */
+    using BTN_DEF_LIST = std::vector<BTN_DEF>;
+
+    /**
+     * Construct a SIMPLE_BUTTON_PANEL with a set of buttons on each side.
+     *
+     * @param aLeftBtns: buttons on the left side, from left to right
+     * @param aRightBtns: buttons on the right side, from left to right
+     */
+    BUTTON_ROW_PANEL( wxWindow* aWindow,
+        const BTN_DEF_LIST& aLeftBtns,
+        const BTN_DEF_LIST& aRightBtns );
+
+private:
+
+    /**
+     * Add a set of buttons to one side of the panel.
+     *
+     * @param aSizer the sizer to add them to
+     * @param aLeft  place on the left (false for right)
+     * @param aDefs  list of button defs, from left to right
+     */
+    void addButtons( bool aLeft, const BTN_DEF_LIST& aDefs );
+
+    wxBoxSizer* m_sizer;
+};
+
+#endif // SIMPLE_BUTTON_PANEL_H
\ No newline at end of file
diff --git a/include/widgets/widget_hotkey_list.h b/include/widgets/widget_hotkey_list.h
index 2e728d3c8..413ab5bb0 100644
--- a/include/widgets/widget_hotkey_list.h
+++ b/include/widgets/widget_hotkey_list.h
@@ -166,14 +166,6 @@ public:
      */
     WIDGET_HOTKEY_LIST( wxWindow* aParent, HOTKEY_STORE& aHotkeyStore );
 
-    /**
-     * 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 ApplyFilterString
      * Apply a filter string to the hotkey list, selecting which hotkeys
-- 
2.19.0


Follow ups