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-2023 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 <advanced_config.h>
28#include <limits>
29#include <kiplatform/ui.h>
31#include <board.h>
33#include <footprint.h>
34#include <pcb_shape.h>
35#include <pcb_group.h>
36#include <pcb_target.h>
37#include <pcb_text.h>
38#include <pcb_textbox.h>
39#include <pcb_generator.h>
40#include <collectors.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>
60#include <core/kicad_algo.h>
61#include <fix_board_shape.h>
62#include <bitmaps.h>
63#include <cassert>
64#include <functional>
65using namespace std::placeholders;
66#include "kicad_clipboard.h"
67#include <wx/hyperlink.h>
68#include <router/router_tool.h>
72#include <board_commit.h>
73#include <zone_filler.h>
74#include <pcb_reference_image.h>
75
76const unsigned int EDIT_TOOL::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
77
79 PCB_TOOL_BASE( "pcbnew.InteractiveEdit" ),
80 m_selectionTool( nullptr ),
81 m_dragging( false )
82{
83}
84
85
87{
88 m_dragging = false;
89
90 m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( getEditFrame<PCB_BASE_EDIT_FRAME>() );
91}
92
93
94static std::shared_ptr<CONDITIONAL_MENU> makePositioningToolsMenu( TOOL_INTERACTIVE* aTool )
95{
96 auto menu = std::make_shared<CONDITIONAL_MENU>( aTool );
97
98 menu->SetIcon( BITMAPS::special_tools );
99 menu->SetTitle( _( "Positioning Tools" ) );
100
101 auto notMovingCondition = []( const SELECTION& aSelection )
102 {
103 return aSelection.Empty() || !aSelection.Front()->IsMoving();
104 };
105
106 // clang-format off
107 menu->AddItem( PCB_ACTIONS::moveExact, SELECTION_CONDITIONS::NotEmpty && notMovingCondition );
108 menu->AddItem( PCB_ACTIONS::moveWithReference, SELECTION_CONDITIONS::NotEmpty && notMovingCondition );
109 menu->AddItem( PCB_ACTIONS::copyWithReference, SELECTION_CONDITIONS::NotEmpty && notMovingCondition );
110 menu->AddItem( PCB_ACTIONS::positionRelative, SELECTION_CONDITIONS::NotEmpty && notMovingCondition );
111 // clang-format on
112 return menu;
113};
114
115
116static std::shared_ptr<CONDITIONAL_MENU> makeShapeModificationMenu( TOOL_INTERACTIVE* aTool )
117{
118 auto menu = std::make_shared<CONDITIONAL_MENU>( aTool );
119
120 menu->SetTitle( _( "Shape Modification" ) );
121
122 static const std::vector<KICAD_T> filletChamferTypes = { PCB_SHAPE_LOCATE_POLY_T,
125
126 static const std::vector<KICAD_T> healShapesTypes = { PCB_SHAPE_LOCATE_SEGMENT_T,
129
130 static const std::vector<KICAD_T> lineExtendTypes = { PCB_SHAPE_LOCATE_SEGMENT_T };
131
132 static const std::vector<KICAD_T> polygonBooleanTypes = { PCB_SHAPE_LOCATE_RECT_T,
134
135 auto hasCornerCondition =
136 [aTool]( const SELECTION& aSelection )
137 {
138 PCB_POINT_EDITOR *pt_tool = aTool->GetManager()->GetTool<PCB_POINT_EDITOR>();
139
140 return pt_tool && pt_tool->HasCorner();
141 };
142
143 auto hasMidpointCondition =
144 [aTool]( const SELECTION& aSelection )
145 {
146 PCB_POINT_EDITOR *pt_tool = aTool->GetManager()->GetTool<PCB_POINT_EDITOR>();
147
148 return pt_tool && pt_tool->HasMidpoint();
149 };
150
151 // clang-format off
152 menu->AddItem( PCB_ACTIONS::healShapes, SELECTION_CONDITIONS::HasTypes( healShapesTypes ) );
153 menu->AddItem( PCB_ACTIONS::filletLines, SELECTION_CONDITIONS::OnlyTypes( filletChamferTypes ) );
154 menu->AddItem( PCB_ACTIONS::chamferLines, SELECTION_CONDITIONS::OnlyTypes( filletChamferTypes ) );
155 menu->AddItem( PCB_ACTIONS::extendLines, SELECTION_CONDITIONS::OnlyTypes( lineExtendTypes )
157 menu->AddItem( PCB_ACTIONS::pointEditorMoveCorner, hasCornerCondition );
158 menu->AddItem( PCB_ACTIONS::pointEditorMoveMidpoint, hasMidpointCondition );
159
160 menu->AddSeparator( SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
162
163 menu->AddItem( PCB_ACTIONS::mergePolygons, SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
165 menu->AddItem( PCB_ACTIONS::subtractPolygons, SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
167 menu->AddItem( PCB_ACTIONS::intersectPolygons, SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
169 // clang-format on
170
171 return menu;
172};
173
175{
176 // Find the selection tool, so they can cooperate
178
179 std::shared_ptr<CONDITIONAL_MENU> positioningToolsSubMenu = makePositioningToolsMenu( this );
180 m_selectionTool->GetToolMenu().RegisterSubMenu( positioningToolsSubMenu );
181
182 std::shared_ptr<CONDITIONAL_MENU> shapeModificationSubMenu = makeShapeModificationMenu( this );
183 m_selectionTool->GetToolMenu().RegisterSubMenu( shapeModificationSubMenu );
184
185 auto propertiesCondition =
186 [&]( const SELECTION& aSel )
187 {
188 if( aSel.GetSize() == 0 )
189 {
191 {
194
195 if( ds && ds->HitTestDrawingSheetItems( getView(), cursor ) )
196 return true;
197 }
198
199 return false;
200 }
201
202 if( aSel.GetSize() == 1 )
203 return true;
204
205 for( EDA_ITEM* item : aSel )
206 {
207 if( !dynamic_cast<PCB_TRACK*>( item ) )
208 return false;
209 }
210
211 return true;
212 };
213
214 auto inFootprintEditor =
215 [ this ]( const SELECTION& aSelection )
216 {
217 return m_isFootprintEditor;
218 };
219
220 auto canMirror =
221 [ this ]( const SELECTION& aSelection )
222 {
224 && SELECTION_CONDITIONS::OnlyTypes( { PCB_PAD_T } )( aSelection ) )
225 {
226 return false;
227 }
228
230 };
231
232 auto singleFootprintCondition = SELECTION_CONDITIONS::OnlyTypes( { PCB_FOOTPRINT_T } )
234
235 auto multipleFootprintsCondition =
236 []( const SELECTION& aSelection )
237 {
238 bool foundFirst = false;
239
240 for( EDA_ITEM* item : aSelection )
241 {
242 if( item->Type() == PCB_FOOTPRINT_T )
243 {
244 if( foundFirst )
245 return true;
246 else
247 foundFirst = true;
248 }
249 }
250
251 return false;
252 };
253
254 auto noActiveToolCondition =
255 [ this ]( const SELECTION& aSelection )
256 {
257 return frame()->ToolStackIsEmpty();
258 };
259
260 auto notMovingCondition =
261 []( const SELECTION& aSelection )
262 {
263 return aSelection.Empty() || !aSelection.Front()->IsMoving();
264 };
265
266 auto noItemsCondition =
267 [ this ]( const SELECTION& aSelections ) -> bool
268 {
269 return frame()->GetBoard() && !frame()->GetBoard()->IsEmpty();
270 };
271
272 auto isSkippable =
273 [ this ]( const SELECTION& aSelection )
274 {
276 };
277
278 static std::vector<KICAD_T> connectedTypes = { PCB_TRACE_T,
279 PCB_ARC_T,
280 PCB_VIA_T,
281 PCB_PAD_T,
282 PCB_ZONE_T };
283
284 static std::vector<KICAD_T> unroutableTypes = { PCB_TRACE_T,
285 PCB_ARC_T,
286 PCB_VIA_T,
287 PCB_PAD_T,
289
290 static std::vector<KICAD_T> trackTypes = { PCB_TRACE_T,
291 PCB_ARC_T,
292 PCB_VIA_T };
293
294 // Add context menu entries that are displayed when selection tool is active
296
297 // clang-format off
299 && notMovingCondition );
301 && SELECTION_CONDITIONS::OnlyTypes( unroutableTypes )
302 && notMovingCondition
303 && !inFootprintEditor );
305 && notMovingCondition );
306 menu.AddItem( PCB_ACTIONS::skip, isSkippable );
308 && SELECTION_CONDITIONS::OnlyTypes( trackTypes ) );
318 menu.AddItem( PCB_ACTIONS::mirrorH, canMirror );
319 menu.AddItem( PCB_ACTIONS::mirrorV, canMirror );
323
324 menu.AddItem( PCB_ACTIONS::properties, propertiesCondition );
325
327 && !inFootprintEditor );
329
330 // Footprint actions
331 menu.AddSeparator();
332 menu.AddItem( PCB_ACTIONS::editFpInFpEditor, singleFootprintCondition );
333 menu.AddItem( PCB_ACTIONS::updateFootprint, singleFootprintCondition );
334 menu.AddItem( PCB_ACTIONS::updateFootprints, multipleFootprintsCondition );
335 menu.AddItem( PCB_ACTIONS::changeFootprint, singleFootprintCondition );
336 menu.AddItem( PCB_ACTIONS::changeFootprints, multipleFootprintsCondition );
337
338 // Add the submenu for the special tools: modfiers and positioning tools
339 menu.AddSeparator( 100 );
340 menu.AddMenu( shapeModificationSubMenu.get(), SELECTION_CONDITIONS::NotEmpty, 100 );
341 menu.AddMenu( positioningToolsSubMenu.get(), SELECTION_CONDITIONS::NotEmpty, 100 );
342
343 menu.AddSeparator( 150 );
346
347 // Selection tool handles the context menu for some other tools, such as the Picker.
348 // Don't add things like Paste when another tool is active.
349 menu.AddItem( ACTIONS::paste, noActiveToolCondition, 150 );
350 menu.AddItem( ACTIONS::pasteSpecial, noActiveToolCondition && !inFootprintEditor, 150 );
353
354 menu.AddSeparator( 150 );
355 menu.AddItem( ACTIONS::selectAll, noItemsCondition, 150 );
356 menu.AddItem( ACTIONS::unselectAll, noItemsCondition, 150 );
357 // clang-format on
358
359 return true;
360}
361
362
364{
365 // GetAndPlace makes sense only in board editor, although it is also called
366 // in fpeditor, that shares the same EDIT_TOOL list
367 if( !getEditFrame<PCB_BASE_FRAME>()->IsType( FRAME_PCB_EDITOR ) )
368 return 0;
369
371 FOOTPRINT* fp = getEditFrame<PCB_BASE_FRAME>()->GetFootprintFromBoardByReference();
372
373 if( fp )
374 {
377
378 selectionTool->GetSelection().SetReferencePoint( fp->GetPosition() );
380 }
381
382 return 0;
383}
384
385
386bool EDIT_TOOL::invokeInlineRouter( int aDragMode )
387{
388 ROUTER_TOOL* theRouter = m_toolMgr->GetTool<ROUTER_TOOL>();
389
390 if( !theRouter )
391 return false;
392
393 // don't allow switch from moving to dragging
394 if( m_dragging )
395 {
396 wxBell();
397 return false;
398 }
399
400 // make sure we don't accidentally invoke inline routing mode while the router is already
401 // active!
402 if( theRouter->IsToolActive() )
403 return false;
404
405 if( theRouter->CanInlineDrag( aDragMode ) )
406 {
408 return true;
409 }
410
411 return false;
412}
413
414
416{
418
419 return router && router->RoutingInProgress();
420}
421
422
423int EDIT_TOOL::Drag( const TOOL_EVENT& aEvent )
424{
425 if( !m_toolMgr->GetTool<ROUTER_TOOL>() )
426 {
427 wxBell();
428 return false; // don't drag when no router tool (i.e. fp editor)
429 }
430
432 {
433 wxBell();
434 return false; // don't drag when router is already active
435 }
436
437 if( m_dragging )
438 {
439 wxBell();
440 return false; // don't do a router drag when already in an EDIT_TOOL drag
441 }
442
443 int mode = PNS::DM_ANY;
444
445 if( aEvent.IsAction( &PCB_ACTIONS::dragFreeAngle ) )
446 mode |= PNS::DM_FREE_ANGLE;
447
449 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
450 {
451 sTool->FilterCollectorForFreePads( aCollector );
452 sTool->FilterCollectorForHierarchy( aCollector, true );
453
454 if( aCollector.GetCount() > 1 )
455 sTool->GuessSelectionCandidates( aCollector, aPt );
456
457 /*
458 * If we have a knee between two segments, or a via attached to two segments,
459 * then drop the selection to a single item.
460 */
461
462 std::vector<PCB_TRACK*> tracks;
463 std::vector<PCB_TRACK*> vias;
464
465 for( EDA_ITEM* item : aCollector )
466 {
467 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
468 {
469 if( track->Type() == PCB_VIA_T )
470 vias.push_back( track );
471 else
472 tracks.push_back( track );
473 }
474 }
475
476 auto connected = []( PCB_TRACK* track, const VECTOR2I& pt )
477 {
478 return track->GetStart() == pt || track->GetEnd() == pt;
479 };
480
481 if( tracks.size() == 2 && vias.size() == 0 )
482 {
483 if( connected( tracks[0], tracks[1]->GetStart() )
484 || connected( tracks[0], tracks[1]->GetEnd() ) )
485 {
486 aCollector.Remove( tracks[1] );
487 }
488 }
489 else if( tracks.size() == 2 && vias.size() == 1 )
490 {
491 if( connected( tracks[0], vias[0]->GetPosition() )
492 && connected( tracks[1], vias[0]->GetPosition() ) )
493 {
494 aCollector.Remove( tracks[0] );
495 aCollector.Remove( tracks[1] );
496 }
497 }
498 },
499 true /* prompt user regarding locked items */ );
500
501 if( selection.Empty() )
502 return 0;
503
504 if( selection.Size() == 1 && selection.Front()->Type() == PCB_ARC_T )
505 {
506 // TODO: This really should be done in PNS to ensure DRC is maintained, but for now
507 // it allows interactive editing of an arc track
508 return DragArcTrack( aEvent );
509 }
510 else
511 {
512 invokeInlineRouter( mode );
513 }
514
515 return 0;
516}
517
518
520{
522
523 if( selection.Size() != 1 || selection.Front()->Type() != PCB_ARC_T )
524 return 0;
525
526 PCB_ARC* theArc = static_cast<PCB_ARC*>( selection.Front() );
527 EDA_ANGLE maxTangentDeviation( ADVANCED_CFG::GetCfg().m_MaxTangentAngleDeviation, DEGREES_T );
528
529 if( theArc->GetAngle() + maxTangentDeviation >= ANGLE_180 )
530 {
531 wxString msg = wxString::Format( _( "Unable to resize arc tracks of %s or greater." ),
532 EDA_UNIT_UTILS::UI::MessageTextFromValue( ANGLE_180 - maxTangentDeviation ) );
533 frame()->ShowInfoBarError( msg );
534
535 return 0; // don't bother with > 180 degree arcs
536 }
537
539
540 Activate();
541 // Must be done after Activate() so that it gets set into the correct context
542 controls->ShowCursor( true );
543 controls->SetAutoPan( true );
544
545 BOARD_COMMIT commit( this );
546 bool restore_state = false;
547
548 commit.Modify( theArc );
549
550 VECTOR2I arcCenter = theArc->GetCenter();
551 SEG tanStart = SEG( arcCenter, theArc->GetStart() ).PerpendicularSeg( theArc->GetStart() );
552 SEG tanEnd = SEG( arcCenter, theArc->GetEnd() ).PerpendicularSeg( theArc->GetEnd() );
553
554 //Ensure the tangent segments are in the correct orientation
555 OPT_VECTOR2I tanIntersect = tanStart.IntersectLines( tanEnd );
556
557 if( !tanIntersect )
558 return 0;
559
560 tanStart.A = *tanIntersect;
561 tanStart.B = theArc->GetStart();
562 tanEnd.A = *tanIntersect;
563 tanEnd.B = theArc->GetEnd();
564
565 std::set<PCB_TRACK*> addedTracks;
566
567 auto getUniqueTrackAtAnchorCollinear =
568 [&]( const VECTOR2I& aAnchor, const SEG& aCollinearSeg ) -> PCB_TRACK*
569 {
570 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
571
572 // Allow items at a distance within the width of the arc track
573 int allowedDeviation = theArc->GetWidth();
574
575 std::vector<BOARD_CONNECTED_ITEM*> itemsOnAnchor;
576
577 for( int i = 0; i < 3; i++ )
578 {
579 itemsOnAnchor = conn->GetConnectedItemsAtAnchor( theArc, aAnchor,
582 allowedDeviation );
583 allowedDeviation /= 2;
584
585 if( itemsOnAnchor.size() == 1 )
586 break;
587 }
588
589 PCB_TRACK* track = nullptr;
590
591 if( itemsOnAnchor.size() == 1 && itemsOnAnchor.front()->Type() == PCB_TRACE_T )
592 {
593 track = static_cast<PCB_TRACK*>( itemsOnAnchor.front() );
594 commit.Modify( track );
595
596 SEG trackSeg( track->GetStart(), track->GetEnd() );
597
598 // Allow deviations in colinearity as defined in ADVANCED_CFG
599 if( trackSeg.Angle( aCollinearSeg ) > maxTangentDeviation )
600 track = nullptr;
601 }
602
603 if( !track )
604 {
605 track = new PCB_TRACK( theArc->GetParent() );
606 track->SetStart( aAnchor );
607 track->SetEnd( aAnchor );
608 track->SetNet( theArc->GetNet() );
609 track->SetLayer( theArc->GetLayer() );
610 track->SetWidth( theArc->GetWidth() );
611 track->SetLocked( theArc->IsLocked() );
612 track->SetFlags( IS_NEW );
613 getView()->Add( track );
614 addedTracks.insert( track );
615 }
616
617 return track;
618 };
619
620 PCB_TRACK* trackOnStart = getUniqueTrackAtAnchorCollinear( theArc->GetStart(), tanStart);
621 PCB_TRACK* trackOnEnd = getUniqueTrackAtAnchorCollinear( theArc->GetEnd(), tanEnd );
622
623 if( trackOnStart->GetLength() != 0 )
624 {
625 tanStart.A = trackOnStart->GetStart();
626 tanStart.B = trackOnStart->GetEnd();
627 }
628
629 if( trackOnEnd->GetLength() != 0 )
630 {
631 tanEnd.A = trackOnEnd->GetStart();
632 tanEnd.B = trackOnEnd->GetEnd();
633 }
634
635 // Recalculate intersection point
636 if( tanIntersect = tanStart.IntersectLines( tanEnd ); !tanIntersect )
637 return 0;
638
639 auto isTrackStartClosestToArcStart =
640 [&]( PCB_TRACK* aTrack ) -> bool
641 {
642 double trackStartToArcStart = GetLineLength( aTrack->GetStart(), theArc->GetStart() );
643 double trackEndToArcStart = GetLineLength( aTrack->GetEnd(), theArc->GetStart() );
644
645 return trackStartToArcStart < trackEndToArcStart;
646 };
647
648 bool isStartTrackOnStartPt = isTrackStartClosestToArcStart( trackOnStart );
649 bool isEndTrackOnStartPt = isTrackStartClosestToArcStart( trackOnEnd );
650
651 // Calculate constraints
652 //======================
653 // maxTanCircle is the circle with maximum radius that is tangent to the two adjacent straight
654 // tracks and whose tangent points are constrained within the original tracks and their
655 // projected intersection points.
656 //
657 // The cursor will be constrained first within the isosceles triangle formed by the segments
658 // cSegTanStart, cSegTanEnd and cSegChord. After that it will be constrained to be outside
659 // maxTanCircle.
660 //
661 //
662 // ____________ <-cSegTanStart
663 // / * . ' *
664 // cSegTanEnd-> / * . ' *
665 // /* . ' <-cSegChord *
666 // /. '
667 // /* *
668 //
669 // * c * <-maxTanCircle
670 //
671 // * *
672 //
673 // * *
674 // * *
675 // * *
676 //
677
678 auto getFurthestPointToTanInterstect =
679 [&]( VECTOR2I& aPointA, VECTOR2I& aPointB ) -> VECTOR2I
680 {
681 if( ( aPointA - *tanIntersect ).EuclideanNorm()
682 > ( aPointB - *tanIntersect ).EuclideanNorm() )
683 {
684 return aPointA;
685 }
686 else
687 {
688 return aPointB;
689 }
690 };
691
692 CIRCLE maxTanCircle;
693 VECTOR2I tanStartPoint = getFurthestPointToTanInterstect( tanStart.A, tanStart.B );
694 VECTOR2I tanEndPoint = getFurthestPointToTanInterstect( tanEnd.A, tanEnd.B );
695 VECTOR2I tempTangentPoint = tanEndPoint;
696
697 if( getFurthestPointToTanInterstect( tanStartPoint, tanEndPoint ) == tanEndPoint )
698 tempTangentPoint = tanStartPoint;
699
700 maxTanCircle.ConstructFromTanTanPt( tanStart, tanEnd, tempTangentPoint );
701 VECTOR2I maxTanPtStart = tanStart.LineProject( maxTanCircle.Center );
702 VECTOR2I maxTanPtEnd = tanEnd.LineProject( maxTanCircle.Center );
703
704 SEG cSegTanStart( maxTanPtStart, *tanIntersect );
705 SEG cSegTanEnd( maxTanPtEnd, *tanIntersect );
706 SEG cSegChord( maxTanPtStart, maxTanPtEnd );
707
708 int cSegTanStartSide = cSegTanStart.Side( theArc->GetMid() );
709 int cSegTanEndSide = cSegTanEnd.Side( theArc->GetMid() );
710 int cSegChordSide = cSegChord.Side( theArc->GetMid() );
711
712 bool eatFirstMouseUp = true;
713
714 // Start the tool loop
715 while( TOOL_EVENT* evt = Wait() )
716 {
717 m_cursor = controls->GetMousePosition();
718
719 // Constrain cursor within the isosceles triangle
720 if( cSegTanStartSide != cSegTanStart.Side( m_cursor )
721 || cSegTanEndSide != cSegTanEnd.Side( m_cursor )
722 || cSegChordSide != cSegChord.Side( m_cursor ) )
723 {
724 std::vector<VECTOR2I> possiblePoints;
725
726 possiblePoints.push_back( cSegTanEnd.NearestPoint( m_cursor ) );
727 possiblePoints.push_back( cSegChord.NearestPoint( m_cursor ) );
728
729 VECTOR2I closest = cSegTanStart.NearestPoint( m_cursor );
730
731 for( const VECTOR2I& candidate : possiblePoints )
732 {
733 if( ( candidate - m_cursor ).SquaredEuclideanNorm()
734 < ( closest - m_cursor ).SquaredEuclideanNorm() )
735 {
736 closest = candidate;
737 }
738 }
739
740 m_cursor = closest;
741 }
742
743 // Constrain cursor to be outside maxTanCircle
744 if( ( m_cursor - maxTanCircle.Center ).EuclideanNorm() < maxTanCircle.Radius )
745 m_cursor = maxTanCircle.NearestPoint( m_cursor );
746
747 controls->ForceCursorPosition( true, m_cursor );
748
749 // Calculate resulting object coordinates
750 CIRCLE circlehelper;
751 circlehelper.ConstructFromTanTanPt( cSegTanStart, cSegTanEnd, m_cursor );
752
753 VECTOR2I newCenter = circlehelper.Center;
754 VECTOR2I newStart = cSegTanStart.LineProject( newCenter );
755 VECTOR2I newEnd = cSegTanEnd.LineProject( newCenter );
756 VECTOR2I newMid = CalcArcMid( newStart, newEnd, newCenter );
757
758 // Update objects
759 theArc->SetStart( newStart );
760 theArc->SetEnd( newEnd );
761 theArc->SetMid( newMid );
762
763 if( isStartTrackOnStartPt )
764 trackOnStart->SetStart( newStart );
765 else
766 trackOnStart->SetEnd( newStart );
767
768 if( isEndTrackOnStartPt )
769 trackOnEnd->SetStart( newEnd );
770 else
771 trackOnEnd->SetEnd( newEnd );
772
773 // Update view
774 getView()->Update( trackOnStart );
775 getView()->Update( trackOnEnd );
776 getView()->Update( theArc );
777
778 // Handle events
779 if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
780 {
781 eatFirstMouseUp = false;
782 }
783 else if( evt->IsCancelInteractive() || evt->IsActivate() )
784 {
785 restore_state = true; // Canceling the tool means that items have to be restored
786 break; // Finish
787 }
788 else if( evt->IsAction( &ACTIONS::undo ) )
789 {
790 restore_state = true; // Perform undo locally
791 break; // Finish
792 }
793 else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT )
794 || evt->IsDblClick( BUT_LEFT ) )
795 {
796 // Eat mouse-up/-click events that leaked through from the lock dialog
797 if( eatFirstMouseUp && !evt->IsAction( &ACTIONS::cursorClick ) )
798 {
799 eatFirstMouseUp = false;
800 continue;
801 }
802
803 break; // Finish
804 }
805 }
806
807 // Amend the end points of the arc if we delete the joining tracks
808 VECTOR2I newStart = trackOnStart->GetStart();
809 VECTOR2I newEnd = trackOnEnd->GetStart();
810
811 if( isStartTrackOnStartPt )
812 newStart = trackOnStart->GetEnd();
813
814 if( isEndTrackOnStartPt )
815 newEnd = trackOnEnd->GetEnd();
816
817 int maxLengthIU =
818 KiROUND( ADVANCED_CFG::GetCfg().m_MaxTrackLengthToKeep * pcbIUScale.IU_PER_MM );
819
820 if( trackOnStart->GetLength() <= maxLengthIU )
821 {
822 if( addedTracks.count( trackOnStart ) )
823 {
824 getView()->Remove( trackOnStart );
825 addedTracks.erase( trackOnStart );
826 delete trackOnStart;
827 }
828 else
829 {
830 commit.Remove( trackOnStart );
831 }
832
833 theArc->SetStart( newStart );
834 }
835
836 if( trackOnEnd->GetLength() <= maxLengthIU )
837 {
838 if( addedTracks.count( trackOnEnd ) )
839 {
840 getView()->Remove( trackOnEnd );
841 addedTracks.erase( trackOnEnd );
842 delete trackOnEnd;
843 }
844 else
845 {
846 commit.Remove( trackOnEnd );
847 }
848
849 theArc->SetEnd( newEnd );
850 }
851
852 if( theArc->GetLength() <= 0 )
853 commit.Remove( theArc );
854
855 for( PCB_TRACK* added : addedTracks )
856 {
857 getView()->Remove( added );
858 commit.Add( added );
859 }
860
861 // Should we commit?
862 if( restore_state )
863 commit.Revert();
864 else
865 commit.Push( _( "Drag Arc Track" ) );
866
867 return 0;
868}
869
870
872{
874 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
875 {
876 // Iterate from the back so we don't have to worry about removals.
877 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
878 {
879 BOARD_ITEM* item = aCollector[ i ];
880
881 if( !dynamic_cast<PCB_TRACK*>( item ) )
882 aCollector.Remove( item );
883 }
884 },
885 true /* prompt user regarding locked items */ );
886
887 BOARD_COMMIT commit( this );
888
889 for( EDA_ITEM* item : selection )
890 {
891 if( item->Type() == PCB_VIA_T )
892 {
893 PCB_VIA* via = static_cast<PCB_VIA*>( item );
894
895 commit.Modify( via );
896
897 int new_width;
898 int new_drill;
899
900 if( via->GetViaType() == VIATYPE::MICROVIA )
901 {
902 NETCLASS* netClass = via->GetEffectiveNetClass();
903
904 new_width = netClass->GetuViaDiameter();
905 new_drill = netClass->GetuViaDrill();
906 }
907 else
908 {
909 new_width = board()->GetDesignSettings().GetCurrentViaSize();
910 new_drill = board()->GetDesignSettings().GetCurrentViaDrill();
911 }
912
913 via->SetDrill( new_drill );
914 via->SetWidth( new_width );
915 }
916 else if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
917 {
918 PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item );
919
920 wxCHECK( track, 0 );
921
922 commit.Modify( track );
923
924 int new_width = board()->GetDesignSettings().GetCurrentTrackWidth();
925 track->SetWidth( new_width );
926 }
927 }
928
929 commit.Push( _( "Edit track width/via size" ) );
930
931 if( selection.IsHover() )
932 {
934
935 // Notify other tools of the changes -- This updates the visual ratsnest
937 }
938
939 return 0;
940}
941
942
944{
945 // Store last used fillet radius to allow pressing "enter" if repeat fillet is required
946 static int filletRadius = 0;
947
949 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
950 {
951 // Iterate from the back so we don't have to worry about removals.
952 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
953 {
954 BOARD_ITEM* item = aCollector[i];
955
956 if( !dynamic_cast<PCB_TRACK*>( item ) )
957 aCollector.Remove( item );
958 }
959 },
960 true /* prompt user regarding locked items */ );
961
962 if( selection.Size() < 2 )
963 {
964 frame()->ShowInfoBarMsg( _( "At least two straight track segments must be selected." ) );
965 return 0;
966 }
967
968 WX_UNIT_ENTRY_DIALOG dlg( frame(), _( "Fillet Tracks" ), _( "Enter fillet radius:" ),
969 filletRadius );
970
971 if( dlg.ShowModal() == wxID_CANCEL )
972 return 0;
973
974 filletRadius = dlg.GetValue();
975
976 if( filletRadius == 0 )
977 {
978 frame()->ShowInfoBarMsg( _( "A radius of zero was entered.\n"
979 "The fillet operation was not performed." ) );
980 return 0;
981 }
982
983 struct FILLET_OP
984 {
985 PCB_TRACK* t1;
986 PCB_TRACK* t2;
987 // Start point of track is modified after PCB_ARC is added, otherwise the end point:
988 bool t1Start = true;
989 bool t2Start = true;
990 };
991
992 std::vector<FILLET_OP> filletOperations;
993 bool operationPerformedOnAtLeastOne = false;
994 bool didOneAttemptFail = false;
995 std::set<PCB_TRACK*> processedTracks;
996
997 auto processFilletOp =
998 [&]( PCB_TRACK* aTrack, bool aStartPoint )
999 {
1000 std::shared_ptr<CONNECTIVITY_DATA> c = board()->GetConnectivity();
1001 VECTOR2I anchor = aStartPoint ? aTrack->GetStart() : aTrack->GetEnd();
1002 std::vector<BOARD_CONNECTED_ITEM*> itemsOnAnchor;
1003
1004 itemsOnAnchor = c->GetConnectedItemsAtAnchor( aTrack, anchor,
1007
1008 if( itemsOnAnchor.size() > 0
1009 && selection.Contains( itemsOnAnchor.at( 0 ) )
1010 && itemsOnAnchor.at( 0 )->Type() == PCB_TRACE_T )
1011 {
1012 PCB_TRACK* trackOther = static_cast<PCB_TRACK*>( itemsOnAnchor.at( 0 ) );
1013
1014 // Make sure we don't fillet the same pair of tracks twice
1015 if( processedTracks.find( trackOther ) == processedTracks.end() )
1016 {
1017 if( itemsOnAnchor.size() == 1 )
1018 {
1019 FILLET_OP filletOp;
1020 filletOp.t1 = aTrack;
1021 filletOp.t2 = trackOther;
1022 filletOp.t1Start = aStartPoint;
1023 filletOp.t2Start = aTrack->IsPointOnEnds( filletOp.t2->GetStart() );
1024 filletOperations.push_back( filletOp );
1025 }
1026 else
1027 {
1028 // User requested to fillet these two tracks but not possible as
1029 // there are other elements connected at that point
1030 didOneAttemptFail = true;
1031 }
1032 }
1033 }
1034 };
1035
1036 for( EDA_ITEM* item : selection )
1037 {
1038 if( item->Type() == PCB_TRACE_T )
1039 {
1040 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
1041
1042 if( track->GetLength() > 0 )
1043 {
1044 processFilletOp( track, true ); // on the start point of track
1045 processFilletOp( track, false ); // on the end point of track
1046
1047 processedTracks.insert( track );
1048 }
1049 }
1050 }
1051
1052 BOARD_COMMIT commit( this );
1053 std::vector<BOARD_ITEM*> itemsToAddToSelection;
1054
1055 for( FILLET_OP filletOp : filletOperations )
1056 {
1057 PCB_TRACK* track1 = filletOp.t1;
1058 PCB_TRACK* track2 = filletOp.t2;
1059
1060 bool trackOnStart = track1->IsPointOnEnds( track2->GetStart() );
1061 bool trackOnEnd = track1->IsPointOnEnds( track2->GetEnd() );
1062
1063 if( trackOnStart && trackOnEnd )
1064 continue; // Ignore duplicate tracks
1065
1066 if( ( trackOnStart || trackOnEnd ) && track1->GetLayer() == track2->GetLayer() )
1067 {
1068 SEG t1Seg( track1->GetStart(), track1->GetEnd() );
1069 SEG t2Seg( track2->GetStart(), track2->GetEnd() );
1070
1071 if( t1Seg.ApproxCollinear( t2Seg ) )
1072 continue;
1073
1074 SHAPE_ARC sArc( t1Seg, t2Seg, filletRadius );
1075 VECTOR2I t1newPoint, t2newPoint;
1076
1077 auto setIfPointOnSeg =
1078 []( VECTOR2I& aPointToSet, const SEG& aSegment, const VECTOR2I& aVecToTest )
1079 {
1080 VECTOR2I segToVec = aSegment.NearestPoint( aVecToTest ) - aVecToTest;
1081
1082 // Find out if we are on the segment (minimum precision)
1084 {
1085 aPointToSet.x = aVecToTest.x;
1086 aPointToSet.y = aVecToTest.y;
1087 return true;
1088 }
1089
1090 return false;
1091 };
1092
1093 //Do not draw a fillet if the end points of the arc are not within the track segments
1094 if( !setIfPointOnSeg( t1newPoint, t1Seg, sArc.GetP0() )
1095 && !setIfPointOnSeg( t2newPoint, t2Seg, sArc.GetP0() ) )
1096 {
1097 didOneAttemptFail = true;
1098 continue;
1099 }
1100
1101 if( !setIfPointOnSeg( t1newPoint, t1Seg, sArc.GetP1() )
1102 && !setIfPointOnSeg( t2newPoint, t2Seg, sArc.GetP1() ) )
1103 {
1104 didOneAttemptFail = true;
1105 continue;
1106 }
1107
1108 PCB_ARC* tArc = new PCB_ARC( frame()->GetBoard(), &sArc );
1109 tArc->SetLayer( track1->GetLayer() );
1110 tArc->SetWidth( track1->GetWidth() );
1111 tArc->SetNet( track1->GetNet() );
1112 tArc->SetLocked( track1->IsLocked() );
1113 commit.Add( tArc );
1114 itemsToAddToSelection.push_back( tArc );
1115
1116 commit.Modify( track1 );
1117 commit.Modify( track2 );
1118
1119 if( filletOp.t1Start )
1120 track1->SetStart( t1newPoint );
1121 else
1122 track1->SetEnd( t1newPoint );
1123
1124 if( filletOp.t2Start )
1125 track2->SetStart( t2newPoint );
1126 else
1127 track2->SetEnd( t2newPoint );
1128
1129 operationPerformedOnAtLeastOne = true;
1130 }
1131 }
1132
1133 commit.Push( _( "Fillet Tracks" ) );
1134
1135 //select the newly created arcs
1136 for( BOARD_ITEM* item : itemsToAddToSelection )
1138
1139 if( !operationPerformedOnAtLeastOne )
1140 frame()->ShowInfoBarMsg( _( "Unable to fillet the selected track segments." ) );
1141 else if( didOneAttemptFail )
1142 frame()->ShowInfoBarMsg( _( "Some of the track segments could not be filleted." ) );
1143
1144 return 0;
1145}
1146
1155static std::optional<int> GetFilletParams( PCB_BASE_EDIT_FRAME& aFrame, wxString& aErrorMsg )
1156{
1157 // Store last used fillet radius to allow pressing "enter" if repeat fillet is required
1158 static int filletRadius = 0;
1159
1160 WX_UNIT_ENTRY_DIALOG dlg( &aFrame, _( "Fillet Lines" ), _( "Enter fillet radius:" ),
1161 filletRadius );
1162
1163 if( dlg.ShowModal() == wxID_CANCEL )
1164 return std::nullopt;
1165
1166 filletRadius = dlg.GetValue();
1167
1168 if( filletRadius == 0 )
1169 {
1170 aErrorMsg = _( "A radius of zero was entered.\n"
1171 "The fillet operation was not performed." );
1172 return std::nullopt;
1173 }
1174
1175 return filletRadius;
1176}
1177
1186static std::optional<CHAMFER_PARAMS> GetChamferParams( PCB_BASE_EDIT_FRAME& aFrame,
1187 wxString& aErrorMsg )
1188{
1189 // Non-zero and the KLC default for Fab layer chamfers
1190 const int default_setback = pcbIUScale.mmToIU( 1 );
1191 // Store last used setback to allow pressing "enter" if repeat chamfer is required
1192 static CHAMFER_PARAMS params{ default_setback, default_setback };
1193
1194 WX_UNIT_ENTRY_DIALOG dlg( &aFrame, _( "Chamfer Lines" ), _( "Enter chamfer setback:" ),
1195 params.m_chamfer_setback_a );
1196
1197 if( dlg.ShowModal() == wxID_CANCEL )
1198 return std::nullopt;
1199
1200 params.m_chamfer_setback_a = dlg.GetValue();
1201 // It's hard to easily specify an asymmetric chamfer (which line gets the longer
1202 // setbeck?), so we just use the same setback for each
1203 params.m_chamfer_setback_b = params.m_chamfer_setback_a;
1204
1205 // Some technically-valid chamfers are not useful to actually do
1206 if( params.m_chamfer_setback_a == 0 )
1207 {
1208 aErrorMsg = _( "A setback of zero was entered.\n"
1209 "The chamfer operation was not performed." );
1210 return std::nullopt;
1211 }
1212
1213 return params;
1214}
1215
1217{
1219 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1220 {
1221 std::vector<VECTOR2I> pts;
1222
1223 // Iterate from the back so we don't have to worry about removals.
1224 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1225 {
1226 BOARD_ITEM* item = aCollector[i];
1227
1228 // We've converted the polygon and rectangle to segments, so drop everything
1229 // that isn't a segment at this point
1230 if( !item->IsType( { PCB_SHAPE_LOCATE_SEGMENT_T,
1231 PCB_SHAPE_LOCATE_POLY_T,
1232 PCB_SHAPE_LOCATE_RECT_T } ) )
1233 {
1234 aCollector.Remove( item );
1235 }
1236 }
1237 },
1238 true /* prompt user regarding locked items */ );
1239
1240 std::set<PCB_SHAPE*> lines_to_add;
1241 std::vector<PCB_SHAPE*> items_to_remove;
1242
1243 for( EDA_ITEM* item : selection )
1244 {
1245 std::vector<VECTOR2I> pts;
1246 PCB_SHAPE *graphic = static_cast<PCB_SHAPE*>( item );
1247 PCB_LAYER_ID layer = graphic->GetLayer();
1248 int width = graphic->GetWidth();
1249
1250 if( graphic->GetShape() == SHAPE_T::RECTANGLE )
1251 {
1252 items_to_remove.push_back( graphic );
1253 VECTOR2I start( graphic->GetStart() );
1254 VECTOR2I end( graphic->GetEnd() );
1255 pts.emplace_back( start );
1256 pts.emplace_back( VECTOR2I( end.x, start.y ) );
1257 pts.emplace_back( end );
1258 pts.emplace_back( VECTOR2I( start.x, end.y ) );
1259 }
1260
1261 if( graphic->GetShape() == SHAPE_T::POLY )
1262 {
1263 items_to_remove.push_back( graphic );
1264
1265 for( int jj = 0; jj < graphic->GetPolyShape().VertexCount(); ++jj )
1266 pts.emplace_back( graphic->GetPolyShape().CVertex( jj ) );
1267 }
1268
1269 for( size_t jj = 1; jj < pts.size(); ++jj )
1270 {
1271 PCB_SHAPE *line = new PCB_SHAPE( frame()->GetModel(), SHAPE_T::SEGMENT );
1272
1273 line->SetStart( pts[jj - 1] );
1274 line->SetEnd( pts[jj] );
1275 line->SetWidth( width );
1276 line->SetLayer( layer );
1277 lines_to_add.insert( line );
1278 }
1279
1280 if( pts.size() > 1 )
1281 {
1282 PCB_SHAPE *line = new PCB_SHAPE( frame()->GetModel(), SHAPE_T::SEGMENT );
1283
1284 line->SetStart( pts.back() );
1285 line->SetEnd( pts.front() );
1286 line->SetWidth( width );
1287 line->SetLayer( layer );
1288 lines_to_add.insert( line );
1289 }
1290 }
1291
1292 for( PCB_SHAPE* item : lines_to_add )
1293 selection.Add( item );
1294
1295 for( PCB_SHAPE* item : items_to_remove )
1296 selection.Remove( item );
1297
1298 if( selection.CountType( PCB_SHAPE_LOCATE_SEGMENT_T ) < 2 )
1299 {
1300 frame()->ShowInfoBarMsg( _( "A shape with least two lines must be selected." ) );
1301 return 0;
1302 }
1303
1304 BOARD_COMMIT commit{ this };
1305
1306 // List of thing to select at the end of the operation
1307 // (doing it as we go will invalidate the iterator)
1308 std::vector<PCB_SHAPE*> items_to_select_on_success;
1309
1310 // And same for items to deselect
1311 std::vector<PCB_SHAPE*> items_to_deselect_on_success;
1312
1313 // Handle modifications to existing items by the routine
1314 // How to deal with this depends on whether we're in the footprint editor or not
1315 // and whether the item was conjured up by decomposing a polygon or rectangle
1316 const auto item_modification_handler = [&]( PCB_SHAPE& aItem )
1317 {
1318 // If the item was "conjured up" it will be added later separately
1319 if( !alg::contains( lines_to_add, &aItem ) )
1320 {
1321 commit.Modify( &aItem );
1322 items_to_select_on_success.push_back( &aItem );
1323 }
1324 };
1325
1326 bool any_items_created = !lines_to_add.empty();
1327 const auto item_creation_handler = [&]( std::unique_ptr<PCB_SHAPE> aItem )
1328 {
1329 any_items_created = true;
1330 items_to_select_on_success.push_back( aItem.get() );
1331 commit.Add( aItem.release() );
1332 };
1333
1334 bool any_items_removed = !items_to_remove.empty();
1335 const auto item_removal_handler = [&]( PCB_SHAPE& aItem )
1336 {
1337 any_items_removed = true;
1338 items_to_deselect_on_success.push_back( &aItem );
1339 commit.Remove( &aItem );
1340 };
1341
1342 // Combine these callbacks into a CHANGE_HANDLER to inject in the ROUTINE
1344 item_creation_handler, item_modification_handler, item_removal_handler );
1345
1346 // Construct an appropriate tool
1347 std::unique_ptr<PAIRWISE_LINE_ROUTINE> pairwise_line_routine;
1348 wxString error_message;
1349
1350 if( aEvent.IsAction( &PCB_ACTIONS::filletLines ) )
1351 {
1352 const std::optional<int> filletRadiusIU = GetFilletParams( *frame(), error_message );
1353
1354 if( filletRadiusIU.has_value() )
1355 {
1356 pairwise_line_routine = std::make_unique<LINE_FILLET_ROUTINE>(
1357 frame()->GetModel(), change_handler, *filletRadiusIU );
1358 }
1359 }
1360 else if( aEvent.IsAction( &PCB_ACTIONS::chamferLines ) )
1361 {
1362 const std::optional<CHAMFER_PARAMS> chamfer_params =
1363 GetChamferParams( *frame(), error_message );
1364
1365 if( chamfer_params.has_value() )
1366 {
1367 pairwise_line_routine = std::make_unique<LINE_CHAMFER_ROUTINE>(
1368 frame()->GetModel(), change_handler, *chamfer_params );
1369 }
1370 }
1371 else if( aEvent.IsAction( &PCB_ACTIONS::extendLines ) )
1372 {
1373 if( selection.CountType( PCB_SHAPE_LOCATE_SEGMENT_T ) != 2 )
1374 {
1375 error_message = _( "Exactly two lines must be selected to extend them." );
1376 }
1377 else
1378 {
1379 pairwise_line_routine =
1380 std::make_unique<LINE_EXTENSION_ROUTINE>( frame()->GetModel(), change_handler );
1381 }
1382 }
1383
1384 if( !pairwise_line_routine )
1385 {
1386 // Didn't construct any mofication routine - could be an error or cancellation
1387 if( !error_message.empty() )
1388 frame()->ShowInfoBarMsg( error_message );
1389
1390 return 0;
1391 }
1392
1393 // Apply the tool to every line pair
1394 alg::for_all_pairs( selection.begin(), selection.end(),
1395 [&]( EDA_ITEM* a, EDA_ITEM* b )
1396 {
1397 PCB_SHAPE* line_a = static_cast<PCB_SHAPE*>( a );
1398 PCB_SHAPE* line_b = static_cast<PCB_SHAPE*>( b );
1399
1400 pairwise_line_routine->ProcessLinePair( *line_a, *line_b );
1401 } );
1402
1403 // Items created like lines from a rectangle
1404 // Some of these might have been changed by the tool, but we need to
1405 // add *all* of them to the selection and commit them
1406 for( PCB_SHAPE* item : lines_to_add )
1407 {
1408 m_selectionTool->AddItemToSel( item, true );
1409 commit.Add( item );
1410 }
1411
1412 // Remove items like rectangles that we decomposed into lines
1413 for( PCB_SHAPE* item : items_to_remove )
1414 {
1415 commit.Remove( item );
1416 m_selectionTool->RemoveItemFromSel( item, true );
1417 }
1418
1419 // Select added and modified items
1420 for( PCB_SHAPE* item : items_to_select_on_success )
1421 m_selectionTool->AddItemToSel( item, true );
1422
1423 // Deselect removed items
1424 for( PCB_SHAPE* item : items_to_deselect_on_success )
1425 m_selectionTool->RemoveItemFromSel( item, true );
1426
1427 if( any_items_removed )
1428 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1429
1430 if( any_items_created )
1431 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1432
1433 // Notify other tools of the changes
1434 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1435
1436 commit.Push( pairwise_line_routine->GetCommitDescription() );
1437
1438 if (const std::optional<wxString> msg = pairwise_line_routine->GetStatusMessage()) {
1439 frame()->ShowInfoBarMsg( *msg );
1440 }
1441
1442 return 0;
1443}
1444
1445
1447{
1449 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1450 {
1451 std::vector<VECTOR2I> pts;
1452
1453 // Iterate from the back so we don't have to worry about removals.
1454 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1455 {
1456 BOARD_ITEM* item = aCollector[i];
1457
1458 // We've converted the polygon and rectangle to segments, so drop everything
1459 // that isn't a segment at this point
1460 if( !item->IsType( { PCB_SHAPE_LOCATE_SEGMENT_T, PCB_SHAPE_LOCATE_ARC_T,
1461 PCB_SHAPE_LOCATE_BEZIER_T } ) )
1462 {
1463 aCollector.Remove( item );
1464 }
1465 }
1466 },
1467 true /* prompt user regarding locked items */ );
1468
1469 // Store last used value
1470 static int s_toleranceValue = pcbIUScale.mmToIU( 3 );
1471
1472 WX_UNIT_ENTRY_DIALOG dlg( frame(), _( "Heal Shapes" ), _( "Tolerance value:" ),
1473 s_toleranceValue );
1474
1475 if( dlg.ShowModal() == wxID_CANCEL )
1476 return 0;
1477
1478 s_toleranceValue = dlg.GetValue();
1479
1480 if( s_toleranceValue <= 0 )
1481 return 0;
1482
1483 BOARD_COMMIT commit{ this };
1484
1485 std::vector<PCB_SHAPE*> shapeList;
1486 std::vector<std::unique_ptr<PCB_SHAPE>> newShapes;
1487
1488 for( EDA_ITEM* item : selection )
1489 {
1490 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item ) )
1491 {
1492 shapeList.push_back( shape );
1493 commit.Modify( shape );
1494 }
1495 }
1496
1497 ConnectBoardShapes( shapeList, newShapes, s_toleranceValue );
1498
1499 std::vector<PCB_SHAPE*> items_to_select;
1500
1501 for( std::unique_ptr<PCB_SHAPE>& ptr : newShapes )
1502 {
1503 PCB_SHAPE* shape = ptr.release();
1504
1505 commit.Add( shape );
1506 items_to_select.push_back( shape );
1507 }
1508
1509 commit.Push( _( "Heal shapes" ) );
1510
1511 // Select added items
1512 for( PCB_SHAPE* item : items_to_select )
1513 m_selectionTool->AddItemToSel( item, true );
1514
1515 if( items_to_select.size() > 0 )
1516 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1517
1518 // Notify other tools of the changes
1519 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1520
1521 return 0;
1522}
1523
1524
1526{
1528 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1529 {
1530 // Iterate from the back so we don't have to worry about removals.
1531 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1532 {
1533 BOARD_ITEM* item = aCollector[i];
1534
1535 if( !item->IsType( {
1536 PCB_SHAPE_LOCATE_POLY_T,
1537 PCB_SHAPE_LOCATE_RECT_T,
1538 } ) )
1539 {
1540 aCollector.Remove( item );
1541 }
1542 }
1543 },
1544 true /* prompt user regarding locked items */ );
1545
1546 const EDA_ITEM* const last_item = selection.GetLastAddedItem();
1547
1548 // Gather or construct polygon source shapes to merge
1549 std::vector<PCB_SHAPE*> items_to_process;
1550 for( EDA_ITEM* item : selection )
1551 {
1552 items_to_process.push_back( static_cast<PCB_SHAPE*>( item ) );
1553
1554 // put the last one in the selection at the front of the vector
1555 // so it can be used as the property donor and as the basis for the
1556 // boolean operation
1557 if( item == last_item )
1558 {
1559 std::swap( items_to_process.back(), items_to_process.front() );
1560 }
1561 }
1562
1563 BOARD_COMMIT commit{ this };
1564
1565 // Handle modifications to existing items by the routine
1566 const auto item_modification_handler = [&]( PCB_SHAPE& aItem )
1567 {
1568 commit.Modify( &aItem );
1569 };
1570
1571 std::vector<PCB_SHAPE*> items_to_select_on_success;
1572 const auto item_creation_handler = [&]( std::unique_ptr<PCB_SHAPE> aItem )
1573 {
1574 items_to_select_on_success.push_back( aItem.get() );
1575 commit.Add( aItem.release() );
1576 };
1577
1578 const auto item_removal_handler = [&]( PCB_SHAPE& aItem )
1579 {
1580 commit.Remove( &aItem );
1581 };
1582
1583 // Combine these callbacks into a CHANGE_HANDLER to inject in the ROUTINE
1585 item_creation_handler, item_modification_handler, item_removal_handler );
1586
1587 // Construct an appropriate routine
1588 std::unique_ptr<POLYGON_BOOLEAN_ROUTINE> boolean_routine;
1589 if( aEvent.IsAction( &PCB_ACTIONS::mergePolygons ) )
1590 {
1591 boolean_routine =
1592 std::make_unique<POLYGON_MERGE_ROUTINE>( frame()->GetModel(), change_handler );
1593 }
1594 else if( aEvent.IsAction( &PCB_ACTIONS::subtractPolygons ) )
1595 {
1596 boolean_routine =
1597 std::make_unique<POLYGON_SUBTRACT_ROUTINE>( frame()->GetModel(), change_handler );
1598 }
1599 else if( aEvent.IsAction( &PCB_ACTIONS::intersectPolygons ) )
1600 {
1601 boolean_routine =
1602 std::make_unique<POLYGON_INTERSECT_ROUTINE>( frame()->GetModel(), change_handler );
1603 }
1604 else
1605 {
1606 wxASSERT_MSG( false, "Could not find a polygon routine for this action" );
1607 return 0;
1608 }
1609
1610 // Perform the operation on each polygon
1611 for( PCB_SHAPE* shape : items_to_process )
1612 {
1613 boolean_routine->ProcessShape( *shape );
1614 }
1615
1616 // Select new items
1617 for( PCB_SHAPE* item : items_to_select_on_success )
1618 {
1619 m_selectionTool->AddItemToSel( item, true );
1620 }
1621
1622 // Notify other tools of the changes
1623 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1624
1625 commit.Push( boolean_routine->GetCommitDescription() );
1626
1627 if( const std::optional<wxString> msg = boolean_routine->GetStatusMessage() )
1628 {
1629 frame()->ShowInfoBarMsg( *msg );
1630 }
1631
1632 return 0;
1633}
1634
1635
1637{
1638 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1640 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1641 {
1642 } );
1643
1644 // Tracks & vias are treated in a special way:
1645 if( ( SELECTION_CONDITIONS::OnlyTypes( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } ) )( selection ) )
1646 {
1647 DIALOG_TRACK_VIA_PROPERTIES dlg( editFrame, selection );
1648 dlg.ShowQuasiModal(); // QuasiModal required for NET_SELECTOR
1649 }
1650 else if( selection.Size() == 1 )
1651 {
1652 // Display properties dialog
1653 if( BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( selection.Front() ) )
1654 {
1655 // Do not handle undo buffer, it is done by the properties dialogs
1656 editFrame->OnEditItemRequest( item );
1657
1658 // Notify other tools of the changes
1660 }
1661 }
1662 else if( selection.Size() == 0 && getView()->IsLayerVisible( LAYER_DRAWINGSHEET ) )
1663 {
1664 DS_PROXY_VIEW_ITEM* ds = editFrame->GetCanvas()->GetDrawingSheet();
1665 VECTOR2D cursorPos = getViewControls()->GetCursorPosition( false );
1666
1667 if( ds && ds->HitTestDrawingSheetItems( getView(), cursorPos ) )
1669 else
1671 }
1672
1673 if( selection.IsHover() )
1674 {
1676 }
1677 else
1678 {
1679 // Check for items becoming invisible and drop them from the selection.
1680
1681 PCB_SELECTION selCopy = selection;
1682 LSET visible = editFrame->GetBoard()->GetVisibleLayers();
1683
1684 for( EDA_ITEM* eda_item : selCopy )
1685 {
1686 if( BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( eda_item ) )
1687 {
1688 if( !( item->GetLayerSet() & visible ).any() )
1690 }
1691 }
1692 }
1693
1694 return 0;
1695}
1696
1697
1698int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
1699{
1700 if( isRouterActive() )
1701 {
1702 wxBell();
1703 return 0;
1704 }
1705
1706 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1707 BOARD_COMMIT localCommit( this );
1708 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
1709
1710 if( !commit )
1711 commit = &localCommit;
1712
1713 // Be sure that there is at least one item that we can modify. If nothing was selected before,
1714 // try looking for the stuff under mouse cursor (i.e. KiCad old-style hover selection)
1716 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1717 {
1718 sTool->FilterCollectorForHierarchy( aCollector, true );
1719 sTool->FilterCollectorForMarkers( aCollector );
1720 },
1721 // Prompt user regarding locked items if in board editor and in free-pad-mode (if
1722 // we're not in free-pad mode we delay this until the second RequestSelection()).
1724
1725 if( selection.Empty() )
1726 return 0;
1727
1728 std::optional<VECTOR2I> oldRefPt;
1729 bool is_hover = selection.IsHover(); // N.B. This must be saved before the second
1730 // call to RequestSelection() below
1731
1732 if( selection.HasReferencePoint() )
1733 oldRefPt = selection.GetReferencePoint();
1734
1735 // Now filter out pads if not in free pads mode. We cannot do this in the first
1736 // RequestSelection() as we need the reference point when a pad is the selection front.
1738 {
1739 selection = m_selectionTool->RequestSelection(
1740 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1741 {
1742 sTool->FilterCollectorForMarkers( aCollector );
1743 sTool->FilterCollectorForHierarchy( aCollector, true );
1744 sTool->FilterCollectorForFreePads( aCollector );
1745 },
1746 true /* prompt user regarding locked items */ );
1747 }
1748
1749 // Did we filter everything out? If so, don't try to operate further
1750 if( selection.Empty() )
1751 return 0;
1752
1753 if( selection.Size() == 1 )
1754 {
1755 if( BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( selection.Front() ) )
1756 selection.SetReferencePoint( item->GetCenter() );
1757 }
1758 else
1759 {
1760 updateModificationPoint( selection );
1761 }
1762
1763 VECTOR2I refPt = selection.GetReferencePoint();
1764 EDA_ANGLE rotateAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *editFrame, aEvent );
1765
1766 if( frame()->GetCanvas()->GetView()->GetGAL()->IsFlippedX() )
1767 rotateAngle = -rotateAngle;
1768
1769 // Calculate view bounding box
1770 BOX2I viewBBox = selection.Front()->ViewBBox();
1771
1772 for( EDA_ITEM* item : selection )
1773 viewBBox.Merge( item->ViewBBox() );
1774
1775 // Check if the view bounding box will go out of bounds
1776 VECTOR2D rotPos = viewBBox.GetPosition();
1777 VECTOR2D rotEnd = viewBBox.GetEnd();
1778
1779 RotatePoint( &rotPos.x, &rotPos.y, refPt.x, refPt.y, rotateAngle );
1780 RotatePoint( &rotEnd.x, &rotEnd.y, refPt.x, refPt.y, rotateAngle );
1781
1782 typedef std::numeric_limits<int> coord_limits;
1783
1784 int max = coord_limits::max() - COORDS_PADDING;
1785 int min = -max;
1786
1787 bool outOfBounds = rotPos.x < min || rotPos.x > max || rotPos.y < min || rotPos.y > max
1788 || rotEnd.x < min || rotEnd.x > max || rotEnd.y < min || rotEnd.y > max;
1789
1790 if( !outOfBounds )
1791 {
1792 for( EDA_ITEM* item : selection )
1793 {
1794 if( !item->IsNew() && !item->IsMoving() )
1795 commit->Modify( item );
1796
1797 if( BOARD_ITEM* board_item = dynamic_cast<BOARD_ITEM*>( item ) )
1798 {
1799 board_item->Rotate( refPt, rotateAngle );
1800 board_item->Normalize();
1801 }
1802 }
1803
1804 if( !localCommit.Empty() )
1805 localCommit.Push( _( "Rotate" ) );
1806
1807 if( is_hover && !m_dragging )
1809
1811
1812 if( m_dragging )
1814 }
1815
1816 // Restore the old reference so any mouse dragging that occurs doesn't make the selection jump
1817 // to this now invalid reference
1818 if( oldRefPt )
1819 selection.SetReferencePoint( *oldRefPt );
1820 else
1821 selection.ClearReferencePoint();
1822
1823 return 0;
1824}
1825
1826
1830static VECTOR2I mirrorPointX( const VECTOR2I& aPoint, const VECTOR2I& aMirrorPoint )
1831{
1832 VECTOR2I mirrored = aPoint;
1833
1834 mirrored.x -= aMirrorPoint.x;
1835 mirrored.x = -mirrored.x;
1836 mirrored.x += aMirrorPoint.x;
1837
1838 return mirrored;
1839}
1840
1841
1845static VECTOR2I mirrorPointY( const VECTOR2I& aPoint, const VECTOR2I& aMirrorPoint )
1846{
1847 VECTOR2I mirrored = aPoint;
1848
1849 mirrored.y -= aMirrorPoint.y;
1850 mirrored.y = -mirrored.y;
1851 mirrored.y += aMirrorPoint.y;
1852
1853 return mirrored;
1854}
1855
1856
1860static void mirrorPadX( PAD& aPad, const VECTOR2I& aMirrorPoint )
1861{
1862 if( aPad.GetShape() == PAD_SHAPE::CUSTOM )
1863 aPad.FlipPrimitives( true ); // mirror primitives left to right
1864
1865 VECTOR2I tmpPt = mirrorPointX( aPad.GetPosition(), aMirrorPoint );
1866 aPad.SetPosition( tmpPt );
1867
1868 tmpPt = aPad.GetOffset();
1869 tmpPt.x = -tmpPt.x;
1870 aPad.SetOffset( tmpPt );
1871
1872 auto tmpz = aPad.GetDelta();
1873 tmpz.x = -tmpz.x;
1874 aPad.SetDelta( tmpz );
1875
1876 aPad.SetOrientation( -aPad.GetOrientation() );
1877}
1878
1879
1883static void mirrorPadY( PAD& aPad, const VECTOR2I& aMirrorPoint )
1884{
1885 if( aPad.GetShape() == PAD_SHAPE::CUSTOM )
1886 aPad.FlipPrimitives( false ); // mirror primitives top to bottom
1887
1888 VECTOR2I tmpPt = mirrorPointY( aPad.GetPosition(), aMirrorPoint );
1889 aPad.SetPosition( tmpPt );
1890
1891 tmpPt = aPad.GetOffset();
1892 tmpPt.y = -tmpPt.y;
1893 aPad.SetOffset( tmpPt );
1894
1895 auto tmpz = aPad.GetDelta();
1896 tmpz.y = -tmpz.y;
1897 aPad.SetDelta( tmpz );
1898
1899 aPad.SetOrientation( -aPad.GetOrientation() );
1900}
1901
1902
1903const std::vector<KICAD_T> EDIT_TOOL::MirrorableItems = {
1906 PCB_TEXT_T,
1908 PCB_ZONE_T,
1909 PCB_PAD_T,
1911 PCB_ARC_T,
1912 PCB_VIA_T,
1913};
1914
1915int EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
1916{
1917 if( isRouterActive() )
1918 {
1919 wxBell();
1920 return 0;
1921 }
1922
1923 BOARD_COMMIT localCommit( this );
1924 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
1925
1926 if( !commit )
1927 commit = &localCommit;
1928
1930 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1931 {
1932 sTool->FilterCollectorForMarkers( aCollector );
1933 sTool->FilterCollectorForHierarchy( aCollector, true );
1934 sTool->FilterCollectorForFreePads( aCollector );
1935 },
1936 !m_dragging /* prompt user regarding locked items */ );
1937
1938 if( selection.Empty() )
1939 return 0;
1940
1941 updateModificationPoint( selection );
1942 VECTOR2I mirrorPoint = selection.GetReferencePoint();
1943
1944 // Set the mirroring options.
1945 // Unfortunately, the mirror function do not have the same parameter for all items
1946 // So we need these 2 parameters to avoid mistakes
1947 bool mirrorLeftRight = true;
1948 bool mirrorAroundXaxis = false;
1949
1950 if( aEvent.IsAction( &PCB_ACTIONS::mirrorV ) )
1951 {
1952 mirrorLeftRight = false;
1953 mirrorAroundXaxis = true;
1954 }
1955
1956 for( EDA_ITEM* item : selection )
1957 {
1958 if( !item->IsType( MirrorableItems ) )
1959 continue;
1960
1961 if( !item->IsNew() && !item->IsMoving() )
1962 commit->Modify( item );
1963
1964 // modify each object as necessary
1965 switch( item->Type() )
1966 {
1967 case PCB_SHAPE_T:
1968 static_cast<PCB_SHAPE*>( item )->Mirror( mirrorPoint, mirrorAroundXaxis );
1969 break;
1970
1971 case PCB_ZONE_T:
1972 static_cast<ZONE*>( item )->Mirror( mirrorPoint, mirrorLeftRight );
1973 break;
1974
1975 case PCB_FIELD_T:
1976 case PCB_TEXT_T:
1977 static_cast<PCB_TEXT*>( item )->Mirror( mirrorPoint, mirrorAroundXaxis );
1978 break;
1979
1980 case PCB_TEXTBOX_T:
1981 static_cast<PCB_TEXTBOX*>( item )->Mirror( mirrorPoint, mirrorAroundXaxis );
1982 break;
1983
1984 case PCB_PAD_T:
1985 if( mirrorLeftRight )
1986 mirrorPadX( *static_cast<PAD*>( item ), mirrorPoint );
1987 else
1988 mirrorPadY( *static_cast<PAD*>( item ), mirrorPoint );
1989
1990 break;
1991
1992 case PCB_TRACE_T:
1993 case PCB_ARC_T:
1994 case PCB_VIA_T:
1995 static_cast<PCB_TRACK*>( item )->Mirror( mirrorPoint, mirrorAroundXaxis );
1996 break;
1997
1998 default:
1999 // it's likely the commit object is wrong if you get here
2000 // Unsure if PCB_GROUP_T needs special attention here.
2001 assert( false );
2002 break;
2003 }
2004 }
2005
2006 if( !localCommit.Empty() )
2007 localCommit.Push( _( "Mirror" ) );
2008
2009 if( selection.IsHover() && !m_dragging )
2011
2013
2014 if( m_dragging )
2016
2017 return 0;
2018}
2019
2020
2021int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent )
2022{
2023 if( isRouterActive() )
2024 {
2025 wxBell();
2026 return 0;
2027 }
2028
2029 BOARD_COMMIT localCommit( this );
2030 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
2031
2032 if( !commit )
2033 commit = &localCommit;
2034
2036 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2037 {
2038 sTool->FilterCollectorForMarkers( aCollector );
2039 sTool->FilterCollectorForHierarchy( aCollector, true );
2040 sTool->FilterCollectorForFreePads( aCollector );
2041 },
2042 !m_dragging /* prompt user regarding locked items */ );
2043
2044 if( selection.Empty() )
2045 return 0;
2046
2047 std::optional<VECTOR2I> oldRefPt;
2048
2049 if( selection.HasReferencePoint() )
2050 oldRefPt = selection.GetReferencePoint();
2051
2052 updateModificationPoint( selection );
2053
2054 // Flip around the anchor for footprints, and the bounding box center for board items
2055 VECTOR2I refPt = IsFootprintEditor() ? VECTOR2I( 0, 0 ) : selection.GetCenter();
2056
2057 // If only one item selected, flip around the selection or item anchor point (instead
2058 // of the bounding box center) to avoid moving the item anchor
2059 if( selection.GetSize() == 1 )
2060 refPt = selection.GetReferencePoint();
2061
2062 bool leftRight = frame()->GetPcbNewSettings()->m_FlipLeftRight;
2063
2064 for( EDA_ITEM* item : selection )
2065 {
2066 if( BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( item ) )
2067 {
2068 if( !boardItem->IsNew() && !boardItem->IsMoving() )
2069 commit->Modify( boardItem );
2070
2071 boardItem->Flip( refPt, leftRight );
2072 boardItem->Normalize();
2073 }
2074 }
2075
2076 if( !localCommit.Empty() )
2077 localCommit.Push( _( "Change Side / Flip" ) );
2078
2079 if( selection.IsHover() && !m_dragging )
2081
2083
2084 if( m_dragging )
2086
2087 // Restore the old reference so any mouse dragging that occurs doesn't make the selection jump
2088 // to this now invalid reference
2089 if( oldRefPt )
2090 selection.SetReferencePoint( *oldRefPt );
2091 else
2092 selection.ClearReferencePoint();
2093
2094 return 0;
2095}
2096
2097
2098void EDIT_TOOL::DeleteItems( const PCB_SELECTION& aItems, bool aIsCut )
2099{
2100 BOARD_COMMIT commit( this );
2101
2102 // As we are about to remove items, they have to be removed from the selection first
2104
2105 for( EDA_ITEM* item : aItems )
2106 {
2107 BOARD_ITEM* board_item = dynamic_cast<BOARD_ITEM*>( item );
2108 wxCHECK2( board_item, continue );
2109
2110 FOOTPRINT* parentFP = board_item->GetParentFootprint();
2111
2112 if( PCB_GROUP* parentGroup = board_item->GetParentGroup() )
2113 {
2114 commit.Modify( parentGroup );
2115 parentGroup->RemoveItem( board_item );
2116 }
2117
2118 switch( item->Type() )
2119 {
2120 case PCB_FIELD_T:
2121 wxASSERT( parentFP );
2122 commit.Modify( parentFP );
2123 static_cast<PCB_TEXT*>( board_item )->SetVisible( false );
2124 getView()->Update( board_item );
2125 break;
2126
2127 case PCB_TEXT_T:
2128 case PCB_SHAPE_T:
2129 case PCB_TEXTBOX_T:
2131 case PCB_DIMENSION_T:
2132 case PCB_DIM_ALIGNED_T:
2133 case PCB_DIM_LEADER_T:
2134 case PCB_DIM_CENTER_T:
2135 case PCB_DIM_RADIAL_T:
2137 case PCB_GROUP_T:
2138 commit.Remove( board_item );
2139 break;
2140
2141 case PCB_PAD_T:
2142 if( IsFootprintEditor() || frame()->GetPcbNewSettings()->m_AllowFreePads )
2143 {
2144 commit.Modify( parentFP );
2145 getView()->Remove( board_item );
2146 parentFP->Remove( board_item );
2147 }
2148
2149 break;
2150
2151 case PCB_ZONE_T:
2152 // We process the zones special so that cutouts can be deleted when the delete
2153 // tool is called from inside a cutout when the zone is selected.
2154 // Only interact with cutouts when deleting and a single item is selected
2155 if( !aIsCut && aItems.GetSize() == 1 )
2156 {
2158 ZONE* zone = static_cast<ZONE*>( board_item );
2159
2160 int outlineIdx, holeIdx;
2161
2162 if( zone->HitTestCutout( curPos, &outlineIdx, &holeIdx ) )
2163 {
2164 // Remove the cutout
2165 commit.Modify( zone );
2166 zone->RemoveCutout( outlineIdx, holeIdx );
2167 zone->UnFill();
2168
2169 // TODO Refill zone when KiCad supports auto re-fill
2170
2171 // Update the display
2172 zone->HatchBorder();
2173 canvas()->Refresh();
2174
2175 // Restore the selection on the original zone
2177
2178 break;
2179 }
2180 }
2181
2182 // Remove the entire zone otherwise
2183 commit.Remove( board_item );
2184 break;
2185
2186 case PCB_GENERATOR_T:
2187 if( aItems.Size() == 1 )
2188 {
2189 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( board_item );
2190
2192 generator );
2193 }
2194 else
2195 {
2196 commit.Remove( board_item );
2197 }
2198
2199 break;
2200
2201 default:
2202 wxASSERT_MSG( parentFP == nullptr, wxT( "Try to delete an item living in a footrprint" ) );
2203 commit.Remove( board_item );
2204 break;
2205 }
2206 }
2207
2208 // If the entered group has been emptied then leave it.
2209 PCB_GROUP* enteredGroup = m_selectionTool->GetEnteredGroup();
2210
2211 if( enteredGroup && enteredGroup->GetItems().empty() )
2213
2214 if( aIsCut )
2215 commit.Push( _( "Cut" ) );
2216 else
2217 commit.Push( _( "Delete" ) );
2218}
2219
2220
2221int EDIT_TOOL::Remove( const TOOL_EVENT& aEvent )
2222{
2223 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2224
2225 editFrame->PushTool( aEvent );
2226
2227 std::vector<BOARD_ITEM*> lockedItems;
2228 Activate();
2229
2230 // get a copy instead of reference (as we're going to clear the selection before removing items)
2231 PCB_SELECTION selectionCopy;
2234
2235 // If we are in a "Cut" operation, then the copied selection exists already and we want to
2236 // delete exactly that; no more, no fewer. Any filtering for locked items must be done in
2237 // the copyToClipboard() routine.
2238 if( isCut )
2239 {
2240 selectionCopy = m_selectionTool->GetSelection();
2241 }
2242 else
2243 {
2244 // When not in free-pad mode we normally auto-promote selected pads to their parent
2245 // footprints. But this is probably a little too dangerous for a destructive operation,
2246 // so we just do the promotion but not the deletion (allowing for a second delete to do
2247 // it if that's what the user wanted).
2248 selectionCopy = m_selectionTool->RequestSelection(
2249 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2250 {
2251 } );
2252
2253 size_t beforeFPCount = selectionCopy.CountType( PCB_FOOTPRINT_T );
2254
2256 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2257 {
2258 sTool->FilterCollectorForFreePads( aCollector );
2259 } );
2260
2261 if( !selectionCopy.IsHover()
2262 && m_selectionTool->GetSelection().CountType( PCB_FOOTPRINT_T ) > beforeFPCount )
2263 {
2264 wxBell();
2265 canvas()->Refresh();
2266 editFrame->PopTool( aEvent );
2267 return 0;
2268 }
2269
2270 // In "alternative" mode, we expand selected track items to their full connection.
2271 if( isAlt && ( selectionCopy.HasType( PCB_TRACE_T ) || selectionCopy.HasType( PCB_VIA_T ) ) )
2272 {
2274 }
2275
2276 // Finally run RequestSelection() one more time to find out what user wants to do about
2277 // locked objects.
2278 selectionCopy = m_selectionTool->RequestSelection(
2279 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2280 {
2281 sTool->FilterCollectorForFreePads( aCollector );
2282 },
2283 true /* prompt user regarding locked items */ );
2284 }
2285
2286 DeleteItems( selectionCopy, isCut );
2287 canvas()->Refresh();
2288
2289 editFrame->PopTool( aEvent );
2290 return 0;
2291}
2292
2293
2295{
2296 if( isRouterActive() )
2297 {
2298 wxBell();
2299 return 0;
2300 }
2301
2303 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2304 {
2305 sTool->FilterCollectorForMarkers( aCollector );
2306 sTool->FilterCollectorForHierarchy( aCollector, true );
2307 },
2308 true /* prompt user regarding locked items */ );
2309
2310 if( selection.Empty() )
2311 return 0;
2312
2313 VECTOR2I translation;
2314 EDA_ANGLE rotation;
2315 ROTATION_ANCHOR rotationAnchor = selection.Size() > 1 ? ROTATE_AROUND_SEL_CENTER
2317
2318 // TODO: Implement a visible bounding border at the edge
2319 BOX2I sel_box = selection.GetBoundingBox();
2320
2321 DIALOG_MOVE_EXACT dialog( frame(), translation, rotation, rotationAnchor, sel_box );
2322 int ret = dialog.ShowModal();
2323
2324 if( ret == wxID_OK )
2325 {
2326 BOARD_COMMIT commit( this );
2327 EDA_ANGLE angle = rotation;
2328 VECTOR2I rp = selection.GetCenter();
2329 VECTOR2I selCenter( rp.x, rp.y );
2330
2331 // Make sure the rotation is from the right reference point
2332 selCenter += translation;
2333
2334 if( !frame()->GetPcbNewSettings()->m_Display.m_DisplayInvertYAxis )
2335 rotation = -rotation;
2336
2337 for( EDA_ITEM* item : selection )
2338 {
2339 BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( item );
2340
2341 wxCHECK2( boardItem, continue );
2342
2343 if( !boardItem->IsNew() )
2344 commit.Modify( boardItem );
2345
2346 if( !boardItem->GetParent() || !boardItem->GetParent()->IsSelected() )
2347 boardItem->Move( translation );
2348
2349 switch( rotationAnchor )
2350 {
2352 boardItem->Rotate( boardItem->GetPosition(), angle );
2353 break;
2354 case ROTATE_AROUND_SEL_CENTER: boardItem->Rotate( selCenter, angle );
2355 break;
2357 boardItem->Rotate( frame()->GetScreen()->m_LocalOrigin, angle );
2358 break;
2360 boardItem->Rotate( board()->GetDesignSettings().GetAuxOrigin(), angle );
2361 break;
2362 }
2363
2364 if( !m_dragging )
2365 getView()->Update( boardItem );
2366 }
2367
2368 commit.Push( _( "Move exact" ) );
2369
2370 if( selection.IsHover() )
2372
2374
2375 if( m_dragging )
2377 }
2378
2379 return 0;
2380}
2381
2382
2384{
2385 if( isRouterActive() )
2386 {
2387 wxBell();
2388 return 0;
2389 }
2390
2391 bool increment = aEvent.IsAction( &PCB_ACTIONS::duplicateIncrement );
2392
2393 // Be sure that there is at least one item that we can modify
2395 []( const VECTOR2I&, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2396 {
2397 sTool->FilterCollectorForFreePads( aCollector, true );
2398 sTool->FilterCollectorForMarkers( aCollector );
2399 sTool->FilterCollectorForHierarchy( aCollector, true );
2400
2401 // Iterate from the back so we don't have to worry about removals.
2402 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
2403 {
2404 BOARD_ITEM* item = aCollector[i];
2405
2406 if( item->Type() == PCB_GENERATOR_T )
2407 {
2408 // Could you duplicate something like a generated stitching pattern?
2409 // Dunno. But duplicating a tuning pattern is a sure crash.
2410 //
2411 aCollector.Remove( item );
2412 }
2413 }
2414 } );
2415
2416 if( selection.Empty() )
2417 return 0;
2418
2419 // we have a selection to work on now, so start the tool process
2420 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2421 BOARD_COMMIT commit( this );
2422
2423 // If the selection was given a hover, we do not keep the selection after completion
2424 bool is_hover = selection.IsHover();
2425
2426 std::vector<BOARD_ITEM*> new_items;
2427 new_items.reserve( selection.Size() );
2428
2429 // Each selected item is duplicated and pushed to new_items list
2430 // Old selection is cleared, and new items are then selected.
2431 for( EDA_ITEM* item : selection )
2432 {
2433 BOARD_ITEM* dupe_item = nullptr;
2434 BOARD_ITEM* orig_item = dynamic_cast<BOARD_ITEM*>( item );
2435
2436 wxCHECK2( orig_item, continue );
2437
2438 if( m_isFootprintEditor )
2439 {
2440 FOOTPRINT* parentFootprint = editFrame->GetBoard()->GetFirstFootprint();
2441 dupe_item = parentFootprint->DuplicateItem( orig_item );
2442
2443 if( increment && dupe_item->Type() == PCB_PAD_T
2444 && static_cast<PAD*>( dupe_item )->CanHaveNumber() )
2445 {
2446 PAD_TOOL* padTool = m_toolMgr->GetTool<PAD_TOOL>();
2447 wxString padNumber = padTool->GetLastPadNumber();
2448 padNumber = parentFootprint->GetNextPadNumber( padNumber );
2449 padTool->SetLastPadNumber( padNumber );
2450 static_cast<PAD*>( dupe_item )->SetNumber( padNumber );
2451 }
2452 }
2453 else if( /*FOOTPRINT* parentFootprint =*/ orig_item->GetParentFootprint() )
2454 {
2455 // No sub-footprint modifications allowed outside of footprint editor
2456 // and parentFootprint is not (yet?) used
2457 }
2458 else
2459 {
2460 switch( orig_item->Type() )
2461 {
2462 case PCB_FOOTPRINT_T:
2463 case PCB_TEXT_T:
2464 case PCB_TEXTBOX_T:
2466 case PCB_GENERATOR_T:
2467 case PCB_SHAPE_T:
2468 case PCB_TRACE_T:
2469 case PCB_ARC_T:
2470 case PCB_VIA_T:
2471 case PCB_ZONE_T:
2472 case PCB_TARGET_T:
2473 case PCB_DIM_ALIGNED_T:
2474 case PCB_DIM_CENTER_T:
2475 case PCB_DIM_RADIAL_T:
2477 case PCB_DIM_LEADER_T:
2478 dupe_item = orig_item->Duplicate();
2479 break;
2480
2481 case PCB_GROUP_T:
2482 dupe_item = static_cast<PCB_GROUP*>( orig_item )->DeepDuplicate();
2483 break;
2484
2485 default:
2486 wxASSERT_MSG( false, wxString::Format( wxT( "Unhandled item type %d" ),
2487 orig_item->Type() ) );
2488 break;
2489 }
2490 }
2491
2492 if( dupe_item )
2493 {
2494 // Clear the selection flag here, otherwise the PCB_SELECTION_TOOL
2495 // will not properly select it later on
2496 dupe_item->ClearSelected();
2497
2498 new_items.push_back( dupe_item );
2499 commit.Add( dupe_item );
2500 }
2501 }
2502
2503 // Clear the old selection first
2504 m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
2505
2506 // Select the new items
2507 EDA_ITEMS nItems( new_items.begin(), new_items.end() );
2508 m_toolMgr->RunAction<EDA_ITEMS*>( PCB_ACTIONS::selectItems, &nItems );
2509
2510 // record the new items as added
2511 if( !selection.Empty() )
2512 {
2513 editFrame->DisplayToolMsg( wxString::Format( _( "Duplicated %d item(s)" ),
2514 (int) new_items.size() ) );
2515
2516 // If items were duplicated, pick them up
2517 if( doMoveSelection( aEvent, &commit ) )
2518 commit.Push( _( "Duplicate" ) );
2519 else
2520 commit.Revert();
2521
2522 // Deselect the duplicated item if we originally started as a hover selection
2523 if( is_hover )
2524 m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
2525 }
2526
2527 return 0;
2528}
2529
2530
2532{
2533 if( isRouterActive() )
2534 {
2535 wxBell();
2536 return 0;
2537 }
2538
2539 // Be sure that there is at least one item that we can modify
2541 []( const VECTOR2I&, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2542 {
2543 sTool->FilterCollectorForMarkers( aCollector );
2544 sTool->FilterCollectorForHierarchy( aCollector, true );
2545 } );
2546
2547 if( selection.Empty() )
2548 return 0;
2549
2550 // we have a selection to work on now, so start the tool process
2551 PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
2552 ARRAY_CREATOR array_creator( *editFrame, m_isFootprintEditor, selection, m_toolMgr );
2553 array_creator.Invoke();
2554
2555 return 0;
2556}
2557
2558
2560 PCB_SELECTION_TOOL* sTool )
2561{
2562 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
2563 {
2564 if( aCollector[i]->Type() != PCB_PAD_T )
2565 aCollector.Remove( i );
2566 }
2567}
2568
2569
2571 PCB_SELECTION_TOOL* sTool )
2572{
2573 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
2574 {
2575 if( aCollector[i]->Type() != PCB_FOOTPRINT_T )
2576 aCollector.Remove( i );
2577 }
2578}
2579
2580
2582{
2583 if( m_dragging && aSelection.HasReferencePoint() )
2584 return false;
2585
2586 // Can't modify an empty group
2587 if( aSelection.Empty() )
2588 return false;
2589
2590 // When there is only one item selected, the reference point is its position...
2591 if( aSelection.Size() == 1 )
2592 {
2593 if( BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( aSelection.Front() ) )
2594 aSelection.SetReferencePoint( item->GetPosition() );
2595 }
2596 // ...otherwise modify items with regard to the grid-snapped center position
2597 else
2598 {
2599 PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
2600 aSelection.SetReferencePoint( grid.BestSnapAnchor( aSelection.GetCenter(), nullptr ) );
2601 }
2602
2603 return true;
2604}
2605
2606
2607bool EDIT_TOOL::pickReferencePoint( const wxString& aTooltip, const wxString& aSuccessMessage,
2608 const wxString& aCanceledMessage, VECTOR2I& aReferencePoint )
2609{
2611 std::optional<VECTOR2I> pickedPoint;
2612 bool done = false;
2613
2614 m_statusPopup->SetText( aTooltip );
2615
2617 picker->SetSnapping( true );
2618
2619 picker->SetClickHandler(
2620 [&]( const VECTOR2D& aPoint ) -> bool
2621 {
2622 pickedPoint = aPoint;
2623
2624 if( !aSuccessMessage.empty() )
2625 {
2626 m_statusPopup->SetText( aSuccessMessage );
2627 m_statusPopup->Expire( 800 );
2628 }
2629 else
2630 {
2631 m_statusPopup->Hide();
2632 }
2633
2634 return false; // we don't need any more points
2635 } );
2636
2637 picker->SetMotionHandler(
2638 [&]( const VECTOR2D& aPos )
2639 {
2640 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
2641 } );
2642
2643 picker->SetCancelHandler(
2644 [&]()
2645 {
2646 if( !aCanceledMessage.empty() )
2647 {
2648 m_statusPopup->SetText( aCanceledMessage );
2649 m_statusPopup->Expire( 800 );
2650 }
2651 else
2652 {
2653 m_statusPopup->Hide();
2654 }
2655 } );
2656
2657 picker->SetFinalizeHandler(
2658 [&]( const int& aFinalState )
2659 {
2660 done = true;
2661 } );
2662
2663 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
2664 m_statusPopup->Popup();
2665 canvas()->SetStatusPopup( m_statusPopup->GetPanel() );
2666
2668
2669 while( !done )
2670 {
2671 // Pass events unless we receive a null event, then we must shut down
2672 if( TOOL_EVENT* evt = Wait() )
2673 evt->SetPassEvent();
2674 else
2675 break;
2676 }
2677
2678 // Ensure statusPopup is hidden after use and before deleting it:
2679 canvas()->SetStatusPopup( nullptr );
2680 m_statusPopup->Hide();
2681
2682 if( pickedPoint )
2683 aReferencePoint = *pickedPoint;
2684
2685 return pickedPoint.has_value();
2686}
2687
2688
2690{
2691 CLIPBOARD_IO io;
2693 getEditFrame<PCB_BASE_EDIT_FRAME>()->GetMagneticItemsSettings() );
2694 TOOL_EVENT selectReferencePoint( aEvent.Category(), aEvent.Action(),
2695 "pcbnew.InteractiveEdit.selectReferencePoint",
2696 TOOL_ACTION_SCOPE::AS_GLOBAL );
2697
2698 frame()->PushTool( selectReferencePoint );
2699 Activate();
2700
2702 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2703 {
2704 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
2705 {
2706 BOARD_ITEM* item = aCollector[i];
2707
2708 // We can't copy both a footprint and its text in the same operation, so if
2709 // both are selected, remove the text
2710 if( ( item->Type() == PCB_FIELD_T || item->Type() == PCB_TEXT_T )
2711 && aCollector.HasItem( item->GetParentFootprint() ) )
2712 {
2713 aCollector.Remove( item );
2714 }
2715 }
2716 },
2717
2718 // Prompt user regarding locked items.
2719 aEvent.IsAction( &ACTIONS::cut ) && !m_isFootprintEditor );
2720
2721 if( !selection.Empty() )
2722 {
2723 std::vector<BOARD_ITEM*> items;
2724
2725 for( EDA_ITEM* item : selection )
2726 {
2727 if( BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( item ) )
2728 items.push_back( boardItem );
2729 }
2730
2731 VECTOR2I refPoint;
2732
2733 if( aEvent.IsAction( &PCB_ACTIONS::copyWithReference ) )
2734 {
2735 if( !pickReferencePoint( _( "Select reference point for the copy..." ),
2736 _( "Selection copied" ),
2737 _( "Copy canceled" ),
2738 refPoint ) )
2739 {
2740 frame()->PopTool( selectReferencePoint );
2741 return 0;
2742 }
2743 }
2744 else
2745 {
2746 refPoint = grid.BestDragOrigin( getViewControls()->GetCursorPosition(), items );
2747 }
2748
2749 selection.SetReferencePoint( refPoint );
2750
2751 io.SetBoard( board() );
2752 io.SaveSelection( selection, m_isFootprintEditor );
2753 frame()->SetStatusText( _( "Selection copied" ) );
2754 }
2755
2756 frame()->PopTool( selectReferencePoint );
2757
2758 if( selection.IsHover() )
2759 m_selectionTool->ClearSelection();
2760
2761 return 0;
2762}
2763
2764
2766{
2767 if( !copyToClipboard( aEvent ) )
2768 {
2769 // N.B. Setting the CUT flag prevents lock filtering as we only want to delete the items
2770 // that were copied to the clipboard, no more, no fewer. Filtering for locked item, if
2771 // any will be done in the copyToClipboard() routine
2772 TOOL_EVENT evt = aEvent;
2774 Remove( evt );
2775 }
2776
2777 return 0;
2778}
2779
2780
2782{
2786}
2787
2788
2790{
2792 Go( &EDIT_TOOL::Move, PCB_ACTIONS::move.MakeEvent() );
2798 Go( &EDIT_TOOL::Flip, PCB_ACTIONS::flip.MakeEvent() );
2799 Go( &EDIT_TOOL::Remove, ACTIONS::doDelete.MakeEvent() );
2807 Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorH.MakeEvent() );
2808 Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorV.MakeEvent() );
2809 Go( &EDIT_TOOL::Swap, PCB_ACTIONS::swap.MakeEvent() );
2820
2823 Go( &EDIT_TOOL::cutToClipboard, ACTIONS::cut.MakeEvent() );
2824}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
@ special_tools
static TOOL_ACTION paste
Definition: actions.h:69
static TOOL_ACTION pickerSubTool
Definition: actions.h:166
static TOOL_ACTION unselectAll
Definition: actions.h:72
static TOOL_ACTION copy
Definition: actions.h:68
static TOOL_ACTION pasteSpecial
Definition: actions.h:70
static TOOL_ACTION pageSettings
Definition: actions.h:56
static TOOL_ACTION undo
Definition: actions.h:65
static TOOL_ACTION duplicate
Definition: actions.h:73
static TOOL_ACTION doDelete
Definition: actions.h:74
static TOOL_ACTION cursorClick
Definition: actions.h:130
REMOVE_FLAGS
Definition: actions.h:218
static TOOL_ACTION cut
Definition: actions.h:67
static TOOL_ACTION selectAll
Definition: actions.h:71
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.
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:225
virtual void SetLocked(bool aLocked)
Definition: board_item.h:299
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:289
virtual BOARD_ITEM * Duplicate() const
Create a copy of this BOARD_ITEM.
Definition: board_item.cpp:192
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition: board_item.h:313
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:259
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:247
virtual bool IsLocked() const
Definition: board_item.cpp:73
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:203
LSET GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:663
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:182
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:414
bool IsEmpty() const
Definition: board.h:372
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:766
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:441
const Vec & GetPosition() const
Definition: box2.h:185
const Vec GetEnd() const
Definition: box2.h:186
BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:589
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:90
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
bool Empty() const
Returns status of an item.
Definition: commit.h:142
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition: commit.h:78
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:85
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:239
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:123
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
void ClearSelected()
Definition: eda_item.h:118
bool IsSelected() const
Definition: eda_item.h:106
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:103
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:258
SHAPE_T GetShape() const
Definition: eda_shape.h:119
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:151
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:130
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:126
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:155
void SetWidth(int aWidth)
Definition: eda_shape.h:108
bool isRouterActive() const
Definition: edit_tool.cpp:415
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:423
int Flip(const TOOL_EVENT &aEvent)
Rotate currently selected items.
Definition: edit_tool.cpp:2021
int Duplicate(const TOOL_EVENT &aEvent)
Duplicate the current selection and starts a move action.
Definition: edit_tool.cpp:2383
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:1915
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:2531
bool pickReferencePoint(const wxString &aTooltip, const wxString &aSuccessMessage, const wxString &aCanceledMessage, VECTOR2I &aReferencePoint)
Definition: edit_tool.cpp:2607
bool Init() override
Init() is called once upon a registration of the tool.
Definition: edit_tool.cpp:174
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
Definition: edit_tool.cpp:86
int ModifyLines(const TOOL_EVENT &aEvent)
"Modify" graphical lines.
Definition: edit_tool.cpp:1216
void DeleteItems(const PCB_SELECTION &aItems, bool aIsCut)
Definition: edit_tool.cpp:2098
bool m_dragging
Definition: edit_tool.h:210
int MoveExact(const TOOL_EVENT &aEvent)
Invoke a dialog box to allow moving of the item by an exact amount.
Definition: edit_tool.cpp:2294
int Move(const TOOL_EVENT &aEvent)
Main loop in which events are handled.
static const unsigned int COORDS_PADDING
Definition: edit_tool.h:215
bool updateModificationPoint(PCB_SELECTION &aSelection)
Definition: edit_tool.cpp:2581
std::unique_ptr< STATUS_TEXT_POPUP > m_statusPopup
Definition: edit_tool.h:213
int DragArcTrack(const TOOL_EVENT &aTrack)
Drag-resize an arc (and change end points of connected straight segments).
Definition: edit_tool.cpp:519
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:2689
int Remove(const TOOL_EVENT &aEvent)
Delete currently selected items.
Definition: edit_tool.cpp:2221
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:2765
static const std::vector< KICAD_T > MirrorableItems
Definition: edit_tool.h:104
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:2559
VECTOR2I m_cursor
Definition: edit_tool.h:211
bool invokeInlineRouter(int aDragMode)
Definition: edit_tool.cpp:386
void rebuildConnectivity()
Definition: edit_tool.cpp:2781
void setTransitions() override
< Set up handlers for various events.
Definition: edit_tool.cpp:2789
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:1446
int ChangeTrackWidth(const TOOL_EVENT &aEvent)
Definition: edit_tool.cpp:871
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:1525
int GetAndPlace(const TOOL_EVENT &aEvent)
Definition: edit_tool.cpp:363
int FilletTracks(const TOOL_EVENT &aEvent)
Fillet (i.e.
Definition: edit_tool.cpp:943
PCB_SELECTION_TOOL * m_selectionTool
Definition: edit_tool.h:209
int Properties(const TOOL_EVENT &aEvent)
Display properties window for the selected object.
Definition: edit_tool.cpp:1636
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:2570
int Rotate(const TOOL_EVENT &aEvent)
Rotate currently selected items.
Definition: edit_tool.cpp:1698
static const TOOL_EVENT SelectedEvent
Definition: actions.h:233
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:240
static const TOOL_EVENT ConnectivityChangedEvent
Selected item had a property changed (except movement)
Definition: actions.h:237
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:234
void Remove(BOARD_ITEM *aItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: footprint.cpp:786
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:2110
wxString GetNextPadNumber(const wxString &aLastPadName) const
Return the next available pad number in the footprint.
Definition: footprint.cpp:2224
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:313
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:350
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:1618
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:556
A collection of nets and the parameters used to route or test these nets.
Definition: netclass.h:47
int GetuViaDrill() const
Definition: netclass.h:92
int GetuViaDiameter() const
Definition: netclass.h:88
Tool relating to pads and pad settings.
Definition: pad_tool.h:37
void SetLastPadNumber(const wxString &aPadNumber)
Definition: pad_tool.h:68
wxString GetLastPadNumber() const
Definition: pad_tool.h:67
Definition: pad.h:59
bool CanHaveNumber() const
Indicates whether or not the pad can have a number.
Definition: pad.cpp:141
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:752
VECTOR2I GetPosition() const override
Definition: pad.h:198
void SetOffset(const VECTOR2I &aOffset)
Definition: pad.h:260
const VECTOR2I & GetOffset() const
Definition: pad.h:261
void SetDelta(const VECTOR2I &aSize)
Definition: pad.h:250
void SetPosition(const VECTOR2I &aPos) override
Definition: pad.h:192
const VECTOR2I & GetDelta() const
Definition: pad.h:251
PAD_SHAPE GetShape() const
Definition: pad.h:190
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition: pad.h:342
void SetOrientation(const EDA_ANGLE &aAngle)
Set the rotation angle of the pad.
Definition: pad.cpp:669
static TOOL_ACTION drag45Degree
Definition: pcb_actions.h:191
static TOOL_ACTION duplicateIncrement
Activation of the duplication tool with incrementing (e.g. pad number)
Definition: pcb_actions.h:180
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:411
static TOOL_ACTION breakTrack
Break a single track into two segments at the cursor.
Definition: pcb_actions.h:189
static TOOL_ACTION pointEditorMoveMidpoint
Definition: pcb_actions.h:292
static TOOL_ACTION getAndPlace
Find an item and start moving.
Definition: pcb_actions.h:564
static TOOL_ACTION properties
Activation of the edit tool.
Definition: pcb_actions.h:174
static TOOL_ACTION editFpInFpEditor
Definition: pcb_actions.h:434
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:177
static TOOL_ACTION intersectPolygons
Intersection of multiple polygons.
Definition: pcb_actions.h:171
static TOOL_ACTION pointEditorMoveCorner
Definition: pcb_actions.h:291
static TOOL_ACTION genRemove
Definition: pcb_actions.h:280
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:386
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:192
static TOOL_ACTION inspectClearance
Definition: pcb_actions.h:540
static TOOL_ACTION updateLocalRatsnest
Definition: pcb_actions.h:558
static TOOL_ACTION updateFootprints
Definition: pcb_actions.h:412
static TOOL_ACTION deleteFull
Definition: pcb_actions.h:184
static TOOL_ACTION moveIndividually
move items one-by-one
Definition: pcb_actions.h:123
static TOOL_ACTION changeFootprints
Definition: pcb_actions.h:414
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 footprintProperties
Definition: pcb_actions.h:467
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:413
static TOOL_ACTION routerInlineDrag
Activation of the Push and Shove router (inline dragging mode)
Definition: pcb_actions.h:269
static TOOL_ACTION positionRelative
Activation of the position relative tool.
Definition: pcb_actions.h:312
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:167
static TOOL_ACTION subtractPolygons
Subtract polygons from other polygons.
Definition: pcb_actions.h:169
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:482
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:352
void SetMid(const VECTOR2I &aMid)
Definition: pcb_track.h:318
EDA_ANGLE GetAngle() const
Definition: pcb_track.cpp:1346
const VECTOR2I & GetMid() const
Definition: pcb_track.h:319
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_track.h:325
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 GuessSelectionCandidates(GENERAL_COLLECTOR &aCollector, const VECTOR2I &aWhere) const
Try to guess best selection candidates in case multiple items are clicked, by doing some brain-dead h...
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 ExitGroup(bool aSelectGroup=false)
Leave the currently-entered group.
int GetWidth() const override
Definition: pcb_shape.cpp:149
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:97
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: pcb_shape.h:67
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
void SetWidth(int aWidth)
Definition: pcb_track.h:106
virtual double GetLength() const
Get the length of the track using the hypotenuse calculation.
Definition: pcb_track.cpp:483
int GetWidth() const
Definition: pcb_track.h:107
void SetEnd(const VECTOR2I &aEnd)
Definition: pcb_track.h:109
void SetStart(const VECTOR2I &aStart)
Definition: pcb_track.h:112
const VECTOR2I & GetStart() const
Definition: pcb_track.h:113
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:110
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:405
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:392
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)
VECTOR2I GetReferencePoint() const
Definition: selection.h:252
virtual VECTOR2I GetCenter() const
Returns the center point of the selection area bounding box.
Definition: selection.cpp:93
bool IsHover() const
Definition: selection.h:83
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition: selection.h:99
EDA_ITEM * Front() const
Definition: selection.h:208
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:115
void ClearReferencePoint()
Definition: selection.h:265
void SetReferencePoint(const VECTOR2I &aP)
Definition: selection.h:260
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:109
bool HasReferencePoint() const
Definition: selection.h:247
size_t CountType(KICAD_T aType) const
Definition: selection.cpp:156
bool Contains(EDA_ITEM *aItem) const
Definition: selection.cpp:84
virtual BOX2I GetBoundingBox() const
Definition: selection.cpp:133
const VECTOR2I & GetP1() const
Definition: shape_arc.h:113
const VECTOR2I & GetP0() const
Definition: shape_arc.h:112
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:123
bool IsCurrentTool(const TOOL_ACTION &aAction) const
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:216
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
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:145
bool PostAction(const std::string &aActionName, T aParam)
Run the specified action after the current action (coroutine) ends.
Definition: tool_manager.h:230
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:192
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:218
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:527
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:927
void RemoveCutout(int aOutlineIdx, int aHoleIdx)
Remove a cutout from the zone.
Definition: zone.cpp:772
ROTATION_ANCHOR
@ ROTATE_AROUND_USER_ORIGIN
@ ROTATE_AROUND_SEL_CENTER
@ ROTATE_AROUND_AUX_ORIGIN
@ ROTATE_AROUND_ITEM_ANCHOR
#define _(s)
static constexpr EDA_ANGLE & ANGLE_180
Definition: eda_angle.h:441
@ DEGREES_T
Definition: eda_angle.h:31
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
Definition: eda_item.h:529
#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:1845
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:1860
static std::optional< CHAMFER_PARAMS > GetChamferParams(PCB_BASE_EDIT_FRAME &aFrame, wxString &aErrorMsg)
Prompt the user for chamfer parameters.
Definition: edit_tool.cpp:1186
static std::shared_ptr< CONDITIONAL_MENU > makePositioningToolsMenu(TOOL_INTERACTIVE *aTool)
Definition: edit_tool.cpp:94
static std::shared_ptr< CONDITIONAL_MENU > makeShapeModificationMenu(TOOL_INTERACTIVE *aTool)
Definition: edit_tool.cpp:116
static VECTOR2I mirrorPointX(const VECTOR2I &aPoint, const VECTOR2I &aMirrorPoint)
Mirror a point about the vertical axis passing through another point.
Definition: edit_tool.cpp:1830
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:1883
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:1155
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:218
@ LAYER_SCHEMATIC_DRAWINGSHEET
Definition: layer_ids.h:388
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
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:378
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition: gtk/ui.cpp:588
@ 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:77
constexpr int mmToIU(double mm) const
Definition: base_units.h:89
@ 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:103
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:100
@ 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:95
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:101
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:108
@ 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:105
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:92
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition: typeinfo.h:89
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition: typeinfo.h:90
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition: typeinfo.h:104
@ PCB_SHAPE_LOCATE_SEGMENT_T
Definition: typeinfo.h:131
@ PCB_SHAPE_LOCATE_RECT_T
Definition: typeinfo.h:132
@ 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:99
@ PCB_SHAPE_LOCATE_BEZIER_T
Definition: typeinfo.h:136
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_SHAPE_LOCATE_POLY_T
Definition: typeinfo.h:135
@ PCB_SHAPE_LOCATE_ARC_T
Definition: typeinfo.h:134
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:96
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition: typeinfo.h:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:94
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:102
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:85
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588