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