KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_selection_tool.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2013-2017 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Tomasz Wlostowski <[email protected]>
7 * @author Maciej Suminski <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
27#include <limits>
28#include <cmath>
29#include <functional>
30#include <stack>
31using namespace std::placeholders;
32
33#include <advanced_config.h>
34#include <macros.h>
35#include <core/kicad_algo.h>
36#include <board.h>
38#include <board_item.h>
39#include <pcb_reference_image.h>
40#include <pcb_track.h>
41#include <footprint.h>
42#include <pad.h>
43#include <pcb_group.h>
44#include <pcb_shape.h>
45#include <pcb_text.h>
46#include <pcb_textbox.h>
47#include <pcb_table.h>
48#include <pcb_tablecell.h>
49#include <pcb_marker.h>
50#include <pcb_generator.h>
51#include <zone.h>
52#include <collectors.h>
56#include <view/view_controls.h>
57#include <gal/painter.h>
58#include <router/router_tool.h>
59#include <pcbnew_settings.h>
60#include <tool/tool_event.h>
61#include <tool/tool_manager.h>
65#include <tools/pcb_actions.h>
71#include <wx/event.h>
72#include <wx/timer.h>
73#include <wx/log.h>
74#include <wx/debug.h>
75#include <core/profile.h>
76#include <math/vector2wx.h>
77
78
80{
82 double m_Opacity;
84};
85
86
88{
89public:
91 ACTION_MENU( true )
92 {
93 SetTitle( _( "Select" ) );
94
96
97 AppendSeparator();
98
101
102 // This could be enabled if we have better logic for picking the target net with the mouse
103 // Add( PCB_ACTIONS::deselectNet );
106
109 }
110
111private:
112 ACTION_MENU* create() const override
113 {
114 return new SELECT_MENU();
115 }
116};
117
118
123{
124public:
126};
127
128
130 SELECTION_TOOL( "common.InteractiveSelection" ),
131 m_frame( nullptr ),
132 m_isFootprintEditor( false ),
133 m_nonModifiedCursor( KICURSOR::ARROW ),
134 m_enteredGroup( nullptr ),
135 m_selectionMode( SELECTION_MODE::INSIDE_RECTANGLE ),
136 m_priv( std::make_unique<PRIV>() )
137{
138 m_filter.lockedItems = false;
139 m_filter.footprints = true;
140 m_filter.text = true;
141 m_filter.tracks = true;
142 m_filter.vias = true;
143 m_filter.pads = true;
144 m_filter.graphics = true;
145 m_filter.zones = true;
146 m_filter.keepouts = true;
147 m_filter.dimensions = true;
148 m_filter.otherItems = true;
149}
150
151
153{
156
157 Disconnect( wxEVT_TIMER, wxTimerEventHandler( PCB_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
158}
159
160
162{
163 PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
164
166 {
168 return true;
169 }
170
171 std::shared_ptr<SELECT_MENU> selectMenu = std::make_shared<SELECT_MENU>();
172 selectMenu->SetTool( this );
173 m_menu->RegisterSubMenu( selectMenu );
174
175 static const std::vector<KICAD_T> tableCellTypes = { PCB_TABLECELL_T };
176
177 auto& menu = m_menu->GetMenu();
178
179 auto activeToolCondition =
180 [ frame ] ( const SELECTION& aSel )
181 {
182 return !frame->ToolStackIsEmpty();
183 };
184
185 auto haveHighlight =
186 [&]( const SELECTION& sel )
187 {
189
190 return !cfg->GetHighlightNetCodes().empty();
191 };
192
193 auto groupEnterCondition =
195
196 auto inGroupCondition =
197 [this] ( const SELECTION& )
198 {
199 return m_enteredGroup != nullptr;
200 };
201
202 auto tableCellSelection = SELECTION_CONDITIONS::MoreThan( 0 )
204
206 {
207 menu.AddMenu( selectMenu.get(), SELECTION_CONDITIONS::NotEmpty );
208 menu.AddSeparator( 1000 );
209 }
210
211 // "Cancel" goes at the top of the context menu when a tool is active
212 menu.AddItem( ACTIONS::cancelInteractive, activeToolCondition, 1 );
213 menu.AddItem( ACTIONS::groupEnter, groupEnterCondition, 1 );
214 menu.AddItem( ACTIONS::groupLeave, inGroupCondition, 1 );
215 menu.AddItem( PCB_ACTIONS::placeLinkedDesignBlock, groupEnterCondition, 1 );
216 menu.AddItem( PCB_ACTIONS::saveToLinkedDesignBlock, groupEnterCondition, 1 );
217 menu.AddItem( PCB_ACTIONS::clearHighlight, haveHighlight, 1 );
218 menu.AddSeparator( haveHighlight, 1 );
219
220 menu.AddItem( ACTIONS::selectColumns, tableCellSelection, 2 );
221 menu.AddItem( ACTIONS::selectRows, tableCellSelection, 2 );
222 menu.AddItem( ACTIONS::selectTable, tableCellSelection, 2 );
223
224 menu.AddSeparator( 1 );
225
226 if( frame )
228
229 m_disambiguateTimer.SetOwner( this );
230 Connect( m_disambiguateTimer.GetId(), wxEVT_TIMER,
231 wxTimerEventHandler( PCB_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
232
233 return true;
234}
235
236
238{
239 m_frame = getEditFrame<PCB_BASE_FRAME>();
241
242 if( aReason != TOOL_BASE::REDRAW )
243 {
244 if( m_enteredGroup )
245 ExitGroup();
246
247 // Deselect any item being currently in edit, to avoid unexpected behavior and remove
248 // pointers to the selected items from containers.
249 ClearSelection( true );
250 }
251
252 if( aReason == TOOL_BASE::MODEL_RELOAD )
253 getView()->GetPainter()->GetSettings()->SetHighlight( false );
254
255 // Reinsert the VIEW_GROUP, in case it was removed from the VIEW
256 view()->Remove( &m_selection );
257 view()->Add( &m_selection );
258
261}
262
263
264void PCB_SELECTION_TOOL::OnIdle( wxIdleEvent& aEvent )
265{
267 {
268 wxMouseState keyboardState = wxGetMouseState();
269
270 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
271 keyboardState.AltDown() );
272
273 if( m_additive )
274 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ADD );
275 else if( m_subtractive )
276 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SUBTRACT );
277 else if( m_exclusive_or )
278 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::XOR );
279 else
281 }
282}
283
284
286{
287 // Main loop: keep receiving events
288 while( TOOL_EVENT* evt = Wait() )
289 {
291 TRACK_DRAG_ACTION trackDragAction = TRACK_DRAG_ACTION::MOVE;
292
294 trackDragAction = cfg->m_TrackDragAction;
295
296 // on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
297 setModifiersState( evt->Modifier( MD_SHIFT ), evt->Modifier( MD_CTRL ),
298 evt->Modifier( MD_ALT ) );
299
300 PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
301 bool brd_editor = frame && frame->IsType( FRAME_PCB_EDITOR );
303
304 // If the router tool is active, don't override
305 if( router && router->IsToolActive() && router->RoutingInProgress() )
306 {
307 evt->SetPassEvent();
308 }
309 else if( evt->IsMouseDown( BUT_LEFT ) )
310 {
311 // Avoid triggering when running under other tools
313
314 if( m_frame->ToolStackIsEmpty() && pt_tool && !pt_tool->HasPoint() )
315 {
318 }
319 }
320 else if( evt->IsClick( BUT_LEFT ) )
321 {
322 // If there is no disambiguation, this routine is still running and will
323 // register a `click` event when released
324 if( m_disambiguateTimer.IsRunning() )
325 {
326 m_disambiguateTimer.Stop();
327
328 // Single click? Select single object
329 if( m_highlight_modifier && brd_editor )
330 {
332 }
333 else
334 {
336 selectPoint( evt->Position() );
337 }
338 }
339
340 m_canceledMenu = false;
341 }
342 else if( evt->IsClick( BUT_RIGHT ) )
343 {
344 m_disambiguateTimer.Stop();
345
346 // Right click? if there is any object - show the context menu
347 bool selectionCancelled = false;
348
349 if( m_selection.Empty() )
350 {
351 selectPoint( evt->Position(), false, &selectionCancelled );
352 m_selection.SetIsHover( true );
353 }
354
355 // Show selection before opening menu
357
358 if( !selectionCancelled )
359 {
361 m_menu->ShowContextMenu( m_selection );
362 }
363 }
364 else if( evt->IsDblClick( BUT_LEFT ) )
365 {
366 m_disambiguateTimer.Stop();
367
368 // Double clicks make no sense in the footprint viewer
370 {
371 evt->SetPassEvent();
372 continue;
373 }
374
375 // Double click? Display the properties window
377
378 if( m_selection.Empty() )
379 selectPoint( evt->Position() );
380
381 if( m_selection.GetSize() == 1 && m_selection[0]->Type() == PCB_GROUP_T )
382 EnterGroup();
383 else
385 }
386 else if( evt->IsDblClick( BUT_MIDDLE ) )
387 {
388 // Middle double click? Do zoom to fit or zoom to objects
389 if( evt->Modifier( MD_CTRL ) ) // Is CTRL key down?
391 else
393 }
394 else if( evt->Action() == TA_MOUSE_WHEEL )
395 {
396 int field = -1;
397
398 if( evt->Modifier() == ( MD_SHIFT | MD_ALT ) )
399 field = 0;
400 else if( evt->Modifier() == ( MD_CTRL | MD_ALT ) )
401 field = 1;
402 // any more?
403
404 if( field >= 0 )
405 {
406 const int delta = evt->Parameter<int>();
407 ACTIONS::INCREMENT incParams{
408 delta > 0 ? 1 : -1,
409 field
410 };
411
413 }
414 }
415 else if( evt->IsDrag( BUT_LEFT ) )
416 {
417 m_disambiguateTimer.Stop();
418
419 // Is another tool already moving a new object? Don't allow a drag start
420 if( !m_selection.Empty() && m_selection[0]->HasFlag( IS_NEW | IS_MOVING ) )
421 {
422 evt->SetPassEvent();
423 continue;
424 }
425
426 // Drag with LMB? Select multiple objects (or at least draw a selection box)
427 // or drag them
430
431 GENERAL_COLLECTOR hoverCells;
432
434 {
435 hoverCells.Collect( board()->GetFirstFootprint(), { PCB_TABLECELL_T }, evt->DragOrigin(),
437 }
438 else
439 {
440 hoverCells.Collect( board(), { PCB_TABLECELL_T }, evt->DragOrigin(), getCollectorsGuide() );
441 }
442
443 if( hoverCells.GetCount() )
444 {
446 {
447 selectTableCells( static_cast<PCB_TABLE*>( hoverCells[0]->GetParent() ) );
448 }
449 else
450 {
452 }
453 }
454 else if( ( hasModifier() || dragAction == MOUSE_DRAG_ACTION::SELECT )
455 || ( m_selection.Empty() && dragAction != MOUSE_DRAG_ACTION::DRAG_ANY ) )
456 {
457 if( m_selectionMode == SELECTION_MODE::INSIDE_RECTANGLE || m_selectionMode == SELECTION_MODE::TOUCHING_RECTANGLE )
458 SelectRectArea( aEvent );
459 else if( m_selectionMode == SELECTION_MODE::INSIDE_LASSO || m_selectionMode == SELECTION_MODE::TOUCHING_LASSO )
460 SelectPolyArea( aEvent );
461 else
462 {
463 wxASSERT_MSG( false, wxT( "Unknown selection mode" ) );
464 SelectRectArea( aEvent );
465 }
466 }
467 else
468 {
469 // Don't allow starting a drag from a zone filled area that isn't already selected
470 auto zoneFilledAreaFilter =
471 []( const VECTOR2I& aWhere, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* aTool )
472 {
473 int accuracy = aCollector.GetGuide()->Accuracy();
474 std::set<EDA_ITEM*> remove;
475
476 for( EDA_ITEM* item : aCollector )
477 {
478 if( item->Type() == PCB_ZONE_T )
479 {
480 ZONE* zone = static_cast<ZONE*>( item );
481
482 if( !zone->HitTestForCorner( aWhere, accuracy * 2 )
483 && !zone->HitTestForEdge( aWhere, accuracy ) )
484 {
485 remove.insert( zone );
486 }
487 }
488 }
489
490 for( EDA_ITEM* item : remove )
491 aCollector.Remove( item );
492 };
493
494 // See if we can drag before falling back to SelectRectArea()
495 bool doDrag = false;
496
497 if( evt->HasPosition() )
498 {
499 if( m_selection.Empty()
500 && selectPoint( evt->DragOrigin(), false, nullptr, zoneFilledAreaFilter ) )
501 {
502 m_selection.SetIsHover( true );
503 doDrag = true;
504 }
505 // Check if dragging has started within any of selected items bounding box.
506 else if( evt->HasPosition() && selectionContains( evt->DragOrigin() ) )
507 {
508 doDrag = true;
509 }
510 }
511
512 if( doDrag )
513 {
514 size_t segs = m_selection.CountType( PCB_TRACE_T );
515 size_t arcs = m_selection.CountType( PCB_ARC_T );
516 size_t vias = m_selection.CountType( PCB_VIA_T );
517 // Note: multi-track dragging is currently supported, but not multi-via
518 bool routable = ( segs >= 1 || arcs >= 1 || vias == 1 )
519 && ( segs + arcs + vias == m_selection.GetSize() );
520
521 if( routable && trackDragAction == TRACK_DRAG_ACTION::DRAG )
523 else if( routable && trackDragAction == TRACK_DRAG_ACTION::DRAG_FREE_ANGLE )
525 else
527 }
528 else
529 {
530 // Otherwise drag a selection box
531 SelectRectArea( aEvent );
532 }
533 }
534 }
535 else if( evt->IsCancel() )
536 {
537 m_disambiguateTimer.Stop();
539
540 if( !GetSelection().Empty() )
541 {
543 }
544 else if( evt->FirstResponder() == this && evt->GetCommandId() == (int) WXK_ESCAPE )
545 {
546 if( m_enteredGroup )
547 {
548 ExitGroup();
549 }
550 else
551 {
553
554 try
555 {
557 controller->ClearHighlight( *evt );
558 }
559 catch( const std::runtime_error& e )
560 {
561 wxCHECK_MSG( false, 0, e.what() );
562 }
563 }
564 }
565 }
566 else
567 {
568 evt->SetPassEvent();
569 }
570
571
573 {
574 // move cursor prediction
575 if( !hasModifier()
576 && dragAction == MOUSE_DRAG_ACTION::DRAG_SELECTED
577 && !m_selection.Empty()
578 && evt->HasPosition()
579 && selectionContains( evt->Position() ) )
580 {
581 m_nonModifiedCursor = KICURSOR::MOVING;
582 }
583 else
584 {
585 m_nonModifiedCursor = KICURSOR::ARROW;
586 }
587 }
588 }
589
590 // Shutting down; clear the selection
592 m_disambiguateTimer.Stop();
593
594 return 0;
595}
596
597
599{
600 wxCHECK_RET( m_selection.GetSize() == 1 && m_selection[0]->Type() == PCB_GROUP_T,
601 wxT( "EnterGroup called when selection is not a single group" ) );
602 PCB_GROUP* aGroup = static_cast<PCB_GROUP*>( m_selection[0] );
603
604 if( m_enteredGroup != nullptr )
605 ExitGroup();
606
608 m_enteredGroup = aGroup;
610
611 for( EDA_ITEM* member : m_enteredGroup->GetItems() )
612 select( member );
613
615
616 view()->Hide( m_enteredGroup, true );
619}
620
621
622void PCB_SELECTION_TOOL::ExitGroup( bool aSelectGroup )
623{
624 // Only continue if there is a group entered
625 if( m_enteredGroup == nullptr )
626 return;
627
629 view()->Hide( m_enteredGroup, false );
631
632 if( aSelectGroup )
633 {
636 }
637
639 m_enteredGroup = nullptr;
641}
642
643
645{
646 return m_selection;
647}
648
649
651 bool aConfirmLockedItems )
652{
653 bool selectionEmpty = m_selection.Empty();
654 m_selection.SetIsHover( selectionEmpty );
655
656 if( selectionEmpty )
657 {
660 }
661
662 if( aClientFilter )
663 {
664 enum DISPOSITION { BEFORE = 1, AFTER, BOTH };
665
666 std::map<EDA_ITEM*, DISPOSITION> itemDispositions;
668 GENERAL_COLLECTOR collector;
669
670 collector.SetGuide( &guide );
671
672 for( EDA_ITEM* item : m_selection )
673 {
674 collector.Append( item );
675 itemDispositions[ item ] = BEFORE;
676 }
677
678 aClientFilter( VECTOR2I(), collector, this );
679
680 for( EDA_ITEM* item : collector )
681 {
682 if( itemDispositions.count( item ) )
683 itemDispositions[ item ] = BOTH;
684 else
685 itemDispositions[ item ] = AFTER;
686 }
687
688 // Unhighlight the BEFORE items before highlighting the AFTER items.
689 // This is so that in the case of groups, if aClientFilter replaces a selection
690 // with the enclosing group, the unhighlight of the element doesn't undo the
691 // recursive highlighting of that element by the group.
692
693 for( std::pair<EDA_ITEM* const, DISPOSITION> itemDisposition : itemDispositions )
694 {
695 EDA_ITEM* item = itemDisposition.first;
696 DISPOSITION disposition = itemDisposition.second;
697
698 if( disposition == BEFORE )
700 }
701
702 for( std::pair<EDA_ITEM* const, DISPOSITION> itemDisposition : itemDispositions )
703 {
704 EDA_ITEM* item = itemDisposition.first;
705 DISPOSITION disposition = itemDisposition.second;
706
707 // Note that we must re-highlight even previously-highlighted items
708 // (ie: disposition BOTH) in case we removed any of their children.
709 if( disposition == AFTER || disposition == BOTH )
710 highlight( item, SELECTED, &m_selection );
711 }
712
714 }
715
716 if( aConfirmLockedItems )
717 {
718 std::vector<BOARD_ITEM*> lockedItems;
719
720 for( EDA_ITEM* item : m_selection )
721 {
722 if( !item->IsBOARD_ITEM() )
723 continue;
724
725 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
726 bool lockedDescendant = false;
727
728 boardItem->RunOnChildren(
729 [&]( BOARD_ITEM* curr_item )
730 {
731 if( curr_item->IsLocked() )
732 lockedDescendant = true;
733 },
734 RECURSE_MODE::RECURSE );
735
736 if( boardItem->IsLocked() || lockedDescendant )
737 lockedItems.push_back( boardItem );
738 }
739
741
742 if( !lockedItems.empty() )
743 {
744 DIALOG_LOCKED_ITEMS_QUERY dlg( frame(), lockedItems.size(), settings->m_LockingOptions );
745
746 switch( dlg.ShowModal() )
747 {
748 case wxID_OK:
749 // remove locked items from selection
750 for( BOARD_ITEM* item : lockedItems )
751 unselect( item );
752
753 break;
754
755 case wxID_CANCEL:
756 // cancel operation
758 break;
759
760 case wxID_APPLY:
761 // continue with operation with current selection
762 break;
763 }
764 }
765 }
766
767 return m_selection;
768}
769
770
772{
773 GENERAL_COLLECTORS_GUIDE guide( board()->GetVisibleLayers(),
774 (PCB_LAYER_ID) view()->GetTopLayer(), view() );
775
776 bool padsDisabled = !board()->IsElementVisible( LAYER_PADS );
777
778 // account for the globals
779 guide.SetIgnoreFPTextOnBack( !board()->IsElementVisible( LAYER_FP_TEXT ) );
780 guide.SetIgnoreFPTextOnFront( !board()->IsElementVisible( LAYER_FP_TEXT ) );
781 guide.SetIgnoreFootprintsOnBack( !board()->IsElementVisible( LAYER_FOOTPRINTS_BK ) );
782 guide.SetIgnoreFootprintsOnFront( !board()->IsElementVisible( LAYER_FOOTPRINTS_FR ) );
783 guide.SetIgnorePadsOnBack( padsDisabled );
784 guide.SetIgnorePadsOnFront( padsDisabled );
785 guide.SetIgnoreThroughHolePads( padsDisabled );
786 guide.SetIgnoreFPValues( !board()->IsElementVisible( LAYER_FP_VALUES ) );
787 guide.SetIgnoreFPReferences( !board()->IsElementVisible( LAYER_FP_REFERENCES ) );
788 guide.SetIgnoreThroughVias( ! board()->IsElementVisible( LAYER_VIAS ) );
789 guide.SetIgnoreBlindBuriedVias( ! board()->IsElementVisible( LAYER_VIAS ) );
790 guide.SetIgnoreMicroVias( ! board()->IsElementVisible( LAYER_VIAS ) );
791 guide.SetIgnoreTracks( ! board()->IsElementVisible( LAYER_TRACKS ) );
792
793 return guide;
794}
795
796
798{
800}
801
802
803bool PCB_SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag,
804 bool* aSelectionCancelledFlag,
805 CLIENT_SELECTION_FILTER aClientFilter )
806{
808 GENERAL_COLLECTOR collector;
809 const PCB_DISPLAY_OPTIONS& displayOpts = m_frame->GetDisplayOptions();
810
811 guide.SetIgnoreZoneFills( displayOpts.m_ZoneDisplayMode != ZONE_DISPLAY_MODE::SHOW_FILLED );
812
814 ExitGroup();
815
818 aWhere, guide );
819
820 // Remove unselectable items
821 for( int i = collector.GetCount() - 1; i >= 0; --i )
822 {
823 if( !Selectable( collector[ i ] ) || ( aOnDrag && collector[i]->IsLocked() ) )
824 collector.Remove( i );
825 }
826
828
829 // Apply the stateful filter (remove items disabled by the Selection Filter)
830 FilterCollectedItems( collector, false );
831
832 // Allow the client to do tool- or action-specific filtering to see if we can get down
833 // to a single item
834 if( aClientFilter )
835 aClientFilter( aWhere, collector, this );
836
837 FilterCollectorForHierarchy( collector, false );
838
839 FilterCollectorForFootprints( collector, aWhere );
840
841 // For subtracting, we only want items that are selected
842 if( m_subtractive )
843 {
844 for( int i = collector.GetCount() - 1; i >= 0; --i )
845 {
846 if( !collector[i]->IsSelected() )
847 collector.Remove( i );
848 }
849 }
850
851 // Apply some ugly heuristics to avoid disambiguation menus whenever possible
852 if( collector.GetCount() > 1 && !m_skip_heuristics )
853 {
854 try
855 {
856 GuessSelectionCandidates( collector, aWhere );
857 }
858 catch( const std::exception& exc )
859 {
860 wxLogWarning( wxS( "Exception \"%s\" occurred attempting to guess selection "
861 "candidates." ), exc.what() );
862 return false;
863 }
864 }
865
866 // If still more than one item we're going to have to ask the user.
867 if( collector.GetCount() > 1 )
868 {
869 if( aOnDrag )
871
872 if( !doSelectionMenu( &collector ) )
873 {
874 if( aSelectionCancelledFlag )
875 *aSelectionCancelledFlag = true;
876
877 return false;
878 }
879 }
880
881 int addedCount = 0;
882 bool anySubtracted = false;
883
885 {
886 if( m_selection.GetSize() > 0 )
887 {
888 ClearSelection( true /*quiet mode*/ );
889 anySubtracted = true;
890 }
891 }
892
893 if( collector.GetCount() > 0 )
894 {
895 for( int i = 0; i < collector.GetCount(); ++i )
896 {
897 if( m_subtractive || ( m_exclusive_or && collector[i]->IsSelected() ) )
898 {
899 unselect( collector[i] );
900 anySubtracted = true;
901 }
902 else
903 {
904 select( collector[i] );
905 addedCount++;
906 }
907 }
908 }
909
910 if( addedCount == 1 )
911 {
913 return true;
914 }
915 else if( addedCount > 1 )
916 {
918 return true;
919 }
920 else if( anySubtracted )
921 {
923 return true;
924 }
925
926 return false;
927}
928
929
930bool PCB_SELECTION_TOOL::selectCursor( bool aForceSelect, CLIENT_SELECTION_FILTER aClientFilter )
931{
932 if( aForceSelect || m_selection.Empty() )
933 {
934 ClearSelection( true /*quiet mode*/ );
935 selectPoint( getViewControls()->GetCursorPosition( false ), false, nullptr, aClientFilter );
936 }
937
938 return !m_selection.Empty();
939}
940
941
942// Some navigation actions are allowed in selectMultiple
952 &ACTIONS::zoomFitObjects, nullptr };
953
954
955static void passEvent( TOOL_EVENT* const aEvent, const TOOL_ACTION* const aAllowedActions[] )
956{
957 for( int i = 0; aAllowedActions[i]; ++i )
958 {
959 if( aEvent->IsAction( aAllowedActions[i] ) )
960 {
961 aEvent->SetPassEvent();
962 break;
963 }
964 }
965}
966
967
969{
970 bool cancelled = false; // Was the tool canceled while it was running?
971 m_multiple = true; // Multiple selection mode is active
972
973 for( PCB_TABLECELL* cell : aTable->GetCells() )
974 {
975 if( cell->IsSelected() )
976 cell->SetFlags( CANDIDATE );
977 else
978 cell->ClearFlags( CANDIDATE );
979 }
980
981 auto wasSelected =
982 []( EDA_ITEM* aItem )
983 {
984 return ( aItem->GetFlags() & CANDIDATE ) > 0;
985 };
986
987 while( TOOL_EVENT* evt = Wait() )
988 {
989 if( evt->IsCancelInteractive() || evt->IsActivate() )
990 {
991 cancelled = true;
992 break;
993 }
994 else if( evt->IsDrag( BUT_LEFT ) )
995 {
996 getViewControls()->SetAutoPan( true );
997
998 BOX2I selectionRect( evt->DragOrigin(), evt->Position() - evt->DragOrigin() );
999 selectionRect.Normalize();
1000
1001 for( PCB_TABLECELL* cell : aTable->GetCells() )
1002 {
1003 bool doSelect = false;
1004
1005 if( cell->HitTest( selectionRect, false ) )
1006 {
1007 if( m_subtractive )
1008 doSelect = false;
1009 else if( m_exclusive_or )
1010 doSelect = !wasSelected( cell );
1011 else
1012 doSelect = true;
1013 }
1014 else if( wasSelected( cell ) )
1015 {
1016 doSelect = m_additive || m_subtractive || m_exclusive_or;
1017 }
1018
1019 if( doSelect && !cell->IsSelected() )
1020 select( cell );
1021 else if( !doSelect && cell->IsSelected() )
1022 unselect( cell );
1023 }
1024 }
1025 else if( evt->IsMouseUp( BUT_LEFT ) )
1026 {
1027 m_selection.SetIsHover( false );
1028
1029 bool anyAdded = false;
1030 bool anySubtracted = false;
1031
1032 for( PCB_TABLECELL* cell : aTable->GetCells() )
1033 {
1034 if( cell->IsSelected() && !wasSelected( cell ) )
1035 anyAdded = true;
1036 else if( wasSelected( cell ) && !cell->IsSelected() )
1037 anySubtracted = true;
1038 }
1039
1040 // Inform other potentially interested tools
1041 if( anyAdded )
1043
1044 if( anySubtracted )
1046
1047 break; // Stop waiting for events
1048 }
1049 else
1050 {
1051 // Allow some actions for navigation
1052 passEvent( evt, allowedActions );
1053 }
1054 }
1055
1056 getViewControls()->SetAutoPan( false );
1057
1058 m_multiple = false; // Multiple selection mode is inactive
1059
1060 if( !cancelled )
1062
1063 return cancelled;
1064}
1065
1066
1068{
1069 bool cancelled = false; // Was the tool canceled while it was running?
1070 m_multiple = true; // Multiple selection mode is active
1072
1074 view->Add( &area );
1075
1076 while( TOOL_EVENT* evt = Wait() )
1077 {
1078 /* Selection mode depends on direction of drag-selection:
1079 * Left > Right : Select objects that are fully enclosed by selection
1080 * Right > Left : Select objects that are crossed by selection
1081 */
1082 bool greedySelection = area.GetEnd().x < area.GetOrigin().x;
1083
1084 if( view->IsMirroredX() )
1085 greedySelection = !greedySelection;
1086
1087 m_frame->GetCanvas()->SetCurrentCursor( !greedySelection ? KICURSOR::SELECT_WINDOW
1088 : KICURSOR::SELECT_LASSO );
1089
1090 if( evt->IsCancelInteractive() || evt->IsActivate() )
1091 {
1092 cancelled = true;
1093 break;
1094 }
1095
1096 if( evt->IsDrag( BUT_LEFT ) )
1097 {
1099 {
1100 if( m_selection.GetSize() > 0 )
1101 {
1102 ClearSelection( true /*quiet mode*/ );
1104 }
1105 }
1106
1107 // Start drawing a selection box
1108 area.SetOrigin( evt->DragOrigin() );
1109 area.SetEnd( evt->Position() );
1112 area.SetExclusiveOr( false );
1113 area.SetMode( greedySelection
1114 ? SELECTION_MODE::TOUCHING_RECTANGLE
1115 : SELECTION_MODE::INSIDE_RECTANGLE );
1116
1117 view->SetVisible( &area, true );
1118 view->Update( &area );
1119 getViewControls()->SetAutoPan( true );
1120 }
1121
1122 if( evt->IsMouseUp( BUT_LEFT ) )
1123 {
1124 getViewControls()->SetAutoPan( false );
1125
1126 // End drawing the selection box
1127 view->SetVisible( &area, false );
1128
1130
1131 break; // Stop waiting for events
1132 }
1133
1134 // Allow some actions for navigation
1135 passEvent( evt, allowedActions );
1136 }
1137
1138 getViewControls()->SetAutoPan( false );
1139
1140 // Stop drawing the selection box
1141 view->Remove( &area );
1142 m_multiple = false; // Multiple selection mode is inactive
1143
1144 if( !cancelled )
1146
1148
1149 return cancelled;
1150}
1151
1152
1154{
1155 m_selectionMode = SELECTION_MODE::INSIDE_LASSO;
1156 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
1158 return 0; // No need to wait for an event, just set the mode
1159}
1160
1161
1163{
1164 m_selectionMode = SELECTION_MODE::INSIDE_RECTANGLE;
1165 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1167 return 0; // No need to wait for an event, just set the mode
1168}
1169
1170
1172{
1173 bool cancelled = false; // Was the tool canceled while it was running?
1174
1175 SELECTION_MODE selectionMode = SELECTION_MODE::TOUCHING_LASSO;
1176 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
1177
1178
1179 SHAPE_LINE_CHAIN points;
1180 points.SetClosed( true );
1181
1183 getView()->Add( &area );
1184 getView()->SetVisible( &area, true );
1185 getViewControls()->SetAutoPan( true );
1186
1187 while( TOOL_EVENT* evt = Wait() )
1188 {
1189 // Auto mode: clockwise = inside, counterclockwise = touching
1190 double shapeArea = area.GetPoly().Area( false );
1191 bool isClockwise = shapeArea > 0 ? true : false;
1192
1193 if( getView()->IsMirroredX() && shapeArea != 0 )
1194 isClockwise = !isClockwise;
1195
1196 selectionMode = isClockwise ? SELECTION_MODE::INSIDE_LASSO
1197 : SELECTION_MODE::TOUCHING_LASSO;
1198
1199 if( evt->IsCancelInteractive() || evt->IsActivate() )
1200 {
1201 cancelled = true;
1202 evt->SetPassEvent( false );
1203 break;
1204 }
1205 else if( evt->IsDrag( BUT_LEFT ) || evt->IsClick( BUT_LEFT )
1206 || evt->IsAction( &ACTIONS::cursorClick ) )
1207 {
1208 points.Append( evt->Position() );
1209 }
1210 else if( evt->IsDblClick( BUT_LEFT ) || evt->IsAction( &ACTIONS::cursorDblClick )
1211 || evt->IsAction( &ACTIONS::finishInteractive ) )
1212 {
1213 area.GetPoly().GenerateBBoxCache();
1215 evt->SetPassEvent( false );
1216 break;
1217 }
1218 else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint )
1219 || evt->IsAction( &ACTIONS::doDelete )
1220 || evt->IsAction( &ACTIONS::undo ) )
1221 {
1222 if( points.GetPointCount() > 0 )
1223 {
1225 points.Remove( points.GetPointCount() - 1 );
1226 }
1227 }
1228 else
1229 {
1230 // Allow navigation actions
1231 passEvent( evt, allowedActions );
1232 }
1233
1234 if( points.PointCount() > 0 )
1235 {
1237 {
1238 if( m_selection.GetSize() > 0 )
1239 {
1240 ClearSelection( true /*quiet mode*/ );
1242 }
1243 }
1244 }
1245
1246 area.SetPoly( points );
1248 area.SetAdditive( m_additive );
1250 area.SetExclusiveOr( false );
1251 area.SetMode( selectionMode );
1252 getView()->Update( &area );
1253 }
1254
1255 getViewControls()->SetAutoPan( false );
1256 getView()->SetVisible( &area, false );
1257 getView()->Remove( &area );
1258 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1259
1260 if( !cancelled )
1262
1264
1265 return cancelled;
1266}
1267
1268
1270 bool aExclusiveOr )
1271{
1273
1274 bool anyAdded = false;
1275 bool anySubtracted = false;
1276
1277 SELECTION_MODE selectionMode = aArea.GetMode();
1278 bool containedMode = ( selectionMode == SELECTION_MODE::INSIDE_RECTANGLE
1279 || selectionMode == SELECTION_MODE::INSIDE_LASSO ) ? true : false;
1280 bool boxMode = ( selectionMode == SELECTION_MODE::INSIDE_RECTANGLE
1281 || selectionMode == SELECTION_MODE::TOUCHING_RECTANGLE ) ? true : false;
1282
1283 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> candidates;
1284 BOX2I selectionBox = aArea.ViewBBox();
1285 view->Query( selectionBox, candidates ); // Get the list of nearby items
1286
1287 GENERAL_COLLECTOR collector;
1288 GENERAL_COLLECTOR padsCollector;
1289 std::set<EDA_ITEM*> group_items;
1290
1291 for( PCB_GROUP* group : board()->Groups() )
1292 {
1293 // The currently entered group does not get limited
1294 if( m_enteredGroup == group )
1295 continue;
1296
1297 std::unordered_set<EDA_ITEM*>& newset = group->GetItems();
1298
1299 auto boxContained =
1300 [&]( const BOX2I& aBox )
1301 {
1302 return boxMode ? selectionBox.Contains( aBox )
1303 : KIGEOM::BoxHitTest( aArea.GetPoly(), aBox, true );
1304 };
1305
1306 // If we are not greedy and have selected the whole group, add just one item
1307 // to allow it to be promoted to the group later
1308 if( containedMode && boxContained( group->GetBoundingBox() ) && newset.size() )
1309 {
1310 for( EDA_ITEM* group_item : newset )
1311 {
1312 if( !group_item->IsBOARD_ITEM() )
1313 continue;
1314
1315 if( Selectable( static_cast<BOARD_ITEM*>( group_item ) ) )
1316 collector.Append( *newset.begin() );
1317 }
1318 }
1319
1320 for( EDA_ITEM* group_item : newset )
1321 group_items.emplace( group_item );
1322 }
1323
1324 auto hitTest =
1325 [&]( const EDA_ITEM* aItem )
1326 {
1327 return boxMode ? aItem->HitTest( selectionBox, containedMode )
1328 : aItem->HitTest( aArea.GetPoly(), containedMode );
1329 };
1330
1331 for( const auto& [item, layer] : candidates )
1332 {
1333 if( !item->IsBOARD_ITEM() )
1334 continue;
1335
1336 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
1337
1338 if( Selectable( boardItem ) && hitTest( boardItem )
1339 && ( !containedMode || !group_items.count( boardItem ) ) )
1340 {
1341 if( boardItem->Type() == PCB_PAD_T && !m_isFootprintEditor )
1342 padsCollector.Append( boardItem );
1343 else
1344 collector.Append( boardItem );
1345 }
1346 }
1347
1348 // Apply the stateful filter
1349 FilterCollectedItems( collector, true );
1350
1351 FilterCollectorForHierarchy( collector, true );
1352
1353 // If we selected nothing but pads, allow them to be selected
1354 if( collector.GetCount() == 0 )
1355 {
1356 collector = padsCollector;
1357 FilterCollectedItems( collector, true );
1358 FilterCollectorForHierarchy( collector, true );
1359 }
1360
1361 // Sort the filtered selection by rows and columns to have a nice default
1362 // for tools that can use it.
1363 std::sort( collector.begin(), collector.end(),
1364 []( EDA_ITEM* a, EDA_ITEM* b )
1365 {
1366 VECTOR2I aPos = a->GetPosition();
1367 VECTOR2I bPos = b->GetPosition();
1368
1369 if( aPos.y == bPos.y )
1370 return aPos.x < bPos.x;
1371
1372 return aPos.y < bPos.y;
1373 } );
1374
1375 for( EDA_ITEM* i : collector )
1376 {
1377 if( !i->IsBOARD_ITEM() )
1378 continue;
1379
1380 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
1381
1382 if( aSubtractive || ( aExclusiveOr && item->IsSelected() ) )
1383 {
1384 unselect( item );
1385 anySubtracted = true;
1386 }
1387 else
1388 {
1389 select( item );
1390 anyAdded = true;
1391 }
1392 }
1393
1394 m_selection.SetIsHover( false );
1395
1396 // Inform other potentially interested tools
1397 if( anyAdded )
1399 else if( anySubtracted )
1401}
1402
1403
1405{
1406 wxMouseState keyboardState = wxGetMouseState();
1407
1408 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
1409 keyboardState.AltDown() );
1410
1411 m_skip_heuristics = true;
1413 m_skip_heuristics = false;
1414
1415 return 0;
1416}
1417
1418
1419
1421{
1423
1424 selectCursor( false, aClientFilter );
1425
1426 return 0;
1427}
1428
1429
1431{
1433
1434 return 0;
1435}
1436
1437
1439{
1440 GENERAL_COLLECTOR collection;
1441 BOX2I selectionBox;
1442
1443 selectionBox.SetMaximum();
1444
1445 getView()->Query( selectionBox,
1446 [&]( KIGFX::VIEW_ITEM* viewItem ) -> bool
1447 {
1448 if( viewItem->IsBOARD_ITEM() )
1449 {
1450 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( viewItem );
1451
1452 if( item && Selectable( item ) && itemPassesFilter( item, true ) )
1453 collection.Append( item );
1454 }
1455
1456 return true;
1457 } );
1458
1459 FilterCollectorForHierarchy( collection, true );
1460
1461 for( EDA_ITEM* item : collection )
1462 select( item );
1463
1465
1467
1468 return 0;
1469}
1470
1471
1473{
1474 BOX2I selectionBox;
1475
1476 selectionBox.SetMaximum();
1477
1478 getView()->Query( selectionBox,
1479 [&]( KIGFX::VIEW_ITEM* viewItem ) -> bool
1480 {
1481 if( viewItem->IsBOARD_ITEM() )
1482 {
1483 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( viewItem );
1484
1485 if( item && Selectable( item ) )
1486 unselect( item );
1487 }
1488
1489 return true;
1490 } );
1491
1493
1495
1496 return 0;
1497}
1498
1499
1501 PCB_SELECTION_TOOL* sTool )
1502{
1503 // Narrow the collection down to a single BOARD_CONNECTED_ITEM for each represented net.
1504 // All other items types are removed.
1505 std::set<int> representedNets;
1506
1507 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
1508 {
1509 BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( aCollector[i] );
1510
1511 if( !item )
1512 aCollector.Remove( i );
1513 else if ( representedNets.count( item->GetNetCode() ) )
1514 aCollector.Remove( i );
1515 else
1516 representedNets.insert( item->GetNetCode() );
1517 }
1518}
1519
1520
1522{
1523 std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
1524
1525 // Get all footprints and pads
1526 std::vector<BOARD_CONNECTED_ITEM*> toUnroute;
1527
1528 for( EDA_ITEM* item : selectedItems )
1529 {
1530 if( item->Type() == PCB_FOOTPRINT_T )
1531 {
1532 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
1533 toUnroute.push_back( pad );
1534 }
1535 else if( BOARD_CONNECTED_ITEM::ClassOf( item ) )
1536 {
1537 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
1538 }
1539 }
1540
1541 // Clear selection so we don't delete our footprints/pads
1542 ClearSelection( true );
1543
1544 // Get the tracks on our list of pads, then delete them
1547
1548 // Reselect our footprint/pads as they were in our original selection
1549 for( EDA_ITEM* item : selectedItems )
1550 {
1551 if( item->Type() == PCB_FOOTPRINT_T || item->Type() == PCB_PAD_T )
1552 select( item );
1553 }
1554
1555 return 0;
1556}
1557
1558
1560{
1561 std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
1562
1563 // Get all footprints and pads
1564 std::vector<BOARD_CONNECTED_ITEM*> toUnroute;
1565
1566 for( EDA_ITEM* item : selectedItems )
1567 {
1568 if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T || item->Type() == PCB_VIA_T )
1569 {
1570 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
1571 }
1572 }
1573
1574 // Get the tracks connecting to our starting objects
1577 std::deque<EDA_ITEM*> toSelectAfter;
1578 // This will select the unroute items too, so filter them out
1580 {
1581 if( !item->IsBOARD_ITEM() )
1582 continue;
1583
1584 if( std::find( toUnroute.begin(), toUnroute.end(), item ) == toUnroute.end() )
1585 toSelectAfter.push_back( item );
1586 }
1587
1588 ClearSelection( true );
1589
1590 for( EDA_ITEM* item : toUnroute )
1591 select( item );
1592
1594
1595 // Now our after tracks so the user can continue backing up as desired
1596 ClearSelection( true );
1597
1598 for( EDA_ITEM* item : toSelectAfter )
1599 select( item );
1600
1601 return 0;
1602}
1603
1604
1606{
1607 // expandConnection will get called no matter whether the user selected a connected item or a
1608 // non-connected shape (graphic on a non-copper layer). The algorithm for expanding to connected
1609 // items is different from graphics, so they need to be handled separately.
1610 unsigned initialCount = 0;
1611
1612 for( const EDA_ITEM* item : m_selection.GetItems() )
1613 {
1614 if( item->Type() == PCB_FOOTPRINT_T
1615 || item->Type() == PCB_GENERATOR_T
1616 || ( static_cast<const BOARD_ITEM*>( item )->IsConnected() ) )
1617 {
1618 initialCount++;
1619 }
1620 }
1621
1622 if( initialCount == 0 )
1623 {
1624 // First, process any graphic shapes we have
1625 std::vector<PCB_SHAPE*> startShapes;
1626
1627 for( EDA_ITEM* item : m_selection.GetItems() )
1628 {
1629 if( isExpandableGraphicShape( item ) )
1630 startShapes.push_back( static_cast<PCB_SHAPE*>( item ) );
1631 }
1632
1633 // If no non-copper shapes; fall back to looking for connected items
1634 if( !startShapes.empty() )
1635 selectAllConnectedShapes( startShapes );
1636 else
1638 }
1639
1640 m_frame->SetStatusText( _( "Select/Expand Connection..." ) );
1641
1642 for( STOP_CONDITION stopCondition : { STOP_AT_JUNCTION, STOP_AT_PAD, STOP_NEVER } )
1643 {
1644 std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
1645
1646 for( EDA_ITEM* item : selectedItems )
1647 item->ClearTempFlags();
1648
1649 std::vector<BOARD_CONNECTED_ITEM*> startItems;
1650
1651 for( EDA_ITEM* item : selectedItems )
1652 {
1653 if( item->Type() == PCB_FOOTPRINT_T )
1654 {
1655 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
1656
1657 for( PAD* pad : footprint->Pads() )
1658 startItems.push_back( pad );
1659 }
1660 else if( item->Type() == PCB_GENERATOR_T )
1661 {
1662 for( BOARD_ITEM* generatedItem : static_cast<PCB_GENERATOR*>( item )->GetBoardItems() )
1663 {
1664 if( BOARD_CONNECTED_ITEM::ClassOf( generatedItem ) )
1665 startItems.push_back( static_cast<BOARD_CONNECTED_ITEM*>( generatedItem ) );
1666 }
1667 }
1668 else if( BOARD_CONNECTED_ITEM::ClassOf( item ) )
1669 {
1670 startItems.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
1671 }
1672 }
1673
1674 selectAllConnectedTracks( startItems, stopCondition );
1675
1676 if( m_selection.GetItems().size() > initialCount )
1677 break;
1678 }
1679
1680 m_frame->SetStatusText( wxEmptyString );
1681
1682 // Inform other potentially interested tools
1684
1685 return 0;
1686}
1687
1688
1689void PCB_SELECTION_TOOL::selectAllConnectedTracks( const std::vector<BOARD_CONNECTED_ITEM*>& aStartItems,
1690 STOP_CONDITION aStopCondition )
1691{
1692 PROF_TIMER refreshTimer;
1693 double refreshIntervalMs = 500; // Refresh display with this interval to indicate progress
1694 int lastSelectionSize = (int) m_selection.GetSize();
1695
1696 auto connectivity = board()->GetConnectivity();
1697
1698 std::set<PAD*> startPadSet;
1699 std::vector<BOARD_CONNECTED_ITEM*> cleanupItems;
1700
1701 for( BOARD_CONNECTED_ITEM* startItem : aStartItems )
1702 {
1703 // Register starting pads
1704 if( startItem->Type() == PCB_PAD_T )
1705 startPadSet.insert( static_cast<PAD*>( startItem ) );
1706
1707 // Select any starting track items
1708 if( startItem->IsType( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } ) )
1709 select( startItem );
1710 }
1711
1712 for( BOARD_CONNECTED_ITEM* startItem : aStartItems )
1713 {
1714 std::map<VECTOR2I, std::vector<PCB_TRACK*>> trackMap;
1715 std::map<VECTOR2I, PCB_VIA*> viaMap;
1716 std::map<VECTOR2I, PAD*> padMap;
1717 std::map<VECTOR2I, std::vector<PCB_SHAPE*>> shapeMap;
1718 std::vector<std::pair<VECTOR2I, LSET>> activePts;
1719
1720 if( startItem->HasFlag( SKIP_STRUCT ) ) // Skip already visited items
1721 continue;
1722
1723 auto connectedItems = connectivity->GetConnectedItems( startItem,
1725
1726 // Build maps of connected items
1727 for( BOARD_CONNECTED_ITEM* item : connectedItems )
1728 {
1729 switch( item->Type() )
1730 {
1731 case PCB_ARC_T:
1732 case PCB_TRACE_T:
1733 {
1734 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
1735 trackMap[track->GetStart()].push_back( track );
1736 trackMap[track->GetEnd()].push_back( track );
1737 break;
1738 }
1739
1740 case PCB_VIA_T:
1741 {
1742 PCB_VIA* via = static_cast<PCB_VIA*>( item );
1743 viaMap[via->GetStart()] = via;
1744 break;
1745 }
1746
1747 case PCB_PAD_T:
1748 {
1749 PAD* pad = static_cast<PAD*>( item );
1750 padMap[pad->GetPosition()] = pad;
1751 break;
1752 }
1753
1754 case PCB_SHAPE_T:
1755 {
1756 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
1757
1758 for( const auto& point : shape->GetConnectionPoints() )
1759 shapeMap[point].push_back( shape );
1760
1761 break;
1762 }
1763
1764 default:
1765 break;
1766 }
1767 }
1768
1769 // Set up the initial active points
1770 switch( startItem->Type() )
1771 {
1772 case PCB_ARC_T:
1773 case PCB_TRACE_T:
1774 {
1775 PCB_TRACK* track = static_cast<PCB_TRACK*>( startItem );
1776
1777 activePts.push_back( { track->GetStart(), track->GetLayerSet() } );
1778 activePts.push_back( { track->GetEnd(), track->GetLayerSet() } );
1779 break;
1780 }
1781
1782 case PCB_VIA_T:
1783 activePts.push_back( { startItem->GetPosition(), startItem->GetLayerSet() } );
1784 break;
1785
1786 case PCB_PAD_T:
1787 activePts.push_back( { startItem->GetPosition(), startItem->GetLayerSet() } );
1788 break;
1789
1790 case PCB_SHAPE_T:
1791 {
1792 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( startItem );
1793
1794 for( const auto& point : shape->GetConnectionPoints() )
1795 activePts.push_back( { point, startItem->GetLayerSet() } );
1796
1797 break;
1798 }
1799
1800 default:
1801 break;
1802 }
1803
1804 bool expand = true;
1805 int failSafe = 0;
1806
1807 // Iterative push from all active points
1808 while( expand && failSafe++ < 100000 )
1809 {
1810 expand = false;
1811
1812 for( int i = (int) activePts.size() - 1; i >= 0; --i )
1813 {
1814 VECTOR2I pt = activePts[i].first;
1815 LSET layerSetCu = activePts[i].second & LSET::AllCuMask();
1816
1817 auto viaIt = viaMap.find( pt );
1818 auto padIt = padMap.find( pt );
1819
1820 bool gotVia = ( viaIt != viaMap.end() )
1821 && ( layerSetCu & ( viaIt->second->GetLayerSet() ) ).any();
1822
1823 bool gotPad = ( padIt != padMap.end() )
1824 && ( layerSetCu & ( padIt->second->GetLayerSet() ) ).any();
1825
1826 bool gotNonStartPad =
1827 gotPad && ( startPadSet.find( padIt->second ) == startPadSet.end() );
1828
1829 if( aStopCondition == STOP_AT_JUNCTION )
1830 {
1831 size_t pt_count = 0;
1832
1833 for( PCB_TRACK* track : trackMap[pt] )
1834 {
1835 if( track->GetStart() != track->GetEnd()
1836 && layerSetCu.Contains( track->GetLayer() ) )
1837 {
1838 pt_count++;
1839 }
1840 }
1841
1842 if( pt_count > 2 || gotVia || gotNonStartPad )
1843 {
1844 activePts.erase( activePts.begin() + i );
1845 continue;
1846 }
1847 }
1848 else if( aStopCondition == STOP_AT_PAD )
1849 {
1850 if( gotNonStartPad )
1851 {
1852 activePts.erase( activePts.begin() + i );
1853 continue;
1854 }
1855 }
1856
1857 if( gotPad )
1858 {
1859 PAD* pad = padIt->second;
1860
1861 if( !pad->HasFlag( SKIP_STRUCT ) )
1862 {
1863 pad->SetFlags( SKIP_STRUCT );
1864 cleanupItems.push_back( pad );
1865
1866 activePts.push_back( { pad->GetPosition(), pad->GetLayerSet() } );
1867 expand = true;
1868 }
1869 }
1870
1871 for( PCB_TRACK* track : trackMap[pt] )
1872 {
1873 if( !layerSetCu.Contains( track->GetLayer() ) )
1874 continue;
1875
1876 if( !track->IsSelected() )
1877 select( track );
1878
1879 if( !track->HasFlag( SKIP_STRUCT ) )
1880 {
1881 track->SetFlags( SKIP_STRUCT );
1882 cleanupItems.push_back( track );
1883
1884 if( track->GetStart() == pt )
1885 activePts.push_back( { track->GetEnd(), track->GetLayerSet() } );
1886 else
1887 activePts.push_back( { track->GetStart(), track->GetLayerSet() } );
1888
1889 if( aStopCondition != STOP_AT_SEGMENT )
1890 expand = true;
1891 }
1892 }
1893
1894 for( PCB_SHAPE* shape : shapeMap[pt] )
1895 {
1896 if( !layerSetCu.Contains( shape->GetLayer() ) )
1897 continue;
1898
1899 if( !shape->IsSelected() )
1900 select( shape );
1901
1902 if( !shape->HasFlag( SKIP_STRUCT ) )
1903 {
1904 shape->SetFlags( SKIP_STRUCT );
1905 cleanupItems.push_back( shape );
1906
1907 for( const VECTOR2I& newPoint : shape->GetConnectionPoints() )
1908 {
1909 if( newPoint == pt )
1910 continue;
1911
1912 activePts.push_back( { newPoint, shape->GetLayerSet() } );
1913 }
1914
1915 if( aStopCondition != STOP_AT_SEGMENT )
1916 expand = true;
1917 }
1918 }
1919
1920 if( viaMap.count( pt ) )
1921 {
1922 PCB_VIA* via = viaMap[pt];
1923
1924 if( !via->IsSelected() )
1925 select( via );
1926
1927 if( !via->HasFlag( SKIP_STRUCT ) )
1928 {
1929 via->SetFlags( SKIP_STRUCT );
1930 cleanupItems.push_back( via );
1931
1932 activePts.push_back( { via->GetPosition(), via->GetLayerSet() } );
1933
1934 if( aStopCondition != STOP_AT_SEGMENT )
1935 expand = true;
1936 }
1937 }
1938
1939 activePts.erase( activePts.begin() + i );
1940 }
1941
1942 // Refresh display for the feel of progress
1943 if( refreshTimer.msecs() >= refreshIntervalMs )
1944 {
1945 if( m_selection.Size() != lastSelectionSize )
1946 {
1948 lastSelectionSize = m_selection.Size();
1949 }
1950
1951 refreshTimer.Start();
1952 }
1953 }
1954 }
1955
1956 std::set<EDA_ITEM*> toDeselect;
1957 std::set<EDA_ITEM*> toSelect;
1958
1959 // Promote generated members to their PCB_GENERATOR parents
1960 for( EDA_ITEM* item : m_selection )
1961 {
1962 if( !item->IsBOARD_ITEM() )
1963 continue;
1964
1965 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
1966 EDA_GROUP* parent = boardItem->GetParentGroup();
1967
1968 if( parent && parent->AsEdaItem()->Type() == PCB_GENERATOR_T )
1969 {
1970 toDeselect.insert( item );
1971
1972 if( !parent->AsEdaItem()->IsSelected() )
1973 toSelect.insert( parent->AsEdaItem() );
1974 }
1975 }
1976
1977 for( EDA_ITEM* item : toDeselect )
1978 unselect( item );
1979
1980 for( EDA_ITEM* item : toSelect )
1981 select( item );
1982
1983 for( BOARD_CONNECTED_ITEM* item : cleanupItems )
1984 item->ClearFlags( SKIP_STRUCT );
1985}
1986
1987
1989{
1990 if( aItem->Type() == PCB_SHAPE_T )
1991 {
1992 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
1993
1994 switch( shape->GetShape() )
1995 {
1996 case SHAPE_T::SEGMENT:
1997 case SHAPE_T::ARC:
1998 case SHAPE_T::BEZIER:
1999 return !shape->IsOnCopperLayer();
2000
2001 case SHAPE_T::POLY:
2002 return !shape->IsOnCopperLayer() && !shape->IsClosed();
2003
2004 default:
2005 return false;
2006 }
2007 }
2008
2009 return false;
2010}
2011
2012
2013void PCB_SELECTION_TOOL::selectAllConnectedShapes( const std::vector<PCB_SHAPE*>& aStartItems )
2014{
2015 std::stack<PCB_SHAPE*> toSearch;
2016 std::set<PCB_SHAPE*> toCleanup;
2017
2018 for( PCB_SHAPE* startItem : aStartItems )
2019 toSearch.push( startItem );
2020
2021 GENERAL_COLLECTOR collector;
2023
2024 auto searchPoint = [&]( const VECTOR2I& aWhere )
2025 {
2026 collector.Collect( board(), { PCB_SHAPE_T }, aWhere, guide );
2027
2028 for( EDA_ITEM* item : collector )
2029 {
2030 if( isExpandableGraphicShape( item ) )
2031 toSearch.push( static_cast<PCB_SHAPE*>( item ) );
2032 }
2033 };
2034
2035 while( !toSearch.empty() )
2036 {
2037 PCB_SHAPE* shape = toSearch.top();
2038 toSearch.pop();
2039
2040 if( shape->HasFlag( SKIP_STRUCT ) )
2041 continue;
2042
2043 select( shape );
2044 shape->SetFlags( SKIP_STRUCT );
2045 toCleanup.insert( shape );
2046
2047 guide.SetLayerVisibleBits( shape->GetLayerSet() );
2048
2049 searchPoint( shape->GetStart() );
2050 searchPoint( shape->GetEnd() );
2051 }
2052
2053 for( PCB_SHAPE* shape : toCleanup )
2054 shape->ClearFlags( SKIP_STRUCT );
2055}
2056
2057
2059{
2060 // Get all pads
2061 std::vector<PAD*> pads;
2062
2063 for( EDA_ITEM* item : m_selection.GetItems() )
2064 {
2065 if( item->Type() == PCB_FOOTPRINT_T )
2066 {
2067 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
2068 pads.push_back( pad );
2069 }
2070 else if( item->Type() == PCB_PAD_T )
2071 {
2072 pads.push_back( static_cast<PAD*>( item ) );
2073 }
2074 }
2075
2076 // Select every footprint on the end of the ratsnest for each pad in our selection
2077 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
2078
2079 for( PAD* pad : pads )
2080 {
2081 for( const CN_EDGE& edge : conn->GetRatsnestForPad( pad ) )
2082 {
2083 wxCHECK2( edge.GetSourceNode() && !edge.GetSourceNode()->Dirty(), continue );
2084 wxCHECK2( edge.GetTargetNode() && !edge.GetTargetNode()->Dirty(), continue );
2085
2086 BOARD_CONNECTED_ITEM* sourceParent = edge.GetSourceNode()->Parent();
2087 BOARD_CONNECTED_ITEM* targetParent = edge.GetTargetNode()->Parent();
2088
2089 if( sourceParent == pad )
2090 {
2091 if( targetParent->Type() == PCB_PAD_T )
2092 select( static_cast<PAD*>( targetParent )->GetParent() );
2093 }
2094 else if( targetParent == pad )
2095 {
2096 if( sourceParent->Type() == PCB_PAD_T )
2097 select( static_cast<PAD*>( sourceParent )->GetParent() );
2098 }
2099 }
2100 }
2101
2102 return 0;
2103}
2104
2105
2107{
2108 PCB_SELECTION originalSelection = m_selection;
2109
2110 // Get all pads
2111 std::vector<PAD*> pads;
2112
2113 for( EDA_ITEM* item : m_selection.GetItems() )
2114 {
2115 if( item->Type() == PCB_FOOTPRINT_T )
2116 {
2117 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
2118 pads.push_back( pad );
2119 }
2120 else if( item->Type() == PCB_PAD_T )
2121 {
2122 pads.push_back( static_cast<PAD*>( item ) );
2123 }
2124 }
2125
2127
2128 // Select every footprint on the end of the ratsnest for each pad in our selection
2129 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
2130
2131 for( PAD* pad : pads )
2132 {
2133 const std::vector<CN_EDGE> edges = conn->GetRatsnestForPad( pad );
2134
2135 // Need to have something unconnected to grab
2136 if( edges.size() == 0 )
2137 continue;
2138
2139 double currentDistance = DBL_MAX;
2140 FOOTPRINT* nearest = nullptr;
2141
2142 // Check every ratsnest line for the nearest one
2143 for( const CN_EDGE& edge : edges )
2144 {
2145 if( edge.GetSourceNode()->Parent()->GetParentFootprint()
2146 == edge.GetTargetNode()->Parent()->GetParentFootprint() )
2147 {
2148 continue; // This edge is a loop on the same footprint
2149 }
2150
2151 // Figure out if we are the source or the target node on the ratnest
2152 const CN_ANCHOR* other = edge.GetSourceNode()->Parent() == pad ? edge.GetTargetNode().get()
2153 : edge.GetSourceNode().get();
2154
2155 wxCHECK2( other && !other->Dirty(), continue );
2156
2157 // We only want to grab footprints, so the ratnest has to point to a pad
2158 if( other->Parent()->Type() != PCB_PAD_T )
2159 continue;
2160
2161 if( edge.GetLength() < currentDistance )
2162 {
2163 currentDistance = edge.GetLength();
2164 nearest = other->Parent()->GetParentFootprint();
2165 }
2166 }
2167
2168 if( nearest != nullptr )
2169 select( nearest );
2170 }
2171
2173
2174 return 0;
2175}
2176
2177
2178void PCB_SELECTION_TOOL::SelectAllItemsOnNet( int aNetCode, bool aSelect )
2179{
2180 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
2181
2182 for( BOARD_ITEM* item : conn->GetNetItems( aNetCode, { PCB_TRACE_T,
2183 PCB_ARC_T,
2184 PCB_VIA_T,
2185 PCB_SHAPE_T } ) )
2186 {
2187 if( itemPassesFilter( item, true ) )
2188 aSelect ? select( item ) : unselect( item );
2189 }
2190}
2191
2192
2194{
2195 bool select = aEvent.IsAction( &PCB_ACTIONS::selectNet );
2196
2197 // If we've been passed an argument, just select that netcode1
2198 int netcode = aEvent.Parameter<int>();
2199
2200 if( netcode > 0 )
2201 {
2202 SelectAllItemsOnNet( netcode, select );
2203
2204 // Inform other potentially interested tools
2205 if( m_selection.Size() > 0 )
2207 else
2209
2210 return 0;
2211 }
2212
2213 if( !selectCursor() )
2214 return 0;
2215
2216 // copy the selection, since we're going to iterate and modify
2218
2219 for( EDA_ITEM* i : selection )
2220 {
2221 BOARD_CONNECTED_ITEM* connItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( i );
2222
2223 if( connItem )
2224 SelectAllItemsOnNet( connItem->GetNetCode(), select );
2225 }
2226
2227 // Inform other potentially interested tools
2228 if( m_selection.Size() > 0 )
2230 else
2232
2233 return 0;
2234}
2235
2236
2238{
2239 std::vector<BOARD_ITEM*> footprints;
2240
2241 // store all footprints that are on that sheet path
2242 for( FOOTPRINT* footprint : board()->Footprints() )
2243 {
2244 if( footprint == nullptr )
2245 continue;
2246
2247 wxString footprint_path = footprint->GetPath().AsString().BeforeLast( '/' );
2248
2249 if( footprint_path.IsEmpty() )
2250 footprint_path += '/';
2251
2252 if( footprint_path == aSheetPath )
2253 footprints.push_back( footprint );
2254 }
2255
2256 for( BOARD_ITEM* i : footprints )
2257 {
2258 if( i != nullptr )
2259 select( i );
2260 }
2261
2262 selectConnections( footprints );
2263}
2264
2265
2266void PCB_SELECTION_TOOL::selectConnections( const std::vector<BOARD_ITEM*>& aItems )
2267{
2268 // Generate a list of all pads, and of all nets they belong to.
2269 std::list<int> netcodeList;
2270 std::vector<BOARD_CONNECTED_ITEM*> padList;
2271
2272 for( BOARD_ITEM* item : aItems )
2273 {
2274 switch( item->Type() )
2275 {
2276 case PCB_FOOTPRINT_T:
2277 {
2278 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
2279 {
2280 if( pad->IsConnected() )
2281 {
2282 netcodeList.push_back( pad->GetNetCode() );
2283 padList.push_back( pad );
2284 }
2285 }
2286
2287 break;
2288 }
2289
2290 case PCB_PAD_T:
2291 {
2292 PAD* pad = static_cast<PAD*>( item );
2293
2294 if( pad->IsConnected() )
2295 {
2296 netcodeList.push_back( pad->GetNetCode() );
2297 padList.push_back( pad );
2298 }
2299
2300 break;
2301 }
2302
2303 default:
2304 break;
2305 }
2306 }
2307
2308 // Sort for binary search
2309 std::sort( padList.begin(), padList.end() );
2310
2311 // remove all duplicates
2312 netcodeList.sort();
2313 netcodeList.unique();
2314
2316
2317 // now we need to find all footprints that are connected to each of these nets then we need
2318 // to determine if these footprints are in the list of footprints
2319 std::vector<int> removeCodeList;
2320 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
2321
2322 for( int netCode : netcodeList )
2323 {
2324 for( BOARD_CONNECTED_ITEM* pad : conn->GetNetItems( netCode, { PCB_PAD_T } ) )
2325 {
2326 if( !std::binary_search( padList.begin(), padList.end(), pad ) )
2327 {
2328 // if we cannot find the pad in the padList then we can assume that that pad
2329 // should not be used, therefore invalidate this netcode.
2330 removeCodeList.push_back( netCode );
2331 break;
2332 }
2333 }
2334 }
2335
2336 for( int removeCode : removeCodeList )
2337 netcodeList.remove( removeCode );
2338
2339 std::unordered_set<BOARD_ITEM*> localConnectionList;
2340
2341 for( int netCode : netcodeList )
2342 {
2343 for( BOARD_ITEM* item : conn->GetNetItems( netCode, { PCB_TRACE_T,
2344 PCB_ARC_T,
2345 PCB_VIA_T,
2346 PCB_SHAPE_T } ) )
2347 {
2348 localConnectionList.insert( item );
2349 }
2350 }
2351
2352 for( BOARD_ITEM* item : localConnectionList )
2353 select( item );
2354}
2355
2356
2358{
2359 std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
2360
2361 if( items )
2362 doSyncSelection( *items, false );
2363
2364 return 0;
2365}
2366
2367
2369{
2370 std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
2371
2372 if( items )
2373 doSyncSelection( *items, true );
2374
2375 return 0;
2376}
2377
2378
2379void PCB_SELECTION_TOOL::doSyncSelection( const std::vector<BOARD_ITEM*>& aItems, bool aWithNets )
2380{
2382 return;
2383
2384 ClearSelection( true /*quiet mode*/ );
2385
2386 // Perform individual selection of each item before processing the event.
2387 for( BOARD_ITEM* item : aItems )
2388 select( item );
2389
2390 if( aWithNets )
2391 selectConnections( aItems );
2392
2394
2395 if( bbox.GetWidth() != 0 && bbox.GetHeight() != 0 )
2396 {
2398 {
2400 ZoomFitCrossProbeBBox( bbox );
2401
2402 m_frame->FocusOnLocation( bbox.Centre() );
2403 }
2404 }
2405
2407
2409
2410 if( m_selection.Size() > 0 )
2412}
2413
2414
2416{
2417 ClearSelection( true /*quiet mode*/ );
2418 wxString sheetPath = *aEvent.Parameter<wxString*>();
2419
2420 selectAllItemsOnSheet( sheetPath );
2421
2423
2424 if( m_selection.Size() > 0 )
2426
2427 return 0;
2428}
2429
2430
2432{
2433 // this function currently only supports footprints since they are only on one sheet.
2434 EDA_ITEM* item = m_selection.Front();
2435
2436 if( !item )
2437 return 0;
2438
2439 if( item->Type() != PCB_FOOTPRINT_T )
2440 return 0;
2441
2442 FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( item );
2443
2444 if( !footprint || footprint->GetPath().empty() )
2445 return 0;
2446
2447 ClearSelection( true /*quiet mode*/ );
2448
2449 // get the sheet path only.
2450 wxString sheetPath = footprint->GetPath().AsString().BeforeLast( '/' );
2451
2452 if( sheetPath.IsEmpty() )
2453 sheetPath += '/';
2454
2455 selectAllItemsOnSheet( sheetPath );
2456
2457 // Inform other potentially interested tools
2458 if( m_selection.Size() > 0 )
2460
2461 return 0;
2462}
2463
2464
2466{
2467 // Should recalculate the view to zoom in on the selection.
2468 BOX2I selectionBox = m_selection.GetBoundingBox();
2470
2471 VECTOR2D screenSize = view->ToWorld( ToVECTOR2D( m_frame->GetCanvas()->GetClientSize() ),
2472 false );
2473 screenSize.x = std::max( 10.0, screenSize.x );
2474 screenSize.y = std::max( 10.0, screenSize.y );
2475
2476 if( selectionBox.GetWidth() != 0 || selectionBox.GetHeight() != 0 )
2477 {
2478 VECTOR2D vsize = selectionBox.GetSize();
2479 double scale = view->GetScale() / std::max( fabs( vsize.x / screenSize.x ),
2480 fabs( vsize.y / screenSize.y ) );
2481 view->SetScale( scale );
2482 view->SetCenter( selectionBox.Centre() );
2483 view->Add( &m_selection );
2484 }
2485
2487}
2488
2489
2491{
2492 // Should recalculate the view to zoom in on the bbox.
2494
2495 if( aBBox.GetWidth() == 0 )
2496 return;
2497
2498 BOX2I bbox = aBBox;
2499 bbox.Normalize();
2500
2501 //#define DEFAULT_PCBNEW_CODE // Un-comment for normal full zoom KiCad algorithm
2502#ifdef DEFAULT_PCBNEW_CODE
2503 auto bbSize = bbox.Inflate( bbox.GetWidth() * 0.2f ).GetSize();
2504 auto screenSize = view->ToWorld( GetCanvas()->GetClientSize(), false );
2505
2506 // The "fabs" on x ensures the right answer when the view is flipped
2507 screenSize.x = std::max( 10.0, fabs( screenSize.x ) );
2508 screenSize.y = std::max( 10.0, screenSize.y );
2509 double ratio = std::max( fabs( bbSize.x / screenSize.x ), fabs( bbSize.y / screenSize.y ) );
2510
2511 // Try not to zoom on every cross-probe; it gets very noisy
2512 if( crossProbingSettings.zoom_to_fit && ( ratio < 0.5 || ratio > 1.0 ) )
2513 view->SetScale( view->GetScale() / ratio );
2514#endif // DEFAULT_PCBNEW_CODE
2515
2516#ifndef DEFAULT_PCBNEW_CODE // Do the scaled zoom
2517 auto bbSize = bbox.Inflate( KiROUND( bbox.GetWidth() * 0.2 ) ).GetSize();
2518 VECTOR2D screenSize = view->ToWorld( ToVECTOR2D( m_frame->GetCanvas()->GetClientSize() ),
2519 false );
2520
2521 // This code tries to come up with a zoom factor that doesn't simply zoom in
2522 // to the cross probed component, but instead shows a reasonable amount of the
2523 // circuit around it to provide context. This reduces or eliminates the need
2524 // to manually change the zoom because it's too close.
2525
2526 // Using the default text height as a constant to compare against, use the
2527 // height of the bounding box of visible items for a footprint to figure out
2528 // if this is a big footprint (like a processor) or a small footprint (like a resistor).
2529 // This ratio is not useful by itself as a scaling factor. It must be "bent" to
2530 // provide good scaling at varying component sizes. Bigger components need less
2531 // scaling than small ones.
2532 double currTextHeight = pcbIUScale.mmToIU( DEFAULT_TEXT_SIZE );
2533
2534 double compRatio = bbSize.y / currTextHeight; // Ratio of component to text height
2535
2536 // This will end up as the scaling factor we apply to "ratio".
2537 double compRatioBent = 1.0;
2538
2539 // This is similar to the original KiCad code that scaled the zoom to make sure
2540 // components were visible on screen. It's simply a ratio of screen size to
2541 // component size, and its job is to zoom in to make the component fullscreen.
2542 // Earlier in the code the component BBox is given a 20% margin to add some
2543 // breathing room. We compare the height of this enlarged component bbox to the
2544 // default text height. If a component will end up with the sides clipped, we
2545 // adjust later to make sure it fits on screen.
2546 //
2547 // The "fabs" on x ensures the right answer when the view is flipped
2548 screenSize.x = std::max( 10.0, fabs( screenSize.x ) );
2549 screenSize.y = std::max( 10.0, screenSize.y );
2550 double ratio = std::max( -1.0, fabs( bbSize.y / screenSize.y ) );
2551
2552 // Original KiCad code for how much to scale the zoom
2553 double kicadRatio = std::max( fabs( bbSize.x / screenSize.x ),
2554 fabs( bbSize.y / screenSize.y ) );
2555
2556 // LUT to scale zoom ratio to provide reasonable schematic context. Must work
2557 // with footprints of varying sizes (e.g. 0402 package and 200 pin BGA).
2558 // "first" is used as the input and "second" as the output
2559 //
2560 // "first" = compRatio (footprint height / default text height)
2561 // "second" = Amount to scale ratio by
2562 std::vector<std::pair<double, double>> lut {
2563 { 1, 8 },
2564 { 1.5, 5 },
2565 { 3, 3 },
2566 { 4.5, 2.5 },
2567 { 8, 2.0 },
2568 { 12, 1.7 },
2569 { 16, 1.5 },
2570 { 24, 1.3 },
2571 { 32, 1.0 },
2572 };
2573
2574
2575 std::vector<std::pair<double, double>>::iterator it;
2576
2577 compRatioBent = lut.back().second; // Large component default
2578
2579 if( compRatio >= lut.front().first )
2580 {
2581 // Use LUT to do linear interpolation of "compRatio" within "first", then
2582 // use that result to linearly interpolate "second" which gives the scaling
2583 // factor needed.
2584
2585 for( it = lut.begin(); it < lut.end() - 1; it++ )
2586 {
2587 if( it->first <= compRatio && next( it )->first >= compRatio )
2588 {
2589 double diffx = compRatio - it->first;
2590 double diffn = next( it )->first - it->first;
2591
2592 compRatioBent = it->second + ( next( it )->second - it->second ) * diffx / diffn;
2593 break; // We have our interpolated value
2594 }
2595 }
2596 }
2597 else
2598 {
2599 compRatioBent = lut.front().second; // Small component default
2600 }
2601
2602 // If the width of the part we're probing is bigger than what the screen width will be
2603 // after the zoom, then punt and use the KiCad zoom algorithm since it guarantees the
2604 // part's width will be encompassed within the screen. This will apply to parts that
2605 // are much wider than they are tall.
2606
2607 if( bbSize.x > screenSize.x * ratio * compRatioBent )
2608 {
2609 // Use standard KiCad zoom algorithm for parts too wide to fit screen/
2610 ratio = kicadRatio;
2611 compRatioBent = 1.0; // Reset so we don't modify the "KiCad" ratio
2612 wxLogTrace( "CROSS_PROBE_SCALE",
2613 "Part TOO WIDE for screen. Using normal KiCad zoom ratio: %1.5f", ratio );
2614 }
2615
2616 // Now that "compRatioBent" holds our final scaling factor we apply it to the original
2617 // fullscreen zoom ratio to arrive at the final ratio itself.
2618 ratio *= compRatioBent;
2619
2620 bool alwaysZoom = false; // DEBUG - allows us to minimize zooming or not
2621
2622 // Try not to zoom on every cross-probe; it gets very noisy
2623 if( ( ratio < 0.5 || ratio > 1.0 ) || alwaysZoom )
2624 view->SetScale( view->GetScale() / ratio );
2625#endif // ifndef DEFAULT_PCBNEW_CODE
2626}
2627
2628
2630{
2631 bool cleared = false;
2632
2633 if( m_selection.GetSize() > 0 )
2634 {
2635 // Don't fire an event now; most of the time it will be redundant as we're about to
2636 // fire a SelectedEvent.
2637 cleared = true;
2638 ClearSelection( true /*quiet mode*/ );
2639 }
2640
2641 if( aItem )
2642 {
2643 switch( aItem->Type() )
2644 {
2645 case PCB_NETINFO_T:
2646 {
2647 int netCode = static_cast<NETINFO_ITEM*>( aItem )->GetNetCode();
2648
2649 if( netCode > 0 )
2650 {
2651 SelectAllItemsOnNet( netCode, true );
2652 m_frame->FocusOnLocation( aItem->GetCenter() );
2653 }
2654 break;
2655 }
2656
2657 default:
2658 select( aItem );
2659 m_frame->FocusOnLocation( aItem->GetPosition() );
2660 }
2661
2662 // If the item has a bounding box, then zoom out if needed
2663 if( aItem->GetBoundingBox().GetHeight() > 0 && aItem->GetBoundingBox().GetWidth() > 0 )
2664 {
2665 // This adds some margin
2666 double marginFactor = 2;
2667
2668 KIGFX::PCB_VIEW* pcbView = canvas()->GetView();
2669 BOX2D screenBox = pcbView->GetViewport();
2670 VECTOR2D screenSize = screenBox.GetSize();
2671 BOX2I screenRect = BOX2ISafe( screenBox.GetOrigin(), screenSize / marginFactor );
2672
2673 if( !screenRect.Contains( aItem->GetBoundingBox() ) )
2674 {
2675 double scaleX = screenSize.x /
2676 static_cast<double>( aItem->GetBoundingBox().GetWidth() );
2677 double scaleY = screenSize.y /
2678 static_cast<double>( aItem->GetBoundingBox().GetHeight() );
2679
2680 scaleX /= marginFactor;
2681 scaleY /= marginFactor;
2682
2683 double scale = scaleX > scaleY ? scaleY : scaleX;
2684
2685 if( scale < 1 ) // Don't zoom in, only zoom out
2686 {
2687 pcbView->SetScale( pcbView->GetScale() * ( scale ) );
2688
2689 //Let's refocus because there is an algorithm to avoid dialogs in there.
2690 m_frame->FocusOnLocation( aItem->GetCenter() );
2691 }
2692 }
2693 }
2694 // Inform other potentially interested tools
2696 }
2697 else if( cleared )
2698 {
2700 }
2701
2703}
2704
2705
2711static bool itemIsIncludedByFilter( const BOARD_ITEM& aItem, const BOARD& aBoard,
2712 const DIALOG_FILTER_SELECTION::OPTIONS& aFilterOptions )
2713{
2714 switch( aItem.Type() )
2715 {
2716 case PCB_FOOTPRINT_T:
2717 {
2718 const FOOTPRINT& footprint = static_cast<const FOOTPRINT&>( aItem );
2719
2720 return aFilterOptions.includeModules
2721 && ( aFilterOptions.includeLockedModules || !footprint.IsLocked() );
2722 }
2723
2724 case PCB_TRACE_T:
2725 case PCB_ARC_T:
2726 return aFilterOptions.includeTracks;
2727
2728 case PCB_VIA_T:
2729 return aFilterOptions.includeVias;
2730
2731 case PCB_ZONE_T:
2732 return aFilterOptions.includeZones;
2733
2734 case PCB_SHAPE_T:
2735 case PCB_TARGET_T:
2736 case PCB_DIM_ALIGNED_T:
2737 case PCB_DIM_CENTER_T:
2738 case PCB_DIM_RADIAL_T:
2740 case PCB_DIM_LEADER_T:
2741 if( aItem.GetLayer() == Edge_Cuts )
2742 return aFilterOptions.includeBoardOutlineLayer;
2743 else
2744 return aFilterOptions.includeItemsOnTechLayers;
2745
2746 case PCB_FIELD_T:
2747 case PCB_TEXT_T:
2748 case PCB_TEXTBOX_T:
2749 case PCB_TABLE_T:
2750 case PCB_TABLECELL_T:
2751 return aFilterOptions.includePcbTexts;
2752
2753 default:
2754 // Filter dialog is inclusive, not exclusive. If it's not included, then it doesn't
2755 // get selected.
2756 return false;
2757 }
2758}
2759
2760
2762{
2763 const BOARD& board = *getModel<BOARD>();
2764 DIALOG_FILTER_SELECTION::OPTIONS& opts = m_priv->m_filterOpts;
2765 DIALOG_FILTER_SELECTION dlg( m_frame, opts );
2766
2767 const int cmd = dlg.ShowModal();
2768
2769 if( cmd != wxID_OK )
2770 return 0;
2771
2772 // copy current selection
2773 std::deque<EDA_ITEM*> selection = m_selection.GetItems();
2774
2775 ClearSelection( true /*quiet mode*/ );
2776
2777 // re-select items from the saved selection according to the dialog options
2778 for( EDA_ITEM* i : selection )
2779 {
2780 if( !i->IsBOARD_ITEM() )
2781 continue;
2782
2783 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
2784 bool include = itemIsIncludedByFilter( *item, board, opts );
2785
2786 if( include )
2787 select( item );
2788 }
2789
2791
2792 return 0;
2793}
2794
2795
2797{
2798 if( aCollector.GetCount() == 0 )
2799 return;
2800
2801 std::set<BOARD_ITEM*> rejected;
2802
2803 for( EDA_ITEM* i : aCollector )
2804 {
2805 if( !i->IsBOARD_ITEM() )
2806 continue;
2807
2808 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
2809
2810 if( !itemPassesFilter( item, aMultiSelect ) )
2811 rejected.insert( item );
2812 }
2813
2814 for( BOARD_ITEM* item : rejected )
2815 aCollector.Remove( item );
2816}
2817
2818
2819bool PCB_SELECTION_TOOL::itemPassesFilter( BOARD_ITEM* aItem, bool aMultiSelect )
2820{
2821 if( !m_filter.lockedItems )
2822 {
2823 if( aItem->IsLocked() || ( aItem->GetParent() && aItem->GetParent()->IsLocked() ) )
2824 {
2825 if( aItem->Type() == PCB_PAD_T && !aMultiSelect )
2826 {
2827 // allow a single pad to be selected -- there are a lot of operations that
2828 // require this so we allow this one inconsistency
2829 }
2830 else
2831 {
2832 return false;
2833 }
2834 }
2835 }
2836
2837 if( !aItem )
2838 return false;
2839
2840 KICAD_T itemType = aItem->Type();
2841
2842 if( itemType == PCB_GENERATOR_T )
2843 {
2844 if( static_cast<PCB_GENERATOR*>( aItem )->GetItems().empty() )
2845 {
2846 if( !m_filter.otherItems )
2847 return false;
2848 }
2849 else
2850 {
2851 itemType = ( *static_cast<PCB_GENERATOR*>( aItem )->GetItems().begin() )->Type();
2852 }
2853 }
2854
2855 switch( itemType )
2856 {
2857 case PCB_FOOTPRINT_T:
2858 if( !m_filter.footprints )
2859 return false;
2860
2861 break;
2862
2863 case PCB_PAD_T:
2864 if( !m_filter.pads )
2865 return false;
2866
2867 break;
2868
2869 case PCB_TRACE_T:
2870 case PCB_ARC_T:
2871 if( !m_filter.tracks )
2872 return false;
2873
2874 break;
2875
2876 case PCB_VIA_T:
2877 if( !m_filter.vias )
2878 return false;
2879
2880 break;
2881
2882 case PCB_ZONE_T:
2883 {
2884 ZONE* zone = static_cast<ZONE*>( aItem );
2885
2886 if( ( !m_filter.zones && !zone->GetIsRuleArea() )
2887 || ( !m_filter.keepouts && zone->GetIsRuleArea() ) )
2888 {
2889 return false;
2890 }
2891
2892 // m_SolderMaskBridges zone is a special zone, only used to showsolder mask briges
2893 // after running DRC. it is not really a board item.
2894 // Never select it or delete by a Commit.
2895 if( zone == m_frame->GetBoard()->m_SolderMaskBridges )
2896 return false;
2897
2898 break;
2899 }
2900
2901 case PCB_SHAPE_T:
2902 case PCB_TARGET_T:
2903 if( !m_filter.graphics )
2904 return false;
2905
2906 break;
2907
2909 if( !m_filter.graphics )
2910 return false;
2911
2912 // a reference image living in a footprint must not be selected inside the board editor
2913 if( !m_isFootprintEditor && aItem->GetParentFootprint() )
2914 return false;
2915
2916 break;
2917
2918 case PCB_FIELD_T:
2919 case PCB_TEXT_T:
2920 case PCB_TEXTBOX_T:
2921 case PCB_TABLE_T:
2922 case PCB_TABLECELL_T:
2923 if( !m_filter.text )
2924 return false;
2925
2926 break;
2927
2928 case PCB_DIM_ALIGNED_T:
2929 case PCB_DIM_CENTER_T:
2930 case PCB_DIM_RADIAL_T:
2932 case PCB_DIM_LEADER_T:
2933 if( !m_filter.dimensions )
2934 return false;
2935
2936 break;
2937
2938 default:
2939 if( !m_filter.otherItems )
2940 return false;
2941 }
2942
2943 return true;
2944}
2945
2946
2948{
2949 if( m_selection.Empty() )
2950 return;
2951
2952 while( m_selection.GetSize() )
2954
2955 view()->Update( &m_selection );
2956
2957 m_selection.SetIsHover( false );
2959
2960 // Inform other potentially interested tools
2961 if( !aQuietMode )
2962 {
2965 }
2966}
2967
2968
2970{
2972
2973 bool enteredGroupFound = false;
2974
2975 INSPECTOR_FUNC inspector =
2976 [&]( EDA_ITEM* item, void* testData )
2977 {
2978 if( item->IsSelected() )
2979 {
2980 EDA_ITEM* parent = item->GetParent();
2981
2982 // Let selected parents handle their children.
2983 if( parent && parent->IsSelected() )
2984 return INSPECT_RESULT::CONTINUE;
2985
2986 highlight( item, SELECTED, &m_selection );
2987 }
2988
2989 if( item->Type() == PCB_GROUP_T )
2990 {
2991 if( item == m_enteredGroup )
2992 {
2993 item->SetFlags( ENTERED );
2994 enteredGroupFound = true;
2995 }
2996 else
2997 {
2998 item->ClearFlags( ENTERED );
2999 }
3000 }
3001
3002 return INSPECT_RESULT::CONTINUE;
3003 };
3004
3007
3008 if( !enteredGroupFound )
3009 {
3011 m_enteredGroup = nullptr;
3012 }
3013}
3014
3015
3016bool PCB_SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibilityOnly ) const
3017{
3018 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
3019 const PCB_DISPLAY_OPTIONS& options = frame()->GetDisplayOptions();
3020
3021 auto visibleLayers =
3022 [&]() -> LSET
3023 {
3025 {
3026 LSET set;
3027
3028 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
3029 set.set( layer, view()->IsLayerVisible( layer ) );
3030
3031 return set;
3032 }
3033 else
3034 {
3035 return board()->GetVisibleLayers();
3036 }
3037 };
3038
3039 auto layerVisible =
3040 [&]( PCB_LAYER_ID aLayer )
3041 {
3043 return view()->IsLayerVisible( aLayer );
3044 else
3045 return board()->IsLayerVisible( aLayer );
3046 };
3047
3048 if( settings->GetHighContrast() )
3049 {
3050 const std::set<int> activeLayers = settings->GetHighContrastLayers();
3051 bool onActiveLayer = false;
3052
3053 for( int layer : activeLayers )
3054 {
3055 // NOTE: Only checking the regular layers (not GAL meta-layers)
3056 if( layer < PCB_LAYER_ID_COUNT && aItem->IsOnLayer( ToLAYER_ID( layer ) ) )
3057 {
3058 onActiveLayer = true;
3059 break;
3060 }
3061 }
3062
3063 if( !onActiveLayer && aItem->Type() != PCB_MARKER_T )
3064 {
3065 // We do not want to select items that are in the background
3066 return false;
3067 }
3068 }
3069
3070 if( aItem->Type() == PCB_FOOTPRINT_T )
3071 {
3072 const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
3073
3074 // In footprint editor, we do not want to select the footprint itself.
3076 return false;
3077
3078 // Allow selection of footprints if some part of the footprint is visible.
3079 if( footprint->GetSide() != UNDEFINED_LAYER && !m_skip_heuristics )
3080 {
3081 LSET boardSide = footprint->IsFlipped() ? LSET::BackMask() : LSET::FrontMask();
3082
3083 if( !( visibleLayers() & boardSide ).any() )
3084 return false;
3085 }
3086
3087 // If the footprint has no items except the reference and value fields, include the
3088 // footprint in the selections.
3089 if( footprint->GraphicalItems().empty()
3090 && footprint->Pads().empty()
3091 && footprint->Zones().empty() )
3092 {
3093 return true;
3094 }
3095
3096 for( const BOARD_ITEM* item : footprint->GraphicalItems() )
3097 {
3098 if( Selectable( item, true ) )
3099 return true;
3100 }
3101
3102 for( const PAD* pad : footprint->Pads() )
3103 {
3104 if( Selectable( pad, true ) )
3105 return true;
3106 }
3107
3108 for( const ZONE* zone : footprint->Zones() )
3109 {
3110 if( Selectable( zone, true ) )
3111 return true;
3112 }
3113
3114 return false;
3115 }
3116 else if( aItem->Type() == PCB_GROUP_T )
3117 {
3118 PCB_GROUP* group = const_cast<PCB_GROUP*>( static_cast<const PCB_GROUP*>( aItem ) );
3119
3120 // Similar to logic for footprint, a group is selectable if any of its members are.
3121 // (This recurses.)
3122 for( BOARD_ITEM* item : group->GetBoardItems() )
3123 {
3124 if( Selectable( item, true ) )
3125 return true;
3126 }
3127
3128 return false;
3129 }
3130
3131 if( aItem->GetParentGroup() && aItem->GetParentGroup()->AsEdaItem()->Type() == PCB_GENERATOR_T )
3132 return false;
3133
3134 const ZONE* zone = nullptr;
3135 const PCB_VIA* via = nullptr;
3136 const PAD* pad = nullptr;
3137 const PCB_TEXT* text = nullptr;
3138 const PCB_FIELD* field = nullptr;
3139 const PCB_MARKER* marker = nullptr;
3140
3141 // Most footprint children can only be selected in the footprint editor.
3142 if( aItem->GetParentFootprint() && !m_isFootprintEditor && !checkVisibilityOnly )
3143 {
3144 if( aItem->Type() != PCB_FIELD_T && aItem->Type() != PCB_PAD_T
3145 && aItem->Type() != PCB_TEXT_T )
3146 {
3147 return false;
3148 }
3149 }
3150
3151 switch( aItem->Type() )
3152 {
3153 case PCB_ZONE_T:
3154 if( !board()->IsElementVisible( LAYER_ZONES ) || ( options.m_ZoneOpacity == 0.00 ) )
3155 return false;
3156
3157 zone = static_cast<const ZONE*>( aItem );
3158
3159 // A teardrop is modelled as a property of a via, pad or the board (for track-to-track
3160 // teardrops). The underlying zone is only an implementation detail.
3161 if( zone->IsTeardropArea() && !board()->LegacyTeardrops() )
3162 return false;
3163
3164 // zones can exist on multiple layers!
3165 if( !( zone->GetLayerSet() & visibleLayers() ).any() )
3166 return false;
3167
3168 break;
3169
3170 case PCB_TRACE_T:
3171 case PCB_ARC_T:
3172 if( !board()->IsElementVisible( LAYER_TRACKS ) || ( options.m_TrackOpacity == 0.00 ) )
3173 return false;
3174
3175 if( !layerVisible( aItem->GetLayer() ) )
3176 return false;
3177
3178 break;
3179
3180 case PCB_VIA_T:
3181 if( !board()->IsElementVisible( LAYER_VIAS ) || ( options.m_ViaOpacity == 0.00 ) )
3182 return false;
3183
3184 via = static_cast<const PCB_VIA*>( aItem );
3185
3186 // For vias it is enough if only one of its layers is visible
3187 if( !( visibleLayers() & via->GetLayerSet() ).any() )
3188 return false;
3189
3190 break;
3191
3192 case PCB_FIELD_T:
3193 field = static_cast<const PCB_FIELD*>( aItem );
3194
3195 if( !field->IsVisible() )
3196 return false;
3197
3198 if( field->IsReference() && !view()->IsLayerVisible( LAYER_FP_REFERENCES ) )
3199 return false;
3200
3201 if( field->IsValue() && !view()->IsLayerVisible( LAYER_FP_VALUES ) )
3202 return false;
3203
3204 // Handle all other fields with normal text visibility controls
3206 case PCB_TEXT_T:
3207 text = static_cast<const PCB_TEXT*>( aItem );
3208
3209 if( !layerVisible( text->GetLayer() ) )
3210 return false;
3211
3212 // Apply the LOD visibility test as well
3213 if( !view()->IsVisible( text ) )
3214 return false;
3215
3216 if( aItem->GetParentFootprint() )
3217 {
3218 int controlLayer = LAYER_FP_TEXT;
3219
3220 if( text->GetText() == wxT( "${REFERENCE}" ) )
3221 controlLayer = LAYER_FP_REFERENCES;
3222 else if( text->GetText() == wxT( "${VALUE}" ) )
3223 controlLayer = LAYER_FP_VALUES;
3224
3225 if( !view()->IsLayerVisible( controlLayer ) )
3226 return false;
3227 }
3228
3229 break;
3230
3232 if( options.m_ImageOpacity == 0.00 )
3233 return false;
3234
3235 // Bitmap images on board are hidden if LAYER_DRAW_BITMAPS is not visible
3237 return false;
3238
3240
3241 case PCB_SHAPE_T:
3242 if( options.m_FilledShapeOpacity == 0.0 && static_cast<const PCB_SHAPE*>( aItem )->IsAnyFill() )
3243 return false;
3244
3246
3247 case PCB_TEXTBOX_T:
3248 case PCB_TABLE_T:
3249 case PCB_TABLECELL_T:
3250 if( !layerVisible( aItem->GetLayer() ) )
3251 return false;
3252
3253 if( aItem->Type() == PCB_TABLECELL_T )
3254 {
3255 const PCB_TABLECELL* cell = static_cast<const PCB_TABLECELL*>( aItem );
3256
3257 if( cell->GetRowSpan() == 0 || cell->GetColSpan() == 0 )
3258 return false;
3259 }
3260
3261 break;
3262
3263 case PCB_DIM_ALIGNED_T:
3264 case PCB_DIM_LEADER_T:
3265 case PCB_DIM_CENTER_T:
3266 case PCB_DIM_RADIAL_T:
3268 if( !layerVisible( aItem->GetLayer() ) )
3269 return false;
3270
3271 break;
3272
3273 case PCB_PAD_T:
3274 if( options.m_PadOpacity == 0.00 )
3275 return false;
3276
3277 pad = static_cast<const PAD*>( aItem );
3278
3279 if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
3280 {
3281 // A pad's hole is visible on every layer the pad is visible on plus many layers the
3282 // pad is not visible on -- so we only need to check for any visible hole layers.
3283 if( !( visibleLayers() & LSET::PhysicalLayersMask() ).any() )
3284 return false;
3285 }
3286 else
3287 {
3288 if( !( pad->GetLayerSet() & visibleLayers() ).any() )
3289 return false;
3290 }
3291
3292 break;
3293
3294 case PCB_MARKER_T:
3295 marker = static_cast<const PCB_MARKER*>( aItem );
3296
3297 if( marker && marker->IsExcluded() && !board()->IsElementVisible( LAYER_DRC_EXCLUSION ) )
3298 return false;
3299
3300 break;
3301
3302 // These are not selectable
3303 case PCB_NETINFO_T:
3304 case NOT_USED:
3305 case TYPE_NOT_INIT:
3306 return false;
3307
3308 default: // Suppress warnings
3309 break;
3310 }
3311
3312 return true;
3313}
3314
3315
3317{
3318 if( !aItem || aItem->IsSelected() || !aItem->IsBOARD_ITEM() )
3319 return;
3320
3321 if( aItem->Type() == PCB_PAD_T )
3322 {
3323 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem->GetParent() );
3324
3325 if( m_selection.Contains( footprint ) )
3326 return;
3327 }
3328
3329 if( m_enteredGroup &&
3330 !PCB_GROUP::WithinScope( static_cast<BOARD_ITEM*>( aItem ), m_enteredGroup,
3332 {
3333 ExitGroup();
3334 }
3335
3336 highlight( aItem, SELECTED, &m_selection );
3337}
3338
3339
3341{
3342 unhighlight( aItem, SELECTED, &m_selection );
3343}
3344
3345
3346void PCB_SELECTION_TOOL::highlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
3347{
3348 if( aGroup )
3349 aGroup->Add( aItem );
3350
3351 highlightInternal( aItem, aMode, aGroup != nullptr );
3352 view()->Update( aItem, KIGFX::REPAINT );
3353
3354 // Many selections are very temporal and updating the display each time just
3355 // creates noise.
3356 if( aMode == BRIGHTENED )
3358}
3359
3360
3361void PCB_SELECTION_TOOL::highlightInternal( EDA_ITEM* aItem, int aMode, bool aUsingOverlay )
3362{
3363 if( aMode == SELECTED )
3364 aItem->SetSelected();
3365 else if( aMode == BRIGHTENED )
3366 aItem->SetBrightened();
3367
3368 if( aUsingOverlay && aMode != BRIGHTENED )
3369 view()->Hide( aItem, true ); // Hide the original item, so it is shown only on overlay
3370
3371 if( aItem->IsBOARD_ITEM() )
3372 {
3373 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( aItem );
3374 boardItem->RunOnChildren( std::bind( &PCB_SELECTION_TOOL::highlightInternal, this, _1, aMode, aUsingOverlay ),
3375 RECURSE_MODE::RECURSE );
3376 }
3377}
3378
3379
3380void PCB_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
3381{
3382 if( aGroup )
3383 aGroup->Remove( aItem );
3384
3385 unhighlightInternal( aItem, aMode, aGroup != nullptr );
3386 view()->Update( aItem, KIGFX::REPAINT );
3387
3388 // Many selections are very temporal and updating the display each time just creates noise.
3389 if( aMode == BRIGHTENED )
3391}
3392
3393
3394void PCB_SELECTION_TOOL::unhighlightInternal( EDA_ITEM* aItem, int aMode, bool aUsingOverlay )
3395{
3396 if( aMode == SELECTED )
3397 aItem->ClearSelected();
3398 else if( aMode == BRIGHTENED )
3399 aItem->ClearBrightened();
3400
3401 if( aUsingOverlay && aMode != BRIGHTENED )
3402 {
3403 view()->Hide( aItem, false ); // Restore original item visibility...
3404 view()->Update( aItem ); // ... and make sure it's redrawn un-selected
3405 }
3406
3407 if( aItem->IsBOARD_ITEM() )
3408 {
3409 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( aItem );
3410 boardItem->RunOnChildren( std::bind( &PCB_SELECTION_TOOL::unhighlightInternal, this, _1, aMode, aUsingOverlay ),
3411 RECURSE_MODE::RECURSE );
3412 }
3413}
3414
3415
3417{
3418 const unsigned GRIP_MARGIN = 20;
3419 int margin = KiROUND( getView()->ToWorld( GRIP_MARGIN ) );
3420
3421 // Check if the point is located close to any of the currently selected items
3422 for( EDA_ITEM* item : m_selection )
3423 {
3424 if( !item->IsBOARD_ITEM() )
3425 continue;
3426
3427 BOX2I itemBox = item->ViewBBox();
3428 itemBox.Inflate( margin ); // Give some margin for gripping an item
3429
3430 if( itemBox.Contains( aPoint ) )
3431 {
3432 if( item->HitTest( aPoint, margin ) )
3433 return true;
3434
3435 bool found = false;
3436
3437 if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( item ) )
3438 {
3439 group->RunOnChildren(
3440 [&]( BOARD_ITEM* aItem )
3441 {
3442 if( aItem->HitTest( aPoint, margin ) )
3443 found = true;
3444 },
3445 RECURSE_MODE::RECURSE );
3446 }
3447
3448 if( found )
3449 return true;
3450 }
3451 }
3452
3453 return false;
3454}
3455
3456
3458 int aMaxDistance ) const
3459{
3460 BOX2D viewportD = getView()->GetViewport();
3461 BOX2I viewport = BOX2ISafe( viewportD );
3462 int distance = INT_MAX;
3463 SEG loc( aWhere, aWhere );
3464
3465 switch( aItem->Type() )
3466 {
3467 case PCB_FIELD_T:
3468 case PCB_TEXT_T:
3469 {
3470 PCB_TEXT* text = static_cast<PCB_TEXT*>( aItem );
3471
3472 // Add a bit of slop to text-shapes
3473 if( text->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance ) )
3474 distance = std::clamp( distance - ( aMaxDistance / 2 ), 0, distance );
3475
3476 break;
3477 }
3478
3479 case PCB_TEXTBOX_T:
3480 case PCB_TABLECELL_T:
3481 {
3482 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( aItem );
3483
3484 // Add a bit of slop to text-shapes
3485 if( textbox->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance ) )
3486 distance = std::clamp( distance - ( aMaxDistance / 2 ), 0, distance );
3487
3488 break;
3489 }
3490
3491 case PCB_TABLE_T:
3492 {
3493 PCB_TABLE* table = static_cast<PCB_TABLE*>( aItem );
3494
3495 for( PCB_TABLECELL* cell : table->GetCells() )
3496 {
3497 // Add a bit of slop to text-shapes
3498 if( cell->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance ) )
3499 distance = std::clamp( distance - ( aMaxDistance / 2 ), 0, distance );
3500 }
3501
3502 break;
3503 }
3504
3505 case PCB_ZONE_T:
3506 {
3507 ZONE* zone = static_cast<ZONE*>( aItem );
3508
3509 // Zone borders are very specific
3510 if( zone->HitTestForEdge( aWhere, aMaxDistance / 2 ) )
3511 distance = 0;
3512 else if( zone->HitTestForEdge( aWhere, aMaxDistance ) )
3513 distance = aMaxDistance / 2;
3514 else
3515 aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
3516
3517 break;
3518 }
3519
3520 case PCB_FOOTPRINT_T:
3521 {
3522 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
3523 BOX2I bbox = footprint->GetBoundingBox( false );
3524
3525 try
3526 {
3527 footprint->GetBoundingHull().Collide( loc, aMaxDistance, &distance );
3528 }
3529 catch( const std::exception& e )
3530 {
3531 wxFAIL_MSG( wxString::Format( wxT( "Clipper exception occurred: %s" ), e.what() ) );
3532 }
3533
3534 // Consider footprints larger than the viewport only as a last resort
3535 if( bbox.GetHeight() > viewport.GetHeight() || bbox.GetWidth() > viewport.GetWidth() )
3536 distance = INT_MAX / 2;
3537
3538 break;
3539 }
3540
3541 case PCB_MARKER_T:
3542 {
3543 PCB_MARKER* marker = static_cast<PCB_MARKER*>( aItem );
3544 SHAPE_LINE_CHAIN polygon;
3545
3546 marker->ShapeToPolygon( polygon );
3547 polygon.Move( marker->GetPos() );
3548 polygon.Collide( loc, aMaxDistance, &distance );
3549 break;
3550 }
3551
3552 case PCB_GROUP_T:
3553 case PCB_GENERATOR_T:
3554 {
3555 PCB_GROUP* group = static_cast<PCB_GROUP*>( aItem );
3556
3557 for( BOARD_ITEM* member : group->GetBoardItems() )
3558 distance = std::min( distance, hitTestDistance( aWhere, member, aMaxDistance ) );
3559
3560 break;
3561 }
3562
3563 case PCB_PAD_T:
3564 {
3565 static_cast<PAD*>( aItem )->Padstack().ForEachUniqueLayer(
3566 [&]( PCB_LAYER_ID aLayer )
3567 {
3568 int layerDistance = INT_MAX;
3569 aItem->GetEffectiveShape( aLayer )->Collide( loc, aMaxDistance, &layerDistance );
3570 distance = std::min( distance, layerDistance );
3571 } );
3572
3573 break;
3574 }
3575
3576 default:
3577 aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
3578 break;
3579 }
3580
3581 return distance;
3582}
3583
3584
3586{
3587 wxCHECK( m_frame, /* void */ );
3588
3589 if( aCollector.GetCount() < 2 )
3590 return;
3591
3592 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
3593
3594 wxCHECK( settings, /* void */ );
3595
3596 PCB_LAYER_ID activeLayer = m_frame->GetActiveLayer();
3597 LSET visibleLayers = m_frame->GetBoard()->GetVisibleLayers();
3598 LSET enabledLayers = m_frame->GetBoard()->GetEnabledLayers();
3599 LSEQ enabledLayerStack = enabledLayers.SeqStackupTop2Bottom( activeLayer );
3600
3601 wxCHECK( !enabledLayerStack.empty(), /* void */ );
3602
3603 auto isZoneFillKeepout =
3604 []( const BOARD_ITEM* aItem ) -> bool
3605 {
3606 if( aItem->Type() == PCB_ZONE_T )
3607 {
3608 const ZONE* zone = static_cast<const ZONE*>( aItem );
3609
3610 if( zone->GetIsRuleArea() && zone->GetDoNotAllowZoneFills() )
3611 return true;
3612 }
3613
3614 return false;
3615 };
3616
3617 std::vector<LAYER_OPACITY_ITEM> opacityStackup;
3618
3619 for( int i = 0; i < aCollector.GetCount(); i++ )
3620 {
3621 const BOARD_ITEM* item = aCollector[i];
3622
3623 LSET itemLayers = item->GetLayerSet() & enabledLayers & visibleLayers;
3624 LSEQ itemLayerSeq = itemLayers.Seq( enabledLayerStack );
3625
3626 for( PCB_LAYER_ID layer : itemLayerSeq )
3627 {
3628 COLOR4D color = settings->GetColor( item, layer );
3629
3630 if( color.a == 0 )
3631 continue;
3632
3633 LAYER_OPACITY_ITEM opacityItem;
3634
3635 opacityItem.m_Layer = layer;
3636 opacityItem.m_Opacity = color.a;
3637 opacityItem.m_Item = item;
3638
3639 if( isZoneFillKeepout( item ) )
3640 opacityItem.m_Opacity = 0.0;
3641
3642 opacityStackup.emplace_back( opacityItem );
3643 }
3644 }
3645
3646 std::sort( opacityStackup.begin(), opacityStackup.end(),
3647 [&]( const LAYER_OPACITY_ITEM& aLhs, const LAYER_OPACITY_ITEM& aRhs ) -> bool
3648 {
3649 int retv = enabledLayerStack.TestLayers( aLhs.m_Layer, aRhs.m_Layer );
3650
3651 if( retv )
3652 return retv > 0;
3653
3654 return aLhs.m_Opacity > aRhs.m_Opacity;
3655 } );
3656
3657 std::set<const BOARD_ITEM*> visibleItems;
3658 std::set<const BOARD_ITEM*> itemsToRemove;
3659 double minAlphaLimit = ADVANCED_CFG::GetCfg().m_PcbSelectionVisibilityRatio;
3660 double currentStackupOpacity = 0.0;
3661 PCB_LAYER_ID lastVisibleLayer = PCB_LAYER_ID::UNDEFINED_LAYER;
3662
3663 for( const LAYER_OPACITY_ITEM& opacityItem : opacityStackup )
3664 {
3665 if( lastVisibleLayer == PCB_LAYER_ID::UNDEFINED_LAYER )
3666 {
3667 currentStackupOpacity = opacityItem.m_Opacity;
3668 lastVisibleLayer = opacityItem.m_Layer;
3669 visibleItems.emplace( opacityItem.m_Item );
3670 continue;
3671 }
3672
3673 // Objects to ignore and fallback to the old selection behavior.
3674 auto ignoreItem =
3675 [&]()
3676 {
3677 const BOARD_ITEM* item = opacityItem.m_Item;
3678
3679 wxCHECK( item, false );
3680
3681 // Check items that span multiple layers for visibility.
3682 if( visibleItems.count( item ) )
3683 return true;
3684
3685 // Don't prune child items of a footprint that is already visible.
3686 if( item->GetParent()
3687 && ( item->GetParent()->Type() == PCB_FOOTPRINT_T )
3688 && visibleItems.count( item->GetParent() ) )
3689 {
3690 return true;
3691 }
3692
3693 // Keepout zones are transparent but for some reason, PCB_PAINTER::GetColor()
3694 // returns the color of the zone it prevents from filling.
3695 if( isZoneFillKeepout( item ) )
3696 return true;
3697
3698 return false;
3699 };
3700
3701 // Everything on the currently selected layer is visible;
3702 if( opacityItem.m_Layer == enabledLayerStack[0] )
3703 {
3704 visibleItems.emplace( opacityItem.m_Item );
3705 }
3706 else
3707 {
3708 double itemVisibility = opacityItem.m_Opacity * ( 1.0 - currentStackupOpacity );
3709
3710 if( ( itemVisibility <= minAlphaLimit ) && !ignoreItem() )
3711 itemsToRemove.emplace( opacityItem.m_Item );
3712 else
3713 visibleItems.emplace( opacityItem.m_Item );
3714 }
3715
3716 if( opacityItem.m_Layer != lastVisibleLayer )
3717 {
3718 currentStackupOpacity += opacityItem.m_Opacity * ( 1.0 - currentStackupOpacity );
3719 currentStackupOpacity = std::min( currentStackupOpacity, 1.0 );
3720 lastVisibleLayer = opacityItem.m_Layer;
3721 }
3722 }
3723
3724 for( const BOARD_ITEM* itemToRemove : itemsToRemove )
3725 {
3726 wxCHECK( aCollector.GetCount() > 1, /* void */ );
3727 aCollector.Remove( itemToRemove );
3728 }
3729}
3730
3731
3732// The general idea here is that if the user clicks directly on a small item inside a larger
3733// one, then they want the small item. The quintessential case of this is clicking on a pad
3734// within a footprint, but we also apply it for text within a footprint, footprints within
3735// larger footprints, and vias within either larger pads or longer tracks.
3736//
3737// These "guesses" presume there is area within the larger item to click in to select it. If
3738// an item is mostly covered by smaller items within it, then the guesses are inappropriate as
3739// there might not be any area left to click to select the larger item. In this case we must
3740// leave the items in the collector and bring up a Selection Clarification menu.
3741//
3742// We currently check for pads and text mostly covering a footprint, but we don't check for
3743// smaller footprints mostly covering a larger footprint.
3744//
3746 const VECTOR2I& aWhere ) const
3747{
3748 static const LSET silkLayers( { B_SilkS, F_SilkS } );
3749 static const LSET courtyardLayers( { B_CrtYd, F_CrtYd } );
3750 static std::vector<KICAD_T> singleLayerSilkTypes = { PCB_FIELD_T,
3753 PCB_SHAPE_T };
3754
3755 if( ADVANCED_CFG::GetCfg().m_PcbSelectionVisibilityRatio != 1.0 )
3757
3758 if( aCollector.GetCount() == 1 )
3759 return;
3760
3761 std::set<BOARD_ITEM*> preferred;
3762 std::set<BOARD_ITEM*> rejected;
3763 VECTOR2I where( aWhere.x, aWhere.y );
3764 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
3765 PCB_LAYER_ID activeLayer = m_frame->GetActiveLayer();
3766
3767 // If a silk layer is in front, we assume the user is working with silk and give preferential
3768 // treatment to single-layer items on *either* silk layer.
3769 if( silkLayers[activeLayer] )
3770 {
3771 for( int i = 0; i < aCollector.GetCount(); ++i )
3772 {
3773 BOARD_ITEM* item = aCollector[i];
3774
3775 if( item->IsType( singleLayerSilkTypes ) && silkLayers[ item->GetLayer() ] )
3776 preferred.insert( item );
3777 }
3778 }
3779 // Similarly, if a courtyard layer is in front, we assume the user is positioning footprints
3780 // and give preferential treatment to footprints on *both* top and bottom.
3781 else if( courtyardLayers[activeLayer] && settings->GetHighContrast() )
3782 {
3783 for( int i = 0; i < aCollector.GetCount(); ++i )
3784 {
3785 BOARD_ITEM* item = aCollector[i];
3786
3787 if( item->Type() == PCB_FOOTPRINT_T )
3788 preferred.insert( item );
3789 }
3790 }
3791
3792 if( preferred.size() > 0 )
3793 {
3794 aCollector.Empty();
3795
3796 for( BOARD_ITEM* item : preferred )
3797 aCollector.Append( item );
3798
3799 if( preferred.size() == 1 )
3800 return;
3801 }
3802
3803 // Prefer exact hits to sloppy ones
3804 constexpr int MAX_SLOP = 5;
3805
3806 int singlePixel = KiROUND( aCollector.GetGuide()->OnePixelInIU() );
3807 int maxSlop = KiROUND( MAX_SLOP * aCollector.GetGuide()->OnePixelInIU() );
3808 int minSlop = INT_MAX;
3809
3810 std::map<BOARD_ITEM*, int> itemsBySloppiness;
3811
3812 for( int i = 0; i < aCollector.GetCount(); ++i )
3813 {
3814 BOARD_ITEM* item = aCollector[i];
3815 int itemSlop = hitTestDistance( where, item, maxSlop );
3816
3817 itemsBySloppiness[ item ] = itemSlop;
3818
3819 if( itemSlop < minSlop )
3820 minSlop = itemSlop;
3821 }
3822
3823 // Prune sloppier items
3824 if( minSlop < INT_MAX )
3825 {
3826 for( std::pair<BOARD_ITEM*, int> pair : itemsBySloppiness )
3827 {
3828 if( pair.second > minSlop + singlePixel )
3829 aCollector.Transfer( pair.first );
3830 }
3831 }
3832
3833 // If the user clicked on a small item within a much larger one then it's pretty clear
3834 // they're trying to select the smaller one.
3835 constexpr double sizeRatio = 1.5;
3836
3837 std::vector<std::pair<BOARD_ITEM*, double>> itemsByArea;
3838
3839 for( int i = 0; i < aCollector.GetCount(); ++i )
3840 {
3841 BOARD_ITEM* item = aCollector[i];
3842 double area = 0.0;
3843
3844 if( item->Type() == PCB_ZONE_T
3845 && static_cast<ZONE*>( item )->HitTestForEdge( where, maxSlop / 2 ) )
3846 {
3847 // Zone borders are very specific, so make them "small"
3848 area = (double) SEG::Square( singlePixel ) * MAX_SLOP;
3849 }
3850 else if( item->Type() == PCB_VIA_T )
3851 {
3852 // Vias rarely hide other things, and we don't want them deferring to short track
3853 // segments underneath them -- so artificially reduce their size from πr² to r².
3854 area = (double) SEG::Square( static_cast<PCB_VIA*>( item )->GetDrill() / 2 );
3855 }
3856 else if( item->Type() == PCB_REFERENCE_IMAGE_T )
3857 {
3858 BOX2I box = item->GetBoundingBox();
3859 area = (double) box.GetWidth() * box.GetHeight();
3860 }
3861 else
3862 {
3863 try
3864 {
3865 area = FOOTPRINT::GetCoverageArea( item, aCollector );
3866 }
3867 catch( const std::exception& e )
3868 {
3869 wxFAIL_MSG( wxString::Format( wxT( "Clipper exception occurred: %s" ), e.what() ) );
3870 }
3871 }
3872
3873 itemsByArea.emplace_back( item, area );
3874 }
3875
3876 std::sort( itemsByArea.begin(), itemsByArea.end(),
3877 []( const std::pair<BOARD_ITEM*, double>& lhs,
3878 const std::pair<BOARD_ITEM*, double>& rhs ) -> bool
3879 {
3880 return lhs.second < rhs.second;
3881 } );
3882
3883 bool rejecting = false;
3884
3885 for( int i = 1; i < (int) itemsByArea.size(); ++i )
3886 {
3887 if( itemsByArea[i].second > itemsByArea[i-1].second * sizeRatio )
3888 rejecting = true;
3889
3890 if( rejecting )
3891 rejected.insert( itemsByArea[i].first );
3892 }
3893
3894 // Special case: if a footprint is completely covered with other features then there's no
3895 // way to select it -- so we need to leave it in the list for user disambiguation.
3896 constexpr double maxCoverRatio = 0.70;
3897
3898 for( int i = 0; i < aCollector.GetCount(); ++i )
3899 {
3900 if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aCollector[i] ) )
3901 {
3902 if( footprint->CoverageRatio( aCollector ) > maxCoverRatio )
3903 rejected.erase( footprint );
3904 }
3905 }
3906
3907 // Hopefully we've now got what the user wanted.
3908 if( (unsigned) aCollector.GetCount() > rejected.size() ) // do not remove everything
3909 {
3910 for( BOARD_ITEM* item : rejected )
3911 aCollector.Transfer( item );
3912 }
3913
3914 // Finally, what we are left with is a set of items of similar coverage area. We now reject
3915 // any that are not on the active layer, to reduce the number of disambiguation menus shown.
3916 // If the user wants to force-disambiguate, they can either switch layers or use the modifier
3917 // key to force the menu.
3918 if( aCollector.GetCount() > 1 )
3919 {
3920 bool haveItemOnActive = false;
3921 rejected.clear();
3922
3923 for( int i = 0; i < aCollector.GetCount(); ++i )
3924 {
3925 if( !aCollector[i]->IsOnLayer( activeLayer ) )
3926 rejected.insert( aCollector[i] );
3927 else
3928 haveItemOnActive = true;
3929 }
3930
3931 if( haveItemOnActive )
3932 {
3933 for( BOARD_ITEM* item : rejected )
3934 aCollector.Transfer( item );
3935 }
3936 }
3937}
3938
3939
3941 bool aMultiselect ) const
3942{
3943 std::unordered_set<EDA_ITEM*> toAdd;
3944
3945 // Set CANDIDATE on all parents which are included in the GENERAL_COLLECTOR. This
3946 // algorithm is O(3n), whereas checking for the parent inclusion could potentially be O(n^2).
3947 for( int j = 0; j < aCollector.GetCount(); j++ )
3948 {
3949 if( aCollector[j]->GetParent() )
3950 aCollector[j]->GetParent()->ClearFlags( CANDIDATE );
3951
3952 if( aCollector[j]->GetParentFootprint() )
3953 aCollector[j]->GetParentFootprint()->ClearFlags( CANDIDATE );
3954 }
3955
3956 if( aMultiselect )
3957 {
3958 for( int j = 0; j < aCollector.GetCount(); j++ )
3959 aCollector[j]->SetFlags( CANDIDATE );
3960 }
3961
3962 for( int j = 0; j < aCollector.GetCount(); )
3963 {
3964 BOARD_ITEM* item = aCollector[j];
3965 FOOTPRINT* fp = item->GetParentFootprint();
3966 BOARD_ITEM* start = item;
3967
3968 if( !m_isFootprintEditor && fp )
3969 start = fp;
3970
3971 // If a group is entered, disallow selections of objects outside the group.
3973 {
3974 aCollector.Remove( item );
3975 continue;
3976 }
3977
3978 // If any element is a member of a group, replace those elements with the top containing
3979 // group.
3981 {
3982 if( top->AsEdaItem() != item )
3983 {
3984 toAdd.insert( top->AsEdaItem() );
3985 top->AsEdaItem()->SetFlags( CANDIDATE );
3986
3987 aCollector.Remove( item );
3988 continue;
3989 }
3990 }
3991
3992 // Footprints are a bit easier as they can't be nested.
3993 if( fp && ( fp->GetFlags() & CANDIDATE ) )
3994 {
3995 // Remove children of selected items
3996 aCollector.Remove( item );
3997 continue;
3998 }
3999
4000 ++j;
4001 }
4002
4003 for( EDA_ITEM* item : toAdd )
4004 {
4005 if( !aCollector.HasItem( item ) )
4006 aCollector.Append( item );
4007 }
4008}
4009
4010
4012{
4013 std::set<BOARD_ITEM*> to_add;
4014
4015 // Iterate from the back so we don't have to worry about removals.
4016 for( int i = (int) aCollector.GetCount() - 1; i >= 0; --i )
4017 {
4018 BOARD_ITEM* item = aCollector[i];
4019
4020 if( item->Type() == PCB_TABLECELL_T )
4021 {
4022 if( !aCollector.HasItem( item->GetParent() ) )
4023 to_add.insert( item->GetParent() );
4024
4025 aCollector.Remove( item );
4026 }
4027 }
4028
4029 for( BOARD_ITEM* item : to_add )
4030 aCollector.Append( item );
4031}
4032
4033
4035 bool aForcePromotion ) const
4036{
4037 std::set<BOARD_ITEM*> to_add;
4038
4039 // Iterate from the back so we don't have to worry about removals.
4040 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
4041 {
4042 BOARD_ITEM* item = aCollector[i];
4043
4044 if( !m_isFootprintEditor && item->Type() == PCB_PAD_T
4045 && ( !frame()->GetPcbNewSettings()->m_AllowFreePads || aForcePromotion ) )
4046 {
4047 if( !aCollector.HasItem( item->GetParent() ) )
4048 to_add.insert( item->GetParent() );
4049
4050 aCollector.Remove( item );
4051 }
4052 }
4053
4054 for( BOARD_ITEM* item : to_add )
4055 aCollector.Append( item );
4056}
4057
4058
4060{
4061 // Iterate from the back so we don't have to worry about removals.
4062 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
4063 {
4064 BOARD_ITEM* item = aCollector[i];
4065
4066 if( item->Type() == PCB_MARKER_T )
4067 aCollector.Remove( item );
4068 }
4069}
4070
4071
4073 const VECTOR2I& aWhere ) const
4074{
4075 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
4076 BOX2D viewport = getView()->GetViewport();
4077 BOX2I extents = BOX2ISafe( viewport );
4078
4079 bool need_direct_hit = false;
4080 FOOTPRINT* single_fp = nullptr;
4081
4082 // If the designer is not modifying the existing selection AND we already have
4083 // a selection, then we only want to select items that are directly under the cursor.
4084 // This prevents us from being unable to clear the selection when zoomed into a footprint
4086 {
4087 need_direct_hit = true;
4088
4089 for( EDA_ITEM* item : m_selection )
4090 {
4091 FOOTPRINT* fp = nullptr;
4092
4093 if( item->Type() == PCB_FOOTPRINT_T )
4094 fp = static_cast<FOOTPRINT*>( item );
4095 else if( item->IsBOARD_ITEM() )
4096 fp = static_cast<BOARD_ITEM*>( item )->GetParentFootprint();
4097
4098 // If the selection contains items that are not footprints, then don't restrict
4099 // whether we deselect the item or not.
4100 if( !fp )
4101 {
4102 single_fp = nullptr;
4103 break;
4104 }
4105 else if( !single_fp )
4106 {
4107 single_fp = fp;
4108 }
4109 // If the selection contains items from multiple footprints, then don't restrict
4110 // whether we deselect the item or not.
4111 else if( single_fp != fp )
4112 {
4113 single_fp = nullptr;
4114 break;
4115 }
4116 }
4117 }
4118
4119 auto visibleLayers =
4120 [&]() -> LSET
4121 {
4123 {
4124 LSET set;
4125
4126 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
4127 set.set( layer, view()->IsLayerVisible( layer ) );
4128
4129 return set;
4130 }
4131 else
4132 {
4133 return board()->GetVisibleLayers();
4134 }
4135 };
4136
4137 LSET layers = visibleLayers();
4138
4139 if( settings->GetHighContrast() )
4140 {
4141 layers.reset();
4142
4143 const std::set<int> activeLayers = settings->GetHighContrastLayers();
4144
4145 for( int layer : activeLayers )
4146 {
4147 if( layer >= 0 && layer < PCB_LAYER_ID_COUNT )
4148 layers.set( layer );
4149 }
4150 }
4151
4152 // Iterate from the back so we don't have to worry about removals.
4153 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
4154 {
4155 BOARD_ITEM* item = aCollector[i];
4156 FOOTPRINT* fp = dyn_cast<FOOTPRINT*>( item );
4157
4158 if( !fp )
4159 continue;
4160
4161 // Make footprints not difficult to select in high-contrast modes.
4162 if( layers[fp->GetLayer()] )
4163 continue;
4164
4165 BOX2I bbox = fp->GetLayerBoundingBox( layers );
4166
4167 // If the point clicked is not inside the visible bounding box, we can also remove it.
4168 if( !bbox.Contains( aWhere) )
4169 aCollector.Remove( item );
4170
4171 bool has_hit = false;
4172
4173 for( PCB_LAYER_ID layer : layers )
4174 {
4175 if( fp->HitTestOnLayer( extents, false, layer ) )
4176 {
4177 has_hit = true;
4178 break;
4179 }
4180 }
4181
4182 // If the point is outside of the visible bounding box, we can remove it.
4183 if( !has_hit )
4184 {
4185 aCollector.Remove( item );
4186 }
4187 // Do not require a direct hit on this fp if the existing selection only contains
4188 // this fp's items. This allows you to have a selection of pads from a single
4189 // footprint and still click in the center of the footprint to select it.
4190 else if( single_fp )
4191 {
4192 if( fp == single_fp )
4193 continue;
4194 }
4195 else if( need_direct_hit )
4196 {
4197 has_hit = false;
4198
4199 for( PCB_LAYER_ID layer : layers )
4200 {
4201 if( fp->HitTestOnLayer( aWhere, layer ) )
4202 {
4203 has_hit = true;
4204 break;
4205 }
4206 }
4207
4208 if( !has_hit )
4209 aCollector.Remove( item );
4210 }
4211 }
4212}
4213
4214
4216{
4217 getView()->Update( &m_selection );
4219
4220 return 0;
4221}
4222
4223
4225{
4226 std::set<std::pair<PCB_TABLE*, int>> columns;
4227 bool added = false;
4228
4229 for( EDA_ITEM* item : m_selection )
4230 {
4231 if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( item ) )
4232 {
4233 PCB_TABLE* table = static_cast<PCB_TABLE*>( cell->GetParent() );
4234 columns.insert( std::make_pair( table, cell->GetColumn() ) );
4235 }
4236 }
4237
4238 for( auto& [ table, col ] : columns )
4239 {
4240 for( int row = 0; row < table->GetRowCount(); ++row )
4241 {
4242 PCB_TABLECELL* cell = table->GetCell( row, col );
4243
4244 if( !cell->IsSelected() )
4245 {
4246 select( table->GetCell( row, col ) );
4247 added = true;
4248 }
4249 }
4250 }
4251
4252 if( added )
4254
4255 return 0;
4256}
4257
4258
4260{
4261 std::set<std::pair<PCB_TABLE*, int>> rows;
4262 bool added = false;
4263
4264 for( EDA_ITEM* item : m_selection )
4265 {
4266 if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( item ) )
4267 {
4268 PCB_TABLE* table = static_cast<PCB_TABLE*>( cell->GetParent() );
4269 rows.insert( std::make_pair( table, cell->GetRow() ) );
4270 }
4271 }
4272
4273 for( auto& [ table, row ] : rows )
4274 {
4275 for( int col = 0; col < table->GetColCount(); ++col )
4276 {
4277 PCB_TABLECELL* cell = table->GetCell( row, col );
4278
4279 if( !cell->IsSelected() )
4280 {
4281 select( table->GetCell( row, col ) );
4282 added = true;
4283 }
4284 }
4285 }
4286
4287 if( added )
4289
4290 return 0;
4291}
4292
4293
4295{
4296 std::set<PCB_TABLE*> tables;
4297 bool added = false;
4298
4299 for( EDA_ITEM* item : m_selection )
4300 {
4301 if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( item ) )
4302 tables.insert( static_cast<PCB_TABLE*>( cell->GetParent() ) );
4303 }
4304
4306
4307 for( PCB_TABLE* table : tables )
4308 {
4309 if( !table->IsSelected() )
4310 {
4311 select( table );
4312 added = true;
4313 }
4314 }
4315
4316 if( added )
4318
4319 return 0;
4320}
4321
4322
4324{
4326
4330
4337
4357
4362
4364}
int color
Definition: DXF_plotter.cpp:63
std::function< void(const VECTOR2I &, GENERAL_COLLECTOR &, PCB_SELECTION_TOOL *)> CLIENT_SELECTION_FILTER
Definition: actions.h:38
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:112
constexpr BOX2I BOX2ISafe(const BOX2D &aInput)
Definition: box2.h:929
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
static TOOL_ACTION cancelInteractive
Definition: actions.h:72
static TOOL_ACTION unselectAll
Definition: actions.h:83
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition: actions.h:224
static TOOL_ACTION cursorLeft
Definition: actions.h:169
static TOOL_ACTION zoomOutCenter
Definition: actions.h:135
static TOOL_ACTION unselectItem
Definition: actions.h:225
static TOOL_ACTION zoomIn
Definition: actions.h:132
static TOOL_ACTION cursorLeftFast
Definition: actions.h:174
static TOOL_ACTION selectionCursor
Select a single item under the cursor position.
Definition: actions.h:214
static TOOL_ACTION selectSetLasso
Definition: actions.h:218
static TOOL_ACTION selectSetRect
Set lasso selection mode.
Definition: actions.h:217
static TOOL_ACTION groupEnter
Definition: actions.h:240
static TOOL_ACTION selectColumns
Definition: actions.h:102
static TOOL_ACTION cursorDown
Definition: actions.h:168
static TOOL_ACTION zoomOut
Definition: actions.h:133
static TOOL_ACTION cursorRightFast
Definition: actions.h:175
static TOOL_ACTION zoomCenter
Definition: actions.h:140
static TOOL_ACTION panDown
Definition: actions.h:182
static TOOL_ACTION cursorDblClick
Definition: actions.h:178
static TOOL_ACTION undo
Definition: actions.h:75
static TOOL_ACTION selectionActivate
Activation of the selection tool.
Definition: actions.h:211
static TOOL_ACTION cursorDownFast
Definition: actions.h:173
static TOOL_ACTION selectionMenu
Run a selection menu to select from a list of items.
Definition: actions.h:233
static TOOL_ACTION reselectItem
Definition: actions.h:226
static TOOL_ACTION selectRows
Definition: actions.h:101
static TOOL_ACTION cursorUpFast
Definition: actions.h:172
static TOOL_ACTION panLeft
Definition: actions.h:183
static TOOL_ACTION updateMenu
Definition: actions.h:267
static TOOL_ACTION doDelete
Definition: actions.h:85
static TOOL_ACTION selectionTool
Definition: actions.h:248
static TOOL_ACTION cursorClick
Definition: actions.h:177
static TOOL_ACTION zoomFitScreen
Definition: actions.h:141
static TOOL_ACTION increment
Definition: actions.h:94
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: actions.h:221
static TOOL_ACTION panUp
Definition: actions.h:181
static TOOL_ACTION zoomFitObjects
Definition: actions.h:142
static TOOL_ACTION zoomInCenter
Definition: actions.h:134
static TOOL_ACTION panRight
Definition: actions.h:184
static TOOL_ACTION selectTable
Definition: actions.h:103
static TOOL_ACTION cursorUp
Cursor control with keyboard.
Definition: actions.h:167
static TOOL_ACTION groupLeave
Definition: actions.h:241
static TOOL_ACTION finishInteractive
Definition: actions.h:73
static TOOL_ACTION cursorRight
Definition: actions.h:170
static TOOL_ACTION selectAll
Definition: actions.h:82
static TOOL_ACTION unselectItems
Definition: actions.h:230
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition: actions.h:229
Define the structure of a menu based on ACTIONs.
Definition: action_menu.h:49
void SetTitle(const wxString &aTitle) override
Set title for the menu.
Definition: action_menu.cpp:92
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.
CROSS_PROBING_SETTINGS m_CrossProbing
Definition: app_settings.h:213
BASE_SET & reset(size_t pos)
Definition: base_set.h:143
BASE_SET & set(size_t pos)
Definition: base_set.h:116
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.
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:79
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:232
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition: board_item.h:134
bool IsLocked() const override
Definition: board_item.cpp:103
virtual VECTOR2I GetCenter() const
This defaults to the center of the bounding box if not overridden.
Definition: board_item.h:112
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.
Definition: board_item.cpp:326
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:97
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:252
virtual void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const
Invoke a function on all children.
Definition: board_item.h:208
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:210
virtual bool IsOnCopperLayer() const
Definition: board_item.h:151
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:317
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:1950
ZONE * m_SolderMaskBridges
Definition: board.h:1385
bool IsElementVisible(GAL_LAYER_ID aLayer) const
Test whether a given element category is visible.
Definition: board.cpp:973
const LSET & GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:921
const GROUPS & Groups() const
The groups must maintain the following invariants.
Definition: board.h:389
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:489
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:913
bool LegacyTeardrops() const
Definition: board.h:1319
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:907
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:522
constexpr void SetMaximum()
Definition: box2.h:80
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:558
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition: box2.h:146
constexpr size_type GetWidth() const
Definition: box2.h:214
constexpr Vec Centre() const
Definition: box2.h:97
constexpr size_type GetHeight() const
Definition: box2.h:215
constexpr bool Contains(const Vec &aPoint) const
Definition: box2.h:168
constexpr const Vec & GetOrigin() const
Definition: box2.h:210
constexpr const SizeVec & GetSize() const
Definition: box2.h:206
CN_ANCHOR represents a physical location that can be connected: a pad or a track/arc/via endpoint.
bool Dirty() const
BOARD_CONNECTED_ITEM * Parent() const
CN_EDGE represents a point-to-point connection, whether realized or unrealized (ie: tracks etc.
virtual double OnePixelInIU() const =0
void Transfer(int aIndex)
Move the item at aIndex (first position is 0) to the backup list.
Definition: collector.h:153
void Empty()
Clear the list.
Definition: collector.h:91
ITER begin()
Definition: collector.h:75
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:83
bool HasItem(const EDA_ITEM *aItem) const
Tests if aItem has already been collected.
Definition: collector.h:197
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition: collector.h:111
ITER end()
Definition: collector.h:76
void Append(EDA_ITEM *item)
Add an item to the end of the list.
Definition: collector.h:101
int ShowModal() override
bool IsType(FRAME_T aType) const
void AddStandardSubMenus(TOOL_MENU &aMenu)
Construct a "basic" menu for a tool, containing only items that apply to all tools (e....
void FocusOnLocation(const VECTOR2I &aPos)
Useful to focus on a particular location, in find functions.
virtual void ClearFocus()
void ForceRefresh()
Force a redraw.
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
A set of EDA_ITEMs (i.e., without duplicates).
Definition: eda_group.h:46
std::unordered_set< EDA_ITEM * > & GetItems()
Definition: eda_group.h:54
virtual EDA_ITEM * AsEdaItem()=0
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:98
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:272
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition: eda_item.cpp:110
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:142
virtual EDA_GROUP * GetParentGroup() const
Definition: eda_item.h:116
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:110
void ClearSelected()
Definition: eda_item.h:137
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: eda_item.h:144
bool IsSelected() const
Definition: eda_item.h:127
void SetSelected()
Definition: eda_item.h:134
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition: eda_item.h:192
void ClearBrightened()
Definition: eda_item.h:138
void SetBrightened()
Definition: eda_item.h:135
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:233
EDA_ITEM * GetParent() const
Definition: eda_item.h:112
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition: eda_item.h:146
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:145
bool IsMoving() const
Definition: eda_item.h:125
SHAPE_T GetShape() const
Definition: eda_shape.h:168
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:215
bool IsClosed() const
Definition: eda_shape.cpp:509
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:173
bool IsAnyFill() const
Definition: eda_shape.h:112
virtual bool IsVisible() const
Definition: eda_text.h:184
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.
Definition: eda_text.cpp:1108
static const TOOL_EVENT DisambiguatePoint
Used for hotkey feedback.
Definition: actions.h:359
static const TOOL_EVENT ClearedEvent
Definition: actions.h:344
static const TOOL_EVENT InhibitSelectionEditing
Definition: actions.h:355
static const TOOL_EVENT SelectedEvent
Definition: actions.h:342
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:349
static const TOOL_EVENT UninhibitSelectionEditing
Used to inform tool that it should display the disambiguation menu.
Definition: actions.h:356
static const TOOL_EVENT PointSelectedEvent
Definition: actions.h:341
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:352
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:343
ZONES & Zones()
Definition: footprint.h:215
static double GetCoverageArea(const BOARD_ITEM *aItem, const GENERAL_COLLECTOR &aCollector)
Definition: footprint.cpp:2865
PCB_LAYER_ID GetSide() const
Use instead of IsFlipped() when you also need to account for unsided footprints (those purely on user...
Definition: footprint.cpp:1772
const BOX2I GetLayerBoundingBox(const LSET &aLayers) const
Return the bounding box of the footprint on a given set of layers.
Definition: footprint.cpp:1497
std::deque< PAD * > & Pads()
Definition: footprint.h:209
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: footprint.h:239
bool IsFlipped() const
Definition: footprint.h:400
SHAPE_POLY_SET GetBoundingHull() const
Return a bounding polygon for the shapes and pads in the footprint.
Definition: footprint.cpp:1542
bool IsLocked() const override
Definition: footprint.h:420
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.
Definition: footprint.cpp:1834
const KIID_PATH & GetPath() const
Definition: footprint.h:266
DRAWINGS & GraphicalItems()
Definition: footprint.h:212
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: footprint.cpp:1347
A general implementation of a COLLECTORS_GUIDE.
Definition: collectors.h:324
void SetIgnoreBlindBuriedVias(bool ignore)
Definition: collectors.h:464
void SetIgnoreTracks(bool ignore)
Definition: collectors.h:470
void SetIgnoreFootprintsOnFront(bool ignore)
Definition: collectors.h:428
void SetIgnoreFPTextOnFront(bool ignore)
Definition: collectors.h:416
void SetIgnoreMicroVias(bool ignore)
Definition: collectors.h:467
void SetIgnoreZoneFills(bool ignore)
Definition: collectors.h:473
void SetIgnorePadsOnBack(bool ignore)
Definition: collectors.h:434
void SetIgnoreFPTextOnBack(bool ignore)
Definition: collectors.h:410
void SetLayerVisibleBits(const LSET &aLayerBits)
Definition: collectors.h:384
void SetIgnoreThroughVias(bool ignore)
Definition: collectors.h:461
void SetIgnoreThroughHolePads(bool ignore)
Definition: collectors.h:446
void SetIgnoreFPReferences(bool ignore)
Definition: collectors.h:458
void SetIgnoreFPValues(bool ignore)
Definition: collectors.h:452
void SetIgnorePadsOnFront(bool ignore)
Definition: collectors.h:440
void SetIgnoreFootprintsOnBack(bool ignore)
Definition: collectors.h:422
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:207
void SetGuide(const COLLECTORS_GUIDE *aGuide)
Record which COLLECTORS_GUIDE to use.
Definition: collectors.h:291
const COLLECTORS_GUIDE * GetGuide() const
Definition: collectors.h:293
static const std::vector< KICAD_T > AllBoardItems
A scan list for all editable board items.
Definition: collectors.h:227
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.
Definition: collectors.cpp:505
static const std::vector< KICAD_T > FootprintItems
A scan list for primary footprint items.
Definition: collectors.h:253
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const override
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: pcb_view.cpp:91
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1) override
Add a VIEW_ITEM to the view.
Definition: pcb_view.cpp:57
virtual void Remove(VIEW_ITEM *aItem) override
Remove a VIEW_ITEM from the view.
Definition: pcb_view.cpp:74
Represent a selection area (currently a rectangle) in a VIEW, drawn corner-to-corner between two poin...
void SetMode(SELECTION_MODE aMode)
void SetSubtractive(bool aSubtractive)
SELECTION_MODE GetMode() const
void SetAdditive(bool aAdditive)
void SetPoly(SHAPE_LINE_CHAIN &aPoly)
void SetOrigin(const VECTOR2I &aOrigin)
const BOX2I ViewBBox() const override
Set the origin of the rectangle (the fixed corner)
SHAPE_LINE_CHAIN & GetPoly()
void SetExclusiveOr(bool aExclusiveOr)
void SetEnd(const VECTOR2I &aEnd)
Set the current end of the rectangle (the corner that moves with the cursor.
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
const std::set< int > & GetHighlightNetCodes() const
Return the netcode of currently highlighted net.
const std::set< int > GetHighContrastLayers() const
Returns the set of currently high-contrast layers.
virtual COLOR4D GetColor(const VIEW_ITEM *aItem, int aLayer) const =0
Returns the color that should be used to draw the specific VIEW_ITEM on the specific layer using curr...
bool GetHighContrast() const
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.
virtual void Clear()
Remove all the stored items from the group.
Definition: view_group.cpp:77
virtual void Add(VIEW_ITEM *aItem)
Add an item to the group.
Definition: view_group.cpp:65
An abstract base class for deriving all objects that can be added to a VIEW.
Definition: view_item.h:86
bool IsBOARD_ITEM() const
Definition: view_item.h:102
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition: view.h:66
double GetScale() const
Definition: view.h:276
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition: view.cpp:530
virtual void SetScale(double aScale, VECTOR2D aAnchor={ 0, 0 })
Set the scaling factor, zooming around a given anchor point.
Definition: view.cpp:570
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:298
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:341
void UpdateAllLayersColor()
Apply the new coloring scheme to all layers.
Definition: view.cpp:775
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:420
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:1685
VECTOR2D ToWorld(const VECTOR2D &aCoord, bool aAbsolute=true) const
Converts a screen space point/vector to a point/vector in world space coordinates.
Definition: view.cpp:467
bool IsMirroredX() const
Return true if view is flipped across the X axis.
Definition: view.h:250
void Hide(VIEW_ITEM *aItem, bool aHide=true, bool aHideOverlay=false)
Temporarily hide the item in the view (e.g.
Definition: view.cpp:1633
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition: view.h:422
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition: view.h:220
void SetCenter(const VECTOR2D &aCenter)
Set the center point of the VIEW (i.e.
Definition: view.cpp:596
void MarkTargetDirty(int aTarget)
Set or clear target 'dirty' flag.
Definition: view.h:639
bool IsVisible(const VIEW_ITEM *aItem) const
Return information if the item is visible (or not).
Definition: view.cpp:1655
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition: view.cpp:1612
wxString AsString() const
Definition: kiid.cpp:356
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 & FrontMask()
Return a mask holding all technical layers and the external CU layer on front side.
Definition: lset.cpp:705
static const LSET & BackMask()
Return a mask holding all technical layers and the external CU layer on back side.
Definition: lset.cpp:712
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:296
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:326
static const LSET & AllLayersMask()
Definition: lset.cpp:624
static LSET AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition: lset.cpp:591
static const LSET & PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition: lset.cpp:680
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition: lset.h:63
bool IsExcluded() const
Definition: marker_base.h:93
const VECTOR2I & GetPos() const
Definition: marker_base.h:83
void ShapeToPolygon(SHAPE_LINE_CHAIN &aPolygon, int aScale=-1) const
Return the shape polygon in internal units in a SHAPE_LINE_CHAIN the coordinates are relatives to the...
Handle the data for a net.
Definition: netinfo.h:56
Definition: pad.h:54
LOCKING_OPTIONS m_LockingOptions
static TOOL_ACTION deleteLastPoint
Definition: pcb_actions.h:219
static TOOL_ACTION drag45Degree
Definition: pcb_actions.h:185
static TOOL_ACTION unrouteSelected
Removes all tracks from the selected items to the first pad.
Definition: pcb_actions.h:76
static TOOL_ACTION saveToLinkedDesignBlock
Definition: pcb_actions.h:459
static TOOL_ACTION grabUnconnected
Select and move nearest unconnected footprint from ratsnest of selection.
Definition: pcb_actions.h:91
static TOOL_ACTION filterSelection
Filter the items in the current selection (invokes dialog)
Definition: pcb_actions.h:103
static TOOL_ACTION highlightNet
Definition: pcb_actions.h:566
static TOOL_ACTION hideLocalRatsnest
Definition: pcb_actions.h:576
static TOOL_ACTION properties
Activation of the edit tool.
Definition: pcb_actions.h:168
static TOOL_ACTION selectOnSheetFromEeschema
Select all components on sheet from Eeschema crossprobing.
Definition: pcb_actions.h:94
static TOOL_ACTION selectConnection
Select tracks between junctions or expands an existing selection to pads or the entire connection.
Definition: pcb_actions.h:73
static TOOL_ACTION dragFreeAngle
Definition: pcb_actions.h:186
static TOOL_ACTION clearHighlight
Definition: pcb_actions.h:565
static TOOL_ACTION selectUnconnected
Select unconnected footprints from ratsnest of selection.
Definition: pcb_actions.h:88
static TOOL_ACTION unrouteSegment
Removes track segment from the selected item to the next segment.
Definition: pcb_actions.h:79
static TOOL_ACTION moveIndividually
move items one-by-one
Definition: pcb_actions.h:109
static TOOL_ACTION syncSelection
Sets selection to specified items, zooms to fit, if enabled.
Definition: pcb_actions.h:63
static TOOL_ACTION selectSameSheet
Select all components on the same sheet as the selected footprint.
Definition: pcb_actions.h:97
static TOOL_ACTION selectNet
Select all connections belonging to a single net.
Definition: pcb_actions.h:82
static TOOL_ACTION move
move or drag an item
Definition: pcb_actions.h:106
static TOOL_ACTION syncSelectionWithNets
Sets selection to specified items with connected nets, zooms to fit, if enabled.
Definition: pcb_actions.h:66
static TOOL_ACTION deselectNet
Remove all connections belonging to a single net from the active selection.
Definition: pcb_actions.h:85
static TOOL_ACTION placeLinkedDesignBlock
Definition: pcb_actions.h:458
static TOOL_ACTION selectOnSchematic
Select symbols/pins on schematic corresponding to selected footprints/pads.
Definition: pcb_actions.h:100
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...
PCBNEW_SETTINGS * GetPcbNewSettings() const
virtual PCB_LAYER_ID GetActiveLayer() const
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
BOARD * GetBoard() const
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.
bool IsReference() const
Definition: pcb_field.h:68
bool IsValue() const
Definition: pcb_field.h:69
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:53
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: pcb_group.cpp:266
static bool WithinScope(BOARD_ITEM *aItem, PCB_GROUP *aScope, bool isFootprintEditor)
Definition: pcb_group.cpp:151
static EDA_GROUP * TopLevelGroup(BOARD_ITEM *aItem, EDA_GROUP *aScope, bool isFootprintEditor)
Definition: pcb_group.cpp:145
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.
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...
bool itemPassesFilter(BOARD_ITEM *aItem, bool aMultiSelect)
void ExitGroup(bool aSelectGroup=false) override
Leave the currently-entered group.
bool isExpandableGraphicShape(const EDA_ITEM *aItem) const
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_SELECTION & RequestSelection(CLIENT_SELECTION_FILTER aClientFilter, bool aConfirmLockedItems=false)
Return the current selection, filtered according to aClientFilter.
int unrouteSelected(const TOOL_EVENT &aEvent)
Unroute the selected board connected items.
int unrouteSegment(const TOOL_EVENT &aEvent)
Unroute the selected track connected item.
SELECTION & selection() override
Return a reference to the selection.
int grabUnconnected(const TOOL_EVENT &aEvent)
Select and move other nearest footprint unconnected on same net as selected items.
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...
const GENERAL_COLLECTORS_GUIDE getCollectorsGuide() const
bool Selectable(const BOARD_ITEM *aItem, bool checkVisibilityOnly=false) const
void FilterCollectedItems(GENERAL_COLLECTOR &aCollector, bool aMultiSelect)
Apply the SELECTION_FITLER_OPTIONS to the collector.
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.
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 highlightInternal(EDA_ITEM *aItem, int aHighlightMode, bool aUsingOverlay)
PCB_BASE_FRAME * m_frame
PCB_SELECTION_FILTER_OPTIONS m_filter
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.
void FindItem(BOARD_ITEM *aItem)
Take necessary actions to mark an item as found.
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.
BOARD * board() const
KIGFX::VIEW_GROUP m_enteredGroupOverlay
void selectConnections(const std::vector< BOARD_ITEM * > &aItems)
int hitTestDistance(const VECTOR2I &aWhere, BOARD_ITEM *aItem, int aMaxDistance) const
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.
PCB_SELECTION m_selection
bool selectTableCells(PCB_TABLE *aTable)
void unhighlight(EDA_ITEM *aItem, int aHighlightMode, SELECTION *aGroup=nullptr) override
Unhighlight the item visually.
BOX2I GetBoundingBox() const override
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pcb_shape.cpp:230
std::vector< VECTOR2I > GetConnectionPoints() const
Definition: pcb_shape.cpp:260
int GetRowSpan() const
Definition: pcb_tablecell.h:71
int GetColSpan() const
Definition: pcb_tablecell.h:68
std::vector< PCB_TABLECELL * > GetCells() const
Definition: pcb_table.h:154
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pcb_track.cpp:1243
const VECTOR2I & GetStart() const
Definition: pcb_track.h:152
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:149
int GetDrill() const
Return the local drill setting for this PCB_VIA.
Definition: pcb_track.h:680
A small class to help profiling.
Definition: profile.h:49
void Start()
Start or restart the counter.
Definition: profile.h:77
double msecs(bool aSinceLast=false)
Definition: profile.h:149
bool RoutingInProgress()
Returns whether routing is currently active.
Definition: seg.h:42
static SEG::ecoord Square(int a)
Definition: seg.h:123
static SELECTION_CONDITION HasType(KICAD_T aType)
Create a functor that tests if among the selected items there is at least one of a given type.
static bool NotEmpty(const SELECTION &aSelection)
Test if there are any items selected.
static SELECTION_CONDITION MoreThan(int aNumber)
Create a functor that tests if the number of selected items is greater than the value given as parame...
static SELECTION_CONDITION Count(int aNumber)
Create a functor that tests if the number of selected items is equal to the value given as parameter.
static SELECTION_CONDITION OnlyTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if the selected items are only of given types.
bool m_multiple
Multiple selection mode is active.
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
bool doSelectionMenu(COLLECTOR *aCollector)
wxTimer m_disambiguateTimer
Timer to show the disambiguate menu.
bool m_drag_additive
Add multiple items to selection.
bool m_exclusive_or
Items' selection state should be toggled.
int AddItemsToSel(const TOOL_EVENT &aEvent)
int AddItemToSel(const TOOL_EVENT &aEvent)
int UpdateMenu(const TOOL_EVENT &aEvent)
Update a menu's state based on the current selection.
void setModifiersState(bool aShiftState, bool aCtrlState, bool aAltState)
Set the configuration of m_additive, m_subtractive, m_exclusive_or, m_skip_heuristics from the state ...
VECTOR2I m_originalCursor
Location of original cursor when starting click.
int SelectionMenu(const TOOL_EVENT &aEvent)
Show a popup menu to trim the COLLECTOR passed as aEvent's parameter down to a single item.
int RemoveItemsFromSel(const TOOL_EVENT &aEvent)
bool m_subtractive
Items should be removed from selection.
bool m_highlight_modifier
Select highlight net on left click.
bool m_skip_heuristics
Show disambiguation menu for all items under the cursor rather than trying to narrow them down first ...
int ReselectItem(const TOOL_EVENT &aEvent)
bool m_drag_subtractive
Remove multiple from selection.
bool m_additive
Items should be added to sel (instead of replacing).
bool hasModifier()
True if a selection modifier is enabled, false otherwise.
bool m_canceledMenu
Sets to true if the disambiguation menu was canceled.
void onDisambiguationExpire(wxTimerEvent &aEvent)
Start the process to show our disambiguation menu once the user has kept the mouse down for the minim...
virtual void Add(EDA_ITEM *aItem)
Definition: selection.cpp:42
const std::deque< EDA_ITEM * > GetItems() const
Definition: selection.h:126
void SetIsHover(bool aIsHover)
Definition: selection.h:84
virtual void Remove(EDA_ITEM *aItem)
Definition: selection.cpp:60
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition: selection.h:105
EDA_ITEM * Front() const
Definition: selection.h:177
virtual void Clear() override
Remove all the stored items from the group.
Definition: selection.h:98
int Size() const
Returns the number of selected parts.
Definition: selection.h:121
void ClearReferencePoint()
Definition: selection.cpp:184
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:115
std::vector< EDA_ITEM * > GetItemsSortedByTypeAndXY(bool leftBeforeRight=true, bool topBeforeBottom=true) const
Returns a copy of this selection of items sorted by their X then Y position.
Definition: selection.cpp:221
size_t CountType(KICAD_T aType) const
Definition: selection.cpp:155
bool Contains(EDA_ITEM *aItem) const
Definition: selection.cpp:84
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,...
MOUSE_DRAG_ACTION GetDragAction() const
Indicates whether a drag should draw a selection rectangle or drag selected (or unselected) objects.
Definition: tools_holder.h:144
bool ToolStackIsEmpty()
Definition: tools_holder.h:125
Represent a single user action.
Definition: tool_action.h:304
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:44
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:220
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:38
bool IsToolActive() const
Definition: tool_base.cpp:32
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
@ REDRAW
Full drawing refresh.
Definition: tool_base.h:83
@ MODEL_RELOAD
Model changes (the sheet for a schematic)
Definition: tool_base.h:80
Generic, UI-independent tool event.
Definition: tool_event.h:168
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
Definition: tool_event.cpp:82
T Parameter() const
Return a parameter assigned to the event.
Definition: tool_event.h:465
void SetPassEvent(bool aPass=true)
Definition: tool_event.h:253
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.
bool ProcessEvent(const TOOL_EVENT &aEvent)
Propagate an event to tools that requested events of matching type(s).
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:150
VECTOR2D GetMousePosition() const
bool PostAction(const std::string &aActionName, T aParam)
Run the specified action after the current action (coroutine) ends.
Definition: tool_manager.h:235
void VetoContextMenuMouseWarp()
Disable mouse warping after the current context menu is closed.
Definition: tool_manager.h:515
KIGFX::VIEW * GetView() const
Definition: tool_manager.h:395
Handle a list of polygons defining a copper zone.
Definition: zone.h:74
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:704
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:713
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:720
bool IsTeardropArea() const
Definition: zone.h:679
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.h:136
bool GetDoNotAllowZoneFills() const
Definition: zone.h:719
MOUSE_DRAG_ACTION
#define EXCLUDE_ZONES
#define IGNORE_NETS
Function GetConnectedItems() Returns a list of items connected to a source item aItem.
KICURSOR
Definition: cursors.h:44
#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)
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:88
#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.
@ FRAME_PCB_EDITOR
Definition: frame_type.h:42
@ FRAME_FOOTPRINT_VIEWER
Definition: frame_type.h:45
@ FRAME_FOOTPRINT_EDITOR
Definition: frame_type.h:43
a few functions useful in geometry calculations.
int m_DisambiguationMenuDelay
The number of milliseconds to wait in a click before showing a disambiguation menu.
double m_PcbSelectionVisibilityRatio
Board object selection visibility limit.
@ LAYER_FOOTPRINTS_FR
Show footprints on front.
Definition: layer_ids.h:258
@ LAYER_DRAW_BITMAPS
Draw images.
Definition: layer_ids.h:283
@ LAYER_FP_REFERENCES
Show footprints references (when texts are visible).
Definition: layer_ids.h:265
@ LAYER_DRC_EXCLUSION
Layer for DRC markers which have been individually excluded.
Definition: layer_ids.h:303
@ LAYER_ZONES
Control for copper zone opacity/visibility (color ignored).
Definition: layer_ids.h:294
@ LAYER_PADS
Meta control for all pads opacity/visibility (color ignored).
Definition: layer_ids.h:291
@ LAYER_TRACKS
Definition: layer_ids.h:266
@ LAYER_FP_TEXT
Definition: layer_ids.h:239
@ LAYER_FOOTPRINTS_BK
Show footprints on back.
Definition: layer_ids.h:259
@ LAYER_FP_VALUES
Show footprints values (when texts are visible).
Definition: layer_ids.h:262
@ LAYER_VIAS
Meta control for all vias opacity/visibility.
Definition: layer_ids.h:232
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ F_CrtYd
Definition: layer_ids.h:116
@ Edge_Cuts
Definition: layer_ids.h:112
@ F_SilkS
Definition: layer_ids.h:100
@ B_CrtYd
Definition: layer_ids.h:115
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ B_SilkS
Definition: layer_ids.h:101
@ PCB_LAYER_ID_COUNT
Definition: layer_ids.h:171
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:744
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
bool BoxHitTest(const VECTOR2I &aHitPoint, const BOX2I &aHittee, int aAccuracy)
Perform a point-to-box hit test.
@ REPAINT
Item needs to be redrawn.
Definition: view_item.h:58
@ TARGET_OVERLAY
Items that may change while the view stays the same (noncached)
Definition: definitions.h:39
STL namespace.
Class to handle a set of BOARD_ITEMs.
void connectedItemFilter(const VECTOR2I &, GENERAL_COLLECTOR &aCollector, PCB_SELECTION_TOOL *sTool)
const TOOL_ACTION * allowedActions[]
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.
TRACK_DRAG_ACTION
CITER next(CITER it)
Definition: ptree.cpp:124
Class that computes missing connections on a PCB.
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
static std::vector< KICAD_T > tableCellTypes
const TOOL_ACTION * allowedActions[]
SELECTION_MODE
const int scale
bool zoom_to_fit
Zoom to fit items (ignored if center_on_items is off).
Definition: app_settings.h:35
bool center_on_items
Automatically pan to cross-probed items.
Definition: app_settings.h:34
Struct that will be set with the result of the user choices in the dialog.
constexpr int mmToIU(double mm) const
Definition: base_units.h:92
const BOARD_ITEM * m_Item
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.
const int accuracy
int delta
@ TA_MOUSE_UP
Definition: tool_event.h:69
@ TA_MOUSE_WHEEL
Definition: tool_event.h:73
@ TC_ANY
Definition: tool_event.h:60
@ MD_ALT
Definition: tool_event.h:145
@ MD_CTRL
Definition: tool_event.h:144
@ MD_SHIFT
Definition: tool_event.h:143
@ BUT_MIDDLE
Definition: tool_event.h:134
@ BUT_LEFT
Definition: tool_event.h:132
@ BUT_RIGHT
Definition: tool_event.h:133
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:78
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:105
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:102
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition: typeinfo.h:91
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
@ TYPE_NOT_INIT
Definition: typeinfo.h:81
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:103
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:110
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition: typeinfo.h:93
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:107
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:92
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition: typeinfo.h:89
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition: typeinfo.h:90
@ NOT_USED
the 3d code uses this value
Definition: typeinfo.h:79
@ PCB_MARKER_T
class PCB_MARKER, a marker used to show something
Definition: typeinfo.h:99
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition: typeinfo.h:106
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition: typeinfo.h:95
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition: typeinfo.h:86
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:101
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition: typeinfo.h:94
@ PCB_NETINFO_T
class NETINFO_ITEM, a description of a net
Definition: typeinfo.h:109
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:104
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695
VECTOR2D ToVECTOR2D(const wxPoint &aPoint)
Definition: vector2wx.h:40