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