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