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