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