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