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  * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
6  * @author Maciej Suminski <maciej.suminski@cern.ch>
7  * Copyright (C) 2018-2020 KiCad Developers, see AUTHORS.txt for contributors.
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_item.h>
33 #include <clipper.hpp>
34 #include <track.h>
35 #include <footprint.h>
36 #include <pcb_shape.h>
37 #include <pcb_text.h>
38 #include <pcb_marker.h>
39 #include <zone.h>
40 #include <collectors.h>
41 #include <dialog_find.h>
44 #include <class_draw_panel_gal.h>
45 #include <view/view_controls.h>
47 #include <painter.h>
48 #include <router/router_tool.h>
49 #include <bitmaps.h>
50 #include <pcbnew_settings.h>
51 #include <tool/tool_event.h>
52 #include <tool/tool_manager.h>
54 #include <footprint_viewer_frame.h>
55 #include <id.h>
56 #include "tool_event_utils.h"
57 #include "pcb_selection_tool.h"
58 #include "pcb_actions.h"
59 
60 
61 class SELECT_MENU : public ACTION_MENU
62 {
63 public:
65  ACTION_MENU( true )
66  {
67  SetTitle( _( "Select" ) );
68 
70 
71  AppendSeparator();
72 
75  // This could be enabled if we have better logic for picking the target net with the mouse
76  // Add( PCB_ACTIONS::deselectNet );
78  }
79 
80 private:
81  ACTION_MENU* create() const override
82  {
83  return new SELECT_MENU();
84  }
85 };
86 
87 
92 {
93 public:
95 };
96 
97 
99  PCB_TOOL_BASE( "pcbnew.InteractiveSelection" ),
100  m_frame( NULL ),
101  m_additive( false ),
102  m_subtractive( false ),
103  m_exclusive_or( false ),
104  m_multiple( false ),
105  m_skip_heuristics( false ),
106  m_highlight_modifier( false ),
107  m_nonModifiedCursor( KICURSOR::ARROW ),
108  m_enteredGroup( nullptr ),
109  m_priv( std::make_unique<PRIV>() )
110 {
111  m_filter.lockedItems = false;
112  m_filter.footprints = true;
113  m_filter.text = true;
114  m_filter.tracks = true;
115  m_filter.vias = true;
116  m_filter.pads = true;
117  m_filter.graphics = true;
118  m_filter.zones = true;
119  m_filter.keepouts = true;
120  m_filter.dimensions = true;
121  m_filter.otherItems = true;
122 }
123 
124 
126 {
127  getView()->Remove( &m_selection );
129 }
130 
131 
133 {
134  auto frame = getEditFrame<PCB_BASE_FRAME>();
135 
138  {
140  return true;
141  }
142 
143  auto selectMenu = std::make_shared<SELECT_MENU>();
144  selectMenu->SetTool( this );
145  m_menu.AddSubMenu( selectMenu );
146 
147  auto& menu = m_menu.GetMenu();
148 
149  auto activeToolCondition =
150  [ frame ] ( const SELECTION& aSel )
151  {
152  return !frame->ToolStackIsEmpty();
153  };
154 
155  auto inGroupCondition =
156  [this] ( const SELECTION& )
157  {
158  return m_enteredGroup != nullptr;
159  };
160 
161  if( frame && frame->IsType( FRAME_PCB_EDITOR ) )
162  {
163  menu.AddMenu( selectMenu.get(), SELECTION_CONDITIONS::NotEmpty );
164  menu.AddSeparator( 1000 );
165  }
166 
167  // "Cancel" goes at the top of the context menu when a tool is active
168  menu.AddItem( ACTIONS::cancelInteractive, activeToolCondition, 1 );
169  menu.AddItem( PCB_ACTIONS::groupLeave, inGroupCondition, 1 );
170  menu.AddSeparator( 1 );
171 
172  if( frame )
174 
175  return true;
176 }
177 
178 
180 {
181  m_frame = getEditFrame<PCB_BASE_FRAME>();
182 
183  if( m_enteredGroup )
184  ExitGroup();
185 
186  if( aReason == TOOL_BASE::MODEL_RELOAD )
187  {
188  // Deselect any item being currently in edit, to avoid unexpected behavior
189  // and remove pointers to the selected items from containers
190  // without changing their properties (as they are already deleted
191  // while a new board is loaded)
192  ClearSelection( true );
193 
194  getView()->GetPainter()->GetSettings()->SetHighlight( false );
195  }
196  else
197  {
198  // Restore previous properties of selected items and remove them from containers
199  ClearSelection( true );
200  }
201 
202  // Reinsert the VIEW_GROUP, in case it was removed from the VIEW
203  view()->Remove( &m_selection );
204  view()->Add( &m_selection );
205 
208 }
209 
210 
211 void PCB_SELECTION_TOOL::setModifiersState( bool aShiftState, bool aCtrlState, bool aAltState )
212 {
213  // Set the configuration of m_additive, m_subtractive, m_exclusive_or
214  // from the state of modifier keys SHIFT, CTRL, ALT and the OS
215 
216  // on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
217  // Due to the fact ALT key modifier cannot be useed freely on Winows and Linux,
218  // actions are different on OSX and others OS
219  // Especially, ALT key cannot be used to force showing the full selection choice
220  // context menu (the menu is immediately closed on Windows )
221  //
222  // No modifier = select items and deselect previous selection
223  // ALT (on OSX) = skip heuristic and show full selection choice
224  // ALT (on others) = exclusive OR of selected items (inverse selection)
225  //
226  // CTRL (on OSX) = exclusive OR of selected items (inverse selection)
227  // CTRL (on others) = skip heuristic and show full selection choice
228  //
229  // SHIFT = add selected items to the current selection
230  //
231  // CTRL+SHIFT (on OSX) = remove selected items to the current selection
232  // CTRL+SHIFT (on others) = highlight net
233  //
234  // CTRL+ALT (on OSX) = highlight net
235  // CTRL+ALT (on others) = do nothing (same as no modifier)
236  //
237  // SHIFT+ALT (on OSX) = do nothing (same as no modifier)
238  // SHIFT+ALT (on others) = remove selected items to the current selection
239 
240 #ifdef __WXOSX_MAC__
241  m_subtractive = aCtrlState && aShiftState && !aAltState;
242  m_additive = aShiftState && !aCtrlState && !aAltState;
243  m_exclusive_or = aCtrlState && !aShiftState && !aAltState;
244  m_skip_heuristics = aAltState && !aShiftState && !aCtrlState;
245  m_highlight_modifier = aCtrlState && aAltState && !aShiftState;
246 
247 #else
248  m_subtractive = aShiftState && !aCtrlState && aAltState;
249  m_additive = aShiftState && !aCtrlState && !aAltState;
250  m_exclusive_or = !aShiftState && !aCtrlState && aAltState;
251 
252  // Is the user requesting that the selection list include all possible
253  // items without removing less likely selection candidates
254  // Cannot use the Alt key on windows or the disambiguation context menu is immediately
255  // dismissed rendering it useless.
256  m_skip_heuristics = aCtrlState && !aShiftState && !aAltState;
257 
258  m_highlight_modifier = aCtrlState && aShiftState && !aAltState;
259 #endif
260 }
261 
262 
263 void PCB_SELECTION_TOOL::OnIdle( wxIdleEvent& aEvent )
264 {
265  if( m_frame->ToolStackIsEmpty() && !m_multiple )
266  {
267  wxMouseState keyboardState = wxGetMouseState();
268 
269  setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
270  keyboardState.AltDown() );
271 
272  if( m_additive )
274  else if( m_subtractive )
276  else if( m_exclusive_or )
278  else
280  }
281 }
282 
283 
285 {
286  // Main loop: keep receiving events
287  while( TOOL_EVENT* evt = Wait() )
288  {
289  MOUSE_DRAG_ACTION dragAction = m_frame->GetDragAction();
291 
292  // on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
293  setModifiersState( evt->Modifier( MD_SHIFT ), evt->Modifier( MD_CTRL ),
294  evt->Modifier( MD_ALT ) );
295 
296  bool modifier_enabled = m_subtractive || m_additive || m_exclusive_or;
297  PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
298  bool brd_editor = frame && frame->IsType( FRAME_PCB_EDITOR );
300 
301  // If the router tool is active, don't override
302  if( router && router->IsToolActive() )
303  {
304  evt->SetPassEvent();
305  }
306  // Single click? Select single object
307  else if( evt->IsClick( BUT_LEFT ) )
308  {
309  if( m_highlight_modifier && brd_editor )
311  else
312  {
313  m_frame->FocusOnItem( nullptr );
314  selectPoint( evt->Position() );
315  }
316  }
317  else if( evt->IsClick( BUT_RIGHT ) )
318  {
319  // Right click? if there is any object - show the context menu
320  bool selectionCancelled = false;
321 
322  if( m_selection.Empty() )
323  {
324  selectPoint( evt->Position(), false, &selectionCancelled );
325  m_selection.SetIsHover( true );
326  }
327 
328  if( !selectionCancelled )
330  }
331  else if( evt->IsDblClick( BUT_LEFT ) )
332  {
333  // Double click? Display the properties window
334  m_frame->FocusOnItem( nullptr );
335 
336  if( m_selection.Empty() )
337  selectPoint( evt->Position() );
338 
339  if( m_selection.GetSize() == 1 && m_selection[0]->Type() == PCB_GROUP_T )
340  {
341  EnterGroup();
342  }
343  else
344  {
346  }
347  }
348  else if( evt->IsDblClick( BUT_MIDDLE ) )
349  {
350  // Middle double click? Do zoom to fit or zoom to objects
351  if( evt->Modifier( MD_CTRL ) ) // Is CTRL key down?
353  else
355  }
356  else if( evt->IsDrag( BUT_LEFT ) )
357  {
358  // Is another tool already moving a new object? Don't allow a drag start
359  if( !m_selection.Empty() && m_selection[0]->HasFlag( IS_NEW | IS_MOVED ) )
360  {
361  evt->SetPassEvent();
362  continue;
363  }
364 
365  // Drag with LMB? Select multiple objects (or at least draw a selection box)
366  // or drag them
367  m_frame->FocusOnItem( nullptr );
369 
370  if( modifier_enabled || dragAction == MOUSE_DRAG_ACTION::SELECT )
371  {
372  selectMultiple();
373  }
374  else if( m_selection.Empty() && dragAction != MOUSE_DRAG_ACTION::DRAG_ANY )
375  {
376  selectMultiple();
377  }
378  else
379  {
380  // Don't allow starting a drag from a zone filled area that isn't already selected
381  auto zoneFilledAreaFilter =
382  []( const VECTOR2I& aWhere, GENERAL_COLLECTOR& aCollector,
383  PCB_SELECTION_TOOL* aTool )
384  {
385  wxPoint location = wxPoint( aWhere );
386  int accuracy = KiROUND( 5 * aCollector.GetGuide()->OnePixelInIU() );
387  std::set<EDA_ITEM*> remove;
388 
389  for( EDA_ITEM* item : aCollector )
390  {
391  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
392  {
393  ZONE* zone = static_cast<ZONE*>( item );
394 
395  if( !zone->HitTestForCorner( location, accuracy * 2 ) &&
396  !zone->HitTestForEdge( location, accuracy ) )
397  remove.insert( zone );
398  }
399  }
400 
401  for( EDA_ITEM* item : remove )
402  aCollector.Remove( item );
403  };
404 
405  // Selection is empty? try to start dragging the item under the point where drag
406  // started
407  if( m_selection.Empty() && selectCursor( false, zoneFilledAreaFilter ) )
408  m_selection.SetIsHover( true );
409 
410  // Check if dragging has started within any of selected items bounding box.
411  // We verify "HasPosition()" first to protect against edge case involving
412  // moving off menus that causes problems (issue #5250)
413  if( evt->HasPosition() && selectionContains( evt->Position() ) )
414  {
415  // Yes -> run the move tool and wait till it finishes
416  TRACK* track = dynamic_cast<TRACK*>( m_selection.GetItem( 0 ) );
417 
418  // If there is only item in the selection and it's a track, then we need to route it
419  bool doRouting = ( track && ( 1 == m_selection.GetSize() ) );
420 
421  if( doRouting && trackDragAction == TRACK_DRAG_ACTION::DRAG )
423  else if( doRouting && trackDragAction == TRACK_DRAG_ACTION::DRAG_FREE_ANGLE )
425  else
427  }
428  else
429  {
430  // No -> drag a selection box
431  selectMultiple();
432  }
433  }
434  }
435  else if( evt->IsCancel() )
436  {
437  m_frame->FocusOnItem( nullptr );
438 
439  if( m_enteredGroup )
440  ExitGroup();
441 
442  ClearSelection();
443 
444  if( evt->FirstResponder() == this )
446  }
447  else
448  {
449  evt->SetPassEvent();
450  }
451 
452 
453  if( m_frame->ToolStackIsEmpty() )
454  {
455  // move cursor prediction
456  if( !modifier_enabled
457  && dragAction == MOUSE_DRAG_ACTION::DRAG_SELECTED
458  && !m_selection.Empty()
459  && evt->HasPosition()
460  && selectionContains( evt->Position() ) )
461  {
463  }
464  else
465  {
467  }
468  }
469  }
470 
471  // Shutting down; clear the selection
472  m_selection.Clear();
473 
474  return 0;
475 }
476 
477 
479 {
480  wxCHECK_RET( m_selection.GetSize() == 1 && m_selection[0]->Type() == PCB_GROUP_T,
481  "EnterGroup called when selection is not a single group" );
482  PCB_GROUP* aGroup = static_cast<PCB_GROUP*>( m_selection[0] );
483 
484  if( m_enteredGroup != NULL )
485  ExitGroup();
486 
487  ClearSelection();
488  m_enteredGroup = aGroup;
490  m_enteredGroup->RunOnChildren( [&]( BOARD_ITEM* titem )
491  {
492  select( titem );
493  } );
494 
496 }
497 
498 
499 void PCB_SELECTION_TOOL::ExitGroup( bool aSelectGroup )
500 {
501  // Only continue if there is a group entered
502  if( m_enteredGroup == nullptr )
503  return;
504 
506  ClearSelection();
507 
508  if( aSelectGroup )
510 
512  m_enteredGroup = nullptr;
513 }
514 
515 
517 {
518  return m_selection;
519 }
520 
521 
523  bool aConfirmLockedItems )
524 {
525  bool selectionEmpty = m_selection.Empty();
526  m_selection.SetIsHover( selectionEmpty );
527 
528  if( selectionEmpty )
529  {
530  m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true, aClientFilter );
532  }
533 
534  if( aClientFilter )
535  {
536  enum DISPOSITION { BEFORE = 1, AFTER, BOTH };
537 
538  std::map<EDA_ITEM*, DISPOSITION> itemDispositions;
539  GENERAL_COLLECTOR collector;
540 
541  for( EDA_ITEM* item : m_selection )
542  {
543  collector.Append( item );
544  itemDispositions[ item ] = BEFORE;
545  }
546 
547  aClientFilter( VECTOR2I(), collector, this );
548 
549  for( EDA_ITEM* item : collector )
550  {
551  if( itemDispositions.count( item ) )
552  itemDispositions[ item ] = BOTH;
553  else
554  itemDispositions[ item ] = AFTER;
555  }
556 
557  // Unhighlight the BEFORE items before highlighting the AFTER items.
558  // This is so that in the case of groups, if aClientFilter replaces a selection
559  // with the enclosing group, the unhighlight of the element doesn't undo the
560  // recursive highlighting of that elemetn by the group.
561 
562  for( std::pair<EDA_ITEM* const, DISPOSITION> itemDisposition : itemDispositions )
563  {
564  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( itemDisposition.first );
565  DISPOSITION disposition = itemDisposition.second;
566 
567  if( disposition == BEFORE )
568  {
569  unhighlight( item, SELECTED, &m_selection );
570  }
571  }
572 
573  for( std::pair<EDA_ITEM* const, DISPOSITION> itemDisposition : itemDispositions )
574  {
575  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( itemDisposition.first );
576  DISPOSITION disposition = itemDisposition.second;
577 
578  if( disposition == AFTER )
579  {
580  highlight( item, SELECTED, &m_selection );
581  }
582  else if( disposition == BOTH )
583  {
584  // nothing to do
585  }
586  }
587 
589  }
590 
591  if( aConfirmLockedItems )
592  {
593  std::vector<BOARD_ITEM*> lockedItems;
594 
595  for( EDA_ITEM* item : m_selection )
596  {
597  BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
598 
599  if( boardItem->Type() == PCB_GROUP_T )
600  {
601  PCB_GROUP* group = static_cast<PCB_GROUP*>( boardItem );
602  bool lockedDescendant = false;
603 
604  group->RunOnDescendants(
605  [&lockedDescendant]( BOARD_ITEM* child )
606  {
607  if( child->IsLocked() )
608  lockedDescendant = true;
609  } );
610 
611  if( lockedDescendant )
612  lockedItems.push_back( group );
613  }
614  else if( boardItem->IsLocked() )
615  {
616  lockedItems.push_back( boardItem );
617  }
618  }
619 
620  if( !lockedItems.empty() )
621  {
622  DIALOG_LOCKED_ITEMS_QUERY dlg( frame(), lockedItems.size() );
623 
624  switch( dlg.ShowModal() )
625  {
626  case wxID_OK:
627  // remove locked items from selection
628  for( BOARD_ITEM* item : lockedItems )
629  unselect( item );
630 
631  break;
632 
633  case wxID_CANCEL:
634  // cancel operation
635  ClearSelection();
636  break;
637 
638  case wxID_APPLY:
639  // continue with operation with current selection
640  break;
641  }
642  }
643  }
644 
645  return m_selection;
646 }
647 
648 
650 {
651  GENERAL_COLLECTORS_GUIDE guide( board()->GetVisibleLayers(),
652  (PCB_LAYER_ID) view()->GetTopLayer(), view() );
653 
654  bool padsDisabled = !board()->IsElementVisible( LAYER_PADS );
655 
656  // account for the globals
657  guide.SetIgnoreMTextsMarkedNoShow( ! board()->IsElementVisible( LAYER_MOD_TEXT_INVISIBLE ) );
658  guide.SetIgnoreMTextsOnBack( ! board()->IsElementVisible( LAYER_MOD_TEXT_BK ) );
659  guide.SetIgnoreMTextsOnFront( ! board()->IsElementVisible( LAYER_MOD_TEXT_FR ) );
660  guide.SetIgnoreModulesOnBack( ! board()->IsElementVisible( LAYER_MOD_BK ) );
661  guide.SetIgnoreModulesOnFront( ! board()->IsElementVisible( LAYER_MOD_FR ) );
662  guide.SetIgnorePadsOnBack( padsDisabled || ! board()->IsElementVisible( LAYER_PAD_BK ) );
663  guide.SetIgnorePadsOnFront( padsDisabled || ! board()->IsElementVisible( LAYER_PAD_FR ) );
664  guide.SetIgnoreThroughHolePads( padsDisabled || ! board()->IsElementVisible( LAYER_PADS_TH ) );
665  guide.SetIgnoreModulesVals( ! board()->IsElementVisible( LAYER_MOD_VALUES ) );
666  guide.SetIgnoreModulesRefs( ! board()->IsElementVisible( LAYER_MOD_REFERENCES ) );
667  guide.SetIgnoreThroughVias( ! board()->IsElementVisible( LAYER_VIAS ) );
668  guide.SetIgnoreBlindBuriedVias( ! board()->IsElementVisible( LAYER_VIAS ) );
669  guide.SetIgnoreMicroVias( ! board()->IsElementVisible( LAYER_VIAS ) );
670  guide.SetIgnoreTracks( ! board()->IsElementVisible( LAYER_TRACKS ) );
671 
672  return guide;
673 }
674 
675 
676 bool PCB_SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag,
677  bool* aSelectionCancelledFlag,
678  CLIENT_SELECTION_FILTER aClientFilter )
679 {
681  GENERAL_COLLECTOR collector;
682  const PCB_DISPLAY_OPTIONS& displayOpts = m_frame->GetDisplayOptions();
683 
685 
686  if( m_enteredGroup && !m_enteredGroup->GetBoundingBox().Contains( (wxPoint) aWhere ) )
687  ExitGroup();
688 
691  (wxPoint) aWhere, guide );
692 
693  // Remove unselectable items
694  for( int i = collector.GetCount() - 1; i >= 0; --i )
695  {
696  if( !Selectable( collector[ i ] ) || ( aOnDrag && collector[i]->IsLocked() ) )
697  collector.Remove( i );
698  }
699 
701 
702  // Allow the client to do tool- or action-specific filtering to see if we can get down
703  // to a single item
704  if( aClientFilter )
705  aClientFilter( aWhere, collector, this );
706 
707  // Apply the stateful filter
708  FilterCollectedItems( collector );
709 
710  FilterCollectorForHierarchy( collector, false );
711 
712  // Apply some ugly heuristics to avoid disambiguation menus whenever possible
713  if( collector.GetCount() > 1 && !m_skip_heuristics )
714  GuessSelectionCandidates( collector, aWhere );
715 
716  // If still more than one item we're going to have to ask the user.
717  if( collector.GetCount() > 1 )
718  {
719  if( aOnDrag )
721 
722  if( !doSelectionMenu( &collector ) )
723  {
724  if( aSelectionCancelledFlag )
725  *aSelectionCancelledFlag = true;
726 
727  return false;
728  }
729  }
730 
731  bool anyAdded = false;
732  bool anySubtracted = false;
733 
735  {
736  if( m_selection.GetSize() > 0 )
737  {
738  ClearSelection( true /*quiet mode*/ );
739  anySubtracted = true;
740  }
741  }
742 
743  if( collector.GetCount() > 0 )
744  {
745  for( int i = 0; i < collector.GetCount(); ++i )
746  {
747  if( m_subtractive || ( m_exclusive_or && collector[i]->IsSelected() ) )
748  {
749  unselect( collector[i] );
750  anySubtracted = true;
751  }
752  else
753  {
754  select( collector[i] );
755  anyAdded = true;
756  }
757  }
758  }
759 
760  if( anyAdded )
761  {
763  return true;
764  }
765  else if( anySubtracted )
766  {
768  return true;
769  }
770 
771  return false;
772 }
773 
774 
775 bool PCB_SELECTION_TOOL::selectCursor( bool aForceSelect, CLIENT_SELECTION_FILTER aClientFilter )
776 {
777  if( aForceSelect || m_selection.Empty() )
778  {
779  ClearSelection( true /*quiet mode*/ );
780  selectPoint( getViewControls()->GetCursorPosition( false ), false, NULL, aClientFilter );
781  }
782 
783  return !m_selection.Empty();
784 }
785 
786 
788 {
789  bool cancelled = false; // Was the tool cancelled while it was running?
790  m_multiple = true; // Multiple selection mode is active
791  KIGFX::VIEW* view = getView();
792 
794  view->Add( &area );
795 
796  bool anyAdded = false;
797  bool anySubtracted = false;
798 
799  while( TOOL_EVENT* evt = Wait() )
800  {
801  int width = area.GetEnd().x - area.GetOrigin().x;
802 
803  /* Selection mode depends on direction of drag-selection:
804  * Left > Right : Select objects that are fully enclosed by selection
805  * Right > Left : Select objects that are crossed by selection
806  */
807  bool windowSelection = width >= 0 ? true : false;
808 
809  if( view->IsMirroredX() )
810  windowSelection = !windowSelection;
811 
814 
815  if( evt->IsCancelInteractive() || evt->IsActivate() )
816  {
817  cancelled = true;
818  break;
819  }
820 
821  if( evt->IsDrag( BUT_LEFT ) )
822  {
824  {
825  if( m_selection.GetSize() > 0 )
826  {
827  anySubtracted = true;
828  ClearSelection( true /*quiet mode*/ );
829  }
830  }
831 
832  // Start drawing a selection box
833  area.SetOrigin( evt->DragOrigin() );
834  area.SetEnd( evt->Position() );
835  area.SetAdditive( m_additive );
838 
839  view->SetVisible( &area, true );
840  view->Update( &area );
841  getViewControls()->SetAutoPan( true );
842  }
843 
844  if( evt->IsMouseUp( BUT_LEFT ) )
845  {
846  getViewControls()->SetAutoPan( false );
847 
848  // End drawing the selection box
849  view->SetVisible( &area, false );
850 
851  std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> candidates;
852  BOX2I selectionBox = area.ViewBBox();
853  view->Query( selectionBox, candidates ); // Get the list of nearby items
854 
855  int height = area.GetEnd().y - area.GetOrigin().y;
856 
857  // Construct an EDA_RECT to determine BOARD_ITEM selection
858  EDA_RECT selectionRect( (wxPoint) area.GetOrigin(), wxSize( width, height ) );
859 
860  selectionRect.Normalize();
861 
862  GENERAL_COLLECTOR collector;
863 
864  for( auto it = candidates.begin(), it_end = candidates.end(); it != it_end; ++it )
865  {
866  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it->first );
867 
868  if( item && Selectable( item ) && item->HitTest( selectionRect, windowSelection ) )
869  collector.Append( item );
870  }
871 
872  // Apply the stateful filter
873  FilterCollectedItems( collector );
874 
875  FilterCollectorForHierarchy( collector, true );
876 
877  for( EDA_ITEM* i : collector )
878  {
879  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
880 
881  if( m_subtractive || ( m_exclusive_or && item->IsSelected() ) )
882  {
883  unselect( item );
884  anySubtracted = true;
885  }
886  else
887  {
888  select( item );
889  anyAdded = true;
890  }
891  }
892 
893  m_selection.SetIsHover( false );
894 
895  // Inform other potentially interested tools
896  if( anyAdded )
898  else if( anySubtracted )
900 
901  break; // Stop waiting for events
902  }
903  }
904 
905  getViewControls()->SetAutoPan( false );
906 
907  // Stop drawing the selection box
908  view->Remove( &area );
909  m_multiple = false; // Multiple selection mode is inactive
910 
911  if( !cancelled )
913 
915 
916  return cancelled;
917 }
918 
919 
921 {
923 
924  selectCursor( false, aClientFilter );
925 
926  return 0;
927 }
928 
929 
931 {
932  ClearSelection();
933 
934  return 0;
935 }
936 
937 
939 {
940  std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
941 
942  if( items )
943  {
944  // Perform individual selection of each item before processing the event.
945  for( BOARD_ITEM* item : *items )
946  select( item );
947 
949  }
950 
951  return 0;
952 }
953 
954 
956 {
957  AddItemToSel( aEvent.Parameter<BOARD_ITEM*>() );
958  return 0;
959 }
960 
961 
963 {
964  KIGFX::VIEW* view = getView();
965 
966  // hold all visible items
967  std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
968 
969  // Filter the view items based on the selection box
970  BOX2I selectionBox;
971 
972  // Intermediate step to allow filtering against hierarchy
973  GENERAL_COLLECTOR collection;
974 
975  selectionBox.SetMaximum();
976  view->Query( selectionBox, selectedItems ); // Get the list of selected items
977 
978  for( const KIGFX::VIEW::LAYER_ITEM_PAIR& item_pair : selectedItems )
979  {
980  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( item_pair.first );
981 
982  if( !item || !Selectable( item ) || !itemPassesFilter( item ) )
983  continue;
984 
985  collection.Append( item );
986  }
987 
988  FilterCollectorForHierarchy( collection, true );
989 
990  for( EDA_ITEM* item : collection )
991  select( static_cast<BOARD_ITEM*>( item ) );
992 
994 
995  return 0;
996 }
997 
998 
999 void PCB_SELECTION_TOOL::AddItemToSel( BOARD_ITEM* aItem, bool aQuietMode )
1000 {
1001  if( aItem )
1002  {
1003  select( aItem );
1004 
1005  // Inform other potentially interested tools
1006  if( !aQuietMode )
1008  }
1009 }
1010 
1011 
1013 {
1014  std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
1015 
1016  if( items )
1017  {
1018  // Perform individual unselection of each item before processing the event
1019  for( auto item : *items )
1020  unselect( item );
1021 
1023  }
1024 
1025  return 0;
1026 }
1027 
1028 
1030 {
1031  RemoveItemFromSel( aEvent.Parameter<BOARD_ITEM*>() );
1032  return 0;
1033 }
1034 
1035 
1036 void PCB_SELECTION_TOOL::RemoveItemFromSel( BOARD_ITEM* aItem, bool aQuietMode )
1037 {
1038  if( aItem )
1039  {
1040  unselect( aItem );
1041 
1042  // Inform other potentially interested tools
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  TRACK* trackItem = dynamic_cast<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<TRACK*>> trackMap;
1131  std::map<wxPoint, 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  TRACK* track = static_cast<TRACK*>( item );
1143  trackMap[ track->GetStart() ].push_back( track );
1144  trackMap[ track->GetEnd() ].push_back( track );
1145  }
1146  break;
1147 
1148  case PCB_VIA_T:
1149  {
1150  VIA* via = static_cast<VIA*>( item );
1151  viaMap[ via->GetStart() ] = via;
1152  }
1153  break;
1154 
1155  case PCB_PAD_T:
1156  {
1157  PAD* pad = static_cast<PAD*>( item );
1158  padMap[ pad->GetPosition() ] = pad;
1159  }
1160  break;
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<TRACK*>( &aStartItem )->GetStart() );
1177  activePts.push_back( static_cast<TRACK*>( &aStartItem )->GetEnd() );
1178  break;
1179 
1180  case PCB_VIA_T:
1181  activePts.push_back( static_cast<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( 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() && aStopCondition != STOP_AT_JUNCTION )
1233  select( viaMap[ pt ] );
1234 
1235  activePts.erase( activePts.begin() + i );
1236  }
1237  }
1238 }
1239 
1240 
1241 void PCB_SELECTION_TOOL::selectAllItemsOnNet( int aNetCode, bool aSelect )
1242 {
1243  constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, EOT };
1244  auto connectivity = board()->GetConnectivity();
1245 
1246  for( BOARD_CONNECTED_ITEM* item : connectivity->GetNetItems( aNetCode, types ) )
1247  if( itemPassesFilter( item ) )
1248  aSelect ? select( item ) : unselect( item );
1249 }
1250 
1251 
1253 {
1254  bool select = aEvent.IsAction( &PCB_ACTIONS::selectNet );
1255 
1256  // If we've been passed an argument, just select that netcode1
1257  int netcode = aEvent.Parameter<intptr_t>();
1258 
1259  if( netcode > 0 )
1260  {
1261  selectAllItemsOnNet( netcode, select );
1262  return 0;
1263  }
1264 
1265  if( !selectCursor() )
1266  return 0;
1267 
1268  // copy the selection, since we're going to iterate and modify
1269  auto selection = m_selection.GetItems();
1270 
1271  for( EDA_ITEM* i : selection )
1272  {
1273  BOARD_CONNECTED_ITEM* connItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( i );
1274 
1275  if( connItem )
1276  selectAllItemsOnNet( connItem->GetNetCode(), select );
1277  }
1278 
1279  // Inform other potentially interested tools
1280  if( m_selection.Size() > 0 )
1282 
1283  return 0;
1284 }
1285 
1286 
1287 void PCB_SELECTION_TOOL::selectAllItemsOnSheet( wxString& aSheetPath )
1288 {
1289  std::list<FOOTPRINT*> footprintList;
1290 
1291  // store all footprints that are on that sheet path
1292  for( FOOTPRINT* footprint : board()->Footprints() )
1293  {
1294  if( footprint == nullptr )
1295  continue;
1296 
1297  wxString footprint_path = footprint->GetPath().AsString().BeforeLast('/');
1298 
1299  if( aSheetPath.IsEmpty() )
1300  aSheetPath += '/';
1301 
1302  if( footprint_path == aSheetPath )
1303  footprintList.push_back( footprint );
1304  }
1305 
1306  //Generate a list of all pads, and of all nets they belong to.
1307  std::list<int> netcodeList;
1308  std::list<PAD*> padList;
1309 
1310  for( FOOTPRINT* footprint : footprintList )
1311  {
1312  for( PAD* pad : footprint->Pads() )
1313  {
1314  if( pad->IsConnected() )
1315  {
1316  netcodeList.push_back( pad->GetNetCode() );
1317  padList.push_back( pad );
1318  }
1319  }
1320  }
1321  // remove all duplicates
1322  netcodeList.sort();
1323  netcodeList.unique();
1324 
1325  for( PAD* pad : padList )
1327 
1328  // now we need to find all footprints that are connected to each of these nets then we need
1329  // to determine if these footprints are in the list of footprints belonging to this sheet
1330  std::list<int> removeCodeList;
1331  constexpr KICAD_T padType[] = { PCB_PAD_T, EOT };
1332 
1333  for( int netCode : netcodeList )
1334  {
1335  for( BOARD_CONNECTED_ITEM* mitem : board()->GetConnectivity()->GetNetItems( netCode, padType ) )
1336  {
1337  if( mitem->Type() == PCB_PAD_T && !alg::contains( footprintList, mitem->GetParent() ) )
1338  {
1339  // if we cannot find the footprint of the pad in the footprintList then we can
1340  // assume that that footprint is not located in the same schematic, therefore
1341  // invalidate this netcode.
1342  removeCodeList.push_back( netCode );
1343  break;
1344  }
1345  }
1346  }
1347 
1348  // remove all duplicates
1349  removeCodeList.sort();
1350  removeCodeList.unique();
1351 
1352  for( int removeCode : removeCodeList )
1353  {
1354  netcodeList.remove( removeCode );
1355  }
1356 
1357  std::list<BOARD_CONNECTED_ITEM*> localConnectionList;
1358  constexpr KICAD_T trackViaType[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, EOT };
1359 
1360  for( int netCode : netcodeList )
1361  {
1362  for( BOARD_CONNECTED_ITEM* item : board()->GetConnectivity()->GetNetItems( netCode, trackViaType ) )
1363  localConnectionList.push_back( item );
1364  }
1365 
1366  for( BOARD_ITEM* i : footprintList )
1367  {
1368  if( i != NULL )
1369  select( i );
1370  }
1371 
1372  for( BOARD_CONNECTED_ITEM* i : localConnectionList )
1373  {
1374  if( i != NULL )
1375  select( i );
1376  }
1377 }
1378 
1379 
1381 {
1382  //Should recalculate the view to zoom in on the selection
1383  auto selectionBox = m_selection.GetBoundingBox();
1384  auto view = getView();
1385 
1386  VECTOR2D screenSize = view->ToWorld( m_frame->GetCanvas()->GetClientSize(), false );
1387  screenSize.x = std::max( 10.0, screenSize.x );
1388  screenSize.y = std::max( 10.0, screenSize.y );
1389 
1390  if( selectionBox.GetWidth() != 0 || selectionBox.GetHeight() != 0 )
1391  {
1392  VECTOR2D vsize = selectionBox.GetSize();
1393  double scale = view->GetScale() / std::max( fabs( vsize.x / screenSize.x ),
1394  fabs( vsize.y / screenSize.y ) );
1395  view->SetScale( scale );
1396  view->SetCenter( selectionBox.Centre() );
1397  view->Add( &m_selection );
1398  }
1399 
1401 }
1402 
1403 
1405 {
1406  ClearSelection( true /*quiet mode*/ );
1407  wxString sheetPath = *aEvent.Parameter<wxString*>();
1408 
1409  selectAllItemsOnSheet( sheetPath );
1410 
1411  zoomFitSelection();
1412 
1413  if( m_selection.Size() > 0 )
1415 
1416  return 0;
1417 }
1418 
1419 
1421 {
1422  if( !selectCursor( true ) )
1423  return 0;
1424 
1425  // this function currently only supports footprints since they are only
1426  // on one sheet.
1427  auto item = m_selection.Front();
1428 
1429  if( !item )
1430  return 0;
1431 
1432  if( item->Type() != PCB_FOOTPRINT_T )
1433  return 0;
1434 
1435  FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( item );
1436 
1437  if( footprint->GetPath().empty() )
1438  return 0;
1439 
1440  ClearSelection( true /*quiet mode*/ );
1441 
1442  // get the sheet path only.
1443  wxString sheetPath = footprint->GetPath().AsString().BeforeLast( '/' );
1444 
1445  if( sheetPath.IsEmpty() )
1446  sheetPath += '/';
1447 
1448  selectAllItemsOnSheet( sheetPath );
1449 
1450  // Inform other potentially interested tools
1451  if( m_selection.Size() > 0 )
1453 
1454  return 0;
1455 }
1456 
1457 
1459 {
1460  bool cleared = false;
1461 
1462  if( m_selection.GetSize() > 0 )
1463  {
1464  // Don't fire an event now; most of the time it will be redundant as we're about to
1465  // fire a SelectedEvent.
1466  cleared = true;
1467  ClearSelection( true /*quiet mode*/ );
1468  }
1469 
1470  if( aItem )
1471  {
1472  select( aItem );
1473  m_frame->FocusOnLocation( aItem->GetPosition() );
1474 
1475  // Inform other potentially interested tools
1477  }
1478  else if( cleared )
1479  {
1481  }
1482 
1484 }
1485 
1486 
1488 {
1489  DIALOG_FIND dlg( m_frame );
1490  dlg.SetCallback( std::bind( &PCB_SELECTION_TOOL::findCallback, this, _1 ) );
1491  dlg.ShowModal();
1492 
1493  return 0;
1494 }
1495 
1496 
1504 static bool itemIsIncludedByFilter( const BOARD_ITEM& aItem, const BOARD& aBoard,
1505  const DIALOG_FILTER_SELECTION::OPTIONS& aFilterOptions )
1506 {
1507  bool include = true;
1508  const PCB_LAYER_ID layer = aItem.GetLayer();
1509 
1510  // if the item needs to be checked against the options
1511  if( include )
1512  {
1513  switch( aItem.Type() )
1514  {
1515  case PCB_FOOTPRINT_T:
1516  {
1517  const FOOTPRINT& footprint = static_cast<const FOOTPRINT&>( aItem );
1518 
1519  include = aFilterOptions.includeModules;
1520 
1521  if( include && !aFilterOptions.includeLockedModules )
1522  include = !footprint.IsLocked();
1523 
1524  break;
1525  }
1526  case PCB_TRACE_T:
1527  case PCB_ARC_T:
1528  include = aFilterOptions.includeTracks;
1529  break;
1530 
1531  case PCB_VIA_T:
1532  include = aFilterOptions.includeVias;
1533  break;
1534 
1535  case PCB_FP_ZONE_T:
1536  case PCB_ZONE_T:
1537  include = aFilterOptions.includeZones;
1538  break;
1539 
1540  case PCB_SHAPE_T:
1541  case PCB_TARGET_T:
1542  case PCB_DIM_ALIGNED_T:
1543  case PCB_DIM_CENTER_T:
1544  case PCB_DIM_ORTHOGONAL_T:
1545  case PCB_DIM_LEADER_T:
1546  if( layer == Edge_Cuts )
1547  include = aFilterOptions.includeBoardOutlineLayer;
1548  else
1549  include = aFilterOptions.includeItemsOnTechLayers;
1550  break;
1551 
1552  case PCB_FP_TEXT_T:
1553  case PCB_TEXT_T:
1554  include = aFilterOptions.includePcbTexts;
1555  break;
1556 
1557  default:
1558  // no filtering, just select it
1559  break;
1560  }
1561  }
1562 
1563  return include;
1564 }
1565 
1566 
1568 {
1569  const BOARD& board = *getModel<BOARD>();
1570  DIALOG_FILTER_SELECTION::OPTIONS& opts = m_priv->m_filterOpts;
1571  DIALOG_FILTER_SELECTION dlg( m_frame, opts );
1572 
1573  const int cmd = dlg.ShowModal();
1574 
1575  if( cmd != wxID_OK )
1576  return 0;
1577 
1578  // copy current selection
1579  std::deque<EDA_ITEM*> selection = m_selection.GetItems();
1580 
1581  ClearSelection( true /*quiet mode*/ );
1582 
1583  // re-select items from the saved selection according to the dialog options
1584  for( EDA_ITEM* i : selection )
1585  {
1586  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
1587  bool include = itemIsIncludedByFilter( *item, board, opts );
1588 
1589  if( include )
1590  select( item );
1591  }
1592 
1594 
1595  return 0;
1596 }
1597 
1598 
1600 {
1601  if( aCollector.GetCount() == 0 )
1602  return;
1603 
1604  std::set<BOARD_ITEM*> rejected;
1605 
1606  for( EDA_ITEM* i : aCollector )
1607  {
1608  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
1609 
1610  if( !itemPassesFilter( item ) )
1611  rejected.insert( item );
1612  }
1613 
1614  for( BOARD_ITEM* item : rejected )
1615  aCollector.Remove( item );
1616 }
1617 
1618 
1620 {
1621  if( aItem->IsLocked() && !m_filter.lockedItems && aItem->Type() != PCB_PAD_T )
1622  return false;
1623 
1624  switch( aItem->Type() )
1625  {
1626  case PCB_FOOTPRINT_T:
1627  if( !m_filter.footprints )
1628  return false;
1629 
1630  break;
1631 
1632  case PCB_PAD_T:
1633  if( !m_filter.pads )
1634  return false;
1635 
1636  break;
1637 
1638  case PCB_TRACE_T:
1639  case PCB_ARC_T:
1640  if( !m_filter.tracks )
1641  return false;
1642 
1643  break;
1644 
1645  case PCB_VIA_T:
1646  if( !m_filter.vias )
1647  return false;
1648 
1649  break;
1650 
1651  case PCB_FP_ZONE_T:
1652  case PCB_ZONE_T:
1653  {
1654  ZONE* zone = static_cast<ZONE*>( aItem );
1655 
1656  if( ( !m_filter.zones && !zone->GetIsRuleArea() )
1657  || ( !m_filter.keepouts && zone->GetIsRuleArea() ) )
1658  {
1659  return false;
1660  }
1661  }
1662  break;
1663 
1664  case PCB_FP_SHAPE_T:
1665  case PCB_SHAPE_T:
1666  case PCB_TARGET_T:
1667  if( !m_filter.graphics )
1668  return false;
1669 
1670  break;
1671 
1672  case PCB_FP_TEXT_T:
1673  case PCB_TEXT_T:
1674  if( !m_filter.text )
1675  return false;
1676 
1677  break;
1678 
1679  case PCB_DIM_ALIGNED_T:
1680  case PCB_DIM_CENTER_T:
1681  case PCB_DIM_ORTHOGONAL_T:
1682  case PCB_DIM_LEADER_T:
1683  if( !m_filter.dimensions )
1684  return false;
1685 
1686  break;
1687 
1688  default:
1689  if( !m_filter.otherItems )
1690  return false;
1691  }
1692 
1693  return true;
1694 }
1695 
1696 
1697 void PCB_SELECTION_TOOL::ClearSelection( bool aQuietMode )
1698 {
1699  if( m_selection.Empty() )
1700  return;
1701 
1702  while( m_selection.GetSize() )
1703  unhighlight( static_cast<BOARD_ITEM*>( m_selection.Front() ), SELECTED, &m_selection );
1704 
1705  view()->Update( &m_selection );
1706 
1707  m_selection.SetIsHover( false );
1709 
1710  // Inform other potentially interested tools
1711  if( !aQuietMode )
1712  {
1715  }
1716 }
1717 
1718 
1720 {
1721  m_selection.Clear();
1722 
1723  bool enteredGroupFound = false;
1724 
1725  INSPECTOR_FUNC inspector =
1726  [&]( EDA_ITEM* item, void* testData )
1727  {
1728  if( item->IsSelected() )
1729  {
1730  EDA_ITEM* parent = item->GetParent();
1731 
1732  // Let selected parents handle their children.
1733  if( parent && parent->IsSelected() )
1734  return SEARCH_RESULT::CONTINUE;
1735 
1736  highlight( (BOARD_ITEM*) item, SELECTED, &m_selection );
1737  }
1738 
1739  if( item == m_enteredGroup )
1740  {
1741  item->SetFlags( ENTERED );
1742  enteredGroupFound = true;
1743  }
1744  else
1745  {
1746  item->ClearFlags( ENTERED );
1747  }
1748 
1749  return SEARCH_RESULT::CONTINUE;
1750  };
1751 
1754 
1755  if( !enteredGroupFound )
1756  {
1758  m_enteredGroup = nullptr;
1759  }
1760 }
1761 
1762 
1764 {
1765  GENERAL_COLLECTOR* collector = aEvent.Parameter<GENERAL_COLLECTOR*>();
1766 
1767  doSelectionMenu( collector );
1768 
1769  return 0;
1770 }
1771 
1772 
1774 {
1775  BOARD_ITEM* current = nullptr;
1776  PCB_SELECTION highlightGroup;
1777  bool selectAll = false;
1778  bool expandSelection = false;
1779 
1780  highlightGroup.SetLayer( LAYER_SELECT_OVERLAY );
1781  getView()->Add( &highlightGroup );
1782 
1783  do
1784  {
1786  if( expandSelection )
1787  aCollector->Combine();
1788 
1789  expandSelection = false;
1790 
1791  int limit = std::min( 9, aCollector->GetCount() );
1792  ACTION_MENU menu( true );
1793 
1794  for( int i = 0; i < limit; ++i )
1795  {
1796  wxString text;
1797  BOARD_ITEM* item = ( *aCollector )[i];
1799 
1800  wxString menuText = wxString::Format( "&%d. %s\t%d", i + 1, text, i + 1 );
1801  menu.Add( menuText, i + 1, item->GetMenuImage() );
1802  }
1803 
1804  menu.AppendSeparator();
1805  menu.Add( _( "Select &All\tA" ), limit + 1, BITMAPS::INVALID_BITMAP );
1806 
1807  if( !expandSelection && aCollector->HasAdditionalItems() )
1808  menu.Add( _( "&Expand Selection\tE" ), limit + 2, BITMAPS::INVALID_BITMAP );
1809 
1810  if( aCollector->m_MenuTitle.Length() )
1811  {
1812  menu.SetTitle( aCollector->m_MenuTitle );
1813  menu.SetIcon( BITMAPS::info );
1814  menu.DisplayTitle( true );
1815  }
1816  else
1817  {
1818  menu.DisplayTitle( false );
1819  }
1820 
1821  SetContextMenu( &menu, CMENU_NOW );
1822 
1823  while( TOOL_EVENT* evt = Wait() )
1824  {
1825  if( evt->Action() == TA_CHOICE_MENU_UPDATE )
1826  {
1827  if( selectAll )
1828  {
1829  for( int i = 0; i < aCollector->GetCount(); ++i )
1830  unhighlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
1831  }
1832  else if( current )
1833  unhighlight( current, BRIGHTENED, &highlightGroup );
1834 
1835  int id = *evt->GetCommandId();
1836 
1837  // User has pointed an item, so show it in a different way
1838  if( id > 0 && id <= limit )
1839  {
1840  current = ( *aCollector )[id - 1];
1841  highlight( current, BRIGHTENED, &highlightGroup );
1842  }
1843  else
1844  current = nullptr;
1845 
1846  // User has pointed on the "Select All" option
1847  if( id == limit + 1 )
1848  {
1849  for( int i = 0; i < aCollector->GetCount(); ++i )
1850  highlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
1851  selectAll = true;
1852  }
1853  else
1854  selectAll = false;
1855  }
1856  else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
1857  {
1858  if( selectAll )
1859  {
1860  for( int i = 0; i < aCollector->GetCount(); ++i )
1861  unhighlight( ( *aCollector )[i], BRIGHTENED, &highlightGroup );
1862  }
1863  else if( current )
1864  unhighlight( current, BRIGHTENED, &highlightGroup );
1865 
1866  OPT<int> id = evt->GetCommandId();
1867 
1868  // User has selected the "Select All" option
1869  if( id == limit + 1 )
1870  {
1871  selectAll = true;
1872  current = nullptr;
1873  }
1874  else if( id == limit + 2 )
1875  {
1876  expandSelection = true;
1877  selectAll = false;
1878  current = nullptr;
1879  }
1880  // User has selected an item, so this one will be returned
1881  else if( id && ( *id > 0 ) && ( *id <= limit ) )
1882  {
1883  selectAll = false;
1884  current = ( *aCollector )[*id - 1];
1885  }
1886  else
1887  {
1888  selectAll = false;
1889  current = nullptr;
1890  }
1891  }
1892  else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
1893  {
1894  break;
1895  }
1896  }
1897  } while( expandSelection );
1898 
1899  getView()->Remove( &highlightGroup );
1900 
1901  if( selectAll )
1902  return true;
1903  else if( current )
1904  {
1905  aCollector->Empty();
1906  aCollector->Append( current );
1907  return true;
1908  }
1909 
1910  return false;
1911 }
1912 
1913 
1914 bool PCB_SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibilityOnly ) const
1915 {
1916  const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
1917 
1918  if( settings->GetHighContrast() )
1919  {
1920  std::set<unsigned int> activeLayers = settings->GetHighContrastLayers();
1921  bool onActiveLayer = false;
1922 
1923  for( unsigned int layer : activeLayers )
1924  {
1925  // NOTE: Only checking the regular layers (not GAL meta-layers)
1926  if( layer < PCB_LAYER_ID_COUNT && aItem->IsOnLayer( ToLAYER_ID( layer ) ) )
1927  {
1928  onActiveLayer = true;
1929  break;
1930  }
1931  }
1932 
1933  if( !onActiveLayer ) // We do not want to select items that are in the background
1934  return false;
1935  }
1936 
1937  if( aItem->Type() == PCB_FOOTPRINT_T )
1938  {
1939  // In footprint editor, we do not want to select the footprint itself.
1940  if( m_isFootprintEditor )
1941  return false;
1942 
1943  // Allow selection of footprints if some part of the footprint is visible.
1944 
1945  const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
1946 
1947  for( const BOARD_ITEM* item : footprint->GraphicalItems() )
1948  {
1949  if( Selectable( item, true ) )
1950  return true;
1951  }
1952 
1953  for( const PAD* pad : footprint->Pads() )
1954  {
1955  if( Selectable( pad, true ) )
1956  return true;
1957  }
1958 
1959  for( const ZONE* zone : footprint->Zones() )
1960  {
1961  if( Selectable( zone, true ) )
1962  return true;
1963  }
1964 
1965  return false;
1966  }
1967  else if( aItem->Type() == PCB_GROUP_T )
1968  {
1969  PCB_GROUP* group = const_cast<PCB_GROUP*>( static_cast<const PCB_GROUP*>( aItem ) );
1970 
1971  // Similar to logic for footprint, a group is selectable if any of its members are.
1972  // (This recurses.)
1973  for( BOARD_ITEM* item : group->GetItems() )
1974  {
1975  if( Selectable( item, true ) )
1976  return true;
1977  }
1978 
1979  return false;
1980  }
1981 
1982  const ZONE* zone = nullptr;
1983  const VIA* via = nullptr;
1984  const PAD* pad = nullptr;
1985 
1986  switch( aItem->Type() )
1987  {
1988  case PCB_ZONE_T:
1989  case PCB_FP_ZONE_T:
1990  if( !board()->IsElementVisible( LAYER_ZONES ) )
1991  return false;
1992 
1993  zone = static_cast<const ZONE*>( aItem );
1994 
1995  // A footprint zone is only selectable within the footprint editor
1996  if( zone->GetParent()
1997  && zone->GetParent()->Type() == PCB_FOOTPRINT_T
1999  && !checkVisibilityOnly )
2000  {
2001  return false;
2002  }
2003 
2004  // zones can exist on multiple layers!
2005  if( !( zone->GetLayerSet() & board()->GetVisibleLayers() ).any() )
2006  return false;
2007 
2008  break;
2009 
2010  case PCB_TRACE_T:
2011  case PCB_ARC_T:
2012  if( !board()->IsElementVisible( LAYER_TRACKS ) )
2013  return false;
2014 
2015  if( m_isFootprintEditor )
2016  {
2017  if( !view()->IsLayerVisible( aItem->GetLayer() ) )
2018  return false;
2019  }
2020  else
2021  {
2022  if( !board()->IsLayerVisible( aItem->GetLayer() ) )
2023  return false;
2024  }
2025 
2026  break;
2027 
2028  case PCB_VIA_T:
2029  if( !board()->IsElementVisible( LAYER_VIAS ) )
2030  return false;
2031 
2032  via = static_cast<const VIA*>( aItem );
2033 
2034  // For vias it is enough if only one of its layers is visible
2035  if( !( board()->GetVisibleLayers() & via->GetLayerSet() ).any() )
2036  return false;
2037 
2038  break;
2039 
2040  case PCB_FP_TEXT_T:
2041  if( m_isFootprintEditor )
2042  {
2043  if( !view()->IsLayerVisible( aItem->GetLayer() ) )
2044  return false;
2045  }
2046  else
2047  {
2048  if( !view()->IsVisible( aItem ) )
2049  return false;
2050 
2051  if( !board()->IsLayerVisible( aItem->GetLayer() ) )
2052  return false;
2053  }
2054 
2055  break;
2056 
2057  case PCB_FP_SHAPE_T:
2058  if( m_isFootprintEditor )
2059  {
2060  if( !view()->IsLayerVisible( aItem->GetLayer() ) )
2061  return false;
2062  }
2063  else
2064  {
2065  // Footprint shape selections are only allowed in footprint editor mode.
2066  if( !checkVisibilityOnly )
2067  return false;
2068 
2069  if( !board()->IsLayerVisible( aItem->GetLayer() ) )
2070  return false;
2071  }
2072 
2073  break;
2074 
2075  case PCB_PAD_T:
2076  // Multiple selection is only allowed in footprint editor mode. In pcbnew, you have to
2077  // select footprint subparts one by one, rather than with a drag selection. This is so
2078  // you can pick up items under an (unlocked) footprint without also moving the
2079  // footprint's sub-parts.
2080  if( !m_isFootprintEditor && !checkVisibilityOnly )
2081  {
2082  if( m_multiple )
2083  return false;
2084  }
2085 
2086  pad = static_cast<const PAD*>( aItem );
2087 
2088  if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
2089  {
2090  // Check render mode (from the Items tab) first
2091  if( !board()->IsElementVisible( LAYER_PADS_TH ) )
2092  return false;
2093 
2094  // A pad's hole is visible on every layer the pad is visible on plus many layers the
2095  // pad is not visible on -- so we only need to check for any visible hole layers.
2096  if( !( board()->GetVisibleLayers() & LSET::PhysicalLayersMask() ).any() )
2097  return false;
2098  }
2099  else
2100  {
2101  // Check render mode (from the Items tab) first
2102  if( pad->IsOnLayer( F_Cu ) && !board()->IsElementVisible( LAYER_PAD_FR ) )
2103  return false;
2104  else if( pad->IsOnLayer( B_Cu ) && !board()->IsElementVisible( LAYER_PAD_BK ) )
2105  return false;
2106 
2107  if( !( pad->GetLayerSet() & board()->GetVisibleLayers() ).any() )
2108  return false;
2109  }
2110 
2111  break;
2112 
2113  // These are not selectable
2114  case PCB_NETINFO_T:
2115  case NOT_USED:
2116  case TYPE_NOT_INIT:
2117  return false;
2118 
2119  default: // Suppress warnings
2120  break;
2121  }
2122 
2123  return aItem->ViewGetLOD( aItem->GetLayer(), view() ) < view()->GetScale();
2124 }
2125 
2126 
2128 {
2129  if( aItem->IsSelected() )
2130  return;
2131 
2132  if( aItem->Type() == PCB_PAD_T )
2133  {
2134  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem->GetParent() );
2135 
2136  if( m_selection.Contains( footprint ) )
2137  return;
2138  }
2139 
2140  highlight( aItem, SELECTED, &m_selection );
2141 }
2142 
2143 
2145 {
2146  unhighlight( aItem, SELECTED, &m_selection );
2147 }
2148 
2149 
2150 void PCB_SELECTION_TOOL::highlight( BOARD_ITEM* aItem, int aMode, PCB_SELECTION* aGroup )
2151 {
2152  if( aGroup )
2153  aGroup->Add( aItem );
2154 
2155  highlightInternal( aItem, aMode, aGroup != nullptr );
2156  view()->Update( aItem, KIGFX::REPAINT );
2157 
2158  // Many selections are very temporal and updating the display each time just
2159  // creates noise.
2160  if( aMode == BRIGHTENED )
2162 }
2163 
2164 
2165 void PCB_SELECTION_TOOL::highlightInternal( BOARD_ITEM* aItem, int aMode, bool aUsingOverlay )
2166 {
2167  if( aMode == SELECTED )
2168  aItem->SetSelected();
2169  else if( aMode == BRIGHTENED )
2170  aItem->SetBrightened();
2171 
2172  if( aUsingOverlay )
2173  view()->Hide( aItem, true ); // Hide the original item, so it is shown only on overlay
2174 
2175  if( aItem->Type() == PCB_FOOTPRINT_T )
2176  {
2177  static_cast<FOOTPRINT*>( aItem )->RunOnChildren(
2178  [&]( BOARD_ITEM* aChild )
2179  {
2180  highlightInternal( aChild, aMode, aUsingOverlay );
2181  } );
2182  }
2183  else if( aItem->Type() == PCB_GROUP_T )
2184  {
2185  static_cast<PCB_GROUP*>( aItem )->RunOnChildren(
2186  [&]( BOARD_ITEM* aChild )
2187  {
2188  highlightInternal( aChild, aMode, aUsingOverlay );
2189  } );
2190  }
2191 }
2192 
2193 
2195 {
2196  if( aGroup )
2197  aGroup->Remove( aItem );
2198 
2199  unhighlightInternal( aItem, aMode, aGroup != nullptr );
2200  view()->Update( aItem, KIGFX::REPAINT );
2201 
2202  // Many selections are very temporal and updating the display each time just
2203  // creates noise.
2204  if( aMode == BRIGHTENED )
2206 }
2207 
2208 
2209 void PCB_SELECTION_TOOL::unhighlightInternal( BOARD_ITEM* aItem, int aMode, bool aUsingOverlay )
2210 {
2211  if( aMode == SELECTED )
2212  aItem->ClearSelected();
2213  else if( aMode == BRIGHTENED )
2214  aItem->ClearBrightened();
2215 
2216  if( aUsingOverlay )
2217  view()->Hide( aItem, false ); // // Restore original item visibility
2218 
2219  if( aItem->Type() == PCB_FOOTPRINT_T )
2220  {
2221  static_cast<FOOTPRINT*>( aItem )->RunOnChildren(
2222  [&]( BOARD_ITEM* aChild )
2223  {
2224  unhighlightInternal( aChild, aMode, aUsingOverlay );
2225  } );
2226  }
2227  else if( aItem->Type() == PCB_GROUP_T )
2228  {
2229  static_cast<PCB_GROUP*>( aItem )->RunOnChildren(
2230  [&]( BOARD_ITEM* aChild )
2231  {
2232  unhighlightInternal( aChild, aMode, aUsingOverlay );
2233  } );
2234  }
2235 }
2236 
2237 
2239 {
2241  GENERAL_COLLECTOR collector;
2242 
2243  // Since we're just double-checking, we want a considerably sloppier check than the initial
2244  // selection (for which most tools use 5 pixels). So we increase this to an effective 20
2245  // pixels by artificially inflating the value of a pixel by 4X.
2246  guide.SetOnePixelInIU( guide.OnePixelInIU() * 4 );
2247 
2250  (wxPoint) aPoint, guide );
2251 
2252  for( int i = collector.GetCount() - 1; i >= 0; --i )
2253  {
2254  BOARD_ITEM* item = collector[i];
2255 
2256  if( item->IsSelected() && item->HitTest( (wxPoint) aPoint, 5 * guide.OnePixelInIU() ) )
2257  return true;
2258  }
2259 
2260  return false;
2261 }
2262 
2263 
2264 int PCB_SELECTION_TOOL::hitTestDistance( const wxPoint& aWhere, BOARD_ITEM* aItem,
2265  int aMaxDistance ) const
2266 {
2267  BOX2D viewportD = getView()->GetViewport();
2268  BOX2I viewport( VECTOR2I( viewportD.GetPosition() ), VECTOR2I( viewportD.GetSize() ) );
2269  int distance = INT_MAX;
2270  SEG loc( aWhere, aWhere );
2271 
2272  switch( aItem->Type() )
2273  {
2274  case PCB_TEXT_T:
2275  {
2276  PCB_TEXT* text = static_cast<PCB_TEXT*>( aItem );
2277  text->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance );
2278  }
2279  break;
2280 
2281  case PCB_FP_TEXT_T:
2282  {
2283  FP_TEXT* text = static_cast<FP_TEXT*>( aItem );
2284  text->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance );
2285  }
2286  break;
2287 
2288  case PCB_ZONE_T:
2289  {
2290  ZONE* zone = static_cast<ZONE*>( aItem );
2291 
2292  // Zone borders are very specific
2293  if( zone->HitTestForEdge( aWhere, aMaxDistance / 2 ) )
2294  distance = 0;
2295  else if( zone->HitTestForEdge( aWhere, aMaxDistance ) )
2296  distance = aMaxDistance / 2;
2297  else
2298  aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
2299  }
2300  break;
2301 
2302  case PCB_FOOTPRINT_T:
2303  {
2304  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
2305  EDA_RECT bbox = footprint->GetBoundingBox( false, false );
2306 
2307  footprint->GetBoundingHull().Collide( loc, aMaxDistance, &distance );
2308 
2309  // Consider footprints larger than the viewport only as a last resort
2310  if( bbox.GetHeight() > viewport.GetHeight() || bbox.GetWidth() > viewport.GetWidth() )
2311  distance = INT_MAX / 2;
2312  }
2313  break;
2314 
2315  case PCB_MARKER_T:
2316  {
2317  PCB_MARKER* marker = static_cast<PCB_MARKER*>( aItem );
2318  SHAPE_LINE_CHAIN polygon;
2319 
2320  marker->ShapeToPolygon( polygon );
2321  polygon.Move( marker->GetPos() );
2322  polygon.Collide( loc, aMaxDistance, &distance );
2323  }
2324  break;
2325 
2326  case PCB_GROUP_T:
2327  {
2328  PCB_GROUP* group = static_cast<PCB_GROUP*>( aItem );
2329 
2330  for( BOARD_ITEM* member : group->GetItems() )
2331  distance = std::min( distance, hitTestDistance( aWhere, member, aMaxDistance ) );
2332  }
2333  break;
2334 
2335  default:
2336  aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
2337  break;
2338  }
2339 
2340  return distance;
2341 }
2342 
2343 
2344 // The general idea here is that if the user clicks directly on a small item inside a larger
2345 // one, then they want the small item. The quintessential case of this is clicking on a pad
2346 // within a footprint, but we also apply it for text within a footprint, footprints within
2347 // larger footprints, and vias within either larger pads or longer tracks.
2348 //
2349 // These "guesses" presume there is area within the larger item to click in to select it. If
2350 // an item is mostly covered by smaller items within it, then the guesses are inappropriate as
2351 // there might not be any area left to click to select the larger item. In this case we must
2352 // leave the items in the collector and bring up a Selection Clarification menu.
2353 //
2354 // We currently check for pads and text mostly covering a footprint, but we don't check for
2355 // smaller footprints mostly covering a larger footprint.
2356 //
2358  const VECTOR2I& aWhere ) const
2359 {
2360  std::set<BOARD_ITEM*> preferred;
2361  std::set<BOARD_ITEM*> rejected;
2362  wxPoint where( aWhere.x, aWhere.y );
2363 
2364  PCB_LAYER_ID activeLayer = (PCB_LAYER_ID) view()->GetTopLayer();
2365  LSET silkLayers( 2, B_SilkS, F_SilkS );
2366 
2367  if( silkLayers[activeLayer] )
2368  {
2369  for( int i = 0; i < aCollector.GetCount(); ++i )
2370  {
2371  BOARD_ITEM* item = aCollector[i];
2372  KICAD_T type = item->Type();
2373 
2374  if( ( type == PCB_FP_TEXT_T || type == PCB_TEXT_T || type == PCB_SHAPE_T )
2375  && silkLayers[item->GetLayer()] )
2376  {
2377  preferred.insert( item );
2378  }
2379  }
2380 
2381  if( preferred.size() > 0 )
2382  {
2383  aCollector.Empty();
2384 
2385  for( BOARD_ITEM* item : preferred )
2386  aCollector.Append( item );
2387 
2388  return;
2389  }
2390  }
2391 
2392  // Prefer exact hits to sloppy ones
2393 
2394  constexpr int MAX_SLOP = 5;
2395 
2396  int pixel = (int) aCollector.GetGuide()->OnePixelInIU();
2397  int minSlop = INT_MAX;
2398 
2399  std::map<BOARD_ITEM*, int> itemsBySloppiness;
2400 
2401  for( int i = 0; i < aCollector.GetCount(); ++i )
2402  {
2403  BOARD_ITEM* item = aCollector[i];
2404  int itemSlop = hitTestDistance( where, item, MAX_SLOP * pixel );
2405 
2406  itemsBySloppiness[ item ] = itemSlop;
2407 
2408  if( itemSlop < minSlop )
2409  minSlop = itemSlop;
2410  }
2411 
2412  // Prune sloppier items
2413 
2414  if( minSlop < INT_MAX )
2415  {
2416  for( std::pair<BOARD_ITEM*, int> pair : itemsBySloppiness )
2417  {
2418  if( pair.second > minSlop + pixel )
2419  aCollector.Transfer( pair.first );
2420  }
2421  }
2422 
2423  // If the user clicked on a small item within a much larger one then it's pretty clear
2424  // they're trying to select the smaller one.
2425 
2426  constexpr double sizeRatio = 1.5;
2427 
2428  std::vector<std::pair<BOARD_ITEM*, double>> itemsByArea;
2429 
2430  for( int i = 0; i < aCollector.GetCount(); ++i )
2431  {
2432  BOARD_ITEM* item = aCollector[i];
2433  double area;
2434 
2435  if( ( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2436  && static_cast<ZONE*>( item )->HitTestForEdge( where, MAX_SLOP * pixel / 2 ) )
2437  {
2438  // Zone borders are very specific, so make them "small"
2439  area = MAX_SLOP * SEG::Square( pixel );
2440  }
2441  else if( item->Type() == PCB_VIA_T )
2442  {
2443  // Vias rarely hide other things, and we don't want them deferring to short track
2444  // segments -- so make them artificially small by dropping the π from πr².
2445  area = SEG::Square( static_cast<VIA*>( item )->GetDrill() / 2 );
2446  }
2447  else
2448  {
2449  try
2450  {
2451  area = FOOTPRINT::GetCoverageArea( item, aCollector );
2452  }
2453  catch( const ClipperLib::clipperException& e )
2454  {
2455  wxLogError( "A clipper exception %s was detected.", e.what() );
2456  }
2457  }
2458 
2459  itemsByArea.emplace_back( item, area );
2460  }
2461 
2462  std::sort( itemsByArea.begin(), itemsByArea.end(),
2463  []( const std::pair<BOARD_ITEM*, double>& lhs,
2464  const std::pair<BOARD_ITEM*, double>& rhs ) -> bool
2465  {
2466  return lhs.second < rhs.second;
2467  } );
2468 
2469  bool rejecting = false;
2470 
2471  for( int i = 1; i < (int) itemsByArea.size(); ++i )
2472  {
2473  if( itemsByArea[i].second > itemsByArea[i-1].second * sizeRatio )
2474  rejecting = true;
2475 
2476  if( rejecting )
2477  rejected.insert( itemsByArea[i].first );
2478  }
2479 
2480  // Special case: if a footprint is completely covered with other features then there's no
2481  // way to select it -- so we need to leave it in the list for user disambiguation.
2482 
2483  constexpr double maxCoverRatio = 0.70;
2484 
2485  for( int i = 0; i < aCollector.GetCount(); ++i )
2486  {
2487  if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aCollector[i] ) )
2488  {
2489  if( footprint->CoverageRatio( aCollector ) > maxCoverRatio )
2490  rejected.erase( footprint );
2491  }
2492  }
2493 
2494  // Hopefully we've now got what the user wanted.
2495 
2496  if( (unsigned) aCollector.GetCount() > rejected.size() ) // do not remove everything
2497  {
2498  for( BOARD_ITEM* item : rejected )
2499  aCollector.Transfer( item );
2500  }
2501 
2502  // Finally, what we are left with is a set of items of similar coverage area. We now reject
2503  // any that are not on the active layer, to reduce the number of disambiguation menus shown.
2504  // If the user wants to force-disambiguate, they can either switch layers or use the modifier
2505  // key to force the menu.
2506  if( aCollector.GetCount() > 1 )
2507  {
2508  bool haveItemOnActive = false;
2509  rejected.clear();
2510 
2511  for( int i = 0; i < aCollector.GetCount(); ++i )
2512  {
2513  if( !aCollector[i]->IsOnLayer( activeLayer ) )
2514  rejected.insert( aCollector[i] );
2515  else
2516  haveItemOnActive = true;
2517  }
2518 
2519  if( haveItemOnActive )
2520  for( BOARD_ITEM* item : rejected )
2521  aCollector.Transfer( item );
2522  }
2523 }
2524 
2525 
2527  bool aMultiselect ) const
2528 {
2529  std::unordered_set<BOARD_ITEM*> toAdd;
2530 
2531  // Set TEMP_SELECTED on all parents which are included in the GENERAL_COLLECTOR. This
2532  // algorithm is O3n, whereas checking for the parent inclusion could potentially be On^2.
2533 
2534  for( int j = 0; j < aCollector.GetCount(); j++ )
2535  {
2536  if( aCollector[j]->GetParent() )
2537  aCollector[j]->GetParent()->ClearFlags( TEMP_SELECTED );
2538  }
2539 
2540  if( aMultiselect )
2541  {
2542  for( int j = 0; j < aCollector.GetCount(); j++ )
2543  aCollector[j]->SetFlags( TEMP_SELECTED );
2544  }
2545 
2546  for( int j = 0; j < aCollector.GetCount(); )
2547  {
2548  BOARD_ITEM* item = aCollector[j];
2549  BOARD_ITEM* parent = item->GetParent();
2550  BOARD_ITEM* start = item;
2551 
2552  if( !m_isFootprintEditor && parent && parent->Type() == PCB_FOOTPRINT_T )
2553  start = parent;
2554 
2555  // If any element is a member of a group, replace those elements with the top containing
2556  // group.
2558 
2559  if( aTop )
2560  {
2561  if( aTop != item )
2562  {
2563  toAdd.insert( aTop );
2564  aTop->SetFlags( TEMP_SELECTED );
2565 
2566  aCollector.Remove( item );
2567  continue;
2568  }
2569  }
2570  else if( m_enteredGroup
2572  {
2573  // If a group is entered, disallow selections of objects outside the group.
2574  aCollector.Remove( item );
2575  continue;
2576  }
2577 
2578  // Footprints are a bit easier as they can't be nested.
2579  if( parent && parent->GetFlags() & TEMP_SELECTED )
2580  {
2581  // Remove children of selected items
2582  aCollector.Remove( item );
2583  continue;
2584  }
2585 
2586  ++j;
2587  }
2588 
2589  for( BOARD_ITEM* item : toAdd )
2590  {
2591  if( !aCollector.HasItem( item ) )
2592  aCollector.Append( item );
2593  }
2594 }
2595 
2596 
2598 {
2599  getView()->Update( &m_selection );
2601 
2602  return 0;
2603 }
2604 
2605 
2607 {
2608  ACTION_MENU* actionMenu = aEvent.Parameter<ACTION_MENU*>();
2609  CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( actionMenu );
2610 
2611  if( conditionalMenu )
2612  conditionalMenu->Evaluate( m_selection );
2613 
2614  if( actionMenu )
2615  actionMenu->UpdateAll();
2616 
2617  return 0;
2618 }
2619 
2620 
2622 {
2624 
2628 
2634 
2635  Go( &PCB_SELECTION_TOOL::find, ACTIONS::find.MakeEvent() );
2636 
2645 
2647 }
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition: pcb_actions.h:70
void Empty()
Clear the list.
Definition: collector.h:95
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:63
const GENERAL_COLLECTORS_GUIDE getCollectorsGuide() const
static TOOL_ACTION selectionActivate
Activation of the selection tool.
Definition: pcb_actions.h:57
void Hide(VIEW_ITEM *aItem, bool aHide=true)
Temporarily hide the item in the view (e.g.
Definition: view.cpp:1470
void ClearReferencePoint()
Definition: selection.h:269
int selectSameSheet(const TOOL_EVENT &aEvent)
Find dialog callback.
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:1749
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:282
Filled polygons are shown.
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:603
bool AddItem(BOARD_ITEM *aItem)
Add item to group.
Definition: pcb_group.cpp:38
static const KICAD_T FootprintItems[]
A scan list for primary footprint items.
Definition: collectors.h:294
Definition: track.h:343
int CursorSelection(const TOOL_EVENT &aEvent)
Clear current selection event handler.
static const TOOL_EVENT SelectedEvent
Definition: actions.h:201
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition: view.cpp:514
void SetEnd(VECTOR2I aEnd)
Set the current end of the rectangle (the corner that moves with the cursor.
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:96
SHAPE_POLY_SET GetBoundingHull() const
Return a bounding polygon for the shapes and pads in the footprint.
Definition: footprint.cpp:739
void SetOnePixelInIU(double aValue)
Definition: collectors.h:609
class ALIGNED_DIMENSION, a linear dimension (graphic item)
Definition: typeinfo.h:100
virtual void Clear() override
Remove all the stored items from the group.
Definition: selection.h:96
void ForceRefresh()
Force a redraw.
class 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:431
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:268
void SetIgnoreBlindBuriedVias(bool ignore)
Definition: collectors.h:597
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:173
const KIID_PATH & GetPath() const
Definition: footprint.h:199
Model changes (required full reload)
Definition: tool_base.h:81
multilayer pads, usually with holes
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:202
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:730
Defines the structure of a menu based on ACTIONs.
Definition: action_menu.h:47
int find(const TOOL_EVENT &aEvent)
Invoke filter dialog and modify current selection.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:82
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:451
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:108
void SetOrigin(VECTOR2I aOrigin)
void ClearSelected()
Definition: eda_item.h:182
const wxPoint & GetStart() const
Definition: track.h:116
int SelectionMenu(const TOOL_EVENT &aEvent)
Show a popup menu to trim the COLLECTOR passed as aEvent's parameter down to a single item.
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:567
virtual double OnePixelInIU() const =0
void SetIgnoreModulesVals(bool ignore)
Definition: collectors.h:585
void SetIgnoreMicroVias(bool ignore)
Definition: collectors.h:600
Control for copper zone opacity/visibility (color ignored)
Stop at any place where more than two traces meet.
class CENTER_DIMENSION, a center point marking (graphic item)
Definition: typeinfo.h:102
void SetIgnoreModulesOnBack(bool ignore)
Definition: collectors.h:555
static TOOL_ACTION unselectItem
Definition: pcb_actions.h:67
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:81
static TOOL_ACTION dragFreeAngle
Definition: pcb_actions.h:142
void highlightInternal(BOARD_ITEM *aItem, int aHighlightMode, bool aUsingOverlay)
void Move(const VECTOR2I &aVector) override
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:215
static TOOL_ACTION unselectItems
Definition: pcb_actions.h:71
static TOOL_ACTION cancelInteractive
Definition: actions.h:65
DIALOG_FILTER_SELECTION::OPTIONS m_filterOpts
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:352
static TOOL_ACTION properties
Activation of the edit tool.
Definition: pcb_actions.h:121
Stop when reaching a pad.
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.cpp:289
int GetWidth() const
Definition: eda_rect.h:114
show footprints on back
void SetBrightened()
Definition: eda_item.h:180
static TOOL_ACTION zoomFitScreen
Definition: actions.h:99
class PCB_TEXT, text on a layer
Definition: typeinfo.h:91
void SetIgnoreModulesRefs(bool ignore)
Definition: collectors.h:591
LSET GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings Returns a bit-mask of all t...
Definition: board.cpp:461
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:141
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:1831
static TOOL_ACTION selectionMenu
Run a selection menu to select from a list of items.
Definition: pcb_actions.h:74
static TOOL_ACTION selectConnection
Select tracks between junctions or expands an existing selection to pads or the entire connection.
Definition: pcb_actions.h:78
class ARC, an arc track segment on a copper layer
Definition: typeinfo.h:97
void SetExclusiveOr(bool aExclusiveOr)
show footprints values (when texts are visibles)
bool selectPoint(const VECTOR2I &aWhere, bool aOnDrag=false, bool *aSelectionCancelledFlag=NULL, CLIENT_SELECTION_FILTER aClientFilter=NULL)
Select an item pointed by the parameter aWhere.
Definition: bitmap.cpp:63
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:141
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:608
virtual void Clear()
Remove all the stored items from the group.
Definition: view_group.cpp:75
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:100
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:210
virtual wxPoint GetPosition() const
Definition: eda_item.h:302
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.
static TOOL_ACTION find
Definition: actions.h:79
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
static SEG::ecoord Square(int a)
Definition: seg.h:123
#define TEMP_SELECTED
flag indicating that the structure has already selected
Definition: eda_item.h:115
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:115
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:206
bool HitTestForCorner(const wxPoint &refPos, int aAccuracy, SHAPE_POLY_SET::VERTEX_INDEX &aCornerHit) const
Test if the given wxPoint is near a corner.
Definition: zone.cpp:423
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition: view.h:207
static bool itemIsIncludedByFilter(const BOARD_ITEM &aItem, const BOARD &aBoard, const DIALOG_FILTER_SELECTION::OPTIONS &aFilterOptions)
Function itemIsIncludedByFilter()
class TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
PADS & Pads()
Definition: footprint.h:164
void SetIsHover(bool aIsHover)
Definition: selection.h:67
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.
virtual void Add(EDA_ITEM *aItem)
Definition: selection.h:77
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:70
PCB_BASE_FRAME * m_frame
void ClearBrightened()
Definition: eda_item.h:183
void Append(EDA_ITEM *item)
Add an item to the end of the list.
Definition: collector.h:105
void RebuildSelection()
Rebuild the selection from the EDA_ITEMs' selection flags.
void SetAdditive(bool aAdditive)
show footprints on front
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:275
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:1147
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:87
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:170
void UnbrightenItem(BOARD_ITEM *aItem)
int updateSelection(const TOOL_EVENT &aEvent)
Event handler to update the selection VIEW_ITEM.
void Transfer(int aIndex)
Move the item at aIndex (first position is 0) to the backup list.
Definition: collector.h:159
MOUSE_DRAG_ACTION
like PAD_PTH, but not plated
Container for display options like enable/disable some optional drawings.
PCB_LAYER_ID
A quick note on layer IDs:
#define BRIGHTENED
item is drawn with a bright contour
Definition: eda_item.h:131
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:343
Item needs to be redrawn.
Definition: view_item.h:57
LSET is a set of PCB_LAYER_IDs.
MOUSE_DRAG_ACTION GetDragAction() const
Indicates whether a drag should draw a selection rectangle or drag selected (or unselected) objects.
Definition: tools_holder.h:134
void SetCenter(const VECTOR2D &aCenter)
Set the center point of the VIEW (i.e.
Definition: view.cpp:580
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
void SetFlags(STATUS_FLAGS aMask)
Definition: eda_item.h:203
#define NULL
void SetSelected()
Definition: eda_item.h:179
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:543
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:70
TRACK_DRAG_ACTION m_TrackDragAction
KIGFX::VIEW_GROUP m_enteredGroupOverlay
bool dimensions
Dimension items.
void SetIgnorePadsOnFront(bool ignore)
Definition: collectors.h:573
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:574
virtual EDA_RECT GetBoundingBox() const
Definition: selection.h:180
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:1489
bool graphics
Graphic lines, shapes, polygons.
void SetIgnoreMTextsOnFront(bool ignore)
Definition: collectors.h:549
void AddItemToSel(BOARD_ITEM *aItem, bool aQuietMode=false)
Select all items on the board.
T Parameter() const
Return a non-standard parameter assigned to the event.
Definition: tool_event.h:448
#define ENTERED
indicates a group has been entered
Definition: eda_item.h:138
virtual int GetTopLayer() const
Definition: view.cpp:828
coord_type GetWidth() const
Definition: box2.h:197
Meta control for all pads opacity/visibility (color ignored)
#define SELECTED
Definition: eda_item.h:114
Generic, UI-independent tool event.
Definition: tool_event.h:173
void connectedItemFilter(const VECTOR2I &, GENERAL_COLLECTOR &aCollector, PCB_SELECTION_TOOL *sTool)
void SetMaximum()
Definition: box2.h:73
bool Contains(EDA_ITEM *aItem) const
Definition: selection.h:114
void findCallback(BOARD_ITEM *aItem)
Find an item.
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:416
const std::deque< EDA_ITEM * > GetItems() const
Definition: selection.h:133
KIGFX::PCB_VIEW * view() const
bool ToolStackIsEmpty()
Definition: tools_holder.h:117
bool HasItem(const EDA_ITEM *aItem) const
Tests if aItem has already been collected.
Definition: collector.h:203
static const TOOL_EVENT UninhibitSelectionEditing
Definition: actions.h:213
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:165
Items that may change while the view stays the same (noncached)
Definition: definitions.h:50
static const TOOL_EVENT ClearedEvent
Selected item had a property changed (except movement)
Definition: actions.h:203
DRAWINGS & GraphicalItems()
Definition: footprint.h:167
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:57
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:209
PCB_SELECTION m_selection
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
static TOOL_ACTION updateMenu
Definition: actions.h:170
virtual void Add(VIEW_ITEM *aItem)
Add an item to the group.
Definition: view_group.cpp:56
void SetIgnoreMTextsMarkedNoShow(bool ignore)
Definition: collectors.h:537
virtual KIGFX::VIEW_ITEM * GetItem(unsigned int aIdx) const override
Definition: selection.h:106
void ExitGroup(bool aSelectGroup=false)
Leave the currently entered group.
#define IS_MOVED
Item being moved.
Definition: eda_item.h:106
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:108
static TOOL_ACTION hideDynamicRatsnest
Definition: pcb_actions.h:460
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:81
int GetHeight() const
Definition: eda_rect.h:115
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.
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:122
class PCB_TARGET, a target (graphic item)
Definition: typeinfo.h:104
wxString m_MenuTitle
Definition: collector.h:250
class FOOTPRINT, a footprint
Definition: typeinfo.h:88
bool IsMirroredX() const
Return true if view is flipped across the X axis.
Definition: view.h:237
static TOOL_ACTION clearHighlight
Definition: pcb_actions.h:449
void SetSubtractive(bool aSubtractive)
const Vec & GetPosition() const
Definition: box2.h:194
bool IsElementVisible(GAL_LAYER_ID aLayer) const
Test whether a given element category is visible.
Definition: board.cpp:507
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition: selection.h:101
virtual void SetScale(double aScale, VECTOR2D aAnchor={ 0, 0 })
Set the scaling factor, zooming around a given anchor point.
Definition: view.cpp:554
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:41
static TOOL_ACTION filterSelection
Filter the items in the current selection (invokes dialog)
Definition: pcb_actions.h:93
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
void SetCallback(boost::function< void(BOARD_ITEM *)> aCallback)
Function to be called on each found event.
Definition: dialog_find.h:62
bool GetHighContrast() const
static LSET PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition: lset.cpp:849
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:453
virtual bool IsLocked() const
Definition: board_item.h:249
int SelectItem(const TOOL_EVENT &aEvent)
bool lockedItems
Allow selecting locked items.
bool selectCursor(bool aForceSelect=false, CLIENT_SELECTION_FILTER aClientFilter=NULL)
Select an item under the cursor unless there is something already selected or aSelectAlways is true.
class MARKER_PCB, a marker used to show something
Definition: typeinfo.h:98
bool HitTestForEdge(const wxPoint &refPos, int aAccuracy, SHAPE_POLY_SET::VERTEX_INDEX &aCornerHit) const
Test if the given wxPoint is near a segment defined by 2 corners.
Definition: zone.cpp:437
const int scale
smd pads, front layer
PCB_SELECTION & RequestSelection(CLIENT_SELECTION_FILTER aClientFilter, bool aConfirmLockedItems=false)
Return the current selection set, filtered according to aFlags and aClientFilter.
void setModifiersState(bool aShiftState, bool aCtrlState, bool aAltState)
Set the configuration of m_additive, m_subtractive, m_exclusive_or, m_skip_heuristics and m_highlight...
bool HasAdditionalItems()
Test if the collector has heuristic backup items.
Definition: collector.h:140
Meta control for all vias opacity/visibility.
static TOOL_ACTION highlightNet
Definition: pcb_actions.h:450
bool IsType(FRAME_T aType) const
void SetHighlight(bool aEnabled, int aNetcode=-1, bool aMulti=false)
Turns on/off highlighting.
static TOOL_ACTION selectOnSheetFromEeschema
Select all components on sheet from Eeschema crossprobing.
Definition: pcb_actions.h:87
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
#define _(s)
Definition: 3d_actions.cpp:33
SHAPE_LINE_CHAIN.
int hitTestDistance(const wxPoint &aWhere, BOARD_ITEM *aItem, int aMaxDistance) const
void SetIgnoreModulesOnFront(bool ignore)
Definition: collectors.h:561
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:241
void SetIgnoreThroughVias(bool ignore)
Definition: collectors.h:594
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:107
const EDA_RECT GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: footprint.cpp:631
virtual BITMAPS GetMenuImage() const
Return a pointer to an image to be used in menus.
Definition: eda_item.cpp:206
std::pair< VIEW_ITEM *, int > LAYER_ITEM_PAIR
Definition: view.h:72
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
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition: pcb_actions.h:66
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:128
The selection tool: currently supports:
currently selected items overlay
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:68
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:150
static const TOOL_EVENT InhibitSelectionEditing
Definition: actions.h:212
coord_type GetHeight() const
Definition: box2.h:198
void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction) const
Invoke a function on all members of the group.
Definition: pcb_group.cpp:327
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:78
const wxPoint & GetEnd() const
Definition: track.h:113
static TOOL_ACTION deselectNet
Remove all connections belonging to a single net from the active selection.
Definition: pcb_actions.h:84
boost::optional< T > OPT
Definition: optional.h:7
void ClearFlags(STATUS_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: eda_item.h:204
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:153
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition: view.cpp:1449
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:148
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...
bool itemPassesFilter(BOARD_ITEM *aItem)
class ORTHOGONAL_DIMENSION, a linear dimension constrained to x/y
Definition: typeinfo.h:103
class 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:322
const Vec & GetSize() const
Definition: box2.h:189
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:428
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:67
#define SKIP_STRUCT
flag indicating that the structure should be ignored
Definition: eda_item.h:118
A general implementation of a COLLECTORS_GUIDE.
Definition: collectors.h:381
bool selectionContains(const VECTOR2I &aPoint) const
Definition: pad.h:60
double GetScale() const
Definition: view.h:263
int ClearSelection(const TOOL_EVENT &aEvent)
STATUS_FLAGS GetFlags() const
Definition: eda_item.h:205
void FilterCollectedItems(GENERAL_COLLECTOR &aCollector)
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:168
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:173
static TOOL_ACTION selectAll
Definition: actions.h:73
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
void FocusOnItem(BOARD_ITEM *aItem)
#define IS_NEW
New item, just created.
Definition: eda_item.h:107
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:60
void SetIgnoreThroughHolePads(bool ignore)
Definition: collectors.h:579
show footprints references (when texts are visibles)
EDA_UNITS GetUserUnits() const
Return the user units currently in use.
void SetIgnoreZoneFills(bool ignore)
Definition: collectors.h:606
Definition: track.h:83
virtual double ViewGetLOD(int aLayer, VIEW *aView) const
Return the level of detail (LOD) of the item.
Definition: view_item.h:137
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:203
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:1503
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:163
static TOOL_ACTION selectSameSheet
Select all components on the same sheet as the selected footprint.
Definition: pcb_actions.h:90
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition: view.h:404
void SetIgnorePadsOnBack(bool ignore)
Definition: collectors.h:567
virtual void Remove(EDA_ITEM *aItem)
Definition: selection.h:88