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  "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  m_skip_heuristics = true;
927  m_skip_heuristics = false;
928 
929  return 0;
930 }
931 
932 
933 
935 {
937 
938  selectCursor( false, aClientFilter );
939 
940  return 0;
941 }
942 
943 
945 {
946  ClearSelection();
947 
948  return 0;
949 }
950 
951 
953 {
954  std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
955 
956  if( items )
957  {
958  // Perform individual selection of each item before processing the event.
959  for( BOARD_ITEM* item : *items )
960  select( item );
961 
963  }
964 
965  return 0;
966 }
967 
968 
970 {
971  AddItemToSel( aEvent.Parameter<BOARD_ITEM*>() );
972  return 0;
973 }
974 
975 
977 {
978  KIGFX::VIEW* view = getView();
979 
980  // hold all visible items
981  std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
982 
983  // Filter the view items based on the selection box
984  BOX2I selectionBox;
985 
986  // Intermediate step to allow filtering against hierarchy
987  GENERAL_COLLECTOR collection;
988 
989  selectionBox.SetMaximum();
990  view->Query( selectionBox, selectedItems ); // Get the list of selected items
991 
992  for( const KIGFX::VIEW::LAYER_ITEM_PAIR& item_pair : selectedItems )
993  {
994  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( item_pair.first );
995 
996  if( !item || !Selectable( item ) || !itemPassesFilter( item, true ) )
997  continue;
998 
999  collection.Append( item );
1000  }
1001 
1002  FilterCollectorForHierarchy( collection, true );
1003 
1004  for( EDA_ITEM* item : collection )
1005  select( static_cast<BOARD_ITEM*>( item ) );
1006 
1008 
1009  return 0;
1010 }
1011 
1012 
1013 void PCB_SELECTION_TOOL::AddItemToSel( BOARD_ITEM* aItem, bool aQuietMode )
1014 {
1015  if( aItem )
1016  {
1017  select( aItem );
1018 
1019  // Inform other potentially interested tools
1020  if( !aQuietMode )
1022  }
1023 }
1024 
1025 
1027 {
1028  std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
1029 
1030  if( items )
1031  {
1032  // Perform individual unselection of each item before processing the event
1033  for( auto item : *items )
1034  unselect( item );
1035 
1037  }
1038 
1039  return 0;
1040 }
1041 
1042 
1044 {
1045  RemoveItemFromSel( aEvent.Parameter<BOARD_ITEM*>() );
1046  return 0;
1047 }
1048 
1049 
1050 void PCB_SELECTION_TOOL::RemoveItemFromSel( BOARD_ITEM* aItem, bool aQuietMode )
1051 {
1052  if( aItem )
1053  {
1054  unselect( aItem );
1055 
1056  if( !aQuietMode )
1057  {
1058  // Inform other potentially interested tools
1060  }
1061  }
1062 }
1063 
1064 
1066 {
1067  highlight( aItem, BRIGHTENED );
1068 }
1069 
1070 
1072 {
1073  unhighlight( aItem, BRIGHTENED );
1074 }
1075 
1076 
1078  PCB_SELECTION_TOOL* sTool )
1079 {
1080  // Narrow the collection down to a single BOARD_CONNECTED_ITEM for each represented net.
1081  // All other items types are removed.
1082  std::set<int> representedNets;
1083 
1084  for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
1085  {
1086  BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( aCollector[i] );
1087  if( !item )
1088  aCollector.Remove( i );
1089  else if ( representedNets.count( item->GetNetCode() ) )
1090  aCollector.Remove( i );
1091  else
1092  representedNets.insert( item->GetNetCode() );
1093  }
1094 }
1095 
1096 
1098 {
1099  unsigned initialCount = 0;
1100 
1101  for( const EDA_ITEM* item : m_selection.GetItems() )
1102  {
1103  if( dynamic_cast<const BOARD_CONNECTED_ITEM*>( item ) )
1104  initialCount++;
1105  }
1106 
1107  if( initialCount == 0 )
1109 
1110  for( STOP_CONDITION stopCondition : { STOP_AT_JUNCTION, STOP_AT_PAD, STOP_NEVER } )
1111  {
1112  // copy the selection, since we're going to iterate and modify
1113  std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
1114 
1115  for( EDA_ITEM* item : selectedItems )
1116  item->ClearTempFlags();
1117 
1118  for( EDA_ITEM* item : selectedItems )
1119  {
1120  PCB_TRACK* trackItem = dynamic_cast<PCB_TRACK*>( item );
1121 
1122  // Track items marked SKIP_STRUCT have already been visited
1123  if( trackItem && !( trackItem->GetFlags() & SKIP_STRUCT ) )
1124  selectConnectedTracks( *trackItem, stopCondition );
1125  }
1126 
1127  if( m_selection.GetItems().size() > initialCount )
1128  break;
1129  }
1130 
1131  // Inform other potentially interested tools
1132  if( m_selection.Size() > 0 )
1134 
1135  return 0;
1136 }
1137 
1138 
1140  STOP_CONDITION aStopCondition )
1141 {
1142  constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, EOT };
1143  constexpr PCB_LAYER_ID ALL_LAYERS = UNDEFINED_LAYER;
1144 
1145  auto connectivity = board()->GetConnectivity();
1146  auto connectedItems = connectivity->GetConnectedItems( &aStartItem, types, true );
1147 
1148  std::map<wxPoint, std::vector<PCB_TRACK*>> trackMap;
1149  std::map<wxPoint, PCB_VIA*> viaMap;
1150  std::map<wxPoint, PAD*> padMap;
1151 
1152  // Build maps of connected items
1153  for( BOARD_CONNECTED_ITEM* item : connectedItems )
1154  {
1155  switch( item->Type() )
1156  {
1157  case PCB_ARC_T:
1158  case PCB_TRACE_T:
1159  {
1160  PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
1161  trackMap[ track->GetStart() ].push_back( track );
1162  trackMap[ track->GetEnd() ].push_back( track );
1163  break;
1164  }
1165 
1166  case PCB_VIA_T:
1167  {
1168  PCB_VIA* via = static_cast<PCB_VIA*>( item );
1169  viaMap[ via->GetStart() ] = via;
1170  break;
1171  }
1172 
1173  case PCB_PAD_T:
1174  {
1175  PAD* pad = static_cast<PAD*>( item );
1176  padMap[ pad->GetPosition() ] = pad;
1177  break;
1178  }
1179 
1180  default:
1181  break;
1182  }
1183 
1184  item->ClearFlags( TEMP_SELECTED );
1185  }
1186 
1187  std::vector< std::pair<wxPoint, PCB_LAYER_ID> > activePts;
1188 
1189  // Set up the initial active points
1190  switch( aStartItem.Type() )
1191  {
1192  case PCB_ARC_T:
1193  case PCB_TRACE_T:
1194  {
1195  PCB_TRACK* track = static_cast<PCB_TRACK*>( &aStartItem );
1196 
1197  activePts.push_back( { track->GetStart(), track->GetLayer() } );
1198  activePts.push_back( { track->GetEnd(), track->GetLayer() } );
1199  }
1200  break;
1201 
1202  case PCB_VIA_T:
1203  activePts.push_back( { aStartItem.GetPosition(), ALL_LAYERS } );
1204  break;
1205 
1206  case PCB_PAD_T:
1207  activePts.push_back( { aStartItem.GetPosition(), ALL_LAYERS } );
1208  break;
1209 
1210  default:
1211  break;
1212  }
1213 
1214  bool expand = true;
1215  int failSafe = 0;
1216 
1217  // Iterative push from all active points
1218  while( expand && failSafe++ < 100000 )
1219  {
1220  expand = false;
1221 
1222  for( int i = activePts.size() - 1; i >= 0; --i )
1223  {
1224  wxPoint pt = activePts[i].first;
1225  PCB_LAYER_ID layer = activePts[i].second;
1226  size_t pt_count = 0;
1227 
1228  for( PCB_TRACK* track : trackMap[pt] )
1229  {
1230  if( layer == ALL_LAYERS || layer == track->GetLayer() )
1231  pt_count++;
1232  }
1233 
1234  if( aStopCondition == STOP_AT_JUNCTION )
1235  {
1236  if( pt_count > 2
1237  || ( viaMap.count( pt ) && layer != ALL_LAYERS )
1238  || ( padMap.count( pt ) && layer != ALL_LAYERS ) )
1239  {
1240  activePts.erase( activePts.begin() + i );
1241  continue;
1242  }
1243  }
1244  else if( aStopCondition == STOP_AT_PAD )
1245  {
1246  if( padMap.count( pt ) )
1247  {
1248  activePts.erase( activePts.begin() + i );
1249  continue;
1250  }
1251  }
1252 
1253  if( padMap.count( pt ) )
1254  {
1255  PAD* pad = padMap[ pt ];
1256 
1257  if( !( pad->GetFlags() & TEMP_SELECTED ) )
1258  {
1259  pad->SetFlags( TEMP_SELECTED );
1260  activePts.push_back( { pad->GetPosition(), ALL_LAYERS } );
1261  expand = true;
1262  }
1263  }
1264 
1265  for( PCB_TRACK* track : trackMap[ pt ] )
1266  {
1267  if( layer != ALL_LAYERS && track->GetLayer() != layer )
1268  continue;
1269 
1270  if( !track->IsSelected() )
1271  {
1272  select( track );
1273 
1274  if( track->GetStart() == pt )
1275  activePts.push_back( { track->GetEnd(), track->GetLayer() } );
1276  else
1277  activePts.push_back( { track->GetStart(), track->GetLayer() } );
1278 
1279  expand = true;
1280  }
1281  }
1282 
1283  if( viaMap.count( pt ) )
1284  {
1285  PCB_VIA* via = viaMap[ pt ];
1286 
1287  if( !via->IsSelected() )
1288  {
1289  select( via );
1290  activePts.push_back( { via->GetPosition(), ALL_LAYERS } );
1291  expand = true;
1292  }
1293  }
1294 
1295  activePts.erase( activePts.begin() + i );
1296  }
1297  }
1298 }
1299 
1300 
1301 void PCB_SELECTION_TOOL::selectAllItemsOnNet( int aNetCode, bool aSelect )
1302 {
1303  constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, EOT };
1304  auto connectivity = board()->GetConnectivity();
1305 
1306  for( BOARD_CONNECTED_ITEM* item : connectivity->GetNetItems( aNetCode, types ) )
1307  {
1308  if( itemPassesFilter( item, true ) )
1309  aSelect ? select( item ) : unselect( item );
1310  }
1311 }
1312 
1313 
1315 {
1316  bool select = aEvent.IsAction( &PCB_ACTIONS::selectNet );
1317 
1318  // If we've been passed an argument, just select that netcode1
1319  int netcode = aEvent.Parameter<intptr_t>();
1320 
1321  if( netcode > 0 )
1322  {
1323  selectAllItemsOnNet( netcode, select );
1324  return 0;
1325  }
1326 
1327  if( !selectCursor() )
1328  return 0;
1329 
1330  // copy the selection, since we're going to iterate and modify
1331  auto selection = m_selection.GetItems();
1332 
1333  for( EDA_ITEM* i : selection )
1334  {
1335  BOARD_CONNECTED_ITEM* connItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( i );
1336 
1337  if( connItem )
1338  selectAllItemsOnNet( connItem->GetNetCode(), select );
1339  }
1340 
1341  // Inform other potentially interested tools
1342  if( m_selection.Size() > 0 )
1344 
1345  return 0;
1346 }
1347 
1348 
1349 void PCB_SELECTION_TOOL::selectAllItemsOnSheet( wxString& aSheetPath )
1350 {
1351  std::list<FOOTPRINT*> footprintList;
1352 
1353  // store all footprints that are on that sheet path
1354  for( FOOTPRINT* footprint : board()->Footprints() )
1355  {
1356  if( footprint == nullptr )
1357  continue;
1358 
1359  wxString footprint_path = footprint->GetPath().AsString().BeforeLast('/');
1360 
1361  if( aSheetPath.IsEmpty() )
1362  aSheetPath += '/';
1363 
1364  if( footprint_path == aSheetPath )
1365  footprintList.push_back( footprint );
1366  }
1367 
1368  // Generate a list of all pads, and of all nets they belong to.
1369  std::list<int> netcodeList;
1370  std::list<PAD*> padList;
1371 
1372  for( FOOTPRINT* footprint : footprintList )
1373  {
1374  for( PAD* pad : footprint->Pads() )
1375  {
1376  if( pad->IsConnected() )
1377  {
1378  netcodeList.push_back( pad->GetNetCode() );
1379  padList.push_back( pad );
1380  }
1381  }
1382  }
1383 
1384  // remove all duplicates
1385  netcodeList.sort();
1386  netcodeList.unique();
1387 
1388  for( PAD* pad : padList )
1390 
1391  // now we need to find all footprints that are connected to each of these nets then we need
1392  // to determine if these footprints are in the list of footprints belonging to this sheet
1393  std::list<int> removeCodeList;
1394  constexpr KICAD_T padType[] = { PCB_PAD_T, EOT };
1395 
1396  for( int netCode : netcodeList )
1397  {
1398  for( BOARD_CONNECTED_ITEM* mitem : board()->GetConnectivity()->GetNetItems( netCode,
1399  padType ) )
1400  {
1401  if( mitem->Type() == PCB_PAD_T && !alg::contains( footprintList, mitem->GetParent() ) )
1402  {
1403  // if we cannot find the footprint of the pad in the footprintList then we can
1404  // assume that that footprint is not located in the same schematic, therefore
1405  // invalidate this netcode.
1406  removeCodeList.push_back( netCode );
1407  break;
1408  }
1409  }
1410  }
1411 
1412  // remove all duplicates
1413  removeCodeList.sort();
1414  removeCodeList.unique();
1415 
1416  for( int removeCode : removeCodeList )
1417  {
1418  netcodeList.remove( removeCode );
1419  }
1420 
1421  std::list<BOARD_CONNECTED_ITEM*> localConnectionList;
1422  constexpr KICAD_T trackViaType[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, EOT };
1423 
1424  for( int netCode : netcodeList )
1425  {
1426  for( BOARD_CONNECTED_ITEM* item : board()->GetConnectivity()->GetNetItems( netCode,
1427  trackViaType ) )
1428  localConnectionList.push_back( item );
1429  }
1430 
1431  for( BOARD_ITEM* i : footprintList )
1432  {
1433  if( i != nullptr )
1434  select( i );
1435  }
1436 
1437  for( BOARD_CONNECTED_ITEM* i : localConnectionList )
1438  {
1439  if( i != nullptr )
1440  select( i );
1441  }
1442 }
1443 
1444 
1446 {
1447  // Should recalculate the view to zoom in on the selection.
1448  auto selectionBox = m_selection.GetBoundingBox();
1449  auto view = getView();
1450 
1451  VECTOR2D screenSize = view->ToWorld( m_frame->GetCanvas()->GetClientSize(), false );
1452  screenSize.x = std::max( 10.0, screenSize.x );
1453  screenSize.y = std::max( 10.0, screenSize.y );
1454 
1455  if( selectionBox.GetWidth() != 0 || selectionBox.GetHeight() != 0 )
1456  {
1457  VECTOR2D vsize = selectionBox.GetSize();
1458  double scale = view->GetScale() / std::max( fabs( vsize.x / screenSize.x ),
1459  fabs( vsize.y / screenSize.y ) );
1460  view->SetScale( scale );
1461  view->SetCenter( selectionBox.Centre() );
1462  view->Add( &m_selection );
1463  }
1464 
1466 }
1467 
1468 
1470 {
1471  ClearSelection( true /*quiet mode*/ );
1472  wxString sheetPath = *aEvent.Parameter<wxString*>();
1473 
1474  selectAllItemsOnSheet( sheetPath );
1475 
1476  zoomFitSelection();
1477 
1478  if( m_selection.Size() > 0 )
1480 
1481  return 0;
1482 }
1483 
1484 
1486 {
1487  if( !selectCursor( true ) )
1488  return 0;
1489 
1490  // this function currently only supports footprints since they are only on one sheet.
1491  auto item = m_selection.Front();
1492 
1493  if( !item )
1494  return 0;
1495 
1496  if( item->Type() != PCB_FOOTPRINT_T )
1497  return 0;
1498 
1499  FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( item );
1500 
1501  if( footprint->GetPath().empty() )
1502  return 0;
1503 
1504  ClearSelection( true /*quiet mode*/ );
1505 
1506  // get the sheet path only.
1507  wxString sheetPath = footprint->GetPath().AsString().BeforeLast( '/' );
1508 
1509  if( sheetPath.IsEmpty() )
1510  sheetPath += '/';
1511 
1512  selectAllItemsOnSheet( sheetPath );
1513 
1514  // Inform other potentially interested tools
1515  if( m_selection.Size() > 0 )
1517 
1518  return 0;
1519 }
1520 
1521 
1523 {
1524  bool cleared = false;
1525 
1526  if( m_selection.GetSize() > 0 )
1527  {
1528  // Don't fire an event now; most of the time it will be redundant as we're about to
1529  // fire a SelectedEvent.
1530  cleared = true;
1531  ClearSelection( true /*quiet mode*/ );
1532  }
1533 
1534  if( aItem )
1535  {
1536  select( aItem );
1537  m_frame->FocusOnLocation( aItem->GetPosition() );
1538 
1539  // Inform other potentially interested tools
1541  }
1542  else if( cleared )
1543  {
1545  }
1546 
1548 }
1549 
1550 
1556 static bool itemIsIncludedByFilter( const BOARD_ITEM& aItem, const BOARD& aBoard,
1557  const DIALOG_FILTER_SELECTION::OPTIONS& aFilterOptions )
1558 {
1559  bool include = true;
1560  const PCB_LAYER_ID layer = aItem.GetLayer();
1561 
1562  // if the item needs to be checked against the options
1563  if( include )
1564  {
1565  switch( aItem.Type() )
1566  {
1567  case PCB_FOOTPRINT_T:
1568  {
1569  const FOOTPRINT& footprint = static_cast<const FOOTPRINT&>( aItem );
1570 
1571  include = aFilterOptions.includeModules;
1572 
1573  if( include && !aFilterOptions.includeLockedModules )
1574  include = !footprint.IsLocked();
1575 
1576  break;
1577  }
1578  case PCB_TRACE_T:
1579  case PCB_ARC_T:
1580  include = aFilterOptions.includeTracks;
1581  break;
1582 
1583  case PCB_VIA_T:
1584  include = aFilterOptions.includeVias;
1585  break;
1586 
1587  case PCB_FP_ZONE_T:
1588  case PCB_ZONE_T:
1589  include = aFilterOptions.includeZones;
1590  break;
1591 
1592  case PCB_SHAPE_T:
1593  case PCB_TARGET_T:
1594  case PCB_DIM_ALIGNED_T:
1595  case PCB_DIM_CENTER_T:
1596  case PCB_DIM_ORTHOGONAL_T:
1597  case PCB_DIM_LEADER_T:
1598  if( layer == Edge_Cuts )
1599  include = aFilterOptions.includeBoardOutlineLayer;
1600  else
1601  include = aFilterOptions.includeItemsOnTechLayers;
1602  break;
1603 
1604  case PCB_FP_TEXT_T:
1605  case PCB_TEXT_T:
1606  include = aFilterOptions.includePcbTexts;
1607  break;
1608 
1609  default:
1610  // no filtering, just select it
1611  break;
1612  }
1613  }
1614 
1615  return include;
1616 }
1617 
1618 
1620 {
1621  const BOARD& board = *getModel<BOARD>();
1622  DIALOG_FILTER_SELECTION::OPTIONS& opts = m_priv->m_filterOpts;
1623  DIALOG_FILTER_SELECTION dlg( m_frame, opts );
1624 
1625  const int cmd = dlg.ShowModal();
1626 
1627  if( cmd != wxID_OK )
1628  return 0;
1629 
1630  // copy current selection
1631  std::deque<EDA_ITEM*> selection = m_selection.GetItems();
1632 
1633  ClearSelection( true /*quiet mode*/ );
1634 
1635  // re-select items from the saved selection according to the dialog options
1636  for( EDA_ITEM* i : selection )
1637  {
1638  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
1639  bool include = itemIsIncludedByFilter( *item, board, opts );
1640 
1641  if( include )
1642  select( item );
1643  }
1644 
1646 
1647  return 0;
1648 }
1649 
1650 
1651 void PCB_SELECTION_TOOL::FilterCollectedItems( GENERAL_COLLECTOR& aCollector, bool aMultiSelect )
1652 {
1653  if( aCollector.GetCount() == 0 )
1654  return;
1655 
1656  std::set<BOARD_ITEM*> rejected;
1657 
1658  for( EDA_ITEM* i : aCollector )
1659  {
1660  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
1661 
1662  if( !itemPassesFilter( item, aMultiSelect ) )
1663  rejected.insert( item );
1664  }
1665 
1666  for( BOARD_ITEM* item : rejected )
1667  aCollector.Remove( item );
1668 }
1669 
1670 
1671 bool PCB_SELECTION_TOOL::itemPassesFilter( BOARD_ITEM* aItem, bool aMultiSelect )
1672 {
1673  if( !m_filter.lockedItems )
1674  {
1675  if( aItem->IsLocked() || ( aItem->GetParent() && aItem->GetParent()->IsLocked() ) )
1676  {
1677  if( aItem->Type() == PCB_PAD_T && !aMultiSelect )
1678  {
1679  // allow a single pad to be selected -- there are a lot of operations that
1680  // require this so we allow this one inconsistency
1681  }
1682  else
1683  {
1684  return false;
1685  }
1686  }
1687  }
1688 
1689  switch( aItem->Type() )
1690  {
1691  case PCB_FOOTPRINT_T:
1692  if( !m_filter.footprints )
1693  return false;
1694 
1695  break;
1696 
1697  case PCB_PAD_T:
1698  if( !m_filter.pads )
1699  return false;
1700 
1701  break;
1702 
1703  case PCB_TRACE_T:
1704  case PCB_ARC_T:
1705  if( !m_filter.tracks )
1706  return false;
1707 
1708  break;
1709 
1710  case PCB_VIA_T:
1711  if( !m_filter.vias )
1712  return false;
1713 
1714  break;
1715 
1716  case PCB_FP_ZONE_T:
1717  case PCB_ZONE_T:
1718  {
1719  ZONE* zone = static_cast<ZONE*>( aItem );
1720 
1721  if( ( !m_filter.zones && !zone->GetIsRuleArea() )
1722  || ( !m_filter.keepouts && zone->GetIsRuleArea() ) )
1723  {
1724  return false;
1725  }
1726 
1727  break;
1728  }
1729 
1730  case PCB_FP_SHAPE_T:
1731  case PCB_SHAPE_T:
1732  case PCB_TARGET_T:
1733  if( !m_filter.graphics )
1734  return false;
1735 
1736  break;
1737 
1738  case PCB_FP_TEXT_T:
1739  case PCB_TEXT_T:
1740  if( !m_filter.text )
1741  return false;
1742 
1743  break;
1744 
1745  case PCB_DIM_ALIGNED_T:
1746  case PCB_DIM_CENTER_T:
1747  case PCB_DIM_ORTHOGONAL_T:
1748  case PCB_DIM_LEADER_T:
1749  if( !m_filter.dimensions )
1750  return false;
1751 
1752  break;
1753 
1754  default:
1755  if( !m_filter.otherItems )
1756  return false;
1757  }
1758 
1759  return true;
1760 }
1761 
1762 
1763 void PCB_SELECTION_TOOL::ClearSelection( bool aQuietMode )
1764 {
1765  if( m_selection.Empty() )
1766  return;
1767 
1768  while( m_selection.GetSize() )
1769  unhighlight( static_cast<BOARD_ITEM*>( m_selection.Front() ), SELECTED, &m_selection );
1770 
1771  view()->Update( &m_selection );
1772 
1773  m_selection.SetIsHover( false );
1775 
1776  // Inform other potentially interested tools
1777  if( !aQuietMode )
1778  {
1781  }
1782 }
1783 
1784 
1786 {
1787  m_selection.Clear();
1788 
1789  bool enteredGroupFound = false;
1790 
1791  INSPECTOR_FUNC inspector =
1792  [&]( EDA_ITEM* item, void* testData )
1793  {
1794  if( item->IsSelected() )
1795  {
1796  EDA_ITEM* parent = item->GetParent();
1797 
1798  // Let selected parents handle their children.
1799  if( parent && parent->IsSelected() )
1800  return SEARCH_RESULT::CONTINUE;
1801 
1802  highlight( (BOARD_ITEM*) item, SELECTED, &m_selection );
1803  }
1804 
1805  if( item == m_enteredGroup )
1806  {
1807  item->SetFlags( ENTERED );
1808  enteredGroupFound = true;
1809  }
1810  else
1811  {
1812  item->ClearFlags( ENTERED );
1813  }
1814 
1815  return SEARCH_RESULT::CONTINUE;
1816  };
1817 
1820 
1821  if( !enteredGroupFound )
1822  {
1824  m_enteredGroup = nullptr;
1825  }
1826 }
1827 
1828 
1830 {
1831  GENERAL_COLLECTOR* collector = aEvent.Parameter<GENERAL_COLLECTOR*>();
1832 
1833  doSelectionMenu( collector );
1834 
1835  return 0;
1836 }
1837 
1838 
1840 {
1841  BOARD_ITEM* current = nullptr;
1842  PCB_SELECTION highlightGroup;
1843  bool selectAll = false;
1844  bool expandSelection = false;
1845 
1846  highlightGroup.SetLayer( LAYER_SELECT_OVERLAY );
1847  getView()->Add( &highlightGroup );
1848 
1849  do
1850  {
1852  if( expandSelection )
1853  aCollector->Combine();
1854 
1855  expandSelection = false;
1856 
1857  int limit = std::min( 9, aCollector->GetCount() );
1858  ACTION_MENU menu( true );
1859 
1860  for( int i = 0; i < limit; ++i )
1861  {
1862  wxString text;
1863  BOARD_ITEM* item = ( *aCollector )[i];
1865 
1866  wxString menuText = wxString::Format( "&%d. %s\t%d", i + 1, text, i + 1 );
1867  menu.Add( menuText, i + 1, item->GetMenuImage() );
1868  }
1869 
1870  menu.AppendSeparator();
1871  menu.Add( _( "Select &All\tA" ), limit + 1, BITMAPS::INVALID_BITMAP );
1872 
1873  if( !expandSelection && aCollector->HasAdditionalItems() )
1874  menu.Add( _( "&Expand Selection\tE" ), limit + 2, BITMAPS::INVALID_BITMAP );
1875 
1876  if( aCollector->m_MenuTitle.Length() )
1877  {
1878  menu.SetTitle( aCollector->m_MenuTitle );
1879  menu.SetIcon( BITMAPS::info );
1880  menu.DisplayTitle( true );
1881  }
1882  else
1883  {
1884  menu.DisplayTitle( false );
1885  }
1886 
1887  SetContextMenu( &menu, CMENU_NOW );
1888 
1889  while( TOOL_EVENT* evt = Wait() )
1890  {
1891  if( evt->Action() == TA_CHOICE_MENU_UPDATE )
1892  {
1893  if( selectAll )
1894  {
1895  for( int i = 0; i < aCollector->GetCount(); ++i )
1896  unhighlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
1897  }
1898  else if( current )
1899  {
1900  unhighlight( current, BRIGHTENED, &highlightGroup );
1901  }
1902 
1903  int id = *evt->GetCommandId();
1904 
1905  // User has pointed an item, so show it in a different way
1906  if( id > 0 && id <= limit )
1907  {
1908  current = ( *aCollector )[id - 1];
1909  highlight( current, BRIGHTENED, &highlightGroup );
1910  }
1911  else
1912  {
1913  current = nullptr;
1914  }
1915 
1916  // User has pointed on the "Select All" option
1917  if( id == limit + 1 )
1918  {
1919  for( int i = 0; i < aCollector->GetCount(); ++i )
1920  highlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
1921  selectAll = true;
1922  }
1923  else
1924  {
1925  selectAll = false;
1926  }
1927  }
1928  else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
1929  {
1930  if( selectAll )
1931  {
1932  for( int i = 0; i < aCollector->GetCount(); ++i )
1933  unhighlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
1934  }
1935  else if( current )
1936  {
1937  unhighlight( current, BRIGHTENED, &highlightGroup );
1938  }
1939 
1940  OPT<int> id = evt->GetCommandId();
1941 
1942  // User has selected the "Select All" option
1943  if( id == limit + 1 )
1944  {
1945  selectAll = true;
1946  current = nullptr;
1947  }
1948  else if( id == limit + 2 )
1949  {
1950  expandSelection = true;
1951  selectAll = false;
1952  current = nullptr;
1953  }
1954  // User has selected an item, so this one will be returned
1955  else if( id && ( *id > 0 ) && ( *id <= limit ) )
1956  {
1957  selectAll = false;
1958  current = ( *aCollector )[*id - 1];
1959  }
1960  else
1961  {
1962  selectAll = false;
1963  current = nullptr;
1964  }
1965  }
1966  else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
1967  {
1968  break;
1969  }
1970  }
1971  } while( expandSelection );
1972 
1973  getView()->Remove( &highlightGroup );
1974 
1975  if( selectAll )
1976  {
1977  return true;
1978  }
1979  else if( current )
1980  {
1981  aCollector->Empty();
1982  aCollector->Append( current );
1983  return true;
1984  }
1985 
1986  return false;
1987 }
1988 
1989 
1990 bool PCB_SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibilityOnly ) const
1991 {
1992  const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
1993 
1994  if( settings->GetHighContrast() )
1995  {
1996  std::set<unsigned int> activeLayers = settings->GetHighContrastLayers();
1997  bool onActiveLayer = false;
1998 
1999  for( unsigned int layer : activeLayers )
2000  {
2001  // NOTE: Only checking the regular layers (not GAL meta-layers)
2002  if( layer < PCB_LAYER_ID_COUNT && aItem->IsOnLayer( ToLAYER_ID( layer ) ) )
2003  {
2004  onActiveLayer = true;
2005  break;
2006  }
2007  }
2008 
2009  if( !onActiveLayer ) // We do not want to select items that are in the background
2010  return false;
2011  }
2012 
2013  if( aItem->Type() == PCB_FOOTPRINT_T )
2014  {
2015  // In footprint editor, we do not want to select the footprint itself.
2016  if( m_isFootprintEditor )
2017  return false;
2018 
2019  // Allow selection of footprints if some part of the footprint is visible.
2020  const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
2021 
2022  // If the footprint has no items except the reference and value fields, include the
2023  // footprint in the selections.
2024  if( footprint->GraphicalItems().empty()
2025  && footprint->Pads().empty()
2026  && footprint->Zones().empty() )
2027  return true;
2028 
2029  for( const BOARD_ITEM* item : footprint->GraphicalItems() )
2030  {
2031  if( Selectable( item, true ) )
2032  return true;
2033  }
2034 
2035  for( const PAD* pad : footprint->Pads() )
2036  {
2037  if( Selectable( pad, true ) )
2038  return true;
2039  }
2040 
2041  for( const ZONE* zone : footprint->Zones() )
2042  {
2043  if( Selectable( zone, true ) )
2044  return true;
2045  }
2046 
2047  return false;
2048  }
2049  else if( aItem->Type() == PCB_GROUP_T )
2050  {
2051  PCB_GROUP* group = const_cast<PCB_GROUP*>( static_cast<const PCB_GROUP*>( aItem ) );
2052 
2053  // Similar to logic for footprint, a group is selectable if any of its members are.
2054  // (This recurses.)
2055  for( BOARD_ITEM* item : group->GetItems() )
2056  {
2057  if( Selectable( item, true ) )
2058  return true;
2059  }
2060 
2061  return false;
2062  }
2063 
2064  const ZONE* zone = nullptr;
2065  const PCB_VIA* via = nullptr;
2066  const PAD* pad = nullptr;
2067 
2068  switch( aItem->Type() )
2069  {
2070  case PCB_ZONE_T:
2071  case PCB_FP_ZONE_T:
2072  if( !board()->IsElementVisible( LAYER_ZONES ) )
2073  return false;
2074 
2075  zone = static_cast<const ZONE*>( aItem );
2076 
2077  // A footprint zone is only selectable within the footprint editor
2078  if( zone->GetParent()
2079  && zone->GetParent()->Type() == PCB_FOOTPRINT_T
2081  && !checkVisibilityOnly )
2082  {
2083  return false;
2084  }
2085 
2086  // zones can exist on multiple layers!
2087  if( !( zone->GetLayerSet() & board()->GetVisibleLayers() ).any() )
2088  return false;
2089 
2090  break;
2091 
2092  case PCB_TRACE_T:
2093  case PCB_ARC_T:
2094  if( !board()->IsElementVisible( LAYER_TRACKS ) )
2095  return false;
2096 
2097  if( m_isFootprintEditor )
2098  {
2099  if( !view()->IsLayerVisible( aItem->GetLayer() ) )
2100  return false;
2101  }
2102  else
2103  {
2104  if( !board()->IsLayerVisible( aItem->GetLayer() ) )
2105  return false;
2106  }
2107 
2108  break;
2109 
2110  case PCB_VIA_T:
2111  if( !board()->IsElementVisible( LAYER_VIAS ) )
2112  return false;
2113 
2114  via = static_cast<const PCB_VIA*>( aItem );
2115 
2116  // For vias it is enough if only one of its layers is visible
2117  if( !( board()->GetVisibleLayers() & via->GetLayerSet() ).any() )
2118  return false;
2119 
2120  break;
2121 
2122  case PCB_FP_TEXT_T:
2123  if( m_isFootprintEditor )
2124  {
2125  if( !view()->IsLayerVisible( aItem->GetLayer() ) )
2126  return false;
2127  }
2128  else
2129  {
2130  if( !view()->IsVisible( aItem ) )
2131  return false;
2132 
2133  if( !board()->IsLayerVisible( aItem->GetLayer() ) )
2134  return false;
2135  }
2136 
2137  break;
2138 
2139  case PCB_FP_SHAPE_T:
2140  if( m_isFootprintEditor )
2141  {
2142  if( !view()->IsLayerVisible( aItem->GetLayer() ) )
2143  return false;
2144  }
2145  else
2146  {
2147  // Footprint shape selections are only allowed in footprint editor mode.
2148  if( !checkVisibilityOnly )
2149  return false;
2150 
2151  if( !board()->IsLayerVisible( aItem->GetLayer() ) )
2152  return false;
2153  }
2154 
2155  break;
2156 
2157  case PCB_PAD_T:
2158  // Multiple selection is only allowed in footprint editor mode. In pcbnew, you have to
2159  // select footprint subparts one by one, rather than with a drag selection. This is so
2160  // you can pick up items under an (unlocked) footprint without also moving the
2161  // footprint's sub-parts.
2162  if( !m_isFootprintEditor && !checkVisibilityOnly )
2163  {
2164  if( m_multiple )
2165  return false;
2166  }
2167 
2168  pad = static_cast<const PAD*>( aItem );
2169 
2170  if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
2171  {
2172  // Check render mode (from the Items tab) first
2173  if( !board()->IsElementVisible( LAYER_PADS_TH ) )
2174  return false;
2175 
2176  // A pad's hole is visible on every layer the pad is visible on plus many layers the
2177  // pad is not visible on -- so we only need to check for any visible hole layers.
2178  if( !( board()->GetVisibleLayers() & LSET::PhysicalLayersMask() ).any() )
2179  return false;
2180  }
2181  else
2182  {
2183  // Check render mode (from the Items tab) first
2184  if( pad->IsOnLayer( F_Cu ) && !board()->IsElementVisible( LAYER_PAD_FR ) )
2185  return false;
2186  else if( pad->IsOnLayer( B_Cu ) && !board()->IsElementVisible( LAYER_PAD_BK ) )
2187  return false;
2188 
2189  if( !( pad->GetLayerSet() & board()->GetVisibleLayers() ).any() )
2190  return false;
2191  }
2192 
2193  break;
2194 
2195  // These are not selectable
2196  case PCB_NETINFO_T:
2197  case NOT_USED:
2198  case TYPE_NOT_INIT:
2199  return false;
2200 
2201  default: // Suppress warnings
2202  break;
2203  }
2204 
2205  return aItem->ViewGetLOD( aItem->GetLayer(), view() ) < view()->GetScale();
2206 }
2207 
2208 
2210 {
2211  if( aItem->IsSelected() )
2212  return;
2213 
2214  if( aItem->Type() == PCB_PAD_T )
2215  {
2216  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem->GetParent() );
2217 
2218  if( m_selection.Contains( footprint ) )
2219  return;
2220  }
2221 
2222  highlight( aItem, SELECTED, &m_selection );
2223 }
2224 
2225 
2227 {
2228  unhighlight( aItem, SELECTED, &m_selection );
2229 }
2230 
2231 
2232 void PCB_SELECTION_TOOL::highlight( BOARD_ITEM* aItem, int aMode, PCB_SELECTION* aGroup )
2233 {
2234  if( aGroup )
2235  aGroup->Add( aItem );
2236 
2237  highlightInternal( aItem, aMode, aGroup != nullptr );
2238  view()->Update( aItem, KIGFX::REPAINT );
2239 
2240  // Many selections are very temporal and updating the display each time just
2241  // creates noise.
2242  if( aMode == BRIGHTENED )
2244 }
2245 
2246 
2247 void PCB_SELECTION_TOOL::highlightInternal( BOARD_ITEM* aItem, int aMode, bool aUsingOverlay )
2248 {
2249  if( aMode == SELECTED )
2250  aItem->SetSelected();
2251  else if( aMode == BRIGHTENED )
2252  aItem->SetBrightened();
2253 
2254  if( aUsingOverlay )
2255  view()->Hide( aItem, true ); // Hide the original item, so it is shown only on overlay
2256 
2257  if( aItem->Type() == PCB_FOOTPRINT_T )
2258  {
2259  static_cast<FOOTPRINT*>( aItem )->RunOnChildren(
2260  [&]( BOARD_ITEM* aChild )
2261  {
2262  highlightInternal( aChild, aMode, aUsingOverlay );
2263  } );
2264  }
2265  else if( aItem->Type() == PCB_GROUP_T )
2266  {
2267  static_cast<PCB_GROUP*>( aItem )->RunOnChildren(
2268  [&]( BOARD_ITEM* aChild )
2269  {
2270  highlightInternal( aChild, aMode, aUsingOverlay );
2271  } );
2272  }
2273 }
2274 
2275 
2277 {
2278  if( aGroup )
2279  aGroup->Remove( aItem );
2280 
2281  unhighlightInternal( aItem, aMode, aGroup != nullptr );
2282  view()->Update( aItem, KIGFX::REPAINT );
2283 
2284  // Many selections are very temporal and updating the display each time just creates noise.
2285  if( aMode == BRIGHTENED )
2287 }
2288 
2289 
2290 void PCB_SELECTION_TOOL::unhighlightInternal( BOARD_ITEM* aItem, int aMode, bool aUsingOverlay )
2291 {
2292  if( aMode == SELECTED )
2293  aItem->ClearSelected();
2294  else if( aMode == BRIGHTENED )
2295  aItem->ClearBrightened();
2296 
2297  if( aUsingOverlay )
2298  view()->Hide( aItem, false ); // // Restore original item visibility
2299 
2300  if( aItem->Type() == PCB_FOOTPRINT_T )
2301  {
2302  static_cast<FOOTPRINT*>( aItem )->RunOnChildren(
2303  [&]( BOARD_ITEM* aChild )
2304  {
2305  unhighlightInternal( aChild, aMode, aUsingOverlay );
2306  } );
2307  }
2308  else if( aItem->Type() == PCB_GROUP_T )
2309  {
2310  static_cast<PCB_GROUP*>( aItem )->RunOnChildren(
2311  [&]( BOARD_ITEM* aChild )
2312  {
2313  unhighlightInternal( aChild, aMode, aUsingOverlay );
2314  } );
2315  }
2316 }
2317 
2318 
2320 {
2322  GENERAL_COLLECTOR collector;
2323 
2324  // Since we're just double-checking, we want a considerably sloppier check than the initial
2325  // selection (for which most tools use 5 pixels). So we increase this to an effective 20
2326  // pixels by artificially inflating the value of a pixel by 4X.
2327  guide.SetOnePixelInIU( guide.OnePixelInIU() * 4 );
2328 
2331  (wxPoint) aPoint, guide );
2332 
2333  for( int i = collector.GetCount() - 1; i >= 0; --i )
2334  {
2335  BOARD_ITEM* item = collector[i];
2336 
2337  if( item->IsSelected() && item->HitTest( (wxPoint) aPoint, 5 * guide.OnePixelInIU() ) )
2338  return true;
2339  }
2340 
2341  return false;
2342 }
2343 
2344 
2345 int PCB_SELECTION_TOOL::hitTestDistance( const wxPoint& aWhere, BOARD_ITEM* aItem,
2346  int aMaxDistance ) const
2347 {
2348  BOX2D viewportD = getView()->GetViewport();
2349  BOX2I viewport( VECTOR2I( viewportD.GetPosition() ), VECTOR2I( viewportD.GetSize() ) );
2350  int distance = INT_MAX;
2351  SEG loc( aWhere, aWhere );
2352 
2353  switch( aItem->Type() )
2354  {
2355  case PCB_TEXT_T:
2356  {
2357  PCB_TEXT* text = static_cast<PCB_TEXT*>( aItem );
2358  text->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance );
2359  break;
2360  }
2361 
2362  case PCB_FP_TEXT_T:
2363  {
2364  FP_TEXT* text = static_cast<FP_TEXT*>( aItem );
2365  text->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance );
2366  break;
2367  }
2368 
2369  case PCB_ZONE_T:
2370  {
2371  ZONE* zone = static_cast<ZONE*>( aItem );
2372 
2373  // Zone borders are very specific
2374  if( zone->HitTestForEdge( aWhere, aMaxDistance / 2 ) )
2375  distance = 0;
2376  else if( zone->HitTestForEdge( aWhere, aMaxDistance ) )
2377  distance = aMaxDistance / 2;
2378  else
2379  aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
2380 
2381  break;
2382  }
2383 
2384  case PCB_FOOTPRINT_T:
2385  {
2386  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
2387  EDA_RECT bbox = footprint->GetBoundingBox( false, false );
2388 
2389  try
2390  {
2391  footprint->GetBoundingHull().Collide( loc, aMaxDistance, &distance );
2392  }
2393  catch( const ClipperLib::clipperException& exc )
2394  {
2395  // This may be overkill and could be an assertion but we are more likely to find
2396  // any clipper errors this way.
2397  wxLogError( wxT( "Clipper library exception '%s' occurred." ), exc.what() );
2398  }
2399 
2400  // Consider footprints larger than the viewport only as a last resort
2401  if( bbox.GetHeight() > viewport.GetHeight() || bbox.GetWidth() > viewport.GetWidth() )
2402  distance = INT_MAX / 2;
2403 
2404  break;
2405  }
2406 
2407  case PCB_MARKER_T:
2408  {
2409  PCB_MARKER* marker = static_cast<PCB_MARKER*>( aItem );
2410  SHAPE_LINE_CHAIN polygon;
2411 
2412  marker->ShapeToPolygon( polygon );
2413  polygon.Move( marker->GetPos() );
2414  polygon.Collide( loc, aMaxDistance, &distance );
2415  break;
2416  }
2417 
2418  case PCB_GROUP_T:
2419  {
2420  PCB_GROUP* group = static_cast<PCB_GROUP*>( aItem );
2421 
2422  for( BOARD_ITEM* member : group->GetItems() )
2423  distance = std::min( distance, hitTestDistance( aWhere, member, aMaxDistance ) );
2424 
2425  break;
2426  }
2427 
2428  default:
2429  aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
2430  break;
2431  }
2432 
2433  return distance;
2434 }
2435 
2436 
2437 // The general idea here is that if the user clicks directly on a small item inside a larger
2438 // one, then they want the small item. The quintessential case of this is clicking on a pad
2439 // within a footprint, but we also apply it for text within a footprint, footprints within
2440 // larger footprints, and vias within either larger pads or longer tracks.
2441 //
2442 // These "guesses" presume there is area within the larger item to click in to select it. If
2443 // an item is mostly covered by smaller items within it, then the guesses are inappropriate as
2444 // there might not be any area left to click to select the larger item. In this case we must
2445 // leave the items in the collector and bring up a Selection Clarification menu.
2446 //
2447 // We currently check for pads and text mostly covering a footprint, but we don't check for
2448 // smaller footprints mostly covering a larger footprint.
2449 //
2451  const VECTOR2I& aWhere ) const
2452 {
2453  std::set<BOARD_ITEM*> preferred;
2454  std::set<BOARD_ITEM*> rejected;
2455  wxPoint where( aWhere.x, aWhere.y );
2456 
2457  PCB_LAYER_ID activeLayer = m_frame->GetActiveLayer();
2458  LSET silkLayers( 2, B_SilkS, F_SilkS );
2459 
2460  if( silkLayers[activeLayer] )
2461  {
2462  for( int i = 0; i < aCollector.GetCount(); ++i )
2463  {
2464  BOARD_ITEM* item = aCollector[i];
2465  KICAD_T type = item->Type();
2466 
2467  if( ( type == PCB_FP_TEXT_T || type == PCB_TEXT_T || type == PCB_SHAPE_T )
2468  && silkLayers[item->GetLayer()] )
2469  {
2470  preferred.insert( item );
2471  }
2472  }
2473 
2474  if( preferred.size() > 0 )
2475  {
2476  aCollector.Empty();
2477 
2478  for( BOARD_ITEM* item : preferred )
2479  aCollector.Append( item );
2480 
2481  return;
2482  }
2483  }
2484 
2485  // Prefer exact hits to sloppy ones
2486  constexpr int MAX_SLOP = 5;
2487 
2488  int pixel = (int) aCollector.GetGuide()->OnePixelInIU();
2489  int minSlop = INT_MAX;
2490 
2491  std::map<BOARD_ITEM*, int> itemsBySloppiness;
2492 
2493  for( int i = 0; i < aCollector.GetCount(); ++i )
2494  {
2495  BOARD_ITEM* item = aCollector[i];
2496  int itemSlop = hitTestDistance( where, item, MAX_SLOP * pixel );
2497 
2498  itemsBySloppiness[ item ] = itemSlop;
2499 
2500  if( itemSlop < minSlop )
2501  minSlop = itemSlop;
2502  }
2503 
2504  // Prune sloppier items
2505  if( minSlop < INT_MAX )
2506  {
2507  for( std::pair<BOARD_ITEM*, int> pair : itemsBySloppiness )
2508  {
2509  if( pair.second > minSlop + pixel )
2510  aCollector.Transfer( pair.first );
2511  }
2512  }
2513 
2514  // If the user clicked on a small item within a much larger one then it's pretty clear
2515  // they're trying to select the smaller one.
2516  constexpr double sizeRatio = 1.5;
2517 
2518  std::vector<std::pair<BOARD_ITEM*, double>> itemsByArea;
2519 
2520  for( int i = 0; i < aCollector.GetCount(); ++i )
2521  {
2522  BOARD_ITEM* item = aCollector[i];
2523  double area;
2524 
2525  if( ( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2526  && static_cast<ZONE*>( item )->HitTestForEdge( where, MAX_SLOP * pixel / 2 ) )
2527  {
2528  // Zone borders are very specific, so make them "small"
2529  area = MAX_SLOP * SEG::Square( pixel );
2530  }
2531  else if( item->Type() == PCB_VIA_T )
2532  {
2533  // Vias rarely hide other things, and we don't want them deferring to short track
2534  // segments underneath them -- so artificially reduce their size from πr² to 1.5r².
2535  area = SEG::Square( static_cast<PCB_VIA*>( item )->GetDrill() / 2 ) * 1.5;
2536  }
2537  else
2538  {
2539  try
2540  {
2541  area = FOOTPRINT::GetCoverageArea( item, aCollector );
2542  }
2543  catch( const ClipperLib::clipperException& e )
2544  {
2545  wxLogError( "A clipper exception %s was detected.", e.what() );
2546  }
2547  }
2548 
2549  itemsByArea.emplace_back( item, area );
2550  }
2551 
2552  std::sort( itemsByArea.begin(), itemsByArea.end(),
2553  []( const std::pair<BOARD_ITEM*, double>& lhs,
2554  const std::pair<BOARD_ITEM*, double>& rhs ) -> bool
2555  {
2556  return lhs.second < rhs.second;
2557  } );
2558 
2559  bool rejecting = false;
2560 
2561  for( int i = 1; i < (int) itemsByArea.size(); ++i )
2562  {
2563  if( itemsByArea[i].second > itemsByArea[i-1].second * sizeRatio )
2564  rejecting = true;
2565 
2566  if( rejecting )
2567  rejected.insert( itemsByArea[i].first );
2568  }
2569 
2570  // Special case: if a footprint is completely covered with other features then there's no
2571  // way to select it -- so we need to leave it in the list for user disambiguation.
2572  constexpr double maxCoverRatio = 0.70;
2573 
2574  for( int i = 0; i < aCollector.GetCount(); ++i )
2575  {
2576  if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aCollector[i] ) )
2577  {
2578  if( footprint->CoverageRatio( aCollector ) > maxCoverRatio )
2579  rejected.erase( footprint );
2580  }
2581  }
2582 
2583  // Hopefully we've now got what the user wanted.
2584  if( (unsigned) aCollector.GetCount() > rejected.size() ) // do not remove everything
2585  {
2586  for( BOARD_ITEM* item : rejected )
2587  aCollector.Transfer( item );
2588  }
2589 
2590  // Finally, what we are left with is a set of items of similar coverage area. We now reject
2591  // any that are not on the active layer, to reduce the number of disambiguation menus shown.
2592  // If the user wants to force-disambiguate, they can either switch layers or use the modifier
2593  // key to force the menu.
2594  if( aCollector.GetCount() > 1 )
2595  {
2596  bool haveItemOnActive = false;
2597  rejected.clear();
2598 
2599  for( int i = 0; i < aCollector.GetCount(); ++i )
2600  {
2601  if( !aCollector[i]->IsOnLayer( activeLayer ) )
2602  rejected.insert( aCollector[i] );
2603  else
2604  haveItemOnActive = true;
2605  }
2606 
2607  if( haveItemOnActive )
2608  for( BOARD_ITEM* item : rejected )
2609  aCollector.Transfer( item );
2610  }
2611 }
2612 
2613 
2615  bool aMultiselect ) const
2616 {
2617  std::unordered_set<BOARD_ITEM*> toAdd;
2618 
2619  // Set TEMP_SELECTED on all parents which are included in the GENERAL_COLLECTOR. This
2620  // algorithm is O3n, whereas checking for the parent inclusion could potentially be On^2.
2621  for( int j = 0; j < aCollector.GetCount(); j++ )
2622  {
2623  if( aCollector[j]->GetParent() )
2624  aCollector[j]->GetParent()->ClearFlags( TEMP_SELECTED );
2625  }
2626 
2627  if( aMultiselect )
2628  {
2629  for( int j = 0; j < aCollector.GetCount(); j++ )
2630  aCollector[j]->SetFlags( TEMP_SELECTED );
2631  }
2632 
2633  for( int j = 0; j < aCollector.GetCount(); )
2634  {
2635  BOARD_ITEM* item = aCollector[j];
2636  BOARD_ITEM* parent = item->GetParent();
2637  BOARD_ITEM* start = item;
2638 
2639  if( !m_isFootprintEditor && parent && parent->Type() == PCB_FOOTPRINT_T )
2640  start = parent;
2641 
2642  // If any element is a member of a group, replace those elements with the top containing
2643  // group.
2645 
2646  if( aTop )
2647  {
2648  if( aTop != item )
2649  {
2650  toAdd.insert( aTop );
2651  aTop->SetFlags( TEMP_SELECTED );
2652 
2653  aCollector.Remove( item );
2654  continue;
2655  }
2656  }
2657  else if( m_enteredGroup
2659  {
2660  // If a group is entered, disallow selections of objects outside the group.
2661  aCollector.Remove( item );
2662  continue;
2663  }
2664 
2665  // Footprints are a bit easier as they can't be nested.
2666  if( parent && ( parent->GetFlags() & TEMP_SELECTED ) )
2667  {
2668  // Remove children of selected items
2669  aCollector.Remove( item );
2670  continue;
2671  }
2672 
2673  ++j;
2674  }
2675 
2676  for( BOARD_ITEM* item : toAdd )
2677  {
2678  if( !aCollector.HasItem( item ) )
2679  aCollector.Append( item );
2680  }
2681 }
2682 
2683 
2685 {
2686  std::set<BOARD_ITEM*> to_add;
2687 
2688  // Iterate from the back so we don't have to worry about removals.
2689  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
2690  {
2691  BOARD_ITEM* item = aCollector[i];
2692 
2693  if( !IsFootprintEditor() && item->Type() == PCB_PAD_T
2694  && !frame()->Settings().m_AllowFreePads )
2695  {
2696  if( !aCollector.HasItem( item->GetParent() ) )
2697  to_add.insert( item->GetParent() );
2698 
2699  aCollector.Remove( item );
2700  }
2701  }
2702 
2703  for( BOARD_ITEM* item : to_add )
2704  aCollector.Append( item );
2705 }
2706 
2707 
2709 {
2710  // Iterate from the back so we don't have to worry about removals.
2711  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
2712  {
2713  BOARD_ITEM* item = aCollector[i];
2714 
2715  if( item->Type() == PCB_MARKER_T )
2716  aCollector.Remove( item );
2717  }
2718 }
2719 
2720 
2722 {
2723  getView()->Update( &m_selection );
2725 
2726  return 0;
2727 }
2728 
2729 
2731 {
2732  ACTION_MENU* actionMenu = aEvent.Parameter<ACTION_MENU*>();
2733  CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( actionMenu );
2734 
2735  if( conditionalMenu )
2736  conditionalMenu->Evaluate( m_selection );
2737 
2738  if( actionMenu )
2739  actionMenu->UpdateAll();
2740 
2741  return 0;
2742 }
2743 
2744 
2746 {
2748 
2752 
2758 
2768 
2770 
2772 }
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:194
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:294
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:215
smd pads, front layer
Definition: layer_ids.h:198
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:204
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:733
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:226
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:206
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:199
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:295
int GetWidth() const
Definition: eda_rect.h:109
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:205
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
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).
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:429
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:227
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:1181
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:505
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:189
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:196
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:204
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 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:110
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:207
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:443
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:209
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:310
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
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER) const
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition: board_item.cpp:169
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:135
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:142
static TOOL_ACTION selectAll
Definition: actions.h:70
virtual EDA_RECT GetBoundingBox() const
Definition: selection.cpp:112
void selectAllItemsOnSheet(wxString &aSheetPath)
Select all items with the given sheet timestamp/UUID name (the 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