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 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 
29 #include "pcbnew_action_plugins.h"
30 #include <python_scripting.h>
31 #include <stdio.h>
32 #include <macros.h>
33 #include <pcbnew_id.h>
34 #include <menus_helpers.h>
35 #include <class_drawpanel.h> // m_canvas
36 #include <class_board.h>
37 #include <class_module.h>
38 #include <class_track.h>
39 #include <class_drawsegment.h>
40 #include <class_zone.h>
41 #include <board_commit.h>
42 #include <kicad_device_context.h>
43 
45 {
46  PyLOCK lock;
47 
48  this->m_PyAction = aAction;
49  Py_XINCREF( aAction );
50 }
51 
52 
54 {
55  PyLOCK lock;
56 
57  Py_XDECREF( this->m_PyAction );
58 }
59 
60 
61 PyObject* PYTHON_ACTION_PLUGIN::CallMethod( const char* aMethod, PyObject* aArglist )
62 {
63  PyLOCK lock;
64 
65  PyErr_Clear();
66  // pFunc is a new reference to the desired method
67  PyObject* pFunc = PyObject_GetAttrString( this->m_PyAction, aMethod );
68 
69  if( pFunc && PyCallable_Check( pFunc ) )
70  {
71  PyObject* result = PyObject_CallObject( pFunc, aArglist );
72 
73  if( PyErr_Occurred() )
74  {
75  wxMessageBox( PyErrStringWithTraceback(),
76  wxT( "Exception on python action plugin code" ),
77  wxICON_ERROR | wxOK );
78  }
79 
80  if( result )
81  {
82  Py_XDECREF( pFunc );
83  return result;
84  }
85  }
86  else
87  {
88  printf( "method not found, or not callable: %s\n", aMethod );
89  }
90 
91  if( pFunc )
92  {
93  Py_XDECREF( pFunc );
94  }
95 
96  return NULL;
97 }
98 
99 
100 wxString PYTHON_ACTION_PLUGIN::CallRetStrMethod( const char* aMethod, PyObject* aArglist )
101 {
102  wxString ret;
103  PyLOCK lock;
104 
105  PyObject* result = CallMethod( aMethod, aArglist );
106 
107  if( result )
108  {
109  const char* str_res = PyString_AsString( result );
110  ret = FROM_UTF8( str_res );
111  Py_DECREF( result );
112  }
113 
114  return ret;
115 }
116 
117 
119 {
120  PyLOCK lock;
121 
122  return CallRetStrMethod( "GetCategoryName" );
123 }
124 
125 
127 {
128  PyLOCK lock;
129 
130  return CallRetStrMethod( "GetName" );
131 }
132 
133 
135 {
136  PyLOCK lock;
137 
138  return CallRetStrMethod( "GetDescription" );
139 }
140 
141 
143 {
144  PyLOCK lock;
145 
146  CallMethod( "Run" );
147 }
148 
149 
151 {
152  return (void*) m_PyAction;
153 }
154 
155 
156 void PYTHON_ACTION_PLUGINS::register_action( PyObject* aPyAction )
157 {
158  PYTHON_ACTION_PLUGIN* fw = new PYTHON_ACTION_PLUGIN( aPyAction );
159 
160  fw->register_action();
161 }
162 
163 
164 void PYTHON_ACTION_PLUGINS::deregister_action( PyObject* aPyAction )
165 {
166  // deregister also destroyes the previously created "PYTHON_ACTION_PLUGIN object"
167  ACTION_PLUGINS::deregister_object( (void*) aPyAction );
168 }
169 
170 
171 #if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
172 
173 void PCB_EDIT_FRAME::OnActionPlugin( wxCommandEvent& aEvent )
174 {
175  int id = aEvent.GetId();
176 
177  ACTION_PLUGIN* actionPlugin = ACTION_PLUGINS::GetActionByMenu( id );
178 
179  if( actionPlugin )
180  {
181  PICKED_ITEMS_LIST itemsList;
182  BOARD* currentPcb = GetBoard();
183  bool fromEmpty = false;
184 
185  itemsList.m_Status = UR_CHANGED;
186 
187  OnModify();
188 
189  // Append tracks:
190  for( BOARD_ITEM* item = currentPcb->m_Track; item != NULL; item = item->Next() )
191  {
192  ITEM_PICKER picker( item, UR_CHANGED );
193  itemsList.PushItem( picker );
194  }
195 
196  // Append modules:
197  for( BOARD_ITEM* item = currentPcb->m_Modules; item != NULL; item = item->Next() )
198  {
199  ITEM_PICKER picker( item, UR_CHANGED );
200  itemsList.PushItem( picker );
201  }
202 
203  // Append drawings
204  for( BOARD_ITEM* item = currentPcb->m_Drawings; item != NULL; item = item->Next() )
205  {
206  ITEM_PICKER picker( item, UR_CHANGED );
207  itemsList.PushItem( picker );
208  }
209 
210  // Append zones outlines
211  for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
212  {
213  ITEM_PICKER picker( (EDA_ITEM*) currentPcb->GetArea(
214  ii ), UR_CHANGED );
215  itemsList.PushItem( picker );
216  }
217 
218  // Append zones segm:
219  for( BOARD_ITEM* item = currentPcb->m_Zone; item != NULL; item = item->Next() )
220  {
221  ITEM_PICKER picker( item, UR_CHANGED );
222  itemsList.PushItem( picker );
223  }
224 
225  if( itemsList.GetCount() > 0 )
226  SaveCopyInUndoList( itemsList, UR_CHANGED, wxPoint( 0.0, 0.0 ) );
227  else
228  fromEmpty = true;
229 
230  itemsList.ClearItemsList();
231 
232  // Execute plugin himself...
233  actionPlugin->Run();
234 
235  currentPcb->m_Status_Pcb = 0;
236 
237  // Get back the undo buffer to fix some modifications
238  PICKED_ITEMS_LIST* oldBuffer = NULL;
239 
240  if( fromEmpty )
241  {
242  oldBuffer = new PICKED_ITEMS_LIST();
243  oldBuffer->m_Status = UR_NEW;
244  }
245  else
246  {
247  oldBuffer = GetScreen()->PopCommandFromUndoList();
248  wxASSERT( oldBuffer );
249  }
250 
251  // Try do discover what was modified
252 
253  PICKED_ITEMS_LIST deletedItemsList;
254 
255  // Found deleted modules
256  for( unsigned int i = 0; i < oldBuffer->GetCount(); i++ )
257  {
258  BOARD_ITEM* item = (BOARD_ITEM*) oldBuffer->GetPickedItem( i );
259  ITEM_PICKER picker( item, UR_DELETED );
260 
261  wxASSERT( item );
262 
263  switch( item->Type() )
264  {
265  case PCB_NETINFO_T:
266  case PCB_MARKER_T:
267  case PCB_MODULE_T:
268  case PCB_TRACE_T:
269  case PCB_VIA_T:
270  case PCB_LINE_T:
271  case PCB_TEXT_T:
272  case PCB_DIMENSION_T:
273  case PCB_TARGET_T:
274  case PCB_ZONE_T:
275 
276  // If item has a list it's mean that the element is on the board
277  if( item->GetList() == NULL )
278  {
279  deletedItemsList.PushItem( picker );
280  }
281 
282  break;
283 
284  case PCB_ZONE_AREA_T:
285  {
286  bool zoneFound = false;
287 
288  for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
289  zoneFound |= currentPcb->GetArea( ii ) == item;
290 
291  if( !zoneFound )
292  {
293  deletedItemsList.PushItem( picker );
294  }
295 
296  break;
297  }
298 
299  default:
300  wxString msg;
301  msg.Printf( wxT( "(PCB_EDIT_FRAME::OnActionPlugin) needs work: "
302  "BOARD_ITEM type (%d) not handled" ),
303  item->Type() );
304  wxFAIL_MSG( msg );
305  break;
306  }
307  }
308 
309  // Mark deleted elements in undolist
310  for( unsigned int i = 0; i < deletedItemsList.GetCount(); i++ )
311  {
312  oldBuffer->PushItem( deletedItemsList.GetItemWrapper( i ) );
313  }
314 
315  // Find new modules
316  for( BOARD_ITEM* item = currentPcb->m_Modules; item != NULL; item = item->Next() )
317  {
318  if( !oldBuffer->ContainsItem( item ) )
319  {
320  ITEM_PICKER picker( item, UR_NEW );
321  oldBuffer->PushItem( picker );
322  }
323  }
324 
325  for( BOARD_ITEM* item = currentPcb->m_Track; item != NULL; item = item->Next() )
326  {
327  if( !oldBuffer->ContainsItem( item ) )
328  {
329  ITEM_PICKER picker( item, UR_NEW );
330  oldBuffer->PushItem( picker );
331  }
332  }
333 
334  for( BOARD_ITEM* item = currentPcb->m_Drawings; item != NULL; item = item->Next() )
335  {
336  if( !oldBuffer->ContainsItem( item ) )
337  {
338  ITEM_PICKER picker( item, UR_NEW );
339  oldBuffer->PushItem( picker );
340  }
341  }
342 
343  for( BOARD_ITEM* item = currentPcb->m_Zone; item != NULL; item = item->Next() )
344  {
345  if( !oldBuffer->ContainsItem( item ) )
346  {
347  ITEM_PICKER picker( item, UR_NEW );
348  oldBuffer->PushItem( picker );
349  }
350  }
351 
352  for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
353  {
354  if( !oldBuffer->ContainsItem( (EDA_ITEM*) currentPcb->GetArea( ii ) ) )
355  {
356  ITEM_PICKER picker( (EDA_ITEM*) currentPcb->GetArea(
357  ii ), UR_NEW );
358  oldBuffer->PushItem( picker );
359  }
360  }
361 
362 
363  GetScreen()->PushCommandToUndoList( oldBuffer );
364 
365  if( IsGalCanvasActive() )
366  {
368  }
369  else
370  {
371  GetScreen()->SetModify();
372  Refresh();
373  }
374  }
375 }
376 
377 
378 void PCB_EDIT_FRAME::RebuildActionPluginMenus()
379 {
380  wxMenu* actionMenu = GetMenuBar()->FindItem( ID_TOOLBARH_PCB_ACTION_PLUGIN )->GetSubMenu();
381 
382  if( !actionMenu ) // Should not occur.
383  return;
384 
385  // First, remove existing submenus, if they are too many
386  wxMenuItemList list = actionMenu->GetMenuItems();
387  // The first menuitems are the refresh menu and separator. do not count them
388  int act_menu_count = -2;
389 
390  std::vector<wxMenuItem*> available_menus;
391 
392  for( auto iter = list.begin(); iter != list.end(); ++iter, act_menu_count++ )
393  {
394  if( act_menu_count < 0 )
395  continue;
396 
397  wxMenuItem* item = *iter;
398 
399  if( act_menu_count < ACTION_PLUGINS::GetActionsCount() )
400  {
401  available_menus.push_back( item );
402  continue;
403  }
404 
405  // Remove menus which are not usable for our current plugin list
406  Disconnect( item->GetId(), wxEVT_COMMAND_MENU_SELECTED,
407  (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &
408  PCB_EDIT_FRAME::OnActionPlugin );
409  actionMenu->Delete( item );
410  }
411 
412  for( int ii = 0; ii < ACTION_PLUGINS::GetActionsCount(); ii++ )
413  {
414  wxMenuItem* item;
415 
416  if( ii < (int) available_menus.size() )
417  {
418  item = available_menus[ii];
419  item->SetItemLabel( ACTION_PLUGINS::GetAction( ii )->GetName() );
420  item->SetHelp( ACTION_PLUGINS::GetAction( ii )->GetDescription() );
421  }
422  else
423  {
424  item = AddMenuItem( actionMenu, wxID_ANY,
425  ACTION_PLUGINS::GetAction( ii )->GetName(),
426  ACTION_PLUGINS::GetAction( ii )->GetDescription(),
427  KiBitmap( hammer_xpm ) );
428 
429  Connect( item->GetId(), wxEVT_COMMAND_MENU_SELECTED,
430  (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &
431  PCB_EDIT_FRAME::OnActionPlugin );
432  }
433 
434  ACTION_PLUGINS::SetActionMenu( ii, item->GetId() );
435  }
436 }
437 
438 
439 #endif
DHEAD * GetList() const
Definition: base_struct.h:209
virtual void PushCommandToUndoList(PICKED_ITEMS_LIST *aItem)
Function PushCommandToUndoList add a command to undo in undo list delete the very old commands when t...
KICAD_T Type() const
Function Type()
Definition: base_struct.h:198
virtual void OnModify() override
Function OnModify must be called after a board change to set the modified flag.
Definition: pcbframe.cpp:994
Class ACTION_PLUGIN This is the parent class from where any action plugin class must derive...
wxString GetName() override
Function GetName.
wxString CallRetStrMethod(const char *aMethod, PyObject *aArglist=NULL)
static ACTION_PLUGIN * GetAction(wxString aName)
Function GetAction.
static wxString FROM_UTF8(const char *cstring)
function FROM_UTF8 converts a UTF8 encoded C string to a wxString for all wxWidgets build modes...
Definition: macros.h:53
void UseGalCanvas(bool aEnable) override
>
Definition: pcbframe.cpp:710
Class BOARD_ITEM is a base class for any item which can be embedded within the BOARD container class...
wxMenuItem * AddMenuItem(wxMenu *aMenu, int aId, const wxString &aText, const wxBitmap &aImage, wxItemKind aType=wxITEM_NORMAL)
Function AddMenuItem is an inline helper function to create and insert a menu item with an icon into ...
Definition: bitmap.cpp:55
Class BOARD to handle a board.
MODULE * Next() const
Definition: class_module.h:100
class ZONE_CONTAINER, a zone area
Definition: typeinfo.h:114
class TEXTE_PCB, text on a layer
Definition: typeinfo.h:104
BOARD * GetBoard() const
Classes to handle copper zones.
DLIST< SEGZONE > m_Zone
Definition: class_board.h:247
void PushItem(const ITEM_PICKER &aItem)
Function PushItem pushes aItem to the top of the list.
void register_action()
Function register_action It's the standard method of a "ACTION_PLUGIN" to register itself into the AC...
Functions relatives to tracks, vias and segments used to fill zones.
class TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:107
This file contains miscellaneous commonly used macros and functions.
SEGZONE * Next() const
Definition: class_track.h:358
BOARD_ITEM * Next() const
virtual PICKED_ITEMS_LIST * PopCommandFromUndoList()
PopCommandFromUndoList return the last command to undo and remove it from list nothing is deleted...
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Function KiBitmap constructs a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:36
bool ContainsItem(const EDA_ITEM *aItem) const
Function IsItemInList.
class MODULE, a footprint
Definition: typeinfo.h:101
static void deregister_action(PyObject *aPyAction)
void * GetObject() override
Function GetObject This method gets the pointer to the object from where this action constructs...
a helper to handle the real device context used in KiCad
wxString GetDescription() override
Function GetDescription.
void Refresh()
virtual void Run()=0
Function Run This method the the action.
static ACTION_PLUGIN * GetActionByMenu(int aMenu)
Function GetActionByMenu find action plugin associated to a menu id.
void Run() override
Function Run This method the the action.
static int GetActionsCount()
Function GetActionsCount.
DLIST< BOARD_ITEM > m_Drawings
Definition: class_board.h:241
bool IsGalCanvasActive() const
Function IsGalCanvasActive is used to check which canvas (GAL-based or standard) is currently in use...
Definition: draw_frame.h:809
class SEGZONE, a segment used to fill a zone area (segment on a copper layer)
Definition: typeinfo.h:109
PYTHON_ACTION_PLUGIN(PyObject *action)
int GetAreaCount() const
Function GetAreaCount.
Definition: class_board.h:1011
Class PICKED_ITEMS_LIST is a holder to handle information on schematic or board items.
void SaveCopyInUndoList(BOARD_ITEM *aItemToCopy, UNDO_REDO_T aTypeCommand, const wxPoint &aTransformPoint=wxPoint(0, 0)) override
Function SaveCopyInUndoList Creates a new entry in undo list of commands.
Definition: undo_redo.cpp:172
PyObject * CallMethod(const char *aMethod, PyObject *aArglist=NULL)
class DIMENSION, a dimension (graphic item)
Definition: typeinfo.h:112
class PCB_TARGET, a target (graphic item)
Definition: typeinfo.h:113
unsigned GetCount() const
Function GetCount.
class MARKER_PCB, a marker used to show something
Definition: typeinfo.h:111
TRACK * Next() const
Definition: class_track.h:98
ZONE_CONTAINER * GetArea(int index) const
Function GetArea returns the Area (Zone Container) at a given index.
Definition: class_board.h:982
Class to handle a graphic segment.
EDA_ITEM * GetPickedItem(unsigned int aIdx) const
Function GetPickedItem.
Class BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:169
static void SetActionMenu(int aIndex, int idMenu)
Function SetActionMenu Associate a menu id to an action plugin.
DLIST< MODULE > m_Modules
Definition: class_board.h:245
Class PCBNEW_ACTION_PLUGINS.
class NETINFO_ITEM, a description of a net
Definition: typeinfo.h:116
Class EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boa...
Definition: base_struct.h:151
wxString PyErrStringWithTraceback()
void ClearItemsList()
Function ClearItemsList deletes only the list of pickers, NOT the picked data itself.
PCB_SCREEN * GetScreen() const override
Function GetScreen returns a pointer to a BASE_SCREEN or one of its derivatives.
EDA_DRAW_PANEL_GAL * GetGalCanvas() const
Function GetGalCanvas returns a pointer to GAL-based canvas of given EDA draw frame.
Definition: draw_frame.h:817
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:108
DLIST< TRACK > m_Track
Definition: class_board.h:246
Module description (excepted pads)
class DRAWSEGMENT, a segment not on copper layers
Definition: typeinfo.h:103
static void register_action(PyObject *aPyAction)
ITEM_PICKER GetItemWrapper(unsigned int aIdx) const
Function GetItemWrapper.
static bool deregister_object(void *aObject)
Function deregister_object Anyone calls this method to deregister an object which builds a action...
wxString GetCategoryName() override
Function GetCategoryName.
int m_Status_Pcb
Flags used in ratsnest calculation and update.
Definition: class_board.h:237