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