KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_selection_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-2017 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Tomasz Wlostowski <[email protected]>
7 * @author Maciej Suminski <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23#include <algorithm>
24#include <cmath>
25#include <functional>
26#include <stack>
27using namespace std::placeholders;
28
29#include <advanced_config.h>
30#include <macros.h>
31#include <board.h>
33#include <footprint.h>
34#include <pad.h>
35#include <pcb_point.h>
36#include <pcb_table.h>
37#include <pcb_tablecell.h>
38#include <pcb_track.h>
39#include <pcb_marker.h>
40#include <pad.h>
41#include <pcb_generator.h>
42#include <pcb_group.h>
43#include <pcb_base_edit_frame.h>
44#include <zone.h>
45#include <collectors.h>
47#include <view/view_controls.h>
48#include <gal/painter.h>
49#include <router/router_tool.h>
50#include <pcbnew_settings.h>
51#include <tool/tool_event.h>
52#include <tool/tool_manager.h>
57#include <tools/pcb_actions.h>
61#include <wx/event.h>
62#include <wx/timer.h>
63#include <wx/log.h>
64#include <wx/debug.h>
65#include <core/profile.h>
66#include <math/vector2wx.h>
67
74
75
77{
78public:
80 ACTION_MENU( true )
81 {
82 SetTitle( _( "Select" ) );
83
85
86 AppendSeparator();
87
91
92 // This could be enabled if we have better logic for picking the target net with the mouse
93 // Add( PCB_ACTIONS::deselectNet );
96
99 }
100
101private:
102 ACTION_MENU* create() const override
103 {
104 return new SELECT_MENU();
105 }
106};
107
108enum
109{
110 ID_REPLACE_TERMINAL_PAD_A = wxID_HIGHEST + 3000,
112};
113
115{
116public:
118 {
119 SetTitle( _( "Set terminal pad" ) );
120 }
121
122protected:
123 ACTION_MENU* create() const override { return new REPLACE_TERMINAL_PAD_MENU(); }
124
125 void update() override
126 {
127 Clear();
128
129 TOOL_MANAGER* toolMgr = getToolManager();
130 if( !toolMgr )
131 return;
132
133 PCB_SELECTION_TOOL* selTool = toolMgr->GetTool<PCB_SELECTION_TOOL>();
134 if( !selTool )
135 return;
136
137 const SELECTION& sel = selTool->GetSelection();
138 if( sel.Empty() )
139 return;
140
141 PAD* pad = dynamic_cast<PAD*>( sel.Front() );
142 PCB_EDIT_FRAME* frame = static_cast<PCB_EDIT_FRAME*>( toolMgr->GetToolHolder() );
143
144 if( !pad || !frame )
145 return;
146
147 NETINFO_ITEM* net = pad->GetNet();
148
149 if( !net || net->GetNetChain().IsEmpty() )
150 return;
151
152 PAD* oldA = net->GetTerminalPad( 0 );
153 PAD* oldB = net->GetTerminalPad( 1 );
154 KIID newId = pad->m_Uuid;
155
156 wxMenuItem* itemA = Append( ID_REPLACE_TERMINAL_PAD_A, _( "Terminal A" ) );
157 wxMenuItem* itemB = Append( ID_REPLACE_TERMINAL_PAD_B, _( "Terminal B" ) );
158
159 if( oldA && oldA->m_Uuid == newId )
160 itemA->Enable( false );
161
162 if( oldB && oldB->m_Uuid == newId )
163 itemB->Enable( false );
164
165 m_oldA = oldA ? oldA->m_Uuid : niluuid;
166 m_oldB = oldB ? oldB->m_Uuid : niluuid;
167 m_new = newId;
168 }
169
170 OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
171 {
172 if( aEvent.GetId() == ID_REPLACE_TERMINAL_PAD_A )
173 {
175 te.SetParameter( std::make_pair( m_oldA.AsString(), m_new.AsString() ) );
176 return te;
177 }
178 else if( aEvent.GetId() == ID_REPLACE_TERMINAL_PAD_B )
179 {
181 te.SetParameter( std::make_pair( m_oldB.AsString(), m_new.AsString() ) );
182 return te;
183 }
184
185 return OPT_TOOL_EVENT();
186 }
187
188private:
192};
193
195{
196public:
198 {
199 SetTitle( _( "Net Chains..." ) );
203 }
204
205protected:
206 ACTION_MENU* create() const override { return new NET_CHAINS_MENU(); }
207
208private:
210};
211
212
221
222
224 SELECTION_TOOL( "common.InteractiveSelection" ),
225 m_frame( nullptr ),
226 m_isFootprintEditor( false ),
228 m_enteredGroup( nullptr ),
230 m_lockedItemsFiltered( false ),
231 m_previousFirstCell( nullptr ),
232 m_priv( std::make_unique<PRIV>() )
233{
234 m_filter.lockedItems = false;
235 m_filter.footprints = true;
236 m_filter.text = true;
237 m_filter.tracks = true;
238 m_filter.vias = true;
239 m_filter.pads = true;
240 m_filter.graphics = true;
241 m_filter.zones = true;
242 m_filter.keepouts = true;
243 m_filter.dimensions = true;
244 m_filter.points = true;
245 m_filter.otherItems = true;
246}
247
248
250{
253
254 Disconnect( wxEVT_TIMER, wxTimerEventHandler( PCB_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
255}
256
257
259{
261
262 if( frame && frame->IsType( FRAME_FOOTPRINT_VIEWER ) )
263 {
264 frame->AddStandardSubMenus( *m_menu.get() );
265 return true;
266 }
267
268 std::shared_ptr<SELECT_MENU> selectMenu = std::make_shared<SELECT_MENU>();
269 selectMenu->SetTool( this );
270 m_menu->RegisterSubMenu( selectMenu );
271
272 std::shared_ptr<NET_CHAINS_MENU> netChainsMenu = std::make_shared<NET_CHAINS_MENU>();
273 netChainsMenu->SetTool( this );
274 m_menu->RegisterSubMenu( netChainsMenu );
275
276 static const std::vector<KICAD_T> tableCellTypes = { PCB_TABLECELL_T };
277
278 auto& menu = m_menu->GetMenu();
279
280 auto activeToolCondition =
281 [this] ( const SELECTION& aSel )
282 {
284 return pcbFrame && !pcbFrame->ToolStackIsEmpty();
285 };
286
287 auto haveHighlight =
288 [this]( const SELECTION& sel )
289 {
290 KIGFX::RENDER_SETTINGS* cfg = m_toolMgr->GetView()->GetPainter()->GetSettings();
291
292 return !cfg->GetHighlightNetCodes().empty();
293 };
294
295 auto groupEnterCondition =
297
298 auto applyDesignBlockLayoutCondition = []( const SELECTION& aSel )
299 {
300 for( EDA_ITEM* item : aSel )
301 {
302 if( item->Type() == PCB_GROUP_T && static_cast<PCB_GROUP*>( item )->HasDesignBlockLink() )
303 {
304 return true;
305 }
306 }
307
308 return false;
309 };
310
311 auto inGroupCondition =
312 [this] ( const SELECTION& )
313 {
314 return m_enteredGroup != nullptr;
315 };
316
317 auto tableCellSelection = SELECTION_CONDITIONS::MoreThan( 0 )
319
320 SELECTION_CONDITION netItemSelection = []( const SELECTION& aSel )
321 {
322 if( aSel.GetSize() != 1 )
323 return false;
324 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( *aSel.begin() );
325 return item->Type() == PCB_PAD_T || item->Type() == PCB_VIA_T || item->Type() == PCB_TRACE_T
326 || item->Type() == PCB_ARC_T;
327 };
328
329 if( frame && frame->IsType( FRAME_PCB_EDITOR ) )
330 {
331 menu.AddMenu( selectMenu.get(), SELECTION_CONDITIONS::NotEmpty );
332 menu.AddMenu( netChainsMenu.get(), netItemSelection );
333 menu.AddSeparator( 1000 );
334 }
335
336 // "Cancel" goes at the top of the context menu when a tool is active
337 menu.AddItem( ACTIONS::cancelInteractive, activeToolCondition, 1 );
338 menu.AddItem( ACTIONS::groupEnter, groupEnterCondition, 1 );
339 menu.AddItem( ACTIONS::groupLeave, inGroupCondition, 1 );
340 menu.AddItem( PCB_ACTIONS::applyDesignBlockLayout, applyDesignBlockLayoutCondition, 1 );
341 menu.AddItem( PCB_ACTIONS::placeLinkedDesignBlock, groupEnterCondition, 1 );
342 menu.AddItem( PCB_ACTIONS::saveToLinkedDesignBlock, groupEnterCondition, 1 );
343 menu.AddItem( PCB_ACTIONS::clearHighlight, haveHighlight, 1 );
344 menu.AddSeparator( haveHighlight, 1 );
345
346 menu.AddItem( ACTIONS::selectColumns, tableCellSelection, 2 );
347 menu.AddItem( ACTIONS::selectRows, tableCellSelection, 2 );
348 menu.AddItem( ACTIONS::selectTable, tableCellSelection, 2 );
349
350 menu.AddSeparator( 1 );
351
352 if( frame )
353 frame->AddStandardSubMenus( *m_menu.get() );
354
355 m_disambiguateTimer.SetOwner( this );
356 Connect( m_disambiguateTimer.GetId(), wxEVT_TIMER,
357 wxTimerEventHandler( PCB_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
358
359 return true;
360}
361
362
364{
367
368 if( aReason != TOOL_BASE::REDRAW )
369 {
370 if( m_enteredGroup )
371 ExitGroup();
372
373 // Deselect any item being currently in edit, to avoid unexpected behavior and remove
374 // pointers to the selected items from containers.
375 ClearSelection( true );
376 }
377
378 if( aReason == TOOL_BASE::MODEL_RELOAD )
379 getView()->GetPainter()->GetSettings()->SetHighlight( false );
380
381 // Reinsert the VIEW_GROUP, in case it was removed from the VIEW
382 view()->Remove( &m_selection );
383 view()->Add( &m_selection );
384
387}
388
389
390void PCB_SELECTION_TOOL::OnIdle( wxIdleEvent& aEvent )
391{
392 if( m_frame->ToolStackIsEmpty() && !m_multiple )
393 {
394 wxMouseState keyboardState = wxGetMouseState();
395
396 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(), keyboardState.AltDown() );
397
398 if( m_additive )
399 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ADD );
400 else if( m_subtractive )
401 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SUBTRACT );
402 else if( m_exclusive_or )
403 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::XOR );
404 else
405 m_frame->GetCanvas()->SetCurrentCursor( m_nonModifiedCursor );
406 }
407}
408
409
411{
412 // Main loop: keep receiving events
413 while( TOOL_EVENT* evt = Wait() )
414 {
415 MOUSE_DRAG_ACTION dragAction = m_frame->GetDragAction();
417
418 if( PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings() )
419 trackDragAction = cfg->m_TrackDragAction;
420
421 // on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
422 setModifiersState( evt->Modifier( MD_SHIFT ), evt->Modifier( MD_CTRL ), evt->Modifier( MD_ALT ) );
423
425 bool brd_editor = frame && frame->IsType( FRAME_PCB_EDITOR );
426 ROUTER_TOOL* router = m_toolMgr->GetTool<ROUTER_TOOL>();
427
428 // If the router tool is active, don't override
429 if( router && router->IsToolActive() && router->RoutingInProgress() )
430 {
431 evt->SetPassEvent();
432 }
433 else if( evt->IsMouseDown( BUT_LEFT ) )
434 {
435 // Avoid triggering when running under other tools
436 PCB_POINT_EDITOR *pt_tool = m_toolMgr->GetTool<PCB_POINT_EDITOR>();
437
438 if( m_frame->ToolStackIsEmpty() && pt_tool && !pt_tool->HasPoint() )
439 {
440 m_originalCursor = m_toolMgr->GetMousePosition();
441 m_disambiguateTimer.StartOnce( ADVANCED_CFG::GetCfg().m_DisambiguationMenuDelay );
442 }
443 }
444 else if( evt->IsClick( BUT_LEFT ) )
445 {
446 // If there is no disambiguation, this routine is still running and will
447 // register a `click` event when released
448 if( m_disambiguateTimer.IsRunning() )
449 {
450 m_disambiguateTimer.Stop();
451
452 // A click on a geometric-constraint badge selects that constraint instead of a
453 // board item (the badges have no selectable geometry of their own); a click that
454 // misses every badge clears any badge selection.
455 if( CONSTRAINT_EDIT_TOOL* constraintTool =
456 m_toolMgr->GetTool<CONSTRAINT_EDIT_TOOL>() )
457 {
458 if( constraintTool->SelectConstraintAt( evt->Position() ) )
459 {
460 m_canceledMenu = false;
461 continue;
462 }
463
464 constraintTool->ClearConstraintSelection();
465 }
466
467 // Single click? Select single object
468 if( m_highlight_modifier && brd_editor )
469 {
470 if( !toggleTableCellSelection( evt->Position() ) )
472 }
473 else
474 {
475 m_frame->ClearFocus();
476
477 // Mirrors eeschema's SCH_TABLE shift+click range select.
478 if( !extendTableCellSelectionTo( evt->Position() ) )
479 {
480 selectPoint( evt->Position() );
481
482 // Anchor for a subsequent shift+click or shift+drag whose IsClick
483 // jitter could otherwise promote into IsDrag, collapsing the range
484 // rectangle to (DragOrigin, Position) at the press point.
486 }
487 }
488 }
489
490 m_canceledMenu = false;
491 }
492 else if( evt->IsClick( BUT_RIGHT ) )
493 {
494 m_disambiguateTimer.Stop();
495
496 // Right click? if there is any object - show the context menu
497 bool selectionCancelled = false;
498
499 if( m_selection.Empty() )
500 {
501 selectPoint( evt->Position(), false, &selectionCancelled );
502 m_selection.SetIsHover( true );
503 }
504
505 // Show selection before opening menu
506 m_frame->GetCanvas()->ForceRefresh();
507
508 if( !selectionCancelled )
509 {
510 m_toolMgr->VetoContextMenuMouseWarp();
511 m_menu->ShowContextMenu( m_selection );
512 }
513 }
514 else if( evt->IsDblClick( BUT_LEFT ) )
515 {
516 m_disambiguateTimer.Stop();
517
518 // Double clicks make no sense in the footprint viewer
519 if( frame && frame->IsType( FRAME_FOOTPRINT_VIEWER ) )
520 {
521 evt->SetPassEvent();
522 continue;
523 }
524
525 // A double-click on a constraint badge edits that relation's value.
526 if( CONSTRAINT_EDIT_TOOL* constraintTool = m_toolMgr->GetTool<CONSTRAINT_EDIT_TOOL>();
527 constraintTool && constraintTool->EditConstraintAt( evt->Position() ) )
528 {
529 continue;
530 }
531
532 // Double click? Display the properties window
533 m_frame->ClearFocus();
534
535 if( m_selection.Empty() )
536 selectPoint( evt->Position() );
537
538 if( m_selection.GetSize() == 1 && m_selection[0]->Type() == PCB_GROUP_T )
539 EnterGroup();
540 else
542 }
543 else if( evt->IsDblClick( BUT_MIDDLE ) )
544 {
545 // Middle double click? Do zoom to fit or zoom to objects
546 if( evt->Modifier( MD_CTRL ) ) // Is CTRL key down?
548 else
549 m_toolMgr->RunAction( ACTIONS::zoomFitScreen );
550 }
551 else if( evt->Action() == TA_MOUSE_WHEEL )
552 {
553 int field = -1;
554
555 if( evt->Modifier() == ( MD_SHIFT | MD_ALT ) )
556 field = 0;
557 else if( evt->Modifier() == ( MD_CTRL | MD_ALT ) )
558 field = 1;
559 // any more?
560
561 if( field >= 0 )
562 {
563 const int delta = evt->Parameter<int>();
564 ACTIONS::INCREMENT params { delta > 0 ? 1 : -1, field };
565
566 m_toolMgr->RunAction( ACTIONS::increment, params );
567 }
568 }
569 else if( evt->IsDrag( BUT_LEFT ) )
570 {
571 m_disambiguateTimer.Stop();
572
573 // Is another tool already moving a new object? Don't allow a drag start
574 if( !m_selection.Empty() && m_selection[0]->HasFlag( IS_NEW | IS_MOVING ) )
575 {
576 evt->SetPassEvent();
577 continue;
578 }
579
580 // Drag with LMB? Select multiple objects (or at least draw a selection box)
581 // or drag them
582 m_frame->ClearFocus();
584
585 GENERAL_COLLECTOR hoverCells;
586 collectTableCellsAt( evt->DragOrigin(), hoverCells );
587
588 if( hoverCells.GetCount() )
589 {
590 if( m_selection.Empty() || SELECTION_CONDITIONS::OnlyTypes( { PCB_TABLECELL_T } )( m_selection ) )
591 {
592 selectTableCells( static_cast<PCB_TABLE*>( hoverCells[0]->GetParent() ) );
593 }
594 else
595 {
596 m_toolMgr->RunAction( PCB_ACTIONS::move );
597 }
598 }
599 else if( ( hasModifier() || dragAction == MOUSE_DRAG_ACTION::SELECT )
600 || ( m_selection.Empty() && dragAction != MOUSE_DRAG_ACTION::DRAG_ANY ) )
601 {
604 {
605 SelectRectArea( aEvent );
606 }
609 {
610 SelectPolyArea( aEvent );
611 }
612 else
613 {
614 wxASSERT_MSG( false, wxT( "Unknown selection mode" ) );
615 SelectRectArea( aEvent );
616 }
617 }
618 else
619 {
620 // Don't allow starting a drag from a zone filled area that isn't already selected
621 auto zoneFilledAreaFilter =
622 []( const VECTOR2I& aWhere, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* aTool )
623 {
624 int accuracy = aCollector.GetGuide()->Accuracy();
625 std::set<EDA_ITEM*> remove;
626
627 for( EDA_ITEM* item : aCollector )
628 {
629 if( item->Type() == PCB_ZONE_T )
630 {
631 ZONE* zone = static_cast<ZONE*>( item );
632
633 if( !zone->HitTestForCorner( aWhere, accuracy * 2 )
634 && !zone->HitTestForEdge( aWhere, accuracy ) )
635 {
636 remove.insert( zone );
637 }
638 }
639 }
640
641 for( EDA_ITEM* item : remove )
642 aCollector.Remove( item );
643 };
644
645 // See if we can drag before falling back to SelectRectArea()
646 bool doDrag = false;
647
648 if( evt->HasPosition() )
649 {
650 if( m_selection.Empty()
651 && selectPoint( evt->DragOrigin(), false, nullptr, zoneFilledAreaFilter ) )
652 {
653 m_selection.SetIsHover( true );
654 doDrag = true;
655 }
656 // Check if dragging has started within any of selected items bounding box.
657 else if( evt->HasPosition() && selectionContains( evt->DragOrigin() ) )
658 {
659 doDrag = true;
660 }
661 }
662
663 if( doDrag )
664 {
665 size_t segs = m_selection.CountType( PCB_TRACE_T );
666 size_t arcs = m_selection.CountType( PCB_ARC_T );
667 size_t vias = m_selection.CountType( PCB_VIA_T );
668 // Note: multi-track dragging is currently supported, but not multi-via
669 bool routable = ( segs >= 1 || arcs >= 1 || vias == 1 )
670 && ( segs + arcs + vias == m_selection.GetSize() );
671
672 if( routable && trackDragAction == TRACK_DRAG_ACTION::DRAG )
674 else if( routable && trackDragAction == TRACK_DRAG_ACTION::DRAG_FREE_ANGLE )
676 else
677 m_toolMgr->RunAction( PCB_ACTIONS::move );
678 }
679 else
680 {
681 // Otherwise drag a selection box
684 {
685 SelectPolyArea( aEvent );
686 }
687 else
688 {
689 SelectRectArea( aEvent );
690 }
691 }
692 }
693 }
694 else if( evt->IsCancel() )
695 {
696 m_disambiguateTimer.Stop();
697 m_frame->ClearFocus();
698
699 if( CONSTRAINT_EDIT_TOOL* constraintTool = m_toolMgr->GetTool<CONSTRAINT_EDIT_TOOL>();
700 constraintTool && constraintTool->ClearConstraintSelection() )
701 {
702 continue;
703 }
704
705 if( !GetSelection().Empty() )
706 {
708 }
709 else if( evt->FirstResponder() == this && evt->GetCommandId() == (int) WXK_ESCAPE )
710 {
711 if( m_enteredGroup )
712 {
713 ExitGroup();
714 }
715 else
716 {
717 BOARD_INSPECTION_TOOL* controller = m_toolMgr->GetTool<BOARD_INSPECTION_TOOL>();
718
719 try
720 {
721 if( controller && m_frame->GetPcbNewSettings()->m_ESCClearsNetHighlight )
722 controller->ClearHighlight( *evt );
723 }
724 catch( const std::runtime_error& e )
725 {
726 wxCHECK_MSG( false, 0, e.what() );
727 }
728 }
729 }
730 }
731 else
732 {
733 evt->SetPassEvent();
734 }
735
736
737 if( m_frame->ToolStackIsEmpty() )
738 {
739 // move cursor prediction
740 if( !hasModifier()
741 && dragAction == MOUSE_DRAG_ACTION::DRAG_SELECTED
742 && !m_selection.Empty()
743 && evt->HasPosition()
744 && selectionContains( evt->Position() ) )
745 {
747 }
748 else
749 {
751 }
752 }
753 }
754
755 // Shutting down; clear the selection
756 m_previousFirstCell = nullptr;
757 m_selection.Clear();
758 m_disambiguateTimer.Stop();
759
760 return 0;
761}
762
763
765{
766 wxCHECK_RET( m_selection.GetSize() == 1 && m_selection[0]->Type() == PCB_GROUP_T,
767 wxT( "EnterGroup called when selection is not a single group" ) );
768 PCB_GROUP* aGroup = static_cast<PCB_GROUP*>( m_selection[0] );
769
770 if( m_enteredGroup != nullptr )
771 ExitGroup();
772
774 m_enteredGroup = aGroup;
775 m_enteredGroup->SetFlags( ENTERED );
776
777 for( EDA_ITEM* member : m_enteredGroup->GetItems() )
778 select( member );
779
780 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
781
782 // Processing the selection event can re-enter the tool and ExitGroup(), which clears
783 // m_enteredGroup. If that happened, don't operate on the now-stale (possibly null) group
784 // or we would hide/overlay a null item and crash (issue #24391).
785 if( m_enteredGroup != aGroup )
786 return;
787
788 view()->Hide( m_enteredGroup, true );
791}
792
793
794void PCB_SELECTION_TOOL::ExitGroup( bool aSelectGroup )
795{
796 // Only continue if there is a group entered
797 if( m_enteredGroup == nullptr )
798 return;
799
800 m_enteredGroup->ClearFlags( ENTERED );
801 view()->Hide( m_enteredGroup, false );
803
804 if( aSelectGroup )
805 {
807 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
808 }
809
810 m_enteredGroupOverlay.Clear();
811 m_enteredGroup = nullptr;
813}
814
815
820
821
823{
824 bool selectionEmpty = m_selection.Empty();
825 m_selection.SetIsHover( selectionEmpty );
826 m_lockedItemsFiltered = false;
827
828 if( selectionEmpty )
829 {
830 m_toolMgr->RunAction( ACTIONS::selectionCursor, aClientFilter );
831 m_selection.ClearReferencePoint();
832 }
833
834 if( aClientFilter )
835 {
836 enum DISPOSITION { BEFORE = 1, AFTER, BOTH };
837
838 std::map<EDA_ITEM*, DISPOSITION> itemDispositions;
840 GENERAL_COLLECTOR collector;
841
842 collector.SetGuide( &guide );
843
844 for( EDA_ITEM* item : m_selection )
845 {
846 collector.Append( item );
847 itemDispositions[ item ] = BEFORE;
848 }
849
850 aClientFilter( VECTOR2I(), collector, this );
851
852 // Locked items were filtered with Override locks off. Keep the selection and return an
853 // empty one so the action does nothing. The banner then prompts to enable the override.
855 {
856 m_frame->GetCanvas()->ForceRefresh();
857 m_blockedSelection.Clear();
858 return m_blockedSelection;
859 }
860
861 for( EDA_ITEM* item : collector )
862 {
863 if( itemDispositions.count( item ) )
864 itemDispositions[ item ] = BOTH;
865 else
866 itemDispositions[ item ] = AFTER;
867 }
868
869 // Unhighlight the BEFORE items before highlighting the AFTER items.
870 // This is so that in the case of groups, if aClientFilter replaces a selection
871 // with the enclosing group, the unhighlight of the element doesn't undo the
872 // recursive highlighting of that element by the group.
873
874 for( std::pair<EDA_ITEM* const, DISPOSITION> itemDisposition : itemDispositions )
875 {
876 EDA_ITEM* item = itemDisposition.first;
877 DISPOSITION disposition = itemDisposition.second;
878
879 if( disposition == BEFORE )
881 }
882
883 for( std::pair<EDA_ITEM* const, DISPOSITION> itemDisposition : itemDispositions )
884 {
885 EDA_ITEM* item = itemDisposition.first;
886 DISPOSITION disposition = itemDisposition.second;
887
888 // Note that we must re-highlight even previously-highlighted items
889 // (ie: disposition BOTH) in case we removed any of their children.
890 if( disposition == AFTER || disposition == BOTH )
891 highlight( item, SELECTED, &m_selection );
892 }
893
894 m_frame->GetCanvas()->ForceRefresh();
895 }
896
897 return m_selection;
898}
899
900
902{
903 GENERAL_COLLECTORS_GUIDE guide( board()->GetVisibleLayers(), (PCB_LAYER_ID) view()->GetTopLayer(),
904 view() );
905
906 bool padsDisabled = !board()->IsElementVisible( LAYER_PADS );
907
908 // account for the globals
909 guide.SetIgnoreFPTextOnBack( !board()->IsElementVisible( LAYER_FP_TEXT ) );
910 guide.SetIgnoreFPTextOnFront( !board()->IsElementVisible( LAYER_FP_TEXT ) );
911 guide.SetIgnoreFootprintsOnBack( !board()->IsElementVisible( LAYER_FOOTPRINTS_BK ) );
912 guide.SetIgnoreFootprintsOnFront( !board()->IsElementVisible( LAYER_FOOTPRINTS_FR ) );
913 guide.SetIgnorePadsOnBack( padsDisabled );
914 guide.SetIgnorePadsOnFront( padsDisabled );
915 guide.SetIgnoreThroughHolePads( padsDisabled );
916 guide.SetIgnoreFPValues( !board()->IsElementVisible( LAYER_FP_VALUES ) );
917 guide.SetIgnoreFPReferences( !board()->IsElementVisible( LAYER_FP_REFERENCES ) );
918 guide.SetIgnoreThroughVias( ! board()->IsElementVisible( LAYER_VIAS ) );
919 guide.SetIgnoreBlindBuriedVias( ! board()->IsElementVisible( LAYER_VIAS ) );
920 guide.SetIgnoreMicroVias( ! board()->IsElementVisible( LAYER_VIAS ) );
921 guide.SetIgnoreTracks( ! board()->IsElementVisible( LAYER_TRACKS ) );
922
923 return guide;
924}
925
926
928{
929 return m_frame && m_frame->GetPcbNewSettings() && m_frame->GetPcbNewSettings()->m_CtrlClickHighlight && !m_isFootprintEditor;
930}
931
932
933bool PCB_SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag, bool* aSelectionCancelledFlag,
934 CLIENT_SELECTION_FILTER aClientFilter )
935{
937 GENERAL_COLLECTOR collector;
938 const PCB_DISPLAY_OPTIONS& displayOpts = m_frame->GetDisplayOptions();
939
941
942 if( m_enteredGroup && !m_enteredGroup->GetBoundingBox().Contains( aWhere ) )
943 ExitGroup();
944
947 aWhere, guide );
948
949 // Remove unselectable items
950 for( int i = collector.GetCount() - 1; i >= 0; --i )
951 {
952 if( !Selectable( collector[ i ] ) || ( aOnDrag && collector[i]->IsLocked() ) )
953 collector.Remove( i );
954 }
955
956 m_selection.ClearReferencePoint();
957
958 size_t preFilterCount = collector.GetCount();
960 rejected.SetAll( false );
961
962 // Apply the stateful filter (remove items disabled by the Selection Filter)
963 FilterCollectedItems( collector, false, &rejected );
964
965 if( collector.GetCount() == 0 && preFilterCount > 0 )
966 {
968 editFrame->HighlightSelectionFilter( rejected );
969
970 if( !m_additive && !m_subtractive && !m_exclusive_or && m_selection.GetSize() > 0 )
971 {
972 ClearSelection( true /*quiet mode*/ );
973 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
974 return false;
975 }
976
977 return false;
978 }
979
980 // Allow the client to do tool- or action-specific filtering to see if we can get down
981 // to a single item
982 if( aClientFilter )
983 aClientFilter( aWhere, collector, this );
984
985 FilterCollectorForHierarchy( collector, false );
986
987 FilterCollectorForFootprints( collector, aWhere );
988
989 // For subtracting, we only want items that are selected
990 if( m_subtractive )
991 {
992 for( int i = collector.GetCount() - 1; i >= 0; --i )
993 {
994 if( !collector[i]->IsSelected() )
995 collector.Remove( i );
996 }
997 }
998
999 // Apply some ugly heuristics to avoid disambiguation menus whenever possible
1000 if( collector.GetCount() > 1 && !m_skip_heuristics )
1001 {
1002 try
1003 {
1004 GuessSelectionCandidates( collector, aWhere );
1005 }
1006 catch( const std::exception& exc )
1007 {
1008 wxLogWarning( wxS( "Exception '%s' occurred attempting to guess selection candidates." ),
1009 exc.what() );
1010 return false;
1011 }
1012 }
1013
1014 // If still more than one item we're going to have to ask the user.
1015 if( collector.GetCount() > 1 )
1016 {
1017 if( aOnDrag )
1019
1020 if( !doSelectionMenu( &collector ) )
1021 {
1022 if( aSelectionCancelledFlag )
1023 *aSelectionCancelledFlag = true;
1024
1025 return false;
1026 }
1027 }
1028
1029 int addedCount = 0;
1030 bool anySubtracted = false;
1031
1033 {
1034 if( m_selection.GetSize() > 0 )
1035 {
1036 ClearSelection( true /*quiet mode*/ );
1037 anySubtracted = true;
1038 }
1039 }
1040
1041 if( collector.GetCount() > 0 )
1042 {
1043 for( int i = 0; i < collector.GetCount(); ++i )
1044 {
1045 if( m_subtractive || ( m_exclusive_or && collector[i]->IsSelected() ) )
1046 {
1047 unselect( collector[i] );
1048 anySubtracted = true;
1049 }
1050 else
1051 {
1052 select( collector[i] );
1053 addedCount++;
1054 }
1055 }
1056 }
1057
1058 if( addedCount == 1 )
1059 {
1060 m_toolMgr->ProcessEvent( EVENTS::PointSelectedEvent );
1061 return true;
1062 }
1063 else if( addedCount > 1 )
1064 {
1065 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1066 return true;
1067 }
1068 else if( anySubtracted )
1069 {
1070 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1071 return true;
1072 }
1073
1074 return false;
1075}
1076
1077
1078bool PCB_SELECTION_TOOL::selectCursor( bool aForceSelect, CLIENT_SELECTION_FILTER aClientFilter )
1079{
1080 if( aForceSelect || m_selection.Empty() )
1081 {
1082 ClearSelection( true /*quiet mode*/ );
1083 selectPoint( getViewControls()->GetCursorPosition( false ), false, nullptr, aClientFilter );
1084 }
1085
1086 return !m_selection.Empty();
1087}
1088
1089
1090// Some navigation actions are allowed in selectMultiple
1101
1102
1103static void passEvent( TOOL_EVENT* const aEvent, const TOOL_ACTION* const aAllowedActions[] )
1104{
1105 for( int i = 0; aAllowedActions[i]; ++i )
1106 {
1107 if( aEvent->IsAction( aAllowedActions[i] ) )
1108 {
1109 aEvent->SetPassEvent();
1110 break;
1111 }
1112 }
1113}
1114
1115
1117{
1118 for( PCB_TABLECELL* cell : aTable->GetCells() )
1119 {
1120 if( cell->IsSelected() )
1121 cell->SetFlags( CANDIDATE );
1122 else
1123 cell->ClearFlags( CANDIDATE );
1124 }
1125}
1126
1127
1129 PCB_TABLE* aTable )
1130{
1131 BOX2I selectionRect( aStart, aEnd );
1132 selectionRect.Normalize();
1133
1134 auto wasSelected =
1135 []( EDA_ITEM* aItem )
1136 {
1137 return ( aItem->GetFlags() & CANDIDATE ) > 0;
1138 };
1139
1140 for( PCB_TABLECELL* cell : aTable->GetCells() )
1141 {
1142 bool doSelect = false;
1143
1144 if( cell->HitTest( selectionRect, false ) )
1145 {
1146 if( m_subtractive )
1147 doSelect = false;
1148 else if( m_exclusive_or )
1149 doSelect = !wasSelected( cell );
1150 else
1151 doSelect = true;
1152 }
1153 else if( wasSelected( cell ) )
1154 {
1155 doSelect = m_additive || m_subtractive || m_exclusive_or;
1156 }
1157
1158 if( doSelect && !cell->IsSelected() )
1159 select( cell );
1160 else if( !doSelect && cell->IsSelected() )
1161 unselect( cell );
1162 }
1163}
1164
1165
1167{
1168 if( !m_additive || m_selection.GetSize() == 0
1169 || !dynamic_cast<PCB_TABLECELL*>( m_selection.GetItem( 0 ) ) )
1170 {
1171 return false;
1172 }
1173
1174 GENERAL_COLLECTOR clickCells;
1175 collectTableCellsAt( aPosition, clickCells );
1176
1177 if( clickCells.GetCount() != 1 )
1178 return false;
1179
1180 PCB_TABLECELL* clickedCell = static_cast<PCB_TABLECELL*>( clickCells[0] );
1181 PCB_TABLECELL* firstCell = static_cast<PCB_TABLECELL*>( m_selection.GetItem( 0 ) );
1182 PCB_TABLE* parentTable = static_cast<PCB_TABLE*>( clickedCell->GetParent() );
1183
1184 // Drop the cached anchor when the selection no longer holds only cells of the
1185 // clicked table, so it cannot survive into a later shift+drag on a different table.
1186 for( EDA_ITEM* item : m_selection )
1187 {
1188 if( !dynamic_cast<PCB_TABLECELL*>( item ) || item->GetParent() != parentTable )
1189 {
1190 m_previousFirstCell = nullptr;
1191 return false;
1192 }
1193 }
1194
1195 // Contains() prevents reading GetCenter() on a cell freed by an external mutation
1196 // that did not clear the cached anchor.
1198 m_previousFirstCell = firstCell;
1199
1200 if( m_previousFirstCell->GetParent() != parentTable )
1201 {
1202 m_previousFirstCell = nullptr;
1203 return false;
1204 }
1205
1206 // Snapshot the prior selection so we can fire SelectedEvent/UnselectedEvent based
1207 // on the net delta rather than on every range change.
1208 std::set<EDA_ITEM*> previousSelection;
1209
1210 for( EDA_ITEM* item : m_selection )
1211 previousSelection.insert( item );
1212
1213 // Restore main-view visibility of the previously-selected cells via the overlay
1214 // mechanism; ClearSelected() alone would leave them hidden because
1215 // highlightInternal/unhighlightInternal toggle view()->Hide.
1216 while( m_selection.GetSize() )
1217 unselect( m_selection.Front() );
1218
1219 initializeTableCellSelectionState( parentTable );
1220
1221 VECTOR2D start = m_previousFirstCell->GetCenter();
1222 VECTOR2D end = clickedCell->GetCenter();
1223 VECTOR2D topLeft( std::min( start.x, end.x ), std::min( start.y, end.y ) );
1224 VECTOR2D bottomRight( std::max( start.x, end.x ), std::max( start.y, end.y ) );
1225
1226 selectCellsBetween( topLeft, bottomRight - topLeft, parentTable );
1227
1228 bool anyAdded = false;
1229 bool anySubtracted = false;
1230
1231 for( PCB_TABLECELL* cell : parentTable->GetCells() )
1232 {
1233 bool wasInPrevious = previousSelection.count( cell ) > 0;
1234
1235 if( cell->IsSelected() && !wasInPrevious )
1236 anyAdded = true;
1237 else if( wasInPrevious && !cell->IsSelected() )
1238 anySubtracted = true;
1239 }
1240
1241 if( anyAdded )
1242 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1243
1244 if( anySubtracted )
1245 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1246
1247 return true;
1248}
1249
1250
1252 GENERAL_COLLECTOR& aCollector )
1253{
1255 ? static_cast<BOARD_ITEM*>( board()->GetFirstFootprint() )
1256 : static_cast<BOARD_ITEM*>( board() );
1257
1258 aCollector.Collect( scope, { PCB_TABLECELL_T }, aPosition, getCollectorsGuide() );
1259}
1260
1261
1263{
1264 if( m_selection.GetSize() != 1 )
1265 return nullptr;
1266
1267 return dynamic_cast<PCB_TABLECELL*>( m_selection.GetItem( 0 ) );
1268}
1269
1270
1272{
1273 if( m_selection.GetSize() == 0 )
1274 return false;
1275
1276 PCB_TABLECELL* firstCell = dynamic_cast<PCB_TABLECELL*>( m_selection.GetItem( 0 ) );
1277
1278 if( !firstCell )
1279 return false;
1280
1281 // Suppress highlightNet only when every selected item is a cell of the same table;
1282 // a mixed selection must fall through to the usual Ctrl+click action.
1283 PCB_TABLE* selectedTable = static_cast<PCB_TABLE*>( firstCell->GetParent() );
1284
1285 for( EDA_ITEM* item : m_selection )
1286 {
1287 PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( item );
1288
1289 if( !cell || cell->GetParent() != selectedTable )
1290 return false;
1291 }
1292
1293 GENERAL_COLLECTOR clickCells;
1294 collectTableCellsAt( aPosition, clickCells );
1295
1296 if( clickCells.GetCount() != 1 )
1297 return false;
1298
1299 PCB_TABLECELL* clickedCell = static_cast<PCB_TABLECELL*>( clickCells[0] );
1300
1301 if( clickedCell->GetParent() != selectedTable )
1302 return false;
1303
1304 if( clickedCell->IsSelected() )
1305 {
1306 unselect( clickedCell );
1307 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1308 }
1309 else
1310 {
1311 select( clickedCell );
1312 m_toolMgr->ProcessEvent( EVENTS::PointSelectedEvent );
1313 }
1314
1315 // A toggle breaks the contiguous-rectangle invariant the anchor represents.
1316 m_previousFirstCell = nullptr;
1317
1318 return true;
1319}
1320
1321
1323{
1324 bool cancelled = false; // Was the tool canceled while it was running?
1325 m_multiple = true; // Multiple selection mode is active
1326
1327 // Shift+click can jitter into IsDrag, collapsing DragOrigin..Position to the press
1328 // point; honour the cached anchor instead. Snapshot its coordinate so the drag loop
1329 // never dereferences a cell that an external mutation could free underneath us.
1330 bool haveAnchorStart = false;
1331 VECTOR2D anchorStart;
1332
1334 && m_previousFirstCell->GetParent() == aTable )
1335 {
1336 anchorStart = VECTOR2D( m_previousFirstCell->GetCenter() );
1337 haveAnchorStart = true;
1338 }
1339 else if( m_previousFirstCell && !m_selection.Contains( m_previousFirstCell ) )
1340 {
1341 m_previousFirstCell = nullptr;
1342 }
1343
1345
1346 auto wasSelected =
1347 []( EDA_ITEM* aItem )
1348 {
1349 return ( aItem->GetFlags() & CANDIDATE ) > 0;
1350 };
1351
1352 while( TOOL_EVENT* evt = Wait() )
1353 {
1354 if( evt->IsCancelInteractive() || evt->IsActivate() )
1355 {
1356 cancelled = true;
1357 break;
1358 }
1359 else if( evt->IsDrag( BUT_LEFT ) )
1360 {
1361 getViewControls()->SetAutoPan( true );
1362
1363 VECTOR2D start = haveAnchorStart ? anchorStart : VECTOR2D( evt->DragOrigin() );
1364 VECTOR2D end = VECTOR2D( evt->Position() );
1365
1366 selectCellsBetween( start, end - start, aTable );
1367 }
1368 else if( evt->IsMouseUp( BUT_LEFT ) )
1369 {
1370 m_selection.SetIsHover( false );
1371
1372 bool anyAdded = false;
1373 bool anySubtracted = false;
1374
1375 for( PCB_TABLECELL* cell : aTable->GetCells() )
1376 {
1377 if( cell->IsSelected() && !wasSelected( cell ) )
1378 anyAdded = true;
1379 else if( wasSelected( cell ) && !cell->IsSelected() )
1380 anySubtracted = true;
1381 }
1382
1383 // Inform other potentially interested tools
1384 if( anyAdded )
1385 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1386
1387 if( anySubtracted )
1388 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1389
1390 break; // Stop waiting for events
1391 }
1392 else
1393 {
1394 // Allow some actions for navigation
1395 passEvent( evt, allowedActions );
1396 }
1397 }
1398
1399 getViewControls()->SetAutoPan( false );
1400
1401 m_multiple = false; // Multiple selection mode is inactive
1402
1403 if( !cancelled )
1404 m_selection.ClearReferencePoint();
1405
1406 return cancelled;
1407}
1408
1409
1411{
1412 bool cancelled = false; // Was the tool canceled while it was running?
1413 m_multiple = true; // Multiple selection mode is active
1415
1417 view->Add( &area );
1418
1419 while( TOOL_EVENT* evt = Wait() )
1420 {
1421 /* Selection mode depends on direction of drag-selection:
1422 * Left > Right : Select objects that are fully enclosed by selection
1423 * Right > Left : Select objects that are crossed by selection
1424 */
1425 bool greedySelection = area.GetEnd().x < area.GetOrigin().x;
1426
1427 if( view->IsMirroredX() )
1428 greedySelection = !greedySelection;
1429
1430 m_frame->GetCanvas()->SetCurrentCursor( !greedySelection ? KICURSOR::SELECT_WINDOW
1432
1433 if( evt->IsCancelInteractive() || evt->IsActivate() )
1434 {
1435 cancelled = true;
1436 break;
1437 }
1438
1439 if( evt->IsDrag( BUT_LEFT ) )
1440 {
1442 {
1443 if( m_selection.GetSize() > 0 )
1444 {
1445 ClearSelection( true /*quiet mode*/ );
1446 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1447 }
1448 }
1449
1450 // Start drawing a selection box
1451 area.SetOrigin( evt->DragOrigin() );
1452 area.SetEnd( evt->Position() );
1455 area.SetExclusiveOr( false );
1457
1458 view->SetVisible( &area, true );
1459 view->Update( &area );
1460 getViewControls()->SetAutoPan( true );
1461 }
1462
1463 if( evt->IsMouseUp( BUT_LEFT ) )
1464 {
1465 getViewControls()->SetAutoPan( false );
1466
1467 // End drawing the selection box
1468 view->SetVisible( &area, false );
1469
1471
1472 break; // Stop waiting for events
1473 }
1474
1475 // Allow some actions for navigation
1476 passEvent( evt, allowedActions );
1477 }
1478
1479 getViewControls()->SetAutoPan( false );
1480
1481 // Stop drawing the selection box
1482 view->Remove( &area );
1483 m_multiple = false; // Multiple selection mode is inactive
1484
1485 if( !cancelled )
1486 m_selection.ClearReferencePoint();
1487
1489
1490 return cancelled;
1491}
1492
1493
1495{
1497 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
1498 m_toolMgr->PostAction( ACTIONS::selectionTool );
1499 return 0; // No need to wait for an event, just set the mode
1500}
1501
1502
1504{
1506 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1507 m_toolMgr->PostAction( ACTIONS::selectionTool );
1508 return 0; // No need to wait for an event, just set the mode
1509}
1510
1511
1513{
1514 bool cancelled = false; // Was the tool canceled while it was running?
1515
1517 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
1518
1519
1520 SHAPE_LINE_CHAIN points;
1521 points.SetClosed( true );
1522
1524 getView()->Add( &area );
1525 getView()->SetVisible( &area, true );
1526 getViewControls()->SetAutoPan( true );
1527
1528 while( TOOL_EVENT* evt = Wait() )
1529 {
1530 // Auto mode: clockwise = inside, counterclockwise = touching
1531 double shapeArea = area.GetPoly().Area( false );
1532 bool isClockwise = shapeArea > 0 ? true : false;
1533
1534 if( getView()->IsMirroredX() && shapeArea != 0 )
1535 isClockwise = !isClockwise;
1536
1537 selectionMode = isClockwise ? SELECTION_MODE::INSIDE_LASSO : SELECTION_MODE::TOUCHING_LASSO;
1538
1539 if( evt->IsCancelInteractive() || evt->IsActivate() )
1540 {
1541 cancelled = true;
1542 evt->SetPassEvent( false );
1543 break;
1544 }
1545 else if( evt->IsDrag( BUT_LEFT )
1546 || evt->IsClick( BUT_LEFT )
1547 || evt->IsAction( &ACTIONS::cursorClick ) )
1548 {
1549 points.Append( evt->Position() );
1550 }
1551 else if( evt->IsDblClick( BUT_LEFT )
1552 || evt->IsAction( &ACTIONS::cursorDblClick )
1553 || evt->IsAction( &ACTIONS::finishInteractive ) )
1554 {
1555 area.GetPoly().GenerateBBoxCache();
1557 evt->SetPassEvent( false );
1558 break;
1559 }
1560 else if( evt->IsAction( &ACTIONS::deleteLastPoint )
1561 || evt->IsAction( &ACTIONS::doDelete )
1562 || evt->IsAction( &ACTIONS::undo ) )
1563 {
1564 if( points.GetPointCount() > 0 )
1565 {
1567 points.Remove( points.GetPointCount() - 1 );
1568 }
1569 }
1570 else
1571 {
1572 // Allow navigation actions
1573 passEvent( evt, allowedActions );
1574 }
1575
1576 if( points.PointCount() > 0 )
1577 {
1579 {
1580 if( m_selection.GetSize() > 0 )
1581 {
1582 ClearSelection( true /*quiet mode*/ );
1583 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1584 }
1585 }
1586 }
1587
1588 area.SetPoly( points );
1589 area.GetPoly().Append( m_toolMgr->GetMousePosition() );
1590 area.SetAdditive( m_additive );
1592 area.SetExclusiveOr( false );
1593 area.SetMode( selectionMode );
1594 getView()->Update( &area );
1595 }
1596
1597 getViewControls()->SetAutoPan( false );
1598 getView()->SetVisible( &area, false );
1599 getView()->Remove( &area );
1600 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1601
1602 if( !cancelled )
1604
1606
1607 return cancelled;
1608}
1609
1610
1612 bool aExclusiveOr )
1613{
1615
1616 bool anyAdded = false;
1617 bool anySubtracted = false;
1618
1619 SELECTION_MODE selectionMode = aArea.GetMode();
1620 bool containedMode = ( selectionMode == SELECTION_MODE::INSIDE_RECTANGLE
1621 || selectionMode == SELECTION_MODE::INSIDE_LASSO ) ? true : false;
1622 bool boxMode = ( selectionMode == SELECTION_MODE::INSIDE_RECTANGLE
1623 || selectionMode == SELECTION_MODE::TOUCHING_RECTANGLE ) ? true : false;
1624
1625 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> candidates;
1626 BOX2I selectionBox = aArea.ViewBBox();
1627 view->Query( selectionBox, candidates ); // Get the list of nearby items
1628
1629 GENERAL_COLLECTOR collector;
1630 GENERAL_COLLECTOR padsCollector;
1631 std::set<EDA_ITEM*> group_items;
1632
1633 for( PCB_GROUP* group : board()->Groups() )
1634 {
1635 // The currently entered group does not get limited
1636 if( m_enteredGroup == group )
1637 continue;
1638
1639 std::unordered_set<EDA_ITEM*>& newset = group->GetItems();
1640
1641 auto boxContained =
1642 [&]( const BOX2I& aBox )
1643 {
1644 return boxMode ? selectionBox.Contains( aBox )
1645 : KIGEOM::BoxHitTest( aArea.GetPoly(), aBox, true );
1646 };
1647
1648 // If we are not greedy and have selected the whole group, add just one item
1649 // to allow it to be promoted to the group later
1650 if( containedMode && boxContained( group->GetBoundingBox() ) && newset.size() )
1651 {
1652 for( EDA_ITEM* group_item : newset )
1653 {
1654 if( !group_item->IsBOARD_ITEM() )
1655 continue;
1656
1657 if( Selectable( static_cast<BOARD_ITEM*>( group_item ) ) )
1658 collector.Append( *newset.begin() );
1659 }
1660 }
1661
1662 for( EDA_ITEM* group_item : newset )
1663 group_items.emplace( group_item );
1664 }
1665
1666 auto hitTest =
1667 [&]( const EDA_ITEM* aItem )
1668 {
1669 return boxMode ? aItem->HitTest( selectionBox, containedMode )
1670 : aItem->HitTest( aArea.GetPoly(), containedMode );
1671 };
1672
1673 for( const auto& [item, layer] : candidates )
1674 {
1675 if( !item->IsBOARD_ITEM() )
1676 continue;
1677
1678 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
1679
1680 if( Selectable( boardItem ) && hitTest( boardItem )
1681 && ( !containedMode || !group_items.count( boardItem ) ) )
1682 {
1683 if( boardItem->Type() == PCB_PAD_T && !m_isFootprintEditor )
1684 padsCollector.Append( boardItem );
1685 else
1686 collector.Append( boardItem );
1687 }
1688 }
1689
1690 // Apply the stateful filter
1691 FilterCollectedItems( collector, true, nullptr );
1692
1693 FilterCollectorForHierarchy( collector, true );
1694
1695 // If we selected nothing but pads, allow them to be selected
1696 if( collector.GetCount() == 0 )
1697 {
1698 collector = padsCollector;
1699 FilterCollectedItems( collector, true, nullptr );
1700 FilterCollectorForHierarchy( collector, true );
1701 }
1702
1703 // Sort the filtered selection by rows and columns to have a nice default
1704 // for tools that can use it.
1705 std::sort( collector.begin(), collector.end(),
1706 []( EDA_ITEM* a, EDA_ITEM* b )
1707 {
1708 VECTOR2I aPos = a->GetPosition();
1709 VECTOR2I bPos = b->GetPosition();
1710
1711 if( aPos.y == bPos.y )
1712 return aPos.x < bPos.x;
1713
1714 return aPos.y < bPos.y;
1715 } );
1716
1717 for( EDA_ITEM* i : collector )
1718 {
1719 if( !i->IsBOARD_ITEM() )
1720 continue;
1721
1722 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
1723
1724 if( aSubtractive || ( aExclusiveOr && item->IsSelected() ) )
1725 {
1726 unselect( item );
1727 anySubtracted = true;
1728 }
1729 else
1730 {
1731 select( item );
1732 anyAdded = true;
1733 }
1734 }
1735
1736 m_selection.SetIsHover( false );
1737
1738 // Inform other potentially interested tools
1739 if( anyAdded )
1740 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1741 else if( anySubtracted )
1742 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1743}
1744
1745
1747{
1748 wxMouseState keyboardState = wxGetMouseState();
1749
1750 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(), keyboardState.AltDown() );
1751
1752 m_skip_heuristics = true;
1754 m_skip_heuristics = false;
1755
1756 return 0;
1757}
1758
1759
1760
1762{
1764
1765 selectCursor( false, aClientFilter );
1766
1767 return 0;
1768}
1769
1770
1772{
1774
1775 return 0;
1776}
1777
1778
1780{
1781 GENERAL_COLLECTOR collection;
1782 BOX2I selectionBox;
1783
1784 selectionBox.SetMaximum();
1785
1786 getView()->Query( selectionBox,
1787 [&]( KIGFX::VIEW_ITEM* viewItem ) -> bool
1788 {
1789 if( viewItem->IsBOARD_ITEM() )
1790 {
1791 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( viewItem );
1792
1793 if( item && Selectable( item ) && itemPassesFilter( item, true, nullptr ) )
1794 collection.Append( item );
1795 }
1796
1797 return true;
1798 } );
1799
1800 FilterCollectorForHierarchy( collection, true );
1801
1802 for( EDA_ITEM* item : collection )
1803 select( item );
1804
1805 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1806
1807 m_frame->GetCanvas()->ForceRefresh();
1808
1809 return 0;
1810}
1811
1812
1814{
1815 BOX2I selectionBox;
1816
1817 selectionBox.SetMaximum();
1818
1819 getView()->Query( selectionBox,
1820 [&]( KIGFX::VIEW_ITEM* viewItem ) -> bool
1821 {
1822 if( viewItem->IsBOARD_ITEM() )
1823 {
1824 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( viewItem );
1825
1826 if( item && Selectable( item ) )
1827 unselect( item );
1828 }
1829
1830 return true;
1831 } );
1832
1833 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1834
1835 m_frame->GetCanvas()->ForceRefresh();
1836
1837 return 0;
1838}
1839
1840
1842{
1843 // Narrow the collection down to a single BOARD_CONNECTED_ITEM for each represented net.
1844 // All other items types are removed.
1845 std::set<int> representedNets;
1846
1847 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
1848 {
1849 BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( aCollector[i] );
1850
1851 if( !item )
1852 aCollector.Remove( i );
1853 else if ( representedNets.count( item->GetNetCode() ) )
1854 aCollector.Remove( i );
1855 else
1856 representedNets.insert( item->GetNetCode() );
1857 }
1858}
1859
1860
1862{
1863 std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
1864
1865 // Get all footprints and pads
1866 std::vector<BOARD_CONNECTED_ITEM*> toUnroute;
1867 std::set<EDA_ITEM*> toDelete;
1868
1869 for( EDA_ITEM* item : selectedItems )
1870 {
1871 if( item->Type() == PCB_FOOTPRINT_T )
1872 {
1873 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
1874 toUnroute.push_back( pad );
1875 }
1876 else if( item->Type() == PCB_GENERATOR_T )
1877 {
1878 toDelete.insert( item );
1879
1880 for( BOARD_ITEM* generatedItem : static_cast<PCB_GENERATOR*>( item )->GetBoardItems() )
1881 {
1882 if( BOARD_CONNECTED_ITEM::ClassOf( generatedItem ) )
1883 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( generatedItem ) );
1884 }
1885 }
1886 else if( BOARD_CONNECTED_ITEM::ClassOf( item ) )
1887 {
1888 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
1889 }
1890 }
1891
1892 // Find generators connected to tracks and add their children to toUnroute
1893 // so selectAllConnectedTracks can traverse through meanders
1894 std::set<int> selectedNets;
1895
1896 for( BOARD_CONNECTED_ITEM* item : toUnroute )
1897 if( item->GetNetCode() > 0 )
1898 selectedNets.insert( item->GetNetCode() );
1899
1900 std::set<VECTOR2I> endpointSet;
1901
1902 for( BOARD_CONNECTED_ITEM* item : toUnroute )
1903 {
1904 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
1905 {
1906 endpointSet.insert( track->GetStart() );
1907 endpointSet.insert( track->GetEnd() );
1908 }
1909 else if( item->Type() == PCB_VIA_T )
1910 {
1911 endpointSet.insert( item->GetPosition() );
1912 }
1913 else if( item->Type() == PCB_PAD_T )
1914 {
1915 endpointSet.insert( item->GetPosition() );
1916 }
1917 }
1918
1919 bool expanded = true;
1920
1921 while( expanded )
1922 {
1923 expanded = false;
1924
1925 for( PCB_GENERATOR* gen : board()->Generators() )
1926 {
1927 if( toDelete.count( gen ) )
1928 continue;
1929
1930 // Find this generator's external endpoints (meander entry/exit)
1931 std::map<VECTOR2I, int> epCount;
1932
1933 for( BOARD_ITEM* child : gen->GetBoardItems() )
1934 {
1935 PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( child );
1936
1937 if( track && selectedNets.count( track->GetNetCode() ) )
1938 {
1939 epCount[track->GetStart()]++;
1940 epCount[track->GetEnd()]++;
1941 }
1942 }
1943
1944 // Check if any external endpoint matches our track endpoints
1945 bool connected = false;
1946
1947 for( auto& [pt, count] : epCount )
1948 {
1949 if( count == 1 && endpointSet.count( pt ) )
1950 {
1951 connected = true;
1952 break;
1953 }
1954 }
1955
1956 if( connected )
1957 {
1958 toDelete.insert( gen );
1959
1960 for( BOARD_ITEM* c : gen->GetBoardItems() )
1961 {
1963 continue;
1964
1965 if( !selectedNets.count( static_cast<BOARD_CONNECTED_ITEM*>( c )->GetNetCode() ) )
1966 continue;
1967
1968 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( c ) );
1969
1970 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( c ) )
1971 {
1972 endpointSet.insert( track->GetStart() );
1973 endpointSet.insert( track->GetEnd() );
1974 }
1975
1976 expanded = true;
1977 }
1978 }
1979 }
1980 }
1981
1982 // Because selectAllConnectedTracks() use m_filter to collect connected tracks
1983 // to pads, enable filter for these items, regardless the curent filter options
1984 // via filter is not changed, because it can be useful to keep via filter disabled
1985 struct PCB_SELECTION_FILTER_OPTIONS save_filter = m_filter;
1986 m_filter.tracks = true;
1987 m_filter.pads = true;
1988
1989 // Clear selection so we don't delete our footprints/pads
1990 ClearSelection( true );
1991
1992 // Get the tracks on our list of pads, then delete them
1994
1995 BOARD_COMMIT commit( m_toolMgr );
1996 std::set<BOARD_ITEM*> removed;
1997
1998 for( EDA_ITEM* item : m_selection )
1999 {
2000 if( !item->IsBOARD_ITEM() )
2001 continue;
2002
2003 BOARD_ITEM* bi = static_cast<BOARD_ITEM*>( item );
2004
2005 if( bi->Type() == PCB_GENERATOR_T )
2006 toDelete.insert( bi );
2007
2008 commit.Remove( bi );
2009 removed.insert( bi );
2010 }
2011
2012 // Find generators whose children were removed
2013 for( PCB_GENERATOR* gen : board()->Generators() )
2014 {
2015 for( BOARD_ITEM* child : gen->GetBoardItems() )
2016 {
2017 if( removed.count( child ) )
2018 {
2019 toDelete.insert( gen );
2020 break;
2021 }
2022 }
2023 }
2024
2025 for( EDA_ITEM* item : toDelete )
2026 {
2027 if( !item->IsBOARD_ITEM() )
2028 continue;
2029
2030 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
2031
2032 boardItem->RunOnChildren(
2033 [&commit, &removed]( BOARD_ITEM* aItem )
2034 {
2035 if( removed.find( aItem ) == removed.end() )
2036 {
2037 commit.Remove( aItem );
2038 removed.insert( aItem );
2039 }
2040 },
2042
2043 if( removed.find( boardItem ) == removed.end() )
2044 commit.Remove( boardItem );
2045 }
2046
2047 ClearSelection( true );
2048 commit.Push( _( "Unroute Selected" ) );
2049
2050 m_filter = save_filter; // restore current filter options
2051
2052 // Reselect our footprint/pads as they were in our original selection
2053 for( EDA_ITEM* item : selectedItems )
2054 {
2055 if( item->Type() == PCB_FOOTPRINT_T || item->Type() == PCB_PAD_T )
2056 select( item );
2057 }
2058
2059 return 0;
2060}
2061
2062
2064{
2065 std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
2066
2067 // Get all footprints and pads
2068 std::vector<BOARD_CONNECTED_ITEM*> toUnroute;
2069
2070 std::set<EDA_ITEM*> toDelete;
2071
2072 for( EDA_ITEM* item : selectedItems )
2073 {
2074 if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T || item->Type() == PCB_VIA_T )
2075 {
2076 BOARD_ITEM* bi = static_cast<BOARD_ITEM*>( item );
2077 EDA_GROUP* parentGroup = bi->GetParentGroup();
2078
2079 if( parentGroup && parentGroup->AsEdaItem()->Type() == PCB_GENERATOR_T )
2080 {
2081 PCB_GENERATOR* gen = static_cast<PCB_GENERATOR*>( parentGroup->AsEdaItem() );
2082
2083 if( !toDelete.count( gen ) )
2084 {
2085 toDelete.insert( gen );
2086
2087 for( BOARD_ITEM* generatedItem : gen->GetBoardItems() )
2088 {
2089 toDelete.insert( generatedItem );
2090
2091 if( BOARD_CONNECTED_ITEM::ClassOf( generatedItem ) )
2092 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( generatedItem ) );
2093 }
2094 }
2095 }
2096 else
2097 {
2098 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
2099 toDelete.insert( item );
2100 }
2101 }
2102 else if( item->Type() == PCB_GENERATOR_T )
2103 {
2104 toDelete.insert( item );
2105
2106 for( BOARD_ITEM* generatedItem : static_cast<PCB_GENERATOR*>( item )->GetBoardItems() )
2107 {
2108 toDelete.insert( generatedItem );
2109
2110 if( BOARD_CONNECTED_ITEM::ClassOf( generatedItem ) )
2111 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( generatedItem ) );
2112 }
2113 }
2114 }
2115
2116 // Get the tracks connecting to our starting objects
2119 std::deque<EDA_ITEM*> toSelectAfter;
2120 // This will select the unroute items too, so filter them out
2121 for( EDA_ITEM* item : m_selection.GetItemsSortedByTypeAndXY() )
2122 {
2123 if( !item->IsBOARD_ITEM() )
2124 continue;
2125
2126 if( toDelete.find( item ) == toDelete.end() )
2127 toSelectAfter.push_back( item );
2128 }
2129
2130 BOARD_COMMIT commit( m_toolMgr );
2131 std::set<BOARD_ITEM*> removed;
2132
2133 for( EDA_ITEM* item : toDelete )
2134 {
2135 if( !item->IsBOARD_ITEM() )
2136 continue;
2137
2138 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
2139
2140 if( item->Type() == PCB_GENERATOR_T )
2141 {
2142 boardItem->RunOnChildren(
2143 [&commit, &removed]( BOARD_ITEM* aItem )
2144 {
2145 if( removed.insert( aItem ).second )
2146 commit.Remove( aItem );
2147 },
2149
2150 if( removed.insert( boardItem ).second )
2151 commit.Remove( boardItem );
2152 }
2153 else
2154 {
2155 if( removed.insert( boardItem ).second )
2156 commit.Remove( boardItem );
2157 }
2158 }
2159
2160 commit.Push( _( "Unroute Segment" ) );
2161
2162 // Now our after tracks so the user can continue backing up as desired
2163 ClearSelection( true );
2164
2165 for( EDA_ITEM* item : toSelectAfter )
2166 {
2167 if( !toDelete.count( item ) )
2168 select( item );
2169 }
2170
2171 return 0;
2172}
2173
2174
2176{
2177 // expandConnection will get called no matter whether the user selected a connected item or a
2178 // non-connected shape (graphic on a non-copper layer). The algorithm for expanding to connected
2179 // items is different from graphics, so they need to be handled separately.
2180 unsigned initialCount = 0;
2181
2182 for( const EDA_ITEM* item : m_selection.GetItems() )
2183 {
2184 if( item->Type() == PCB_FOOTPRINT_T
2185 || item->Type() == PCB_GENERATOR_T
2186 || ( static_cast<const BOARD_ITEM*>( item )->IsConnected() ) )
2187 {
2188 initialCount++;
2189 }
2190 }
2191
2192 if( initialCount == 0 )
2193 {
2194 // First, process any graphic shapes we have
2195 std::vector<PCB_SHAPE*> startShapes;
2196
2197 for( EDA_ITEM* item : m_selection.GetItems() )
2198 {
2199 if( isExpandableGraphicShape( item ) )
2200 startShapes.push_back( static_cast<PCB_SHAPE*>( item ) );
2201 }
2202
2203 // If no non-copper shapes; fall back to looking for connected items
2204 if( !startShapes.empty() )
2205 selectAllConnectedShapes( startShapes );
2206 else
2208 }
2209
2210 m_frame->SetStatusText( _( "Select/Expand Connection..." ) );
2211
2212 for( STOP_CONDITION stopCondition : { STOP_AT_JUNCTION, STOP_AT_PAD, STOP_NEVER } )
2213 {
2214 std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
2215
2216 for( EDA_ITEM* item : selectedItems )
2217 item->ClearTempFlags();
2218
2219 std::vector<BOARD_CONNECTED_ITEM*> startItems;
2220
2221 for( EDA_ITEM* item : selectedItems )
2222 {
2223 if( item->Type() == PCB_FOOTPRINT_T )
2224 {
2225 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
2226
2227 for( PAD* pad : footprint->Pads() )
2228 startItems.push_back( pad );
2229 }
2230 else if( item->Type() == PCB_GENERATOR_T )
2231 {
2232 for( BOARD_ITEM* generatedItem : static_cast<PCB_GENERATOR*>( item )->GetBoardItems() )
2233 {
2234 if( BOARD_CONNECTED_ITEM::ClassOf( generatedItem ) )
2235 startItems.push_back( static_cast<BOARD_CONNECTED_ITEM*>( generatedItem ) );
2236 }
2237 }
2238 else if( BOARD_CONNECTED_ITEM::ClassOf( item ) )
2239 {
2240 startItems.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
2241 }
2242 }
2243
2244 selectAllConnectedTracks( startItems, stopCondition );
2245
2246 if( m_selection.GetItems().size() > initialCount )
2247 break;
2248 }
2249
2250 m_frame->SetStatusText( wxEmptyString );
2251
2252 // Inform other potentially interested tools
2253 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2254
2255 return 0;
2256}
2257
2258
2259void PCB_SELECTION_TOOL::selectAllConnectedTracks( const std::vector<BOARD_CONNECTED_ITEM*>& aStartItems,
2260 STOP_CONDITION aStopCondition )
2261{
2262 PROF_TIMER refreshTimer;
2263 double refreshIntervalMs = 500; // Refresh display with this interval to indicate progress
2264 int lastSelectionSize = (int) m_selection.GetSize();
2265
2266 auto connectivity = board()->GetConnectivity();
2267
2268 // Don't let expansion select outside an entered group, or select() would ExitGroup mid-walk.
2269 auto inScope = [this]( BOARD_ITEM* aItem )
2270 {
2272 };
2273
2274 std::set<PAD*> startPadSet;
2275 std::vector<BOARD_CONNECTED_ITEM*> cleanupItems;
2276
2277 for( BOARD_CONNECTED_ITEM* startItem : aStartItems )
2278 {
2279 // Register starting pads
2280 if( startItem->Type() == PCB_PAD_T )
2281 startPadSet.insert( static_cast<PAD*>( startItem ) );
2282
2283 // Select any starting track items
2284 if( startItem->IsType( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } ) )
2285 {
2286 if( itemPassesFilter( startItem, true ) && inScope( startItem ) )
2287 select( startItem );
2288 }
2289 }
2290
2291 for( BOARD_CONNECTED_ITEM* startItem : aStartItems )
2292 {
2293 std::map<VECTOR2I, std::vector<PCB_TRACK*>> trackMap;
2294 std::map<VECTOR2I, PCB_VIA*> viaMap;
2295 std::map<VECTOR2I, PAD*> padMap;
2296 std::map<VECTOR2I, std::vector<PCB_SHAPE*>> shapeMap;
2297 std::vector<std::pair<VECTOR2I, LSET>> activePts;
2298
2299 if( startItem->HasFlag( SKIP_STRUCT ) ) // Skip already visited items
2300 continue;
2301
2302 auto connectedItems = connectivity->GetConnectedItems( startItem, EXCLUDE_ZONES | IGNORE_NETS );
2303
2304 // Build maps of connected items
2305 for( BOARD_CONNECTED_ITEM* item : connectedItems )
2306 {
2307 switch( item->Type() )
2308 {
2309 case PCB_ARC_T:
2310 case PCB_TRACE_T:
2311 {
2312 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
2313 trackMap[track->GetStart()].push_back( track );
2314 trackMap[track->GetEnd()].push_back( track );
2315 break;
2316 }
2317
2318 case PCB_VIA_T:
2319 {
2320 PCB_VIA* via = static_cast<PCB_VIA*>( item );
2321 viaMap[via->GetStart()] = via;
2322 break;
2323 }
2324
2325 case PCB_PAD_T:
2326 {
2327 PAD* pad = static_cast<PAD*>( item );
2328 padMap[pad->GetPosition()] = pad;
2329 break;
2330 }
2331
2332 case PCB_SHAPE_T:
2333 {
2334 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2335
2336 for( const auto& point : shape->GetConnectionPoints() )
2337 shapeMap[point].push_back( shape );
2338
2339 break;
2340 }
2341
2342 default:
2343 break;
2344 }
2345 }
2346
2347 // Set up the initial active points
2348 switch( startItem->Type() )
2349 {
2350 case PCB_ARC_T:
2351 case PCB_TRACE_T:
2352 {
2353 PCB_TRACK* track = static_cast<PCB_TRACK*>( startItem );
2354
2355 activePts.push_back( { track->GetStart(), track->GetLayerSet() } );
2356 activePts.push_back( { track->GetEnd(), track->GetLayerSet() } );
2357 break;
2358 }
2359
2360 case PCB_VIA_T:
2361 activePts.push_back( { startItem->GetPosition(), startItem->GetLayerSet() } );
2362 break;
2363
2364 case PCB_PAD_T:
2365 activePts.push_back( { startItem->GetPosition(), startItem->GetLayerSet() } );
2366 break;
2367
2368 case PCB_SHAPE_T:
2369 {
2370 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( startItem );
2371
2372 for( const auto& point : shape->GetConnectionPoints() )
2373 activePts.push_back( { point, startItem->GetLayerSet() } );
2374
2375 break;
2376 }
2377
2378 default:
2379 break;
2380 }
2381
2382 bool expand = true;
2383 int failSafe = 0;
2384
2385 // Iterative push from all active points
2386 while( expand && failSafe++ < 100000 )
2387 {
2388 expand = false;
2389
2390 for( int i = (int) activePts.size() - 1; i >= 0; --i )
2391 {
2392 VECTOR2I pt = activePts[i].first;
2393 LSET layerSetCu = activePts[i].second & LSET::AllCuMask();
2394
2395 PCB_VIA* hitVia = nullptr;
2396
2397 // exact position match (common case)
2398 auto exactIt = viaMap.find( pt );
2399
2400 if( exactIt != viaMap.end() && ( exactIt->second->GetLayerSet() & layerSetCu ).any() )
2401 {
2402 hitVia = exactIt->second;
2403 }
2404 else
2405 {
2406 // off-center VIA connection
2407 for( auto& [pos, via] : viaMap )
2408 {
2409 if( !( via->GetLayerSet() & layerSetCu ).any() )
2410 continue;
2411
2412 bool hit = false;
2413
2414 for( PCB_LAYER_ID layer : LSET( via->GetLayerSet() & layerSetCu ).CuStack() )
2415 {
2416 int radius = via->GetWidth( layer ) / 2;
2417 int64_t radiusSq = static_cast<int64_t>( radius ) * radius;
2418
2419 if( ( pt - pos ).SquaredEuclideanNorm() <= radiusSq )
2420 {
2421 hit = true;
2422 break;
2423 }
2424 }
2425
2426 if( hit )
2427 {
2428 hitVia = via;
2429 break;
2430 }
2431 }
2432 }
2433
2434 auto padIt = padMap.find( pt );
2435
2436 bool gotVia = hitVia != nullptr;
2437 bool gotPad = padIt != padMap.end() && ( padIt->second->GetLayerSet() & layerSetCu ).any();
2438 bool gotNonStartPad = gotPad && ( startPadSet.find( padIt->second ) == startPadSet.end() );
2439
2440 if( gotPad && !itemPassesFilter( padIt->second, true ) )
2441 {
2442 activePts.erase( activePts.begin() + i );
2443 continue;
2444 }
2445
2446 if( gotVia && !itemPassesFilter( hitVia, true ) )
2447 {
2448 activePts.erase( activePts.begin() + i );
2449 continue;
2450 }
2451
2452 if( aStopCondition == STOP_AT_JUNCTION )
2453 {
2454 size_t pt_count = 0;
2455
2456 for( PCB_TRACK* track : trackMap[pt] )
2457 {
2458 if( track->GetStart() != track->GetEnd() && layerSetCu.Contains( track->GetLayer() ) )
2459 pt_count++;
2460 }
2461
2462 if( pt_count > 2 || gotVia || gotNonStartPad )
2463 {
2464 activePts.erase( activePts.begin() + i );
2465 continue;
2466 }
2467 }
2468 else if( aStopCondition == STOP_AT_PAD )
2469 {
2470 if( gotNonStartPad )
2471 {
2472 activePts.erase( activePts.begin() + i );
2473 continue;
2474 }
2475 }
2476
2477 if( gotPad )
2478 {
2479 PAD* pad = padIt->second;
2480
2481 if( !pad->HasFlag( SKIP_STRUCT ) )
2482 {
2483 pad->SetFlags( SKIP_STRUCT );
2484 cleanupItems.push_back( pad );
2485
2486 activePts.push_back( { pad->GetPosition(), pad->GetLayerSet() } );
2487 expand = true;
2488 }
2489 }
2490
2491 for( PCB_TRACK* track : trackMap[pt] )
2492 {
2493 if( !layerSetCu.Contains( track->GetLayer() ) )
2494 continue;
2495
2496 if( !itemPassesFilter( track, true ) )
2497 continue;
2498
2499 if( !track->IsSelected() && inScope( track ) )
2500 select( track );
2501
2502 if( !track->HasFlag( SKIP_STRUCT ) )
2503 {
2504 track->SetFlags( SKIP_STRUCT );
2505 cleanupItems.push_back( track );
2506
2507 if( track->GetStart() == pt )
2508 activePts.push_back( { track->GetEnd(), track->GetLayerSet() } );
2509 else
2510 activePts.push_back( { track->GetStart(), track->GetLayerSet() } );
2511
2512 if( aStopCondition != STOP_AT_SEGMENT )
2513 expand = true;
2514 }
2515 }
2516
2517 for( PCB_SHAPE* shape : shapeMap[pt] )
2518 {
2519 if( !layerSetCu.Contains( shape->GetLayer() ) )
2520 continue;
2521
2522 if( !itemPassesFilter( shape, true ) )
2523 continue;
2524
2525 if( !shape->IsSelected() && inScope( shape ) )
2526 select( shape );
2527
2528 if( !shape->HasFlag( SKIP_STRUCT ) )
2529 {
2530 shape->SetFlags( SKIP_STRUCT );
2531 cleanupItems.push_back( shape );
2532
2533 for( const VECTOR2I& newPoint : shape->GetConnectionPoints() )
2534 {
2535 if( newPoint == pt )
2536 continue;
2537
2538 activePts.push_back( { newPoint, shape->GetLayerSet() } );
2539 }
2540
2541 if( aStopCondition != STOP_AT_SEGMENT )
2542 expand = true;
2543 }
2544 }
2545
2546 if( hitVia )
2547 {
2548 if( !hitVia->IsSelected() && inScope( hitVia ) )
2549 select( hitVia );
2550
2551 if( !hitVia->HasFlag( SKIP_STRUCT ) )
2552 {
2553 hitVia->SetFlags( SKIP_STRUCT );
2554 cleanupItems.push_back( hitVia );
2555
2556 VECTOR2I viaPos = hitVia->GetPosition();
2557
2558 int maxRadius = 0;
2559
2560 for( PCB_LAYER_ID layer : hitVia->GetLayerSet().CuStack() )
2561 maxRadius = std::max( maxRadius, hitVia->GetWidth( layer ) / 2 );
2562
2563 int64_t maxRadiusSq = static_cast<int64_t>( maxRadius ) * maxRadius;
2564
2565 for( auto& [trkPt, tracks] : trackMap )
2566 {
2567 if( ( trkPt - viaPos ).SquaredEuclideanNorm() > maxRadiusSq )
2568 continue;
2569
2570 // Verify point is inside the VIA pad on at least one track layer
2571 bool inside = false;
2572
2573 for( PCB_TRACK* trk : tracks )
2574 {
2575 PCB_LAYER_ID trkLayer = trk->GetLayer();
2576
2577 if( !hitVia->GetLayerSet().Contains( trkLayer ) )
2578 continue;
2579
2580 int r = hitVia->GetWidth( trkLayer ) / 2;
2581 int64_t rSq = static_cast<int64_t>( r ) * r;
2582
2583 if( ( trkPt - viaPos ).SquaredEuclideanNorm() <= rSq )
2584 {
2585 inside = true;
2586 break;
2587 }
2588 }
2589
2590 if( inside )
2591 activePts.push_back( { trkPt, hitVia->GetLayerSet() } );
2592 }
2593
2594 if( aStopCondition != STOP_AT_SEGMENT )
2595 expand = true;
2596 }
2597 }
2598
2599 activePts.erase( activePts.begin() + i );
2600 }
2601
2602 // Refresh display for the feel of progress
2603 if( refreshTimer.msecs() >= refreshIntervalMs )
2604 {
2605 if( m_selection.Size() != lastSelectionSize )
2606 {
2607 m_frame->GetCanvas()->ForceRefresh();
2608 lastSelectionSize = m_selection.Size();
2609 }
2610
2611 refreshTimer.Start();
2612 }
2613 }
2614 }
2615
2616 std::set<EDA_ITEM*> toDeselect;
2617 std::set<EDA_ITEM*> toSelect;
2618
2619 // Promote generated members to their PCB_GENERATOR parents
2620 for( EDA_ITEM* item : m_selection )
2621 {
2622 if( !item->IsBOARD_ITEM() )
2623 continue;
2624
2625 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
2626 EDA_GROUP* parent = boardItem->GetParentGroup();
2627
2628 if( parent && parent->AsEdaItem()->Type() == PCB_GENERATOR_T )
2629 {
2630 toDeselect.insert( item );
2631
2632 if( !parent->AsEdaItem()->IsSelected() )
2633 toSelect.insert( parent->AsEdaItem() );
2634 }
2635 }
2636
2637 for( EDA_ITEM* item : toDeselect )
2638 unselect( item );
2639
2640 for( EDA_ITEM* item : toSelect )
2641 select( item );
2642
2643 for( BOARD_CONNECTED_ITEM* item : cleanupItems )
2644 item->ClearFlags( SKIP_STRUCT );
2645}
2646
2647
2649{
2650 if( aItem->Type() == PCB_SHAPE_T )
2651 {
2652 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
2653
2654 switch( shape->GetShape() )
2655 {
2656 case SHAPE_T::SEGMENT:
2657 case SHAPE_T::ARC:
2658 case SHAPE_T::BEZIER:
2659 return !shape->IsOnCopperLayer();
2660
2661 case SHAPE_T::POLY:
2662 return !shape->IsOnCopperLayer() && !shape->IsClosed();
2663
2664 default:
2665 return false;
2666 }
2667 }
2668
2669 return false;
2670}
2671
2672
2673void PCB_SELECTION_TOOL::selectAllConnectedShapes( const std::vector<PCB_SHAPE*>& aStartItems )
2674{
2675 std::stack<PCB_SHAPE*> toSearch;
2676 std::set<PCB_SHAPE*> toCleanup;
2677
2678 for( PCB_SHAPE* startItem : aStartItems )
2679 toSearch.push( startItem );
2680
2681 GENERAL_COLLECTOR collector;
2683
2684 auto searchPoint =
2685 [&]( const VECTOR2I& aWhere )
2686 {
2687 collector.Collect( board(), { PCB_SHAPE_T }, aWhere, guide );
2688
2689 for( EDA_ITEM* item : collector )
2690 {
2691 if( isExpandableGraphicShape( item ) )
2692 toSearch.push( static_cast<PCB_SHAPE*>( item ) );
2693 }
2694 };
2695
2696 while( !toSearch.empty() )
2697 {
2698 PCB_SHAPE* shape = toSearch.top();
2699 toSearch.pop();
2700
2701 if( shape->HasFlag( SKIP_STRUCT ) )
2702 continue;
2703
2704 shape->SetFlags( SKIP_STRUCT );
2705 toCleanup.insert( shape );
2706
2707 if( !itemPassesFilter( shape, true ) )
2708 continue;
2709
2710 select( shape );
2711 guide.SetLayerVisibleBits( shape->GetLayerSet() );
2712
2713 searchPoint( shape->GetStart() );
2714 searchPoint( shape->GetEnd() );
2715 }
2716
2717 for( PCB_SHAPE* shape : toCleanup )
2718 shape->ClearFlags( SKIP_STRUCT );
2719}
2720
2721
2723{
2724 // Get all pads
2725 std::vector<PAD*> pads;
2726
2727 for( EDA_ITEM* item : m_selection.GetItems() )
2728 {
2729 if( item->Type() == PCB_FOOTPRINT_T )
2730 {
2731 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
2732 pads.push_back( pad );
2733 }
2734 else if( item->Type() == PCB_PAD_T )
2735 {
2736 pads.push_back( static_cast<PAD*>( item ) );
2737 }
2738 }
2739
2740 // Select every footprint on the end of the ratsnest for each pad in our selection
2741 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
2742
2743 for( PAD* pad : pads )
2744 {
2745 for( const CN_EDGE& edge : conn->GetRatsnestForPad( pad ) )
2746 {
2747 wxCHECK2( edge.GetSourceNode() && !edge.GetSourceNode()->Dirty(), continue );
2748 wxCHECK2( edge.GetTargetNode() && !edge.GetTargetNode()->Dirty(), continue );
2749
2750 BOARD_CONNECTED_ITEM* sourceParent = edge.GetSourceNode()->Parent();
2751 BOARD_CONNECTED_ITEM* targetParent = edge.GetTargetNode()->Parent();
2752
2753 if( sourceParent == pad )
2754 {
2755 if( targetParent->Type() == PCB_PAD_T )
2756 select( static_cast<PAD*>( targetParent )->GetParent() );
2757 }
2758 else if( targetParent == pad )
2759 {
2760 if( sourceParent->Type() == PCB_PAD_T )
2761 select( static_cast<PAD*>( sourceParent )->GetParent() );
2762 }
2763 }
2764 }
2765
2766 return 0;
2767}
2768
2769
2771{
2772 PCB_SELECTION originalSelection = m_selection;
2773
2774 // Get all connected items that can represent the source side of the ratsnest.
2775 std::vector<BOARD_CONNECTED_ITEM*> sourceItems;
2776
2777 for( EDA_ITEM* item : m_selection.GetItems() )
2778 {
2779 if( item->Type() == PCB_FOOTPRINT_T )
2780 {
2781 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
2782 sourceItems.push_back( pad );
2783 }
2784 else if( BOARD_CONNECTED_ITEM* connItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
2785 {
2786 sourceItems.push_back( connItem );
2787 }
2788 }
2789
2791
2792 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
2793 std::shared_ptr<CN_CONNECTIVITY_ALGO> connAlgo = conn->GetConnectivityAlgo();
2794
2795 for( BOARD_CONNECTED_ITEM* sourceItem : sourceItems )
2796 {
2797 RN_NET* net = conn->GetRatsnestForNet( sourceItem->GetNetCode() );
2798
2799 // Need to have something unconnected to grab
2800 if( !net || net->GetEdges().empty() || !connAlgo->ItemExists( sourceItem ) )
2801 continue;
2802
2803 std::vector<std::shared_ptr<CN_CLUSTER>> sourceClusters;
2804
2805 for( CN_ITEM* cnItem : connAlgo->ItemEntry( sourceItem ).GetItems() )
2806 {
2807 for( const std::shared_ptr<CN_ANCHOR>& anchor : cnItem->Anchors() )
2808 {
2809 if( anchor->GetCluster() )
2810 sourceClusters.push_back( anchor->GetCluster() );
2811 }
2812 }
2813
2814 if( sourceClusters.empty() )
2815 continue;
2816
2817 auto isSourceAnchor =
2818 [&]( const std::shared_ptr<const CN_ANCHOR>& aAnchor )
2819 {
2820 if( aAnchor->Parent() == sourceItem )
2821 return true;
2822
2823 return std::find( sourceClusters.begin(), sourceClusters.end(),
2824 aAnchor->GetCluster() ) != sourceClusters.end();
2825 };
2826
2827 double currentDistance = DBL_MAX;
2828 FOOTPRINT* nearest = nullptr;
2829
2830 // Check every ratsnest line for the nearest one
2831 for( const CN_EDGE& edge : net->GetEdges() )
2832 {
2833 const std::shared_ptr<const CN_ANCHOR>& source = edge.GetSourceNode();
2834 const std::shared_ptr<const CN_ANCHOR>& target = edge.GetTargetNode();
2835
2836 wxCHECK2( source && !source->Dirty() && target && !target->Dirty(), continue );
2837
2838 if( source->Parent()->GetParentFootprint() == target->Parent()->GetParentFootprint() )
2839 {
2840 continue; // This edge is a loop on the same footprint
2841 }
2842
2843 bool sourceMatches = isSourceAnchor( source );
2844 bool targetMatches = isSourceAnchor( target );
2845
2846 if( sourceMatches == targetMatches )
2847 continue;
2848
2849 const CN_ANCHOR* other = sourceMatches ? target.get() : source.get();
2850
2851 // We only want to grab footprints, so the ratnest has to point to a pad
2852 if( other->Parent()->Type() != PCB_PAD_T )
2853 continue;
2854
2855 if( edge.GetLength() < currentDistance )
2856 {
2857 currentDistance = edge.GetLength();
2858 nearest = other->Parent()->GetParentFootprint();
2859 }
2860 }
2861
2862 if( nearest != nullptr )
2863 select( nearest );
2864 }
2865
2867
2868 return 0;
2869}
2870
2871
2872void PCB_SELECTION_TOOL::SelectAllItemsOnNet( int aNetCode, bool aSelect )
2873{
2874 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
2875
2876 for( BOARD_ITEM* item : conn->GetNetItems( aNetCode, { PCB_TRACE_T,
2877 PCB_ARC_T,
2878 PCB_VIA_T,
2879 PCB_SHAPE_T } ) )
2880 {
2881 if( itemPassesFilter( item, true, nullptr ) )
2882 aSelect ? select( item ) : unselect( item );
2883 }
2884}
2885
2886
2888{
2889 bool select = aEvent.IsAction( &PCB_ACTIONS::selectNet );
2890
2891 // If we've been passed an argument, just select that netcode1
2892 int netcode = aEvent.Parameter<int>();
2893
2894 if( netcode > 0 )
2895 {
2896 SelectAllItemsOnNet( netcode, select );
2897
2898 // Inform other potentially interested tools
2899 if( m_selection.Size() > 0 )
2900 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2901 else
2902 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2903
2904 return 0;
2905 }
2906
2907 if( !selectCursor() )
2908 return 0;
2909
2910 // copy the selection, since we're going to iterate and modify
2911 auto selection = m_selection.GetItems();
2912
2913 for( EDA_ITEM* i : selection )
2914 {
2915 BOARD_CONNECTED_ITEM* connItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( i );
2916
2917 if( connItem )
2918 SelectAllItemsOnNet( connItem->GetNetCode(), select );
2919 }
2920
2921 // Inform other potentially interested tools
2922 if( m_selection.Size() > 0 )
2923 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2924 else
2925 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2926
2927 return 0;
2928}
2929
2930
2932{
2933 if( !selectCursor() )
2934 return 0;
2935
2936 auto selection = m_selection.GetItems();
2937
2938 for( EDA_ITEM* i : selection )
2939 {
2940 BOARD_CONNECTED_ITEM* connItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( i );
2941
2942 if( !connItem )
2943 continue;
2944
2945 NETINFO_ITEM* netInfo = connItem->GetNet();
2946
2947 if( !netInfo )
2948 continue;
2949
2950 const wxString& chainName = netInfo->GetNetChain();
2951
2952 if( chainName.IsEmpty() )
2953 {
2954 // Net is not part of any chain; fall back to single-net behaviour.
2955 SelectAllItemsOnNet( connItem->GetNetCode(), true );
2956 continue;
2957 }
2958
2959 for( NETINFO_ITEM* candidate : board()->GetNetInfo() )
2960 {
2961 if( candidate && candidate->GetNetChain() == chainName )
2962 SelectAllItemsOnNet( candidate->GetNetCode(), true );
2963 }
2964 }
2965
2966 if( m_selection.Size() > 0 )
2967 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2968 else
2969 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2970
2971 return 0;
2972}
2973
2974
2976{
2977 std::vector<BOARD_ITEM*> footprints;
2978
2979 // store all footprints that are on that sheet path
2980 for( FOOTPRINT* footprint : board()->Footprints() )
2981 {
2982 if( footprint == nullptr )
2983 continue;
2984
2985 wxString footprint_path = footprint->GetPath().AsString().BeforeLast( '/' );
2986
2987 if( footprint_path.IsEmpty() )
2988 footprint_path += '/';
2989
2990 if( footprint_path == aSheetPath )
2991 footprints.push_back( footprint );
2992 }
2993
2994 for( BOARD_ITEM* i : footprints )
2995 {
2996 if( i != nullptr )
2997 select( i );
2998 }
2999
3000 selectConnections( footprints );
3001}
3002
3003
3004void PCB_SELECTION_TOOL::selectConnections( const std::vector<BOARD_ITEM*>& aItems )
3005{
3006 // Generate a list of all pads, and of all nets they belong to.
3007 std::list<int> netcodeList;
3008 std::vector<BOARD_CONNECTED_ITEM*> padList;
3009
3010 for( BOARD_ITEM* item : aItems )
3011 {
3012 switch( item->Type() )
3013 {
3014 case PCB_FOOTPRINT_T:
3015 {
3016 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
3017 {
3018 if( pad->IsConnected() )
3019 {
3020 netcodeList.push_back( pad->GetNetCode() );
3021 padList.push_back( pad );
3022 }
3023 }
3024
3025 break;
3026 }
3027
3028 case PCB_PAD_T:
3029 {
3030 PAD* pad = static_cast<PAD*>( item );
3031
3032 if( pad->IsConnected() )
3033 {
3034 netcodeList.push_back( pad->GetNetCode() );
3035 padList.push_back( pad );
3036 }
3037
3038 break;
3039 }
3040
3041 default:
3042 break;
3043 }
3044 }
3045
3046 // Sort for binary search
3047 std::sort( padList.begin(), padList.end() );
3048
3049 // remove all duplicates
3050 netcodeList.sort();
3051 netcodeList.unique();
3052
3054
3055 // now we need to find all footprints that are connected to each of these nets then we need
3056 // to determine if these footprints are in the list of footprints
3057 std::vector<int> removeCodeList;
3058 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
3059
3060 for( int netCode : netcodeList )
3061 {
3062 for( BOARD_CONNECTED_ITEM* pad : conn->GetNetItems( netCode, { PCB_PAD_T } ) )
3063 {
3064 if( !std::binary_search( padList.begin(), padList.end(), pad ) )
3065 {
3066 // if we cannot find the pad in the padList then we can assume that that pad
3067 // should not be used, therefore invalidate this netcode.
3068 removeCodeList.push_back( netCode );
3069 break;
3070 }
3071 }
3072 }
3073
3074 for( int removeCode : removeCodeList )
3075 netcodeList.remove( removeCode );
3076
3077 std::unordered_set<BOARD_ITEM*> localConnectionList;
3078
3079 for( int netCode : netcodeList )
3080 {
3081 for( BOARD_ITEM* item : conn->GetNetItems( netCode, { PCB_TRACE_T,
3082 PCB_ARC_T,
3083 PCB_VIA_T,
3084 PCB_SHAPE_T } ) )
3085 {
3086 localConnectionList.insert( item );
3087 }
3088 }
3089
3090 for( BOARD_ITEM* item : localConnectionList )
3091 select( item );
3092}
3093
3094
3096{
3097 std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
3098
3099 if( items )
3100 doSyncSelection( *items, false );
3101
3102 return 0;
3103}
3104
3105
3107{
3108 std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
3109
3110 if( items )
3111 doSyncSelection( *items, true );
3112
3113 return 0;
3114}
3115
3116
3117void PCB_SELECTION_TOOL::doSyncSelection( const std::vector<BOARD_ITEM*>& aItems, bool aWithNets )
3118{
3119 if( m_selection.Front() && m_selection.Front()->IsMoving() )
3120 return;
3121
3122 // Also check the incoming items. If the cross-probe flash timer cleared the selection
3123 // during a move, Front() would be null but the items are still being actively moved.
3124 for( const BOARD_ITEM* item : aItems )
3125 {
3126 if( item->IsMoving() )
3127 return;
3128 }
3129
3130 ClearSelection( true /*quiet mode*/ );
3131
3132 // Perform individual selection of each item before processing the event.
3133 for( BOARD_ITEM* item : aItems )
3134 select( item );
3135
3136 if( aWithNets )
3137 selectConnections( aItems );
3138
3139 BOX2I bbox = m_selection.GetBoundingBox();
3140
3141 if( bbox.GetWidth() != 0 && bbox.GetHeight() != 0 )
3142 {
3143 if( m_frame->GetPcbNewSettings()->m_CrossProbing.center_on_items )
3144 {
3145 if( m_frame->GetPcbNewSettings()->m_CrossProbing.zoom_to_fit )
3146 ZoomFitCrossProbeBBox( bbox );
3147
3148 m_frame->FocusOnLocation( bbox.Centre() );
3149 }
3150 }
3151
3153
3154 m_frame->GetCanvas()->ForceRefresh();
3155
3156 if( m_selection.Size() > 0 )
3157 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3158}
3159
3160
3162{
3163 ClearSelection( true /*quiet mode*/ );
3164 wxString sheetPath = *aEvent.Parameter<wxString*>();
3165
3166 selectAllItemsOnSheet( sheetPath );
3167
3169
3170 if( m_selection.Size() > 0 )
3171 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3172
3173 return 0;
3174}
3175
3176
3178{
3179 // this function currently only supports footprints since they are only on one sheet.
3180 EDA_ITEM* item = m_selection.Front();
3181
3182 if( !item )
3183 return 0;
3184
3185 if( item->Type() != PCB_FOOTPRINT_T )
3186 return 0;
3187
3188 FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( item );
3189
3190 if( !footprint || footprint->GetPath().empty() )
3191 return 0;
3192
3193 ClearSelection( true /*quiet mode*/ );
3194
3195 // get the sheet path only.
3196 wxString sheetPath = footprint->GetPath().AsString().BeforeLast( '/' );
3197
3198 if( sheetPath.IsEmpty() )
3199 sheetPath += '/';
3200
3201 selectAllItemsOnSheet( sheetPath );
3202
3203 // Inform other potentially interested tools
3204 if( m_selection.Size() > 0 )
3205 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3206
3207 return 0;
3208}
3209
3210
3212{
3213 // Should recalculate the view to zoom in on the selection.
3214 BOX2I selectionBox = m_selection.GetBoundingBox();
3216
3217 VECTOR2D screenSize = view->ToWorld( ToVECTOR2D( m_frame->GetCanvas()->GetClientSize() ),
3218 false );
3219 screenSize.x = std::max( 10.0, screenSize.x );
3220 screenSize.y = std::max( 10.0, screenSize.y );
3221
3222 if( selectionBox.GetWidth() != 0 || selectionBox.GetHeight() != 0 )
3223 {
3224 VECTOR2D vsize = selectionBox.GetSize();
3225 double scale = view->GetScale() / std::max( fabs( vsize.x / screenSize.x ),
3226 fabs( vsize.y / screenSize.y ) );
3227 view->SetScale( scale );
3228 view->SetCenter( selectionBox.Centre() );
3229 view->Add( &m_selection );
3230 }
3231
3232 m_frame->GetCanvas()->ForceRefresh();
3233}
3234
3235
3237{
3238 // Should recalculate the view to zoom in on the bbox.
3240
3241 if( aBBox.GetWidth() == 0 )
3242 return;
3243
3244 BOX2I bbox = aBBox;
3245 bbox.Normalize();
3246
3247 //#define DEFAULT_PCBNEW_CODE // Un-comment for normal full zoom KiCad algorithm
3248#ifdef DEFAULT_PCBNEW_CODE
3249 auto bbSize = bbox.Inflate( bbox.GetWidth() * 0.2f ).GetSize();
3250 auto screenSize = view->ToWorld( GetCanvas()->GetClientSize(), false );
3251
3252 // The "fabs" on x ensures the right answer when the view is flipped
3253 screenSize.x = std::max( 10.0, fabs( screenSize.x ) );
3254 screenSize.y = std::max( 10.0, screenSize.y );
3255 double ratio = std::max( fabs( bbSize.x / screenSize.x ), fabs( bbSize.y / screenSize.y ) );
3256
3257 // Try not to zoom on every cross-probe; it gets very noisy
3258 if( crossProbingSettings.zoom_to_fit && ( ratio < 0.5 || ratio > 1.0 ) )
3259 view->SetScale( view->GetScale() / ratio );
3260#endif // DEFAULT_PCBNEW_CODE
3261
3262#ifndef DEFAULT_PCBNEW_CODE // Do the scaled zoom
3263 auto bbSize = bbox.Inflate( KiROUND( bbox.GetWidth() * 0.2 ) ).GetSize();
3264 VECTOR2D screenSize = view->ToWorld( ToVECTOR2D( m_frame->GetCanvas()->GetClientSize() ), false );
3265
3266 // This code tries to come up with a zoom factor that doesn't simply zoom in
3267 // to the cross probed component, but instead shows a reasonable amount of the
3268 // circuit around it to provide context. This reduces or eliminates the need
3269 // to manually change the zoom because it's too close.
3270
3271 // Using the default text height as a constant to compare against, use the
3272 // height of the bounding box of visible items for a footprint to figure out
3273 // if this is a big footprint (like a processor) or a small footprint (like a resistor).
3274 // This ratio is not useful by itself as a scaling factor. It must be "bent" to
3275 // provide good scaling at varying component sizes. Bigger components need less
3276 // scaling than small ones.
3277 double currTextHeight = pcbIUScale.mmToIU( DEFAULT_TEXT_SIZE );
3278
3279 double compRatio = bbSize.y / currTextHeight; // Ratio of component to text height
3280
3281 // This will end up as the scaling factor we apply to "ratio".
3282 double compRatioBent = 1.0;
3283
3284 // This is similar to the original KiCad code that scaled the zoom to make sure
3285 // components were visible on screen. It's simply a ratio of screen size to
3286 // component size, and its job is to zoom in to make the component fullscreen.
3287 // Earlier in the code the component BBox is given a 20% margin to add some
3288 // breathing room. We compare the height of this enlarged component bbox to the
3289 // default text height. If a component will end up with the sides clipped, we
3290 // adjust later to make sure it fits on screen.
3291 //
3292 // The "fabs" on x ensures the right answer when the view is flipped
3293 screenSize.x = std::max( 10.0, fabs( screenSize.x ) );
3294 screenSize.y = std::max( 10.0, screenSize.y );
3295 double ratio = std::max( -1.0, fabs( bbSize.y / screenSize.y ) );
3296
3297 // Original KiCad code for how much to scale the zoom
3298 double kicadRatio = std::max( fabs( bbSize.x / screenSize.x ),
3299 fabs( bbSize.y / screenSize.y ) );
3300
3301 // LUT to scale zoom ratio to provide reasonable schematic context. Must work
3302 // with footprints of varying sizes (e.g. 0402 package and 200 pin BGA).
3303 // "first" is used as the input and "second" as the output
3304 //
3305 // "first" = compRatio (footprint height / default text height)
3306 // "second" = Amount to scale ratio by
3307 std::vector<std::pair<double, double>> lut {
3308 { 1, 8 },
3309 { 1.5, 5 },
3310 { 3, 3 },
3311 { 4.5, 2.5 },
3312 { 8, 2.0 },
3313 { 12, 1.7 },
3314 { 16, 1.5 },
3315 { 24, 1.3 },
3316 { 32, 1.0 },
3317 };
3318
3319
3320 std::vector<std::pair<double, double>>::iterator it;
3321
3322 compRatioBent = lut.back().second; // Large component default
3323
3324 if( compRatio >= lut.front().first )
3325 {
3326 // Use LUT to do linear interpolation of "compRatio" within "first", then
3327 // use that result to linearly interpolate "second" which gives the scaling
3328 // factor needed.
3329
3330 for( it = lut.begin(); it < lut.end() - 1; it++ )
3331 {
3332 if( it->first <= compRatio && next( it )->first >= compRatio )
3333 {
3334 double diffx = compRatio - it->first;
3335 double diffn = next( it )->first - it->first;
3336
3337 compRatioBent = it->second + ( next( it )->second - it->second ) * diffx / diffn;
3338 break; // We have our interpolated value
3339 }
3340 }
3341 }
3342 else
3343 {
3344 compRatioBent = lut.front().second; // Small component default
3345 }
3346
3347 // If the width of the part we're probing is bigger than what the screen width will be
3348 // after the zoom, then punt and use the KiCad zoom algorithm since it guarantees the
3349 // part's width will be encompassed within the screen. This will apply to parts that
3350 // are much wider than they are tall.
3351
3352 if( bbSize.x > screenSize.x * ratio * compRatioBent )
3353 {
3354 // Use standard KiCad zoom algorithm for parts too wide to fit screen/
3355 ratio = kicadRatio;
3356 compRatioBent = 1.0; // Reset so we don't modify the "KiCad" ratio
3357 wxLogTrace( "CROSS_PROBE_SCALE", "Part TOO WIDE for screen. Using normal KiCad zoom ratio: %1.5f", ratio );
3358 }
3359
3360 // Now that "compRatioBent" holds our final scaling factor we apply it to the original
3361 // fullscreen zoom ratio to arrive at the final ratio itself.
3362 ratio *= compRatioBent;
3363
3364 bool alwaysZoom = false; // DEBUG - allows us to minimize zooming or not
3365
3366 // Try not to zoom on every cross-probe; it gets very noisy
3367 if( ( ratio < 0.5 || ratio > 1.0 ) || alwaysZoom )
3368 view->SetScale( view->GetScale() / ratio );
3369#endif // ifndef DEFAULT_PCBNEW_CODE
3370}
3371
3372
3374{
3375 bool cleared = false;
3376
3377 if( m_selection.GetSize() > 0 )
3378 {
3379 // Don't fire an event now; most of the time it will be redundant as we're about to
3380 // fire a SelectedEvent.
3381 cleared = true;
3382 ClearSelection( true /*quiet mode*/ );
3383 }
3384
3385 if( aItem )
3386 {
3387 switch( aItem->Type() )
3388 {
3389 case PCB_NETINFO_T:
3390 {
3391 int netCode = static_cast<NETINFO_ITEM*>( aItem )->GetNetCode();
3392
3393 if( netCode > 0 )
3394 {
3395 SelectAllItemsOnNet( netCode, true );
3396 m_frame->FocusOnLocation( aItem->GetCenter() );
3397 }
3398 break;
3399 }
3400
3401 default:
3402 select( aItem );
3403 m_frame->FocusOnLocation( aItem->GetPosition() );
3404 }
3405
3406 // If the item has a bounding box, then zoom out if needed
3407 if( aItem->GetBoundingBox().GetHeight() > 0 && aItem->GetBoundingBox().GetWidth() > 0 )
3408 {
3409 // This adds some margin
3410 double marginFactor = 2;
3411
3412 KIGFX::PCB_VIEW* pcbView = canvas()->GetView();
3413 BOX2D screenBox = pcbView->GetViewport();
3414 VECTOR2D screenSize = screenBox.GetSize();
3415 BOX2I screenRect = BOX2ISafe( screenBox.GetOrigin(), screenSize / marginFactor );
3416
3417 if( !screenRect.Contains( aItem->GetBoundingBox() ) )
3418 {
3419 double scaleX = screenSize.x / static_cast<double>( aItem->GetBoundingBox().GetWidth() );
3420 double scaleY = screenSize.y / static_cast<double>( aItem->GetBoundingBox().GetHeight() );
3421
3422 scaleX /= marginFactor;
3423 scaleY /= marginFactor;
3424
3425 double scale = scaleX > scaleY ? scaleY : scaleX;
3426
3427 if( scale < 1 ) // Don't zoom in, only zoom out
3428 {
3429 pcbView->SetScale( pcbView->GetScale() * ( scale ) );
3430
3431 //Let's refocus because there is an algorithm to avoid dialogs in there.
3432 m_frame->FocusOnLocation( aItem->GetCenter() );
3433 }
3434 }
3435 }
3436 // Inform other potentially interested tools
3437 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3438 }
3439 else if( cleared )
3440 {
3441 m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
3442 }
3443
3444 m_frame->GetCanvas()->ForceRefresh();
3445}
3446
3447
3453static bool itemIsIncludedByFilter( const BOARD_ITEM& aItem, const BOARD& aBoard,
3454 const DIALOG_FILTER_SELECTION::OPTIONS& aFilterOptions )
3455{
3456 switch( aItem.Type() )
3457 {
3458 case PCB_FOOTPRINT_T:
3459 {
3460 const FOOTPRINT& footprint = static_cast<const FOOTPRINT&>( aItem );
3461
3462 return aFilterOptions.includeFootprints && ( aFilterOptions.includeLockedFootprints
3463 || !footprint.IsLocked() );
3464 }
3465
3466 case PCB_TRACE_T:
3467 case PCB_ARC_T:
3468 return aFilterOptions.includeTracks;
3469
3470 case PCB_VIA_T:
3471 return aFilterOptions.includeVias;
3472
3473 case PCB_ZONE_T:
3474 return aFilterOptions.includeZones;
3475
3476 case PCB_SHAPE_T:
3477 case PCB_TARGET_T:
3478 case PCB_DIM_ALIGNED_T:
3479 case PCB_DIM_CENTER_T:
3480 case PCB_DIM_RADIAL_T:
3482 case PCB_DIM_LEADER_T:
3483 if( aItem.GetLayer() == Edge_Cuts )
3484 return aFilterOptions.includeBoardOutlineLayer;
3485 else
3486 return aFilterOptions.includeItemsOnTechLayers;
3487
3488 case PCB_FIELD_T:
3489 case PCB_TEXT_T:
3490 case PCB_TEXTBOX_T:
3491 case PCB_TABLE_T:
3492 case PCB_TABLECELL_T:
3493 return aFilterOptions.includePcbTexts;
3494
3495 default:
3496 // Filter dialog is inclusive, not exclusive. If it's not included, then it doesn't
3497 // get selected.
3498 return false;
3499 }
3500}
3501
3502
3504{
3505 const BOARD& board = *getModel<BOARD>();
3506 DIALOG_FILTER_SELECTION::OPTIONS& opts = m_priv->m_filterOpts;
3507 DIALOG_FILTER_SELECTION dlg( m_frame, opts );
3508
3509 const int cmd = dlg.ShowModal();
3510
3511 if( cmd != wxID_OK )
3512 return 0;
3513
3514 // copy current selection
3515 std::deque<EDA_ITEM*> selection = m_selection.GetItems();
3516
3517 ClearSelection( true /*quiet mode*/ );
3518
3519 // re-select items from the saved selection according to the dialog options
3520 for( EDA_ITEM* i : selection )
3521 {
3522 if( !i->IsBOARD_ITEM() )
3523 continue;
3524
3525 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
3526 bool include = itemIsIncludedByFilter( *item, board, opts );
3527
3528 if( include )
3529 select( item );
3530 }
3531
3532 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3533
3534 return 0;
3535}
3536
3537
3539 PCB_SELECTION_FILTER_OPTIONS* aRejected )
3540{
3541 if( aCollector.GetCount() == 0 )
3542 return;
3543
3544 std::set<BOARD_ITEM*> rejected;
3545
3546 for( EDA_ITEM* i : aCollector )
3547 {
3548 if( !i->IsBOARD_ITEM() )
3549 continue;
3550
3551 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
3552
3553 if( !itemPassesFilter( item, aMultiSelect, aRejected ) )
3554 rejected.insert( item );
3555 }
3556
3557 for( BOARD_ITEM* item : rejected )
3558 aCollector.Remove( item );
3559}
3560
3561
3562bool PCB_SELECTION_TOOL::itemPassesFilter( BOARD_ITEM* aItem, bool aMultiSelect,
3563 PCB_SELECTION_FILTER_OPTIONS* aRejected )
3564{
3565 if( !m_filter.lockedItems )
3566 {
3567 if( aItem->IsLocked() || ( aItem->GetParent() && aItem->GetParent()->IsLocked() ) )
3568 {
3569 if( aItem->Type() == PCB_PAD_T && !aMultiSelect )
3570 {
3571 // allow a single pad to be selected -- there are a lot of operations that
3572 // require this so we allow this one inconsistency
3573 }
3574 else
3575 {
3576 if( aRejected )
3577 aRejected->lockedItems = true;
3578 return false;
3579 }
3580 }
3581 }
3582
3583 if( !aItem )
3584 return false;
3585
3586 KICAD_T itemType = aItem->Type();
3587
3588 if( itemType == PCB_GENERATOR_T )
3589 {
3590 if( static_cast<PCB_GENERATOR*>( aItem )->GetItems().empty() )
3591 {
3592 if( !m_filter.otherItems )
3593 {
3594 if( aRejected )
3595 aRejected->otherItems = true;
3596
3597 return false;
3598 }
3599 }
3600 else
3601 {
3602 itemType = ( *static_cast<PCB_GENERATOR*>( aItem )->GetItems().begin() )->Type();
3603 }
3604 }
3605
3606 switch( itemType )
3607 {
3608 case PCB_FOOTPRINT_T:
3609 if( !m_filter.footprints )
3610 {
3611 if( aRejected )
3612 aRejected->footprints = true;
3613
3614 return false;
3615 }
3616
3617 break;
3618
3619 case PCB_PAD_T:
3620 if( !m_filter.pads )
3621 {
3622 if( aRejected )
3623 aRejected->pads = true;
3624
3625 return false;
3626 }
3627
3628 break;
3629
3630 case PCB_TRACE_T:
3631 case PCB_ARC_T:
3632 if( !m_filter.tracks )
3633 {
3634 if( aRejected )
3635 aRejected->tracks = true;
3636
3637 return false;
3638 }
3639
3640 break;
3641
3642 case PCB_VIA_T:
3643 if( !m_filter.vias )
3644 {
3645 if( aRejected )
3646 aRejected->vias = true;
3647
3648 return false;
3649 }
3650
3651 break;
3652
3653 case PCB_ZONE_T:
3654 {
3655 ZONE* zone = static_cast<ZONE*>( aItem );
3656
3657 if( ( !m_filter.zones && !zone->GetIsRuleArea() )
3658 || ( !m_filter.keepouts && zone->GetIsRuleArea() ) )
3659 {
3660 if( aRejected )
3661 {
3662 if( zone->GetIsRuleArea() )
3663 aRejected->keepouts = true;
3664 else
3665 aRejected->zones = true;
3666 }
3667
3668 return false;
3669 }
3670
3671 // m_SolderMaskBridges zone is a special zone, only used to showsolder mask briges
3672 // after running DRC. it is not really a board item.
3673 // Never select it or delete by a Commit.
3674 if( zone == m_frame->GetBoard()->m_SolderMaskBridges )
3675 return false;
3676
3677 break;
3678 }
3679
3680 case PCB_SHAPE_T:
3681 case PCB_TARGET_T:
3682 if( !m_filter.graphics )
3683 {
3684 if( aRejected )
3685 aRejected->graphics = true;
3686
3687 return false;
3688 }
3689
3690 break;
3691
3693 if( !m_filter.graphics )
3694 {
3695 if( aRejected )
3696 aRejected->graphics = true;
3697
3698 return false;
3699 }
3700
3701 // a reference image living in a footprint must not be selected inside the board editor
3702 if( !m_isFootprintEditor && aItem->GetParentFootprint() )
3703 {
3704 if( aRejected )
3705 aRejected->text = true;
3706
3707 return false;
3708 }
3709
3710 break;
3711
3712 case PCB_FIELD_T:
3713 case PCB_TEXT_T:
3714 case PCB_TEXTBOX_T:
3715 case PCB_TABLE_T:
3716 case PCB_TABLECELL_T:
3717 if( !m_filter.text )
3718 return false;
3719
3720 break;
3721
3722 case PCB_DIM_ALIGNED_T:
3723 case PCB_DIM_CENTER_T:
3724 case PCB_DIM_RADIAL_T:
3726 case PCB_DIM_LEADER_T:
3727 if( !m_filter.dimensions )
3728 {
3729 if( aRejected )
3730 aRejected->dimensions = true;
3731
3732 return false;
3733 }
3734
3735 break;
3736
3737 case PCB_POINT_T:
3738 if( !m_filter.points )
3739 {
3740 if( aRejected )
3741 aRejected->points = true;
3742
3743 return false;
3744 }
3745
3746 break;
3747
3748 case PCB_BARCODE_T:
3749 default:
3750 if( !m_filter.otherItems )
3751 {
3752 if( aRejected )
3753 aRejected->otherItems = true;
3754
3755 return false;
3756 }
3757 }
3758
3759 return true;
3760}
3761
3762
3764{
3765 // Drop any table-cell range anchor along with the selection itself (do this even when the
3766 // selection is already empty so that the cached pointer is not left stranded)
3767 m_previousFirstCell = nullptr;
3768
3769 if( m_selection.Empty() )
3770 return;
3771
3772 while( m_selection.GetSize() )
3774
3775 view()->Update( &m_selection );
3776
3777 m_selection.SetIsHover( false );
3778 m_selection.ClearReferencePoint();
3779
3780 // Inform other potentially interested tools
3781 if( !aQuietMode )
3782 {
3783 m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
3785 }
3786}
3787
3788
3790{
3791 // Drop the table-cell range anchor; the board may have been reloaded and any cached
3792 // pointer is no longer guaranteed to be valid.
3793 m_previousFirstCell = nullptr;
3794
3795 m_selection.Clear();
3796
3797 bool enteredGroupFound = false;
3798
3799 INSPECTOR_FUNC inspector =
3800 [&]( EDA_ITEM* item, void* testData )
3801 {
3802 if( item->IsSelected() )
3803 {
3804 EDA_ITEM* parent = item->GetParent();
3805
3806 // Let selected parents handle their children.
3807 if( parent && parent->IsSelected() )
3809
3810 highlight( item, SELECTED, &m_selection );
3811 }
3812
3813 if( item->Type() == PCB_GROUP_T )
3814 {
3815 if( item == m_enteredGroup )
3816 {
3817 item->SetFlags( ENTERED );
3818 enteredGroupFound = true;
3819 }
3820 else
3821 {
3822 item->ClearFlags( ENTERED );
3823 }
3824 }
3825
3827 };
3828
3831
3832 if( !enteredGroupFound )
3833 {
3834 m_enteredGroupOverlay.Clear();
3835 m_enteredGroup = nullptr;
3836 }
3837}
3838
3839
3840bool PCB_SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibilityOnly ) const
3841{
3842 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
3843 const PCB_DISPLAY_OPTIONS& options = frame()->GetDisplayOptions();
3844
3845 auto visibleLayers =
3846 [&]() -> LSET
3847 {
3849 {
3850 LSET set;
3851
3852 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
3853 set.set( layer, view()->IsLayerVisible( layer ) );
3854
3855 return set;
3856 }
3857 else
3858 {
3859 return board()->GetVisibleLayers();
3860 }
3861 };
3862
3863 auto layerVisible =
3864 [&]( PCB_LAYER_ID aLayer )
3865 {
3867 return view()->IsLayerVisible( aLayer );
3868 else
3869 return board()->IsLayerVisible( aLayer );
3870 };
3871
3872 if( settings->GetHighContrast() )
3873 {
3874 const std::set<int> activeLayers = settings->GetHighContrastLayers();
3875 bool onActiveLayer = false;
3876
3877 for( int layer : activeLayers )
3878 {
3879 // NOTE: Only checking the regular layers (not GAL meta-layers)
3880 if( layer < PCB_LAYER_ID_COUNT && aItem->IsOnLayer( ToLAYER_ID( layer ) ) )
3881 {
3882 onActiveLayer = true;
3883 break;
3884 }
3885 }
3886
3887 if( !onActiveLayer && aItem->Type() != PCB_MARKER_T )
3888 {
3889 // We do not want to select items that are in the background
3890 return false;
3891 }
3892 }
3893
3894 if( aItem->Type() == PCB_FOOTPRINT_T )
3895 {
3896 const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
3897
3898 // In footprint editor, we do not want to select the footprint itself.
3900 return false;
3901
3902 // If the footprint has no items except the reference and value fields, include the
3903 // footprint in the selections.
3904 if( footprint->GraphicalItems().empty()
3905 && footprint->Pads().empty()
3906 && footprint->Zones().empty() )
3907 {
3908 return true;
3909 }
3910
3911 for( const BOARD_ITEM* item : footprint->GraphicalItems() )
3912 {
3913 if( Selectable( item, true ) )
3914 return true;
3915 }
3916
3917 for( const PAD* pad : footprint->Pads() )
3918 {
3919 if( Selectable( pad, true ) )
3920 return true;
3921 }
3922
3923 for( const ZONE* zone : footprint->Zones() )
3924 {
3925 if( Selectable( zone, true ) )
3926 return true;
3927 }
3928
3929 for( const PCB_POINT* point: footprint->Points() )
3930 {
3931 if( Selectable( point, true ) )
3932 return true;
3933 }
3934
3935 return false;
3936 }
3937 else if( aItem->Type() == PCB_GROUP_T )
3938 {
3939 PCB_GROUP* group = const_cast<PCB_GROUP*>( static_cast<const PCB_GROUP*>( aItem ) );
3940
3941 // Similar to logic for footprint, a group is selectable if any of its members are.
3942 // (This recurses.)
3943 for( BOARD_ITEM* item : group->GetBoardItems() )
3944 {
3945 if( Selectable( item, true ) )
3946 return true;
3947 }
3948
3949 return false;
3950 }
3951
3952 if( aItem->GetParentGroup() && aItem->GetParentGroup()->AsEdaItem()->Type() == PCB_GENERATOR_T )
3953 return false;
3954
3955 const ZONE* zone = nullptr;
3956 const PCB_VIA* via = nullptr;
3957 const PAD* pad = nullptr;
3958 const PCB_TEXT* text = nullptr;
3959 const PCB_FIELD* field = nullptr;
3960 const PCB_MARKER* marker = nullptr;
3961 const PCB_TABLECELL* cell = nullptr;
3962
3963 // Most footprint children can only be selected in the footprint editor.
3964 if( aItem->GetParentFootprint() && !m_isFootprintEditor && !checkVisibilityOnly )
3965 {
3966 if( aItem->Type() != PCB_FIELD_T && aItem->Type() != PCB_PAD_T && aItem->Type() != PCB_TEXT_T )
3967 return false;
3968 }
3969
3970 switch( aItem->Type() )
3971 {
3972 case PCB_ZONE_T:
3973 if( !board()->IsElementVisible( LAYER_ZONES ) || ( options.m_ZoneOpacity == 0.00 ) )
3974 return false;
3975
3976 zone = static_cast<const ZONE*>( aItem );
3977
3978 // A teardrop is modelled as a property of a via, pad or the board (for track-to-track
3979 // teardrops). The underlying zone is only an implementation detail.
3980 if( zone->IsTeardropArea() && !board()->LegacyTeardrops() )
3981 return false;
3982
3983 // zones can exist on multiple layers!
3984 if( !( zone->GetLayerSet() & visibleLayers() ).any() )
3985 return false;
3986
3987 break;
3988
3989 case PCB_TRACE_T:
3990 case PCB_ARC_T:
3991 if( !board()->IsElementVisible( LAYER_TRACKS ) || ( options.m_TrackOpacity == 0.00 ) )
3992 return false;
3993
3994 if( !layerVisible( aItem->GetLayer() ) )
3995 return false;
3996
3997 break;
3998
3999 case PCB_VIA_T:
4000 if( !board()->IsElementVisible( LAYER_VIAS ) || ( options.m_ViaOpacity == 0.00 ) )
4001 return false;
4002
4003 via = static_cast<const PCB_VIA*>( aItem );
4004
4005 // For vias it is enough if only one of its layers is visible
4006 if( !( visibleLayers() & via->GetLayerSet() ).any() )
4007 return false;
4008
4009 break;
4010
4011 case PCB_FIELD_T:
4012 field = static_cast<const PCB_FIELD*>( aItem );
4013
4014 if( !field->IsVisible() )
4015 return false;
4016
4017 if( field->IsReference() && !view()->IsLayerVisible( LAYER_FP_REFERENCES ) )
4018 return false;
4019
4020 if( field->IsValue() && !view()->IsLayerVisible( LAYER_FP_VALUES ) )
4021 return false;
4022
4023 // Handle all other fields with normal text visibility controls
4025 case PCB_TEXT_T:
4026 text = static_cast<const PCB_TEXT*>( aItem );
4027
4028 if( !layerVisible( text->GetLayer() ) )
4029 return false;
4030
4031 // Apply the LOD visibility test as well
4032 if( !view()->IsVisible( text ) )
4033 return false;
4034
4035 if( aItem->GetParentFootprint() )
4036 {
4037 int controlLayer = LAYER_FP_TEXT;
4038
4039 if( text->GetText() == wxT( "${REFERENCE}" ) )
4040 controlLayer = LAYER_FP_REFERENCES;
4041 else if( text->GetText() == wxT( "${VALUE}" ) )
4042 controlLayer = LAYER_FP_VALUES;
4043
4044 if( !view()->IsLayerVisible( controlLayer ) )
4045 return false;
4046 }
4047
4048 break;
4049
4051 if( options.m_ImageOpacity == 0.00 )
4052 return false;
4053
4054 // Bitmap images on board are hidden if LAYER_DRAW_BITMAPS is not visible
4055 if( !view()->IsLayerVisible( LAYER_DRAW_BITMAPS ) )
4056 return false;
4057
4058 if( !layerVisible( aItem->GetLayer() ) )
4059 return false;
4060
4061 break;
4062
4063 case PCB_SHAPE_T:
4064 if( options.m_FilledShapeOpacity == 0.0 && static_cast<const PCB_SHAPE*>( aItem )->IsAnyFill() )
4065 return false;
4066
4067 if( !layerVisible( aItem->GetLayer() ) )
4068 return false;
4069
4070 break;
4071
4072 case PCB_BARCODE_T:
4073 if( !layerVisible( aItem->GetLayer() ) )
4074 return false;
4075
4076 break;
4077
4078 case PCB_TEXTBOX_T:
4079 case PCB_TABLE_T:
4080 if( !layerVisible( aItem->GetLayer() ) )
4081 return false;
4082
4083 break;
4084
4085 case PCB_TABLECELL_T:
4086 cell = static_cast<const PCB_TABLECELL*>( aItem );
4087
4088 if( !layerVisible( aItem->GetLayer() ) )
4089 return false;
4090
4091 if( cell->GetRowSpan() == 0 || cell->GetColSpan() == 0 )
4092 return false;
4093
4094 break;
4095
4096 case PCB_DIM_ALIGNED_T:
4097 case PCB_DIM_LEADER_T:
4098 case PCB_DIM_CENTER_T:
4099 case PCB_DIM_RADIAL_T:
4101 if( !layerVisible( aItem->GetLayer() ) )
4102 return false;
4103
4104 break;
4105
4106 case PCB_PAD_T:
4107 if( options.m_PadOpacity == 0.00 )
4108 return false;
4109
4110 pad = static_cast<const PAD*>( aItem );
4111
4112 if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
4113 {
4114 // A pad's hole is visible on every layer the pad is visible on plus many layers the
4115 // pad is not visible on -- so we only need to check for any visible hole layers.
4116 if( !( visibleLayers() & LSET::PhysicalLayersMask() ).any() )
4117 return false;
4118 }
4119 else
4120 {
4121 if( !( pad->GetLayerSet() & visibleLayers() ).any() )
4122 return false;
4123 }
4124
4125 break;
4126
4127 case PCB_MARKER_T:
4128 marker = static_cast<const PCB_MARKER*>( aItem );
4129
4130 if( marker && marker->IsExcluded() && !board()->IsElementVisible( LAYER_DRC_EXCLUSION ) )
4131 return false;
4132
4133 break;
4134
4135 case PCB_POINT_T:
4136 if( !layerVisible( aItem->GetLayer() ) )
4137 return false;
4138
4139 if( !board()->IsElementVisible( LAYER_POINTS ) )
4140 return false;
4141
4142 break;
4143
4144 // These are not selectable
4145 case PCB_NETINFO_T:
4146 case PCB_CONSTRAINT_T: // geometry-free, never rendered or hit-tested (#2329)
4147 case NOT_USED:
4148 case TYPE_NOT_INIT:
4149 return false;
4150
4151 default: // Suppress warnings
4152 break;
4153 }
4154
4155 return true;
4156}
4157
4158
4160{
4161 if( !aItem || aItem->IsSelected() || !aItem->IsBOARD_ITEM() )
4162 return;
4163
4164 if( aItem->Type() == PCB_PAD_T )
4165 {
4166 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem->GetParent() );
4167
4168 if( m_selection.Contains( footprint ) )
4169 return;
4170 }
4171
4172 if( m_enteredGroup && !PCB_GROUP::WithinScope( static_cast<BOARD_ITEM*>( aItem ), m_enteredGroup,
4174 {
4175 ExitGroup();
4176 }
4177
4178 highlight( aItem, SELECTED, &m_selection );
4179}
4180
4181
4183{
4184 unhighlight( aItem, SELECTED, &m_selection );
4185}
4186
4187
4188void PCB_SELECTION_TOOL::highlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
4189{
4190 if( aGroup )
4191 aGroup->Add( aItem );
4192
4193 highlightInternal( aItem, aMode, aGroup != nullptr );
4194 view()->Update( aItem, KIGFX::REPAINT );
4195
4196 // Many selections are very temporal and updating the display each time just
4197 // creates noise.
4198 if( aMode == BRIGHTENED )
4200}
4201
4202
4203void PCB_SELECTION_TOOL::highlightInternal( EDA_ITEM* aItem, int aMode, bool aUsingOverlay )
4204{
4205 if( aMode == SELECTED )
4206 aItem->SetSelected();
4207 else if( aMode == BRIGHTENED )
4208 aItem->SetBrightened();
4209
4210 if( aUsingOverlay && aMode != BRIGHTENED )
4211 view()->Hide( aItem, true ); // Hide the original item, so it is shown only on overlay
4212
4213 if( aItem->IsBOARD_ITEM() )
4214 {
4215 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( aItem );
4216 boardItem->RunOnChildren( std::bind( &PCB_SELECTION_TOOL::highlightInternal, this, _1, aMode, aUsingOverlay ),
4218 }
4219}
4220
4221
4222void PCB_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
4223{
4224 if( aGroup )
4225 aGroup->Remove( aItem );
4226
4227 unhighlightInternal( aItem, aMode, aGroup != nullptr );
4228 view()->Update( aItem, KIGFX::REPAINT );
4229
4230 // Many selections are very temporal and updating the display each time just creates noise.
4231 if( aMode == BRIGHTENED )
4233}
4234
4235
4236void PCB_SELECTION_TOOL::unhighlightInternal( EDA_ITEM* aItem, int aMode, bool aUsingOverlay )
4237{
4238 if( aMode == SELECTED )
4239 aItem->ClearSelected();
4240 else if( aMode == BRIGHTENED )
4241 aItem->ClearBrightened();
4242
4243 if( aUsingOverlay && aMode != BRIGHTENED )
4244 {
4245 view()->Hide( aItem, false ); // Restore original item visibility...
4246 view()->Update( aItem ); // ... and make sure it's redrawn un-selected
4247 }
4248
4249 if( aItem->IsBOARD_ITEM() )
4250 {
4251 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( aItem );
4252 boardItem->RunOnChildren( std::bind( &PCB_SELECTION_TOOL::unhighlightInternal, this, _1, aMode, aUsingOverlay ),
4254 }
4255}
4256
4257
4259{
4260 const unsigned GRIP_MARGIN = 20;
4261 int margin = KiROUND( getView()->ToWorld( GRIP_MARGIN ) );
4262
4263 // Check if the point is located close to any of the currently selected items
4264 for( EDA_ITEM* item : m_selection )
4265 {
4266 if( !item->IsBOARD_ITEM() )
4267 continue;
4268
4269 BOX2I itemBox = item->ViewBBox();
4270 itemBox.Inflate( margin ); // Give some margin for gripping an item
4271
4272 if( itemBox.Contains( aPoint ) )
4273 {
4274 if( item->HitTest( aPoint, margin ) )
4275 return true;
4276
4277 bool found = false;
4278
4279 if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( item ) )
4280 {
4281 group->RunOnChildren(
4282 [&]( BOARD_ITEM* aItem )
4283 {
4284 if( aItem->HitTest( aPoint, margin ) )
4285 found = true;
4286 },
4288 }
4289
4290 if( found )
4291 return true;
4292 }
4293 }
4294
4295 return false;
4296}
4297
4298
4299int PCB_SELECTION_TOOL::hitTestDistance( const VECTOR2I& aWhere, BOARD_ITEM* aItem, int aMaxDistance ) const
4300{
4301 BOX2D viewportD = getView()->GetViewport();
4302 BOX2I viewport = BOX2ISafe( viewportD );
4303 int distance = INT_MAX;
4304 SEG loc( aWhere, aWhere );
4305
4306 switch( aItem->Type() )
4307 {
4308 case PCB_FIELD_T:
4309 case PCB_TEXT_T:
4310 {
4311 PCB_TEXT* text = static_cast<PCB_TEXT*>( aItem );
4312
4313 // Add a bit of slop to text-shapes
4314 if( text->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance ) )
4315 distance = std::clamp( distance - ( aMaxDistance / 2 ), 0, distance );
4316
4317 break;
4318 }
4319
4320 case PCB_TEXTBOX_T:
4321 {
4322 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( aItem );
4323
4324 // Add a bit of slop to text-shapes
4325 if( textbox->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance ) )
4326 distance = std::clamp( distance - ( aMaxDistance / 2 ), 0, distance );
4327
4328 break;
4329 }
4330
4331 case PCB_TABLECELL_T:
4332 {
4333 PCB_TABLECELL* tablecell = static_cast<PCB_TABLECELL*>( aItem );
4334 auto shape = std::make_shared<SHAPE_COMPOUND>( tablecell->MakeEffectiveShapesForHitTesting() );
4335
4336 shape->Collide( loc, aMaxDistance, &distance );
4337
4338 break;
4339 }
4340
4341 case PCB_TABLE_T:
4342 {
4343 PCB_TABLE* table = static_cast<PCB_TABLE*>( aItem );
4344 distance = aMaxDistance;
4345
4346 for( PCB_TABLECELL* cell : table->GetCells() )
4347 distance = std::min( distance, hitTestDistance( aWhere, cell, aMaxDistance ) );
4348
4349 // Tables should defer to their table cells. Never consider them exact.
4350 distance = std::clamp( distance + ( aMaxDistance / 4 ), 0, aMaxDistance );
4351 break;
4352 }
4353
4354 case PCB_ZONE_T:
4355 {
4356 ZONE* zone = static_cast<ZONE*>( aItem );
4357
4358 // Zone borders are very specific
4359 if( zone->HitTestForEdge( aWhere, aMaxDistance / 2 ) )
4360 distance = 0;
4361 else if( zone->HitTestForEdge( aWhere, aMaxDistance ) )
4362 distance = aMaxDistance / 2;
4363 else
4364 aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
4365
4366 break;
4367 }
4368
4369 case PCB_FOOTPRINT_T:
4370 {
4371 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
4372 BOX2I bbox = footprint->GetBoundingBox( false );
4373
4374 try
4375 {
4376 footprint->GetBoundingHull().Collide( loc, aMaxDistance, &distance );
4377 }
4378 catch( const std::exception& e )
4379 {
4380 wxFAIL_MSG( wxString::Format( wxT( "Clipper exception occurred: %s" ), e.what() ) );
4381 }
4382
4383 // Consider footprints larger than the viewport only as a last resort
4384 if( bbox.GetHeight() > viewport.GetHeight() || bbox.GetWidth() > viewport.GetWidth() )
4385 distance = INT_MAX / 2;
4386
4387 break;
4388 }
4389
4390 case PCB_MARKER_T:
4391 {
4392 PCB_MARKER* marker = static_cast<PCB_MARKER*>( aItem );
4393 SHAPE_LINE_CHAIN polygon;
4394
4395 marker->ShapeToPolygon( polygon );
4396 polygon.Move( marker->GetPos() );
4397 polygon.Collide( loc, aMaxDistance, &distance );
4398 break;
4399 }
4400
4401 case PCB_GROUP_T:
4402 case PCB_GENERATOR_T:
4403 {
4404 PCB_GROUP* group = static_cast<PCB_GROUP*>( aItem );
4405
4406 for( BOARD_ITEM* member : group->GetBoardItems() )
4407 distance = std::min( distance, hitTestDistance( aWhere, member, aMaxDistance ) );
4408
4409 break;
4410 }
4411
4412 case PCB_PAD_T:
4413 {
4414 static_cast<PAD*>( aItem )->Padstack().ForEachUniqueLayer(
4415 [&]( PCB_LAYER_ID aLayer )
4416 {
4417 int layerDistance = INT_MAX;
4418 aItem->GetEffectiveShape( aLayer )->Collide( loc, aMaxDistance, &layerDistance );
4419 distance = std::min( distance, layerDistance );
4420 } );
4421
4422 break;
4423 }
4424
4425 default:
4426 aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
4427 break;
4428 }
4429
4430 return distance;
4431}
4432
4433
4435{
4436 wxCHECK( m_frame, /* void */ );
4437
4438 if( aCollector.GetCount() < 2 )
4439 return;
4440
4441 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
4442
4443 wxCHECK( settings, /* void */ );
4444
4445 PCB_LAYER_ID activeLayer = m_frame->GetActiveLayer();
4446 LSET visibleLayers = m_frame->GetBoard()->GetVisibleLayers();
4447 LSET enabledLayers = m_frame->GetBoard()->GetEnabledLayers();
4448 LSEQ enabledLayerStack = enabledLayers.SeqStackupTop2Bottom( activeLayer );
4449
4450 wxCHECK( !enabledLayerStack.empty(), /* void */ );
4451
4452 auto isZoneFillKeepout =
4453 []( const BOARD_ITEM* aItem ) -> bool
4454 {
4455 if( aItem->Type() == PCB_ZONE_T )
4456 {
4457 const ZONE* zone = static_cast<const ZONE*>( aItem );
4458
4459 if( zone->GetIsRuleArea() && zone->GetDoNotAllowZoneFills() )
4460 return true;
4461 }
4462
4463 return false;
4464 };
4465
4466 std::vector<LAYER_OPACITY_ITEM> opacityStackup;
4467
4468 for( int i = 0; i < aCollector.GetCount(); i++ )
4469 {
4470 const BOARD_ITEM* item = aCollector[i];
4471
4472 LSET itemLayers = item->GetLayerSet() & enabledLayers & visibleLayers;
4473 LSEQ itemLayerSeq = itemLayers.Seq( enabledLayerStack );
4474
4475 for( PCB_LAYER_ID layer : itemLayerSeq )
4476 {
4477 COLOR4D color = settings->GetColor( item, layer );
4478
4479 if( color.a == 0 )
4480 continue;
4481
4482 LAYER_OPACITY_ITEM opacityItem;
4483
4484 opacityItem.m_Layer = layer;
4485 opacityItem.m_Opacity = color.a;
4486 opacityItem.m_Item = item;
4487
4488 if( isZoneFillKeepout( item ) )
4489 opacityItem.m_Opacity = 0.0;
4490
4491 opacityStackup.emplace_back( opacityItem );
4492 }
4493 }
4494
4495 std::sort( opacityStackup.begin(), opacityStackup.end(),
4496 [&]( const LAYER_OPACITY_ITEM& aLhs, const LAYER_OPACITY_ITEM& aRhs ) -> bool
4497 {
4498 int retv = enabledLayerStack.TestLayers( aLhs.m_Layer, aRhs.m_Layer );
4499
4500 if( retv )
4501 return retv > 0;
4502
4503 return aLhs.m_Opacity > aRhs.m_Opacity;
4504 } );
4505
4506 std::set<const BOARD_ITEM*> visibleItems;
4507 std::set<const BOARD_ITEM*> itemsToRemove;
4508 double minAlphaLimit = ADVANCED_CFG::GetCfg().m_PcbSelectionVisibilityRatio;
4509 double currentStackupOpacity = 0.0;
4511
4512 for( const LAYER_OPACITY_ITEM& opacityItem : opacityStackup )
4513 {
4514 if( lastVisibleLayer == PCB_LAYER_ID::UNDEFINED_LAYER )
4515 {
4516 currentStackupOpacity = opacityItem.m_Opacity;
4517 lastVisibleLayer = opacityItem.m_Layer;
4518 visibleItems.emplace( opacityItem.m_Item );
4519 continue;
4520 }
4521
4522 // Objects to ignore and fallback to the old selection behavior.
4523 auto ignoreItem =
4524 [&]()
4525 {
4526 const BOARD_ITEM* item = opacityItem.m_Item;
4527
4528 wxCHECK( item, false );
4529
4530 // Check items that span multiple layers for visibility.
4531 if( visibleItems.count( item ) )
4532 return true;
4533
4534 // Don't prune child items of a footprint that is already visible.
4535 if( item->GetParent()
4536 && ( item->GetParent()->Type() == PCB_FOOTPRINT_T )
4537 && visibleItems.count( item->GetParent() ) )
4538 {
4539 return true;
4540 }
4541
4542 // Keepout zones are transparent but for some reason, PCB_PAINTER::GetColor()
4543 // returns the color of the zone it prevents from filling.
4544 if( isZoneFillKeepout( item ) )
4545 return true;
4546
4547 return false;
4548 };
4549
4550 // Everything on the currently selected layer is visible;
4551 if( opacityItem.m_Layer == enabledLayerStack[0] )
4552 {
4553 visibleItems.emplace( opacityItem.m_Item );
4554 }
4555 else
4556 {
4557 double itemVisibility = opacityItem.m_Opacity * ( 1.0 - currentStackupOpacity );
4558
4559 if( ( itemVisibility <= minAlphaLimit ) && !ignoreItem() )
4560 itemsToRemove.emplace( opacityItem.m_Item );
4561 else
4562 visibleItems.emplace( opacityItem.m_Item );
4563 }
4564
4565 if( opacityItem.m_Layer != lastVisibleLayer )
4566 {
4567 currentStackupOpacity += opacityItem.m_Opacity * ( 1.0 - currentStackupOpacity );
4568 currentStackupOpacity = std::min( currentStackupOpacity, 1.0 );
4569 lastVisibleLayer = opacityItem.m_Layer;
4570 }
4571 }
4572
4573 for( const BOARD_ITEM* itemToRemove : itemsToRemove )
4574 {
4575 wxCHECK( aCollector.GetCount() > 1, /* void */ );
4576 aCollector.Remove( itemToRemove );
4577 }
4578}
4579
4580
4581// The general idea here is that if the user clicks directly on a small item inside a larger
4582// one, then they want the small item. The quintessential case of this is clicking on a pad
4583// within a footprint, but we also apply it for text within a footprint, footprints within
4584// larger footprints, and vias within either larger pads or longer tracks.
4585//
4586// These "guesses" presume there is area within the larger item to click in to select it. If
4587// an item is mostly covered by smaller items within it, then the guesses are inappropriate as
4588// there might not be any area left to click to select the larger item. In this case we must
4589// leave the items in the collector and bring up a Selection Clarification menu.
4590//
4591// We currently check for pads and text mostly covering a footprint, but we don't check for
4592// smaller footprints mostly covering a larger footprint.
4593//
4595 const VECTOR2I& aWhere ) const
4596{
4597 static const LSET silkLayers( { B_SilkS, F_SilkS } );
4598 static const LSET courtyardLayers( { B_CrtYd, F_CrtYd } );
4599 static std::vector<KICAD_T> singleLayerSilkTypes = { PCB_FIELD_T,
4603 PCB_BARCODE_T };
4604
4605 if( ADVANCED_CFG::GetCfg().m_PcbSelectionVisibilityRatio != 1.0 )
4607
4608 if( aCollector.GetCount() == 1 )
4609 return;
4610
4611 std::set<BOARD_ITEM*> preferred;
4612 std::set<BOARD_ITEM*> rejected;
4613 VECTOR2I where( aWhere.x, aWhere.y );
4614 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
4615 PCB_LAYER_ID activeLayer = m_frame->GetActiveLayer();
4616
4617 // If a silk layer is in front, we assume the user is working with silk and give preferential
4618 // treatment to single-layer items on *either* silk layer.
4619 if( silkLayers[activeLayer] )
4620 {
4621 for( int i = 0; i < aCollector.GetCount(); ++i )
4622 {
4623 BOARD_ITEM* item = aCollector[i];
4624
4625 if( item->IsType( singleLayerSilkTypes ) && silkLayers[ item->GetLayer() ] )
4626 preferred.insert( item );
4627 }
4628 }
4629 // Similarly, if a courtyard layer is in front, we assume the user is positioning footprints
4630 // and give preferential treatment to footprints on *both* top and bottom.
4631 else if( courtyardLayers[activeLayer] && settings->GetHighContrast() )
4632 {
4633 for( int i = 0; i < aCollector.GetCount(); ++i )
4634 {
4635 BOARD_ITEM* item = aCollector[i];
4636
4637 if( item->Type() == PCB_FOOTPRINT_T )
4638 preferred.insert( item );
4639 }
4640 }
4641
4642 if( preferred.size() > 0 )
4643 {
4644 aCollector.Empty();
4645
4646 for( BOARD_ITEM* item : preferred )
4647 aCollector.Append( item );
4648
4649 if( preferred.size() == 1 )
4650 return;
4651 }
4652
4653 // Prefer exact hits to sloppy ones
4654 constexpr int MAX_SLOP = 5;
4655
4656 int singlePixel = KiROUND( aCollector.GetGuide()->OnePixelInIU() );
4657 int maxSlop = KiROUND( MAX_SLOP * aCollector.GetGuide()->OnePixelInIU() );
4658 int minSlop = INT_MAX;
4659
4660 std::map<BOARD_ITEM*, int> itemsBySloppiness;
4661
4662 for( int i = 0; i < aCollector.GetCount(); ++i )
4663 {
4664 BOARD_ITEM* item = aCollector[i];
4665 int itemSlop = hitTestDistance( where, item, maxSlop );
4666
4667 itemsBySloppiness[ item ] = itemSlop;
4668
4669 if( itemSlop < minSlop )
4670 minSlop = itemSlop;
4671 }
4672
4673 // Prune sloppier items
4674 if( minSlop < INT_MAX )
4675 {
4676 for( std::pair<BOARD_ITEM*, int> pair : itemsBySloppiness )
4677 {
4678 if( pair.second > minSlop + singlePixel )
4679 aCollector.Transfer( pair.first );
4680 }
4681 }
4682
4683 // If the user clicked on a small item within a much larger one then it's pretty clear
4684 // they're trying to select the smaller one.
4685 constexpr double sizeRatio = 1.5;
4686
4687 std::vector<std::pair<BOARD_ITEM*, double>> itemsByArea;
4688
4689 for( int i = 0; i < aCollector.GetCount(); ++i )
4690 {
4691 BOARD_ITEM* item = aCollector[i];
4692 double area = 0.0;
4693
4694 if( item->Type() == PCB_ZONE_T
4695 && static_cast<ZONE*>( item )->HitTestForEdge( where, maxSlop / 2 ) )
4696 {
4697 // Zone borders are very specific, so make them "small"
4698 area = (double) SEG::Square( singlePixel ) * MAX_SLOP;
4699 }
4700 else if( item->Type() == PCB_VIA_T )
4701 {
4702 // Vias rarely hide other things, and we don't want them deferring to short track
4703 // segments underneath them -- so artificially reduce their size from πr² to r².
4704 area = (double) SEG::Square( static_cast<PCB_VIA*>( item )->GetDrill() / 2 );
4705 }
4706 else if( item->Type() == PCB_REFERENCE_IMAGE_T )
4707 {
4708 BOX2I box = item->GetBoundingBox();
4709 area = (double) box.GetWidth() * box.GetHeight();
4710 }
4711 else
4712 {
4713 try
4714 {
4715 area = FOOTPRINT::GetCoverageArea( item, aCollector );
4716 }
4717 catch( const std::exception& e )
4718 {
4719 wxFAIL_MSG( wxString::Format( wxT( "Clipper exception occurred: %s" ), e.what() ) );
4720 }
4721 }
4722
4723 itemsByArea.emplace_back( item, area );
4724 }
4725
4726 std::sort( itemsByArea.begin(), itemsByArea.end(),
4727 []( const std::pair<BOARD_ITEM*, double>& lhs,
4728 const std::pair<BOARD_ITEM*, double>& rhs ) -> bool
4729 {
4730 return lhs.second < rhs.second;
4731 } );
4732
4733 bool rejecting = false;
4734
4735 for( int i = 1; i < (int) itemsByArea.size(); ++i )
4736 {
4737 if( itemsByArea[i].second > itemsByArea[i-1].second * sizeRatio )
4738 rejecting = true;
4739
4740 if( rejecting )
4741 rejected.insert( itemsByArea[i].first );
4742 }
4743
4744 // Special case: if a footprint is completely covered with other features then there's no
4745 // way to select it -- so we need to leave it in the list for user disambiguation.
4746 constexpr double maxCoverRatio = 0.70;
4747
4748 for( int i = 0; i < aCollector.GetCount(); ++i )
4749 {
4750 if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aCollector[i] ) )
4751 {
4752 if( footprint->CoverageRatio( aCollector ) > maxCoverRatio )
4753 rejected.erase( footprint );
4754 }
4755 }
4756
4757 // Hopefully we've now got what the user wanted.
4758 if( (unsigned) aCollector.GetCount() > rejected.size() ) // do not remove everything
4759 {
4760 for( BOARD_ITEM* item : rejected )
4761 aCollector.Transfer( item );
4762 }
4763
4764 // Finally, what we are left with is a set of items of similar coverage area. We now reject
4765 // any that are not on the active layer, to reduce the number of disambiguation menus shown.
4766 // If the user wants to force-disambiguate, they can either switch layers or use the modifier
4767 // key to force the menu.
4768 if( aCollector.GetCount() > 1 )
4769 {
4770 bool haveItemOnActive = false;
4771 rejected.clear();
4772
4773 for( int i = 0; i < aCollector.GetCount(); ++i )
4774 {
4775 if( !aCollector[i]->IsOnLayer( activeLayer ) )
4776 rejected.insert( aCollector[i] );
4777 else
4778 haveItemOnActive = true;
4779 }
4780
4781 if( haveItemOnActive )
4782 {
4783 for( BOARD_ITEM* item : rejected )
4784 aCollector.Transfer( item );
4785 }
4786 }
4787}
4788
4789
4791{
4793 {
4794 m_frame->ShowInfoBarWarning( _( "Selection contains locked items. "
4795 "Enable 'Override locks' to operate on them." ),
4796 true );
4797 }
4798
4799 return m_lockedItemsFiltered;
4800}
4801
4802
4804{
4805 bool lockedDescendant = false;
4806
4807 aItem->RunOnChildren(
4808 [&]( BOARD_ITEM* curr_item )
4809 {
4810 if( !curr_item->GetParentFootprint() && curr_item->IsLocked() )
4811 lockedDescendant = true;
4812 },
4814
4815 return lockedDescendant;
4816}
4817
4818
4819bool PCB_SELECTION_TOOL::isWithinEnteredGroup( BOARD_ITEM* aItem, PCB_GROUP* aEnteredGroup, bool aIsFootprintEditor )
4820{
4821 if( aEnteredGroup )
4822 return PCB_GROUP::WithinScope( aItem, aEnteredGroup, aIsFootprintEditor );
4823
4824 // Not entered: keep expansion at the top level so it can't reach into a group and
4825 // silently pull the whole group into a later delete.
4826 return aItem->GetParentGroup() == nullptr;
4827}
4828
4829
4831{
4832 m_lockedItemsFiltered = false;
4833
4834 if( m_frame && m_frame->IsType( FRAME_PCB_EDITOR ) && !m_frame->GetOverrideLocks() )
4835 {
4836 // Iterate from the back so we don't have to worry about removals.
4837 for( int i = (int) aCollector.GetCount() - 1; i >= 0; --i )
4838 {
4839 BOARD_ITEM* item = aCollector[i];
4840
4841 if( item->IsLocked() || HasLockedDescendant( item ) )
4842 {
4843 aCollector.Remove( item );
4844 m_lockedItemsFiltered = true;
4845 }
4846 }
4847 }
4848}
4849
4850
4851void PCB_SELECTION_TOOL::FilterCollectorForHierarchy( GENERAL_COLLECTOR& aCollector, bool aMultiselect ) const
4852{
4853 std::unordered_set<EDA_ITEM*> toAdd;
4854
4855 // Set CANDIDATE on all parents which are included in the GENERAL_COLLECTOR. This
4856 // algorithm is O(3n), whereas checking for the parent inclusion could potentially be O(n^2).
4857 for( int j = 0; j < aCollector.GetCount(); j++ )
4858 {
4859 if( aCollector[j]->GetParent() )
4860 aCollector[j]->GetParent()->ClearFlags( CANDIDATE );
4861
4862 if( aCollector[j]->GetParentFootprint() )
4863 aCollector[j]->GetParentFootprint()->ClearFlags( CANDIDATE );
4864 }
4865
4866 if( aMultiselect )
4867 {
4868 for( int j = 0; j < aCollector.GetCount(); j++ )
4869 aCollector[j]->SetFlags( CANDIDATE );
4870 }
4871
4872 for( int j = 0; j < aCollector.GetCount(); )
4873 {
4874 BOARD_ITEM* item = aCollector[j];
4875 FOOTPRINT* fp = item->GetParentFootprint();
4876 BOARD_ITEM* start = item;
4877
4878 if( !m_isFootprintEditor && fp )
4879 start = fp;
4880
4881 // If a group is entered, disallow selections of objects outside the group.
4883 {
4884 aCollector.Remove( item );
4885 continue;
4886 }
4887
4888 // If any element is a member of a group, replace those elements with the top containing
4889 // group.
4891 {
4892 if( top->AsEdaItem() != item )
4893 {
4894 toAdd.insert( top->AsEdaItem() );
4895 top->AsEdaItem()->SetFlags( CANDIDATE );
4896
4897 aCollector.Remove( item );
4898 continue;
4899 }
4900 }
4901
4902 // Footprints are a bit easier as they can't be nested.
4903 if( fp && ( fp->GetFlags() & CANDIDATE ) )
4904 {
4905 // Remove children of selected items
4906 aCollector.Remove( item );
4907 continue;
4908 }
4909
4910 ++j;
4911 }
4912
4913 for( EDA_ITEM* item : toAdd )
4914 {
4915 if( !aCollector.HasItem( item ) )
4916 aCollector.Append( item );
4917 }
4918}
4919
4920
4922{
4923 std::set<BOARD_ITEM*> to_add;
4924
4925 // Iterate from the back so we don't have to worry about removals.
4926 for( int i = (int) aCollector.GetCount() - 1; i >= 0; --i )
4927 {
4928 BOARD_ITEM* item = aCollector[i];
4929
4930 if( item->Type() == PCB_TABLECELL_T )
4931 {
4932 if( !aCollector.HasItem( item->GetParent() ) )
4933 to_add.insert( item->GetParent() );
4934
4935 aCollector.Remove( item );
4936 }
4937 }
4938
4939 for( BOARD_ITEM* item : to_add )
4940 aCollector.Append( item );
4941}
4942
4943
4945 bool aForcePromotion ) const
4946{
4947 std::set<BOARD_ITEM*> to_add;
4948
4949 // Iterate from the back so we don't have to worry about removals.
4950 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
4951 {
4952 BOARD_ITEM* item = aCollector[i];
4953
4954 if( !m_isFootprintEditor && item->Type() == PCB_PAD_T
4955 && ( !frame()->GetPcbNewSettings()->m_AllowFreePads || aForcePromotion ) )
4956 {
4957 if( !aCollector.HasItem( item->GetParent() ) )
4958 to_add.insert( item->GetParent() );
4959
4960 aCollector.Remove( item );
4961 }
4962 }
4963
4964 for( BOARD_ITEM* item : to_add )
4965 aCollector.Append( item );
4966}
4967
4968
4970{
4971 // Iterate from the back so we don't have to worry about removals.
4972 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
4973 {
4974 BOARD_ITEM* item = aCollector[i];
4975
4976 if( item->Type() == PCB_MARKER_T )
4977 aCollector.Remove( item );
4978 }
4979}
4980
4981
4983 const VECTOR2I& aWhere ) const
4984{
4985 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
4986 BOX2D viewport = getView()->GetViewport();
4987 BOX2I extents = BOX2ISafe( viewport );
4988
4989 bool need_direct_hit = false;
4990 FOOTPRINT* single_fp = nullptr;
4991
4992 // If the designer is not modifying the existing selection AND we already have
4993 // a selection, then we only want to select items that are directly under the cursor.
4994 // This prevents us from being unable to clear the selection when zoomed into a footprint
4995 if( !m_additive && !m_subtractive && !m_exclusive_or && m_selection.GetSize() > 0 )
4996 {
4997 need_direct_hit = true;
4998
4999 for( EDA_ITEM* item : m_selection )
5000 {
5001 FOOTPRINT* fp = nullptr;
5002
5003 if( item->Type() == PCB_FOOTPRINT_T )
5004 fp = static_cast<FOOTPRINT*>( item );
5005 else if( item->IsBOARD_ITEM() )
5006 fp = static_cast<BOARD_ITEM*>( item )->GetParentFootprint();
5007
5008 // If the selection contains items that are not footprints, then don't restrict
5009 // whether we deselect the item or not.
5010 if( !fp )
5011 {
5012 single_fp = nullptr;
5013 break;
5014 }
5015 else if( !single_fp )
5016 {
5017 single_fp = fp;
5018 }
5019 // If the selection contains items from multiple footprints, then don't restrict
5020 // whether we deselect the item or not.
5021 else if( single_fp != fp )
5022 {
5023 single_fp = nullptr;
5024 break;
5025 }
5026 }
5027 }
5028
5029 auto visibleLayers =
5030 [&]() -> LSET
5031 {
5033 {
5034 LSET set;
5035
5036 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
5037 set.set( layer, view()->IsLayerVisible( layer ) );
5038
5039 return set;
5040 }
5041 else
5042 {
5043 return board()->GetVisibleLayers();
5044 }
5045 };
5046
5047 LSET layers = visibleLayers();
5048
5049 if( settings->GetHighContrast() )
5050 {
5051 layers.reset();
5052
5053 const std::set<int> activeLayers = settings->GetHighContrastLayers();
5054
5055 for( int layer : activeLayers )
5056 {
5057 if( layer >= 0 && layer < PCB_LAYER_ID_COUNT )
5058 layers.set( layer );
5059 }
5060 }
5061
5062 // Iterate from the back so we don't have to worry about removals.
5063 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
5064 {
5065 BOARD_ITEM* item = aCollector[i];
5066 FOOTPRINT* fp = dyn_cast<FOOTPRINT*>( item );
5067
5068 if( !fp )
5069 continue;
5070
5071 // Make footprints not difficult to select in high-contrast modes.
5072 if( layers[fp->GetLayer()] )
5073 continue;
5074
5075 BOX2I bbox = fp->GetLayerBoundingBox( layers );
5076
5077 // If the point clicked is not inside the visible bounding box, we can also remove it.
5078 if( !bbox.Contains( aWhere) )
5079 aCollector.Remove( item );
5080
5081 bool has_hit = false;
5082
5083 for( PCB_LAYER_ID layer : layers )
5084 {
5085 if( fp->HitTestOnLayer( extents, false, layer ) )
5086 {
5087 has_hit = true;
5088 break;
5089 }
5090 }
5091
5092 // If the point is outside of the visible bounding box, we can remove it.
5093 if( !has_hit )
5094 {
5095 aCollector.Remove( item );
5096 }
5097 // Do not require a direct hit on this fp if the existing selection only contains
5098 // this fp's items. This allows you to have a selection of pads from a single
5099 // footprint and still click in the center of the footprint to select it.
5100 else if( single_fp )
5101 {
5102 if( fp == single_fp )
5103 continue;
5104 }
5105 else if( need_direct_hit )
5106 {
5107 has_hit = false;
5108
5109 for( PCB_LAYER_ID layer : layers )
5110 {
5111 if( fp->HitTestOnLayer( aWhere, layer ) )
5112 {
5113 has_hit = true;
5114 break;
5115 }
5116 }
5117
5118 if( !has_hit )
5119 aCollector.Remove( item );
5120 }
5121 }
5122}
5123
5124
5126{
5127 getView()->Update( &m_selection );
5129
5130 return 0;
5131}
5132
5133
5135{
5136 std::set<std::pair<PCB_TABLE*, int>> columns;
5137 bool added = false;
5138
5139 for( EDA_ITEM* item : m_selection )
5140 {
5141 if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( item ) )
5142 {
5143 PCB_TABLE* table = static_cast<PCB_TABLE*>( cell->GetParent() );
5144 columns.insert( std::make_pair( table, cell->GetColumn() ) );
5145 }
5146 }
5147
5148 for( auto& [ table, col ] : columns )
5149 {
5150 for( int row = 0; row < table->GetRowCount(); ++row )
5151 {
5152 PCB_TABLECELL* cell = table->GetCell( row, col );
5153
5154 if( !cell->IsSelected() )
5155 {
5156 select( table->GetCell( row, col ) );
5157 added = true;
5158 }
5159 }
5160 }
5161
5162 if( added )
5163 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
5164
5165 return 0;
5166}
5167
5168
5170{
5171 std::set<std::pair<PCB_TABLE*, int>> rows;
5172 bool added = false;
5173
5174 for( EDA_ITEM* item : m_selection )
5175 {
5176 if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( item ) )
5177 {
5178 PCB_TABLE* table = static_cast<PCB_TABLE*>( cell->GetParent() );
5179 rows.insert( std::make_pair( table, cell->GetRow() ) );
5180 }
5181 }
5182
5183 for( auto& [ table, row ] : rows )
5184 {
5185 for( int col = 0; col < table->GetColCount(); ++col )
5186 {
5187 PCB_TABLECELL* cell = table->GetCell( row, col );
5188
5189 if( !cell->IsSelected() )
5190 {
5191 select( table->GetCell( row, col ) );
5192 added = true;
5193 }
5194 }
5195 }
5196
5197 if( added )
5198 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
5199
5200 return 0;
5201}
5202
5203
5205{
5206 std::set<PCB_TABLE*> tables;
5207 bool added = false;
5208
5209 for( EDA_ITEM* item : m_selection )
5210 {
5211 if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( item ) )
5212 tables.insert( static_cast<PCB_TABLE*>( cell->GetParent() ) );
5213 }
5214
5216
5217 for( PCB_TABLE* table : tables )
5218 {
5219 if( !table->IsSelected() )
5220 {
5221 select( table );
5222 added = true;
5223 }
5224 }
5225
5226 if( added )
5227 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
5228
5229 return 0;
5230}
5231
5232
5234{
5236
5240
5247
5266
5271
5273}
std::function< void(const VECTOR2I &, GENERAL_COLLECTOR &, PCB_SELECTION_TOOL *)> CLIENT_SELECTION_FILTER
Definition actions.h:33
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
constexpr BOX2I BOX2ISafe(const BOX2D &aInput)
Definition box2.h:925
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
BOX2< VECTOR2D > BOX2D
Definition box2.h:919
static TOOL_ACTION cancelInteractive
Definition actions.h:68
static TOOL_ACTION unselectAll
Definition actions.h:79
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition actions.h:223
static TOOL_ACTION cursorLeft
Definition actions.h:168
static TOOL_ACTION zoomOutCenter
Definition actions.h:132
static TOOL_ACTION unselectItem
Definition actions.h:224
static TOOL_ACTION zoomIn
Definition actions.h:129
static TOOL_ACTION cursorLeftFast
Definition actions.h:173
static TOOL_ACTION selectionCursor
Select a single item under the cursor position.
Definition actions.h:213
static TOOL_ACTION selectSetLasso
Definition actions.h:217
static TOOL_ACTION selectSetRect
Set lasso selection mode.
Definition actions.h:216
static TOOL_ACTION groupEnter
Definition actions.h:239
static TOOL_ACTION selectColumns
Definition actions.h:98
static TOOL_ACTION cursorDown
Definition actions.h:167
static TOOL_ACTION zoomOut
Definition actions.h:130
static TOOL_ACTION cursorRightFast
Definition actions.h:174
static TOOL_ACTION deleteLastPoint
Definition actions.h:269
static TOOL_ACTION zoomCenter
Definition actions.h:137
static TOOL_ACTION panDown
Definition actions.h:181
static TOOL_ACTION cursorDblClick
Definition actions.h:177
static TOOL_ACTION undo
Definition actions.h:71
static TOOL_ACTION selectionActivate
Activation of the selection tool.
Definition actions.h:210
static TOOL_ACTION cursorDownFast
Definition actions.h:172
static TOOL_ACTION selectionMenu
Run a selection menu to select from a list of items.
Definition actions.h:232
static TOOL_ACTION reselectItem
Definition actions.h:225
static TOOL_ACTION selectRows
Definition actions.h:97
static TOOL_ACTION cursorUpFast
Definition actions.h:171
static TOOL_ACTION panLeft
Definition actions.h:182
static TOOL_ACTION updateMenu
Definition actions.h:266
static TOOL_ACTION doDelete
Definition actions.h:81
static TOOL_ACTION selectionTool
Definition actions.h:247
static TOOL_ACTION cursorClick
Definition actions.h:176
static TOOL_ACTION zoomFitScreen
Definition actions.h:138
static TOOL_ACTION increment
Definition actions.h:90
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
static TOOL_ACTION panUp
Definition actions.h:180
static TOOL_ACTION zoomFitObjects
Definition actions.h:139
static TOOL_ACTION zoomInCenter
Definition actions.h:131
static TOOL_ACTION panRight
Definition actions.h:183
static TOOL_ACTION selectTable
Definition actions.h:99
static TOOL_ACTION cursorUp
Cursor control with keyboard.
Definition actions.h:166
static TOOL_ACTION groupLeave
Definition actions.h:240
static TOOL_ACTION finishInteractive
Definition actions.h:69
static TOOL_ACTION cursorRight
Definition actions.h:169
static TOOL_ACTION selectAll
Definition actions.h:78
static TOOL_ACTION unselectItems
Definition actions.h:229
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition actions.h:228
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.
wxMenuItem * Add(const wxString &aLabel, int aId, BITMAPS aIcon)
Add a wxWidgets-style entry to the menu.
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
BASE_SET & reset(size_t pos)
Definition base_set.h:143
BASE_SET & set(size_t pos)
Definition base_set.h:116
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
static bool ClassOf(const EDA_ITEM *aItem)
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
Tool for pcb inspection.
int ClearHighlight(const TOOL_EVENT &aEvent)
Perform the appropriate action in response to an Eeschema cross-probe.
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:295
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition board_item.h:159
bool IsLocked() const override
virtual VECTOR2I GetCenter() const
This defaults to the center of the bounding box if not overridden.
Definition board_item.h:137
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
FOOTPRINT * GetParentFootprint() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:315
virtual void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const
Invoke a function on all children.
Definition board_item.h:233
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:235
virtual bool IsOnCopperLayer() const
Definition board_item.h:176
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:373
INSPECT_RESULT Visit(INSPECTOR inspector, void *testData, const std::vector< KICAD_T > &scanTypes) override
May be re-implemented for each derived class in order to handle all the types given by its member dat...
Definition board.cpp:2617
bool IsElementVisible(GAL_LAYER_ID aLayer) const
Test whether a given element category is visible.
Definition board.cpp:1109
const LSET & GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition board.cpp:1057
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition board.h:599
bool IsLayerVisible(PCB_LAYER_ID aLayer) const
A proxy function that calls the correspondent function in m_BoardSettings tests whether a given layer...
Definition board.cpp:1049
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:646
constexpr void SetMaximum()
Definition box2.h:76
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:554
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:142
constexpr size_type GetWidth() const
Definition box2.h:210
constexpr Vec Centre() const
Definition box2.h:93
constexpr size_type GetHeight() const
Definition box2.h:211
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:164
constexpr const Vec & GetOrigin() const
Definition box2.h:206
constexpr const SizeVec & GetSize() const
Definition box2.h:202
CN_ANCHOR represents a physical location that can be connected: a pad or a track/arc/via endpoint.
BOARD_CONNECTED_ITEM * Parent() const
CN_EDGE represents a point-to-point connection, whether realized or unrealized (ie: tracks etc.
CN_ITEM represents a BOARD_CONNETED_ITEM in the connectivity system (ie: a pad, track/arc/via,...
virtual double OnePixelInIU() const =0
void Transfer(int aIndex)
Move the item at aIndex (first position is 0) to the backup list.
Definition collector.h:149
void Empty()
Clear the list.
Definition collector.h:87
ITER begin()
Definition collector.h:71
int GetCount() const
Return the number of objects in the list.
Definition collector.h:79
bool HasItem(const EDA_ITEM *aItem) const
Tests if aItem has already been collected.
Definition collector.h:193
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition collector.h:107
ITER end()
Definition collector.h:72
void Append(EDA_ITEM *item)
Add an item to the end of the list.
Definition collector.h:97
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:86
Interactive authoring of geometric constraints (issue #2329).
bool ClearConstraintSelection()
Clear any badge-selected constraint. Returns true if a constraint was selected and is now cleared.
bool EditConstraintAt(const VECTOR2I &aPos)
Open the value dialog for the constraint badged at aPos; returns true if one was hit.
int ShowModal() override
A set of EDA_ITEMs (i.e., without duplicates).
Definition eda_group.h:42
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:50
bool HasDesignBlockLink() const
Definition eda_group.h:71
virtual EDA_ITEM * AsEdaItem()=0
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
virtual VECTOR2I GetPosition() const
Definition eda_item.h:282
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:135
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:152
const KIID m_Uuid
Definition eda_item.h:531
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:114
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
void ClearSelected()
Definition eda_item.h:147
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:154
bool IsSelected() const
Definition eda_item.h:132
void SetSelected()
Definition eda_item.h:144
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition eda_item.h:202
void ClearBrightened()
Definition eda_item.h:148
void SetBrightened()
Definition eda_item.h:145
virtual bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const
Test if aPosition is inside or on the boundary of this item.
Definition eda_item.h:243
EDA_ITEM * GetParent() const
Definition eda_item.h:110
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:156
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:155
SHAPE_T GetShape() const
Definition eda_shape.h:185
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:240
bool IsClosed() const
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:190
bool IsAnyFill() const
Definition eda_shape.h:128
virtual std::vector< SHAPE * > MakeEffectiveShapesForHitTesting() const
Definition eda_shape.h:467
virtual bool IsVisible() const
Definition eda_text.h:208
std::shared_ptr< SHAPE_COMPOUND > GetEffectiveTextShape(bool aTriangulate=true, const BOX2I &aBBox=BOX2I(), const EDA_ANGLE &aAngle=ANGLE_0) const
build a list of segments (SHAPE_SEGMENT) to describe a text shape.
static const TOOL_EVENT DisambiguatePoint
Used for hotkey feedback.
Definition actions.h:360
static const TOOL_EVENT ClearedEvent
Definition actions.h:345
static const TOOL_EVENT InhibitSelectionEditing
Definition actions.h:356
static const TOOL_EVENT SelectedEvent
Definition actions.h:343
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:350
static const TOOL_EVENT UninhibitSelectionEditing
Used to inform tool that it should display the disambiguation menu.
Definition actions.h:357
static const TOOL_EVENT PointSelectedEvent
Definition actions.h:342
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition actions.h:353
static const TOOL_EVENT UnselectedEvent
Definition actions.h:344
ZONES & Zones()
Definition footprint.h:381
PCB_POINTS & Points()
Definition footprint.h:390
static double GetCoverageArea(const BOARD_ITEM *aItem, const GENERAL_COLLECTOR &aCollector)
const BOX2I GetLayerBoundingBox(const LSET &aLayers) const
Return the bounding box of the footprint on a given set of layers.
std::deque< PAD * > & Pads()
Definition footprint.h:375
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition footprint.h:420
SHAPE_POLY_SET GetBoundingHull() const
Return a bounding polygon for the shapes and pads in the footprint.
bool IsLocked() const override
Definition footprint.h:637
bool HitTestOnLayer(const VECTOR2I &aPosition, PCB_LAYER_ID aLayer, int aAccuracy=0) const
Test if the point hits one or more of the footprint elements on a given layer.
const KIID_PATH & GetPath() const
Definition footprint.h:467
DRAWINGS & GraphicalItems()
Definition footprint.h:378
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
A general implementation of a COLLECTORS_GUIDE.
Definition collectors.h:320
void SetIgnoreBlindBuriedVias(bool ignore)
Definition collectors.h:460
void SetIgnoreTracks(bool ignore)
Definition collectors.h:466
void SetIgnoreFootprintsOnFront(bool ignore)
Definition collectors.h:424
void SetIgnoreFPTextOnFront(bool ignore)
Definition collectors.h:412
void SetIgnoreMicroVias(bool ignore)
Definition collectors.h:463
void SetIgnoreZoneFills(bool ignore)
Definition collectors.h:469
void SetIgnorePadsOnBack(bool ignore)
Definition collectors.h:430
void SetIgnoreFPTextOnBack(bool ignore)
Definition collectors.h:406
void SetLayerVisibleBits(const LSET &aLayerBits)
Definition collectors.h:380
void SetIgnoreThroughVias(bool ignore)
Definition collectors.h:457
void SetIgnoreThroughHolePads(bool ignore)
Definition collectors.h:442
void SetIgnoreFPReferences(bool ignore)
Definition collectors.h:454
void SetIgnoreFPValues(bool ignore)
Definition collectors.h:448
void SetIgnorePadsOnFront(bool ignore)
Definition collectors.h:436
void SetIgnoreFootprintsOnBack(bool ignore)
Definition collectors.h:418
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:203
void SetGuide(const COLLECTORS_GUIDE *aGuide)
Record which COLLECTORS_GUIDE to use.
Definition collectors.h:287
const COLLECTORS_GUIDE * GetGuide() const
Definition collectors.h:289
static const std::vector< KICAD_T > AllBoardItems
A scan list for all editable board items.
Definition collectors.h:37
void Collect(BOARD_ITEM *aItem, const std::vector< KICAD_T > &aScanList, const VECTOR2I &aRefPos, const COLLECTORS_GUIDE &aGuide)
Scan a BOARD_ITEM using this class's Inspector method, which does the collection.
static const std::vector< KICAD_T > FootprintItems
A scan list for primary footprint items.
Definition collectors.h:103
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
double a
Alpha component.
Definition color4d.h:392
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const override
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition pcb_view.cpp:87
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1) override
Add a VIEW_ITEM to the view.
Definition pcb_view.cpp:53
virtual void Remove(VIEW_ITEM *aItem) override
Remove a VIEW_ITEM from the view.
Definition pcb_view.cpp:70
Represent a selection area (currently a rectangle) in a VIEW, drawn corner-to-corner between two poin...
void SetMode(SELECTION_MODE aMode)
void SetSubtractive(bool aSubtractive)
SELECTION_MODE GetMode() const
void SetAdditive(bool aAdditive)
void SetPoly(SHAPE_LINE_CHAIN &aPoly)
void SetOrigin(const VECTOR2I &aOrigin)
const BOX2I ViewBBox() const override
Set the origin of the rectangle (the fixed corner)
SHAPE_LINE_CHAIN & GetPoly()
void SetExclusiveOr(bool aExclusiveOr)
void SetEnd(const VECTOR2I &aEnd)
Set the current end of the rectangle (the corner that moves with the cursor.
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
const std::set< int > & GetHighlightNetCodes() const
Return the netcode of currently highlighted net.
const std::set< int > GetHighContrastLayers() const
Returns the set of currently high-contrast layers.
virtual COLOR4D GetColor(const VIEW_ITEM *aItem, int aLayer) const =0
Returns the color that should be used to draw the specific VIEW_ITEM on the specific layer using curr...
void SetHighlight(bool aEnabled, int aNetcode=-1, bool aMulti=false)
Turns on/off highlighting.
virtual void SetCursorPosition(const VECTOR2D &aPosition, bool aWarpView=true, bool aTriggeredByArrows=false, long aArrowCommand=0)=0
Move cursor to the requested position expressed in world coordinates.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
An abstract base class for deriving all objects that can be added to a VIEW.
Definition view_item.h:82
bool IsBOARD_ITEM() const
Definition view_item.h:98
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:63
double GetScale() const
Definition view.h:281
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition view.cpp:597
virtual void SetScale(double aScale, VECTOR2D aAnchor={ 0, 0 })
Set the scaling factor, zooming around a given anchor point.
Definition view.cpp:637
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition view.cpp:300
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition view.cpp:404
void UpdateAllLayersColor()
Apply the new coloring scheme to all layers.
Definition view.cpp:844
int Query(const BOX2I &aRect, std::vector< LAYER_ITEM_PAIR > &aResult) const
Find all visible items that touch or are within the rectangle aRect.
Definition view.cpp:487
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition view.cpp:1835
void Hide(VIEW_ITEM *aItem, bool aHide=true, bool aHideOverlay=false)
Temporarily hide the item in the view (e.g.
Definition view.cpp:1780
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition view.h:427
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition view.h:225
void MarkTargetDirty(int aTarget)
Set or clear target 'dirty' flag.
Definition view.h:657
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition view.cpp:1756
wxString AsString() const
Definition kiid.cpp:423
Definition kiid.h:46
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition lseq.h:47
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
LSEQ CuStack() const
Return a sequence of copper layers in starting from the front/top and extending to the back/bottom.
Definition lset.cpp:259
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:309
LSEQ SeqStackupTop2Bottom(PCB_LAYER_ID aSelectedLayer=UNDEFINED_LAYER) const
Generate a sequence of layers that represent a top to bottom stack of this set of layers.
Definition lset.cpp:339
static const LSET & AllLayersMask()
Definition lset.cpp:637
static const LSET & PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition lset.cpp:693
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
bool IsExcluded() const
Definition marker_base.h:89
const VECTOR2I & GetPos() const
Definition marker_base.h:79
void ShapeToPolygon(SHAPE_LINE_CHAIN &aPolygon, int aScale=-1) const
Return the shape polygon in internal units in a SHAPE_LINE_CHAIN the coordinates are relatives to the...
Handle the data for a net.
Definition netinfo.h:46
const wxString & GetNetChain() const
Definition netinfo.h:112
PAD * GetTerminalPad(int aIndex) const
Definition netinfo.h:115
REPLACE_TERMINAL_PAD_MENU * m_replaceMenu
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
Definition pad.h:61
static TOOL_ACTION drag45Degree
static TOOL_ACTION selectNetChain
Select all connections belonging to every net in the current item's net chain.
Definition pcb_actions.h:86
static TOOL_ACTION unrouteSelected
Removes all tracks from the selected items to the first pad.
Definition pcb_actions.h:74
static TOOL_ACTION saveToLinkedDesignBlock
static TOOL_ACTION grabUnconnected
Select and move nearest unconnected footprint from ratsnest of selection.
Definition pcb_actions.h:92
static TOOL_ACTION filterSelection
Filter the items in the current selection (invokes dialog)
static TOOL_ACTION setTerminalPad
static TOOL_ACTION highlightNet
static TOOL_ACTION hideLocalRatsnest
static TOOL_ACTION properties
Activation of the edit tool.
static TOOL_ACTION highlightNetChain
static TOOL_ACTION selectOnSheetFromEeschema
Select all components on sheet from Eeschema crossprobing.
Definition pcb_actions.h:95
static TOOL_ACTION selectConnection
Select tracks between junctions or expands an existing selection to pads or the entire connection.
Definition pcb_actions.h:71
static TOOL_ACTION applyDesignBlockLayout
static TOOL_ACTION dragFreeAngle
static TOOL_ACTION clearHighlight
static TOOL_ACTION selectUnconnected
Select unconnected footprints from ratsnest of selection.
Definition pcb_actions.h:89
static TOOL_ACTION unrouteSegment
Removes track segment from the selected item to the next segment.
Definition pcb_actions.h:77
static TOOL_ACTION moveIndividually
move items one-by-one
static TOOL_ACTION syncSelection
Sets selection to specified items, zooms to fit, if enabled.
Definition pcb_actions.h:61
static TOOL_ACTION selectSameSheet
Select all components on the same sheet as the selected footprint.
Definition pcb_actions.h:98
static TOOL_ACTION selectNet
Select all connections belonging to a single net.
Definition pcb_actions.h:80
static TOOL_ACTION move
move or drag an item
static TOOL_ACTION syncSelectionWithNets
Sets selection to specified items with connected nets, zooms to fit, if enabled.
Definition pcb_actions.h:64
static TOOL_ACTION deselectNet
Remove all connections belonging to a single net from the active selection.
Definition pcb_actions.h:83
static TOOL_ACTION placeLinkedDesignBlock
static TOOL_ACTION selectOnSchematic
Select symbols/pins on schematic corresponding to selected footprints/pads.
Common, abstract interface for edit frames.
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
const PCB_DISPLAY_OPTIONS & GetDisplayOptions() const
Display options control the way tracks, vias, outlines and other things are shown (for instance solid...
double m_TrackOpacity
Opacity override for all tracks.
double m_FilledShapeOpacity
Opacity override for graphic shapes.
double m_ZoneOpacity
Opacity override for filled zone areas.
double m_ImageOpacity
Opacity override for user images.
double m_PadOpacity
Opacity override for SMD pads and PTHs.
double m_ViaOpacity
Opacity override for all types of via.
ZONE_DISPLAY_MODE m_ZoneDisplayMode
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
The main frame for Pcbnew.
bool IsReference() const
Definition pcb_field.h:66
bool IsValue() const
Definition pcb_field.h:67
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:51
static bool WithinScope(BOARD_ITEM *aItem, PCB_GROUP *aScope, bool isFootprintEditor)
static EDA_GROUP * TopLevelGroup(BOARD_ITEM *aItem, EDA_GROUP *aScope, bool isFootprintEditor)
std::unordered_set< BOARD_ITEM * > GetBoardItems() const
Definition pcb_group.cpp:98
Tool that displays edit points allowing to modify items by dragging the points.
bool HasPoint()
Indicate the cursor is over an edit point.
A PCB_POINT is a 0-dimensional point that is used to mark a position on a PCB, or more usually a foot...
Definition pcb_point.h:39
Private implementation of firewalled private data.
DIALOG_FILTER_SELECTION::OPTIONS m_filterOpts
The selection tool: currently supports:
void highlight(EDA_ITEM *aItem, int aHighlightMode, SELECTION *aGroup=nullptr) override
Highlight the item visually.
int syncSelectionWithNets(const TOOL_EVENT &aEvent)
int SelectTable(const TOOL_EVENT &aEvent)
Clear current selection event handler.
PCB_BASE_FRAME * frame() const
int syncSelection(const TOOL_EVENT &aEvent)
int selectNet(const TOOL_EVENT &aEvent)
Select all copper connections belonging to the same net(s) as the items in the selection.
int filterSelection(const TOOL_EVENT &aEvent)
Return true if the given item passes the current SELECTION_FILTER_OPTIONS.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
void ZoomFitCrossProbeBBox(const BOX2I &bbox)
void EnterGroup() override
Enter the group at the head of the current selection.
void doSyncSelection(const std::vector< BOARD_ITEM * > &aItems, bool aWithNets)
Invoke filter dialog and modify current selection.
SELECTION_MODE m_selectionMode
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...
int selectNetChain(const TOOL_EVENT &aEvent)
Select all copper connections belonging to every net in the selected item's net chain.
void ExitGroup(bool aSelectGroup=false) override
Leave the currently-entered group.
bool isExpandableGraphicShape(const EDA_ITEM *aItem) const
bool toggleTableCellSelection(const VECTOR2I &aPosition)
When the user Ctrl-clicks inside a PCB_TABLE whose cells are already in the selection,...
static bool HasLockedDescendant(const BOARD_ITEM *aItem)
int disambiguateCursor(const TOOL_EVENT &aEvent)
Handle disambiguation actions including displaying the menu.
int SelectPolyArea(const TOOL_EVENT &aEvent)
Handles drawing a lasso selection area that allows multiple items to be selected simultaneously.
void FilterCollectorForMarkers(GENERAL_COLLECTOR &aCollector) const
Drop any PCB_MARKERs from the collector.
int UnselectAll(const TOOL_EVENT &aEvent)
Change the selection mode.
void unhighlightInternal(EDA_ITEM *aItem, int aHighlightMode, bool aUsingOverlay)
bool selectionContains(const VECTOR2I &aPoint) const
void select(EDA_ITEM *aItem) override
Take necessary action mark an item as selected.
bool selectCursor(bool aForceSelect=false, CLIENT_SELECTION_FILTER aClientFilter=nullptr)
Select an item under the cursor unless there is something already selected.
int SetSelectPoly(const TOOL_EVENT &aEvent)
int SetSelectRect(const TOOL_EVENT &aEvent)
int SelectColumns(const TOOL_EVENT &aEvent)
std::unique_ptr< PRIV > m_priv
PCB_TABLECELL * singleSelectedCell() const
int unrouteSelected(const TOOL_EVENT &aEvent)
Unroute the selected board connected items.
int unrouteSegment(const TOOL_EVENT &aEvent)
Unroute the selected track connected item.
SELECTION & selection() override
Return a reference to the selection.
int grabUnconnected(const TOOL_EVENT &aEvent)
Select and move other nearest footprint unconnected on same net as selected items.
PCB_SELECTION & RequestSelection(CLIENT_SELECTION_FILTER aClientFilter)
Return the current selection, filtered according to aClientFilter.
static bool isWithinEnteredGroup(BOARD_ITEM *aItem, PCB_GROUP *aEnteredGroup, bool aIsFootprintEditor)
True if aItem may be selected while aEnteredGroup is entered (24967).
bool ReportFilteredLockedItems()
If the most recent FilterCollectorForLockedItems call filtered a locked item, show an InfoBar warning...
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...
bool itemPassesFilter(BOARD_ITEM *aItem, bool aMultiSelect, PCB_SELECTION_FILTER_OPTIONS *aRejected=nullptr)
const GENERAL_COLLECTORS_GUIDE getCollectorsGuide() const
bool Selectable(const BOARD_ITEM *aItem, bool checkVisibilityOnly=false) const
bool selectPoint(const VECTOR2I &aWhere, bool aOnDrag=false, bool *aSelectionCancelledFlag=nullptr, CLIENT_SELECTION_FILTER aClientFilter=nullptr)
Select an item pointed by the parameter aWhere.
KIGFX::PCB_VIEW * view() const
void selectAllItemsOnSheet(wxString &aSheetPath)
Select all items with the given sheet timestamp/UUID name (the sheet path).
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.
void SelectMultiple(KIGFX::PREVIEW::SELECTION_AREA &aArea, bool aSubtractive=false, bool aExclusiveOr=false)
Selects multiple PCB items within a specified area.
void setTransitions() override
Zoom the screen to center and fit the current selection.
PCB_BASE_EDIT_FRAME * editFrame() const
int expandConnection(const TOOL_EVENT &aEvent)
Expand the current connected-item selection to the next boundary (junctions, pads,...
int selectUnconnected(const TOOL_EVENT &aEvent)
Select nearest unconnected footprints on same net as selected items.
virtual bool ctrlClickHighlights() override
Determine if ctrl-click is highlight net or XOR selection.
int selectSheetContents(const TOOL_EVENT &aEvent)
Select all footprints belonging to same hierarchical sheet as the selected footprint (same sheet path...
int SelectRows(const TOOL_EVENT &aEvent)
int selectSameSheet(const TOOL_EVENT &aEvent)
Set selection to items passed by parameter and connected nets (optionally).
void zoomFitSelection()
Zoom the screen to fit the bounding box for cross probing/selection sync.
void selectAllConnectedTracks(const std::vector< BOARD_CONNECTED_ITEM * > &aStartItems, STOP_CONDITION aStopCondition)
Select connected tracks and vias.
int CursorSelection(const TOOL_EVENT &aEvent)
int ClearSelection(const TOOL_EVENT &aEvent)
void collectTableCellsAt(const VECTOR2I &aPosition, GENERAL_COLLECTOR &aCollector)
Collect PCB_TABLECELL items at aPosition into aCollector, scoped to either the active footprint (in t...
void highlightInternal(EDA_ITEM *aItem, int aHighlightMode, bool aUsingOverlay)
PCB_BASE_FRAME * m_frame
PCB_SELECTION_FILTER_OPTIONS m_filter
void initializeTableCellSelectionState(PCB_TABLE *aTable)
Mark the existing selection state of table cells so that drag- and shift-click range selection can pr...
PCB_SELECTION & GetSelection()
@ STOP_AT_SEGMENT
Stop when reaching a segment (next track/arc/via).
@ STOP_AT_PAD
Stop when reaching a pad.
@ STOP_NEVER
Select the entire net.
@ STOP_AT_JUNCTION
Stop at any place where more than two traces meet.
int Main(const TOOL_EVENT &aEvent)
The main loop.
void RebuildSelection()
Rebuild the selection from the EDA_ITEMs' selection flags.
void pruneObscuredSelectionCandidates(GENERAL_COLLECTOR &aCollector) const
void OnIdle(wxIdleEvent &aEvent)
PCB_DRAW_PANEL_GAL * canvas() const
void FilterCollectorForFootprints(GENERAL_COLLECTOR &aCollector, const VECTOR2I &aWhere) const
Drop footprints that are not directly selected.
PCB_TABLECELL * m_previousFirstCell
PCB_SELECTION m_blockedSelection
void FindItem(BOARD_ITEM *aItem)
Take necessary actions to mark an item as found.
void FilterCollectorForLockedItems(GENERAL_COLLECTOR &aCollector)
In the PCB editor strip out any locked items unless the OverrideLocks checkbox is set.
int SelectRectArea(const TOOL_EVENT &aEvent)
Handles drawing a selection box that allows multiple items to be selected simultaneously.
int updateSelection(const TOOL_EVENT &aEvent)
Event handler to update the selection VIEW_ITEM.
bool Init() override
Init() is called once upon a registration of the tool.
KIGFX::VIEW_GROUP m_enteredGroupOverlay
void selectConnections(const std::vector< BOARD_ITEM * > &aItems)
int hitTestDistance(const VECTOR2I &aWhere, BOARD_ITEM *aItem, int aMaxDistance) const
bool extendTableCellSelectionTo(const VECTOR2I &aPosition)
If the current selection holds one or more cells from a single PCB_TABLE and the cursor is over anoth...
void selectAllConnectedShapes(const std::vector< PCB_SHAPE * > &aStartItems)
Select all non-closed shapes that are graphically connected to the given start items.
void SelectAllItemsOnNet(int aNetCode, bool aSelect=true)
Select all items with the given net code.
void FilterCollectorForTableCells(GENERAL_COLLECTOR &aCollector) const
Promote any table cell selections to the whole table.
void unselect(EDA_ITEM *aItem) override
Take necessary action mark an item as unselected.
int SelectAll(const TOOL_EVENT &aEvent)
Unselect all items on the board.
void FilterCollectedItems(GENERAL_COLLECTOR &aCollector, bool aMultiSelect, PCB_SELECTION_FILTER_OPTIONS *aRejected=nullptr)
Apply the SELECTION_FITLER_OPTIONS to the collector.
void selectCellsBetween(const VECTOR2D &aStart, const VECTOR2D &aEnd, PCB_TABLE *aTable)
Select table cells contained within the rectangle defined by two corner points, combining the result ...
bool selectTableCells(PCB_TABLE *aTable)
void unhighlight(EDA_ITEM *aItem, int aHighlightMode, SELECTION *aGroup=nullptr) override
Unhighlight the item visually.
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_shape.h:78
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
std::vector< VECTOR2I > GetConnectionPoints() const
int GetRowSpan() const
int GetColSpan() const
std::vector< PCB_TABLECELL * > GetCells() const
Definition pcb_table.h:156
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
const VECTOR2I & GetStart() const
Definition pcb_track.h:93
const VECTOR2I & GetEnd() const
Definition pcb_track.h:90
VECTOR2I GetPosition() const override
Definition pcb_track.h:561
int GetWidth() const override
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
A small class to help profiling.
Definition profile.h:46
void Start()
Start or restart the counter.
Definition profile.h:74
double msecs(bool aSinceLast=false)
Definition profile.h:147
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
OPT_TOOL_EVENT eventHandler(const wxMenuEvent &aEvent) override
Event handler stub.
void update() override
Update menu state stub.
Describe ratsnest for a single net.
const std::vector< CN_EDGE > & GetEdges() const
bool RoutingInProgress()
Returns whether routing is currently active.
Definition seg.h:38
static SEG::ecoord Square(int a)
Definition seg.h:119
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.
bool m_multiple
Multiple selection mode is active.
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
bool doSelectionMenu(COLLECTOR *aCollector)
wxTimer m_disambiguateTimer
Timer to show the disambiguate menu.
bool m_drag_additive
Add multiple items to selection.
bool m_exclusive_or
Items' selection state should be toggled.
int AddItemsToSel(const TOOL_EVENT &aEvent)
int AddItemToSel(const TOOL_EVENT &aEvent)
int UpdateMenu(const TOOL_EVENT &aEvent)
Update a menu's state based on the current selection.
void setModifiersState(bool aShiftState, bool aCtrlState, bool aAltState)
Set the configuration of m_additive, m_subtractive, m_exclusive_or, m_skip_heuristics from the state ...
VECTOR2I m_originalCursor
Location of original cursor when starting click.
int SelectionMenu(const TOOL_EVENT &aEvent)
Show a popup menu to trim the COLLECTOR passed as aEvent's parameter down to a single item.
int RemoveItemsFromSel(const TOOL_EVENT &aEvent)
bool m_subtractive
Items should be removed from selection.
SELECTION_TOOL(const std::string &aName)
bool m_highlight_modifier
Select highlight net on left click.
bool m_skip_heuristics
Show disambiguation menu for all items under the cursor rather than trying to narrow them down first ...
int ReselectItem(const TOOL_EVENT &aEvent)
bool m_drag_subtractive
Remove multiple from selection.
bool m_additive
Items should be added to sel (instead of replacing).
bool hasModifier()
True if a selection modifier is enabled, false otherwise.
bool m_canceledMenu
Sets to true if the disambiguation menu was canceled.
void onDisambiguationExpire(wxTimerEvent &aEvent)
Start the process to show our disambiguation menu once the user has kept the mouse down for the minim...
virtual void Add(EDA_ITEM *aItem)
A null aItem is ignored; the selection never holds null members.
Definition selection.cpp:38
virtual void Remove(EDA_ITEM *aItem)
Definition selection.cpp:60
EDA_ITEM * Front() const
Definition selection.h:176
void ClearReferencePoint()
bool Empty() const
Checks if there is anything selected.
Definition selection.h:114
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Move(const VECTOR2I &aVector) override
void GenerateBBoxCache() const
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if point aP lies closer to us than aClearance.
double Area(bool aAbsolute=true) const
Return the area of this chain.
virtual size_t GetPointCount() const override
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CLastPoint() const
Return the last point in the line chain.
void Remove(int aStartIndex, int aEndIndex)
Remove the range of points [start_index, end_index] from the line chain.
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const
Check if the boundary of shape (this) lies closer to the point aP than aClearance,...
Definition shape.h:179
bool ToolStackIsEmpty()
Represent a single user action.
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:182
T * getModel() const
Return the model object if it matches the requested type.
Definition tool_base.h:195
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition tool_base.cpp:40
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition tool_base.cpp:34
bool IsToolActive() const
Definition tool_base.cpp:28
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:74
@ REDRAW
Full drawing refresh.
Definition tool_base.h:79
@ MODEL_RELOAD
Model changes (the sheet for a schematic)
Definition tool_base.h:76
Generic, UI-independent tool event.
Definition tool_event.h:167
void SetParameter(T aParam)
Set a non-standard parameter assigned to the event.
Definition tool_event.h:524
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:469
void SetPassEvent(bool aPass=true)
Definition tool_event.h:252
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).
std::unique_ptr< TOOL_MENU > m_menu
The functions below are not yet implemented - their interface may change.
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.
Master controller class:
TOOLS_HOLDER * GetToolHolder() const
Handle a list of polygons defining a copper zone.
Definition zone.h:70
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:813
bool HitTestForCorner(const VECTOR2I &refPos, int aAccuracy, SHAPE_POLY_SET::VERTEX_INDEX *aCornerHit=nullptr) const
Test if the given VECTOR2I is near a corner.
Definition zone.cpp:900
bool HitTestForEdge(const VECTOR2I &refPos, int aAccuracy, SHAPE_POLY_SET::VERTEX_INDEX *aCornerHit=nullptr) const
Test if the given VECTOR2I is near a segment defined by 2 corners.
Definition zone.cpp:912
bool IsTeardropArea() const
Definition zone.h:788
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:133
bool GetDoNotAllowZoneFills() const
Definition zone.h:823
A type-safe container of any type.
Definition ki_any.h:92
#define EXCLUDE_ZONES
#define IGNORE_NETS
Function GetConnectedItems() Returns a list of items connected to a source item aItem.
KICURSOR
Definition cursors.h:40
@ SUBTRACT
Definition cursors.h:68
@ SELECT_WINDOW
Definition cursors.h:82
@ SELECT_LASSO
Definition cursors.h:84
@ MOVING
Definition cursors.h:44
@ ARROW
Definition cursors.h:42
#define DEFAULT_TEXT_SIZE
Ratio of the font height to the baseline of the text above the wire.
static bool empty(const wxTextEntryBase *aCtrl)
#define _(s)
@ RECURSE
Definition eda_item.h:49
std::function< INSPECT_RESULT(EDA_ITEM *aItem, void *aTestData) > INSPECTOR_FUNC
Used to inspect and possibly collect the (search) results of iterating over a list or tree of KICAD_T...
Definition eda_item.h:86
#define BRIGHTENED
item is drawn with a bright contour
#define IS_NEW
New item, just created.
#define SELECTED
Item was manually selected by the user.
#define ENTERED
indicates a group has been entered
#define SKIP_STRUCT
flag indicating that the structure should be ignored
#define CANDIDATE
flag indicating that the structure is connected
#define IS_MOVING
Item being moved.
@ SEGMENT
Definition eda_shape.h:46
@ FRAME_PCB_EDITOR
Definition frame_type.h:38
@ FRAME_FOOTPRINT_VIEWER
Definition frame_type.h:41
@ FRAME_FOOTPRINT_EDITOR
Definition frame_type.h:39
a few functions useful in geometry calculations.
double m_PcbSelectionVisibilityRatio
Board object selection visibility limit.
KIID niluuid(0)
@ LAYER_POINTS
PCB reference/manual snap points visibility.
Definition layer_ids.h:317
@ LAYER_FOOTPRINTS_FR
Show footprints on front.
Definition layer_ids.h:255
@ LAYER_DRAW_BITMAPS
Draw images.
Definition layer_ids.h:280
@ LAYER_FP_REFERENCES
Show footprints references (when texts are visible).
Definition layer_ids.h:262
@ LAYER_DRC_EXCLUSION
Layer for DRC markers which have been individually excluded.
Definition layer_ids.h:300
@ LAYER_ZONES
Control for copper zone opacity/visibility (color ignored).
Definition layer_ids.h:291
@ LAYER_PADS
Meta control for all pads opacity/visibility (color ignored).
Definition layer_ids.h:288
@ LAYER_TRACKS
Definition layer_ids.h:263
@ LAYER_FP_TEXT
Definition layer_ids.h:236
@ LAYER_FOOTPRINTS_BK
Show footprints on back.
Definition layer_ids.h:256
@ LAYER_FP_VALUES
Show footprints values (when texts are visible).
Definition layer_ids.h:259
@ LAYER_VIAS
Meta control for all vias opacity/visibility.
Definition layer_ids.h:228
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ Edge_Cuts
Definition layer_ids.h:108
@ F_SilkS
Definition layer_ids.h:96
@ B_CrtYd
Definition layer_ids.h:111
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ B_SilkS
Definition layer_ids.h:97
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:167
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition lset.cpp:750
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:79
MOUSE_DRAG_ACTION
bool BoxHitTest(const VECTOR2I &aHitPoint, const BOX2I &aHittee, int aAccuracy)
Perform a point-to-box hit test.
@ REPAINT
Item needs to be redrawn.
Definition view_item.h:54
@ TARGET_OVERLAY
Items that may change while the view stays the same (noncached)
Definition definitions.h:35
STL namespace.
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ PTH
Plated through hole pad.
Definition padstack.h:98
Class to handle a set of BOARD_ITEMs.
void connectedItemFilter(const VECTOR2I &, GENERAL_COLLECTOR &aCollector, PCB_SELECTION_TOOL *sTool)
static void passEvent(TOOL_EVENT *const aEvent, const TOOL_ACTION *const aAllowedActions[])
static bool itemIsIncludedByFilter(const BOARD_ITEM &aItem, const BOARD &aBoard, const DIALOG_FILTER_SELECTION::OPTIONS &aFilterOptions)
Determine if an item is included by the filter specified.
@ ID_REPLACE_TERMINAL_PAD_A
@ ID_REPLACE_TERMINAL_PAD_B
TRACK_DRAG_ACTION
CITER next(CITER it)
Definition ptree.cpp:120
Class that computes missing connections on a PCB.
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
static std::vector< KICAD_T > tableCellTypes
const TOOL_ACTION * allowedActions[]
static void passEvent(TOOL_EVENT *const aEvent, const TOOL_ACTION *const aAllowedActions[])
std::function< bool(const SELECTION &)> SELECTION_CONDITION
Functor type that checks a specific condition for selected items.
SELECTION_MODE
const int scale
Struct that will be set with the result of the user choices in the dialog.
const BOARD_ITEM * m_Item
This file contains data structures that are saved in the project file or project local settings file ...
bool otherItems
Anything not fitting one of the above categories.
bool graphics
Graphic lines, shapes, polygons.
bool footprints
Allow selecting entire footprints.
bool text
Text (free or attached to a footprint)
bool lockedItems
Allow selecting locked items.
KIBIS top(path, &reporter)
int radius
VECTOR2I end
const int accuracy
int delta
@ TA_MOUSE_UP
Definition tool_event.h:65
@ TA_MOUSE_WHEEL
Definition tool_event.h:69
std::optional< TOOL_EVENT > OPT_TOOL_EVENT
Definition tool_event.h:637
@ MD_ALT
Definition tool_event.h:141
@ MD_CTRL
Definition tool_event.h:140
@ MD_SHIFT
Definition tool_event.h:139
@ TC_ANY
Definition tool_event.h:56
@ BUT_MIDDLE
Definition tool_event.h:130
@ BUT_LEFT
Definition tool_event.h:128
@ BUT_RIGHT
Definition tool_event.h:129
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:71
@ PCB_CONSTRAINT_T
class PCB_CONSTRAINT, a geometric constraint between board items
Definition typeinfo.h:238
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:81
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:99
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:96
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:84
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:90
@ TYPE_NOT_INIT
Definition typeinfo.h:74
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:97
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:104
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:86
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:101
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:85
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:82
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:83
@ NOT_USED
the 3d code uses this value
Definition typeinfo.h:72
@ PCB_MARKER_T
class PCB_MARKER, a marker used to show something
Definition typeinfo.h:92
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:94
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition typeinfo.h:100
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:88
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:79
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:95
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:80
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:91
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:87
@ PCB_NETINFO_T
class NETINFO_ITEM, a description of a net
Definition typeinfo.h:103
@ PCB_POINT_T
class PCB_POINT, a 0-dimensional point
Definition typeinfo.h:106
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:89
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:98
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:56
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682
VECTOR2D ToVECTOR2D(const wxPoint &aPoint)
Definition vector2wx.h:36