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