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