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