From 72f87861bb22bf2ecb82ea570d9828a287436748 Mon Sep 17 00:00:00 2001
From: qu1ck <anlutsenko@gmail.com>
Date: Wed, 15 Aug 2018 02:26:32 -0700
Subject: [PATCH] pcbnew: Add toolbar buttons for action plugins

Allows optional toolbar buttons for action plugins.
---
 common/bitmap.cpp                             |  19 +
 common/widgets/grid_icon_text_helpers.cpp     |  41 +-
 include/bitmap_types.h                        |   8 +
 include/widgets/grid_icon_text_helpers.h      |  19 +-
 pcbnew/CMakeLists.txt                         |   9 +-
 pcbnew/action_plugin.cpp                      |  46 +-
 pcbnew/action_plugin.h                        |  60 +-
 .../dialogs/panel_pcbnew_action_plugins.cpp   | 199 ++++++
 pcbnew/dialogs/panel_pcbnew_action_plugins.h  |  70 +++
 .../panel_pcbnew_action_plugins_base.cpp      |  98 +++
 .../panel_pcbnew_action_plugins_base.fbp      | 578 ++++++++++++++++++
 .../panel_pcbnew_action_plugins_base.h        |  58 ++
 pcbnew/pcb_edit_frame.cpp                     |   2 +
 pcbnew/pcb_edit_frame.h                       |  59 +-
 pcbnew/pcb_general_settings.cpp               |  45 ++
 pcbnew/pcb_general_settings.h                 |   5 +
 pcbnew/pcbnew_config.cpp                      |   4 +
 pcbnew/swig/pcbnew_action_plugins.cpp         | 526 ++++++++++------
 pcbnew/swig/pcbnew_action_plugins.h           |   3 +
 pcbnew/tool_pcb_editor.cpp                    |   4 +
 scripting/kicadplugins.i                      |  24 +-
 21 files changed, 1664 insertions(+), 213 deletions(-)
 create mode 100644 pcbnew/dialogs/panel_pcbnew_action_plugins.cpp
 create mode 100644 pcbnew/dialogs/panel_pcbnew_action_plugins.h
 create mode 100644 pcbnew/dialogs/panel_pcbnew_action_plugins_base.cpp
 create mode 100644 pcbnew/dialogs/panel_pcbnew_action_plugins_base.fbp
 create mode 100644 pcbnew/dialogs/panel_pcbnew_action_plugins_base.h

diff --git a/common/bitmap.cpp b/common/bitmap.cpp
index 031b2cc556..bce8344c55 100644
--- a/common/bitmap.cpp
+++ b/common/bitmap.cpp
@@ -144,6 +144,25 @@ wxBitmap KiScaledBitmap( BITMAP_DEF aBitmap, EDA_BASE_FRAME* aWindow )
 }
 
 
+wxBitmap KiScaledBitmap( const wxBitmap& aBitmap, EDA_BASE_FRAME* aWindow )
+{
+    const int scale = get_scale_factor( aWindow );
+
+    if( scale == 4)
+    {
+        return wxBitmap( aBitmap );
+    }
+    else
+    {
+        wxImage image = aBitmap.ConvertToImage();
+        image.Rescale( scale * image.GetWidth() / 4, scale * image.GetHeight() / 4,
+            wxIMAGE_QUALITY_BILINEAR );
+
+        return wxBitmap( image );
+    }
+}
+
+
 void KiScaledSeparator( wxAuiToolBar* aToolbar, EDA_BASE_FRAME* aWindow )
 {
     const int scale = get_scale_factor( aWindow );
diff --git a/common/widgets/grid_icon_text_helpers.cpp b/common/widgets/grid_icon_text_helpers.cpp
index 8d348c7d0f..b1f949e977 100644
--- a/common/widgets/grid_icon_text_helpers.cpp
+++ b/common/widgets/grid_icon_text_helpers.cpp
@@ -27,7 +27,7 @@
 #include <wx/dc.h>
 
 
-//---- Grid helpers: custom wxGridCellRenderer ------------------------------------------
+//---- Grid helpers: custom wxGridCellRenderer that renders icon and a label ------------
 
 
 GRID_CELL_ICON_TEXT_RENDERER::GRID_CELL_ICON_TEXT_RENDERER( const std::vector<BITMAP_DEF>& icons,
@@ -68,6 +68,45 @@ void GRID_CELL_ICON_TEXT_RENDERER::Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, w
     aGrid.DrawTextRectangle( aDC, value, rect, wxALIGN_LEFT, wxALIGN_CENTRE );
 }
 
+//---- Grid helpers: custom wxGridCellRenderer that renders just an icon ----------------
+//
+// Note: this renderer is supposed to be used with read only cells
+
+GRID_CELL_ICON_RENDERER::GRID_CELL_ICON_RENDERER(const wxBitmap& icon) : m_icon( icon )
+{
+}
+
+
+void GRID_CELL_ICON_RENDERER::Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC,
+                                    const wxRect& aRect, int aRow, int aCol, bool isSelected )
+{
+    wxRect rect = aRect;
+    rect.Inflate( -1 );
+
+    // erase background
+    wxGridCellRenderer::Draw( aGrid, aAttr, aDC, aRect, aRow, aCol, isSelected );
+
+    // Draw icon
+    if( m_icon.IsOk() )
+    {
+        aDC.DrawBitmap( m_icon,
+                        rect.GetLeft() + ( rect.GetWidth() - m_icon.GetWidth() ) / 2,
+                        rect.GetTop() + ( rect.GetHeight() - m_icon.GetHeight() ) / 2,
+                        true );
+    }
+}
+
+
+wxSize GRID_CELL_ICON_RENDERER::GetBestSize( wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, int row, int col )
+{
+    return wxSize( m_icon.GetWidth() + 6, m_icon.GetHeight() + 4 );
+}
+
+
+wxGridCellRenderer* GRID_CELL_ICON_RENDERER::Clone() const
+{
+    return new GRID_CELL_ICON_RENDERER( m_icon );
+}
 
 
 //---- Grid helpers: custom wxGridCellEditor ------------------------------------------
diff --git a/include/bitmap_types.h b/include/bitmap_types.h
index 8657df4969..f703ddbbdc 100644
--- a/include/bitmap_types.h
+++ b/include/bitmap_types.h
@@ -73,6 +73,14 @@ wxBitmap KiBitmap( BITMAP_DEF aBitmap );
  */
 wxBitmap KiScaledBitmap( BITMAP_DEF aBitmap, EDA_BASE_FRAME* aWindow );
 
