KiCad PCB EDA Suite
pad_tool.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-2022 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
24
25#include "pad_tool.h"
26#include <macros.h>
28#include <view/view_controls.h>
29#include <tool/tool_manager.h>
31#include <board_item.h>
32#include <footprint.h>
33#include <fp_shape.h>
34#include <pad.h>
35#include <pcbnew_settings.h>
36#include <board_commit.h>
38#include <tools/pcb_actions.h>
42#include <tools/edit_tool.h>
44#include <widgets/infobar.h>
45
47 PCB_TOOL_BASE( "pcbnew.PadTool" ),
48 m_wasHighContrast( false ),
49 m_editPad( niluuid )
50{}
51
52
54{}
55
56
58{
59 if( aReason == MODEL_RELOAD )
60 m_lastPadNumber = wxT( "1" );
61
62 if( board() && board()->GetItem( m_editPad ) == DELETED_BOARD_ITEM::GetInstance() )
63 {
65 bool highContrast = ( opts.m_ContrastModeDisplay != HIGH_CONTRAST_MODE::NORMAL );
66
67 if( m_wasHighContrast != highContrast )
69
70 frame()->GetInfoBar()->Dismiss();
71
73 }
74}
75
76
78{
80
81 if( selTool )
82 {
83 // Add context menu entries that are displayed when selection tool is active
84 CONDITIONAL_MENU& menu = selTool->GetToolMenu().GetMenu();
85
89
90 auto explodeCondition =
91 [&]( const SELECTION& aSel )
92 {
93 return m_editPad == niluuid && aSel.Size() == 1 && aSel[0]->Type() == PCB_PAD_T;
94 };
95
96 auto recombineCondition =
97 [&]( const SELECTION& aSel )
98 {
99 return m_editPad != niluuid;
100 };
101
102 menu.AddSeparator( 400 );
103
105 {
107 menu.AddItem( PCB_ACTIONS::recombinePad, recombineCondition, 400 );
108 menu.AddItem( PCB_ACTIONS::explodePad, explodeCondition, 400 );
109 }
110
111 menu.AddItem( PCB_ACTIONS::copyPadSettings, singlePadSel, 400 );
112 menu.AddItem( PCB_ACTIONS::applyPadSettings, padSel, 400 );
113 menu.AddItem( PCB_ACTIONS::pushPadSettings, singlePadSel, 400 );
114 }
115
116 auto& ctxMenu = m_menu.GetMenu();
117
118 // cancel current tool goes in main context menu at the top if present
120 ctxMenu.AddSeparator( 1 );
121
128
129 // Finally, add the standard zoom/grid items
130 getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( m_menu );
131
132 return true;
133}
134
135
137{
139 const PCB_SELECTION& selection = selTool->GetSelection();
140 const PAD* masterPad = frame()->GetDesignSettings().m_Pad_Master.get();
141
142 BOARD_COMMIT commit( frame() );
143
144 // for every selected pad, paste global settings
145 for( EDA_ITEM* item : selection )
146 {
147 if( item->Type() == PCB_PAD_T )
148 {
149 commit.Modify( item );
150 static_cast<PAD&>( *item ).ImportSettingsFrom( *masterPad );
151 }
152 }
153
154 commit.Push( _( "Paste Pad Properties" ) );
155
157 frame()->Refresh();
158
159 return 0;
160}
161
162
164{
166 const PCB_SELECTION& selection = selTool->GetSelection();
167
168 // can only copy from a single pad
169 if( selection.Size() == 1 )
170 {
171 EDA_ITEM* item = selection[0];
172
173 if( item->Type() == PCB_PAD_T )
174 {
175 const PAD& selPad = static_cast<const PAD&>( *item );
176 frame()->GetDesignSettings().m_Pad_Master->ImportSettingsFrom( selPad );
177 }
178 }
179
180 return 0;
181}
182
183
184static void doPushPadProperties( BOARD& board, const PAD& aSrcPad, BOARD_COMMIT& commit,
185 bool aSameFootprints,
186 bool aPadShapeFilter,
187 bool aPadOrientFilter,
188 bool aPadLayerFilter,
189 bool aPadTypeFilter )
190{
191 const FOOTPRINT* refFootprint = aSrcPad.GetParent();
192
193 EDA_ANGLE srcPadAngle = aSrcPad.GetOrientation() - refFootprint->GetOrientation();
194
195 for( FOOTPRINT* footprint : board.Footprints() )
196 {
197 if( !aSameFootprints && ( footprint != refFootprint ) )
198 continue;
199
200 if( footprint->GetFPID() != refFootprint->GetFPID() )
201 continue;
202
203 for( PAD* pad : footprint->Pads() )
204 {
205 if( aPadShapeFilter && ( pad->GetShape() != aSrcPad.GetShape() ) )
206 continue;
207
208 EDA_ANGLE padAngle = pad->GetOrientation() - footprint->GetOrientation();
209
210 if( aPadOrientFilter && ( padAngle != srcPadAngle ) )
211 continue;
212
213 if( aPadLayerFilter && ( pad->GetLayerSet() != aSrcPad.GetLayerSet() ) )
214 continue;
215
216 if( aPadTypeFilter && ( pad->GetAttribute() != aSrcPad.GetAttribute() ) )
217 continue;
218
219 // Special-case for aperture pads
220 if( aPadTypeFilter && pad->GetAttribute() == PAD_ATTRIB::CONN )
221 {
222 if( pad->IsAperturePad() != aSrcPad.IsAperturePad() )
223 continue;
224 }
225
226 commit.Modify( pad );
227
228 // Apply source pad settings to this pad
229 pad->ImportSettingsFrom( aSrcPad );
230 }
231 }
232}
233
234
236{
238 const PCB_SELECTION& selection = selTool->GetSelection();
239 PAD* srcPad;
240
241 if( selection.Size() == 1 && selection[0]->Type() == PCB_PAD_T )
242 srcPad = static_cast<PAD*>( selection[0] );
243 else
244 return 0;
245
246 FOOTPRINT* footprint = srcPad->GetParent();
247
248 if( !footprint )
249 return 0;
250
252
254 int dialogRet = dlg.ShowModal();
255
256 if( dialogRet == wxID_CANCEL )
257 return 0;
258
259 const bool edit_Same_Modules = (dialogRet == 1);
260
261 BOARD_COMMIT commit( frame() );
262
263 doPushPadProperties( *getModel<BOARD>(), *srcPad, commit, edit_Same_Modules,
268
269 commit.Push( _( "Push Pad Settings" ) );
270
272 frame()->Refresh();
273
274 return 0;
275}
276
277
279{
280 if( !board()->GetFirstFootprint() || board()->GetFirstFootprint()->Pads().empty() )
281 return 0;
282
283 GENERAL_COLLECTOR collector;
285 guide.SetIgnoreMTextsMarkedNoShow( true );
286 guide.SetIgnoreMTextsOnBack( true );
287 guide.SetIgnoreMTextsOnFront( true );
288 guide.SetIgnoreModulesVals( true );
289 guide.SetIgnoreModulesRefs( true );
290
291 DIALOG_ENUM_PADS settingsDlg( frame() );
292
293 if( settingsDlg.ShowModal() != wxID_OK )
294 return 0;
295
296 int seqPadNum = settingsDlg.GetStartNumber();
297 wxString padPrefix = settingsDlg.GetPrefix();
298 std::deque<int> storedPadNumbers;
299 std::map<wxString, std::pair<int, wxString>> oldNumbers;
300
302
303 frame()->PushTool( aEvent );
304
305 VECTOR2I oldCursorPos; // store the previous mouse cursor position, during mouse drag
306 std::list<PAD*> selectedPads;
307 BOARD_COMMIT commit( frame() );
308 bool isFirstPoint = true; // make sure oldCursorPos is initialized at least once
309 PADS pads = board()->GetFirstFootprint()->Pads();
310
311 MAGNETIC_SETTINGS mag_settings;
312 mag_settings.graphics = false;
313 mag_settings.tracks = MAGNETIC_OPTIONS::NO_EFFECT;
315 PCB_GRID_HELPER grid( m_toolMgr, &mag_settings );
316
317 grid.SetSnap( true );
318 grid.SetUseGrid( false );
319
320 auto setCursor =
321 [&]()
322 {
324 };
325
326 Activate();
327 // Must be done after Activate() so that it gets set into the correct context
328 getViewControls()->ShowCursor( true );
330 // Set initial cursor
331 setCursor();
332
333 STATUS_TEXT_POPUP statusPopup( frame() );
334 wxString msg = _( "Click on pad %s%d\nPress <esc> to cancel all; double-click to finish" );
335 statusPopup.SetText( wxString::Format( msg, padPrefix, seqPadNum ) );
336 statusPopup.Popup();
337 statusPopup.Move( wxGetMousePosition() + wxPoint( 20, 20 ) );
338 canvas()->SetStatusPopup( statusPopup.GetPanel() );
339
340 while( TOOL_EVENT* evt = Wait() )
341 {
342 setCursor();
343
344 VECTOR2I cursorPos = grid.AlignToNearestPad( getViewControls()->GetMousePosition(), pads );
345 getViewControls()->ForceCursorPosition( true, cursorPos );
346
347 if( evt->IsCancelInteractive() )
348 {
350 commit.Revert();
351
352 frame()->PopTool( aEvent );
353 break;
354 }
355 else if( evt->IsActivate() )
356 {
357 commit.Push( _( "Renumber pads" ) );
358
359 frame()->PopTool( aEvent );
360 break;
361 }
362 else if( evt->IsDrag( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) )
363 {
364 selectedPads.clear();
365
366 // Be sure the old cursor mouse position was initialized:
367 if( isFirstPoint )
368 {
369 oldCursorPos = cursorPos;
370 isFirstPoint = false;
371 }
372
373 // wxWidgets deliver mouse move events not frequently enough, resulting in skipping
374 // pads if the user moves cursor too fast. To solve it, create a line that approximates
375 // the mouse move and search pads that are on the line.
376 int distance = ( cursorPos - oldCursorPos ).EuclideanNorm();
377 // Search will be made every 0.1 mm:
378 int segments = distance / int( 0.1 * pcbIUScale.IU_PER_MM ) + 1;
379 const wxPoint line_step( ( cursorPos - oldCursorPos ) / segments );
380
381 collector.Empty();
382
383 for( int j = 0; j < segments; ++j )
384 {
385 wxPoint testpoint( cursorPos.x - j * line_step.x, cursorPos.y - j * line_step.y );
386 collector.Collect( board(), { PCB_PAD_T }, testpoint, guide );
387
388 for( int i = 0; i < collector.GetCount(); ++i )
389 selectedPads.push_back( static_cast<PAD*>( collector[i] ) );
390 }
391
392 selectedPads.unique();
393
394 for( PAD* pad : selectedPads )
395 {
396 // If pad was not selected, then enumerate it
397 if( !pad->IsSelected() )
398 {
399 commit.Modify( pad );
400
401 // Rename pad and store the old name
402 int newval;
403
404 if( storedPadNumbers.size() > 0 )
405 {
406 newval = storedPadNumbers.front();
407 storedPadNumbers.pop_front();
408 }
409 else
410 newval = seqPadNum++;
411
412 wxString newNumber = wxString::Format( wxT( "%s%d" ), padPrefix, newval );
413 oldNumbers[newNumber] = { newval, pad->GetNumber() };
414 pad->SetNumber( newNumber );
415 SetLastPadNumber( newNumber );
416 pad->SetSelected();
417 getView()->Update( pad );
418
419 // Ensure the popup text shows the correct next value
420 if( storedPadNumbers.size() > 0 )
421 newval = storedPadNumbers.front();
422 else
423 newval = seqPadNum;
424
425 statusPopup.SetText( wxString::Format( msg, padPrefix, newval ) );
426 }
427
428 // ... or restore the old name if it was enumerated and clicked again
429 else if( pad->IsSelected() && evt->IsClick( BUT_LEFT ) )
430 {
431 auto it = oldNumbers.find( pad->GetNumber() );
432 wxASSERT( it != oldNumbers.end() );
433
434 if( it != oldNumbers.end() )
435 {
436 storedPadNumbers.push_back( it->second.first );
437 pad->SetNumber( it->second.second );
438 SetLastPadNumber( it->second.second );
439 oldNumbers.erase( it );
440
441 int newval = storedPadNumbers.front();
442
443 statusPopup.SetText( wxString::Format( msg, padPrefix, newval ) );
444 }
445
446 pad->ClearSelected();
447 getView()->Update( pad );
448 }
449 }
450 }
451 else if( evt->IsDblClick( BUT_LEFT ) )
452 {
453 commit.Push( _( "Renumber pads" ) );
454 frame()->PopTool( aEvent );
455 break;
456 }
457 else if( evt->IsClick( BUT_RIGHT ) )
458 {
460 }
461 else
462 {
463 evt->SetPassEvent();
464 }
465
466 // Prepare the next loop by updating the old cursor mouse position
467 // to this last mouse cursor position
468 oldCursorPos = getViewControls()->GetCursorPosition();
469 statusPopup.Move( wxGetMousePosition() + wxPoint( 20, 20 ) );
470 }
471
472 for( PAD* p : board()->GetFirstFootprint()->Pads() )
473 {
474 p->ClearSelected();
475 getView()->Update( p );
476 }
477
478 canvas()->SetStatusPopup( nullptr );
479 statusPopup.Hide();
480
483 return 0;
484}
485
486
487int PAD_TOOL::PlacePad( const TOOL_EVENT& aEvent )
488{
489 if( !board()->GetFirstFootprint() )
490 return 0;
491
492 struct PAD_PLACER : public INTERACTIVE_PLACER_BASE
493 {
494 PAD_PLACER( PAD_TOOL* aPadTool )
495 {
496 m_padTool = aPadTool;
497 }
498
499 virtual ~PAD_PLACER()
500 {
501 }
502
503 std::unique_ptr<BOARD_ITEM> CreateItem() override
504 {
505 PAD* pad = new PAD( m_board->GetFirstFootprint() );
506 PAD* master = m_frame->GetDesignSettings().m_Pad_Master.get();
507
508 pad->ImportSettingsFrom( *master );
509
510 // If the footprint type and master pad type directly conflict then make some
511 // adjustments. Otherwise assume the user set what they wanted.
512 if( ( m_board->GetFirstFootprint()->GetAttributes() & FP_SMD )
513 && master->GetAttribute() == PAD_ATTRIB::PTH )
514 {
515 pad->SetAttribute( PAD_ATTRIB::SMD );
516 pad->SetShape( PAD_SHAPE::ROUNDRECT );
517 pad->SetSizeX( 1.5 * pad->GetSizeY() );
518 pad->SetLayerSet( PAD::SMDMask() );
519 }
520 else if( ( m_board->GetFirstFootprint()->GetAttributes() & FP_THROUGH_HOLE )
521 && master->GetAttribute() == PAD_ATTRIB::SMD )
522 {
523 pad->SetAttribute( PAD_ATTRIB::PTH );
524 pad->SetShape( PAD_SHAPE::CIRCLE );
525 pad->SetSize( wxSize( pad->GetSizeX(), pad->GetSizeX() ) );
526 pad->SetLayerSet( PAD::PTHMask() );
527 }
528
529 if( pad->CanHaveNumber() )
530 {
531 wxString padNumber = m_padTool->GetLastPadNumber();
532 padNumber = m_board->GetFirstFootprint()->GetNextPadNumber( padNumber );
533 pad->SetNumber( padNumber );
534 m_padTool->SetLastPadNumber( padNumber );
535 }
536
537 return std::unique_ptr<BOARD_ITEM>( pad );
538 }
539
540 bool PlaceItem( BOARD_ITEM *aItem, BOARD_COMMIT& aCommit ) override
541 {
542 PAD* pad = dynamic_cast<PAD*>( aItem );
543
544 if( pad )
545 {
546 m_frame->GetDesignSettings().m_Pad_Master->ImportSettingsFrom( *pad );
547 pad->SetLocalCoord();
548 aCommit.Add( aItem );
549 return true;
550 }
551
552 return false;
553 }
554
555 PAD_TOOL* m_padTool;
556 };
557
558 PAD_PLACER placer( this );
559
560 doInteractiveItemPlacement( aEvent, &placer, _( "Place pad" ),
562
563 return 0;
564}
565
566
567int PAD_TOOL::EditPad( const TOOL_EVENT& aEvent )
568{
570 WX_INFOBAR* infoBar = frame()->GetInfoBar();
572 wxString msg;
573
574 if( m_editPad != niluuid )
575 {
576 PAD* pad = dynamic_cast<PAD*>( frame()->GetItem( m_editPad ) );
577
578 if( pad )
579 {
580 BOARD_COMMIT commit( frame() );
581 RecombinePad( pad, false, commit );
582 commit.Push( _( "Recombine pad" ) );
583 }
584
586 }
587 else if( selection.Size() == 1 && selection[0]->Type() == PCB_PAD_T )
588 {
589 PAD* pad = static_cast<PAD*>( selection[0] );
590 PCB_LAYER_ID layer = explodePad( pad );
591
593 frame()->SetActiveLayer( layer );
594
595 if( !m_wasHighContrast )
597
598 if( PCB_ACTIONS::explodePad.GetHotKey() == PCB_ACTIONS::recombinePad.GetHotKey() )
599 {
600 msg.Printf( _( "Pad Edit Mode. Press %s again to exit." ),
602 }
603 else
604 {
605 msg.Printf( _( "Pad Edit Mode. Press %s to exit." ),
607 }
608
609 infoBar->RemoveAllButtons();
610 infoBar->ShowMessage( msg, wxICON_INFORMATION );
611
612 m_editPad = pad->m_Uuid;
613 }
614
615 if( m_editPad == niluuid )
616 {
617 bool highContrast = ( opts.m_ContrastModeDisplay != HIGH_CONTRAST_MODE::NORMAL );
618
619 if( m_wasHighContrast != highContrast )
621
622 infoBar->Dismiss();
623 }
624
625 return 0;
626}
627
628
630{
631 PCB_LAYER_ID layer;
632 BOARD_COMMIT commit( frame() );
633
634 if( aPad->IsOnLayer( F_Cu ) )
635 layer = F_Cu;
636 else if( aPad->IsOnLayer( B_Cu ) )
637 layer = B_Cu;
638 else
639 layer = *aPad->GetLayerSet().UIOrder();
640
641 if( aPad->GetShape() == PAD_SHAPE::CUSTOM )
642 {
643 commit.Modify( aPad );
644
645 for( const std::shared_ptr<PCB_SHAPE>& primitive : aPad->GetPrimitives() )
646 {
647 FP_SHAPE* shape = new FP_SHAPE( board()->GetFirstFootprint() );
648
649 shape->SetShape( primitive->GetShape() );
650 shape->SetIsAnnotationProxy( primitive->IsAnnotationProxy());
651 shape->SetFilled( primitive->IsFilled() );
652 shape->SetStroke( primitive->GetStroke() );
653
654 switch( shape->GetShape() )
655 {
656 case SHAPE_T::SEGMENT:
657 case SHAPE_T::RECT:
658 case SHAPE_T::CIRCLE:
659 shape->SetStart( primitive->GetStart() );
660 shape->SetEnd( primitive->GetEnd() );
661 break;
662
663 case SHAPE_T::ARC:
664 shape->SetStart( primitive->GetStart() );
665 shape->SetEnd( primitive->GetEnd() );
666 shape->SetCenter( primitive->GetCenter() );
667 break;
668
669 case SHAPE_T::BEZIER:
670 shape->SetStart( primitive->GetStart() );
671 shape->SetEnd( primitive->GetEnd() );
672 shape->SetBezierC1( primitive->GetBezierC1() );
673 shape->SetBezierC2( primitive->GetBezierC2() );
674 break;
675
676 case SHAPE_T::POLY:
677 shape->SetPolyShape( primitive->GetPolyShape() );
678 break;
679
680 default:
682 }
683
684 shape->SetLocalCoord();
685 shape->Move( aPad->GetPosition() );
686 shape->Rotate( aPad->GetPosition(), aPad->GetOrientation() );
687 shape->SetLayer( layer );
688
689 commit.Add( shape );
690 }
691
692 aPad->SetShape( aPad->GetAnchorPadShape() );
693 aPad->DeletePrimitivesList();
694 aPad->SetFlags( ENTERED );
695 m_editPad = aPad->m_Uuid;
696 }
697
698 commit.Push( _("Edit pad shapes") );
700 return layer;
701}
702
703
704std::vector<FP_SHAPE*> PAD_TOOL::RecombinePad( PAD* aPad, bool aIsDryRun, BOARD_COMMIT& aCommit )
705{
706 int maxError = board()->GetDesignSettings().m_MaxError;
707 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aPad->GetParentFootprint() );
708
709 // Don't leave an object in the point editor that might no longer exist after
710 // recombining the pad.
712
713 for( BOARD_ITEM* item : footprint->GraphicalItems() )
714 item->ClearFlags( SKIP_STRUCT );
715
716 auto findNext =
717 [&]( PCB_LAYER_ID aLayer ) -> FP_SHAPE*
718 {
719 SHAPE_POLY_SET padPoly;
720 aPad->TransformShapeToPolygon( padPoly, aLayer, 0, maxError, ERROR_INSIDE );
721
722 for( BOARD_ITEM* item : footprint->GraphicalItems() )
723 {
724 FP_SHAPE* shape = dynamic_cast<FP_SHAPE*>( item );
725
726 if( !shape || ( shape->GetFlags() & SKIP_STRUCT ) )
727 continue;
728
729 if( shape->GetLayer() != aLayer )
730 continue;
731
732 if( shape->IsAnnotationProxy() ) // Pad number (and net name) box
733 return (FP_SHAPE*) item;
734
735 SHAPE_POLY_SET drawPoly;
736 shape->TransformShapeToPolygon( drawPoly, aLayer, 0, maxError, ERROR_INSIDE );
737 drawPoly.BooleanIntersection( padPoly, SHAPE_POLY_SET::PM_FAST );
738
739 if( !drawPoly.IsEmpty() )
740 return (FP_SHAPE*) item;
741 }
742
743 return nullptr;
744 };
745
746 auto findMatching =
747 [&]( FP_SHAPE* aShape ) -> std::vector<FP_SHAPE*>
748 {
749 std::vector<FP_SHAPE*> matching;
750
751 for( BOARD_ITEM* item : footprint->GraphicalItems() )
752 {
753 FP_SHAPE* other = dynamic_cast<FP_SHAPE*>( item );
754
755 if( !other || ( other->GetFlags() & SKIP_STRUCT ) )
756 continue;
757
758 if( aPad->GetLayerSet().test( other->GetLayer() )
759 && aShape->Compare( other ) == 0 )
760 {
761 matching.push_back( other );
762 }
763 }
764
765 return matching;
766 };
767
768 PCB_LAYER_ID layer;
769 std::vector<FP_SHAPE*> mergedShapes;
770
771 if( aPad->IsOnLayer( F_Cu ) )
772 layer = F_Cu;
773 else if( aPad->IsOnLayer( B_Cu ) )
774 layer = B_Cu;
775 else
776 layer = *aPad->GetLayerSet().UIOrder();
777
778 while( FP_SHAPE* fpShape = findNext( layer ) )
779 {
780 // We've found an intersecting item to combine.
781 //
782 fpShape->SetFlags( SKIP_STRUCT );
783
784 // First convert the pad to a custom-shape pad (if it isn't already)
785 //
786 if( !aIsDryRun )
787 {
788 aCommit.Modify( aPad );
789
790 if( aPad->GetShape() == PAD_SHAPE::RECT || aPad->GetShape() == PAD_SHAPE::CIRCLE )
791 {
792 aPad->SetAnchorPadShape( aPad->GetShape() );
793 }
794 else if( aPad->GetShape() != PAD_SHAPE::CUSTOM )
795 {
796 // Create a new minimally-sized circular anchor and convert existing pad
797 // to a polygon primitive
798 SHAPE_POLY_SET existingOutline;
799 aPad->TransformShapeToPolygon( existingOutline, layer, 0, maxError, ERROR_INSIDE );
800
802
803 if( aPad->GetSizeX() > aPad->GetSizeY() )
804 aPad->SetSizeX( aPad->GetSizeY() );
805
806 aPad->SetOffset( VECTOR2I( 0, 0 ) );
807
808 PCB_SHAPE* shape = new PCB_SHAPE( nullptr, SHAPE_T::POLY );
809 shape->SetFilled( true );
811 shape->SetPolyShape( existingOutline );
812 shape->Move( - aPad->GetPosition() );
813 shape->Rotate( VECTOR2I( 0, 0 ), - aPad->GetOrientation() );
814
815 aPad->AddPrimitive( shape );
816 }
817
819 }
820
821 // Now add the new shape to the primitives list
822 //
823 mergedShapes.push_back( fpShape );
824
825 if( !aIsDryRun )
826 {
827 PCB_SHAPE* pcbShape = new PCB_SHAPE;
828
829 pcbShape->SetShape( fpShape->GetShape() );
830 pcbShape->SetFilled( fpShape->IsFilled() );
831 pcbShape->SetStroke( fpShape->GetStroke() );
832
833 switch( pcbShape->GetShape() )
834 {
835 case SHAPE_T::SEGMENT:
836 case SHAPE_T::RECT:
837 case SHAPE_T::CIRCLE:
838 pcbShape->SetStart( fpShape->GetStart() );
839 pcbShape->SetEnd( fpShape->GetEnd() );
840 break;
841
842 case SHAPE_T::ARC:
843 pcbShape->SetStart( fpShape->GetStart() );
844 pcbShape->SetEnd( fpShape->GetEnd() );
845 pcbShape->SetCenter( fpShape->GetCenter() );
846 break;
847
848 case SHAPE_T::BEZIER:
849 pcbShape->SetStart( fpShape->GetStart() );
850 pcbShape->SetEnd( fpShape->GetEnd() );
851 pcbShape->SetBezierC1( fpShape->GetBezierC1() );
852 pcbShape->SetBezierC2( fpShape->GetBezierC2() );
853 break;
854
855 case SHAPE_T::POLY:
856 pcbShape->SetPolyShape( fpShape->GetPolyShape() );
857 break;
858
859 default:
860 UNIMPLEMENTED_FOR( pcbShape->SHAPE_T_asString() );
861 }
862
863 pcbShape->Move( - aPad->GetPosition() );
864 pcbShape->Rotate( VECTOR2I( 0, 0 ), - aPad->GetOrientation() );
865 pcbShape->SetIsAnnotationProxy( fpShape->IsAnnotationProxy());
866 aPad->AddPrimitive( pcbShape );
867
868 aCommit.Remove( fpShape );
869 }
870
871 // See if there are other shapes that match and mark them for delete. (KiCad won't
872 // produce these, but old footprints from other vendors have them.)
873 for( FP_SHAPE* other : findMatching( fpShape ) )
874 {
875 other->SetFlags( SKIP_STRUCT );
876 mergedShapes.push_back( other );
877
878 if( !aIsDryRun )
879 aCommit.Remove( other );
880 }
881 }
882
883 for( BOARD_ITEM* item : footprint->GraphicalItems() )
884 item->ClearFlags( SKIP_STRUCT );
885
886 if( !aIsDryRun )
887 aPad->ClearFlags( ENTERED );
888
889 return mergedShapes;
890}
891
892
894{
898
901
904}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
@ NORMAL
Inactive layers are shown normally (no high-contrast mode)
static TOOL_ACTION cancelInteractive
Definition: actions.h:63
static TOOL_ACTION highContrastMode
Definition: actions.h:105
virtual void Revert() override
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
std::unique_ptr< PAD > m_Pad_Master
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:58
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:180
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:214
BOARD_ITEM_CONTAINER * GetParentFootprint() const
Definition: board_item.cpp:239
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:265
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:397
FOOTPRINTS & Footprints()
Definition: board.h:307
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:643
void Empty()
Clear the list.
Definition: collector.h:89
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:81
COMMIT & Modify(EDA_ITEM *aItem)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
COMMIT & Remove(EDA_ITEM *aItem)
Notify observers that aItem has been removed.
Definition: commit.h:90
COMMIT & Add(EDA_ITEM *aItem)
Notify observers that aItem has been added.
Definition: commit.h:78
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
void AddSeparator(int aOrder=ANY_ORDER)
Add a separator to the menu.
static DELETED_BOARD_ITEM * GetInstance()
Definition: board_item.h:368
int GetStartNumber() const
Return common prefix for all enumerated pads.
wxString GetPrefix() const
WX_INFOBAR * GetInfoBar()
void SetMsgPanel(const std::vector< MSG_PANEL_ITEM > &aList)
Clear the message panel and populates it with the contents of aList.
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
void SetStatusPopup(wxWindow *aPopup)
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:85
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:142
const KIID m_Uuid
Definition: eda_item.h:494
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: eda_item.h:143
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:144
void SetBezierC2(const VECTOR2I &aPt)
Definition: eda_shape.h:178
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:458
void SetFilled(bool aFlag)
Definition: eda_shape.h:95
SHAPE_T GetShape() const
Definition: eda_shape.h:113
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: eda_shape.h:255
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:124
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:112
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:149
void SetBezierC1(const VECTOR2I &aPt)
Definition: eda_shape.h:175
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:75
bool IsAnnotationProxy() const
Definition: eda_shape.h:87
void SetIsAnnotationProxy(bool aIsProxy=true)
Definition: eda_shape.h:88
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:213
EDA_ANGLE GetOrientation() const
Definition: footprint.h:191
PADS & Pads()
Definition: footprint.h:170
const LIB_ID & GetFPID() const
Definition: footprint.h:212
DRAWINGS & GraphicalItems()
Definition: footprint.h:173
virtual void SetLocalCoord()
Set relative coordinates from draw coordinates.
Definition: fp_shape.cpp:52
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition: fp_shape.cpp:343
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition: fp_shape.cpp:355
A general implementation of a COLLECTORS_GUIDE.
Definition: collectors.h:326
void SetIgnoreMTextsMarkedNoShow(bool ignore)
Definition: collectors.h:411
void SetIgnoreModulesRefs(bool ignore)
Definition: collectors.h:465
void SetIgnoreModulesVals(bool ignore)
Definition: collectors.h:459
void SetIgnoreMTextsOnFront(bool ignore)
Definition: collectors.h:423
void SetIgnoreMTextsOnBack(bool ignore)
Definition: collectors.h:417
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:204
void Collect(BOARD_ITEM *aItem, const std::vector< KICAD_T > &aScanList, const VECTOR2I &aRefPos, const COLLECTORS_GUIDE &aGuide)
Scan a BOARD_ITEM using this class's Inspector method, which does the collection.
Definition: collectors.cpp:596
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: view.cpp:1586
LSEQ UIOrder() const
Definition: lset.cpp:922
Tool relating to pads and pad settings.
Definition: pad_tool.h:37
void Reset(RESET_REASON aReason) override
Basic initialization.
Definition: pad_tool.cpp:57
~PAD_TOOL()
React to model/view changes.
Definition: pad_tool.cpp:53
bool m_wasHighContrast
Definition: pad_tool.h:95
void setTransitions() override
< Bind handlers to corresponding TOOL_ACTIONs.
Definition: pad_tool.cpp:893
int EditPad(const TOOL_EVENT &aEvent)
Enter/exit WYSIWYG pad shape editing.
Definition: pad_tool.cpp:567
KIID m_editPad
Definition: pad_tool.h:96
int copyPadSettings(const TOOL_EVENT &aEvent)
Push pad settings from a pad to other pads on board or footprint.
Definition: pad_tool.cpp:163
int pastePadProperties(const TOOL_EVENT &aEvent)
Copy pad settings from a pad to the board design settings.
Definition: pad_tool.cpp:136
int PlacePad(const TOOL_EVENT &aEvent)
Place a pad in footprint editor.
Definition: pad_tool.cpp:487
std::vector< FP_SHAPE * > RecombinePad(PAD *aPad, bool aIsDryRun, BOARD_COMMIT &aCommit)
Recombine an exploded pad (or one produced with overlapping polygons in an older version).
Definition: pad_tool.cpp:704
int EnumeratePads(const TOOL_EVENT &aEvent)
Tool for quick pad enumeration.
Definition: pad_tool.cpp:278
PAD_TOOL()
Definition: pad_tool.cpp:46
PCB_LAYER_ID explodePad(PAD *aPad)
Definition: pad_tool.cpp:629
bool Init() override
Init() is called once upon a registration of the tool.
Definition: pad_tool.cpp:77
void SetLastPadNumber(const wxString &aPadNumber)
Definition: pad_tool.h:66
wxString m_lastPadNumber
Definition: pad_tool.h:93
int pushPadSettings(const TOOL_EVENT &aEvent)
Definition: pad_tool.cpp:235
Definition: pad.h:59
bool IsAperturePad() const
Definition: pad.h:402
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pad.h:392
int GetSizeX() const
Definition: pad.h:254
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pad.h:625
PAD_ATTRIB GetAttribute() const
Definition: pad.h:395
static LSET PTHMask()
layer set for a through hole pad
Definition: pad.cpp:179
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Convert the pad shape to a closed polygon.
Definition: pad.cpp:1573
void DeletePrimitivesList()
Clear the basic shapes list.
VECTOR2I GetPosition() const override
Definition: pad.h:197
const std::vector< std::shared_ptr< PCB_SHAPE > > & GetPrimitives() const
Accessor to the basic shape list for custom-shaped pads.
Definition: pad.h:325
void SetOffset(const VECTOR2I &aOffset)
Definition: pad.h:268
void ImportSettingsFrom(const PAD &aMasterPad)
Import the pad settings from aMasterPad.
Definition: pad.cpp:1464
FOOTPRINT * GetParent() const
Definition: pad.cpp:1458
PAD_SHAPE GetShape() const
Definition: pad.h:189
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition: pad.h:365
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition: pad.cpp:186
void SetShape(PAD_SHAPE aShape)
Set the new shape of this pad.
Definition: pad.h:180
int GetSizeY() const
Definition: pad.h:256
void SetAnchorPadShape(PAD_SHAPE aShape)
Set the shape of the anchor pad for custom shaped pads.
Definition: pad.h:228
void AddPrimitive(PCB_SHAPE *aPrimitive)
Add item to the custom shape primitives list.
void SetSizeX(const int aX)
Definition: pad.h:253
PAD_SHAPE GetAnchorPadShape() const
Definition: pad.h:202
static TOOL_ACTION recombinePad
Definition: pcb_actions.h:429
static TOOL_ACTION enumeratePads
Tool for quick pad enumeration.
Definition: pcb_actions.h:432
static TOOL_ACTION pushPadSettings
Copy the current pad's settings to other pads in the footprint or on the board.
Definition: pcb_actions.h:452
static TOOL_ACTION mirrorH
Mirroring of selected items.
Definition: pcb_actions.h:129
static TOOL_ACTION copyPadSettings
Copy the selected pad's settings to the board design settings.
Definition: pcb_actions.h:446
static TOOL_ACTION placePad
Activation of the drawing tool (placing a PAD)
Definition: pcb_actions.h:426
static TOOL_ACTION properties
Activation of the edit tool.
Definition: pcb_actions.h:149
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:59
static TOOL_ACTION explodePad
Definition: pcb_actions.h:428
static TOOL_ACTION applyPadSettings
Copy the default pad settings to the selected pad.
Definition: pcb_actions.h:449
static TOOL_ACTION mirrorV
Definition: pcb_actions.h:130
static TOOL_ACTION flip
Flipping of selected objects.
Definition: pcb_actions.h:126
static TOOL_ACTION rotateCw
Rotation of selected objects.
Definition: pcb_actions.h:122
static TOOL_ACTION rotateCcw
Definition: pcb_actions.h:123
const PCB_DISPLAY_OPTIONS & GetDisplayOptions() const
Display options control the way tracks, vias, outlines and other things are shown (for instance solid...
EDA_ITEM * GetItem(const KIID &aId) const override
Fetch an item by KIID.
virtual BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Returns the BOARD_DESIGN_SETTINGS for the open project.
GENERAL_COLLECTORS_GUIDE GetCollectorsGuide()
virtual void SetActiveLayer(PCB_LAYER_ID aLayer)
HIGH_CONTRAST_MODE m_ContrastModeDisplay
How inactive layers are displayed.
The selection tool: currently supports:
PCB_SELECTION & GetSelection()
virtual void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition: pcb_shape.cpp:191
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Convert the shape to a closed polygon.
Definition: pcb_shape.cpp:388
virtual void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition: pcb_shape.cpp:163
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:72
PCB_BASE_EDIT_FRAME * frame() const
BOARD * board() const
PCB_DRAW_PANEL_GAL * canvas() const
@ IPO_FLIP
Handle flip action in the loop by calling the item's flip method.
@ IPO_ROTATE
Handle the rotate action in the loop by calling the item's rotate method.
@ IPO_SINGLE_CLICK
Create an item immediately on placement starting, otherwise show the pencil cursor until the item is ...
@ IPO_REPEAT
Allow repeat placement of the item.
bool m_isFootprintEditor
void doInteractiveItemPlacement(const TOOL_EVENT &aTool, 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...
const PCB_SELECTION & selection() const
FOOTPRINT * footprint() const
static SELECTION_CONDITION HasType(KICAD_T aType)
Create a functor that tests if among the selected items there is at least one of a given type.
static SELECTION_CONDITION Count(int aNumber)
Create a functor that tests if the number of selected items is equal to the value given as parameter.
static bool ShowAlways(const SELECTION &aSelection)
The default condition function (always returns true).
static SELECTION_CONDITION OnlyTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if the selected items are only of given types.
int Size() const
Returns the number of selected parts.
Definition: selection.h:113
Represent a set of closed polygons.
bool IsEmpty() const
void BooleanIntersection(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset union between a and b, store the result in it self For aFastMode meaning,...
wxWindow * GetPanel()
Definition: status_popup.h:62
virtual void Popup(wxWindow *aFocus=nullptr)
virtual void Move(const wxPoint &aWhere)
Extension of STATUS_POPUP for displaying a single line text.
Definition: status_popup.h:83
void SetText(const wxString &aText)
Display a text.
Simple container to manage line stroke parameters.
Definition: stroke_params.h:88
virtual void PopTool(const TOOL_EVENT &aEvent)
Pops a tool from the stack.
virtual void PushTool(const TOOL_EVENT &aEvent)
NB: the definition of "tool" is different at the user level.
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:214
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
@ MODEL_RELOAD
Model changes (required full reload)
Definition: tool_base.h:80
Generic, UI-independent tool event.
Definition: tool_event.h:156
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
TOOL_MENU & GetToolMenu()
TOOL_MENU m_menu
The functions below are not yet implemented - their interface may change.
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
void Activate()
Run the tool.
bool ProcessEvent(const TOOL_EVENT &aEvent)
Propagate an event to tools that requested events of matching type(s).
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:142
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:44
void ShowContextMenu(SELECTION &aSelection)
Helper function to set and immediately show a CONDITIONAL_MENU in concert with the given SELECTION.
Definition: tool_menu.cpp:57
A modified version of the wxInfoBar class that allows us to:
Definition: infobar.h:75
void RemoveAllButtons()
Remove all the buttons that have been added by the user.
Definition: infobar.cpp:286
void Dismiss() override
Dismisses the infobar and updates the containing layout and AUI manager (if one is provided).
Definition: infobar.cpp:175
void ShowMessage(const wxString &aMessage, int aFlags=wxICON_INFORMATION) override
Show the info bar with the provided message and icon.
Definition: infobar.cpp:142
static PCB_SHAPE * findNext(PCB_SHAPE *aShape, const VECTOR2I &aPoint, const std::vector< PCB_SHAPE * > &aList, unsigned aLimit)
Searches for a PCB_SHAPE matching a given end point or start point in a list.
static bool empty(const wxTextEntryBase *aCtrl)
#define _(s)
#define ENTERED
indicates a group has been entered
#define SKIP_STRUCT
flag indicating that the structure should be ignored
@ FP_SMD
Definition: footprint.h:69
@ FP_THROUGH_HOLE
Definition: footprint.h:68
@ ERROR_INSIDE
wxString KeyNameFromKeyCode(int aKeycode, bool *aIsFound)
Return the key name from the key code.
KIID niluuid(0)
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ B_Cu
Definition: layer_ids.h:95
@ F_Cu
Definition: layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:120
@ SMD
Smd pad, appears on the solder paste layer (default)
@ PTH
Plated through hole pad.
@ CONN
Like smd, does not appear on the solder paste layer (default)
static void doPushPadProperties(BOARD &board, const PAD &aSrcPad, BOARD_COMMIT &commit, bool aSameFootprints, bool aPadShapeFilter, bool aPadOrientFilter, bool aPadLayerFilter, bool aPadTypeFilter)
Definition: pad_tool.cpp:184
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
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
std::function< bool(const SELECTION &)> SELECTION_CONDITION
< Functor type that checks a specific condition for selected items.
const double IU_PER_MM
Definition: base_units.h:77
MAGNETIC_OPTIONS tracks
MAGNETIC_OPTIONS pads
@ BUT_LEFT
Definition: tool_event.h:127
@ BUT_RIGHT
Definition: tool_event.h:128
double EuclideanNorm(const VECTOR2I &vector)
Definition: trigo.h:129
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
VECTOR2< int > VECTOR2I
Definition: vector2d.h:618