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