+/**
+ * Function KiScaledBitmap
+ * Overload of the above function that takes another wxBitmap as a parameter
+ *
+ * @param aBitmap bitmap definition
+ * @param aWindow target window for scaling context
+ */
+wxBitmap KiScaledBitmap( const wxBitmap& aBitmap, EDA_BASE_FRAME* aWindow );
 
 /**
  * Function KiScaledSeparator
diff --git a/include/widgets/grid_icon_text_helpers.h b/include/widgets/grid_icon_text_helpers.h
index 9e14829599..60ea612d32 100644
--- a/include/widgets/grid_icon_text_helpers.h
+++ b/include/widgets/grid_icon_text_helpers.h
@@ -34,7 +34,7 @@
 class wxGrid;
 
 
-//---- Grid helpers: custom wxGridCellRenderer ------------------------------------------
+//---- Grid helpers: custom wxGridCellRenderer that renders icon and a label ------------
 
 class GRID_CELL_ICON_TEXT_RENDERER : public wxGridCellStringRenderer
 {
@@ -49,6 +49,23 @@ private:
     const wxArrayString&           m_names;
 };
 
+//---- Grid helpers: custom wxGridCellRenderer that renders just an icon ----------------
+//
+// Note: use with read only cells
+
+class GRID_CELL_ICON_RENDERER : public wxGridCellRenderer
+{
+public:
+    GRID_CELL_ICON_RENDERER( const wxBitmap& icon );
+
+    void Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC,
+               const wxRect& aRect, int aRow, int aCol, bool isSelected ) override;
+    wxSize GetBestSize( wxGrid & grid, wxGridCellAttr & attr, wxDC & dc, int row, int col ) override;
+    wxGridCellRenderer* Clone() const override;
+
+private:
+    const wxBitmap& m_icon;
+};
 
 //---- Grid helpers: custom wxGridCellEditor ------------------------------------------
 //
diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt
index a77f8ebfdb..22020800b7 100644
--- a/pcbnew/CMakeLists.txt
+++ b/pcbnew/CMakeLists.txt
@@ -152,8 +152,6 @@ set( PCBNEW_DIALOGS
     dialogs/panel_modedit_settings_base.cpp
     dialogs/panel_pcbnew_display_options.cpp
     dialogs/panel_pcbnew_display_options_base.cpp
-    dialogs/panel_pcbnew_display_options.cpp
-    dialogs/panel_pcbnew_display_options_base.cpp
     dialogs/panel_pcbnew_settings.cpp
     dialogs/panel_pcbnew_settings_base.cpp
     dialogs/panel_setup_mask_and_paste.cpp
@@ -174,6 +172,13 @@ set( PCBNEW_DIALOGS
     ${GITHUB_3DLIBRARIES_WIZARD}
     )
 
+if( KICAD_SCRIPTING AND KICAD_SCRIPTING_ACTION_MENU )
+    set( PCBNEW_DIALOGS ${PCBNEW_DIALOGS}
+        dialogs/panel_pcbnew_action_plugins.cpp
+        dialogs/panel_pcbnew_action_plugins_base.cpp
+        )
+endif()
+
 set( PCBNEW_IMPORT_DXF
     import_dxf/dialog_dxf_import.cpp
     import_dxf/dialog_dxf_import_base.cpp
diff --git a/pcbnew/action_plugin.cpp b/pcbnew/action_plugin.cpp
index c10d0fafe2..e79ea29638 100644
--- a/pcbnew/action_plugin.cpp
+++ b/pcbnew/action_plugin.cpp
@@ -73,9 +73,37 @@ void ACTION_PLUGINS::SetActionMenu( int aIndex, int idMenu )
 }
 
 
-int ACTION_PLUGINS::GetActionMenu( int aIndex )
+ACTION_PLUGIN* ACTION_PLUGINS::GetActionByButton( int aButton )
 {
-    return m_actionsList[aIndex]->m_actionMenuId;
+    int max = GetActionsCount();
+
+    for( int i = 0; i < max; i++ )
+    {
+        if( m_actionsList[i]->m_actionButtonId == aButton )
+            return m_actionsList[i];
+    }
+
+    return NULL;
+}
+
+
+void ACTION_PLUGINS::SetActionButton( ACTION_PLUGIN* aAction, int idButton )
+{
+    aAction->m_actionButtonId = idButton;
+}
+
+
+ACTION_PLUGIN* ACTION_PLUGINS::GetActionByPath(const wxString& aPath)
+{
+    for( int i = 0; i < GetActionsCount() ; i++ )
+    {
+        if( m_actionsList[i]->GetPluginPath() == aPath)
+        {
+            return m_actionsList[i];
+        }
+    }
+
+    return NULL;
 }
 
 
@@ -127,6 +155,20 @@ void ACTION_PLUGINS::register_action( ACTION_PLUGIN* aAction )
         }
     }
 
+    // Load icon if supplied
+    if (!aAction->GetIconFileName().IsEmpty())
+    {
+        {
+            wxLogNull eat_errors;
+            aAction->iconBitmap.LoadFile( aAction->GetIconFileName() , wxBITMAP_TYPE_PNG );
+        }
+
+        if ( !aAction->iconBitmap.IsOk() )
+        {
+            wxLogVerbose( "Failed to load icon " + aAction->GetIconFileName() + " for action plugin " );
+        }
+    }
+
     m_actionsList.push_back( aAction );
 }
 
diff --git a/pcbnew/action_plugin.h b/pcbnew/action_plugin.h
index ff3ed14559..8bb6c9c4aa 100644
--- a/pcbnew/action_plugin.h
+++ b/pcbnew/action_plugin.h
@@ -44,9 +44,16 @@ public:
     // m_actionMenuId set to 0 means the corresponding menuitem to call this
     // action is not yet created
     int m_actionMenuId;
+    // Same for button id
+    int m_actionButtonId;
+    // Icon for the action button and menu entry
+    wxBitmap iconBitmap;
+    // If show_on_toolbar is true a button will be added to top toolbar
+    bool show_on_toolbar;
 
 public:
-    ACTION_PLUGIN() : m_actionMenuId( 0 ) {}
+    ACTION_PLUGIN() : m_actionMenuId( 0 ), m_actionButtonId( 0 ),
+                      show_on_toolbar( false ) {}
     virtual ~ACTION_PLUGIN();
 
     /**
@@ -68,6 +75,24 @@ public:
      */
     virtual wxString GetDescription() = 0;
 
+    /**
+     * Function GetShowToolbarButton
+     * @return true if button should be shown on top toolbar
+     */
+    virtual bool GetShowToolbarButton() = 0;
+
+    /**
+     * Function GetIconFileName
+     * @return a path to icon for the action plugin button
+     */
+    virtual wxString GetIconFileName() = 0;
+
+    /**
+     * Function GetPluginPath
+     * @return a path this plugin was loaded from
+     */
+    virtual wxString GetPluginPath() = 0;
+
     /**
      * Function GetObject
      * This method gets the pointer to the object from where this action constructs
@@ -137,16 +162,6 @@ public:
      */
     static void SetActionMenu( int aIndex, int idMenu );
 
-
-    /**
-     * Function GetActionMenu
-     * Provide menu id for a plugin index
-     * @param aIndex is the action index
-     * @return associated menuitem id
-     */
-    static int GetActionMenu( int aIndex );
-
-
     /**
      * Function GetActionByMenu
      * find action plugin associated to a menu id
@@ -155,6 +170,29 @@ public:
      */
     static ACTION_PLUGIN* GetActionByMenu( int aMenu );
 
