KiCad PCB EDA Suite
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-2021 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 <functional>
29 using namespace std::placeholders;
30 #include <core/kicad_algo.h>
31 #include <board.h>
32 #include <board_design_settings.h>
33 #include <board_item.h>
34 #include <clipper.hpp>
35 #include <pcb_track.h>
36 #include <footprint.h>
37 #include <pad.h>
38 #include <pcb_group.h>
39 #include <pcb_shape.h>
40 #include <pcb_text.h>
41 #include <pcb_marker.h>
42 #include <zone.h>
43 #include <collectors.h>
46 #include <class_draw_panel_gal.h>
47 #include <view/view_controls.h>
49 #include <painter.h>
50 #include <router/router_tool.h>
51 #include <bitmaps.h>
52 #include <pcbnew_settings.h>
53 #include <tool/tool_event.h>
54 #include <tool/tool_manager.h>
55 #include <tools/tool_event_utils.h>
56 #include <tools/pcb_point_editor.h>
58 #include <tools/pcb_actions.h>
60 #include <footprint_viewer_frame.h>
61 #include <id.h>
62 #include <wx/event.h>
63 #include <wx/timer.h>
64 #include <wx/log.h>
65 
66 
67 class SELECT_MENU : public ACTION_MENU
68 {
69 public:
71  ACTION_MENU( true )
72  {
73  SetTitle( _( "Select" ) );
74 
76 
77  AppendSeparator();
78 
81 
82  // This could be enabled if we have better logic for picking the target net with the mouse
83  // Add( PCB_ACTIONS::deselectNet );
85  }
86 
87 private:
88  ACTION_MENU* create() const override
89  {
90  return new SELECT_MENU();
91  }
92 };
93 
94 
99 {
100 public:
102 };
103 
104 
106  PCB_TOOL_BASE( "pcbnew.InteractiveSelection" ),
107  m_frame( nullptr ),
108  m_nonModifiedCursor( KICURSOR::ARROW ),
109  m_enteredGroup( nullptr ),
110  m_priv( std::make_unique<PRIV>() )
111 {
112  m_filter.lockedItems = false;
113  m_filter.footprints = true;
114  m_filter.text = true;
115  m_filter.tracks = true;
116  m_filter.vias = true;
117  m_filter.pads = true;
118  m_filter.graphics = true;
119  m_filter.zones = true;
120  m_filter.keepouts = true;
121  m_filter.dimensions = true;
122  m_filter.otherItems = true;
123 }
124 
125 
127 {
128  getView()->Remove( &m_selection );
130 
131  Disconnect( wxEVT_TIMER, wxTimerEventHandler( PCB_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
132 }
133 
134 
136 {
137  auto frame = getEditFrame<PCB_BASE_FRAME>();
138 
141  {
143  return true;
144  }
145 
146  auto selectMenu = std::make_shared<SELECT_MENU>();
147  selectMenu->SetTool( this );
148  m_menu.AddSubMenu( selectMenu );
149 
150  auto& menu = m_menu.GetMenu();
151 
152  auto activeToolCondition =
153  [ frame ] ( const SELECTION& aSel )
154  {
155  return !frame->ToolStackIsEmpty();
156  };
157 
158  auto inGroupCondition =
159  [this] ( const SELECTION& )
160  {
161  return m_enteredGroup != nullptr;
162  };
163 
164  if( frame && frame->IsType( FRAME_PCB_EDITOR ) )
165  {
166  menu.AddMenu( selectMenu.get(), SELECTION_CONDITIONS::NotEmpty );
167  menu.AddSeparator( 1000 );
168  }
169 
170  // "Cancel" goes at the top of the context menu when a tool is active
171  menu.AddItem( ACTIONS::cancelInteractive, activeToolCondition, 1 );
172  menu.AddItem( PCB_ACTIONS::groupLeave, inGroupCondition, 1 );
173  menu.AddSeparator( 1 );
174 
175  if( frame )
177 
178  m_disambiguateTimer.SetOwner( this );
179  Connect( wxEVT_TIMER, wxTimerEventHandler( PCB_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
180 
181  return true;
182 }
183 
184 
186 {
187  m_frame = getEditFrame<PCB_BASE_FRAME>();
188 
189  if( m_enteredGroup )
190  ExitGroup();
191 
192  if( aReason == TOOL_BASE::MODEL_RELOAD )
193  {
194  // Deselect any item being currently in edit, to avoid unexpected behavior
195  // and remove pointers to the selected items from containers
196  // without changing their properties (as they are already deleted
197  // while a new board is loaded)
198  ClearSelection( true );
199 
200  getView()->GetPainter()->GetSettings()->SetHighlight( false );
201  }
202  else
203  {
204  // Restore previous properties of selected items and remove them from containers
205  ClearSelection( true );
206  }
207 
208  // Reinsert the VIEW_GROUP, in case it was removed from the VIEW
209  view()->Remove( &m_selection );
210  view()->Add( &m_selection );
211 
214 }
215 
216 
217 void PCB_SELECTION_TOOL::onDisambiguationExpire( wxTimerEvent& aEvent )
218 {
220 }
221 
222 
223 void PCB_SELECTION_TOOL::OnIdle( wxIdleEvent& aEvent )
224 {
225  if( m_frame->ToolStackIsEmpty() && !m_multiple )
226  {
227  wxMouseState keyboardState = wxGetMouseState();
228 
229  setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
230  keyboardState.AltDown() );
231 
232  if( m_additive )
234  else if( m_subtractive )
236  else if( m_exclusive_or )
238  else
240  }
241 }
242 
243 
245 {
246  // Main loop: keep receiving events
247  while( TOOL_EVENT* evt = Wait() )
248  {
249  MOUSE_DRAG_ACTION dragAction = m_frame->GetDragAction();
251 
252  // on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
253  setModifiersState( evt->Modifier( MD_SHIFT ), evt->Modifier( MD_CTRL ),
254  evt->Modifier( MD_ALT ) );
255 
256  bool modifier_enabled = m_subtractive || m_additive || m_exclusive_or;
257  PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
258  bool brd_editor = frame && frame->IsType( FRAME_PCB_EDITOR );
260 
261  // If the router tool is active, don't override
262  if( router && router->IsToolActive() && router->RoutingInProgress() )
263  {
264  evt->SetPassEvent();
265  }
266  else if( evt->IsMouseDown( BUT_LEFT ) )
267  {
268  // Avoid triggering when running under other tools
270 
271  if( m_frame->ToolStackIsEmpty() && pt_tool && !pt_tool->HasPoint() )
272  {
274  m_disambiguateTimer.StartOnce( 500 );
275  }
276  }
277  else if( evt->IsClick( BUT_LEFT ) )
278  {
279  // If there is no disambiguation, this routine is still running and will
280  // register a `click` event when released
281  if( m_disambiguateTimer.IsRunning() )
282  {
283  m_disambiguateTimer.Stop();
284 
285  // Single click? Select single object
286  if( m_highlight_modifier && brd_editor )
288  else
289  {
290  m_frame->FocusOnItem( nullptr );
291  selectPoint( evt->Position() );
292  }
293  }
294 
295  m_canceledMenu = false;
296  }
297  else if( evt->IsClick( BUT_RIGHT ) )
298  {
299  m_disambiguateTimer.Stop();
300 
301  // Right click? if there is any object - show the context menu
302  bool selectionCancelled = false;
303 
304  if( m_selection.Empty() )
305  {
306  selectPoint( evt->Position(), false, &selectionCancelled );
307  m_selection.SetIsHover( true );
308  }
309 
310  if( !selectionCancelled )
312  }
313  else if( evt->IsDblClick( BUT_LEFT ) )
314  {
315  m_disambiguateTimer.Stop();
316 
317  // Double click? Display the properties window
318  m_frame->FocusOnItem( nullptr );
319 
320  if( m_selection.Empty() )
321  selectPoint( evt->Position() );
322 
323  if( m_selection.GetSize() == 1 && m_selection[0]->Type() == PCB_GROUP_T )
324  {
325  EnterGroup();
326  }
327  else
328  {
330  }
331  }
332  else if( evt->IsDblClick( BUT_MIDDLE ) )
333  {
334  // Middle double click? Do zoom to fit or zoom to objects
335  if( evt->Modifier( MD_CTRL ) ) // Is CTRL key down?
337  else
339  }
340  else if( evt->IsDrag( BUT_LEFT ) )
341  {
342  m_disambiguateTimer.Stop();
343 
344  // Is another tool already moving a new object? Don't allow a drag start
345  if( !m_selection.Empty() && m_selection[0]->HasFlag( IS_NEW | IS_MOVING ) )
346  {
347  evt->SetPassEvent();
348  continue;
349  }
350 
351  // Drag with LMB? Select multiple objects (or at least draw a selection box)
352  // or drag them
353  m_frame->FocusOnItem( nullptr );
355 
356  if( modifier_enabled || dragAction == MOUSE_DRAG_ACTION::SELECT )
357  {
358  selectMultiple();
359  }
360  else if( m_selection.Empty() && dragAction != MOUSE_DRAG_ACTION::DRAG_ANY )
361  {
362  selectMultiple();
363  }
364  else
365  {
366  // Don't allow starting a drag from a zone filled area that isn't already selected
367  auto zoneFilledAreaFilter =
368  []( const VECTOR2I& aWhere, GENERAL_COLLECTOR& aCollector,
369  PCB_SELECTION_TOOL* aTool )
370  {
371  wxPoint location = wxPoint( aWhere );
372  int accuracy = KiROUND( 5 * aCollector.GetGuide()->OnePixelInIU() );
373  std::set<EDA_ITEM*> remove;
374 
375  for( EDA_ITEM* item : aCollector )
376  {
377  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
378  {
379  ZONE* zone = static_cast<ZONE*>( item );
380 
381  if( !zone->HitTestForCorner( location, accuracy * 2 ) &&
382  !zone->HitTestForEdge( location, accuracy ) )
383  remove.insert( zone );
384  }
385  }
386 
387  for( EDA_ITEM* item : remove )
388  aCollector.Remove( item );
389  };
390 
391  // Selection is empty? try to start dragging the item under the point where drag
392  // started
393  if( m_selection.Empty() && selectCursor( false, zoneFilledAreaFilter ) )
394  m_selection.SetIsHover( true );
395 
396  // Check if dragging has started within any of selected items bounding box.
397  // We verify "HasPosition()" first to protect against edge case involving
398  // moving off menus that causes problems (issue #5250)
399  if( evt->HasPosition() && selectionContains( evt->Position() ) )
400  {
401  // Yes -> run the move tool and wait till it finishes
402  PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( m_selection.GetItem( 0 ) );
403 
404  // If there is only item in the selection and it's a track, then we need
405  // to route it.
406  bool doRouting = ( track && ( 1 == m_selection.GetSize() ) );
407 
408  if( doRouting && trackDragAction == TRACK_DRAG_ACTION::DRAG )
410  else if( doRouting && trackDragAction == TRACK_DRAG_ACTION::DRAG_FREE_ANGLE )
412  else
414  }
415  else
416  {
417  // No -> drag a selection box
418  selectMultiple();
419  }
420  }
421  }
422  else if( evt->IsCancel() )
423  {
424  m_disambiguateTimer.Stop();
425  m_frame->FocusOnItem( nullptr );
426 
427  if( m_enteredGroup )
428  ExitGroup();
429 
430  ClearSelection();
431  }
432  else
433  {
434  evt->SetPassEvent();
435  }
436 
437 
438  if( m_frame->ToolStackIsEmpty() )
439  {
440  // move cursor prediction
441  if( !modifier_enabled
442  && dragAction == MOUSE_DRAG_ACTION::DRAG_SELECTED
443  && !m_selection.Empty()
444  && evt->HasPosition()
445  && selectionContains( evt->Position() ) )
446  {
448  }
449  else
450  {
452  }
453  }
454  }
455 
456  // Shutting down; clear the selection
457  m_selection.Clear();
458  m_disambiguateTimer.Stop();
459 
460  return 0;
461 }
462 
463 
465 {
466  wxCHECK_RET( m_selection.GetSize() == 1 && m_selection[0]->Type() == PCB_GROUP_T,
467  wxT( "EnterGroup called when selection is not a single group" ) );
468  PCB_GROUP* aGroup = static_cast<PCB_GROUP*>( m_selection[0] );
469 
470  if( m_enteredGroup != nullptr )
471  ExitGroup();
472 
473  ClearSelection();
474  m_enteredGroup = aGroup;
476  m_enteredGroup->RunOnChildren( [&]( BOARD_ITEM* titem )
477  {
478  select( titem );
479  } );
480 
482 }
483 
484 
485 void PCB_SELECTION_TOOL::ExitGroup( bool aSelectGroup )
486 {
487  // Only continue if there is a group entered
488  if( m_enteredGroup == nullptr )
489  return;
490 
492  ClearSelection();
493 
494  if( aSelectGroup )
496 
498  m_enteredGroup = nullptr;
499 }
500 
501 
503 {
504  return m_selection;
505 }
506 
507 
509  bool aConfirmLockedItems )
510 {
511  bool selectionEmpty = m_selection.Empty();
512  m_selection.SetIsHover( selectionEmpty );
513 
514  if( selectionEmpty )
515  {
516  m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true, aClientFilter );
518  }
519 
520  if( aClientFilter )
521  {
522  enum DISPOSITION { BEFORE = 1, AFTER, BOTH };
523 
524  std::map<EDA_ITEM*, DISPOSITION> itemDispositions;
525  GENERAL_COLLECTOR collector;
526 
527  for( EDA_ITEM* item : m_selection )
528  {
529  collector.Append( item );
530  itemDispositions[ item ] = BEFORE;
531  }
532 
533  aClientFilter( VECTOR2I(), collector, this );
534 
535  for( EDA_ITEM* item : collector )
536  {
537  if( itemDispositions.count( item ) )
538  itemDispositions[ item ] = BOTH;
539  else
540  itemDispositions[ item ] = AFTER;
541  }
542 
543  // Unhighlight the BEFORE items before highlighting the AFTER items.
544  // This is so that in the case of groups, if aClientFilter replaces a selection
545  // with the enclosing group, the unhighlight of the element doesn't undo the
546  // recursive highlighting of that element by the group.
547 
548  for( std::pair<EDA_ITEM* const, DISPOSITION> itemDisposition : itemDispositions )
549  {
550  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( itemDisposition.first );
551  DISPOSITION disposition = itemDisposition.second;
552 
553  if( disposition == BEFORE )
554  unhighlight( item, SELECTED, &m_selection );
555  }
556 
557  for( std::pair<EDA_ITEM* const, DISPOSITION> itemDisposition : itemDispositions )
558  {
559  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( itemDisposition.first );
560  DISPOSITION disposition = itemDisposition.second;
561 
562  // Note that we must re-highlight even previously-highlighted items
563  // (ie: disposition BOTH) in case we removed any of their children.
564  if( disposition == AFTER || disposition == BOTH )
565  highlight( item, SELECTED, &m_selection );
566  }
567 
569  }
570 
571  if( aConfirmLockedItems )
572  {
573  std::vector<BOARD_ITEM*> lockedItems;
574 
575  for( EDA_ITEM* item : m_selection )
576  {
577  BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
578 
579  if( boardItem->Type() == PCB_GROUP_T )
580  {
581  PCB_GROUP* group = static_cast<PCB_GROUP*>( boardItem );
582  bool lockedDescendant = false;
583 
584  group->RunOnDescendants(
585  [&lockedDescendant]( BOARD_ITEM* child )
586  {
587  if( child->IsLocked() )
588  lockedDescendant = true;
589  } );
590 
591  if( lockedDescendant )
592  lockedItems.push_back( group );
593  }
594  else if( boardItem->IsLocked() )
595  {
596  lockedItems.push_back( boardItem );
597  }
598  }
599 
600  if( !lockedItems.empty() )
601  {
602  DIALOG_LOCKED_ITEMS_QUERY dlg( frame(), lockedItems.size() );
603 
604  switch( dlg.ShowModal() )
605  {
606  case wxID_OK:
607  // remove locked items from selection
608  for( BOARD_ITEM* item : lockedItems )
609  unselect( item );
610 
611  break;
612 
613  case wxID_CANCEL:
614  // cancel operation
615  ClearSelection();
616  break;
617 
618  case wxID_APPLY:
619  // continue with operation with current selection
620  break;
621  }
622  }
623  }
624 
625  return m_selection;
626 }
627 
628 
630 {
631  GENERAL_COLLECTORS_GUIDE guide( board()->GetVisibleLayers(),
632  (PCB_LAYER_ID) view()->GetTopLayer(), view() );
633 
634  bool padsDisabled = !board()->IsElementVisible( LAYER_PADS );
635 
636  // account for the globals
637  guide.SetIgnoreMTextsMarkedNoShow( ! board()->IsElementVisible( LAYER_MOD_TEXT_INVISIBLE ) );
638  guide.SetIgnoreMTextsOnBack( ! board()->IsElementVisible( LAYER_MOD_TEXT ) );
639  guide.SetIgnoreMTextsOnFront( ! board()->IsElementVisible( LAYER_MOD_TEXT ) );
640  guide.SetIgnoreModulesOnBack( ! board()->IsElementVisible( LAYER_MOD_BK ) );
641  guide.SetIgnoreModulesOnFront( ! board()->IsElementVisible( LAYER_MOD_FR ) );
642  guide.SetIgnorePadsOnBack( padsDisabled || ! board()->IsElementVisible( LAYER_PAD_BK ) );
643  guide.SetIgnorePadsOnFront( padsDisabled || ! board()->IsElementVisible( LAYER_PAD_FR ) );
644  guide.SetIgnoreThroughHolePads( padsDisabled || ! board()->IsElementVisible( LAYER_PADS_TH ) );
645  guide.SetIgnoreModulesVals( ! board()->IsElementVisible( LAYER_MOD_VALUES ) );
646  guide.SetIgnoreModulesRefs( ! board()->IsElementVisible( LAYER_MOD_REFERENCES ) );
647  guide.SetIgnoreThroughVias( ! board()->IsElementVisible( LAYER_VIAS ) );
648  guide.SetIgnoreBlindBuriedVias( ! board()->IsElementVisible( LAYER_VIAS ) );
649  guide.SetIgnoreMicroVias( ! board()->IsElementVisible( LAYER_VIAS ) );
650  guide.SetIgnoreTracks( ! board()->IsElementVisible( LAYER_TRACKS ) );
651 
652  return guide;
653 }
654 
655 
656 bool PCB_SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag,
657  bool* aSelectionCancelledFlag,
658  CLIENT_SELECTION_FILTER aClientFilter )
659 {
661  GENERAL_COLLECTOR collector;
662  const PCB_DISPLAY_OPTIONS& displayOpts = m_frame->GetDisplayOptions();
663 
665 
666  if( m_enteredGroup && !m_enteredGroup->GetBoundingBox().Contains( (wxPoint) aWhere ) )
667  ExitGroup();
668 
671  (wxPoint) aWhere, guide );
672 
673  // Remove unselectable items
674  for( int i = collector.GetCount() - 1; i >= 0; --i )
675  {
676  if( !Selectable( collector[ i ] ) || ( aOnDrag && collector[i]->IsLocked() ) )
677  collector.Remove( i );
678  }
679 
681 
682  // Allow the client to do tool- or action-specific filtering to see if we can get down
683  // to a single item
684  if( aClientFilter )
685  aClientFilter( aWhere, collector, this );
686 
687  FilterCollectorForHierarchy( collector, false );
688 
689  // Apply the stateful filter
690  FilterCollectedItems( collector, false );
691 
692  // Apply some ugly heuristics to avoid disambiguation menus whenever possible
693  if( collector.GetCount() > 1 && !m_skip_heuristics )
694  GuessSelectionCandidates( collector, aWhere );
695 
696  // If still more than one item we're going to have to ask the user.
697  if( collector.GetCount() > 1 )
698  {
699  if( aOnDrag )
701 
702  if( !doSelectionMenu( &collector ) )
703  {
704  if( aSelectionCancelledFlag )
705  *aSelectionCancelledFlag = true;
706 
707  return false;
708  }
709  }
710 
711  bool anyAdded = false;
712  bool anySubtracted = false;
713 
715  {
716  if( m_selection.GetSize() > 0 )
717  {
718  ClearSelection( true /*quiet mode*/ );
719  anySubtracted = true;
720  }
721  }
722 
723  if( collector.GetCount() > 0 )
724  {
725  for( int i = 0; i < collector.GetCount(); ++i )
726  {
727  if( m_subtractive || ( m_exclusive_or && collector[i]->IsSelected() ) )
728  {
729  unselect( collector[i] );
730  anySubtracted = true;
731  }
732  else
733  {
734  select( collector[i] );
735  anyAdded = true;
736  }
737  }
738  }
739 
740  if( anyAdded )
741  {
743  return true;
744  }
745  else if( anySubtracted )
746  {
748  return true;
749  }
750 
751  return false;
752 }
753 
754 
755 bool PCB_SELECTION_TOOL::selectCursor( bool aForceSelect, CLIENT_SELECTION_FILTER aClientFilter )
756 {
757  if( aForceSelect || m_selection.Empty() )
758  {
759  ClearSelection( true /*quiet mode*/ );
760  selectPoint( getViewControls()->GetCursorPosition( false ), false, nullptr, aClientFilter );
761  }
762 
763  return !m_selection.Empty();
764 }
765 
766 
767 // Some navigation actions are allowed in selectMultiple
777  &ACTIONS::zoomFitObjects, nullptr };
778 
779 
781 {
782  bool cancelled = false; // Was the tool cancelled while it was running?
783  m_multiple = true; // Multiple selection mode is active
784  KIGFX::VIEW* view = getView();
785 
787  view->Add( &area );
788 
789  bool anyAdded = false;
790  bool anySubtracted = false;
791 
792  while( TOOL_EVENT* evt = Wait() )
793  {
794  int width = area.GetEnd().x - area.GetOrigin().x;
795 
796  /* Selection mode depends on direction of drag-selection:
797  * Left > Right : Select objects that are fully enclosed by selection
798  * Right > Left : Select objects that are crossed by selection
799  */
800  bool windowSelection = width >= 0 ? true : false;
801 
802  if( view->IsMirroredX() )
803  windowSelection = !windowSelection;
804 
807 
808  if( evt->IsCancelInteractive() || evt->IsActivate() )
809  {
810  cancelled = true;
811  break;
812  }
813 
814  if( evt->IsDrag( BUT_LEFT ) )
815  {
817  {
818  if( m_selection.GetSize() > 0 )
819  {
820  anySubtracted = true;
821  ClearSelection( true /*quiet mode*/ );
822  }
823  }
824 
825  // Start drawing a selection box
826  area.SetOrigin( evt->DragOrigin() );
827  area.SetEnd( evt->Position() );
830  area.SetExclusiveOr( false );
831 
832  view->SetVisible( &area, true );
833  view->Update( &area );
834  getViewControls()->SetAutoPan( true );
835  }
836 
837  if( evt->IsMouseUp( BUT_LEFT ) )
838  {
839  getViewControls()->SetAutoPan( false );
840 
841  // End drawing the selection box
842  view->SetVisible( &area, false );
843 
844  std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> candidates;
845  BOX2I selectionBox = area.ViewBBox();
846  view->Query( selectionBox, candidates ); // Get the list of nearby items
847 
848  int height = area.GetEnd().y - area.GetOrigin().y;
849 
850  // Construct an EDA_RECT to determine BOARD_ITEM selection
851  EDA_RECT selectionRect( (wxPoint) area.GetOrigin(), wxSize( width, height ) );
852 
853  selectionRect.Normalize();
854 
855  GENERAL_COLLECTOR collector;
856 
857  for( auto it = candidates.begin(), it_end = candidates.end(); it != it_end; ++it )
858  {
859  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it->first );
860 
861  if( item && Selectable( item ) && item->HitTest( selectionRect, windowSelection ) )
862  collector.Append( item );
863  }
864 
865  // Apply the stateful filter
866  FilterCollectedItems( collector, true );
867 
868  FilterCollectorForHierarchy( collector, true );
869 
870  for( EDA_ITEM* i : collector )
871  {
872  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
873 
874  if( m_subtractive || ( m_exclusive_or && item->IsSelected() ) )
875  {
876  unselect( item );
877  anySubtracted = true;
878  }
879  else
880  {
881  select( item );
882  anyAdded = true;
883  }
884  }
885 
886  m_selection.SetIsHover( false );
887 
888  // Inform other potentially interested tools
889  if( anyAdded )
891  else if( anySubtracted )
893 
894  break; // Stop waiting for events
895  }
896 
897  // Allow some actions for navigation
898  for( int i = 0; allowedActions[i]; ++i )
899  {
900  if( evt->IsAction( allowedActions[i] ) )
901  {
902  evt->SetPassEvent();
903  break;
904  }
905  }
906  }
907 
908  getViewControls()->SetAutoPan( false );
909 
910  // Stop drawing the selection box
911  view->Remove( &area );
912  m_multiple = false; // Multiple selection mode is inactive
913 
914  if( !cancelled )
916 
918 
919  return cancelled;
920 }
921 
922 
924 {
925  wxMouseState keyboardState = wxGetMouseState();
926 
927  setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
928  keyboardState.AltDown() );
929 
930  m_skip_heuristics = true;
932  m_skip_heuristics = false;
933 
934  return 0;
935 }
936 
937 
938 
940 {
942 
943  selectCursor( false, aClientFilter );
944 
945  return 0;
946 }
947 
948 
950 {
951  ClearSelection();
952 
953  return 0;
954 }
955 
956 
958 {
959  std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
960 
961  if( items )
962  {
963  // Perform individual selection of each item before processing the event.
964  for( BOARD_ITEM* item : *items )
965  select( item );
966 
968  }
969 
970  return 0;
971 }
972 
973 
975 {
976  AddItemToSel( aEvent.Parameter<BOARD_ITEM*>() );
977  return 0;
978 }
979 
980 
982 {
983  KIGFX::VIEW* view = getView();
984 
985  // hold all visible items
986  std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
987 
988  // Filter the view items based on the selection box
989  BOX2I selectionBox;
990 
991  // Intermediate step to allow filtering against hierarchy
992  GENERAL_COLLECTOR collection;
993 
994  selectionBox.SetMaximum();
995  view->Query( selectionBox, selectedItems ); // Get the list of selected items
996 
997  for( const KIGFX::VIEW::LAYER_ITEM_PAIR& item_pair : selectedItems )
998  {
999  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( item_pair.first );
1000 
1001  if( !item || !Selectable( item ) || !itemPassesFilter( item, true ) )
1002  continue;
1003 
1004  collection.Append( item );
1005  }
1006 
1007  FilterCollectorForHierarchy( collection, true );
1008 
1009  for( EDA_ITEM* item : collection )
1010  select( static_cast<BOARD_ITEM*>( item ) );
1011 
1013 
1014  return 0;
1015 }
1016 
1017 
1018 void PCB_SELECTION_TOOL::AddItemToSel( BOARD_ITEM* aItem, bool aQuietMode )
1019 {
1020  if( aItem )
1021  {
1022  select( aItem );
1023 
1024  // Inform other potentially interested tools
1025  if( !aQuietMode )
1027  }
1028 }
1029 
1030 
1032 {
1033  std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
1034 
1035  if( items )
1036  {
1037  // Perform individual unselection of each item before processing the event
1038  for( auto item : *items )
1039  unselect( item );
1040 
1042  }
1043 
1044  return 0;
1045 }
1046 
1047 
1049 {
1050  RemoveItemFromSel( aEvent.Parameter<BOARD_ITEM*>() );
1051  return 0;
1052 }
1053 
1054 
1055 void PCB_SELECTION_TOOL::RemoveItemFromSel( BOARD_ITEM* aItem, bool aQuietMode )
1056 {
1057  if( aItem )
1058  {
1059  unselect( aItem );
1060 
1061  if( !aQuietMode )
1062  {
1063  // Inform other potentially interested tools
1065  }
1066  }
1067 }
1068 
1069 
1071 {
1072  highlight( aItem, BRIGHTENED );
1073 }
1074 
1075 
1077 {
1078  unhighlight( aItem, BRIGHTENED );
1079 }
1080 
1081 
1083  PCB_SELECTION_TOOL* sTool )
1084 {
1085  // Narrow the collection down to a single BOARD_CONNECTED_ITEM for each represented net.
1086  // All other items types are removed.
1087  std::set<int> representedNets;
1088 
1089  for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
1090  {
1091  BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( aCollector[i] );
1092  if( !item )
1093  aCollector.Remove( i );
1094  else if ( representedNets.count( item->GetNetCode() ) )
1095  aCollector.Remove( i );
1096  else
1097  representedNets.insert( item->GetNetCode() );
1098  }
1099 }
1100 
1101 
1103 {
1104  unsigned initialCount = 0;
1105 
1106  for( const EDA_ITEM* item : m_selection.GetItems() )
1107  {
1108  if( dynamic_cast<const BOARD_CONNECTED_ITEM*>( item ) )
1109  initialCount++;
1110  }
1111 
1112  if( initialCount == 0 )
1114 
1115  for( STOP_CONDITION stopCondition : { STOP_AT_JUNCTION, STOP_AT_PAD, STOP_NEVER } )
1116  {
1117  // copy the selection, since we're going to iterate and modify
1118  std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
1119 
1120  for( EDA_ITEM* item : selectedItems )
1121  item->ClearTempFlags();
1122 
1123  for( EDA_ITEM* item : selectedItems )
1124  {
1125  PCB_TRACK* trackItem = dynamic_cast<PCB_TRACK*>( item );
1126 
1127  // Track items marked SKIP_STRUCT have already been visited
1128  if( trackItem && !( trackItem->GetFlags() & SKIP_STRUCT ) )
1129  selectConnectedTracks( *trackItem, stopCondition );
1130  }
1131 
1132  if( m_selection.GetItems().size() > initialCount )
1133  break;
1134  }
1135 
1136  // Inform other potentially interested tools
1137  if( m_selection.Size() > 0 )
1139 
1140  return 0;
1141 }
1142 
1143 
1145  STOP_CONDITION aStopCondition )
1146 {
1147  constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, EOT };
1148  constexpr PCB_LAYER_ID ALL_LAYERS = UNDEFINED_LAYER;
1149 
1150  auto connectivity = board()->GetConnectivity();
1151  auto connectedItems = connectivity->GetConnectedItems( &aStartItem, types, true );
1152 
1153  std::map<wxPoint, std::vector<PCB_TRACK*>> trackMap;
1154  std::map<wxPoint, PCB_VIA*> viaMap;
1155  std::map<wxPoint, PAD*> padMap;
1156 
1157  // Build maps of connected items
1158  for( BOARD_CONNECTED_ITEM* item : connectedItems )
1159  {
1160  switch( item->Type() )
1161  {
1162  case PCB_ARC_T:
1163  case PCB_TRACE_T:
1164  {
1165  PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
1166  trackMap[ track->GetStart() ].push_back( track );
1167  trackMap[ track->GetEnd() ].push_back( track );
1168  break;
1169  }
1170 
1171  case PCB_VIA_T:
1172  {
1173  PCB_VIA* via = static_cast<PCB_VIA*>( item );
1174  viaMap[ via->GetStart() ] = via;
1175  break;
1176  }
1177 
1178  case PCB_PAD_T:
1179  {
1180  PAD* pad = static_cast<PAD*>( item );
1181  padMap[ pad->GetPosition() ] = pad;
1182  break;
1183  }
1184 
1185  default:
1186  break;
1187  }
1188 
1189  item->ClearFlags( TEMP_SELECTED );
1190  }
1191 
1192  std::vector< std::pair<wxPoint, PCB_LAYER_ID> > activePts;
1193 
1194  // Set up the initial active points
1195  switch( aStartItem.Type() )
1196  {
1197  case PCB_ARC_T:
1198  case PCB_TRACE_T:
1199  {
1200  PCB_TRACK* track = static_cast<PCB_TRACK*>( &aStartItem );
1201 
1202  activePts.push_back( { track->GetStart(), track->GetLayer() } );
1203  activePts.push_back( { track->GetEnd(), track->GetLayer() } );
1204  }
1205  break;
1206 
1207  case PCB_VIA_T:
1208  activePts.push_back( { aStartItem.GetPosition(), ALL_LAYERS } );
1209  break;
1210 
1211  case PCB_PAD_T:
1212  activePts.push_back( { aStartItem.GetPosition(), ALL_LAYERS } );
1213  break;
1214 
1215  default:
1216  break;
1217  }
1218 
1219  bool expand = true;
1220  int failSafe = 0;
1221 
1222  // Iterative push from all active points
1223  while( expand && failSafe++ < 100000 )
1224  {
1225  expand = false;
1226 
1227  for( int i = activePts.size() - 1; i >= 0; --i )
1228  {
1229  wxPoint pt = activePts[i].first;
1230  PCB_LAYER_ID layer = activePts[i].second;
1231  size_t pt_count = 0;
1232 
1233  for( PCB_TRACK* track : trackMap[pt] )
1234  {
1235  if( layer == ALL_LAYERS || layer == track->GetLayer() )
1236  pt_count++;
1237  }
1238 
1239  if( aStopCondition == STOP_AT_JUNCTION )
1240  {
1241  if( pt_count > 2
1242  || ( viaMap.count( pt ) && layer != ALL_LAYERS )
1243  || ( padMap.count( pt ) && layer != ALL_LAYERS ) )
1244  {
1245  activePts.erase( activePts.begin() + i );
1246  continue;
1247  }
1248  }
1249  else if( aStopCondition == STOP_AT_PAD )
1250  {
1251  if( padMap.count( pt ) )
1252  {
1253  activePts.erase( activePts.begin() + i );
1254  continue;
1255  }
1256  }
1257 
1258  if( padMap.count( pt ) )
1259  {
1260  PAD* pad = padMap[ pt ];
1261 
1262  if( !( pad->GetFlags() & TEMP_SELECTED ) )
1263  {
1264  pad->SetFlags( TEMP_SELECTED );
1265  activePts.push_back( { pad->GetPosition(), ALL_LAYERS } );
1266  expand = true;
1267  }
1268  }
1269 
1270  for( PCB_TRACK* track : trackMap[ pt ] )
1271  {
1272  if( layer != ALL_LAYERS && track->GetLayer() != layer )
1273  continue;
1274 
1275  if( !track->IsSelected() )
1276  {
1277  select( track );
1278 
1279  if( track->GetStart() == pt )
1280  activePts.push_back( { track->GetEnd(), track->GetLayer() } );
1281  else
1282  activePts.push_back( { track->GetStart(), track->GetLayer() } );
1283 
1284  expand = true;
1285  }
1286  }
1287 
1288  if( viaMap.count( pt ) )
1289  {
1290  PCB_VIA* via = viaMap[ pt ];
1291 
1292  if( !via->IsSelected() )
1293  {
1294  select( via );
1295  activePts.push_back( { via->GetPosition(), ALL_LAYERS } );
1296  expand = true;
1297  }
1298  }
1299 
1300  activePts.erase( activePts.begin() + i );
1301  }
1302  }
1303 }
1304 
1305 
1306 void PCB_SELECTION_TOOL::selectAllItemsOnNet( int aNetCode, bool aSelect )
1307 {
1308  constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, EOT };
1309  auto connectivity = board()->GetConnectivity();
1310 
1311  for( BOARD_CONNECTED_ITEM* item : connectivity->GetNetItems( aNetCode, types ) )
1312  {
1313  if( itemPassesFilter( item, true ) )
1314  aSelect ? select( item ) : unselect( item );
1315  }
1316 }
1317 
1318 
1320 {
1321  bool select = aEvent.IsAction( &PCB_ACTIONS::selectNet );
1322 
1323  // If we've been passed an argument, just select that netcode1
1324  int netcode = aEvent.Parameter<intptr_t>();
1325 
1326  if( netcode > 0 )
1327  {
1328  selectAllItemsOnNet( netcode, select );
1329  return 0;
1330  }
1331 
1332  if( !selectCursor() )
1333  return 0;
1334 
1335  // copy the selection, since we're going to iterate and modify
1336  auto selection = m_selection.GetItems();
1337 
1338  for( EDA_ITEM* i : selection )
1339  {
1340  BOARD_CONNECTED_ITEM* connItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( i );
1341 
1342  if( connItem )
1343  selectAllItemsOnNet( connItem->GetNetCode(), select );
1344  }
1345 
1346  // Inform other potentially interested tools
1347  if( m_selection.Size() > 0 )
1349 
1350  return 0;
1351 }
1352 
1353 
1354 void PCB_SELECTION_TOOL::selectAllItemsOnSheet( wxString& aSheetPath )
1355 {
1356  std::list<FOOTPRINT*> footprintList;
1357 
1358  // store all footprints that are on that sheet path
1359  for( FOOTPRINT* footprint : board()->Footprints() )
1360  {
1361  if( footprint == nullptr )
1362  continue;
1363 
1364  wxString footprint_path = footprint->GetPath().AsString().BeforeLast('/');
1365 
1366  if( aSheetPath.IsEmpty() )
1367  aSheetPath += '/';
1368 
1369  if( footprint_path.StartsWith( aSheetPath ) )
1370  footprintList.push_back( footprint );
1371  }
1372 
1373  // Generate a list of all pads, and of all nets they belong to.
1374  std::list<int> netcodeList;
1375  std::list<PAD*> padList;
1376 
1377  for( FOOTPRINT* footprint : footprintList )
1378  {
1379  for( PAD* pad : footprint->Pads() )
1380  {
1381  if( pad->IsConnected() )
1382  {
1383  netcodeList.push_back( pad->GetNetCode() );
1384  padList.push_back( pad );
1385  }
1386  }
1387  }
1388 
1389  // remove all duplicates
1390  netcodeList.sort();
1391  netcodeList.unique();
1392 
1393  for( PAD* pad : padList )
1395 
1396  // now we need to find all footprints that are connected to each of these nets then we need
1397  // to determine if these footprints are in the list of footprints belonging to this sheet
1398  std::list<int> removeCodeList;
1399  constexpr KICAD_T padType[] = { PCB_PAD_T, EOT };
1400 
1401  for( int netCode : netcodeList )
1402  {
1403  for( BOARD_CONNECTED_ITEM* mitem : board()->GetConnectivity()->GetNetItems( netCode,
1404  padType ) )
1405  {
1406  if( mitem->Type() == PCB_PAD_T && !alg::contains( footprintList, mitem->GetParent() ) )
1407  {
1408  // if we cannot find the footprint of the pad in the footprintList then we can
1409  // assume that that footprint is not located in the same schematic, therefore
1410  // invalidate this netcode.
1411  removeCodeList.push_back( netCode );
1412  break;
1413  }
1414  }
1415  }
1416 
1417  // remove all duplicates
1418  removeCodeList.sort();
1419  removeCodeList.unique();
1420 
1421  for( int removeCode : removeCodeList )
1422  {
1423  netcodeList.remove( removeCode );
1424  }
1425 
1426  std::list<BOARD_CONNECTED_ITEM*> localConnectionList;
1427  constexpr KICAD_T trackViaType[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, EOT };
1428 
1429  for( int netCode : netcodeList )
1430  {
1431  for( BOARD_CONNECTED_ITEM* item : board()->GetConnectivity()->GetNetItems( netCode,
1432  trackViaType ) )
1433  localConnectionList.push_back( item );
1434  }
1435 
1436  for( BOARD_ITEM* i : footprintList )
1437  {
1438  if( i != nullptr )
1439  select( i );
1440  }
1441 
1442  for( BOARD_CONNECTED_ITEM* i : localConnectionList )
1443  {
1444  if( i != nullptr )
1445  select( i );
1446  }
1447 }
1448 
1449 
1451 {
1452  // Should recalculate the view to zoom in on the selection.
1453  auto selectionBox = m_selection.GetBoundingBox();
1454  auto view = getView();
1455 
1456  VECTOR2D screenSize = view->ToWorld( m_frame->GetCanvas()->GetClientSize(), false );
1457  screenSize.x = std::max( 10.0, screenSize.x );
1458  screenSize.y = std::max( 10.0, screenSize.y );
1459 
1460  if( selectionBox.GetWidth() != 0 || selectionBox.GetHeight() != 0 )
1461  {
1462  VECTOR2D vsize = selectionBox.GetSize();
1463  double scale = view->GetScale() / std::max( fabs( vsize.x / screenSize.x ),
1464  fabs( vsize.y / screenSize.y ) );
1465  view->SetScale( scale );
1466  view->SetCenter( selectionBox.Centre() );
1467  view->Add( &m_selection );
1468  }
1469 
1471 }
1472 
1473 
1475 {
1476  ClearSelection( true /*quiet mode*/ );
1477  wxString sheetPath = *aEvent.Parameter<wxString*>();
1478 
1479  selectAllItemsOnSheet( sheetPath );
1480 
1481  zoomFitSelection();
1482 
1483  if( m_selection.Size() > 0 )
1485 
1486  return 0;
1487 }
1488 
1489 
1491 {
1492  // this function currently only supports footprints since they are only on one sheet.
1493  EDA_ITEM* item = m_selection.Front();
1494 
1495  if( !item )
1496  return 0;
1497 
1498  if( item->Type() != PCB_FOOTPRINT_T )
1499  return 0;
1500 
1501  FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( item );
1502 
1503  if( footprint->GetPath().empty() )
1504  return 0;
1505 
1506  ClearSelection( true /*quiet mode*/ );
1507 
1508  // get the sheet path only.
1509  wxString sheetPath = footprint->GetPath().AsString().BeforeLast( '/' );
1510 
1511  if( sheetPath.IsEmpty() )
1512  sheetPath += '/';
1513 
1514  selectAllItemsOnSheet( sheetPath );
1515 
1516  // Inform other potentially interested tools
1517  if( m_selection.Size() > 0 )
1519 
1520  return 0;
1521 }
1522 
1523 
1525 {
1526  bool cleared = false;
1527 
1528  if( m_selection.GetSize() > 0 )
1529  {
1530  // Don't fire an event now; most of the time it will be redundant as we're about to
1531  // fire a SelectedEvent.
1532  cleared = true;
1533  ClearSelection( true /*quiet mode*/ );
1534  }
1535 
1536  if( aItem )
1537  {
1538  select( aItem );
1539  m_frame->FocusOnLocation( aItem->GetPosition() );
1540 
1541  // Inform other potentially interested tools
1543  }
1544  else if( cleared )
1545  {
1547  }
1548 
1550 }
1551 
1552 
1558 static bool itemIsIncludedByFilter( const BOARD_ITEM& aItem, const BOARD& aBoard,
1559  const DIALOG_FILTER_SELECTION::OPTIONS& aFilterOptions )
1560 {
1561  bool include = true;
1562  const PCB_LAYER_ID layer = aItem.GetLayer();
1563 
1564  // if the item needs to be checked against the options
1565  if( include )
1566  {
1567  switch( aItem.Type() )
1568  {
1569  case PCB_FOOTPRINT_T:
1570  {
1571  const FOOTPRINT& footprint = static_cast<const FOOTPRINT&>( aItem );
1572 
1573  include = aFilterOptions.includeModules;
1574 
1575  if( include && !aFilterOptions.includeLockedModules )
1576  include = !footprint.IsLocked();
1577 
1578  break;
1579  }
1580  case PCB_TRACE_T:
1581  case PCB_ARC_T:
1582  include = aFilterOptions.includeTracks;
1583  break;
1584 
1585  case PCB_VIA_T:
1586  include = aFilterOptions.includeVias;
1587  break;
1588 
1589  case PCB_FP_ZONE_T:
1590  case PCB_ZONE_T:
1591  include = aFilterOptions.includeZones;
1592  break;
1593 
1594  case PCB_SHAPE_T:
1595  case PCB_TARGET_T:
1596  case PCB_DIM_ALIGNED_T:
1597  case PCB_DIM_CENTER_T:
1598  case PCB_DIM_ORTHOGONAL_T:
1599  case PCB_DIM_LEADER_T:
1600  if( layer == Edge_Cuts )
1601  include = aFilterOptions.includeBoardOutlineLayer;
1602  else
1603  include = aFilterOptions.includeItemsOnTechLayers;
1604  break;
1605 
1606  case PCB_FP_TEXT_T:
1607  case PCB_TEXT_T:
1608  include = aFilterOptions.includePcbTexts;
1609  break;
1610 
1611  default:
1612  // no filtering, just select it
1613  break;
1614  }
1615  }
1616 
1617  return include;
1618 }
1619 
1620 
1622 {
1623  const BOARD& board = *getModel<BOARD>();
1624  DIALOG_FILTER_SELECTION::OPTIONS& opts = m_priv->m_filterOpts;
1625  DIALOG_FILTER_SELECTION dlg( m_frame, opts );
1626 
1627  const int cmd = dlg.ShowModal();
1628 
1629  if( cmd != wxID_OK )
1630  return 0;
1631 
1632  // copy current selection
1633  std::deque<EDA_ITEM*> selection = m_selection.GetItems();
1634 
1635  ClearSelection( true /*quiet mode*/ );
1636 
1637  // re-select items from the saved selection according to the dialog options
1638  for( EDA_ITEM* i : selection )
1639  {
1640  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
1641  bool include = itemIsIncludedByFilter( *item, board, opts );
1642 
1643  if( include )
1644  select( item );
1645  }
1646 
1648 
1649  return 0;
1650 }
1651 
1652 
1653 void PCB_SELECTION_TOOL::FilterCollectedItems( GENERAL_COLLECTOR& aCollector, bool aMultiSelect )
1654 {
1655  if( aCollector.GetCount() == 0 )
1656  return;
1657 
1658  std::set<BOARD_ITEM*> rejected;
1659 
1660  for( EDA_ITEM* i : aCollector )
1661  {
1662  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
1663 
1664  if( !itemPassesFilter( item, aMultiSelect ) )
1665  rejected.insert( item );
1666  }
1667 
1668  for( BOARD_ITEM* item : rejected )
1669  aCollector.Remove( item );
1670 }
1671 
1672 
1673 bool PCB_SELECTION_TOOL::itemPassesFilter( BOARD_ITEM* aItem, bool aMultiSelect )
1674 {
1675  if( !m_filter.lockedItems )
1676  {
1677  if( aItem->IsLocked() || ( aItem->GetParent() && aItem->GetParent()->IsLocked() ) )
1678  {
1679  if( aItem->Type() == PCB_PAD_T && !aMultiSelect )
1680  {
1681  // allow a single pad to be selected -- there are a lot of operations that
1682  // require this so we allow this one inconsistency
1683  }
1684  else
1685  {
1686  return false;
1687  }
1688  }
1689  }
1690 
1691  switch( aItem->Type() )
1692  {
1693  case PCB_FOOTPRINT_T:
1694  if( !m_filter.footprints )
1695  return false;
1696 
1697  break;
1698 
1699  case PCB_PAD_T:
1700  if( !m_filter.pads )
1701  return false;
1702 
1703  break;
1704 
1705  case PCB_TRACE_T:
1706  case PCB_ARC_T:
1707  if( !m_filter.tracks )
1708  return false;
1709 
1710  break;
1711 
1712  case PCB_VIA_T:
1713  if( !m_filter.vias )
1714  return false;
1715 
1716  break;
1717 
1718  case PCB_FP_ZONE_T:
1719  case PCB_ZONE_T:
1720  {
1721  ZONE* zone = static_cast<ZONE*>( aItem );
1722 
1723  if( ( !m_filter.zones && !zone->GetIsRuleArea() )
1724  || ( !m_filter.keepouts && zone->GetIsRuleArea() ) )
1725  {
1726  return false;
1727  }
1728 
1729  break;
1730  }
1731 
1732  case PCB_FP_SHAPE_T:
1733  case PCB_SHAPE_T:
1734  case PCB_TARGET_T:
1735  if( !m_filter.graphics )
1736  return false;
1737 
1738  break;
1739 
1740  case PCB_FP_TEXT_T:
1741  case PCB_TEXT_T:
1742  if( !m_filter.text )
1743  return false;
1744 
1745  break;
1746 
1747  case PCB_DIM_ALIGNED_T:
1748  case PCB_DIM_CENTER_T:
1749  case PCB_DIM_ORTHOGONAL_T:
1750  case PCB_DIM_LEADER_T:
1751  if( !m_filter.dimensions )
1752  return false;
1753 
1754  break;
1755 
1756  default:
1757  if( !m_filter.otherItems )
1758  return false;
1759  }
1760 
1761  return true;
1762 }
1763 
1764 
1765 void PCB_SELECTION_TOOL::ClearSelection( bool aQuietMode )
1766 {
1767  if( m_selection.Empty() )
1768  return;
1769 
1770  while( m_selection.GetSize() )
1771  unhighlight( static_cast<BOARD_ITEM*>( m_selection.Front() ), SELECTED, &m_selection );
1772 
1773  view()->Update( &m_selection );
1774 
1775  m_selection.SetIsHover( false );
1777 
1778  // Inform other potentially interested tools
1779  if( !aQuietMode )
1780  {
1783  }
1784 }
1785 
1786 
1788 {
1789  m_selection.Clear();
1790 
1791  bool enteredGroupFound = false;
1792 
1793  INSPECTOR_FUNC inspector =
1794  [&]( EDA_ITEM* item, void* testData )
1795  {
1796  if( item->IsSelected() )
1797  {
1798  EDA_ITEM* parent = item->GetParent();
1799 
1800  // Let selected parents handle their children.
1801  if( parent && parent->IsSelected() )
1802  return SEARCH_RESULT::CONTINUE;
1803 
1804  highlight( (BOARD_ITEM*) item, SELECTED, &m_selection );
1805  }
1806 
1807  if( item == m_enteredGroup )
1808  {
1809  item->SetFlags( ENTERED );
1810  enteredGroupFound = true;
1811  }
1812  else
1813  {
1814  item->ClearFlags( ENTERED );
1815  }
1816 
1817  return SEARCH_RESULT::CONTINUE;
1818  };
1819 
1822 
1823  if( !enteredGroupFound )
1824  {
1826  m_enteredGroup = nullptr;
1827  }
1828 }
1829 
1830 
1832 {
1833  GENERAL_COLLECTOR* collector = aEvent.Parameter<GENERAL_COLLECTOR*>();
1834 
1835  doSelectionMenu( collector );
1836 
1837  return 0;
1838 }
1839 
1840 
1842 {
1843  BOARD_ITEM* current = nullptr;
1844  PCB_SELECTION highlightGroup;
1845  bool selectAll = false;
1846  bool expandSelection = false;
1847 
1848  highlightGroup.SetLayer( LAYER_SELECT_OVERLAY );
1849  getView()->Add( &highlightGroup );
1850 
1851  do
1852  {
1854  if( expandSelection )
1855  aCollector->Combine();
1856 
1857  expandSelection = false;
1858 
1859  int limit = std::min( 9, aCollector->GetCount() );
1860  ACTION_MENU menu( true );
1861 
1862  for( int i = 0; i < limit; ++i )
1863  {
1864  wxString text;
1865  BOARD_ITEM* item = ( *aCollector )[i];
1867 
1868  wxString menuText = wxString::Format( wxT( "&%d. %s\t%d" ), i + 1, text, i + 1 );
1869  menu.Add( menuText, i + 1, item->GetMenuImage() );
1870  }
1871 
1872  menu.AppendSeparator();
1873  menu.Add( _( "Select &All\tA" ), limit + 1, BITMAPS::INVALID_BITMAP );
1874 
1875  if( !expandSelection && aCollector->HasAdditionalItems() )
1876  menu.Add( _( "&Expand Selection\tE" ), limit + 2, BITMAPS::INVALID_BITMAP );
1877 
1878  if( aCollector->m_MenuTitle.Length() )
1879  {
1880  menu.SetTitle( aCollector->m_MenuTitle );
1881  menu.SetIcon( BITMAPS::info );
1882  menu.DisplayTitle( true );
1883  }
1884  else
1885  {
1886  menu.DisplayTitle( false );
1887  }
1888 
1889  SetContextMenu( &menu, CMENU_NOW );
1890 
1891  while( TOOL_EVENT* evt = Wait() )
1892  {
1893  if( evt->Action() == TA_CHOICE_MENU_UPDATE )
1894  {
1895  if( selectAll )
1896  {
1897  for( int i = 0; i < aCollector->GetCount(); ++i )
1898  unhighlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
1899  }
1900  else if( current )
1901  {
1902  unhighlight( current, BRIGHTENED, &highlightGroup );
1903  }
1904 
1905  int id = *evt->GetCommandId();
1906 
1907  // User has pointed an item, so show it in a different way
1908  if( id > 0 && id <= limit )
1909  {
1910  current = ( *aCollector )[id - 1];
1911  highlight( current, BRIGHTENED, &highlightGroup );
1912  }
1913  else
1914  {
1915  current = nullptr;
1916  }
1917 
1918  // User has pointed on the "Select All" option
1919  if( id == limit + 1 )
1920  {
1921  for( int i = 0; i < aCollector->GetCount(); ++i )
1922  highlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
1923  selectAll = true;
1924  }
1925  else
1926  {
1927  selectAll = false;
1928  }
1929  }
1930  else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
1931  {
1932  if( selectAll )
1933  {
1934  for( int i = 0; i < aCollector->GetCount(); ++i )
1935  unhighlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
1936  }
1937  else if( current )
1938  {
1939  unhighlight( current, BRIGHTENED, &highlightGroup );
1940  }
1941 
1942  OPT<int> id = evt->GetCommandId();
1943 
1944  // User has selected the "Select All" option
1945  if( id == limit + 1 )
1946  {
1947  selectAll = true;
1948  current = nullptr;
1949  }
1950  else if( id == limit + 2 )
1951  {
1952  expandSelection = true;
1953  selectAll = false;
1954  current = nullptr;
1955  }
1956  // User has selected an item, so this one will be returned
1957  else if( id && ( *id > 0 ) && ( *id <= limit ) )
1958  {
1959  selectAll = false;
1960  current = ( *aCollector )[*id - 1];
1961  }
1962  else
1963  {
1964  selectAll = false;
1965  current = nullptr;
1966  }
1967  }
1968  else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
1969  {
1970  break;
1971  }
1972  }
1973  } while( expandSelection );
1974 
1975  getView()->Remove( &highlightGroup );
1976 
1977  if( selectAll )
1978  {
1979  return true;
1980  }
1981  else if( current )
1982  {
1983  aCollector->Empty();
1984  aCollector->Append( current );
1985  return true;
1986  }
1987 
1988  return false;
1989 }
1990 
1991 
1992 bool PCB_SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibilityOnly ) const
1993 {
1994  const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
1995 
1996  auto visibleLayers =
1997  [&]()
1998  {
1999  if( m_isFootprintEditor )
2000  {
2001  LSET set;
2002 
2003  for( PCB_LAYER_ID layer : LSET::AllLayersMask().Seq() )
2004  set.set( layer, view()->IsLayerVisible( layer ) );
2005 
2006  return set;
2007  }
2008  else
2009  {
2010  return board()->GetVisibleLayers();
2011  }
2012  };
2013 
2014  if( settings->GetHighContrast() )
2015  {
2016  std::set<unsigned int> activeLayers = settings->GetHighContrastLayers();
2017  bool onActiveLayer = false;
2018 
2019  for( unsigned int layer : activeLayers )
2020  {
2021  // NOTE: Only checking the regular layers (not GAL meta-layers)
2022  if( layer < PCB_LAYER_ID_COUNT && aItem->IsOnLayer( ToLAYER_ID( layer ) ) )
2023  {
2024  onActiveLayer = true;
2025  break;
2026  }
2027  }
2028 
2029  if( !onActiveLayer ) // We do not want to select items that are in the background
2030  return false;
2031  }
2032 
2033  if( aItem->Type() == PCB_FOOTPRINT_T )
2034  {
2035  // In footprint editor, we do not want to select the footprint itself.
2036  if( m_isFootprintEditor )
2037  return false;
2038 
2039  // Allow selection of footprints if some part of the footprint is visible.
2040  const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
2041 
2042  // If the footprint has no items except the reference and value fields, include the
2043  // footprint in the selections.
2044  if( footprint->GraphicalItems().empty()
2045  && footprint->Pads().empty()
2046  && footprint->Zones().empty() )
2047  return true;
2048 
2049  for( const BOARD_ITEM* item : footprint->GraphicalItems() )
2050  {
2051  if( Selectable( item, true ) )
2052  return true;
2053  }
2054 
2055  for( const PAD* pad : footprint->Pads() )
2056  {
2057  if( Selectable( pad, true ) )
2058  return true;
2059  }
2060 
2061  for( const ZONE* zone : footprint->Zones() )
2062  {
2063  if( Selectable( zone, true ) )
2064  return true;
2065  }
2066 
2067  return false;
2068  }
2069  else if( aItem->Type() == PCB_GROUP_T )
2070  {
2071  PCB_GROUP* group = const_cast<PCB_GROUP*>( static_cast<const PCB_GROUP*>( aItem ) );
2072 
2073  // Similar to logic for footprint, a group is selectable if any of its members are.
2074  // (This recurses.)
2075  for( BOARD_ITEM* item : group->GetItems() )
2076  {
2077  if( Selectable( item, true ) )
2078  return true;
2079  }
2080 
2081  return false;
2082  }
2083 
2084  const ZONE* zone = nullptr;
2085  const PCB_VIA* via = nullptr;
2086  const PAD* pad = nullptr;
2087  const FP_TEXT* text = nullptr;
2088 
2089  switch( aItem->Type() )
2090  {
2091  case PCB_ZONE_T:
2092  case PCB_FP_ZONE_T:
2093  if( !board()->IsElementVisible( LAYER_ZONES ) )
2094  return false;
2095 
2096  zone = static_cast<const ZONE*>( aItem );
2097 
2098  // A footprint zone is only selectable within the footprint editor
2099  if( zone->GetParent()
2100  && zone->GetParent()->Type() == PCB_FOOTPRINT_T
2102  && !checkVisibilityOnly )
2103  {
2104  return false;
2105  }
2106 
2107  // zones can exist on multiple layers!
2108  if( !( zone->GetLayerSet() & visibleLayers() ).any() )
2109  return false;
2110 
2111  break;
2112 
2113  case PCB_TRACE_T:
2114  case PCB_ARC_T:
2115  if( !board()->IsElementVisible( LAYER_TRACKS ) )
2116  return false;
2117 
2118  if( m_isFootprintEditor )
2119  {
2120  if( !view()->IsLayerVisible( aItem->GetLayer() ) )
2121  return false;
2122  }
2123  else
2124  {
2125  if( !board()->IsLayerVisible( aItem->GetLayer() ) )
2126  return false;
2127  }
2128 
2129  break;
2130 
2131  case PCB_VIA_T:
2132  if( !board()->IsElementVisible( LAYER_VIAS ) )
2133  return false;
2134 
2135  via = static_cast<const PCB_VIA*>( aItem );
2136 
2137  // For vias it is enough if only one of its layers is visible
2138  if( !( visibleLayers() & via->GetLayerSet() ).any() )
2139  return false;
2140 
2141  break;
2142 
2143  case PCB_FP_TEXT_T:
2144  if( m_isFootprintEditor )
2145  {
2146  text = static_cast<const FP_TEXT*>( aItem );
2147 
2148  if( !text->IsVisible() && !view()->IsLayerVisible( LAYER_MOD_TEXT_INVISIBLE ) )
2149  return false;
2150 
2151  if( !view()->IsLayerVisible( aItem->GetLayer() ) )
2152  return false;
2153  }
2154  else
2155  {
2156  if( !view()->IsVisible( aItem ) )
2157  return false;
2158 
2159  if( !board()->IsLayerVisible( aItem->GetLayer() ) )
2160  return false;
2161  }
2162 
2163  break;
2164 
2165  case PCB_FP_SHAPE_T:
2166  if( m_isFootprintEditor )
2167  {
2168  if( !view()->IsLayerVisible( aItem->GetLayer() ) )
2169  return false;
2170  }
2171  else
2172  {
2173  // Footprint shape selections are only allowed in footprint editor mode.
2174  if( !checkVisibilityOnly )
2175  return false;
2176 
2177  if( !board()->IsLayerVisible( aItem->GetLayer() ) )
2178  return false;
2179  }
2180 
2181  break;
2182 
2183  case PCB_PAD_T:
2184  // Multiple selection is only allowed in footprint editor mode. In pcbnew, you have to
2185  // select footprint subparts one by one, rather than with a drag selection. This is so
2186  // you can pick up items under an (unlocked) footprint without also moving the
2187  // footprint's sub-parts.
2188  if( !m_isFootprintEditor && !checkVisibilityOnly )
2189  {
2190  if( m_multiple )
2191  return false;
2192  }
2193 
2194  pad = static_cast<const PAD*>( aItem );
2195 
2196  if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
2197  {
2198  // Check render mode (from the Items tab) first
2199  if( !board()->IsElementVisible( LAYER_PADS_TH ) )
2200  return false;
2201 
2202  // A pad's hole is visible on every layer the pad is visible on plus many layers the
2203  // pad is not visible on -- so we only need to check for any visible hole layers.
2204  if( !( visibleLayers() & LSET::PhysicalLayersMask() ).any() )
2205  return false;
2206  }
2207  else
2208  {
2209  // Check render mode (from the Items tab) first
2210  if( pad->IsOnLayer( F_Cu ) && !board()->IsElementVisible( LAYER_PAD_FR ) )
2211  return false;
2212  else if( pad->IsOnLayer( B_Cu ) && !board()->IsElementVisible( LAYER_PAD_BK ) )
2213  return false;
2214 
2215  if( !( pad->GetLayerSet() & visibleLayers() ).any() )
2216  return false;
2217  }
2218 
2219  break;
2220 
2221  // These are not selectable
2222  case PCB_NETINFO_T:
2223  case NOT_USED:
2224  case TYPE_NOT_INIT:
2225  return false;
2226 
2227  default: // Suppress warnings
2228  break;
2229  }
2230 
2231  return aItem->ViewGetLOD( aItem->GetLayer(), view() ) < view()->GetScale();
2232 }
2233 
2234 
2236 {
2237  if( aItem->IsSelected() )
2238  return;
2239 
2240  if( aItem->Type() == PCB_PAD_T )
2241  {
2242  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem->GetParent() );
2243 
2244  if( m_selection.Contains( footprint ) )
2245  return;
2246  }
2247 
2248  highlight( aItem, SELECTED, &m_selection );
2249 }
2250 
2251 
2253 {
2254  unhighlight( aItem, SELECTED, &m_selection );
2255 }
2256 
2257 
2258 void PCB_SELECTION_TOOL::highlight( BOARD_ITEM* aItem, int aMode, PCB_SELECTION* aGroup )
2259 {
2260  if( aGroup )
2261  aGroup->Add( aItem );
2262 
2263  highlightInternal( aItem, aMode, aGroup != nullptr );
2264  view()->Update( aItem, KIGFX::REPAINT );
2265 
2266  // Many selections are very temporal and updating the display each time just
2267  // creates noise.
2268  if( aMode == BRIGHTENED )
2270 }
2271 
2272 
2273 void PCB_SELECTION_TOOL::highlightInternal( BOARD_ITEM* aItem, int aMode, bool aUsingOverlay )
2274 {
2275  if( aMode == SELECTED )
2276  aItem->SetSelected();
2277  else if( aMode == BRIGHTENED )
2278  aItem->SetBrightened();
2279 
2280  if( aUsingOverlay )
2281  view()->Hide( aItem, true ); // Hide the original item, so it is shown only on overlay
2282 
2283  if( aItem->Type() == PCB_FOOTPRINT_T )
2284  {
2285  static_cast<FOOTPRINT*>( aItem )->RunOnChildren(
2286  [&]( BOARD_ITEM* aChild )
2287  {
2288  highlightInternal( aChild, aMode, aUsingOverlay );
2289  } );
2290  }
2291  else if( aItem->Type() == PCB_GROUP_T )
2292  {
2293  static_cast<PCB_GROUP*>( aItem )->RunOnChildren(
2294  [&]( BOARD_ITEM* aChild )
2295  {
2296  highlightInternal( aChild, aMode, aUsingOverlay );
2297  } );
2298  }
2299 }
2300 
2301 
2303 {
2304  if( aGroup )
2305  aGroup->Remove( aItem );
2306 
2307  unhighlightInternal( aItem, aMode, aGroup != nullptr );
2308  view()->Update( aItem, KIGFX::REPAINT );
2309 
2310  // Many selections are very temporal and updating the display each time just creates noise.
2311  if( aMode == BRIGHTENED )
2313 }
2314 
2315 
2316 void PCB_SELECTION_TOOL::unhighlightInternal( BOARD_ITEM* aItem, int aMode, bool aUsingOverlay )
2317 {
2318  if( aMode == SELECTED )
2319  aItem->ClearSelected();
2320  else if( aMode == BRIGHTENED )
2321  aItem->ClearBrightened();
2322 
2323  if( aUsingOverlay )
2324  view()->Hide( aItem, false ); // // Restore original item visibility
2325 
2326  if( aItem->Type() == PCB_FOOTPRINT_T )
2327  {
2328  static_cast<FOOTPRINT*>( aItem )->RunOnChildren(
2329  [&]( BOARD_ITEM* aChild )
2330  {
2331  unhighlightInternal( aChild, aMode, aUsingOverlay );
2332  } );
2333  }
2334  else if( aItem->Type() == PCB_GROUP_T )
2335  {
2336  static_cast<PCB_GROUP*>( aItem )->RunOnChildren(
2337  [&]( BOARD_ITEM* aChild )
2338  {
2339  unhighlightInternal( aChild, aMode, aUsingOverlay );
2340  } );
2341  }
2342 }
2343 
2344 
2346 {
2348  GENERAL_COLLECTOR collector;
2349 
2350  // Since we're just double-checking, we want a considerably sloppier check than the initial
2351  // selection (for which most tools use 5 pixels). So we increase this to an effective 20
2352  // pixels by artificially inflating the value of a pixel by 4X.
2353  guide.SetOnePixelInIU( guide.OnePixelInIU() * 4 );
2354 
2357  (wxPoint) aPoint, guide );
2358 
2359  for( int i = collector.GetCount() - 1; i >= 0; --i )
2360  {
2361  BOARD_ITEM* item = collector[i];
2362 
2363  if( item->IsSelected() && item->HitTest( (wxPoint) aPoint, 5 * guide.OnePixelInIU() ) )
2364  return true;
2365  }
2366 
2367  return false;
2368 }
2369 
2370 
2371 int PCB_SELECTION_TOOL::hitTestDistance( const wxPoint& aWhere, BOARD_ITEM* aItem,
2372  int aMaxDistance ) const
2373 {
2374  BOX2D viewportD = getView()->GetViewport();
2375  BOX2I viewport( VECTOR2I( viewportD.GetPosition() ), VECTOR2I( viewportD.GetSize() ) );
2376  int distance = INT_MAX;
2377  SEG loc( aWhere, aWhere );
2378 
2379  switch( aItem->Type() )
2380  {
2381  case PCB_TEXT_T:
2382  {
2383  PCB_TEXT* text = static_cast<PCB_TEXT*>( aItem );
2384  text->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance );
2385  break;
2386  }
2387 
2388  case PCB_FP_TEXT_T:
2389  {
2390  FP_TEXT* text = static_cast<FP_TEXT*>( aItem );
2391  text->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance );
2392  break;
2393  }
2394 
2395  case PCB_ZONE_T:
2396  {
2397  ZONE* zone = static_cast<ZONE*>( aItem );
2398 
2399  // Zone borders are very specific
2400  if( zone->HitTestForEdge( aWhere, aMaxDistance / 2 ) )
2401  distance = 0;
2402  else if( zone->HitTestForEdge( aWhere, aMaxDistance ) )
2403  distance = aMaxDistance / 2;
2404  else
2405  aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
2406 
2407  break;
2408  }
2409 
2410  case PCB_FOOTPRINT_T:
2411  {
2412  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
2413  EDA_RECT bbox = footprint->GetBoundingBox( false, false );
2414 
2415  try
2416  {
2417  footprint->GetBoundingHull().Collide( loc, aMaxDistance, &distance );
2418  }
2419  catch( const ClipperLib::clipperException& exc )
2420  {
2421  // This may be overkill and could be an assertion but we are more likely to find
2422  // any clipper errors this way.
2423  wxLogError( wxT( "Clipper library exception '%s' occurred." ), exc.what() );
2424  }
2425 
2426  // Consider footprints larger than the viewport only as a last resort
2427  if( bbox.GetHeight() > viewport.GetHeight() || bbox.GetWidth() > viewport.GetWidth() )
2428  distance = INT_MAX / 2;
2429 
2430  break;
2431  }
2432 
2433  case PCB_MARKER_T:
2434  {
2435  PCB_MARKER* marker = static_cast<PCB_MARKER*>( aItem );
2436  SHAPE_LINE_CHAIN polygon;
2437 
2438  marker->ShapeToPolygon( polygon );
2439  polygon.Move( marker->GetPos() );
2440  polygon.Collide( loc, aMaxDistance, &distance );
2441  break;
2442  }
2443 
2444  case PCB_GROUP_T:
2445  {
2446  PCB_GROUP* group = static_cast<PCB_GROUP*>( aItem );
2447 
2448  for( BOARD_ITEM* member : group->GetItems() )
2449  distance = std::min( distance, hitTestDistance( aWhere, member, aMaxDistance ) );
2450 
2451  break;
2452  }
2453 
2454  default:
2455  aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
2456  break;
2457  }
2458 
2459  return distance;
2460 }
2461 
2462 
2463 // The general idea here is that if the user clicks directly on a small item inside a larger
2464 // one, then they want the small item. The quintessential case of this is clicking on a pad
2465 // within a footprint, but we also apply it for text within a footprint, footprints within
2466 // larger footprints, and vias within either larger pads or longer tracks.
2467 //
2468 // These "guesses" presume there is area within the larger item to click in to select it. If
2469 // an item is mostly covered by smaller items within it, then the guesses are inappropriate as
2470 // there might not be any area left to click to select the larger item. In this case we must
2471 // leave the items in the collector and bring up a Selection Clarification menu.
2472 //
2473 // We currently check for pads and text mostly covering a footprint, but we don't check for
2474 // smaller footprints mostly covering a larger footprint.
2475 //
2477  const VECTOR2I& aWhere ) const
2478 {
2479  std::set<BOARD_ITEM*> preferred;
2480  std::set<BOARD_ITEM*> rejected;
2481  wxPoint where( aWhere.x, aWhere.y );
2482 
2483  PCB_LAYER_ID activeLayer = m_frame->GetActiveLayer();
2484  LSET silkLayers( 2, B_SilkS, F_SilkS );
2485 
2486  if( silkLayers[activeLayer] )
2487  {
2488  for( int i = 0; i < aCollector.GetCount(); ++i )
2489  {
2490  BOARD_ITEM* item = aCollector[i];
2491  KICAD_T type = item->Type();
2492 
2493  if( ( type == PCB_FP_TEXT_T || type == PCB_TEXT_T || type == PCB_SHAPE_T )
2494  && silkLayers[item->GetLayer()] )
2495  {
2496  preferred.insert( item );
2497  }
2498  }
2499 
2500  if( preferred.size() > 0 )
2501  {
2502  aCollector.Empty();
2503 
2504  for( BOARD_ITEM* item : preferred )
2505  aCollector.Append( item );
2506 
2507  return;
2508  }
2509  }
2510 
2511  // Prefer exact hits to sloppy ones
2512  constexpr int MAX_SLOP = 5;
2513 
2514  int pixel = (int) aCollector.GetGuide()->OnePixelInIU();
2515  int minSlop = INT_MAX;
2516 
2517  std::map<BOARD_ITEM*, int> itemsBySloppiness;
2518 
2519  for( int i = 0; i < aCollector.GetCount(); ++i )
2520  {
2521  BOARD_ITEM* item = aCollector[i];
2522  int itemSlop = hitTestDistance( where, item, MAX_SLOP * pixel );
2523 
2524  itemsBySloppiness[ item ] = itemSlop;
2525 
2526  if( itemSlop < minSlop )
2527  minSlop = itemSlop;
2528  }
2529 
2530  // Prune sloppier items
2531  if( minSlop < INT_MAX )
2532  {
2533  for( std::pair<BOARD_ITEM*, int> pair : itemsBySloppiness )
2534  {
2535  if( pair.second > minSlop + pixel )
2536  aCollector.Transfer( pair.first );
2537  }
2538  }
2539 
2540  // If the user clicked on a small item within a much larger one then it's pretty clear
2541  // they're trying to select the smaller one.
2542  constexpr double sizeRatio = 1.5;
2543 
2544  std::vector<std::pair<BOARD_ITEM*, double>> itemsByArea;
2545 
2546  for( int i = 0; i < aCollector.GetCount(); ++i )
2547  {
2548  BOARD_ITEM* item = aCollector[i];
2549  double area;
2550 
2551  if( ( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2552  && static_cast<ZONE*>( item )->HitTestForEdge( where, MAX_SLOP * pixel / 2 ) )
2553  {
2554  // Zone borders are very specific, so make them "small"
2555  area = MAX_SLOP * SEG::Square( pixel );
2556  }
2557  else if( item->Type() == PCB_VIA_T )
2558  {
2559  // Vias rarely hide other things, and we don't want them deferring to short track
2560  // segments underneath them -- so artificially reduce their size from πr² to 1.5r².
2561  area = SEG::Square( static_cast<PCB_VIA*>( item )->GetDrill() / 2 ) * 1.5;
2562  }
2563  else
2564  {
2565  try
2566  {
2567  area = FOOTPRINT::GetCoverageArea( item, aCollector );
2568  }
2569  catch( const ClipperLib::clipperException& e )
2570  {
2571  wxLogError( wxT( "A clipper exception %s was detected." ), e.what() );
2572  }
2573  }
2574 
2575  itemsByArea.emplace_back( item, area );
2576  }
2577 
2578  std::sort( itemsByArea.begin(), itemsByArea.end(),
2579  []( const std::pair<BOARD_ITEM*, double>& lhs,
2580  const std::pair<BOARD_ITEM*, double>& rhs ) -> bool
2581  {
2582  return lhs.second < rhs.second;
2583  } );
2584 
2585  bool rejecting = false;
2586 
2587  for( int i = 1; i < (int) itemsByArea.size(); ++i )
2588  {
2589  if( itemsByArea[i].second > itemsByArea[i-1].second * sizeRatio )
2590  rejecting = true;
2591 
2592  if( rejecting )
2593  rejected.insert( itemsByArea[i].first );
2594  }
2595 
2596  // Special case: if a footprint is completely covered with other features then there's no
2597  // way to select it -- so we need to leave it in the list for user disambiguation.
2598  constexpr double maxCoverRatio = 0.70;
2599 
2600  for( int i = 0; i < aCollector.GetCount(); ++i )
2601  {
2602  if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aCollector[i] ) )
2603  {
2604  if( footprint->CoverageRatio( aCollector ) > maxCoverRatio )
2605  rejected.erase( footprint );
2606  }
2607  }
2608 
2609  // Hopefully we've now got what the user wanted.
2610  if( (unsigned) aCollector.GetCount() > rejected.size() ) // do not remove everything
2611  {
2612  for( BOARD_ITEM* item : rejected )
2613  aCollector.Transfer( item );
2614  }
2615 
2616  // Finally, what we are left with is a set of items of similar coverage area. We now reject
2617  // any that are not on the active layer, to reduce the number of disambiguation menus shown.
2618  // If the user wants to force-disambiguate, they can either switch layers or use the modifier
2619  // key to force the menu.
2620  if( aCollector.GetCount() > 1 )
2621  {
2622  bool haveItemOnActive = false;
2623  rejected.clear();
2624 
2625  for( int i = 0; i < aCollector.GetCount(); ++i )
2626  {
2627  if( !aCollector[i]->IsOnLayer( activeLayer ) )
2628  rejected.insert( aCollector[i] );
2629  else
2630  haveItemOnActive = true;
2631  }
2632 
2633  if( haveItemOnActive )
2634  for( BOARD_ITEM* item : rejected )
2635  aCollector.Transfer( item );
2636  }
2637 }
2638 
2639 
2641  bool aMultiselect ) const
2642 {
2643  std::unordered_set<BOARD_ITEM*> toAdd;
2644 
2645  // Set TEMP_SELECTED on all parents which are included in the GENERAL_COLLECTOR. This
2646  // algorithm is O3n, whereas checking for the parent inclusion could potentially be On^2.
2647  for( int j = 0; j < aCollector.GetCount(); j++ )
2648  {
2649  if( aCollector[j]->GetParent() )
2650  aCollector[j]->GetParent()->ClearFlags( TEMP_SELECTED );
2651  }
2652 
2653  if( aMultiselect )
2654  {
2655  for( int j = 0; j < aCollector.GetCount(); j++ )
2656  aCollector[j]->SetFlags( TEMP_SELECTED );
2657  }
2658 
2659  for( int j = 0; j < aCollector.GetCount(); )
2660  {
2661  BOARD_ITEM* item = aCollector[j];
2662  BOARD_ITEM* parent = item->GetParent();
2663  BOARD_ITEM* start = item;
2664 
2665  if( !m_isFootprintEditor && parent && parent->Type() == PCB_FOOTPRINT_T )
2666  start = parent;
2667 
2668  // If any element is a member of a group, replace those elements with the top containing
2669  // group.
2671 
2672  if( aTop )
2673  {
2674  if( aTop != item )
2675  {
2676  toAdd.insert( aTop );
2677  aTop->SetFlags( TEMP_SELECTED );
2678 
2679  aCollector.Remove( item );
2680  continue;
2681  }
2682  }
2683  else if( m_enteredGroup
2685  {
2686  // If a group is entered, disallow selections of objects outside the group.
2687  aCollector.Remove( item );
2688  continue;
2689  }
2690 
2691  // Footprints are a bit easier as they can't be nested.
2692  if( parent && ( parent->GetFlags() & TEMP_SELECTED ) )
2693  {
2694  // Remove children of selected items
2695  aCollector.Remove( item );
2696  continue;
2697  }
2698 
2699  ++j;
2700  }
2701 
2702  for( BOARD_ITEM* item : toAdd )
2703  {
2704  if( !aCollector.HasItem( item ) )
2705  aCollector.Append( item );
2706  }
2707 }
2708 
2709 
2711 {
2712  std::set<BOARD_ITEM*> to_add;
2713 
2714  // Iterate from the back so we don't have to worry about removals.
2715  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
2716  {
2717  BOARD_ITEM* item = aCollector[i];
2718 
2719  if( !IsFootprintEditor() && item->Type() == PCB_PAD_T
2720  && !frame()->Settings().m_AllowFreePads )
2721  {
2722  if( !aCollector.HasItem( item->GetParent() ) )
2723  to_add.insert( item->GetParent() );
2724 
2725  aCollector.Remove( item );
2726  }
2727  }
2728 
2729  for( BOARD_ITEM* item : to_add )
2730  aCollector.Append( item );
2731 }
2732 
2733 
2735 {
2736  // Iterate from the back so we don't have to worry about removals.
2737  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
2738  {
2739  BOARD_ITEM* item = aCollector[i];
2740 
2741  if( item->Type() == PCB_MARKER_T )
2742  aCollector.Remove( item );
2743  }
2744 }
2745 
2746 
2748 {
2749  getView()->Update( &m_selection );
2751 
2752  return 0;
2753 }
2754 
2755 
2757 {
2758  ACTION_MENU* actionMenu = aEvent.Parameter<ACTION_MENU*>();
2759  CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( actionMenu );
2760 
2761  if( conditionalMenu )
2762  conditionalMenu->Evaluate( m_selection );
2763 
2764  if( actionMenu )
2765  actionMenu->UpdateAll();
2766 
2767  return 0;
2768 }
2769 
2770 
2772 {
2774 
2778 
2784 
2794 
2796 
2798 }
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition: pcb_actions.h:66
void Empty()
Clear the list.
Definition: collector.h:90
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:59
void SetEnd(const VECTOR2I &aEnd)
Set the current end of the rectangle (the corner that moves with the cursor.
const GENERAL_COLLECTORS_GUIDE getCollectorsGuide() const
static TOOL_ACTION selectionActivate
Activation of the selection tool.
Definition: pcb_actions.h:53
void Hide(VIEW_ITEM *aItem, bool aHide=true)
Temporarily hide the item in the view (e.g.
Definition: view.cpp:1537
void ClearReferencePoint()
Definition: selection.h:197
int selectSameSheet(const TOOL_EVENT &aEvent)
Invoke filter dialog and modify current selection.
static double GetCoverageArea(const BOARD_ITEM *aItem, const GENERAL_COLLECTOR &aCollector)
Return the initial comments block or NULL if none, without transfer of ownership.
Definition: footprint.cpp:1870
const std::set< unsigned int > GetHighContrastLayers() const
Returns the set of currently high-contrast layers.
const wxPoint & GetPos() const
Definition: marker_base.h:85
bool IsLocked() const override
Definition: footprint.h:297
void AddStandardSubMenus(TOOL_MENU &aMenu)
Construct a "basic" menu for a tool, containing only items that apply to all tools (e....
void SetIgnoreTracks(bool ignore)
Definition: collectors.h:565
bool AddItem(BOARD_ITEM *aItem)
Add item to group.
Definition: pcb_group.cpp:39
currently selected items overlay
Definition: layer_ids.h:226
smd pads, front layer
Definition: layer_ids.h:209
static const KICAD_T FootprintItems[]
A scan list for primary footprint items.
Definition: collectors.h:293
int CursorSelection(const TOOL_EVENT &aEvent)
Clear current selection event handler.
static const TOOL_EVENT SelectedEvent
Definition: actions.h:200
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition: view.cpp:512
int disambiguateCursor(const TOOL_EVENT &aEvent)
Handle disambiguation actions including displaying the menu.
TOOL_MENU m_menu
The functions below are not yet implemented - their interface may change.
static TOOL_ACTION move
move or drag an item
Definition: pcb_actions.h:92
SHAPE_POLY_SET GetBoundingHull() const
Return a bounding polygon for the shapes and pads in the footprint.
Definition: footprint.cpp:847
void SetOnePixelInIU(double aValue)
Definition: collectors.h:571
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:100
virtual void Clear() override
Remove all the stored items from the group.
Definition: selection.h:83
void ForceRefresh()
Force a redraw.
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:101
void select(BOARD_ITEM *aItem)
Take necessary action mark an item as selected.
bool otherItems
Anything not fitting one of the above categories.
static TOOL_ACTION groupLeave
Definition: pcb_actions.h:432
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
class FP_TEXT, text in a footprint
Definition: typeinfo.h:92
static const KICAD_T AllBoardItems[]
A scan list for all editable board items.
Definition: collectors.h:267
void SetIgnoreBlindBuriedVias(bool ignore)
Definition: collectors.h:559
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.
static TOOL_ACTION zoomInCenter
Definition: actions.h:93
BOARD * board() const
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
bool IsSelected() const
Definition: eda_item.h:122
const KIID_PATH & GetPath() const
Definition: footprint.h:207
const wxPoint & GetEnd() const
Definition: pcb_track.h:105
Model changes (required full reload)
Definition: tool_base.h:80
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:201
#define IS_NEW
New item, just created.
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:735
Defines the structure of a menu based on ACTIONs.
Definition: action_menu.h:48
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:49
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:449
virtual void Add(EDA_ITEM *aItem)
Definition: selection.cpp:32
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:108
static const TOOL_EVENT DisambiguatePoint
Definition: actions.h:215
Meta control for all pads opacity/visibility (color ignored)
Definition: layer_ids.h:237
void ClearSelected()
Definition: eda_item.h:131
int SelectionMenu(const TOOL_EVENT &aEvent)
Show a popup menu to trim the COLLECTOR passed as aEvent's parameter down to a single item.
void FilterCollectorForFreePads(GENERAL_COLLECTOR &aCollector) const
Check the "allow free pads" setting and if disabled, upgrade any pad selection to the selection of it...
show footprints values (when texts are visible)
Definition: layer_ids.h:217
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:50
void Collect(BOARD_ITEM *aItem, const KICAD_T aScanList[], const wxPoint &aRefPos, const COLLECTORS_GUIDE &aGuide)
Scan a BOARD_ITEM using this class's Inspector method, which does the collection.
Definition: collectors.cpp:571
virtual double OnePixelInIU() const =0
VECTOR2D GetMousePosition() const
void SetIgnoreModulesVals(bool ignore)
Definition: collectors.h:547
void FilterCollectorForMarkers(GENERAL_COLLECTOR &aCollector) const
Apply the SELECTION_FILTER_OPTIONS to a collection of items.
void SetIgnoreMicroVias(bool ignore)
Definition: collectors.h:562
Stop at any place where more than two traces meet.
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:102
void SetIgnoreModulesOnBack(bool ignore)
Definition: collectors.h:517
static TOOL_ACTION unselectItem
Definition: pcb_actions.h:63
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:46
the 3d code uses this value
Definition: typeinfo.h:79
static TOOL_ACTION selectNet
Select all connections belonging to a single net.
Definition: pcb_actions.h:77
static TOOL_ACTION dragFreeAngle
Definition: pcb_actions.h:135
smd pads, back layer
Definition: layer_ids.h:210
void highlightInternal(BOARD_ITEM *aItem, int aHighlightMode, bool aUsingOverlay)
void Move(const VECTOR2I &aVector) override
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:214
static TOOL_ACTION unselectItems
Definition: pcb_actions.h:67
static TOOL_ACTION cancelInteractive
Definition: actions.h:62
DIALOG_FILTER_SELECTION::OPTIONS m_filterOpts
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:350
static TOOL_ACTION properties
Activation of the edit tool.
Definition: pcb_actions.h:117
Stop when reaching a pad.
bool m_highlight_modifier
void FilterCollectedItems(GENERAL_COLLECTOR &aCollector, bool aMultiSelect)
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.cpp:289
int GetWidth() const
Definition: eda_rect.h:118
static TOOL_ACTION cursorRight
Definition: actions.h:116
void SetBrightened()
Definition: eda_item.h:129
static TOOL_ACTION zoomFitScreen
Definition: actions.h:96
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:152
class PCB_TEXT, text on a layer
Definition: typeinfo.h:91
void SetIgnoreModulesRefs(bool ignore)
Definition: collectors.h:553
LSET GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:481
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:143
double CoverageRatio(const GENERAL_COLLECTOR &aCollector) const
Calculate the ratio of total area of the footprint pads and graphical items to the area of the footpr...
Definition: footprint.cpp:1950
static TOOL_ACTION selectionMenu
Run a selection menu to select from a list of items.
Definition: pcb_actions.h:70
static TOOL_ACTION selectConnection
Select tracks between junctions or expands an existing selection to pads or the entire connection.
Definition: pcb_actions.h:74
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:97
static TOOL_ACTION cursorRightFast
Definition: actions.h:121
void SetExclusiveOr(bool aExclusiveOr)
Definition: bitmap.cpp:64
static PCB_GROUP * TopLevelGroup(BOARD_ITEM *aItem, PCB_GROUP *aScope, bool isFootprintEditor)
Definition: pcb_group.cpp:100
static TOOL_ACTION drag45Degree
Definition: pcb_actions.h:134
void highlight(BOARD_ITEM *aItem, int aHighlightMode, PCB_SELECTION *aGroup=nullptr)
Highlight the item visually.
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:93
class PAD, a pad in a footprint
Definition: typeinfo.h:89
double OnePixelInIU() const override
Definition: collectors.h:570
show footprints on back
Definition: layer_ids.h:216
virtual void Clear()
Remove all the stored items from the group.
Definition: view_group.cpp:69
Private implementation of firewalled private data.
void UpdateAll()
Run update handlers for the menu and its submenus.
KICURSOR
Definition: cursors.h:33
void setTransitions() override
Zoom the screen to center and fit the current selection.
int UnselectItem(const TOOL_EVENT &aEvent)
void(* CLIENT_SELECTION_FILTER)(const VECTOR2I &, GENERAL_COLLECTOR &, PCB_SELECTION_TOOL *)
static TOOL_ACTION zoomFitObjects
Definition: actions.h:97
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
Struct that will be set with the result of the user choices in the dialog.
const EDA_RECT GetBoundingBox() const override
May be re-implemented for each derived class in order to handle all the types given by its member dat...
Definition: pcb_group.cpp:223
virtual wxPoint GetPosition() const
Definition: eda_item.h:251
void FindItem(BOARD_ITEM *aItem)
Handle finding an item.
void selectConnectedTracks(BOARD_CONNECTED_ITEM &aSourceItem, STOP_CONDITION aStopCondition)
Select connected tracks and vias.
bool doSelectionMenu(GENERAL_COLLECTOR *aItems)
Allow the selection of a single item from a list via pop-up menu.
static TOOL_ACTION panLeft
Definition: actions.h:129
void SetContextMenu(ACTION_MENU *aMenu, CONTEXT_MENU_TRIGGER aTrigger=CMENU_BUTTON)
Assign a context menu and tells when it should be activated.
VECTOR2< int > VECTOR2I
Definition: vector2d.h:622
virtual bool IsLocked() const
Definition: board_item.cpp:64
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
static SEG::ecoord Square(int a)
Definition: seg.h:122
const PCB_DISPLAY_OPTIONS & GetDisplayOptions() const
Display options control the way tracks, vias, outlines and other things are shown (for instance solid...
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
void OnIdle(wxIdleEvent &aEvent)
int UnselectItems(const TOOL_EVENT &aEvent)
static bool NotEmpty(const SELECTION &aSelection)
Test if there are any items selected.
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).
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:181
bool Contains(const wxPoint &aPoint) const
Definition: eda_rect.cpp:57
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition: collector.h:110
virtual void Remove(VIEW_ITEM *aItem) override
Remove a VIEW_ITEM from the view.
Definition: pcb_view.cpp:75
search types array terminator (End Of Types)
Definition: typeinfo.h:81
TRACK_DRAG_ACTION
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:77
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:205
bool HitTestForCorner(const wxPoint &refPos, int aAccuracy, SHAPE_POLY_SET::VERTEX_INDEX &aCornerHit) const
Test if the given wxPoint is near a corner.
Definition: zone.cpp:423
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition: view.h:208
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.
static TOOL_ACTION zoomOutCenter
Definition: actions.h:94
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
bool HasPoint()
Indicate the cursor is over an edit point.
PADS & Pads()
Definition: footprint.h:169
Control for copper zone opacity/visibility (color ignored)
Definition: layer_ids.h:238
void SetIsHover(bool aIsHover)
Definition: selection.h:69
Plated through hole pad.
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 ...
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:88
PCB_BASE_FRAME * m_frame
void ClearBrightened()
Definition: eda_item.h:132
void Append(EDA_ITEM *item)
Add an item to the end of the list.
Definition: collector.h:100
void onDisambiguationExpire(wxTimerEvent &aEvent)
Start the process to show our disambiguation menu once the user has kept the mouse down for the minim...
void RebuildSelection()
Rebuild the selection from the EDA_ITEMs' selection flags.
void SetAdditive(bool aAdditive)
PCB_BASE_EDIT_FRAME * frame() const
virtual bool HitTest(const wxPoint &aPosition, int aAccuracy=0) const
Test if aPosition is inside or on the boundary of this item.
Definition: eda_item.h:224
virtual PCB_LAYER_ID GetActiveLayer() const
SEARCH_RESULT Visit(INSPECTOR inspector, void *testData, const 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:1184
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:82
bool Init() override
Init() is called once upon a registration of the tool.
PCB_SELECTION & GetSelection()
Return the set of currently selected items.
FP_ZONES & Zones()
Definition: footprint.h:175
void UnbrightenItem(BOARD_ITEM *aItem)
int updateSelection(const TOOL_EVENT &aEvent)
Event handler to update the selection VIEW_ITEM.
void SetOrigin(const VECTOR2I &aOrigin)
void Transfer(int aIndex)
Move the item at aIndex (first position is 0) to the backup list.
Definition: collector.h:152
MOUSE_DRAG_ACTION
like PAD_PTH, but not plated
Container for display options like enable/disable some optional drawings.
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
const COLLECTORS_GUIDE * GetGuide() const
Definition: collectors.h:339
Item needs to be redrawn.
Definition: view_item.h:52
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:516
MOUSE_DRAG_ACTION GetDragAction() const
Indicates whether a drag should draw a selection rectangle or drag selected (or unselected) objects.
Definition: tools_holder.h:135
void SetCenter(const VECTOR2D &aCenter)
Set the center point of the VIEW (i.e.
Definition: view.cpp:578
bool selectMultiple()
Handle drawing a selection box that allows one to select many items at the same time.
bool text
Text (free or attached to a footprint)
const PCB_SELECTION & selection() const
#define IS_MOVING
Item being moved.
void SetSelected()
Definition: eda_item.h:128
static TOOL_ACTION panDown
Definition: actions.h:128
Meta control for all vias opacity/visibility.
Definition: layer_ids.h:200
int selectNet(const TOOL_EVENT &aEvent)
Select all copper connections belonging to the same net(s) as the items in the selection.
void SetIgnoreMTextsOnBack(bool ignore)
Definition: collectors.h:505
std::function< SEARCH_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:68
TRACK_DRAG_ACTION m_TrackDragAction
KIGFX::VIEW_GROUP m_enteredGroupOverlay
bool dimensions
Dimension items.
void SetIgnorePadsOnFront(bool ignore)
Definition: collectors.h:535
SELECTION_FILTER_OPTIONS m_filter
int expandConnection(const TOOL_EVENT &aEvent)
Expand the current track selection to the next boundary (junctions, pads, or all)
void MarkTargetDirty(int aTarget)
Set or clear target 'dirty' flag.
Definition: view.h:609
bool ProcessEvent(const TOOL_EVENT &aEvent)
Propagate an event to tools that requested events of matching type(s).
bool IsVisible(const VIEW_ITEM *aItem) const
Return information if the item is visible (or not).
Definition: view.cpp:1556
bool graphics
Graphic lines, shapes, polygons.
void SetIgnoreMTextsOnFront(bool ignore)
Definition: collectors.h:511
void AddItemToSel(BOARD_ITEM *aItem, bool aQuietMode=false)
Select all items on the board.
bool selectPoint(const VECTOR2I &aWhere, bool aOnDrag=false, bool *aSelectionCancelledFlag=nullptr, CLIENT_SELECTION_FILTER aClientFilter=nullptr)
Select an item pointed by the parameter aWhere.
T Parameter() const
Return a non-standard parameter assigned to the event.
Definition: tool_event.h:432
coord_type GetWidth() const
Definition: box2.h:180
Generic, UI-independent tool event.
Definition: tool_event.h:152
void connectedItemFilter(const VECTOR2I &, GENERAL_COLLECTOR &aCollector, PCB_SELECTION_TOOL *sTool)
static TOOL_ACTION cursorUpFast
Definition: actions.h:118
static TOOL_ACTION cursorDownFast
Definition: actions.h:119
static TOOL_ACTION cursorLeft
Definition: actions.h:115
void SetMaximum()
Definition: box2.h:57
static TOOL_ACTION panRight
Definition: actions.h:130
text marked as invisible
Definition: layer_ids.h:207
bool Contains(EDA_ITEM *aItem) const
Definition: selection.cpp:62
FOOTPRINT * footprint() const
const BOX2I ViewBBox() const override
Set the origin of the rectangle (the fixed corner)
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:345
const std::deque< EDA_ITEM * > GetItems() const
Definition: selection.h:109
show footprints on front
Definition: layer_ids.h:215
KIGFX::PCB_VIEW * view() const
bool ToolStackIsEmpty()
Definition: tools_holder.h:116
bool HasItem(const EDA_ITEM *aItem) const
Tests if aItem has already been collected.
Definition: collector.h:196
const TOOL_ACTION * allowedActions[]
static const TOOL_EVENT UninhibitSelectionEditing
Used to inform tool that it should display the disambiguation menu.
Definition: actions.h:212
bool Selectable(const BOARD_ITEM *aItem, bool checkVisibilityOnly=false) const
Check conditions for an item to be selected.
EDA_ITEM * GetParent() const
Definition: eda_item.h:114
Items that may change while the view stays the same (noncached)
Definition: definitions.h:50
#define _(s)
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: eda_item.h:153
static const TOOL_EVENT ClearedEvent
Selected item had a property changed (except movement)
Definition: actions.h:202
#define SELECTED
DRAWINGS & GraphicalItems()
Definition: footprint.h:172
void BrightenItem(BOARD_ITEM *aItem)
static LSET AllLayersMask()
Definition: lset.cpp:796
static bool WithinScope(BOARD_ITEM *aItem, PCB_GROUP *aScope, bool isFootprintEditor)
Definition: pcb_group.cpp:108
Handle a list of polygons defining a copper zone.
Definition: zone.h:56
class ZONE, a copper pour area
Definition: typeinfo.h:105
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:208
static TOOL_ACTION cursorLeftFast
Definition: actions.h:120
bool RoutingInProgress()
Returns whether routing is currently active.
PCB_SELECTION m_selection
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
wxTimer m_disambiguateTimer
static TOOL_ACTION updateMenu
Definition: actions.h:167
virtual void Add(VIEW_ITEM *aItem)
Add an item to the group.
Definition: view_group.cpp:57
void SetIgnoreMTextsMarkedNoShow(bool ignore)
Definition: collectors.h:499
void ExitGroup(bool aSelectGroup=false)
Leave the currently entered group.
void unselect(BOARD_ITEM *aItem)
Take necessary action mark an item as unselected.
bool m_isFootprintEditor
virtual void SetLayer(int aLayer)
Set layer used to draw the group.
Definition: view_group.h:98
static TOOL_ACTION hideDynamicRatsnest
Definition: pcb_actions.h:461
bool footprints
Allow selecting entire footprints.
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition: kicad_algo.h:99
int GetHeight() const
Definition: eda_rect.h:119
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
void FocusOnItem(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer=UNDEFINED_LAYER)
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:98
class PCB_TARGET, a target (graphic item)
Definition: typeinfo.h:104
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:154
wxString m_MenuTitle
Definition: collector.h:243
class FOOTPRINT, a footprint
Definition: typeinfo.h:88
bool IsMirroredX() const
Return true if view is flipped across the X axis.
Definition: view.h:238
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.
void SetSubtractive(bool aSubtractive)
const Vec & GetPosition() const
Definition: box2.h:177
bool IsElementVisible(GAL_LAYER_ID aLayer) const
Test whether a given element category is visible.
Definition: board.cpp:533
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition: selection.h:88
virtual void SetScale(double aScale, VECTOR2D aAnchor={ 0, 0 })
Set the scaling factor, zooming around a given anchor point.
Definition: view.cpp:552
int selectSheetContents(const TOOL_EVENT &aEvent)
Select all footprints belonging to same hierarchical sheet as the selected footprint (same sheet path...
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
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...
Definition: seg.h:40
static TOOL_ACTION filterSelection
Filter the items in the current selection (invokes dialog)
Definition: pcb_actions.h:89
void Normalize()
Ensures that the height ant width are positive.
Definition: eda_rect.cpp:35
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,...
std::unique_ptr< PRIV > m_priv
show footprints references (when texts are visible)
Definition: layer_ids.h:218
bool GetHighContrast() const
static LSET PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition: lset.cpp:858
#define ENTERED
indicates a group has been entered
int Main(const TOOL_EVENT &aEvent)
The main loop.
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
int SelectItems(const TOOL_EVENT &aEvent)
Item unselection event handler.
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.
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:473
int SelectItem(const TOOL_EVENT &aEvent)
bool lockedItems
Allow selecting locked items.
bool itemPassesFilter(BOARD_ITEM *aItem, bool aMultiSelect)
bool selectCursor(bool aForceSelect=false, CLIENT_SELECTION_FILTER aClientFilter=nullptr)
Select an item under the cursor unless there is something already selected or aSelectAlways is true.
class PCB_MARKER, a marker used to show something
Definition: typeinfo.h:98
bool HitTestForEdge(const wxPoint &refPos, int aAccuracy, SHAPE_POLY_SET::VERTEX_INDEX &aCornerHit) const
Test if the given wxPoint is near a segment defined by 2 corners.
Definition: zone.cpp:437
const int scale
PCB_SELECTION & RequestSelection(CLIENT_SELECTION_FILTER aClientFilter, bool aConfirmLockedItems=false)
Return the current selection set, filtered according to aFlags and aClientFilter.
Tool that displays edit points allowing to modify items by dragging the points.
bool HasAdditionalItems()
Test if the collector has heuristic backup items.
Definition: collector.h:133
#define SKIP_STRUCT
flag indicating that the structure should be ignored
static TOOL_ACTION highlightNet
Definition: pcb_actions.h:451
multilayer pads, usually with holes
Definition: layer_ids.h:220
bool IsType(FRAME_T aType) const
#define TEMP_SELECTED
flag indicating that the structure has already selected
void SetHighlight(bool aEnabled, int aNetcode=-1, bool aMulti=false)
Turns on/off highlighting.
static TOOL_ACTION zoomCenter
Definition: actions.h:95
virtual KIGFX::VIEW_ITEM * GetItem(unsigned int aIdx) const override
Definition: selection.cpp:53
static TOOL_ACTION selectOnSheetFromEeschema
Select all components on sheet from Eeschema crossprobing.
Definition: pcb_actions.h:83
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:191
static TOOL_ACTION cursorUp
Cursor control with keyboard.
Definition: actions.h:113
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:65
int hitTestDistance(const wxPoint &aWhere, BOARD_ITEM *aItem, int aMaxDistance) const
void SetIgnoreModulesOnFront(bool ignore)
Definition: collectors.h:523
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:240
void SetIgnoreThroughVias(bool ignore)
Definition: collectors.h:556
virtual wxString GetSelectMenuText(EDA_UNITS aUnits) const
Return the text to display to be used in the selection clarification context menu when multiple items...
Definition: eda_item.cpp:109
const EDA_RECT GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: footprint.cpp:736
virtual BITMAPS GetMenuImage() const
Return a pointer to an image to be used in menus.
Definition: eda_item.cpp:274
std::pair< VIEW_ITEM *, int > LAYER_ITEM_PAIR
Definition: view.h:73
Definition: layer_ids.h:71
void AddSubMenu(std::shared_ptr< ACTION_MENU > aSubMenu)
Store a submenu of this menu model.
Definition: tool_menu.cpp:52
bool IsToolActive() const
Definition: tool_base.cpp:31
wxString AsString() const
Definition: kiid.cpp:316
class NETINFO_ITEM, a description of a net
Definition: typeinfo.h:107
Represent a single user action.
Definition: tool_action.h:67
virtual void Remove(EDA_ITEM *aItem)
Definition: selection.cpp:44
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition: pcb_actions.h:62
class ZONE, managed by a footprint
Definition: typeinfo.h:94
Handle the component boundary box.
Definition: eda_rect.h:42
ZONE_DISPLAY_MODE m_ZoneDisplayMode
PCBNEW_SETTINGS & Settings()
int Size() const
Returns the number of selected parts.
Definition: selection.h:104
The selection tool: currently supports:
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:73
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:99
bool IsFootprintEditor() const
static const TOOL_EVENT InhibitSelectionEditing
Definition: actions.h:211
coord_type GetHeight() const
Definition: box2.h:181
void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction) const
Invoke a function on all members of the group.
Definition: pcb_group.cpp:343
int UpdateMenu(const TOOL_EVENT &aEvent)
Pass the selection to a conditional menu for updating.
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:77
static TOOL_ACTION deselectNet
Remove all connections belonging to a single net from the active selection.
Definition: pcb_actions.h:80
boost::optional< T > OPT
Definition: optional.h:7
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition: view.cpp:1516
int SelectAll(const TOOL_EVENT &aEvent)
Multiple item selection event handler.
void Combine()
Re-combine the backup list into the main list of the collector.
Definition: collector.h:141
static TOOL_ACTION panUp
Definition: actions.h:127
ACTION_MENU * create() const override
< Return an instance of this class. It has to be overridden in inheriting classes.
static TOOL_ACTION zoomIn
Definition: actions.h:91
Represent a selection area (currently a rectangle) in a VIEW, drawn corner-to-corner between two poin...
static TOOL_ACTION zoomOut
Definition: actions.h:92
VECTOR2I m_originalCursor
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:103
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1) override
Add a VIEW_ITEM to the view.
Definition: pcb_view.cpp:58
void selectAllItemsOnNet(int aNetCode, bool aSelect=true)
Select all items with the given net code.
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:320
const Vec & GetSize() const
Definition: box2.h:172
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:426
void RemoveItemFromSel(BOARD_ITEM *aItem, bool aQuietMode=false)
Multiple item unselection event handler.
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition: view.h:68
A general implementation of a COLLECTORS_GUIDE.
Definition: collectors.h:377
bool selectionContains(const VECTOR2I &aPoint) const
Definition: pad.h:57
double GetScale() const
Definition: view.h:264
int ClearSelection(const TOOL_EVENT &aEvent)
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:136
static TOOL_ACTION cursorDown
Definition: actions.h:114
void FocusOnLocation(const wxPoint &aPos)
Useful to focus on a particular location, in find functions.
void unhighlightInternal(BOARD_ITEM *aItem, int aHighlightMode, bool aUsingOverlay)
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...
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:90
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:143
static TOOL_ACTION selectAll
Definition: actions.h:70
virtual EDA_RECT GetBoundingBox() const
Definition: selection.cpp:112
void selectAllItemsOnSheet(wxString &aSheetPath)
Select all items on a sheet and its subsheets, given the full sheet path.
void ShowContextMenu(SELECTION &aSelection)
Helper function to set and immediately show a CONDITIONAL_MENU in concert with the given SELECTION.
Definition: tool_menu.cpp:59
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:914
void unhighlight(BOARD_ITEM *aItem, int aHighlightMode, PCB_SELECTION *aGroup=nullptr)
Unhighlight the item visually.
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
static TOOL_ACTION selectionCursor
Select a single item under the cursor position.
Definition: pcb_actions.h:56
void SetIgnoreThroughHolePads(bool ignore)
Definition: collectors.h:541
EDA_UNITS GetUserUnits() const
Return the user units currently in use.
void SetIgnoreZoneFills(bool ignore)
Definition: collectors.h:568
const wxPoint & GetStart() const
Definition: pcb_track.h:108
virtual double ViewGetLOD(int aLayer, VIEW *aView) const
Return the level of detail (LOD) of the item.
Definition: view_item.h:132
int filterSelection(const TOOL_EVENT &aEvent)
Return true if the given item passes the current SELECTION_FILTER_OPTIONS.
EDA_ITEM * Front() const
Definition: selection.h:145
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:1570
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:112
static TOOL_ACTION selectSameSheet
Select all components on the same sheet as the selected footprint.
Definition: pcb_actions.h:86
#define BRIGHTENED
item is drawn with a bright contour
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition: view.h:405
void SetIgnorePadsOnBack(bool ignore)
Definition: collectors.h:529