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 (C) 2018-2023 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>
30using namespace std::placeholders;
31#include <core/kicad_algo.h>
32#include <board.h>
34#include <board_item.h>
35#include <clipper.hpp>
36#include <pcb_bitmap.h>
37#include <pcb_track.h>
38#include <footprint.h>
39#include <pad.h>
40#include <pcb_group.h>
41#include <pcb_shape.h>
42#include <pcb_text.h>
43#include <pcb_textbox.h>
44#include <pcb_marker.h>
45#include <zone.h>
46#include <collectors.h>
50#include <view/view_controls.h>
52#include <painter.h>
53#include <router/router_tool.h>
54#include <pcbnew_settings.h>
55#include <tool/tool_event.h>
56#include <tool/tool_manager.h>
60#include <tools/pcb_actions.h>
65#include <wx/event.h>
66#include <wx/timer.h>
67#include <wx/log.h>
68#include <profile.h>
69#include <math/vector2wx.h>
70
71
73{
74public:
76 ACTION_MENU( true )
77 {
78 SetTitle( _( "Select" ) );
79
81
82 AppendSeparator();
83
86
87 // This could be enabled if we have better logic for picking the target net with the mouse
88 // Add( PCB_ACTIONS::deselectNet );
91
94 }
95
96private:
97 ACTION_MENU* create() const override
98 {
99 return new SELECT_MENU();
100 }
101};
102
103
108{
109public:
111};
112
113
115 SELECTION_TOOL( "pcbnew.InteractiveSelection" ),
116 m_frame( nullptr ),
117 m_isFootprintEditor( false ),
118 m_nonModifiedCursor( KICURSOR::ARROW ),
119 m_enteredGroup( nullptr ),
120 m_priv( std::make_unique<PRIV>() )
121{
122 m_filter.lockedItems = false;
123 m_filter.footprints = true;
124 m_filter.text = true;
125 m_filter.tracks = true;
126 m_filter.vias = true;
127 m_filter.pads = true;
128 m_filter.graphics = true;
129 m_filter.zones = true;
130 m_filter.keepouts = true;
131 m_filter.dimensions = true;
132 m_filter.otherItems = true;
133}
134
135
137{
140
141 Disconnect( wxEVT_TIMER, wxTimerEventHandler( PCB_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
142}
143
144
146{
147 PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
148
151 {
153 return true;
154 }
155
156 std::shared_ptr<SELECT_MENU> selectMenu = std::make_shared<SELECT_MENU>();
157 selectMenu->SetTool( this );
158 m_menu.RegisterSubMenu( selectMenu );
159
160 auto& menu = m_menu.GetMenu();
161
162 auto activeToolCondition =
163 [ frame ] ( const SELECTION& aSel )
164 {
165 return !frame->ToolStackIsEmpty();
166 };
167
168 auto haveHighlight =
169 [&]( const SELECTION& sel )
170 {
172
173 return !cfg->GetHighlightNetCodes().empty();
174 };
175
176 auto groupEnterCondition =
178
179 auto inGroupCondition =
180 [this] ( const SELECTION& )
181 {
182 return m_enteredGroup != nullptr;
183 };
184
186 {
187 menu.AddMenu( selectMenu.get(), SELECTION_CONDITIONS::NotEmpty );
188 menu.AddSeparator( 1000 );
189 }
190
191 // "Cancel" goes at the top of the context menu when a tool is active
192 menu.AddItem( ACTIONS::cancelInteractive, activeToolCondition, 1 );
193 menu.AddItem( PCB_ACTIONS::groupEnter, groupEnterCondition, 1 );
194 menu.AddItem( PCB_ACTIONS::groupLeave, inGroupCondition, 1 );
195 menu.AddItem( PCB_ACTIONS::clearHighlight, haveHighlight, 1 );
196
197 menu.AddSeparator( 1 );
198
199 if( frame )
201
202 m_disambiguateTimer.SetOwner( this );
203 Connect( wxEVT_TIMER, wxTimerEventHandler( PCB_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
204
205 return true;
206}
207
208
210{
211 m_frame = getEditFrame<PCB_BASE_FRAME>();
213
214 if( aReason != TOOL_BASE::REDRAW )
215 {
216 if( m_enteredGroup )
217 ExitGroup();
218
219 // Deselect any item being currently in edit, to avoid unexpected behavior and remove
220 // pointers to the selected items from containers.
221 ClearSelection( true );
222 }
223
224 if( aReason == TOOL_BASE::MODEL_RELOAD )
225 getView()->GetPainter()->GetSettings()->SetHighlight( false );
226
227 // Reinsert the VIEW_GROUP, in case it was removed from the VIEW
228 view()->Remove( &m_selection );
229 view()->Add( &m_selection );
230
233}
234
235
236void PCB_SELECTION_TOOL::OnIdle( wxIdleEvent& aEvent )
237{
239 {
240 wxMouseState keyboardState = wxGetMouseState();
241
242 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
243 keyboardState.AltDown() );
244
245 if( m_additive )
246 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ADD );
247 else if( m_subtractive )
248 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SUBTRACT );
249 else if( m_exclusive_or )
250 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::XOR );
251 else
253 }
254}
255
256
258{
259 // Main loop: keep receiving events
260 while( TOOL_EVENT* evt = Wait() )
261 {
264
265 // on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
266 setModifiersState( evt->Modifier( MD_SHIFT ), evt->Modifier( MD_CTRL ),
267 evt->Modifier( MD_ALT ) );
268
269 PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
270 bool brd_editor = frame && frame->IsType( FRAME_PCB_EDITOR );
272
273 // If the router tool is active, don't override
274 if( router && router->IsToolActive() && router->RoutingInProgress() )
275 {
276 evt->SetPassEvent();
277 }
278 else if( evt->IsMouseDown( BUT_LEFT ) )
279 {
280 // Avoid triggering when running under other tools
282
283 if( m_frame->ToolStackIsEmpty() && pt_tool && !pt_tool->HasPoint() )
284 {
286 m_disambiguateTimer.StartOnce( 500 );
287 }
288 }
289 else if( evt->IsClick( BUT_LEFT ) )
290 {
291 // If there is no disambiguation, this routine is still running and will
292 // register a `click` event when released
293 if( m_disambiguateTimer.IsRunning() )
294 {
295 m_disambiguateTimer.Stop();
296
297 // Single click? Select single object
298 if( m_highlight_modifier && brd_editor )
299 {
301 }
302 else
303 {
304 m_frame->FocusOnItem( nullptr );
305 selectPoint( evt->Position() );
306 }
307 }
308
309 m_canceledMenu = false;
310 }
311 else if( evt->IsClick( BUT_RIGHT ) )
312 {
313 m_disambiguateTimer.Stop();
314
315 // Right click? if there is any object - show the context menu
316 bool selectionCancelled = false;
317
318 if( m_selection.Empty() )
319 {
320 selectPoint( evt->Position(), false, &selectionCancelled );
321 m_selection.SetIsHover( true );
322 }
323
324 if( !selectionCancelled )
326 }
327 else if( evt->IsDblClick( BUT_LEFT ) )
328 {
329 m_disambiguateTimer.Stop();
330
331 // Double click? Display the properties window
332 m_frame->FocusOnItem( nullptr );
333
334 if( m_selection.Empty() )
335 selectPoint( evt->Position() );
336
337 if( m_selection.GetSize() == 1 && m_selection[0]->Type() == PCB_GROUP_T )
338 EnterGroup();
339 else
341 }
342 else if( evt->IsDblClick( BUT_MIDDLE ) )
343 {
344 // Middle double click? Do zoom to fit or zoom to objects
345 if( evt->Modifier( MD_CTRL ) ) // Is CTRL key down?
347 else
349 }
350 else if( evt->IsDrag( BUT_LEFT ) )
351 {
352 m_disambiguateTimer.Stop();
353
354 // Is another tool already moving a new object? Don't allow a drag start
355 if( !m_selection.Empty() && m_selection[0]->HasFlag( IS_NEW | IS_MOVING ) )
356 {
357 evt->SetPassEvent();
358 continue;
359 }
360
361 // Drag with LMB? Select multiple objects (or at least draw a selection box)
362 // or drag them
363 m_frame->FocusOnItem( nullptr );
365
366 if( hasModifier() || dragAction == MOUSE_DRAG_ACTION::SELECT )
367 {
369 }
370 else if( m_selection.Empty() && dragAction != MOUSE_DRAG_ACTION::DRAG_ANY )
371 {
373 }
374 else
375 {
376 // Don't allow starting a drag from a zone filled area that isn't already selected
377 auto zoneFilledAreaFilter =
378 []( const VECTOR2I& aWhere, GENERAL_COLLECTOR& aCollector,
379 PCB_SELECTION_TOOL* aTool )
380 {
381 int accuracy = KiROUND( 5 * aCollector.GetGuide()->OnePixelInIU() );
382 std::set<EDA_ITEM*> remove;
383
384 for( EDA_ITEM* item : aCollector )
385 {
386 if( item->Type() == PCB_ZONE_T )
387 {
388 ZONE* zone = static_cast<ZONE*>( item );
389
390 if( !zone->HitTestForCorner( aWhere, accuracy * 2 )
391 && !zone->HitTestForEdge( aWhere, accuracy ) )
392 {
393 remove.insert( zone );
394 }
395 }
396 }
397
398 for( EDA_ITEM* item : remove )
399 aCollector.Remove( item );
400 };
401
402 // See if we can drag before falling back to selectMultiple()
403 bool doDrag = false;
404
405 if( evt->HasPosition() )
406 {
407 if( m_selection.Empty()
408 && selectPoint( evt->DragOrigin(), false, nullptr, zoneFilledAreaFilter ) )
409 {
410 m_selection.SetIsHover( true );
411 doDrag = true;
412 }
413 // Check if dragging has started within any of selected items bounding box.
414 else if( selectionContains( evt->DragOrigin() ) )
415 {
416 doDrag = true;
417 }
418 }
419
420 if( doDrag )
421 {
422 bool haveTrack = m_selection.GetSize() == 1
423 && dynamic_cast<PCB_TRACK*>( m_selection.GetItem( 0 ) );
424
425 if( haveTrack && trackDragAction == TRACK_DRAG_ACTION::DRAG )
427 else if( haveTrack && trackDragAction == TRACK_DRAG_ACTION::DRAG_FREE_ANGLE )
429 else
431 }
432 else
433 {
434 // Otherwise drag a selection box
436 }
437 }
438 }
439 else if( evt->IsCancel() )
440 {
441 m_disambiguateTimer.Stop();
442 m_frame->FocusOnItem( nullptr );
443
444 if( !GetSelection().Empty() )
445 {
447 }
448 else if( evt->FirstResponder() == this && evt->GetCommandId() == (int) WXK_ESCAPE )
449 {
450 if( m_enteredGroup )
451 {
452 ExitGroup();
453 }
454 else
455 {
457
459 controller->ClearHighlight( *evt );
460 }
461 }
462 }
463 else
464 {
465 evt->SetPassEvent();
466 }
467
468
470 {
471 // move cursor prediction
472 if( !hasModifier()
473 && dragAction == MOUSE_DRAG_ACTION::DRAG_SELECTED
474 && !m_selection.Empty()
475 && evt->HasPosition()
476 && selectionContains( evt->Position() ) )
477 {
478 m_nonModifiedCursor = KICURSOR::MOVING;
479 }
480 else
481 {
482 m_nonModifiedCursor = KICURSOR::ARROW;
483 }
484 }
485 }
486
487 // Shutting down; clear the selection
489 m_disambiguateTimer.Stop();
490
491 return 0;
492}
493
494
496{
497 wxCHECK_RET( m_selection.GetSize() == 1 && m_selection[0]->Type() == PCB_GROUP_T,
498 wxT( "EnterGroup called when selection is not a single group" ) );
499 PCB_GROUP* aGroup = static_cast<PCB_GROUP*>( m_selection[0] );
500
501 if( m_enteredGroup != nullptr )
502 ExitGroup();
503
505 m_enteredGroup = aGroup;
508 {
509 select( titem );
510 } );
511
513
514 view()->Hide( m_enteredGroup, true );
517}
518
519
520void PCB_SELECTION_TOOL::ExitGroup( bool aSelectGroup )
521{
522 // Only continue if there is a group entered
523 if( m_enteredGroup == nullptr )
524 return;
525
527 view()->Hide( m_enteredGroup, false );
529
530 if( aSelectGroup )
531 {
534 }
535
537 m_enteredGroup = nullptr;
539}
540
541
543{
544 return m_selection;
545}
546
547
549 bool aConfirmLockedItems )
550{
551 bool selectionEmpty = m_selection.Empty();
552 m_selection.SetIsHover( selectionEmpty );
553
554 if( selectionEmpty )
555 {
556 m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true, aClientFilter );
558 }
559
560 if( aClientFilter )
561 {
562 enum DISPOSITION { BEFORE = 1, AFTER, BOTH };
563
564 std::map<EDA_ITEM*, DISPOSITION> itemDispositions;
566 GENERAL_COLLECTOR collector;
567
568 collector.SetGuide( &guide );
569
570 for( EDA_ITEM* item : m_selection )
571 {
572 collector.Append( item );
573 itemDispositions[ item ] = BEFORE;
574 }
575
576 aClientFilter( VECTOR2I(), collector, this );
577
578 for( EDA_ITEM* item : collector )
579 {
580 if( itemDispositions.count( item ) )
581 itemDispositions[ item ] = BOTH;
582 else
583 itemDispositions[ item ] = AFTER;
584 }
585
586 // Unhighlight the BEFORE items before highlighting the AFTER items.
587 // This is so that in the case of groups, if aClientFilter replaces a selection
588 // with the enclosing group, the unhighlight of the element doesn't undo the
589 // recursive highlighting of that element by the group.
590
591 for( std::pair<EDA_ITEM* const, DISPOSITION> itemDisposition : itemDispositions )
592 {
593 EDA_ITEM* item = itemDisposition.first;
594 DISPOSITION disposition = itemDisposition.second;
595
596 if( disposition == BEFORE )
598 }
599
600 for( std::pair<EDA_ITEM* const, DISPOSITION> itemDisposition : itemDispositions )
601 {
602 EDA_ITEM* item = itemDisposition.first;
603 DISPOSITION disposition = itemDisposition.second;
604
605 // Note that we must re-highlight even previously-highlighted items
606 // (ie: disposition BOTH) in case we removed any of their children.
607 if( disposition == AFTER || disposition == BOTH )
608 highlight( item, SELECTED, &m_selection );
609 }
610
612 }
613
614 if( aConfirmLockedItems )
615 {
616 std::vector<BOARD_ITEM*> lockedItems;
617
618 for( EDA_ITEM* item : m_selection )
619 {
620 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
621
622 if( boardItem->Type() == PCB_GROUP_T )
623 {
624 PCB_GROUP* group = static_cast<PCB_GROUP*>( boardItem );
625 bool lockedDescendant = false;
626
627 group->RunOnDescendants(
628 [&lockedDescendant]( BOARD_ITEM* child )
629 {
630 if( child->IsLocked() )
631 lockedDescendant = true;
632 } );
633
634 if( lockedDescendant )
635 lockedItems.push_back( group );
636 }
637 else if( boardItem->IsLocked() )
638 {
639 lockedItems.push_back( boardItem );
640 }
641 }
642
643 if( !lockedItems.empty() )
644 {
645 DIALOG_LOCKED_ITEMS_QUERY dlg( frame(), (int) lockedItems.size() );
646
647 switch( dlg.ShowModal() )
648 {
649 case wxID_OK:
650 // remove locked items from selection
651 for( BOARD_ITEM* item : lockedItems )
652 unselect( item );
653
654 break;
655
656 case wxID_CANCEL:
657 // cancel operation
659 break;
660
661 case wxID_APPLY:
662 // continue with operation with current selection
663 break;
664 }
665 }
666 }
667
668 return m_selection;
669}
670
671
673{
674 GENERAL_COLLECTORS_GUIDE guide( board()->GetVisibleLayers(),
675 (PCB_LAYER_ID) view()->GetTopLayer(), view() );
676
677 bool padsDisabled = !board()->IsElementVisible( LAYER_PADS );
678
679 // account for the globals
680 guide.SetIgnoreMTextsMarkedNoShow( ! board()->IsElementVisible( LAYER_MOD_TEXT_INVISIBLE ) );
681 guide.SetIgnoreMTextsOnBack( ! board()->IsElementVisible( LAYER_MOD_TEXT ) );
682 guide.SetIgnoreMTextsOnFront( ! board()->IsElementVisible( LAYER_MOD_TEXT ) );
683 guide.SetIgnoreModulesOnBack( ! board()->IsElementVisible( LAYER_MOD_BK ) );
684 guide.SetIgnoreModulesOnFront( ! board()->IsElementVisible( LAYER_MOD_FR ) );
685 guide.SetIgnorePadsOnBack( padsDisabled || ! board()->IsElementVisible( LAYER_PAD_BK ) );
686 guide.SetIgnorePadsOnFront( padsDisabled || ! board()->IsElementVisible( LAYER_PAD_FR ) );
687 guide.SetIgnoreThroughHolePads( padsDisabled || ! board()->IsElementVisible( LAYER_PADS_TH ) );
688 guide.SetIgnoreModulesVals( ! board()->IsElementVisible( LAYER_MOD_VALUES ) );
689 guide.SetIgnoreModulesRefs( ! board()->IsElementVisible( LAYER_MOD_REFERENCES ) );
690 guide.SetIgnoreThroughVias( ! board()->IsElementVisible( LAYER_VIAS ) );
691 guide.SetIgnoreBlindBuriedVias( ! board()->IsElementVisible( LAYER_VIAS ) );
692 guide.SetIgnoreMicroVias( ! board()->IsElementVisible( LAYER_VIAS ) );
693 guide.SetIgnoreTracks( ! board()->IsElementVisible( LAYER_TRACKS ) );
694
695 return guide;
696}
697
698
700{
702}
703
704
705bool PCB_SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag,
706 bool* aSelectionCancelledFlag,
707 CLIENT_SELECTION_FILTER aClientFilter )
708{
710 GENERAL_COLLECTOR collector;
711 const PCB_DISPLAY_OPTIONS& displayOpts = m_frame->GetDisplayOptions();
712
713 guide.SetIgnoreZoneFills( displayOpts.m_ZoneDisplayMode != ZONE_DISPLAY_MODE::SHOW_FILLED );
714
716 ExitGroup();
717
720 aWhere, guide );
721
722 // Remove unselectable items
723 for( int i = collector.GetCount() - 1; i >= 0; --i )
724 {
725 if( !Selectable( collector[ i ] ) || ( aOnDrag && collector[i]->IsLocked() ) )
726 collector.Remove( i );
727 }
728
730
731 // Allow the client to do tool- or action-specific filtering to see if we can get down
732 // to a single item
733 if( aClientFilter )
734 aClientFilter( aWhere, collector, this );
735
736 FilterCollectorForHierarchy( collector, false );
737
738 // Apply the stateful filter
739 FilterCollectedItems( collector, false );
740
741 // For subtracting, we only want items that are selected
742 if( m_subtractive )
743 {
744 for( int i = collector.GetCount() - 1; i >= 0; --i )
745 {
746 if( !collector[i]->IsSelected() )
747 collector.Remove( i );
748 }
749 }
750
751 // Apply some ugly heuristics to avoid disambiguation menus whenever possible
752 if( collector.GetCount() > 1 && !m_skip_heuristics )
753 {
754 try
755 {
756 GuessSelectionCandidates( collector, aWhere );
757 }
758 catch( const ClipperLib::clipperException& exc )
759 {
760 wxLogWarning( wxS( "Exception \"%s\" occurred attemption to guess selection "
761 "candidates." ), exc.what() );
762 return false;
763 }
764 }
765
766 // If still more than one item we're going to have to ask the user.
767 if( collector.GetCount() > 1 )
768 {
769 if( aOnDrag )
771
772 if( !doSelectionMenu( &collector ) )
773 {
774 if( aSelectionCancelledFlag )
775 *aSelectionCancelledFlag = true;
776
777 return false;
778 }
779 }
780
781 int addedCount = 0;
782 bool anySubtracted = false;
783
785 {
786 if( m_selection.GetSize() > 0 )
787 {
788 ClearSelection( true /*quiet mode*/ );
789 anySubtracted = true;
790 }
791 }
792
793 if( collector.GetCount() > 0 )
794 {
795 for( int i = 0; i < collector.GetCount(); ++i )
796 {
797 if( m_subtractive || ( m_exclusive_or && collector[i]->IsSelected() ) )
798 {
799 unselect( collector[i] );
800 anySubtracted = true;
801 }
802 else
803 {
804 select( collector[i] );
805 addedCount++;
806 }
807 }
808 }
809
810 if( addedCount == 1 )
811 {
813 return true;
814 }
815 else if( addedCount > 1 )
816 {
818 return true;
819 }
820 else if( anySubtracted )
821 {
823 return true;
824 }
825
826 return false;
827}
828
829
830bool PCB_SELECTION_TOOL::selectCursor( bool aForceSelect, CLIENT_SELECTION_FILTER aClientFilter )
831{
832 if( aForceSelect || m_selection.Empty() )
833 {
834 ClearSelection( true /*quiet mode*/ );
835 selectPoint( getViewControls()->GetCursorPosition( false ), false, nullptr, aClientFilter );
836 }
837
838 return !m_selection.Empty();
839}
840
841
842// Some navigation actions are allowed in selectMultiple
852 &ACTIONS::zoomFitObjects, nullptr };
853
854
856{
857 bool cancelled = false; // Was the tool cancelled while it was running?
858 m_multiple = true; // Multiple selection mode is active
860
862 view->Add( &area );
863
864 bool anyAdded = false;
865 bool anySubtracted = false;
866
867 while( TOOL_EVENT* evt = Wait() )
868 {
869 int width = area.GetEnd().x - area.GetOrigin().x;
870
871 /* Selection mode depends on direction of drag-selection:
872 * Left > Right : Select objects that are fully enclosed by selection
873 * Right > Left : Select objects that are crossed by selection
874 */
875 bool greedySelection = width >= 0 ? false : true;
876
877 if( view->IsMirroredX() )
878 greedySelection = !greedySelection;
879
880 m_frame->GetCanvas()->SetCurrentCursor( !greedySelection ? KICURSOR::SELECT_WINDOW
881 : KICURSOR::SELECT_LASSO );
882
883 if( evt->IsCancelInteractive() || evt->IsActivate() )
884 {
885 cancelled = true;
886 break;
887 }
888
889 if( evt->IsDrag( BUT_LEFT ) )
890 {
892 {
893 if( m_selection.GetSize() > 0 )
894 {
895 anySubtracted = true;
896 ClearSelection( true /*quiet mode*/ );
897 }
898 }
899
900 // Start drawing a selection box
901 area.SetOrigin( evt->DragOrigin() );
902 area.SetEnd( evt->Position() );
905 area.SetExclusiveOr( false );
906
907 view->SetVisible( &area, true );
908 view->Update( &area );
909 getViewControls()->SetAutoPan( true );
910 }
911
912 if( evt->IsMouseUp( BUT_LEFT ) )
913 {
914 getViewControls()->SetAutoPan( false );
915
916 // End drawing the selection box
917 view->SetVisible( &area, false );
918
919 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> candidates;
920 BOX2I selectionBox = area.ViewBBox();
921 view->Query( selectionBox, candidates ); // Get the list of nearby items
922
923 int height = area.GetEnd().y - area.GetOrigin().y;
924
925 // Construct a BOX2I to determine BOARD_ITEM selection
926 BOX2I selectionRect( area.GetOrigin(), VECTOR2I( width, height ) );
927
928 selectionRect.Normalize();
929
930 GENERAL_COLLECTOR collector;
931 GENERAL_COLLECTOR padsCollector;
932 std::set<BOARD_ITEM*> group_items;
933
934 for( PCB_GROUP* group : board()->Groups() )
935 {
936 // The currently entered group does not get limited
937 if( m_enteredGroup == group )
938 continue;
939
940 std::unordered_set<BOARD_ITEM*>& newset = group->GetItems();
941
942 // If we are not greedy and have selected the whole group, add just one item
943 // to allow it to be promoted to the group later
944 if( !greedySelection && selectionRect.Contains( group->GetBoundingBox() )
945 && newset.size() )
946 {
947 for( BOARD_ITEM* group_item : newset )
948 {
949 if( Selectable( group_item ) )
950 collector.Append( *newset.begin() );
951 }
952 }
953
954 for( BOARD_ITEM* group_item : newset )
955 group_items.emplace( group_item );
956 }
957
958 for( const KIGFX::VIEW::LAYER_ITEM_PAIR& candidate : candidates )
959 {
960 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( candidate.first );
961
962 if( item && Selectable( item ) && item->HitTest( selectionRect, !greedySelection )
963 && ( greedySelection || !group_items.count( item ) ) )
964 {
965 if( item->Type() == PCB_PAD_T && !m_isFootprintEditor )
966 padsCollector.Append( item );
967 else
968 collector.Append( item );
969 }
970 }
971
972 // Apply the stateful filter
973 FilterCollectedItems( collector, true );
974
975 FilterCollectorForHierarchy( collector, true );
976
977 // If we selected nothing but pads, allow them to be selected
978 if( collector.GetCount() == 0 )
979 {
980 collector = padsCollector;
981 FilterCollectedItems( collector, true );
982 FilterCollectorForHierarchy( collector, true );
983 }
984
985 for( EDA_ITEM* i : collector )
986 {
987 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
988
989 if( m_subtractive || ( m_exclusive_or && item->IsSelected() ) )
990 {
991 unselect( item );
992 anySubtracted = true;
993 }
994 else
995 {
996 select( item );
997 anyAdded = true;
998 }
999 }
1000
1001 m_selection.SetIsHover( false );
1002
1003 // Inform other potentially interested tools
1004 if( anyAdded )
1006 else if( anySubtracted )
1008
1009 break; // Stop waiting for events
1010 }
1011
1012 // Allow some actions for navigation
1013 for( int i = 0; allowedActions[i]; ++i )
1014 {
1015 if( evt->IsAction( allowedActions[i] ) )
1016 {
1017 evt->SetPassEvent();
1018 break;
1019 }
1020 }
1021 }
1022
1023 getViewControls()->SetAutoPan( false );
1024
1025 // Stop drawing the selection box
1026 view->Remove( &area );
1027 m_multiple = false; // Multiple selection mode is inactive
1028
1029 if( !cancelled )
1031
1033
1034 return cancelled;
1035}
1036
1037
1039{
1040 wxMouseState keyboardState = wxGetMouseState();
1041
1042 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
1043 keyboardState.AltDown() );
1044
1045 m_skip_heuristics = true;
1047 m_skip_heuristics = false;
1048
1049 return 0;
1050}
1051
1052
1053
1055{
1057
1058 selectCursor( false, aClientFilter );
1059
1060 return 0;
1061}
1062
1063
1065{
1067
1068 return 0;
1069}
1070
1071
1073{
1075
1076 // hold all visible items
1077 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
1078
1079 // Filter the view items based on the selection box
1080 BOX2I selectionBox;
1081
1082 // Intermediate step to allow filtering against hierarchy
1083 GENERAL_COLLECTOR collection;
1084
1085 selectionBox.SetMaximum();
1086 view->Query( selectionBox, selectedItems ); // Get the list of selected items
1087
1088 for( const KIGFX::VIEW::LAYER_ITEM_PAIR& item_pair : selectedItems )
1089 {
1090 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( item_pair.first );
1091
1092 if( !item || !Selectable( item ) || !itemPassesFilter( item, true ) )
1093 continue;
1094
1095 collection.Append( item );
1096 }
1097
1098 FilterCollectorForHierarchy( collection, true );
1099
1100 for( EDA_ITEM* item : collection )
1101 select( item );
1102
1104
1106
1107 return 0;
1108}
1109
1110
1112 PCB_SELECTION_TOOL* sTool )
1113{
1114 // Narrow the collection down to a single BOARD_CONNECTED_ITEM for each represented net.
1115 // All other items types are removed.
1116 std::set<int> representedNets;
1117
1118 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
1119 {
1120 BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( aCollector[i] );
1121
1122 if( !item )
1123 aCollector.Remove( i );
1124 else if ( representedNets.count( item->GetNetCode() ) )
1125 aCollector.Remove( i );
1126 else
1127 representedNets.insert( item->GetNetCode() );
1128 }
1129}
1130
1131
1133{
1134 std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
1135
1136 // Get all footprints and pads
1137 std::vector<BOARD_CONNECTED_ITEM*> toUnroute;
1138
1139 for( EDA_ITEM* item : selectedItems )
1140 {
1141 if( item->Type() == PCB_FOOTPRINT_T )
1142 {
1143 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
1144 toUnroute.push_back( pad );
1145 }
1146 else if( BOARD_CONNECTED_ITEM::ClassOf( item ) )
1147 {
1148 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
1149 }
1150 }
1151
1152 // Clear selection so we don't delete our footprints/pads
1153 ClearSelection( true );
1154
1155 // Get the tracks on our list of pads, then delete them
1158
1159 // Reselect our footprint/pads as they were in our original selection
1160 for( EDA_ITEM* item : selectedItems )
1161 {
1162 if( item->Type() == PCB_FOOTPRINT_T || item->Type() == PCB_PAD_T )
1163 select( item );
1164 }
1165
1166 return 0;
1167}
1168
1169
1171{
1172 unsigned initialCount = 0;
1173
1174 for( const EDA_ITEM* item : m_selection.GetItems() )
1175 {
1176 if( item->Type() == PCB_FOOTPRINT_T || BOARD_CONNECTED_ITEM::ClassOf( item ) )
1177 initialCount++;
1178 }
1179
1180 if( initialCount == 0 )
1182
1183 m_frame->SetStatusText( _( "Select/Expand Connection..." ) );
1184
1185 for( STOP_CONDITION stopCondition : { STOP_AT_JUNCTION, STOP_AT_PAD, STOP_NEVER } )
1186 {
1187 std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
1188
1189 for( EDA_ITEM* item : selectedItems )
1190 item->ClearTempFlags();
1191
1192 std::vector<BOARD_CONNECTED_ITEM*> startItems;
1193
1194 for( EDA_ITEM* item : selectedItems )
1195 {
1196 if( item->Type() == PCB_FOOTPRINT_T )
1197 {
1198 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
1199
1200 for( PAD* pad : footprint->Pads() )
1201 startItems.push_back( pad );
1202 }
1203 else if( BOARD_CONNECTED_ITEM::ClassOf( item ) )
1204 {
1205 startItems.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
1206 }
1207 }
1208
1209 selectAllConnectedTracks( startItems, stopCondition );
1210
1211 if( m_selection.GetItems().size() > initialCount )
1212 break;
1213 }
1214
1215 m_frame->SetStatusText( wxEmptyString );
1216
1217 // Inform other potentially interested tools
1219
1220 return 0;
1221}
1222
1223
1225 const std::vector<BOARD_CONNECTED_ITEM*>& aStartItems, STOP_CONDITION aStopCondition )
1226{
1227 const LSET allCuMask = LSET::AllCuMask();
1228
1229 PROF_TIMER refreshTimer;
1230 double refreshIntervalMs = 500; // Refresh display with this interval to indicate progress
1231 int lastSelectionSize = (int) m_selection.GetSize();
1232
1233 auto connectivity = board()->GetConnectivity();
1234
1235 std::map<VECTOR2I, std::vector<PCB_TRACK*>> trackMap;
1236 std::map<VECTOR2I, PCB_VIA*> viaMap;
1237 std::map<VECTOR2I, PAD*> padMap;
1238 std::set<PAD*> startPadSet;
1239 std::vector<BOARD_CONNECTED_ITEM*> cleanupItems;
1240 std::vector<std::pair<VECTOR2I, LSET>> activePts;
1241
1242 for( BOARD_CONNECTED_ITEM* startItem : aStartItems )
1243 {
1244 // Track starting pads
1245 if( startItem->Type() == PCB_PAD_T )
1246 startPadSet.insert( static_cast<PAD*>( startItem ) );
1247 }
1248
1249 for( BOARD_CONNECTED_ITEM* startItem : aStartItems )
1250 {
1251 if( startItem->HasFlag( SKIP_STRUCT ) ) // Skip already visited items
1252 continue;
1253
1254 auto connectedItems = connectivity->GetConnectedItems( startItem,
1256
1257 // Build maps of connected items
1258 for( BOARD_CONNECTED_ITEM* item : connectedItems )
1259 {
1260 switch( item->Type() )
1261 {
1262 case PCB_ARC_T:
1263 case PCB_TRACE_T:
1264 {
1265 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
1266 trackMap[track->GetStart()].push_back( track );
1267 trackMap[track->GetEnd()].push_back( track );
1268 break;
1269 }
1270
1271 case PCB_VIA_T:
1272 {
1273 PCB_VIA* via = static_cast<PCB_VIA*>( item );
1274 viaMap[via->GetStart()] = via;
1275 break;
1276 }
1277
1278 case PCB_PAD_T:
1279 {
1280 PAD* pad = static_cast<PAD*>( item );
1281 padMap[pad->GetPosition()] = pad;
1282 break;
1283 }
1284
1285 default:
1286 break;
1287 }
1288 }
1289
1290 // Set up the initial active points
1291 switch( startItem->Type() )
1292 {
1293 case PCB_ARC_T:
1294 case PCB_TRACE_T:
1295 {
1296 PCB_TRACK* track = static_cast<PCB_TRACK*>( startItem );
1297
1298 activePts.push_back( { track->GetStart(), track->GetLayerSet() } );
1299 activePts.push_back( { track->GetEnd(), track->GetLayerSet() } );
1300 break;
1301 }
1302
1303 case PCB_VIA_T:
1304 activePts.push_back( { startItem->GetPosition(), startItem->GetLayerSet() } );
1305 break;
1306
1307 case PCB_PAD_T:
1308 activePts.push_back( { startItem->GetPosition(), startItem->GetLayerSet() } );
1309 break;
1310
1311 default:
1312 break;
1313 }
1314
1315 bool expand = true;
1316 int failSafe = 0;
1317
1318 // Iterative push from all active points
1319 while( expand && failSafe++ < 100000 )
1320 {
1321 expand = false;
1322
1323 for( int i = (int) activePts.size() - 1; i >= 0; --i )
1324 {
1325 VECTOR2I pt = activePts[i].first;
1326 LSET layerSetCu = activePts[i].second & allCuMask;
1327
1328 auto viaIt = viaMap.find( pt );
1329 auto padIt = padMap.find( pt );
1330
1331 bool gotVia = ( viaIt != viaMap.end() )
1332 && ( layerSetCu & ( viaIt->second->GetLayerSet() ) ).any();
1333
1334 bool gotPad = ( padIt != padMap.end() )
1335 && ( layerSetCu & ( padIt->second->GetLayerSet() ) ).any();
1336
1337 bool gotNonStartPad =
1338 gotPad && ( startPadSet.find( padIt->second ) == startPadSet.end() );
1339
1340 if( aStopCondition == STOP_AT_JUNCTION )
1341 {
1342 size_t pt_count = 0;
1343
1344 for( PCB_TRACK* track : trackMap[pt] )
1345 {
1346 if( track->GetStart() != track->GetEnd()
1347 && layerSetCu.Contains( track->GetLayer() ) )
1348 {
1349 pt_count++;
1350 }
1351 }
1352
1353 if( pt_count > 2 || gotVia || gotNonStartPad )
1354 {
1355 activePts.erase( activePts.begin() + i );
1356 continue;
1357 }
1358 }
1359 else if( aStopCondition == STOP_AT_PAD )
1360 {
1361 if( gotNonStartPad )
1362 {
1363 activePts.erase( activePts.begin() + i );
1364 continue;
1365 }
1366 }
1367
1368 if( gotPad )
1369 {
1370 PAD* pad = padIt->second;
1371
1372 if( !pad->HasFlag( SKIP_STRUCT ) )
1373 {
1374 pad->SetFlags( SKIP_STRUCT );
1375 cleanupItems.push_back( pad );
1376
1377 activePts.push_back( { pad->GetPosition(), pad->GetLayerSet() } );
1378 expand = true;
1379 }
1380 }
1381
1382 for( PCB_TRACK* track : trackMap[pt] )
1383 {
1384 if( !layerSetCu.Contains( track->GetLayer() ) )
1385 continue;
1386
1387 if( !track->IsSelected() )
1388 select( track );
1389
1390 if( !track->HasFlag( SKIP_STRUCT ) )
1391 {
1392 track->SetFlags( SKIP_STRUCT );
1393 cleanupItems.push_back( track );
1394
1395 if( track->GetStart() == pt )
1396 activePts.push_back( { track->GetEnd(), track->GetLayerSet() } );
1397 else
1398 activePts.push_back( { track->GetStart(), track->GetLayerSet() } );
1399
1400 expand = true;
1401 }
1402 }
1403
1404 if( viaMap.count( pt ) )
1405 {
1406 PCB_VIA* via = viaMap[pt];
1407
1408 if( !via->IsSelected() )
1409 select( via );
1410
1411 if( !via->HasFlag( SKIP_STRUCT ) )
1412 {
1413 via->SetFlags( SKIP_STRUCT );
1414 cleanupItems.push_back( via );
1415
1416 activePts.push_back( { via->GetPosition(), via->GetLayerSet() } );
1417 expand = true;
1418 }
1419 }
1420
1421 activePts.erase( activePts.begin() + i );
1422 }
1423
1424 // Refresh display for the feel of progress
1425 if( refreshTimer.msecs() >= refreshIntervalMs )
1426 {
1427 if( m_selection.Size() != lastSelectionSize )
1428 {
1430 lastSelectionSize = m_selection.Size();
1431 }
1432
1433 refreshTimer.Start();
1434 }
1435 }
1436 }
1437
1438 for( BOARD_CONNECTED_ITEM* item : cleanupItems )
1439 item->ClearFlags( SKIP_STRUCT );
1440}
1441
1442
1444{
1445 // Get all pads
1446 std::vector<PAD*> pads;
1447
1448 for( EDA_ITEM* item : m_selection.GetItems() )
1449 {
1450 if( item->Type() == PCB_FOOTPRINT_T )
1451 {
1452 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
1453 pads.push_back( pad );
1454 }
1455 else if( item->Type() == PCB_PAD_T )
1456 {
1457 pads.push_back( static_cast<PAD*>( item ) );
1458 }
1459 }
1460
1461 // Select every footprint on the end of the ratsnest for each pad in our selection
1462 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
1463
1464 for( PAD* pad : pads )
1465 {
1466 for( const CN_EDGE& edge : conn->GetRatsnestForPad( pad ) )
1467 {
1468 BOARD_CONNECTED_ITEM* sourceParent = edge.GetSourceNode()->Parent();
1469 BOARD_CONNECTED_ITEM* targetParent = edge.GetTargetNode()->Parent();
1470
1471 if( sourceParent == pad )
1472 {
1473 if( targetParent->Type() == PCB_PAD_T )
1474 select( static_cast<PAD*>( targetParent )->GetParent() );
1475 }
1476 else if( targetParent == pad )
1477 {
1478 if( sourceParent->Type() == PCB_PAD_T )
1479 select( static_cast<PAD*>( sourceParent )->GetParent() );
1480 }
1481 }
1482 }
1483
1484 return 0;
1485}
1486
1487
1489{
1490 PCB_SELECTION originalSelection = m_selection;
1491
1492 // Get all pads
1493 std::vector<PAD*> pads;
1494
1495 for( EDA_ITEM* item : m_selection.GetItems() )
1496 {
1497 if( item->Type() == PCB_FOOTPRINT_T )
1498 {
1499 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
1500 pads.push_back( pad );
1501 }
1502 else if( item->Type() == PCB_PAD_T )
1503 {
1504 pads.push_back( static_cast<PAD*>( item ) );
1505 }
1506 }
1507
1509
1510 // Select every footprint on the end of the ratsnest for each pad in our selection
1511 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
1512
1513 for( PAD* pad : pads )
1514 {
1515 const std::vector<CN_EDGE> edges = conn->GetRatsnestForPad( pad );
1516
1517 // Need to have something unconnected to grab
1518 if( edges.size() == 0 )
1519 continue;
1520
1521 double currentDistance = DBL_MAX;
1522 FOOTPRINT* nearest = nullptr;
1523
1524 // Check every ratsnest line for the nearest one
1525 for( const CN_EDGE& edge : edges )
1526 {
1527 // Figure out if we are the source or the target node on the ratnest
1528 const CN_ANCHOR* other = edge.GetSourceNode()->Parent() == pad ? edge.GetTargetNode().get()
1529 : edge.GetSourceNode().get();
1530
1531 // We only want to grab footprints, so the ratnest has to point to a pad
1532 if( other->Parent()->Type() != PCB_PAD_T )
1533 continue;
1534
1535 if( edge.GetLength() < currentDistance )
1536 {
1537 currentDistance = edge.GetLength();
1538 nearest = static_cast<PAD*>( other->Parent() )->GetParent();
1539 }
1540 }
1541
1542 if( nearest != nullptr )
1543 select( nearest );
1544 }
1545
1547
1548 return 0;
1549}
1550
1551
1552void PCB_SELECTION_TOOL::SelectAllItemsOnNet( int aNetCode, bool aSelect )
1553{
1554 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
1555
1556 for( BOARD_ITEM* item : conn->GetNetItems( aNetCode, { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } ) )
1557 {
1558 if( itemPassesFilter( item, true ) )
1559 aSelect ? select( item ) : unselect( item );
1560 }
1561}
1562
1563
1565{
1566 bool select = aEvent.IsAction( &PCB_ACTIONS::selectNet );
1567
1568 // If we've been passed an argument, just select that netcode1
1569 int netcode = (int) aEvent.Parameter<intptr_t>();
1570
1571 if( netcode > 0 )
1572 {
1573 SelectAllItemsOnNet( netcode, select );
1574
1575 // Inform other potentially interested tools
1576 if( m_selection.Size() > 0 )
1578 else
1580
1581 return 0;
1582 }
1583
1584 if( !selectCursor() )
1585 return 0;
1586
1587 // copy the selection, since we're going to iterate and modify
1589
1590 for( EDA_ITEM* i : selection )
1591 {
1592 BOARD_CONNECTED_ITEM* connItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( i );
1593
1594 if( connItem )
1595 SelectAllItemsOnNet( connItem->GetNetCode(), select );
1596 }
1597
1598 // Inform other potentially interested tools
1599 if( m_selection.Size() > 0 )
1601 else
1603
1604 return 0;
1605}
1606
1607
1609{
1610 std::vector<BOARD_ITEM*> footprints;
1611
1612 // store all footprints that are on that sheet path
1613 for( FOOTPRINT* footprint : board()->Footprints() )
1614 {
1615 if( footprint == nullptr )
1616 continue;
1617
1618 wxString footprint_path = footprint->GetPath().AsString().BeforeLast( '/' );
1619
1620 if( footprint_path.IsEmpty() )
1621 footprint_path += '/';
1622
1623 if( footprint_path == aSheetPath )
1624 footprints.push_back( footprint );
1625 }
1626
1627 for( BOARD_ITEM* i : footprints )
1628 {
1629 if( i != nullptr )
1630 select( i );
1631 }
1632
1633 selectConnections( footprints );
1634}
1635
1636
1637void PCB_SELECTION_TOOL::selectConnections( const std::vector<BOARD_ITEM*>& aItems )
1638{
1639 // Generate a list of all pads, and of all nets they belong to.
1640 std::list<int> netcodeList;
1641 std::vector<BOARD_CONNECTED_ITEM*> padList;
1642
1643 for( BOARD_ITEM* item : aItems )
1644 {
1645 switch( item->Type() )
1646 {
1647 case PCB_FOOTPRINT_T:
1648 {
1649 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
1650 {
1651 if( pad->IsConnected() )
1652 {
1653 netcodeList.push_back( pad->GetNetCode() );
1654 padList.push_back( pad );
1655 }
1656 }
1657
1658 break;
1659 }
1660
1661 case PCB_PAD_T:
1662 {
1663 PAD* pad = static_cast<PAD*>( item );
1664
1665 if( pad->IsConnected() )
1666 {
1667 netcodeList.push_back( pad->GetNetCode() );
1668 padList.push_back( pad );
1669 }
1670
1671 break;
1672 }
1673
1674 default:
1675 break;
1676 }
1677 }
1678
1679 // Sort for binary search
1680 std::sort( padList.begin(), padList.end() );
1681
1682 // remove all duplicates
1683 netcodeList.sort();
1684 netcodeList.unique();
1685
1687
1688 // now we need to find all footprints that are connected to each of these nets then we need
1689 // to determine if these footprints are in the list of footprints
1690 std::vector<int> removeCodeList;
1691 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
1692
1693 for( int netCode : netcodeList )
1694 {
1695 for( BOARD_CONNECTED_ITEM* pad : conn->GetNetItems( netCode, { PCB_PAD_T } ) )
1696 {
1697 if( !std::binary_search( padList.begin(), padList.end(), pad ) )
1698 {
1699 // if we cannot find the pad in the padList then we can assume that that pad
1700 // should not be used, therefore invalidate this netcode.
1701 removeCodeList.push_back( netCode );
1702 break;
1703 }
1704 }
1705 }
1706
1707 for( int removeCode : removeCodeList )
1708 netcodeList.remove( removeCode );
1709
1710 std::unordered_set<BOARD_ITEM*> localConnectionList;
1711
1712 for( int netCode : netcodeList )
1713 {
1714 for( BOARD_ITEM* item : conn->GetNetItems( netCode, { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } ) )
1715 localConnectionList.insert( item );
1716 }
1717
1718 for( BOARD_ITEM* item : localConnectionList )
1719 select( item );
1720}
1721
1722
1724{
1725 std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
1726
1727 if( items )
1728 doSyncSelection( *items, false );
1729
1730 return 0;
1731}
1732
1733
1735{
1736 std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
1737
1738 if( items )
1739 doSyncSelection( *items, true );
1740
1741 return 0;
1742}
1743
1744
1745void PCB_SELECTION_TOOL::doSyncSelection( const std::vector<BOARD_ITEM*>& aItems, bool aWithNets )
1746{
1747 ClearSelection( true /*quiet mode*/ );
1748
1749 // Perform individual selection of each item before processing the event.
1750 for( BOARD_ITEM* item : aItems )
1751 select( item );
1752
1753 if( aWithNets )
1754 selectConnections( aItems );
1755
1757
1758 if( bbox.GetWidth() != 0 && bbox.GetHeight() != 0 )
1759 {
1761 {
1763 ZoomFitCrossProbeBBox( bbox );
1764
1765 m_frame->FocusOnLocation( bbox.Centre() );
1766 }
1767 }
1768
1770
1772
1773 if( m_selection.Size() > 0 )
1775}
1776
1777
1779{
1780 ClearSelection( true /*quiet mode*/ );
1781 wxString sheetPath = *aEvent.Parameter<wxString*>();
1782
1783 selectAllItemsOnSheet( sheetPath );
1784
1786
1787 if( m_selection.Size() > 0 )
1789
1790 return 0;
1791}
1792
1793
1795{
1796 // this function currently only supports footprints since they are only on one sheet.
1797 EDA_ITEM* item = m_selection.Front();
1798
1799 if( !item )
1800 return 0;
1801
1802 if( item->Type() != PCB_FOOTPRINT_T )
1803 return 0;
1804
1805 FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( item );
1806
1807 if( !footprint || footprint->GetPath().empty() )
1808 return 0;
1809
1810 ClearSelection( true /*quiet mode*/ );
1811
1812 // get the sheet path only.
1813 wxString sheetPath = footprint->GetPath().AsString().BeforeLast( '/' );
1814
1815 if( sheetPath.IsEmpty() )
1816 sheetPath += '/';
1817
1818 selectAllItemsOnSheet( sheetPath );
1819
1820 // Inform other potentially interested tools
1821 if( m_selection.Size() > 0 )
1823
1824 return 0;
1825}
1826
1827
1829{
1830 // Should recalculate the view to zoom in on the selection.
1831 BOX2I selectionBox = m_selection.GetBoundingBox();
1833
1834 VECTOR2D screenSize = view->ToWorld( ToVECTOR2D( m_frame->GetCanvas()->GetClientSize() ), false );
1835 screenSize.x = std::max( 10.0, screenSize.x );
1836 screenSize.y = std::max( 10.0, screenSize.y );
1837
1838 if( selectionBox.GetWidth() != 0 || selectionBox.GetHeight() != 0 )
1839 {
1840 VECTOR2D vsize = selectionBox.GetSize();
1841 double scale = view->GetScale()
1842 / std::max( fabs( vsize.x / screenSize.x ), fabs( vsize.y / screenSize.y ) );
1843 view->SetScale( scale );
1844 view->SetCenter( selectionBox.Centre() );
1845 view->Add( &m_selection );
1846 }
1847
1849}
1850
1851
1853{
1854 // Should recalculate the view to zoom in on the bbox.
1856
1857 if( aBBox.GetWidth() == 0 )
1858 return;
1859
1860 BOX2I bbox = aBBox;
1861 bbox.Normalize();
1862
1863 //#define DEFAULT_PCBNEW_CODE // Un-comment for normal full zoom KiCad algorithm
1864#ifdef DEFAULT_PCBNEW_CODE
1865 auto bbSize = bbox.Inflate( bbox.GetWidth() * 0.2f ).GetSize();
1866 auto screenSize = view->ToWorld( GetCanvas()->GetClientSize(), false );
1867
1868 // The "fabs" on x ensures the right answer when the view is flipped
1869 screenSize.x = std::max( 10.0, fabs( screenSize.x ) );
1870 screenSize.y = std::max( 10.0, screenSize.y );
1871 double ratio = std::max( fabs( bbSize.x / screenSize.x ), fabs( bbSize.y / screenSize.y ) );
1872
1873 // Try not to zoom on every cross-probe; it gets very noisy
1874 if( crossProbingSettings.zoom_to_fit && ( ratio < 0.5 || ratio > 1.0 ) )
1875 view->SetScale( view->GetScale() / ratio );
1876#endif // DEFAULT_PCBNEW_CODE
1877
1878#ifndef DEFAULT_PCBNEW_CODE // Do the scaled zoom
1879 auto bbSize = bbox.Inflate( KiROUND( bbox.GetWidth() * 0.2 ) ).GetSize();
1880 VECTOR2D screenSize = view->ToWorld( ToVECTOR2D( m_frame->GetCanvas()->GetClientSize() ), false );
1881
1882 // This code tries to come up with a zoom factor that doesn't simply zoom in
1883 // to the cross probed component, but instead shows a reasonable amount of the
1884 // circuit around it to provide context. This reduces or eliminates the need
1885 // to manually change the zoom because it's too close.
1886
1887 // Using the default text height as a constant to compare against, use the
1888 // height of the bounding box of visible items for a footprint to figure out
1889 // if this is a big footprint (like a processor) or a small footprint (like a resistor).
1890 // This ratio is not useful by itself as a scaling factor. It must be "bent" to
1891 // provide good scaling at varying component sizes. Bigger components need less
1892 // scaling than small ones.
1893 double currTextHeight = pcbIUScale.mmToIU( DEFAULT_TEXT_SIZE );
1894
1895 double compRatio = bbSize.y / currTextHeight; // Ratio of component to text height
1896
1897 // This will end up as the scaling factor we apply to "ratio".
1898 double compRatioBent = 1.0;
1899
1900 // This is similar to the original KiCad code that scaled the zoom to make sure
1901 // components were visible on screen. It's simply a ratio of screen size to
1902 // component size, and its job is to zoom in to make the component fullscreen.
1903 // Earlier in the code the component BBox is given a 20% margin to add some
1904 // breathing room. We compare the height of this enlarged component bbox to the
1905 // default text height. If a component will end up with the sides clipped, we
1906 // adjust later to make sure it fits on screen.
1907 //
1908 // The "fabs" on x ensures the right answer when the view is flipped
1909 screenSize.x = std::max( 10.0, fabs( screenSize.x ) );
1910 screenSize.y = std::max( 10.0, screenSize.y );
1911 double ratio = std::max( -1.0, fabs( bbSize.y / screenSize.y ) );
1912
1913 // Original KiCad code for how much to scale the zoom
1914 double kicadRatio =
1915 std::max( fabs( bbSize.x / screenSize.x ), fabs( bbSize.y / screenSize.y ) );
1916
1917 // LUT to scale zoom ratio to provide reasonable schematic context. Must work
1918 // with footprints of varying sizes (e.g. 0402 package and 200 pin BGA).
1919 // "first" is used as the input and "second" as the output
1920 //
1921 // "first" = compRatio (footprint height / default text height)
1922 // "second" = Amount to scale ratio by
1923 std::vector<std::pair<double, double>> lut{
1924 { 1, 8 }, { 1.5, 5 }, { 3, 3 }, { 4.5, 2.5 }, { 8, 2.0 },
1925 { 12, 1.7 }, { 16, 1.5 }, { 24, 1.3 }, { 32, 1.0 },
1926 };
1927
1928
1929 std::vector<std::pair<double, double>>::iterator it;
1930
1931 compRatioBent = lut.back().second; // Large component default
1932
1933 if( compRatio >= lut.front().first )
1934 {
1935 // Use LUT to do linear interpolation of "compRatio" within "first", then
1936 // use that result to linearly interpolate "second" which gives the scaling
1937 // factor needed.
1938
1939 for( it = lut.begin(); it < lut.end() - 1; it++ )
1940 {
1941 if( it->first <= compRatio && next( it )->first >= compRatio )
1942 {
1943 double diffx = compRatio - it->first;
1944 double diffn = next( it )->first - it->first;
1945
1946 compRatioBent = it->second + ( next( it )->second - it->second ) * diffx / diffn;
1947 break; // We have our interpolated value
1948 }
1949 }
1950 }
1951 else
1952 {
1953 compRatioBent = lut.front().second; // Small component default
1954 }
1955
1956 // If the width of the part we're probing is bigger than what the screen width will be
1957 // after the zoom, then punt and use the KiCad zoom algorithm since it guarantees the
1958 // part's width will be encompassed within the screen. This will apply to parts that
1959 // are much wider than they are tall.
1960
1961 if( bbSize.x > screenSize.x * ratio * compRatioBent )
1962 {
1963 // Use standard KiCad zoom algorithm for parts too wide to fit screen/
1964 ratio = kicadRatio;
1965 compRatioBent = 1.0; // Reset so we don't modify the "KiCad" ratio
1966 wxLogTrace( "CROSS_PROBE_SCALE",
1967 "Part TOO WIDE for screen. Using normal KiCad zoom ratio: %1.5f", ratio );
1968 }
1969
1970 // Now that "compRatioBent" holds our final scaling factor we apply it to the original
1971 // fullscreen zoom ratio to arrive at the final ratio itself.
1972 ratio *= compRatioBent;
1973
1974 bool alwaysZoom = false; // DEBUG - allows us to minimize zooming or not
1975
1976 // Try not to zoom on every cross-probe; it gets very noisy
1977 if( ( ratio < 0.5 || ratio > 1.0 ) || alwaysZoom )
1978 view->SetScale( view->GetScale() / ratio );
1979#endif // ifndef DEFAULT_PCBNEW_CODE
1980}
1981
1982
1984{
1985 bool cleared = false;
1986
1987 if( m_selection.GetSize() > 0 )
1988 {
1989 // Don't fire an event now; most of the time it will be redundant as we're about to
1990 // fire a SelectedEvent.
1991 cleared = true;
1992 ClearSelection( true /*quiet mode*/ );
1993 }
1994
1995 if( aItem )
1996 {
1997 switch( aItem->Type() )
1998 {
1999 case PCB_NETINFO_T:
2000 {
2001 int netCode = static_cast<NETINFO_ITEM*>( aItem )->GetNetCode();
2002
2003 if( netCode > 0 )
2004 {
2005 SelectAllItemsOnNet( netCode, true );
2006 m_frame->FocusOnLocation( aItem->GetCenter() );
2007 }
2008 break;
2009 }
2010
2011 default:
2012 select( aItem );
2013 m_frame->FocusOnLocation( aItem->GetPosition() );
2014 }
2015
2016 // If the item has a bounding box, then zoom out if needed
2017 if( aItem->GetBoundingBox().GetHeight() > 0 && aItem->GetBoundingBox().GetWidth() > 0 )
2018 {
2019 // This adds some margin
2020 double marginFactor = 2;
2021
2022 KIGFX::PCB_VIEW* pcbView = canvas()->GetView();
2023 BOX2D screenBox = pcbView->GetViewport();
2024 VECTOR2I screenSize = screenBox.GetSize();
2025 BOX2I screenRect( screenBox.GetOrigin(), screenSize / marginFactor );
2026
2027 if( !screenRect.Contains( aItem->GetBoundingBox() ) )
2028 {
2029 double scaleX = screenSize.x / static_cast<double>( aItem->GetBoundingBox().GetWidth() );
2030 double scaleY = screenSize.y / static_cast<double>( aItem->GetBoundingBox().GetHeight() );
2031
2032 scaleX /= marginFactor;
2033 scaleY /= marginFactor;
2034
2035 double scale = scaleX > scaleY ? scaleY : scaleX;
2036
2037 if( scale < 1 ) // Don't zoom in, only zoom out
2038 {
2039 pcbView->SetScale( pcbView->GetScale() * ( scale ) );
2040
2041 //Let's refocus because there is an algorithm to avoid dialogs in there.
2042 m_frame->FocusOnLocation( aItem->GetCenter() );
2043 }
2044 }
2045 }
2046 // Inform other potentially interested tools
2048 }
2049 else if( cleared )
2050 {
2052 }
2053
2055}
2056
2057
2063static bool itemIsIncludedByFilter( const BOARD_ITEM& aItem, const BOARD& aBoard,
2064 const DIALOG_FILTER_SELECTION::OPTIONS& aFilterOptions )
2065{
2066 switch( aItem.Type() )
2067 {
2068 case PCB_FOOTPRINT_T:
2069 {
2070 const FOOTPRINT& footprint = static_cast<const FOOTPRINT&>( aItem );
2071
2072 return aFilterOptions.includeModules
2073 && ( aFilterOptions.includeLockedModules || !footprint.IsLocked() );
2074 }
2075
2076 case PCB_TRACE_T:
2077 case PCB_ARC_T:
2078 return aFilterOptions.includeTracks;
2079
2080 case PCB_VIA_T:
2081 return aFilterOptions.includeVias;
2082
2083 case PCB_ZONE_T:
2084 return aFilterOptions.includeZones;
2085
2086 case PCB_SHAPE_T:
2087 case PCB_TARGET_T:
2088 case PCB_DIM_ALIGNED_T:
2089 case PCB_DIM_CENTER_T:
2090 case PCB_DIM_RADIAL_T:
2092 case PCB_DIM_LEADER_T:
2093 if( aItem.GetLayer() == Edge_Cuts )
2094 return aFilterOptions.includeBoardOutlineLayer;
2095 else
2096 return aFilterOptions.includeItemsOnTechLayers;
2097
2098 case PCB_TEXT_T:
2099 case PCB_TEXTBOX_T:
2100 return aFilterOptions.includePcbTexts;
2101
2102 default:
2103 // Filter dialog is inclusive, not exclusive. If it's not included, then it doesn't
2104 // get selected.
2105 return false;
2106 }
2107}
2108
2109
2111{
2112 const BOARD& board = *getModel<BOARD>();
2113 DIALOG_FILTER_SELECTION::OPTIONS& opts = m_priv->m_filterOpts;
2114 DIALOG_FILTER_SELECTION dlg( m_frame, opts );
2115
2116 const int cmd = dlg.ShowModal();
2117
2118 if( cmd != wxID_OK )
2119 return 0;
2120
2121 // copy current selection
2122 std::deque<EDA_ITEM*> selection = m_selection.GetItems();
2123
2124 ClearSelection( true /*quiet mode*/ );
2125
2126 // re-select items from the saved selection according to the dialog options
2127 for( EDA_ITEM* i : selection )
2128 {
2129 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
2130 bool include = itemIsIncludedByFilter( *item, board, opts );
2131
2132 if( include )
2133 select( item );
2134 }
2135
2137
2138 return 0;
2139}
2140
2141
2143{
2144 if( aCollector.GetCount() == 0 )
2145 return;
2146
2147 std::set<BOARD_ITEM*> rejected;
2148
2149 for( EDA_ITEM* i : aCollector )
2150 {
2151 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
2152
2153 if( !itemPassesFilter( item, aMultiSelect ) )
2154 rejected.insert( item );
2155 }
2156
2157 for( BOARD_ITEM* item : rejected )
2158 aCollector.Remove( item );
2159}
2160
2161
2162bool PCB_SELECTION_TOOL::itemPassesFilter( BOARD_ITEM* aItem, bool aMultiSelect )
2163{
2164 if( !m_filter.lockedItems )
2165 {
2166 if( aItem->IsLocked() || ( aItem->GetParent() && aItem->GetParent()->IsLocked() ) )
2167 {
2168 if( aItem->Type() == PCB_PAD_T && !aMultiSelect )
2169 {
2170 // allow a single pad to be selected -- there are a lot of operations that
2171 // require this so we allow this one inconsistency
2172 }
2173 else
2174 {
2175 return false;
2176 }
2177 }
2178 }
2179
2180 switch( aItem->Type() )
2181 {
2182 case PCB_FOOTPRINT_T:
2183 if( !m_filter.footprints )
2184 return false;
2185
2186 break;
2187
2188 case PCB_PAD_T:
2189 if( !m_filter.pads )
2190 return false;
2191
2192 break;
2193
2194 case PCB_TRACE_T:
2195 case PCB_ARC_T:
2196 if( !m_filter.tracks )
2197 return false;
2198
2199 break;
2200
2201 case PCB_VIA_T:
2202 if( !m_filter.vias )
2203 return false;
2204
2205 break;
2206
2207 case PCB_ZONE_T:
2208 {
2209 ZONE* zone = static_cast<ZONE*>( aItem );
2210
2211 if( ( !m_filter.zones && !zone->GetIsRuleArea() )
2212 || ( !m_filter.keepouts && zone->GetIsRuleArea() ) )
2213 {
2214 return false;
2215 }
2216
2217 break;
2218 }
2219
2220 case PCB_SHAPE_T:
2221 case PCB_TARGET_T:
2222 if( !m_filter.graphics )
2223 return false;
2224
2225 break;
2226
2227 case PCB_BITMAP_T:
2228 if( !m_filter.graphics )
2229 return false;
2230
2231 // a bitmap living in a footprint must not be selected inside the board editor
2232 if( !m_isFootprintEditor && aItem->GetParentFootprint() )
2233 return false;
2234
2235 break;
2236
2237 case PCB_TEXT_T:
2238 case PCB_TEXTBOX_T:
2239 if( !m_filter.text )
2240 return false;
2241
2242 break;
2243
2244 case PCB_DIM_ALIGNED_T:
2245 case PCB_DIM_CENTER_T:
2246 case PCB_DIM_RADIAL_T:
2248 case PCB_DIM_LEADER_T:
2249 if( !m_filter.dimensions )
2250 return false;
2251
2252 break;
2253
2254 default:
2255 if( !m_filter.otherItems )
2256 return false;
2257 }
2258
2259 return true;
2260}
2261
2262
2264{
2265 if( m_selection.Empty() )
2266 return;
2267
2268 while( m_selection.GetSize() )
2270
2271 view()->Update( &m_selection );
2272
2273 m_selection.SetIsHover( false );
2275
2276 // Inform other potentially interested tools
2277 if( !aQuietMode )
2278 {
2281 }
2282}
2283
2284
2286{
2288
2289 bool enteredGroupFound = false;
2290
2291 INSPECTOR_FUNC inspector =
2292 [&]( EDA_ITEM* item, void* testData )
2293 {
2294 if( item->IsSelected() )
2295 {
2296 EDA_ITEM* parent = item->GetParent();
2297
2298 // Let selected parents handle their children.
2299 if( parent && parent->IsSelected() )
2300 return INSPECT_RESULT::CONTINUE;
2301
2302 highlight( item, SELECTED, &m_selection );
2303 }
2304
2305 if( item == m_enteredGroup )
2306 {
2307 item->SetFlags( ENTERED );
2308 enteredGroupFound = true;
2309 }
2310 else
2311 {
2312 item->ClearFlags( ENTERED );
2313 }
2314
2315 return INSPECT_RESULT::CONTINUE;
2316 };
2317
2320
2321 if( !enteredGroupFound )
2322 {
2324 m_enteredGroup = nullptr;
2325 }
2326}
2327
2328
2329bool PCB_SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibilityOnly ) const
2330{
2331 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
2332
2333 auto visibleLayers =
2334 [&]()
2335 {
2337 {
2338 LSET set;
2339
2340 for( PCB_LAYER_ID layer : LSET::AllLayersMask().Seq() )
2341 set.set( layer, view()->IsLayerVisible( layer ) );
2342
2343 return set;
2344 }
2345 else
2346 {
2347 return board()->GetVisibleLayers();
2348 }
2349 };
2350
2351 if( settings->GetHighContrast() )
2352 {
2353 const std::set<int> activeLayers = settings->GetHighContrastLayers();
2354 bool onActiveLayer = false;
2355
2356 for( int layer : activeLayers )
2357 {
2358 // NOTE: Only checking the regular layers (not GAL meta-layers)
2359 if( layer < PCB_LAYER_ID_COUNT && aItem->IsOnLayer( ToLAYER_ID( layer ), true ) )
2360 {
2361 onActiveLayer = true;
2362 break;
2363 }
2364 }
2365
2366 if( !onActiveLayer ) // We do not want to select items that are in the background
2367 return false;
2368 }
2369
2370 if( aItem->Type() == PCB_FOOTPRINT_T )
2371 {
2372 // In footprint editor, we do not want to select the footprint itself.
2374 return false;
2375
2376 // Allow selection of footprints if some part of the footprint is visible.
2377 const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
2378 LSET boardSide = footprint->IsFlipped() ? LSET::BackMask() : LSET::FrontMask();
2379
2380 if( !( visibleLayers() & boardSide ).any() && !m_skip_heuristics )
2381 return false;
2382
2383 // If the footprint has no items except the reference and value fields, include the
2384 // footprint in the selections.
2385 if( footprint->GraphicalItems().empty()
2386 && footprint->Pads().empty()
2387 && footprint->Zones().empty() )
2388 {
2389 return true;
2390 }
2391
2392 for( const BOARD_ITEM* item : footprint->GraphicalItems() )
2393 {
2394 if( Selectable( item, true ) )
2395 return true;
2396 }
2397
2398 for( const PAD* pad : footprint->Pads() )
2399 {
2400 if( Selectable( pad, true ) )
2401 return true;
2402 }
2403
2404 for( const ZONE* zone : footprint->Zones() )
2405 {
2406 if( Selectable( zone, true ) )
2407 return true;
2408 }
2409
2410 return false;
2411 }
2412 else if( aItem->Type() == PCB_GROUP_T )
2413 {
2414 PCB_GROUP* group = const_cast<PCB_GROUP*>( static_cast<const PCB_GROUP*>( aItem ) );
2415
2416 // Similar to logic for footprint, a group is selectable if any of its members are.
2417 // (This recurses.)
2418 for( BOARD_ITEM* item : group->GetItems() )
2419 {
2420 if( Selectable( item, true ) )
2421 return true;
2422 }
2423
2424 return false;
2425 }
2426
2427 const ZONE* zone = nullptr;
2428 const PCB_VIA* via = nullptr;
2429 const PAD* pad = nullptr;
2430 const PCB_TEXT* text = nullptr;
2431
2432 switch( aItem->Type() )
2433 {
2434 case PCB_ZONE_T:
2436 return false;
2437
2438 zone = static_cast<const ZONE*>( aItem );
2439
2440 // A teardrop is modelled as a property of a via, pad or the board (for track-to-track
2441 // teardrops). The underlying zone is only an implementation detail.
2442 if( zone->IsTeardropArea() && !board()->LegacyTeardrops() )
2443 return false;
2444
2445 // A footprint zone is only selectable within the footprint editor
2446 if( zone->GetParent()
2447 && zone->GetParent()->Type() == PCB_FOOTPRINT_T
2449 && !checkVisibilityOnly )
2450 {
2451 return false;
2452 }
2453
2454 // zones can exist on multiple layers!
2455 if( !( zone->GetLayerSet() & visibleLayers() ).any() )
2456 return false;
2457
2458 break;
2459
2460 case PCB_TRACE_T:
2461 case PCB_ARC_T:
2463 return false;
2464
2466 {
2467 if( !view()->IsLayerVisible( aItem->GetLayer() ) )
2468 return false;
2469 }
2470 else
2471 {
2472 if( !board()->IsLayerVisible( aItem->GetLayer() ) )
2473 return false;
2474 }
2475
2476 break;
2477
2478 case PCB_VIA_T:
2479 if( !board()->IsElementVisible( LAYER_VIAS ) )
2480 return false;
2481
2482 via = static_cast<const PCB_VIA*>( aItem );
2483
2484 // For vias it is enough if only one of its layers is visible
2485 if( !( visibleLayers() & via->GetLayerSet() ).any() )
2486 return false;
2487
2488 break;
2489
2490 case PCB_TEXT_T:
2491 text = static_cast<const PCB_TEXT*>( aItem );
2492
2494 {
2495 if( !text->IsVisible() && !view()->IsLayerVisible( LAYER_MOD_TEXT_INVISIBLE ) )
2496 return false;
2497
2498 if( !view()->IsLayerVisible( text->GetLayer() ) )
2499 return false;
2500 }
2501 else if( aItem->GetParentFootprint() )
2502 {
2503 if( !view()->IsVisible( text ) )
2504 return false;
2505
2506 if( !board()->IsLayerVisible( text->GetLayer() ) )
2507 return false;
2508
2509 int controlLayer = UNDEFINED_LAYER;
2510
2511 switch( text->GetType() )
2512 {
2513 case PCB_TEXT::TEXT_is_REFERENCE: controlLayer = LAYER_MOD_REFERENCES; break;
2514 case PCB_TEXT::TEXT_is_VALUE: controlLayer = LAYER_MOD_VALUES; break;
2515 case PCB_TEXT::TEXT_is_DIVERS: controlLayer = LAYER_MOD_TEXT; break;
2516 }
2517
2518 if( controlLayer == UNDEFINED_LAYER )
2519 return false;
2520
2521 if( !view()->IsLayerVisible( controlLayer ) )
2522 return false;
2523 }
2524
2525 break;
2526
2527 case PCB_SHAPE_T:
2528 case PCB_TEXTBOX_T:
2530 {
2531 if( !view()->IsLayerVisible( aItem->GetLayer() ) )
2532 return false;
2533 }
2534 else if( aItem->GetParentFootprint() )
2535 {
2536 // Footprint shape selections are only allowed in footprint editor mode.
2537 if( !checkVisibilityOnly )
2538 return false;
2539
2540 if( !board()->IsLayerVisible( aItem->GetLayer() ) )
2541 return false;
2542 }
2543
2544 break;
2545
2546 case PCB_PAD_T:
2547 pad = static_cast<const PAD*>( aItem );
2548
2549 if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
2550 {
2551 // Check render mode (from the Items tab) first
2553 return false;
2554
2555 // A pad's hole is visible on every layer the pad is visible on plus many layers the
2556 // pad is not visible on -- so we only need to check for any visible hole layers.
2557 if( !( visibleLayers() & LSET::PhysicalLayersMask() ).any() )
2558 return false;
2559 }
2560 else
2561 {
2562 // Check render mode (from the Items tab) first
2563 if( pad->IsOnLayer( F_Cu ) && !board()->IsElementVisible( LAYER_PAD_FR ) )
2564 return false;
2565 else if( pad->IsOnLayer( B_Cu ) && !board()->IsElementVisible( LAYER_PAD_BK ) )
2566 return false;
2567
2568 if( !( pad->GetLayerSet() & visibleLayers() ).any() )
2569 return false;
2570 }
2571
2572 break;
2573
2574 // These are not selectable
2575 case PCB_NETINFO_T:
2576 case NOT_USED:
2577 case TYPE_NOT_INIT:
2578 return false;
2579
2580 default: // Suppress warnings
2581 break;
2582 }
2583
2584 return true;
2585}
2586
2587
2589{
2590 if( aItem->IsSelected() )
2591 return;
2592
2593 if( aItem->Type() == PCB_PAD_T )
2594 {
2595 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem->GetParent() );
2596
2597 if( m_selection.Contains( footprint ) )
2598 return;
2599 }
2600
2601 if( m_enteredGroup &&
2602 !PCB_GROUP::WithinScope( static_cast<BOARD_ITEM*>( aItem ), m_enteredGroup,
2604 {
2605 ExitGroup();
2606 }
2607
2608 highlight( aItem, SELECTED, &m_selection );
2609}
2610
2611
2613{
2614 unhighlight( aItem, SELECTED, &m_selection );
2615}
2616
2617
2618void PCB_SELECTION_TOOL::highlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
2619{
2620 if( aGroup )
2621 aGroup->Add( aItem );
2622
2623 highlightInternal( aItem, aMode, aGroup != nullptr );
2624 view()->Update( aItem, KIGFX::REPAINT );
2625
2626 // Many selections are very temporal and updating the display each time just
2627 // creates noise.
2628 if( aMode == BRIGHTENED )
2630}
2631
2632
2633void PCB_SELECTION_TOOL::highlightInternal( EDA_ITEM* aItem, int aMode, bool aUsingOverlay )
2634{
2635 if( aMode == SELECTED )
2636 aItem->SetSelected();
2637 else if( aMode == BRIGHTENED )
2638 aItem->SetBrightened();
2639
2640 if( aUsingOverlay && aMode != BRIGHTENED )
2641 view()->Hide( aItem, true ); // Hide the original item, so it is shown only on overlay
2642
2643 if( aItem->Type() == PCB_FOOTPRINT_T )
2644 {
2645 static_cast<FOOTPRINT*>( aItem )->RunOnChildren(
2646 [&]( BOARD_ITEM* aChild )
2647 {
2648 highlightInternal( aChild, aMode, aUsingOverlay );
2649 } );
2650 }
2651 else if( aItem->Type() == PCB_GROUP_T )
2652 {
2653 static_cast<PCB_GROUP*>( aItem )->RunOnChildren(
2654 [&]( BOARD_ITEM* aChild )
2655 {
2656 highlightInternal( aChild, aMode, aUsingOverlay );
2657 } );
2658 }
2659}
2660
2661
2662void PCB_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
2663{
2664 if( aGroup )
2665 aGroup->Remove( aItem );
2666
2667 unhighlightInternal( aItem, aMode, aGroup != nullptr );
2668 view()->Update( aItem, KIGFX::REPAINT );
2669
2670 // Many selections are very temporal and updating the display each time just creates noise.
2671 if( aMode == BRIGHTENED )
2673}
2674
2675
2676void PCB_SELECTION_TOOL::unhighlightInternal( EDA_ITEM* aItem, int aMode, bool aUsingOverlay )
2677{
2678 if( aMode == SELECTED )
2679 aItem->ClearSelected();
2680 else if( aMode == BRIGHTENED )
2681 aItem->ClearBrightened();
2682
2683 if( aUsingOverlay && aMode != BRIGHTENED )
2684 view()->Hide( aItem, false ); // // Restore original item visibility
2685
2686 if( aItem->Type() == PCB_FOOTPRINT_T )
2687 {
2688 static_cast<FOOTPRINT*>( aItem )->RunOnChildren(
2689 [&]( BOARD_ITEM* aChild )
2690 {
2691 unhighlightInternal( aChild, aMode, aUsingOverlay );
2692 } );
2693 }
2694 else if( aItem->Type() == PCB_GROUP_T )
2695 {
2696 static_cast<PCB_GROUP*>( aItem )->RunOnChildren(
2697 [&]( BOARD_ITEM* aChild )
2698 {
2699 unhighlightInternal( aChild, aMode, aUsingOverlay );
2700 } );
2701 }
2702}
2703
2704
2706{
2707 const unsigned GRIP_MARGIN = 20;
2708 int margin = KiROUND( getView()->ToWorld( GRIP_MARGIN ) );
2709
2710 // Check if the point is located close to any of the currently selected items
2711 for( EDA_ITEM* item : m_selection )
2712 {
2713 BOX2I itemBox = item->ViewBBox();
2714 itemBox.Inflate( margin ); // Give some margin for gripping an item
2715
2716 if( itemBox.Contains( aPoint ) )
2717 {
2718 if( item->HitTest( aPoint, margin ) )
2719 return true;
2720
2721 bool found = false;
2722
2723 std::function<void( PCB_GROUP* )> checkGroup;
2724 checkGroup = [&]( PCB_GROUP* aGroup )
2725 {
2726 aGroup->RunOnChildren(
2727 [&]( BOARD_ITEM* aItem )
2728 {
2729 if( PCB_GROUP* group = dyn_cast<PCB_GROUP*>( aItem ) )
2730 checkGroup( group );
2731 else if( aItem->HitTest( aPoint, margin ) )
2732 found = true;
2733 } );
2734 };
2735
2736 if( PCB_GROUP* group = dyn_cast<PCB_GROUP*>( item ) )
2737 {
2738 checkGroup( group );
2739
2740 if( found )
2741 return true;
2742 }
2743 }
2744 }
2745
2746 return false;
2747}
2748
2749
2751 int aMaxDistance ) const
2752{
2753 BOX2D viewportD = getView()->GetViewport();
2754 BOX2I viewport( VECTOR2I( viewportD.GetPosition() ), VECTOR2I( viewportD.GetSize() ) );
2755 int distance = INT_MAX;
2756 SEG loc( aWhere, aWhere );
2757
2758 switch( aItem->Type() )
2759 {
2760 case PCB_TEXT_T:
2761 {
2762 PCB_TEXT* text = static_cast<PCB_TEXT*>( aItem );
2763 text->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance );
2764 break;
2765 }
2766
2767 case PCB_TEXTBOX_T:
2768 {
2769 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( aItem );
2770 textbox->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance );
2771 break;
2772 }
2773
2774 case PCB_ZONE_T:
2775 {
2776 ZONE* zone = static_cast<ZONE*>( aItem );
2777
2778 // Zone borders are very specific
2779 if( zone->HitTestForEdge( aWhere, aMaxDistance / 2 ) )
2780 distance = 0;
2781 else if( zone->HitTestForEdge( aWhere, aMaxDistance ) )
2782 distance = aMaxDistance / 2;
2783 else
2784 aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
2785
2786 break;
2787 }
2788
2789 case PCB_FOOTPRINT_T:
2790 {
2791 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
2792 BOX2I bbox = footprint->GetBoundingBox( false, false );
2793
2794 try
2795 {
2796 footprint->GetBoundingHull().Collide( loc, aMaxDistance, &distance );
2797 }
2798 catch( const ClipperLib::clipperException& exc )
2799 {
2800 // This may be overkill and could be an assertion but we are more likely to find
2801 // any clipper errors this way.
2802 wxLogError( wxT( "Clipper library exception '%s' occurred." ), exc.what() );
2803 }
2804
2805 // Consider footprints larger than the viewport only as a last resort
2806 if( bbox.GetHeight() > viewport.GetHeight() || bbox.GetWidth() > viewport.GetWidth() )
2807 distance = INT_MAX / 2;
2808
2809 break;
2810 }
2811
2812 case PCB_MARKER_T:
2813 {
2814 PCB_MARKER* marker = static_cast<PCB_MARKER*>( aItem );
2815 SHAPE_LINE_CHAIN polygon;
2816
2817 marker->ShapeToPolygon( polygon );
2818 polygon.Move( marker->GetPos() );
2819 polygon.Collide( loc, aMaxDistance, &distance );
2820 break;
2821 }
2822
2823 case PCB_GROUP_T:
2824 {
2825 PCB_GROUP* group = static_cast<PCB_GROUP*>( aItem );
2826
2827 for( BOARD_ITEM* member : group->GetItems() )
2828 distance = std::min( distance, hitTestDistance( aWhere, member, aMaxDistance ) );
2829
2830 break;
2831 }
2832
2833 default:
2834 aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
2835 break;
2836 }
2837
2838 return distance;
2839}
2840
2841
2842// The general idea here is that if the user clicks directly on a small item inside a larger
2843// one, then they want the small item. The quintessential case of this is clicking on a pad
2844// within a footprint, but we also apply it for text within a footprint, footprints within
2845// larger footprints, and vias within either larger pads or longer tracks.
2846//
2847// These "guesses" presume there is area within the larger item to click in to select it. If
2848// an item is mostly covered by smaller items within it, then the guesses are inappropriate as
2849// there might not be any area left to click to select the larger item. In this case we must
2850// leave the items in the collector and bring up a Selection Clarification menu.
2851//
2852// We currently check for pads and text mostly covering a footprint, but we don't check for
2853// smaller footprints mostly covering a larger footprint.
2854//
2856 const VECTOR2I& aWhere ) const
2857{
2858 static const LSET silkLayers( 2, B_SilkS, F_SilkS );
2859 static const LSET courtyardLayers( 2, B_CrtYd, F_CrtYd );
2860
2861 std::set<BOARD_ITEM*> preferred;
2862 std::set<BOARD_ITEM*> rejected;
2863 VECTOR2I where( aWhere.x, aWhere.y );
2864 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
2865 PCB_LAYER_ID activeLayer = m_frame->GetActiveLayer();
2866
2867 if( silkLayers[activeLayer] )
2868 {
2869 for( int i = 0; i < aCollector.GetCount(); ++i )
2870 {
2871 BOARD_ITEM* item = aCollector[i];
2872 KICAD_T type = item->Type();
2873
2874 if( ( type == PCB_TEXT_T || type == PCB_TEXTBOX_T || type == PCB_SHAPE_T )
2875 && silkLayers[item->GetLayer()] )
2876 {
2877 preferred.insert( item );
2878 }
2879 }
2880 }
2881 else if( courtyardLayers[activeLayer] && settings->GetHighContrast() )
2882 {
2883 for( int i = 0; i < aCollector.GetCount(); ++i )
2884 {
2885 BOARD_ITEM* item = aCollector[i];
2886 KICAD_T type = item->Type();
2887
2888 if( type == PCB_FOOTPRINT_T )
2889 preferred.insert( item );
2890 }
2891 }
2892
2893 if( preferred.size() > 0 )
2894 {
2895 aCollector.Empty();
2896
2897 for( BOARD_ITEM* item : preferred )
2898 aCollector.Append( item );
2899
2900 return;
2901 }
2902
2903 // Prefer exact hits to sloppy ones
2904 constexpr int MAX_SLOP = 5;
2905
2906 int singlePixel = KiROUND( aCollector.GetGuide()->OnePixelInIU() );
2907 int maxSlop = KiROUND( MAX_SLOP * aCollector.GetGuide()->OnePixelInIU() );
2908 int minSlop = INT_MAX;
2909
2910 std::map<BOARD_ITEM*, int> itemsBySloppiness;
2911
2912 for( int i = 0; i < aCollector.GetCount(); ++i )
2913 {
2914 BOARD_ITEM* item = aCollector[i];
2915 int itemSlop = hitTestDistance( where, item, maxSlop );
2916
2917 itemsBySloppiness[ item ] = itemSlop;
2918
2919 if( itemSlop < minSlop )
2920 minSlop = itemSlop;
2921 }
2922
2923 // Prune sloppier items
2924 if( minSlop < INT_MAX )
2925 {
2926 for( std::pair<BOARD_ITEM*, int> pair : itemsBySloppiness )
2927 {
2928 if( pair.second > minSlop + singlePixel )
2929 aCollector.Transfer( pair.first );
2930 }
2931 }
2932
2933 // If the user clicked on a small item within a much larger one then it's pretty clear
2934 // they're trying to select the smaller one.
2935 constexpr double sizeRatio = 1.5;
2936
2937 std::vector<std::pair<BOARD_ITEM*, double>> itemsByArea;
2938
2939 for( int i = 0; i < aCollector.GetCount(); ++i )
2940 {
2941 BOARD_ITEM* item = aCollector[i];
2942 double area = 0.0;
2943
2944 if( item->Type() == PCB_ZONE_T
2945 && static_cast<ZONE*>( item )->HitTestForEdge( where, maxSlop / 2 ) )
2946 {
2947 // Zone borders are very specific, so make them "small"
2948 area = (double) SEG::Square( singlePixel ) * MAX_SLOP;
2949 }
2950 else if( item->Type() == PCB_VIA_T )
2951 {
2952 // Vias rarely hide other things, and we don't want them deferring to short track
2953 // segments underneath them -- so artificially reduce their size from πr² to 1.5r².
2954 area = (double) SEG::Square( static_cast<PCB_VIA*>( item )->GetDrill() / 2 ) * 1.5;
2955 }
2956 else if( item->Type() == PCB_BITMAP_T )
2957 {
2958 VECTOR2D size = static_cast<const PCB_BITMAP*>( item )->GetSize();
2959 area = size.x * size.y;
2960 }
2961 else
2962 {
2963 try
2964 {
2965 area = FOOTPRINT::GetCoverageArea( item, aCollector );
2966 }
2967 catch( const ClipperLib::clipperException& e )
2968 {
2969 wxLogError( wxT( "A clipper exception %s was detected." ), e.what() );
2970 }
2971 }
2972
2973 itemsByArea.emplace_back( item, area );
2974 }
2975
2976 std::sort( itemsByArea.begin(), itemsByArea.end(),
2977 []( const std::pair<BOARD_ITEM*, double>& lhs,
2978 const std::pair<BOARD_ITEM*, double>& rhs ) -> bool
2979 {
2980 return lhs.second < rhs.second;
2981 } );
2982
2983 bool rejecting = false;
2984
2985 for( int i = 1; i < (int) itemsByArea.size(); ++i )
2986 {
2987 if( itemsByArea[i].second > itemsByArea[i-1].second * sizeRatio )
2988 rejecting = true;
2989
2990 if( rejecting )
2991 rejected.insert( itemsByArea[i].first );
2992 }
2993
2994 // Special case: if a footprint is completely covered with other features then there's no
2995 // way to select it -- so we need to leave it in the list for user disambiguation.
2996 constexpr double maxCoverRatio = 0.70;
2997
2998 for( int i = 0; i < aCollector.GetCount(); ++i )
2999 {
3000 if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aCollector[i] ) )
3001 {
3002 if( footprint->CoverageRatio( aCollector ) > maxCoverRatio )
3003 rejected.erase( footprint );
3004 }
3005 }
3006
3007 // Hopefully we've now got what the user wanted.
3008 if( (unsigned) aCollector.GetCount() > rejected.size() ) // do not remove everything
3009 {
3010 for( BOARD_ITEM* item : rejected )
3011 aCollector.Transfer( item );
3012 }
3013
3014 // Finally, what we are left with is a set of items of similar coverage area. We now reject
3015 // any that are not on the active layer, to reduce the number of disambiguation menus shown.
3016 // If the user wants to force-disambiguate, they can either switch layers or use the modifier
3017 // key to force the menu.
3018 if( aCollector.GetCount() > 1 )
3019 {
3020 bool haveItemOnActive = false;
3021 rejected.clear();
3022
3023 for( int i = 0; i < aCollector.GetCount(); ++i )
3024 {
3025 if( !aCollector[i]->IsOnLayer( activeLayer ) )
3026 rejected.insert( aCollector[i] );
3027 else
3028 haveItemOnActive = true;
3029 }
3030
3031 if( haveItemOnActive )
3032 for( BOARD_ITEM* item : rejected )
3033 aCollector.Transfer( item );
3034 }
3035}
3036
3037
3039 bool aMultiselect ) const
3040{
3041 std::unordered_set<BOARD_ITEM*> toAdd;
3042
3043 // Set CANDIDATE on all parents which are included in the GENERAL_COLLECTOR. This
3044 // algorithm is O(3n), whereas checking for the parent inclusion could potentially be O(n^2).
3045 for( int j = 0; j < aCollector.GetCount(); j++ )
3046 {
3047 if( aCollector[j]->GetParent() )
3048 aCollector[j]->GetParent()->ClearFlags( CANDIDATE );
3049 }
3050
3051 if( aMultiselect )
3052 {
3053 for( int j = 0; j < aCollector.GetCount(); j++ )
3054 aCollector[j]->SetFlags( CANDIDATE );
3055 }
3056
3057 for( int j = 0; j < aCollector.GetCount(); )
3058 {
3059 BOARD_ITEM* item = aCollector[j];
3060 BOARD_ITEM* parent = item->GetParent();
3061 BOARD_ITEM* start = item;
3062
3063 if( !m_isFootprintEditor && parent && parent->Type() == PCB_FOOTPRINT_T )
3064 start = parent;
3065
3066 // If a group is entered, disallow selections of objects outside the group.
3068 {
3069 aCollector.Remove( item );
3070 continue;
3071 }
3072
3073 // If any element is a member of a group, replace those elements with the top containing
3074 // group.
3076 {
3077 if( top != item )
3078 {
3079 toAdd.insert( top );
3080 top->SetFlags(CANDIDATE );
3081
3082 aCollector.Remove( item );
3083 continue;
3084 }
3085 }
3086
3087 // Footprints are a bit easier as they can't be nested.
3088 if( parent && ( parent->GetFlags() & CANDIDATE ) )
3089 {
3090 // Remove children of selected items
3091 aCollector.Remove( item );
3092 continue;
3093 }
3094
3095 ++j;
3096 }
3097
3098 for( BOARD_ITEM* item : toAdd )
3099 {
3100 if( !aCollector.HasItem( item ) )
3101 aCollector.Append( item );
3102 }
3103}
3104
3105
3107{
3108 std::set<BOARD_ITEM*> to_add;
3109
3110 // Iterate from the back so we don't have to worry about removals.
3111 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
3112 {
3113 BOARD_ITEM* item = aCollector[i];
3114
3115 if( !m_isFootprintEditor && item->Type() == PCB_PAD_T
3116 && !frame()->GetPcbNewSettings()->m_AllowFreePads )
3117 {
3118 if( !aCollector.HasItem( item->GetParent() ) )
3119 to_add.insert( item->GetParent() );
3120
3121 aCollector.Remove( item );
3122 }
3123 }
3124
3125 for( BOARD_ITEM* item : to_add )
3126 aCollector.Append( item );
3127}
3128
3129
3131{
3132 // Iterate from the back so we don't have to worry about removals.
3133 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
3134 {
3135 BOARD_ITEM* item = aCollector[i];
3136
3137 if( item->Type() == PCB_MARKER_T )
3138 aCollector.Remove( item );
3139 }
3140}
3141
3142
3144{
3145 getView()->Update( &m_selection );
3147
3148 return 0;
3149}
3150
3151
3153{
3155
3159
3165
3181
3183
3185}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
static TOOL_ACTION cancelInteractive
Definition: actions.h:63
static TOOL_ACTION cursorLeft
Definition: actions.h:119
static TOOL_ACTION zoomOutCenter
Definition: actions.h:97
static TOOL_ACTION zoomIn
Definition: actions.h:94
static TOOL_ACTION cursorLeftFast
Definition: actions.h:124
static TOOL_ACTION cursorDown
Definition: actions.h:118
static TOOL_ACTION zoomOut
Definition: actions.h:95
static TOOL_ACTION cursorRightFast
Definition: actions.h:125
static TOOL_ACTION zoomCenter
Definition: actions.h:98
static TOOL_ACTION panDown
Definition: actions.h:132
static TOOL_ACTION cursorDownFast
Definition: actions.h:123
static TOOL_ACTION cursorUpFast
Definition: actions.h:122
static TOOL_ACTION panLeft
Definition: actions.h:133
static TOOL_ACTION updateMenu
Definition: actions.h:172
static TOOL_ACTION doDelete
Definition: actions.h:73
static TOOL_ACTION zoomFitScreen
Definition: actions.h:99
static TOOL_ACTION panUp
Definition: actions.h:131
static TOOL_ACTION zoomFitObjects
Definition: actions.h:100
static TOOL_ACTION zoomInCenter
Definition: actions.h:96
static TOOL_ACTION panRight
Definition: actions.h:134
static TOOL_ACTION cursorUp
Cursor control with keyboard.
Definition: actions.h:117
static TOOL_ACTION cursorRight
Definition: actions.h:120
static TOOL_ACTION selectAll
Definition: actions.h:71
Defines 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:87
wxMenuItem * Add(const wxString &aLabel, int aId, BITMAPS aIcon)
Add a wxWidgets-style entry to the menu.
CROSS_PROBING_SETTINGS m_CrossProbing
Definition: app_settings.h:173
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:71
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:196
virtual VECTOR2I GetCenter() const
This defaults to the center of the bounding box if not overridden.
Definition: board_item.h:106
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:220
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:240
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:201
virtual bool IsLocked() const
Definition: board_item.cpp:72
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:176
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:270
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:1407
LSET GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:625
bool IsElementVisible(GAL_LAYER_ID aLayer) const
Test whether a given element category is visible.
Definition: board.cpp:677
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:617
bool LegacyTeardrops() const
Definition: board.h:1160
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:432
BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition: box2.h:119
const Vec & GetPosition() const
Definition: box2.h:184
const Vec & GetOrigin() const
Definition: box2.h:183
void SetMaximum()
Definition: box2.h:63
coord_type GetHeight() const
Definition: box2.h:188
coord_type GetWidth() const
Definition: box2.h:187
bool Contains(const Vec &aPoint) const
Definition: box2.h:141
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:506
Vec Centre() const
Definition: box2.h:70
const Vec & GetSize() const
Definition: box2.h:179
CN_ANCHOR represents a physical location that can be connected: a pad or a track/arc/via endpoint.
BOARD_CONNECTED_ITEM * Parent() const
CN_EDGE represents a point-to-point connection, whether realized or unrealized (ie: tracks etc.
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:151
void Empty()
Clear the list.
Definition: collector.h:89
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:81
bool HasItem(const EDA_ITEM *aItem) const
Tests if aItem has already been collected.
Definition: collector.h:195
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition: collector.h:109
void Append(EDA_ITEM *item)
Add an item to the end of the list.
Definition: collector.h:99
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.
void ForceRefresh()
Force a redraw.
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:85
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:232
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition: eda_item.cpp:73
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:123
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
void ClearSelected()
Definition: eda_item.h:118
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: eda_item.h:125
bool IsSelected() const
Definition: eda_item.h:106
void SetSelected()
Definition: eda_item.h:115
void ClearBrightened()
Definition: eda_item.h:119
void SetBrightened()
Definition: eda_item.h:116
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:205
EDA_ITEM * GetParent() const
Definition: eda_item.h:99
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:126
std::shared_ptr< SHAPE_COMPOUND > GetEffectiveTextShape(bool aTriangulate=true, bool aUseTextRotation=true) const
build a list of segments (SHAPE_SEGMENT) to describe a text shape.
Definition: eda_text.cpp:885
static const TOOL_EVENT DisambiguatePoint
Definition: actions.h:224
static const TOOL_EVENT ClearedEvent
Definition: actions.h:209
static const TOOL_EVENT InhibitSelectionEditing
Definition: actions.h:220
static const TOOL_EVENT SelectedEvent
Definition: actions.h:207
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:214
static const TOOL_EVENT UninhibitSelectionEditing
Used to inform tool that it should display the disambiguation menu.
Definition: actions.h:221
static const TOOL_EVENT PointSelectedEvent
Definition: actions.h:206
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:217
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:208
ZONES & Zones()
Definition: footprint.h:178
static double GetCoverageArea(const BOARD_ITEM *aItem, const GENERAL_COLLECTOR &aCollector)
Definition: footprint.cpp:1927
bool IsFlipped() const
Definition: footprint.h:326
PADS & Pads()
Definition: footprint.h:172
SHAPE_POLY_SET GetBoundingHull() const
Return a bounding polygon for the shapes and pads in the footprint.
Definition: footprint.cpp:941
bool IsLocked() const override
Definition: footprint.h:342
const KIID_PATH & GetPath() const
Definition: footprint.h:226
DRAWINGS & GraphicalItems()
Definition: footprint.h:175
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: footprint.cpp:806
A general implementation of a COLLECTORS_GUIDE.
Definition: collectors.h:326
void SetIgnoreBlindBuriedVias(bool ignore)
Definition: collectors.h:471
void SetIgnoreTracks(bool ignore)
Definition: collectors.h:477
void SetIgnoreMTextsMarkedNoShow(bool ignore)
Definition: collectors.h:411
void SetIgnoreModulesOnFront(bool ignore)
Definition: collectors.h:435
void SetIgnoreModulesRefs(bool ignore)
Definition: collectors.h:465
void SetIgnoreMicroVias(bool ignore)
Definition: collectors.h:474
void SetIgnoreZoneFills(bool ignore)
Definition: collectors.h:480
void SetIgnorePadsOnBack(bool ignore)
Definition: collectors.h:441
void SetIgnoreModulesOnBack(bool ignore)
Definition: collectors.h:429
void SetIgnoreModulesVals(bool ignore)
Definition: collectors.h:459
void SetIgnoreThroughVias(bool ignore)
Definition: collectors.h:468
void SetIgnoreThroughHolePads(bool ignore)
Definition: collectors.h:453
void SetIgnoreMTextsOnFront(bool ignore)
Definition: collectors.h:423
void SetIgnoreMTextsOnBack(bool ignore)
Definition: collectors.h:417
void SetIgnorePadsOnFront(bool ignore)
Definition: collectors.h:447
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:204
void SetGuide(const COLLECTORS_GUIDE *aGuide)
Record which COLLECTORS_GUIDE to use.
Definition: collectors.h:293
const COLLECTORS_GUIDE * GetGuide() const
Definition: collectors.h:295
static const std::vector< KICAD_T > AllBoardItems
A scan list for all editable board items.
Definition: collectors.h:224
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:536
static const std::vector< KICAD_T > FootprintItems
A scan list for primary footprint items.
Definition: collectors.h:250
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:92
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1) override
Add a VIEW_ITEM to the view.
Definition: pcb_view.cpp:58
virtual void Remove(VIEW_ITEM *aItem) override
Remove a VIEW_ITEM from the view.
Definition: pcb_view.cpp:75
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.
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:69
virtual void Add(VIEW_ITEM *aItem)
Add an item to the group.
Definition: view_group.cpp:57
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition: view.h:69
double GetScale() const
Definition: view.h:269
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition: view.cpp:508
virtual void SetScale(double aScale, VECTOR2D aAnchor={ 0, 0 })
Set the scaling factor, zooming around a given anchor point.
Definition: view.cpp:548
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:351
void UpdateAllLayersColor()
Apply the new coloring scheme to all layers.
Definition: view.cpp:758
virtual 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:422
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:1608
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:445
void Hide(VIEW_ITEM *aItem, bool aHide=true)
Temporarily hide the item in the view (e.g.
Definition: view.cpp:1567
bool IsMirroredX() const
Return true if view is flipped across the X axis.
Definition: view.h:243
std::pair< VIEW_ITEM *, int > LAYER_ITEM_PAIR
Definition: view.h:73
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition: view.h:410
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition: view.h:213
void SetCenter(const VECTOR2D &aCenter)
Set the center point of the VIEW (i.e.
Definition: view.cpp:574
void MarkTargetDirty(int aTarget)
Set or clear target 'dirty' flag.
Definition: view.h:617
bool IsVisible(const VIEW_ITEM *aItem) const
Return information if the item is visible (or not).
Definition: view.cpp:1586
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition: view.cpp:1546
wxString AsString() const
Definition: kiid.cpp:359
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:536
static LSET AllLayersMask()
Definition: lset.cpp:808
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:411
bool Contains(PCB_LAYER_ID aLayer)
See if the layer set contains a PCB layer.
Definition: layer_ids.h:606
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:773
static LSET PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition: lset.cpp:870
static LSET FrontMask()
Return a mask holding all technical layers and the external CU layer on front side.
Definition: lset.cpp:895
static LSET BackMask()
Return a mask holding all technical layers and the external CU layer on back side.
Definition: lset.cpp:902
const VECTOR2I & GetPos() const
Definition: marker_base.h:87
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:67
Definition: pad.h:59
TRACK_DRAG_ACTION m_TrackDragAction
static TOOL_ACTION drag45Degree
Definition: pcb_actions.h:166
static TOOL_ACTION unrouteSelected
Removes all tracks from the selected items to the first pad.
Definition: pcb_actions.h:83
static TOOL_ACTION selectionCursor
Select a single item under the cursor position.
Definition: pcb_actions.h:56
static TOOL_ACTION groupLeave
Definition: pcb_actions.h:479
static TOOL_ACTION grabUnconnected
Select and move nearest unconnected footprint from ratsnest of selection.
Definition: pcb_actions.h:95
static TOOL_ACTION filterSelection
Filter the items in the current selection (invokes dialog)
Definition: pcb_actions.h:107
static TOOL_ACTION highlightNet
Definition: pcb_actions.h:499
static TOOL_ACTION unselectItem
Definition: pcb_actions.h:63
static TOOL_ACTION hideLocalRatsnest
Definition: pcb_actions.h:509
static TOOL_ACTION properties
Activation of the edit tool.
Definition: pcb_actions.h:149
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:59
static TOOL_ACTION selectOnSheetFromEeschema
Select all components on sheet from Eeschema crossprobing.
Definition: pcb_actions.h:98
static TOOL_ACTION selectConnection
Select tracks between junctions or expands an existing selection to pads or the entire connection.
Definition: pcb_actions.h:80
static TOOL_ACTION dragFreeAngle
Definition: pcb_actions.h:167
static TOOL_ACTION clearHighlight
Definition: pcb_actions.h:498
static TOOL_ACTION unselectItems
Definition: pcb_actions.h:67
static TOOL_ACTION selectUnconnected
Select unconnected footprints from ratsnest of selection.
Definition: pcb_actions.h:92
static TOOL_ACTION moveIndividually
move items one-by-one
Definition: pcb_actions.h:113
static TOOL_ACTION syncSelection
Sets selection to specified items, zooms to fit, if enabled.
Definition: pcb_actions.h:70
static TOOL_ACTION groupEnter
Definition: pcb_actions.h:478
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition: pcb_actions.h:62
static TOOL_ACTION selectSameSheet
Select all components on the same sheet as the selected footprint.
Definition: pcb_actions.h:101
static TOOL_ACTION selectNet
Select all connections belonging to a single net.
Definition: pcb_actions.h:86
static TOOL_ACTION selectionActivate
Activation of the selection tool.
Definition: pcb_actions.h:53
static TOOL_ACTION move
move or drag an item
Definition: pcb_actions.h:110
static TOOL_ACTION syncSelectionWithNets
Sets selection to specified items with connected nets, zooms to fit, if enabled.
Definition: pcb_actions.h:73
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
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:89
static TOOL_ACTION selectionMenu
Run a selection menu to select from a list of items.
Definition: pcb_actions.h:76
static TOOL_ACTION selectOnSchematic
Select symbols/pins on schematic corresponding to selected footprints/pads.
Definition: pcb_actions.h:104
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.
void FocusOnItem(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer=UNDEFINED_LAYER)
Object to handle a bitmap image that can be inserted in a PCB.
Definition: pcb_bitmap.h:42
ZONE_DISPLAY_MODE m_ZoneDisplayMode
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:51
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: pcb_group.cpp:267
static bool WithinScope(BOARD_ITEM *aItem, PCB_GROUP *aScope, bool isFootprintEditor)
Definition: pcb_group.cpp:147
static PCB_GROUP * TopLevelGroup(BOARD_ITEM *aItem, PCB_GROUP *aScope, bool isFootprintEditor)
Definition: pcb_group.cpp:141
void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction) const
Invoke a function on all members of the group.
Definition: pcb_group.cpp:398
bool AddItem(BOARD_ITEM *aItem)
Add item to group.
Definition: pcb_group.cpp:71
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 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 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)
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.
PCB_BASE_EDIT_FRAME * frame() const
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.
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.
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.
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 FilterCollectorForFreePads(GENERAL_COLLECTOR &aCollector) const
Check the "allow free pads" setting and if disabled, replace any pads in the collector with their par...
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
Determines 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 selectSameSheet(const TOOL_EVENT &aEvent)
Set selection to items passed by parameter and connected nets (optionally).
void EnterGroup()
Enter the group at the head of the current selection.
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)
Clear current selection event handler.
int ClearSelection(const TOOL_EVENT &aEvent)
void highlightInternal(EDA_ITEM *aItem, int aHighlightMode, bool aUsingOverlay)
PCB_BASE_FRAME * m_frame
PCB_SELECTION & GetSelection()
@ 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 OnIdle(wxIdleEvent &aEvent)
PCB_DRAW_PANEL_GAL * canvas() const
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)
SELECTION_FILTER_OPTIONS m_filter
int hitTestDistance(const VECTOR2I &aWhere, BOARD_ITEM *aItem, int aMaxDistance) const
void SelectAllItemsOnNet(int aNetCode, bool aSelect=true)
Select all items with the given net code.
bool selectMultiple()
Handle drawing a selection box that allows one to select many items at the same time.
void ExitGroup(bool aSelectGroup=false)
Leave the currently-entered group.
void unselect(EDA_ITEM *aItem) override
Take necessary action mark an item as unselected.
int SelectAll(const TOOL_EVENT &aEvent)
PCB_SELECTION m_selection
void unhighlight(EDA_ITEM *aItem, int aHighlightMode, SELECTION *aGroup=nullptr) override
Unhighlight the item visually.
@ TEXT_is_DIVERS
Definition: pcb_text.h:52
@ TEXT_is_REFERENCE
Definition: pcb_text.h:50
@ TEXT_is_VALUE
Definition: pcb_text.h:51
const VECTOR2I & GetStart() const
Definition: pcb_track.h:113
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:110
int GetDrill() const
Return the local drill setting for this PCB_VIA.
Definition: pcb_track.h:530
A small class to help profiling.
Definition: profile.h:47
void Start()
Start or restart the counter.
Definition: profile.h:75
double msecs(bool aSinceLast=false)
Definition: profile.h:147
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 Count(int aNumber)
Create a functor that tests if the number of selected items is equal to the value given as parameter.
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
bool doSelectionMenu(COLLECTOR *aCollector)
wxTimer m_disambiguateTimer
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
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 hasModifier()
True if a selection modifier is enabled, false otherwise.
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:120
void SetIsHover(bool aIsHover)
Definition: selection.h:78
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:99
EDA_ITEM * Front() const
Definition: selection.h:208
virtual void Clear() override
Remove all the stored items from the group.
Definition: selection.h:92
int Size() const
Returns the number of selected parts.
Definition: selection.h:115
void ClearReferencePoint()
Definition: selection.h:265
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:109
bool Contains(EDA_ITEM *aItem) const
Definition: selection.cpp:84
virtual BOX2I GetBoundingBox() const
Definition: selection.cpp:133
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:147
bool ToolStackIsEmpty()
Definition: tools_holder.h:128
Represent a single user action.
Definition: tool_action.h:68
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:216
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
bool IsToolActive() const
Definition: tool_base.cpp:31
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:156
T Parameter() const
Return a non-standard parameter assigned to the event.
Definition: tool_event.h:442
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:81
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).
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, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:142
VECTOR2D GetMousePosition() const
KIGFX::VIEW * GetView() const
Definition: tool_manager.h:285
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:44
void RegisterSubMenu(std::shared_ptr< ACTION_MENU > aSubMenu)
Store a submenu of this menu model.
Definition: tool_menu.cpp:50
void ShowContextMenu(SELECTION &aSelection)
Helper function to set and immediately show a CONDITIONAL_MENU in concert with the given SELECTION.
Definition: tool_menu.cpp:57
Handle a list of polygons defining a copper zone.
Definition: zone.h:72
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:710
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:443
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:450
bool IsTeardropArea() const
Definition: zone.h:694
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.h:129
MOUSE_DRAG_ACTION
KICURSOR
Definition: cursors.h:34
#define DEFAULT_TEXT_SIZE
Ratio of the font height to the baseline of the text above the wire.
#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:75
#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.
const TOOL_ACTION * allowedActions[]
@ FRAME_PCB_EDITOR
Definition: frame_type.h:40
@ FRAME_FOOTPRINT_VIEWER_MODAL
Definition: frame_type.h:43
@ FRAME_FOOTPRINT_VIEWER
Definition: frame_type.h:42
@ FRAME_FOOTPRINT_EDITOR
Definition: frame_type.h:41
@ LAYER_MOD_TEXT_INVISIBLE
text marked as invisible
Definition: layer_ids.h:200
@ LAYER_PAD_FR
smd pads, front layer
Definition: layer_ids.h:202
@ LAYER_MOD_TEXT
Definition: layer_ids.h:198
@ LAYER_ZONES
Control for copper zone opacity/visibility (color ignored)
Definition: layer_ids.h:231
@ LAYER_PADS
Meta control for all pads opacity/visibility (color ignored)
Definition: layer_ids.h:230
@ LAYER_TRACKS
Definition: layer_ids.h:212
@ LAYER_MOD_FR
show footprints on front
Definition: layer_ids.h:208
@ LAYER_PAD_BK
smd pads, back layer
Definition: layer_ids.h:203
@ LAYER_MOD_VALUES
show footprints values (when texts are visible)
Definition: layer_ids.h:210
@ LAYER_PADS_TH
multilayer pads, usually with holes
Definition: layer_ids.h:213
@ LAYER_VIAS
Meta control for all vias opacity/visibility.
Definition: layer_ids.h:193
@ LAYER_MOD_BK
show footprints on back
Definition: layer_ids.h:209
@ LAYER_MOD_REFERENCES
show footprints references (when texts are visible)
Definition: layer_ids.h:211
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ F_CrtYd
Definition: layer_ids.h:117
@ Edge_Cuts
Definition: layer_ids.h:113
@ B_Cu
Definition: layer_ids.h:95
@ F_SilkS
Definition: layer_ids.h:104
@ B_CrtYd
Definition: layer_ids.h:116
@ UNDEFINED_LAYER
Definition: layer_ids.h:60
@ B_SilkS
Definition: layer_ids.h:103
@ F_Cu
Definition: layer_ids.h:64
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:932
@ REPAINT
Item needs to be redrawn.
Definition: view_item.h:52
@ TARGET_OVERLAY
Items that may change while the view stays the same (noncached)
Definition: definitions.h:50
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.
void(* CLIENT_SELECTION_FILTER)(const VECTOR2I &, GENERAL_COLLECTOR &, PCB_SELECTION_TOOL *)
TRACK_DRAG_ACTION
CITER next(CITER it)
Definition: ptree.cpp:126
Class that computes missing connections on a PCB.
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
const int scale
bool zoom_to_fit
Zoom to fit items (ignored if center_on_items is off)
Definition: app_settings.h:34
bool center_on_items
Automatically pan to cross-probed items.
Definition: app_settings.h:33
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:89
bool graphics
Graphic lines, shapes, polygons.
bool text
Text (free or attached to a footprint)
bool lockedItems
Allow selecting locked items.
bool footprints
Allow selecting entire footprints.
bool dimensions
Dimension items.
bool otherItems
Anything not fitting one of the above categories.
@ TA_MOUSE_UP
Definition: tool_event.h:64
@ TC_ANY
Definition: tool_event.h:55
@ MD_ALT
Definition: tool_event.h:140
@ MD_CTRL
Definition: tool_event.h:139
@ MD_SHIFT
Definition: tool_event.h:138
@ BUT_MIDDLE
Definition: tool_event.h:129
@ BUT_LEFT
Definition: tool_event.h:127
@ BUT_RIGHT
Definition: tool_event.h:128
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:101
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:98
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:93
@ TYPE_NOT_INIT
Definition: typeinfo.h:81
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:99
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:106
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition: typeinfo.h:91
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:103
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
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:95
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition: typeinfo.h:102
@ 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:97
@ PCB_BITMAP_T
class PCB_BITMAP, bitmap on a layer
Definition: typeinfo.h:89
@ 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:94
@ PCB_NETINFO_T
class NETINFO_ITEM, a description of a net
Definition: typeinfo.h:105
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:92
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:100
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588
VECTOR2D ToVECTOR2D(const wxPoint &aPoint)
Definition: vector2wx.h:40