KiCad PCB EDA Suite
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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 <maciej.suminski@cern.ch>
7 * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
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::routerRouteSelectedFromEnd, isRoutable );
362 menu.AddItem( PCB_ACTIONS::unrouteSelected, isRoutable );
363 menu.AddItem( PCB_ACTIONS::routerAutorouteSelected, isRoutable );
365 && notMovingCondition );
366 menu.AddItem( PCB_ACTIONS::skip, isSkippable );
378 menu.AddItem( PCB_ACTIONS::mirrorH, canMirror );
379 menu.AddItem( PCB_ACTIONS::mirrorV, canMirror );
383
384 menu.AddItem( PCB_ACTIONS::properties, propertiesCondition );
385
387 && !inFootprintEditor );
389
390 // Footprint actions
391 menu.AddSeparator();
392 menu.AddItem( PCB_ACTIONS::editFpInFpEditor, singleFootprintCondition );
393 menu.AddItem( PCB_ACTIONS::updateFootprint, singleFootprintCondition );
394 menu.AddItem( PCB_ACTIONS::updateFootprints, multipleFootprintsCondition );
395 menu.AddItem( PCB_ACTIONS::changeFootprint, singleFootprintCondition );
396 menu.AddItem( PCB_ACTIONS::changeFootprints, multipleFootprintsCondition );
397
398 // Add the submenu for the special tools: modfiers and positioning tools
399 menu.AddSeparator( 100 );
400 menu.AddMenu( shapeModificationSubMenu.get(), shapeModificationCondition, 100 );
401 menu.AddMenu( positioningToolsSubMenu.get(), positioningToolsCondition, 100 );
402
403 menu.AddSeparator( 150 );
404 menu.AddItem( ACTIONS::cut, SELECTION_CONDITIONS::NotEmpty, 150 );
405 menu.AddItem( ACTIONS::copy, SELECTION_CONDITIONS::NotEmpty, 150 );
406 menu.AddItem( ACTIONS::copyAsText, canCopyAsText, 150 );
407
408 // Selection tool handles the context menu for some other tools, such as the Picker.
409 // Don't add things like Paste when another tool is active.
410 menu.AddItem( ACTIONS::paste, noActiveToolCondition, 150 );
411 menu.AddItem( ACTIONS::pasteSpecial, noActiveToolCondition && !inFootprintEditor, 150 );
414
415 menu.AddSeparator( 150 );
416 menu.AddItem( ACTIONS::selectAll, noItemsCondition, 150 );
417 menu.AddItem( ACTIONS::unselectAll, noItemsCondition, 150 );
418 // clang-format on
419
420 return true;
421}
422
423
430{
431 wxString footprintName;
432 wxArrayString fplist;
433 const FOOTPRINTS& footprints = aFrame.GetBoard()->Footprints();
434
435 // Build list of available fp references, to display them in dialog
436 for( FOOTPRINT* fp : footprints )
437 fplist.Add( fp->GetReference() + wxT( " ( " ) + fp->GetValue() + wxT( " )" ) );
438
439 fplist.Sort();
440
441 DIALOG_GET_FOOTPRINT_BY_NAME dlg( &aFrame, fplist );
442
443 if( dlg.ShowModal() != wxID_OK ) //Aborted by user
444 return nullptr;
445
446 footprintName = dlg.GetValue();
447 footprintName.Trim( true );
448 footprintName.Trim( false );
449
450 if( !footprintName.IsEmpty() )
451 {
452 for( FOOTPRINT* fp : footprints )
453 {
454 if( fp->GetReference().CmpNoCase( footprintName ) == 0 )
455 return fp;
456 }
457 }
458
459 return nullptr;
460}
461
462
464{
465 // GetAndPlace makes sense only in board editor, although it is also called
466 // in fpeditor, that shares the same EDIT_TOOL list
467 if( IsFootprintEditor() )
468 return 0;
469
472
473 if( fp )
474 {
477
478 selectionTool->GetSelection().SetReferencePoint( fp->GetPosition() );
480 }
481
482 return 0;
483}
484
485
486bool EDIT_TOOL::invokeInlineRouter( int aDragMode )
487{
488 ROUTER_TOOL* theRouter = m_toolMgr->GetTool<ROUTER_TOOL>();
489
490 if( !theRouter )
491 return false;
492
493 // don't allow switch from moving to dragging
494 if( m_dragging )
495 {
496 wxBell();
497 return false;
498 }
499
500 // make sure we don't accidentally invoke inline routing mode while the router is already
501 // active!
502 if( theRouter->IsToolActive() )
503 return false;
504
505 if( theRouter->CanInlineDrag( aDragMode ) )
506 {
508 return true;
509 }
510
511 return false;
512}
513
514
516{
518
519 return router && router->RoutingInProgress();
520}
521
522
523int EDIT_TOOL::Drag( const TOOL_EVENT& aEvent )
524{
525 if( !m_toolMgr->GetTool<ROUTER_TOOL>() )
526 {
527 wxBell();
528 return false; // don't drag when no router tool (i.e. fp editor)
529 }
530
532 {
533 wxBell();
534 return false; // don't drag when router is already active
535 }
536
537 if( m_dragging )
538 {
539 wxBell();
540 return false; // don't do a router drag when already in an EDIT_TOOL drag
541 }
542
543 int mode = PNS::DM_ANY;
544
545 if( aEvent.IsAction( &PCB_ACTIONS::dragFreeAngle ) )
546 mode |= PNS::DM_FREE_ANGLE;
547
549 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
550 {
551 sTool->FilterCollectorForFreePads( aCollector );
552 sTool->FilterCollectorForHierarchy( aCollector, true );
553
554 std::vector<PCB_TRACK*> tracks;
555 std::vector<PCB_TRACK*> vias;
556 std::vector<FOOTPRINT*> footprints;
557
558 for( EDA_ITEM* item : aCollector )
559 {
560 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
561 {
562 if( track->Type() == PCB_VIA_T )
563 vias.push_back( track );
564 else
565 tracks.push_back( track );
566 }
567 else if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( item ) )
568 {
569 footprints.push_back( footprint );
570 }
571 }
572
573 if( !sTool->GetSelection().IsHover() && footprints.size() )
574 {
575 // Remove non-footprints so box-selection will drag footprints.
576 for( int ii = aCollector.GetCount() - 1; ii >= 0; --ii )
577 {
578 if( aCollector[ii]->Type() != PCB_FOOTPRINT_T )
579 aCollector.Remove( ii );
580 }
581 }
582 else if( tracks.size() || vias.size() )
583 {
584 /*
585 * First trim down selection to active layer, tracks vs zones, etc.
586 */
587 if( aCollector.GetCount() > 1 )
588 sTool->GuessSelectionCandidates( aCollector, aPt );
589
590 /*
591 * If we have a knee between two tracks, or a via attached to two tracks,
592 * then drop the selection to a single item. We don't want a selection
593 * disambiguation menu when it doesn't matter which items is picked.
594 */
595 auto connected = []( PCB_TRACK* track, const VECTOR2I& pt )
596 {
597 return track->GetStart() == pt || track->GetEnd() == pt;
598 };
599
600 if( tracks.size() == 2 && vias.size() == 0 )
601 {
602 if( connected( tracks[0], tracks[1]->GetStart() )
603 || connected( tracks[0], tracks[1]->GetEnd() ) )
604 {
605 aCollector.Remove( tracks[1] );
606 }
607 }
608 else if( tracks.size() == 2 && vias.size() == 1 )
609 {
610 if( connected( tracks[0], vias[0]->GetPosition() )
611 && connected( tracks[1], vias[0]->GetPosition() ) )
612 {
613 aCollector.Remove( tracks[0] );
614 aCollector.Remove( tracks[1] );
615 }
616 }
617 }
618 },
619 true /* prompt user regarding locked items */ );
620
621 if( selection.Empty() )
622 return 0;
623
624 if( selection.Size() == 1 && selection.Front()->Type() == PCB_ARC_T )
625 {
626 // TODO: This really should be done in PNS to ensure DRC is maintained, but for now
627 // it allows interactive editing of an arc track
628 return DragArcTrack( aEvent );
629 }
630 else
631 {
632 invokeInlineRouter( mode );
633 }
634
635 return 0;
636}
637
638
640{
642
643 if( selection.Size() != 1 || selection.Front()->Type() != PCB_ARC_T )
644 return 0;
645
646 PCB_ARC* theArc = static_cast<PCB_ARC*>( selection.Front() );
647 EDA_ANGLE maxTangentDeviation( ADVANCED_CFG::GetCfg().m_MaxTangentAngleDeviation, DEGREES_T );
648
649 if( theArc->GetAngle() + maxTangentDeviation >= ANGLE_180 )
650 {
651 wxString msg = wxString::Format( _( "Unable to resize arc tracks of %s or greater." ),
652 EDA_UNIT_UTILS::UI::MessageTextFromValue( ANGLE_180 - maxTangentDeviation ) );
653 frame()->ShowInfoBarError( msg );
654
655 return 0; // don't bother with > 180 degree arcs
656 }
657
659
660 Activate();
661 // Must be done after Activate() so that it gets set into the correct context
662 controls->ShowCursor( true );
663 controls->SetAutoPan( true );
664
665 BOARD_COMMIT commit( this );
666 bool restore_state = false;
667
668 commit.Modify( theArc );
669
670 VECTOR2I arcCenter = theArc->GetCenter();
671 SEG tanStart = SEG( arcCenter, theArc->GetStart() ).PerpendicularSeg( theArc->GetStart() );
672 SEG tanEnd = SEG( arcCenter, theArc->GetEnd() ).PerpendicularSeg( theArc->GetEnd() );
673
674 //Ensure the tangent segments are in the correct orientation
675 OPT_VECTOR2I tanIntersect = tanStart.IntersectLines( tanEnd );
676
677 if( !tanIntersect )
678 return 0;
679
680 tanStart.A = *tanIntersect;
681 tanStart.B = theArc->GetStart();
682 tanEnd.A = *tanIntersect;
683 tanEnd.B = theArc->GetEnd();
684
685 std::set<PCB_TRACK*> addedTracks;
686
687 auto getUniqueTrackAtAnchorCollinear =
688 [&]( const VECTOR2I& aAnchor, const SEG& aCollinearSeg ) -> PCB_TRACK*
689 {
690 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
691
692 // Allow items at a distance within the width of the arc track
693 int allowedDeviation = theArc->GetWidth();
694
695 std::vector<BOARD_CONNECTED_ITEM*> itemsOnAnchor;
696
697 for( int i = 0; i < 3; i++ )
698 {
699 itemsOnAnchor = conn->GetConnectedItemsAtAnchor( theArc, aAnchor,
701 allowedDeviation );
702 allowedDeviation /= 2;
703
704 if( itemsOnAnchor.size() == 1 )
705 break;
706 }
707
708 PCB_TRACK* track = nullptr;
709
710 if( itemsOnAnchor.size() == 1 && itemsOnAnchor.front()->Type() == PCB_TRACE_T )
711 {
712 track = static_cast<PCB_TRACK*>( itemsOnAnchor.front() );
713 commit.Modify( track );
714
715 SEG trackSeg( track->GetStart(), track->GetEnd() );
716
717 // Allow deviations in colinearity as defined in ADVANCED_CFG
718 if( trackSeg.Angle( aCollinearSeg ) > maxTangentDeviation )
719 track = nullptr;
720 }
721
722 if( !track )
723 {
724 track = new PCB_TRACK( theArc->GetParent() );
725 track->SetStart( aAnchor );
726 track->SetEnd( aAnchor );
727 track->SetNet( theArc->GetNet() );
728 track->SetLayer( theArc->GetLayer() );
729 track->SetWidth( theArc->GetWidth() );
730 track->SetLocked( theArc->IsLocked() );
731 track->SetHasSolderMask( theArc->HasSolderMask() );
733 track->SetFlags( IS_NEW );
734 getView()->Add( track );
735 addedTracks.insert( track );
736 }
737
738 return track;
739 };
740
741 PCB_TRACK* trackOnStart = getUniqueTrackAtAnchorCollinear( theArc->GetStart(), tanStart);
742 PCB_TRACK* trackOnEnd = getUniqueTrackAtAnchorCollinear( theArc->GetEnd(), tanEnd );
743
744 if( trackOnStart->GetLength() != 0 )
745 {
746 tanStart.A = trackOnStart->GetStart();
747 tanStart.B = trackOnStart->GetEnd();
748 }
749
750 if( trackOnEnd->GetLength() != 0 )
751 {
752 tanEnd.A = trackOnEnd->GetStart();
753 tanEnd.B = trackOnEnd->GetEnd();
754 }
755
756 // Recalculate intersection point
757 if( tanIntersect = tanStart.IntersectLines( tanEnd ); !tanIntersect )
758 return 0;
759
760 auto isTrackStartClosestToArcStart =
761 [&]( PCB_TRACK* aTrack ) -> bool
762 {
763 double trackStartToArcStart = aTrack->GetStart().Distance( theArc->GetStart() );
764 double trackEndToArcStart = aTrack->GetEnd().Distance( theArc->GetStart() );
765
766 return trackStartToArcStart < trackEndToArcStart;
767 };
768
769 bool isStartTrackOnStartPt = isTrackStartClosestToArcStart( trackOnStart );
770 bool isEndTrackOnStartPt = isTrackStartClosestToArcStart( trackOnEnd );
771
772 // Calculate constraints
773 //======================
774 // maxTanCircle is the circle with maximum radius that is tangent to the two adjacent straight
775 // tracks and whose tangent points are constrained within the original tracks and their
776 // projected intersection points.
777 //
778 // The cursor will be constrained first within the isosceles triangle formed by the segments
779 // cSegTanStart, cSegTanEnd and cSegChord. After that it will be constrained to be outside
780 // maxTanCircle.
781 //
782 //
783 // ____________ <-cSegTanStart
784 // / * . ' *
785 // cSegTanEnd-> / * . ' *
786 // /* . ' <-cSegChord *
787 // /. '
788 // /* *
789 //
790 // * c * <-maxTanCircle
791 //
792 // * *
793 //
794 // * *
795 // * *
796 // * *
797 //
798
799 auto getFurthestPointToTanInterstect =
800 [&]( VECTOR2I& aPointA, VECTOR2I& aPointB ) -> VECTOR2I
801 {
802 if( ( aPointA - *tanIntersect ).EuclideanNorm()
803 > ( aPointB - *tanIntersect ).EuclideanNorm() )
804 {
805 return aPointA;
806 }
807 else
808 {
809 return aPointB;
810 }
811 };
812
813 CIRCLE maxTanCircle;
814 VECTOR2I tanStartPoint = getFurthestPointToTanInterstect( tanStart.A, tanStart.B );
815 VECTOR2I tanEndPoint = getFurthestPointToTanInterstect( tanEnd.A, tanEnd.B );
816 VECTOR2I tempTangentPoint = tanEndPoint;
817
818 if( getFurthestPointToTanInterstect( tanStartPoint, tanEndPoint ) == tanEndPoint )
819 tempTangentPoint = tanStartPoint;
820
821 maxTanCircle.ConstructFromTanTanPt( tanStart, tanEnd, tempTangentPoint );
822 VECTOR2I maxTanPtStart = tanStart.LineProject( maxTanCircle.Center );
823 VECTOR2I maxTanPtEnd = tanEnd.LineProject( maxTanCircle.Center );
824
825 SEG cSegTanStart( maxTanPtStart, *tanIntersect );
826 SEG cSegTanEnd( maxTanPtEnd, *tanIntersect );
827 SEG cSegChord( maxTanPtStart, maxTanPtEnd );
828
829 int cSegTanStartSide = cSegTanStart.Side( theArc->GetMid() );
830 int cSegTanEndSide = cSegTanEnd.Side( theArc->GetMid() );
831 int cSegChordSide = cSegChord.Side( theArc->GetMid() );
832
833 bool eatFirstMouseUp = true;
834
835 // Start the tool loop
836 while( TOOL_EVENT* evt = Wait() )
837 {
838 m_cursor = controls->GetMousePosition();
839
840 // Constrain cursor within the isosceles triangle
841 if( cSegTanStartSide != cSegTanStart.Side( m_cursor )
842 || cSegTanEndSide != cSegTanEnd.Side( m_cursor )
843 || cSegChordSide != cSegChord.Side( m_cursor ) )
844 {
845 std::vector<VECTOR2I> possiblePoints;
846
847 possiblePoints.push_back( cSegTanEnd.NearestPoint( m_cursor ) );
848 possiblePoints.push_back( cSegChord.NearestPoint( m_cursor ) );
849
850 VECTOR2I closest = cSegTanStart.NearestPoint( m_cursor );
851
852 for( const VECTOR2I& candidate : possiblePoints )
853 {
854 if( ( candidate - m_cursor ).SquaredEuclideanNorm()
855 < ( closest - m_cursor ).SquaredEuclideanNorm() )
856 {
857 closest = candidate;
858 }
859 }
860
861 m_cursor = closest;
862 }
863
864 // Constrain cursor to be outside maxTanCircle
865 if( ( m_cursor - maxTanCircle.Center ).EuclideanNorm() < maxTanCircle.Radius )
866 m_cursor = maxTanCircle.NearestPoint( m_cursor );
867
868 controls->ForceCursorPosition( true, m_cursor );
869
870 // Calculate resulting object coordinates
871 CIRCLE circlehelper;
872 circlehelper.ConstructFromTanTanPt( cSegTanStart, cSegTanEnd, m_cursor );
873
874 VECTOR2I newCenter = circlehelper.Center;
875 VECTOR2I newStart = cSegTanStart.LineProject( newCenter );
876 VECTOR2I newEnd = cSegTanEnd.LineProject( newCenter );
877 VECTOR2I newMid = CalcArcMid( newStart, newEnd, newCenter );
878
879 // Update objects
880 theArc->SetStart( newStart );
881 theArc->SetEnd( newEnd );
882 theArc->SetMid( newMid );
883
884 if( isStartTrackOnStartPt )
885 trackOnStart->SetStart( newStart );
886 else
887 trackOnStart->SetEnd( newStart );
888
889 if( isEndTrackOnStartPt )
890 trackOnEnd->SetStart( newEnd );
891 else
892 trackOnEnd->SetEnd( newEnd );
893
894 // Update view
895 getView()->Update( trackOnStart );
896 getView()->Update( trackOnEnd );
897 getView()->Update( theArc );
898
899 // Handle events
900 if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
901 {
902 eatFirstMouseUp = false;
903 }
904 else if( evt->IsCancelInteractive() || evt->IsActivate() )
905 {
906 restore_state = true; // Canceling the tool means that items have to be restored
907 break; // Finish
908 }
909 else if( evt->IsAction( &ACTIONS::undo ) )
910 {
911 restore_state = true; // Perform undo locally
912 break; // Finish
913 }
914 else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT )
915 || evt->IsDblClick( BUT_LEFT ) )
916 {
917 // Eat mouse-up/-click events that leaked through from the lock dialog
918 if( eatFirstMouseUp && !evt->IsAction( &ACTIONS::cursorClick ) )
919 {
920 eatFirstMouseUp = false;
921 continue;
922 }
923
924 break; // Finish
925 }
926 }
927
928 // Amend the end points of the arc if we delete the joining tracks
929 VECTOR2I newStart = trackOnStart->GetStart();
930 VECTOR2I newEnd = trackOnEnd->GetStart();
931
932 if( isStartTrackOnStartPt )
933 newStart = trackOnStart->GetEnd();
934
935 if( isEndTrackOnStartPt )
936 newEnd = trackOnEnd->GetEnd();
937
938 int maxLengthIU =
939 KiROUND( ADVANCED_CFG::GetCfg().m_MaxTrackLengthToKeep * pcbIUScale.IU_PER_MM );
940
941 if( trackOnStart->GetLength() <= maxLengthIU )
942 {
943 if( addedTracks.count( trackOnStart ) )
944 {
945 getView()->Remove( trackOnStart );
946 addedTracks.erase( trackOnStart );
947 delete trackOnStart;
948 }
949 else
950 {
951 commit.Remove( trackOnStart );
952 }
953
954 theArc->SetStart( newStart );
955 }
956
957 if( trackOnEnd->GetLength() <= maxLengthIU )
958 {
959 if( addedTracks.count( trackOnEnd ) )
960 {
961 getView()->Remove( trackOnEnd );
962 addedTracks.erase( trackOnEnd );
963 delete trackOnEnd;
964 }
965 else
966 {
967 commit.Remove( trackOnEnd );
968 }
969
970 theArc->SetEnd( newEnd );
971 }
972
973 if( theArc->GetLength() <= 0 )
974 commit.Remove( theArc );
975
976 for( PCB_TRACK* added : addedTracks )
977 {
978 getView()->Remove( added );
979 commit.Add( added );
980 }
981
982 // Should we commit?
983 if( restore_state )
984 commit.Revert();
985 else
986 commit.Push( _( "Drag Arc Track" ) );
987
988 return 0;
989}
990
991
993{
995 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
996 {
997 // Iterate from the back so we don't have to worry about removals.
998 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
999 {
1000 BOARD_ITEM* item = aCollector[ i ];
1001
1002 if( !dynamic_cast<PCB_TRACK*>( item ) )
1003 aCollector.Remove( item );
1004 }
1005 },
1006 true /* prompt user regarding locked items */ );
1007
1008 BOARD_COMMIT commit( this );
1009
1010 for( EDA_ITEM* item : selection )
1011 {
1012 if( item->Type() == PCB_VIA_T )
1013 {
1014 PCB_VIA* via = static_cast<PCB_VIA*>( item );
1015
1016 commit.Modify( via );
1017
1018 int new_width;
1019 int new_drill;
1020
1021 if( via->GetViaType() == VIATYPE::MICROVIA )
1022 {
1023 NETCLASS* netClass = via->GetEffectiveNetClass();
1024
1025 new_width = netClass->GetuViaDiameter();
1026 new_drill = netClass->GetuViaDrill();
1027 }
1028 else
1029 {
1030 new_width = board()->GetDesignSettings().GetCurrentViaSize();
1031 new_drill = board()->GetDesignSettings().GetCurrentViaDrill();
1032 }
1033
1034 via->SetDrill( new_drill );
1035 // TODO(JE) padstacks - is this correct behavior already? If so, also change stack mode
1036 via->SetWidth( PADSTACK::ALL_LAYERS, new_width );
1037 }
1038 else if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
1039 {
1040 PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item );
1041
1042 wxCHECK( track, 0 );
1043
1044 commit.Modify( track );
1045
1046 int new_width = board()->GetDesignSettings().GetCurrentTrackWidth();
1047 track->SetWidth( new_width );
1048 }
1049 }
1050
1051 commit.Push( _( "Edit Track Width/Via Size" ) );
1052
1053 if( selection.IsHover() )
1054 {
1056
1057 // Notify other tools of the changes -- This updates the visual ratsnest
1059 }
1060
1061 return 0;
1062}
1063
1064
1066{
1067 // Store last used fillet radius to allow pressing "enter" if repeat fillet is required
1068 static int filletRadius = 0;
1069
1071 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1072 {
1073 // Iterate from the back so we don't have to worry about removals.
1074 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1075 {
1076 BOARD_ITEM* item = aCollector[i];
1077
1078 if( !dynamic_cast<PCB_TRACK*>( item ) )
1079 aCollector.Remove( item );
1080 }
1081 },
1082 true /* prompt user regarding locked items */ );
1083
1084 if( selection.Size() < 2 )
1085 {
1086 frame()->ShowInfoBarMsg( _( "At least two straight track segments must be selected." ) );
1087 return 0;
1088 }
1089
1090 WX_UNIT_ENTRY_DIALOG dlg( frame(), _( "Fillet Tracks" ), _( "Radius:" ), filletRadius );
1091
1092 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue() == 0 )
1093 return 0;
1094
1095 filletRadius = dlg.GetValue();
1096
1097 struct FILLET_OP
1098 {
1099 PCB_TRACK* t1;
1100 PCB_TRACK* t2;
1101 // Start point of track is modified after PCB_ARC is added, otherwise the end point:
1102 bool t1Start = true;
1103 bool t2Start = true;
1104 };
1105
1106 std::vector<FILLET_OP> filletOperations;
1107 bool operationPerformedOnAtLeastOne = false;
1108 bool didOneAttemptFail = false;
1109 std::set<PCB_TRACK*> processedTracks;
1110
1111 auto processFilletOp =
1112 [&]( PCB_TRACK* aTrack, bool aStartPoint )
1113 {
1114 std::shared_ptr<CONNECTIVITY_DATA> c = board()->GetConnectivity();
1115 VECTOR2I anchor = aStartPoint ? aTrack->GetStart() : aTrack->GetEnd();
1116 std::vector<BOARD_CONNECTED_ITEM*> itemsOnAnchor;
1117
1118 itemsOnAnchor = c->GetConnectedItemsAtAnchor( aTrack, anchor, baseConnectedTypes );
1119
1120 if( itemsOnAnchor.size() > 0
1121 && selection.Contains( itemsOnAnchor.at( 0 ) )
1122 && itemsOnAnchor.at( 0 )->Type() == PCB_TRACE_T )
1123 {
1124 PCB_TRACK* trackOther = static_cast<PCB_TRACK*>( itemsOnAnchor.at( 0 ) );
1125
1126 // Make sure we don't fillet the same pair of tracks twice
1127 if( processedTracks.find( trackOther ) == processedTracks.end() )
1128 {
1129 if( itemsOnAnchor.size() == 1 )
1130 {
1131 FILLET_OP filletOp;
1132 filletOp.t1 = aTrack;
1133 filletOp.t2 = trackOther;
1134 filletOp.t1Start = aStartPoint;
1135 filletOp.t2Start = aTrack->IsPointOnEnds( filletOp.t2->GetStart() );
1136 filletOperations.push_back( filletOp );
1137 }
1138 else
1139 {
1140 // User requested to fillet these two tracks but not possible as
1141 // there are other elements connected at that point
1142 didOneAttemptFail = true;
1143 }
1144 }
1145 }
1146 };
1147
1148 for( EDA_ITEM* item : selection )
1149 {
1150 if( item->Type() == PCB_TRACE_T )
1151 {
1152 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
1153
1154 if( track->GetLength() > 0 )
1155 {
1156 processFilletOp( track, true ); // on the start point of track
1157 processFilletOp( track, false ); // on the end point of track
1158
1159 processedTracks.insert( track );
1160 }
1161 }
1162 }
1163
1164 BOARD_COMMIT commit( this );
1165 std::vector<BOARD_ITEM*> itemsToAddToSelection;
1166
1167 for( FILLET_OP filletOp : filletOperations )
1168 {
1169 PCB_TRACK* track1 = filletOp.t1;
1170 PCB_TRACK* track2 = filletOp.t2;
1171
1172 bool trackOnStart = track1->IsPointOnEnds( track2->GetStart() );
1173 bool trackOnEnd = track1->IsPointOnEnds( track2->GetEnd() );
1174
1175 if( trackOnStart && trackOnEnd )
1176 continue; // Ignore duplicate tracks
1177
1178 if( ( trackOnStart || trackOnEnd ) && track1->GetLayer() == track2->GetLayer() )
1179 {
1180 SEG t1Seg( track1->GetStart(), track1->GetEnd() );
1181 SEG t2Seg( track2->GetStart(), track2->GetEnd() );
1182
1183 if( t1Seg.ApproxCollinear( t2Seg ) )
1184 continue;
1185
1186 SHAPE_ARC sArc( t1Seg, t2Seg, filletRadius );
1187 VECTOR2I t1newPoint, t2newPoint;
1188
1189 auto setIfPointOnSeg =
1190 []( VECTOR2I& aPointToSet, const SEG& aSegment, const VECTOR2I& aVecToTest )
1191 {
1192 VECTOR2I segToVec = aSegment.NearestPoint( aVecToTest ) - aVecToTest;
1193
1194 // Find out if we are on the segment (minimum precision)
1196 {
1197 aPointToSet.x = aVecToTest.x;
1198 aPointToSet.y = aVecToTest.y;
1199 return true;
1200 }
1201
1202 return false;
1203 };
1204
1205 //Do not draw a fillet if the end points of the arc are not within the track segments
1206 if( !setIfPointOnSeg( t1newPoint, t1Seg, sArc.GetP0() )
1207 && !setIfPointOnSeg( t2newPoint, t2Seg, sArc.GetP0() ) )
1208 {
1209 didOneAttemptFail = true;
1210 continue;
1211 }
1212
1213 if( !setIfPointOnSeg( t1newPoint, t1Seg, sArc.GetP1() )
1214 && !setIfPointOnSeg( t2newPoint, t2Seg, sArc.GetP1() ) )
1215 {
1216 didOneAttemptFail = true;
1217 continue;
1218 }
1219
1220 PCB_ARC* tArc = new PCB_ARC( frame()->GetBoard(), &sArc );
1221 tArc->SetLayer( track1->GetLayer() );
1222 tArc->SetWidth( track1->GetWidth() );
1223 tArc->SetNet( track1->GetNet() );
1224 tArc->SetLocked( track1->IsLocked() );
1225 tArc->SetHasSolderMask( track1->HasSolderMask() );
1227 commit.Add( tArc );
1228 itemsToAddToSelection.push_back( tArc );
1229
1230 commit.Modify( track1 );
1231 commit.Modify( track2 );
1232
1233 if( filletOp.t1Start )
1234 track1->SetStart( t1newPoint );
1235 else
1236 track1->SetEnd( t1newPoint );
1237
1238 if( filletOp.t2Start )
1239 track2->SetStart( t2newPoint );
1240 else
1241 track2->SetEnd( t2newPoint );
1242
1243 operationPerformedOnAtLeastOne = true;
1244 }
1245 }
1246
1247 commit.Push( _( "Fillet Tracks" ) );
1248
1249 //select the newly created arcs
1250 for( BOARD_ITEM* item : itemsToAddToSelection )
1252
1253 if( !operationPerformedOnAtLeastOne )
1254 frame()->ShowInfoBarMsg( _( "Unable to fillet the selected track segments." ) );
1255 else if( didOneAttemptFail )
1256 frame()->ShowInfoBarMsg( _( "Some of the track segments could not be filleted." ) );
1257
1258 return 0;
1259}
1260
1261
1271static std::optional<int> GetRadiusParams( PCB_BASE_EDIT_FRAME& aFrame, const wxString& aTitle,
1272 int& aPersitentRadius )
1273{
1274 WX_UNIT_ENTRY_DIALOG dlg( &aFrame, aTitle, _( "Radius:" ), aPersitentRadius );
1275
1276 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue() == 0 )
1277 return std::nullopt;
1278
1279 aPersitentRadius = dlg.GetValue();
1280
1281 return aPersitentRadius;
1282}
1283
1284
1285static std::optional<DOGBONE_CORNER_ROUTINE::PARAMETERS>
1287{
1288 // Persistent parameters
1289 static DOGBONE_CORNER_ROUTINE::PARAMETERS s_dogBoneParams{
1290 pcbIUScale.mmToIU( 1 ),
1291 true,
1292 };
1293
1294 std::vector<WX_MULTI_ENTRY_DIALOG::ENTRY> entries{
1295 {
1296 _( "Arc radius:" ),
1297 WX_MULTI_ENTRY_DIALOG::UNIT_BOUND{ s_dogBoneParams.DogboneRadiusIU },
1298 wxEmptyString,
1299 },
1300 {
1301 _( "Add slots in acute corners" ),
1302 WX_MULTI_ENTRY_DIALOG::CHECKBOX{ s_dogBoneParams.AddSlots },
1303 _( "Add slots in acute corners to allow access to a cutter of the given radius" ),
1304 },
1305 };
1306
1307 WX_MULTI_ENTRY_DIALOG dlg( &aFrame, _( "Dogbone Corner Settings" ), entries );
1308
1309 if( dlg.ShowModal() == wxID_CANCEL )
1310 return std::nullopt;
1311
1312 std::vector<WX_MULTI_ENTRY_DIALOG::RESULT> results = dlg.GetValues();
1313 wxCHECK( results.size() == 2, std::nullopt );
1314
1315 try
1316 {
1317 s_dogBoneParams.DogboneRadiusIU = std::get<long long int>( results[0] );
1318 s_dogBoneParams.AddSlots = std::get<bool>( results[1] );
1319 }
1320 catch( const std::bad_variant_access& )
1321 {
1322 wxASSERT( false );
1323 return std::nullopt;
1324 }
1325
1326 return s_dogBoneParams;
1327}
1328
1337static std::optional<CHAMFER_PARAMS> GetChamferParams( PCB_BASE_EDIT_FRAME& aFrame )
1338{
1339 // Non-zero and the KLC default for Fab layer chamfers
1340 const int default_setback = pcbIUScale.mmToIU( 1 );
1341 // Store last used setback to allow pressing "enter" if repeat chamfer is required
1342 static CHAMFER_PARAMS params{ default_setback, default_setback };
1343
1344 WX_UNIT_ENTRY_DIALOG dlg( &aFrame, _( "Chamfer Lines" ), _( "Chamfer setback:" ),
1345 params.m_chamfer_setback_a );
1346
1347 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue() == 0 )
1348 return std::nullopt;
1349
1350 params.m_chamfer_setback_a = dlg.GetValue();
1351 // It's hard to easily specify an asymmetric chamfer (which line gets the longer setback?),
1352 // so we just use the same setback for each
1353 params.m_chamfer_setback_b = params.m_chamfer_setback_a;
1354
1355 return params;
1356}
1357
1358
1360{
1362 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1363 {
1364 std::vector<VECTOR2I> pts;
1365
1366 // Iterate from the back so we don't have to worry about removals.
1367 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1368 {
1369 BOARD_ITEM* item = aCollector[i];
1370
1371 // We've converted the polygon and rectangle to segments, so drop everything
1372 // that isn't a segment at this point
1373 if( !item->IsType( { PCB_SHAPE_LOCATE_SEGMENT_T,
1374 PCB_SHAPE_LOCATE_POLY_T,
1375 PCB_SHAPE_LOCATE_RECT_T } ) )
1376 {
1377 aCollector.Remove( item );
1378 }
1379 }
1380 },
1381 true /* prompt user regarding locked items */ );
1382
1383 std::set<PCB_SHAPE*> lines_to_add;
1384 std::vector<PCB_SHAPE*> items_to_remove;
1385
1386 for( EDA_ITEM* item : selection )
1387 {
1388 std::vector<VECTOR2I> pts;
1389 PCB_SHAPE *graphic = static_cast<PCB_SHAPE*>( item );
1390 PCB_LAYER_ID layer = graphic->GetLayer();
1391 int width = graphic->GetWidth();
1392
1393 if( graphic->GetShape() == SHAPE_T::RECTANGLE )
1394 {
1395 items_to_remove.push_back( graphic );
1396 VECTOR2I start( graphic->GetStart() );
1397 VECTOR2I end( graphic->GetEnd() );
1398 pts.emplace_back( start );
1399 pts.emplace_back( VECTOR2I( end.x, start.y ) );
1400 pts.emplace_back( end );
1401 pts.emplace_back( VECTOR2I( start.x, end.y ) );
1402 }
1403
1404 if( graphic->GetShape() == SHAPE_T::POLY )
1405 {
1406 items_to_remove.push_back( graphic );
1407
1408 for( int jj = 0; jj < graphic->GetPolyShape().VertexCount(); ++jj )
1409 pts.emplace_back( graphic->GetPolyShape().CVertex( jj ) );
1410 }
1411
1412 for( size_t jj = 1; jj < pts.size(); ++jj )
1413 {
1414 PCB_SHAPE *line = new PCB_SHAPE( frame()->GetModel(), SHAPE_T::SEGMENT );
1415
1416 line->SetStart( pts[jj - 1] );
1417 line->SetEnd( pts[jj] );
1418 line->SetWidth( width );
1419 line->SetLayer( layer );
1420 lines_to_add.insert( line );
1421 }
1422
1423 if( pts.size() > 1 )
1424 {
1425 PCB_SHAPE *line = new PCB_SHAPE( frame()->GetModel(), SHAPE_T::SEGMENT );
1426
1427 line->SetStart( pts.back() );
1428 line->SetEnd( pts.front() );
1429 line->SetWidth( width );
1430 line->SetLayer( layer );
1431 lines_to_add.insert( line );
1432 }
1433 }
1434
1435 int segmentCount = selection.CountType( PCB_SHAPE_LOCATE_SEGMENT_T ) + lines_to_add.size();
1436
1437 if( aEvent.IsAction( &PCB_ACTIONS::extendLines ) && segmentCount != 2 )
1438 {
1439 frame()->ShowInfoBarMsg( _( "Exactly two lines must be selected to extend them." ) );
1440
1441 for( PCB_SHAPE* line : lines_to_add )
1442 delete line;
1443
1444 return 0;
1445 }
1446 else if( segmentCount < 2 )
1447 {
1448 frame()->ShowInfoBarMsg( _( "A shape with at least two lines must be selected." ) );
1449
1450 for( PCB_SHAPE* line : lines_to_add )
1451 delete line;
1452
1453 return 0;
1454 }
1455
1456 BOARD_COMMIT commit( this );
1457
1458 // Items created like lines from a rectangle
1459 for( PCB_SHAPE* item : lines_to_add )
1460 {
1461 commit.Add( item );
1462 selection.Add( item );
1463 }
1464
1465 // Remove items like rectangles that we decomposed into lines
1466 for( PCB_SHAPE* item : items_to_remove )
1467 {
1468 selection.Remove( item );
1469 commit.Remove( item );
1470 }
1471
1472 for( EDA_ITEM* item : selection )
1473 item->ClearFlags( STRUCT_DELETED );
1474
1475 // List of thing to select at the end of the operation
1476 // (doing it as we go will invalidate the iterator)
1477 std::vector<BOARD_ITEM*> items_to_select_on_success;
1478
1479 // And same for items to deselect
1480 std::vector<BOARD_ITEM*> items_to_deselect_on_success;
1481
1482 // Handle modifications to existing items by the routine
1483 // How to deal with this depends on whether we're in the footprint editor or not
1484 // and whether the item was conjured up by decomposing a polygon or rectangle
1485 auto item_modification_handler =
1486 [&]( BOARD_ITEM& aItem )
1487 {
1488 // If the item was "conjured up" it will be added later separately
1489 if( !alg::contains( lines_to_add, &aItem ) )
1490 {
1491 commit.Modify( &aItem );
1492 items_to_select_on_success.push_back( &aItem );
1493 }
1494 };
1495
1496 bool any_items_created = !lines_to_add.empty();
1497 auto item_creation_handler =
1498 [&]( std::unique_ptr<BOARD_ITEM> aItem )
1499 {
1500 any_items_created = true;
1501 items_to_select_on_success.push_back( aItem.get() );
1502 commit.Add( aItem.release() );
1503 };
1504
1505 bool any_items_removed = !items_to_remove.empty();
1506 auto item_removal_handler =
1507 [&]( BOARD_ITEM& aItem )
1508 {
1509 aItem.SetFlags( STRUCT_DELETED );
1510 any_items_removed = true;
1511 items_to_deselect_on_success.push_back( &aItem );
1512 commit.Remove( &aItem );
1513 };
1514
1515 // Combine these callbacks into a CHANGE_HANDLER to inject in the ROUTINE
1517 item_creation_handler, item_modification_handler, item_removal_handler );
1518
1519 // Construct an appropriate tool
1520 std::unique_ptr<PAIRWISE_LINE_ROUTINE> pairwise_line_routine;
1521
1522 if( aEvent.IsAction( &PCB_ACTIONS::filletLines ) )
1523 {
1524 static int s_filletRadius = pcbIUScale.mmToIU( 1 );
1525 std::optional<int> filletRadiusIU =
1526 GetRadiusParams( *frame(), _( "Fillet Lines" ), s_filletRadius );
1527
1528 if( filletRadiusIU.has_value() )
1529 {
1530 pairwise_line_routine = std::make_unique<LINE_FILLET_ROUTINE>(
1531 frame()->GetModel(), change_handler, *filletRadiusIU );
1532 }
1533 }
1534 else if( aEvent.IsAction( &PCB_ACTIONS::dogboneCorners ) )
1535 {
1536 std::optional<DOGBONE_CORNER_ROUTINE::PARAMETERS> dogboneParams =
1537 GetDogboneParams( *frame() );
1538
1539 if( dogboneParams.has_value() )
1540 {
1541 pairwise_line_routine = std::make_unique<DOGBONE_CORNER_ROUTINE>(
1542 frame()->GetModel(), change_handler, *dogboneParams );
1543 }
1544 }
1545 else if( aEvent.IsAction( &PCB_ACTIONS::chamferLines ) )
1546 {
1547 std::optional<CHAMFER_PARAMS> chamfer_params = GetChamferParams( *frame() );
1548
1549 if( chamfer_params.has_value() )
1550 {
1551 pairwise_line_routine = std::make_unique<LINE_CHAMFER_ROUTINE>( frame()->GetModel(),
1552 change_handler,
1553 *chamfer_params );
1554 }
1555 }
1556 else if( aEvent.IsAction( &PCB_ACTIONS::extendLines ) )
1557 {
1558 pairwise_line_routine = std::make_unique<LINE_EXTENSION_ROUTINE>( frame()->GetModel(),
1559 change_handler );
1560 }
1561
1562 if( !pairwise_line_routine )
1563 {
1564 // Didn't construct any mofication routine - user must have cancelled
1565 commit.Revert();
1566 return 0;
1567 }
1568
1569 // Apply the tool to every line pair
1570 alg::for_all_pairs( selection.begin(), selection.end(),
1571 [&]( EDA_ITEM* a, EDA_ITEM* b )
1572 {
1573 if( ( a->GetFlags() & STRUCT_DELETED ) == 0
1574 && ( b->GetFlags() & STRUCT_DELETED ) == 0 )
1575 {
1576 PCB_SHAPE* line_a = static_cast<PCB_SHAPE*>( a );
1577 PCB_SHAPE* line_b = static_cast<PCB_SHAPE*>( b );
1578
1579 pairwise_line_routine->ProcessLinePair( *line_a, *line_b );
1580 }
1581 } );
1582
1583 // Select added and modified items
1584 for( BOARD_ITEM* item : items_to_select_on_success )
1585 m_selectionTool->AddItemToSel( item, true );
1586
1587 // Deselect removed items
1588 for( BOARD_ITEM* item : items_to_deselect_on_success )
1589 m_selectionTool->RemoveItemFromSel( item, true );
1590
1591 if( any_items_removed )
1592 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1593
1594 if( any_items_created )
1595 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1596
1597 // Notify other tools of the changes
1598 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1599
1600 commit.Push( pairwise_line_routine->GetCommitDescription() );
1601
1602 if( const std::optional<wxString> msg = pairwise_line_routine->GetStatusMessage( segmentCount ) )
1603 frame()->ShowInfoBarMsg( *msg );
1604
1605 return 0;
1606}
1607
1608
1610{
1612 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1613 {
1614 std::vector<VECTOR2I> pts;
1615
1616 // Iterate from the back so we don't have to worry about removals.
1617 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1618 {
1619 BOARD_ITEM* item = aCollector[i];
1620
1621 if( !item->IsType( { PCB_SHAPE_LOCATE_POLY_T, PCB_ZONE_T } ) )
1622 aCollector.Remove( item );
1623
1624 if( ZONE* zone = dyn_cast<ZONE*>( item ) )
1625 {
1626 if( zone->IsTeardropArea() )
1627 aCollector.Remove( item );
1628 }
1629 }
1630 },
1631 true /* prompt user regarding locked items */ );
1632
1633 // Store last used value
1634 static int s_toleranceValue = pcbIUScale.mmToIU( 3 );
1635
1636 WX_UNIT_ENTRY_DIALOG dlg( frame(), _( "Simplify Shapes" ), _( "Tolerance value:" ),
1637 s_toleranceValue );
1638
1639 if( dlg.ShowModal() == wxID_CANCEL )
1640 return 0;
1641
1642 s_toleranceValue = dlg.GetValue();
1643
1644 if( s_toleranceValue <= 0 )
1645 return 0;
1646
1647 BOARD_COMMIT commit{ this };
1648
1649 std::vector<PCB_SHAPE*> shapeList;
1650
1651 for( EDA_ITEM* item : selection )
1652 {
1653 commit.Modify( item );
1654
1655 if( PCB_SHAPE* shape = dyn_cast<PCB_SHAPE*>( item ) )
1656 {
1657 SHAPE_POLY_SET& poly = shape->GetPolyShape();
1658
1659 poly.SimplifyOutlines( s_toleranceValue );
1660 }
1661
1662 if( ZONE* zone = dyn_cast<ZONE*>( item ) )
1663 {
1664 SHAPE_POLY_SET* poly = zone->Outline();
1665
1666 poly->SimplifyOutlines( s_toleranceValue );
1667 }
1668 }
1669
1670 commit.Push( _( "Simplify Polygons" ) );
1671
1672 // Notify other tools of the changes
1673 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1674
1675 return 0;
1676}
1677
1678
1680{
1682 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1683 {
1684 std::vector<VECTOR2I> pts;
1685
1686 // Iterate from the back so we don't have to worry about removals.
1687 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1688 {
1689 BOARD_ITEM* item = aCollector[i];
1690
1691 // We've converted the polygon and rectangle to segments, so drop everything
1692 // that isn't a segment at this point
1693 if( !item->IsType( { PCB_SHAPE_LOCATE_SEGMENT_T, PCB_SHAPE_LOCATE_ARC_T,
1694 PCB_SHAPE_LOCATE_BEZIER_T } ) )
1695 {
1696 aCollector.Remove( item );
1697 }
1698 }
1699 },
1700 true /* prompt user regarding locked items */ );
1701
1702 // Store last used value
1703 static int s_toleranceValue = pcbIUScale.mmToIU( 3 );
1704
1705 WX_UNIT_ENTRY_DIALOG dlg( frame(), _( "Heal Shapes" ), _( "Tolerance value:" ),
1706 s_toleranceValue );
1707
1708 if( dlg.ShowModal() == wxID_CANCEL )
1709 return 0;
1710
1711 s_toleranceValue = dlg.GetValue();
1712
1713 if( s_toleranceValue <= 0 )
1714 return 0;
1715
1716 BOARD_COMMIT commit{ this };
1717
1718 std::vector<PCB_SHAPE*> shapeList;
1719 std::vector<std::unique_ptr<PCB_SHAPE>> newShapes;
1720
1721 for( EDA_ITEM* item : selection )
1722 {
1723 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item ) )
1724 {
1725 shapeList.push_back( shape );
1726 commit.Modify( shape );
1727 }
1728 }
1729
1730 ConnectBoardShapes( shapeList, newShapes, s_toleranceValue );
1731
1732 std::vector<PCB_SHAPE*> items_to_select;
1733
1734 for( std::unique_ptr<PCB_SHAPE>& ptr : newShapes )
1735 {
1736 PCB_SHAPE* shape = ptr.release();
1737
1738 commit.Add( shape );
1739 items_to_select.push_back( shape );
1740 }
1741
1742 commit.Push( _( "Heal Shapes" ) );
1743
1744 // Select added items
1745 for( PCB_SHAPE* item : items_to_select )
1746 m_selectionTool->AddItemToSel( item, true );
1747
1748 if( items_to_select.size() > 0 )
1749 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1750
1751 // Notify other tools of the changes
1752 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1753
1754 return 0;
1755}
1756
1757
1759{
1761 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1762 {
1763 // Iterate from the back so we don't have to worry about removals.
1764 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1765 {
1766 BOARD_ITEM* item = aCollector[i];
1767
1768 if( !item->IsType( {
1769 PCB_SHAPE_LOCATE_POLY_T,
1770 PCB_SHAPE_LOCATE_RECT_T,
1771 } ) )
1772 {
1773 aCollector.Remove( item );
1774 }
1775 }
1776 },
1777 true /* prompt user regarding locked items */ );
1778
1779 const EDA_ITEM* const last_item = selection.GetLastAddedItem();
1780
1781 // Gather or construct polygon source shapes to merge
1782 std::vector<PCB_SHAPE*> items_to_process;
1783
1784 for( EDA_ITEM* item : selection )
1785 {
1786 items_to_process.push_back( static_cast<PCB_SHAPE*>( item ) );
1787
1788 // put the last one in the selection at the front of the vector
1789 // so it can be used as the property donor and as the basis for the
1790 // boolean operation
1791 if( item == last_item )
1792 std::swap( items_to_process.back(), items_to_process.front() );
1793 }
1794
1795 BOARD_COMMIT commit{ this };
1796
1797 // Handle modifications to existing items by the routine
1798 auto item_modification_handler =
1799 [&]( BOARD_ITEM& aItem )
1800 {
1801 commit.Modify( &aItem );
1802 };
1803
1804 std::vector<BOARD_ITEM*> items_to_select_on_success;
1805
1806 auto item_creation_handler =
1807 [&]( std::unique_ptr<BOARD_ITEM> aItem )
1808 {
1809 items_to_select_on_success.push_back( aItem.get() );
1810 commit.Add( aItem.release() );
1811 };
1812
1813 auto item_removal_handler =
1814 [&]( BOARD_ITEM& aItem )
1815 {
1816 commit.Remove( &aItem );
1817 };
1818
1819 // Combine these callbacks into a CHANGE_HANDLER to inject in the ROUTINE
1821 item_creation_handler, item_modification_handler, item_removal_handler );
1822
1823 // Construct an appropriate routine
1824 std::unique_ptr<POLYGON_BOOLEAN_ROUTINE> boolean_routine;
1825 if( aEvent.IsAction( &PCB_ACTIONS::mergePolygons ) )
1826 {
1827 boolean_routine = std::make_unique<POLYGON_MERGE_ROUTINE>( frame()->GetModel(),
1828 change_handler );
1829 }
1830 else if( aEvent.IsAction( &PCB_ACTIONS::subtractPolygons ) )
1831 {
1832 boolean_routine = std::make_unique<POLYGON_SUBTRACT_ROUTINE>( frame()->GetModel(),
1833 change_handler );
1834 }
1835 else if( aEvent.IsAction( &PCB_ACTIONS::intersectPolygons ) )
1836 {
1837 boolean_routine = std::make_unique<POLYGON_INTERSECT_ROUTINE>( frame()->GetModel(),
1838 change_handler );
1839 }
1840 else
1841 {
1842 wxASSERT_MSG( false, "Could not find a polygon routine for this action" );
1843 return 0;
1844 }
1845
1846 // Perform the operation on each polygon
1847 for( PCB_SHAPE* shape : items_to_process )
1848 boolean_routine->ProcessShape( *shape );
1849
1850 boolean_routine->Finalize();
1851
1852 // Select new items
1853 for( BOARD_ITEM* item : items_to_select_on_success )
1854 m_selectionTool->AddItemToSel( item, true );
1855
1856 // Notify other tools of the changes
1857 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1858
1859 commit.Push( boolean_routine->GetCommitDescription() );
1860
1861 if( const std::optional<wxString> msg = boolean_routine->GetStatusMessage() )
1862 frame()->ShowInfoBarMsg( *msg );
1863
1864 return 0;
1865}
1866
1867
1869{
1870 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1872 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1873 {
1874 } );
1875
1876 // Tracks & vias are treated in a special way:
1877 if( ( SELECTION_CONDITIONS::OnlyTypes( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } ) )( selection ) )
1878 {
1879 DIALOG_TRACK_VIA_PROPERTIES dlg( editFrame, selection );
1880 dlg.ShowQuasiModal(); // QuasiModal required for NET_SELECTOR
1881 }
1882 else if( ( SELECTION_CONDITIONS::OnlyTypes( { PCB_TABLECELL_T } ) )( selection ) )
1883 {
1884 std::vector<PCB_TABLECELL*> cells;
1885
1886 for( EDA_ITEM* item : selection.Items() )
1887 cells.push_back( static_cast<PCB_TABLECELL*>( item ) );
1888
1889 DIALOG_TABLECELL_PROPERTIES dlg( editFrame, cells );
1890
1891 dlg.ShowModal();
1892
1894 {
1895 PCB_TABLE* table = static_cast<PCB_TABLE*>( cells[0]->GetParent() );
1896 DIALOG_TABLE_PROPERTIES tableDlg( frame(), table );
1897
1898 tableDlg.ShowQuasiModal(); // Scintilla's auto-complete requires quasiModal
1899 }
1900 }
1901 else if( selection.Size() == 1 && selection.Front()->IsBOARD_ITEM() )
1902 {
1903 // Display properties dialog
1904 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
1905
1906 // Do not handle undo buffer, it is done by the properties dialogs
1907 editFrame->OnEditItemRequest( item );
1908
1909 // Notify other tools of the changes
1911 }
1912 else if( selection.Size() == 0 && getView()->IsLayerVisible( LAYER_DRAWINGSHEET ) )
1913 {
1914 DS_PROXY_VIEW_ITEM* ds = editFrame->GetCanvas()->GetDrawingSheet();
1915 VECTOR2D cursorPos = getViewControls()->GetCursorPosition( false );
1916
1917 if( ds && ds->HitTestDrawingSheetItems( getView(), cursorPos ) )
1919 else
1921 }
1922
1923 if( selection.IsHover() )
1924 {
1926 }
1927 else
1928 {
1929 // Check for items becoming invisible and drop them from the selection.
1930
1931 PCB_SELECTION selCopy = selection;
1932 LSET visible = editFrame->GetBoard()->GetVisibleLayers();
1933
1934 for( EDA_ITEM* eda_item : selCopy )
1935 {
1936 if( !eda_item->IsBOARD_ITEM() )
1937 continue;
1938
1939 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( eda_item );
1940
1941 if( !( item->GetLayerSet() & visible ).any() )
1943 }
1944 }
1945
1946 if( m_dragging )
1947 {
1950 }
1951
1952 return 0;
1953}
1954
1955
1956int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
1957{
1958 if( isRouterActive() )
1959 {
1960 wxBell();
1961 return 0;
1962 }
1963
1964 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1965 BOARD_COMMIT localCommit( this );
1966 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
1967
1968 if( !commit )
1969 commit = &localCommit;
1970
1971 // Be sure that there is at least one item that we can modify. If nothing was selected before,
1972 // try looking for the stuff under mouse cursor (i.e. KiCad old-style hover selection)
1974 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1975 {
1976 sTool->FilterCollectorForHierarchy( aCollector, true );
1977 sTool->FilterCollectorForMarkers( aCollector );
1978 sTool->FilterCollectorForTableCells( aCollector );
1979 },
1980 // Prompt user regarding locked items if in board editor and in free-pad-mode (if
1981 // we're not in free-pad mode we delay this until the second RequestSelection()).
1982 !m_dragging && frame()->GetPcbNewSettings()->m_AllowFreePads && !m_isFootprintEditor );
1983
1984 if( selection.Empty() )
1985 return 0;
1986
1987 std::optional<VECTOR2I> oldRefPt;
1988 bool is_hover = selection.IsHover(); // N.B. This must be saved before the second
1989 // call to RequestSelection() below
1990
1991 if( selection.HasReferencePoint() )
1992 oldRefPt = selection.GetReferencePoint();
1993
1994 // Now filter out pads if not in free pads mode. We cannot do this in the first
1995 // RequestSelection() as we need the reference point when a pad is the selection front.
1996 if( !m_isFootprintEditor && !frame()->GetPcbNewSettings()->m_AllowFreePads )
1997 {
1998 selection = m_selectionTool->RequestSelection(
1999 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2000 {
2001 sTool->FilterCollectorForMarkers( aCollector );
2002 sTool->FilterCollectorForHierarchy( aCollector, true );
2003 sTool->FilterCollectorForFreePads( aCollector );
2004 sTool->FilterCollectorForTableCells( aCollector );
2005 },
2006 !m_dragging /* prompt user regarding locked items */ );
2007 }
2008
2009 // Did we filter everything out? If so, don't try to operate further
2010 if( selection.Empty() )
2011 return 0;
2012
2013 if( selection.Size() == 1 && dynamic_cast<PCB_TEXTBOX*>( selection.Front() ) )
2014 {
2015 selection.SetReferencePoint( static_cast<PCB_TEXTBOX*>( selection.Front() )->GetCenter() );
2016 }
2017 else
2018 {
2019 updateModificationPoint( selection );
2020 }
2021
2022 VECTOR2I refPt = selection.GetReferencePoint();
2023 EDA_ANGLE rotateAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *editFrame, aEvent );
2024
2025 if( frame()->GetCanvas()->GetView()->GetGAL()->IsFlippedX() )
2026 rotateAngle = -rotateAngle;
2027
2028 // Calculate view bounding box
2029 BOX2I viewBBox = selection.Front()->ViewBBox();
2030
2031 for( EDA_ITEM* item : selection )
2032 viewBBox.Merge( item->ViewBBox() );
2033
2034 // Check if the view bounding box will go out of bounds
2035 VECTOR2D rotPos = viewBBox.GetPosition();
2036 VECTOR2D rotEnd = viewBBox.GetEnd();
2037
2038 RotatePoint( &rotPos.x, &rotPos.y, refPt.x, refPt.y, rotateAngle );
2039 RotatePoint( &rotEnd.x, &rotEnd.y, refPt.x, refPt.y, rotateAngle );
2040
2041 typedef std::numeric_limits<int> coord_limits;
2042
2043 int max = coord_limits::max() - COORDS_PADDING;
2044 int min = -max;
2045
2046 bool outOfBounds = rotPos.x < min || rotPos.x > max || rotPos.y < min || rotPos.y > max
2047 || rotEnd.x < min || rotEnd.x > max || rotEnd.y < min || rotEnd.y > max;
2048
2049 if( !outOfBounds )
2050 {
2051 for( EDA_ITEM* item : selection )
2052 {
2053 if( !item->IsNew() && !item->IsMoving() )
2054 commit->Modify( item );
2055
2056 if( item->IsBOARD_ITEM() )
2057 {
2058 BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item );
2059
2060 board_item->Rotate( refPt, rotateAngle );
2061 board_item->Normalize();
2062
2063 if( board_item->Type() == PCB_FOOTPRINT_T )
2064 static_cast<FOOTPRINT*>( board_item )->InvalidateComponentClassCache();
2065 }
2066 }
2067
2068 if( !localCommit.Empty() )
2069 localCommit.Push( _( "Rotate" ) );
2070
2071 if( is_hover && !m_dragging )
2073
2075
2076 if( m_dragging )
2077 {
2080 }
2081 }
2082
2083 // Restore the old reference so any mouse dragging that occurs doesn't make the selection jump
2084 // to this now invalid reference
2085 if( oldRefPt )
2086 selection.SetReferencePoint( *oldRefPt );
2087 else
2088 selection.ClearReferencePoint();
2089
2090 return 0;
2091}
2092
2093
2097static void mirrorPad( PAD& aPad, const VECTOR2I& aMirrorPoint, FLIP_DIRECTION aFlipDirection )
2098{
2099 // TODO(JE) padstacks
2101 aPad.FlipPrimitives( aFlipDirection );
2102
2103 VECTOR2I tmpPt = aPad.GetPosition();
2104 MIRROR( tmpPt, aMirrorPoint, aFlipDirection );
2105 aPad.SetPosition( tmpPt );
2106
2107 tmpPt = aPad.GetOffset( PADSTACK::ALL_LAYERS );
2108 MIRROR( tmpPt, VECTOR2I{ 0, 0 }, aFlipDirection );
2109 aPad.SetOffset( PADSTACK::ALL_LAYERS, tmpPt );
2110
2111 VECTOR2I tmpz = aPad.GetDelta( PADSTACK::ALL_LAYERS );
2112 MIRROR( tmpz, VECTOR2I{ 0, 0 }, aFlipDirection );
2113 aPad.SetDelta( PADSTACK::ALL_LAYERS, tmpz );
2114
2115 aPad.SetOrientation( -aPad.GetOrientation() );
2116}
2117
2118
2119const std::vector<KICAD_T> EDIT_TOOL::MirrorableItems = {
2122 PCB_TEXT_T,
2124 PCB_ZONE_T,
2125 PCB_PAD_T,
2127 PCB_ARC_T,
2128 PCB_VIA_T,
2130};
2131
2132int EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
2133{
2134 if( isRouterActive() )
2135 {
2136 wxBell();
2137 return 0;
2138 }
2139
2140 BOARD_COMMIT localCommit( this );
2141 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
2142
2143 if( !commit )
2144 commit = &localCommit;
2145
2147 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2148 {
2149 sTool->FilterCollectorForMarkers( aCollector );
2150 sTool->FilterCollectorForHierarchy( aCollector, true );
2151 sTool->FilterCollectorForFreePads( aCollector );
2152 },
2153 !m_dragging /* prompt user regarding locked items */ );
2154
2155 if( selection.Empty() )
2156 return 0;
2157
2158 updateModificationPoint( selection );
2159 VECTOR2I mirrorPoint = selection.GetReferencePoint();
2160
2161 FLIP_DIRECTION flipDirection = aEvent.IsAction( &PCB_ACTIONS::mirrorV )
2162 ? FLIP_DIRECTION::TOP_BOTTOM
2163 : FLIP_DIRECTION::LEFT_RIGHT;
2164
2165 std::vector<EDA_ITEM*> items;
2166
2167 for( EDA_ITEM* item : selection )
2168 {
2169 if( item->Type() == PCB_GROUP_T )
2170 {
2171 static_cast<PCB_GROUP*>( item )->RunOnChildren(
2172 [&]( BOARD_ITEM* descendant )
2173 {
2174 items.push_back( descendant );
2175 },
2176 RECURSE_MODE::RECURSE );
2177 }
2178 else
2179 {
2180 items.push_back( item );
2181 }
2182 }
2183
2184 for( EDA_ITEM* item : items )
2185 {
2186 if( !item->IsType( MirrorableItems ) )
2187 continue;
2188
2189 if( !item->IsNew() && !item->IsMoving() )
2190 commit->Modify( item );
2191
2192 // modify each object as necessary
2193 switch( item->Type() )
2194 {
2195 case PCB_SHAPE_T:
2196 static_cast<PCB_SHAPE*>( item )->Mirror( mirrorPoint, flipDirection );
2197 break;
2198
2199 case PCB_ZONE_T:
2200 static_cast<ZONE*>( item )->Mirror( mirrorPoint, flipDirection );
2201 break;
2202
2203 case PCB_FIELD_T:
2204 case PCB_TEXT_T:
2205 static_cast<PCB_TEXT*>( item )->Mirror( mirrorPoint, flipDirection );
2206 break;
2207
2208 case PCB_TEXTBOX_T:
2209 static_cast<PCB_TEXTBOX*>( item )->Mirror( mirrorPoint, flipDirection );
2210 break;
2211
2212 case PCB_TABLE_T:
2213 // JEY TODO: tables
2214 break;
2215
2216 case PCB_PAD_T:
2217 mirrorPad( *static_cast<PAD*>( item ), mirrorPoint, flipDirection );
2218 break;
2219
2220 case PCB_TRACE_T:
2221 case PCB_ARC_T:
2222 case PCB_VIA_T:
2223 static_cast<PCB_TRACK*>( item )->Mirror( mirrorPoint, flipDirection );
2224 break;
2225
2226 case PCB_GENERATOR_T:
2227 static_cast<PCB_GENERATOR*>( item )->Mirror( mirrorPoint, flipDirection );
2228 break;
2229
2230 default:
2231 // it's likely the commit object is wrong if you get here
2232 UNIMPLEMENTED_FOR( item->GetClass() );
2233 }
2234 }
2235
2236 if( !localCommit.Empty() )
2237 localCommit.Push( _( "Mirror" ) );
2238
2239 if( selection.IsHover() && !m_dragging )
2241
2243
2244 if( m_dragging )
2245 {
2248 }
2249
2250 return 0;
2251}
2252
2253
2255{
2256 if( isRouterActive() )
2257 {
2258 wxBell();
2259 return 0;
2260 }
2261
2262 BOARD_COMMIT localCommit( this );
2263 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
2264
2265 if( !commit )
2266 commit = &localCommit;
2267
2269 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2270 {
2271 sTool->FilterCollectorForHierarchy( aCollector, true );
2272 },
2273 !m_dragging /* prompt user regarding locked items */ );
2274
2275 if( selection.Empty() )
2276 return 0;
2277
2278 auto setJustify =
2279 [&]( EDA_TEXT* aTextItem )
2280 {
2281 if( aEvent.Matches( ACTIONS::leftJustify.MakeEvent() ) )
2282 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
2283 else if( aEvent.Matches( ACTIONS::centerJustify.MakeEvent() ) )
2284 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
2285 else
2286 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
2287 };
2288
2289 for( EDA_ITEM* item : selection )
2290 {
2291 if( item->Type() == PCB_FIELD_T || item->Type() == PCB_TEXT_T )
2292 {
2293 if( !item->IsNew() && !item->IsMoving() )
2294 commit->Modify( item );
2295
2296 setJustify( static_cast<PCB_TEXT*>( item ) );
2297 }
2298 else if( item->Type() == PCB_TEXTBOX_T )
2299 {
2300 if( !item->IsNew() && !item->IsMoving() )
2301 commit->Modify( item );
2302
2303 setJustify( static_cast<PCB_TEXTBOX*>( item ) );
2304 }
2305 }
2306
2307 if( !localCommit.Empty() )
2308 {
2309 if( aEvent.Matches( ACTIONS::leftJustify.MakeEvent() ) )
2310 localCommit.Push( _( "Left Justify" ) );
2311 else if( aEvent.Matches( ACTIONS::centerJustify.MakeEvent() ) )
2312 localCommit.Push( _( "Center Justify" ) );
2313 else
2314 localCommit.Push( _( "Right Justify" ) );
2315 }
2316
2317 if( selection.IsHover() && !m_dragging )
2319
2321
2322 if( m_dragging )
2323 {
2326 }
2327
2328 return 0;
2329}
2330
2331
2332int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent )
2333{
2334 if( isRouterActive() )
2335 {
2336 wxBell();
2337 return 0;
2338 }
2339
2340 BOARD_COMMIT localCommit( this );
2341 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
2342
2343 if( !commit )
2344 commit = &localCommit;
2345
2347 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2348 {
2349 sTool->FilterCollectorForMarkers( aCollector );
2350 sTool->FilterCollectorForHierarchy( aCollector, true );
2351 sTool->FilterCollectorForFreePads( aCollector );
2352 sTool->FilterCollectorForTableCells( aCollector );
2353 },
2354 !m_dragging /* prompt user regarding locked items */ );
2355
2356 if( selection.Empty() )
2357 return 0;
2358
2359 std::optional<VECTOR2I> oldRefPt;
2360
2361 if( selection.HasReferencePoint() )
2362 oldRefPt = selection.GetReferencePoint();
2363
2364 updateModificationPoint( selection );
2365
2366 // Flip around the anchor for footprints, and the bounding box center for board items
2367 VECTOR2I refPt = IsFootprintEditor() ? VECTOR2I( 0, 0 ) : selection.GetCenter();
2368
2369 // If only one item selected, flip around the selection or item anchor point (instead
2370 // of the bounding box center) to avoid moving the item anchor
2371 // but only if the item is not a PCB_SHAPE with SHAPE_T::RECTANGLE shape, because
2372 // for this shape the flip transform swap start and end coordinates and move the shape.
2373 // So using the center of the shape is better (the shape does not move)
2374 if( selection.GetSize() == 1 )
2375 {
2376 PCB_SHAPE* item = dynamic_cast<PCB_SHAPE*>( selection.GetItem( 0 ) );
2377
2378 if( !item || item->GetShape() != SHAPE_T::RECTANGLE )
2379 refPt = selection.GetReferencePoint();
2380 }
2381
2382 const FLIP_DIRECTION flipDirection = frame()->GetPcbNewSettings()->m_FlipDirection;
2383
2384 for( EDA_ITEM* item : selection )
2385 {
2386 if( !item->IsBOARD_ITEM() )
2387 continue;
2388
2389 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
2390
2391 if( !boardItem->IsNew() && !boardItem->IsMoving() )
2392 commit->Modify( boardItem );
2393
2394 boardItem->Flip( refPt, flipDirection );
2395 boardItem->Normalize();
2396
2397 if( boardItem->Type() == PCB_FOOTPRINT_T )
2398 static_cast<FOOTPRINT*>( boardItem )->InvalidateComponentClassCache();
2399 }
2400
2401 if( !localCommit.Empty() )
2402 localCommit.Push( _( "Change Side / Flip" ) );
2403
2404 if( selection.IsHover() && !m_dragging )
2406
2408
2409 if( m_dragging )
2410 {
2413 }
2414
2415 // Restore the old reference so any mouse dragging that occurs doesn't make the selection jump
2416 // to this now invalid reference
2417 if( oldRefPt )
2418 selection.SetReferencePoint( *oldRefPt );
2419 else
2420 selection.ClearReferencePoint();
2421
2422 return 0;
2423}
2424
2425
2427 std::unordered_set<BOARD_ITEM*>& children )
2428{
2429 wxASSERT( item->Type() == PCB_GROUP_T || item->Type() == PCB_GENERATOR_T );
2430
2431 std::unordered_set<BOARD_ITEM*>& childItems = static_cast<PCB_GROUP*>( item )->GetItems();
2432
2433 for( BOARD_ITEM* childItem : childItems )
2434 {
2435 children.insert( childItem );
2436
2437 if( childItem->Type() == PCB_GROUP_T || childItem->Type() == PCB_GENERATOR_T )
2438 getChildItemsOfGroupsAndGenerators( childItem, children );
2439 }
2440}
2441
2442
2443void EDIT_TOOL::removeNonRootItems( std::unordered_set<EDA_ITEM*>& items )
2444{
2445 auto itr = items.begin();
2446
2447 while( itr != items.end() )
2448 {
2449 EDA_ITEM* item = *itr;
2450
2451 if( item->IsBOARD_ITEM() )
2452 {
2453 BOARD_ITEM* curItem = static_cast<BOARD_ITEM*>( item );
2454
2455 if( curItem->Type() == PCB_GROUP_T || curItem->Type() == PCB_GENERATOR_T )
2456 {
2457 std::unordered_set<BOARD_ITEM*> childItems;
2458 getChildItemsOfGroupsAndGenerators( curItem, childItems );
2459
2460 std::for_each( childItems.begin(), childItems.end(),
2461 [&]( auto eraseItem )
2462 {
2463 items.erase( eraseItem );
2464 } );
2465 }
2466 }
2467
2468 ++itr;
2469 }
2470}
2471
2472
2473void EDIT_TOOL::DeleteItems( const PCB_SELECTION& aItems, bool aIsCut )
2474{
2475 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2476 BOARD_COMMIT commit( this );
2477
2478 // As we are about to remove items, they have to be removed from the selection first
2480
2481 // Only delete items that are the root of a selected set (e.g. only delete grouped / generated
2482 // items from the parent item, not individually) - issue #17527
2483 std::unordered_set<EDA_ITEM*> rootItems( aItems.begin(), aItems.end() );
2484 removeNonRootItems( rootItems );
2485
2486 int itemsDeleted = 0;
2487 int fieldsHidden = 0;
2488 int fieldsAlreadyHidden = 0;
2489
2490 for( EDA_ITEM* item : rootItems )
2491 {
2492 if( !item->IsBOARD_ITEM() )
2493 continue;
2494
2495 BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item );
2496 FOOTPRINT* parentFP = board_item->GetParentFootprint();
2497
2498 if( board_item->GetParentGroup() )
2499 commit.Stage( board_item, CHT_UNGROUP );
2500
2501 switch( item->Type() )
2502 {
2503 case PCB_FIELD_T:
2504 {
2505 PCB_FIELD* field = static_cast<PCB_FIELD*>( board_item );
2506
2507 wxASSERT( parentFP );
2508 commit.Modify( parentFP );
2509
2510 if( field->IsVisible() )
2511 {
2512 field->SetVisible( false );
2513 fieldsHidden++;
2514 }
2515 else
2516 {
2517 fieldsAlreadyHidden++;
2518 }
2519
2520 getView()->Update( parentFP );
2521 break;
2522 }
2523
2524 case PCB_TEXT_T:
2525 if( parentFP )
2526 {
2527 commit.Modify( parentFP );
2528 parentFP->Delete( board_item );
2529 getView()->Update( parentFP );
2530 }
2531 else
2532 {
2533 commit.Remove( board_item );
2534 }
2535
2536 itemsDeleted++;
2537 break;
2538
2539 case PCB_SHAPE_T:
2540 case PCB_TEXTBOX_T:
2541 case PCB_TABLE_T:
2543 case PCB_DIMENSION_T:
2544 case PCB_DIM_ALIGNED_T:
2545 case PCB_DIM_LEADER_T:
2546 case PCB_DIM_CENTER_T:
2547 case PCB_DIM_RADIAL_T:
2549 commit.Remove( board_item );
2550 itemsDeleted++;
2551 break;
2552
2553 case PCB_TABLECELL_T:
2554 // Clear contents of table cell
2555 commit.Modify( board_item );
2556 static_cast<PCB_TABLECELL*>( board_item )->SetText( wxEmptyString );
2557 itemsDeleted++;
2558 break;
2559
2560 case PCB_GROUP_T:
2561 board_item->RunOnChildren(
2562 [&commit]( BOARD_ITEM* aItem )
2563 {
2564 commit.Stage( aItem, CHT_UNGROUP );
2565 },
2566 RECURSE_MODE::RECURSE );
2567
2568 board_item->RunOnChildren(
2569 [&commit]( BOARD_ITEM* aItem )
2570 {
2571 commit.Remove( aItem );
2572 },
2573 RECURSE_MODE::RECURSE );
2574
2575 commit.Remove( board_item );
2576 itemsDeleted++;
2577 break;
2578
2579 case PCB_PAD_T:
2580 if( IsFootprintEditor() || frame()->GetPcbNewSettings()->m_AllowFreePads )
2581 {
2582 commit.Modify( parentFP );
2583 getView()->Remove( board_item );
2584 parentFP->Remove( board_item );
2585 itemsDeleted++;
2586 }
2587
2588 break;
2589
2590 case PCB_ZONE_T:
2591 // We process the zones special so that cutouts can be deleted when the delete
2592 // tool is called from inside a cutout when the zone is selected.
2593 // Only interact with cutouts when deleting and a single item is selected
2594 if( !aIsCut && aItems.GetSize() == 1 )
2595 {
2597 ZONE* zone = static_cast<ZONE*>( board_item );
2598
2599 int outlineIdx, holeIdx;
2600
2601 if( zone->HitTestCutout( curPos, &outlineIdx, &holeIdx ) )
2602 {
2603 // Remove the cutout
2604 commit.Modify( zone );
2605 zone->RemoveCutout( outlineIdx, holeIdx );
2606 zone->UnFill();
2607
2608 // Update the display
2609 zone->HatchBorder();
2610 canvas()->Refresh();
2611
2612 // Restore the selection on the original zone
2614
2615 break;
2616 }
2617 }
2618
2619 // Remove the entire zone otherwise
2620 commit.Remove( board_item );
2621 itemsDeleted++;
2622 break;
2623
2624 case PCB_GENERATOR_T:
2625 if( rootItems.size() == 1 )
2626 {
2627 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( board_item );
2628
2630 generator );
2631 }
2632 else
2633 {
2634 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( board_item );
2635
2636 for( BOARD_ITEM* member : generator->GetItems() )
2637 commit.Stage( member, CHT_UNGROUP );
2638
2639 for( BOARD_ITEM* member : generator->GetItems() )
2640 commit.Remove( member );
2641
2642 commit.Remove( board_item );
2643 }
2644
2645 itemsDeleted++;
2646 break;
2647
2648 default:
2649 wxASSERT_MSG( parentFP == nullptr, wxT( "Try to delete an item living in a footprint" ) );
2650 commit.Remove( board_item );
2651 itemsDeleted++;
2652 break;
2653 }
2654 }
2655
2656 // If the entered group has been emptied then leave it.
2657 PCB_GROUP* enteredGroup = m_selectionTool->GetEnteredGroup();
2658
2659 if( enteredGroup && enteredGroup->GetItems().empty() )
2661
2662 if( aIsCut )
2663 {
2664 commit.Push( _( "Cut" ) );
2665 }
2666 else if( itemsDeleted == 0 )
2667 {
2668 if( fieldsHidden == 1 )
2669 commit.Push( _( "Hide Field" ) );
2670 else if( fieldsHidden > 1 )
2671 commit.Push( _( "Hide Fields" ) );
2672 else if( fieldsAlreadyHidden > 0 )
2673 editFrame->ShowInfoBarError( _( "Use the Footprint Properties dialog to remove fields." ) );
2674 }
2675 else
2676 {
2677 commit.Push( _( "Delete" ) );
2678 }
2679}
2680
2681
2682int EDIT_TOOL::Remove( const TOOL_EVENT& aEvent )
2683{
2684 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2685
2686 editFrame->PushTool( aEvent );
2687
2688 std::vector<BOARD_ITEM*> lockedItems;
2689 Activate();
2690
2691 // get a copy instead of reference (as we're going to clear the selection before removing items)
2692 PCB_SELECTION selectionCopy;
2695
2696 // If we are in a "Cut" operation, then the copied selection exists already and we want to
2697 // delete exactly that; no more, no fewer. Any filtering for locked items must be done in
2698 // the copyToClipboard() routine.
2699 if( isCut )
2700 {
2701 selectionCopy = m_selectionTool->GetSelection();
2702 }
2703 else
2704 {
2705 // When not in free-pad mode we normally auto-promote selected pads to their parent
2706 // footprints. But this is probably a little too dangerous for a destructive operation,
2707 // so we just do the promotion but not the deletion (allowing for a second delete to do
2708 // it if that's what the user wanted).
2709 selectionCopy = m_selectionTool->RequestSelection(
2710 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2711 {
2712 } );
2713
2714 size_t beforeFPCount = selectionCopy.CountType( PCB_FOOTPRINT_T );
2715
2717 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2718 {
2719 sTool->FilterCollectorForFreePads( aCollector );
2720 } );
2721
2722 if( !selectionCopy.IsHover()
2723 && m_selectionTool->GetSelection().CountType( PCB_FOOTPRINT_T ) > beforeFPCount )
2724 {
2725 wxBell();
2726 canvas()->Refresh();
2727 editFrame->PopTool( aEvent );
2728 return 0;
2729 }
2730
2731 // In "alternative" mode, we expand selected track items to their full connection.
2732 if( isAlt && ( selectionCopy.HasType( PCB_TRACE_T ) || selectionCopy.HasType( PCB_VIA_T ) ) )
2733 {
2735 }
2736
2737 // Finally run RequestSelection() one more time to find out what user wants to do about
2738 // locked objects.
2739 selectionCopy = m_selectionTool->RequestSelection(
2740 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2741 {
2742 sTool->FilterCollectorForFreePads( aCollector );
2743 },
2744 true /* prompt user regarding locked items */ );
2745 }
2746
2747 DeleteItems( selectionCopy, isCut );
2748 canvas()->Refresh();
2749
2750 editFrame->PopTool( aEvent );
2751 return 0;
2752}
2753
2754
2756{
2757 if( isRouterActive() )
2758 {
2759 wxBell();
2760 return 0;
2761 }
2762
2764 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2765 {
2766 sTool->FilterCollectorForMarkers( aCollector );
2767 sTool->FilterCollectorForHierarchy( aCollector, true );
2768 sTool->FilterCollectorForTableCells( aCollector );
2769 },
2770 true /* prompt user regarding locked items */ );
2771
2772 if( selection.Empty() )
2773 return 0;
2774
2775 VECTOR2I translation;
2776 EDA_ANGLE rotation;
2777 ROTATION_ANCHOR rotationAnchor = selection.Size() > 1 ? ROTATE_AROUND_SEL_CENTER
2779
2780 // TODO: Implement a visible bounding border at the edge
2781 BOX2I sel_box = selection.GetBoundingBox();
2782
2783 DIALOG_MOVE_EXACT dialog( frame(), translation, rotation, rotationAnchor, sel_box );
2784 int ret = dialog.ShowModal();
2785
2786 if( ret == wxID_OK )
2787 {
2788 BOARD_COMMIT commit( this );
2789 EDA_ANGLE angle = rotation;
2790 VECTOR2I rp = selection.GetCenter();
2791 VECTOR2I selCenter( rp.x, rp.y );
2792
2793 // Make sure the rotation is from the right reference point
2794 selCenter += translation;
2795
2796 if( !frame()->GetPcbNewSettings()->m_Display.m_DisplayInvertYAxis )
2797 rotation = -rotation;
2798
2799 for( EDA_ITEM* item : selection )
2800 {
2801 if( !item->IsBOARD_ITEM() )
2802 continue;
2803
2804 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
2805
2806 if( !boardItem->IsNew() )
2807 commit.Modify( boardItem );
2808
2809 if( !boardItem->GetParent() || !boardItem->GetParent()->IsSelected() )
2810 boardItem->Move( translation );
2811
2812 switch( rotationAnchor )
2813 {
2815 boardItem->Rotate( boardItem->GetPosition(), angle );
2816 break;
2818 boardItem->Rotate( selCenter, angle );
2819 break;
2821 boardItem->Rotate( frame()->GetScreen()->m_LocalOrigin, angle );
2822 break;
2824 boardItem->Rotate( board()->GetDesignSettings().GetAuxOrigin(), angle );
2825 break;
2826 }
2827
2828 if( !m_dragging )
2829 getView()->Update( boardItem );
2830 }
2831
2832 commit.Push( _( "Move Exactly" ) );
2833
2834 if( selection.IsHover() )
2836
2838
2839 if( m_dragging )
2840 {
2843 }
2844 }
2845
2846 return 0;
2847}
2848
2849
2851{
2852 if( isRouterActive() )
2853 {
2854 wxBell();
2855 return 0;
2856 }
2857
2858 bool increment = aEvent.IsAction( &PCB_ACTIONS::duplicateIncrement );
2859
2860 // Be sure that there is at least one item that we can modify
2862 []( const VECTOR2I&, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2863 {
2864 sTool->FilterCollectorForFreePads( aCollector, true );
2865 sTool->FilterCollectorForMarkers( aCollector );
2866 sTool->FilterCollectorForHierarchy( aCollector, true );
2867 sTool->FilterCollectorForTableCells( aCollector );
2868 } );
2869
2870 if( selection.Empty() )
2871 return 0;
2872
2873 // Duplicating tuning patterns alone is not supported
2874 if( selection.Size() == 1 && selection.CountType( PCB_GENERATOR_T ) )
2875 return 0;
2876
2877 // we have a selection to work on now, so start the tool process
2878 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2879 BOARD_COMMIT commit( this );
2880 FOOTPRINT* parentFootprint = nullptr;
2881
2883 parentFootprint = editFrame->GetBoard()->GetFirstFootprint();
2884
2885 // If the selection was given a hover, we do not keep the selection after completion
2886 bool is_hover = selection.IsHover();
2887
2888 std::vector<BOARD_ITEM*> new_items;
2889 new_items.reserve( selection.Size() );
2890
2891 // Each selected item is duplicated and pushed to new_items list
2892 // Old selection is cleared, and new items are then selected.
2893 for( EDA_ITEM* item : selection )
2894 {
2895 if( !item->IsBOARD_ITEM() )
2896 continue;
2897
2898 BOARD_ITEM* dupe_item = nullptr;
2899 BOARD_ITEM* orig_item = static_cast<BOARD_ITEM*>( item );
2900
2902 && /*FOOTPRINT* parentFootprint =*/ orig_item->GetParentFootprint() )
2903 {
2904 // No sub-footprint modifications allowed outside of footprint editor
2905 // and parentFootprint is not (yet?) used
2906 }
2907 else
2908 {
2909 switch( orig_item->Type() )
2910 {
2911 case PCB_FOOTPRINT_T:
2912 case PCB_TEXT_T:
2913 case PCB_TEXTBOX_T:
2915 case PCB_SHAPE_T:
2916 case PCB_TRACE_T:
2917 case PCB_ARC_T:
2918 case PCB_VIA_T:
2919 case PCB_ZONE_T:
2920 case PCB_TARGET_T:
2921 case PCB_DIM_ALIGNED_T:
2922 case PCB_DIM_CENTER_T:
2923 case PCB_DIM_RADIAL_T:
2925 case PCB_DIM_LEADER_T:
2927 dupe_item = parentFootprint->DuplicateItem( orig_item );
2928 else
2929 dupe_item = orig_item->Duplicate();
2930
2931 // Clear the selection flag here, otherwise the PCB_SELECTION_TOOL
2932 // will not properly select it later on
2933 dupe_item->ClearSelected();
2934
2935 new_items.push_back( dupe_item );
2936 commit.Add( dupe_item );
2937 break;
2938
2939 case PCB_FIELD_T:
2940 // PCB_FIELD items are specific items (not only graphic, but are properies)
2941 // and cannot be duplicated like other footprint items. So skip it:
2942 orig_item->ClearSelected();
2943 break;
2944
2945 case PCB_PAD_T:
2946 dupe_item = parentFootprint->DuplicateItem( orig_item );
2947
2948 if( increment && static_cast<PAD*>( dupe_item )->CanHaveNumber() )
2949 {
2950 PAD_TOOL* padTool = m_toolMgr->GetTool<PAD_TOOL>();
2951 wxString padNumber = padTool->GetLastPadNumber();
2952 padNumber = parentFootprint->GetNextPadNumber( padNumber );
2953 padTool->SetLastPadNumber( padNumber );
2954 static_cast<PAD*>( dupe_item )->SetNumber( padNumber );
2955 }
2956
2957 // Clear the selection flag here, otherwise the PCB_SELECTION_TOOL
2958 // will not properly select it later on
2959 dupe_item->ClearSelected();
2960
2961 new_items.push_back( dupe_item );
2962 commit.Add( dupe_item );
2963 break;
2964
2965 case PCB_TABLE_T:
2966 // JEY TODO: tables
2967 break;
2968
2969 case PCB_GENERATOR_T:
2970 case PCB_GROUP_T:
2971 dupe_item = static_cast<PCB_GROUP*>( orig_item )->DeepDuplicate();
2972
2973 dupe_item->RunOnChildren(
2974 [&]( BOARD_ITEM* aItem )
2975 {
2976 aItem->ClearSelected();
2977 new_items.push_back( aItem );
2978 commit.Add( aItem );
2979 },
2980 RECURSE_MODE::RECURSE );
2981
2982 dupe_item->ClearSelected();
2983 new_items.push_back( dupe_item );
2984 commit.Add( dupe_item );
2985 break;
2986
2987 default:
2988 wxASSERT_MSG( false, wxString::Format( wxT( "Unhandled item type %d" ),
2989 orig_item->Type() ) );
2990 break;
2991 }
2992 }
2993 }
2994
2995 // Clear the old selection first
2997
2998 // Select the new items
2999 EDA_ITEMS nItems( new_items.begin(), new_items.end() );
3001
3002 // record the new items as added
3003 if( !selection.Empty() )
3004 {
3005 editFrame->DisplayToolMsg( wxString::Format( _( "Duplicated %d item(s)" ),
3006 (int) new_items.size() ) );
3007
3008 // If items were duplicated, pick them up
3009 if( doMoveSelection( aEvent, &commit, true ) )
3010 commit.Push( _( "Duplicate" ) );
3011 else
3012 commit.Revert();
3013
3014 // Deselect the duplicated item if we originally started as a hover selection
3015 if( is_hover )
3017 }
3018
3019 return 0;
3020}
3021
3022
3024{
3025 const auto incrementableFilter =
3026 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
3027 {
3028 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
3029 {
3030 switch( aCollector[i]->Type() )
3031 {
3032 case PCB_PAD_T:
3033 case PCB_TEXT_T:
3034 break;
3035 default:
3036 aCollector.Remove( i );
3037 break;
3038 }
3039 }
3040 };
3041
3043 incrementableFilter, true /* prompt user regarding locked items */ );
3044
3045 if( selection.Empty() )
3046 return 0;
3047
3048 const ACTIONS::INCREMENT incParam = aEvent.Parameter<ACTIONS::INCREMENT>();
3049
3050 STRING_INCREMENTER incrementer;
3051 incrementer.SetSkipIOSQXZ( true );
3052
3053 // If we're coming via another action like 'Move', use that commit
3054 BOARD_COMMIT localCommit( m_toolMgr );
3055 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
3056
3057 if( !commit )
3058 commit = &localCommit;
3059
3060 const auto modifyItem = [&]( EDA_ITEM& aItem )
3061 {
3062 if( aItem.IsNew() )
3064 else
3065 commit->Modify( &aItem );
3066 };
3067
3068 for( EDA_ITEM* item : selection )
3069 {
3070 switch( item->Type() )
3071 {
3072 case PCB_PAD_T:
3073 {
3074 // Only increment pad numbers in the footprint editor
3075 if( !m_isFootprintEditor )
3076 break;
3077
3078 PAD& pad = static_cast<PAD&>( *item );
3079
3080 if( !pad.CanHaveNumber() )
3081 continue;
3082
3083 // Increment on the pad numbers
3084 std::optional<wxString> newNumber =
3085 incrementer.Increment( pad.GetNumber(), incParam.Delta, incParam.Index );
3086
3087 if( newNumber )
3088 {
3089 modifyItem( pad );
3090 pad.SetNumber( *newNumber );
3091 }
3092
3093 break;
3094 }
3095 case PCB_TEXT_T:
3096 {
3097 PCB_TEXT& text = static_cast<PCB_TEXT&>( *item );
3098
3099 std::optional<wxString> newText =
3100 incrementer.Increment( text.GetText(), incParam.Delta, incParam.Index );
3101
3102 if( newText )
3103 {
3104 modifyItem( text );
3105 text.SetText( *newText );
3106 }
3107
3108 break;
3109 }
3110 default:
3111 break;
3112 }
3113 }
3114
3115 commit->Push( _( "Increment" ) );
3116
3117 return 0;
3118}
3119
3120
3122 PCB_SELECTION_TOOL* sTool )
3123{
3124 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
3125 {
3126 if( aCollector[i]->Type() != PCB_PAD_T )
3127 aCollector.Remove( i );
3128 }
3129}
3130
3131
3133 PCB_SELECTION_TOOL* sTool )
3134{
3135 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
3136 {
3137 if( aCollector[i]->Type() != PCB_FOOTPRINT_T )
3138 aCollector.Remove( i );
3139 }
3140}
3141
3142
3144{
3145 // Can't modify an empty group
3146 if( aSelection.Empty() )
3147 return false;
3148
3149 if( ( m_dragging || aSelection[0]->IsMoving() ) && aSelection.HasReferencePoint() )
3150 return false;
3151
3152 // When there is only one item selected, the reference point is its position...
3153 if( aSelection.Size() == 1 && aSelection.Front()->Type() != PCB_TABLE_T )
3154 {
3155 if( aSelection.Front()->IsBOARD_ITEM() )
3156 {
3157 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( aSelection.Front() );
3158 aSelection.SetReferencePoint( item->GetPosition() );
3159 }
3160 }
3161 // ...otherwise modify items with regard to the grid-snapped center position
3162 else
3163 {
3164 PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
3165 VECTOR2I refPt = aSelection.GetCenter();
3166
3167 // Exclude text in the footprint editor if there's anything else selected
3169 {
3170 BOX2I nonFieldsBBox;
3171
3172 for( EDA_ITEM* item : aSelection.Items() )
3173 {
3174 if( !item->IsType( { PCB_TEXT_T, PCB_FIELD_T } ) )
3175 nonFieldsBBox.Merge( item->GetBoundingBox() );
3176 }
3177
3178 if( nonFieldsBBox.IsValid() )
3179 refPt = nonFieldsBBox.GetCenter();
3180 }
3181
3182 aSelection.SetReferencePoint( grid.BestSnapAnchor( refPt, nullptr ) );
3183 }
3184
3185 return true;
3186}
3187
3188
3189bool EDIT_TOOL::pickReferencePoint( const wxString& aTooltip, const wxString& aSuccessMessage,
3190 const wxString& aCanceledMessage, VECTOR2I& aReferencePoint )
3191{
3193 PCB_EDIT_FRAME& editFrame = *getEditFrame<PCB_EDIT_FRAME>();
3194 std::optional<VECTOR2I> pickedPoint;
3195 bool done = false;
3196
3197 m_statusPopup->SetText( aTooltip );
3198
3200 picker->SetSnapping( true );
3201
3202 const auto setPickerLayerSet = [&]()
3203 {
3204 MAGNETIC_SETTINGS& magSettings = *editFrame.GetMagneticItemsSettings();
3205 LSET layerFilter;
3206 if( !magSettings.allLayers )
3207 layerFilter = LSET( { editFrame.GetActiveLayer() } );
3208 else
3209 layerFilter = LSET::AllLayersMask();
3210
3211 picker->SetLayerSet( layerFilter );
3212 };
3213
3214 // Initial set
3215 setPickerLayerSet();
3216
3217 picker->SetClickHandler(
3218 [&]( const VECTOR2D& aPoint ) -> bool
3219 {
3220 pickedPoint = aPoint;
3221
3222 if( !aSuccessMessage.empty() )
3223 {
3224 m_statusPopup->SetText( aSuccessMessage );
3225 m_statusPopup->Expire( 800 );
3226 }
3227 else
3228 {
3229 m_statusPopup->Hide();
3230 }
3231
3232 return false; // we don't need any more points
3233 } );
3234
3235 picker->SetMotionHandler(
3236 [&]( const VECTOR2D& aPos )
3237 {
3238 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
3239 } );
3240
3241 picker->SetCancelHandler(
3242 [&]()
3243 {
3244 if( !aCanceledMessage.empty() )
3245 {
3246 m_statusPopup->SetText( aCanceledMessage );
3247 m_statusPopup->Expire( 800 );
3248 }
3249 else
3250 {
3251 m_statusPopup->Hide();
3252 }
3253 } );
3254
3255 picker->SetFinalizeHandler(
3256 [&]( const int& aFinalState )
3257 {
3258 done = true;
3259 } );
3260
3261 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
3262 m_statusPopup->Popup();
3263 canvas()->SetStatusPopup( m_statusPopup->GetPanel() );
3264
3266
3267 while( !done )
3268 {
3269 // Pass events unless we receive a null event, then we must shut down
3270 if( TOOL_EVENT* evt = Wait() )
3271 {
3272 if( evt->Matches( PCB_EVENTS::SnappingModeChangedByKeyEvent() ) )
3273 {
3274 // Update the layer set when the snapping mode changes
3275 setPickerLayerSet();
3276 }
3277
3278 evt->SetPassEvent();
3279 }
3280 else
3281 break;
3282 }
3283
3284 // Ensure statusPopup is hidden after use and before deleting it:
3285 canvas()->SetStatusPopup( nullptr );
3286 m_statusPopup->Hide();
3287
3288 if( pickedPoint )
3289 aReferencePoint = *pickedPoint;
3290
3291 return pickedPoint.has_value();
3292}
3293
3294
3296{
3297 CLIPBOARD_IO io;
3299 getEditFrame<PCB_BASE_EDIT_FRAME>()->GetMagneticItemsSettings() );
3300 TOOL_EVENT selectReferencePoint( aEvent.Category(), aEvent.Action(),
3301 "pcbnew.InteractiveEdit.selectReferencePoint",
3302 TOOL_ACTION_SCOPE::AS_GLOBAL );
3303
3304 frame()->PushTool( selectReferencePoint );
3305 Activate();
3306
3308 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
3309 {
3310 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
3311 {
3312 BOARD_ITEM* item = aCollector[i];
3313
3314 // We can't copy both a footprint and its text in the same operation, so if
3315 // both are selected, remove the text
3316 if( ( item->Type() == PCB_FIELD_T || item->Type() == PCB_TEXT_T )
3317 && aCollector.HasItem( item->GetParentFootprint() ) )
3318 {
3319 aCollector.Remove( item );
3320 }
3321 else if( item->Type() == PCB_MARKER_T )
3322 {
3323 // Don't allow copying marker objects
3324 aCollector.Remove( item );
3325 }
3326 }
3327 },
3328
3329 // Prompt user regarding locked items.
3330 aEvent.IsAction( &ACTIONS::cut ) && !m_isFootprintEditor );
3331
3332 if( !selection.Empty() )
3333 {
3334 std::vector<BOARD_ITEM*> items;
3335
3336 for( EDA_ITEM* item : selection )
3337 {
3338 if( item->IsBOARD_ITEM() )
3339 items.push_back( static_cast<BOARD_ITEM*>( item ) );
3340 }
3341
3342 VECTOR2I refPoint;
3343
3344 if( aEvent.IsAction( &PCB_ACTIONS::copyWithReference ) )
3345 {
3346 if( !pickReferencePoint( _( "Select reference point for the copy..." ),
3347 _( "Selection copied" ),
3348 _( "Copy canceled" ),
3349 refPoint ) )
3350 {
3351 frame()->PopTool( selectReferencePoint );
3352 return 0;
3353 }
3354 }
3355 else
3356 {
3357 refPoint = grid.BestDragOrigin( getViewControls()->GetCursorPosition(), items );
3358 }
3359
3360 selection.SetReferencePoint( refPoint );
3361
3362 io.SetBoard( board() );
3363 io.SaveSelection( selection, m_isFootprintEditor );
3364 frame()->SetStatusText( _( "Selection copied" ) );
3365 }
3366
3367 frame()->PopTool( selectReferencePoint );
3368
3369 if( selection.IsHover() )
3370 m_selectionTool->ClearSelection();
3371
3372 return 0;
3373}
3374
3375
3377{
3379 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
3380 {
3381 // Anything unsupported will just be ignored
3382 },
3383 // No prompt for locked items
3384 false );
3385
3386 if( selection.IsHover() )
3388
3389 const auto getItemText = [&]( const BOARD_ITEM& aItem ) -> wxString
3390 {
3391 switch( aItem.Type() )
3392 {
3393 case PCB_TEXT_T:
3394 case PCB_FIELD_T:
3395 case PCB_DIM_ALIGNED_T:
3396 case PCB_DIM_LEADER_T:
3397 case PCB_DIM_CENTER_T:
3398 case PCB_DIM_RADIAL_T:
3400 {
3401 // These can all go via the PCB_TEXT class
3402 const PCB_TEXT& text = static_cast<const PCB_TEXT&>( aItem );
3403 return text.GetShownText( true );
3404 }
3405 case PCB_TEXTBOX_T:
3406 case PCB_TABLECELL_T:
3407 {
3408 // This one goes via EDA_TEXT
3409 const PCB_TEXTBOX& textBox = static_cast<const PCB_TEXTBOX&>( aItem );
3410 return textBox.GetShownText( true );
3411 }
3412 case PCB_TABLE_T:
3413 {
3414 const PCB_TABLE& table = static_cast<const PCB_TABLE&>( aItem );
3415 wxString s;
3416
3417 for( int row = 0; row < table.GetRowCount(); ++row )
3418 {
3419 for( int col = 0; col < table.GetColCount(); ++col )
3420 {
3421 const PCB_TABLECELL* cell = table.GetCell( row, col );
3422 s << cell->GetShownText( true );
3423
3424 if( col < table.GetColCount() - 1 )
3425 {
3426 s << '\t';
3427 }
3428 }
3429
3430 if( row < table.GetRowCount() - 1 )
3431 {
3432 s << '\n';
3433 }
3434 }
3435 return s;
3436 }
3437 default:
3438 // No string representation for this item type
3439 break;
3440 }
3441 return wxEmptyString;
3442 };
3443
3444 wxArrayString itemTexts;
3445
3446 for( EDA_ITEM* item : selection )
3447 {
3448 if( item->IsBOARD_ITEM() )
3449 {
3450 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
3451 wxString itemText = getItemText( *boardItem );
3452
3453 itemText.Trim( false ).Trim( true );
3454
3455 if( !itemText.IsEmpty() )
3456 {
3457 itemTexts.Add( std::move( itemText ) );
3458 }
3459 }
3460 }
3461
3462 // Send the text to the clipboard
3463 if( !itemTexts.empty() )
3464 {
3465 SaveClipboard( wxJoin( itemTexts, '\n', '\0' ).ToStdString() );
3466 }
3467
3468 return 0;
3469}
3470
3471
3473{
3474 if( !copyToClipboard( aEvent ) )
3475 {
3476 // N.B. Setting the CUT flag prevents lock filtering as we only want to delete the items
3477 // that were copied to the clipboard, no more, no fewer. Filtering for locked item, if
3478 // any will be done in the copyToClipboard() routine
3479 TOOL_EVENT evt = aEvent;
3481 Remove( evt );
3482 }
3483
3484 return 0;
3485}
3486
3487
3489{
3493}
3494
3495
3496// clang-format off
3498{
3500 Go( &EDIT_TOOL::Move, PCB_ACTIONS::move.MakeEvent() );
3506 Go( &EDIT_TOOL::Flip, PCB_ACTIONS::flip.MakeEvent() );
3507 Go( &EDIT_TOOL::Remove, ACTIONS::doDelete.MakeEvent() );
3514 Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorH.MakeEvent() );
3515 Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorV.MakeEvent() );
3516 Go( &EDIT_TOOL::Swap, PCB_ACTIONS::swap.MakeEvent() );
3526
3532
3536
3540
3544 Go( &EDIT_TOOL::cutToClipboard, ACTIONS::cut.MakeEvent() );
3545}
3546// 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:209
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:173
REMOVE_FLAGS
Definition: actions.h:271
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() const
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:78
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:235
virtual void SetLocked(bool aLocked)
Definition: board_item.h:326
PCB_GROUP * GetParentGroup() const
Definition: board_item.h:94
virtual void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle)
Rotate this object.
Definition: board_item.cpp:362
virtual BOARD_ITEM * Duplicate() const
Create a copy of this BOARD_ITEM.
Definition: board_item.cpp:250
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition: board_item.h:340
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:286
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:305
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:255
virtual bool IsLocked() const
Definition: board_item.cpp:82
virtual void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const
Invoke a function on all children.
Definition: board_item.h:211
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:213
virtual void Normalize()
Perform any normalization required after a user rotate and/or flip.
Definition: board_item.h:371
virtual void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection)
Flip this object, i.e.
Definition: board_item.cpp:368
LSET GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:843
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:185
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:463
const FOOTPRINTS & Footprints() const
Definition: board.h:338
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:946
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:495
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:82
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition: collector.h:110
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:95
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:248
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:131
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:107
void ClearSelected()
Definition: eda_item.h:126
bool IsSelected() const
Definition: eda_item.h:116
virtual const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
Definition: eda_item.cpp:315
bool IsMoving() const
Definition: eda_item.h:114
bool IsNew() const
Definition: eda_item.h:113
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:337
SHAPE_T GetShape() const
Definition: eda_shape.h:168
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:215
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:177
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:173
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:219
void SetWidth(int aWidth)
Definition: eda_shape.cpp:2303
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:379
bool isRouterActive() const
Definition: edit_tool.cpp:515
int Duplicate(const TOOL_EVENT &aItem)
Duplicate the current selection and starts a move action.
Definition: edit_tool.cpp:2850
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:523
int Flip(const TOOL_EVENT &aEvent)
Rotate currently selected items.
Definition: edit_tool.cpp:2332
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:2132
int Increment(const TOOL_EVENT &aEvent)
Increment some aspect of the selected items.q.
Definition: edit_tool.cpp:3023
bool pickReferencePoint(const wxString &aTooltip, const wxString &aSuccessMessage, const wxString &aCanceledMessage, VECTOR2I &aReferencePoint)
Definition: edit_tool.cpp:3189
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:1359
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:2755
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:2254
void getChildItemsOfGroupsAndGenerators(EDA_ITEM *item, std::unordered_set< BOARD_ITEM * > &children)
Definition: edit_tool.cpp:2426
bool updateModificationPoint(PCB_SELECTION &aSelection)
Definition: edit_tool.cpp:3143
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:639
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:3295
int Remove(const TOOL_EVENT &aEvent)
Delete currently selected items.
Definition: edit_tool.cpp:2682
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:3472
static const std::vector< KICAD_T > MirrorableItems
Definition: edit_tool.h:105
void DeleteItems(const PCB_SELECTION &aItem, bool aIsCut)
Definition: edit_tool.cpp:2473
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:3121
VECTOR2I m_cursor
Definition: edit_tool.h:241
bool invokeInlineRouter(int aDragMode)
Definition: edit_tool.cpp:486
void removeNonRootItems(std::unordered_set< EDA_ITEM * > &items)
Recursively adds any child items of the given item to the set.
Definition: edit_tool.cpp:2443
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:3488
void setTransitions() override
< Set up handlers for various events.
Definition: edit_tool.cpp:3497
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:1679
int ChangeTrackWidth(const TOOL_EVENT &aEvent)
Definition: edit_tool.cpp:992
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:1758
int copyToClipboardAsText(const TOOL_EVENT &aEvent)
Send the current selection to the clipboard as text.
Definition: edit_tool.cpp:3376
int GetAndPlace(const TOOL_EVENT &aEvent)
Definition: edit_tool.cpp:463
int FilletTracks(const TOOL_EVENT &aEvent)
Fillet (i.e.
Definition: edit_tool.cpp:1065
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:1609
int Properties(const TOOL_EVENT &aEvent)
Display properties window for the selected object.
Definition: edit_tool.cpp:1868
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:3132
int Rotate(const TOOL_EVENT &aEvent)
Rotate currently selected items.
Definition: edit_tool.cpp:1956
static const TOOL_EVENT SelectedEvent
Definition: actions.h:296
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:303
static const TOOL_EVENT ConnectivityChangedEvent
Selected item had a property changed (except movement)
Definition: actions.h:300
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:297
void InvalidateComponentClassCache() const
Forces deferred (on next access) recalculation of the component class for this footprint.
Definition: footprint.cpp:4083
void Remove(BOARD_ITEM *aItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: footprint.cpp:1130
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:2580
wxString GetNextPadNumber(const wxString &aLastPadName) const
Return the next available pad number in the footprint.
Definition: footprint.cpp:2707
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:601
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:231
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition: pad.h:299
VECTOR2I GetPosition() const override
Definition: pad.h:208
void SetDelta(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
Definition: pad.h:293
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:1025
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition: pad.h:195
void SetOffset(PCB_LAYER_ID aLayer, const VECTOR2I &aOffset)
Definition: pad.h:311
void SetPosition(const VECTOR2I &aPos) override
Definition: pad.h:202
const VECTOR2I & GetOffset(PCB_LAYER_ID aLayer) const
Definition: pad.h:317
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition: pad.h:408
void SetOrientation(const EDA_ANGLE &aAngle)
Set the rotation angle of the pad.
Definition: pad.cpp:933
static TOOL_ACTION drag45Degree
Definition: pcb_actions.h:198
static TOOL_ACTION duplicateIncrement
Activation of the duplication tool with incrementing (e.g. pad number)
Definition: pcb_actions.h:187
static TOOL_ACTION changeTrackWidth
Update selected tracks & vias to the current track & via dimensions.
Definition: pcb_actions.h:153
static TOOL_ACTION unrouteSelected
Removes all tracks from the selected items to the first pad.
Definition: pcb_actions.h:94
static TOOL_ACTION mirrorH
Mirroring of selected items.
Definition: pcb_actions.h:140
static TOOL_ACTION updateFootprint
Definition: pcb_actions.h:432
static TOOL_ACTION breakTrack
Break a single track into two segments at the cursor.
Definition: pcb_actions.h:196
static TOOL_ACTION pointEditorMoveMidpoint
Definition: pcb_actions.h:307
static TOOL_ACTION getAndPlace
Find an item and start moving.
Definition: pcb_actions.h:595
static TOOL_ACTION routerRouteSelectedFromEnd
Definition: pcb_actions.h:269
static TOOL_ACTION properties
Activation of the edit tool.
Definition: pcb_actions.h:181
static TOOL_ACTION editFpInFpEditor
Definition: pcb_actions.h:455
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:69
static TOOL_ACTION moveWithReference
move with a reference point
Definition: pcb_actions.h:127
static TOOL_ACTION swap
Swapping of selected items.
Definition: pcb_actions.h:144
static TOOL_ACTION routerAutorouteSelected
Definition: pcb_actions.h:270
static TOOL_ACTION moveExact
Activation of the exact move tool.
Definition: pcb_actions.h:184
static TOOL_ACTION intersectPolygons
Intersection of multiple polygons.
Definition: pcb_actions.h:178
static TOOL_ACTION pointEditorMoveCorner
Definition: pcb_actions.h:306
static TOOL_ACTION genRemove
Definition: pcb_actions.h:294
static TOOL_ACTION selectConnection
Select tracks between junctions or expands an existing selection to pads or the entire connection.
Definition: pcb_actions.h:91
static TOOL_ACTION assignNetClass
Definition: pcb_actions.h:408
static TOOL_ACTION packAndMoveFootprints
Pack and start moving selected footprints.
Definition: pcb_actions.h:147
static TOOL_ACTION copyWithReference
copy command with manual reference point selection
Definition: pcb_actions.h:130
static TOOL_ACTION healShapes
Connect selected shapes, possibly extending or cutting them, or adding extra geometry.
Definition: pcb_actions.h:165
static TOOL_ACTION dragFreeAngle
Definition: pcb_actions.h:199
static TOOL_ACTION positionRelativeInteractively
Definition: pcb_actions.h:329
static TOOL_ACTION inspectClearance
Definition: pcb_actions.h:571
static TOOL_ACTION updateLocalRatsnest
Definition: pcb_actions.h:589
static TOOL_ACTION updateFootprints
Definition: pcb_actions.h:433
static TOOL_ACTION deleteFull
Definition: pcb_actions.h:191
static TOOL_ACTION moveIndividually
move items one-by-one
Definition: pcb_actions.h:124
static TOOL_ACTION changeFootprints
Definition: pcb_actions.h:435
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition: pcb_actions.h:72
static TOOL_ACTION chamferLines
Chamfer (i.e. adds a straight line) all selected straight lines by a user defined setback.
Definition: pcb_actions.h:161
static TOOL_ACTION dogboneCorners
Add "dogbone" corners to selected lines to allow routing with a cutter radius.
Definition: pcb_actions.h:163
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:156
static TOOL_ACTION simplifyPolygons
Simplify polygon outlines.
Definition: pcb_actions.h:169
static TOOL_ACTION footprintProperties
Definition: pcb_actions.h:492
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:159
static TOOL_ACTION changeFootprint
Definition: pcb_actions.h:434
static TOOL_ACTION routerInlineDrag
Activation of the Push and Shove router (inline dragging mode)
Definition: pcb_actions.h:283
static TOOL_ACTION positionRelative
Definition: pcb_actions.h:328
static TOOL_ACTION skip
Definition: pcb_actions.h:150
static TOOL_ACTION move
move or drag an item
Definition: pcb_actions.h:121
static TOOL_ACTION mirrorV
Definition: pcb_actions.h:141
static TOOL_ACTION mergePolygons
Merge multiple polygons into a single polygon.
Definition: pcb_actions.h:174
static TOOL_ACTION subtractPolygons
Subtract polygons from other polygons.
Definition: pcb_actions.h:176
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition: pcb_actions.h:77
static TOOL_ACTION flip
Flipping of selected objects.
Definition: pcb_actions.h:137
static TOOL_ACTION extendLines
Extend selected lines to meet at a point.
Definition: pcb_actions.h:167
static TOOL_ACTION routerRouteSelected
Definition: pcb_actions.h:268
static TOOL_ACTION rotateCw
Rotation of selected objects.
Definition: pcb_actions.h:133
static TOOL_ACTION rotateCcw
Definition: pcb_actions.h:134
virtual double GetLength() const override
Return the length of the arc track.
Definition: pcb_track.h:371
void SetMid(const VECTOR2I &aMid)
Definition: pcb_track.h:336
EDA_ANGLE GetAngle() const
Definition: pcb_track.cpp:2040
const VECTOR2I & GetMid() const
Definition: pcb_track.h:337
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_track.h:344
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(const 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:375
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:179
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: pcb_shape.h:71
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:175
virtual double GetLength() const
Get the length of the track using the hypotenuse calculation.
Definition: pcb_track.cpp:751
void SetEnd(const VECTOR2I &aEnd)
Definition: pcb_track.h:148
bool HasSolderMask() const
Definition: pcb_track.h:176
void SetStart(const VECTOR2I &aStart)
Definition: pcb_track.h:151
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
Definition: pcb_track.h:178
std::optional< int > GetLocalSolderMaskMargin() const
Definition: pcb_track.h:179
const VECTOR2I & GetStart() const
Definition: pcb_track.h:152
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:149
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:649
virtual void SetWidth(int aWidth)
Definition: pcb_track.h:145
virtual int GetWidth() const
Definition: pcb_track.h:146
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:74
bool UnFill()
Removes the zone filling.
Definition: zone.cpp:459
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:815
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:1231
void RemoveCutout(int aOutlineIdx, int aHoleIdx)
Remove a cutout from the zone.
Definition: zone.cpp:1082
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:543
#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:429
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:1337
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:1271
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:2097
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:1286
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:361
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()
static std::vector< KICAD_T > connectedTypes
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