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