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