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