KiCad PCB EDA Suite
Loading...
Searching...
No Matches
edit_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) 2013-2023 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
7 * @author Tomasz Wlostowski <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
27#include <macros.h>
28#include <advanced_config.h>
29#include <clipboard.h>
30#include <limits>
31#include <kiplatform/ui.h>
33#include <board.h>
35#include <footprint.h>
36#include <increment.h>
37#include <pcb_shape.h>
38#include <pcb_group.h>
39#include <pcb_target.h>
40#include <pcb_textbox.h>
41#include <pcb_table.h>
42#include <pcb_generator.h>
43#include <pcb_edit_frame.h>
45#include <kiway.h>
46#include <status_popup.h>
48#include <tool/tool_manager.h>
49#include <tools/pcb_actions.h>
51#include <tools/edit_tool.h>
57#include <tools/pad_tool.h>
58#include <view/view_controls.h>
60#include <core/kicad_algo.h>
61#include <fix_board_shape.h>
62#include <bitmaps.h>
63#include <functional>
64using namespace std::placeholders;
65#include "kicad_clipboard.h"
66#include <wx/hyperlink.h>
67#include <router/router_tool.h>
71#include <dialogs/dialog_tablecell_properties.h>
72#include <dialogs/dialog_table_properties.h>
75#include <pcb_reference_image.h>
76
77const unsigned int EDIT_TOOL::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
78
79static const std::vector<KICAD_T> padTypes = { PCB_PAD_T };
80
81static const std::vector<KICAD_T> footprintTypes = { PCB_FOOTPRINT_T };
82
83static const std::vector<KICAD_T> groupTypes = { PCB_GROUP_T };
84
85static const std::vector<KICAD_T> trackTypes = { PCB_TRACE_T,
87 PCB_VIA_T };
88
89static const std::vector<KICAD_T> baseConnectedTypes = { PCB_PAD_T,
92 PCB_ARC_T };
93
94static const std::vector<KICAD_T> connectedTypes = { PCB_TRACE_T,
98 PCB_ZONE_T };
99
100static const std::vector<KICAD_T> routableTypes = { PCB_TRACE_T,
101 PCB_ARC_T,
102 PCB_VIA_T,
103 PCB_PAD_T,
105
106
108 PCB_TOOL_BASE( "pcbnew.InteractiveEdit" ),
109 m_selectionTool( nullptr ),
110 m_dragging( false )
111{
112}
113
114
116{
117 m_dragging = false;
118
119 m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( getEditFrame<PCB_BASE_EDIT_FRAME>() );
120}
121
122
123static std::shared_ptr<CONDITIONAL_MENU> makePositioningToolsMenu( TOOL_INTERACTIVE* aTool )
124{
125 auto menu = std::make_shared<CONDITIONAL_MENU>( aTool );
126
127 menu->SetIcon( BITMAPS::special_tools );
128 menu->SetTitle( _( "Positioning Tools" ) );
129
130 auto notMovingCondition = []( const SELECTION& aSelection )
131 {
132 return aSelection.Empty() || !aSelection.Front()->IsMoving();
133 };
134
135 // clang-format off
136 menu->AddItem( PCB_ACTIONS::moveExact, SELECTION_CONDITIONS::NotEmpty && notMovingCondition );
137 menu->AddItem( PCB_ACTIONS::moveWithReference, SELECTION_CONDITIONS::NotEmpty && notMovingCondition );
138 menu->AddItem( PCB_ACTIONS::copyWithReference, SELECTION_CONDITIONS::NotEmpty && notMovingCondition );
139 menu->AddItem( PCB_ACTIONS::positionRelative, SELECTION_CONDITIONS::NotEmpty && notMovingCondition );
141 // clang-format on
142 return menu;
143};
144
145
146static std::shared_ptr<CONDITIONAL_MENU> makeShapeModificationMenu( TOOL_INTERACTIVE* aTool )
147{
148 auto menu = std::make_shared<CONDITIONAL_MENU>( aTool );
149
150 menu->SetTitle( _( "Shape Modification" ) );
151
152 static const std::vector<KICAD_T> filletChamferTypes = { PCB_SHAPE_LOCATE_POLY_T,
155
156 static const std::vector<KICAD_T> healShapesTypes = { PCB_SHAPE_LOCATE_SEGMENT_T,
159
160 static const std::vector<KICAD_T> lineExtendTypes = { PCB_SHAPE_LOCATE_SEGMENT_T };
161
162 static const std::vector<KICAD_T> polygonBooleanTypes = { PCB_SHAPE_LOCATE_RECT_T,
164
165 static const std::vector<KICAD_T> polygonSimplifyTypes = { PCB_SHAPE_LOCATE_POLY_T,
166 PCB_ZONE_T };
167
168 auto hasCornerCondition =
169 [aTool]( const SELECTION& aSelection )
170 {
171 PCB_POINT_EDITOR *pt_tool = aTool->GetManager()->GetTool<PCB_POINT_EDITOR>();
172
173 return pt_tool && pt_tool->HasCorner();
174 };
175
176 auto hasMidpointCondition =
177 [aTool]( const SELECTION& aSelection )
178 {
179 PCB_POINT_EDITOR *pt_tool = aTool->GetManager()->GetTool<PCB_POINT_EDITOR>();
180
181 return pt_tool && pt_tool->HasMidpoint();
182 };
183
184 // clang-format off
185 menu->AddItem( PCB_ACTIONS::healShapes, SELECTION_CONDITIONS::HasTypes( healShapesTypes ) );
186 menu->AddItem( PCB_ACTIONS::simplifyPolygons, SELECTION_CONDITIONS::HasTypes( polygonSimplifyTypes ) );
187 menu->AddItem( PCB_ACTIONS::filletLines, SELECTION_CONDITIONS::OnlyTypes( filletChamferTypes ) );
188 menu->AddItem( PCB_ACTIONS::chamferLines, SELECTION_CONDITIONS::OnlyTypes( filletChamferTypes ) );
189 menu->AddItem( PCB_ACTIONS::dogboneCorners, SELECTION_CONDITIONS::OnlyTypes( filletChamferTypes ) );
190 menu->AddItem( PCB_ACTIONS::extendLines, SELECTION_CONDITIONS::OnlyTypes( lineExtendTypes )
192 menu->AddItem( PCB_ACTIONS::pointEditorMoveCorner, hasCornerCondition );
193 menu->AddItem( PCB_ACTIONS::pointEditorMoveMidpoint, hasMidpointCondition );
194
195 menu->AddSeparator( SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
197
198 menu->AddItem( PCB_ACTIONS::mergePolygons, SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
200 menu->AddItem( PCB_ACTIONS::subtractPolygons, SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
202 menu->AddItem( PCB_ACTIONS::intersectPolygons, SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
204 // clang-format on
205
206 return menu;
207};
208
210{
211 // Find the selection tool, so they can cooperate
213
214 std::shared_ptr<CONDITIONAL_MENU> positioningToolsSubMenu = makePositioningToolsMenu( this );
215 m_selectionTool->GetToolMenu().RegisterSubMenu( positioningToolsSubMenu );
216
217 std::shared_ptr<CONDITIONAL_MENU> shapeModificationSubMenu = makeShapeModificationMenu( this );
218 m_selectionTool->GetToolMenu().RegisterSubMenu( shapeModificationSubMenu );
219
220 auto positioningToolsCondition =
221 [&]( const SELECTION& aSel )
222 {
223 std::shared_ptr<CONDITIONAL_MENU> subMenu = makePositioningToolsMenu( this );
224 subMenu->Evaluate( aSel );
225 return subMenu->GetMenuItemCount() > 0;
226 };
227
228 auto shapeModificationCondition =
229 [&]( const SELECTION& aSel )
230 {
231 std::shared_ptr<CONDITIONAL_MENU> subMenu = makeShapeModificationMenu( this );
232 subMenu->Evaluate( aSel );
233 return subMenu->GetMenuItemCount() > 0;
234 };
235
236 auto propertiesCondition =
237 [&]( const SELECTION& aSel )
238 {
239 if( aSel.GetSize() == 0 )
240 {
242 {
245
246 if( ds && ds->HitTestDrawingSheetItems( getView(), cursor ) )
247 return true;
248 }
249
250 return false;
251 }
252
253 if( aSel.GetSize() == 1 )
254 return true;
255
256 for( EDA_ITEM* item : aSel )
257 {
258 if( !dynamic_cast<PCB_TRACK*>( item ) )
259 return false;
260 }
261
262 return true;
263 };
264
265 auto inFootprintEditor =
266 [ this ]( const SELECTION& aSelection )
267 {
268 return m_isFootprintEditor;
269 };
270
271 auto canMirror =
272 [ this ]( const SELECTION& aSelection )
273 {
275 && SELECTION_CONDITIONS::OnlyTypes( padTypes )( aSelection ) )
276 {
277 return false;
278 }
279
280 if( SELECTION_CONDITIONS::HasTypes( groupTypes )( aSelection ) )
281 return true;
282
284 };
285
286 auto singleFootprintCondition = SELECTION_CONDITIONS::OnlyTypes( footprintTypes )
288
289 auto multipleFootprintsCondition =
290 []( const SELECTION& aSelection )
291 {
292 bool foundFirst = false;
293
294 for( EDA_ITEM* item : aSelection )
295 {
296 if( item->Type() == PCB_FOOTPRINT_T )
297 {
298 if( foundFirst )
299 return true;
300 else
301 foundFirst = true;
302 }
303 }
304
305 return false;
306 };
307
308 auto noActiveToolCondition =
309 [ this ]( const SELECTION& aSelection )
310 {
311 return frame()->ToolStackIsEmpty();
312 };
313
314 auto notMovingCondition =
315 []( const SELECTION& aSelection )
316 {
317 return aSelection.Empty() || !aSelection.Front()->IsMoving();
318 };
319
320 auto noItemsCondition =
321 [ this ]( const SELECTION& aSelections ) -> bool
322 {
323 return frame()->GetBoard() && !frame()->GetBoard()->IsEmpty();
324 };
325
326 auto isSkippable =
327 [ this ]( const SELECTION& aSelection )
328 {
329 return frame()->IsCurrentTool( PCB_ACTIONS::moveIndividually );
330 };
331
332 SELECTION_CONDITION isRoutable =
335 && notMovingCondition
336 && !inFootprintEditor;
337
338
339 const auto canCopyAsText = SELECTION_CONDITIONS::NotEmpty
351 } );
352
353 // Add context menu entries that are displayed when selection tool is active
355
356 // clang-format off
358 && notMovingCondition );
359
360 menu.AddItem( PCB_ACTIONS::routerRouteSelected, isRoutable );
361 menu.AddItem( PCB_ACTIONS::unrouteSelected, isRoutable );
362 menu.AddItem( PCB_ACTIONS::routerAutorouteSelected, isRoutable );
364 && notMovingCondition );
365 menu.AddItem( PCB_ACTIONS::skip, isSkippable );
377 menu.AddItem( PCB_ACTIONS::mirrorH, canMirror );
378 menu.AddItem( PCB_ACTIONS::mirrorV, canMirror );
382
383 menu.AddItem( PCB_ACTIONS::properties, propertiesCondition );
384
386 && !inFootprintEditor );
388
389 // Footprint actions
390 menu.AddSeparator();
391 menu.AddItem( PCB_ACTIONS::editFpInFpEditor, singleFootprintCondition );
392 menu.AddItem( PCB_ACTIONS::updateFootprint, singleFootprintCondition );
393 menu.AddItem( PCB_ACTIONS::updateFootprints, multipleFootprintsCondition );
394 menu.AddItem( PCB_ACTIONS::changeFootprint, singleFootprintCondition );
395 menu.AddItem( PCB_ACTIONS::changeFootprints, multipleFootprintsCondition );
396
397 // Add the submenu for the special tools: modfiers and positioning tools
398 menu.AddSeparator( 100 );
399 menu.AddMenu( shapeModificationSubMenu.get(), shapeModificationCondition, 100 );
400 menu.AddMenu( positioningToolsSubMenu.get(), positioningToolsCondition, 100 );
401
402 menu.AddSeparator( 150 );
403 menu.AddItem( ACTIONS::cut, SELECTION_CONDITIONS::NotEmpty, 150 );
404 menu.AddItem( ACTIONS::copy, SELECTION_CONDITIONS::NotEmpty, 150 );
405 menu.AddItem( ACTIONS::copyAsText, canCopyAsText, 150 );
406
407 // Selection tool handles the context menu for some other tools, such as the Picker.
408 // Don't add things like Paste when another tool is active.
409 menu.AddItem( ACTIONS::paste, noActiveToolCondition, 150 );
410 menu.AddItem( ACTIONS::pasteSpecial, noActiveToolCondition && !inFootprintEditor, 150 );
413
414 menu.AddSeparator( 150 );
415 menu.AddItem( ACTIONS::selectAll, noItemsCondition, 150 );
416 menu.AddItem( ACTIONS::unselectAll, noItemsCondition, 150 );
417 // clang-format on
418
419 return true;
420}
421
422
429{
430 wxString footprintName;
431 wxArrayString fplist;
432 const FOOTPRINTS& footprints = aFrame.GetBoard()->Footprints();
433
434 // Build list of available fp references, to display them in dialog
435 for( FOOTPRINT* fp : footprints )
436 fplist.Add( fp->GetReference() + wxT( " ( " ) + fp->GetValue() + wxT( " )" ) );
437
438 fplist.Sort();
439
440 DIALOG_GET_FOOTPRINT_BY_NAME dlg( &aFrame, fplist );
441
442 if( dlg.ShowModal() != wxID_OK ) //Aborted by user
443 return nullptr;
444
445 footprintName = dlg.GetValue();
446 footprintName.Trim( true );
447 footprintName.Trim( false );
448
449 if( !footprintName.IsEmpty() )
450 {
451 for( FOOTPRINT* fp : footprints )
452 {
453 if( fp->GetReference().CmpNoCase( footprintName ) == 0 )
454 return fp;
455 }
456 }
457
458 return nullptr;
459}
460
461
463{
464 // GetAndPlace makes sense only in board editor, although it is also called
465 // in fpeditor, that shares the same EDIT_TOOL list
466 if( IsFootprintEditor() )
467 return 0;
468
471
472 if( fp )
473 {
476
477 selectionTool->GetSelection().SetReferencePoint( fp->GetPosition() );
479 }
480
481 return 0;
482}
483
484
485bool EDIT_TOOL::invokeInlineRouter( int aDragMode )
486{
487 ROUTER_TOOL* theRouter = m_toolMgr->GetTool<ROUTER_TOOL>();
488
489 if( !theRouter )
490 return false;
491
492 // don't allow switch from moving to dragging
493 if( m_dragging )
494 {
495 wxBell();
496 return false;
497 }
498
499 // make sure we don't accidentally invoke inline routing mode while the router is already
500 // active!
501 if( theRouter->IsToolActive() )
502 return false;
503
504 if( theRouter->CanInlineDrag( aDragMode ) )
505 {
507 return true;
508 }
509
510 return false;
511}
512
513
515{
517
518 return router && router->RoutingInProgress();
519}
520
521
522int EDIT_TOOL::Drag( const TOOL_EVENT& aEvent )
523{
524 if( !m_toolMgr->GetTool<ROUTER_TOOL>() )
525 {
526 wxBell();
527 return false; // don't drag when no router tool (i.e. fp editor)
528 }
529
531 {
532 wxBell();
533 return false; // don't drag when router is already active
534 }
535
536 if( m_dragging )
537 {
538 wxBell();
539 return false; // don't do a router drag when already in an EDIT_TOOL drag
540 }
541
542 int mode = PNS::DM_ANY;
543
544 if( aEvent.IsAction( &PCB_ACTIONS::dragFreeAngle ) )
545 mode |= PNS::DM_FREE_ANGLE;
546
548 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
549 {
550 sTool->FilterCollectorForFreePads( aCollector );
551 sTool->FilterCollectorForHierarchy( aCollector, true );
552
553 std::vector<PCB_TRACK*> tracks;
554 std::vector<PCB_TRACK*> vias;
555 std::vector<FOOTPRINT*> footprints;
556
557 for( EDA_ITEM* item : aCollector )
558 {
559 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
560 {
561 if( track->Type() == PCB_VIA_T )
562 vias.push_back( track );
563 else
564 tracks.push_back( track );
565 }
566 else if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( item ) )
567 {
568 footprints.push_back( footprint );
569 }
570 }
571
572 if( !sTool->GetSelection().IsHover() && footprints.size() )
573 {
574 // Remove non-footprints so box-selection will drag footprints.
575 for( int ii = aCollector.GetCount() - 1; ii >= 0; --ii )
576 {
577 if( aCollector[ii]->Type() != PCB_FOOTPRINT_T )
578 aCollector.Remove( ii );
579 }
580 }
581 else if( tracks.size() || vias.size() )
582 {
583 /*
584 * First trim down selection to active layer, tracks vs zones, etc.
585 */
586 if( aCollector.GetCount() > 1 )
587 sTool->GuessSelectionCandidates( aCollector, aPt );
588
589 /*
590 * If we have a knee between two tracks, or a via attached to two tracks,
591 * then drop the selection to a single item. We don't want a selection
592 * disambiguation menu when it doesn't matter which items is picked.
593 */
594 auto connected = []( PCB_TRACK* track, const VECTOR2I& pt )
595 {
596 return track->GetStart() == pt || track->GetEnd() == pt;
597 };
598
599 if( tracks.size() == 2 && vias.size() == 0 )
600 {
601 if( connected( tracks[0], tracks[1]->GetStart() )
602 || connected( tracks[0], tracks[1]->GetEnd() ) )
603 {
604 aCollector.Remove( tracks[1] );
605 }
606 }
607 else if( tracks.size() == 2 && vias.size() == 1 )
608 {
609 if( connected( tracks[0], vias[0]->GetPosition() )
610 && connected( tracks[1], vias[0]->GetPosition() ) )
611 {
612 aCollector.Remove( tracks[0] );
613 aCollector.Remove( tracks[1] );
614 }
615 }
616 }
617 },
618 true /* prompt user regarding locked items */ );
619
620 if( selection.Empty() )
621 return 0;
622
623 if( selection.Size() == 1 && selection.Front()->Type() == PCB_ARC_T )
624 {
625 // TODO: This really should be done in PNS to ensure DRC is maintained, but for now
626 // it allows interactive editing of an arc track
627 return DragArcTrack( aEvent );
628 }
629 else
630 {
631 invokeInlineRouter( mode );
632 }
633
634 return 0;
635}
636
637
639{
641
642 if( selection.Size() != 1 || selection.Front()->Type() != PCB_ARC_T )
643 return 0;
644
645 PCB_ARC* theArc = static_cast<PCB_ARC*>( selection.Front() );
646 EDA_ANGLE maxTangentDeviation( ADVANCED_CFG::GetCfg().m_MaxTangentAngleDeviation, DEGREES_T );
647
648 if( theArc->GetAngle() + maxTangentDeviation >= ANGLE_180 )
649 {
650 wxString msg = wxString::Format( _( "Unable to resize arc tracks of %s or greater." ),
651 EDA_UNIT_UTILS::UI::MessageTextFromValue( ANGLE_180 - maxTangentDeviation ) );
652 frame()->ShowInfoBarError( msg );
653
654 return 0; // don't bother with > 180 degree arcs
655 }
656
658
659 Activate();
660 // Must be done after Activate() so that it gets set into the correct context
661 controls->ShowCursor( true );
662 controls->SetAutoPan( true );
663
664 BOARD_COMMIT commit( this );
665 bool restore_state = false;
666
667 commit.Modify( theArc );
668
669 VECTOR2I arcCenter = theArc->GetCenter();
670 SEG tanStart = SEG( arcCenter, theArc->GetStart() ).PerpendicularSeg( theArc->GetStart() );
671 SEG tanEnd = SEG( arcCenter, theArc->GetEnd() ).PerpendicularSeg( theArc->GetEnd() );
672
673 //Ensure the tangent segments are in the correct orientation
674 OPT_VECTOR2I tanIntersect = tanStart.IntersectLines( tanEnd );
675
676 if( !tanIntersect )
677 return 0;
678
679 tanStart.A = *tanIntersect;
680 tanStart.B = theArc->GetStart();
681 tanEnd.A = *tanIntersect;
682 tanEnd.B = theArc->GetEnd();
683
684 std::set<PCB_TRACK*> addedTracks;
685
686 auto getUniqueTrackAtAnchorCollinear =
687 [&]( const VECTOR2I& aAnchor, const SEG& aCollinearSeg ) -> PCB_TRACK*
688 {
689 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
690
691 // Allow items at a distance within the width of the arc track
692 int allowedDeviation = theArc->GetWidth();
693
694 std::vector<BOARD_CONNECTED_ITEM*> itemsOnAnchor;
695
696 for( int i = 0; i < 3; i++ )
697 {
698 itemsOnAnchor = conn->GetConnectedItemsAtAnchor( theArc, aAnchor,
700 allowedDeviation );
701 allowedDeviation /= 2;
702
703 if( itemsOnAnchor.size() == 1 )
704 break;
705 }
706
707 PCB_TRACK* track = nullptr;
708
709 if( itemsOnAnchor.size() == 1 && itemsOnAnchor.front()->Type() == PCB_TRACE_T )
710 {
711 track = static_cast<PCB_TRACK*>( itemsOnAnchor.front() );
712 commit.Modify( track );
713
714 SEG trackSeg( track->GetStart(), track->GetEnd() );
715
716 // Allow deviations in colinearity as defined in ADVANCED_CFG
717 if( trackSeg.Angle( aCollinearSeg ) > maxTangentDeviation )
718 track = nullptr;
719 }
720
721 if( !track )
722 {
723 track = new PCB_TRACK( theArc->GetParent() );
724 track->SetStart( aAnchor );
725 track->SetEnd( aAnchor );
726 track->SetNet( theArc->GetNet() );
727 track->SetLayer( theArc->GetLayer() );
728 track->SetWidth( theArc->GetWidth() );
729 track->SetLocked( theArc->IsLocked() );
730 track->SetHasSolderMask( theArc->HasSolderMask() );
732 track->SetFlags( IS_NEW );
733 getView()->Add( track );
734 addedTracks.insert( track );
735 }
736
737 return track;
738 };
739
740 PCB_TRACK* trackOnStart = getUniqueTrackAtAnchorCollinear( theArc->GetStart(), tanStart);
741 PCB_TRACK* trackOnEnd = getUniqueTrackAtAnchorCollinear( theArc->GetEnd(), tanEnd );
742
743 if( trackOnStart->GetLength() != 0 )
744 {
745 tanStart.A = trackOnStart->GetStart();
746 tanStart.B = trackOnStart->GetEnd();
747 }
748
749 if( trackOnEnd->GetLength() != 0 )
750 {
751 tanEnd.A = trackOnEnd->GetStart();
752 tanEnd.B = trackOnEnd->GetEnd();
753 }
754
755 // Recalculate intersection point
756 if( tanIntersect = tanStart.IntersectLines( tanEnd ); !tanIntersect )
757 return 0;
758
759 auto isTrackStartClosestToArcStart =
760 [&]( PCB_TRACK* aTrack ) -> bool
761 {
762 double trackStartToArcStart = aTrack->GetStart().Distance( theArc->GetStart() );
763 double trackEndToArcStart = aTrack->GetEnd().Distance( theArc->GetStart() );
764
765 return trackStartToArcStart < trackEndToArcStart;
766 };
767
768 bool isStartTrackOnStartPt = isTrackStartClosestToArcStart( trackOnStart );
769 bool isEndTrackOnStartPt = isTrackStartClosestToArcStart( trackOnEnd );
770
771 // Calculate constraints
772 //======================
773 // maxTanCircle is the circle with maximum radius that is tangent to the two adjacent straight
774 // tracks and whose tangent points are constrained within the original tracks and their
775 // projected intersection points.
776 //
777 // The cursor will be constrained first within the isosceles triangle formed by the segments
778 // cSegTanStart, cSegTanEnd and cSegChord. After that it will be constrained to be outside
779 // maxTanCircle.
780 //
781 //
782 // ____________ <-cSegTanStart
783 // / * . ' *
784 // cSegTanEnd-> / * . ' *
785 // /* . ' <-cSegChord *
786 // /. '
787 // /* *
788 //
789 // * c * <-maxTanCircle
790 //
791 // * *
792 //
793 // * *
794 // * *
795 // * *
796 //
797
798 auto getFurthestPointToTanInterstect =
799 [&]( VECTOR2I& aPointA, VECTOR2I& aPointB ) -> VECTOR2I
800 {
801 if( ( aPointA - *tanIntersect ).EuclideanNorm()
802 > ( aPointB - *tanIntersect ).EuclideanNorm() )
803 {
804 return aPointA;
805 }
806 else
807 {
808 return aPointB;
809 }
810 };
811
812 CIRCLE maxTanCircle;
813 VECTOR2I tanStartPoint = getFurthestPointToTanInterstect( tanStart.A, tanStart.B );
814 VECTOR2I tanEndPoint = getFurthestPointToTanInterstect( tanEnd.A, tanEnd.B );
815 VECTOR2I tempTangentPoint = tanEndPoint;
816
817 if( getFurthestPointToTanInterstect( tanStartPoint, tanEndPoint ) == tanEndPoint )
818 tempTangentPoint = tanStartPoint;
819
820 maxTanCircle.ConstructFromTanTanPt( tanStart, tanEnd, tempTangentPoint );
821 VECTOR2I maxTanPtStart = tanStart.LineProject( maxTanCircle.Center );
822 VECTOR2I maxTanPtEnd = tanEnd.LineProject( maxTanCircle.Center );
823
824 SEG cSegTanStart( maxTanPtStart, *tanIntersect );
825 SEG cSegTanEnd( maxTanPtEnd, *tanIntersect );
826 SEG cSegChord( maxTanPtStart, maxTanPtEnd );
827
828 int cSegTanStartSide = cSegTanStart.Side( theArc->GetMid() );
829 int cSegTanEndSide = cSegTanEnd.Side( theArc->GetMid() );
830 int cSegChordSide = cSegChord.Side( theArc->GetMid() );
831
832 bool eatFirstMouseUp = true;
833
834 // Start the tool loop
835 while( TOOL_EVENT* evt = Wait() )
836 {
837 m_cursor = controls->GetMousePosition();
838
839 // Constrain cursor within the isosceles triangle
840 if( cSegTanStartSide != cSegTanStart.Side( m_cursor )
841 || cSegTanEndSide != cSegTanEnd.Side( m_cursor )
842 || cSegChordSide != cSegChord.Side( m_cursor ) )
843 {
844 std::vector<VECTOR2I> possiblePoints;
845
846 possiblePoints.push_back( cSegTanEnd.NearestPoint( m_cursor ) );
847 possiblePoints.push_back( cSegChord.NearestPoint( m_cursor ) );
848
849 VECTOR2I closest = cSegTanStart.NearestPoint( m_cursor );
850
851 for( const VECTOR2I& candidate : possiblePoints )
852 {
853 if( ( candidate - m_cursor ).SquaredEuclideanNorm()
854 < ( closest - m_cursor ).SquaredEuclideanNorm() )
855 {
856 closest = candidate;
857 }
858 }
859
860 m_cursor = closest;
861 }
862
863 // Constrain cursor to be outside maxTanCircle
864 if( ( m_cursor - maxTanCircle.Center ).EuclideanNorm() < maxTanCircle.Radius )
865 m_cursor = maxTanCircle.NearestPoint( m_cursor );
866
867 controls->ForceCursorPosition( true, m_cursor );
868
869 // Calculate resulting object coordinates
870 CIRCLE circlehelper;
871 circlehelper.ConstructFromTanTanPt( cSegTanStart, cSegTanEnd, m_cursor );
872
873 VECTOR2I newCenter = circlehelper.Center;
874 VECTOR2I newStart = cSegTanStart.LineProject( newCenter );
875 VECTOR2I newEnd = cSegTanEnd.LineProject( newCenter );
876 VECTOR2I newMid = CalcArcMid( newStart, newEnd, newCenter );
877
878 // Update objects
879 theArc->SetStart( newStart );
880 theArc->SetEnd( newEnd );
881 theArc->SetMid( newMid );
882
883 if( isStartTrackOnStartPt )
884 trackOnStart->SetStart( newStart );
885 else
886 trackOnStart->SetEnd( newStart );
887
888 if( isEndTrackOnStartPt )
889 trackOnEnd->SetStart( newEnd );
890 else
891 trackOnEnd->SetEnd( newEnd );
892
893 // Update view
894 getView()->Update( trackOnStart );
895 getView()->Update( trackOnEnd );
896 getView()->Update( theArc );
897
898 // Handle events
899 if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
900 {
901 eatFirstMouseUp = false;
902 }
903 else if( evt->IsCancelInteractive() || evt->IsActivate() )
904 {
905 restore_state = true; // Canceling the tool means that items have to be restored
906 break; // Finish
907 }
908 else if( evt->IsAction( &ACTIONS::undo ) )
909 {
910 restore_state = true; // Perform undo locally
911 break; // Finish
912 }
913 else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT )
914 || evt->IsDblClick( BUT_LEFT ) )
915 {
916 // Eat mouse-up/-click events that leaked through from the lock dialog
917 if( eatFirstMouseUp && !evt->IsAction( &ACTIONS::cursorClick ) )
918 {
919 eatFirstMouseUp = false;
920 continue;
921 }
922
923 break; // Finish
924 }
925 }
926
927 // Amend the end points of the arc if we delete the joining tracks
928 VECTOR2I newStart = trackOnStart->GetStart();
929 VECTOR2I newEnd = trackOnEnd->GetStart();
930
931 if( isStartTrackOnStartPt )
932 newStart = trackOnStart->GetEnd();
933
934 if( isEndTrackOnStartPt )
935 newEnd = trackOnEnd->GetEnd();
936
937 int maxLengthIU =
938 KiROUND( ADVANCED_CFG::GetCfg().m_MaxTrackLengthToKeep * pcbIUScale.IU_PER_MM );
939
940 if( trackOnStart->GetLength() <= maxLengthIU )
941 {
942 if( addedTracks.count( trackOnStart ) )
943 {
944 getView()->Remove( trackOnStart );
945 addedTracks.erase( trackOnStart );
946 delete trackOnStart;
947 }
948 else
949 {
950 commit.Remove( trackOnStart );
951 }
952
953 theArc->SetStart( newStart );
954 }
955
956 if( trackOnEnd->GetLength() <= maxLengthIU )
957 {
958 if( addedTracks.count( trackOnEnd ) )
959 {
960 getView()->Remove( trackOnEnd );
961 addedTracks.erase( trackOnEnd );
962 delete trackOnEnd;
963 }
964 else
965 {
966 commit.Remove( trackOnEnd );
967 }
968
969 theArc->SetEnd( newEnd );
970 }
971
972 if( theArc->GetLength() <= 0 )
973 commit.Remove( theArc );
974
975 for( PCB_TRACK* added : addedTracks )
976 {
977 getView()->Remove( added );
978 commit.Add( added );
979 }
980
981 // Should we commit?
982 if( restore_state )
983 commit.Revert();
984 else
985 commit.Push( _( "Drag Arc Track" ) );
986
987 return 0;
988}
989
990
992{
994 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
995 {
996 // Iterate from the back so we don't have to worry about removals.
997 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
998 {
999 BOARD_ITEM* item = aCollector[ i ];
1000
1001 if( !dynamic_cast<PCB_TRACK*>( item ) )
1002 aCollector.Remove( item );
1003 }
1004 },
1005 true /* prompt user regarding locked items */ );
1006
1007 BOARD_COMMIT commit( this );
1008
1009 for( EDA_ITEM* item : selection )
1010 {
1011 if( item->Type() == PCB_VIA_T )
1012 {
1013 PCB_VIA* via = static_cast<PCB_VIA*>( item );
1014
1015 commit.Modify( via );
1016
1017 int new_width;
1018 int new_drill;
1019
1020 if( via->GetViaType() == VIATYPE::MICROVIA )
1021 {
1022 NETCLASS* netClass = via->GetEffectiveNetClass();
1023
1024 new_width = netClass->GetuViaDiameter();
1025 new_drill = netClass->GetuViaDrill();
1026 }
1027 else
1028 {
1029 new_width = board()->GetDesignSettings().GetCurrentViaSize();
1030 new_drill = board()->GetDesignSettings().GetCurrentViaDrill();
1031 }
1032
1033 via->SetDrill( new_drill );
1034 // TODO(JE) padstacks - is this correct behavior already? If so, also change stack mode
1035 via->SetWidth( PADSTACK::ALL_LAYERS, new_width );
1036 }
1037 else if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
1038 {
1039 PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item );
1040
1041 wxCHECK( track, 0 );
1042
1043 commit.Modify( track );
1044
1045 int new_width = board()->GetDesignSettings().GetCurrentTrackWidth();
1046 track->SetWidth( new_width );
1047 }
1048 }
1049
1050 commit.Push( _( "Edit Track Width/Via Size" ) );
1051
1052 if( selection.IsHover() )
1053 {
1055
1056 // Notify other tools of the changes -- This updates the visual ratsnest
1058 }
1059
1060 return 0;
1061}
1062
1063
1065{
1066 // Store last used fillet radius to allow pressing "enter" if repeat fillet is required
1067 static int filletRadius = 0;
1068
1070 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1071 {
1072 // Iterate from the back so we don't have to worry about removals.
1073 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1074 {
1075 BOARD_ITEM* item = aCollector[i];
1076
1077 if( !dynamic_cast<PCB_TRACK*>( item ) )
1078 aCollector.Remove( item );
1079 }
1080 },
1081 true /* prompt user regarding locked items */ );
1082
1083 if( selection.Size() < 2 )
1084 {
1085 frame()->ShowInfoBarMsg( _( "At least two straight track segments must be selected." ) );
1086 return 0;
1087 }
1088
1089 WX_UNIT_ENTRY_DIALOG dlg( frame(), _( "Fillet Tracks" ), _( "Radius:" ), filletRadius );
1090
1091 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue() == 0 )
1092 return 0;
1093
1094 filletRadius = dlg.GetValue();
1095
1096 struct FILLET_OP
1097 {
1098 PCB_TRACK* t1;
1099 PCB_TRACK* t2;
1100 // Start point of track is modified after PCB_ARC is added, otherwise the end point:
1101 bool t1Start = true;
1102 bool t2Start = true;
1103 };
1104
1105 std::vector<FILLET_OP> filletOperations;
1106 bool operationPerformedOnAtLeastOne = false;
1107 bool didOneAttemptFail = false;
1108 std::set<PCB_TRACK*> processedTracks;
1109
1110 auto processFilletOp =
1111 [&]( PCB_TRACK* aTrack, bool aStartPoint )
1112 {
1113 std::shared_ptr<CONNECTIVITY_DATA> c = board()->GetConnectivity();
1114 VECTOR2I anchor = aStartPoint ? aTrack->GetStart() : aTrack->GetEnd();
1115 std::vector<BOARD_CONNECTED_ITEM*> itemsOnAnchor;
1116
1117 itemsOnAnchor = c->GetConnectedItemsAtAnchor( aTrack, anchor, baseConnectedTypes );
1118
1119 if( itemsOnAnchor.size() > 0
1120 && selection.Contains( itemsOnAnchor.at( 0 ) )
1121 && itemsOnAnchor.at( 0 )->Type() == PCB_TRACE_T )
1122 {
1123 PCB_TRACK* trackOther = static_cast<PCB_TRACK*>( itemsOnAnchor.at( 0 ) );
1124
1125 // Make sure we don't fillet the same pair of tracks twice
1126 if( processedTracks.find( trackOther ) == processedTracks.end() )
1127 {
1128 if( itemsOnAnchor.size() == 1 )
1129 {
1130 FILLET_OP filletOp;
1131 filletOp.t1 = aTrack;
1132 filletOp.t2 = trackOther;
1133 filletOp.t1Start = aStartPoint;
1134 filletOp.t2Start = aTrack->IsPointOnEnds( filletOp.t2->GetStart() );
1135 filletOperations.push_back( filletOp );
1136 }
1137 else
1138 {
1139 // User requested to fillet these two tracks but not possible as
1140 // there are other elements connected at that point
1141 didOneAttemptFail = true;
1142 }
1143 }
1144 }
1145 };
1146
1147 for( EDA_ITEM* item : selection )
1148 {
1149 if( item->Type() == PCB_TRACE_T )
1150 {
1151 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
1152
1153 if( track->GetLength() > 0 )
1154 {
1155 processFilletOp( track, true ); // on the start point of track
1156 processFilletOp( track, false ); // on the end point of track
1157
1158 processedTracks.insert( track );
1159 }
1160 }
1161 }
1162
1163 BOARD_COMMIT commit( this );
1164 std::vector<BOARD_ITEM*> itemsToAddToSelection;
1165
1166 for( FILLET_OP filletOp : filletOperations )
1167 {
1168 PCB_TRACK* track1 = filletOp.t1;
1169 PCB_TRACK* track2 = filletOp.t2;
1170
1171 bool trackOnStart = track1->IsPointOnEnds( track2->GetStart() );
1172 bool trackOnEnd = track1->IsPointOnEnds( track2->GetEnd() );
1173
1174 if( trackOnStart && trackOnEnd )
1175 continue; // Ignore duplicate tracks
1176
1177 if( ( trackOnStart || trackOnEnd ) && track1->GetLayer() == track2->GetLayer() )
1178 {
1179 SEG t1Seg( track1->GetStart(), track1->GetEnd() );
1180 SEG t2Seg( track2->GetStart(), track2->GetEnd() );
1181
1182 if( t1Seg.ApproxCollinear( t2Seg ) )
1183 continue;
1184
1185 SHAPE_ARC sArc( t1Seg, t2Seg, filletRadius );
1186 VECTOR2I t1newPoint, t2newPoint;
1187
1188 auto setIfPointOnSeg =
1189 []( VECTOR2I& aPointToSet, const SEG& aSegment, const VECTOR2I& aVecToTest )
1190 {
1191 VECTOR2I segToVec = aSegment.NearestPoint( aVecToTest ) - aVecToTest;
1192
1193 // Find out if we are on the segment (minimum precision)
1195 {
1196 aPointToSet.x = aVecToTest.x;
1197 aPointToSet.y = aVecToTest.y;
1198 return true;
1199 }
1200
1201 return false;
1202 };
1203
1204 //Do not draw a fillet if the end points of the arc are not within the track segments
1205 if( !setIfPointOnSeg( t1newPoint, t1Seg, sArc.GetP0() )
1206 && !setIfPointOnSeg( t2newPoint, t2Seg, sArc.GetP0() ) )
1207 {
1208 didOneAttemptFail = true;
1209 continue;
1210 }
1211
1212 if( !setIfPointOnSeg( t1newPoint, t1Seg, sArc.GetP1() )
1213 && !setIfPointOnSeg( t2newPoint, t2Seg, sArc.GetP1() ) )
1214 {
1215 didOneAttemptFail = true;
1216 continue;
1217 }
1218
1219 PCB_ARC* tArc = new PCB_ARC( frame()->GetBoard(), &sArc );
1220 tArc->SetLayer( track1->GetLayer() );
1221 tArc->SetWidth( track1->GetWidth() );
1222 tArc->SetNet( track1->GetNet() );
1223 tArc->SetLocked( track1->IsLocked() );
1224 tArc->SetHasSolderMask( track1->HasSolderMask() );
1226 commit.Add( tArc );
1227 itemsToAddToSelection.push_back( tArc );
1228
1229 commit.Modify( track1 );
1230 commit.Modify( track2 );
1231
1232 if( filletOp.t1Start )
1233 track1->SetStart( t1newPoint );
1234 else
1235 track1->SetEnd( t1newPoint );
1236
1237 if( filletOp.t2Start )
1238 track2->SetStart( t2newPoint );
1239 else
1240 track2->SetEnd( t2newPoint );
1241
1242 operationPerformedOnAtLeastOne = true;
1243 }
1244 }
1245
1246 commit.Push( _( "Fillet Tracks" ) );
1247
1248 //select the newly created arcs
1249 for( BOARD_ITEM* item : itemsToAddToSelection )
1251
1252 if( !operationPerformedOnAtLeastOne )
1253 frame()->ShowInfoBarMsg( _( "Unable to fillet the selected track segments." ) );
1254 else if( didOneAttemptFail )
1255 frame()->ShowInfoBarMsg( _( "Some of the track segments could not be filleted." ) );
1256
1257 return 0;
1258}
1259
1260
1270static std::optional<int> GetRadiusParams( PCB_BASE_EDIT_FRAME& aFrame, const wxString& aTitle,
1271 int& aPersitentRadius )
1272{
1273 WX_UNIT_ENTRY_DIALOG dlg( &aFrame, aTitle, _( "Radius:" ), aPersitentRadius );
1274
1275 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue() == 0 )
1276 return std::nullopt;
1277
1278 aPersitentRadius = dlg.GetValue();
1279
1280 return aPersitentRadius;
1281}
1282
1283
1284static std::optional<DOGBONE_CORNER_ROUTINE::PARAMETERS>
1286{
1287 // Persistent parameters
1288 static DOGBONE_CORNER_ROUTINE::PARAMETERS s_dogBoneParams{
1289 pcbIUScale.mmToIU( 1 ),
1290 true,
1291 };
1292
1293 std::vector<WX_MULTI_ENTRY_DIALOG::ENTRY> entries{
1294 {
1295 _( "Arc radius:" ),
1296 WX_MULTI_ENTRY_DIALOG::UNIT_BOUND{ s_dogBoneParams.DogboneRadiusIU },
1297 wxEmptyString,
1298 },
1299 {
1300 _( "Add slots in acute corners" ),
1301 WX_MULTI_ENTRY_DIALOG::CHECKBOX{ s_dogBoneParams.AddSlots },
1302 _( "Add slots in acute corners to allow access to a cutter of the given radius" ),
1303 },
1304 };
1305
1306 WX_MULTI_ENTRY_DIALOG dlg( &aFrame, _( "Dogbone Corner Settings" ), entries );
1307
1308 if( dlg.ShowModal() == wxID_CANCEL )
1309 return std::nullopt;
1310
1311 std::vector<WX_MULTI_ENTRY_DIALOG::RESULT> results = dlg.GetValues();
1312 wxCHECK( results.size() == 2, std::nullopt );
1313
1314 try
1315 {
1316 s_dogBoneParams.DogboneRadiusIU = std::get<long long int>( results[0] );
1317 s_dogBoneParams.AddSlots = std::get<bool>( results[1] );
1318 }
1319 catch( const std::bad_variant_access& )
1320 {
1321 wxASSERT( false );
1322 return std::nullopt;
1323 }
1324
1325 return s_dogBoneParams;
1326}
1327
1336static std::optional<CHAMFER_PARAMS> GetChamferParams( PCB_BASE_EDIT_FRAME& aFrame )
1337{
1338 // Non-zero and the KLC default for Fab layer chamfers
1339 const int default_setback = pcbIUScale.mmToIU( 1 );
1340 // Store last used setback to allow pressing "enter" if repeat chamfer is required
1341 static CHAMFER_PARAMS params{ default_setback, default_setback };
1342
1343 WX_UNIT_ENTRY_DIALOG dlg( &aFrame, _( "Chamfer Lines" ), _( "Chamfer setback:" ),
1344 params.m_chamfer_setback_a );
1345
1346 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue() == 0 )
1347 return std::nullopt;
1348
1349 params.m_chamfer_setback_a = dlg.GetValue();
1350 // It's hard to easily specify an asymmetric chamfer (which line gets the longer setback?),
1351 // so we just use the same setback for each
1352 params.m_chamfer_setback_b = params.m_chamfer_setback_a;
1353
1354 return params;
1355}
1356
1357
1359{
1361 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1362 {
1363 std::vector<VECTOR2I> pts;
1364
1365 // Iterate from the back so we don't have to worry about removals.
1366 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1367 {
1368 BOARD_ITEM* item = aCollector[i];
1369
1370 // We've converted the polygon and rectangle to segments, so drop everything
1371 // that isn't a segment at this point
1372 if( !item->IsType( { PCB_SHAPE_LOCATE_SEGMENT_T,
1373 PCB_SHAPE_LOCATE_POLY_T,
1374 PCB_SHAPE_LOCATE_RECT_T } ) )
1375 {
1376 aCollector.Remove( item );
1377 }
1378 }
1379 },
1380 true /* prompt user regarding locked items */ );
1381
1382 std::set<PCB_SHAPE*> lines_to_add;
1383 std::vector<PCB_SHAPE*> items_to_remove;
1384
1385 for( EDA_ITEM* item : selection )
1386 {
1387 std::vector<VECTOR2I> pts;
1388 PCB_SHAPE *graphic = static_cast<PCB_SHAPE*>( item );
1389 PCB_LAYER_ID layer = graphic->GetLayer();
1390 int width = graphic->GetWidth();
1391
1392 if( graphic->GetShape() == SHAPE_T::RECTANGLE )
1393 {
1394 items_to_remove.push_back( graphic );
1395 VECTOR2I start( graphic->GetStart() );
1396 VECTOR2I end( graphic->GetEnd() );
1397 pts.emplace_back( start );
1398 pts.emplace_back( VECTOR2I( end.x, start.y ) );
1399 pts.emplace_back( end );
1400 pts.emplace_back( VECTOR2I( start.x, end.y ) );
1401 }
1402
1403 if( graphic->GetShape() == SHAPE_T::POLY )
1404 {
1405 items_to_remove.push_back( graphic );
1406
1407 for( int jj = 0; jj < graphic->GetPolyShape().VertexCount(); ++jj )
1408 pts.emplace_back( graphic->GetPolyShape().CVertex( jj ) );
1409 }
1410
1411 for( size_t jj = 1; jj < pts.size(); ++jj )
1412 {
1413 PCB_SHAPE *line = new PCB_SHAPE( frame()->GetModel(), SHAPE_T::SEGMENT );
1414
1415 line->SetStart( pts[jj - 1] );
1416 line->SetEnd( pts[jj] );
1417 line->SetWidth( width );
1418 line->SetLayer( layer );
1419 lines_to_add.insert( line );
1420 }
1421
1422 if( pts.size() > 1 )
1423 {
1424 PCB_SHAPE *line = new PCB_SHAPE( frame()->GetModel(), SHAPE_T::SEGMENT );
1425
1426 line->SetStart( pts.back() );
1427 line->SetEnd( pts.front() );
1428 line->SetWidth( width );
1429 line->SetLayer( layer );
1430 lines_to_add.insert( line );
1431 }
1432 }
1433
1434 int segmentCount = selection.CountType( PCB_SHAPE_LOCATE_SEGMENT_T ) + lines_to_add.size();
1435
1436 if( aEvent.IsAction( &PCB_ACTIONS::extendLines ) && segmentCount != 2 )
1437 {
1438 frame()->ShowInfoBarMsg( _( "Exactly two lines must be selected to extend them." ) );
1439
1440 for( PCB_SHAPE* line : lines_to_add )
1441 delete line;
1442
1443 return 0;
1444 }
1445 else if( segmentCount < 2 )
1446 {
1447 frame()->ShowInfoBarMsg( _( "A shape with at least two lines must be selected." ) );
1448
1449 for( PCB_SHAPE* line : lines_to_add )
1450 delete line;
1451
1452 return 0;
1453 }
1454
1455 BOARD_COMMIT commit( this );
1456
1457 // Items created like lines from a rectangle
1458 for( PCB_SHAPE* item : lines_to_add )
1459 {
1460 commit.Add( item );
1461 selection.Add( item );
1462 }
1463
1464 // Remove items like rectangles that we decomposed into lines
1465 for( PCB_SHAPE* item : items_to_remove )
1466 {
1467 selection.Remove( item );
1468 commit.Remove( item );
1469 }
1470
1471 for( EDA_ITEM* item : selection )
1472 item->ClearFlags( STRUCT_DELETED );
1473
1474 // List of thing to select at the end of the operation
1475 // (doing it as we go will invalidate the iterator)
1476 std::vector<BOARD_ITEM*> items_to_select_on_success;
1477
1478 // And same for items to deselect
1479 std::vector<BOARD_ITEM*> items_to_deselect_on_success;
1480
1481 // Handle modifications to existing items by the routine
1482 // How to deal with this depends on whether we're in the footprint editor or not
1483 // and whether the item was conjured up by decomposing a polygon or rectangle
1484 auto item_modification_handler =
1485 [&]( BOARD_ITEM& aItem )
1486 {
1487 // If the item was "conjured up" it will be added later separately
1488 if( !alg::contains( lines_to_add, &aItem ) )
1489 {
1490 commit.Modify( &aItem );
1491 items_to_select_on_success.push_back( &aItem );
1492 }
1493 };
1494
1495 bool any_items_created = !lines_to_add.empty();
1496 auto item_creation_handler =
1497 [&]( std::unique_ptr<BOARD_ITEM> aItem )
1498 {
1499 any_items_created = true;
1500 items_to_select_on_success.push_back( aItem.get() );
1501 commit.Add( aItem.release() );
1502 };
1503
1504 bool any_items_removed = !items_to_remove.empty();
1505 auto item_removal_handler =
1506 [&]( BOARD_ITEM& aItem )
1507 {
1508 aItem.SetFlags( STRUCT_DELETED );
1509 any_items_removed = true;
1510 items_to_deselect_on_success.push_back( &aItem );
1511 commit.Remove( &aItem );
1512 };
1513
1514 // Combine these callbacks into a CHANGE_HANDLER to inject in the ROUTINE
1516 item_creation_handler, item_modification_handler, item_removal_handler );
1517
1518 // Construct an appropriate tool
1519 std::unique_ptr<PAIRWISE_LINE_ROUTINE> pairwise_line_routine;
1520
1521 if( aEvent.IsAction( &PCB_ACTIONS::filletLines ) )
1522 {
1523 static int s_filletRadius = pcbIUScale.mmToIU( 1 );
1524 std::optional<int> filletRadiusIU =
1525 GetRadiusParams( *frame(), _( "Fillet Lines" ), s_filletRadius );
1526
1527 if( filletRadiusIU.has_value() )
1528 {
1529 pairwise_line_routine = std::make_unique<LINE_FILLET_ROUTINE>(
1530 frame()->GetModel(), change_handler, *filletRadiusIU );
1531 }
1532 }
1533 else if( aEvent.IsAction( &PCB_ACTIONS::dogboneCorners ) )
1534 {
1535 std::optional<DOGBONE_CORNER_ROUTINE::PARAMETERS> dogboneParams =
1536 GetDogboneParams( *frame() );
1537
1538 if( dogboneParams.has_value() )
1539 {
1540 pairwise_line_routine = std::make_unique<DOGBONE_CORNER_ROUTINE>(
1541 frame()->GetModel(), change_handler, *dogboneParams );
1542 }
1543 }
1544 else if( aEvent.IsAction( &PCB_ACTIONS::chamferLines ) )
1545 {
1546 std::optional<CHAMFER_PARAMS> chamfer_params = GetChamferParams( *frame() );
1547
1548 if( chamfer_params.has_value() )
1549 {
1550 pairwise_line_routine = std::make_unique<LINE_CHAMFER_ROUTINE>( frame()->GetModel(),
1551 change_handler,
1552 *chamfer_params );
1553 }
1554 }
1555 else if( aEvent.IsAction( &PCB_ACTIONS::extendLines ) )
1556 {
1557 pairwise_line_routine = std::make_unique<LINE_EXTENSION_ROUTINE>( frame()->GetModel(),
1558 change_handler );
1559 }
1560
1561 if( !pairwise_line_routine )
1562 {
1563 // Didn't construct any mofication routine - user must have cancelled
1564 commit.Revert();
1565 return 0;
1566 }
1567
1568 // Apply the tool to every line pair
1569 alg::for_all_pairs( selection.begin(), selection.end(),
1570 [&]( EDA_ITEM* a, EDA_ITEM* b )
1571 {
1572 if( ( a->GetFlags() & STRUCT_DELETED ) == 0
1573 && ( b->GetFlags() & STRUCT_DELETED ) == 0 )
1574 {
1575 PCB_SHAPE* line_a = static_cast<PCB_SHAPE*>( a );
1576 PCB_SHAPE* line_b = static_cast<PCB_SHAPE*>( b );
1577
1578 pairwise_line_routine->ProcessLinePair( *line_a, *line_b );
1579 }
1580 } );
1581
1582 // Select added and modified items
1583 for( BOARD_ITEM* item : items_to_select_on_success )
1584 m_selectionTool->AddItemToSel( item, true );
1585
1586 // Deselect removed items
1587 for( BOARD_ITEM* item : items_to_deselect_on_success )
1588 m_selectionTool->RemoveItemFromSel( item, true );
1589
1590 if( any_items_removed )
1591 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1592
1593 if( any_items_created )
1594 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1595
1596 // Notify other tools of the changes
1597 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1598
1599 commit.Push( pairwise_line_routine->GetCommitDescription() );
1600
1601 if( const std::optional<wxString> msg = pairwise_line_routine->GetStatusMessage() )
1602 frame()->ShowInfoBarMsg( *msg );
1603
1604 return 0;
1605}
1606
1607
1609{
1611 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1612 {
1613 std::vector<VECTOR2I> pts;
1614
1615 // Iterate from the back so we don't have to worry about removals.
1616 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1617 {
1618 BOARD_ITEM* item = aCollector[i];
1619
1620 if( !item->IsType( { PCB_SHAPE_LOCATE_POLY_T, PCB_ZONE_T } ) )
1621 aCollector.Remove( item );
1622
1623 if( ZONE* zone = dyn_cast<ZONE*>( item ) )
1624 {
1625 if( zone->IsTeardropArea() )
1626 aCollector.Remove( item );
1627 }
1628 }
1629 },
1630 true /* prompt user regarding locked items */ );
1631
1632 // Store last used value
1633 static int s_toleranceValue = pcbIUScale.mmToIU( 3 );
1634
1635 WX_UNIT_ENTRY_DIALOG dlg( frame(), _( "Simplify Shapes" ), _( "Tolerance value:" ),
1636 s_toleranceValue );
1637
1638 if( dlg.ShowModal() == wxID_CANCEL )
1639 return 0;
1640
1641 s_toleranceValue = dlg.GetValue();
1642
1643 if( s_toleranceValue <= 0 )
1644 return 0;
1645
1646 BOARD_COMMIT commit{ this };
1647
1648 std::vector<PCB_SHAPE*> shapeList;
1649
1650 for( EDA_ITEM* item : selection )
1651 {
1652 commit.Modify( item );
1653
1654 if( PCB_SHAPE* shape = dyn_cast<PCB_SHAPE*>( item ) )
1655 {
1656 SHAPE_POLY_SET& poly = shape->GetPolyShape();
1657
1658 poly.SimplifyOutlines( s_toleranceValue );
1659 }
1660
1661 if( ZONE* zone = dyn_cast<ZONE*>( item ) )
1662 {
1663 SHAPE_POLY_SET* poly = zone->Outline();
1664
1665 poly->SimplifyOutlines( s_toleranceValue );
1666 }
1667 }
1668
1669 commit.Push( _( "Simplify Polygons" ) );
1670
1671 // Notify other tools of the changes
1672 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1673
1674 return 0;
1675}
1676
1677
1679{
1681 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1682 {
1683 std::vector<VECTOR2I> pts;
1684
1685 // Iterate from the back so we don't have to worry about removals.
1686 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1687 {
1688 BOARD_ITEM* item = aCollector[i];
1689
1690 // We've converted the polygon and rectangle to segments, so drop everything
1691 // that isn't a segment at this point
1692 if( !item->IsType( { PCB_SHAPE_LOCATE_SEGMENT_T, PCB_SHAPE_LOCATE_ARC_T,
1693 PCB_SHAPE_LOCATE_BEZIER_T } ) )
1694 {
1695 aCollector.Remove( item );
1696 }
1697 }
1698 },
1699 true /* prompt user regarding locked items */ );
1700
1701 // Store last used value
1702 static int s_toleranceValue = pcbIUScale.mmToIU( 3 );
1703
1704 WX_UNIT_ENTRY_DIALOG dlg( frame(), _( "Heal Shapes" ), _( "Tolerance value:" ),
1705 s_toleranceValue );
1706
1707 if( dlg.ShowModal() == wxID_CANCEL )
1708 return 0;
1709
1710 s_toleranceValue = dlg.GetValue();
1711
1712 if( s_toleranceValue <= 0 )
1713 return 0;
1714
1715 BOARD_COMMIT commit{ this };
1716
1717 std::vector<PCB_SHAPE*> shapeList;
1718 std::vector<std::unique_ptr<PCB_SHAPE>> newShapes;
1719
1720 for( EDA_ITEM* item : selection )
1721 {
1722 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item ) )
1723 {
1724 shapeList.push_back( shape );
1725 commit.Modify( shape );
1726 }
1727 }
1728
1729 ConnectBoardShapes( shapeList, newShapes, s_toleranceValue );
1730
1731 std::vector<PCB_SHAPE*> items_to_select;
1732
1733 for( std::unique_ptr<PCB_SHAPE>& ptr : newShapes )
1734 {
1735 PCB_SHAPE* shape = ptr.release();
1736
1737 commit.Add( shape );
1738 items_to_select.push_back( shape );
1739 }
1740
1741 commit.Push( _( "Heal Shapes" ) );
1742
1743 // Select added items
1744 for( PCB_SHAPE* item : items_to_select )
1745 m_selectionTool->AddItemToSel( item, true );
1746
1747 if( items_to_select.size() > 0 )
1748 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1749
1750 // Notify other tools of the changes
1751 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1752
1753 return 0;
1754}
1755
1756
1758{
1760 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1761 {
1762 // Iterate from the back so we don't have to worry about removals.
1763 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1764 {
1765 BOARD_ITEM* item = aCollector[i];
1766
1767 if( !item->IsType( {
1768 PCB_SHAPE_LOCATE_POLY_T,
1769 PCB_SHAPE_LOCATE_RECT_T,
1770 } ) )
1771 {
1772 aCollector.Remove( item );
1773 }
1774 }
1775 },
1776 true /* prompt user regarding locked items */ );
1777
1778 const EDA_ITEM* const last_item = selection.GetLastAddedItem();
1779
1780 // Gather or construct polygon source shapes to merge
1781 std::vector<PCB_SHAPE*> items_to_process;
1782
1783 for( EDA_ITEM* item : selection )
1784 {
1785 items_to_process.push_back( static_cast<PCB_SHAPE*>( item ) );
1786
1787 // put the last one in the selection at the front of the vector
1788 // so it can be used as the property donor and as the basis for the
1789 // boolean operation
1790 if( item == last_item )
1791 std::swap( items_to_process.back(), items_to_process.front() );
1792 }
1793
1794 BOARD_COMMIT commit{ this };
1795
1796 // Handle modifications to existing items by the routine
1797 auto item_modification_handler =
1798 [&]( BOARD_ITEM& aItem )
1799 {
1800 commit.Modify( &aItem );
1801 };
1802
1803 std::vector<BOARD_ITEM*> items_to_select_on_success;
1804
1805 auto item_creation_handler =
1806 [&]( std::unique_ptr<BOARD_ITEM> aItem )
1807 {
1808 items_to_select_on_success.push_back( aItem.get() );
1809 commit.Add( aItem.release() );
1810 };
1811
1812 auto item_removal_handler =
1813 [&]( BOARD_ITEM& aItem )
1814 {
1815 commit.Remove( &aItem );
1816 };
1817
1818 // Combine these callbacks into a CHANGE_HANDLER to inject in the ROUTINE
1820 item_creation_handler, item_modification_handler, item_removal_handler );
1821
1822 // Construct an appropriate routine
1823 std::unique_ptr<POLYGON_BOOLEAN_ROUTINE> boolean_routine;
1824 if( aEvent.IsAction( &PCB_ACTIONS::mergePolygons ) )
1825 {
1826 boolean_routine = std::make_unique<POLYGON_MERGE_ROUTINE>( frame()->GetModel(),
1827 change_handler );
1828 }
1829 else if( aEvent.IsAction( &PCB_ACTIONS::subtractPolygons ) )
1830 {
1831 boolean_routine = std::make_unique<POLYGON_SUBTRACT_ROUTINE>( frame()->GetModel(),
1832 change_handler );
1833 }
1834 else if( aEvent.IsAction( &PCB_ACTIONS::intersectPolygons ) )
1835 {
1836 boolean_routine = std::make_unique<POLYGON_INTERSECT_ROUTINE>( frame()->GetModel(),
1837 change_handler );
1838 }
1839 else
1840 {
1841 wxASSERT_MSG( false, "Could not find a polygon routine for this action" );
1842 return 0;
1843 }
1844
1845 // Perform the operation on each polygon
1846 for( PCB_SHAPE* shape : items_to_process )
1847 boolean_routine->ProcessShape( *shape );
1848
1849 boolean_routine->Finalize();
1850
1851 // Select new items
1852 for( BOARD_ITEM* item : items_to_select_on_success )
1853 m_selectionTool->AddItemToSel( item, true );
1854
1855 // Notify other tools of the changes
1856 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1857
1858 commit.Push( boolean_routine->GetCommitDescription() );
1859
1860 if( const std::optional<wxString> msg = boolean_routine->GetStatusMessage() )
1861 frame()->ShowInfoBarMsg( *msg );
1862
1863 return 0;
1864}
1865
1866
1868{
1869 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1871 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1872 {
1873 } );
1874
1875 // Tracks & vias are treated in a special way:
1876 if( ( SELECTION_CONDITIONS::OnlyTypes( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } ) )( selection ) )
1877 {
1878 DIALOG_TRACK_VIA_PROPERTIES dlg( editFrame, selection );
1879 dlg.ShowQuasiModal(); // QuasiModal required for NET_SELECTOR
1880 }
1881 else if( ( SELECTION_CONDITIONS::OnlyTypes( { PCB_TABLECELL_T } ) )( selection ) )
1882 {
1883 std::vector<PCB_TABLECELL*> cells;
1884
1885 for( EDA_ITEM* item : selection.Items() )
1886 cells.push_back( static_cast<PCB_TABLECELL*>( item ) );
1887
1888 DIALOG_TABLECELL_PROPERTIES dlg( editFrame, cells );
1889
1890 dlg.ShowModal();
1891
1893 {
1894 PCB_TABLE* table = static_cast<PCB_TABLE*>( cells[0]->GetParent() );
1895 DIALOG_TABLE_PROPERTIES tableDlg( frame(), table );
1896
1897 tableDlg.ShowQuasiModal(); // Scintilla's auto-complete requires quasiModal
1898 }
1899 }
1900 else if( selection.Size() == 1 && selection.Front()->IsBOARD_ITEM() )
1901 {
1902 // Display properties dialog
1903 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
1904
1905 // Do not handle undo buffer, it is done by the properties dialogs
1906 editFrame->OnEditItemRequest( item );
1907
1908 // Notify other tools of the changes
1910 }
1911 else if( selection.Size() == 0 && getView()->IsLayerVisible( LAYER_DRAWINGSHEET ) )
1912 {
1913 DS_PROXY_VIEW_ITEM* ds = editFrame->GetCanvas()->GetDrawingSheet();
1914 VECTOR2D cursorPos = getViewControls()->GetCursorPosition( false );
1915
1916 if( ds && ds->HitTestDrawingSheetItems( getView(), cursorPos ) )
1918 else
1920 }
1921
1922 if( selection.IsHover() )
1923 {
1925 }
1926 else
1927 {
1928 // Check for items becoming invisible and drop them from the selection.
1929
1930 PCB_SELECTION selCopy = selection;
1931 LSET visible = editFrame->GetBoard()->GetVisibleLayers();
1932
1933 for( EDA_ITEM* eda_item : selCopy )
1934 {
1935 if( !eda_item->IsBOARD_ITEM() )
1936 continue;
1937
1938 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( eda_item );
1939
1940 if( !( item->GetLayerSet() & visible ).any() )
1942 }
1943 }
1944
1945 return 0;
1946}
1947
1948
1949int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
1950{
1951 if( isRouterActive() )
1952 {
1953 wxBell();
1954 return 0;
1955 }
1956
1957 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1958 BOARD_COMMIT localCommit( this );
1959 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
1960
1961 if( !commit )
1962 commit = &localCommit;
1963
1964 // Be sure that there is at least one item that we can modify. If nothing was selected before,
1965 // try looking for the stuff under mouse cursor (i.e. KiCad old-style hover selection)
1967 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1968 {
1969 sTool->FilterCollectorForHierarchy( aCollector, true );
1970 sTool->FilterCollectorForMarkers( aCollector );
1971 sTool->FilterCollectorForTableCells( aCollector );
1972 },
1973 // Prompt user regarding locked items if in board editor and in free-pad-mode (if
1974 // we're not in free-pad mode we delay this until the second RequestSelection()).
1975 !m_dragging && frame()->GetPcbNewSettings()->m_AllowFreePads && !m_isFootprintEditor );
1976
1977 if( selection.Empty() )
1978 return 0;
1979
1980 std::optional<VECTOR2I> oldRefPt;
1981 bool is_hover = selection.IsHover(); // N.B. This must be saved before the second
1982 // call to RequestSelection() below
1983
1984 if( selection.HasReferencePoint() )
1985 oldRefPt = selection.GetReferencePoint();
1986
1987 // Now filter out pads if not in free pads mode. We cannot do this in the first
1988 // RequestSelection() as we need the reference point when a pad is the selection front.
1989 if( !m_isFootprintEditor && !frame()->GetPcbNewSettings()->m_AllowFreePads )
1990 {
1991 selection = m_selectionTool->RequestSelection(
1992 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1993 {
1994 sTool->FilterCollectorForMarkers( aCollector );
1995 sTool->FilterCollectorForHierarchy( aCollector, true );
1996 sTool->FilterCollectorForFreePads( aCollector );
1997 sTool->FilterCollectorForTableCells( aCollector );
1998 },
1999 !m_dragging /* prompt user regarding locked items */ );
2000 }
2001
2002 // Did we filter everything out? If so, don't try to operate further
2003 if( selection.Empty() )
2004 return 0;
2005
2006 // A single textbox "walks" if we rotate it around its position, as we keep resetting which
2007 // corner is the origin.
2008 if( selection.Size() == 1 && dynamic_cast<PCB_TEXTBOX*>( selection.Front() ) )
2009 {
2010 selection.SetReferencePoint( static_cast<PCB_TEXTBOX*>( selection.Front() )->GetCenter() );
2011 }
2012 else
2013 {
2014 updateModificationPoint( selection );
2015 }
2016
2017 VECTOR2I refPt = selection.GetReferencePoint();
2018 EDA_ANGLE rotateAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *editFrame, aEvent );
2019
2020 if( frame()->GetCanvas()->GetView()->GetGAL()->IsFlippedX() )
2021 rotateAngle = -rotateAngle;
2022
2023 // Calculate view bounding box
2024 BOX2I viewBBox = selection.Front()->ViewBBox();
2025
2026 for( EDA_ITEM* item : selection )
2027 viewBBox.Merge( item->ViewBBox() );
2028
2029 // Check if the view bounding box will go out of bounds
2030 VECTOR2D rotPos = viewBBox.GetPosition();
2031 VECTOR2D rotEnd = viewBBox.GetEnd();
2032
2033 RotatePoint( &rotPos.x, &rotPos.y, refPt.x, refPt.y, rotateAngle );
2034 RotatePoint( &rotEnd.x, &rotEnd.y, refPt.x, refPt.y, rotateAngle );
2035
2036 typedef std::numeric_limits<int> coord_limits;
2037
2038 int max = coord_limits::max() - COORDS_PADDING;
2039 int min = -max;
2040
2041 bool outOfBounds = rotPos.x < min || rotPos.x > max || rotPos.y < min || rotPos.y > max
2042 || rotEnd.x < min || rotEnd.x > max || rotEnd.y < min || rotEnd.y > max;
2043
2044 if( !outOfBounds )
2045 {
2046 for( EDA_ITEM* item : selection )
2047 {
2048 if( !item->IsNew() && !item->IsMoving() )
2049 commit->Modify( item );
2050
2051 if( item->IsBOARD_ITEM() )
2052 {
2053 BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item );
2054
2055 board_item->Rotate( refPt, rotateAngle );
2056 board_item->Normalize();
2057 }
2058 }
2059
2060 if( !localCommit.Empty() )
2061 localCommit.Push( _( "Rotate" ) );
2062
2063 if( is_hover && !m_dragging )
2065
2067
2068 if( m_dragging )
2070 }
2071
2072 // Restore the old reference so any mouse dragging that occurs doesn't make the selection jump
2073 // to this now invalid reference
2074 if( oldRefPt )
2075 selection.SetReferencePoint( *oldRefPt );
2076 else
2077 selection.ClearReferencePoint();
2078
2079 return 0;
2080}
2081
2082
2086static void mirrorPad( PAD& aPad, const VECTOR2I& aMirrorPoint, FLIP_DIRECTION aFlipDirection )
2087{
2088 // TODO(JE) padstacks
2090 aPad.FlipPrimitives( aFlipDirection );
2091
2092 VECTOR2I tmpPt = aPad.GetPosition();
2093 MIRROR( tmpPt, aMirrorPoint, aFlipDirection );
2094 aPad.SetPosition( tmpPt );
2095
2096 tmpPt = aPad.GetOffset( PADSTACK::ALL_LAYERS );
2097 MIRROR( tmpPt, VECTOR2I{ 0, 0 }, aFlipDirection );
2098 aPad.SetOffset( PADSTACK::ALL_LAYERS, tmpPt );
2099
2100 VECTOR2I tmpz = aPad.GetDelta( PADSTACK::ALL_LAYERS );
2101 MIRROR( tmpz, VECTOR2I{ 0, 0 }, aFlipDirection );
2102 aPad.SetDelta( PADSTACK::ALL_LAYERS, tmpz );
2103
2104 aPad.SetOrientation( -aPad.GetOrientation() );
2105}
2106
2107
2108const std::vector<KICAD_T> EDIT_TOOL::MirrorableItems = {
2111 PCB_TEXT_T,
2113 PCB_ZONE_T,
2114 PCB_PAD_T,
2116 PCB_ARC_T,
2117 PCB_VIA_T,
2119};
2120
2121int EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
2122{
2123 if( isRouterActive() )
2124 {
2125 wxBell();
2126 return 0;
2127 }
2128
2129 BOARD_COMMIT localCommit( this );
2130 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
2131
2132 if( !commit )
2133 commit = &localCommit;
2134
2136 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2137 {
2138 sTool->FilterCollectorForMarkers( aCollector );
2139 sTool->FilterCollectorForHierarchy( aCollector, true );
2140 sTool->FilterCollectorForFreePads( aCollector );
2141 },
2142 !m_dragging /* prompt user regarding locked items */ );
2143
2144 if( selection.Empty() )
2145 return 0;
2146
2147 updateModificationPoint( selection );
2148 VECTOR2I mirrorPoint = selection.GetReferencePoint();
2149
2150 FLIP_DIRECTION flipDirection = aEvent.IsAction( &PCB_ACTIONS::mirrorV )
2151 ? FLIP_DIRECTION::TOP_BOTTOM
2152 : FLIP_DIRECTION::LEFT_RIGHT;
2153
2154 std::vector<EDA_ITEM*> items;
2155
2156 for( EDA_ITEM* item : selection )
2157 {
2158 if( item->Type() == PCB_GROUP_T )
2159 {
2160 static_cast<PCB_GROUP*>( item )->RunOnDescendants(
2161 [&]( BOARD_ITEM* descendant )
2162 {
2163 items.push_back( descendant );
2164 } );
2165 }
2166 else
2167 {
2168 items.push_back( item );
2169 }
2170 }
2171
2172 for( EDA_ITEM* item : items )
2173 {
2174 if( !item->IsType( MirrorableItems ) )
2175 continue;
2176
2177 if( !item->IsNew() && !item->IsMoving() )
2178 commit->Modify( item );
2179
2180 // modify each object as necessary
2181 switch( item->Type() )
2182 {
2183 case PCB_SHAPE_T:
2184 static_cast<PCB_SHAPE*>( item )->Mirror( mirrorPoint, flipDirection );
2185 break;
2186
2187 case PCB_ZONE_T:
2188 static_cast<ZONE*>( item )->Mirror( mirrorPoint, flipDirection );
2189 break;
2190
2191 case PCB_FIELD_T:
2192 case PCB_TEXT_T:
2193 static_cast<PCB_TEXT*>( item )->Mirror( mirrorPoint, flipDirection );
2194 break;
2195
2196 case PCB_TEXTBOX_T:
2197 static_cast<PCB_TEXTBOX*>( item )->Mirror( mirrorPoint, flipDirection );
2198 break;
2199
2200 case PCB_TABLE_T:
2201 // JEY TODO: tables
2202 break;
2203
2204 case PCB_PAD_T:
2205 mirrorPad( *static_cast<PAD*>( item ), mirrorPoint, flipDirection );
2206 break;
2207
2208 case PCB_TRACE_T:
2209 case PCB_ARC_T:
2210 case PCB_VIA_T:
2211 static_cast<PCB_TRACK*>( item )->Mirror( mirrorPoint, flipDirection );
2212 break;
2213
2214 case PCB_GENERATOR_T:
2215 static_cast<PCB_GENERATOR*>( item )->Mirror( mirrorPoint, flipDirection );
2216 break;
2217
2218 default:
2219 // it's likely the commit object is wrong if you get here
2220 UNIMPLEMENTED_FOR( item->GetClass() );
2221 }
2222 }
2223
2224 if( !localCommit.Empty() )
2225 localCommit.Push( _( "Mirror" ) );
2226
2227 if( selection.IsHover() && !m_dragging )
2229
2231
2232 if( m_dragging )
2234
2235 return 0;
2236}
2237
2238
2240{
2241 if( isRouterActive() )
2242 {
2243 wxBell();
2244 return 0;
2245 }
2246
2247 BOARD_COMMIT localCommit( this );
2248 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
2249
2250 if( !commit )
2251 commit = &localCommit;
2252
2254 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2255 {
2256 sTool->FilterCollectorForHierarchy( aCollector, true );
2257 },
2258 !m_dragging /* prompt user regarding locked items */ );
2259
2260 if( selection.Empty() )
2261 return 0;
2262
2263 auto setJustify =
2264 [&]( EDA_TEXT* aTextItem )
2265 {
2266 if( aEvent.Matches( ACTIONS::leftJustify.MakeEvent() ) )
2267 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
2268 else if( aEvent.Matches( ACTIONS::centerJustify.MakeEvent() ) )
2269 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
2270 else
2271 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
2272 };
2273
2274 for( EDA_ITEM* item : selection )
2275 {
2276 if( item->Type() == PCB_FIELD_T || item->Type() == PCB_TEXT_T )
2277 {
2278 if( !item->IsNew() && !item->IsMoving() )
2279 commit->Modify( item );
2280
2281 setJustify( static_cast<PCB_TEXT*>( item ) );
2282 }
2283 else if( item->Type() == PCB_TEXTBOX_T )
2284 {
2285 if( !item->IsNew() && !item->IsMoving() )
2286 commit->Modify( item );
2287
2288 setJustify( static_cast<PCB_TEXTBOX*>( item ) );
2289 }
2290 }
2291
2292 if( !localCommit.Empty() )
2293 {
2294 if( aEvent.Matches( ACTIONS::leftJustify.MakeEvent() ) )
2295 localCommit.Push( _( "Left Justify" ) );
2296 else if( aEvent.Matches( ACTIONS::centerJustify.MakeEvent() ) )
2297 localCommit.Push( _( "Center Justify" ) );
2298 else
2299 localCommit.Push( _( "Right Justify" ) );
2300 }
2301
2302 if( selection.IsHover() && !m_dragging )
2304
2306
2307 if( m_dragging )
2309
2310 return 0;
2311}
2312
2313
2314int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent )
2315{
2316 if( isRouterActive() )
2317 {
2318 wxBell();
2319 return 0;
2320 }
2321
2322 BOARD_COMMIT localCommit( this );
2323 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
2324
2325 if( !commit )
2326 commit = &localCommit;
2327
2329 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2330 {
2331 sTool->FilterCollectorForMarkers( aCollector );
2332 sTool->FilterCollectorForHierarchy( aCollector, true );
2333 sTool->FilterCollectorForFreePads( aCollector );
2334 sTool->FilterCollectorForTableCells( aCollector );
2335 },
2336 !m_dragging /* prompt user regarding locked items */ );
2337
2338 if( selection.Empty() )
2339 return 0;
2340
2341 std::optional<VECTOR2I> oldRefPt;
2342
2343 if( selection.HasReferencePoint() )
2344 oldRefPt = selection.GetReferencePoint();
2345
2346 updateModificationPoint( selection );
2347
2348 // Flip around the anchor for footprints, and the bounding box center for board items
2349 VECTOR2I refPt = IsFootprintEditor() ? VECTOR2I( 0, 0 ) : selection.GetCenter();
2350
2351 // If only one item selected, flip around the selection or item anchor point (instead
2352 // of the bounding box center) to avoid moving the item anchor
2353 // but only if the item is not a PCB_SHAPE with SHAPE_T::RECTANGLE shape, because
2354 // for this shape the flip transform swap start and end coordinates and move the shape.
2355 // So using the center of the shape is better (the shape does not move)
2356 if( selection.GetSize() == 1 )
2357 {
2358 PCB_SHAPE* item = dynamic_cast<PCB_SHAPE*>( selection.GetItem( 0 ) );
2359
2360 if( !item || item->GetShape() != SHAPE_T::RECTANGLE )
2361 refPt = selection.GetReferencePoint();
2362 }
2363
2364 const FLIP_DIRECTION flipDirection = frame()->GetPcbNewSettings()->m_FlipDirection;
2365
2366 for( EDA_ITEM* item : selection )
2367 {
2368 if( !item->IsBOARD_ITEM() )
2369 continue;
2370
2371 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
2372
2373 if( !boardItem->IsNew() && !boardItem->IsMoving() )
2374 commit->Modify( boardItem );
2375
2376 boardItem->Flip( refPt, flipDirection );
2377 boardItem->Normalize();
2378 }
2379
2380 if( !localCommit.Empty() )
2381 localCommit.Push( _( "Change Side / Flip" ) );
2382
2383 if( selection.IsHover() && !m_dragging )
2385
2387
2388 if( m_dragging )
2390
2391 // Restore the old reference so any mouse dragging that occurs doesn't make the selection jump
2392 // to this now invalid reference
2393 if( oldRefPt )
2394 selection.SetReferencePoint( *oldRefPt );
2395 else
2396 selection.ClearReferencePoint();
2397
2398 return 0;
2399}
2400
2401
2403 std::unordered_set<BOARD_ITEM*>& children )
2404{
2405 wxASSERT( item->Type() == PCB_GROUP_T || item->Type() == PCB_GENERATOR_T );
2406
2407 std::unordered_set<BOARD_ITEM*>& childItems = static_cast<PCB_GROUP*>( item )->GetItems();
2408
2409 for( BOARD_ITEM* childItem : childItems )
2410 {
2411 children.insert( childItem );
2412
2413 if( childItem->Type() == PCB_GROUP_T || childItem->Type() == PCB_GENERATOR_T )
2414 getChildItemsOfGroupsAndGenerators( childItem, children );
2415 }
2416}
2417
2418
2419void EDIT_TOOL::removeNonRootItems( std::unordered_set<EDA_ITEM*>& items )
2420{
2421 auto itr = items.begin();
2422
2423 while( itr != items.end() )
2424 {
2425 EDA_ITEM* item = *itr;
2426
2427 if( item->IsBOARD_ITEM() )
2428 {
2429 BOARD_ITEM* curItem = static_cast<BOARD_ITEM*>( item );
2430
2431 if( curItem->Type() == PCB_GROUP_T || curItem->Type() == PCB_GENERATOR_T )
2432 {
2433 std::unordered_set<BOARD_ITEM*> childItems;
2434 getChildItemsOfGroupsAndGenerators( curItem, childItems );
2435
2436 std::for_each( childItems.begin(), childItems.end(),
2437 [&]( auto eraseItem )
2438 {
2439 items.erase( eraseItem );
2440 } );
2441 }
2442 }
2443
2444 ++itr;
2445 }
2446}
2447
2448
2449void EDIT_TOOL::DeleteItems( const PCB_SELECTION& aItems, bool aIsCut )
2450{
2451 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2452 BOARD_COMMIT commit( this );
2453
2454 // As we are about to remove items, they have to be removed from the selection first
2456
2457 // Only delete items that are the root of a selected set (e.g. only delete grouped / generated
2458 // items from the parent item, not individually) - issue #17527
2459 std::unordered_set<EDA_ITEM*> rootItems( aItems.begin(), aItems.end() );
2460 removeNonRootItems( rootItems );
2461
2462 int itemsDeleted = 0;
2463 int fieldsHidden = 0;
2464 int fieldsAlreadyHidden = 0;
2465
2466 for( EDA_ITEM* item : rootItems )
2467 {
2468 if( !item->IsBOARD_ITEM() )
2469 continue;
2470
2471 BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item );
2472 FOOTPRINT* parentFP = board_item->GetParentFootprint();
2473
2474 if( board_item->GetParentGroup() )
2475 commit.Stage( board_item, CHT_UNGROUP );
2476
2477 switch( item->Type() )
2478 {
2479 case PCB_FIELD_T:
2480 {
2481 PCB_FIELD* field = static_cast<PCB_FIELD*>( board_item );
2482
2483 wxASSERT( parentFP );
2484 commit.Modify( parentFP );
2485
2486 if( field->IsVisible() )
2487 {
2488 field->SetVisible( false );
2489 fieldsHidden++;
2490 }
2491 else
2492 {
2493 fieldsAlreadyHidden++;
2494 }
2495
2496 getView()->Update( parentFP );
2497 break;
2498 }
2499
2500 case PCB_TEXT_T:
2501 if( parentFP )
2502 {
2503 commit.Modify( parentFP );
2504 parentFP->Delete( board_item );
2505 getView()->Update( parentFP );
2506 }
2507 else
2508 {
2509 commit.Remove( board_item );
2510 }
2511
2512 itemsDeleted++;
2513 break;
2514
2515 case PCB_SHAPE_T:
2516 case PCB_TEXTBOX_T:
2517 case PCB_TABLE_T:
2519 case PCB_DIMENSION_T:
2520 case PCB_DIM_ALIGNED_T:
2521 case PCB_DIM_LEADER_T:
2522 case PCB_DIM_CENTER_T:
2523 case PCB_DIM_RADIAL_T:
2525 commit.Remove( board_item );
2526 itemsDeleted++;
2527 break;
2528
2529 case PCB_TABLECELL_T:
2530 // Clear contents of table cell
2531 commit.Modify( board_item );
2532 static_cast<PCB_TABLECELL*>( board_item )->SetText( wxEmptyString );
2533 itemsDeleted++;
2534 break;
2535
2536 case PCB_GROUP_T:
2537 board_item->RunOnDescendants(
2538 [&commit]( BOARD_ITEM* aItem )
2539 {
2540 commit.Stage( aItem, CHT_UNGROUP );
2541 } );
2542
2543 board_item->RunOnDescendants(
2544 [&commit]( BOARD_ITEM* aItem )
2545 {
2546 commit.Remove( aItem );
2547 } );
2548
2549 commit.Remove( board_item );
2550 itemsDeleted++;
2551 break;
2552
2553 case PCB_PAD_T:
2554 if( IsFootprintEditor() || frame()->GetPcbNewSettings()->m_AllowFreePads )
2555 {
2556 commit.Modify( parentFP );
2557 getView()->Remove( board_item );
2558 parentFP->Remove( board_item );
2559 itemsDeleted++;
2560 }
2561
2562 break;
2563
2564 case PCB_ZONE_T:
2565 // We process the zones special so that cutouts can be deleted when the delete
2566 // tool is called from inside a cutout when the zone is selected.
2567 // Only interact with cutouts when deleting and a single item is selected
2568 if( !aIsCut && aItems.GetSize() == 1 )
2569 {
2571 ZONE* zone = static_cast<ZONE*>( board_item );
2572
2573 int outlineIdx, holeIdx;
2574
2575 if( zone->HitTestCutout( curPos, &outlineIdx, &holeIdx ) )
2576 {
2577 // Remove the cutout
2578 commit.Modify( zone );
2579 zone->RemoveCutout( outlineIdx, holeIdx );
2580 zone->UnFill();
2581
2582 // TODO Refill zone when KiCad supports auto re-fill
2583
2584 // Update the display
2585 zone->HatchBorder();
2586 canvas()->Refresh();
2587
2588 // Restore the selection on the original zone
2590
2591 break;
2592 }
2593 }
2594
2595 // Remove the entire zone otherwise
2596 commit.Remove( board_item );
2597 itemsDeleted++;
2598 break;
2599
2600 case PCB_GENERATOR_T:
2601 if( rootItems.size() == 1 )
2602 {
2603 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( board_item );
2604
2606 generator );
2607 }
2608 else
2609 {
2610 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( board_item );
2611
2612 for( BOARD_ITEM* member : generator->GetItems() )
2613 commit.Stage( member, CHT_UNGROUP );
2614
2615 for( BOARD_ITEM* member : generator->GetItems() )
2616 commit.Remove( member );
2617
2618 commit.Remove( board_item );
2619 }
2620
2621 itemsDeleted++;
2622 break;
2623
2624 default:
2625 wxASSERT_MSG( parentFP == nullptr, wxT( "Try to delete an item living in a footprint" ) );
2626 commit.Remove( board_item );
2627 itemsDeleted++;
2628 break;
2629 }
2630 }
2631
2632 // If the entered group has been emptied then leave it.
2633 PCB_GROUP* enteredGroup = m_selectionTool->GetEnteredGroup();
2634
2635 if( enteredGroup && enteredGroup->GetItems().empty() )
2637
2638 if( aIsCut )
2639 {
2640 commit.Push( _( "Cut" ) );
2641 }
2642 else if( itemsDeleted == 0 )
2643 {
2644 if( fieldsHidden == 1 )
2645 commit.Push( _( "Hide Field" ) );
2646 else if( fieldsHidden > 1 )
2647 commit.Push( _( "Hide Fields" ) );
2648 else if( fieldsAlreadyHidden > 0 )
2649 editFrame->ShowInfoBarError( _( "Use the Footprint Properties dialog to remove fields." ) );
2650 }
2651 else
2652 {
2653 commit.Push( _( "Delete" ) );
2654 }
2655}
2656
2657
2658int EDIT_TOOL::Remove( const TOOL_EVENT& aEvent )
2659{
2660 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2661
2662 editFrame->PushTool( aEvent );
2663
2664 std::vector<BOARD_ITEM*> lockedItems;
2665 Activate();
2666
2667 // get a copy instead of reference (as we're going to clear the selection before removing items)
2668 PCB_SELECTION selectionCopy;
2671
2672 // If we are in a "Cut" operation, then the copied selection exists already and we want to
2673 // delete exactly that; no more, no fewer. Any filtering for locked items must be done in
2674 // the copyToClipboard() routine.
2675 if( isCut )
2676 {
2677 selectionCopy = m_selectionTool->GetSelection();
2678 }
2679 else
2680 {
2681 // When not in free-pad mode we normally auto-promote selected pads to their parent
2682 // footprints. But this is probably a little too dangerous for a destructive operation,
2683 // so we just do the promotion but not the deletion (allowing for a second delete to do
2684 // it if that's what the user wanted).
2685 selectionCopy = m_selectionTool->RequestSelection(
2686 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2687 {
2688 } );
2689
2690 size_t beforeFPCount = selectionCopy.CountType( PCB_FOOTPRINT_T );
2691
2693 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2694 {
2695 sTool->FilterCollectorForFreePads( aCollector );
2696 } );
2697
2698 if( !selectionCopy.IsHover()
2699 && m_selectionTool->GetSelection().CountType( PCB_FOOTPRINT_T ) > beforeFPCount )
2700 {
2701 wxBell();
2702 canvas()->Refresh();
2703 editFrame->PopTool( aEvent );
2704 return 0;
2705 }
2706
2707 // In "alternative" mode, we expand selected track items to their full connection.
2708 if( isAlt && ( selectionCopy.HasType( PCB_TRACE_T ) || selectionCopy.HasType( PCB_VIA_T ) ) )
2709 {
2711 }
2712
2713 // Finally run RequestSelection() one more time to find out what user wants to do about
2714 // locked objects.
2715 selectionCopy = m_selectionTool->RequestSelection(
2716 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2717 {
2718 sTool->FilterCollectorForFreePads( aCollector );
2719 },
2720 true /* prompt user regarding locked items */ );
2721 }
2722
2723 DeleteItems( selectionCopy, isCut );
2724 canvas()->Refresh();
2725
2726 editFrame->PopTool( aEvent );
2727 return 0;
2728}
2729
2730
2732{
2733 if( isRouterActive() )
2734 {
2735 wxBell();
2736 return 0;
2737 }
2738
2740 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2741 {
2742 sTool->FilterCollectorForMarkers( aCollector );
2743 sTool->FilterCollectorForHierarchy( aCollector, true );
2744 sTool->FilterCollectorForTableCells( aCollector );
2745 },
2746 true /* prompt user regarding locked items */ );
2747
2748 if( selection.Empty() )
2749 return 0;
2750
2751 VECTOR2I translation;
2752 EDA_ANGLE rotation;
2753 ROTATION_ANCHOR rotationAnchor = selection.Size() > 1 ? ROTATE_AROUND_SEL_CENTER
2755
2756 // TODO: Implement a visible bounding border at the edge
2757 BOX2I sel_box = selection.GetBoundingBox();
2758
2759 DIALOG_MOVE_EXACT dialog( frame(), translation, rotation, rotationAnchor, sel_box );
2760 int ret = dialog.ShowModal();
2761
2762 if( ret == wxID_OK )
2763 {
2764 BOARD_COMMIT commit( this );
2765 EDA_ANGLE angle = rotation;
2766 VECTOR2I rp = selection.GetCenter();
2767 VECTOR2I selCenter( rp.x, rp.y );
2768
2769 // Make sure the rotation is from the right reference point
2770 selCenter += translation;
2771
2772 if( !frame()->GetPcbNewSettings()->m_Display.m_DisplayInvertYAxis )
2773 rotation = -rotation;
2774
2775 for( EDA_ITEM* item : selection )
2776 {
2777 if( !item->IsBOARD_ITEM() )
2778 continue;
2779
2780 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
2781
2782 if( !boardItem->IsNew() )
2783 commit.Modify( boardItem );
2784
2785 if( !boardItem->GetParent() || !boardItem->GetParent()->IsSelected() )
2786 boardItem->Move( translation );
2787
2788 switch( rotationAnchor )
2789 {
2791 boardItem->Rotate( boardItem->GetPosition(), angle );
2792 break;
2794 boardItem->Rotate( selCenter, angle );
2795 break;
2797 boardItem->Rotate( frame()->GetScreen()->m_LocalOrigin, angle );
2798 break;
2800 boardItem->Rotate( board()->GetDesignSettings().GetAuxOrigin(), angle );
2801 break;
2802 }
2803
2804 if( !m_dragging )
2805 getView()->Update( boardItem );
2806 }
2807
2808 commit.Push( _( "Move Exactly" ) );
2809
2810 if( selection.IsHover() )
2812
2814
2815 if( m_dragging )
2817 }
2818
2819 return 0;
2820}
2821
2822
2824{
2825 if( isRouterActive() )
2826 {
2827 wxBell();
2828 return 0;
2829 }
2830
2831 bool increment = aEvent.IsAction( &PCB_ACTIONS::duplicateIncrement );
2832
2833 // Be sure that there is at least one item that we can modify
2835 []( const VECTOR2I&, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2836 {
2837 sTool->FilterCollectorForFreePads( aCollector, true );
2838 sTool->FilterCollectorForMarkers( aCollector );
2839 sTool->FilterCollectorForHierarchy( aCollector, true );
2840 sTool->FilterCollectorForTableCells( aCollector );
2841 } );
2842
2843 if( selection.Empty() )
2844 return 0;
2845
2846 // Duplicating tuning patterns alone is not supported
2847 if( selection.Size() == 1 && selection.CountType( PCB_GENERATOR_T ) )
2848 return 0;
2849
2850 // we have a selection to work on now, so start the tool process
2851 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2852 BOARD_COMMIT commit( this );
2853 FOOTPRINT* parentFootprint = nullptr;
2854
2856 parentFootprint = editFrame->GetBoard()->GetFirstFootprint();
2857
2858 // If the selection was given a hover, we do not keep the selection after completion
2859 bool is_hover = selection.IsHover();
2860
2861 std::vector<BOARD_ITEM*> new_items;
2862 new_items.reserve( selection.Size() );
2863
2864 // Each selected item is duplicated and pushed to new_items list
2865 // Old selection is cleared, and new items are then selected.
2866 for( EDA_ITEM* item : selection )
2867 {
2868 if( !item->IsBOARD_ITEM() )
2869 continue;
2870
2871 BOARD_ITEM* dupe_item = nullptr;
2872 BOARD_ITEM* orig_item = static_cast<BOARD_ITEM*>( item );
2873
2875 && /*FOOTPRINT* parentFootprint =*/ orig_item->GetParentFootprint() )
2876 {
2877 // No sub-footprint modifications allowed outside of footprint editor
2878 // and parentFootprint is not (yet?) used
2879 }
2880 else
2881 {
2882 switch( orig_item->Type() )
2883 {
2884 case PCB_FOOTPRINT_T:
2885 case PCB_TEXT_T:
2886 case PCB_TEXTBOX_T:
2888 case PCB_SHAPE_T:
2889 case PCB_TRACE_T:
2890 case PCB_ARC_T:
2891 case PCB_VIA_T:
2892 case PCB_ZONE_T:
2893 case PCB_TARGET_T:
2894 case PCB_DIM_ALIGNED_T:
2895 case PCB_DIM_CENTER_T:
2896 case PCB_DIM_RADIAL_T:
2898 case PCB_DIM_LEADER_T:
2900 dupe_item = parentFootprint->DuplicateItem( orig_item );
2901 else
2902 dupe_item = orig_item->Duplicate();
2903
2904 // Clear the selection flag here, otherwise the PCB_SELECTION_TOOL
2905 // will not properly select it later on
2906 dupe_item->ClearSelected();
2907
2908 new_items.push_back( dupe_item );
2909 commit.Add( dupe_item );
2910 break;
2911
2912 case PCB_FIELD_T:
2913 // PCB_FIELD items are specific items (not only graphic, but are properies)
2914 // and cannot be duplicated like other footprint items. So skip it:
2915 orig_item->ClearSelected();
2916 break;
2917
2918 case PCB_PAD_T:
2919 dupe_item = parentFootprint->DuplicateItem( orig_item );
2920
2921 if( increment && static_cast<PAD*>( dupe_item )->CanHaveNumber() )
2922 {
2923 PAD_TOOL* padTool = m_toolMgr->GetTool<PAD_TOOL>();
2924 wxString padNumber = padTool->GetLastPadNumber();
2925 padNumber = parentFootprint->GetNextPadNumber( padNumber );
2926 padTool->SetLastPadNumber( padNumber );
2927 static_cast<PAD*>( dupe_item )->SetNumber( padNumber );
2928 }
2929
2930 // Clear the selection flag here, otherwise the PCB_SELECTION_TOOL
2931 // will not properly select it later on
2932 dupe_item->ClearSelected();
2933
2934 new_items.push_back( dupe_item );
2935 commit.Add( dupe_item );
2936 break;
2937
2938 case PCB_TABLE_T:
2939 // JEY TODO: tables
2940 break;
2941
2942 case PCB_GENERATOR_T:
2943 case PCB_GROUP_T:
2944 dupe_item = static_cast<PCB_GROUP*>( orig_item )->DeepDuplicate();
2945
2946 dupe_item->RunOnDescendants(
2947 [&]( BOARD_ITEM* aItem )
2948 {
2949 aItem->ClearSelected();
2950 new_items.push_back( aItem );
2951 commit.Add( aItem );
2952 } );
2953
2954 dupe_item->ClearSelected();
2955 new_items.push_back( dupe_item );
2956 commit.Add( dupe_item );
2957 break;
2958
2959 default:
2960 wxASSERT_MSG( false, wxString::Format( wxT( "Unhandled item type %d" ),
2961 orig_item->Type() ) );
2962 break;
2963 }
2964 }
2965 }
2966
2967 // Clear the old selection first
2969
2970 // Select the new items
2971 EDA_ITEMS nItems( new_items.begin(), new_items.end() );
2973
2974 // record the new items as added
2975 if( !selection.Empty() )
2976 {
2977 editFrame->DisplayToolMsg( wxString::Format( _( "Duplicated %d item(s)" ),
2978 (int) new_items.size() ) );
2979
2980 // If items were duplicated, pick them up
2981 if( doMoveSelection( aEvent, &commit, true ) )
2982 commit.Push( _( "Duplicate" ) );
2983 else
2984 commit.Revert();
2985
2986 // Deselect the duplicated item if we originally started as a hover selection
2987 if( is_hover )
2989 }
2990
2991 return 0;
2992}
2993
2994
2996{
2997 const auto incrementableFilter =
2998 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2999 {
3000 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
3001 {
3002 switch( aCollector[i]->Type() )
3003 {
3004 case PCB_PAD_T:
3005 case PCB_TEXT_T:
3006 break;
3007 default:
3008 aCollector.Remove( i );
3009 break;
3010 }
3011 }
3012 };
3013
3015 incrementableFilter, true /* prompt user regarding locked items */ );
3016
3017 if( selection.Empty() )
3018 return 0;
3019
3020 const ACTIONS::INCREMENT incParam = aEvent.Parameter<ACTIONS::INCREMENT>();
3021
3022 STRING_INCREMENTER incrementer;
3023 incrementer.SetSkipIOSQXZ( true );
3024
3025 // If we're coming via another action like 'Move', use that commit
3026 BOARD_COMMIT localCommit( m_toolMgr );
3027 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
3028
3029 if( !commit )
3030 commit = &localCommit;
3031
3032 const auto modifyItem = [&]( EDA_ITEM& aItem )
3033 {
3034 if( aItem.IsNew() )
3036 else
3037 commit->Modify( &aItem );
3038 };
3039
3040 for( EDA_ITEM* item : selection )
3041 {
3042 switch( item->Type() )
3043 {
3044 case PCB_PAD_T:
3045 {
3046 // Only increment pad numbers in the footprint editor
3047 if( !m_isFootprintEditor )
3048 break;
3049
3050 PAD& pad = static_cast<PAD&>( *item );
3051
3052 if( !pad.CanHaveNumber() )
3053 continue;
3054
3055 // Increment on the pad numbers
3056 std::optional<wxString> newNumber =
3057 incrementer.Increment( pad.GetNumber(), incParam.Delta, incParam.Index );
3058
3059 if( newNumber )
3060 {
3061 modifyItem( pad );
3062 pad.SetNumber( *newNumber );
3063 }
3064
3065 break;
3066 }
3067 case PCB_TEXT_T:
3068 {
3069 PCB_TEXT& text = static_cast<PCB_TEXT&>( *item );
3070
3071 std::optional<wxString> newText =
3072 incrementer.Increment( text.GetText(), incParam.Delta, incParam.Index );
3073
3074 if( newText )
3075 {
3076 modifyItem( text );
3077 text.SetText( *newText );
3078 }
3079
3080 break;
3081 }
3082 default:
3083 break;
3084 }
3085 }
3086
3087 commit->Push( _( "Increment" ) );
3088
3089 return 0;
3090}
3091
3092
3094 PCB_SELECTION_TOOL* sTool )
3095{
3096 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
3097 {
3098 if( aCollector[i]->Type() != PCB_PAD_T )
3099 aCollector.Remove( i );
3100 }
3101}
3102
3103
3105 PCB_SELECTION_TOOL* sTool )
3106{
3107 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
3108 {
3109 if( aCollector[i]->Type() != PCB_FOOTPRINT_T )
3110 aCollector.Remove( i );
3111 }
3112}
3113
3114
3116{
3117 // Can't modify an empty group
3118 if( aSelection.Empty() )
3119 return false;
3120
3121 if( ( m_dragging || aSelection[0]->IsMoving() ) && aSelection.HasReferencePoint() )
3122 return false;
3123
3124 // When there is only one item selected, the reference point is its position...
3125 if( aSelection.Size() == 1 && aSelection.Front()->Type() != PCB_TABLE_T )
3126 {
3127 if( aSelection.Front()->IsBOARD_ITEM() )
3128 {
3129 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( aSelection.Front() );
3130 aSelection.SetReferencePoint( item->GetPosition() );
3131 }
3132 }
3133 // ...otherwise modify items with regard to the grid-snapped center position
3134 else
3135 {
3136 PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
3137 VECTOR2I refPt = aSelection.GetCenter();
3138
3139 // Exclude text in the footprint editor if there's anything else selected
3141 {
3142 BOX2I nonFieldsBBox;
3143
3144 for( EDA_ITEM* item : aSelection.Items() )
3145 {
3146 if( !item->IsType( { PCB_TEXT_T, PCB_FIELD_T } ) )
3147 nonFieldsBBox.Merge( item->GetBoundingBox() );
3148 }
3149
3150 if( nonFieldsBBox.IsValid() )
3151 refPt = nonFieldsBBox.GetCenter();
3152 }
3153
3154 aSelection.SetReferencePoint( grid.BestSnapAnchor( refPt, nullptr ) );
3155 }
3156
3157 return true;
3158}
3159
3160
3161bool EDIT_TOOL::pickReferencePoint( const wxString& aTooltip, const wxString& aSuccessMessage,
3162 const wxString& aCanceledMessage, VECTOR2I& aReferencePoint )
3163{
3165 PCB_EDIT_FRAME& editFrame = *getEditFrame<PCB_EDIT_FRAME>();
3166 std::optional<VECTOR2I> pickedPoint;
3167 bool done = false;
3168
3169 m_statusPopup->SetText( aTooltip );
3170
3172 picker->SetSnapping( true );
3173
3174 const auto setPickerLayerSet = [&]()
3175 {
3176 MAGNETIC_SETTINGS& magSettings = *editFrame.GetMagneticItemsSettings();
3177 LSET layerFilter;
3178 if( !magSettings.allLayers )
3179 layerFilter = LSET( { editFrame.GetActiveLayer() } );
3180 else
3181 layerFilter = LSET::AllLayersMask();
3182
3183 picker->SetLayerSet( layerFilter );
3184 };
3185
3186 // Initial set
3187 setPickerLayerSet();
3188
3189 picker->SetClickHandler(
3190 [&]( const VECTOR2D& aPoint ) -> bool
3191 {
3192 pickedPoint = aPoint;
3193
3194 if( !aSuccessMessage.empty() )
3195 {
3196 m_statusPopup->SetText( aSuccessMessage );
3197 m_statusPopup->Expire( 800 );
3198 }
3199 else
3200 {
3201 m_statusPopup->Hide();
3202 }
3203
3204 return false; // we don't need any more points
3205 } );
3206
3207 picker->SetMotionHandler(
3208 [&]( const VECTOR2D& aPos )
3209 {
3210 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
3211 } );
3212
3213 picker->SetCancelHandler(
3214 [&]()
3215 {
3216 if( !aCanceledMessage.empty() )
3217 {
3218 m_statusPopup->SetText( aCanceledMessage );
3219 m_statusPopup->Expire( 800 );
3220 }
3221 else
3222 {
3223 m_statusPopup->Hide();
3224 }
3225 } );
3226
3227 picker->SetFinalizeHandler(
3228 [&]( const int& aFinalState )
3229 {
3230 done = true;
3231 } );
3232
3233 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
3234 m_statusPopup->Popup();
3235 canvas()->SetStatusPopup( m_statusPopup->GetPanel() );
3236
3238
3239 while( !done )
3240 {
3241 // Pass events unless we receive a null event, then we must shut down
3242 if( TOOL_EVENT* evt = Wait() )
3243 {
3244 if( evt->Matches( PCB_EVENTS::SnappingModeChangedByKeyEvent() ) )
3245 {
3246 // Update the layer set when the snapping mode changes
3247 setPickerLayerSet();
3248 }
3249
3250 evt->SetPassEvent();
3251 }
3252 else
3253 break;
3254 }
3255
3256 // Ensure statusPopup is hidden after use and before deleting it:
3257 canvas()->SetStatusPopup( nullptr );
3258 m_statusPopup->Hide();
3259
3260 if( pickedPoint )
3261 aReferencePoint = *pickedPoint;
3262
3263 return pickedPoint.has_value();
3264}
3265
3266
3268{
3269 CLIPBOARD_IO io;
3271 getEditFrame<PCB_BASE_EDIT_FRAME>()->GetMagneticItemsSettings() );
3272 TOOL_EVENT selectReferencePoint( aEvent.Category(), aEvent.Action(),
3273 "pcbnew.InteractiveEdit.selectReferencePoint",
3274 TOOL_ACTION_SCOPE::AS_GLOBAL );
3275
3276 frame()->PushTool( selectReferencePoint );
3277 Activate();
3278
3280 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
3281 {
3282 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
3283 {
3284 BOARD_ITEM* item = aCollector[i];
3285
3286 // We can't copy both a footprint and its text in the same operation, so if
3287 // both are selected, remove the text
3288 if( ( item->Type() == PCB_FIELD_T || item->Type() == PCB_TEXT_T )
3289 && aCollector.HasItem( item->GetParentFootprint() ) )
3290 {
3291 aCollector.Remove( item );
3292 }
3293 else if( item->Type() == PCB_MARKER_T )
3294 {
3295 // Don't allow copying marker objects
3296 aCollector.Remove( item );
3297 }
3298 }
3299 },
3300
3301 // Prompt user regarding locked items.
3302 aEvent.IsAction( &ACTIONS::cut ) && !m_isFootprintEditor );
3303
3304 if( !selection.Empty() )
3305 {
3306 std::vector<BOARD_ITEM*> items;
3307
3308 for( EDA_ITEM* item : selection )
3309 {
3310 if( item->IsBOARD_ITEM() )
3311 items.push_back( static_cast<BOARD_ITEM*>( item ) );
3312 }
3313
3314 VECTOR2I refPoint;
3315
3316 if( aEvent.IsAction( &PCB_ACTIONS::copyWithReference ) )
3317 {
3318 if( !pickReferencePoint( _( "Select reference point for the copy..." ),
3319 _( "Selection copied" ),
3320 _( "Copy canceled" ),
3321 refPoint ) )
3322 {
3323 frame()->PopTool( selectReferencePoint );
3324 return 0;
3325 }
3326 }
3327 else
3328 {
3329 refPoint = grid.BestDragOrigin( getViewControls()->GetCursorPosition(), items );
3330 }
3331
3332 selection.SetReferencePoint( refPoint );
3333
3334 io.SetBoard( board() );
3335 io.SaveSelection( selection, m_isFootprintEditor );
3336 frame()->SetStatusText( _( "Selection copied" ) );
3337 }
3338
3339 frame()->PopTool( selectReferencePoint );
3340
3341 if( selection.IsHover() )
3342 m_selectionTool->ClearSelection();
3343
3344 return 0;
3345}
3346
3347
3349{
3351 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
3352 {
3353 // Anything unsupported will just be ignored
3354 },
3355 // No prompt for locked items
3356 false );
3357
3358 if( selection.IsHover() )
3360
3361 const auto getItemText = [&]( const BOARD_ITEM& aItem ) -> wxString
3362 {
3363 switch( aItem.Type() )
3364 {
3365 case PCB_TEXT_T:
3366 case PCB_FIELD_T:
3367 case PCB_DIM_ALIGNED_T:
3368 case PCB_DIM_LEADER_T:
3369 case PCB_DIM_CENTER_T:
3370 case PCB_DIM_RADIAL_T:
3372 {
3373 // These can all go via the PCB_TEXT class
3374 const PCB_TEXT& text = static_cast<const PCB_TEXT&>( aItem );
3375 return text.GetShownText( true );
3376 }
3377 case PCB_TEXTBOX_T:
3378 case PCB_TABLECELL_T:
3379 {
3380 // This one goes via EDA_TEXT
3381 const PCB_TEXTBOX& textBox = static_cast<const PCB_TEXTBOX&>( aItem );
3382 return textBox.GetShownText( true );
3383 }
3384 case PCB_TABLE_T:
3385 {
3386 const PCB_TABLE& table = static_cast<const PCB_TABLE&>( aItem );
3387 wxString s;
3388
3389 for( int row = 0; row < table.GetRowCount(); ++row )
3390 {
3391 for( int col = 0; col < table.GetColCount(); ++col )
3392 {
3393 const PCB_TABLECELL* cell = table.GetCell( row, col );
3394 s << cell->GetShownText( true );
3395
3396 if( col < table.GetColCount() - 1 )
3397 {
3398 s << '\t';
3399 }
3400 }
3401
3402 if( row < table.GetRowCount() - 1 )
3403 {
3404 s << '\n';
3405 }
3406 }
3407 return s;
3408 }
3409 default:
3410 // No string representation for this item type
3411 break;
3412 }
3413 return wxEmptyString;
3414 };
3415
3416 wxArrayString itemTexts;
3417
3418 for( EDA_ITEM* item : selection )
3419 {
3420 if( item->IsBOARD_ITEM() )
3421 {
3422 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
3423 wxString itemText = getItemText( *boardItem );
3424
3425 itemText.Trim( false ).Trim( true );
3426
3427 if( !itemText.IsEmpty() )
3428 {
3429 itemTexts.Add( std::move( itemText ) );
3430 }
3431 }
3432 }
3433
3434 // Send the text to the clipboard
3435 if( !itemTexts.empty() )
3436 {
3437 SaveClipboard( wxJoin( itemTexts, '\n', '\0' ).ToStdString() );
3438 }
3439
3440 return 0;
3441}
3442
3443
3445{
3446 if( !copyToClipboard( aEvent ) )
3447 {
3448 // N.B. Setting the CUT flag prevents lock filtering as we only want to delete the items
3449 // that were copied to the clipboard, no more, no fewer. Filtering for locked item, if
3450 // any will be done in the copyToClipboard() routine
3451 TOOL_EVENT evt = aEvent;
3453 Remove( evt );
3454 }
3455
3456 return 0;
3457}
3458
3459
3461{
3465}
3466
3467
3468// clang-format off
3470{
3472 Go( &EDIT_TOOL::Move, PCB_ACTIONS::move.MakeEvent() );
3478 Go( &EDIT_TOOL::Flip, PCB_ACTIONS::flip.MakeEvent() );
3479 Go( &EDIT_TOOL::Remove, ACTIONS::doDelete.MakeEvent() );
3486 Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorH.MakeEvent() );
3487 Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorV.MakeEvent() );
3488 Go( &EDIT_TOOL::Swap, PCB_ACTIONS::swap.MakeEvent() );
3498
3504
3508
3512
3516 Go( &EDIT_TOOL::cutToClipboard, ACTIONS::cut.MakeEvent() );
3517}
3518// clang-format on
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
@ special_tools
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
static TOOL_ACTION decrementPrimary
Definition: actions.h:89
static TOOL_ACTION paste
Definition: actions.h:73
static TOOL_ACTION pickerSubTool
Definition: actions.h:205
static TOOL_ACTION unselectAll
Definition: actions.h:76
static TOOL_ACTION decrementSecondary
Definition: actions.h:91
static TOOL_ACTION copy
Definition: actions.h:71
static TOOL_ACTION pasteSpecial
Definition: actions.h:74
static TOOL_ACTION rightJustify
Definition: actions.h:82
static TOOL_ACTION pageSettings
Definition: actions.h:56
static TOOL_ACTION undo
Definition: actions.h:68
static TOOL_ACTION incrementSecondary
Definition: actions.h:90
static TOOL_ACTION duplicate
Definition: actions.h:77
static TOOL_ACTION incrementPrimary
Definition: actions.h:88
static TOOL_ACTION doDelete
Definition: actions.h:78
static TOOL_ACTION cursorClick
Definition: actions.h:169
REMOVE_FLAGS
Definition: actions.h:267
static TOOL_ACTION increment
Definition: actions.h:87
static TOOL_ACTION leftJustify
Definition: actions.h:80
static TOOL_ACTION cut
Definition: actions.h:70
static TOOL_ACTION copyAsText
Definition: actions.h:72
static TOOL_ACTION refreshPreview
Definition: actions.h:149
static TOOL_ACTION selectAll
Definition: actions.h:75
static TOOL_ACTION centerJustify
Definition: actions.h:81
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
COMMIT & Stage(EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen=nullptr) override
Add a change of the item aItem of type aChangeType to the change list.
virtual void Revert() override
Revert the commit by restoring the modified items state.
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
const VECTOR2I & GetAuxOrigin()
virtual void Delete(BOARD_ITEM *aItem)
Removes an item from the container and deletes it.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:239
virtual void SetLocked(bool aLocked)
Definition: board_item.h:330
PCB_GROUP * GetParentGroup() const
Definition: board_item.h:90
virtual void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle)
Rotate this object.
Definition: board_item.cpp:355
virtual BOARD_ITEM * Duplicate() const
Create a copy of this BOARD_ITEM.
Definition: board_item.cpp:243
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition: board_item.h:344
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:290
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:298
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:259
virtual bool IsLocked() const
Definition: board_item.cpp:75
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:217
virtual void RunOnDescendants(const std::function< void(BOARD_ITEM *)> &aFunction, int aDepth=0) const
Invoke a function on all descendants.
Definition: board_item.h:214
virtual void Normalize()
Perform any normalization required after a user rotate and/or flip.
Definition: board_item.h:375
virtual void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection)
Flip this object, i.e.
Definition: board_item.cpp:361
LSET GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:845
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:190
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:456
const FOOTPRINTS & Footprints() const
Definition: board.h:336
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:948
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:483
constexpr const Vec & GetPosition() const
Definition: box2.h:211
constexpr const Vec GetEnd() const
Definition: box2.h:212
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:658
constexpr const Vec GetCenter() const
Definition: box2.h:230
constexpr bool IsValid() const
Definition: box2.h:909
Represent basic circle geometry with utility geometry functions.
Definition: circle.h:33
VECTOR2I Center
Public to make access simpler.
Definition: circle.h:130
int Radius
Public to make access simpler.
Definition: circle.h:129
CIRCLE & ConstructFromTanTanPt(const SEG &aLineA, const SEG &aLineB, const VECTOR2I &aP)
Construct this circle such that it is tangent to the given segments and passes through the given poin...
Definition: circle.cpp:51
VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute the point on the circumference of the circle that is the closest to aP.
Definition: circle.cpp:197
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:81
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition: collector.h:109
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition: commit.h:92
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Modify a given item in the model.
Definition: commit.h:108
bool Empty() const
Definition: commit.h:150
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition: commit.h:80
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.
DIALOG_GET_FOOTPRINT_BY_NAME is a helper dialog to select a footprint by its reference One can enter ...
int ShowQuasiModal()
int ShowModal() override
bool HitTestDrawingSheetItems(KIGFX::VIEW *aView, const VECTOR2I &aPosition)
void ShowInfoBarError(const wxString &aErrorMsg, bool aShowCloseButton=false, WX_INFOBAR::MESSAGE_TYPE aType=WX_INFOBAR::MESSAGE_TYPE::GENERIC)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an error icon on the left o...
void DisplayToolMsg(const wxString &msg) override
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
void SetStatusPopup(wxWindow *aPopup)
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:244
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:127
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
void ClearSelected()
Definition: eda_item.h:122
bool IsSelected() const
Definition: eda_item.h:110
virtual const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
Definition: eda_item.cpp:276
bool IsMoving() const
Definition: eda_item.h:108
bool IsNew() const
Definition: eda_item.h:107
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:291
SHAPE_T GetShape() const
Definition: eda_shape.h:132
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:174
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:141
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:137
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:178
void SetWidth(int aWidth)
Definition: eda_shape.h:121
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:80
virtual bool IsVisible() const
Definition: eda_text.h:174
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:377
bool isRouterActive() const
Definition: edit_tool.cpp:514
int Duplicate(const TOOL_EVENT &aItem)
Duplicate the current selection and starts a move action.
Definition: edit_tool.cpp:2823
int Drag(const TOOL_EVENT &aEvent)
Invoke the PNS router to drag tracks or do an offline resizing of an arc track if a single arc track ...
Definition: edit_tool.cpp:522
int Flip(const TOOL_EVENT &aEvent)
Rotate currently selected items.
Definition: edit_tool.cpp:2314
bool doMoveSelection(const TOOL_EVENT &aEvent, BOARD_COMMIT *aCommit, bool aAutoStart)
Rebuilds the ratsnest for operations that require it outside the commit rebuild.
int Swap(const TOOL_EVENT &aEvent)
Swap currently selected items' positions.
int PackAndMoveFootprints(const TOOL_EVENT &aEvent)
Try to fit selected footprints inside a minimal area and start movement.
int Mirror(const TOOL_EVENT &aEvent)
Mirror the current selection.
Definition: edit_tool.cpp:2121
int Increment(const TOOL_EVENT &aEvent)
Increment some aspect of the selected items.q.
Definition: edit_tool.cpp:2995
bool pickReferencePoint(const wxString &aTooltip, const wxString &aSuccessMessage, const wxString &aCanceledMessage, VECTOR2I &aReferencePoint)
Definition: edit_tool.cpp:3161
bool Init() override
Init() is called once upon a registration of the tool.
Definition: edit_tool.cpp:209
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
Definition: edit_tool.cpp:115
int ModifyLines(const TOOL_EVENT &aEvent)
"Modify" graphical lines.
Definition: edit_tool.cpp:1358
bool m_dragging
Definition: edit_tool.h:240
int MoveExact(const TOOL_EVENT &aEvent)
Invoke a dialog box to allow moving of the item by an exact amount.
Definition: edit_tool.cpp:2731
int Move(const TOOL_EVENT &aEvent)
Main loop in which events are handled.
static const unsigned int COORDS_PADDING
Definition: edit_tool.h:245
int JustifyText(const TOOL_EVENT &aEvent)
Set the justification on any text items (or fields) in the current selection.
Definition: edit_tool.cpp:2239
void getChildItemsOfGroupsAndGenerators(EDA_ITEM *item, std::unordered_set< BOARD_ITEM * > &children)
Definition: edit_tool.cpp:2402
bool updateModificationPoint(PCB_SELECTION &aSelection)
Definition: edit_tool.cpp:3115
std::unique_ptr< STATUS_TEXT_POPUP > m_statusPopup
Definition: edit_tool.h:243
int DragArcTrack(const TOOL_EVENT &aTrack)
Drag-resize an arc (and change end points of connected straight segments).
Definition: edit_tool.cpp:638
int copyToClipboard(const TOOL_EVENT &aEvent)
Send the current selection to the clipboard by formatting it as a fake pcb see #AppendBoardFromClipbo...
Definition: edit_tool.cpp:3267
int Remove(const TOOL_EVENT &aEvent)
Delete currently selected items.
Definition: edit_tool.cpp:2658
int cutToClipboard(const TOOL_EVENT &aEvent)
Cut the current selection to the clipboard by formatting it as a fake pcb see #AppendBoardFromClipboa...
Definition: edit_tool.cpp:3444
static const std::vector< KICAD_T > MirrorableItems
Definition: edit_tool.h:105
void DeleteItems(const PCB_SELECTION &aItem, bool aIsCut)
Definition: edit_tool.cpp:2449
static void PadFilter(const VECTOR2I &, GENERAL_COLLECTOR &aCollector, PCB_SELECTION_TOOL *sTool)
A selection filter which prunes the selection to contain only items of type PCB_PAD_T.
Definition: edit_tool.cpp:3093
VECTOR2I m_cursor
Definition: edit_tool.h:241
bool invokeInlineRouter(int aDragMode)
Definition: edit_tool.cpp:485
void removeNonRootItems(std::unordered_set< EDA_ITEM * > &items)
Recursively adds any child items of the given item to the set.
Definition: edit_tool.cpp:2419
void rebuildConnectivity()
Removes all items from the set which are children of other PCB_GROUP or PCB_GENERATOR items in the se...
Definition: edit_tool.cpp:3460
void setTransitions() override
< Set up handlers for various events.
Definition: edit_tool.cpp:3469
int HealShapes(const TOOL_EVENT &aEvent)
Make ends of selected shapes meet by extending or cutting them, or adding extra geometry.
Definition: edit_tool.cpp:1678
int ChangeTrackWidth(const TOOL_EVENT &aEvent)
Definition: edit_tool.cpp:991
int BooleanPolygons(const TOOL_EVENT &aEvent)
Modify selected polygons into a single polygon using boolean operations such as merge (union) or subt...
Definition: edit_tool.cpp:1757
int copyToClipboardAsText(const TOOL_EVENT &aEvent)
Send the current selection to the clipboard as text.
Definition: edit_tool.cpp:3348
int GetAndPlace(const TOOL_EVENT &aEvent)
Definition: edit_tool.cpp:462
int FilletTracks(const TOOL_EVENT &aEvent)
Fillet (i.e.
Definition: edit_tool.cpp:1064
PCB_SELECTION_TOOL * m_selectionTool
Definition: edit_tool.h:239
int SimplifyPolygons(const TOOL_EVENT &aEvent)
Simplify the outlines of selected polygon objects.
Definition: edit_tool.cpp:1608
int Properties(const TOOL_EVENT &aEvent)
Display properties window for the selected object.
Definition: edit_tool.cpp:1867
static void FootprintFilter(const VECTOR2I &, GENERAL_COLLECTOR &aCollector, PCB_SELECTION_TOOL *sTool)
A selection filter which prunes the selection to contain only items of type #PCB_MODULE_T.
Definition: edit_tool.cpp:3104
int Rotate(const TOOL_EVENT &aEvent)
Rotate currently selected items.
Definition: edit_tool.cpp:1949
static const TOOL_EVENT SelectedEvent
Definition: actions.h:292
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:299
static const TOOL_EVENT ConnectivityChangedEvent
Selected item had a property changed (except movement)
Definition: actions.h:296
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:293
void Remove(BOARD_ITEM *aItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: footprint.cpp:1156
BOARD_ITEM * DuplicateItem(const BOARD_ITEM *aItem, bool aAddToFootprint=false)
Duplicate a given item within the footprint, optionally adding it to the board.
Definition: footprint.cpp:2607
wxString GetNextPadNumber(const wxString &aLastPadName) const
Return the next available pad number in the footprint.
Definition: footprint.cpp:2734
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:202
static const std::vector< KICAD_T > DraggableItems
A scan list for items that can be dragged.
Definition: collectors.h:263
A handler that is based on a set of callbacks provided by the user of the ITEM_MODIFICATION_ROUTINE.
An interface for classes handling user events controlling the view behavior such as zooming,...
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 VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const =0
Return the current mouse pointer position.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
bool IsBOARD_ITEM() const
Definition: view_item.h:102
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:297
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:332
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:1673
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition: view.h:418
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:37
static LSET AllLayersMask()
Definition: lset.cpp:593
A collection of nets and the parameters used to route or test these nets.
Definition: netclass.h:45
int GetuViaDrill() const
Definition: netclass.h:154
int GetuViaDiameter() const
Definition: netclass.h:146
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition: padstack.h:144
Tool relating to pads and pad settings.
Definition: pad_tool.h:37
void SetLastPadNumber(const wxString &aPadNumber)
Definition: pad_tool.h:69
wxString GetLastPadNumber() const
Definition: pad_tool.h:68
Definition: pad.h:54
bool CanHaveNumber() const
Indicates whether or not the pad can have a number.
Definition: pad.cpp:217
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition: pad.h:297
VECTOR2I GetPosition() const override
Definition: pad.h:206
void SetDelta(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
Definition: pad.h:291
void FlipPrimitives(FLIP_DIRECTION aFlipDirection)
Flip (mirror) the primitives left to right or top to bottom, around the anchor position in custom pad...
Definition: pad.cpp:998
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition: pad.h:193
void SetOffset(PCB_LAYER_ID aLayer, const VECTOR2I &aOffset)
Definition: pad.h:309
void SetPosition(const VECTOR2I &aPos) override
Definition: pad.h:200
const VECTOR2I & GetOffset(PCB_LAYER_ID aLayer) const
Definition: pad.h:315
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition: pad.h:406
void SetOrientation(const EDA_ANGLE &aAngle)
Set the rotation angle of the pad.
Definition: pad.cpp:910
static TOOL_ACTION drag45Degree
Definition: pcb_actions.h:197
static TOOL_ACTION duplicateIncrement
Activation of the duplication tool with incrementing (e.g. pad number)
Definition: pcb_actions.h:186
static TOOL_ACTION changeTrackWidth
Update selected tracks & vias to the current track & via dimensions.
Definition: pcb_actions.h:152
static TOOL_ACTION unrouteSelected
Removes all tracks from the selected items to the first pad.
Definition: pcb_actions.h:93
static TOOL_ACTION mirrorH
Mirroring of selected items.
Definition: pcb_actions.h:139
static TOOL_ACTION updateFootprint
Definition: pcb_actions.h:428
static TOOL_ACTION breakTrack
Break a single track into two segments at the cursor.
Definition: pcb_actions.h:195
static TOOL_ACTION pointEditorMoveMidpoint
Definition: pcb_actions.h:306
static TOOL_ACTION getAndPlace
Find an item and start moving.
Definition: pcb_actions.h:575
static TOOL_ACTION properties
Activation of the edit tool.
Definition: pcb_actions.h:180
static TOOL_ACTION editFpInFpEditor
Definition: pcb_actions.h:451
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:68
static TOOL_ACTION moveWithReference
move with a reference point
Definition: pcb_actions.h:126
static TOOL_ACTION swap
Swapping of selected items.
Definition: pcb_actions.h:143
static TOOL_ACTION routerAutorouteSelected
Definition: pcb_actions.h:269
static TOOL_ACTION moveExact
Activation of the exact move tool.
Definition: pcb_actions.h:183
static TOOL_ACTION intersectPolygons
Intersection of multiple polygons.
Definition: pcb_actions.h:177
static TOOL_ACTION pointEditorMoveCorner
Definition: pcb_actions.h:305
static TOOL_ACTION genRemove
Definition: pcb_actions.h:293
static TOOL_ACTION selectConnection
Select tracks between junctions or expands an existing selection to pads or the entire connection.
Definition: pcb_actions.h:90
static TOOL_ACTION assignNetClass
Definition: pcb_actions.h:404
static TOOL_ACTION packAndMoveFootprints
Pack and start moving selected footprints.
Definition: pcb_actions.h:146
static TOOL_ACTION copyWithReference
copy command with manual reference point selection
Definition: pcb_actions.h:129
static TOOL_ACTION healShapes
Connect selected shapes, possibly extending or cutting them, or adding extra geometry.
Definition: pcb_actions.h:164
static TOOL_ACTION dragFreeAngle
Definition: pcb_actions.h:198
static TOOL_ACTION positionRelativeInteractively
Definition: pcb_actions.h:328
static TOOL_ACTION inspectClearance
Definition: pcb_actions.h:551
static TOOL_ACTION updateLocalRatsnest
Definition: pcb_actions.h:569
static TOOL_ACTION updateFootprints
Definition: pcb_actions.h:429
static TOOL_ACTION deleteFull
Definition: pcb_actions.h:190
static TOOL_ACTION moveIndividually
move items one-by-one
Definition: pcb_actions.h:123
static TOOL_ACTION changeFootprints
Definition: pcb_actions.h:431
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition: pcb_actions.h:71
static TOOL_ACTION chamferLines
Chamfer (i.e. adds a straight line) all selected straight lines by a user defined setback.
Definition: pcb_actions.h:160
static TOOL_ACTION dogboneCorners
Add "dogbone" corners to selected lines to allow routing with a cutter radius.
Definition: pcb_actions.h:162
static TOOL_ACTION filletTracks
Fillet (i.e. adds an arc tangent to) all selected straight tracks by a user defined radius.
Definition: pcb_actions.h:155
static TOOL_ACTION simplifyPolygons
Simplify polygon outlines.
Definition: pcb_actions.h:168
static TOOL_ACTION footprintProperties
Definition: pcb_actions.h:478
static TOOL_ACTION filletLines
Fillet (i.e. adds an arc tangent to) all selected straight lines by a user defined radius.
Definition: pcb_actions.h:158
static TOOL_ACTION changeFootprint
Definition: pcb_actions.h:430
static TOOL_ACTION routerInlineDrag
Activation of the Push and Shove router (inline dragging mode)
Definition: pcb_actions.h:282
static TOOL_ACTION positionRelative
Definition: pcb_actions.h:327
static TOOL_ACTION skip
Definition: pcb_actions.h:149
static TOOL_ACTION move
move or drag an item
Definition: pcb_actions.h:120
static TOOL_ACTION mirrorV
Definition: pcb_actions.h:140
static TOOL_ACTION mergePolygons
Merge multiple polygons into a single polygon.
Definition: pcb_actions.h:173
static TOOL_ACTION subtractPolygons
Subtract polygons from other polygons.
Definition: pcb_actions.h:175
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition: pcb_actions.h:76
static TOOL_ACTION flip
Flipping of selected objects.
Definition: pcb_actions.h:136
static TOOL_ACTION extendLines
Extend selected lines to meet at a point.
Definition: pcb_actions.h:166
static TOOL_ACTION routerRouteSelected
Definition: pcb_actions.h:267
static TOOL_ACTION rotateCw
Rotation of selected objects.
Definition: pcb_actions.h:132
static TOOL_ACTION rotateCcw
Definition: pcb_actions.h:133
virtual double GetLength() const override
Return the length of the arc track.
Definition: pcb_track.h:339
void SetMid(const VECTOR2I &aMid)
Definition: pcb_track.h:304
EDA_ANGLE GetAngle() const
Definition: pcb_track.cpp:1873
const VECTOR2I & GetMid() const
Definition: pcb_track.h:305
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_track.h:312
Common, abstract interface for edit frames.
virtual void OnEditItemRequest(BOARD_ITEM *aItem)
Install the corresponding dialog editor for the given item.
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
BOARD * GetBoard() const
DS_PROXY_VIEW_ITEM * GetDrawingSheet() const
void RedrawRatsnest()
Return the bounding box of the view that should be used if model is not valid.
The main frame for Pcbnew.
static const TOOL_EVENT & SnappingModeChangedByKeyEvent()
Hotkey feedback.
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:52
std::unordered_set< BOARD_ITEM * > & GetItems()
Definition: pcb_group.h:69
Generic tool for picking an item.
void SetLayerSet(LSET aLayerSet)
Set the tool's snap layer set.
Tool that displays edit points allowing to modify items by dragging the points.
The selection tool: currently supports:
void FilterCollectorForMarkers(GENERAL_COLLECTOR &aCollector) const
Drop any PCB_MARKERs from the collector.
PCB_SELECTION & RequestSelection(CLIENT_SELECTION_FILTER aClientFilter, bool aConfirmLockedItems=false)
Return the current selection, filtered according to aClientFilter.
void FilterCollectorForFreePads(GENERAL_COLLECTOR &aCollector, bool aForcePromotion=false) const
Check the "allow free pads" setting and if disabled, replace any pads in the collector with their par...
PCB_GROUP * GetEnteredGroup()
void FilterCollectorForHierarchy(GENERAL_COLLECTOR &aCollector, bool aMultiselect) const
In general we don't want to select both a parent and any of it's children.
int ClearSelection(const TOOL_EVENT &aEvent)
PCB_SELECTION & GetSelection()
void FilterCollectorForTableCells(GENERAL_COLLECTOR &aCollector) const
Promote any table cell selections to the whole table.
void ExitGroup(bool aSelectGroup=false)
Leave the currently-entered group.
BOX2I GetBoundingBox() const override
int GetWidth() const override
Definition: pcb_shape.cpp:301
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:170
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: pcb_shape.h:69
wxString GetShownText(bool aAllowExtraText, int aDepth=0) const override
Return the string actually shown after processing of the base text.
T * frame() const
bool IsFootprintEditor() const
KIGFX::VIEW_CONTROLS * controls() const
BOARD * board() const
PCB_DRAW_PANEL_GAL * canvas() const
bool m_isFootprintEditor
const PCB_SELECTION & selection() const
FOOTPRINT * footprint() const
void SetHasSolderMask(bool aVal)
Definition: pcb_track.h:145
virtual double GetLength() const
Get the length of the track using the hypotenuse calculation.
Definition: pcb_track.cpp:730
void SetEnd(const VECTOR2I &aEnd)
Definition: pcb_track.h:118
bool HasSolderMask() const
Definition: pcb_track.h:146
void SetStart(const VECTOR2I &aStart)
Definition: pcb_track.h:121
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
Definition: pcb_track.h:148
std::optional< int > GetLocalSolderMaskMargin() const
Definition: pcb_track.h:149
const VECTOR2I & GetStart() const
Definition: pcb_track.h:122
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:119
EDA_ITEM_FLAGS IsPointOnEnds(const VECTOR2I &point, int min_dist=0) const
Return STARTPOINT if point if near (dist = min_dist) start point, ENDPOINT if point if near (dist = m...
Definition: pcb_track.cpp:628
virtual void SetWidth(int aWidth)
Definition: pcb_track.h:115
virtual int GetWidth() const
Definition: pcb_track.h:116
void SetMotionHandler(MOTION_HANDLER aHandler)
Set a handler for mouse motion.
Definition: picker_tool.h:84
void SetClickHandler(CLICK_HANDLER aHandler)
Set a handler for mouse click event.
Definition: picker_tool.h:73
void SetSnapping(bool aSnap)
Definition: picker_tool.h:66
void SetCancelHandler(CANCEL_HANDLER aHandler)
Set a handler for cancel events (ESC or context-menu Cancel).
Definition: picker_tool.h:93
void SetFinalizeHandler(FINALIZE_HANDLER aHandler)
Set a handler for the finalize event.
Definition: picker_tool.h:104
bool RoutingInProgress()
Returns whether routing is currently active.
bool CanInlineDrag(int aDragMode)
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition: seg.cpp:327
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition: seg.h:220
bool ApproxCollinear(const SEG &aSeg, int aDistanceThreshold=1) const
Definition: seg.cpp:477
VECTOR2I LineProject(const VECTOR2I &aP) const
Compute the perpendicular projection point of aP on a line passing through ends of the segment.
Definition: seg.cpp:371
SEG PerpendicularSeg(const VECTOR2I &aP) const
Compute a segment perpendicular to this one, passing through point aP.
Definition: seg.cpp:265
int Side(const VECTOR2I &aP) const
Determine on which side of directed line passing via segment ends point aP lies.
Definition: seg.h:143
EDA_ANGLE Angle(const SEG &aOther) const
Determine the smallest angle between two segments.
Definition: seg.cpp:97
static SELECTION_CONDITION HasTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if among the selected items there is at least one of a given types.
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 bool NotEmpty(const SELECTION &aSelection)
Test if there are any items selected.
static SELECTION_CONDITION MoreThan(int aNumber)
Create a functor that tests if the number of selected items is greater than the value given as parame...
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 SELECTION_CONDITION OnlyTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if the selected items are only of given types.
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
int AddItemToSel(const TOOL_EVENT &aEvent)
virtual KIGFX::VIEW_ITEM * GetItem(unsigned int aIdx) const override
Definition: selection.cpp:75
ITER end()
Definition: selection.h:75
ITER begin()
Definition: selection.h:74
VECTOR2I GetReferencePoint() const
Definition: selection.cpp:169
virtual VECTOR2I GetCenter() const
Returns the center point of the selection area bounding box.
Definition: selection.cpp:92
bool IsHover() const
Definition: selection.h:84
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition: selection.h:100
EDA_ITEM * Front() const
Definition: selection.h:172
bool HasType(KICAD_T aType) const
Checks if there is at least one item of requested kind.
Definition: selection.cpp:143
int Size() const
Returns the number of selected parts.
Definition: selection.h:116
std::deque< EDA_ITEM * > & Items()
Definition: selection.h:177
void ClearReferencePoint()
Definition: selection.cpp:184
void SetReferencePoint(const VECTOR2I &aP)
Definition: selection.cpp:178
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:110
bool HasReferencePoint() const
Definition: selection.h:211
size_t CountType(KICAD_T aType) const
Definition: selection.cpp:155
bool Contains(EDA_ITEM *aItem) const
Definition: selection.cpp:84
const VECTOR2I & GetP1() const
Definition: shape_arc.h:117
const VECTOR2I & GetP0() const
Definition: shape_arc.h:116
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
void SimplifyOutlines(int aMaxError=0)
Simplifies the lines in the polyset.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the index-th vertex in a given hole outline within a given outline.
static const int MIN_PRECISION_IU
This is the minimum precision for all the points in a shape.
Definition: shape.h:131
Heuristically increment a string's n'th part from the right.
Definition: increment.h:48
void SetSkipIOSQXZ(bool aSkip)
If a alphabetic part is found, skip the letters I, O, S, Q, X, Z.
Definition: increment.h:54
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.
TOOL_EVENT MakeEvent() const
Return the event associated with the action (i.e.
TOOL_MANAGER * GetManager() const
Return the instance of TOOL_MANAGER that takes care of the tool.
Definition: tool_base.h:146
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:44
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:220
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:38
bool IsToolActive() const
Definition: tool_base.cpp:32
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
Generic, UI-independent tool event.
Definition: tool_event.h:168
TOOL_ACTIONS Action() const
Returns more specific information about the type of an event.
Definition: tool_event.h:247
bool Matches(const TOOL_EVENT &aEvent) const
Test whether two events match in terms of category & action or command.
Definition: tool_event.h:389
COMMIT * Commit() const
Definition: tool_event.h:280
void SetParameter(T aParam)
Set a non-standard parameter assigned to the event.
Definition: tool_event.h:520
TOOL_EVENT_CATEGORY Category() const
Return the category (eg. mouse/keyboard/action) of an event.
Definition: tool_event.h:244
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
Definition: tool_event.cpp:82
T Parameter() const
Return a parameter assigned to the event.
Definition: tool_event.h:465
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_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).
void PostEvent(const TOOL_EVENT &aEvent)
Put an event to the event queue to be processed at the end of event processing cycle.
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:150
bool PostAction(const std::string &aActionName, T aParam)
Run the specified action after the current action (coroutine) ends.
Definition: tool_manager.h:235
bool RunSynchronousAction(const TOOL_ACTION &aAction, COMMIT *aCommit, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:197
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:44
void RegisterSubMenu(std::shared_ptr< ACTION_MENU > aSubMenu)
Store a submenu of this menu model.
Definition: tool_menu.cpp:50
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition: vector2d.h:561
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:283
A dialog like WX_UNIT_ENTRY_DIALOG, but with multiple entries.
std::vector< RESULT > GetValues() const
Returns the values in the order they were added.
An extension of WX_TEXT_ENTRY_DIALOG that uses UNIT_BINDER to request a dimension (e....
int GetValue()
Return the value in internal units.
Handle a list of polygons defining a copper zone.
Definition: zone.h:73
bool UnFill()
Removes the zone filling.
Definition: zone.cpp:418
bool HitTestCutout(const VECTOR2I &aRefPos, int *aOutlineIdx=nullptr, int *aHoleIdx=nullptr) const
Test if the given point is contained within a cutout of the zone.
Definition: zone.cpp:737
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:1145
void RemoveCutout(int aOutlineIdx, int aHoleIdx)
Remove a cutout from the zone.
Definition: zone.cpp:988
bool SaveClipboard(const std::string &aTextUTF8)
Store information to the system clipboard.
Definition: clipboard.cpp:31
@ CHT_UNGROUP
Definition: commit.h:46
ROTATION_ANCHOR
@ ROTATE_AROUND_USER_ORIGIN
@ ROTATE_AROUND_SEL_CENTER
@ ROTATE_AROUND_AUX_ORIGIN
@ ROTATE_AROUND_ITEM_ANCHOR
#define _(s)
@ DEGREES_T
Definition: eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:405
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
Definition: eda_item.h:538
#define IS_NEW
New item, just created.
#define STRUCT_DELETED
flag indication structures to be erased
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
static const std::vector< KICAD_T > baseConnectedTypes
Definition: edit_tool.cpp:89
static const std::vector< KICAD_T > routableTypes
Definition: edit_tool.cpp:100
static std::shared_ptr< CONDITIONAL_MENU > makePositioningToolsMenu(TOOL_INTERACTIVE *aTool)
Definition: edit_tool.cpp:123
static FOOTPRINT * GetFootprintFromBoardByReference(PCB_BASE_FRAME &aFrame)
Definition: edit_tool.cpp:428
static std::shared_ptr< CONDITIONAL_MENU > makeShapeModificationMenu(TOOL_INTERACTIVE *aTool)
Definition: edit_tool.cpp:146
static std::optional< CHAMFER_PARAMS > GetChamferParams(PCB_BASE_EDIT_FRAME &aFrame)
Prompt the user for chamfer parameters.
Definition: edit_tool.cpp:1336
static const std::vector< KICAD_T > footprintTypes
Definition: edit_tool.cpp:81
static const std::vector< KICAD_T > connectedTypes
Definition: edit_tool.cpp:94
static const std::vector< KICAD_T > groupTypes
Definition: edit_tool.cpp:83
static const std::vector< KICAD_T > padTypes
Definition: edit_tool.cpp:79
static std::optional< int > GetRadiusParams(PCB_BASE_EDIT_FRAME &aFrame, const wxString &aTitle, int &aPersitentRadius)
Prompt the user for a radius and return it.
Definition: edit_tool.cpp:1270
static void mirrorPad(PAD &aPad, const VECTOR2I &aMirrorPoint, FLIP_DIRECTION aFlipDirection)
Mirror a pad in the H/V axis passing through a point.
Definition: edit_tool.cpp:2086
static const std::vector< KICAD_T > trackTypes
Definition: edit_tool.cpp:85
static std::optional< DOGBONE_CORNER_ROUTINE::PARAMETERS > GetDogboneParams(PCB_BASE_EDIT_FRAME &aFrame)
Definition: edit_tool.cpp:1285
static std::vector< KICAD_T > connectedTypes
void ConnectBoardShapes(std::vector< PCB_SHAPE * > &aShapeList, std::vector< std::unique_ptr< PCB_SHAPE > > &aNewShapes, int aChainingEpsilon)
Connects shapes to each other, making continious contours (adjacent shapes will have a common vertex)...
@ LAYER_DRAWINGSHEET
Sheet frame and title block.
Definition: layer_ids.h:277
@ LAYER_SCHEMATIC_DRAWINGSHEET
Definition: layer_ids.h:484
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:96
constexpr void MIRROR(T &aPoint, const T &aMirrorRef)
Updates aPoint with the mirror of aPoint relative to the aMirrorRef.
Definition: mirror.h:45
FLIP_DIRECTION
Definition: mirror.h:27
KICOMMON_API wxString MessageTextFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A helper to convert the double length aValue to a string in inches, millimeters, or unscaled units.
Definition: eda_units.cpp:385
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition: wxgtk/ui.cpp:677
@ DM_ANY
Definition: pns_router.h:77
@ DM_FREE_ANGLE
Definition: pns_router.h:75
SGLIB_API S3DMODEL * GetModel(SCENEGRAPH *aNode)
Create an S3DMODEL representation of aNode (raw data, no transforms).
Definition: ifsg_api.cpp:338
EDA_ANGLE GetEventRotationAngle(const PCB_BASE_EDIT_FRAME &aFrame, const TOOL_EVENT &aEvent)
Function getEventRotationAngle()
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition: kicad_algo.h:100
void for_all_pairs(_InputIterator __first, _InputIterator __last, _Function __f)
Apply a function to every possible pair of elements of a sequence.
Definition: kicad_algo.h:84
Class to handle a set of BOARD_ITEMs.
BOARD * GetBoard()
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
std::function< bool(const SELECTION &)> SELECTION_CONDITION
Functor type that checks a specific condition for selected items.
Parameters that define a simple chamfer operation.
const double IU_PER_MM
Definition: base_units.h:76
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
VECTOR2I end
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ BUT_LEFT
Definition: tool_event.h:132
const VECTOR2I CalcArcMid(const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, bool aMinArcAngle=true)
Return the middle point of an arc, half-way between aStart and aEnd.
Definition: trigo.cpp:209
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition: trigo.cpp:229
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:105
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:102
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition: typeinfo.h:91
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:103
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:110
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition: typeinfo.h:93
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:107
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:92
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition: typeinfo.h:89
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition: typeinfo.h:90
@ PCB_MARKER_T
class PCB_MARKER, a marker used to show something
Definition: typeinfo.h:99
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition: typeinfo.h:106
@ PCB_SHAPE_LOCATE_SEGMENT_T
Definition: typeinfo.h:133
@ PCB_SHAPE_LOCATE_RECT_T
Definition: typeinfo.h:134
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition: typeinfo.h:95
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition: typeinfo.h:86
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:101
@ PCB_SHAPE_LOCATE_BEZIER_T
Definition: typeinfo.h:138
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_SHAPE_LOCATE_POLY_T
Definition: typeinfo.h:137
@ PCB_SHAPE_LOCATE_ARC_T
Definition: typeinfo.h:136
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition: typeinfo.h:100
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition: typeinfo.h:94
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:104
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695