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 <tomasz.wlostowski@cern.ch>
7  * @author Maciej Suminski <maciej.suminski@cern.ch>
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  {
555  unhighlight( item, SELECTED, &m_selection );
556  }
557  }
558 
559  for( std::pair<EDA_ITEM* const, DISPOSITION> itemDisposition : itemDispositions )
560  {
561  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( itemDisposition.first );
562  DISPOSITION disposition = itemDisposition.second;
563 
564  if( disposition == AFTER )
565  {
566  highlight( item, SELECTED, &m_selection );
567  }
568  else if( disposition == BOTH )
569  {
570  // nothing to do
571  }
572  }
573 
575  }
576 
577  if( aConfirmLockedItems )
578  {
579  std::vector<BOARD_ITEM*> lockedItems;
580 
581  for( EDA_ITEM* item : m_selection )
582  {
583  BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
584 
585  if( boardItem->Type() == PCB_GROUP_T )
586  {
587  PCB_GROUP* group = static_cast<PCB_GROUP*>( boardItem );
588  bool lockedDescendant = false;
589 
590  group->RunOnDescendants(
591  [&lockedDescendant]( BOARD_ITEM* child )
592  {
593  if( child->IsLocked() )
594  lockedDescendant = true;
595  } );
596 
597  if( lockedDescendant )
598  lockedItems.push_back( group );
599  }
600  else if( boardItem->IsLocked() )
601  {
602  lockedItems.push_back( boardItem );
603  }
604  }
605 
606  if( !lockedItems.empty() )
607  {
608  DIALOG_LOCKED_ITEMS_QUERY dlg( frame(), lockedItems.size() );
609 
610  switch( dlg.ShowModal() )
611  {
612  case wxID_OK:
613  // remove locked items from selection
614  for( BOARD_ITEM* item : lockedItems )
615  unselect( item );
616 
617  break;
618 
619  case wxID_CANCEL:
620  // cancel operation
621  ClearSelection();
622  break;
623 
624  case wxID_APPLY:
625  // continue with operation with current selection
626  break;
627  }
628  }
629  }
630 
631  return m_selection;
632 }
633 
634 
636 {
637  GENERAL_COLLECTORS_GUIDE guide( board()->GetVisibleLayers(),
638  (PCB_LAYER_ID) view()->GetTopLayer(), view() );
639 
640  bool padsDisabled = !board()->IsElementVisible( LAYER_PADS );
641 
642  // account for the globals
643  guide.SetIgnoreMTextsMarkedNoShow( ! board()->IsElementVisible( LAYER_MOD_TEXT_INVISIBLE ) );
644  guide.SetIgnoreMTextsOnBack( ! board()->IsElementVisible( LAYER_MOD_TEXT_BK ) );
645  guide.SetIgnoreMTextsOnFront( ! board()->IsElementVisible( LAYER_MOD_TEXT_FR ) );
646  guide.SetIgnoreModulesOnBack( ! board()->IsElementVisible( LAYER_MOD_BK ) );
647  guide.SetIgnoreModulesOnFront( ! board()->IsElementVisible( LAYER_MOD_FR ) );
648  guide.SetIgnorePadsOnBack( padsDisabled || ! board()->IsElementVisible( LAYER_PAD_BK ) );
649  guide.SetIgnorePadsOnFront( padsDisabled || ! board()->IsElementVisible( LAYER_PAD_FR ) );
650  guide.SetIgnoreThroughHolePads( padsDisabled || ! board()->IsElementVisible( LAYER_PADS_TH ) );
651  guide.SetIgnoreModulesVals( ! board()->IsElementVisible( LAYER_MOD_VALUES ) );
652  guide.SetIgnoreModulesRefs( ! board()->IsElementVisible( LAYER_MOD_REFERENCES ) );
653  guide.SetIgnoreThroughVias( ! board()->IsElementVisible( LAYER_VIAS ) );
654  guide.SetIgnoreBlindBuriedVias( ! board()->IsElementVisible( LAYER_VIAS ) );
655  guide.SetIgnoreMicroVias( ! board()->IsElementVisible( LAYER_VIAS ) );
656  guide.SetIgnoreTracks( ! board()->IsElementVisible( LAYER_TRACKS ) );
657 
658  return guide;
659 }
660 
661 
662 bool PCB_SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag,
663  bool* aSelectionCancelledFlag,
664  CLIENT_SELECTION_FILTER aClientFilter )
665 {
667  GENERAL_COLLECTOR collector;
668  const PCB_DISPLAY_OPTIONS& displayOpts = m_frame->GetDisplayOptions();
669 
671 
672  if( m_enteredGroup && !m_enteredGroup->GetBoundingBox().Contains( (wxPoint) aWhere ) )
673  ExitGroup();
674 
677  (wxPoint) aWhere, guide );
678 
679  // Remove unselectable items
680  for( int i = collector.GetCount() - 1; i >= 0; --i )
681  {
682  if( !Selectable( collector[ i ] ) || ( aOnDrag && collector[i]->IsLocked() ) )
683  collector.Remove( i );
684  }
685 
687 
688  // Allow the client to do tool- or action-specific filtering to see if we can get down
689  // to a single item
690  if( aClientFilter )
691  aClientFilter( aWhere, collector, this );
692 
693  FilterCollectorForHierarchy( collector, false );
694 
695  // Apply the stateful filter
696  FilterCollectedItems( collector, false );
697 
698  // Apply some ugly heuristics to avoid disambiguation menus whenever possible
699  if( collector.GetCount() > 1 && !m_skip_heuristics )
700  GuessSelectionCandidates( collector, aWhere );
701 
702  // If still more than one item we're going to have to ask the user.
703  if( collector.GetCount() > 1 )
704  {
705  if( aOnDrag )
707 
708  if( !doSelectionMenu( &collector ) )
709  {
710  if( aSelectionCancelledFlag )
711  *aSelectionCancelledFlag = true;
712 
713  return false;
714  }
715  }
716 
717  bool anyAdded = false;
718  bool anySubtracted = false;
719 
721  {
722  if( m_selection.GetSize() > 0 )
723  {
724  ClearSelection( true /*quiet mode*/ );
725  anySubtracted = true;
726  }
727  }
728 
729  if( collector.GetCount() > 0 )
730  {
731  for( int i = 0; i < collector.GetCount(); ++i )
732  {
733  if( m_subtractive || ( m_exclusive_or && collector[i]->IsSelected() ) )
734  {
735  unselect( collector[i] );
736  anySubtracted = true;
737  }
738  else
739  {
740  select( collector[i] );
741  anyAdded = true;
742  }
743  }
744  }
745 
746  if( anyAdded )
747  {
749  return true;
750  }
751  else if( anySubtracted )
752  {
754  return true;
755  }
756 
757  return false;
758 }
759 
760 
761 bool PCB_SELECTION_TOOL::selectCursor( bool aForceSelect, CLIENT_SELECTION_FILTER aClientFilter )
762 {
763  if( aForceSelect || m_selection.Empty() )
764  {
765  ClearSelection( true /*quiet mode*/ );
766  selectPoint( getViewControls()->GetCursorPosition( false ), false, nullptr, aClientFilter );
767  }
768 
769  return !m_selection.Empty();
770 }
771 
772 
774 {
775  bool cancelled = false; // Was the tool cancelled while it was running?
776  m_multiple = true; // Multiple selection mode is active
777  KIGFX::VIEW* view = getView();
778 
780  view->Add( &area );
781 
782  bool anyAdded = false;
783  bool anySubtracted = false;
784 
785  while( TOOL_EVENT* evt = Wait() )
786  {
787  int width = area.GetEnd().x - area.GetOrigin().x;
788 
789  /* Selection mode depends on direction of drag-selection:
790  * Left > Right : Select objects that are fully enclosed by selection
791  * Right > Left : Select objects that are crossed by selection
792  */
793  bool windowSelection = width >= 0 ? true : false;
794 
795  if( view->IsMirroredX() )
796  windowSelection = !windowSelection;
797 
800 
801  if( evt->IsCancelInteractive() || evt->IsActivate() )
802  {
803  cancelled = true;
804  break;
805  }
806 
807  if( evt->IsDrag( BUT_LEFT ) )
808  {
810  {
811  if( m_selection.GetSize() > 0 )
812  {
813  anySubtracted = true;
814  ClearSelection( true /*quiet mode*/ );
815  }
816  }
817 
818  // Start drawing a selection box
819  area.SetOrigin( evt->DragOrigin() );
820  area.SetEnd( evt->Position() );
823  area.SetExclusiveOr( false );
824 
825  view->SetVisible( &area, true );
826  view->Update( &area );
827  getViewControls()->SetAutoPan( true );
828  }
829 
830  if( evt->IsMouseUp( BUT_LEFT ) )
831  {
832  getViewControls()->SetAutoPan( false );
833 
834  // End drawing the selection box
835  view->SetVisible( &area, false );
836 
837  std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> candidates;
838  BOX2I selectionBox = area.ViewBBox();
839  view->Query( selectionBox, candidates ); // Get the list of nearby items
840 
841  int height = area.GetEnd().y - area.GetOrigin().y;
842 
843  // Construct an EDA_RECT to determine BOARD_ITEM selection
844  EDA_RECT selectionRect( (wxPoint) area.GetOrigin(), wxSize( width, height ) );
845 
846  selectionRect.Normalize();
847 
848  GENERAL_COLLECTOR collector;
849 
850  for( auto it = candidates.begin(), it_end = candidates.end(); it != it_end; ++it )
851  {
852  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it->first );
853 
854  if( item && Selectable( item ) && item->HitTest( selectionRect, windowSelection ) )
855  collector.Append( item );
856  }
857 
858  // Apply the stateful filter
859  FilterCollectedItems( collector, true );
860 
861  FilterCollectorForHierarchy( collector, true );
862 
863  for( EDA_ITEM* i : collector )
864  {
865  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
866 
867  if( m_subtractive || ( m_exclusive_or && item->IsSelected() ) )
868  {
869  unselect( item );
870  anySubtracted = true;
871  }
872  else
873  {
874  select( item );
875  anyAdded = true;
876  }
877  }
878 
879  m_selection.SetIsHover( false );
880 
881  // Inform other potentially interested tools
882  if( anyAdded )
884  else if( anySubtracted )
886 
887  break; // Stop waiting for events
888  }
889  }
890 
891  getViewControls()->SetAutoPan( false );
892 
893  // Stop drawing the selection box
894  view->Remove( &area );
895  m_multiple = false; // Multiple selection mode is inactive
896 
897  if( !cancelled )
899 
901 
902  return cancelled;
903 }
904 
905 
907 {
908  m_skip_heuristics = true;
910  m_skip_heuristics = false;
911 
912  return 0;
913 }
914 
915 
916 
918 {
920 
921  selectCursor( false, aClientFilter );
922 
923  return 0;
924 }
925 
926 
928 {
929  ClearSelection();
930 
931  return 0;
932 }
933 
934 
936 {
937  std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
938 
939  if( items )
940  {
941  // Perform individual selection of each item before processing the event.
942  for( BOARD_ITEM* item : *items )
943  select( item );
944 
946  }
947 
948  return 0;
949 }
950 
951 
953 {
954  AddItemToSel( aEvent.Parameter<BOARD_ITEM*>() );
955  return 0;
956 }
957 
958 
960 {
961  KIGFX::VIEW* view = getView();
962 
963  // hold all visible items
964  std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
965 
966  // Filter the view items based on the selection box
967  BOX2I selectionBox;
968 
969  // Intermediate step to allow filtering against hierarchy
970  GENERAL_COLLECTOR collection;
971 
972  selectionBox.SetMaximum();
973  view->Query( selectionBox, selectedItems ); // Get the list of selected items
974 
975  for( const KIGFX::VIEW::LAYER_ITEM_PAIR& item_pair : selectedItems )
976  {
977  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( item_pair.first );
978 
979  if( !item || !Selectable( item ) || !itemPassesFilter( item, true ) )
980  continue;
981 
982  collection.Append( item );
983  }
984 
985  FilterCollectorForHierarchy( collection, true );
986 
987  for( EDA_ITEM* item : collection )
988  select( static_cast<BOARD_ITEM*>( item ) );
989 
991 
992  return 0;
993 }
994 
995 
996 void PCB_SELECTION_TOOL::AddItemToSel( BOARD_ITEM* aItem, bool aQuietMode )
997 {
998  if( aItem )
999  {
1000  select( aItem );
1001 
1002  // Inform other potentially interested tools
1003  if( !aQuietMode )
1005  }
1006 }
1007 
1008 
1010 {
1011  std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
1012 
1013  if( items )
1014  {
1015  // Perform individual unselection of each item before processing the event
1016  for( auto item : *items )
1017  unselect( item );
1018 
1020  }
1021 
1022  return 0;
1023 }
1024 
1025 
1027 {
1028  RemoveItemFromSel( aEvent.Parameter<BOARD_ITEM*>() );
1029  return 0;
1030 }
1031 
1032 
1033 void PCB_SELECTION_TOOL::RemoveItemFromSel( BOARD_ITEM* aItem, bool aQuietMode )
1034 {
1035  if( aItem )
1036  {
1037  unselect( aItem );
1038 
1039  if( !aQuietMode )
1040  {
1041  // Inform other potentially interested tools
1043  }
1044  }
1045 }
1046 
1047 
1049 {
1050  highlight( aItem, BRIGHTENED );
1051 }
1052 
1053 
1055 {
1056  unhighlight( aItem, BRIGHTENED );
1057 }
1058 
1059 
1061  PCB_SELECTION_TOOL* sTool )
1062 {
1063  // Narrow the collection down to a single BOARD_CONNECTED_ITEM for each represented net.
1064  // All other items types are removed.
1065  std::set<int> representedNets;
1066 
1067  for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
1068  {
1069  BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( aCollector[i] );
1070  if( !item )
1071  aCollector.Remove( i );
1072  else if ( representedNets.count( item->GetNetCode() ) )
1073  aCollector.Remove( i );
1074  else
1075  representedNets.insert( item->GetNetCode() );
1076  }
1077 }
1078 
1079 
1081 {
1082  unsigned initialCount = 0;
1083 
1084  for( auto item : m_selection.GetItems() )
1085  {
1086  if( dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
1087  initialCount++;
1088  }
1089 
1090  if( initialCount == 0 )
1092 
1093  for( STOP_CONDITION stopCondition : { STOP_AT_JUNCTION, STOP_AT_PAD, STOP_NEVER } )
1094  {
1095  // copy the selection, since we're going to iterate and modify
1096  std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
1097 
1098  for( EDA_ITEM* item : selectedItems )
1099  item->ClearTempFlags();
1100 
1101  for( EDA_ITEM* item : selectedItems )
1102  {
1103  PCB_TRACK* trackItem = dynamic_cast<PCB_TRACK*>( item );
1104 
1105  // Track items marked SKIP_STRUCT have already been visited
1106  if( trackItem && !( trackItem->GetFlags() & SKIP_STRUCT ) )
1107  selectConnectedTracks( *trackItem, stopCondition );
1108  }
1109 
1110  if( m_selection.GetItems().size() > initialCount )
1111  break;
1112  }
1113 
1114  // Inform other potentially interested tools
1115  if( m_selection.Size() > 0 )
1117 
1118  return 0;
1119 }
1120 
1121 
1123  STOP_CONDITION aStopCondition )
1124 {
1125  constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, EOT };
1126 
1127  auto connectivity = board()->GetConnectivity();
1128  auto connectedItems = connectivity->GetConnectedItems( &aStartItem, types, true );
1129 
1130  std::map<wxPoint, std::vector<PCB_TRACK*>> trackMap;
1131  std::map<wxPoint, PCB_VIA*> viaMap;
1132  std::map<wxPoint, PAD*> padMap;
1133 
1134  // Build maps of connected items
1135  for( BOARD_CONNECTED_ITEM* item : connectedItems )
1136  {
1137  switch( item->Type() )
1138  {
1139  case PCB_ARC_T:
1140  case PCB_TRACE_T:
1141  {
1142  PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
1143  trackMap[ track->GetStart() ].push_back( track );
1144  trackMap[ track->GetEnd() ].push_back( track );
1145  break;
1146  }
1147 
1148  case PCB_VIA_T:
1149  {
1150  PCB_VIA* via = static_cast<PCB_VIA*>( item );
1151  viaMap[ via->GetStart() ] = via;
1152  break;
1153  }
1154 
1155  case PCB_PAD_T:
1156  {
1157  PAD* pad = static_cast<PAD*>( item );
1158  padMap[ pad->GetPosition() ] = pad;
1159  break;
1160  }
1161 
1162  default:
1163  break;
1164  }
1165 
1166  item->SetState( SKIP_STRUCT, false );
1167  }
1168 
1169  std::vector<wxPoint> activePts;
1170 
1171  // Set up the initial active points
1172  switch( aStartItem.Type() )
1173  {
1174  case PCB_ARC_T:
1175  case PCB_TRACE_T:
1176  activePts.push_back( static_cast<PCB_TRACK*>( &aStartItem )->GetStart() );
1177  activePts.push_back( static_cast<PCB_TRACK*>( &aStartItem )->GetEnd() );
1178  break;
1179 
1180  case PCB_VIA_T:
1181  activePts.push_back( static_cast<PCB_TRACK*>( &aStartItem )->GetStart() );
1182  break;
1183 
1184  case PCB_PAD_T:
1185  activePts.push_back( aStartItem.GetPosition() );
1186  break;
1187 
1188  default:
1189  break;
1190  }
1191 
1192  bool expand = true;
1193 
1194  // Iterative push from all active points
1195  while( expand )
1196  {
1197  expand = false;
1198 
1199  for( int i = activePts.size() - 1; i >= 0; --i )
1200  {
1201  wxPoint pt = activePts[i];
1202  size_t pt_count = trackMap[ pt ].size() + viaMap.count( pt );
1203 
1204  if( pt_count > 2 && aStopCondition == STOP_AT_JUNCTION )
1205  {
1206  activePts.erase( activePts.begin() + i );
1207  continue;
1208  }
1209 
1210  if( padMap.count( pt ) && aStopCondition != STOP_NEVER )
1211  {
1212  activePts.erase( activePts.begin() + i );
1213  continue;
1214  }
1215 
1216  for( PCB_TRACK* track : trackMap[ pt ] )
1217  {
1218  if( track->GetState( SKIP_STRUCT ) )
1219  continue;
1220 
1221  track->SetState( SKIP_STRUCT, true );
1222  select( track );
1223 
1224  if( track->GetStart() == pt )
1225  activePts.push_back( track->GetEnd() );
1226  else
1227  activePts.push_back( track->GetStart() );
1228 
1229  expand = true;
1230  }
1231 
1232  if( viaMap.count( pt ) && !viaMap[ pt ]->IsSelected()
1233  && aStopCondition != STOP_AT_JUNCTION )
1234  select( viaMap[ pt ] );
1235 
1236  activePts.erase( activePts.begin() + i );
1237  }
1238  }
1239 }
1240 
1241 
1242 void PCB_SELECTION_TOOL::selectAllItemsOnNet( int aNetCode, bool aSelect )
1243 {
1244  constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, EOT };
1245  auto connectivity = board()->GetConnectivity();
1246 
1247  for( BOARD_CONNECTED_ITEM* item : connectivity->GetNetItems( aNetCode, types ) )
1248  {
1249  if( itemPassesFilter( item, true ) )
1250  aSelect ? select( item ) : unselect( item );
1251  }
1252 }
1253 
1254 
1256 {
1257  bool select = aEvent.IsAction( &PCB_ACTIONS::selectNet );
1258 
1259  // If we've been passed an argument, just select that netcode1
1260  int netcode = aEvent.Parameter<intptr_t>();
1261 
1262  if( netcode > 0 )
1263  {
1264  selectAllItemsOnNet( netcode, select );
1265  return 0;
1266  }
1267 
1268  if( !selectCursor() )
1269  return 0;
1270 
1271  // copy the selection, since we're going to iterate and modify
1272  auto selection = m_selection.GetItems();
1273 
1274  for( EDA_ITEM* i : selection )
1275  {
1276  BOARD_CONNECTED_ITEM* connItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( i );
1277 
1278  if( connItem )
1279  selectAllItemsOnNet( connItem->GetNetCode(), select );
1280  }
1281 
1282  // Inform other potentially interested tools
1283  if( m_selection.Size() > 0 )
1285 
1286  return 0;
1287 }
1288 
1289 
1290 void PCB_SELECTION_TOOL::selectAllItemsOnSheet( wxString& aSheetPath )
1291 {
1292  std::list<FOOTPRINT*> footprintList;
1293 
1294  // store all footprints that are on that sheet path
1295  for( FOOTPRINT* footprint : board()->Footprints() )
1296  {
1297  if( footprint == nullptr )
1298  continue;
1299 
1300  wxString footprint_path = footprint->GetPath().AsString().BeforeLast('/');
1301 
1302  if( aSheetPath.IsEmpty() )
1303  aSheetPath += '/';
1304 
1305  if( footprint_path == aSheetPath )
1306  footprintList.push_back( footprint );
1307  }
1308 
1309  // Generate a list of all pads, and of all nets they belong to.
1310  std::list<int> netcodeList;
1311  std::list<PAD*> padList;
1312 
1313  for( FOOTPRINT* footprint : footprintList )
1314  {
1315  for( PAD* pad : footprint->Pads() )
1316  {
1317  if( pad->IsConnected() )
1318  {
1319  netcodeList.push_back( pad->GetNetCode() );
1320  padList.push_back( pad );
1321  }
1322  }
1323  }
1324 
1325  // remove all duplicates
1326  netcodeList.sort();
1327  netcodeList.unique();
1328 
1329  for( PAD* pad : padList )
1331 
1332  // now we need to find all footprints that are connected to each of these nets then we need
1333  // to determine if these footprints are in the list of footprints belonging to this sheet
1334  std::list<int> removeCodeList;
1335  constexpr KICAD_T padType[] = { PCB_PAD_T, EOT };
1336 
1337  for( int netCode : netcodeList )
1338  {
1339  for( BOARD_CONNECTED_ITEM* mitem : board()->GetConnectivity()->GetNetItems( netCode,
1340  padType ) )
1341  {
1342  if( mitem->Type() == PCB_PAD_T && !alg::contains( footprintList, mitem->GetParent() ) )
1343  {
1344  // if we cannot find the footprint of the pad in the footprintList then we can
1345  // assume that that footprint is not located in the same schematic, therefore
1346  // invalidate this netcode.
1347  removeCodeList.push_back( netCode );
1348  break;
1349  }
1350  }
1351  }
1352 
1353  // remove all duplicates
1354  removeCodeList.sort();
1355  removeCodeList.unique();
1356 
1357  for( int removeCode : removeCodeList )
1358  {
1359  netcodeList.remove( removeCode );
1360  }
1361 
1362  std::list<BOARD_CONNECTED_ITEM*> localConnectionList;
1363  constexpr KICAD_T trackViaType[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, EOT };
1364 
1365  for( int netCode : netcodeList )
1366  {
1367  for( BOARD_CONNECTED_ITEM* item : board()->GetConnectivity()->GetNetItems( netCode,
1368  trackViaType ) )
1369  localConnectionList.push_back( item );
1370  }
1371 
1372  for( BOARD_ITEM* i : footprintList )
1373  {
1374  if( i != nullptr )
1375  select( i );
1376  }
1377 
1378  for( BOARD_CONNECTED_ITEM* i : localConnectionList )
1379  {
1380  if( i != nullptr )
1381  select( i );
1382  }
1383 }
1384 
1385 
1387 {
1388  // Should recalculate the view to zoom in on the selection.
1389  auto selectionBox = m_selection.GetBoundingBox();
1390  auto view = getView();
1391 
1392  VECTOR2D screenSize = view->ToWorld( m_frame->GetCanvas()->GetClientSize(), false );
1393  screenSize.x = std::max( 10.0, screenSize.x );
1394  screenSize.y = std::max( 10.0, screenSize.y );
1395 
1396  if( selectionBox.GetWidth() != 0 || selectionBox.GetHeight() != 0 )
1397  {
1398  VECTOR2D vsize = selectionBox.GetSize();
1399  double scale = view->GetScale() / std::max( fabs( vsize.x / screenSize.x ),
1400  fabs( vsize.y / screenSize.y ) );
1401  view->SetScale( scale );
1402  view->SetCenter( selectionBox.Centre() );
1403  view->Add( &m_selection );
1404  }
1405 
1407 }
1408 
1409 
1411 {
1412  ClearSelection( true /*quiet mode*/ );
1413  wxString sheetPath = *aEvent.Parameter<wxString*>();
1414 
1415  selectAllItemsOnSheet( sheetPath );
1416 
1417  zoomFitSelection();
1418 
1419  if( m_selection.Size() > 0 )
1421 
1422  return 0;
1423 }
1424 
1425 
1427 {
1428  if( !selectCursor( true ) )
1429  return 0;
1430 
1431  // this function currently only supports footprints since they are only on one sheet.
1432  auto item = m_selection.Front();
1433 
1434  if( !item )
1435  return 0;
1436 
1437  if( item->Type() != PCB_FOOTPRINT_T )
1438  return 0;
1439 
1440  FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( item );
1441 
1442  if( footprint->GetPath().empty() )
1443  return 0;
1444 
1445  ClearSelection( true /*quiet mode*/ );
1446 
1447  // get the sheet path only.
1448  wxString sheetPath = footprint->GetPath().AsString().BeforeLast( '/' );
1449 
1450  if( sheetPath.IsEmpty() )
1451  sheetPath += '/';
1452 
1453  selectAllItemsOnSheet( sheetPath );
1454 
1455  // Inform other potentially interested tools
1456  if( m_selection.Size() > 0 )
1458 
1459  return 0;
1460 }
1461 
1462 
1464 {
1465  bool cleared = false;
1466 
1467  if( m_selection.GetSize() > 0 )
1468  {
1469  // Don't fire an event now; most of the time it will be redundant as we're about to
1470  // fire a SelectedEvent.
1471  cleared = true;
1472  ClearSelection( true /*quiet mode*/ );
1473  }
1474 
1475  if( aItem )
1476  {
1477  select( aItem );
1478  m_frame->FocusOnLocation( aItem->GetPosition() );
1479 
1480  // Inform other potentially interested tools
1482  }
1483  else if( cleared )
1484  {
1486  }
1487 
1489 }
1490 
1491 
1497 static bool itemIsIncludedByFilter( const BOARD_ITEM& aItem, const BOARD& aBoard,
1498  const DIALOG_FILTER_SELECTION::OPTIONS& aFilterOptions )
1499 {
1500  bool include = true;
1501  const PCB_LAYER_ID layer = aItem.GetLayer();
1502 
1503  // if the item needs to be checked against the options
1504  if( include )
1505  {
1506  switch( aItem.Type() )
1507  {
1508  case PCB_FOOTPRINT_T:
1509  {
1510  const FOOTPRINT& footprint = static_cast<const FOOTPRINT&>( aItem );
1511 
1512  include = aFilterOptions.includeModules;
1513 
1514  if( include && !aFilterOptions.includeLockedModules )
1515  include = !footprint.IsLocked();
1516 
1517  break;
1518  }
1519  case PCB_TRACE_T:
1520  case PCB_ARC_T:
1521  include = aFilterOptions.includeTracks;
1522  break;
1523 
1524  case PCB_VIA_T:
1525  include = aFilterOptions.includeVias;
1526  break;
1527 
1528  case PCB_FP_ZONE_T:
1529  case PCB_ZONE_T:
1530  include = aFilterOptions.includeZones;
1531  break;
1532 
1533  case PCB_SHAPE_T:
1534  case PCB_TARGET_T:
1535  case PCB_DIM_ALIGNED_T:
1536  case PCB_DIM_CENTER_T:
1537  case PCB_DIM_ORTHOGONAL_T:
1538  case PCB_DIM_LEADER_T:
1539  if( layer == Edge_Cuts )
1540  include = aFilterOptions.includeBoardOutlineLayer;
1541  else
1542  include = aFilterOptions.includeItemsOnTechLayers;
1543  break;
1544 
1545  case PCB_FP_TEXT_T:
1546  case PCB_TEXT_T:
1547  include = aFilterOptions.includePcbTexts;
1548  break;
1549 
1550  default:
1551  // no filtering, just select it
1552  break;
1553  }
1554  }
1555 
1556  return include;
1557 }
1558 
1559 
1561 {
1562  const BOARD& board = *getModel<BOARD>();
1563  DIALOG_FILTER_SELECTION::OPTIONS& opts = m_priv->m_filterOpts;
1564  DIALOG_FILTER_SELECTION dlg( m_frame, opts );
1565 
1566  const int cmd = dlg.ShowModal();
1567 
1568  if( cmd != wxID_OK )
1569  return 0;
1570 
1571  // copy current selection
1572  std::deque<EDA_ITEM*> selection = m_selection.GetItems();
1573 
1574  ClearSelection( true /*quiet mode*/ );
1575 
1576  // re-select items from the saved selection according to the dialog options
1577  for( EDA_ITEM* i : selection )
1578  {
1579  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
1580  bool include = itemIsIncludedByFilter( *item, board, opts );
1581 
1582  if( include )
1583  select( item );
1584  }
1585 
1587 
1588  return 0;
1589 }
1590 
1591 
1592 void PCB_SELECTION_TOOL::FilterCollectedItems( GENERAL_COLLECTOR& aCollector, bool aMultiSelect )
1593 {
1594  if( aCollector.GetCount() == 0 )
1595  return;
1596 
1597  std::set<BOARD_ITEM*> rejected;
1598 
1599  for( EDA_ITEM* i : aCollector )
1600  {
1601  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
1602 
1603  if( !itemPassesFilter( item, aMultiSelect ) )
1604  rejected.insert( item );
1605  }
1606 
1607  for( BOARD_ITEM* item : rejected )
1608  aCollector.Remove( item );
1609 }
1610 
1611 
1612 bool PCB_SELECTION_TOOL::itemPassesFilter( BOARD_ITEM* aItem, bool aMultiSelect )
1613 {
1614  if( !m_filter.lockedItems )
1615  {
1616  if( aItem->IsLocked() || ( aItem->GetParent() && aItem->GetParent()->IsLocked() ) )
1617  {
1618  if( aItem->Type() == PCB_PAD_T && !aMultiSelect )
1619  {
1620  // allow a single pad to be selected -- there are a lot of operations that
1621  // require this so we allow this one inconsistency
1622  }
1623  else
1624  {
1625  return false;
1626  }
1627  }
1628  }
1629 
1630  switch( aItem->Type() )
1631  {
1632  case PCB_FOOTPRINT_T:
1633  if( !m_filter.footprints )
1634  return false;
1635 
1636  break;
1637 
1638  case PCB_PAD_T:
1639  if( !m_filter.pads )
1640  return false;
1641 
1642  break;
1643 
1644  case PCB_TRACE_T:
1645  case PCB_ARC_T:
1646  if( !m_filter.tracks )
1647  return false;
1648 
1649  break;
1650 
1651  case PCB_VIA_T:
1652  if( !m_filter.vias )
1653  return false;
1654 
1655  break;
1656 
1657  case PCB_FP_ZONE_T:
1658  case PCB_ZONE_T:
1659  {
1660  ZONE* zone = static_cast<ZONE*>( aItem );
1661 
1662  if( ( !m_filter.zones && !zone->GetIsRuleArea() )
1663  || ( !m_filter.keepouts && zone->GetIsRuleArea() ) )
1664  {
1665  return false;
1666  }
1667 
1668  break;
1669  }
1670 
1671  case PCB_FP_SHAPE_T:
1672  case PCB_SHAPE_T:
1673  case PCB_TARGET_T:
1674  if( !m_filter.graphics )
1675  return false;
1676 
1677  break;
1678 
1679  case PCB_FP_TEXT_T:
1680  case PCB_TEXT_T:
1681  if( !m_filter.text )
1682  return false;
1683 
1684  break;
1685 
1686  case PCB_DIM_ALIGNED_T:
1687  case PCB_DIM_CENTER_T:
1688  case PCB_DIM_ORTHOGONAL_T:
1689  case PCB_DIM_LEADER_T:
1690  if( !m_filter.dimensions )
1691  return false;
1692 
1693  break;
1694 
1695  default:
1696  if( !m_filter.otherItems )
1697  return false;
1698  }
1699 
1700  return true;
1701 }
1702 
1703 
1704 void PCB_SELECTION_TOOL::ClearSelection( bool aQuietMode )
1705 {
1706  if( m_selection.Empty() )
1707  return;
1708 
1709  while( m_selection.GetSize() )
1710  unhighlight( static_cast<BOARD_ITEM*>( m_selection.Front() ), SELECTED, &m_selection );
1711 
1712  view()->Update( &m_selection );
1713 
1714  m_selection.SetIsHover( false );
1716 
1717  // Inform other potentially interested tools
1718  if( !aQuietMode )
1719  {
1722  }
1723 }
1724 
1725 
1727 {
1728  m_selection.Clear();
1729 
1730  bool enteredGroupFound = false;
1731 
1732  INSPECTOR_FUNC inspector =
1733  [&]( EDA_ITEM* item, void* testData )
1734  {
1735  if( item->IsSelected() )
1736  {
1737  EDA_ITEM* parent = item->GetParent();
1738 
1739  // Let selected parents handle their children.
1740  if( parent && parent->IsSelected() )
1741  return SEARCH_RESULT::CONTINUE;
1742 
1743  highlight( (BOARD_ITEM*) item, SELECTED, &m_selection );
1744  }
1745 
1746  if( item == m_enteredGroup )
1747  {
1748  item->SetFlags( ENTERED );
1749  enteredGroupFound = true;
1750  }
1751  else
1752  {
1753  item->ClearFlags( ENTERED );
1754  }
1755 
1756  return SEARCH_RESULT::CONTINUE;
1757  };
1758 
1761 
1762  if( !enteredGroupFound )
1763  {
1765  m_enteredGroup = nullptr;
1766  }
1767 }
1768 
1769 
1771 {
1772  GENERAL_COLLECTOR* collector = aEvent.Parameter<GENERAL_COLLECTOR*>();
1773 
1774  doSelectionMenu( collector );
1775 
1776  return 0;
1777 }
1778 
1779 
1781 {
1782  BOARD_ITEM* current = nullptr;
1783  PCB_SELECTION highlightGroup;
1784  bool selectAll = false;
1785  bool expandSelection = false;
1786 
1787  highlightGroup.SetLayer( LAYER_SELECT_OVERLAY );
1788  getView()->Add( &highlightGroup );
1789 
1790  do
1791  {
1793  if( expandSelection )
1794  aCollector->Combine();
1795 
1796  expandSelection = false;
1797 
1798  int limit = std::min( 9, aCollector->GetCount() );
1799  ACTION_MENU menu( true );
1800 
1801  for( int i = 0; i < limit; ++i )
1802  {
1803  wxString text;
1804  BOARD_ITEM* item = ( *aCollector )[i];
1806 
1807  wxString menuText = wxString::Format( "&%d. %s\t%d", i + 1, text, i + 1 );
1808  menu.Add( menuText, i + 1, item->GetMenuImage() );
1809  }
1810 
1811  menu.AppendSeparator();
1812  menu.Add( _( "Select &All\tA" ), limit + 1, BITMAPS::INVALID_BITMAP );
1813 
1814  if( !expandSelection && aCollector->HasAdditionalItems() )
1815  menu.Add( _( "&Expand Selection\tE" ), limit + 2, BITMAPS::INVALID_BITMAP );
1816 
1817  if( aCollector->m_MenuTitle.Length() )
1818  {
1819  menu.SetTitle( aCollector->m_MenuTitle );
1820  menu.SetIcon( BITMAPS::info );
1821  menu.DisplayTitle( true );
1822  }
1823  else
1824  {
1825  menu.DisplayTitle( false );
1826  }
1827 
1828  SetContextMenu( &menu, CMENU_NOW );
1829 
1830  while( TOOL_EVENT* evt = Wait() )
1831  {
1832  if( evt->Action() == TA_CHOICE_MENU_UPDATE )
1833  {
1834  if( selectAll )
1835  {
1836  for( int i = 0; i < aCollector->GetCount(); ++i )
1837  unhighlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
1838  }
1839  else if( current )
1840  {
1841  unhighlight( current, BRIGHTENED, &highlightGroup );
1842  }
1843 
1844  int id = *evt->GetCommandId();
1845 
1846  // User has pointed an item, so show it in a different way
1847  if( id > 0 && id <= limit )
1848  {
1849  current = ( *aCollector )[id - 1];
1850  highlight( current, BRIGHTENED, &highlightGroup );
1851  }
1852  else
1853  {
1854  current = nullptr;
1855  }
1856 
1857  // User has pointed on the "Select All" option
1858  if( id == limit + 1 )
1859  {
1860  for( int i = 0; i < aCollector->GetCount(); ++i )
1861  highlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
1862  selectAll = true;
1863  }
1864  else
1865  {
1866  selectAll = false;
1867  }
1868  }
1869  else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
1870  {
1871  if( selectAll )
1872  {
1873  for( int i = 0; i < aCollector->GetCount(); ++i )
1874  unhighlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
1875  }
1876  else if( current )
1877  {
1878  unhighlight( current, BRIGHTENED, &highlightGroup );
1879  }
1880 
1881  OPT<int> id = evt->GetCommandId();
1882 
1883  // User has selected the "Select All" option
1884  if( id == limit + 1 )
1885  {
1886  selectAll = true;
1887  current = nullptr;
1888  }
1889  else if( id == limit + 2 )
1890  {
1891  expandSelection = true;
1892  selectAll = false;
1893  current = nullptr;
1894  }
1895  // User has selected an item, so this one will be returned
1896  else if( id && ( *id > 0 ) && ( *id <= limit ) )
1897  {
1898  selectAll = false;
1899  current = ( *aCollector )[*id - 1];
1900  }
1901  else
1902  {
1903  selectAll = false;
1904  current = nullptr;
1905  }
1906  }
1907  else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
1908  {
1909  break;
1910  }
1911  }
1912  } while( expandSelection );
1913 
1914  getView()->Remove( &highlightGroup );
1915 
1916  if( selectAll )
1917  {
1918  return true;
1919  }
1920  else if( current )
1921  {
1922  aCollector->Empty();
1923  aCollector->Append( current );
1924  return true;
1925  }
1926 
1927  return false;
1928 }
1929 
1930 
1931 bool PCB_SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibilityOnly ) const
1932 {
1933  const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
1934 
1935  if( settings->GetHighContrast() )
1936  {
1937  std::set<unsigned int> activeLayers = settings->GetHighContrastLayers();
1938  bool onActiveLayer = false;
1939 
1940  for( unsigned int layer : activeLayers )
1941  {
1942  // NOTE: Only checking the regular layers (not GAL meta-layers)
1943  if( layer < PCB_LAYER_ID_COUNT && aItem->IsOnLayer( ToLAYER_ID( layer ) ) )
1944  {
1945  onActiveLayer = true;
1946  break;
1947  }
1948  }
1949 
1950  if( !onActiveLayer ) // We do not want to select items that are in the background
1951  return false;
1952  }
1953 
1954  if( aItem->Type() == PCB_FOOTPRINT_T )
1955  {
1956  // In footprint editor, we do not want to select the footprint itself.
1957  if( m_isFootprintEditor )
1958  return false;
1959 
1960  // Allow selection of footprints if some part of the footprint is visible.
1961  const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
1962 
1963  // If the footprint has no items except the reference and value fields, include the
1964  // footprint in the selections.
1965  if( footprint->GraphicalItems().empty()
1966  && footprint->Pads().empty()
1967  && footprint->Zones().empty() )
1968  return true;
1969 
1970  for( const BOARD_ITEM* item : footprint->GraphicalItems() )
1971  {
1972  if( Selectable( item, true ) )
1973  return true;
1974  }
1975 
1976  for( const PAD* pad : footprint->Pads() )
1977  {
1978  if( Selectable( pad, true ) )
1979  return true;
1980  }
1981 
1982  for( const ZONE* zone : footprint->Zones() )
1983  {
1984  if( Selectable( zone, true ) )
1985  return true;
1986  }
1987 
1988  return false;
1989  }
1990  else if( aItem->Type() == PCB_GROUP_T )
1991  {
1992  PCB_GROUP* group = const_cast<PCB_GROUP*>( static_cast<const PCB_GROUP*>( aItem ) );
1993 
1994  // Similar to logic for footprint, a group is selectable if any of its members are.
1995  // (This recurses.)
1996  for( BOARD_ITEM* item : group->GetItems() )
1997  {
1998  if( Selectable( item, true ) )
1999  return true;
2000  }
2001 
2002  return false;
2003  }
2004 
2005  const ZONE* zone = nullptr;
2006  const PCB_VIA* via = nullptr;
2007  const PAD* pad = nullptr;
2008 
2009  switch( aItem->Type() )
2010  {
2011  case PCB_ZONE_T:
2012  case PCB_FP_ZONE_T:
2013  if( !board()->IsElementVisible( LAYER_ZONES ) )
2014  return false;
2015 
2016  zone = static_cast<const ZONE*>( aItem );
2017 
2018  // A footprint zone is only selectable within the footprint editor
2019  if( zone->GetParent()
2020  && zone->GetParent()->Type() == PCB_FOOTPRINT_T
2022  && !checkVisibilityOnly )
2023  {
2024  return false;
2025  }
2026 
2027  // zones can exist on multiple layers!
2028  if( !( zone->GetLayerSet() & board()->GetVisibleLayers() ).any() )
2029  return false;
2030 
2031  break;
2032 
2033  case PCB_TRACE_T:
2034  case PCB_ARC_T:
2035  if( !board()->IsElementVisible( LAYER_TRACKS ) )
2036  return false;
2037 
2038  if( m_isFootprintEditor )
2039  {
2040  if( !view()->IsLayerVisible( aItem->GetLayer() ) )
2041  return false;
2042  }
2043  else
2044  {
2045  if( !board()->IsLayerVisible( aItem->GetLayer() ) )
2046  return false;
2047  }
2048 
2049  break;
2050 
2051  case PCB_VIA_T:
2052  if( !board()->IsElementVisible( LAYER_VIAS ) )
2053  return false;
2054 
2055  via = static_cast<const PCB_VIA*>( aItem );
2056 
2057  // For vias it is enough if only one of its layers is visible
2058  if( !( board()->GetVisibleLayers() & via->GetLayerSet() ).any() )
2059  return false;
2060 
2061  break;
2062 
2063  case PCB_FP_TEXT_T:
2064  if( m_isFootprintEditor )
2065  {
2066  if( !view()->IsLayerVisible( aItem->GetLayer() ) )
2067  return false;
2068  }
2069  else
2070  {
2071  if( !view()->IsVisible( aItem ) )
2072  return false;
2073 
2074  if( !board()->IsLayerVisible( aItem->GetLayer() ) )
2075  return false;
2076  }
2077 
2078  break;
2079 
2080  case PCB_FP_SHAPE_T:
2081  if( m_isFootprintEditor )
2082  {
2083  if( !view()->IsLayerVisible( aItem->GetLayer() ) )
2084  return false;
2085  }
2086  else
2087  {
2088  // Footprint shape selections are only allowed in footprint editor mode.
2089  if( !checkVisibilityOnly )
2090  return false;
2091 
2092  if( !board()->IsLayerVisible( aItem->GetLayer() ) )
2093  return false;
2094  }
2095 
2096  break;
2097 
2098  case PCB_PAD_T:
2099  // Multiple selection is only allowed in footprint editor mode. In pcbnew, you have to
2100  // select footprint subparts one by one, rather than with a drag selection. This is so
2101  // you can pick up items under an (unlocked) footprint without also moving the
2102  // footprint's sub-parts.
2103  if( !m_isFootprintEditor && !checkVisibilityOnly )
2104  {
2105  if( m_multiple )
2106  return false;
2107  }
2108 
2109  pad = static_cast<const PAD*>( aItem );
2110 
2111  if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
2112  {
2113  // Check render mode (from the Items tab) first
2114  if( !board()->IsElementVisible( LAYER_PADS_TH ) )
2115  return false;
2116 
2117  // A pad's hole is visible on every layer the pad is visible on plus many layers the
2118  // pad is not visible on -- so we only need to check for any visible hole layers.
2119  if( !( board()->GetVisibleLayers() & LSET::PhysicalLayersMask() ).any() )
2120  return false;
2121  }
2122  else
2123  {
2124  // Check render mode (from the Items tab) first
2125  if( pad->IsOnLayer( F_Cu ) && !board()->IsElementVisible( LAYER_PAD_FR ) )
2126  return false;
2127  else if( pad->IsOnLayer( B_Cu ) && !board()->IsElementVisible( LAYER_PAD_BK ) )
2128  return false;
2129 
2130  if( !( pad->GetLayerSet() & board()->GetVisibleLayers() ).any() )
2131  return false;
2132  }
2133 
2134  break;
2135 
2136  // These are not selectable
2137  case PCB_NETINFO_T:
2138  case NOT_USED:
2139  case TYPE_NOT_INIT:
2140  return false;
2141 
2142  default: // Suppress warnings
2143  break;
2144  }
2145 
2146  return aItem->ViewGetLOD( aItem->GetLayer(), view() ) < view()->GetScale();
2147 }
2148 
2149 
2151 {
2152  if( aItem->IsSelected() )
2153  return;
2154 
2155  if( aItem->Type() == PCB_PAD_T )
2156  {
2157  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem->GetParent() );
2158 
2159  if( m_selection.Contains( footprint ) )
2160  return;
2161  }
2162 
2163  highlight( aItem, SELECTED, &m_selection );
2164 }
2165 
2166 
2168 {
2169  unhighlight( aItem, SELECTED, &m_selection );
2170 }
2171 
2172 
2173 void PCB_SELECTION_TOOL::highlight( BOARD_ITEM* aItem, int aMode, PCB_SELECTION* aGroup )
2174 {
2175  if( aGroup )
2176  aGroup->Add( aItem );
2177 
2178  highlightInternal( aItem, aMode, aGroup != nullptr );
2179  view()->Update( aItem, KIGFX::REPAINT );
2180 
2181  // Many selections are very temporal and updating the display each time just
2182  // creates noise.
2183  if( aMode == BRIGHTENED )
2185 }
2186 
2187 
2188 void PCB_SELECTION_TOOL::highlightInternal( BOARD_ITEM* aItem, int aMode, bool aUsingOverlay )
2189 {
2190  if( aMode == SELECTED )
2191  aItem->SetSelected();
2192  else if( aMode == BRIGHTENED )
2193  aItem->SetBrightened();
2194 
2195  if( aUsingOverlay )
2196  view()->Hide( aItem, true ); // Hide the original item, so it is shown only on overlay
2197 
2198  if( aItem->Type() == PCB_FOOTPRINT_T )
2199  {
2200  static_cast<FOOTPRINT*>( aItem )->RunOnChildren(
2201  [&]( BOARD_ITEM* aChild )
2202  {
2203  highlightInternal( aChild, aMode, aUsingOverlay );
2204  } );
2205  }
2206  else if( aItem->Type() == PCB_GROUP_T )
2207  {
2208  static_cast<PCB_GROUP*>( aItem )->RunOnChildren(
2209  [&]( BOARD_ITEM* aChild )
2210  {
2211  highlightInternal( aChild, aMode, aUsingOverlay );
2212  } );
2213  }
2214 }
2215 
2216 
2218 {
2219  if( aGroup )
2220  aGroup->Remove( aItem );
2221 
2222  unhighlightInternal( aItem, aMode, aGroup != nullptr );
2223  view()->Update( aItem, KIGFX::REPAINT );
2224 
2225  // Many selections are very temporal and updating the display each time just creates noise.
2226  if( aMode == BRIGHTENED )
2228 }
2229 
2230 
2231 void PCB_SELECTION_TOOL::unhighlightInternal( BOARD_ITEM* aItem, int aMode, bool aUsingOverlay )
2232 {
2233  if( aMode == SELECTED )
2234  aItem->ClearSelected();
2235  else if( aMode == BRIGHTENED )
2236  aItem->ClearBrightened();
2237 
2238  if( aUsingOverlay )
2239  view()->Hide( aItem, false ); // // Restore original item visibility
2240 
2241  if( aItem->Type() == PCB_FOOTPRINT_T )
2242  {
2243  static_cast<FOOTPRINT*>( aItem )->RunOnChildren(
2244  [&]( BOARD_ITEM* aChild )
2245  {
2246  unhighlightInternal( aChild, aMode, aUsingOverlay );
2247  } );
2248  }
2249  else if( aItem->Type() == PCB_GROUP_T )
2250  {
2251  static_cast<PCB_GROUP*>( aItem )->RunOnChildren(
2252  [&]( BOARD_ITEM* aChild )
2253  {
2254  unhighlightInternal( aChild, aMode, aUsingOverlay );
2255  } );
2256  }
2257 }
2258 
2259 
2261 {
2263  GENERAL_COLLECTOR collector;
2264 
2265  // Since we're just double-checking, we want a considerably sloppier check than the initial
2266  // selection (for which most tools use 5 pixels). So we increase this to an effective 20
2267  // pixels by artificially inflating the value of a pixel by 4X.
2268  guide.SetOnePixelInIU( guide.OnePixelInIU() * 4 );
2269 
2272  (wxPoint) aPoint, guide );
2273 
2274  for( int i = collector.GetCount() - 1; i >= 0; --i )
2275  {
2276  BOARD_ITEM* item = collector[i];
2277 
2278  if( item->IsSelected() && item->HitTest( (wxPoint) aPoint, 5 * guide.OnePixelInIU() ) )
2279  return true;
2280  }
2281 
2282  return false;
2283 }
2284 
2285 
2286 int PCB_SELECTION_TOOL::hitTestDistance( const wxPoint& aWhere, BOARD_ITEM* aItem,
2287  int aMaxDistance ) const
2288 {
2289  BOX2D viewportD = getView()->GetViewport();
2290  BOX2I viewport( VECTOR2I( viewportD.GetPosition() ), VECTOR2I( viewportD.GetSize() ) );
2291  int distance = INT_MAX;
2292  SEG loc( aWhere, aWhere );
2293 
2294  switch( aItem->Type() )
2295  {
2296  case PCB_TEXT_T:
2297  {
2298  PCB_TEXT* text = static_cast<PCB_TEXT*>( aItem );
2299  text->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance );
2300  break;
2301  }
2302 
2303  case PCB_FP_TEXT_T:
2304  {
2305  FP_TEXT* text = static_cast<FP_TEXT*>( aItem );
2306  text->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance );
2307  break;
2308  }
2309 
2310  case PCB_ZONE_T:
2311  {
2312  ZONE* zone = static_cast<ZONE*>( aItem );
2313 
2314  // Zone borders are very specific
2315  if( zone->HitTestForEdge( aWhere, aMaxDistance / 2 ) )
2316  distance = 0;
2317  else if( zone->HitTestForEdge( aWhere, aMaxDistance ) )
2318  distance = aMaxDistance / 2;
2319  else
2320  aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
2321 
2322  break;
2323  }
2324 
2325  case PCB_FOOTPRINT_T:
2326  {
2327  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
2328  EDA_RECT bbox = footprint->GetBoundingBox( false, false );
2329 
2330  footprint->GetBoundingHull().Collide( loc, aMaxDistance, &distance );
2331 
2332  // Consider footprints larger than the viewport only as a last resort
2333  if( bbox.GetHeight() > viewport.GetHeight() || bbox.GetWidth() > viewport.GetWidth() )
2334  distance = INT_MAX / 2;
2335 
2336  break;
2337  }
2338 
2339  case PCB_MARKER_T:
2340  {
2341  PCB_MARKER* marker = static_cast<PCB_MARKER*>( aItem );
2342  SHAPE_LINE_CHAIN polygon;
2343 
2344  marker->ShapeToPolygon( polygon );
2345  polygon.Move( marker->GetPos() );
2346  polygon.Collide( loc, aMaxDistance, &distance );
2347  break;
2348  }
2349 
2350  case PCB_GROUP_T:
2351  {
2352  PCB_GROUP* group = static_cast<PCB_GROUP*>( aItem );
2353 
2354  for( BOARD_ITEM* member : group->GetItems() )
2355  distance = std::min( distance, hitTestDistance( aWhere, member, aMaxDistance ) );
2356 
2357  break;
2358  }
2359 
2360  default:
2361  aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
2362  break;
2363  }
2364 
2365  return distance;
2366 }
2367 
2368 
2369 // The general idea here is that if the user clicks directly on a small item inside a larger
2370 // one, then they want the small item. The quintessential case of this is clicking on a pad
2371 // within a footprint, but we also apply it for text within a footprint, footprints within
2372 // larger footprints, and vias within either larger pads or longer tracks.
2373 //
2374 // These "guesses" presume there is area within the larger item to click in to select it. If
2375 // an item is mostly covered by smaller items within it, then the guesses are inappropriate as
2376 // there might not be any area left to click to select the larger item. In this case we must
2377 // leave the items in the collector and bring up a Selection Clarification menu.
2378 //
2379 // We currently check for pads and text mostly covering a footprint, but we don't check for
2380 // smaller footprints mostly covering a larger footprint.
2381 //
2383  const VECTOR2I& aWhere ) const
2384 {
2385  std::set<BOARD_ITEM*> preferred;
2386  std::set<BOARD_ITEM*> rejected;
2387  wxPoint where( aWhere.x, aWhere.y );
2388 
2389  PCB_LAYER_ID activeLayer = (PCB_LAYER_ID) view()->GetTopLayer();
2390  LSET silkLayers( 2, B_SilkS, F_SilkS );
2391 
2392  if( silkLayers[activeLayer] )
2393  {
2394  for( int i = 0; i < aCollector.GetCount(); ++i )
2395  {
2396  BOARD_ITEM* item = aCollector[i];
2397  KICAD_T type = item->Type();
2398 
2399  if( ( type == PCB_FP_TEXT_T || type == PCB_TEXT_T || type == PCB_SHAPE_T )
2400  && silkLayers[item->GetLayer()] )
2401  {
2402  preferred.insert( item );
2403  }
2404  }
2405 
2406  if( preferred.size() > 0 )
2407  {
2408  aCollector.Empty();
2409 
2410  for( BOARD_ITEM* item : preferred )
2411  aCollector.Append( item );
2412 
2413  return;
2414  }
2415  }
2416 
2417  // Prefer exact hits to sloppy ones
2418  constexpr int MAX_SLOP = 5;
2419 
2420  int pixel = (int) aCollector.GetGuide()->OnePixelInIU();
2421  int minSlop = INT_MAX;
2422 
2423  std::map<BOARD_ITEM*, int> itemsBySloppiness;
2424 
2425  for( int i = 0; i < aCollector.GetCount(); ++i )
2426  {
2427  BOARD_ITEM* item = aCollector[i];
2428  int itemSlop = hitTestDistance( where, item, MAX_SLOP * pixel );
2429 
2430  itemsBySloppiness[ item ] = itemSlop;
2431 
2432  if( itemSlop < minSlop )
2433  minSlop = itemSlop;
2434  }
2435 
2436  // Prune sloppier items
2437  if( minSlop < INT_MAX )
2438  {
2439  for( std::pair<BOARD_ITEM*, int> pair : itemsBySloppiness )
2440  {
2441  if( pair.second > minSlop + pixel )
2442  aCollector.Transfer( pair.first );
2443  }
2444  }
2445 
2446  // If the user clicked on a small item within a much larger one then it's pretty clear
2447  // they're trying to select the smaller one.
2448  constexpr double sizeRatio = 1.5;
2449 
2450  std::vector<std::pair<BOARD_ITEM*, double>> itemsByArea;
2451 
2452  for( int i = 0; i < aCollector.GetCount(); ++i )
2453  {
2454  BOARD_ITEM* item = aCollector[i];
2455  double area;
2456 
2457  if( ( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2458  && static_cast<ZONE*>( item )->HitTestForEdge( where, MAX_SLOP * pixel / 2 ) )
2459  {
2460  // Zone borders are very specific, so make them "small"
2461  area = MAX_SLOP * SEG::Square( pixel );
2462  }
2463  else if( item->Type() == PCB_VIA_T )
2464  {
2465  // Vias rarely hide other things, and we don't want them deferring to short track
2466  // segments -- so make them artificially small by dropping the π from πr².
2467  area = SEG::Square( static_cast<PCB_VIA*>( item )->GetDrill() / 2 );
2468  }
2469  else
2470  {
2471  try
2472  {
2473  area = FOOTPRINT::GetCoverageArea( item, aCollector );
2474  }
2475  catch( const ClipperLib::clipperException& e )
2476  {
2477  wxLogError( "A clipper exception %s was detected.", e.what() );
2478  }
2479  }
2480 
2481  itemsByArea.emplace_back( item, area );
2482  }
2483 
2484  std::sort( itemsByArea.begin(), itemsByArea.end(),
2485  []( const std::pair<BOARD_ITEM*, double>& lhs,
2486  const std::pair<BOARD_ITEM*, double>& rhs ) -> bool
2487  {
2488  return lhs.second < rhs.second;
2489  } );
2490 
2491  bool rejecting = false;
2492 
2493  for( int i = 1; i < (int) itemsByArea.size(); ++i )
2494  {
2495  if( itemsByArea[i].second > itemsByArea[i-1].second * sizeRatio )
2496  rejecting = true;
2497 
2498  if( rejecting )
2499  rejected.insert( itemsByArea[i].first );
2500  }
2501 
2502  // Special case: if a footprint is completely covered with other features then there's no
2503  // way to select it -- so we need to leave it in the list for user disambiguation.
2504  constexpr double maxCoverRatio = 0.70;
2505 
2506  for( int i = 0; i < aCollector.GetCount(); ++i )
2507  {
2508  if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aCollector[i] ) )
2509  {
2510  if( footprint->CoverageRatio( aCollector ) > maxCoverRatio )
2511  rejected.erase( footprint );
2512  }
2513  }
2514 
2515  // Hopefully we've now got what the user wanted.
2516  if( (unsigned) aCollector.GetCount() > rejected.size() ) // do not remove everything
2517  {
2518  for( BOARD_ITEM* item : rejected )
2519  aCollector.Transfer( item );
2520  }
2521 
2522  // Finally, what we are left with is a set of items of similar coverage area. We now reject
2523  // any that are not on the active layer, to reduce the number of disambiguation menus shown.
2524  // If the user wants to force-disambiguate, they can either switch layers or use the modifier
2525  // key to force the menu.
2526  if( aCollector.GetCount() > 1 )
2527  {
2528  bool haveItemOnActive = false;
2529  rejected.clear();
2530 
2531  for( int i = 0; i < aCollector.GetCount(); ++i )
2532  {
2533  if( !aCollector[i]->IsOnLayer( activeLayer ) )
2534  rejected.insert( aCollector[i] );
2535  else
2536  haveItemOnActive = true;
2537  }
2538 
2539  if( haveItemOnActive )
2540  for( BOARD_ITEM* item : rejected )
2541  aCollector.Transfer( item );
2542  }
2543 }
2544 
2545 
2547  bool aMultiselect ) const
2548 {
2549  std::unordered_set<BOARD_ITEM*> toAdd;
2550 
2551  // Set TEMP_SELECTED on all parents which are included in the GENERAL_COLLECTOR. This
2552  // algorithm is O3n, whereas checking for the parent inclusion could potentially be On^2.
2553  for( int j = 0; j < aCollector.GetCount(); j++ )
2554  {
2555  if( aCollector[j]->GetParent() )
2556  aCollector[j]->GetParent()->ClearFlags( TEMP_SELECTED );
2557  }
2558 
2559  if( aMultiselect )
2560  {
2561  for( int j = 0; j < aCollector.GetCount(); j++ )
2562  aCollector[j]->SetFlags( TEMP_SELECTED );
2563  }
2564 
2565  for( int j = 0; j < aCollector.GetCount(); )
2566  {
2567  BOARD_ITEM* item = aCollector[j];
2568  BOARD_ITEM* parent = item->GetParent();
2569  BOARD_ITEM* start = item;
2570 
2571  if( !m_isFootprintEditor && parent && parent->Type() == PCB_FOOTPRINT_T )
2572  start = parent;
2573 
2574  // If any element is a member of a group, replace those elements with the top containing
2575  // group.
2577 
2578  if( aTop )
2579  {
2580  if( aTop != item )
2581  {
2582  toAdd.insert( aTop );
2583  aTop->SetFlags( TEMP_SELECTED );
2584 
2585  aCollector.Remove( item );
2586  continue;
2587  }
2588  }
2589  else if( m_enteredGroup
2591  {
2592  // If a group is entered, disallow selections of objects outside the group.
2593  aCollector.Remove( item );
2594  continue;
2595  }
2596 
2597  // Footprints are a bit easier as they can't be nested.
2598  if( parent && ( parent->GetFlags() & TEMP_SELECTED ) )
2599  {
2600  // Remove children of selected items
2601  aCollector.Remove( item );
2602  continue;
2603  }
2604 
2605  ++j;
2606  }
2607 
2608  for( BOARD_ITEM* item : toAdd )
2609  {
2610  if( !aCollector.HasItem( item ) )
2611  aCollector.Append( item );
2612  }
2613 }
2614 
2615 
2617 {
2618  getView()->Update( &m_selection );
2620 
2621  return 0;
2622 }
2623 
2624 
2626 {
2627  ACTION_MENU* actionMenu = aEvent.Parameter<ACTION_MENU*>();
2628  CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( actionMenu );
2629 
2630  if( conditionalMenu )
2631  conditionalMenu->Evaluate( m_selection );
2632 
2633  if( actionMenu )
2634  actionMenu->UpdateAll();
2635 
2636  return 0;
2637 }
2638 
2639 
2641 {
2643 
2647 
2653 
2663 
2665 
2667 }
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:1479
void ClearReferencePoint()
Definition: selection.h:192
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:1852
const std::set< unsigned int > GetHighContrastLayers() const
Returns the set of currently high-contrast layers.
const wxPoint & GetPos() const
Definition: marker_base.h:84
bool IsLocked() const override
Definition: footprint.h:293
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:38
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:199
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition: view.cpp:510
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:829
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.
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:123
const KIID_PATH & GetPath() const
Definition: footprint.h:203
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:200
#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:447
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:214
Meta control for all pads opacity/visibility (color ignored)
Definition: layer_ids.h:226
void ClearSelected()
Definition: eda_item.h:132
int SelectionMenu(const TOOL_EVENT &aEvent)
Show a popup menu to trim the COLLECTOR passed as aEvent's parameter down to a single item.
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 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:138
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:348
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
void SetBrightened()
Definition: eda_item.h:130
static TOOL_ACTION zoomFitScreen
Definition: actions.h:96
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:153
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:1932
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
void SetExclusiveOr(bool aExclusiveOr)
Definition: bitmap.cpp:64
static PCB_GROUP * TopLevelGroup(BOARD_ITEM *aItem, PCB_GROUP *aScope, bool isFootprintEditor)
Definition: pcb_group.cpp:99
static TOOL_ACTION drag45Degree
Definition: pcb_actions.h:137
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:222
virtual wxPoint GetPosition() const
Definition: eda_item.h:252
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.
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:623
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:204
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.
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:168
Control for copper zone opacity/visibility (color ignored)
Definition: layer_ids.h:227
void SetIsHover(bool aIsHover)
Definition: selection.h:69
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.
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:133
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 contained within or on the bounding box of an item.
Definition: eda_item.h:225
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:174
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:504
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:576
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:129
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:69
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:1498
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
virtual int GetTopLayer() const
Definition: view.cpp:821
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)
void SetMaximum()
Definition: box2.h:57
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:344
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
static const TOOL_EVENT UninhibitSelectionEditing
Used to inform tool that it should display the disambiguation menu.
Definition: actions.h:211
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:115
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:154
static const TOOL_EVENT ClearedEvent
Selected item had a property changed (except movement)
Definition: actions.h:201
#define SELECTED
DRAWINGS & GraphicalItems()
Definition: footprint.h:171
void BrightenItem(BOARD_ITEM *aItem)
static bool WithinScope(BOARD_ITEM *aItem, PCB_GROUP *aScope, bool isFootprintEditor)
Definition: pcb_group.cpp:107
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:207
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:155
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
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:550
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:849
#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.
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:190
Represent a polyline (an zero-thickness chain of connected line segments).
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:718
virtual BITMAPS GetMenuImage() const
Return a pointer to an image to be used in menus.
Definition: eda_item.cpp:211
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:277
class NETINFO_ITEM, a description of a net
Definition: typeinfo.h:107
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:100
static const TOOL_EVENT InhibitSelectionEditing
Definition: actions.h:210
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:342
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:167
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition: view.cpp:1458
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
ACTION_MENU * create() const override
< Return an instance of this class. It has to be overridden in inheriting classes.
Represent a selection area (currently a rectangle) in a VIEW, drawn corner-to-corner between two poin...
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:318
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:424
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
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:140
static TOOL_ACTION selectAll
Definition: actions.h:70
virtual EDA_RECT GetBoundingBox() const
Definition: selection.cpp:100
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:905
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:1512
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113
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