+    /**
+     * Function SetActionButton
+     * Associate a button id to an action plugin
+     * @param aAction is the action
+     * @param idButton is the associated menuitem id
+     */
+    static void SetActionButton( ACTION_PLUGIN* aAction, int idButton );
+
+    /**
+     * Function GetActionByButton
+     * find action plugin associated to a button id
+     * @param aButton is the button id (defined with SetActionButton)
+     * @return the associated ACTION_PLUGIN (or null if not found)
+     */
+    static ACTION_PLUGIN* GetActionByButton( int aButton );
+
+    /**
+     * Function GetActionByPath
+     * find action plugin by module path
+     * @param aPath the path of plugin
+     * @return the corresponding ACTION_PLUGIN (or null if not found)
+     */
+    static ACTION_PLUGIN* GetActionByPath( const wxString& aPath );
 
     /**
      * Function GetAction
diff --git a/pcbnew/dialogs/panel_pcbnew_action_plugins.cpp b/pcbnew/dialogs/panel_pcbnew_action_plugins.cpp
new file mode 100644
index 0000000000..3887fe9b2f
--- /dev/null
+++ b/pcbnew/dialogs/panel_pcbnew_action_plugins.cpp
@@ -0,0 +1,199 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2018 Andrew Lutsenko, anlutsenko at gmail dot com
+ * 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 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 <pcb_edit_frame.h>
+#include <panel_pcbnew_action_plugins.h>
+#include <widgets/paged_dialog.h>
+#include <widgets/grid_icon_text_helpers.h>
+#include <bitmaps.h>
+#include <action_plugin.h>
+#include <grid_tricks.h>
+#include <widgets/wx_grid.h>
+
+
+PANEL_PCBNEW_ACTION_PLUGINS::PANEL_PCBNEW_ACTION_PLUGINS( PCB_EDIT_FRAME* aFrame, PAGED_DIALOG* aWindow ) :
+    PANEL_PCBNEW_ACTION_PLUGINS_BASE( aWindow->GetTreebook() ),
+    m_frame( aFrame )
+{
+    m_genericIcon = KiBitmap( hammer_xpm );
+    m_grid->PushEventHandler( new GRID_TRICKS( m_grid ) );
+
+    m_moveUpButton->SetBitmap( KiBitmap( up_xpm ) );
+    m_moveDownButton->SetBitmap( KiBitmap( down_xpm ) );
+    m_reloadButton->SetBitmap( KiBitmap( refresh_xpm ) );
+}
+
+
+PANEL_PCBNEW_ACTION_PLUGINS::~PANEL_PCBNEW_ACTION_PLUGINS()
+{
+    m_grid->PopEventHandler( true );
+}
+
+
+void PANEL_PCBNEW_ACTION_PLUGINS::OnGridCellClick( wxGridEvent& event )
+{
+    SelectRow( event.GetRow() );
+}
+
+
+void PANEL_PCBNEW_ACTION_PLUGINS::SelectRow( int aRow )
+{
+    m_grid->ClearSelection();
+    m_grid->SelectRow( aRow );
+}
+
+
+void PANEL_PCBNEW_ACTION_PLUGINS::OnMoveUpButtonClick( wxCommandEvent& event )
+{
+    auto selectedRows = m_grid->GetSelectedRows();
+
+    // If nothing is selected or multiple rows are selected don't do anything.
+    if( selectedRows.size() != 1 ) return;
+
+    int selectedRow = selectedRows[0];
+
+    // If first row is selected, then it can't go any further up.
+    if( selectedRow == 0 )
+    {
+        wxBell();
+        return;
+    }
+
+    SwapRows( selectedRow, selectedRow - 1 );
+
+    SelectRow( selectedRow - 1 );
+}
+
+
+void PANEL_PCBNEW_ACTION_PLUGINS::OnMoveDownButtonClick( wxCommandEvent& event )
+{
+    auto selectedRows = m_grid->GetSelectedRows();
+
+    // If nothing is selected or multiple rows are selected don't do anything.
+    if( selectedRows.size() != 1 ) return;
+
+    int selectedRow = selectedRows[0];
+
+    // If last row is selected, then it can't go any further down.
+    if( selectedRow + 1 == m_grid->GetNumberRows() )
+    {
+        wxBell();
+        return;
+    }
+
+    SwapRows( selectedRow, selectedRow + 1 );
+
+    SelectRow( selectedRow + 1 );
+}
+
+
+void PANEL_PCBNEW_ACTION_PLUGINS::SwapRows( int aRowA, int aRowB )
+{
+    m_grid->Freeze();
+
+    // Swap all columns except icon
+    wxString tempStr;
+
+    for( int column = 1; column < m_grid->GetNumberCols(); column++ )
+    {
+        tempStr = m_grid->GetCellValue( aRowA, column );
+        m_grid->SetCellValue( aRowA, column, m_grid->GetCellValue( aRowB, column ) );
+        m_grid->SetCellValue( aRowB, column, tempStr );
+    }
+
+    // Swap icon column renderers
+    auto cellRenderer = m_grid->GetCellRenderer( aRowA, COLUMN_ICON );
+    m_grid->SetCellRenderer( aRowA, COLUMN_ICON, m_grid->GetCellRenderer( aRowB, COLUMN_ICON ) );
+    m_grid->SetCellRenderer( aRowB, COLUMN_ICON, cellRenderer );
+
+    m_grid->Thaw();
+}
+
+
+void PANEL_PCBNEW_ACTION_PLUGINS::OnReloadButtonClick( wxCommandEvent& event )
+{
+    m_frame->PythonPluginsReload();
+    TransferDataToWindow();
+}
+
+
+bool PANEL_PCBNEW_ACTION_PLUGINS::TransferDataFromWindow()
+{
+    std::vector< std::pair<wxString, wxString> > pluginSettings;
+
+    for( int ii = 0; ii < m_grid->GetNumberRows(); ii++ )
+    {
+        pluginSettings.push_back( std::make_pair(
+                                    m_grid->GetCellValue( ii, COLUMN_PATH ),
+                                    m_grid->GetCellValue( ii, COLUMN_VISIBLE ) == wxT("1") ? wxT( "Visible" ) : wxT( "Hidden" )
+                                ) );
+    }
+
+    m_frame->SetActionPluginSettings( pluginSettings );
+
+    return true;
+}
+
+
+bool PANEL_PCBNEW_ACTION_PLUGINS::TransferDataToWindow()
+{
+    m_grid->Freeze();
+    m_grid->DeleteRows( 0, m_grid->GetNumberRows() );
+
+    const auto& orderedPlugins = m_frame->GetOrderedActionPlugins();
+    m_grid->AppendRows( orderedPlugins.size() );
+
+    for( size_t row = 0; row < orderedPlugins.size(); row++ )
+    {
+        ACTION_PLUGIN* ap = orderedPlugins[row];
+
+        // Icon
+        m_grid->SetCellRenderer( row, COLUMN_ICON, new GRID_CELL_ICON_RENDERER(
+                                 ap->iconBitmap.IsOk() ? ap->iconBitmap : m_genericIcon ) );
+
+        // Toolbar button checkbox
+        m_grid->SetCellRenderer( row, COLUMN_VISIBLE, new wxGridCellBoolRenderer() );
+        m_grid->SetCellAlignment( row, COLUMN_VISIBLE, wxALIGN_CENTER, wxALIGN_CENTER );
+
+        bool showButton = m_frame->GetActionPluginButtonVisible(
+                ap->GetPluginPath(), ap->GetShowToolbarButton() );
+
+        m_grid->SetCellValue( row, COLUMN_VISIBLE, showButton ? wxT( "1" ) : wxEmptyString );
+
+        // Name
+        m_grid->SetCellValue( row, COLUMN_NAME, ap->GetName() );
+
+        // Category
+        m_grid->SetCellValue( row, COLUMN_CATEGORY, ap->GetCategoryName() );
+
+        // Description
+        m_grid->SetCellValue( row, COLUMN_DESCRIPTION, ap->GetDescription() );
+
+        // Path
+        m_grid->SetCellValue( row, COLUMN_PATH, ap->GetPluginPath() );
+    }
+
+    m_grid->AutoSizeColumns();
+    m_grid->AutoSizeRows();
+
+    m_grid->Thaw();
+
+    return true;
+}
diff --git a/pcbnew/dialogs/panel_pcbnew_action_plugins.h b/pcbnew/dialogs/panel_pcbnew_action_plugins.h
new file mode 100644
index 0000000000..48e8dc0155
--- /dev/null
+++ b/pcbnew/dialogs/panel_pcbnew_action_plugins.h
@@ -0,0 +1,70 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2018 Andrew Lutsenko, anlutsenko at gmail dot com
+ * 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 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 "panel_pcbnew_action_plugins_base.h"
+
+class PANEL_PCBNEW_ACTION_PLUGINS : public PANEL_PCBNEW_ACTION_PLUGINS_BASE
+{
+public:
+    PANEL_PCBNEW_ACTION_PLUGINS ( PCB_EDIT_FRAME* aFrame, PAGED_DIALOG* aWindow );
+
+    bool TransferDataFromWindow() override;
+    bool TransferDataToWindow() override;
+    ~PANEL_PCBNEW_ACTION_PLUGINS() override;
+
+    /**
+     * Selects a whole row
+     */
+    void OnGridCellClick( wxGridEvent& event ) override;
+
+    /**
+     * Moves plugin up in the grid
+     */
+    void OnMoveUpButtonClick( wxCommandEvent& event ) override;
+
+    /**
+     * Moves plugin down in the grid
+     */
+    void OnMoveDownButtonClick( wxCommandEvent& event ) override;
+
+    /**
+     * Reloads plugins and updates grid
+     */
+    void OnReloadButtonClick( wxCommandEvent& event ) override;
+
+private:
+
+    enum GRID_COLUMNS
+    {
+        COLUMN_ICON,
+        COLUMN_VISIBLE,
+        COLUMN_NAME,
+        COLUMN_CATEGORY,
+        COLUMN_DESCRIPTION,
+        COLUMN_PATH
+    };
+
+    PCB_EDIT_FRAME*    m_frame;
+    wxBitmap m_genericIcon;
+
+    void SwapRows( int aRowA, int aRowB );
+    void SelectRow( int aRow );
+};
+
diff --git a/pcbnew/dialogs/panel_pcbnew_action_plugins_base.cpp b/pcbnew/dialogs/panel_pcbnew_action_plugins_base.cpp
new file mode 100644
index 0000000000..d8f8909046
--- /dev/null
+++ b/pcbnew/dialogs/panel_pcbnew_action_plugins_base.cpp
@@ -0,0 +1,98 @@
+///////////////////////////////////////////////////////////////////////////
+// C++ code generated with wxFormBuilder (version Jul 11 2018)
+// http://www.wxformbuilder.org/
+//
+// PLEASE DO *NOT* EDIT THIS FILE!
+///////////////////////////////////////////////////////////////////////////
+
+#include "widgets/wx_grid.h"
+
+#include "panel_pcbnew_action_plugins_base.h"
+
+///////////////////////////////////////////////////////////////////////////
+
+PANEL_PCBNEW_ACTION_PLUGINS_BASE::PANEL_PCBNEW_ACTION_PLUGINS_BASE( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style )
+{
+	wxBoxSizer* bPanelSizer;
+	bPanelSizer = new wxBoxSizer( wxHORIZONTAL );
+	
+	wxBoxSizer* bGridSizer;
+	bGridSizer = new wxBoxSizer( wxVERTICAL );
+	
+	m_grid = new WX_GRID( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_SIMPLE );
+	
+	// Grid
+	m_grid->CreateGrid( 3, 6 );
+	m_grid->EnableEditing( false );
+	m_grid->EnableGridLines( true );
+	m_grid->EnableDragGridSize( false );
+	m_grid->SetMargins( 0, 0 );
+	
+	// Columns
+	m_grid->AutoSizeColumns();
+	m_grid->EnableDragColMove( false );
+	m_grid->EnableDragColSize( true );
+	m_grid->SetColLabelSize( 22 );
+	m_grid->SetColLabelValue( 0, wxT("Icon") );
+	m_grid->SetColLabelValue( 1, wxT("Show button") );
+	m_grid->SetColLabelValue( 2, wxT("Name") );
+	m_grid->SetColLabelValue( 3, wxT("Category") );
+	m_grid->SetColLabelValue( 4, wxT("Description") );
+	m_grid->SetColLabelValue( 5, wxT("Path") );
+	m_grid->SetColLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE );
+	
+	// Rows
+	m_grid->EnableDragRowSize( true );
+	m_grid->SetRowLabelSize( 0 );
+	m_grid->SetRowLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE );
+	
+	// Label Appearance
+	
+	// Cell Defaults
+	m_grid->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_CENTRE );
+	bGridSizer->Add( m_grid, 1, wxALL|wxEXPAND, 5 );
+	
+	
+	bPanelSizer->Add( bGridSizer, 1, wxALIGN_LEFT|wxEXPAND|wxLEFT, 0 );
+	
+	wxBoxSizer* bButtonsSizer;
+	bButtonsSizer = new wxBoxSizer( wxVERTICAL );
+	
+	m_moveUpButton = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
+	m_moveUpButton->SetMinSize( wxSize( 32,32 ) );
+	
+	bButtonsSizer->Add( m_moveUpButton, 0, wxALIGN_TOP|wxALL, 5 );
+	
+	m_moveDownButton = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
+	m_moveDownButton->SetMinSize( wxSize( 32,32 ) );
+	
+	bButtonsSizer->Add( m_moveDownButton, 0, wxALL, 5 );
+	
+	m_reloadButton = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
+	m_reloadButton->SetMinSize( wxSize( 32,32 ) );
+	
+	bButtonsSizer->Add( m_reloadButton, 0, wxALL, 5 );
+	
+	
+	bPanelSizer->Add( bButtonsSizer, 0, wxALIGN_RIGHT|wxALIGN_TOP, 0 );
+	
+	
+	this->SetSizer( bPanelSizer );
+	this->Layout();
+	
+	// Connect Events
+	m_grid->Connect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnGridCellClick ), NULL, this );
+	m_moveUpButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnMoveUpButtonClick ), NULL, this );
+	m_moveDownButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnMoveDownButtonClick ), NULL, this );
+	m_reloadButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnReloadButtonClick ), NULL, this );
+}
+
+PANEL_PCBNEW_ACTION_PLUGINS_BASE::~PANEL_PCBNEW_ACTION_PLUGINS_BASE()
+{
+	// Disconnect Events
+	m_grid->Disconnect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnGridCellClick ), NULL, this );
+	m_moveUpButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnMoveUpButtonClick ), NULL, this );
+	m_moveDownButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnMoveDownButtonClick ), NULL, this );
+	m_reloadButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnReloadButtonClick ), NULL, this );
+	
+}
diff --git a/pcbnew/dialogs/panel_pcbnew_action_plugins_base.fbp b/pcbnew/dialogs/panel_pcbnew_action_plugins_base.fbp
new file mode 100644
index 0000000000..6c91f1fd1f
--- /dev/null
+++ b/pcbnew/dialogs/panel_pcbnew_action_plugins_base.fbp
@@ -0,0 +1,578 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<wxFormBuilder_Project>
+    <FileVersion major="1" minor="14" />
+    <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_pcbnew_action_plugins_base</property>
+        <property name="first_id">1000</property>
+        <property name="help_provider">none</property>
+        <property name="indent_with_spaces">0</property>
+        <property name="internationalize">0</property>
+        <property name="name">PanelPcbnewActionPlugins</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">1</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_PCBNEW_ACTION_PLUGINS_BASE</property>
+            <property name="pos"></property>
+            <property name="size">485,200</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="OnAuiPaneActivated"></event>
+            <event name="OnAuiPaneButton"></event>
+            <event name="OnAuiPaneClose"></event>
+            <event name="OnAuiPaneMaximize"></event>
+            <event name="OnAuiPaneRestore"></event>
+            <event name="OnAuiRender"></event>
+            <event name="OnAux1DClick"></event>
+            <event name="OnAux1Down"></event>
+            <event name="OnAux1Up"></event>
+            <event name="OnAux2DClick"></event>
+            <event name="OnAux2Down"></event>
+            <event name="OnAux2Up"></event>
+            <event name="OnChar"></event>
+            <event name="OnCharHook"></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">bPanelSizer</property>
+                <property name="orient">wxHORIZONTAL</property>
+                <property name="permission">none</property>
+                <object class="sizeritem" expanded="1">
+                    <property name="border">0</property>
+                    <property name="flag">wxALIGN_LEFT|wxEXPAND|wxLEFT</property>
+                    <property name="proportion">1</property>
+                    <object class="wxBoxSizer" expanded="1">
+                        <property name="minimum_size"></property>
+                        <property name="name">bGridSizer</property>
+                        <property name="orient">wxVERTICAL</property>
+                        <property name="permission">none</property>
+                        <object class="sizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="flag">wxALL|wxEXPAND</property>
+                            <property name="proportion">1</property>
+                            <object class="wxGrid" 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="autosize_cols">1</property>
+                                <property name="autosize_rows">0</property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="cell_bg"></property>
+                                <property name="cell_font"></property>
+                                <property name="cell_horiz_alignment">wxALIGN_LEFT</property>
+                                <property name="cell_text"></property>
+                                <property name="cell_vert_alignment">wxALIGN_CENTRE</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="col_label_horiz_alignment">wxALIGN_CENTRE</property>
+                                <property name="col_label_size">22</property>
+                                <property name="col_label_values">&quot;Icon&quot; &quot;Show button&quot; &quot;Name&quot; &quot;Category&quot; &quot;Description&quot; &quot;Path&quot;</property>
+                                <property name="col_label_vert_alignment">wxALIGN_CENTRE</property>
+                                <property name="cols">6</property>
+                                <property name="column_sizes"></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="drag_col_move">0</property>
+                                <property name="drag_col_size">1</property>
+                                <property name="drag_grid_size">0</property>
+                                <property name="drag_row_size">1</property>
+                                <property name="editing">0</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="grid_line_color"></property>
+                                <property name="grid_lines">1</property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label_bg"></property>
+                                <property name="label_font"></property>
+                                <property name="label_text"></property>
+                                <property name="margin_height">0</property>
+                                <property name="margin_width">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_grid</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="row_label_horiz_alignment">wxALIGN_CENTRE</property>
+                                <property name="row_label_size">0</property>
+                                <property name="row_label_values"></property>
+                                <property name="row_label_vert_alignment">wxALIGN_CENTRE</property>
+                                <property name="row_sizes"></property>
+                                <property name="rows">3</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="subclass">WX_GRID; widgets/wx_grid.h; forward_declare</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">wxBORDER_SIMPLE</property>
+                                <event name="OnAux1DClick"></event>
+                                <event name="OnAux1Down"></event>
+                                <event name="OnAux1Up"></event>
+                                <event name="OnAux2DClick"></event>
+                                <event name="OnAux2Down"></event>
+                                <event name="OnAux2Up"></event>
+                                <event name="OnChar"></event>
+                                <event name="OnCharHook"></event>
+                                <event name="OnEnterWindow"></event>
+                                <event name="OnEraseBackground"></event>
+                                <event name="OnGridCellChange"></event>
+                                <event name="OnGridCellLeftClick">OnGridCellClick</event>
+                                <event name="OnGridCellLeftDClick"></event>
+                                <event name="OnGridCellRightClick"></event>
+                                <event name="OnGridCellRightDClick"></event>
+                                <event name="OnGridCmdCellChange"></event>
+                                <event name="OnGridCmdCellLeftClick"></event>
+                                <event name="OnGridCmdCellLeftDClick"></event>
+                                <event name="OnGridCmdCellRightClick"></event>
+                                <event name="OnGridCmdCellRightDClick"></event>
+                                <event name="OnGridCmdColSize"></event>
+                                <event name="OnGridCmdEditorCreated"></event>
+                                <event name="OnGridCmdEditorHidden"></event>
+                                <event name="OnGridCmdEditorShown"></event>
+                                <event name="OnGridCmdLabelLeftClick"></event>
+                                <event name="OnGridCmdLabelLeftDClick"></event>
+                                <event name="OnGridCmdLabelRightClick"></event>
+                                <event name="OnGridCmdLabelRightDClick"></event>
+                                <event name="OnGridCmdRangeSelect"></event>
+                                <event name="OnGridCmdRowSize"></event>
+                                <event name="OnGridCmdSelectCell"></event>
+                                <event name="OnGridColSize"></event>
+                                <event name="OnGridEditorCreated"></event>
+                                <event name="OnGridEditorHidden"></event>
+                                <event name="OnGridEditorShown"></event>
+                                <event name="OnGridLabelLeftClick"></event>
+                                <event name="OnGridLabelLeftDClick"></event>
+                                <event name="OnGridLabelRightClick"></event>
+                                <event name="OnGridLabelRightDClick"></event>
+                                <event name="OnGridRangeSelect"></event>
+                                <event name="OnGridRowSize"></event>
+                                <event name="OnGridSelectCell"></event>
+                                <event name="OnKeyDown"></event>
+                                <event name="OnKeyUp"></event>
+                                <event name="OnKillFocus"></event>
+                                <event name="OnLeaveWindow"></event>
+                                <event name="OnLeftDClick"></event>
+                                <event name="OnLeftDown"></event>
+                                <event name="OnLeftUp"></event>
+                                <event name="OnMiddleDClick"></event>
+                                <event name="OnMiddleDown"></event>
+                                <event name="OnMiddleUp"></event>
+                                <event name="OnMotion"></event>
+                                <event name="OnMouseEvents"></event>
+                                <event name="OnMouseWheel"></event>
+                                <event name="OnPaint"></event>
+                                <event name="OnRightDClick"></event>
+                                <event name="OnRightDown"></event>
+                                <event name="OnRightUp"></event>
+                                <event name="OnSetFocus"></event>
+                                <event name="OnSize"></event>
+                                <event name="OnUpdateUI"></event>
+                            </object>
+                        </object>
+                    </object>
+                </object>
+                <object class="sizeritem" expanded="1">
+                    <property name="border">0</property>
+                    <property name="flag">wxALIGN_RIGHT|wxALIGN_TOP</property>
+                    <property name="proportion">0</property>
+                    <object class="wxBoxSizer" expanded="1">
+                        <property name="minimum_size"></property>
+                        <property name="name">bButtonsSizer</property>
+                        <property name="orient">wxVERTICAL</property>
+                        <property name="permission">none</property>
+                        <object class="sizeritem" expanded="0">
+                            <property name="border">5</property>
+                            <property name="flag">wxALIGN_TOP|wxALL</property>
+                            <property name="proportion">0</property>
+                            <object class="wxBitmapButton" expanded="0">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="bitmap"></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="current"></property>
+                                <property name="default">0</property>
+                                <property name="default_pane">0</property>
+                                <property name="disabled"></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="focus"></property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">Move Up</property>
+                                <property name="margins"></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">32,32</property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_moveUpButton</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="position"></property>
+                                <property name="pressed"></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="OnAux1DClick"></event>
+                                <event name="OnAux1Down"></event>
+                                <event name="OnAux1Up"></event>
+                                <event name="OnAux2DClick"></event>
+                                <event name="OnAux2Down"></event>
+                                <event name="OnAux2Up"></event>
+                                <event name="OnButtonClick">OnMoveUpButtonClick</event>
+                                <event name="OnChar"></event>
+                                <event name="OnCharHook"></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">wxALL</property>
+                            <property name="proportion">0</property>
+                            <object class="wxBitmapButton" 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="bitmap"></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="current"></property>
+                                <property name="default">0</property>
+                                <property name="default_pane">0</property>
+                                <property name="disabled"></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="focus"></property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">Move Down</property>
+                                <property name="margins"></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">32,32</property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_moveDownButton</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="position"></property>
+                                <property name="pressed"></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="OnAux1DClick"></event>
+                                <event name="OnAux1Down"></event>
+                                <event name="OnAux1Up"></event>
+                                <event name="OnAux2DClick"></event>
+                                <event name="OnAux2Down"></event>
+                                <event name="OnAux2Up"></event>
+                                <event name="OnButtonClick">OnMoveDownButtonClick</event>
+                                <event name="OnChar"></event>
+                                <event name="OnCharHook"></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">wxALL</property>
+                            <property name="proportion">0</property>
+                            <object class="wxBitmapButton" 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="bitmap"></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="current"></property>
+                                <property name="default">0</property>
+                                <property name="default_pane">0</property>
+                                <property name="disabled"></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="focus"></property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">Reload Plugins</property>
+                                <property name="margins"></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">32,32</property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_reloadButton</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="position"></property>
+                                <property name="pressed"></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="OnAux1DClick"></event>
+                                <event name="OnAux1Down"></event>
+                                <event name="OnAux1Up"></event>
+                                <event name="OnAux2DClick"></event>
+                                <event name="OnAux2Down"></event>
+                                <event name="OnAux2Up"></event>
+                                <event name="OnButtonClick">OnReloadButtonClick</event>
+                                <event name="OnChar"></event>
+                                <event name="OnCharHook"></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>
+</wxFormBuilder_Project>
diff --git a/pcbnew/dialogs/panel_pcbnew_action_plugins_base.h b/pcbnew/dialogs/panel_pcbnew_action_plugins_base.h
new file mode 100644
index 0000000000..8766d0c95e
--- /dev/null
+++ b/pcbnew/dialogs/panel_pcbnew_action_plugins_base.h
@@ -0,0 +1,58 @@
+///////////////////////////////////////////////////////////////////////////
+// C++ code generated with wxFormBuilder (version Jul 11 2018)
+// http://www.wxformbuilder.org/
+//
+// PLEASE DO *NOT* EDIT THIS FILE!
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef __PANEL_PCBNEW_ACTION_PLUGINS_BASE_H__
+#define __PANEL_PCBNEW_ACTION_PLUGINS_BASE_H__
+
+#include <wx/artprov.h>
+#include <wx/xrc/xmlres.h>
+class WX_GRID;
+
+#include <wx/colour.h>
+#include <wx/settings.h>
+#include <wx/string.h>
+#include <wx/font.h>
+#include <wx/grid.h>
+#include <wx/gdicmn.h>
+#include <wx/sizer.h>
+#include <wx/bmpbuttn.h>
+#include <wx/bitmap.h>
+#include <wx/image.h>
+#include <wx/icon.h>
+#include <wx/button.h>
+#include <wx/panel.h>
+
+///////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+/// Class PANEL_PCBNEW_ACTION_PLUGINS_BASE
+///////////////////////////////////////////////////////////////////////////////
+class PANEL_PCBNEW_ACTION_PLUGINS_BASE : public wxPanel 
+{
+	private:
+	
+	protected:
+		WX_GRID* m_grid;
+		wxBitmapButton* m_moveUpButton;
+		wxBitmapButton* m_moveDownButton;
+		wxBitmapButton* m_reloadButton;
+		
+		// Virtual event handlers, overide them in your derived class
+		virtual void OnGridCellClick( wxGridEvent& event ) { event.Skip(); }
+		virtual void OnMoveUpButtonClick( wxCommandEvent& event ) { event.Skip(); }
+		virtual void OnMoveDownButtonClick( wxCommandEvent& event ) { event.Skip(); }
+		virtual void OnReloadButtonClick( wxCommandEvent& event ) { event.Skip(); }
+		
+	
+	public:
+		
+		PANEL_PCBNEW_ACTION_PLUGINS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 485,200 ), long style = wxTAB_TRAVERSAL ); 
+		~PANEL_PCBNEW_ACTION_PLUGINS_BASE();
+	
+};
+
+#endif //__PANEL_PCBNEW_ACTION_PLUGINS_BASE_H__
diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp
index e8bbb886d2..5c4a8d5eb5 100644
--- a/pcbnew/pcb_edit_frame.cpp
+++ b/pcbnew/pcb_edit_frame.cpp
@@ -1243,6 +1243,8 @@ void PCB_EDIT_FRAME::PythonPluginsReload()
         // Action plugins can be modified, therefore the plugins menu
         // must be updated:
         RebuildActionPluginMenus();
