KiCad PCB EDA Suite
pcbnew_action_plugins.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
25#include <pgm_base.h>
27#include <pcbnew_settings.h>
28#include <bitmaps.h>
29#include <board.h>
30#include <board_commit.h>
32#include <footprint.h>
33#include <pcb_track.h>
34#include <zone.h>
35#include <menus_helpers.h>
36#include <pcbnew_settings.h>
37#include <tool/action_menu.h>
38#include <tool/action_toolbar.h>
39#include <tool/tool_manager.h>
40#include <pcb_painter.h>
41#include <wx/msgdlg.h>
42#include "../../scripting/python_scripting.h"
43
45{
46 PyLOCK lock;
47
48 m_PyAction = aAction;
49 Py_XINCREF( aAction );
50}
51
52
54{
55 PyLOCK lock;
56
57 Py_XDECREF( m_PyAction );
58}
59
60
61PyObject* PYTHON_ACTION_PLUGIN::CallMethod( const char* aMethod, PyObject* aArglist )
62{
63 PyLOCK lock;
64
65 PyErr_Clear();
66
67 // pFunc is a new reference to the desired method
68 PyObject* pFunc = PyObject_GetAttrString( m_PyAction, aMethod );
69
70 if( pFunc && PyCallable_Check( pFunc ) )
71 {
72 PyObject* result = PyObject_CallObject( pFunc, aArglist );
73
74 if( PyErr_Occurred() )
75 {
76 wxMessageBox( PyErrStringWithTraceback(),
77 _( "Exception on python action plugin code" ),
78 wxICON_ERROR | wxOK );
79 }
80
81 if( result )
82 {
83 Py_XDECREF( pFunc );
84 return result;
85 }
86 }
87 else
88 {
89 wxString msg = wxString::Format( _( "Method '%s' not found, or not callable" ), aMethod );
90 wxMessageBox( msg, _( "Unknown Method" ), wxICON_ERROR | wxOK );
91 }
92
93 if( pFunc )
94 {
95 Py_XDECREF( pFunc );
96 }
97
98 return nullptr;
99}
100
101
102wxString PYTHON_ACTION_PLUGIN::CallRetStrMethod( const char* aMethod, PyObject* aArglist )
103{
104 wxString ret;
105 PyLOCK lock;
106
107 PyObject* result = CallMethod( aMethod, aArglist );
108
109 ret = PyStringToWx( result );
110 Py_XDECREF( result );
111
112 return ret;
113}
114
115
117{
118 PyLOCK lock;
119
120 return CallRetStrMethod( "GetCategoryName" );
121}
122
123
125{
126 PyLOCK lock;
127
128 return CallRetStrMethod( "GetName" );
129}
130
131
133{
134 PyLOCK lock;
135
136 return CallRetStrMethod( "GetDescription" );
137}
138
139
141{
142 PyLOCK lock;
143
144 PyObject* result = CallMethod( "GetShowToolbarButton");
145
146 return PyObject_IsTrue(result);
147}
148
149
151{
152 PyLOCK lock;
153
154 PyObject* arglist = Py_BuildValue( "(i)", static_cast<int>( aDark ) );
155
156 wxString result = CallRetStrMethod( "GetIconFileName", arglist );
157
158 Py_DECREF( arglist );
159
160 return result;
161}
162
163
165{
166 PyLOCK lock;
167
168 return CallRetStrMethod( "GetPluginPath" );
169}
170
171
173{
174 PyLOCK lock;
175
176 CallMethod( "Run" );
177}
178
179
181{
182 return (void*) m_PyAction;
183}
184
185
186void PYTHON_ACTION_PLUGINS::register_action( PyObject* aPyAction )
187{
188 PYTHON_ACTION_PLUGIN* fw = new PYTHON_ACTION_PLUGIN( aPyAction );
189
190 fw->register_action();
191}
192
193
195{
196 // deregister also destroys the previously created "PYTHON_ACTION_PLUGIN object"
197 ACTION_PLUGINS::deregister_object( (void*) aPyAction );
198}
199
200
201void PCB_EDIT_FRAME::OnActionPluginMenu( wxCommandEvent& aEvent )
202{
203 ACTION_PLUGIN* actionPlugin = ACTION_PLUGINS::GetActionByMenu( aEvent.GetId() );
204
205 if( actionPlugin )
206 RunActionPlugin( actionPlugin );
207}
208
209
210void PCB_EDIT_FRAME::OnActionPluginButton( wxCommandEvent& aEvent )
211{
212 ACTION_PLUGIN* actionPlugin = ACTION_PLUGINS::GetActionByButton( aEvent.GetId() );
213
214 if( actionPlugin )
215 RunActionPlugin( actionPlugin );
216}
217
219{
220
221 PICKED_ITEMS_LIST itemsList;
222 BOARD* currentPcb = GetBoard();
223 bool fromEmpty = false;
224
225 // Append tracks:
226 for( PCB_TRACK* item : currentPcb->Tracks() )
227 {
228 ITEM_PICKER picker( nullptr, item, UNDO_REDO::CHANGED );
229 itemsList.PushItem( picker );
230 }
231
232 // Append footprints:
233 for( FOOTPRINT* item : currentPcb->Footprints() )
234 {
235 ITEM_PICKER picker( nullptr, item, UNDO_REDO::CHANGED );
236 itemsList.PushItem( picker );
237 }
238
239 // Append drawings
240 for( BOARD_ITEM* item : currentPcb->Drawings() )
241 {
242 ITEM_PICKER picker( nullptr, item, UNDO_REDO::CHANGED );
243 itemsList.PushItem( picker );
244 }
245
246 // Append zones outlines
247 for( ZONE* zone : currentPcb->Zones() )
248 {
249 ITEM_PICKER picker( nullptr, zone, UNDO_REDO::CHANGED );
250 itemsList.PushItem( picker );
251 }
252
253 if( itemsList.GetCount() > 0 )
255 else
256 fromEmpty = true;
257
258 itemsList.ClearItemsList();
259
260 BOARD_COMMIT commit( this );
261
262 // Execute plugin itself...
264 aActionPlugin->Run();
266
267 // Get back the undo buffer to fix some modifications
268 PICKED_ITEMS_LIST* oldBuffer = nullptr;
269
270 if( fromEmpty )
271 {
272 oldBuffer = new PICKED_ITEMS_LIST();
273 }
274 else
275 {
276 oldBuffer = PopCommandFromUndoList();
277 wxASSERT( oldBuffer );
278 }
279
280 // Try do discover what was modified
281 PICKED_ITEMS_LIST deletedItemsList;
282
283 // The list of existing items after running the action script
284 std::set<BOARD_ITEM*> currItemList;
285
286 // Append tracks:
287 for( PCB_TRACK* item : currentPcb->Tracks() )
288 currItemList.insert( item );
289
290 // Append footprints:
291 for( FOOTPRINT* item : currentPcb->Footprints() )
292 currItemList.insert( item );
293
294 // Append drawings
295 for( BOARD_ITEM* item : currentPcb->Drawings() )
296 currItemList.insert( item );
297
298 // Append zones outlines
299 for( ZONE* zone : currentPcb->Zones() )
300 currItemList.insert( zone );
301
302 // Found deleted items
303 for( unsigned int i = 0; i < oldBuffer->GetCount(); i++ )
304 {
305 BOARD_ITEM* item = (BOARD_ITEM*) oldBuffer->GetPickedItem( i );
306 ITEM_PICKER picker( nullptr, item, UNDO_REDO::DELETED );
307
308 wxASSERT( item );
309
310 if( currItemList.find( item ) == currItemList.end() )
311 {
312 deletedItemsList.PushItem( picker );
313 commit.Removed( item );
314 }
315 }
316
317 // Mark deleted elements in undolist
318
319 for( unsigned int i = 0; i < deletedItemsList.GetCount(); i++ )
320 {
321 oldBuffer->PushItem( deletedItemsList.GetItemWrapper( i ) );
322 }
323
324 // Find new footprints
325 for( FOOTPRINT* item : currentPcb->Footprints() )
326 {
327 if( !oldBuffer->ContainsItem( item ) )
328 {
329 ITEM_PICKER picker( nullptr, item, UNDO_REDO::NEWITEM );
330 oldBuffer->PushItem( picker );
331 commit.Added( item );
332 }
333 }
334
335 for( PCB_TRACK* item : currentPcb->Tracks() )
336 {
337 if( !oldBuffer->ContainsItem( item ) )
338 {
339 ITEM_PICKER picker( nullptr, item, UNDO_REDO::NEWITEM );
340 oldBuffer->PushItem( picker );
341 commit.Added( item );
342 }
343 }
344
345 for( BOARD_ITEM* item : currentPcb->Drawings() )
346 {
347 if( !oldBuffer->ContainsItem( item ) )
348 {
349 ITEM_PICKER picker( nullptr, item, UNDO_REDO::NEWITEM );
350 oldBuffer->PushItem( picker );
351 commit.Added( item );
352 }
353 }
354
355 for( ZONE* zone : currentPcb->Zones() )
356 {
357 if( !oldBuffer->ContainsItem( zone ) )
358 {
359 ITEM_PICKER picker( nullptr, zone, UNDO_REDO::NEWITEM );
360 oldBuffer->PushItem( picker );
361 commit.Added( zone );
362 }
363 }
364
365
366 if( oldBuffer->GetCount() )
367 {
368 OnModify();
369 PushCommandToUndoList( oldBuffer );
370 }
371 else
372 {
373 delete oldBuffer;
374 }
375
376 // Apply changes, UndoList already handled
377 commit.Push( _( "Apply action script" ), SKIP_UNDO | SKIP_SET_DIRTY );
378
380}
381
382
384{
386
387 PCB_DRAW_PANEL_GAL* canvas = GetCanvas();
388
389 canvas->GetView()->Clear();
390 canvas->GetView()->InitPreview();
392 canvas->DisplayBoard( m_pcb );
393
394 // allow tools to re-add their view items (selection previews, grids, etc.)
395 if( m_toolManager )
397
398 // reload the drawing-sheet
400
401 canvas->SyncLayersVisibility( m_pcb );
402
403 canvas->Refresh();
404}
405
406
408{
409 if( !actionMenu ) // Should not occur.
410 return;
411
412 for( int ii = 0; ii < ACTION_PLUGINS::GetActionsCount(); ii++ )
413 {
414 wxMenuItem* item;
416 const wxBitmap& bitmap = ap->iconBitmap.IsOk() ? ap->iconBitmap :
418
419 item = AddMenuItem( actionMenu, wxID_ANY, ap->GetName(), ap->GetDescription(), bitmap );
420
421 Connect( item->GetId(), wxEVT_COMMAND_MENU_SELECTED,
422 wxCommandEventHandler( PCB_EDIT_FRAME::OnActionPluginMenu ) );
423
424 ACTION_PLUGINS::SetActionMenu( ii, item->GetId() );
425 }
426}
427
428
430{
431 bool need_separator = true;
432 const std::vector<ACTION_PLUGIN*>& orderedPlugins = GetOrderedActionPlugins();
433
434 for( ACTION_PLUGIN* ap : orderedPlugins )
435 {
436 if( GetActionPluginButtonVisible( ap->GetPluginPath(), ap->GetShowToolbarButton() ) )
437 {
438 if( need_separator )
439 {
441 need_separator = false;
442 }
443
444 // Add button
445 wxBitmap bitmap;
446
447 if ( ap->iconBitmap.IsOk() )
448 bitmap = KiScaledBitmap( ap->iconBitmap, this );
449 else
450 bitmap = KiScaledBitmap( BITMAPS::puzzle_piece, this );
451
452 wxAuiToolBarItem* button = m_mainToolBar->AddTool( wxID_ANY, wxEmptyString,
453 bitmap, ap->GetName() );
454
455 Connect( button->GetId(), wxEVT_COMMAND_MENU_SELECTED,
456 wxCommandEventHandler( PCB_EDIT_FRAME::OnActionPluginButton ) );
457
458 // Link action plugin to button
459 ACTION_PLUGINS::SetActionButton( ap, button->GetId() );
460 }
461 }
462}
463
464
465std::vector<ACTION_PLUGIN*> PCB_EDIT_FRAME::GetOrderedActionPlugins()
466{
467 PCBNEW_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
468
469 std::vector<ACTION_PLUGIN*> plugins;
470 std::vector<ACTION_PLUGIN*> orderedPlugins;
471
472 for( int i = 0; i < ACTION_PLUGINS::GetActionsCount(); i++ )
473 plugins.push_back( ACTION_PLUGINS::GetAction( i ) );
474
475 // First add plugins that have entries in settings
476 for( const auto& pair : cfg->m_VisibleActionPlugins )
477 {
478 auto loc = std::find_if( plugins.begin(), plugins.end(),
479 [pair] ( ACTION_PLUGIN* plugin )
480 {
481 return plugin->GetPluginPath() == pair.first;
482 } );
483
484 if( loc != plugins.end() )
485 {
486 orderedPlugins.push_back( *loc );
487 plugins.erase( loc );
488 }
489 }
490
491 // Now append new plugins that have not been configured yet
492 for( auto remaining_plugin : plugins )
493 orderedPlugins.push_back( remaining_plugin );
494
495 return orderedPlugins;
496}
497
498
499bool PCB_EDIT_FRAME::GetActionPluginButtonVisible( const wxString& aPluginPath,
500 bool aPluginDefault )
501{
502 PCBNEW_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
503
504 for( const auto& entry : cfg->m_VisibleActionPlugins )
505 {
506 if( entry.first == aPluginPath )
507 return entry.second;
508 }
509
510 // Plugin is not in settings, return default.
511 return aPluginDefault;
512}
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:105
wxMenuItem * AddMenuItem(wxMenu *aMenu, int aId, const wxString &aText, const wxBitmap &aImage, wxItemKind aType=wxITEM_NORMAL)
Create and insert a menu item with an icon into aMenu.
Definition: bitmap.cpp:265
wxBitmap KiScaledBitmap(BITMAPS aBitmap, wxWindow *aWindow, int aHeight, bool aQuantized)
Construct a wxBitmap from a memory record, scaling it if device DPI demands it.
Definition: bitmap.cpp:156
@ puzzle_piece
#define SKIP_SET_DIRTY
Definition: board_commit.h:41
#define SKIP_UNDO
Definition: board_commit.h:39
Defines the structure of a menu based on ACTIONs.
Definition: action_menu.h:49
static bool deregister_object(void *aObject)
Deregister an object which builds a action.
static ACTION_PLUGIN * GetActionByMenu(int aMenu)
Find action plugin associated to a menu ID.
static int GetActionsCount()
static ACTION_PLUGIN * GetAction(const wxString &aName)
static void SetActionButton(ACTION_PLUGIN *aAction, int idButton)
Associate a button id to an action plugin.
static void SetActionMenu(int aIndex, int idMenu)
Associate a menu id to an action plugin.
static void SetActionRunning(bool aRunning)
static ACTION_PLUGIN * GetActionByButton(int aButton)
Find action plugin associated to a button ID.
This is the parent class from where any action plugin class must derive.
Definition: action_plugin.h:39
wxBitmap iconBitmap
void register_action()
It's the standard method of a "ACTION_PLUGIN" to register itself into the ACTION_PLUGINS singleton ma...
virtual wxString GetDescription()=0
virtual wxString GetName()=0
virtual void Run()=0
This method the the action.
void AddScaledSeparator(wxWindow *aWindow)
Add a separator that introduces space on either side to not squash the tools when scaled.
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
const VECTOR2I & GetGridOrigin()
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:58
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:265
const PAGE_INFO & GetPageSettings() const
Definition: board.h:626
ZONES & Zones()
Definition: board.h:313
bool BuildConnectivity(PROGRESS_REPORTER *aReporter=nullptr)
Build or rebuild the board connectivity database for the board, especially the list of connected item...
Definition: board.cpp:162
FOOTPRINTS & Footprints()
Definition: board.h:307
TRACKS & Tracks()
Definition: board.h:304
DRAWINGS & Drawings()
Definition: board.h:310
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:682
COMMIT & Added(EDA_ITEM *aItem)
Remove a new item from the model.
Definition: commit.h:84
COMMIT & Removed(EDA_ITEM *aItem)
Modify a given item in the model.
Definition: commit.h:96
virtual void PushCommandToUndoList(PICKED_ITEMS_LIST *aItem)
Add a command to undo in the undo list.
virtual PICKED_ITEMS_LIST * PopCommandFromUndoList()
Return the last command to undo and remove it from list, nothing is deleted.
ACTION_TOOLBAR * m_mainToolBar
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
Update the board display after modifying it by a python script (note: it is automatically called by a...
KIGFX::GAL * GetGAL() const
Return a pointer to the GAL instance used in the panel.
void SetGridOrigin(const VECTOR2D &aGridOrigin)
Set the origin point for the grid.
void Clear()
Remove all items from the view.
Definition: view.cpp:1099
void InitPreview()
Definition: view.cpp:1626
ACTION_PLUGIN_SETTINGS_LIST m_VisibleActionPlugins
void SaveCopyInUndoList(EDA_ITEM *aItemToCopy, UNDO_REDO aTypeCommand) override
Create a new entry in undo list of commands.
Definition: undo_redo.cpp:282
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
BOARD * GetBoard() const
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
void DisplayBoard(BOARD *aBoard, PROGRESS_REPORTER *aReporter=nullptr)
Add all items from the current board to the VIEW, so they can be displayed by GAL.
void SyncLayersVisibility(const BOARD *aBoard)
Update "visibility" property of each layer of a given BOARD.
void OnModify() override
Must be called after a board change to set the modified flag.
static std::vector< ACTION_PLUGIN * > GetOrderedActionPlugins()
Return ordered list of plugins in sequence in which they should appear on toolbar or in settings.
void SetPageSettings(const PAGE_INFO &aPageSettings) override
void OnActionPluginButton(wxCommandEvent &aEvent)
Launched by the button when an action is called.
void RunActionPlugin(ACTION_PLUGIN *aActionPlugin)
Execute action plugin's Run() method and updates undo buffer.
void buildActionPluginMenus(ACTION_MENU *aActionMenu)
Fill action menu with all registered action plugins.
void RebuildAndRefresh()
Rebuilds board connectivity, refreshes canvas.
static bool GetActionPluginButtonVisible(const wxString &aPluginPath, bool aPluginDefault)
Return true if button visibility action plugin setting was set to true or it is unset and plugin defa...
void OnActionPluginMenu(wxCommandEvent &aEvent)
Launched by the menu when an action is called.
void AddActionPluginTools()
Append action plugin buttons to main toolbar.
A holder to handle information on schematic or board items.
void PushItem(const ITEM_PICKER &aItem)
Push aItem to the top of the list.
ITEM_PICKER GetItemWrapper(unsigned int aIdx) const
bool ContainsItem(const EDA_ITEM *aItem) const
unsigned GetCount() const
void ClearItemsList()
Delete only the list of pickers NOT the picked data itself.
EDA_ITEM * GetPickedItem(unsigned int aIdx) const
static void deregister_action(PyObject *aPyAction)
static void register_action(PyObject *aPyAction)
bool GetShowToolbarButton() override
wxString CallRetStrMethod(const char *aMethod, PyObject *aArglist=nullptr)
PyObject * CallMethod(const char *aMethod, PyObject *aArglist=nullptr)
wxString GetCategoryName() override
wxString GetName() override
wxString GetDescription() override
wxString GetPluginPath() override
PYTHON_ACTION_PLUGIN(PyObject *action)
void Run() override
This method the the action.
wxString GetIconFileName(bool aDark) override
void * GetObject() override
This method gets the pointer to the object from where this action constructs.
TOOL_MANAGER * m_toolManager
Definition: tools_holder.h:170
@ REDRAW
Full drawing refresh.
Definition: tool_base.h:82
void ResetTools(TOOL_BASE::RESET_REASON aReason)
Reset all tools (i.e.
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
#define _(s)
Macros and inline functions to create menus items in menubars or popup menus.
Class PCBNEW_ACTION_PLUGINS.
see class PGM_BASE
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:111
VECTOR2< double > VECTOR2D
Definition: vector2d.h:617