KiCad PCB EDA Suite
footprint_editor_tools.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) 2014-2015 CERN
5  * @author Maciej Suminski <maciej.suminski@cern.ch>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include "footprint_editor_tools.h"
26 #include "kicad_clipboard.h"
27 #include "selection_tool.h"
28 #include "pcb_actions.h"
29 
30 #include <core/optional.h>
31 
32 #include <tool/tool_manager.h>
33 
34 #include <class_draw_panel_gal.h>
35 #include <view/view_controls.h>
36 #include <view/view_group.h>
37 #include <pcb_painter.h>
38 #include <origin_viewitem.h>
39 
40 #include <kicad_plugin.h>
41 #include <pcbnew_id.h>
42 #include <collectors.h>
43 #include <confirm.h>
45 #include <hotkeys.h>
46 #include <bitmaps.h>
47 
48 #include <pcb_edit_frame.h>
49 #include <class_board.h>
50 #include <class_module.h>
51 #include <class_edge_mod.h>
52 #include <board_commit.h>
53 
54 #include <tools/tool_event_utils.h>
55 
56 #include <functional>
57 using namespace std::placeholders;
58 #include <wx/defs.h>
59 
60 // Module editor tools
61 TOOL_ACTION PCB_ACTIONS::placePad( "pcbnew.ModuleEditor.placePad",
62  AS_GLOBAL, 0,
63  _( "Add Pad" ), _( "Add a pad" ), NULL, AF_ACTIVATE );
64 
65 TOOL_ACTION PCB_ACTIONS::createPadFromShapes( "pcbnew.ModuleEditor.createPadFromShapes",
66  AS_CONTEXT, 0,
67  _( "Create Pad from Selected Shapes" ),
68  _( "Creates a custom-shaped pads from a set of selected shapes" ),
69  primitives_to_custom_pad_xpm );
70 
71 TOOL_ACTION PCB_ACTIONS::explodePadToShapes( "pcbnew.ModuleEditor.explodePadToShapes",
72  AS_CONTEXT, 0,
73  _( "Explode Pad to Graphic Shapes" ),
74  _( "Converts a custom-shaped pads to a set of graphical shapes" ),
75  custom_pad_to_primitives_xpm );
76 
77 TOOL_ACTION PCB_ACTIONS::enumeratePads( "pcbnew.ModuleEditor.enumeratePads",
78  AS_GLOBAL, 0,
79  _( "Renumber Pads..." ), _( "Renumber pads by clicking on them in the desired order" ), pad_enumerate_xpm, AF_ACTIVATE );
80 
81 
83  PCB_TOOL( "pcbnew.ModuleEditor" )
84 {
85 }
86 
87 
89 {
90 }
91 
92 
94 {
95 }
96 
97 
98 
100 {
101  struct PAD_PLACER : public INTERACTIVE_PLACER_BASE
102  {
103  std::unique_ptr<BOARD_ITEM> CreateItem() override
104  {
105  D_PAD* pad = new D_PAD ( m_board->m_Modules );
106  m_frame->Import_Pad_Settings( pad, false ); // use the global settings for pad
107  pad->IncrementPadName( true, true );
108  return std::unique_ptr<BOARD_ITEM>( pad );
109  }
110 
111  void PlaceItem( BOARD_ITEM *aItem, BOARD_COMMIT& aCommit ) override
112  {
113  auto pad = dynamic_cast<D_PAD*>( aItem );
114  m_frame->Export_Pad_Settings( pad );
115  aCommit.Add( aItem );
116  }
117  };
118 
119  PAD_PLACER placer;
120 
121  frame()->SetToolID( ID_MODEDIT_PAD_TOOL, wxCURSOR_PENCIL, _( "Add pads" ) );
122 
123  assert( board()->m_Modules );
124 
125  doInteractiveItemPlacement( &placer, _( "Place pad" ), IPO_REPEAT | IPO_SINGLE_CLICK | IPO_ROTATE | IPO_FLIP );
126 
128 
129  return 0;
130 }
131 
132 
134 {
135  if( !board()->m_Modules || !board()->m_Modules->PadsList() )
136  return 0;
137 
138  DIALOG_ENUM_PADS settingsDlg( frame() );
139 
140  if( settingsDlg.ShowModal() != wxID_OK )
141  return 0;
142 
143  Activate();
144 
145  GENERAL_COLLECTOR collector;
146  const KICAD_T types[] = { PCB_PAD_T, EOT };
147 
149  guide.SetIgnoreMTextsMarkedNoShow( true );
150  guide.SetIgnoreMTextsOnBack( true );
151  guide.SetIgnoreMTextsOnFront( true );
152  guide.SetIgnoreModulesVals( true );
153  guide.SetIgnoreModulesRefs( true );
154 
155  int padNumber = settingsDlg.GetStartNumber();
156  wxString padPrefix = settingsDlg.GetPrefix();
157 
158  frame()->SetToolID( ID_MODEDIT_PAD_TOOL, wxCURSOR_HAND,
159  _( "Click on successive pads to renumber them" ) );
160 
162  getViewControls()->ShowCursor( true );
163 
165  VECTOR2I oldCursorPos; // store the previous mouse cursor position, during mouse drag
166  std::list<D_PAD*> selectedPads;
167  BOARD_COMMIT commit( frame() );
168  std::map<wxString, wxString> oldNames;
169  bool isFirstPoint = true; // used to be sure oldCursorPos will be initialized at least once.
170 
171  while( OPT_TOOL_EVENT evt = Wait() )
172  {
173  if( evt->IsDrag( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) )
174  {
175  selectedPads.clear();
176  VECTOR2I cursorPos = getViewControls()->GetCursorPosition();
177 
178  // Be sure the old cursor mouse position was initialized:
179  if( isFirstPoint )
180  {
181  oldCursorPos = cursorPos;
182  isFirstPoint = false;
183  }
184 
185  // wxWidgets deliver mouse move events not frequently enough, resulting in skipping
186  // pads if the user moves cursor too fast. To solve it, create a line that approximates
187  // the mouse move and search pads that are on the line.
188  int distance = ( cursorPos - oldCursorPos ).EuclideanNorm();
189  // Search will be made every 0.1 mm:
190  int segments = distance / int( 0.1*IU_PER_MM ) + 1;
191  const wxPoint line_step( ( cursorPos - oldCursorPos ) / segments );
192 
193  collector.Empty();
194 
195  for( int j = 0; j < segments; ++j )
196  {
197  wxPoint testpoint( cursorPos.x - j * line_step.x,
198  cursorPos.y - j * line_step.y );
199  collector.Collect( board(), types, testpoint, guide );
200 
201  for( int i = 0; i < collector.GetCount(); ++i )
202  {
203  selectedPads.push_back( static_cast<D_PAD*>( collector[i] ) );
204  }
205  }
206 
207  selectedPads.unique();
208 
209  for( D_PAD* pad : selectedPads )
210  {
211  // If pad was not selected, then enumerate it
212  if( !pad->IsSelected() )
213  {
214  commit.Modify( pad );
215 
216  // Rename pad and store the old name
217  wxString newName = wxString::Format( wxT( "%s%d" ), padPrefix.c_str(), padNumber++ );
218  oldNames[newName] = pad->GetName();
219  pad->SetName( newName );
220  pad->SetSelected();
221  getView()->Update( pad );
222  }
223 
224  // ..or restore the old name if it was enumerated and clicked again
225  else if( pad->IsSelected() && evt->IsClick( BUT_LEFT ) )
226  {
227  auto it = oldNames.find( pad->GetName() );
228  wxASSERT( it != oldNames.end() );
229 
230  if( it != oldNames.end() )
231  {
232  pad->SetName( it->second );
233  oldNames.erase( it );
234  }
235 
236  pad->ClearSelected();
237  getView()->Update( pad );
238  }
239  }
240  }
241 
242  else if( ( evt->IsKeyPressed() && evt->KeyCode() == WXK_RETURN ) ||
243  evt->IsDblClick( BUT_LEFT ) )
244  {
245  commit.Push( _( "Renumber pads" ) );
246  break;
247  }
248 
249  else if( evt->IsCancel() || TOOL_EVT_UTILS::IsCancelInteractive( *evt ) || evt->IsActivate() )
250  {
251  commit.Revert();
252  break;
253  }
254 
255  else
256  {
257  // Delegate BUT_RIGHT, etc. to SELECTION_TOOL
258  m_toolMgr->PassEvent();
259  }
260 
261  // Prepare the next loop by updating the old cursor mouse position
262  // to this last mouse cursor position
263  oldCursorPos = getViewControls()->GetCursorPosition();
264  }
265 
266  for( auto p : board()->m_Modules->Pads() )
267  {
268  p->ClearSelected();
269  view->Update( p );
270  }
271 
273  frame()->GetGalCanvas()->SetCursor( wxCURSOR_ARROW );
274 
275  return 0;
276 }
277 
278 
280 {
281  SELECTION& selection = m_toolMgr->GetTool<SELECTION_TOOL>()->GetSelection();
282  BOARD_COMMIT commit( frame() );
283 
284  if( selection.Size() != 1 )
285  return 0;
286 
287  if( selection[0]->Type() != PCB_PAD_T )
288  return 0;
289 
290  auto pad = static_cast<D_PAD*>( selection[0] );
291 
292  if( pad->GetShape() != PAD_SHAPE_CUSTOM )
293  return 0;
294 
295  commit.Modify( pad );
296 
297  wxPoint anchor = pad->GetPosition();
298 
299  for( auto prim : pad->GetPrimitives() )
300  {
301  auto ds = new EDGE_MODULE( board()->m_Modules );
302 
303  prim.ExportTo( ds ); // ExportTo exports to a DRAWSEGMENT
304  // Fix an arbitray draw layer for this EDGE_MODULE
305  ds->SetLayer( Dwgs_User ); //pad->GetLayer() );
306  ds->Move( anchor );
307 
308  commit.Add( ds );
309  }
310 
311  pad->SetShape( pad->GetAnchorPadShape() );
312  // Cleanup the pad primitives data, because the initial pad was a custom
313  // shaped pad, and it contains primitives, that does not exist in non custom pads,
314  // and can create issues later:
315  if( pad->GetShape() != PAD_SHAPE_CUSTOM ) // should be always the case
316  {
317  pad->DeletePrimitivesList();
318  }
319 
320  commit.Push( _("Explode pad to shapes") );
321 
323 
324  return 0;
325 }
326 
327 
329 {
330  SELECTION& selection = m_toolMgr->GetTool<SELECTION_TOOL>()->GetSelection();
331 
332  std::unique_ptr<D_PAD> pad ( new D_PAD ( board()->m_Modules ) );
333  D_PAD *refPad = nullptr;
334  bool multipleRefPadsFound = false;
335  bool illegalItemsFound = false;
336 
337  std::vector<PAD_CS_PRIMITIVE> shapes;
338 
339  BOARD_COMMIT commit( frame() );
340 
341  for( auto item : selection )
342  {
343  switch( item->Type() )
344  {
345  case PCB_PAD_T:
346  {
347  if( refPad )
348  multipleRefPadsFound = true;
349 
350  refPad = static_cast<D_PAD*>( item );
351  break;
352  }
353 
354  case PCB_MODULE_EDGE_T:
355  {
356  auto em = static_cast<EDGE_MODULE*> ( item );
357 
358  // Currently, S_CURVE shape is not supported. so warn the user
359  if( em->GetShape() == S_CURVE )
360  {
361  illegalItemsFound = true;
362  break;
363  }
364 
365  PAD_CS_PRIMITIVE shape( em->GetShape() );
366  shape.m_Start = em->GetStart();
367  shape.m_End = em->GetEnd();
368  shape.m_Radius = em->GetRadius();
369  shape.m_Thickness = em->GetWidth();
370  shape.m_ArcAngle = em->GetAngle();
371  shape.m_Poly = em->BuildPolyPointsList();
372 
373  shapes.push_back(shape);
374 
375  break;
376  }
377 
378  default:
379  {
380  illegalItemsFound = true;
381  break;
382  }
383  }
384  }
385 
386  if( refPad && selection.Size() == 1 )
387  {
388  // don't convert a pad into itself...
389  return 0;
390  }
391 
392  if( multipleRefPadsFound )
393  {
395  _( "Cannot convert items to a custom-shaped pad:\n"
396  "selection contains more than one reference pad." ) );
397  return 0;
398  }
399 
400  if( illegalItemsFound )
401  {
403  _( "Cannot convert items to a custom-shaped pad:\n"
404  "selection contains unsupported items.\n"
405  "Only graphical lines, circles, arcs and polygons are allowed." ) );
406  return 0;
407  }
408 
409  if( refPad )
410  {
411  pad.reset( static_cast<D_PAD*>( refPad->Clone() ) );
412 
413  if( refPad->GetShape() == PAD_SHAPE_RECT )
414  pad->SetAnchorPadShape( PAD_SHAPE_RECT );
415 
416  // ignore the pad orientation and offset for the moment. Makes more trouble than it's worth.
417  pad->SetOrientation( 0 );
418  pad->SetOffset( wxPoint( 0, 0 ) );
419  }
420  else
421  {
422  // Create a default pad anchor:
423  pad->SetAnchorPadShape( PAD_SHAPE_CIRCLE );
424  pad->SetAttribute( PAD_ATTRIB_SMD );
425  pad->SetLayerSet( D_PAD::SMDMask() );
426  int radius = Millimeter2iu( 0.2 );
427  pad->SetSize ( wxSize( radius, radius ) );
428  pad->IncrementPadName( true, true );
429  pad->SetOrientation( 0 );
430  }
431 
432  pad->SetShape ( PAD_SHAPE_CUSTOM );
433 
434  OPT<VECTOR2I> anchor;
435  VECTOR2I tmp;
436 
437  if( refPad )
438  {
439  anchor = VECTOR2I( pad->GetPosition() );
440  }
441  else if( pad->GetBestAnchorPosition( tmp ) )
442  {
443  anchor = tmp;
444  }
445 
446  if( !anchor )
447  {
449  _( "Cannot convert items to a custom-shaped pad:\n"
450  "unable to determine the anchor point position.\n"
451  "Consider adding a small anchor pad to the selection and try again.") );
452  return 0;
453  }
454 
455 
456  // relocate the shapes, they are relative to the anchor pad position
457  for( auto& shape : shapes )
458  {
459  shape.Move( wxPoint( -anchor->x, -anchor->y ) );
460  }
461 
462 
463  pad->SetPosition( wxPoint( anchor->x, anchor->y ) );
464  pad->AddPrimitives( shapes );
465  pad->ClearFlags();
466 
467  bool result = pad->MergePrimitivesAsPolygon();
468 
469  if( !result )
470  {
472  _( "Cannot convert items to a custom-shaped pad:\n"
473  "selected items do not form a single solid shape.") );
474  return 0;
475  }
476 
477  auto padPtr = pad.release();
478 
479  commit.Add( padPtr );
480  for ( auto item : selection )
481  {
482  commit.Remove( item );
483  }
484 
486  commit.Push(_("Create Pad from Selected Shapes") );
487  m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, padPtr );
488 
489  return 0;
490 }
491 
493 {
498 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:112
void Empty()
Function Empty sets the list to empty.
Definition: collector.h:123
static TOOL_ACTION selectionClear
Clears the current selection.
Definition: pcb_actions.h:53
virtual void ShowCursor(bool aEnabled)
Function ShowCursor() Enables or disables display of cursor.
bool IncrementPadName(bool aSkipUnconnectable, bool aFillSequenceGaps)
Function IncrementPadName.
Definition: class_pad.cpp:532
void Reset(RESET_REASON aReason) override
Function Reset() Brings the tool to a known, initial state.
int GetCount() const
Function GetCount returns the number of objects in the list.
Definition: collector.h:114
EDA_ITEM * Clone() const override
Function Clone creates a duplicate of this item with linked list members set to NULL.
Definition: class_pad.cpp:1244
COMMIT & Modify(EDA_ITEM *aItem)
Modifies a given item in the model.
Definition: commit.h:103
virtual void SetToolID(int aId, int aCursor, const wxString &aToolMsg) override
Set the tool command ID to aId and sets the cursor to aCursor.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Function DisplayErrorMessage displays an error message with aMessage.
Definition: confirm.cpp:259
This file is part of the common library.
Class BOARD_ITEM is a base class for any item which can be embedded within the BOARD container class...
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition: class_pad.cpp:106
COMMIT & Add(EDA_ITEM *aItem)
Adds a new item to the model
Definition: commit.h:78
VIEW_CONTROLS class definition.
void Collect(BOARD_ITEM *aItem, const KICAD_T aScanList[], const wxPoint &aRefPos, const COLLECTORS_GUIDE &aGuide)
Scan a BOARD_ITEM using this class&#39;s Inspector method, which does the collection. ...
Definition: collectors.cpp:468
void SetIgnoreModulesVals(bool ignore)
Definition: collectors.h:583
Class SELECTION_TOOL.
Class BOARD to handle a board.
virtual void Revert() override
Revertes the commit by restoring the modifed items state.
wxPoint m_Start
angle of an arc, from its starting point, in 0.1 deg
Definition: class_pad.h:100
static TOOL_ACTION explodePadToShapes
Definition: pcb_actions.h:312
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:215
static TOOL_ACTION enumeratePads
Tool for quick pad enumeration.
Definition: pcb_actions.h:315
Smd pad, appears on the solder paste layer (default)
Definition: pad_shapes.h:61
OPT_TOOL_EVENT Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Function Wait()
wxString GetPrefix() const
Returns common prefix for all enumerated pads.
void SetIgnoreModulesRefs(bool ignore)
Definition: collectors.h:589
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Function RunAction() Runs the specified action.
Definition: tool_manager.h:125
static TOOL_ACTION placePad
Activation of the drawing tool (placing a PAD)
Definition: pcb_actions.h:309
class D_PAD, a pad in a footprint
Definition: typeinfo.h:90
int CreatePadFromShapes(const TOOL_EVENT &aEvent)
Function CreatePadFromShapes()
VECTOR2< int > VECTOR2I
Definition: vector2d.h:587
PAD_SHAPE_T GetShape() const
Function GetShape.
Definition: class_pad.h:216
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Function Go()
class EDGE_MODULE, a footprint edge
Definition: typeinfo.h:94
Implementing DIALOG_ENUM_PADS_BASE.
search types array terminator (End Of Types)
Definition: typeinfo.h:82
KICAD_T
Enum KICAD_T is the set of class identification values, stored in EDA_ITEM::m_StructType.
Definition: typeinfo.h:78
Pcbnew hotkeys.
void PassEvent()
Allows a tool to pass the already handled event to the next tool on the stack.
Definition: tool_manager.h:351
int ExplodePadToShapes(const TOOL_EVENT &aEvent)
Function ExplodePadToShapes()
GENERAL_COLLECTORS_GUIDE GetCollectorsGuide()
Function GetCollectorsGuide.
int PlacePad(const TOOL_EVENT &aEvent)
Function PlacePad() Places a pad in module editor.
void SetIgnoreMTextsOnBack(bool ignore)
Definition: collectors.h:541
void SetIgnoreMTextsOnFront(bool ignore)
Definition: collectors.h:547
VECTOR2D GetCursorPosition() const
Returns the current cursor position in world coordinates.
PCB_EDIT_FRAME * frame() const
Definition: pcb_tool.h:139
Class TOOL_EVENT.
Definition: tool_event.h:168
Helper class to handle a primitive (basic shape: polygon, segment, circle or arc) to build a custom p...
Definition: class_pad.h:91
void setTransitions() override
Sets up handlers for various events.
VIEW_GROUP extends VIEW_ITEM by possibility of grouping items into a single object.
COMMIT & Remove(EDA_ITEM *aItem)
Removes a new item from the model
Definition: commit.h:90
const SELECTION & selection() const
Definition: pcb_tool.cpp:245
Bezier Curve.
bool IsCancelInteractive(const TOOL_EVENT &aEvt)
Function IsCancelInteractive()
All active tools
Definition: tool_event.h:144
void SetIgnoreMTextsMarkedNoShow(bool ignore)
Definition: collectors.h:535
virtual void Update(VIEW_ITEM *aItem, int aUpdateFlags)
For dynamic VIEWs, informs the associated VIEW that the graphical representation of this item has cha...
Definition: view.cpp:1521
Subclass of DIALOG_ENUM_PADS_BASE, which is generated by wxFormBuilder.
KIGFX::VIEW_CONTROLS * getViewControls() const
Function getViewControls()
Definition: tool_base.cpp:41
KIGFX::VIEW * getView() const
Function getView()
Definition: tool_base.cpp:35
static TOOL_ACTION createPadFromShapes
Definition: pcb_actions.h:311
virtual void SetNoToolSelected()
Select the ID_NO_TOOL_SELECTED id tool (Idle tool)
int GetStartNumber() const
Returns the starting number that is going to be used for the first enumerated pad.
int EnumeratePads(const TOOL_EVENT &aEvent)
Function EnumeratePads() Tool for quick pad enumeration.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
KIGFX::PCB_VIEW * view() const
Definition: pcb_tool.h:137
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:242
Class TOOL_ACTION.
Definition: tool_action.h:46
size_t i
Definition: json11.cpp:597
static TOOL_ACTION selectItem
Selects an item (specified as the event parameter).
Definition: pcb_actions.h:56
virtual void Push(const wxString &aMessage=wxT("A commit"), bool aCreateUndoEntry=true, bool aSetDirtyBit=true) override
Executes the changes.
RESET_REASON
Determines the reason of reset for a tool
Definition: tool_base.h:80
boost::optional< T > OPT
Definition: optional.h:7
void Activate()
Function Activate() Runs the tool.
EDA_DRAW_PANEL_GAL * GetGalCanvas() const
Return a pointer to GAL-based canvas of given EDA draw frame.
Definition: draw_frame.h:918
int Size() const
Returns the number of selected parts.
Definition: selection.h:122
Module description (excepted pads)
void doInteractiveItemPlacement(INTERACTIVE_PLACER_BASE *aPlacer, const wxString &aCommitMessage, int aOptions=IPO_ROTATE|IPO_FLIP|IPO_REPEAT)
Helper function for performing a common interactive idiom: wait for a left click, place an item there...
Definition: pcb_tool.cpp:39
Class VIEW.
Definition: view.h:61
A general implementation of a COLLECTORS_GUIDE.
Definition: collectors.h:386
KIGFX::VIEW * GetView() const
Definition: tool_manager.h:252
EDGE_MODULE class definition.
OPT< TOOL_EVENT > OPT_TOOL_EVENT
Definition: tool_event.h:495
BOARD * board() const
Definition: pcb_tool.h:140