+        // Recreate top toolbar to add action plugin buttons
+        ReCreateHToolbar();
     #endif
 #endif
 }
diff --git a/pcbnew/pcb_edit_frame.h b/pcbnew/pcb_edit_frame.h
index 4179353573..4788841580 100644
--- a/pcbnew/pcb_edit_frame.h
+++ b/pcbnew/pcb_edit_frame.h
@@ -25,13 +25,14 @@
 #define  WXPCB_STRUCT_H_
 
 #include <unordered_map>
+#include <map>
 #include "pcb_base_edit_frame.h"
 #include "config_params.h"
 #include "undo_redo_container.h"
 #include "zones.h"
 
-
 /*  Forward declarations of classes. */
+class ACTION_PLUGIN;
 class PCB_SCREEN;
 class BOARD;
 class BOARD_COMMIT;
@@ -117,16 +118,36 @@ protected:
 #if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
     /**
      * Function RebuildActionPluginMenus
-     * Fill action menu with all registred action plugins
+     * Fill action menu with all registered action plugins
      */
     void RebuildActionPluginMenus();
 
     /**
-     * Function OnActionPlugin
+     * Function AddActionPluginTools
+     * Append action plugin buttons to main toolbar
+     */
+    void AddActionPluginTools();
+
+	/**
+	 * Function RunActionPlugin
+	 * Executes action plugin's Run() method and updates undo buffer
+	 * @param aActionPlugin action plugin
+	 */
+	void RunActionPlugin( ACTION_PLUGIN* aActionPlugin );
+
+    /**
+     * Function OnActionPluginMenu
      * Launched by the menu when an action is called
      * @param aEvent sent by wx
      */
