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