KiCad PCB EDA Suite
Loading...
Searching...
No Matches
edit_tool.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2013-2023 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
7 * @author Tomasz Wlostowski <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23#include <macros.h>
24#include <advanced_config.h>
25#include <clipboard.h>
26#include <limits>
27#include <kiplatform/ui.h>
29#include <board.h>
31#include <collectors.h>
32#include <footprint.h>
33#include <increment.h>
34#include <pcb_shape.h>
35#include <pcb_group.h>
38#include <pcb_point.h>
39#include <pcb_target.h>
40#include <pcb_textbox.h>
41#include <pcb_table.h>
42#include <pcb_generator.h>
43#include <zone.h>
44#include <pad.h>
45#include <pcb_edit_frame.h>
47#include <kiway.h>
48#include <status_popup.h>
49#include <tool/action_manager.h>
51#include <tool/tool_manager.h>
52#include <tools/pcb_actions.h>
55#include <tools/edit_tool.h>
61#include <tools/pad_tool.h>
62#include <view/view_controls.h>
64#include <pcbnew_id.h>
65#include <core/kicad_algo.h>
66#include <fix_board_shape.h>
67#include <bitmaps.h>
68#include <functional>
69using namespace std::placeholders;
70#include "kicad_clipboard.h"
71#include <wx/hyperlink.h>
72#include <router/router_tool.h>
80#include <pcb_reference_image.h>
81
82const unsigned int EDIT_TOOL::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
83
85{
86 if( !aItem )
87 return false;
88
89 if( aItem->Type() == PCB_SHAPE_T )
90 {
91 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
92 return shape->GetShape() == SHAPE_T::POLY;
93 }
94
95 if( aItem->Type() == PCB_ZONE_T )
96 {
97 ZONE* zone = static_cast<ZONE*>( aItem );
98
99 if( zone->IsTeardropArea() )
100 return false;
101
102 return true;
103 }
104
105 return false;
106}
107
108static bool selectionHasEditableCorners( const SELECTION& aSelection )
109{
110 if( aSelection.GetSize() != 1 )
111 return false;
112
113 BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( aSelection.Front() );
114 return itemHasEditableCorners( item );
115}
116
117static const std::vector<KICAD_T> padTypes = { PCB_PAD_T };
118
119static const std::vector<KICAD_T> footprintTypes = { PCB_FOOTPRINT_T };
120
121static const std::vector<KICAD_T> groupTypes = { PCB_GROUP_T };
122
123static const std::vector<KICAD_T> trackTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T };
124
125static const std::vector<KICAD_T> baseConnectedTypes = { PCB_PAD_T, PCB_VIA_T, PCB_TRACE_T, PCB_ARC_T };
126
127static const std::vector<KICAD_T> connectedTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, PCB_ZONE_T };
128
129static const std::vector<KICAD_T> routableTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, PCB_FOOTPRINT_T };
130
131
132// Types with no Mirror() override, which would fall through to the warning-dialog
133// BOARD_ITEM::Mirror. Free pads are handled specially by the tool but not by PCB_GROUP::Mirror.
134static const std::vector<KICAD_T> nonMirrorableTypes = {
136};
137
138
139// A group is mirrorable only if none of its members hit BOARD_ITEM::Mirror.
140static bool groupMirrorable( const PCB_GROUP* aGroup )
141{
142 bool ok = true;
143
144 aGroup->RunOnChildren(
145 [&]( BOARD_ITEM* aChild )
146 {
147 if( aChild->IsType( nonMirrorableTypes ) )
148 ok = false;
149 },
151
152 return ok;
153}
154
155
156// True if at least one selected item can be mirrored. A group counts only if all its members can.
157static bool selectionMirrorable( const SELECTION& aSelection )
158{
159 for( EDA_ITEM* item : aSelection )
160 {
161 if( item->Type() == PCB_GROUP_T )
162 {
163 if( groupMirrorable( static_cast<PCB_GROUP*>( item ) ) )
164 return true;
165 }
166 else if( item->IsType( EDIT_TOOL::MirrorableItems ) )
167 {
168 return true;
169 }
170 }
171
172 return false;
173}
174
175
177 PCB_TOOL_BASE( "pcbnew.InteractiveEdit" ),
178 m_selectionTool( nullptr ),
179 m_dragging( false ),
180 m_inMoveWithReference( false )
181{
182}
183
184
186{
187 m_dragging = false;
188 m_inMoveWithReference = false;
189
190 m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( getEditFrame<PCB_BASE_EDIT_FRAME>() );
191}
192
193
194static std::shared_ptr<CONDITIONAL_MENU> makeMirrorRotateMenu( TOOL_INTERACTIVE* aTool )
195{
196 auto menu = std::make_shared<CONDITIONAL_MENU>( aTool );
197
198 menu->SetIcon( BITMAPS::special_tools );
199 menu->SetUntranslatedTitle( _HKI( "Mirror / Rotate" ) );
200
201 auto canMirror = []( const SELECTION& aSelection )
202 {
203 if( SELECTION_CONDITIONS::OnlyTypes( padTypes )( aSelection ) )
204 return false;
205
206 return selectionMirrorable( aSelection );
207 };
208
211 menu->AddItem( PCB_ACTIONS::mirrorH, canMirror );
212 menu->AddItem( PCB_ACTIONS::mirrorV, canMirror );
213
214 return menu;
215}
216
217
218static std::shared_ptr<CONDITIONAL_MENU> makeRoutingToolsMenu( TOOL_INTERACTIVE* aTool )
219{
220 auto menu = std::make_shared<CONDITIONAL_MENU>( aTool );
221
222 menu->SetIcon( BITMAPS::special_tools );
223 menu->SetUntranslatedTitle( _HKI( "Routing" ) );
224
225 auto notMovingCondition = []( const SELECTION& aSelection )
226 {
227 return aSelection.Empty() || !aSelection.Front()->IsMoving();
228 };
229
230 const SELECTION_CONDITION isRoutable =
232
233 menu->AddItem( PCB_ACTIONS::routerRouteSelected, isRoutable );
234 menu->AddItem( PCB_ACTIONS::routerRouteSelectedFromEnd, isRoutable );
235 menu->AddItem( PCB_ACTIONS::unrouteSelected, isRoutable );
236 menu->AddItem( PCB_ACTIONS::unrouteSegment, isRoutable );
237 menu->AddItem( PCB_ACTIONS::routerAutorouteSelected, isRoutable );
238
239 return menu;
240}
241
242
243static std::shared_ptr<CONDITIONAL_MENU> makePositioningToolsMenu( TOOL_INTERACTIVE* aTool )
244{
245 auto menu = std::make_shared<CONDITIONAL_MENU>( aTool );
246
247 menu->SetIcon( BITMAPS::special_tools );
248 menu->SetUntranslatedTitle( _HKI( "Position" ) );
249
250 auto notMovingCondition = []( const SELECTION& aSelection )
251 {
252 return aSelection.Empty() || !aSelection.Front()->IsMoving();
253 };
254
255 menu->AddItem( PCB_ACTIONS::moveExact, SELECTION_CONDITIONS::NotEmpty && notMovingCondition );
256 menu->AddItem( PCB_ACTIONS::moveWithReference, SELECTION_CONDITIONS::NotEmpty && notMovingCondition );
257 menu->AddItem( PCB_ACTIONS::moveIndividually, SELECTION_CONDITIONS::MoreThan( 1 ) && notMovingCondition );
258 menu->AddItem( PCB_ACTIONS::positionRelative, SELECTION_CONDITIONS::NotEmpty && notMovingCondition );
259 menu->AddItem( PCB_ACTIONS::interactiveOffsetTool, SELECTION_CONDITIONS::NotEmpty && notMovingCondition );
260 return menu;
261};
262
263
264static std::shared_ptr<CONDITIONAL_MENU> makeShapeModificationMenu( TOOL_INTERACTIVE* aTool )
265{
266 auto menu = std::make_shared<CONDITIONAL_MENU>( aTool );
267
268 menu->SetUntranslatedTitle( _HKI( "Shape Modification" ) );
269
270 static const std::vector<KICAD_T> filletChamferTypes = { PCB_SHAPE_LOCATE_POLY_T, PCB_SHAPE_LOCATE_RECT_T,
272
273 static const std::vector<KICAD_T> healShapesTypes = { PCB_SHAPE_LOCATE_SEGMENT_T, PCB_SHAPE_LOCATE_ARC_T,
275
276 static const std::vector<KICAD_T> lineExtendTypes = { PCB_SHAPE_LOCATE_SEGMENT_T };
277
278 static const std::vector<KICAD_T> polygonBooleanTypes = { PCB_SHAPE_LOCATE_RECT_T, PCB_SHAPE_LOCATE_POLY_T,
280
281 static const std::vector<KICAD_T> polygonSimplifyTypes = { PCB_SHAPE_LOCATE_POLY_T, PCB_ZONE_T };
282
283 auto hasCornerCondition = [aTool]( const SELECTION& aSelection )
284 {
285 PCB_POINT_EDITOR* pt_tool = aTool->GetManager()->GetTool<PCB_POINT_EDITOR>();
286
287 return pt_tool && pt_tool->HasCorner();
288 };
289
290 auto hasMidpointCondition = [aTool]( const SELECTION& aSelection )
291 {
292 PCB_POINT_EDITOR* pt_tool = aTool->GetManager()->GetTool<PCB_POINT_EDITOR>();
293
294 return pt_tool && pt_tool->HasMidpoint();
295 };
296
297 auto canAddCornerCondition = []( const SELECTION& aSelection )
298 {
299 const EDA_ITEM* item = aSelection.Front();
300
301 return item && PCB_POINT_EDITOR::CanAddCorner( *item );
302 };
303
304 auto canChamferCornerCondition = []( const SELECTION& aSelection )
305 {
306 const EDA_ITEM* item = aSelection.Front();
307
308 return item && PCB_POINT_EDITOR::CanChamferCorner( *item );
309 };
310
311 auto canRemoveCornerCondition = [aTool]( const SELECTION& aSelection )
312 {
313 PCB_POINT_EDITOR* pt_tool = aTool->GetManager()->GetTool<PCB_POINT_EDITOR>();
314
315 return pt_tool && pt_tool->CanRemoveCorner( aSelection );
316 };
317
318 // clang-format off
319
320 // Shape cleanup
321 menu->AddItem( PCB_ACTIONS::healShapes, SELECTION_CONDITIONS::HasTypes( healShapesTypes ) );
322 menu->AddItem( PCB_ACTIONS::simplifyPolygons, SELECTION_CONDITIONS::HasTypes( polygonSimplifyTypes ) );
323
324 menu->AddSeparator( SELECTION_CONDITIONS::OnlyTypes( filletChamferTypes ) );
325
326 // Shape corner modifications
327 menu->AddItem( PCB_ACTIONS::filletLines, SELECTION_CONDITIONS::OnlyTypes( filletChamferTypes ) );
328 menu->AddItem( PCB_ACTIONS::chamferLines, SELECTION_CONDITIONS::OnlyTypes( filletChamferTypes ) );
329 menu->AddItem( PCB_ACTIONS::dogboneCorners, SELECTION_CONDITIONS::OnlyTypes( filletChamferTypes ) );
330 menu->AddItem( PCB_ACTIONS::extendLines, SELECTION_CONDITIONS::OnlyTypes( lineExtendTypes )
332
333 menu->AddSeparator( SELECTION_CONDITIONS::Count( 1 ) );
334
335 // Point editor corner operations
336 menu->AddItem( PCB_ACTIONS::pointEditorMoveCorner, hasCornerCondition );
337 menu->AddItem( PCB_ACTIONS::pointEditorMoveMidpoint, hasMidpointCondition );
338 menu->AddItem( PCB_ACTIONS::pointEditorAddCorner, SELECTION_CONDITIONS::Count( 1 ) && canAddCornerCondition );
339 menu->AddItem( PCB_ACTIONS::pointEditorRemoveCorner, SELECTION_CONDITIONS::Count( 1 ) && canRemoveCornerCondition );
340 menu->AddItem( PCB_ACTIONS::pointEditorChamferCorner, SELECTION_CONDITIONS::Count( 1 ) && canChamferCornerCondition );
342
343 menu->AddSeparator( SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
345
346 // Polygon boolean operations
347 menu->AddItem( PCB_ACTIONS::mergePolygons, SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
349 menu->AddItem( PCB_ACTIONS::subtractPolygons, SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
351 menu->AddItem( PCB_ACTIONS::intersectPolygons, SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
353 // clang-format on
354
355 return menu;
356};
357
358
359// Gate-swap submenu and helpers
361{
362public:
364 ACTION_MENU( true )
365 {
367 SetTitle( _( "Swap Gate Nets..." ) );
368 }
369
370
371 // We're looking for a selection of pad(s) that belong to a single footprint with multiple units.
372 // Ignore non-pad items since we might have grabbed some traces inside the pad, etc.
373 static const FOOTPRINT* GetSingleEligibleFootprint( const SELECTION& aSelection )
374 {
375 const FOOTPRINT* single = nullptr;
376
377 for( const EDA_ITEM* it : aSelection )
378 {
379 if( it->Type() != PCB_PAD_T )
380 continue;
381
382 const PAD* pad = static_cast<const PAD*>( static_cast<const BOARD_ITEM*>( it ) );
383 const FOOTPRINT* fp = pad->GetParentFootprint();
384
385 if( !fp )
386 continue;
387
388 const auto& units = fp->GetUnitInfo();
389
390 if( units.size() < 2 )
391 continue;
392
393 const wxString& padNum = pad->GetNumber();
394 bool inAnyUnit = false;
395
396 for( const auto& u : units )
397 {
398 for( const auto& pnum : u.m_pins )
399 {
400 if( pnum == padNum )
401 {
402 inAnyUnit = true;
403 break;
404 }
405 }
406
407 if( inAnyUnit )
408 break;
409 }
410
411 if( !inAnyUnit )
412 continue;
413
414 if( !single )
415 single = fp;
416 else if( single != fp )
417 return nullptr;
418 }
419
420 return single;
421 }
422
423
424 static std::unordered_set<wxString> CollectSelectedPadNumbers( const SELECTION& aSelection,
425 const FOOTPRINT* aFootprint )
426 {
427 std::unordered_set<wxString> padNums;
428
429 for( const EDA_ITEM* it : aSelection )
430 {
431 if( it->Type() != PCB_PAD_T )
432 continue;
433
434 const PAD* pad = static_cast<const PAD*>( static_cast<const BOARD_ITEM*>( it ) );
435
436 if( pad->GetParentFootprint() != aFootprint )
437 continue;
438
439 padNums.insert( pad->GetNumber() );
440 }
441
442 return padNums;
443 }
444
445
446 // Make a list of the unit names that have any pad selected
447 static std::vector<int> GetUnitsHitIndices( const FOOTPRINT* aFootprint,
448 const std::unordered_set<wxString>& aSelPadNums )
449 {
450 std::vector<int> indices;
451
452 const auto& units = aFootprint->GetUnitInfo();
453
454 for( size_t i = 0; i < units.size(); ++i )
455 {
456 bool hasAny = false;
457
458 for( const auto& pn : units[i].m_pins )
459 {
460 if( aSelPadNums.count( pn ) )
461 {
462 hasAny = true;
463 break;
464 }
465 }
466
467 if( hasAny )
468 indices.push_back( static_cast<int>( i ) );
469 }
470
471 return indices;
472 }
473
474
475 // Gate swapping requires the swapped units to have equal pin counts
476 static bool EqualPinCounts( const FOOTPRINT* aFootprint, const std::vector<int>& aUnitIndices )
477 {
478 if( aUnitIndices.empty() )
479 return false;
480
481 const auto& units = aFootprint->GetUnitInfo();
482 const size_t cnt = units[static_cast<size_t>( aUnitIndices.front() )].m_pins.size();
483
484 for( int idx : aUnitIndices )
485 {
486 if( units[static_cast<size_t>( idx )].m_pins.size() != cnt )
487 return false;
488 }
489
490 return true;
491 }
492
493
494 // Used when we have exactly one source unit selected; find all other units with equal pin counts
495 static std::vector<int> GetCompatibleTargets( const FOOTPRINT* aFootprint, int aSourceIdx )
496 {
497 std::vector<int> targets;
498
499 const auto& units = aFootprint->GetUnitInfo();
500 const size_t pinCount = units[static_cast<size_t>( aSourceIdx )].m_pins.size();
501
502 for( size_t i = 0; i < units.size(); ++i )
503 {
504 if( static_cast<int>( i ) == aSourceIdx )
505 continue;
506
507 if( units[i].m_pins.size() != pinCount )
508 continue;
509
510 targets.push_back( static_cast<int>( i ) );
511 }
512
513 return targets;
514 }
515
516protected:
517 ACTION_MENU* create() const override { return new GATE_SWAP_MENU(); }
518
519 // The gate swap menu dynamically populates itself based on current selection of pads
520 // on a single multi-unit footprint.
521 //
522 // If there is exactly one unit with any pad selected, we build a menu of available swaps
523 // with all other units with equal pin counts.
524 void update() override
525 {
526 Clear();
527
529 const SELECTION& sel = selTool->GetSelection();
530
531 const FOOTPRINT* fp = GetSingleEligibleFootprint( sel );
532
533 if( !fp )
534 return;
535
536 std::unordered_set<wxString> selPadNums = CollectSelectedPadNumbers( sel, fp );
537
538 std::vector<int> unitsHit = GetUnitsHitIndices( fp, selPadNums );
539
540 if( unitsHit.size() != 1 )
541 return;
542
543 const int sourceIdx = unitsHit.front();
544 std::vector<int> targets = GetCompatibleTargets( fp, sourceIdx );
545
546 for( int idx : targets )
547 {
548 wxString label;
549 label.Printf( _( "Swap with %s" ), fp->GetUnitInfo()[static_cast<size_t>( idx )].m_unitName );
550 Append( ID_POPUP_PCB_SWAP_UNIT_BASE + idx, label );
551 }
552 }
553
554
555 OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
556 {
557 int id = aEvent.GetId();
558
560 {
562 const SELECTION& sel = selTool->GetSelection();
563
564 const FOOTPRINT* fp = GetSingleEligibleFootprint( sel );
565
566 if( !fp )
567 return OPT_TOOL_EVENT();
568
569 const auto& units = fp->GetUnitInfo();
570 const int targetIdx = id - ID_POPUP_PCB_SWAP_UNIT_BASE;
571
572 if( targetIdx < 0 || targetIdx >= static_cast<int>( units.size() ) )
573 return OPT_TOOL_EVENT();
574
575 TOOL_EVENT evt = PCB_ACTIONS::swapGateNets.MakeEvent();
576 evt.SetParameter( units[targetIdx].m_unitName );
577
578 return OPT_TOOL_EVENT( evt );
579 }
580
581 return OPT_TOOL_EVENT();
582 }
583};
584
585
586static std::shared_ptr<ACTION_MENU> makeGateSwapMenu( TOOL_INTERACTIVE* aTool )
587{
588 auto menu = std::make_shared<GATE_SWAP_MENU>();
589 menu->SetTool( aTool );
590 return menu;
591};
592
593
595{
596 // Find the selection tool, so they can cooperate
598
599 std::shared_ptr<CONDITIONAL_MENU> routingSubMenu = makeRoutingToolsMenu( this );
600 m_selectionTool->GetToolMenu().RegisterSubMenu( routingSubMenu );
601
602 std::shared_ptr<CONDITIONAL_MENU> positioningToolsSubMenu = makePositioningToolsMenu( this );
603 m_selectionTool->GetToolMenu().RegisterSubMenu( positioningToolsSubMenu );
604
605 std::shared_ptr<CONDITIONAL_MENU> mirrorRotateSubMenu = makeMirrorRotateMenu( this );
606 m_selectionTool->GetToolMenu().RegisterSubMenu( mirrorRotateSubMenu );
607
608 std::shared_ptr<CONDITIONAL_MENU> shapeModificationSubMenu = makeShapeModificationMenu( this );
609 m_selectionTool->GetToolMenu().RegisterSubMenu( shapeModificationSubMenu );
610
611 std::shared_ptr<ACTION_MENU> gateSwapSubMenu = makeGateSwapMenu( this );
612 m_selectionTool->GetToolMenu().RegisterSubMenu( gateSwapSubMenu );
613
614 auto fpAttributesMenu = std::make_shared<CONDITIONAL_MENU>( this );
615 fpAttributesMenu->SetUntranslatedTitle( _HKI( "Attributes" ) );
618 m_selectionTool->GetToolMenu().RegisterSubMenu( fpAttributesMenu );
619
620 auto positioningToolsCondition = [this]( const SELECTION& aSel )
621 {
622 std::shared_ptr<CONDITIONAL_MENU> subMenu = makePositioningToolsMenu( this );
623 subMenu->Evaluate( aSel );
624 return subMenu->GetMenuItemCount() > 0;
625 };
626
627 auto shapeModificationCondition = [this]( const SELECTION& aSel )
628 {
629 std::shared_ptr<CONDITIONAL_MENU> subMenu = makeShapeModificationMenu( this );
630 subMenu->Evaluate( aSel );
631 return subMenu->GetMenuItemCount() > 0;
632 };
633
634 // Does selection map to a single eligible footprint and exactly one unit?
635 auto gateSwapSingleUnitOnOneFootprint = []( const SELECTION& aSelection )
636 {
638
639 if( !fp )
640 return false;
641
642 std::unordered_set<wxString> selPadNums = GATE_SWAP_MENU::CollectSelectedPadNumbers( aSelection, fp );
643
644 std::vector<int> unitsHit = GATE_SWAP_MENU::GetUnitsHitIndices( fp, selPadNums );
645
646 if( unitsHit.size() != 1 )
647 return false;
648
649 const int sourceIdx = unitsHit.front();
650 std::vector<int> targets = GATE_SWAP_MENU::GetCompatibleTargets( fp, sourceIdx );
651 return !targets.empty();
652 };
653
654 // Does selection map to a single eligible footprint and more than one unit with equal pin counts?
655 auto gateSwapMultipleUnitsOnOneFootprint = []( const SELECTION& aSelection )
656 {
658
659 if( !fp )
660 return false;
661
662 std::unordered_set<wxString> selPadNums = GATE_SWAP_MENU::CollectSelectedPadNumbers( aSelection, fp );
663
664 std::vector<int> unitsHit = GATE_SWAP_MENU::GetUnitsHitIndices( fp, selPadNums );
665
666 if( unitsHit.size() < 2 )
667 return false;
668
669 return GATE_SWAP_MENU::EqualPinCounts( fp, unitsHit );
670 };
671
672 auto propertiesCondition = [this]( const SELECTION& aSel )
673 {
674 if( aSel.GetSize() == 0 )
675 {
676 if( getView()->IsLayerVisible( LAYER_SCHEMATIC_DRAWINGSHEET ) )
677 {
678 DS_PROXY_VIEW_ITEM* ds = canvas()->GetDrawingSheet();
679 VECTOR2D cursor = getViewControls()->GetCursorPosition( false );
680
681 if( ds && ds->HitTestDrawingSheetItems( getView(), cursor ) )
682 return true;
683 }
684
685 return false;
686 }
687
688 if( aSel.GetSize() == 1 )
689 return true;
690
691 for( EDA_ITEM* item : aSel )
692 {
693 if( !dynamic_cast<PCB_TRACK*>( item ) )
694 return false;
695 }
696
697 return true;
698 };
699
700 auto inFootprintEditor = [this]( const SELECTION& aSelection )
701 {
702 return m_isFootprintEditor;
703 };
704
705 auto canMirror = [this]( const SELECTION& aSelection )
706 {
708 {
709 return false;
710 }
711
712 // Gates the whole "Mirror / Rotate" submenu, so keep it open for any group. Rotate works
713 // on a group with footprints, only the mirror items inside disable (see makeMirrorRotateMenu).
714 if( SELECTION_CONDITIONS::HasTypes( groupTypes )( aSelection ) )
715 return true;
716
718 };
719
720 auto singleFootprintCondition =
722
723 auto multipleFootprintsCondition = []( const SELECTION& aSelection )
724 {
725 bool foundFirst = false;
726
727 for( EDA_ITEM* item : aSelection )
728 {
729 if( item->Type() == PCB_FOOTPRINT_T )
730 {
731 if( foundFirst )
732 return true;
733 else
734 foundFirst = true;
735 }
736 }
737
738 return false;
739 };
740
741 auto excludeFromBOMCond = [this]( const SELECTION& aSel )
742 {
743 wxString variantName;
744 int checked = 0, unchecked = 0;
745
746 if( BOARD* board = frame()->GetBoard() )
747 variantName = board->GetCurrentVariant();
748
749 for( const EDA_ITEM* item : aSel )
750 {
751 if( item->Type() == PCB_FOOTPRINT_T )
752 {
753 if( static_cast<const FOOTPRINT*>( item )->GetExcludedFromBOMForVariant( variantName ) )
754 checked++;
755 else
756 unchecked++;
757 }
758 }
759
760 return checked > 0 && unchecked == 0;
761 };
762
763 auto excludeFromPosFilesCond = [this]( const SELECTION& aSel )
764 {
765 wxString variantName;
766 int checked = 0, unchecked = 0;
767
768 if( BOARD* board = frame()->GetBoard() )
769 variantName = board->GetCurrentVariant();
770
771 for( const EDA_ITEM* item : aSel )
772 {
773 if( item->Type() == PCB_FOOTPRINT_T )
774 {
775 if( static_cast<const FOOTPRINT*>( item )->GetExcludedFromPosFilesForVariant( variantName ) )
776 checked++;
777 else
778 unchecked++;
779 }
780 }
781
782 return checked > 0 && unchecked == 0;
783 };
784
785 auto noActiveToolCondition = [this]( const SELECTION& aSelection )
786 {
787 return frame()->ToolStackIsEmpty();
788 };
789
790 auto notMovingCondition = []( const SELECTION& aSelection )
791 {
792 return aSelection.Empty() || !aSelection.Front()->IsMoving();
793 };
794
795 auto noItemsCondition = [this]( const SELECTION& aSelections ) -> bool
796 {
797 return frame()->GetBoard() && !frame()->GetBoard()->IsEmpty();
798 };
799
800 auto isSkippable = [this]( const SELECTION& aSelection )
801 {
802 return frame()->IsCurrentTool( PCB_ACTIONS::moveIndividually );
803 };
804
806 && notMovingCondition && !inFootprintEditor;
807
808 const auto canCopyAsText = SELECTION_CONDITIONS::NotEmpty
820 } );
821
822 // Add context menu entries that are displayed when selection tool is active
823 CONDITIONAL_MENU& menu = m_selectionTool->GetToolMenu().GetMenu();
824
825 // clang-format off
826 menu.AddItem( ACTIONS::selectAll, noItemsCondition );
827 menu.AddItem( ACTIONS::unselectAll, noItemsCondition );
828 menu.AddSeparator();
829
830 menu.AddItem( PCB_ACTIONS::skip, isSkippable );
831 menu.AddItem( PCB_ACTIONS::move, SELECTION_CONDITIONS::NotEmpty && notMovingCondition );
832
839
843 menu.AddItem( PCB_ACTIONS::swapGateNets, gateSwapMultipleUnitsOnOneFootprint );
844 menu.AddMenu( gateSwapSubMenu.get(), gateSwapSingleUnitOnOneFootprint );
845
846 menu.AddSeparator();
847
850
852
853 menu.AddSeparator();
854
857
859 && !inFootprintEditor );
861
862 // Footprint actions
863 menu.AddSeparator();
864 menu.AddItem( PCB_ACTIONS::editFpInFpEditor, singleFootprintCondition );
865 menu.AddItem( PCB_ACTIONS::updateFootprint, singleFootprintCondition );
866 menu.AddItem( PCB_ACTIONS::updateFootprints, multipleFootprintsCondition );
867 menu.AddItem( PCB_ACTIONS::changeFootprint, singleFootprintCondition );
868 menu.AddItem( PCB_ACTIONS::changeFootprints, multipleFootprintsCondition );
869 menu.AddMenu( fpAttributesMenu.get(), singleFootprintCondition || multipleFootprintsCondition );
870
871 // Add the submenu for the special tools: modfiers and positioning tools
872 menu.AddSeparator( 100 );
873 menu.AddMenu( routingSubMenu.get(), isRoutable, 100 );
874 menu.AddMenu( mirrorRotateSubMenu.get(), canMirror, 100 );
875 menu.AddMenu( shapeModificationSubMenu.get(), shapeModificationCondition, 100 );
876 menu.AddMenu( positioningToolsSubMenu.get(), positioningToolsCondition, 100 );
877
878 menu.AddSeparator( 150 );
879 menu.AddItem( ACTIONS::cut, SELECTION_CONDITIONS::NotEmpty, 150 );
880 menu.AddItem( ACTIONS::copy, SELECTION_CONDITIONS::NotEmpty, 150 );
881 menu.AddItem( PCB_ACTIONS::copyWithReference, SELECTION_CONDITIONS::NotEmpty && notMovingCondition, 150 );
882 menu.AddItem( ACTIONS::copyAsText, canCopyAsText, 150 );
883
884 // Selection tool handles the context menu for some other tools, such as the Picker.
885 // Don't add things like Paste when another tool is active.
886 menu.AddItem( ACTIONS::paste, noActiveToolCondition, 150 );
887 menu.AddItem( ACTIONS::pasteSpecial, noActiveToolCondition && !inFootprintEditor, 150 );
890
891 menu.AddSeparator( 2000 );
892 menu.AddItem( PCB_ACTIONS::properties, propertiesCondition, 2000 );
893 // clang-format on
894
895 ACTION_MANAGER* mgr = m_toolMgr->GetActionManager();
896 mgr->SetConditions( PCB_ACTIONS::toggleExcludeFromBOM, ACTION_CONDITIONS().Check( excludeFromBOMCond ) );
897 mgr->SetConditions( PCB_ACTIONS::toggleExcludeFromPosFiles, ACTION_CONDITIONS().Check( excludeFromPosFilesCond ) );
898
899 return true;
900}
901
902
909{
910 wxString footprintName;
911 wxArrayString fplist;
912 const FOOTPRINTS& footprints = aFrame.GetBoard()->Footprints();
913
914 // Build list of available fp references, to display them in dialog
915 for( FOOTPRINT* fp : footprints )
916 fplist.Add( fp->GetReference() + wxT( " ( " ) + fp->GetValue() + wxT( " )" ) );
917
918 fplist.Sort();
919
920 DIALOG_GET_FOOTPRINT_BY_NAME dlg( &aFrame, fplist );
921
922 if( dlg.ShowModal() != wxID_OK ) //Aborted by user
923 return nullptr;
924
925 footprintName = dlg.GetValue();
926 footprintName.Trim( true );
927 footprintName.Trim( false );
928
929 if( !footprintName.IsEmpty() )
930 {
931 for( FOOTPRINT* fp : footprints )
932 {
933 if( fp->GetReference().CmpNoCase( footprintName ) == 0 )
934 return fp;
935 }
936 }
937
938 return nullptr;
939}
940
941
943{
944 // GetAndPlace makes sense only in board editor, although it is also called
945 // in fpeditor, that shares the same EDIT_TOOL list
946 if( IsFootprintEditor() )
947 return 0;
948
949 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
951
952 if( fp )
953 {
955 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, fp );
956
957 selectionTool->GetSelection().SetReferencePoint( fp->GetPosition() );
958 m_toolMgr->PostAction( PCB_ACTIONS::move );
959 }
960
961 return 0;
962}
963
964
965bool EDIT_TOOL::invokeInlineRouter( int aDragMode )
966{
967 ROUTER_TOOL* theRouter = m_toolMgr->GetTool<ROUTER_TOOL>();
968
969 if( !theRouter )
970 return false;
971
972 // don't allow switch from moving to dragging
973 if( m_dragging )
974 {
975 wxBell();
976 return false;
977 }
978
979 // make sure we don't accidentally invoke inline routing mode while the router is already
980 // active!
981 if( theRouter->IsToolActive() )
982 return false;
983
984 if( theRouter->CanInlineDrag( aDragMode ) )
985 {
986 m_toolMgr->RunAction( PCB_ACTIONS::routerInlineDrag, aDragMode );
987 return true;
988 }
989
990 return false;
991}
992
993
995{
996 ROUTER_TOOL* router = m_toolMgr->GetTool<ROUTER_TOOL>();
997
998 return router && router->RoutingInProgress();
999}
1000
1001
1002int EDIT_TOOL::Drag( const TOOL_EVENT& aEvent )
1003{
1004 if( !m_toolMgr->GetTool<ROUTER_TOOL>() )
1005 {
1006 wxBell();
1007 return false; // don't drag when no router tool (i.e. fp editor)
1008 }
1009
1010 if( m_toolMgr->GetTool<ROUTER_TOOL>()->IsToolActive() )
1011 {
1012 wxBell();
1013 return false; // don't drag when router is already active
1014 }
1015
1016 if( m_dragging )
1017 {
1018 wxBell();
1019 return false; // don't do a router drag when already in an EDIT_TOOL drag
1020 }
1021
1022 int mode = PNS::DM_ANY;
1023
1024 if( aEvent.IsAction( &PCB_ACTIONS::dragFreeAngle ) )
1025 mode |= PNS::DM_FREE_ANGLE;
1026
1027 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
1028 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1029 {
1030 sTool->FilterCollectorForFreePads( aCollector );
1031 sTool->FilterCollectorForHierarchy( aCollector, true );
1032
1033 std::vector<PCB_TRACK*> tracks;
1034 std::vector<PCB_TRACK*> vias;
1035 std::vector<FOOTPRINT*> footprints;
1036
1037 // Gather items from the collector into per-type vectors
1038 const auto gatherItemsByType = [&]()
1039 {
1040 for( EDA_ITEM* item : aCollector )
1041 {
1042 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
1043 {
1044 if( track->Type() == PCB_VIA_T )
1045 vias.push_back( track );
1046 else
1047 tracks.push_back( track );
1048 }
1049 else if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( item ) )
1050 {
1051 footprints.push_back( footprint );
1052 }
1053 }
1054 };
1055
1056 // Initial gathering of items
1057 gatherItemsByType();
1058
1059 if( !sTool->GetSelection().IsHover() && footprints.size() )
1060 {
1061 // Remove non-footprints so box-selection will drag footprints.
1062 for( int ii = aCollector.GetCount() - 1; ii >= 0; --ii )
1063 {
1064 if( aCollector[ii]->Type() != PCB_FOOTPRINT_T )
1065 aCollector.Remove( ii );
1066 }
1067 }
1068 else if( tracks.size() || vias.size() )
1069 {
1070 /*
1071 * First trim down selection to active layer, tracks vs zones, etc.
1072 */
1073 if( aCollector.GetCount() > 1 )
1074 {
1075 sTool->GuessSelectionCandidates( aCollector, aPt );
1076
1077 // Re-gather items after trimming to update counts
1078 tracks.clear();
1079 vias.clear();
1080 footprints.clear();
1081
1082 gatherItemsByType();
1083 }
1084
1085 /*
1086 * If we have a knee between two tracks, or a via attached to two tracks,
1087 * then drop the selection to a single item. We don't want a selection
1088 * disambiguation menu when it doesn't matter which items is picked.
1089 */
1090 auto connected = []( PCB_TRACK* track, const VECTOR2I& pt )
1091 {
1092 return track->GetStart() == pt || track->GetEnd() == pt;
1093 };
1094
1095 if( tracks.size() == 2 && vias.size() == 0 )
1096 {
1097 if( connected( tracks[0], tracks[1]->GetStart() )
1098 || connected( tracks[0], tracks[1]->GetEnd() ) )
1099 {
1100 aCollector.Remove( tracks[1] );
1101 }
1102 }
1103 else if( tracks.size() == 2 && vias.size() == 1 )
1104 {
1105 if( connected( tracks[0], vias[0]->GetPosition() )
1106 && connected( tracks[1], vias[0]->GetPosition() ) )
1107 {
1108 aCollector.Remove( tracks[0] );
1109 aCollector.Remove( tracks[1] );
1110 }
1111 }
1112 }
1113
1114 sTool->FilterCollectorForLockedItems( aCollector );
1115 } );
1116
1117 m_selectionTool->ReportFilteredLockedItems();
1118
1119 if( selection.Empty() )
1120 return 0;
1121
1122 invokeInlineRouter( mode );
1123
1124 return 0;
1125}
1126
1127
1129{
1131
1132 if( selection.Empty() )
1133 return 0;
1134
1135 wxString variantName;
1136
1137 if( BOARD* board = frame()->GetBoard() )
1138 variantName = board->GetCurrentVariant();
1139
1140 bool new_state = false;
1141
1142 for( const EDA_ITEM* item : selection )
1143 {
1144 const FOOTPRINT* fp = static_cast<const FOOTPRINT*>( item );
1145
1147 && !fp->GetExcludedFromBOMForVariant( variantName ) )
1149 && !fp->GetExcludedFromPosFilesForVariant( variantName ) ) )
1150 {
1151 new_state = true;
1152 break;
1153 }
1154 }
1155
1156 BOARD_COMMIT commit( this );
1157
1158 for( EDA_ITEM* item : selection )
1159 {
1160 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
1161 commit.Modify( fp );
1162
1163 if( !variantName.IsEmpty() )
1164 {
1165 FOOTPRINT_VARIANT* variant = fp->GetVariant( variantName );
1166
1167 if( !variant )
1168 variant = fp->AddVariant( variantName );
1169
1170 if( variant )
1171 {
1173 variant->SetExcludedFromBOM( new_state );
1175 variant->SetExcludedFromPosFiles( new_state );
1176
1177 continue;
1178 }
1179 }
1180
1182 fp->SetExcludedFromBOM( new_state );
1184 fp->SetExcludedFromPosFiles( new_state );
1185 }
1186
1187 if( !commit.Empty() )
1188 commit.Push( _( "Toggle Attribute" ) );
1189
1190 if( selection.IsHover() )
1191 m_toolMgr->RunAction( ACTIONS::selectionClear );
1192
1193 return 0;
1194}
1195
1196
1198{
1199 const PCB_SELECTION& selection = m_selectionTool->RequestSelection(
1200 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1201 {
1202 // Iterate from the back so we don't have to worry about removals.
1203 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1204 {
1205 BOARD_ITEM* item = aCollector[i];
1206
1207 if( !dynamic_cast<PCB_TRACK*>( item ) )
1208 aCollector.Remove( item );
1209 }
1210
1211 sTool->FilterCollectorForLockedItems( aCollector );
1212 } );
1213
1214 m_selectionTool->ReportFilteredLockedItems();
1215
1216 BOARD_COMMIT commit( this );
1217
1218 for( EDA_ITEM* item : selection )
1219 {
1220 if( item->Type() == PCB_VIA_T )
1221 {
1222 PCB_VIA* via = static_cast<PCB_VIA*>( item );
1223
1224 commit.Modify( via );
1225
1226 int new_width;
1227 int new_drill;
1228
1229 if( via->GetViaType() == VIATYPE::MICROVIA )
1230 {
1231 NETCLASS* netClass = via->GetEffectiveNetClass();
1232
1233 new_width = netClass->GetuViaDiameter();
1234 new_drill = netClass->GetuViaDrill();
1235 }
1236 else
1237 {
1238 new_width = board()->GetDesignSettings().GetCurrentViaSize();
1239 new_drill = board()->GetDesignSettings().GetCurrentViaDrill();
1240 }
1241
1242 via->SetDrill( new_drill );
1243 // TODO(JE) padstacks - is this correct behavior already? If so, also change stack mode
1244 via->SetWidth( PADSTACK::ALL_LAYERS, new_width );
1245 }
1246 else if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
1247 {
1248 PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item );
1249
1250 wxCHECK( track, 0 );
1251
1252 commit.Modify( track );
1253
1254 int new_width = board()->GetDesignSettings().GetCurrentTrackWidth();
1255 track->SetWidth( new_width );
1256 }
1257 }
1258
1259 commit.Push( _( "Edit Track Width/Via Size" ) );
1260
1261 if( selection.IsHover() )
1262 {
1263 m_toolMgr->RunAction( ACTIONS::selectionClear );
1264
1265 // Notify other tools of the changes -- This updates the visual ratsnest
1267 }
1268
1269 return 0;
1270}
1271
1272
1274{
1275 if( m_toolMgr->GetTool<ROUTER_TOOL>() && m_toolMgr->GetTool<ROUTER_TOOL>()->IsToolActive() )
1276 return 0;
1277
1278 bool isNext = aEvent.IsAction( &PCB_ACTIONS::changeTrackLayerNext );
1279
1280 const PCB_SELECTION& selection = m_selectionTool->RequestSelection(
1281 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1282 {
1283 // Iterate from the back so we don't have to worry about removals.
1284 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1285 {
1286 BOARD_ITEM* item = aCollector[i];
1287
1288 if( !dynamic_cast<PCB_TRACK*>( item ) )
1289 aCollector.Remove( item );
1290 }
1291
1292 sTool->FilterCollectorForLockedItems( aCollector );
1293 } );
1294
1295 m_selectionTool->ReportFilteredLockedItems();
1296
1297 PCB_LAYER_ID origLayer = frame()->GetActiveLayer();
1298
1299 if( isNext )
1300 m_toolMgr->RunAction( PCB_ACTIONS::layerNext );
1301 else
1302 m_toolMgr->RunAction( PCB_ACTIONS::layerPrev );
1303
1304 PCB_LAYER_ID newLayer = frame()->GetActiveLayer();
1305
1306 if( newLayer == origLayer )
1307 return 0;
1308
1309 BOARD_COMMIT commit( this );
1310
1311 for( EDA_ITEM* item : selection )
1312 {
1313 if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
1314 {
1315 PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item );
1316
1317 wxCHECK( track, 0 );
1318
1319 commit.Modify( track );
1320
1321 track->SetLayer( newLayer );
1322 }
1323 }
1324
1325 commit.Push( _( "Edit Track Layer" ) );
1326
1327 if( selection.IsHover() )
1328 {
1329 m_toolMgr->RunAction( ACTIONS::selectionClear );
1330
1331 // Notify other tools of the changes -- This updates the visual ratsnest
1333 }
1334
1335 return 0;
1336}
1337
1338
1340{
1341 // Store last used fillet radius to allow pressing "enter" if repeat fillet is required
1342 static int filletRadius = 0;
1343
1344 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
1345 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1346 {
1347 // Iterate from the back so we don't have to worry about removals.
1348 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1349 {
1350 BOARD_ITEM* item = aCollector[i];
1351
1352 if( !dynamic_cast<PCB_TRACK*>( item ) )
1353 aCollector.Remove( item );
1354 }
1355
1356 sTool->FilterCollectorForLockedItems( aCollector );
1357 } );
1358
1359 if( m_selectionTool->ReportFilteredLockedItems() )
1360 return 0;
1361
1362 if( selection.Size() < 2 )
1363 {
1364 frame()->ShowInfoBarMsg( _( "At least two straight track segments must be selected." ) );
1365 return 0;
1366 }
1367
1368 WX_UNIT_ENTRY_DIALOG dlg( frame(), _( "Fillet Tracks" ), _( "Radius:" ), filletRadius );
1369
1370 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue() == 0 )
1371 return 0;
1372
1373 filletRadius = dlg.GetValue();
1374
1375 struct FILLET_OP
1376 {
1377 PCB_TRACK* t1;
1378 PCB_TRACK* t2;
1379 // Start point of track is modified after PCB_ARC is added, otherwise the end point:
1380 bool t1Start = true;
1381 bool t2Start = true;
1382 };
1383
1384 std::vector<FILLET_OP> filletOperations;
1385 bool operationPerformedOnAtLeastOne = false;
1386 bool didOneAttemptFail = false;
1387 std::set<PCB_TRACK*> processedTracks;
1388
1389 auto processFilletOp = [&]( PCB_TRACK* aTrack, bool aStartPoint )
1390 {
1391 std::shared_ptr<CONNECTIVITY_DATA> c = board()->GetConnectivity();
1392 VECTOR2I anchor = aStartPoint ? aTrack->GetStart() : aTrack->GetEnd();
1393 std::vector<BOARD_CONNECTED_ITEM*> itemsOnAnchor;
1394
1395 itemsOnAnchor = c->GetConnectedItemsAtAnchor( aTrack, anchor, baseConnectedTypes );
1396
1397 if( itemsOnAnchor.size() > 0 && selection.Contains( itemsOnAnchor.at( 0 ) )
1398 && itemsOnAnchor.at( 0 )->Type() == PCB_TRACE_T )
1399 {
1400 PCB_TRACK* trackOther = static_cast<PCB_TRACK*>( itemsOnAnchor.at( 0 ) );
1401
1402 // Make sure we don't fillet the same pair of tracks twice
1403 if( processedTracks.find( trackOther ) == processedTracks.end() )
1404 {
1405 if( itemsOnAnchor.size() == 1 )
1406 {
1407 FILLET_OP filletOp;
1408 filletOp.t1 = aTrack;
1409 filletOp.t2 = trackOther;
1410 filletOp.t1Start = aStartPoint;
1411 filletOp.t2Start = aTrack->IsPointOnEnds( filletOp.t2->GetStart() );
1412 filletOperations.push_back( filletOp );
1413 }
1414 else
1415 {
1416 // User requested to fillet these two tracks but not possible as
1417 // there are other elements connected at that point
1418 didOneAttemptFail = true;
1419 }
1420 }
1421 }
1422 };
1423
1424 for( EDA_ITEM* item : selection )
1425 {
1426 if( item->Type() == PCB_TRACE_T )
1427 {
1428 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
1429
1430 if( track->GetLength() > 0 )
1431 {
1432 processFilletOp( track, true ); // on the start point of track
1433 processFilletOp( track, false ); // on the end point of track
1434
1435 processedTracks.insert( track );
1436 }
1437 }
1438 }
1439
1440 BOARD_COMMIT commit( this );
1441 std::vector<BOARD_ITEM*> itemsToAddToSelection;
1442
1443 for( FILLET_OP filletOp : filletOperations )
1444 {
1445 PCB_TRACK* track1 = filletOp.t1;
1446 PCB_TRACK* track2 = filletOp.t2;
1447
1448 bool trackOnStart = track1->IsPointOnEnds( track2->GetStart() );
1449 bool trackOnEnd = track1->IsPointOnEnds( track2->GetEnd() );
1450
1451 if( trackOnStart && trackOnEnd )
1452 continue; // Ignore duplicate tracks
1453
1454 if( ( trackOnStart || trackOnEnd ) && track1->GetLayer() == track2->GetLayer() )
1455 {
1456 SEG t1Seg( track1->GetStart(), track1->GetEnd() );
1457 SEG t2Seg( track2->GetStart(), track2->GetEnd() );
1458
1459 if( t1Seg.ApproxCollinear( t2Seg ) )
1460 continue;
1461
1462 SHAPE_ARC sArc( t1Seg, t2Seg, filletRadius );
1463 VECTOR2I t1newPoint, t2newPoint;
1464
1465 auto setIfPointOnSeg = []( VECTOR2I& aPointToSet, const SEG& aSegment, const VECTOR2I& aVecToTest )
1466 {
1467 VECTOR2I segToVec = aSegment.NearestPoint( aVecToTest ) - aVecToTest;
1468
1469 // Find out if we are on the segment (minimum precision)
1471 {
1472 aPointToSet.x = aVecToTest.x;
1473 aPointToSet.y = aVecToTest.y;
1474 return true;
1475 }
1476
1477 return false;
1478 };
1479
1480 //Do not draw a fillet if the end points of the arc are not within the track segments
1481 if( !setIfPointOnSeg( t1newPoint, t1Seg, sArc.GetP0() )
1482 && !setIfPointOnSeg( t2newPoint, t2Seg, sArc.GetP0() ) )
1483 {
1484 didOneAttemptFail = true;
1485 continue;
1486 }
1487
1488 if( !setIfPointOnSeg( t1newPoint, t1Seg, sArc.GetP1() )
1489 && !setIfPointOnSeg( t2newPoint, t2Seg, sArc.GetP1() ) )
1490 {
1491 didOneAttemptFail = true;
1492 continue;
1493 }
1494
1495 PCB_ARC* tArc = new PCB_ARC( frame()->GetBoard(), &sArc );
1496 tArc->SetLayer( track1->GetLayer() );
1497 tArc->SetWidth( track1->GetWidth() );
1498 tArc->SetNet( track1->GetNet() );
1499 tArc->SetLocked( track1->IsLocked() );
1500 tArc->SetHasSolderMask( track1->HasSolderMask() );
1502 commit.Add( tArc );
1503 itemsToAddToSelection.push_back( tArc );
1504
1505 commit.Modify( track1 );
1506 commit.Modify( track2 );
1507
1508 if( filletOp.t1Start )
1509 track1->SetStart( t1newPoint );
1510 else
1511 track1->SetEnd( t1newPoint );
1512
1513 if( filletOp.t2Start )
1514 track2->SetStart( t2newPoint );
1515 else
1516 track2->SetEnd( t2newPoint );
1517
1518 operationPerformedOnAtLeastOne = true;
1519 }
1520 }
1521
1522 commit.Push( _( "Fillet Tracks" ) );
1523
1524 //select the newly created arcs
1525 for( BOARD_ITEM* item : itemsToAddToSelection )
1526 m_selectionTool->AddItemToSel( item );
1527
1528 if( !operationPerformedOnAtLeastOne )
1529 frame()->ShowInfoBarMsg( _( "Unable to fillet the selected track segments." ) );
1530 else if( didOneAttemptFail )
1531 frame()->ShowInfoBarMsg( _( "Some of the track segments could not be filleted." ) );
1532
1533 return 0;
1534}
1535
1536
1546static std::optional<int> GetRadiusParams( PCB_BASE_EDIT_FRAME& aFrame, const wxString& aTitle, int& aPersitentRadius )
1547{
1548 WX_UNIT_ENTRY_DIALOG dlg( &aFrame, aTitle, _( "Radius:" ), aPersitentRadius );
1549
1550 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue() == 0 )
1551 return std::nullopt;
1552
1553 aPersitentRadius = dlg.GetValue();
1554
1555 return aPersitentRadius;
1556}
1557
1558
1559static std::optional<DOGBONE_CORNER_ROUTINE::PARAMETERS> GetDogboneParams( PCB_BASE_EDIT_FRAME& aFrame )
1560{
1561 // Persistent parameters
1562 static DOGBONE_CORNER_ROUTINE::PARAMETERS s_dogBoneParams{
1563 pcbIUScale.mmToIU( 1 ),
1564 true,
1565 };
1566
1567 std::vector<WX_MULTI_ENTRY_DIALOG::ENTRY> entries{
1568 {
1569 _( "Arc radius:" ),
1570 WX_MULTI_ENTRY_DIALOG::UNIT_BOUND{ s_dogBoneParams.DogboneRadiusIU },
1571 wxEmptyString,
1572 },
1573 {
1574 _( "Add slots in acute corners" ),
1575 WX_MULTI_ENTRY_DIALOG::CHECKBOX{ s_dogBoneParams.AddSlots },
1576 _( "Add slots in acute corners to allow access to a cutter of the given radius" ),
1577 },
1578 };
1579
1580 WX_MULTI_ENTRY_DIALOG dlg( &aFrame, _( "Dogbone Corner Settings" ), entries );
1581
1582 if( dlg.ShowModal() == wxID_CANCEL )
1583 return std::nullopt;
1584
1585 std::vector<WX_MULTI_ENTRY_DIALOG::RESULT> results = dlg.GetValues();
1586 wxCHECK( results.size() == 2, std::nullopt );
1587
1588 try
1589 {
1590 s_dogBoneParams.DogboneRadiusIU = std::get<long long int>( results[0] );
1591 s_dogBoneParams.AddSlots = std::get<bool>( results[1] );
1592 }
1593 catch( const std::bad_variant_access& )
1594 {
1595 wxASSERT( false );
1596 return std::nullopt;
1597 }
1598
1599 return s_dogBoneParams;
1600}
1601
1610static std::optional<CHAMFER_PARAMS> GetChamferParams( PCB_BASE_EDIT_FRAME& aFrame )
1611{
1612 // Non-zero and the KLC default for Fab layer chamfers
1613 const int default_setback = pcbIUScale.mmToIU( 1 );
1614 // Store last used setback to allow pressing "enter" if repeat chamfer is required
1615 static CHAMFER_PARAMS params{ default_setback, default_setback };
1616
1617 WX_UNIT_ENTRY_DIALOG dlg( &aFrame, _( "Chamfer Lines" ), _( "Chamfer setback:" ), params.m_chamfer_setback_a );
1618
1619 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue() == 0 )
1620 return std::nullopt;
1621
1622 params.m_chamfer_setback_a = dlg.GetValue();
1623 // It's hard to easily specify an asymmetric chamfer (which line gets the longer setback?),
1624 // so we just use the same setback for each
1625 params.m_chamfer_setback_b = params.m_chamfer_setback_a;
1626
1627 return params;
1628}
1629
1630
1632{
1633 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
1634 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1635 {
1636 std::vector<VECTOR2I> pts;
1637
1638 // Iterate from the back so we don't have to worry about removals.
1639 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1640 {
1641 BOARD_ITEM* item = aCollector[i];
1642
1643 // We've converted the polygon and rectangle to segments, so drop everything
1644 // that isn't a segment at this point
1645 if( !item->IsType(
1646 { PCB_SHAPE_LOCATE_SEGMENT_T, PCB_SHAPE_LOCATE_POLY_T, PCB_SHAPE_LOCATE_RECT_T } ) )
1647 {
1648 aCollector.Remove( item );
1649 }
1650 }
1651
1652 sTool->FilterCollectorForLockedItems( aCollector );
1653 } );
1654
1655 m_selectionTool->ReportFilteredLockedItems();
1656
1657 std::set<PCB_SHAPE*> lines_to_add;
1658 std::vector<PCB_SHAPE*> items_to_remove;
1659
1660 for( EDA_ITEM* item : selection )
1661 {
1662 std::vector<VECTOR2I> pts;
1663 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( item );
1664 PCB_LAYER_ID layer = graphic->GetLayer();
1665 int width = graphic->GetWidth();
1666
1667 if( graphic->GetShape() == SHAPE_T::RECTANGLE )
1668 {
1669 items_to_remove.push_back( graphic );
1670 VECTOR2I start( graphic->GetStart() );
1671 VECTOR2I end( graphic->GetEnd() );
1672 pts.emplace_back( start );
1673 pts.emplace_back( VECTOR2I( end.x, start.y ) );
1674 pts.emplace_back( end );
1675 pts.emplace_back( VECTOR2I( start.x, end.y ) );
1676 }
1677
1678 if( graphic->GetShape() == SHAPE_T::POLY )
1679 {
1680 items_to_remove.push_back( graphic );
1681
1682 for( int jj = 0; jj < graphic->GetPolyShape().VertexCount(); ++jj )
1683 pts.emplace_back( graphic->GetPolyShape().CVertex( jj ) );
1684 }
1685
1686 for( size_t jj = 1; jj < pts.size(); ++jj )
1687 {
1688 PCB_SHAPE* line = new PCB_SHAPE( frame()->GetModel(), SHAPE_T::SEGMENT );
1689
1690 line->SetStart( pts[jj - 1] );
1691 line->SetEnd( pts[jj] );
1692 line->SetWidth( width );
1693 line->SetLayer( layer );
1694 lines_to_add.insert( line );
1695 }
1696
1697 if( pts.size() > 1 )
1698 {
1699 PCB_SHAPE* line = new PCB_SHAPE( frame()->GetModel(), SHAPE_T::SEGMENT );
1700
1701 line->SetStart( pts.back() );
1702 line->SetEnd( pts.front() );
1703 line->SetWidth( width );
1704 line->SetLayer( layer );
1705 lines_to_add.insert( line );
1706 }
1707 }
1708
1709 int segmentCount = selection.CountType( PCB_SHAPE_LOCATE_SEGMENT_T ) + lines_to_add.size();
1710
1711 if( aEvent.IsAction( &PCB_ACTIONS::extendLines ) && segmentCount != 2 )
1712 {
1713 frame()->ShowInfoBarMsg( _( "Exactly two lines must be selected to extend them." ) );
1714
1715 for( PCB_SHAPE* line : lines_to_add )
1716 delete line;
1717
1718 return 0;
1719 }
1720 else if( segmentCount < 2 )
1721 {
1722 frame()->ShowInfoBarMsg( _( "A shape with at least two lines must be selected." ) );
1723
1724 for( PCB_SHAPE* line : lines_to_add )
1725 delete line;
1726
1727 return 0;
1728 }
1729
1730 BOARD_COMMIT commit( this );
1731
1732 // Items created like lines from a rectangle
1733 for( PCB_SHAPE* item : lines_to_add )
1734 {
1735 commit.Add( item );
1736 selection.Add( item );
1737 }
1738
1739 // Remove items like rectangles that we decomposed into lines
1740 for( PCB_SHAPE* item : items_to_remove )
1741 {
1742 selection.Remove( item );
1743 commit.Remove( item );
1744 }
1745
1746 for( EDA_ITEM* item : selection )
1747 item->ClearFlags( STRUCT_DELETED );
1748
1749 // List of thing to select at the end of the operation
1750 // (doing it as we go will invalidate the iterator)
1751 std::vector<BOARD_ITEM*> items_to_select_on_success;
1752
1753 // And same for items to deselect
1754 std::vector<BOARD_ITEM*> items_to_deselect_on_success;
1755
1756 // Handle modifications to existing items by the routine
1757 // How to deal with this depends on whether we're in the footprint editor or not
1758 // and whether the item was conjured up by decomposing a polygon or rectangle
1759 auto item_modification_handler = [&]( BOARD_ITEM& aItem )
1760 {
1761 // If the item was "conjured up" it will be added later separately
1762 if( !alg::contains( lines_to_add, &aItem ) )
1763 {
1764 commit.Modify( &aItem );
1765 items_to_select_on_success.push_back( &aItem );
1766 }
1767 };
1768
1769 bool any_items_created = !lines_to_add.empty();
1770 auto item_creation_handler = [&]( std::unique_ptr<BOARD_ITEM> aItem )
1771 {
1772 any_items_created = true;
1773 items_to_select_on_success.push_back( aItem.get() );
1774 commit.Add( aItem.release() );
1775 };
1776
1777 bool any_items_removed = !items_to_remove.empty();
1778 auto item_removal_handler = [&]( BOARD_ITEM& aItem )
1779 {
1780 aItem.SetFlags( STRUCT_DELETED );
1781 any_items_removed = true;
1782 items_to_deselect_on_success.push_back( &aItem );
1783 commit.Remove( &aItem );
1784 };
1785
1786 // Combine these callbacks into a CHANGE_HANDLER to inject in the ROUTINE
1787 ITEM_MODIFICATION_ROUTINE::CALLABLE_BASED_HANDLER change_handler( item_creation_handler, item_modification_handler,
1788 item_removal_handler );
1789
1790 // Construct an appropriate tool
1791 std::unique_ptr<PAIRWISE_LINE_ROUTINE> pairwise_line_routine;
1792
1793 if( aEvent.IsAction( &PCB_ACTIONS::filletLines ) )
1794 {
1795 static int s_filletRadius = pcbIUScale.mmToIU( 1 );
1796 std::optional<int> filletRadiusIU = GetRadiusParams( *frame(), _( "Fillet Lines" ), s_filletRadius );
1797
1798 if( filletRadiusIU.has_value() )
1799 {
1800 pairwise_line_routine =
1801 std::make_unique<LINE_FILLET_ROUTINE>( frame()->GetModel(), change_handler, *filletRadiusIU );
1802 }
1803 }
1804 else if( aEvent.IsAction( &PCB_ACTIONS::dogboneCorners ) )
1805 {
1806 std::optional<DOGBONE_CORNER_ROUTINE::PARAMETERS> dogboneParams = GetDogboneParams( *frame() );
1807
1808 if( dogboneParams.has_value() )
1809 {
1810 pairwise_line_routine =
1811 std::make_unique<DOGBONE_CORNER_ROUTINE>( frame()->GetModel(), change_handler, *dogboneParams );
1812 }
1813 }
1814 else if( aEvent.IsAction( &PCB_ACTIONS::chamferLines ) )
1815 {
1816 std::optional<CHAMFER_PARAMS> chamfer_params = GetChamferParams( *frame() );
1817
1818 if( chamfer_params.has_value() )
1819 {
1820 pairwise_line_routine =
1821 std::make_unique<LINE_CHAMFER_ROUTINE>( frame()->GetModel(), change_handler, *chamfer_params );
1822 }
1823 }
1824 else if( aEvent.IsAction( &PCB_ACTIONS::extendLines ) )
1825 {
1826 pairwise_line_routine = std::make_unique<LINE_EXTENSION_ROUTINE>( frame()->GetModel(), change_handler );
1827 }
1828
1829 if( !pairwise_line_routine )
1830 {
1831 // Didn't construct any mofication routine - user must have cancelled
1832 commit.Revert();
1833 return 0;
1834 }
1835
1836 // Apply the tool to every line pair
1837 alg::for_all_pairs( selection.begin(), selection.end(),
1838 [&]( EDA_ITEM* a, EDA_ITEM* b )
1839 {
1840 if( ( a->GetFlags() & STRUCT_DELETED ) == 0 && ( b->GetFlags() & STRUCT_DELETED ) == 0 )
1841 {
1842 PCB_SHAPE* line_a = static_cast<PCB_SHAPE*>( a );
1843 PCB_SHAPE* line_b = static_cast<PCB_SHAPE*>( b );
1844
1845 pairwise_line_routine->ProcessLinePair( *line_a, *line_b );
1846 }
1847 } );
1848
1849 // Select added and modified items
1850 for( BOARD_ITEM* item : items_to_select_on_success )
1851 m_selectionTool->AddItemToSel( item, true );
1852
1853 // Deselect removed items
1854 for( BOARD_ITEM* item : items_to_deselect_on_success )
1855 m_selectionTool->RemoveItemFromSel( item, true );
1856
1857 if( any_items_removed )
1858 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1859
1860 if( any_items_created )
1861 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1862
1863 // Notify other tools of the changes
1864 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1865
1866 commit.Push( pairwise_line_routine->GetCommitDescription() );
1867
1868 if( const std::optional<wxString> msg = pairwise_line_routine->GetStatusMessage( segmentCount ) )
1869 frame()->ShowInfoBarMsg( *msg );
1870
1871 return 0;
1872}
1873
1874
1876{
1877 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
1878 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1879 {
1880 std::vector<VECTOR2I> pts;
1881
1882 // Iterate from the back so we don't have to worry about removals.
1883 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1884 {
1885 BOARD_ITEM* item = aCollector[i];
1886
1887 if( !item->IsType( { PCB_SHAPE_LOCATE_POLY_T, PCB_ZONE_T } ) )
1888 aCollector.Remove( item );
1889
1890 if( ZONE* zone = dyn_cast<ZONE*>( item ) )
1891 {
1892 if( zone->IsTeardropArea() )
1893 aCollector.Remove( item );
1894 }
1895 }
1896
1897 sTool->FilterCollectorForLockedItems( aCollector );
1898 } );
1899
1900 m_selectionTool->ReportFilteredLockedItems();
1901
1902 // Store last used value
1903 static int s_toleranceValue = pcbIUScale.mmToIU( 3 );
1904
1905 WX_UNIT_ENTRY_DIALOG dlg( frame(), _( "Simplify Shapes" ), _( "Tolerance value:" ), s_toleranceValue );
1906
1907 if( dlg.ShowModal() == wxID_CANCEL )
1908 return 0;
1909
1910 s_toleranceValue = dlg.GetValue();
1911
1912 if( s_toleranceValue <= 0 )
1913 return 0;
1914
1915 BOARD_COMMIT commit{ this };
1916
1917 std::vector<PCB_SHAPE*> shapeList;
1918
1919 for( EDA_ITEM* item : selection )
1920 {
1921 commit.Modify( item );
1922
1923 if( PCB_SHAPE* shape = dyn_cast<PCB_SHAPE*>( item ) )
1924 {
1925 SHAPE_POLY_SET& poly = shape->GetPolyShape();
1926
1927 poly.SimplifyOutlines( s_toleranceValue );
1928 }
1929
1930 if( ZONE* zone = dyn_cast<ZONE*>( item ) )
1931 {
1932 SHAPE_POLY_SET* poly = zone->Outline();
1933
1934 poly->SimplifyOutlines( s_toleranceValue );
1935 zone->HatchBorder();
1936 }
1937 }
1938
1939 commit.Push( _( "Simplify Polygons" ) );
1940
1941 // Notify other tools of the changes
1943
1944 return 0;
1945}
1946
1947
1949{
1950 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
1951 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1952 {
1953 std::vector<VECTOR2I> pts;
1954
1955 // Iterate from the back so we don't have to worry about removals.
1956 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1957 {
1958 BOARD_ITEM* item = aCollector[i];
1959
1960 // We've converted the polygon and rectangle to segments, so drop everything
1961 // that isn't a segment at this point
1962 if( !item->IsType(
1963 { PCB_SHAPE_LOCATE_SEGMENT_T, PCB_SHAPE_LOCATE_ARC_T, PCB_SHAPE_LOCATE_BEZIER_T } ) )
1964 {
1965 aCollector.Remove( item );
1966 }
1967 }
1968
1969 sTool->FilterCollectorForLockedItems( aCollector );
1970 } );
1971
1972 m_selectionTool->ReportFilteredLockedItems();
1973
1974 // Store last used value
1975 static int s_toleranceValue = pcbIUScale.mmToIU( 3 );
1976
1977 WX_UNIT_ENTRY_DIALOG dlg( frame(), _( "Heal Shapes" ), _( "Tolerance value:" ), s_toleranceValue );
1978
1979 if( dlg.ShowModal() == wxID_CANCEL )
1980 return 0;
1981
1982 s_toleranceValue = dlg.GetValue();
1983
1984 if( s_toleranceValue <= 0 )
1985 return 0;
1986
1987 BOARD_COMMIT commit{ this };
1988
1989 std::vector<PCB_SHAPE*> shapeList;
1990
1991 for( EDA_ITEM* item : selection )
1992 {
1993 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item ) )
1994 {
1995 shapeList.push_back( shape );
1996 commit.Modify( shape );
1997 }
1998 }
1999
2000 ConnectBoardShapes( shapeList, s_toleranceValue );
2001
2002 commit.Push( _( "Heal Shapes" ) );
2003
2004 // Notify other tools of the changes
2006
2007 return 0;
2008}
2009
2010
2012{
2013 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
2014 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2015 {
2016 // Iterate from the back so we don't have to worry about removals.
2017 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
2018 {
2019 BOARD_ITEM* item = aCollector[i];
2020
2021 static const std::vector<KICAD_T> polygonBooleanTypes = {
2025 };
2026
2027 if( !item->IsType( polygonBooleanTypes ) )
2028 aCollector.Remove( item );
2029 }
2030
2031 sTool->FilterCollectorForLockedItems( aCollector );
2032 } );
2033
2034 m_selectionTool->ReportFilteredLockedItems();
2035
2036 const EDA_ITEM* const last_item = selection.GetLastAddedItem();
2037
2038 // Gather or construct polygon source shapes to merge
2039 std::vector<PCB_SHAPE*> items_to_process;
2040
2041 for( EDA_ITEM* item : selection )
2042 {
2043 items_to_process.push_back( static_cast<PCB_SHAPE*>( item ) );
2044
2045 // put the last one in the selection at the front of the vector
2046 // so it can be used as the property donor and as the basis for the
2047 // boolean operation
2048 if( item == last_item )
2049 std::swap( items_to_process.back(), items_to_process.front() );
2050 }
2051
2052 BOARD_COMMIT commit{ this };
2053
2054 // Handle modifications to existing items by the routine
2055 auto item_modification_handler = [&]( BOARD_ITEM& aItem )
2056 {
2057 commit.Modify( &aItem );
2058 };
2059
2060 std::vector<BOARD_ITEM*> items_to_select_on_success;
2061
2062 auto item_creation_handler = [&]( std::unique_ptr<BOARD_ITEM> aItem )
2063 {
2064 items_to_select_on_success.push_back( aItem.get() );
2065 commit.Add( aItem.release() );
2066 };
2067
2068 auto item_removal_handler = [&]( BOARD_ITEM& aItem )
2069 {
2070 commit.Remove( &aItem );
2071 };
2072
2073 // Combine these callbacks into a CHANGE_HANDLER to inject in the ROUTINE
2074 ITEM_MODIFICATION_ROUTINE::CALLABLE_BASED_HANDLER change_handler( item_creation_handler, item_modification_handler,
2075 item_removal_handler );
2076
2077 // Construct an appropriate routine
2078 std::unique_ptr<POLYGON_BOOLEAN_ROUTINE> boolean_routine;
2079
2080 const auto create_routine = [&]() -> std::unique_ptr<POLYGON_BOOLEAN_ROUTINE>
2081 {
2082 // (Re-)construct the boolean routine based on the action
2083 // This is done here so that we can re-init the routine if we need to
2084 // go again in the reverse order.
2085
2086 BOARD_ITEM_CONTAINER* const model = frame()->GetModel();
2087 wxCHECK( model, nullptr );
2088
2089 if( aEvent.IsAction( &PCB_ACTIONS::mergePolygons ) )
2090 {
2091 return std::make_unique<POLYGON_MERGE_ROUTINE>( model, change_handler );
2092 }
2093 else if( aEvent.IsAction( &PCB_ACTIONS::subtractPolygons ) )
2094 {
2095 return std::make_unique<POLYGON_SUBTRACT_ROUTINE>( model, change_handler );
2096 }
2097 else if( aEvent.IsAction( &PCB_ACTIONS::intersectPolygons ) )
2098 {
2099 return std::make_unique<POLYGON_INTERSECT_ROUTINE>( model, change_handler );
2100 }
2101 return nullptr;
2102 };
2103
2104 const auto run_routine = [&]()
2105 {
2106 // Perform the operation on each polygon
2107 for( PCB_SHAPE* shape : items_to_process )
2108 boolean_routine->ProcessShape( *shape );
2109
2110 boolean_routine->Finalize();
2111 };
2112
2113 boolean_routine = create_routine();
2114
2115 wxCHECK_MSG( boolean_routine, 0, "Could not find a polygon routine for this action" );
2116
2117 // First run the routine and see what we get
2118 run_routine();
2119
2120 // If we are doing a non-commutative operation (e.g. subtract), and we just got null,
2121 // assume the user meant go in a different opposite order
2122 if( !boolean_routine->IsCommutative() && items_to_select_on_success.empty() )
2123 {
2124 // Clear the commit and the selection
2125 commit.Revert();
2126 items_to_select_on_success.clear();
2127
2128 std::map<const PCB_SHAPE*, VECTOR2I::extended_type> items_area;
2129
2130 for( PCB_SHAPE* shape : items_to_process )
2131 {
2132 VECTOR2I::extended_type area = shape->GetBoundingBox().GetArea();
2133 items_area[shape] = area;
2134 }
2135
2136 // Sort the shapes by their bounding box area in descending order
2137 // This way we will start with the largest shape first and subtract the smaller ones
2138 // This may not work perfectly in all cases, but it works well when the larger
2139 // shape completely contains the smaller ones, which is probably the most common case.
2140 // In other cases, the user will need to select the shapes in the correct order (i.e.
2141 // the largest shape last), or do the subtractions in multiple steps.
2142 std::sort( items_to_process.begin(), items_to_process.end(),
2143 [&]( const PCB_SHAPE* a, const PCB_SHAPE* b )
2144 {
2145 return items_area[a] > items_area[b];
2146 } );
2147
2148 // Run the routine again
2149 boolean_routine = create_routine();
2150 run_routine();
2151 }
2152
2153 // Select new items
2154 for( BOARD_ITEM* item : items_to_select_on_success )
2155 m_selectionTool->AddItemToSel( item, true );
2156
2157 // Notify other tools of the changes
2159
2160 commit.Push( boolean_routine->GetCommitDescription() );
2161
2162 if( const std::optional<wxString> msg = boolean_routine->GetStatusMessage() )
2163 frame()->ShowInfoBarMsg( *msg );
2164
2165 return 0;
2166}
2167
2168
2170{
2172
2173 // A constraint selected by clicking its badge has no board selection, so Edit targets it here
2174 // before the normal properties path (mirrors the Delete hook for a badge-selected constraint).
2175 if( CONSTRAINT_EDIT_TOOL* constraintTool = m_toolMgr->GetTool<CONSTRAINT_EDIT_TOOL>();
2176 constraintTool && constraintTool->TryEditSelectedConstraint() )
2177 {
2178 return 0;
2179 }
2180
2181 const PCB_SELECTION& selection = m_selectionTool->RequestSelection(
2182 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2183 {
2184 } );
2185
2186 // Snapshot undo depth to detect whether dialog actually committed
2187 // Cancel leaves it unchanged and must not trigger a constraint resolve
2188 const int undoBefore = editFrame->GetUndoCommandCount();
2189
2190 // Tracks & vias are treated in a special way:
2192 {
2193 DIALOG_TRACK_VIA_PROPERTIES dlg( editFrame, selection );
2194 dlg.ShowQuasiModal(); // QuasiModal required for NET_SELECTOR
2195 }
2197 {
2198 std::vector<PCB_TABLECELL*> cells;
2199
2200 for( EDA_ITEM* item : selection.Items() )
2201 cells.push_back( static_cast<PCB_TABLECELL*>( item ) );
2202
2203 DIALOG_TABLECELL_PROPERTIES dlg( editFrame, cells );
2204
2205 // QuasiModal required for syntax help and Scintilla auto-complete
2206 dlg.ShowQuasiModal();
2207
2209 {
2210 PCB_TABLE* table = static_cast<PCB_TABLE*>( cells[0]->GetParent() );
2211 DIALOG_TABLE_PROPERTIES tableDlg( frame(), table );
2212
2213 tableDlg.ShowQuasiModal(); // Scintilla's auto-complete requires quasiModal
2214 }
2215 }
2216 else if( selection.Size() == 1 && selection.Front()->IsBOARD_ITEM() )
2217 {
2218 // Display properties dialog
2219 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
2220
2221 // Do not handle undo buffer, it is done by the properties dialogs
2222 editFrame->OnEditItemRequest( item );
2223
2224 // Notify other tools of the changes
2226 }
2227 else if( selection.Size() == 0 && getView()->IsLayerVisible( LAYER_DRAWINGSHEET ) )
2228 {
2229 DS_PROXY_VIEW_ITEM* ds = editFrame->GetCanvas()->GetDrawingSheet();
2230 VECTOR2D cursorPos = getViewControls()->GetCursorPosition( false );
2231
2232 if( ds && ds->HitTestDrawingSheetItems( getView(), cursorPos ) )
2233 m_toolMgr->PostAction( ACTIONS::pageSettings );
2234 else
2236 }
2237
2238 // Position or geometry edit via these dialogs settles constraints as if item were dragged
2239 // holding edited item and moving neighbors gated on real commit so canceled dialog skips the solve
2240 if( editFrame->GetUndoCommandCount() > undoBefore )
2241 {
2242 if( CONSTRAINT_EDIT_TOOL* constraintTool = m_toolMgr->GetTool<CONSTRAINT_EDIT_TOOL>() )
2243 {
2244 std::vector<PCB_SHAPE*> shapes;
2246 constraintTool->SolveAfterEdit( shapes );
2247 }
2248 }
2249
2250 if( selection.IsHover() )
2251 {
2252 m_toolMgr->RunAction( ACTIONS::selectionClear );
2253 }
2254 else
2255 {
2256 // Check for items becoming invisible and drop them from the selection.
2257
2258 PCB_SELECTION selCopy = selection;
2259 LSET visible = editFrame->GetBoard()->GetVisibleLayers();
2260
2261 for( EDA_ITEM* eda_item : selCopy )
2262 {
2263 if( !eda_item->IsBOARD_ITEM() )
2264 continue;
2265
2266 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( eda_item );
2267
2268 if( !( item->GetLayerSet() & visible ).any() )
2269 m_selectionTool->RemoveItemFromSel( item );
2270 }
2271 }
2272
2273 if( m_dragging )
2274 {
2277 }
2278
2279 return 0;
2280}
2281
2282
2284{
2285 const PCB_SELECTION& selection = m_selectionTool->RequestSelection(
2286 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2287 {
2288 sTool->FilterCollectorForMarkers( aCollector );
2289 sTool->FilterCollectorForHierarchy( aCollector, true );
2290 sTool->FilterCollectorForTableCells( aCollector );
2291 sTool->FilterCollectorForLockedItems( aCollector );
2292 } );
2293
2294 m_selectionTool->ReportFilteredLockedItems();
2295
2297 {
2298 wxBell();
2299 return 0;
2300 }
2301
2303 BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( selection.Front() );
2304
2305 if( editFrame && item )
2306 editFrame->OpenVertexEditor( item );
2307
2308 return 0;
2309}
2310
2311
2312void EDIT_TOOL::collectConstraintShapes( const SELECTION& aSelection, std::vector<PCB_SHAPE*>& aShapes )
2313{
2314 // Recurse so constrained shapes carried inside a transformed footprint or group seed their
2315 // clusters too; a top-level-only walk would leave those constraints silently violated.
2316 // PCB_GROUP::RunOnChildren only descends into groups and generators, so recurse through every
2317 // container ourselves; the visited set guards against overlapping ownership paths.
2318 std::unordered_set<BOARD_ITEM*> visited;
2319
2320 std::function<void( BOARD_ITEM* )> collect =
2321 [&]( BOARD_ITEM* aItem )
2322 {
2323 if( !aItem || !visited.insert( aItem ).second )
2324 return;
2325
2326 if( aItem->Type() == PCB_SHAPE_T )
2327 aShapes.push_back( static_cast<PCB_SHAPE*>( aItem ) );
2328
2329 aItem->RunOnChildren( collect, RECURSE_MODE::NO_RECURSE );
2330 };
2331
2332 for( EDA_ITEM* item : aSelection )
2333 {
2334 if( item->IsBOARD_ITEM() )
2335 collect( static_cast<BOARD_ITEM*>( item ) );
2336 }
2337}
2338
2339
2341{
2342 CONSTRAINT_EDIT_TOOL* constraintTool = m_toolMgr->GetTool<CONSTRAINT_EDIT_TOOL>();
2343
2344 if( !constraintTool )
2345 return;
2346
2347 std::vector<PCB_SHAPE*> shapes;
2348 collectConstraintShapes( aSelection, shapes );
2349
2350 constraintTool->SolveAfterMove( shapes );
2351}
2352
2353
2354int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
2355{
2356 if( isRouterActive() )
2357 {
2358 wxBell();
2359 return 0;
2360 }
2361
2363 BOARD_COMMIT localCommit( this );
2364 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
2365
2366 if( !commit )
2367 commit = &localCommit;
2368
2369 // Be sure that there is at least one item that we can modify. If nothing was selected before,
2370 // try looking for the stuff under mouse cursor (i.e. KiCad old-style hover selection)
2371 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
2372 [&]( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2373 {
2374 sTool->FilterCollectorForMarkers( aCollector );
2375 sTool->FilterCollectorForHierarchy( aCollector, true );
2376 sTool->FilterCollectorForFreePads( aCollector, false );
2377 sTool->FilterCollectorForTableCells( aCollector );
2378
2379 // Filter locked items if in board editor and in free-pad-mode. (If we're not in
2380 // free-pad mode we delay this until the second RequestSelection().)
2381 if( !m_isFootprintEditor && frame()->GetPcbNewSettings()->m_AllowFreePads )
2382 sTool->FilterCollectorForLockedItems( aCollector );
2383 } );
2384
2385 m_selectionTool->ReportFilteredLockedItems();
2386
2387 if( selection.Empty() )
2388 return 0;
2389
2390 std::optional<VECTOR2I> oldRefPt;
2391 bool is_hover = selection.IsHover(); // N.B. This must be saved before the second
2392 // call to RequestSelection() below
2393
2394 if( selection.HasReferencePoint() )
2395 oldRefPt = selection.GetReferencePoint();
2396
2397 // Now filter out pads if not in free pads mode. We cannot do this in the first
2398 // RequestSelection() as we need the reference point when a pad is the selection front.
2399 if( !m_isFootprintEditor && !frame()->GetPcbNewSettings()->m_AllowFreePads )
2400 {
2401 selection = m_selectionTool->RequestSelection(
2402 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2403 {
2404 sTool->FilterCollectorForMarkers( aCollector );
2405 sTool->FilterCollectorForHierarchy( aCollector, true );
2406 sTool->FilterCollectorForFreePads( aCollector );
2407 sTool->FilterCollectorForTableCells( aCollector );
2408 sTool->FilterCollectorForLockedItems( aCollector );
2409 } );
2410
2411 m_selectionTool->ReportFilteredLockedItems();
2412 }
2413
2414 // Did we filter everything out? If so, don't try to operate further
2415 if( selection.Empty() )
2416 return 0;
2417
2418 // Some PCB_SHAPE must be rotated around their center instead of their start point in
2419 // order to stay to the same place (at least RECT and POLY)
2420 // Note a RECT shape rotated by a not cardinal angle is a POLY shape
2421 bool usePcbShapeCenter = false;
2422
2423 if( selection.Size() == 1 && !m_dragging && dynamic_cast<PCB_SHAPE*>( selection.Front() ) )
2424 {
2425 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( selection.Front() );
2426
2427 if( shape->GetShape() == SHAPE_T::RECTANGLE || shape->GetShape() == SHAPE_T::POLY )
2428 usePcbShapeCenter = true;
2429 }
2430
2431 if( selection.Size() == 1 && !m_dragging && dynamic_cast<PCB_TABLE*>( selection.Front() ) )
2432 usePcbShapeCenter = true;
2433
2434 if( selection.Size() == 1 && !m_dragging && dynamic_cast<PCB_TEXTBOX*>( selection.Front() ) )
2435 {
2436 selection.SetReferencePoint( static_cast<PCB_TEXTBOX*>( selection.Front() )->GetCenter() );
2437 }
2438 else if( usePcbShapeCenter )
2439 {
2440 selection.SetReferencePoint( static_cast<PCB_SHAPE*>( selection.Front() )->GetCenter() );
2441 }
2442 else
2443 {
2445 }
2446
2447 VECTOR2I refPt = selection.GetReferencePoint();
2448 EDA_ANGLE rotateAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *editFrame, aEvent );
2449
2450 if( frame()->GetCanvas()->GetView()->GetGAL()->IsFlippedX() )
2451 rotateAngle = -rotateAngle;
2452
2453 // Calculate view bounding box
2454 BOX2I viewBBox = selection.Front()->ViewBBox();
2455
2456 for( EDA_ITEM* item : selection )
2457 viewBBox.Merge( item->ViewBBox() );
2458
2459 // Check if the view bounding box will go out of bounds
2460 VECTOR2D rotPos = viewBBox.GetPosition();
2461 VECTOR2D rotEnd = viewBBox.GetEnd();
2462
2463 RotatePoint( &rotPos.x, &rotPos.y, refPt.x, refPt.y, rotateAngle );
2464 RotatePoint( &rotEnd.x, &rotEnd.y, refPt.x, refPt.y, rotateAngle );
2465
2466 typedef std::numeric_limits<int> coord_limits;
2467
2468 int max = coord_limits::max() - COORDS_PADDING;
2469 int min = -max;
2470
2471 bool outOfBounds = rotPos.x < min || rotPos.x > max || rotPos.y < min || rotPos.y > max || rotEnd.x < min
2472 || rotEnd.x > max || rotEnd.y < min || rotEnd.y > max;
2473
2474 if( !outOfBounds )
2475 {
2476 for( EDA_ITEM* item : selection )
2477 {
2478 commit->Modify( item, nullptr, RECURSE_MODE::RECURSE );
2479
2480 if( item->IsBOARD_ITEM() )
2481 {
2482 BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item );
2483
2484 board_item->Rotate( refPt, rotateAngle );
2485 board_item->Normalize();
2486
2487 if( board_item->Type() == PCB_FOOTPRINT_T )
2488 static_cast<FOOTPRINT*>( board_item )->InvalidateComponentClassCache();
2489 }
2490 }
2491
2492 // Don't push a separate undo entry when we're in the middle of a move operation.
2493 // The parent move will handle the commit.
2494 if( !localCommit.Empty() && !m_dragging )
2495 {
2496 localCommit.Push( _( "Rotate" ) );
2498 }
2499
2500 if( is_hover && !m_dragging )
2501 m_toolMgr->RunAction( ACTIONS::selectionClear );
2502
2504
2505 if( m_dragging )
2506 {
2509 }
2510 }
2511
2512 // Restore the old reference so any mouse dragging that occurs doesn't make the selection jump
2513 // to this now invalid reference
2514 if( oldRefPt )
2515 selection.SetReferencePoint( *oldRefPt );
2516 else
2517 selection.ClearReferencePoint();
2518
2519 return 0;
2520}
2521
2522
2526static void mirrorPad( PAD& aPad, const VECTOR2I& aMirrorPoint, FLIP_DIRECTION aFlipDirection )
2527{
2528 // TODO(JE) padstacks
2530 aPad.FlipPrimitives( aFlipDirection );
2531
2532 VECTOR2I tmpPt = aPad.GetPosition();
2533 MIRROR( tmpPt, aMirrorPoint, aFlipDirection );
2534 aPad.SetPosition( tmpPt );
2535
2536 tmpPt = aPad.GetOffset( PADSTACK::ALL_LAYERS );
2537 MIRROR( tmpPt, VECTOR2I{ 0, 0 }, aFlipDirection );
2538 aPad.SetOffset( PADSTACK::ALL_LAYERS, tmpPt );
2539
2540 VECTOR2I tmpz = aPad.GetDelta( PADSTACK::ALL_LAYERS );
2541 MIRROR( tmpz, VECTOR2I{ 0, 0 }, aFlipDirection );
2542 aPad.SetDelta( PADSTACK::ALL_LAYERS, tmpz );
2543
2544 aPad.SetOrientation( -aPad.GetOrientation() );
2545}
2546
2547
2548const std::vector<KICAD_T> EDIT_TOOL::MirrorableItems = {
2551};
2552
2553
2554int EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
2555{
2556 if( isRouterActive() )
2557 {
2558 wxBell();
2559 return 0;
2560 }
2561
2562 BOARD_COMMIT localCommit( this );
2563 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
2564
2565 if( !commit )
2566 commit = &localCommit;
2567
2568 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
2569 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2570 {
2571 sTool->FilterCollectorForMarkers( aCollector );
2572 sTool->FilterCollectorForHierarchy( aCollector, true );
2573 sTool->FilterCollectorForFreePads( aCollector );
2574 sTool->FilterCollectorForLockedItems( aCollector );
2575 } );
2576
2577 m_selectionTool->ReportFilteredLockedItems();
2578
2579 if( selection.Empty() )
2580 return 0;
2581
2583 VECTOR2I mirrorPoint = selection.GetReferencePoint();
2584
2587
2588 int skippedFootprints = 0;
2589 int skippedGroups = 0;
2590
2591 for( EDA_ITEM* item : selection )
2592 {
2593 if( !item->IsType( MirrorableItems ) )
2594 {
2595 if( item->Type() == PCB_FOOTPRINT_T )
2596 skippedFootprints++;
2597
2598 continue;
2599 }
2600
2601 // Skip groups that hold non-mirrorable items, else the rest would tear away from them.
2602 if( item->Type() == PCB_GROUP_T && !groupMirrorable( static_cast<PCB_GROUP*>( item ) ) )
2603 {
2604 skippedGroups++;
2605 continue;
2606 }
2607
2608 commit->Modify( item, nullptr, RECURSE_MODE::RECURSE );
2609
2610 // modify each object as necessary
2611 switch( item->Type() )
2612 {
2613 case PCB_SHAPE_T:
2614 static_cast<PCB_SHAPE*>( item )->Mirror( mirrorPoint, flipDirection );
2615 break;
2616
2617 case PCB_ZONE_T:
2618 static_cast<ZONE*>( item )->Mirror( mirrorPoint, flipDirection );
2619 break;
2620
2621 case PCB_FIELD_T:
2622 case PCB_TEXT_T:
2623 static_cast<PCB_TEXT*>( item )->Mirror( mirrorPoint, flipDirection );
2624 break;
2625
2626 case PCB_TEXTBOX_T:
2627 static_cast<PCB_TEXTBOX*>( item )->Mirror( mirrorPoint, flipDirection );
2628 break;
2629
2630 case PCB_TABLE_T: static_cast<PCB_TABLE*>( item )->Mirror( mirrorPoint, flipDirection ); break;
2631
2632 case PCB_PAD_T:
2633 mirrorPad( *static_cast<PAD*>( item ), mirrorPoint, flipDirection );
2634 break;
2635
2636 case PCB_TRACE_T:
2637 case PCB_ARC_T:
2638 case PCB_VIA_T:
2639 static_cast<PCB_TRACK*>( item )->Mirror( mirrorPoint, flipDirection );
2640 break;
2641
2642 case PCB_GROUP_T:
2643 static_cast<PCB_GROUP*>( item )->Mirror( mirrorPoint, flipDirection );
2644 break;
2645
2646 case PCB_GENERATOR_T:
2647 static_cast<PCB_GENERATOR*>( item )->Mirror( mirrorPoint, flipDirection );
2648 break;
2649
2650 case PCB_POINT_T:
2651
2652 static_cast<PCB_POINT*>( item )->Mirror( mirrorPoint, flipDirection ); break;
2653
2654 default:
2655 // it's likely the commit object is wrong if you get here
2656 UNIMPLEMENTED_FOR( item->GetClass() );
2657 }
2658 }
2659
2660 // Don't push a separate undo entry when we're in the middle of a move operation.
2661 // The parent move will handle the commit.
2662 if( !localCommit.Empty() && !m_dragging )
2663 {
2664 localCommit.Push( _( "Mirror" ) );
2666 }
2667
2668 if( skippedFootprints > 0 && !m_dragging )
2669 {
2670 frame()->ShowInfoBarMsg( _( "Footprints cannot be mirrored. Use Flip to move them to "
2671 "the other side of the board." ) );
2672 }
2673 else if( skippedGroups > 0 && !m_dragging )
2674 {
2675 frame()->ShowInfoBarMsg( _( "Groups containing footprints or other items that cannot be "
2676 "mirrored were skipped." ) );
2677 }
2678
2679 if( selection.IsHover() && !m_dragging )
2680 m_toolMgr->RunAction( ACTIONS::selectionClear );
2681
2683
2684 if( m_dragging )
2685 {
2688 }
2689
2690 return 0;
2691}
2692
2693
2695{
2696 if( isRouterActive() )
2697 {
2698 wxBell();
2699 return 0;
2700 }
2701
2702 BOARD_COMMIT localCommit( this );
2703 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
2704
2705 if( !commit )
2706 commit = &localCommit;
2707
2708 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
2709 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2710 {
2711 sTool->FilterCollectorForHierarchy( aCollector, true );
2712 sTool->FilterCollectorForLockedItems( aCollector );
2713 } );
2714
2715 m_selectionTool->ReportFilteredLockedItems();
2716
2717 if( selection.Empty() )
2718 return 0;
2719
2720 auto setJustify = [&]( EDA_TEXT* aTextItem )
2721 {
2722 if( aEvent.Matches( ACTIONS::leftJustify.MakeEvent() ) )
2723 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
2724 else if( aEvent.Matches( ACTIONS::centerJustify.MakeEvent() ) )
2725 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
2726 else
2727 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
2728 };
2729
2730 for( EDA_ITEM* item : selection )
2731 {
2732 if( item->Type() == PCB_FIELD_T || item->Type() == PCB_TEXT_T )
2733 {
2734 commit->Modify( item );
2735 setJustify( static_cast<PCB_TEXT*>( item ) );
2736 }
2737 else if( item->Type() == PCB_TEXTBOX_T )
2738 {
2739 commit->Modify( item );
2740 setJustify( static_cast<PCB_TEXTBOX*>( item ) );
2741 }
2742 }
2743
2744 if( !localCommit.Empty() )
2745 {
2746 if( aEvent.Matches( ACTIONS::leftJustify.MakeEvent() ) )
2747 localCommit.Push( _( "Left Justify" ) );
2748 else if( aEvent.Matches( ACTIONS::centerJustify.MakeEvent() ) )
2749 localCommit.Push( _( "Center Justify" ) );
2750 else
2751 localCommit.Push( _( "Right Justify" ) );
2752 }
2753
2754 if( selection.IsHover() && !m_dragging )
2755 m_toolMgr->RunAction( ACTIONS::selectionClear );
2756
2758
2759 if( m_dragging )
2760 {
2763 }
2764
2765 return 0;
2766}
2767
2768
2769int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent )
2770{
2771 if( isRouterActive() )
2772 {
2773 wxBell();
2774 return 0;
2775 }
2776
2777 BOARD_COMMIT localCommit( this );
2778 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
2779
2780 if( !commit )
2781 commit = &localCommit;
2782
2783 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
2784 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2785 {
2786 sTool->FilterCollectorForMarkers( aCollector );
2787 sTool->FilterCollectorForHierarchy( aCollector, true );
2788 sTool->FilterCollectorForFreePads( aCollector );
2789 sTool->FilterCollectorForTableCells( aCollector );
2790 sTool->FilterCollectorForLockedItems( aCollector );
2791 } );
2792
2793 m_selectionTool->ReportFilteredLockedItems();
2794
2795 if( selection.Empty() )
2796 return 0;
2797
2798 std::optional<VECTOR2I> oldRefPt;
2799
2800 if( selection.HasReferencePoint() )
2801 oldRefPt = selection.GetReferencePoint();
2802
2804
2805 // Flip around the anchor for footprints, and the bounding box center for board items
2806 VECTOR2I refPt = IsFootprintEditor() ? VECTOR2I( 0, 0 ) : selection.GetCenter();
2807
2808 if( m_dragging && m_inMoveWithReference && oldRefPt )
2809 {
2810 refPt = *oldRefPt;
2811 }
2812 else if( selection.GetSize() == 1 )
2813 {
2814 // If only one item selected, flip around the selection or item anchor point (instead
2815 // of the bounding box center) to avoid moving the item anchor
2816 // but only if the item is not a PCB_SHAPE with SHAPE_T::RECTANGLE shape, because
2817 // for this shape the flip transform swap start and end coordinates and move the shape.
2818 // So using the center of the shape is better (the shape does not move)
2819 // (Tables are a bunch of rectangles, so exclude them too)
2820 PCB_SHAPE* rect = dynamic_cast<PCB_SHAPE*>( selection.GetItem( 0 ) );
2821 PCB_TABLE* table = dynamic_cast<PCB_TABLE*>( selection.GetItem( 0 ) );
2822
2823 if( !table && ( !rect || rect->GetShape() != SHAPE_T::RECTANGLE ) )
2824 refPt = selection.GetReferencePoint();
2825 }
2826
2827 const FLIP_DIRECTION flipDirection = frame()->GetPcbNewSettings()->m_FlipDirection;
2828
2829 for( EDA_ITEM* item : selection )
2830 {
2831 if( !item->IsBOARD_ITEM() )
2832 continue;
2833
2834 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
2835
2836 commit->Modify( boardItem, nullptr, RECURSE_MODE::RECURSE );
2837
2838 boardItem->Flip( refPt, flipDirection );
2839 boardItem->Normalize();
2840
2841 if( boardItem->Type() == PCB_FOOTPRINT_T )
2842 static_cast<FOOTPRINT*>( boardItem )->InvalidateComponentClassCache();
2843 }
2844
2845 // Don't push a separate undo entry when we're in the middle of a move operation.
2846 // The parent move will handle the commit.
2847 if( !localCommit.Empty() && !m_dragging )
2848 {
2849 localCommit.Push( _( "Change Side / Flip" ) );
2851 }
2852
2853 if( selection.IsHover() && !m_dragging )
2854 m_toolMgr->RunAction( ACTIONS::selectionClear );
2855
2857
2858 if( m_dragging )
2859 {
2862 }
2863
2864 // Restore the old reference so any mouse dragging that occurs doesn't make the selection jump
2865 // to this now invalid reference
2866 if( oldRefPt )
2867 selection.SetReferencePoint( *oldRefPt );
2868 else
2869 selection.ClearReferencePoint();
2870
2871 return 0;
2872}
2873
2874
2875void EDIT_TOOL::DeleteItems( const PCB_SELECTION& aItems, bool aIsCut )
2876{
2878 BOARD_COMMIT commit( this );
2879 int commitFlags = 0;
2880
2881 // As we are about to remove items, they have to be removed from the selection first
2882 m_toolMgr->RunAction( ACTIONS::selectionClear );
2883
2884 int itemsDeleted = 0;
2885 int fieldsHidden = 0;
2886 int fieldsAlreadyHidden = 0;
2887
2888 for( EDA_ITEM* item : aItems )
2889 {
2890 if( !item->IsBOARD_ITEM() )
2891 continue;
2892
2893 BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item );
2894 FOOTPRINT* parentFP = board_item->GetParentFootprint();
2895
2896 switch( item->Type() )
2897 {
2898 case PCB_FIELD_T:
2899 {
2900 PCB_FIELD* field = static_cast<PCB_FIELD*>( board_item );
2901
2902 wxASSERT( parentFP );
2903 commit.Modify( parentFP );
2904
2905 if( field->IsVisible() )
2906 {
2907 field->SetVisible( false );
2908 fieldsHidden++;
2909 }
2910 else
2911 {
2912 fieldsAlreadyHidden++;
2913 }
2914
2915 getView()->Update( parentFP );
2916 break;
2917 }
2918
2919 case PCB_TEXT_T:
2920 case PCB_SHAPE_T:
2921 case PCB_TEXTBOX_T:
2922 case PCB_BARCODE_T:
2923 case PCB_TABLE_T:
2925 case PCB_DIMENSION_T:
2926 case PCB_DIM_ALIGNED_T:
2927 case PCB_DIM_LEADER_T:
2928 case PCB_DIM_CENTER_T:
2929 case PCB_DIM_RADIAL_T:
2931 case PCB_POINT_T:
2932 commit.Remove( board_item );
2933 itemsDeleted++;
2934 break;
2935
2936 case PCB_TABLECELL_T:
2937 // Clear contents of table cell
2938 commit.Modify( board_item );
2939 static_cast<PCB_TABLECELL*>( board_item )->SetText( wxEmptyString );
2940 itemsDeleted++;
2941 break;
2942
2943 case PCB_GROUP_T:
2944 board_item->RunOnChildren(
2945 [&commit]( BOARD_ITEM* aItem )
2946 {
2947 commit.Remove( aItem );
2948 },
2950
2951 commit.Remove( board_item );
2952 itemsDeleted++;
2953 break;
2954
2955 case PCB_PAD_T:
2956 if( IsFootprintEditor() || frame()->GetPcbNewSettings()->m_AllowFreePads )
2957 {
2958 commit.Remove( board_item );
2959 itemsDeleted++;
2960 }
2961
2962 break;
2963
2964 case PCB_ZONE_T:
2965 // We process the zones special so that cutouts can be deleted when the delete
2966 // tool is called from inside a cutout when the zone is selected.
2967 // Only interact with cutouts when deleting and a single item is selected
2968 if( !aIsCut && aItems.GetSize() == 1 )
2969 {
2970 VECTOR2I curPos = getViewControls()->GetCursorPosition();
2971 ZONE* zone = static_cast<ZONE*>( board_item );
2972
2973 int outlineIdx, holeIdx;
2974
2975 if( zone->HitTestCutout( curPos, &outlineIdx, &holeIdx ) )
2976 {
2977 // Remove the cutout
2978 commit.Modify( zone );
2979 zone->RemoveCutout( outlineIdx, holeIdx );
2980 zone->UnFill();
2981
2982 // Update the display
2983 zone->HatchBorder();
2984 canvas()->Refresh();
2985
2986 // Restore the selection on the original zone
2987 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, zone );
2988
2989 break;
2990 }
2991 }
2992
2993 // Remove the entire zone otherwise
2994 commit.Remove( board_item );
2995 itemsDeleted++;
2996 break;
2997
2998 case PCB_GENERATOR_T:
2999 {
3000 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( board_item );
3001
3002 if( ( SELECTION_CONDITIONS::OnlyTypes( { PCB_GENERATOR_T } ) )( aItems ) )
3003 {
3004 m_toolMgr->RunSynchronousAction<PCB_GENERATOR*>( PCB_ACTIONS::genRemove, &commit, generator );
3005 commit.Push( _( "Delete" ), commitFlags );
3006 commitFlags |= APPEND_UNDO;
3007 }
3008 else
3009 {
3010 for( EDA_ITEM* member : generator->GetItems() )
3011 commit.Remove( member );
3012
3013 commit.Remove( board_item );
3014 }
3015
3016 itemsDeleted++;
3017 break;
3018 }
3019
3020 default:
3021 commit.Remove( board_item );
3022 itemsDeleted++;
3023 break;
3024 }
3025 }
3026
3027 // If the entered group has been emptied then leave it.
3028 PCB_GROUP* enteredGroup = m_selectionTool->GetEnteredGroup();
3029
3030 if( enteredGroup && enteredGroup->GetItems().empty() )
3031 m_selectionTool->ExitGroup();
3032
3033 if( aIsCut )
3034 {
3035 commit.Push( _( "Cut" ), commitFlags );
3036 }
3037 else if( itemsDeleted == 0 )
3038 {
3039 if( fieldsHidden == 1 )
3040 commit.Push( _( "Hide Field" ), commitFlags );
3041 else if( fieldsHidden > 1 )
3042 commit.Push( _( "Hide Fields" ), commitFlags );
3043 else if( fieldsAlreadyHidden > 0 )
3044 editFrame->ShowInfoBarError( _( "Use the Footprint Properties dialog to remove fields." ) );
3045 }
3046 else
3047 {
3048 commit.Push( _( "Delete" ), commitFlags );
3049 }
3050}
3051
3052
3053int EDIT_TOOL::Remove( const TOOL_EVENT& aEvent )
3054{
3056
3057 // A geometric constraint selected by clicking its on-canvas badge has no board selection, so a
3058 // plain Delete targets it here before the normal item-removal path. Cut is excluded: the
3059 // constraint is not on the clipboard, so it must not be silently removed (#2329).
3061
3062 if( !isCut )
3063 {
3064 if( CONSTRAINT_EDIT_TOOL* constraintTool = m_toolMgr->GetTool<CONSTRAINT_EDIT_TOOL>();
3065 constraintTool && constraintTool->TryDeleteSelectedConstraint() )
3066 {
3067 return 0;
3068 }
3069 }
3070
3071 editFrame->PushTool( aEvent );
3072
3073 std::vector<BOARD_ITEM*> lockedItems;
3074 Activate();
3075
3076 // get a copy instead of reference (as we're going to clear the selection before removing items)
3077 PCB_SELECTION selectionCopy;
3079
3080 // If we are in a "Cut" operation, then the copied selection exists already and we want to
3081 // delete exactly that; no more, no fewer. Any filtering for locked items must be done in
3082 // the copyToClipboard() routine.
3083 if( isCut )
3084 {
3085 selectionCopy = m_selectionTool->GetSelection();
3086 }
3087 else
3088 {
3089 // Hover-pick is only a fallback for an empty selection.
3090 const bool hadInitialSelection = !m_selectionTool->GetSelection().Empty();
3091
3092 // When not in free-pad mode we normally auto-promote selected pads to their parent
3093 // footprints. But this is probably a little too dangerous for a destructive operation,
3094 // so we just do the promotion but not the deletion (allowing for a second delete to do
3095 // it if that's what the user wanted).
3096 selectionCopy = m_selectionTool->RequestSelection(
3097 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
3098 {
3099 sTool->FilterCollectorForHierarchy( aCollector, true );
3100 sTool->FilterCollectorForLockedItems( aCollector );
3101 } );
3102
3103 m_selectionTool->ReportFilteredLockedItems();
3104
3105 if( hadInitialSelection && selectionCopy.Empty() )
3106 {
3107 editFrame->PopTool( aEvent );
3108 return 0;
3109 }
3110
3111 size_t beforeFPCount = selectionCopy.CountType( PCB_FOOTPRINT_T );
3112
3113 selectionCopy = m_selectionTool->RequestSelection(
3114 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
3115 {
3116 sTool->FilterCollectorForHierarchy( aCollector, true );
3117 sTool->FilterCollectorForFreePads( aCollector );
3118 sTool->FilterCollectorForLockedItems( aCollector );
3119 } );
3120
3121 if( !selectionCopy.IsHover() && m_selectionTool->GetSelection().CountType( PCB_FOOTPRINT_T ) > beforeFPCount )
3122 {
3123 wxBell();
3124 canvas()->Refresh();
3125 editFrame->PopTool( aEvent );
3126 return 0;
3127 }
3128
3129 // In "alternative" mode, we expand selected track items to their full connection.
3130 if( isAlt && ( selectionCopy.HasType( PCB_TRACE_T ) || selectionCopy.HasType( PCB_VIA_T ) ) )
3132
3133 selectionCopy = m_selectionTool->GetSelection();
3134
3135 if( selectionCopy.Empty() )
3136 {
3137 editFrame->PopTool( aEvent );
3138 return 0;
3139 }
3140 }
3141
3142 DeleteItems( selectionCopy, isCut );
3143 canvas()->Refresh();
3144
3145 editFrame->PopTool( aEvent );
3146 return 0;
3147}
3148
3149
3151{
3152 if( isRouterActive() )
3153 {
3154 wxBell();
3155 return 0;
3156 }
3157
3158 const PCB_SELECTION& selection = m_selectionTool->RequestSelection(
3159 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
3160 {
3161 sTool->FilterCollectorForMarkers( aCollector );
3162 sTool->FilterCollectorForHierarchy( aCollector, true );
3163 sTool->FilterCollectorForFreePads( aCollector, false );
3164 sTool->FilterCollectorForTableCells( aCollector );
3165 sTool->FilterCollectorForLockedItems( aCollector );
3166 } );
3167
3168 m_selectionTool->ReportFilteredLockedItems();
3169
3170 if( selection.Empty() )
3171 return 0;
3172
3173 VECTOR2I translation;
3174 EDA_ANGLE rotation;
3176
3177 // TODO: Implement a visible bounding border at the edge
3178 BOX2I sel_box = selection.GetBoundingBox();
3179
3180 DIALOG_MOVE_EXACT dialog( frame(), translation, rotation, rotationAnchor, sel_box );
3181 int ret = dialog.ShowModal();
3182
3183 if( ret == wxID_OK )
3184 {
3185 BOARD_COMMIT commit( this );
3186 EDA_ANGLE angle = rotation;
3187 VECTOR2I rp = selection.GetCenter();
3188 VECTOR2I selCenter( rp.x, rp.y );
3189
3190 // Make sure the rotation is from the right reference point
3191 selCenter += translation;
3192
3193 if( !frame()->GetPcbNewSettings()->m_Display.m_DisplayInvertYAxis )
3194 rotation = -rotation;
3195
3196 for( EDA_ITEM* item : selection )
3197 {
3198 if( !item->IsBOARD_ITEM() )
3199 continue;
3200
3201 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
3202
3203 commit.Modify( boardItem, nullptr, RECURSE_MODE::RECURSE );
3204
3205 if( !boardItem->GetParent() || !boardItem->GetParent()->IsSelected() )
3206 boardItem->Move( translation );
3207
3208 switch( rotationAnchor )
3209 {
3210 case ROTATE_AROUND_ITEM_ANCHOR: boardItem->Rotate( boardItem->GetPosition(), angle ); break;
3211 case ROTATE_AROUND_SEL_CENTER: boardItem->Rotate( selCenter, angle ); break;
3212 case ROTATE_AROUND_USER_ORIGIN: boardItem->Rotate( frame()->GetScreen()->m_LocalOrigin, angle ); break;
3214 boardItem->Rotate( board()->GetDesignSettings().GetAuxOrigin(), angle );
3215 break;
3216 }
3217
3218 if( !m_dragging )
3219 getView()->Update( boardItem );
3220 }
3221
3222 commit.Push( _( "Move Exactly" ) );
3224
3225 if( selection.IsHover() )
3226 m_toolMgr->RunAction( ACTIONS::selectionClear );
3227
3229
3230 if( m_dragging )
3231 {
3234 }
3235 }
3236
3237 return 0;
3238}
3239
3240
3242{
3243 if( isRouterActive() )
3244 {
3245 wxBell();
3246 return 0;
3247 }
3248
3249 bool increment = aEvent.IsAction( &PCB_ACTIONS::duplicateIncrement );
3250
3251 // Be sure that there is at least one item that we can modify
3252 const PCB_SELECTION& selection = m_selectionTool->RequestSelection(
3253 []( const VECTOR2I&, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
3254 {
3255 sTool->FilterCollectorForMarkers( aCollector );
3256 sTool->FilterCollectorForHierarchy( aCollector, true );
3257 sTool->FilterCollectorForFreePads( aCollector, true );
3258 sTool->FilterCollectorForTableCells( aCollector );
3259 } );
3260
3261 if( selection.Empty() )
3262 return 0;
3263
3264 // Duplicating tuning patterns alone is not supported
3265 if( selection.Size() == 1 && selection.CountType( PCB_GENERATOR_T ) )
3266 return 0;
3267
3268 // we have a selection to work on now, so start the tool process
3270 BOARD_COMMIT commit( this );
3271 FOOTPRINT* parentFootprint = nullptr;
3272
3274 parentFootprint = editFrame->GetBoard()->GetFirstFootprint();
3275
3276 // If the selection was given a hover, we do not keep the selection after completion
3277 bool is_hover = selection.IsHover();
3278
3279 std::vector<BOARD_ITEM*> new_items;
3280 new_items.reserve( selection.Size() );
3281
3282 // Maps each duplicated original KIID to its duplicate so constraints between them
3283 // can be repointed at the copies once duplication finishes
3284 std::map<KIID, KIID> idMap;
3285
3286 // Each selected item is duplicated and pushed to new_items list
3287 // Old selection is cleared, and new items are then selected.
3288 for( EDA_ITEM* item : selection )
3289 {
3290 if( !item->IsBOARD_ITEM() )
3291 continue;
3292
3293 BOARD_ITEM* dupe_item = nullptr;
3294 BOARD_ITEM* orig_item = static_cast<BOARD_ITEM*>( item );
3295
3296 if( !m_isFootprintEditor && orig_item->GetParentFootprint() )
3297 {
3298 // No sub-footprint modifications allowed outside of footprint editor
3299 }
3300 else
3301 {
3302 switch( orig_item->Type() )
3303 {
3304 case PCB_FOOTPRINT_T:
3305 case PCB_TEXT_T:
3306 case PCB_TEXTBOX_T:
3307 case PCB_BARCODE_T:
3309 case PCB_SHAPE_T:
3310 case PCB_TRACE_T:
3311 case PCB_ARC_T:
3312 case PCB_VIA_T:
3313 case PCB_ZONE_T:
3314 case PCB_TARGET_T:
3315 case PCB_POINT_T:
3316 case PCB_DIM_ALIGNED_T:
3317 case PCB_DIM_CENTER_T:
3318 case PCB_DIM_RADIAL_T:
3320 case PCB_DIM_LEADER_T:
3322 dupe_item = parentFootprint->DuplicateItem( true, &commit, orig_item );
3323 else
3324 dupe_item = orig_item->Duplicate( true, &commit );
3325
3326 // Clear the selection flag here, otherwise the PCB_SELECTION_TOOL
3327 // will not properly select it later on
3328 dupe_item->ClearSelected();
3329
3330 if( dupe_item->Type() == PCB_SHAPE_T && static_cast<PCB_SHAPE*>( dupe_item )->IsHatchedFill() )
3331 {
3332 dupe_item->SetFlags( IS_NEW );
3333 }
3334
3335 idMap[orig_item->m_Uuid] = dupe_item->m_Uuid;
3336 new_items.push_back( dupe_item );
3337 commit.Add( dupe_item );
3338 break;
3339
3340 case PCB_FIELD_T:
3341 // PCB_FIELD items are specific items (not only graphic, but are properies)
3342 // and cannot be duplicated like other footprint items. So skip it:
3343 orig_item->ClearSelected();
3344 break;
3345
3346 case PCB_PAD_T:
3347 dupe_item = parentFootprint->DuplicateItem( true, &commit, orig_item );
3348
3349 if( increment && static_cast<PAD*>( dupe_item )->CanHaveNumber() )
3350 {
3351 PAD_TOOL* padTool = m_toolMgr->GetTool<PAD_TOOL>();
3352 wxString padNumber = padTool->GetLastPadNumber();
3353 padNumber = parentFootprint->GetNextPadNumber( padNumber );
3354 padTool->SetLastPadNumber( padNumber );
3355 static_cast<PAD*>( dupe_item )->SetNumber( padNumber );
3356 }
3357
3358 // Clear the selection flag here, otherwise the PCB_SELECTION_TOOL
3359 // will not properly select it later on
3360 dupe_item->ClearSelected();
3361
3362 idMap[orig_item->m_Uuid] = dupe_item->m_Uuid;
3363 new_items.push_back( dupe_item );
3364 commit.Add( dupe_item );
3365 break;
3366
3367 case PCB_TABLE_T:
3368 // JEY TODO: tables
3369 break;
3370
3371 case PCB_GENERATOR_T:
3372 case PCB_GROUP_T:
3373 {
3374 // DeepDuplicate maps original to duplicate KIID per descendant while cloning the group
3375 // so grouped constraints can be repointed unordered iteration blocks a later pairing
3376 dupe_item = static_cast<PCB_GROUP*>( orig_item )->DeepDuplicate( true, &commit, &idMap );
3377
3378 dupe_item->RunOnChildren(
3379 [&]( BOARD_ITEM* aItem )
3380 {
3381 aItem->ClearSelected();
3382 new_items.push_back( aItem );
3383 commit.Add( aItem );
3384 },
3386
3387 dupe_item->ClearSelected();
3388 new_items.push_back( dupe_item );
3389 commit.Add( dupe_item );
3390 break;
3391 }
3392
3393 default: UNIMPLEMENTED_FOR( orig_item->GetClass() ); break;
3394 }
3395 }
3396 }
3397
3398 // Carry constraints whose members were all duplicated repointed at the copies
3399 // constraints are not selectable so add them to commit but keep them out of new selection
3400 const CONSTRAINTS& sourceConstraints = parentFootprint ? parentFootprint->Constraints()
3401 : board()->Constraints();
3402
3403 for( PCB_CONSTRAINT* clone : CloneFullySelectedConstraints( sourceConstraints, idMap ) )
3404 commit.Add( clone );
3405
3406 // Clear the old selection first
3407 m_toolMgr->RunAction( ACTIONS::selectionClear );
3408
3409 // Select the new items
3410 EDA_ITEMS nItems( new_items.begin(), new_items.end() );
3411 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &nItems );
3412
3413 // record the new items as added
3414 if( !selection.Empty() )
3415 {
3416 editFrame->DisplayToolMsg( wxString::Format( _( "Duplicated %d item(s)" ), (int) new_items.size() ) );
3417
3418 // If items were duplicated, pick them up
3419 if( doMoveSelection( aEvent, &commit, true ) )
3420 commit.Push( _( "Duplicate" ) );
3421 else
3422 commit.Revert();
3423
3424 // Deselect the duplicated item if we originally started as a hover selection
3425 if( is_hover )
3426 m_toolMgr->RunAction( ACTIONS::selectionClear );
3427 }
3428
3429 return 0;
3430}
3431
3432
3434{
3435 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
3436 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
3437 {
3438 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
3439 {
3440 switch( aCollector[i]->Type() )
3441 {
3442 case PCB_PAD_T:
3443 case PCB_TEXT_T: break;
3444 default: aCollector.Remove( i ); break;
3445 }
3446 }
3447
3448 sTool->FilterCollectorForLockedItems( aCollector );
3449 } );
3450
3451 m_selectionTool->ReportFilteredLockedItems();
3452
3453 if( selection.Empty() )
3454 return 0;
3455
3456 ACTIONS::INCREMENT param = { 1, 0 };
3457
3458 if( aEvent.HasParameter() )
3459 param = aEvent.Parameter<ACTIONS::INCREMENT>();
3460
3461 STRING_INCREMENTER incrementer;
3462 incrementer.SetSkipIOSQXZ( true );
3463
3464 // If we're coming via another action like 'Move', use that commit
3465 BOARD_COMMIT localCommit( m_toolMgr );
3466 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
3467
3468 if( !commit )
3469 commit = &localCommit;
3470
3471 for( EDA_ITEM* item : selection )
3472 {
3473 switch( item->Type() )
3474 {
3475 case PCB_PAD_T:
3476 {
3477 // Only increment pad numbers in the footprint editor
3478 if( !m_isFootprintEditor )
3479 break;
3480
3481 PAD& pad = static_cast<PAD&>( *item );
3482
3483 if( !pad.CanHaveNumber() )
3484 continue;
3485
3486 // Increment on the pad numbers
3487 std::optional<wxString> newNumber = incrementer.Increment( pad.GetNumber(), param.Delta, param.Index );
3488
3489 if( newNumber )
3490 {
3491 commit->Modify( &pad );
3492 pad.SetNumber( *newNumber );
3493 }
3494
3495 break;
3496 }
3497 case PCB_TEXT_T:
3498 {
3499 PCB_TEXT& text = static_cast<PCB_TEXT&>( *item );
3500
3501 std::optional<wxString> newText = incrementer.Increment( text.GetText(), param.Delta, param.Index );
3502
3503 if( newText )
3504 {
3505 commit->Modify( &text );
3506 text.SetText( *newText );
3507 }
3508
3509 break;
3510 }
3511 default: break;
3512 }
3513 }
3514
3515 if( selection.Front()->IsMoving() )
3516 m_toolMgr->PostAction( ACTIONS::refreshPreview );
3517
3518 commit->Push( _( "Increment" ) );
3519
3520 return 0;
3521}
3522
3523
3525{
3526 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
3527 {
3528 if( aCollector[i]->Type() != PCB_PAD_T )
3529 aCollector.Remove( i );
3530 }
3531}
3532
3533
3535{
3536 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
3537 {
3538 if( aCollector[i]->Type() != PCB_FOOTPRINT_T )
3539 aCollector.Remove( i );
3540 }
3541}
3542
3543
3545{
3546 // Can't modify an empty group
3547 if( aSelection.Empty() )
3548 return false;
3549
3550 if( ( m_dragging || aSelection[0]->IsMoving() ) && aSelection.HasReferencePoint() )
3551 return false;
3552
3553 // When there is only one item selected, the reference point is its position...
3554 if( aSelection.Size() == 1 && aSelection.Front()->Type() != PCB_TABLE_T )
3555 {
3556 if( aSelection.Front()->IsBOARD_ITEM() )
3557 {
3558 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( aSelection.Front() );
3559 aSelection.SetReferencePoint( item->GetPosition() );
3560 }
3561 }
3562 // ...otherwise modify items with regard to the grid-snapped center position
3563 else
3564 {
3565 PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
3566 VECTOR2I refPt = aSelection.GetCenter();
3567
3568 // Exclude text in the footprint editor if there's anything else selected
3570 {
3571 BOX2I nonFieldsBBox;
3572
3573 for( EDA_ITEM* item : aSelection.Items() )
3574 {
3575 if( !item->IsType( { PCB_TEXT_T, PCB_FIELD_T } ) )
3576 nonFieldsBBox.Merge( item->GetBoundingBox() );
3577 }
3578
3579 if( nonFieldsBBox.IsValid() )
3580 refPt = nonFieldsBBox.GetCenter();
3581 }
3582
3583 aSelection.SetReferencePoint( grid.ResolveSnap( refPt, nullptr ).position );
3584 }
3585
3586 return true;
3587}
3588
3589
3590bool EDIT_TOOL::pickReferencePoint( const wxString& aTooltip, const wxString& aSuccessMessage,
3591 const wxString& aCanceledMessage, VECTOR2I& aReferencePoint )
3592{
3593 PCB_PICKER_TOOL* picker = m_toolMgr->GetTool<PCB_PICKER_TOOL>();
3595 std::optional<VECTOR2I> pickedPoint;
3596 bool done = false;
3597
3598 m_statusPopup->SetText( aTooltip );
3599
3601 picker->SetSnapping( true );
3602 picker->SetCursor( KICURSOR::PLACE );
3603 picker->ClearHandlers();
3604
3605 const auto setPickerLayerSet =
3606 [&]()
3607 {
3608 MAGNETIC_SETTINGS* magSettings = editFrame->GetMagneticItemsSettings();
3609 LSET layerFilter;
3610
3611 if( !magSettings->allLayers )
3612 layerFilter = LSET( { editFrame->GetActiveLayer() } );
3613 else
3614 layerFilter = LSET::AllLayersMask();
3615
3616 picker->SetLayerSet( layerFilter );
3617 };
3618
3619 // Initial set
3620 setPickerLayerSet();
3621
3622 picker->SetClickHandler(
3623 [&]( const VECTOR2D& aPoint ) -> bool
3624 {
3625 pickedPoint = aPoint;
3626
3627 if( !aSuccessMessage.empty() )
3628 {
3629 m_statusPopup->SetText( aSuccessMessage );
3630 m_statusPopup->Expire( 800 );
3631 }
3632 else
3633 {
3634 m_statusPopup->Hide();
3635 }
3636
3637 return false; // we don't need any more points
3638 } );
3639
3640 picker->SetMotionHandler(
3641 [&]( const VECTOR2D& aPos )
3642 {
3643 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
3644 } );
3645
3646 picker->SetCancelHandler(
3647 [&]()
3648 {
3649 if( !aCanceledMessage.empty() )
3650 {
3651 m_statusPopup->SetText( aCanceledMessage );
3652 m_statusPopup->Expire( 800 );
3653 }
3654 else
3655 {
3656 m_statusPopup->Hide();
3657 }
3658 } );
3659
3660 picker->SetFinalizeHandler(
3661 [&]( const int& aFinalState )
3662 {
3663 done = true;
3664 } );
3665
3666 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
3667 m_statusPopup->Popup();
3668 canvas()->SetStatusPopup( m_statusPopup->GetPanel() );
3669
3670 m_toolMgr->RunAction( ACTIONS::pickerSubTool );
3671
3672 while( !done )
3673 {
3674 // Pass events unless we receive a null event, then we must shut down
3675 if( TOOL_EVENT* evt = Wait() )
3676 {
3677 if( evt->Matches( PCB_EVENTS::SnappingModeChangedByKeyEvent() ) )
3678 {
3679 // Update the layer set when the snapping mode changes
3680 setPickerLayerSet();
3681 }
3682
3683 evt->SetPassEvent();
3684 }
3685 else
3686 {
3687 break;
3688 }
3689 }
3690
3691 picker->ClearHandlers();
3692
3693 // Ensure statusPopup is hidden after use and before deleting it:
3694 canvas()->SetStatusPopup( nullptr );
3695 m_statusPopup->Hide();
3696
3697 if( pickedPoint )
3698 aReferencePoint = *pickedPoint;
3699
3700 return pickedPoint.has_value();
3701}
3702
3703
3705{
3706 CLIPBOARD_IO io;
3707 PCB_GRID_HELPER grid( m_toolMgr, getEditFrame<PCB_BASE_EDIT_FRAME>()->GetMagneticItemsSettings() );
3708 TOOL_EVENT selectReferencePoint( aEvent.Category(), aEvent.Action(), "pcbnew.InteractiveEdit.selectReferencePoint",
3710
3711 frame()->PushTool( selectReferencePoint );
3712 Activate();
3713
3714 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
3715 [&]( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
3716 {
3717 sTool->FilterCollectorForHierarchy( aCollector, true );
3718 sTool->FilterCollectorForMarkers( aCollector );
3719
3720 if( aEvent.IsAction( &ACTIONS::cut ) )
3721 sTool->FilterCollectorForLockedItems( aCollector );
3722 } );
3723
3724 m_selectionTool->ReportFilteredLockedItems();
3725
3726 if( !selection.Empty() )
3727 {
3728 std::vector<BOARD_ITEM*> items;
3729
3730 for( EDA_ITEM* item : selection )
3731 {
3732 if( item->IsBOARD_ITEM() )
3733 items.push_back( static_cast<BOARD_ITEM*>( item ) );
3734 }
3735
3736 VECTOR2I refPoint;
3737
3739 {
3740 if( !pickReferencePoint( _( "Select reference point for the copy..." ), _( "Selection copied" ),
3741 _( "Copy canceled" ), refPoint ) )
3742 {
3743 frame()->PopTool( selectReferencePoint );
3744 return 0;
3745 }
3746 }
3747 else
3748 {
3749 refPoint = grid.BestDragOrigin( getViewControls()->GetCursorPosition(), items );
3750 }
3751
3752 selection.SetReferencePoint( refPoint );
3753
3754 io.SetBoard( board() );
3756 frame()->SetStatusText( _( "Selection copied" ) );
3757 }
3758
3759 frame()->PopTool( selectReferencePoint );
3760
3761 if( selection.IsHover() )
3762 m_selectionTool->ClearSelection();
3763
3764 return 0;
3765}
3766
3767
3769{
3770 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
3771 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
3772 {
3773 // Anything unsupported will just be ignored
3774 } );
3775
3776 if( selection.IsHover() )
3777 m_selectionTool->ClearSelection();
3778
3779 const auto getItemText = [&]( const BOARD_ITEM& aItem ) -> wxString
3780 {
3781 switch( aItem.Type() )
3782 {
3783 case PCB_TEXT_T:
3784 case PCB_FIELD_T:
3785 case PCB_DIM_ALIGNED_T:
3786 case PCB_DIM_LEADER_T:
3787 case PCB_DIM_CENTER_T:
3788 case PCB_DIM_RADIAL_T:
3790 {
3791 // These can all go via the PCB_TEXT class
3792 const PCB_TEXT& text = static_cast<const PCB_TEXT&>( aItem );
3793 return text.GetShownText( true );
3794 }
3795 case PCB_TEXTBOX_T:
3796 case PCB_TABLECELL_T:
3797 {
3798 // This one goes via EDA_TEXT
3799 const PCB_TEXTBOX& textBox = static_cast<const PCB_TEXTBOX&>( aItem );
3800 return textBox.GetShownText( true );
3801 }
3802 case PCB_TABLE_T:
3803 {
3804 const PCB_TABLE& table = static_cast<const PCB_TABLE&>( aItem );
3805 wxString s;
3806
3807 for( int row = 0; row < table.GetRowCount(); ++row )
3808 {
3809 for( int col = 0; col < table.GetColCount(); ++col )
3810 {
3811 const PCB_TABLECELL* cell = table.GetCell( row, col );
3812 s << cell->GetShownText( true );
3813
3814 if( col < table.GetColCount() - 1 )
3815 {
3816 s << '\t';
3817 }
3818 }
3819
3820 if( row < table.GetRowCount() - 1 )
3821 {
3822 s << '\n';
3823 }
3824 }
3825 return s;
3826 }
3827 default:
3828 // No string representation for this item type
3829 break;
3830 }
3831 return wxEmptyString;
3832 };
3833
3834 wxArrayString itemTexts;
3835
3836 for( EDA_ITEM* item : selection )
3837 {
3838 if( item->IsBOARD_ITEM() )
3839 {
3840 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
3841 wxString itemText = getItemText( *boardItem );
3842
3843 itemText.Trim( false ).Trim( true );
3844
3845 if( !itemText.IsEmpty() )
3846 {
3847 itemTexts.Add( std::move( itemText ) );
3848 }
3849 }
3850 }
3851
3852 // Send the text to the clipboard
3853 if( !itemTexts.empty() )
3854 {
3855 SaveClipboard( wxJoin( itemTexts, '\n', '\0' ).ToStdString() );
3856 }
3857
3858 return 0;
3859}
3860
3861
3863{
3864 if( !copyToClipboard( aEvent ) )
3865 {
3866 // N.B. Setting the CUT flag prevents lock filtering as we only want to delete the items
3867 // that were copied to the clipboard, no more, no fewer. Filtering for locked item, if
3868 // any will be done in the copyToClipboard() routine
3869 TOOL_EVENT evt = aEvent;
3871 Remove( evt );
3872 }
3873
3874 return 0;
3875}
3876
3877
3879{
3880 board()->BuildConnectivity();
3882 canvas()->RedrawRatsnest();
3883}
3884
3885
3886// clang-format off
3888{
3890 Go( &EDIT_TOOL::Move, PCB_ACTIONS::move.MakeEvent() );
3896 Go( &EDIT_TOOL::Flip, PCB_ACTIONS::flip.MakeEvent() );
3897 Go( &EDIT_TOOL::Remove, ACTIONS::doDelete.MakeEvent() );
3904 Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorH.MakeEvent() );
3905 Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorV.MakeEvent() );
3906 Go( &EDIT_TOOL::Swap, PCB_ACTIONS::swap.MakeEvent() );
3923
3929
3933
3937
3941 Go( &EDIT_TOOL::cutToClipboard, ACTIONS::cut.MakeEvent() );
3942}
3943// clang-format on
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
static TOOL_ACTION decrementPrimary
Definition actions.h:92
static TOOL_ACTION paste
Definition actions.h:76
static TOOL_ACTION pickerSubTool
Definition actions.h:250
static TOOL_ACTION unselectAll
Definition actions.h:79
static TOOL_ACTION decrementSecondary
Definition actions.h:94
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition actions.h:223
static TOOL_ACTION copy
Definition actions.h:74
static TOOL_ACTION pasteSpecial
Definition actions.h:77
static TOOL_ACTION rightJustify
Definition actions.h:85
static TOOL_ACTION pageSettings
Definition actions.h:59
static TOOL_ACTION incrementSecondary
Definition actions.h:93
static TOOL_ACTION duplicate
Definition actions.h:80
static TOOL_ACTION incrementPrimary
Definition actions.h:91
static TOOL_ACTION doDelete
Definition actions.h:81
REMOVE_FLAGS
Definition actions.h:318
static TOOL_ACTION increment
Definition actions.h:90
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
static TOOL_ACTION leftJustify
Definition actions.h:83
static TOOL_ACTION cut
Definition actions.h:73
static TOOL_ACTION copyAsText
Definition actions.h:75
static TOOL_ACTION refreshPreview
Definition actions.h:155
static TOOL_ACTION selectAll
Definition actions.h:78
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition actions.h:228
static TOOL_ACTION centerJustify
Definition actions.h:84
Manage TOOL_ACTION objects.
void SetConditions(const TOOL_ACTION &aAction, const ACTION_CONDITIONS &aConditions)
Set the conditions the UI elements for activating a specific tool action should use for determining t...
ACTION_MENU(bool isContextMenu, TOOL_INTERACTIVE *aTool=nullptr)
Default constructor.
TOOL_MANAGER * getToolManager() const
Return an instance of TOOL_MANAGER class.
void Clear()
Remove all the entries from the menu (as well as its title).
void SetTitle(const wxString &aTitle) override
Set title for the menu.
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
virtual void Revert() override
Revert the commit by restoring the modified items state.
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
virtual void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
Abstract interface for BOARD_ITEMs capable of storing other items inside.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:83
virtual BOARD_ITEM * Duplicate(bool addToParentGroup, BOARD_COMMIT *aCommit=nullptr) const
Create a copy of this BOARD_ITEM.
void SetLocked(bool aLocked) override
Definition board_item.h:386
bool IsLocked() const override
virtual void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle)
Rotate this object.
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition board_item.h:404
FOOTPRINT * GetParentFootprint() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:315
virtual void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const
Invoke a function on all children.
Definition board_item.h:233
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:235
virtual void Normalize()
Perform any normalization required after a user rotate and/or flip.
Definition board_item.h:456
virtual void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection)
Flip this object, i.e.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:373
const LSET & GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition board.cpp:1057
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition board.h:599
const FOOTPRINTS & Footprints() const
Definition board.h:421
constexpr const Vec & GetPosition() const
Definition box2.h:207
constexpr const Vec GetEnd() const
Definition box2.h:208
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:654
constexpr const Vec GetCenter() const
Definition box2.h:226
constexpr bool IsValid() const
Definition box2.h:905
void SaveSelection(const PCB_SELECTION &selected, bool isFootprintEditor)
void SetBoard(BOARD *aBoard)
int GetCount() const
Return the number of objects in the list.
Definition collector.h:79
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition collector.h:107
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:86
bool Empty() const
Definition commit.h:134
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:102
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:74
Interactive authoring of geometric constraints (issue #2329).
bool TryDeleteSelectedConstraint()
Delete the currently badge-selected constraint; returns true if one was selected and removed.
bool TryEditSelectedConstraint()
Edit the currently badge-selected constraint's value; returns true if one was selected.
void SolveAfterMove(const std::vector< PCB_SHAPE * > &aShapes)
Re-solve the clusters of aShapes after a whole-shape edit, folded into that edit's undo.
DIALOG_GET_FOOTPRINT_BY_NAME is a helper dialog to select a footprint by its reference One can enter ...
int ShowModal() override
bool HitTestDrawingSheetItems(KIGFX::VIEW *aView, const VECTOR2I &aPosition)
void ShowInfoBarError(const wxString &aErrorMsg, bool aShowCloseButton=false, INFOBAR_MESSAGE_TYPE aType=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...
virtual int GetUndoCommandCount() const
void DisplayToolMsg(const wxString &msg) override
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:50
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
virtual VECTOR2I GetPosition() const
Definition eda_item.h:282
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:152
const KIID m_Uuid
Definition eda_item.h:531
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
void ClearSelected()
Definition eda_item.h:147
bool IsSelected() const
Definition eda_item.h:132
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition eda_item.h:202
SHAPE_POLY_SET & GetPolyShape()
SHAPE_T GetShape() const
Definition eda_shape.h:185
bool IsHatchedFill() const
Definition eda_shape.h:140
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:240
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:190
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:89
virtual bool IsVisible() const
Definition eda_text.h:208
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:381
static void collectConstraintShapes(const SELECTION &aSelection, std::vector< PCB_SHAPE * > &aShapes)
< Collect constrainable PCB_SHAPEs in aSelection recursing groups and footprints so contained shapes ...
bool isRouterActive() const
int Duplicate(const TOOL_EVENT &aItem)
Duplicate the current selection and starts a move action.
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 ...
int Flip(const TOOL_EVENT &aEvent)
Rotate currently selected items.
int SwapGateNets(const TOOL_EVENT &aEvent)
int Swap(const TOOL_EVENT &aEvent)
Swap currently selected items' positions.
bool m_inMoveWithReference
Definition edit_tool.h:249
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.
int Increment(const TOOL_EVENT &aEvent)
Increment some aspect of the selected items.q.
bool doMoveSelection(const TOOL_EVENT &aEvent, BOARD_COMMIT *aCommit, bool aAutoStart, std::vector< PCB_SHAPE * > *aConstraintShapes=nullptr)
Runs interactive move drag when aConstraintShapes is non null previews constraint solve live each tic...
bool pickReferencePoint(const wxString &aTooltip, const wxString &aSuccessMessage, const wxString &aCanceledMessage, VECTOR2I &aReferencePoint)
bool Init() override
Init() is called once upon a registration of the tool.
int EditVertices(const TOOL_EVENT &aEvent)
int ToggleFootprintAttribute(const TOOL_EVENT &aEvent)
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
int ModifyLines(const TOOL_EVENT &aEvent)
"Modify" graphical lines.
bool m_dragging
Definition edit_tool.h:248
int MoveExact(const TOOL_EVENT &aEvent)
Invoke a dialog box to allow moving of the item by an exact amount.
int Move(const TOOL_EVENT &aEvent)
Main loop in which events are handled.
static const unsigned int COORDS_PADDING
Definition edit_tool.h:254
int JustifyText(const TOOL_EVENT &aEvent)
Set the justification on any text items (or fields) in the current selection.
bool updateModificationPoint(PCB_SELECTION &aSelection)
int ChangeTrackLayer(const TOOL_EVENT &aEvent)
std::unique_ptr< STATUS_TEXT_POPUP > m_statusPopup
Definition edit_tool.h:252
int copyToClipboard(const TOOL_EVENT &aEvent)
Send the current selection to the clipboard by formatting it as a fake pcb see #AppendBoardFromClipbo...
int SwapPadNets(const TOOL_EVENT &aEvent)
Swap nets between selected pads and propagate to connected copper items (tracks, arcs,...
int Remove(const TOOL_EVENT &aEvent)
Delete currently selected items.
int cutToClipboard(const TOOL_EVENT &aEvent)
Cut the current selection to the clipboard by formatting it as a fake pcb see #AppendBoardFromClipboa...
static const std::vector< KICAD_T > MirrorableItems
Definition edit_tool.h:2548
void DeleteItems(const PCB_SELECTION &aItem, bool aIsCut)
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.
bool invokeInlineRouter(int aDragMode)
void rebuildConnectivity()
Re-solve the geometric constraints of any shapes in aSelection after a transform.
void setTransitions() override
< Set up handlers for various events.
int HealShapes(const TOOL_EVENT &aEvent)
Make ends of selected shapes meet by extending or cutting them, or adding extra geometry.
void reSolveConstraintsAfterEdit(const PCB_SELECTION &aSelection)
int ChangeTrackWidth(const TOOL_EVENT &aEvent)
int BooleanPolygons(const TOOL_EVENT &aEvent)
Modify selected polygons into a single polygon using boolean operations such as merge (union) or subt...
int copyToClipboardAsText(const TOOL_EVENT &aEvent)
Send the current selection to the clipboard as text.
int GetAndPlace(const TOOL_EVENT &aEvent)
int FilletTracks(const TOOL_EVENT &aEvent)
Fillet (i.e.
PCB_SELECTION_TOOL * m_selectionTool
Definition edit_tool.h:247
int SimplifyPolygons(const TOOL_EVENT &aEvent)
Simplify the outlines of selected polygon objects.
int Properties(const TOOL_EVENT &aEvent)
Display properties window for the selected object.
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.
int Rotate(const TOOL_EVENT &aEvent)
Rotate currently selected items.
static const TOOL_EVENT SelectedEvent
Definition actions.h:343
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:350
static const TOOL_EVENT ConnectivityChangedEvent
Selected item had a property changed (except movement)
Definition actions.h:347
static const TOOL_EVENT UnselectedEvent
Definition actions.h:344
Variant information for a footprint.
Definition footprint.h:215
void SetExcludedFromPosFiles(bool aExclude)
Definition footprint.h:235
void SetExcludedFromBOM(bool aExclude)
Definition footprint.h:232
void SetExcludedFromBOM(bool aExclude=true)
Definition footprint.h:977
const std::vector< FP_UNIT_INFO > & GetUnitInfo() const
Definition footprint.h:956
void SetExcludedFromPosFiles(bool aExclude=true)
Definition footprint.h:968
const FOOTPRINT_VARIANT * GetVariant(const wxString &aVariantName) const
Get a variant by name.
CONSTRAINTS & Constraints()
Definition footprint.h:387
BOARD_ITEM * DuplicateItem(bool addToParentGroup, BOARD_COMMIT *aCommit, const BOARD_ITEM *aItem, bool addToFootprint=false)
Duplicate a given item within the footprint, optionally adding it to the board.
bool GetExcludedFromPosFilesForVariant(const wxString &aVariantName) const
Get the exclude-from-position-files status for a specific variant.
FOOTPRINT_VARIANT * AddVariant(const wxString &aVariantName)
Add a new variant with the given name.
bool GetExcludedFromBOMForVariant(const wxString &aVariantName) const
Get the exclude-from-BOM status for a specific variant.
wxString GetNextPadNumber(const wxString &aLastPadName) const
Return the next available pad number in the footprint.
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
static bool EqualPinCounts(const FOOTPRINT *aFootprint, const std::vector< int > &aUnitIndices)
void update() override
Update menu state stub.
static std::vector< int > GetCompatibleTargets(const FOOTPRINT *aFootprint, int aSourceIdx)
OPT_TOOL_EVENT eventHandler(const wxMenuEvent &aEvent) override
Event handler stub.
static std::unordered_set< wxString > CollectSelectedPadNumbers(const SELECTION &aSelection, const FOOTPRINT *aFootprint)
static const FOOTPRINT * GetSingleEligibleFootprint(const SELECTION &aSelection)
static std::vector< int > GetUnitsHitIndices(const FOOTPRINT *aFootprint, const std::unordered_set< wxString > &aSelPadNums)
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:203
static const std::vector< KICAD_T > DraggableItems
A scan list for items that can be dragged.
Definition collectors.h:141
A handler that is based on a set of callbacks provided by the user of the ITEM_MODIFICATION_ROUTINE.
bool IsBOARD_ITEM() const
Definition view_item.h:98
virtual wxString GetClass() const =0
Return the class name.
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllLayersMask()
Definition lset.cpp:637
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:38
int GetuViaDrill() const
Definition netclass.h:163
int GetuViaDiameter() const
Definition netclass.h:155
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
void SetLastPadNumber(const wxString &aPadNumber)
Definition pad_tool.h:63
wxString GetLastPadNumber() const
Definition pad_tool.h:62
Definition pad.h:61
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition pad.h:302
VECTOR2I GetPosition() const override
Definition pad.cpp:245
void SetDelta(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
Definition pad.h:296
VECTOR2I GetOffset(PCB_LAYER_ID aLayer) const
Definition pad.cpp:796
void FlipPrimitives(FLIP_DIRECTION aFlipDirection)
Flip (mirror) the primitives left to right or top to bottom, around the anchor position in custom pad...
Definition pad.cpp:1813
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:202
void SetOffset(PCB_LAYER_ID aLayer, const VECTOR2I &aOffset)
Definition pad.cpp:785
void SetPosition(const VECTOR2I &aPos) override
Definition pad.cpp:234
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.cpp:1723
void SetOrientation(const EDA_ANGLE &aAngle)
Set the rotation angle of the pad.
Definition pad.cpp:1696
static TOOL_ACTION drag45Degree
static TOOL_ACTION duplicateIncrement
Activation of the duplication tool with incrementing (e.g. pad number)
static TOOL_ACTION layerPrev
static TOOL_ACTION changeTrackWidth
Update selected tracks & vias to the current track & via dimensions.
static TOOL_ACTION unrouteSelected
Removes all tracks from the selected items to the first pad.
Definition pcb_actions.h:74
static TOOL_ACTION mirrorH
Mirroring of selected items.
static TOOL_ACTION updateFootprint
static TOOL_ACTION breakTrack
Break a single track into two segments at the cursor.
static TOOL_ACTION pointEditorMoveMidpoint
static TOOL_ACTION getAndPlace
Find an item and start moving.
static TOOL_ACTION routerRouteSelectedFromEnd
static TOOL_ACTION swapPadNets
Swap nets between selected pads/gates (and connected copper)
static TOOL_ACTION properties
Activation of the edit tool.
static TOOL_ACTION editFpInFpEditor
static TOOL_ACTION moveWithReference
move with a reference point
static TOOL_ACTION changeTrackLayerPrev
static TOOL_ACTION swap
Swapping of selected items.
static TOOL_ACTION routerAutorouteSelected
static TOOL_ACTION moveExact
Activation of the exact move tool.
static TOOL_ACTION intersectPolygons
Intersection of multiple polygons.
static TOOL_ACTION pointEditorMoveCorner
static TOOL_ACTION genRemove
static TOOL_ACTION selectConnection
Select tracks between junctions or expands an existing selection to pads or the entire connection.
Definition pcb_actions.h:71
static TOOL_ACTION assignNetClass
static TOOL_ACTION packAndMoveFootprints
Pack and start moving selected footprints.
static TOOL_ACTION copyWithReference
copy command with manual reference point selection
static TOOL_ACTION healShapes
Connect selected shapes, possibly extending or cutting them, or adding extra geometry.
static TOOL_ACTION toggleExcludeFromBOM
static TOOL_ACTION dragFreeAngle
static TOOL_ACTION inspectClearance
static TOOL_ACTION updateLocalRatsnest
static TOOL_ACTION updateFootprints
static TOOL_ACTION deleteFull
static TOOL_ACTION unrouteSegment
Removes track segment from the selected item to the next segment.
Definition pcb_actions.h:77
static TOOL_ACTION moveIndividually
move items one-by-one
static TOOL_ACTION changeFootprints
static TOOL_ACTION toggleExcludeFromPosFiles
static TOOL_ACTION chamferLines
Chamfer (i.e. adds a straight line) all selected straight lines by a user defined setback.
static TOOL_ACTION dogboneCorners
Add "dogbone" corners to selected lines to allow routing with a cutter radius.
static TOOL_ACTION filletTracks
Fillet (i.e. adds an arc tangent to) all selected straight tracks by a user defined radius.
static TOOL_ACTION simplifyPolygons
Simplify polygon outlines.
static TOOL_ACTION interactiveOffsetTool
static TOOL_ACTION footprintProperties
static TOOL_ACTION pointEditorChamferCorner
static TOOL_ACTION filletLines
Fillet (i.e. adds an arc tangent to) all selected straight lines by a user defined radius.
static TOOL_ACTION changeFootprint
static TOOL_ACTION routerInlineDrag
Activation of the Push and Shove router (inline dragging mode)
static TOOL_ACTION pointEditorRemoveCorner
static TOOL_ACTION positionRelative
static TOOL_ACTION skip
static TOOL_ACTION move
move or drag an item
static TOOL_ACTION mirrorV
static TOOL_ACTION mergePolygons
Merge multiple polygons into a single polygon.
static TOOL_ACTION subtractPolygons
Subtract polygons from other polygons.
static TOOL_ACTION changeTrackLayerNext
static TOOL_ACTION flip
Flipping of selected objects.
static TOOL_ACTION pointEditorAddCorner
static TOOL_ACTION editVertices
Edit polygon vertices in a table.
static TOOL_ACTION swapGateNets
static TOOL_ACTION layerNext
static TOOL_ACTION extendLines
Extend selected lines to meet at a point.
static TOOL_ACTION routerRouteSelected
static TOOL_ACTION rotateCw
Rotation of selected objects.
static TOOL_ACTION rotateCcw
Common, abstract interface for edit frames.
virtual void OnEditItemRequest(BOARD_ITEM *aItem)
Install the corresponding dialog editor for the given item.
void OpenVertexEditor(BOARD_ITEM *aItem)
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
BOARD * GetBoard() const
A geometric constraint between board items (issue #2329).
DS_PROXY_VIEW_ITEM * GetDrawingSheet() const
static const TOOL_EVENT & SnappingModeChangedByKeyEvent()
Hotkey feedback.
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:51
void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const override
Invoke a function on all children.
Generic tool for picking an item.
void SetLayerSet(const LSET &aLayerSet)
Set the tool's snap layer set.
Tool that displays edit points allowing to modify items by dragging the points.
bool CanRemoveCorner(const SELECTION &aSelection)
Condition to display "Remove Corner" context menu entry.
static bool CanChamferCorner(const EDA_ITEM &aItem)
Check if a corner of the given item can be chamfered (zones, polys only).
static bool CanAddCorner(const EDA_ITEM &aItem)
Check if a corner can be added to the given item (zones, polys, segments, arcs).
A PCB_POINT is a 0-dimensional point that is used to mark a position on a PCB, or more usually a foot...
Definition pcb_point.h:39
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.
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...
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 FilterCollectorForLockedItems(GENERAL_COLLECTOR &aCollector)
In the PCB editor strip out any locked items unless the OverrideLocks checkbox is set.
void FilterCollectorForTableCells(GENERAL_COLLECTOR &aCollector) const
Promote any table cell selections to the whole table.
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_shape.h:78
void SetWidth(int aWidth) override
int GetWidth() const override
void SetEnd(const VECTOR2I &aEnd) override
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetStart(const VECTOR2I &aStart) override
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition pcb_shape.h:68
wxString GetShownText(bool aAllowExtraText, int aDepth=0) const override
Return the string actually shown after processing of the base text.
wxString GetShownText(bool aAllowExtraText, int aDepth=0) const override
Return the string actually shown after processing of the base text.
T * frame() const
bool IsFootprintEditor() const
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
BOARD * board() const
PCB_DRAW_PANEL_GAL * canvas() const
const PCB_SELECTION & selection() const
FOOTPRINT * footprint() const
void SetHasSolderMask(bool aVal)
Definition pcb_track.h:116
virtual double GetLength() const
Get the length of the track using the hypotenuse calculation.
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:89
bool HasSolderMask() const
Definition pcb_track.h:117
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:92
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
Definition pcb_track.h:119
std::optional< int > GetLocalSolderMaskMargin() const
Definition pcb_track.h:120
const VECTOR2I & GetStart() const
Definition pcb_track.h:93
const VECTOR2I & GetEnd() const
Definition pcb_track.h:90
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...
virtual void SetWidth(int aWidth)
Definition pcb_track.h:86
virtual int GetWidth() const
Definition pcb_track.h:87
void SetMotionHandler(MOTION_HANDLER aHandler)
Set a handler for mouse motion.
Definition picker_tool.h:88
void SetClickHandler(CLICK_HANDLER aHandler)
Set a handler for mouse click event.
Definition picker_tool.h:77
void SetSnapping(bool aSnap)
Definition picker_tool.h:62
void SetCursor(KICURSOR aCursor)
Definition picker_tool.h:60
void SetCancelHandler(CANCEL_HANDLER aHandler)
Set a handler for cancel events (ESC or context-menu Cancel).
Definition picker_tool.h:97
void SetFinalizeHandler(FINALIZE_HANDLER aHandler)
Set a handler for the finalize event.
bool RoutingInProgress()
Returns whether routing is currently active.
bool CanInlineDrag(int aDragMode)
Definition seg.h:38
bool ApproxCollinear(const SEG &aSeg, int aDistanceThreshold=1) const
Definition seg.cpp:791
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 bool ShowAlways(const SELECTION &aSelection)
The default condition function (always returns true).
static SELECTION_CONDITION OnlyTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if the selected items are only of given types.
virtual VECTOR2I GetCenter() const
Returns the center point of the selection area bounding box.
Definition selection.cpp:92
bool IsHover() const
Definition selection.h:85
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition selection.h:104
EDA_ITEM * Front() const
Definition selection.h:176
bool HasType(KICAD_T aType) const
Checks if there is at least one item of requested kind.
int Size() const
Returns the number of selected parts.
Definition selection.h:120
std::deque< EDA_ITEM * > & Items()
Definition selection.h:181
void SetReferencePoint(const VECTOR2I &aP)
bool Empty() const
Checks if there is anything selected.
Definition selection.h:114
bool HasReferencePoint() const
Definition selection.h:215
size_t CountType(KICAD_T aType) const
const VECTOR2I & GetP1() const
Definition shape_arc.h:115
const VECTOR2I & GetP0() const
Definition shape_arc.h:114
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
void SimplifyOutlines(int aMaxError=0)
Simplifies the lines in the polyset.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the index-th vertex in a given hole outline within a given outline.
static const int MIN_PRECISION_IU
This is the minimum precision for all the points in a shape.
Definition shape.h:129
Heuristically increment a string's n'th part from the right.
Definition increment.h:44
void SetSkipIOSQXZ(bool aSkip)
If a alphabetic part is found, skip the letters I, O, S, Q, X, Z.
Definition increment.h:50
virtual void PopTool(const TOOL_EVENT &aEvent)
Pops a tool from the stack.
virtual void PushTool(const TOOL_EVENT &aEvent)
NB: the definition of "tool" is different at the user level.
TOOL_MANAGER * GetManager() const
Return the instance of TOOL_MANAGER that takes care of the tool.
Definition tool_base.h:142
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:182
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition tool_base.cpp:40
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition tool_base.cpp:34
bool IsToolActive() const
Definition tool_base.cpp:28
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:74
Generic, UI-independent tool event.
Definition tool_event.h:167
bool HasParameter() const
Definition tool_event.h:460
TOOL_ACTIONS Action() const
Returns more specific information about the type of an event.
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:388
COMMIT * Commit() const
Definition tool_event.h:279
void SetParameter(T aParam)
Set a non-standard parameter assigned to the event.
Definition tool_event.h:524
TOOL_EVENT_CATEGORY Category() const
Return the category (eg. mouse/keyboard/action) 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.
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:469
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_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.
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:279
VECTOR2_TRAITS< int32_t >::extended_type extended_type
Definition vector2d.h:69
A dialog like WX_UNIT_ENTRY_DIALOG, but with multiple entries.
std::vector< RESULT > GetValues() const
Returns the values in the order they were added.
An extension of WX_TEXT_ENTRY_DIALOG that uses UNIT_BINDER to request a dimension (e....
int GetValue()
Return the value in internal units.
Handle a list of polygons defining a copper zone.
Definition zone.h:70
bool UnFill()
Removes the zone filling.
Definition zone.cpp:507
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:1047
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition zone.cpp:1520
void RemoveCutout(int aOutlineIdx, int aHoleIdx)
Remove a cutout from the zone.
Definition zone.cpp:1364
bool IsTeardropArea() const
Definition zone.h:788
bool SaveClipboard(const std::string &aTextUTF8)
Store information to the system clipboard.
Definition clipboard.cpp:32
std::vector< PCB_CONSTRAINT * > CloneFullySelectedConstraints(const CONSTRAINTS &aSource, const std::map< KIID, KIID > &aIdMap)
Clone the constraints in aSource that a duplicate should carry.
@ PLACE
Definition cursors.h:94
ROTATION_ANCHOR
@ ROTATE_AROUND_USER_ORIGIN
@ ROTATE_AROUND_SEL_CENTER
@ ROTATE_AROUND_AUX_ORIGIN
@ ROTATE_AROUND_ITEM_ANCHOR
static std::string ToStdString(const wxString &aStr)
#define _(s)
@ RECURSE
Definition eda_item.h:49
@ NO_RECURSE
Definition eda_item.h:50
#define IS_NEW
New item, just created.
#define STRUCT_DELETED
flag indication structures to be erased
@ SEGMENT
Definition eda_shape.h:46
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:47
static const std::vector< KICAD_T > baseConnectedTypes
static std::shared_ptr< ACTION_MENU > makeGateSwapMenu(TOOL_INTERACTIVE *aTool)
static const std::vector< KICAD_T > routableTypes
static std::shared_ptr< CONDITIONAL_MENU > makePositioningToolsMenu(TOOL_INTERACTIVE *aTool)
static FOOTPRINT * GetFootprintFromBoardByReference(PCB_BASE_FRAME &aFrame)
static std::shared_ptr< CONDITIONAL_MENU > makeShapeModificationMenu(TOOL_INTERACTIVE *aTool)
static std::optional< CHAMFER_PARAMS > GetChamferParams(PCB_BASE_EDIT_FRAME &aFrame)
Prompt the user for chamfer parameters.
static bool itemHasEditableCorners(BOARD_ITEM *aItem)
Definition edit_tool.cpp:84
static bool groupMirrorable(const PCB_GROUP *aGroup)
static const std::vector< KICAD_T > footprintTypes
static std::shared_ptr< CONDITIONAL_MENU > makeRoutingToolsMenu(TOOL_INTERACTIVE *aTool)
static const std::vector< KICAD_T > groupTypes
static const std::vector< KICAD_T > padTypes
static const std::vector< KICAD_T > nonMirrorableTypes
static bool selectionMirrorable(const SELECTION &aSelection)
static std::optional< int > GetRadiusParams(PCB_BASE_EDIT_FRAME &aFrame, const wxString &aTitle, int &aPersitentRadius)
Prompt the user for a radius and return it.
static void mirrorPad(PAD &aPad, const VECTOR2I &aMirrorPoint, FLIP_DIRECTION aFlipDirection)
Mirror a pad in the H/V axis passing through a point.
static std::shared_ptr< CONDITIONAL_MENU > makeMirrorRotateMenu(TOOL_INTERACTIVE *aTool)
static bool selectionHasEditableCorners(const SELECTION &aSelection)
static const std::vector< KICAD_T > trackTypes
static std::optional< DOGBONE_CORNER_ROUTINE::PARAMETERS > GetDogboneParams(PCB_BASE_EDIT_FRAME &aFrame)
void ConnectBoardShapes(std::vector< PCB_SHAPE * > &aShapeList, int aChainingEpsilon)
Connects shapes to each other, making continious contours (adjacent shapes will have a common vertex)...
@ LAYER_DRAWINGSHEET
Sheet frame and title block.
Definition layer_ids.h:274
@ LAYER_SCHEMATIC_DRAWINGSHEET
Definition layer_ids.h:502
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:92
constexpr void MIRROR(T &aPoint, const T &aMirrorRef)
Updates aPoint with the mirror of aPoint relative to the aMirrorRef.
Definition mirror.h:41
FLIP_DIRECTION
Definition mirror.h:23
@ LEFT_RIGHT
Flip left to right (around the Y axis)
Definition mirror.h:24
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:25
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition wxgtk/ui.cpp:839
@ DM_ANY
Definition pns_router.h:82
@ DM_FREE_ANGLE
Definition pns_router.h:80
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:96
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:80
#define _HKI(x)
Definition page_info.cpp:40
Class to handle a set of BOARD_ITEMs.
std::deque< FOOTPRINT * > FOOTPRINTS
std::deque< PCB_CONSTRAINT * > CONSTRAINTS
@ ID_POPUP_PCB_SWAP_UNIT_LAST
Definition pcbnew_id.h:58
@ ID_POPUP_PCB_SWAP_UNIT_BASE
Definition pcbnew_id.h:57
#define APPEND_UNDO
Definition sch_commit.h:37
std::vector< EDA_ITEM * > EDA_ITEMS
static std::vector< KICAD_T > connectedTypes
std::function< bool(const SELECTION &)> SELECTION_CONDITION
Functor type that checks a specific condition for selected items.
Functors that can be used to figure out how the action controls should be displayed in the UI and if ...
Parameters that define a simple chamfer operation.
KIBIS_MODEL * model
VECTOR2I end
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ AS_GLOBAL
Global action (toolbar/main menu event, global shortcut)
Definition tool_action.h:45
std::optional< TOOL_EVENT > OPT_TOOL_EVENT
Definition tool_event.h:637
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:225
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:81
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:99
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:96
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:84
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:90
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:97
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:104
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:86
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:101
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:85
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:82
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:83
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:94
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition typeinfo.h:100
@ PCB_SHAPE_LOCATE_CIRCLE_T
Definition typeinfo.h:132
@ PCB_SHAPE_LOCATE_SEGMENT_T
Definition typeinfo.h:130
@ PCB_SHAPE_LOCATE_RECT_T
Definition typeinfo.h:131
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:88
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:79
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:95
@ PCB_SHAPE_LOCATE_BEZIER_T
Definition typeinfo.h:135
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:80
@ PCB_SHAPE_LOCATE_POLY_T
Definition typeinfo.h:134
@ PCB_SHAPE_LOCATE_ARC_T
Definition typeinfo.h:133
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:91
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:93
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:87
@ PCB_POINT_T
class PCB_POINT, a 0-dimensional point
Definition typeinfo.h:106
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:89
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:98
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:56
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682