-    void OnActionPlugin( wxCommandEvent& aEvent);
+    void OnActionPluginMenu( wxCommandEvent& aEvent);
+
+    /**
+     * Function OnActionPluginButton
+     * Launched by the button when an action is called
+     * @param aEvent sent by wx
+     */
+    void OnActionPluginButton( wxCommandEvent& aEvent );
 
     /**
      * Function OnActionPluginRefresh
@@ -379,6 +400,36 @@ public:
     // Configurations:
     void Process_Config( wxCommandEvent& event );
 
+#if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
+
+    /**
+     * Function SetActionPluginSettings
+     * Set a set of plugins that have visible buttons on toolbar
+     * Plugins are identified by their module path
+     */
+    void SetActionPluginSettings( const std::vector< std::pair<wxString, wxString> >& aPluginsWithButtons );
+
+    /**
+     * Function GetActionPluginSettings
+     * Get a set of plugins that have visible buttons on toolbar
+     */
+    std::vector< std::pair<wxString, wxString> > GetActionPluginSettings();
+
+    /**
+     * Function GetActionPluginButtonVisible
+     * Returns true if button visibility action plugin setting was set to true
+     * or it is unset and plugin defaults to true.
+     */
+    bool GetActionPluginButtonVisible( const wxString& aPluginPath, bool aPluginDefault );
+
+    /**
+     * Function GetOrderedActionPlugins
+     * Returns ordered list of plugins in sequence in which they should appear on toolbar or in settings
+     */
+    std::vector<ACTION_PLUGIN*> GetOrderedActionPlugins();
+
+#endif
+
     /**
      * Function GetProjectFileParameters
      * returns a project file parameter list for Pcbnew.
diff --git a/pcbnew/pcb_general_settings.cpp b/pcbnew/pcb_general_settings.cpp
index a8c70cd0aa..96c525ad3a 100644
--- a/pcbnew/pcb_general_settings.cpp
+++ b/pcbnew/pcb_general_settings.cpp
@@ -22,6 +22,7 @@
  */
 
 #include <pcb_general_settings.h>
+#include <wx/tokenzr.h>
 
 PCB_GENERAL_SETTINGS::PCB_GENERAL_SETTINGS( FRAME_T aFrameType )
     : m_frameType( aFrameType ), m_colorsSettings( aFrameType )
@@ -54,6 +55,32 @@ PCB_GENERAL_SETTINGS::PCB_GENERAL_SETTINGS( FRAME_T aFrameType )
 void PCB_GENERAL_SETTINGS::Load( wxConfigBase* aCfg )
 {
     m_colorsSettings.Load( aCfg );
+
+#if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
+
+    m_pluginSettings.clear();
+
+    wxString pluginSettings = aCfg->Read( "ActionPluginButtons" );
+
+    wxStringTokenizer pluginSettingsTokenizer = wxStringTokenizer( pluginSettings, ";" );
+
+    while( pluginSettingsTokenizer.HasMoreTokens() )
+    {
+        wxString plugin = pluginSettingsTokenizer.GetNextToken();
+        wxStringTokenizer pluginTokenizer = wxStringTokenizer( plugin, "=" );
+
+        if( pluginTokenizer.CountTokens() != 2 )
+        {
+            // Bad config
+            continue;
+        }
+
+        plugin = pluginTokenizer.GetNextToken();
+        m_pluginSettings.push_back( std::make_pair( plugin, pluginTokenizer.GetNextToken() ) );
+    }
+
+#endif
+
     SETTINGS::Load( aCfg );
 }
 
