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