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