@@ -61,6 +88,24 @@ void PCB_GENERAL_SETTINGS::Load( wxConfigBase* aCfg )
 void PCB_GENERAL_SETTINGS::Save( wxConfigBase* aCfg )
 {
     m_colorsSettings.Save( aCfg );
+
+#if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
+
+    wxString pluginSettings;
+
+    for( auto const& entry : m_pluginSettings )
+    {
+        if( !pluginSettings.IsEmpty() )
+        {
+            pluginSettings = pluginSettings + wxT( ";" );
+        }
+        pluginSettings = pluginSettings + entry.first + wxT( "=" ) + entry.second;
+    }
+
+    aCfg->Write( "ActionPluginButtons" , pluginSettings );
+
+#endif
+
     SETTINGS::Save( aCfg );
 }
 
diff --git a/pcbnew/pcb_general_settings.h b/pcbnew/pcb_general_settings.h
index bc8f953690..fbcbe90ef1 100644
--- a/pcbnew/pcb_general_settings.h
+++ b/pcbnew/pcb_general_settings.h
@@ -25,6 +25,7 @@
 #define __PCBNEW_GENERAL_SETTINGS_H
 
 #include <colors_design_settings.h>
+#include <vector>
 
 class wxConfigBase;
 class wxString;
@@ -65,6 +66,10 @@ public:
     MAGNETIC_PAD_OPTION_VALUES  m_magneticPads  = CAPTURE_CURSOR_IN_TRACK_TOOL;
     MAGNETIC_PAD_OPTION_VALUES  m_magneticTracks = CAPTURE_CURSOR_IN_TRACK_TOOL;
 
+#if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
+    std::vector< std::pair<wxString, wxString> > m_pluginSettings;  // Settings for action plugins
+#endif
+
 protected:
     const FRAME_T m_frameType;
     COLORS_DESIGN_SETTINGS m_colorsSettings;
diff --git a/pcbnew/pcbnew_config.cpp b/pcbnew/pcbnew_config.cpp
index ae164fd4cc..b6025a526d 100644
--- a/pcbnew/pcbnew_config.cpp
+++ b/pcbnew/pcbnew_config.cpp
@@ -45,6 +45,7 @@
 #include <panel_hotkeys_editor.h>
 #include <panel_pcbnew_settings.h>
 #include <panel_pcbnew_display_options.h>
+#include <panel_pcbnew_action_plugins.h>
 #include <fp_lib_table.h>
 #include <worksheet_shape_builder.h>
 #include <class_board.h>
@@ -97,6 +98,9 @@ void PCB_EDIT_FRAME::InstallPreferences( PAGED_DIALOG* aParent )
 
     book->AddPage( new PANEL_PCBNEW_SETTINGS( this, aParent ), _( "Pcbnew" ) );
     book->AddSubPage( new PANEL_PCBNEW_DISPLAY_OPTIONS( this, aParent ), _( "Display Options" ) );
+#if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
+    book->AddSubPage( new PANEL_PCBNEW_ACTION_PLUGINS( this, aParent ), _( "Action Plugins" ) );
+#endif
 }
 
 
diff --git a/pcbnew/swig/pcbnew_action_plugins.cpp b/pcbnew/swig/pcbnew_action_plugins.cpp
index 4fb232ea8f..0de652664f 100644
--- a/pcbnew/swig/pcbnew_action_plugins.cpp
+++ b/pcbnew/swig/pcbnew_action_plugins.cpp
@@ -140,6 +140,32 @@ wxString PYTHON_ACTION_PLUGIN::GetDescription()
 }
 
 
+bool PYTHON_ACTION_PLUGIN::GetShowToolbarButton()
+{
+    PyLOCK lock;
+
+    PyObject* result = CallMethod( "GetShowToolbarButton");
+
+    return PyObject_IsTrue(result);
+}
+
+
+wxString PYTHON_ACTION_PLUGIN::GetIconFileName()
+{
+    PyLOCK lock;
+
+    return CallRetStrMethod( "GetIconFileName" );
+}
+
+
+wxString PYTHON_ACTION_PLUGIN::GetPluginPath()
+{
+    PyLOCK lock;
+
+    return CallRetStrMethod( "GetPluginPath" );
+}
+
+
 void PYTHON_ACTION_PLUGIN::Run()
 {
     PyLOCK lock;
@@ -164,217 +190,226 @@ void PYTHON_ACTION_PLUGINS::register_action( PyObject* aPyAction )
 
 void PYTHON_ACTION_PLUGINS::deregister_action( PyObject* aPyAction )
 {
-    // deregister also destroyes the previously created "PYTHON_ACTION_PLUGIN object"
+    // deregister also destroys the previously created "PYTHON_ACTION_PLUGIN object"
     ACTION_PLUGINS::deregister_object( (void*) aPyAction );
 }
 
 
 #if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
 
-void PCB_EDIT_FRAME::OnActionPlugin( wxCommandEvent& aEvent )
+void PCB_EDIT_FRAME::OnActionPluginMenu( wxCommandEvent& aEvent )
 {
-    int id = aEvent.GetId();
+    ACTION_PLUGIN* actionPlugin = ACTION_PLUGINS::GetActionByMenu( aEvent.GetId() );
 
-    ACTION_PLUGIN* actionPlugin = ACTION_PLUGINS::GetActionByMenu( id );
+	if( actionPlugin )
+        RunActionPlugin( actionPlugin );
+}
 
-    if( actionPlugin )
+void PCB_EDIT_FRAME::OnActionPluginButton( wxCommandEvent& aEvent )
+{
+	ACTION_PLUGIN* actionPlugin = ACTION_PLUGINS::GetActionByButton( aEvent.GetId() );
+
+	if( actionPlugin )
+        RunActionPlugin( actionPlugin );
+}
+
+void PCB_EDIT_FRAME::RunActionPlugin( ACTION_PLUGIN* aActionPlugin )
+{
+    PICKED_ITEMS_LIST itemsList;
+    BOARD*  currentPcb  = GetBoard();
+    bool    fromEmpty   = false;
+
+    itemsList.m_Status = UR_CHANGED;
+
+    OnModify();
+
+    // Append tracks:
+    for( BOARD_ITEM* item = currentPcb->m_Track; item != NULL; item = item->Next() )
     {
-        PICKED_ITEMS_LIST itemsList;
-        BOARD*  currentPcb  = GetBoard();
-        bool    fromEmpty   = false;
+        ITEM_PICKER picker( item, UR_CHANGED );
+        itemsList.PushItem( picker );
+    }
 
-        itemsList.m_Status = UR_CHANGED;
+    // Append modules:
+    for( BOARD_ITEM* item = currentPcb->m_Modules; item != NULL; item = item->Next() )
+    {
+        ITEM_PICKER picker( item, UR_CHANGED );
+        itemsList.PushItem( picker );
+    }
 
-        OnModify();
+    // Append drawings
+    for( BOARD_ITEM* item = currentPcb->m_Drawings; item != NULL; item = item->Next() )
+    {
+        ITEM_PICKER picker( item, UR_CHANGED );
+        itemsList.PushItem( picker );
+    }
 
-        // Append tracks:
-        for( BOARD_ITEM* item = currentPcb->m_Track; item != NULL; item = item->Next() )
+    // Append zones outlines
+    for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
+    {
+        ITEM_PICKER picker( (EDA_ITEM*) currentPcb->GetArea(
+                        ii ), UR_CHANGED );
+        itemsList.PushItem( picker );
+    }
+
+    // Append zones segm:
+    for( BOARD_ITEM* item = currentPcb->m_SegZoneDeprecated; item != NULL; item = item->Next() )
+    {
+        ITEM_PICKER picker( item, UR_CHANGED );
+        itemsList.PushItem( picker );
+    }
+
+    if( itemsList.GetCount() > 0 )
+        SaveCopyInUndoList( itemsList, UR_CHANGED, wxPoint( 0.0, 0.0 ) );
+    else
+        fromEmpty = true;
+
+    itemsList.ClearItemsList();
+
+    // Execute plugin itself...
+    ACTION_PLUGINS::SetActionRunning( true );
+    aActionPlugin->Run();
+    ACTION_PLUGINS::SetActionRunning( false );
+
+    currentPcb->m_Status_Pcb = 0;
+
+    // Get back the undo buffer to fix some modifications
+    PICKED_ITEMS_LIST* oldBuffer = NULL;
+
+    if( fromEmpty )
+    {
+        oldBuffer = new PICKED_ITEMS_LIST();
+        oldBuffer->m_Status = UR_NEW;
+    }
+    else
+    {
+        oldBuffer = GetScreen()->PopCommandFromUndoList();
+        wxASSERT( oldBuffer );
+    }
+
+    // Try do discover what was modified
+
+    PICKED_ITEMS_LIST deletedItemsList;
+
+    // Found deleted modules
+    for( unsigned int i = 0; i < oldBuffer->GetCount(); i++ )
+    {
+        BOARD_ITEM* item = (BOARD_ITEM*) oldBuffer->GetPickedItem( i );
+        ITEM_PICKER picker( item, UR_DELETED );
+
+        wxASSERT( item );
+
+        switch( item->Type() )
         {
-            ITEM_PICKER picker( item, UR_CHANGED );
-            itemsList.PushItem( picker );
+        case PCB_NETINFO_T:
+        case PCB_MARKER_T:
+        case PCB_MODULE_T:
+        case PCB_TRACE_T:
+        case PCB_VIA_T:
+        case PCB_LINE_T:
+        case PCB_TEXT_T:
+        case PCB_DIMENSION_T:
+        case PCB_TARGET_T:
+        case PCB_ZONE_T:
+
+            // If item has a list it's mean that the element is on the board
+            if( item->GetList() == NULL )
+            {
+                deletedItemsList.PushItem( picker );
+            }
+
+            break;
+
+        case PCB_ZONE_AREA_T:
+        {
+            bool zoneFound = false;
+
+            for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
+                zoneFound |= currentPcb->GetArea( ii ) == item;
+
+            if( !zoneFound )
+            {
+                deletedItemsList.PushItem( picker );
+            }
+
+            break;
         }
 
-        // Append modules:
-        for( BOARD_ITEM* item = currentPcb->m_Modules; item != NULL; item = item->Next() )
-        {
-            ITEM_PICKER picker( item, UR_CHANGED );
-            itemsList.PushItem( picker );
+        default:
+            wxString msg;
+            msg.Printf( _( "(PCB_EDIT_FRAME::OnActionPlugin) needs work: "
+                                "BOARD_ITEM type (%d) not handled" ),
+                    item->Type() );
+            wxFAIL_MSG( msg );
+            break;
         }
+    }
 
-        // Append drawings
-        for( BOARD_ITEM* item = currentPcb->m_Drawings; item != NULL; item = item->Next() )
+    // Mark deleted elements in undolist
+    for( unsigned int i = 0; i < deletedItemsList.GetCount(); i++ )
+    {
+        oldBuffer->PushItem( deletedItemsList.GetItemWrapper( i ) );
+    }
+
+    // Find new modules
+    for( BOARD_ITEM* item = currentPcb->m_Modules; item != NULL; item = item->Next() )
+    {
+        if( !oldBuffer->ContainsItem( item ) )
         {
-            ITEM_PICKER picker( item, UR_CHANGED );
-            itemsList.PushItem( picker );
+            ITEM_PICKER picker( item, UR_NEW );
+            oldBuffer->PushItem( picker );
         }
+    }
 
-        // Append zones outlines
-        for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
+    for( BOARD_ITEM* item = currentPcb->m_Track; item != NULL; item = item->Next() )
+    {
+        if( !oldBuffer->ContainsItem( item ) )
+        {
+            ITEM_PICKER picker( item, UR_NEW );
+            oldBuffer->PushItem( picker );
+        }
+    }
+
+    for( BOARD_ITEM* item = currentPcb->m_Drawings; item != NULL; item = item->Next() )
+    {
+        if( !oldBuffer->ContainsItem( item ) )
+        {
+            ITEM_PICKER picker( item, UR_NEW );
+            oldBuffer->PushItem( picker );
+        }
+    }
+
+    for( BOARD_ITEM* item = currentPcb->m_SegZoneDeprecated; item != NULL; item = item->Next() )
+    {
+        if( !oldBuffer->ContainsItem( item ) )
+        {
+            ITEM_PICKER picker( item, UR_NEW );
+            oldBuffer->PushItem( picker );
+        }
+    }
+
+    for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
+    {
+        if( !oldBuffer->ContainsItem( (EDA_ITEM*) currentPcb->GetArea( ii ) ) )
         {
             ITEM_PICKER picker( (EDA_ITEM*) currentPcb->GetArea(
-                            ii ), UR_CHANGED );
-            itemsList.PushItem( picker );
-        }
-
-        // Append zones segm:
-        for( BOARD_ITEM* item = currentPcb->m_SegZoneDeprecated; item != NULL; item = item->Next() )
-        {
-            ITEM_PICKER picker( item, UR_CHANGED );
-            itemsList.PushItem( picker );
-        }
-
-        if( itemsList.GetCount() > 0 )
-            SaveCopyInUndoList( itemsList, UR_CHANGED, wxPoint( 0.0, 0.0 ) );
-        else
-            fromEmpty = true;
-
-        itemsList.ClearItemsList();
-
-        // Execute plugin himself...
-        ACTION_PLUGINS::SetActionRunning( true );
-        actionPlugin->Run();
-        ACTION_PLUGINS::SetActionRunning( false );
-
-        currentPcb->m_Status_Pcb = 0;
-
-        // Get back the undo buffer to fix some modifications
-        PICKED_ITEMS_LIST* oldBuffer = NULL;
-
-        if( fromEmpty )
-        {
-            oldBuffer = new PICKED_ITEMS_LIST();
-            oldBuffer->m_Status = UR_NEW;
-        }
-        else
-        {
-            oldBuffer = GetScreen()->PopCommandFromUndoList();
-            wxASSERT( oldBuffer );
-        }
-
-        // Try do discover what was modified
-
-        PICKED_ITEMS_LIST deletedItemsList;
-
-        // Found deleted modules
-        for( unsigned int i = 0; i < oldBuffer->GetCount(); i++ )
-        {
-            BOARD_ITEM* item = (BOARD_ITEM*) oldBuffer->GetPickedItem( i );
-            ITEM_PICKER picker( item, UR_DELETED );
-
-            wxASSERT( item );
-
-            switch( item->Type() )
-            {
-            case PCB_NETINFO_T:
-            case PCB_MARKER_T:
-            case PCB_MODULE_T:
-            case PCB_TRACE_T:
-            case PCB_VIA_T:
-            case PCB_LINE_T:
-            case PCB_TEXT_T:
-            case PCB_DIMENSION_T:
-            case PCB_TARGET_T:
-            case PCB_ZONE_T:
-
-                // If item has a list it's mean that the element is on the board
-                if( item->GetList() == NULL )
-                {
-                    deletedItemsList.PushItem( picker );
-                }
-
-                break;
-
-            case PCB_ZONE_AREA_T:
-            {
-                bool zoneFound = false;
-
-                for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
-                    zoneFound |= currentPcb->GetArea( ii ) == item;
-
-                if( !zoneFound )
-                {
-                    deletedItemsList.PushItem( picker );
-                }
-
-                break;
-            }
-
-            default:
-                wxString msg;
-                msg.Printf( _( "(PCB_EDIT_FRAME::OnActionPlugin) needs work: "
-                                 "BOARD_ITEM type (%d) not handled" ),
-                        item->Type() );
-                wxFAIL_MSG( msg );
-                break;
-            }
-        }
-
-        // Mark deleted elements in undolist
-        for( unsigned int i = 0; i < deletedItemsList.GetCount(); i++ )
-        {
-            oldBuffer->PushItem( deletedItemsList.GetItemWrapper( i ) );
-        }
-
-        // Find new modules
-        for( BOARD_ITEM* item = currentPcb->m_Modules; item != NULL; item = item->Next() )
-        {
-            if( !oldBuffer->ContainsItem( item ) )
-            {
-                ITEM_PICKER picker( item, UR_NEW );
-                oldBuffer->PushItem( picker );
-            }
-        }
-
-        for( BOARD_ITEM* item = currentPcb->m_Track; item != NULL; item = item->Next() )
-        {
-            if( !oldBuffer->ContainsItem( item ) )
-            {
-                ITEM_PICKER picker( item, UR_NEW );
-                oldBuffer->PushItem( picker );
-            }
-        }
-
-        for( BOARD_ITEM* item = currentPcb->m_Drawings; item != NULL; item = item->Next() )
-        {
-            if( !oldBuffer->ContainsItem( item ) )
-            {
-                ITEM_PICKER picker( item, UR_NEW );
-                oldBuffer->PushItem( picker );
-            }
-        }
-
-        for( BOARD_ITEM* item = currentPcb->m_SegZoneDeprecated; item != NULL; item = item->Next() )
-        {
-            if( !oldBuffer->ContainsItem( item ) )
-            {
-                ITEM_PICKER picker( item, UR_NEW );
-                oldBuffer->PushItem( picker );
-            }
-        }
-
-        for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
-        {
-            if( !oldBuffer->ContainsItem( (EDA_ITEM*) currentPcb->GetArea( ii ) ) )
-            {
-                ITEM_PICKER picker( (EDA_ITEM*) currentPcb->GetArea(
-                                ii ), UR_NEW );
-                oldBuffer->PushItem( picker );
-            }
+                            ii ), UR_NEW );
+            oldBuffer->PushItem( picker );
         }
+    }
 
 
-        GetScreen()->PushCommandToUndoList( oldBuffer );
+    GetScreen()->PushCommandToUndoList( oldBuffer );
 
-        if( IsGalCanvasActive() )
-        {
-            UseGalCanvas( GetGalCanvas() );
-        }
-        else
-        {
-            UpdateUserInterface();
-            GetScreen()->SetModify();
-            Refresh();
-        }
+    if( IsGalCanvasActive() )
+    {
+        UseGalCanvas( GetGalCanvas() );
+    }
+    else
+    {
+        UpdateUserInterface();
+        GetScreen()->SetModify();
+        Refresh();
     }
 }
 
