Merge ~qu1ck/kicad:plugin-icon-squashed into ~kicad-product-committers/kicad:master

Proposed by Andrew Lutsenko
Status: Superseded
Proposed branch: ~qu1ck/kicad:plugin-icon-squashed
Merge into: ~kicad-product-committers/kicad:master
Diff against target: 2260 lines (+1637/-186)
21 files modified
common/bitmap.cpp (+19/-0)
common/widgets/grid_icon_text_helpers.cpp (+40/-1)
include/bitmap_types.h (+8/-0)
include/widgets/grid_icon_text_helpers.h (+18/-1)
pcbnew/CMakeLists.txt (+7/-2)
pcbnew/action_plugin.cpp (+44/-2)
pcbnew/action_plugin.h (+49/-11)
pcbnew/dialogs/panel_pcbnew_action_plugins.cpp (+199/-0)
pcbnew/dialogs/panel_pcbnew_action_plugins.h (+70/-0)
pcbnew/dialogs/panel_pcbnew_action_plugins_base.cpp (+98/-0)
pcbnew/dialogs/panel_pcbnew_action_plugins_base.fbp (+578/-0)
pcbnew/dialogs/panel_pcbnew_action_plugins_base.h (+58/-0)
pcbnew/pcb_edit_frame.cpp (+2/-0)
pcbnew/pcb_edit_frame.h (+55/-4)
pcbnew/pcb_general_settings.cpp (+45/-0)
pcbnew/pcb_general_settings.h (+5/-0)
pcbnew/pcbnew_config.cpp (+4/-0)
pcbnew/swig/pcbnew_action_plugins.cpp (+308/-164)
pcbnew/swig/pcbnew_action_plugins.h (+3/-0)
pcbnew/tool_pcb_editor.cpp (+4/-0)
scripting/kicadplugins.i (+23/-1)
Reviewer Review Type Date Requested Status
Seth Hillbrand Pending
Jeff Young Pending
Review via email: mp+353692@code.launchpad.net

This proposal supersedes a proposal from 2018-08-24.

This proposal has been superseded by a proposal from 2018-08-25.

Commit message

Add toolbar buttons for action plugins

Description of the change

This adds toolbar buttons for pcbnew action plugins and a corresponding settings dialog in preferences which allows you to choose which buttons to show and their order.

To post a comment you must log in.
Revision history for this message
Seth Hillbrand (sethh) wrote : Posted in a previous version of this proposal

Overall looks good. I haven't checked MacOS compatibility, so I tagged Jeff Young to comment.

The code style issues are scattered around, I think I found most all.

You should remove the unsafe/unused functions.

I don't think we should be mixing IDs between buttons and menus.

You can simplify your map usage to just a vector (O(n) is fine for searching this)

Can you squash these into a single commit (this is to allow git bisect without errors)?

review: Needs Fixing
Revision history for this message
Jeff Young (jeyjey) wrote : Posted in a previous version of this proposal

I didn't try to compile it yet as my tree's in the middle of something.

Revision history for this message
Andrew Lutsenko (qu1ck) wrote : Posted in a previous version of this proposal

Thanks for comments, I replied to all of them inline.

> Can you squash these into a single commit (this is to allow git bisect without errors)?

Can launchpad not do it on merge? If so I think I'll have to open separate merge request in the end. Will LP review system tolerate rewritten commit history?

Revision history for this message
Andrew Lutsenko (qu1ck) wrote :

I found a way to squash commits and resubmit current proposal.

Revision history for this message
Jeff Young (jeyjey) wrote :

A few more comments for you, Andrew. Looking good!

Revision history for this message
Wayne Stambaugh (stambaughw) wrote : Posted in a previous version of this proposal
Download full text (17.6 KiB)

On 08/23/2018 05:01 PM, Seth Hillbrand wrote:
> Review: Needs Fixing
>
> Overall looks good. I haven't checked MacOS compatibility, so I tagged Jeff Young to comment.
>
> The code style issues are scattered around, I think I found most all.
>
> You should remove the unsafe/unused functions.
>
> I don't think we should be mixing IDs between buttons and menus.

@Seth, event IDs for menu and toolbar actions should be the same. In
fact wxMENU_EVENT is the same as wxTOOL_EVENT. It's merely a #define in
wxwidgets. Please use the same event ID for the toolbar and the menu
for running the same script or you will be creating a maintenance issue.

>
> You can simplify your map usage to just a vector (O(n) is fine for searching this)
>
> Can you squash these into a single commit (this is to allow git bisect without errors)?
>
> Diff comments:
>
>> diff --git a/common/widgets/grid_icon_text_helpers.cpp b/common/widgets/grid_icon_text_helpers.cpp
>> index 8d348c7..feb04bb 100644
>> --- a/common/widgets/grid_icon_text_helpers.cpp
>> +++ b/common/widgets/grid_icon_text_helpers.cpp
>> @@ -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,
>
> Whitespace here around parentheses
>
>> + 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)
>
> Whitespace here around parentheses.
>
>> +{
>> + 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/widgets/grid_icon_text_helpers.h b/include/widgets/grid_icon_text_helpers.h
>> index 9e14829..e859f11 100644
>> --- a/include/widgets/grid_icon_text_helpers.h
>> +++ b/include/widgets/grid_icon_text_helpers.h
>> @@ -49,6 +49,23 @@ private:
>> const wxArrayString& m_names;
>> };
>>
>> +//---- Grid helpers: custom wxGridCellRenderer that re...

Revision history for this message
Seth Hillbrand (sethh) wrote : Posted in a previous version of this proposal

@Wayne- My comment here was unclear. The IDs are generated by wx when the menu items are added and a different ID is generated when the toolbar button is added. However, they are linked in the same callback handler, checking first for the menu button ID in the event and then, failing that, for the toolbar button ID. My concern was that separating the event handling based on wx's autogenerated IDs makes an assumption about the IDs. I'd feel better about that line if the handler split based on an event type and then did the ID lookup in the related list.

Revision history for this message
Wayne Stambaugh (stambaughw) wrote : Posted in a previous version of this proposal

@Seth, got it. I don't have any further issues.

Revision history for this message
Andrew Lutsenko (qu1ck) wrote :

Some comments got lost after I force pushed an update. That's why I didn't want to squash commits early.

@Seth
I split event handler for buttons and menus. They call common function. Is that what you had in mind?

@Jeff
I addressed your comments. About pythons calls: they don't throw, they return errors and there is also PyErr_Occurred() which checks for python exception state. It's done in CallMethod()

Also if you are worried about backward compatibility with old plugins I made sure that they still work. New fields in ActionPlugin class are set by default so plugin writers don't have to change anything unless they want to add a custom icon.

Revision history for this message
Andrew Lutsenko (qu1ck) :

Unmerged commits

41192c0... by Andrew Lutsenko

Add toolbar buttons for action plugins

Add icons to action menu items as well

Fix backwards compatibility for plugins that don't define icon

Fix compilation errors for action plugin icon code

Fixes compilation with actio menu option off.
Fixes compilation error on non windows platforms.

Make showing toolbar button optional for action plugins

Add "Action Plugins" panel stub in pcbnew settings

Add action plugin grid form and stub

Load action plugin list in pcbnew prefs

Add category, description and path to action plugin grid

Store and read action plugin settings display buttons accordingly

Add action plugin ordering buttons in preferences

Don't show error on icon load failure

Fix build with actions menu disabled

Adjust action plugins grid properties

Fix bug with plugin order when one is removed and another added

Also fix formatting

Scale plugin icons on toolbar

Address review comments

Removed unused methods, switched to std::vector from map where possible,
fixed formatting and spelling.

Address another round of comments