@@ -409,30 +444,39 @@ void PCB_EDIT_FRAME::RebuildActionPluginMenus()
         // Remove menus which are not usable for our current plugin list
         Disconnect( item->GetId(), wxEVT_COMMAND_MENU_SELECTED,
                 (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &
-                PCB_EDIT_FRAME::OnActionPlugin );
+                PCB_EDIT_FRAME::OnActionPluginMenu );
         actionMenu->Delete( item );
     }
 
     for( int ii = 0; ii < ACTION_PLUGINS::GetActionsCount(); ii++ )
     {
         wxMenuItem* item;
+        ACTION_PLUGIN* ap = ACTION_PLUGINS::GetAction( ii );
+        const wxBitmap& bitmap = ap->iconBitmap.IsOk() ? ap->iconBitmap : KiBitmap( hammer_xpm );
 
         if( ii < (int) available_menus.size() )
         {
             item = available_menus[ii];
-            item->SetItemLabel( ACTION_PLUGINS::GetAction( ii )->GetName() );
-            item->SetHelp( ACTION_PLUGINS::GetAction( ii )->GetDescription() );
+            item->SetItemLabel( ap->GetName() );
+            item->SetHelp( ap->GetDescription() );
+
+            // On windows we need to set "unchecked" bitmap
+#if defined(__WXMSW__)
+            item->SetBitmap( bitmap, false );
+#else
+            item->SetBitmap( bitmap );
+#endif
         }
         else
         {
             item = AddMenuItem( actionMenu, wxID_ANY,
-                    ACTION_PLUGINS::GetAction( ii )->GetName(),
-                    ACTION_PLUGINS::GetAction( ii )->GetDescription(),
-                    KiBitmap( hammer_xpm ) );
+                    ap->GetName(),
+                    ap->GetDescription(),
+                    bitmap );
 
             Connect( item->GetId(), wxEVT_COMMAND_MENU_SELECTED,
                     (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &
-                    PCB_EDIT_FRAME::OnActionPlugin );
+                    PCB_EDIT_FRAME::OnActionPluginMenu );
         }
 
         ACTION_PLUGINS::SetActionMenu( ii, item->GetId() );
@@ -440,4 +484,104 @@ void PCB_EDIT_FRAME::RebuildActionPluginMenus()
 }
 
 
+void PCB_EDIT_FRAME::AddActionPluginTools()
+{
+    bool need_separator = true;
+    const auto& orderedPlugins = GetOrderedActionPlugins();
+
+    for( const auto& ap : orderedPlugins )
+    {
+        if( GetActionPluginButtonVisible( ap->GetPluginPath(), ap->GetShowToolbarButton() ) )
+        {
+
+            if ( need_separator )
+            {
+                KiScaledSeparator( m_mainToolBar, this );
+                need_separator = false;
+            }
+
+            // Add button
+            wxBitmap bitmap;
+
+            if ( ap->iconBitmap.IsOk() )
+                bitmap = KiScaledBitmap( ap->iconBitmap, this );
+            else
+                bitmap = KiScaledBitmap( hammer_xpm, this );
+
+            wxAuiToolBarItem* button = m_mainToolBar->AddTool(
+                    wxID_ANY, wxEmptyString, bitmap, ap->GetName() );
+
+            Connect( button->GetId(), wxEVT_COMMAND_MENU_SELECTED,
+                    (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &
+                    PCB_EDIT_FRAME::OnActionPluginButton );
+
+            // Link action plugin to button
+            ACTION_PLUGINS::SetActionButton( ap, button->GetId() );
+        }
+    }
+}
+
+
+void PCB_EDIT_FRAME::SetActionPluginSettings( const std::vector< std::pair<wxString, wxString> >& aPluginSettings )
+{
+    m_configSettings.m_pluginSettings = aPluginSettings;
+    ReCreateHToolbar();
+}
+
+
+std::vector< std::pair<wxString, wxString> > PCB_EDIT_FRAME::GetActionPluginSettings()
+{
+    return m_configSettings.m_pluginSettings;
+}
+
+
+std::vector<ACTION_PLUGIN*> PCB_EDIT_FRAME::GetOrderedActionPlugins()
+{
+    std::vector<ACTION_PLUGIN*> orderedPlugins;
+    const auto& pluginSettings = GetActionPluginSettings();
+
+    // First add plugins that have entries in settings
+    for( size_t ii = 0; ii < pluginSettings.size(); ii++ )
+    {
+        for( int jj = 0; jj < ACTION_PLUGINS::GetActionsCount(); jj++ )
+        {
+            if( ACTION_PLUGINS::GetAction( jj )->GetPluginPath() == pluginSettings[ii].first )
+                orderedPlugins.push_back( ACTION_PLUGINS::GetAction( jj ) );
+        }
+    }
+
+    // Now append new plugins that have not been configured yet
+    for( int ii = 0; ii < ACTION_PLUGINS::GetActionsCount(); ii++ )
+    {
+        bool found = false;
+
+        for( size_t jj = 0; jj < orderedPlugins.size(); jj++ )
+        {
+            if( ACTION_PLUGINS::GetAction( ii ) == orderedPlugins[jj] )
+                found = true;
+        }
+
+        if ( !found )
+            orderedPlugins.push_back( ACTION_PLUGINS::GetAction( ii ) );
+    }
+
+    return orderedPlugins;
+}
+
+
+bool PCB_EDIT_FRAME::GetActionPluginButtonVisible( const wxString& aPluginPath, bool aPluginDefault )
+{
+    auto& settings = m_configSettings.m_pluginSettings;
+
+    for(const auto& entry : settings )
+    {
+        if (entry.first == aPluginPath )
+            return entry.second == wxT( "Visible" );
+    }
+
+    // Plugin is not in settings, return default.
+    return  aPluginDefault;
+}
+
+
 #endif
diff --git a/pcbnew/swig/pcbnew_action_plugins.h b/pcbnew/swig/pcbnew_action_plugins.h
index 08dfa14e61..a1d6d9ff0e 100644
--- a/pcbnew/swig/pcbnew_action_plugins.h
+++ b/pcbnew/swig/pcbnew_action_plugins.h
@@ -47,6 +47,9 @@ public:
     wxString    GetCategoryName() override;
     wxString    GetName() override;
     wxString    GetDescription() override;
+    bool        GetShowToolbarButton() override;
+    wxString    GetIconFileName() override;
+    wxString    GetPluginPath() override;
     void        Run() override;
     void*       GetObject() override;
 };
diff --git a/pcbnew/tool_pcb_editor.cpp b/pcbnew/tool_pcb_editor.cpp
index 66b6223570..b5c64c3a10 100644
--- a/pcbnew/tool_pcb_editor.cpp
+++ b/pcbnew/tool_pcb_editor.cpp
@@ -321,6 +321,10 @@ void PCB_EDIT_FRAME::ReCreateHToolbar()
         m_mainToolBar->AddTool( ID_TOOLBARH_PCB_SCRIPTING_CONSOLE, wxEmptyString,
                                 KiScaledBitmap( py_script_xpm, this ),
                                 _( "Show/Hide the Python Scripting console" ), wxITEM_CHECK );
+
+#if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
+        AddActionPluginTools();
+#endif
     }
 #endif
 
diff --git a/scripting/kicadplugins.i b/scripting/kicadplugins.i
index 8c1781d50e..ebd8db2c86 100644
--- a/scripting/kicadplugins.i
+++ b/scripting/kicadplugins.i
@@ -260,7 +260,7 @@ def LoadPlugins(bundlepath=None):
             if module == '__init__.py' or module[-3:] != '.py':
                 continue
 
-            LoadOnePlugin(plugins_dir, module);
+            LoadOnePlugin(plugins_dir, module)
 
 
 class KiCadPlugin:
@@ -268,6 +268,9 @@ class KiCadPlugin:
         pass
 
     def register(self):
+        import inspect
+        import os
+
         if isinstance(self,FilePlugin):
             pass # register to file plugins in C++
 
@@ -276,6 +279,14 @@ class KiCadPlugin:
             return
 
         if isinstance(self,ActionPlugin):
+            """
+            Get path to .py or .pyc that has definition of plugin class.
+            If path is binary but source also exists, assume definition is in source.
+            """
+            self.__plugin_path = inspect.getfile(self.__class__)
+            if self.__plugin_path.endswith('.pyc') and os.path.isfile(self.__plugin_path[:-1]):
+                self.__plugin_path = self.__plugin_path[:-1]
+            self.__plugin_path = self.__plugin_path + '/' + self.__class__.__name__
             PYTHON_ACTION_PLUGINS.register_action(self)
             return
 
@@ -295,6 +306,9 @@ class KiCadPlugin:
 
         return
 
+    def GetPluginPath( self ):
+        return self.__plugin_path
+
 
 class FilePlugin(KiCadPlugin):
     def __init__(self):
@@ -633,6 +647,8 @@ class FootprintWizardPlugin(KiCadPlugin, object):
 class ActionPlugin(KiCadPlugin, object):
     def __init__( self ):
         KiCadPlugin.__init__( self )
+        self.icon_file_name = ""
+        self.show_toolbar_button = False
         self.defaults()
 
     def defaults( self ):
@@ -649,6 +665,12 @@ class ActionPlugin(KiCadPlugin, object):
     def GetDescription( self ):
         return self.description
 
+    def GetShowToolbarButton( self ):
+        return self.show_toolbar_button
+
+    def GetIconFileName( self ):
+        return self.icon_file_name
+
     def Run(self):
         return