Also append class name to plugin path to avoid issues with multiple classes in single plugin file

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/common/bitmap.cpp b/common/bitmap.cpp
2index 031b2cc..bce8344 100644
3--- a/common/bitmap.cpp
4+++ b/common/bitmap.cpp
5@@ -144,6 +144,25 @@ wxBitmap KiScaledBitmap( BITMAP_DEF aBitmap, EDA_BASE_FRAME* aWindow )
6 }
7
8
9+wxBitmap KiScaledBitmap( const wxBitmap& aBitmap, EDA_BASE_FRAME* aWindow )
10+{
11+ const int scale = get_scale_factor( aWindow );
12+
13+ if( scale == 4)
14+ {
15+ return wxBitmap( aBitmap );
16+ }
17+ else
18+ {
19+ wxImage image = aBitmap.ConvertToImage();
20+ image.Rescale( scale * image.GetWidth() / 4, scale * image.GetHeight() / 4,
21+ wxIMAGE_QUALITY_BILINEAR );
22+
23+ return wxBitmap( image );
24+ }
25+}
26+
27+
28 void KiScaledSeparator( wxAuiToolBar* aToolbar, EDA_BASE_FRAME* aWindow )
29 {
30 const int scale = get_scale_factor( aWindow );
31diff --git a/common/widgets/grid_icon_text_helpers.cpp b/common/widgets/grid_icon_text_helpers.cpp
32index 8d348c7..b1f949e 100644
33--- a/common/widgets/grid_icon_text_helpers.cpp
34+++ b/common/widgets/grid_icon_text_helpers.cpp
35@@ -27,7 +27,7 @@
36 #include <wx/dc.h>
37
38
39-//---- Grid helpers: custom wxGridCellRenderer ------------------------------------------
40+//---- Grid helpers: custom wxGridCellRenderer that renders icon and a label ------------
41
42
43 GRID_CELL_ICON_TEXT_RENDERER::GRID_CELL_ICON_TEXT_RENDERER( const std::vector<BITMAP_DEF>& icons,
44@@ -68,6 +68,45 @@ void GRID_CELL_ICON_TEXT_RENDERER::Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, w
45 aGrid.DrawTextRectangle( aDC, value, rect, wxALIGN_LEFT, wxALIGN_CENTRE );
46 }
47
48+//---- Grid helpers: custom wxGridCellRenderer that renders just an icon ----------------
49+//
50+// Note: this renderer is supposed to be used with read only cells
51+
52+GRID_CELL_ICON_RENDERER::GRID_CELL_ICON_RENDERER(const wxBitmap& icon) : m_icon( icon )
53+{
54+}
55+
56+
57+void GRID_CELL_ICON_RENDERER::Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC,
58+ const wxRect& aRect, int aRow, int aCol, bool isSelected )
59+{
60+ wxRect rect = aRect;
61+ rect.Inflate( -1 );
62+
63+ // erase background
64+ wxGridCellRenderer::Draw( aGrid, aAttr, aDC, aRect, aRow, aCol, isSelected );
65+
66+ // Draw icon
67+ if( m_icon.IsOk() )
68+ {
69+ aDC.DrawBitmap( m_icon,
70+ rect.GetLeft() + ( rect.GetWidth() - m_icon.GetWidth() ) / 2,
71+ rect.GetTop() + ( rect.GetHeight() - m_icon.GetHeight() ) / 2,
72+ true );
73+ }
74+}
75+
76+
77+wxSize GRID_CELL_ICON_RENDERER::GetBestSize( wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, int row, int col )
78+{
79+ return wxSize( m_icon.GetWidth() + 6, m_icon.GetHeight() + 4 );
80+}
81+
82+
83+wxGridCellRenderer* GRID_CELL_ICON_RENDERER::Clone() const
84+{
85+ return new GRID_CELL_ICON_RENDERER( m_icon );
86+}
87
88
89 //---- Grid helpers: custom wxGridCellEditor ------------------------------------------
90diff --git a/include/bitmap_types.h b/include/bitmap_types.h
91index 8657df4..f703ddb 100644
92--- a/include/bitmap_types.h
93+++ b/include/bitmap_types.h
94@@ -73,6 +73,14 @@ wxBitmap KiBitmap( BITMAP_DEF aBitmap );
95 */
96 wxBitmap KiScaledBitmap( BITMAP_DEF aBitmap, EDA_BASE_FRAME* aWindow );
97
98+/**
99+ * Function KiScaledBitmap
100+ * Overload of the above function that takes another wxBitmap as a parameter
101+ *
102+ * @param aBitmap bitmap definition
103+ * @param aWindow target window for scaling context
104+ */
105+wxBitmap KiScaledBitmap( const wxBitmap& aBitmap, EDA_BASE_FRAME* aWindow );
106
107 /**
108 * Function KiScaledSeparator
109diff --git a/include/widgets/grid_icon_text_helpers.h b/include/widgets/grid_icon_text_helpers.h
110index 9e14829..60ea612 100644
111--- a/include/widgets/grid_icon_text_helpers.h
112+++ b/include/widgets/grid_icon_text_helpers.h
113@@ -34,7 +34,7 @@
114 class wxGrid;
115
116
117-//---- Grid helpers: custom wxGridCellRenderer ------------------------------------------
118+//---- Grid helpers: custom wxGridCellRenderer that renders icon and a label ------------
119
120 class GRID_CELL_ICON_TEXT_RENDERER : public wxGridCellStringRenderer
121 {
122@@ -49,6 +49,23 @@ private:
123 const wxArrayString& m_names;
124 };
125
126+//---- Grid helpers: custom wxGridCellRenderer that renders just an icon ----------------
127+//
128+// Note: use with read only cells
129+
130+class GRID_CELL_ICON_RENDERER : public wxGridCellRenderer
131+{
132+public:
133+ GRID_CELL_ICON_RENDERER( const wxBitmap& icon );
134+
135+ void Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC,
136+ const wxRect& aRect, int aRow, int aCol, bool isSelected ) override;
137+ wxSize GetBestSize( wxGrid & grid, wxGridCellAttr & attr, wxDC & dc, int row, int col ) override;
138+ wxGridCellRenderer* Clone() const override;
139+
140+private:
141+ const wxBitmap& m_icon;
142+};
143
144 //---- Grid helpers: custom wxGridCellEditor ------------------------------------------
145 //
146diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt
147index a77f8eb..2202080 100644
148--- a/pcbnew/CMakeLists.txt
149+++ b/pcbnew/CMakeLists.txt
150@@ -152,8 +152,6 @@ set( PCBNEW_DIALOGS
151 dialogs/panel_modedit_settings_base.cpp
152 dialogs/panel_pcbnew_display_options.cpp
153 dialogs/panel_pcbnew_display_options_base.cpp
154- dialogs/panel_pcbnew_display_options.cpp
155- dialogs/panel_pcbnew_display_options_base.cpp
156 dialogs/panel_pcbnew_settings.cpp
157 dialogs/panel_pcbnew_settings_base.cpp
158 dialogs/panel_setup_mask_and_paste.cpp
159@@ -174,6 +172,13 @@ set( PCBNEW_DIALOGS
160 ${GITHUB_3DLIBRARIES_WIZARD}
161 )
162
163+if( KICAD_SCRIPTING AND KICAD_SCRIPTING_ACTION_MENU )
164+ set( PCBNEW_DIALOGS ${PCBNEW_DIALOGS}
165+ dialogs/panel_pcbnew_action_plugins.cpp
166+ dialogs/panel_pcbnew_action_plugins_base.cpp
167+ )
168+endif()
169+
170 set( PCBNEW_IMPORT_DXF
171 import_dxf/dialog_dxf_import.cpp
172 import_dxf/dialog_dxf_import_base.cpp
173diff --git a/pcbnew/action_plugin.cpp b/pcbnew/action_plugin.cpp
174index c10d0fa..e79ea29 100644
175--- a/pcbnew/action_plugin.cpp
176+++ b/pcbnew/action_plugin.cpp
177@@ -73,9 +73,37 @@ void ACTION_PLUGINS::SetActionMenu( int aIndex, int idMenu )
178 }
179
180
181-int ACTION_PLUGINS::GetActionMenu( int aIndex )
182+ACTION_PLUGIN* ACTION_PLUGINS::GetActionByButton( int aButton )
183 {
184- return m_actionsList[aIndex]->m_actionMenuId;
185+ int max = GetActionsCount();
186+
187+ for( int i = 0; i < max; i++ )
188+ {
189+ if( m_actionsList[i]->m_actionButtonId == aButton )
190+ return m_actionsList[i];
191+ }
192+
193+ return NULL;
194+}
195+
196+
197+void ACTION_PLUGINS::SetActionButton( ACTION_PLUGIN* aAction, int idButton )
198+{
199+ aAction->m_actionButtonId = idButton;
200+}
201+
202+
203+ACTION_PLUGIN* ACTION_PLUGINS::GetActionByPath(const wxString& aPath)
204+{
205+ for( int i = 0; i < GetActionsCount() ; i++ )
206+ {
207+ if( m_actionsList[i]->GetPluginPath() == aPath)
208+ {
209+ return m_actionsList[i];
210+ }
211+ }
212+
213+ return NULL;
214 }
215
216
217@@ -127,6 +155,20 @@ void ACTION_PLUGINS::register_action( ACTION_PLUGIN* aAction )
218 }
219 }
220
221+ // Load icon if supplied
222+ if (!aAction->GetIconFileName().IsEmpty())
223+ {
224+ {
225+ wxLogNull eat_errors;
226+ aAction->iconBitmap.LoadFile( aAction->GetIconFileName() , wxBITMAP_TYPE_PNG );
227+ }
228+
229+ if ( !aAction->iconBitmap.IsOk() )
230+ {
231+ wxLogVerbose( "Failed to load icon " + aAction->GetIconFileName() + " for action plugin " );
232+ }
233+ }
234+
235 m_actionsList.push_back( aAction );
236 }
237
238diff --git a/pcbnew/action_plugin.h b/pcbnew/action_plugin.h
239index ff3ed14..8bb6c9c 100644
240--- a/pcbnew/action_plugin.h
241+++ b/pcbnew/action_plugin.h
242@@ -44,9 +44,16 @@ public:
243 // m_actionMenuId set to 0 means the corresponding menuitem to call this
244 // action is not yet created
245 int m_actionMenuId;
246+ // Same for button id
247+ int m_actionButtonId;
248+ // Icon for the action button and menu entry
249+ wxBitmap iconBitmap;
250+ // If show_on_toolbar is true a button will be added to top toolbar
251+ bool show_on_toolbar;
252
253 public:
254- ACTION_PLUGIN() : m_actionMenuId( 0 ) {}
255+ ACTION_PLUGIN() : m_actionMenuId( 0 ), m_actionButtonId( 0 ),
256+ show_on_toolbar( false ) {}
257 virtual ~ACTION_PLUGIN();
258
259 /**
260@@ -69,6 +76,24 @@ public:
261 virtual wxString GetDescription() = 0;
262
263 /**
264+ * Function GetShowToolbarButton
265+ * @return true if button should be shown on top toolbar
266+ */
267+ virtual bool GetShowToolbarButton() = 0;
268+
269+ /**
270+ * Function GetIconFileName
271+ * @return a path to icon for the action plugin button
272+ */
273+ virtual wxString GetIconFileName() = 0;
274+
275+ /**
276+ * Function GetPluginPath
277+ * @return a path this plugin was loaded from
278+ */
279+ virtual wxString GetPluginPath() = 0;
280+
281+ /**
282 * Function GetObject
283 * This method gets the pointer to the object from where this action constructs
284 * @return it's a void pointer, as it could be a PyObject or any other
285@@ -137,16 +162,6 @@ public:
286 */
287 static void SetActionMenu( int aIndex, int idMenu );
288
289-
290- /**
291- * Function GetActionMenu
292- * Provide menu id for a plugin index
293- * @param aIndex is the action index
294- * @return associated menuitem id
295- */
296- static int GetActionMenu( int aIndex );
297-
298-
299 /**
300 * Function GetActionByMenu
301 * find action plugin associated to a menu id
302@@ -155,6 +170,29 @@ public:
303 */
304 static ACTION_PLUGIN* GetActionByMenu( int aMenu );
305
306+ /**
307+ * Function SetActionButton
308+ * Associate a button id to an action plugin
309+ * @param aAction is the action
310+ * @param idButton is the associated menuitem id
311+ */
312+ static void SetActionButton( ACTION_PLUGIN* aAction, int idButton );
313+
314+ /**
315+ * Function GetActionByButton
316+ * find action plugin associated to a button id
317+ * @param aButton is the button id (defined with SetActionButton)
318+ * @return the associated ACTION_PLUGIN (or null if not found)
319+ */
320+ static ACTION_PLUGIN* GetActionByButton( int aButton );
321+
322+ /**
323+ * Function GetActionByPath
324+ * find action plugin by module path
325+ * @param aPath the path of plugin
326+ * @return the corresponding ACTION_PLUGIN (or null if not found)
327+ */
328+ static ACTION_PLUGIN* GetActionByPath( const wxString& aPath );
329
330 /**
331 * Function GetAction
332diff --git a/pcbnew/dialogs/panel_pcbnew_action_plugins.cpp b/pcbnew/dialogs/panel_pcbnew_action_plugins.cpp
333new file mode 100644
334index 0000000..3887fe9
335--- /dev/null
336+++ b/pcbnew/dialogs/panel_pcbnew_action_plugins.cpp
337@@ -0,0 +1,199 @@
338+/*
339+ * This program source code file is part of KiCad, a free EDA CAD application.
340+ *
341+ * Copyright (C) 2018 Andrew Lutsenko, anlutsenko at gmail dot com
342+ * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
343+ *
344+ * This program is free software: you can redistribute it and/or modify it
345+ * under the terms of the GNU General Public License as published by the
346+ * Free Software Foundation, either version 3 of the License, or (at your
347+ * option) any later version.
348+ *
349+ * This program is distributed in the hope that it will be useful, but
350+ * WITHOUT ANY WARRANTY; without even the implied warranty of
351+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
352+ * General Public License for more details.
353+ *
354+ * You should have received a copy of the GNU General Public License along
355+ * with this program. If not, see <http://www.gnu.org/licenses/>.
356+ */
357+
358+#include <pcb_edit_frame.h>
359+#include <panel_pcbnew_action_plugins.h>
360+#include <widgets/paged_dialog.h>
361+#include <widgets/grid_icon_text_helpers.h>
362+#include <bitmaps.h>
363+#include <action_plugin.h>
364+#include <grid_tricks.h>
365+#include <widgets/wx_grid.h>
366+
367+
368+PANEL_PCBNEW_ACTION_PLUGINS::PANEL_PCBNEW_ACTION_PLUGINS( PCB_EDIT_FRAME* aFrame, PAGED_DIALOG* aWindow ) :
369+ PANEL_PCBNEW_ACTION_PLUGINS_BASE( aWindow->GetTreebook() ),
370+ m_frame( aFrame )
371+{
372+ m_genericIcon = KiBitmap( hammer_xpm );
373+ m_grid->PushEventHandler( new GRID_TRICKS( m_grid ) );
374+
375+ m_moveUpButton->SetBitmap( KiBitmap( up_xpm ) );
376+ m_moveDownButton->SetBitmap( KiBitmap( down_xpm ) );
377+ m_reloadButton->SetBitmap( KiBitmap( refresh_xpm ) );
378+}
379+
380+
381+PANEL_PCBNEW_ACTION_PLUGINS::~PANEL_PCBNEW_ACTION_PLUGINS()
382+{
383+ m_grid->PopEventHandler( true );
384+}
385+
386+
387+void PANEL_PCBNEW_ACTION_PLUGINS::OnGridCellClick( wxGridEvent& event )
388+{
389+ SelectRow( event.GetRow() );
390+}
391+
392+
393+void PANEL_PCBNEW_ACTION_PLUGINS::SelectRow( int aRow )
394+{
395+ m_grid->ClearSelection();
396+ m_grid->SelectRow( aRow );
397+}
398+
399+
400+void PANEL_PCBNEW_ACTION_PLUGINS::OnMoveUpButtonClick( wxCommandEvent& event )
401+{
402+ auto selectedRows = m_grid->GetSelectedRows();
403+
404+ // If nothing is selected or multiple rows are selected don't do anything.
405+ if( selectedRows.size() != 1 ) return;
406+
407+ int selectedRow = selectedRows[0];
408+
409+ // If first row is selected, then it can't go any further up.
410+ if( selectedRow == 0 )
411+ {
412+ wxBell();
413+ return;
414+ }
415+
416+ SwapRows( selectedRow, selectedRow - 1 );
417+
418+ SelectRow( selectedRow - 1 );
419+}
420+
421+
422+void PANEL_PCBNEW_ACTION_PLUGINS::OnMoveDownButtonClick( wxCommandEvent& event )
423+{
424+ auto selectedRows = m_grid->GetSelectedRows();
425+
426+ // If nothing is selected or multiple rows are selected don't do anything.
427+ if( selectedRows.size() != 1 ) return;
428+
429+ int selectedRow = selectedRows[0];
430+
431+ // If last row is selected, then it can't go any further down.
432+ if( selectedRow + 1 == m_grid->GetNumberRows() )
433+ {
434+ wxBell();
435+ return;
436+ }
437+
438+ SwapRows( selectedRow, selectedRow + 1 );
439+
440+ SelectRow( selectedRow + 1 );
441+}
442+
443+
444+void PANEL_PCBNEW_ACTION_PLUGINS::SwapRows( int aRowA, int aRowB )
445+{
446+ m_grid->Freeze();
447+
448+ // Swap all columns except icon
449+ wxString tempStr;
450+
451+ for( int column = 1; column < m_grid->GetNumberCols(); column++ )
452+ {
453+ tempStr = m_grid->GetCellValue( aRowA, column );
454+ m_grid->SetCellValue( aRowA, column, m_grid->GetCellValue( aRowB, column ) );
455+ m_grid->SetCellValue( aRowB, column, tempStr );
456+ }
457+
458+ // Swap icon column renderers
459+ auto cellRenderer = m_grid->GetCellRenderer( aRowA, COLUMN_ICON );
460+ m_grid->SetCellRenderer( aRowA, COLUMN_ICON, m_grid->GetCellRenderer( aRowB, COLUMN_ICON ) );
461+ m_grid->SetCellRenderer( aRowB, COLUMN_ICON, cellRenderer );
462+
463+ m_grid->Thaw();
464+}
465+
466+
467+void PANEL_PCBNEW_ACTION_PLUGINS::OnReloadButtonClick( wxCommandEvent& event )
468+{
469+ m_frame->PythonPluginsReload();
470+ TransferDataToWindow();
471+}
472+
473+
474+bool PANEL_PCBNEW_ACTION_PLUGINS::TransferDataFromWindow()
475+{
476+ std::vector< std::pair<wxString, wxString> > pluginSettings;
477+
478+ for( int ii = 0; ii < m_grid->GetNumberRows(); ii++ )
479+ {
480+ pluginSettings.push_back( std::make_pair(
481+ m_grid->GetCellValue( ii, COLUMN_PATH ),
482+ m_grid->GetCellValue( ii, COLUMN_VISIBLE ) == wxT("1") ? wxT( "Visible" ) : wxT( "Hidden" )
483+ ) );
484+ }
485+
486+ m_frame->SetActionPluginSettings( pluginSettings );
487+
488+ return true;
489+}
490+
491+
492+bool PANEL_PCBNEW_ACTION_PLUGINS::TransferDataToWindow()
493+{
494+ m_grid->Freeze();
495+ m_grid->DeleteRows( 0, m_grid->GetNumberRows() );
496+
497+ const auto& orderedPlugins = m_frame->GetOrderedActionPlugins();
498+ m_grid->AppendRows( orderedPlugins.size() );
499+
500+ for( size_t row = 0; row < orderedPlugins.size(); row++ )
501+ {
502+ ACTION_PLUGIN* ap = orderedPlugins[row];
503+
504+ // Icon
505+ m_grid->SetCellRenderer( row, COLUMN_ICON, new GRID_CELL_ICON_RENDERER(
506+ ap->iconBitmap.IsOk() ? ap->iconBitmap : m_genericIcon ) );
507+
508+ // Toolbar button checkbox
509+ m_grid->SetCellRenderer( row, COLUMN_VISIBLE, new wxGridCellBoolRenderer() );
510+ m_grid->SetCellAlignment( row, COLUMN_VISIBLE, wxALIGN_CENTER, wxALIGN_CENTER );
511+
512+ bool showButton = m_frame->GetActionPluginButtonVisible(
513+ ap->GetPluginPath(), ap->GetShowToolbarButton() );
514+
515+ m_grid->SetCellValue( row, COLUMN_VISIBLE, showButton ? wxT( "1" ) : wxEmptyString );
516+
517+ // Name
518+ m_grid->SetCellValue( row, COLUMN_NAME, ap->GetName() );
519+
520+ // Category
521+ m_grid->SetCellValue( row, COLUMN_CATEGORY, ap->GetCategoryName() );
522+
523+ // Description
524+ m_grid->SetCellValue( row, COLUMN_DESCRIPTION, ap->GetDescription() );
525+
526+ // Path
527+ m_grid->SetCellValue( row, COLUMN_PATH, ap->GetPluginPath() );
528+ }
529+
530+ m_grid->AutoSizeColumns();
531+ m_grid->AutoSizeRows();
532+
533+ m_grid->Thaw();
534+
535+ return true;
536+}
537diff --git a/pcbnew/dialogs/panel_pcbnew_action_plugins.h b/pcbnew/dialogs/panel_pcbnew_action_plugins.h
538new file mode 100644
539index 0000000..48e8dc0
540--- /dev/null
541+++ b/pcbnew/dialogs/panel_pcbnew_action_plugins.h
542@@ -0,0 +1,70 @@
543+/*
544+ * This program source code file is part of KiCad, a free EDA CAD application.
545+ *
546+ * Copyright (C) 2018 Andrew Lutsenko, anlutsenko at gmail dot com
547+ * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
548+ *
549+ * This program is free software: you can redistribute it and/or modify it
550+ * under the terms of the GNU General Public License as published by the
551+ * Free Software Foundation, either version 3 of the License, or (at your
552+ * option) any later version.
553+ *
554+ * This program is distributed in the hope that it will be useful, but
555+ * WITHOUT ANY WARRANTY; without even the implied warranty of
556+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
557+ * General Public License for more details.
558+ *
559+ * You should have received a copy of the GNU General Public License along
560+ * with this program. If not, see <http://www.gnu.org/licenses/>.
561+ */
562+
563+#include "panel_pcbnew_action_plugins_base.h"
564+
565+class PANEL_PCBNEW_ACTION_PLUGINS : public PANEL_PCBNEW_ACTION_PLUGINS_BASE
566+{
567+public:
568+ PANEL_PCBNEW_ACTION_PLUGINS ( PCB_EDIT_FRAME* aFrame, PAGED_DIALOG* aWindow );
569+
570+ bool TransferDataFromWindow() override;
571+ bool TransferDataToWindow() override;
572+ ~PANEL_PCBNEW_ACTION_PLUGINS() override;
573+
574+ /**
575+ * Selects a whole row
576+ */
577+ void OnGridCellClick( wxGridEvent& event ) override;
578+
579+ /**
580+ * Moves plugin up in the grid
581+ */
582+ void OnMoveUpButtonClick( wxCommandEvent& event ) override;
583+
584+ /**
585+ * Moves plugin down in the grid
586+ */
587+ void OnMoveDownButtonClick( wxCommandEvent& event ) override;
588+
589+ /**
590+ * Reloads plugins and updates grid
591+ */
592+ void OnReloadButtonClick( wxCommandEvent& event ) override;
593+
594+private:
595+
596+ enum GRID_COLUMNS
597+ {
598+ COLUMN_ICON,
599+ COLUMN_VISIBLE,
600+ COLUMN_NAME,
601+ COLUMN_CATEGORY,
602+ COLUMN_DESCRIPTION,
603+ COLUMN_PATH
604+ };
605+
606+ PCB_EDIT_FRAME* m_frame;
607+ wxBitmap m_genericIcon;
608+
609+ void SwapRows( int aRowA, int aRowB );
610+ void SelectRow( int aRow );
611+};
612+
613diff --git a/pcbnew/dialogs/panel_pcbnew_action_plugins_base.cpp b/pcbnew/dialogs/panel_pcbnew_action_plugins_base.cpp
614new file mode 100644
615index 0000000..d8f8909
616--- /dev/null
617+++ b/pcbnew/dialogs/panel_pcbnew_action_plugins_base.cpp
618@@ -0,0 +1,98 @@
619+///////////////////////////////////////////////////////////////////////////
620+// C++ code generated with wxFormBuilder (version Jul 11 2018)
621+// http://www.wxformbuilder.org/
622+//
623+// PLEASE DO *NOT* EDIT THIS FILE!
624+///////////////////////////////////////////////////////////////////////////
625+
626+#include "widgets/wx_grid.h"
627+
628+#include "panel_pcbnew_action_plugins_base.h"
629+
630+///////////////////////////////////////////////////////////////////////////
631+
632+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 )
633+{
634+ wxBoxSizer* bPanelSizer;
635+ bPanelSizer = new wxBoxSizer( wxHORIZONTAL );
636+
637+ wxBoxSizer* bGridSizer;
638+ bGridSizer = new wxBoxSizer( wxVERTICAL );
639+
640+ m_grid = new WX_GRID( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_SIMPLE );
641+
642+ // Grid
643+ m_grid->CreateGrid( 3, 6 );
644+ m_grid->EnableEditing( false );
645+ m_grid->EnableGridLines( true );
646+ m_grid->EnableDragGridSize( false );
647+ m_grid->SetMargins( 0, 0 );
648+
649+ // Columns
650+ m_grid->AutoSizeColumns();
651+ m_grid->EnableDragColMove( false );
652+ m_grid->EnableDragColSize( true );
653+ m_grid->SetColLabelSize( 22 );
654+ m_grid->SetColLabelValue( 0, wxT("Icon") );
655+ m_grid->SetColLabelValue( 1, wxT("Show button") );
656+ m_grid->SetColLabelValue( 2, wxT("Name") );
657+ m_grid->SetColLabelValue( 3, wxT("Category") );
658+ m_grid->SetColLabelValue( 4, wxT("Description") );
659+ m_grid->SetColLabelValue( 5, wxT("Path") );
660+ m_grid->SetColLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE );
661+
662+ // Rows
663+ m_grid->EnableDragRowSize( true );
664+ m_grid->SetRowLabelSize( 0 );
665+ m_grid->SetRowLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE );
666+
667+ // Label Appearance
668+
669+ // Cell Defaults
670+ m_grid->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_CENTRE );
671+ bGridSizer->Add( m_grid, 1, wxALL|wxEXPAND, 5 );
672+
673+
674+ bPanelSizer->Add( bGridSizer, 1, wxALIGN_LEFT|wxEXPAND|wxLEFT, 0 );
675+
676+ wxBoxSizer* bButtonsSizer;
677+ bButtonsSizer = new wxBoxSizer( wxVERTICAL );
678+
679+ m_moveUpButton = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
680+ m_moveUpButton->SetMinSize( wxSize( 32,32 ) );
681+
682+ bButtonsSizer->Add( m_moveUpButton, 0, wxALIGN_TOP|wxALL, 5 );
683+
684+ m_moveDownButton = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
685+ m_moveDownButton->SetMinSize( wxSize( 32,32 ) );
686+
687+ bButtonsSizer->Add( m_moveDownButton, 0, wxALL, 5 );
688+
689+ m_reloadButton = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
690+ m_reloadButton->SetMinSize( wxSize( 32,32 ) );
691+
692+ bButtonsSizer->Add( m_reloadButton, 0, wxALL, 5 );
693+
694+
695+ bPanelSizer->Add( bButtonsSizer, 0, wxALIGN_RIGHT|wxALIGN_TOP, 0 );
696+
697+
698+ this->SetSizer( bPanelSizer );
699+ this->Layout();
700+
701+ // Connect Events
702+ m_grid->Connect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnGridCellClick ), NULL, this );
703+ m_moveUpButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnMoveUpButtonClick ), NULL, this );
704+ m_moveDownButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnMoveDownButtonClick ), NULL, this );
705+ m_reloadButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnReloadButtonClick ), NULL, this );
706+}
707+
708+PANEL_PCBNEW_ACTION_PLUGINS_BASE::~PANEL_PCBNEW_ACTION_PLUGINS_BASE()
709+{
710+ // Disconnect Events
711+ m_grid->Disconnect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnGridCellClick ), NULL, this );
712+ m_moveUpButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnMoveUpButtonClick ), NULL, this );
713+ m_moveDownButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnMoveDownButtonClick ), NULL, this );
714+ m_reloadButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnReloadButtonClick ), NULL, this );
715+
716+}
717diff --git a/pcbnew/dialogs/panel_pcbnew_action_plugins_base.fbp b/pcbnew/dialogs/panel_pcbnew_action_plugins_base.fbp
718new file mode 100644
719index 0000000..6c91f1f
720--- /dev/null
721+++ b/pcbnew/dialogs/panel_pcbnew_action_plugins_base.fbp
722@@ -0,0 +1,578 @@
723+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
724+<wxFormBuilder_Project>
725+ <FileVersion major="1" minor="14" />
726+ <object class="Project" expanded="1">
727+ <property name="class_decoration"></property>
728+ <property name="code_generation">C++</property>
729+ <property name="disconnect_events">1</property>
730+ <property name="disconnect_mode">source_name</property>
731+ <property name="disconnect_php_events">0</property>
732+ <property name="disconnect_python_events">0</property>
733+ <property name="embedded_files_path">res</property>
734+ <property name="encoding">UTF-8</property>
735+ <property name="event_generation">connect</property>
736+ <property name="file">panel_pcbnew_action_plugins_base</property>
737+ <property name="first_id">1000</property>
738+ <property name="help_provider">none</property>
739+ <property name="indent_with_spaces">0</property>
740+ <property name="internationalize">0</property>
741+ <property name="name">PanelPcbnewActionPlugins</property>
742+ <property name="namespace"></property>
743+ <property name="path">.</property>
744+ <property name="precompiled_header"></property>
745+ <property name="relative_path">1</property>
746+ <property name="skip_lua_events">1</property>
747+ <property name="skip_php_events">1</property>
748+ <property name="skip_python_events">1</property>
749+ <property name="ui_table">UI</property>
750+ <property name="use_enum">1</property>
751+ <property name="use_microsoft_bom">0</property>
752+ <object class="Panel" expanded="1">
753+ <property name="aui_managed">0</property>
754+ <property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
755+ <property name="bg"></property>
756+ <property name="context_help"></property>
757+ <property name="context_menu">1</property>
758+ <property name="enabled">1</property>
759+ <property name="event_handler">impl_virtual</property>
760+ <property name="fg"></property>
761+ <property name="font"></property>
762+ <property name="hidden">0</property>
763+ <property name="id">wxID_ANY</property>
764+ <property name="maximum_size"></property>
765+ <property name="minimum_size"></property>
766+ <property name="name">PANEL_PCBNEW_ACTION_PLUGINS_BASE</property>
767+ <property name="pos"></property>
768+ <property name="size">485,200</property>
769+ <property name="subclass">; forward_declare</property>
770+ <property name="tooltip"></property>
771+ <property name="window_extra_style"></property>
772+ <property name="window_name"></property>
773+ <property name="window_style">wxTAB_TRAVERSAL</property>
774+ <event name="OnAuiPaneActivated"></event>
775+ <event name="OnAuiPaneButton"></event>
776+ <event name="OnAuiPaneClose"></event>
777+ <event name="OnAuiPaneMaximize"></event>
778+ <event name="OnAuiPaneRestore"></event>
779+ <event name="OnAuiRender"></event>
780+ <event name="OnAux1DClick"></event>
781+ <event name="OnAux1Down"></event>
782+ <event name="OnAux1Up"></event>
783+ <event name="OnAux2DClick"></event>
784+ <event name="OnAux2Down"></event>
785+ <event name="OnAux2Up"></event>
786+ <event name="OnChar"></event>
787+ <event name="OnCharHook"></event>
788+ <event name="OnEnterWindow"></event>
789+ <event name="OnEraseBackground"></event>
790+ <event name="OnInitDialog"></event>
791+ <event name="OnKeyDown"></event>
792+ <event name="OnKeyUp"></event>
793+ <event name="OnKillFocus"></event>
794+ <event name="OnLeaveWindow"></event>
795+ <event name="OnLeftDClick"></event>
796+ <event name="OnLeftDown"></event>
797+ <event name="OnLeftUp"></event>
798+ <event name="OnMiddleDClick"></event>
799+ <event name="OnMiddleDown"></event>
800+ <event name="OnMiddleUp"></event>
801+ <event name="OnMotion"></event>
802+ <event name="OnMouseEvents"></event>
803+ <event name="OnMouseWheel"></event>
804+ <event name="OnPaint"></event>
805+ <event name="OnRightDClick"></event>
806+ <event name="OnRightDown"></event>
807+ <event name="OnRightUp"></event>
808+ <event name="OnSetFocus"></event>
809+ <event name="OnSize"></event>
810+ <event name="OnUpdateUI"></event>
811+ <object class="wxBoxSizer" expanded="1">
812+ <property name="minimum_size"></property>
813+ <property name="name">bPanelSizer</property>
814+ <property name="orient">wxHORIZONTAL</property>
815+ <property name="permission">none</property>
816+ <object class="sizeritem" expanded="1">
817+ <property name="border">0</property>
818+ <property name="flag">wxALIGN_LEFT|wxEXPAND|wxLEFT</property>
819+ <property name="proportion">1</property>
820+ <object class="wxBoxSizer" expanded="1">
821+ <property name="minimum_size"></property>
822+ <property name="name">bGridSizer</property>
823+ <property name="orient">wxVERTICAL</property>
824+ <property name="permission">none</property>
825+ <object class="sizeritem" expanded="1">
826+ <property name="border">5</property>
827+ <property name="flag">wxALL|wxEXPAND</property>
828+ <property name="proportion">1</property>
829+ <object class="wxGrid" expanded="1">
830+ <property name="BottomDockable">1</property>
831+ <property name="LeftDockable">1</property>
832+ <property name="RightDockable">1</property>
833+ <property name="TopDockable">1</property>
834+ <property name="aui_layer"></property>
835+ <property name="aui_name"></property>
836+ <property name="aui_position"></property>
837+ <property name="aui_row"></property>
838+ <property name="autosize_cols">1</property>
839+ <property name="autosize_rows">0</property>
840+ <property name="best_size"></property>
841+ <property name="bg"></property>
842+ <property name="caption"></property>
843+ <property name="caption_visible">1</property>
844+ <property name="cell_bg"></property>
845+ <property name="cell_font"></property>
846+ <property name="cell_horiz_alignment">wxALIGN_LEFT</property>
847+ <property name="cell_text"></property>
848+ <property name="cell_vert_alignment">wxALIGN_CENTRE</property>
849+ <property name="center_pane">0</property>
850+ <property name="close_button">1</property>
851+ <property name="col_label_horiz_alignment">wxALIGN_CENTRE</property>
852+ <property name="col_label_size">22</property>
853+ <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>
854+ <property name="col_label_vert_alignment">wxALIGN_CENTRE</property>
855+ <property name="cols">6</property>
856+ <property name="column_sizes"></property>
857+ <property name="context_help"></property>
858+ <property name="context_menu">1</property>
859+ <property name="default_pane">0</property>
860+ <property name="dock">Dock</property>
861+ <property name="dock_fixed">0</property>
862+ <property name="docking">Left</property>
863+ <property name="drag_col_move">0</property>
864+ <property name="drag_col_size">1</property>
865+ <property name="drag_grid_size">0</property>
866+ <property name="drag_row_size">1</property>
867+ <property name="editing">0</property>
868+ <property name="enabled">1</property>
869+ <property name="fg"></property>
870+ <property name="floatable">1</property>
871+ <property name="font"></property>
872+ <property name="grid_line_color"></property>
873+ <property name="grid_lines">1</property>
874+ <property name="gripper">0</property>
875+ <property name="hidden">0</property>
876+ <property name="id">wxID_ANY</property>
877+ <property name="label_bg"></property>
878+ <property name="label_font"></property>
879+ <property name="label_text"></property>
880+ <property name="margin_height">0</property>
881+ <property name="margin_width">0</property>
882+ <property name="max_size"></property>
883+ <property name="maximize_button">0</property>
884+ <property name="maximum_size"></property>
885+ <property name="min_size"></property>
886+ <property name="minimize_button">0</property>
887+ <property name="minimum_size"></property>
888+ <property name="moveable">1</property>
889+ <property name="name">m_grid</property>
890+ <property name="pane_border">1</property>
891+ <property name="pane_position"></property>
892+ <property name="pane_size"></property>
893+ <property name="permission">protected</property>
894+ <property name="pin_button">1</property>
895+ <property name="pos"></property>
896+ <property name="resize">Resizable</property>
897+ <property name="row_label_horiz_alignment">wxALIGN_CENTRE</property>
898+ <property name="row_label_size">0</property>
899+ <property name="row_label_values"></property>
900+ <property name="row_label_vert_alignment">wxALIGN_CENTRE</property>
901+ <property name="row_sizes"></property>
902+ <property name="rows">3</property>
903+ <property name="show">1</property>
904+ <property name="size"></property>
905+ <property name="subclass">WX_GRID; widgets/wx_grid.h; forward_declare</property>
906+ <property name="toolbar_pane">0</property>
907+ <property name="tooltip"></property>
908+ <property name="window_extra_style"></property>
909+ <property name="window_name"></property>
910+ <property name="window_style">wxBORDER_SIMPLE</property>
911+ <event name="OnAux1DClick"></event>
912+ <event name="OnAux1Down"></event>
913+ <event name="OnAux1Up"></event>
914+ <event name="OnAux2DClick"></event>
915+ <event name="OnAux2Down"></event>
916+ <event name="OnAux2Up"></event>
917+ <event name="OnChar"></event>
918+ <event name="OnCharHook"></event>
919+ <event name="OnEnterWindow"></event>
920+ <event name="OnEraseBackground"></event>
921+ <event name="OnGridCellChange"></event>
922+ <event name="OnGridCellLeftClick">OnGridCellClick</event>
923+ <event name="OnGridCellLeftDClick"></event>
924+ <event name="OnGridCellRightClick"></event>
925+ <event name="OnGridCellRightDClick"></event>
926+ <event name="OnGridCmdCellChange"></event>
927+ <event name="OnGridCmdCellLeftClick"></event>
928+ <event name="OnGridCmdCellLeftDClick"></event>
929+ <event name="OnGridCmdCellRightClick"></event>
930+ <event name="OnGridCmdCellRightDClick"></event>
931+ <event name="OnGridCmdColSize"></event>
932+ <event name="OnGridCmdEditorCreated"></event>
933+ <event name="OnGridCmdEditorHidden"></event>
934+ <event name="OnGridCmdEditorShown"></event>
935+ <event name="OnGridCmdLabelLeftClick"></event>
936+ <event name="OnGridCmdLabelLeftDClick"></event>
937+ <event name="OnGridCmdLabelRightClick"></event>
938+ <event name="OnGridCmdLabelRightDClick"></event>
939+ <event name="OnGridCmdRangeSelect"></event>
940+ <event name="OnGridCmdRowSize"></event>
941+ <event name="OnGridCmdSelectCell"></event>
942+ <event name="OnGridColSize"></event>
943+ <event name="OnGridEditorCreated"></event>
944+ <event name="OnGridEditorHidden"></event>
945+ <event name="OnGridEditorShown"></event>
946+ <event name="OnGridLabelLeftClick"></event>
947+ <event name="OnGridLabelLeftDClick"></event>
948+ <event name="OnGridLabelRightClick"></event>
949+ <event name="OnGridLabelRightDClick"></event>
950+ <event name="OnGridRangeSelect"></event>
951+ <event name="OnGridRowSize"></event>
952+ <event name="OnGridSelectCell"></event>
953+ <event name="OnKeyDown"></event>
954+ <event name="OnKeyUp"></event>
955+ <event name="OnKillFocus"></event>
956+ <event name="OnLeaveWindow"></event>
957+ <event name="OnLeftDClick"></event>
958+ <event name="OnLeftDown"></event>
959+ <event name="OnLeftUp"></event>
960+ <event name="OnMiddleDClick"></event>
961+ <event name="OnMiddleDown"></event>
962+ <event name="OnMiddleUp"></event>
963+ <event name="OnMotion"></event>
964+ <event name="OnMouseEvents"></event>
965+ <event name="OnMouseWheel"></event>
966+ <event name="OnPaint"></event>
967+ <event name="OnRightDClick"></event>
968+ <event name="OnRightDown"></event>
969+ <event name="OnRightUp"></event>
970+ <event name="OnSetFocus"></event>
971+ <event name="OnSize"></event>
972+ <event name="OnUpdateUI"></event>
973+ </object>
974+ </object>
975+ </object>
976+ </object>
977+ <object class="sizeritem" expanded="1">
978+ <property name="border">0</property>
979+ <property name="flag">wxALIGN_RIGHT|wxALIGN_TOP</property>
980+ <property name="proportion">0</property>
981+ <object class="wxBoxSizer" expanded="1">
982+ <property name="minimum_size"></property>
983+ <property name="name">bButtonsSizer</property>
984+ <property name="orient">wxVERTICAL</property>
985+ <property name="permission">none</property>
986+ <object class="sizeritem" expanded="0">
987+ <property name="border">5</property>
988+ <property name="flag">wxALIGN_TOP|wxALL</property>
989+ <property name="proportion">0</property>
990+ <object class="wxBitmapButton" expanded="0">
991+ <property name="BottomDockable">1</property>
992+ <property name="LeftDockable">1</property>
993+ <property name="RightDockable">1</property>
994+ <property name="TopDockable">1</property>
995+ <property name="aui_layer"></property>
996+ <property name="aui_name"></property>
997+ <property name="aui_position"></property>
998+ <property name="aui_row"></property>
999+ <property name="best_size"></property>
1000+ <property name="bg"></property>
1001+ <property name="bitmap"></property>
1002+ <property name="caption"></property>
1003+ <property name="caption_visible">1</property>
1004+ <property name="center_pane">0</property>
1005+ <property name="close_button">1</property>
1006+ <property name="context_help"></property>
1007+ <property name="context_menu">1</property>
1008+ <property name="current"></property>
1009+ <property name="default">0</property>
1010+ <property name="default_pane">0</property>
1011+ <property name="disabled"></property>
1012+ <property name="dock">Dock</property>
1013+ <property name="dock_fixed">0</property>
1014+ <property name="docking">Left</property>
1015+ <property name="enabled">1</property>
1016+ <property name="fg"></property>
1017+ <property name="floatable">1</property>
1018+ <property name="focus"></property>
1019+ <property name="font"></property>
1020+ <property name="gripper">0</property>
1021+ <property name="hidden">0</property>
1022+ <property name="id">wxID_ANY</property>
1023+ <property name="label">Move Up</property>
1024+ <property name="margins"></property>
1025+ <property name="markup">0</property>
1026+ <property name="max_size"></property>
1027+ <property name="maximize_button">0</property>
1028+ <property name="maximum_size"></property>
1029+ <property name="min_size"></property>
1030+ <property name="minimize_button">0</property>
1031+ <property name="minimum_size">32,32</property>
1032+ <property name="moveable">1</property>
1033+ <property name="name">m_moveUpButton</property>
1034+ <property name="pane_border">1</property>
1035+ <property name="pane_position"></property>
1036+ <property name="pane_size"></property>
1037+ <property name="permission">protected</property>
1038+ <property name="pin_button">1</property>
1039+ <property name="pos"></property>
1040+ <property name="position"></property>
1041+ <property name="pressed"></property>
1042+ <property name="resize">Resizable</property>
1043+ <property name="show">1</property>
1044+ <property name="size"></property>
1045+ <property name="style"></property>
1046+ <property name="subclass">; forward_declare</property>
1047+ <property name="toolbar_pane">0</property>
1048+ <property name="tooltip"></property>
1049+ <property name="validator_data_type"></property>
1050+ <property name="validator_style">wxFILTER_NONE</property>
1051+ <property name="validator_type">wxDefaultValidator</property>
1052+ <property name="validator_variable"></property>
1053+ <property name="window_extra_style"></property>
1054+ <property name="window_name"></property>
1055+ <property name="window_style"></property>
1056+ <event name="OnAux1DClick"></event>
1057+ <event name="OnAux1Down"></event>
1058+ <event name="OnAux1Up"></event>
1059+ <event name="OnAux2DClick"></event>
1060+ <event name="OnAux2Down"></event>
1061+ <event name="OnAux2Up"></event>
1062+ <event name="OnButtonClick">OnMoveUpButtonClick</event>
1063+ <event name="OnChar"></event>
1064+ <event name="OnCharHook"></event>
1065+ <event name="OnEnterWindow"></event>
1066+ <event name="OnEraseBackground"></event>
1067+ <event name="OnKeyDown"></event>
1068+ <event name="OnKeyUp"></event>
1069+ <event name="OnKillFocus"></event>
1070+ <event name="OnLeaveWindow"></event>
1071+ <event name="OnLeftDClick"></event>
1072+ <event name="OnLeftDown"></event>
1073+ <event name="OnLeftUp"></event>
1074+ <event name="OnMiddleDClick"></event>
1075+ <event name="OnMiddleDown"></event>
1076+ <event name="OnMiddleUp"></event>
1077+ <event name="OnMotion"></event>
1078+ <event name="OnMouseEvents"></event>
1079+ <event name="OnMouseWheel"></event>
1080+ <event name="OnPaint"></event>
1081+ <event name="OnRightDClick"></event>
1082+ <event name="OnRightDown"></event>
1083+ <event name="OnRightUp"></event>
1084+ <event name="OnSetFocus"></event>
1085+ <event name="OnSize"></event>
1086+ <event name="OnUpdateUI"></event>
1087+ </object>
1088+ </object>
1089+ <object class="sizeritem" expanded="1">
1090+ <property name="border">5</property>
1091+ <property name="flag">wxALL</property>
1092+ <property name="proportion">0</property>
1093+ <object class="wxBitmapButton" expanded="1">
1094+ <property name="BottomDockable">1</property>
1095+ <property name="LeftDockable">1</property>
1096+ <property name="RightDockable">1</property>
1097+ <property name="TopDockable">1</property>
1098+ <property name="aui_layer"></property>
1099+ <property name="aui_name"></property>
1100+ <property name="aui_position"></property>
1101+ <property name="aui_row"></property>
1102+ <property name="best_size"></property>
1103+ <property name="bg"></property>
1104+ <property name="bitmap"></property>
1105+ <property name="caption"></property>
1106+ <property name="caption_visible">1</property>
1107+ <property name="center_pane">0</property>
1108+ <property name="close_button">1</property>
1109+ <property name="context_help"></property>
1110+ <property name="context_menu">1</property>
1111+ <property name="current"></property>
1112+ <property name="default">0</property>
1113+ <property name="default_pane">0</property>
1114+ <property name="disabled"></property>
1115+ <property name="dock">Dock</property>
1116+ <property name="dock_fixed">0</property>
1117+ <property name="docking">Left</property>
1118+ <property name="enabled">1</property>
1119+ <property name="fg"></property>
1120+ <property name="floatable">1</property>
1121+ <property name="focus"></property>
1122+ <property name="font"></property>
1123+ <property name="gripper">0</property>
1124+ <property name="hidden">0</property>
1125+ <property name="id">wxID_ANY</property>
1126+ <property name="label">Move Down</property>
1127+ <property name="margins"></property>
1128+ <property name="markup">0</property>
1129+ <property name="max_size"></property>
1130+ <property name="maximize_button">0</property>
1131+ <property name="maximum_size"></property>
1132+ <property name="min_size"></property>
1133+ <property name="minimize_button">0</property>
1134+ <property name="minimum_size">32,32</property>
1135+ <property name="moveable">1</property>
1136+ <property name="name">m_moveDownButton</property>
1137+ <property name="pane_border">1</property>
1138+ <property name="pane_position"></property>
1139+ <property name="pane_size"></property>
1140+ <property name="permission">protected</property>
1141+ <property name="pin_button">1</property>
1142+ <property name="pos"></property>
1143+ <property name="position"></property>
1144+ <property name="pressed"></property>
1145+ <property name="resize">Resizable</property>
1146+ <property name="show">1</property>
1147+ <property name="size"></property>
1148+ <property name="style"></property>
1149+ <property name="subclass">; forward_declare</property>
1150+ <property name="toolbar_pane">0</property>
1151+ <property name="tooltip"></property>
1152+ <property name="validator_data_type"></property>
1153+ <property name="validator_style">wxFILTER_NONE</property>
1154+ <property name="validator_type">wxDefaultValidator</property>
1155+ <property name="validator_variable"></property>
1156+ <property name="window_extra_style"></property>
1157+ <property name="window_name"></property>
1158+ <property name="window_style"></property>
1159+ <event name="OnAux1DClick"></event>
1160+ <event name="OnAux1Down"></event>
1161+ <event name="OnAux1Up"></event>
1162+ <event name="OnAux2DClick"></event>
1163+ <event name="OnAux2Down"></event>
1164+ <event name="OnAux2Up"></event>
1165+ <event name="OnButtonClick">OnMoveDownButtonClick</event>
1166+ <event name="OnChar"></event>
1167+ <event name="OnCharHook"></event>
1168+ <event name="OnEnterWindow"></event>
1169+ <event name="OnEraseBackground"></event>
1170+ <event name="OnKeyDown"></event>
1171+ <event name="OnKeyUp"></event>
1172+ <event name="OnKillFocus"></event>
1173+ <event name="OnLeaveWindow"></event>
1174+ <event name="OnLeftDClick"></event>
1175+ <event name="OnLeftDown"></event>
1176+ <event name="OnLeftUp"></event>
1177+ <event name="OnMiddleDClick"></event>
1178+ <event name="OnMiddleDown"></event>
1179+ <event name="OnMiddleUp"></event>
1180+ <event name="OnMotion"></event>
1181+ <event name="OnMouseEvents"></event>
1182+ <event name="OnMouseWheel"></event>
1183+ <event name="OnPaint"></event>
1184+ <event name="OnRightDClick"></event>
1185+ <event name="OnRightDown"></event>
1186+ <event name="OnRightUp"></event>
1187+ <event name="OnSetFocus"></event>
1188+ <event name="OnSize"></event>
1189+ <event name="OnUpdateUI"></event>
1190+ </object>
1191+ </object>
1192+ <object class="sizeritem" expanded="1">
1193+ <property name="border">5</property>
1194+ <property name="flag">wxALL</property>
1195+ <property name="proportion">0</property>
1196+ <object class="wxBitmapButton" expanded="1">
1197+ <property name="BottomDockable">1</property>
1198+ <property name="LeftDockable">1</property>
1199+ <property name="RightDockable">1</property>
1200+ <property name="TopDockable">1</property>
1201+ <property name="aui_layer"></property>
1202+ <property name="aui_name"></property>
1203+ <property name="aui_position"></property>
1204+ <property name="aui_row"></property>
1205+ <property name="best_size"></property>
1206+ <property name="bg"></property>
1207+ <property name="bitmap"></property>
1208+ <property name="caption"></property>
1209+ <property name="caption_visible">1</property>
1210+ <property name="center_pane">0</property>
1211+ <property name="close_button">1</property>
1212+ <property name="context_help"></property>
1213+ <property name="context_menu">1</property>
1214+ <property name="current"></property>
1215+ <property name="default">0</property>
1216+ <property name="default_pane">0</property>
1217+ <property name="disabled"></property>
1218+ <property name="dock">Dock</property>
1219+ <property name="dock_fixed">0</property>
1220+ <property name="docking">Left</property>
1221+ <property name="enabled">1</property>
1222+ <property name="fg"></property>
1223+ <property name="floatable">1</property>
1224+ <property name="focus"></property>
1225+ <property name="font"></property>
1226+ <property name="gripper">0</property>
1227+ <property name="hidden">0</property>
1228+ <property name="id">wxID_ANY</property>
1229+ <property name="label">Reload Plugins</property>
1230+ <property name="margins"></property>
1231+ <property name="markup">0</property>
1232+ <property name="max_size"></property>
1233+ <property name="maximize_button">0</property>
1234+ <property name="maximum_size"></property>
1235+ <property name="min_size"></property>
1236+ <property name="minimize_button">0</property>
1237+ <property name="minimum_size">32,32</property>
1238+ <property name="moveable">1</property>
1239+ <property name="name">m_reloadButton</property>
1240+ <property name="pane_border">1</property>
1241+ <property name="pane_position"></property>
1242+ <property name="pane_size"></property>
1243+ <property name="permission">protected</property>
1244+ <property name="pin_button">1</property>
1245+ <property name="pos"></property>
1246+ <property name="position"></property>
1247+ <property name="pressed"></property>
1248+ <property name="resize">Resizable</property>
1249+ <property name="show">1</property>
1250+ <property name="size"></property>
1251+ <property name="style"></property>
1252+ <property name="subclass">; forward_declare</property>
1253+ <property name="toolbar_pane">0</property>
1254+ <property name="tooltip"></property>
1255+ <property name="validator_data_type"></property>
1256+ <property name="validator_style">wxFILTER_NONE</property>
1257+ <property name="validator_type">wxDefaultValidator</property>
1258+ <property name="validator_variable"></property>
1259+ <property name="window_extra_style"></property>
1260+ <property name="window_name"></property>
1261+ <property name="window_style"></property>
1262+ <event name="OnAux1DClick"></event>
1263+ <event name="OnAux1Down"></event>
1264+ <event name="OnAux1Up"></event>
1265+ <event name="OnAux2DClick"></event>
1266+ <event name="OnAux2Down"></event>
1267+ <event name="OnAux2Up"></event>
1268+ <event name="OnButtonClick">OnReloadButtonClick</event>
1269+ <event name="OnChar"></event>
1270+ <event name="OnCharHook"></event>
1271+ <event name="OnEnterWindow"></event>
1272+ <event name="OnEraseBackground"></event>
1273+ <event name="OnKeyDown"></event>
1274+ <event name="OnKeyUp"></event>
1275+ <event name="OnKillFocus"></event>
1276+ <event name="OnLeaveWindow"></event>
1277+ <event name="OnLeftDClick"></event>
1278+ <event name="OnLeftDown"></event>
1279+ <event name="OnLeftUp"></event>
1280+ <event name="OnMiddleDClick"></event>
1281+ <event name="OnMiddleDown"></event>
1282+ <event name="OnMiddleUp"></event>
1283+ <event name="OnMotion"></event>
1284+ <event name="OnMouseEvents"></event>
1285+ <event name="OnMouseWheel"></event>
1286+ <event name="OnPaint"></event>
1287+ <event name="OnRightDClick"></event>
1288+ <event name="OnRightDown"></event>
1289+ <event name="OnRightUp"></event>
1290+ <event name="OnSetFocus"></event>
1291+ <event name="OnSize"></event>
1292+ <event name="OnUpdateUI"></event>
1293+ </object>
1294+ </object>
1295+ </object>
1296+ </object>
1297+ </object>
1298+ </object>
1299+ </object>
1300+</wxFormBuilder_Project>
1301diff --git a/pcbnew/dialogs/panel_pcbnew_action_plugins_base.h b/pcbnew/dialogs/panel_pcbnew_action_plugins_base.h
1302new file mode 100644
1303index 0000000..8766d0c
1304--- /dev/null
1305+++ b/pcbnew/dialogs/panel_pcbnew_action_plugins_base.h
1306@@ -0,0 +1,58 @@
1307+///////////////////////////////////////////////////////////////////////////
1308+// C++ code generated with wxFormBuilder (version Jul 11 2018)
1309+// http://www.wxformbuilder.org/
1310+//
1311+// PLEASE DO *NOT* EDIT THIS FILE!
1312+///////////////////////////////////////////////////////////////////////////
1313+
1314+#ifndef __PANEL_PCBNEW_ACTION_PLUGINS_BASE_H__
1315+#define __PANEL_PCBNEW_ACTION_PLUGINS_BASE_H__
1316+
1317+#include <wx/artprov.h>
1318+#include <wx/xrc/xmlres.h>
1319+class WX_GRID;
1320+
1321+#include <wx/colour.h>
1322+#include <wx/settings.h>
1323+#include <wx/string.h>
1324+#include <wx/font.h>
1325+#include <wx/grid.h>
1326+#include <wx/gdicmn.h>
1327+#include <wx/sizer.h>
1328+#include <wx/bmpbuttn.h>
1329+#include <wx/bitmap.h>
1330+#include <wx/image.h>
1331+#include <wx/icon.h>
1332+#include <wx/button.h>
1333+#include <wx/panel.h>
1334+
1335+///////////////////////////////////////////////////////////////////////////
1336+
1337+///////////////////////////////////////////////////////////////////////////////
1338+/// Class PANEL_PCBNEW_ACTION_PLUGINS_BASE
1339+///////////////////////////////////////////////////////////////////////////////
1340+class PANEL_PCBNEW_ACTION_PLUGINS_BASE : public wxPanel
1341+{
1342+ private:
1343+
1344+ protected:
1345+ WX_GRID* m_grid;
1346+ wxBitmapButton* m_moveUpButton;
1347+ wxBitmapButton* m_moveDownButton;
1348+ wxBitmapButton* m_reloadButton;
1349+
1350+ // Virtual event handlers, overide them in your derived class
1351+ virtual void OnGridCellClick( wxGridEvent& event ) { event.Skip(); }
1352+ virtual void OnMoveUpButtonClick( wxCommandEvent& event ) { event.Skip(); }
1353+ virtual void OnMoveDownButtonClick( wxCommandEvent& event ) { event.Skip(); }
1354+ virtual void OnReloadButtonClick( wxCommandEvent& event ) { event.Skip(); }
1355+
1356+
1357+ public:
1358+
1359+ 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 );
1360+ ~PANEL_PCBNEW_ACTION_PLUGINS_BASE();
1361+
1362+};
1363+
1364+#endif //__PANEL_PCBNEW_ACTION_PLUGINS_BASE_H__
1365diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp
1366index e8bbb88..5c4a8d5 100644
1367--- a/pcbnew/pcb_edit_frame.cpp
1368+++ b/pcbnew/pcb_edit_frame.cpp
1369@@ -1243,6 +1243,8 @@ void PCB_EDIT_FRAME::PythonPluginsReload()
1370 // Action plugins can be modified, therefore the plugins menu
1371 // must be updated:
1372 RebuildActionPluginMenus();
1373+ // Recreate top toolbar to add action plugin buttons
1374+ ReCreateHToolbar();
1375 #endif
1376 #endif
1377 }
1378diff --git a/pcbnew/pcb_edit_frame.h b/pcbnew/pcb_edit_frame.h
1379index 4179353..4788841 100644
1380--- a/pcbnew/pcb_edit_frame.h
1381+++ b/pcbnew/pcb_edit_frame.h
1382@@ -25,13 +25,14 @@
1383 #define WXPCB_STRUCT_H_
1384
1385 #include <unordered_map>
1386+#include <map>
1387 #include "pcb_base_edit_frame.h"
1388 #include "config_params.h"
1389 #include "undo_redo_container.h"
1390 #include "zones.h"
1391
1392-
1393 /* Forward declarations of classes. */
1394+class ACTION_PLUGIN;
1395 class PCB_SCREEN;
1396 class BOARD;
1397 class BOARD_COMMIT;
1398@@ -117,16 +118,36 @@ protected:
1399 #if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
1400 /**
1401 * Function RebuildActionPluginMenus
1402- * Fill action menu with all registred action plugins
1403+ * Fill action menu with all registered action plugins
1404 */
1405 void RebuildActionPluginMenus();
1406
1407 /**
1408- * Function OnActionPlugin
1409+ * Function AddActionPluginTools
1410+ * Append action plugin buttons to main toolbar
1411+ */
1412+ void AddActionPluginTools();
1413+
1414+ /**
1415+ * Function RunActionPlugin
1416+ * Executes action plugin's Run() method and updates undo buffer
1417+ * @param aActionPlugin action plugin
1418+ */
1419+ void RunActionPlugin( ACTION_PLUGIN* aActionPlugin );
1420+
1421+ /**
1422+ * Function OnActionPluginMenu
1423 * Launched by the menu when an action is called
1424 * @param aEvent sent by wx
1425 */
1426- void OnActionPlugin( wxCommandEvent& aEvent);
1427+ void OnActionPluginMenu( wxCommandEvent& aEvent);
1428+
1429+ /**
1430+ * Function OnActionPluginButton
1431+ * Launched by the button when an action is called
1432+ * @param aEvent sent by wx
1433+ */
1434+ void OnActionPluginButton( wxCommandEvent& aEvent );
1435
1436 /**
1437 * Function OnActionPluginRefresh
1438@@ -379,6 +400,36 @@ public:
1439 // Configurations:
1440 void Process_Config( wxCommandEvent& event );
1441
1442+#if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
1443+
1444+ /**
1445+ * Function SetActionPluginSettings
1446+ * Set a set of plugins that have visible buttons on toolbar
1447+ * Plugins are identified by their module path
1448+ */
1449+ void SetActionPluginSettings( const std::vector< std::pair<wxString, wxString> >& aPluginsWithButtons );
1450+
1451+ /**
1452+ * Function GetActionPluginSettings
1453+ * Get a set of plugins that have visible buttons on toolbar
1454+ */
1455+ std::vector< std::pair<wxString, wxString> > GetActionPluginSettings();
1456+
1457+ /**
1458+ * Function GetActionPluginButtonVisible
1459+ * Returns true if button visibility action plugin setting was set to true
1460+ * or it is unset and plugin defaults to true.
1461+ */
1462+ bool GetActionPluginButtonVisible( const wxString& aPluginPath, bool aPluginDefault );
1463+
1464+ /**
1465+ * Function GetOrderedActionPlugins
1466+ * Returns ordered list of plugins in sequence in which they should appear on toolbar or in settings
1467+ */
1468+ std::vector<ACTION_PLUGIN*> GetOrderedActionPlugins();
1469+
1470+#endif
1471+
1472 /**
1473 * Function GetProjectFileParameters
1474 * returns a project file parameter list for Pcbnew.
1475diff --git a/pcbnew/pcb_general_settings.cpp b/pcbnew/pcb_general_settings.cpp
1476index a8c70cd..96c525a 100644
1477--- a/pcbnew/pcb_general_settings.cpp
1478+++ b/pcbnew/pcb_general_settings.cpp
1479@@ -22,6 +22,7 @@
1480 */
1481
1482 #include <pcb_general_settings.h>
1483+#include <wx/tokenzr.h>
1484
1485 PCB_GENERAL_SETTINGS::PCB_GENERAL_SETTINGS( FRAME_T aFrameType )
1486 : m_frameType( aFrameType ), m_colorsSettings( aFrameType )
1487@@ -54,6 +55,32 @@ PCB_GENERAL_SETTINGS::PCB_GENERAL_SETTINGS( FRAME_T aFrameType )
1488 void PCB_GENERAL_SETTINGS::Load( wxConfigBase* aCfg )
1489 {
1490 m_colorsSettings.Load( aCfg );
1491+
1492+#if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
1493+
1494+ m_pluginSettings.clear();
1495+
1496+ wxString pluginSettings = aCfg->Read( "ActionPluginButtons" );
1497+
1498+ wxStringTokenizer pluginSettingsTokenizer = wxStringTokenizer( pluginSettings, ";" );
1499+
1500+ while( pluginSettingsTokenizer.HasMoreTokens() )
1501+ {
1502+ wxString plugin = pluginSettingsTokenizer.GetNextToken();
1503+ wxStringTokenizer pluginTokenizer = wxStringTokenizer( plugin, "=" );
1504+
1505+ if( pluginTokenizer.CountTokens() != 2 )
1506+ {
1507+ // Bad config
1508+ continue;
1509+ }
1510+
1511+ plugin = pluginTokenizer.GetNextToken();
1512+ m_pluginSettings.push_back( std::make_pair( plugin, pluginTokenizer.GetNextToken() ) );
1513+ }
1514+
1515+#endif
1516+
1517 SETTINGS::Load( aCfg );
1518 }
1519
1520@@ -61,6 +88,24 @@ void PCB_GENERAL_SETTINGS::Load( wxConfigBase* aCfg )
1521 void PCB_GENERAL_SETTINGS::Save( wxConfigBase* aCfg )
1522 {
1523 m_colorsSettings.Save( aCfg );
1524+
1525+#if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
1526+
1527+ wxString pluginSettings;
1528+
1529+ for( auto const& entry : m_pluginSettings )
1530+ {
1531+ if( !pluginSettings.IsEmpty() )
1532+ {
1533+ pluginSettings = pluginSettings + wxT( ";" );
1534+ }
1535+ pluginSettings = pluginSettings + entry.first + wxT( "=" ) + entry.second;
1536+ }
1537+
1538+ aCfg->Write( "ActionPluginButtons" , pluginSettings );
1539+
1540+#endif
1541+
1542 SETTINGS::Save( aCfg );
1543 }
1544
1545diff --git a/pcbnew/pcb_general_settings.h b/pcbnew/pcb_general_settings.h
1546index bc8f953..fbcbe90 100644
1547--- a/pcbnew/pcb_general_settings.h
1548+++ b/pcbnew/pcb_general_settings.h
1549@@ -25,6 +25,7 @@
1550 #define __PCBNEW_GENERAL_SETTINGS_H
1551
1552 #include <colors_design_settings.h>
1553+#include <vector>
1554
1555 class wxConfigBase;
1556 class wxString;
1557@@ -65,6 +66,10 @@ public:
1558 MAGNETIC_PAD_OPTION_VALUES m_magneticPads = CAPTURE_CURSOR_IN_TRACK_TOOL;
1559 MAGNETIC_PAD_OPTION_VALUES m_magneticTracks = CAPTURE_CURSOR_IN_TRACK_TOOL;
1560
1561+#if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
1562+ std::vector< std::pair<wxString, wxString> > m_pluginSettings; // Settings for action plugins
1563+#endif
1564+
1565 protected:
1566 const FRAME_T m_frameType;
1567 COLORS_DESIGN_SETTINGS m_colorsSettings;
1568diff --git a/pcbnew/pcbnew_config.cpp b/pcbnew/pcbnew_config.cpp
1569index ae164fd..b6025a5 100644
1570--- a/pcbnew/pcbnew_config.cpp
1571+++ b/pcbnew/pcbnew_config.cpp
1572@@ -45,6 +45,7 @@
1573 #include <panel_hotkeys_editor.h>
1574 #include <panel_pcbnew_settings.h>
1575 #include <panel_pcbnew_display_options.h>
1576+#include <panel_pcbnew_action_plugins.h>
1577 #include <fp_lib_table.h>
1578 #include <worksheet_shape_builder.h>
1579 #include <class_board.h>
1580@@ -97,6 +98,9 @@ void PCB_EDIT_FRAME::InstallPreferences( PAGED_DIALOG* aParent )
1581
1582 book->AddPage( new PANEL_PCBNEW_SETTINGS( this, aParent ), _( "Pcbnew" ) );
1583 book->AddSubPage( new PANEL_PCBNEW_DISPLAY_OPTIONS( this, aParent ), _( "Display Options" ) );
1584+#if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
1585+ book->AddSubPage( new PANEL_PCBNEW_ACTION_PLUGINS( this, aParent ), _( "Action Plugins" ) );
1586+#endif
1587 }
1588
1589
1590diff --git a/pcbnew/swig/pcbnew_action_plugins.cpp b/pcbnew/swig/pcbnew_action_plugins.cpp
1591index 4fb232e..0de6526 100644
1592--- a/pcbnew/swig/pcbnew_action_plugins.cpp
1593+++ b/pcbnew/swig/pcbnew_action_plugins.cpp
1594@@ -140,6 +140,32 @@ wxString PYTHON_ACTION_PLUGIN::GetDescription()
1595 }
1596
1597
1598+bool PYTHON_ACTION_PLUGIN::GetShowToolbarButton()
1599+{
1600+ PyLOCK lock;
1601+
1602+ PyObject* result = CallMethod( "GetShowToolbarButton");
1603+
1604+ return PyObject_IsTrue(result);
1605+}
1606+
1607+
1608+wxString PYTHON_ACTION_PLUGIN::GetIconFileName()
1609+{
1610+ PyLOCK lock;
1611+
1612+ return CallRetStrMethod( "GetIconFileName" );
1613+}
1614+
1615+
1616+wxString PYTHON_ACTION_PLUGIN::GetPluginPath()
1617+{
1618+ PyLOCK lock;
1619+
1620+ return CallRetStrMethod( "GetPluginPath" );
1621+}
1622+
1623+
1624 void PYTHON_ACTION_PLUGIN::Run()
1625 {
1626 PyLOCK lock;
1627@@ -164,217 +190,226 @@ void PYTHON_ACTION_PLUGINS::register_action( PyObject* aPyAction )
1628
1629 void PYTHON_ACTION_PLUGINS::deregister_action( PyObject* aPyAction )
1630 {
1631- // deregister also destroyes the previously created "PYTHON_ACTION_PLUGIN object"
1632+ // deregister also destroys the previously created "PYTHON_ACTION_PLUGIN object"
1633 ACTION_PLUGINS::deregister_object( (void*) aPyAction );
1634 }
1635
1636
1637 #if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
1638
1639-void PCB_EDIT_FRAME::OnActionPlugin( wxCommandEvent& aEvent )
1640+void PCB_EDIT_FRAME::OnActionPluginMenu( wxCommandEvent& aEvent )
1641 {
1642- int id = aEvent.GetId();
1643+ ACTION_PLUGIN* actionPlugin = ACTION_PLUGINS::GetActionByMenu( aEvent.GetId() );
1644
1645- ACTION_PLUGIN* actionPlugin = ACTION_PLUGINS::GetActionByMenu( id );
1646+ if( actionPlugin )
1647+ RunActionPlugin( actionPlugin );
1648+}
1649
1650- if( actionPlugin )
1651- {
1652- PICKED_ITEMS_LIST itemsList;
1653- BOARD* currentPcb = GetBoard();
1654- bool fromEmpty = false;
1655+void PCB_EDIT_FRAME::OnActionPluginButton( wxCommandEvent& aEvent )
1656+{
1657+ ACTION_PLUGIN* actionPlugin = ACTION_PLUGINS::GetActionByButton( aEvent.GetId() );
1658
1659- itemsList.m_Status = UR_CHANGED;
1660+ if( actionPlugin )
1661+ RunActionPlugin( actionPlugin );
1662+}
1663
1664- OnModify();
1665+void PCB_EDIT_FRAME::RunActionPlugin( ACTION_PLUGIN* aActionPlugin )
1666+{
1667+ PICKED_ITEMS_LIST itemsList;
1668+ BOARD* currentPcb = GetBoard();
1669+ bool fromEmpty = false;
1670
1671- // Append tracks:
1672- for( BOARD_ITEM* item = currentPcb->m_Track; item != NULL; item = item->Next() )
1673- {
1674- ITEM_PICKER picker( item, UR_CHANGED );
1675- itemsList.PushItem( picker );
1676- }
1677+ itemsList.m_Status = UR_CHANGED;
1678
1679- // Append modules:
1680- for( BOARD_ITEM* item = currentPcb->m_Modules; item != NULL; item = item->Next() )
1681- {
1682- ITEM_PICKER picker( item, UR_CHANGED );
1683- itemsList.PushItem( picker );
1684- }
1685+ OnModify();
1686
1687- // Append drawings
1688- for( BOARD_ITEM* item = currentPcb->m_Drawings; item != NULL; item = item->Next() )
1689- {
1690- ITEM_PICKER picker( item, UR_CHANGED );
1691- itemsList.PushItem( picker );
1692- }
1693+ // Append tracks:
1694+ for( BOARD_ITEM* item = currentPcb->m_Track; item != NULL; item = item->Next() )
1695+ {
1696+ ITEM_PICKER picker( item, UR_CHANGED );
1697+ itemsList.PushItem( picker );
1698+ }
1699
1700- // Append zones outlines
1701- for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
1702- {
1703- ITEM_PICKER picker( (EDA_ITEM*) currentPcb->GetArea(
1704- ii ), UR_CHANGED );
1705- itemsList.PushItem( picker );
1706- }
1707+ // Append modules:
1708+ for( BOARD_ITEM* item = currentPcb->m_Modules; item != NULL; item = item->Next() )
1709+ {
1710+ ITEM_PICKER picker( item, UR_CHANGED );
1711+ itemsList.PushItem( picker );
1712+ }
1713
1714- // Append zones segm:
1715- for( BOARD_ITEM* item = currentPcb->m_SegZoneDeprecated; item != NULL; item = item->Next() )
1716- {
1717- ITEM_PICKER picker( item, UR_CHANGED );
1718- itemsList.PushItem( picker );
1719- }
1720+ // Append drawings
1721+ for( BOARD_ITEM* item = currentPcb->m_Drawings; item != NULL; item = item->Next() )
1722+ {
1723+ ITEM_PICKER picker( item, UR_CHANGED );
1724+ itemsList.PushItem( picker );
1725+ }
1726
1727- if( itemsList.GetCount() > 0 )
1728- SaveCopyInUndoList( itemsList, UR_CHANGED, wxPoint( 0.0, 0.0 ) );
1729- else
1730- fromEmpty = true;
1731+ // Append zones outlines
1732+ for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
1733+ {
1734+ ITEM_PICKER picker( (EDA_ITEM*) currentPcb->GetArea(
1735+ ii ), UR_CHANGED );
1736+ itemsList.PushItem( picker );
1737+ }
1738
1739- itemsList.ClearItemsList();
1740+ // Append zones segm:
1741+ for( BOARD_ITEM* item = currentPcb->m_SegZoneDeprecated; item != NULL; item = item->Next() )
1742+ {
1743+ ITEM_PICKER picker( item, UR_CHANGED );
1744+ itemsList.PushItem( picker );
1745+ }
1746
1747- // Execute plugin himself...
1748- ACTION_PLUGINS::SetActionRunning( true );
1749- actionPlugin->Run();
1750- ACTION_PLUGINS::SetActionRunning( false );
1751+ if( itemsList.GetCount() > 0 )
1752+ SaveCopyInUndoList( itemsList, UR_CHANGED, wxPoint( 0.0, 0.0 ) );
1753+ else
1754+ fromEmpty = true;
1755
1756- currentPcb->m_Status_Pcb = 0;
1757+ itemsList.ClearItemsList();
1758
1759- // Get back the undo buffer to fix some modifications
1760- PICKED_ITEMS_LIST* oldBuffer = NULL;
1761+ // Execute plugin itself...
1762+ ACTION_PLUGINS::SetActionRunning( true );
1763+ aActionPlugin->Run();
1764+ ACTION_PLUGINS::SetActionRunning( false );
1765
1766- if( fromEmpty )
1767- {
1768- oldBuffer = new PICKED_ITEMS_LIST();
1769- oldBuffer->m_Status = UR_NEW;
1770- }
1771- else
1772- {
1773- oldBuffer = GetScreen()->PopCommandFromUndoList();
1774- wxASSERT( oldBuffer );
1775- }
1776+ currentPcb->m_Status_Pcb = 0;
1777
1778- // Try do discover what was modified
1779+ // Get back the undo buffer to fix some modifications
1780+ PICKED_ITEMS_LIST* oldBuffer = NULL;
1781
1782- PICKED_ITEMS_LIST deletedItemsList;
1783+ if( fromEmpty )
1784+ {
1785+ oldBuffer = new PICKED_ITEMS_LIST();
1786+ oldBuffer->m_Status = UR_NEW;
1787+ }
1788+ else
1789+ {
1790+ oldBuffer = GetScreen()->PopCommandFromUndoList();
1791+ wxASSERT( oldBuffer );
1792+ }
1793
1794- // Found deleted modules
1795- for( unsigned int i = 0; i < oldBuffer->GetCount(); i++ )
1796- {
1797- BOARD_ITEM* item = (BOARD_ITEM*) oldBuffer->GetPickedItem( i );
1798- ITEM_PICKER picker( item, UR_DELETED );
1799+ // Try do discover what was modified
1800
1801- wxASSERT( item );
1802+ PICKED_ITEMS_LIST deletedItemsList;
1803
1804- switch( item->Type() )
1805- {
1806- case PCB_NETINFO_T:
1807- case PCB_MARKER_T:
1808- case PCB_MODULE_T:
1809- case PCB_TRACE_T:
1810- case PCB_VIA_T:
1811- case PCB_LINE_T:
1812- case PCB_TEXT_T:
1813- case PCB_DIMENSION_T:
1814- case PCB_TARGET_T:
1815- case PCB_ZONE_T:
1816-
1817- // If item has a list it's mean that the element is on the board
1818- if( item->GetList() == NULL )
1819- {
1820- deletedItemsList.PushItem( picker );
1821- }
1822-
1823- break;
1824-
1825- case PCB_ZONE_AREA_T:
1826+ // Found deleted modules
1827+ for( unsigned int i = 0; i < oldBuffer->GetCount(); i++ )
1828+ {
1829+ BOARD_ITEM* item = (BOARD_ITEM*) oldBuffer->GetPickedItem( i );
1830+ ITEM_PICKER picker( item, UR_DELETED );
1831+
1832+ wxASSERT( item );
1833+
1834+ switch( item->Type() )
1835+ {
1836+ case PCB_NETINFO_T:
1837+ case PCB_MARKER_T:
1838+ case PCB_MODULE_T:
1839+ case PCB_TRACE_T:
1840+ case PCB_VIA_T:
1841+ case PCB_LINE_T:
1842+ case PCB_TEXT_T:
1843+ case PCB_DIMENSION_T:
1844+ case PCB_TARGET_T:
1845+ case PCB_ZONE_T:
1846+
1847+ // If item has a list it's mean that the element is on the board
1848+ if( item->GetList() == NULL )
1849 {
1850- bool zoneFound = false;
1851+ deletedItemsList.PushItem( picker );
1852+ }
1853
1854- for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
1855- zoneFound |= currentPcb->GetArea( ii ) == item;
1856+ break;
1857
1858- if( !zoneFound )
1859- {
1860- deletedItemsList.PushItem( picker );
1861- }
1862+ case PCB_ZONE_AREA_T:
1863+ {
1864+ bool zoneFound = false;
1865
1866- break;
1867- }
1868+ for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
1869+ zoneFound |= currentPcb->GetArea( ii ) == item;
1870
1871- default:
1872- wxString msg;
1873- msg.Printf( _( "(PCB_EDIT_FRAME::OnActionPlugin) needs work: "
1874- "BOARD_ITEM type (%d) not handled" ),
1875- item->Type() );
1876- wxFAIL_MSG( msg );
1877- break;
1878+ if( !zoneFound )
1879+ {
1880+ deletedItemsList.PushItem( picker );
1881 }
1882+
1883+ break;
1884 }
1885
1886- // Mark deleted elements in undolist
1887- for( unsigned int i = 0; i < deletedItemsList.GetCount(); i++ )
1888- {
1889- oldBuffer->PushItem( deletedItemsList.GetItemWrapper( i ) );
1890+ default:
1891+ wxString msg;
1892+ msg.Printf( _( "(PCB_EDIT_FRAME::OnActionPlugin) needs work: "
1893+ "BOARD_ITEM type (%d) not handled" ),
1894+ item->Type() );
1895+ wxFAIL_MSG( msg );
1896+ break;
1897 }
1898+ }
1899
1900- // Find new modules
1901- for( BOARD_ITEM* item = currentPcb->m_Modules; item != NULL; item = item->Next() )
1902+ // Mark deleted elements in undolist
1903+ for( unsigned int i = 0; i < deletedItemsList.GetCount(); i++ )
1904+ {
1905+ oldBuffer->PushItem( deletedItemsList.GetItemWrapper( i ) );
1906+ }
1907+
1908+ // Find new modules
1909+ for( BOARD_ITEM* item = currentPcb->m_Modules; item != NULL; item = item->Next() )
1910+ {
1911+ if( !oldBuffer->ContainsItem( item ) )
1912 {
1913- if( !oldBuffer->ContainsItem( item ) )
1914- {
1915- ITEM_PICKER picker( item, UR_NEW );
1916- oldBuffer->PushItem( picker );
1917- }
1918+ ITEM_PICKER picker( item, UR_NEW );
1919+ oldBuffer->PushItem( picker );
1920 }
1921+ }
1922
1923- for( BOARD_ITEM* item = currentPcb->m_Track; item != NULL; item = item->Next() )
1924+ for( BOARD_ITEM* item = currentPcb->m_Track; item != NULL; item = item->Next() )
1925+ {
1926+ if( !oldBuffer->ContainsItem( item ) )
1927 {
1928- if( !oldBuffer->ContainsItem( item ) )
1929- {
1930- ITEM_PICKER picker( item, UR_NEW );
1931- oldBuffer->PushItem( picker );
1932- }
1933+ ITEM_PICKER picker( item, UR_NEW );
1934+ oldBuffer->PushItem( picker );
1935 }
1936+ }
1937
1938- for( BOARD_ITEM* item = currentPcb->m_Drawings; item != NULL; item = item->Next() )
1939+ for( BOARD_ITEM* item = currentPcb->m_Drawings; item != NULL; item = item->Next() )
1940+ {
1941+ if( !oldBuffer->ContainsItem( item ) )
1942 {
1943- if( !oldBuffer->ContainsItem( item ) )
1944- {
1945- ITEM_PICKER picker( item, UR_NEW );
1946- oldBuffer->PushItem( picker );
1947- }
1948+ ITEM_PICKER picker( item, UR_NEW );
1949+ oldBuffer->PushItem( picker );
1950 }
1951+ }
1952
1953- for( BOARD_ITEM* item = currentPcb->m_SegZoneDeprecated; item != NULL; item = item->Next() )
1954+ for( BOARD_ITEM* item = currentPcb->m_SegZoneDeprecated; item != NULL; item = item->Next() )
1955+ {
1956+ if( !oldBuffer->ContainsItem( item ) )
1957 {
1958- if( !oldBuffer->ContainsItem( item ) )
1959- {
1960- ITEM_PICKER picker( item, UR_NEW );
1961- oldBuffer->PushItem( picker );
1962- }
1963+ ITEM_PICKER picker( item, UR_NEW );
1964+ oldBuffer->PushItem( picker );
1965 }
1966+ }
1967
1968- for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
1969+ for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
1970+ {
1971+ if( !oldBuffer->ContainsItem( (EDA_ITEM*) currentPcb->GetArea( ii ) ) )
1972 {
1973- if( !oldBuffer->ContainsItem( (EDA_ITEM*) currentPcb->GetArea( ii ) ) )
1974- {
1975- ITEM_PICKER picker( (EDA_ITEM*) currentPcb->GetArea(
1976- ii ), UR_NEW );
1977- oldBuffer->PushItem( picker );
1978- }
1979+ ITEM_PICKER picker( (EDA_ITEM*) currentPcb->GetArea(
1980+ ii ), UR_NEW );
1981+ oldBuffer->PushItem( picker );
1982 }
1983+ }
1984
1985
1986- GetScreen()->PushCommandToUndoList( oldBuffer );
1987+ GetScreen()->PushCommandToUndoList( oldBuffer );
1988
1989- if( IsGalCanvasActive() )
1990- {
1991- UseGalCanvas( GetGalCanvas() );
1992- }
1993- else
1994- {
1995- UpdateUserInterface();
1996- GetScreen()->SetModify();
1997- Refresh();
1998- }
1999+ if( IsGalCanvasActive() )
2000+ {
2001+ UseGalCanvas( GetGalCanvas() );
2002+ }
2003+ else
2004+ {
2005+ UpdateUserInterface();
2006+ GetScreen()->SetModify();
2007+ Refresh();
2008 }
2009 }
2010
2011@@ -409,30 +444,39 @@ void PCB_EDIT_FRAME::RebuildActionPluginMenus()
2012 // Remove menus which are not usable for our current plugin list
2013 Disconnect( item->GetId(), wxEVT_COMMAND_MENU_SELECTED,
2014 (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &
2015- PCB_EDIT_FRAME::OnActionPlugin );
2016+ PCB_EDIT_FRAME::OnActionPluginMenu );
2017 actionMenu->Delete( item );
2018 }
2019
2020 for( int ii = 0; ii < ACTION_PLUGINS::GetActionsCount(); ii++ )
2021 {
2022 wxMenuItem* item;
2023+ ACTION_PLUGIN* ap = ACTION_PLUGINS::GetAction( ii );
2024+ const wxBitmap& bitmap = ap->iconBitmap.IsOk() ? ap->iconBitmap : KiBitmap( hammer_xpm );
2025
2026 if( ii < (int) available_menus.size() )
2027 {
2028 item = available_menus[ii];
2029- item->SetItemLabel( ACTION_PLUGINS::GetAction( ii )->GetName() );
2030- item->SetHelp( ACTION_PLUGINS::GetAction( ii )->GetDescription() );
2031+ item->SetItemLabel( ap->GetName() );
2032+ item->SetHelp( ap->GetDescription() );
2033+
2034+ // On windows we need to set "unchecked" bitmap
2035+#if defined(__WXMSW__)
2036+ item->SetBitmap( bitmap, false );
2037+#else
2038+ item->SetBitmap( bitmap );
2039+#endif
2040 }
2041 else
2042 {
2043 item = AddMenuItem( actionMenu, wxID_ANY,
2044- ACTION_PLUGINS::GetAction( ii )->GetName(),
2045- ACTION_PLUGINS::GetAction( ii )->GetDescription(),
2046- KiBitmap( hammer_xpm ) );
2047+ ap->GetName(),
2048+ ap->GetDescription(),
2049+ bitmap );
2050
2051 Connect( item->GetId(), wxEVT_COMMAND_MENU_SELECTED,
2052 (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &
2053- PCB_EDIT_FRAME::OnActionPlugin );
2054+ PCB_EDIT_FRAME::OnActionPluginMenu );
2055 }
2056
2057 ACTION_PLUGINS::SetActionMenu( ii, item->GetId() );
2058@@ -440,4 +484,104 @@ void PCB_EDIT_FRAME::RebuildActionPluginMenus()
2059 }
2060
2061
2062+void PCB_EDIT_FRAME::AddActionPluginTools()
2063+{
2064+ bool need_separator = true;
2065+ const auto& orderedPlugins = GetOrderedActionPlugins();
2066+
2067+ for( const auto& ap : orderedPlugins )
2068+ {
2069+ if( GetActionPluginButtonVisible( ap->GetPluginPath(), ap->GetShowToolbarButton() ) )
2070+ {
2071+
2072+ if ( need_separator )
2073+ {
2074+ KiScaledSeparator( m_mainToolBar, this );
2075+ need_separator = false;
2076+ }
2077+
2078+ // Add button
2079+ wxBitmap bitmap;
2080+
2081+ if ( ap->iconBitmap.IsOk() )
2082+ bitmap = KiScaledBitmap( ap->iconBitmap, this );
2083+ else
2084+ bitmap = KiScaledBitmap( hammer_xpm, this );
2085+
2086+ wxAuiToolBarItem* button = m_mainToolBar->AddTool(
2087+ wxID_ANY, wxEmptyString, bitmap, ap->GetName() );
2088+
2089+ Connect( button->GetId(), wxEVT_COMMAND_MENU_SELECTED,
2090+ (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &
2091+ PCB_EDIT_FRAME::OnActionPluginButton );
2092+
2093+ // Link action plugin to button
2094+ ACTION_PLUGINS::SetActionButton( ap, button->GetId() );
2095+ }
2096+ }
2097+}
2098+
2099+
2100+void PCB_EDIT_FRAME::SetActionPluginSettings( const std::vector< std::pair<wxString, wxString> >& aPluginSettings )
2101+{
2102+ m_configSettings.m_pluginSettings = aPluginSettings;
2103+ ReCreateHToolbar();
2104+}
2105+
2106+
2107+std::vector< std::pair<wxString, wxString> > PCB_EDIT_FRAME::GetActionPluginSettings()
2108+{
2109+ return m_configSettings.m_pluginSettings;
2110+}
2111+
2112+
2113+std::vector<ACTION_PLUGIN*> PCB_EDIT_FRAME::GetOrderedActionPlugins()
2114+{
2115+ std::vector<ACTION_PLUGIN*> orderedPlugins;
2116+ const auto& pluginSettings = GetActionPluginSettings();
2117+
2118+ // First add plugins that have entries in settings
2119+ for( size_t ii = 0; ii < pluginSettings.size(); ii++ )
2120+ {
2121+ for( int jj = 0; jj < ACTION_PLUGINS::GetActionsCount(); jj++ )
2122+ {
2123+ if( ACTION_PLUGINS::GetAction( jj )->GetPluginPath() == pluginSettings[ii].first )
2124+ orderedPlugins.push_back( ACTION_PLUGINS::GetAction( jj ) );
2125+ }
2126+ }
2127+
2128+ // Now append new plugins that have not been configured yet
2129+ for( int ii = 0; ii < ACTION_PLUGINS::GetActionsCount(); ii++ )
2130+ {
2131+ bool found = false;
2132+
2133+ for( size_t jj = 0; jj < orderedPlugins.size(); jj++ )
2134+ {
2135+ if( ACTION_PLUGINS::GetAction( ii ) == orderedPlugins[jj] )
2136+ found = true;
2137+ }
2138+
2139+ if ( !found )
2140+ orderedPlugins.push_back( ACTION_PLUGINS::GetAction( ii ) );
2141+ }
2142+
2143+ return orderedPlugins;
2144+}
2145+
2146+
2147+bool PCB_EDIT_FRAME::GetActionPluginButtonVisible( const wxString& aPluginPath, bool aPluginDefault )
2148+{
2149+ auto& settings = m_configSettings.m_pluginSettings;
2150+
2151+ for(const auto& entry : settings )
2152+ {
2153+ if (entry.first == aPluginPath )
2154+ return entry.second == wxT( "Visible" );
2155+ }
2156+
2157+ // Plugin is not in settings, return default.
2158+ return aPluginDefault;
2159+}
2160+
2161+
2162 #endif
2163diff --git a/pcbnew/swig/pcbnew_action_plugins.h b/pcbnew/swig/pcbnew_action_plugins.h
2164index 08dfa14..a1d6d9f 100644
2165--- a/pcbnew/swig/pcbnew_action_plugins.h
2166+++ b/pcbnew/swig/pcbnew_action_plugins.h
2167@@ -47,6 +47,9 @@ public:
2168 wxString GetCategoryName() override;
2169 wxString GetName() override;
2170 wxString GetDescription() override;
2171+ bool GetShowToolbarButton() override;
2172+ wxString GetIconFileName() override;
2173+ wxString GetPluginPath() override;
2174 void Run() override;
2175 void* GetObject() override;
2176 };
2177diff --git a/pcbnew/tool_pcb_editor.cpp b/pcbnew/tool_pcb_editor.cpp
2178index 66b6223..b5c64c3 100644
2179--- a/pcbnew/tool_pcb_editor.cpp
2180+++ b/pcbnew/tool_pcb_editor.cpp
2181@@ -321,6 +321,10 @@ void PCB_EDIT_FRAME::ReCreateHToolbar()
2182 m_mainToolBar->AddTool( ID_TOOLBARH_PCB_SCRIPTING_CONSOLE, wxEmptyString,
2183 KiScaledBitmap( py_script_xpm, this ),
2184 _( "Show/Hide the Python Scripting console" ), wxITEM_CHECK );
2185+
2186+#if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
2187+ AddActionPluginTools();
2188+#endif
2189 }
2190 #endif
2191
2192diff --git a/scripting/kicadplugins.i b/scripting/kicadplugins.i
2193index 8c1781d..ebd8db2 100644
2194--- a/scripting/kicadplugins.i
2195+++ b/scripting/kicadplugins.i
2196@@ -260,7 +260,7 @@ def LoadPlugins(bundlepath=None):
2197 if module == '__init__.py' or module[-3:] != '.py':
2198 continue
2199
2200- LoadOnePlugin(plugins_dir, module);
2201+ LoadOnePlugin(plugins_dir, module)
2202
2203
2204 class KiCadPlugin:
2205@@ -268,6 +268,9 @@ class KiCadPlugin:
2206 pass
2207
2208 def register(self):
2209+ import inspect
2210+ import os
2211+
2212 if isinstance(self,FilePlugin):
2213 pass # register to file plugins in C++
2214
2215@@ -276,6 +279,14 @@ class KiCadPlugin:
2216 return
2217
2218 if isinstance(self,ActionPlugin):
2219+ """
2220+ Get path to .py or .pyc that has definition of plugin class.
2221+ If path is binary but source also exists, assume definition is in source.
2222+ """
2223+ self.__plugin_path = inspect.getfile(self.__class__)
2224+ if self.__plugin_path.endswith('.pyc') and os.path.isfile(self.__plugin_path[:-1]):
2225+ self.__plugin_path = self.__plugin_path[:-1]
2226+ self.__plugin_path = self.__plugin_path + '/' + self.__class__.__name__
2227 PYTHON_ACTION_PLUGINS.register_action(self)
2228 return
2229
2230@@ -295,6 +306,9 @@ class KiCadPlugin:
2231
2232 return
2233
2234+ def GetPluginPath( self ):
2235+ return self.__plugin_path
2236+
2237
2238 class FilePlugin(KiCadPlugin):
2239 def __init__(self):
2240@@ -633,6 +647,8 @@ class FootprintWizardPlugin(KiCadPlugin, object):
2241 class ActionPlugin(KiCadPlugin, object):
2242 def __init__( self ):
2243 KiCadPlugin.__init__( self )
2244+ self.icon_file_name = ""
2245+ self.show_toolbar_button = False
2246 self.defaults()
2247
2248 def defaults( self ):
2249@@ -649,6 +665,12 @@ class ActionPlugin(KiCadPlugin, object):
2250 def GetDescription( self ):
2251 return self.description
2252
2253+ def GetShowToolbarButton( self ):
2254+ return self.show_toolbar_button
2255+
2256+ def GetIconFileName( self ):
2257+ return self.icon_file_name
2258+
2259 def Run(self):
2260 return
2261

Subscribers

People subscribed via source and target branches

to status/vote changes: