1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2019 CERN
5  * Copyright (C) 2019-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
23  */
24 
25 #include <bitmaps.h>
26 #include <core/typeinfo.h>
27 #include <core/kicad_algo.h>
28 #include <geometry/shape_compound.h>
29 #include <ee_actions.h>
30 #include <ee_collectors.h>
31 #include <ee_selection_tool.h>
32 #include <eeschema_id.h> // For MAX_SELECT_ITEM_IDS
33 #include <symbol_edit_frame.h>
34 #include <lib_item.h>
35 #include <symbol_viewer_frame.h>
36 #include <math/util.h>
37 #include <geometry/shape_rect.h>
38 #include <menus_helpers.h>
39 #include <painter.h>
40 #include <preview_items/selection_area.h>
41 #include <sch_base_frame.h>
42 #include <sch_symbol.h>
43 #include <sch_field.h>
44 #include <sch_edit_frame.h>
45 #include <sch_item.h>
46 #include <sch_line.h>
47 #include <sch_junction.h>
48 #include <sch_sheet.h>
49 #include <sch_sheet_pin.h>
50 #include <lib_shape.h>
51 #include <schematic.h>
52 #include <tool/tool_event.h>
53 #include <tool/tool_manager.h>
54 #include <tools/ee_grid_helper.h>
55 #include <tools/ee_point_editor.h>
56 #include <tools/sch_line_wire_bus_tool.h>
57 #include <trigo.h>
58 #include <view/view.h>
59 #include <view/view_controls.h>
60 #include <wx/log.h>
61 
62 SELECTION_CONDITION EE_CONDITIONS::SingleSymbol = []( const SELECTION& aSel )
__anon2bab4fc20102( const SELECTION& aSel ) 63 {
64     if( aSel.GetSize() == 1 )
65     {
66         SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aSel.Front() );
67 
68         if( symbol )
69             return !symbol->GetLibSymbolRef() || !symbol->GetLibSymbolRef()->IsPower();
70     }
71 
72     return false;
73 };
74 
75 
76 SELECTION_CONDITION EE_CONDITIONS::SingleSymbolOrPower = []( const SELECTION& aSel )
__anon2bab4fc20202( const SELECTION& aSel ) 77 {
78     return aSel.GetSize() == 1 && aSel.Front()->Type() == SCH_SYMBOL_T;
79 };
80 
81 
82 SELECTION_CONDITION EE_CONDITIONS::SingleDeMorganSymbol = []( const SELECTION& aSel )
__anon2bab4fc20302( const SELECTION& aSel ) 83 {
84     if( aSel.GetSize() == 1 )
85     {
86         SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aSel.Front() );
87 
88         if( symbol )
89             return symbol->GetLibSymbolRef() && symbol->GetLibSymbolRef()->HasConversion();
90     }
91 
92     return false;
93 };
94 
95 
96 SELECTION_CONDITION EE_CONDITIONS::SingleMultiUnitSymbol = []( const SELECTION& aSel )
__anon2bab4fc20402( const SELECTION& aSel ) 97 {
98     if( aSel.GetSize() == 1 )
99     {
100         SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aSel.Front() );
101 
102         if( symbol )
103             return symbol->GetLibSymbolRef() && symbol->GetLibSymbolRef()->GetUnitCount() >= 2;
104     }
105 
106     return false;
107 };
108 
109 
110 #define HITTEST_THRESHOLD_PIXELS 5
111 
112 
EE_SELECTION_TOOL()113 EE_SELECTION_TOOL::EE_SELECTION_TOOL() :
114         TOOL_INTERACTIVE( "eeschema.InteractiveSelection" ),
115         m_frame( nullptr ),
116         m_nonModifiedCursor( KICURSOR::ARROW ),
117         m_isSymbolEditor( false ),
118         m_isSymbolViewer( false ),
119         m_unit( 0 ),
120         m_convert( 0 )
121 {
122     m_selection.Clear();
123 }
124 
125 
~EE_SELECTION_TOOL()126 EE_SELECTION_TOOL::~EE_SELECTION_TOOL()
127 {
128     getView()->Remove( &m_selection );
129 }
130 
131 
132 using E_C = EE_CONDITIONS;
133 
134 
Init()135 bool EE_SELECTION_TOOL::Init()
136 {
137     m_frame = getEditFrame<SCH_BASE_FRAME>();
138 
139     SYMBOL_VIEWER_FRAME* symbolViewerFrame = dynamic_cast<SYMBOL_VIEWER_FRAME*>( m_frame );
140     SYMBOL_EDIT_FRAME*   symbolEditorFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
141 
142     if( symbolEditorFrame )
143     {
144         m_isSymbolEditor = true;
145         m_unit = symbolEditorFrame->GetUnit();
146         m_convert = symbolEditorFrame->GetConvert();
147     }
148     else
149     {
150         m_isSymbolViewer = symbolViewerFrame != nullptr;
151     }
152 
153     static KICAD_T wireOrBusTypes[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT };
154     static KICAD_T connectedTypes[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T,
155                                         SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T, SCH_LABEL_T,
156                                         SCH_SHEET_PIN_T, SCH_PIN_T, EOT };
157 
158     auto wireSelection =      E_C::MoreThan( 0 ) && E_C::OnlyType( SCH_LINE_LOCATE_WIRE_T );
159     auto busSelection =       E_C::MoreThan( 0 ) && E_C::OnlyType( SCH_LINE_LOCATE_BUS_T );
160     auto wireOrBusSelection = E_C::MoreThan( 0 ) && E_C::OnlyTypes( wireOrBusTypes );
161     auto connectedSelection = E_C::MoreThan( 0 ) && E_C::OnlyTypes( connectedTypes );
162     auto sheetSelection =     E_C::Count( 1 )    && E_C::OnlyType( SCH_SHEET_T );
163 
164     auto schEditSheetPageNumberCondition =
165             [&] ( const SELECTION& aSel )
166             {
167                 if( m_isSymbolEditor || m_isSymbolViewer )
168                     return false;
169 
170                 return E_C::LessThan( 2 )( aSel ) && E_C::OnlyType( SCH_SHEET_T )( aSel );
171             };
172 
173     auto schEditCondition =
174             [this] ( const SELECTION& aSel )
175             {
176                 return !m_isSymbolEditor && !m_isSymbolViewer;
177             };
178 
179     auto belowRootSheetCondition =
180             [&]( const SELECTION& aSel )
181             {
182                 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
183 
184                 return editFrame
185                         && editFrame->GetCurrentSheet().Last() != &editFrame->Schematic().Root();
186             };
187 
188     auto haveSymbolCondition =
189             [&]( const SELECTION& sel )
190             {
191                 return m_isSymbolEditor &&
192                        static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
193             };
194 
195     auto& menu = m_menu.GetMenu();
196 
197     menu.AddItem( EE_ACTIONS::enterSheet,         sheetSelection && EE_CONDITIONS::Idle, 1 );
198     menu.AddItem( EE_ACTIONS::explicitCrossProbe, sheetSelection && EE_CONDITIONS::Idle, 1 );
199     menu.AddItem( EE_ACTIONS::leaveSheet,         belowRootSheetCondition, 1 );
200 
201     menu.AddSeparator( 100 );
202     menu.AddItem( EE_ACTIONS::drawWire,           schEditCondition && EE_CONDITIONS::Empty, 100 );
203     menu.AddItem( EE_ACTIONS::drawBus,            schEditCondition && EE_CONDITIONS::Empty, 100 );
204 
205     menu.AddSeparator( 100 );
206     menu.AddItem( EE_ACTIONS::finishWire,         SCH_LINE_WIRE_BUS_TOOL::IsDrawingWire, 100 );
207 
208     menu.AddSeparator( 100 );
209     menu.AddItem( EE_ACTIONS::finishBus,          SCH_LINE_WIRE_BUS_TOOL::IsDrawingBus, 100 );
210 
211     menu.AddSeparator( 200 );
212     menu.AddItem( EE_ACTIONS::selectConnection,   wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
213     menu.AddItem( EE_ACTIONS::placeJunction,      wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
214     menu.AddItem( EE_ACTIONS::placeLabel,         wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
215     menu.AddItem( EE_ACTIONS::placeGlobalLabel,   wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
216     menu.AddItem( EE_ACTIONS::placeHierLabel,     wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
217     menu.AddItem( EE_ACTIONS::breakWire,          wireSelection && EE_CONDITIONS::Idle, 250 );
218     menu.AddItem( EE_ACTIONS::breakBus,           busSelection && EE_CONDITIONS::Idle, 250 );
219     menu.AddItem( EE_ACTIONS::importSingleSheetPin, sheetSelection && EE_CONDITIONS::Idle, 250 );
220     menu.AddItem( EE_ACTIONS::assignNetclass,     connectedSelection && EE_CONDITIONS::Idle, 250 );
221     menu.AddItem( EE_ACTIONS::editPageNumber,     schEditSheetPageNumberCondition, 250 );
222 
223     menu.AddSeparator( 400 );
224     menu.AddItem( EE_ACTIONS::symbolProperties,
225                   haveSymbolCondition && EE_CONDITIONS::Empty, 400 );
226     menu.AddItem( EE_ACTIONS::pinTable,
227                   haveSymbolCondition && EE_CONDITIONS::Empty, 400 );
228 
229     menu.AddSeparator( 1000 );
230     m_frame->AddStandardSubMenus( m_menu );
231 
232     m_disambiguateTimer.SetOwner( this );
233     Connect( wxEVT_TIMER, wxTimerEventHandler( EE_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
234 
235     return true;
236 }
237 
238 
Reset(RESET_REASON aReason)239 void EE_SELECTION_TOOL::Reset( RESET_REASON aReason )
240 {
241     m_frame = getEditFrame<SCH_BASE_FRAME>();
242 
243     if( aReason == TOOL_BASE::MODEL_RELOAD )
244     {
245         // Remove pointers to the selected items from containers without changing their
246         // properties (as they are already deleted while a new sheet is loaded)
247         m_selection.Clear();
248         getView()->GetPainter()->GetSettings()->SetHighlight( false );
249 
250         SYMBOL_EDIT_FRAME*   symbolEditFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
251         SYMBOL_VIEWER_FRAME* symbolViewerFrame = dynamic_cast<SYMBOL_VIEWER_FRAME*>( m_frame );
252 
253         if( symbolEditFrame )
254         {
255             m_isSymbolEditor = true;
256             m_unit = symbolEditFrame->GetUnit();
257             m_convert = symbolEditFrame->GetConvert();
258         }
259         else
260             m_isSymbolViewer = symbolViewerFrame != nullptr;
261     }
262     else
263         // Restore previous properties of selected items and remove them from containers
264         ClearSelection();
265 
266     // Reinsert the VIEW_GROUP, in case it was removed from the VIEW
267     getView()->Remove( &m_selection );
268     getView()->Add( &m_selection );
269 }
270 
271 
UpdateMenu(const TOOL_EVENT & aEvent)272 int EE_SELECTION_TOOL::UpdateMenu( const TOOL_EVENT& aEvent )
273 {
274     ACTION_MENU*      actionMenu = aEvent.Parameter<ACTION_MENU*>();
275     CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( actionMenu );
276 
277     if( conditionalMenu )
278         conditionalMenu->Evaluate( m_selection );
279 
280     if( actionMenu )
281         actionMenu->UpdateAll();
282 
283     return 0;
284 }
285 
286 
287 const KICAD_T movableSchematicItems[] =
288 {
289     SCH_MARKER_T,
290     SCH_JUNCTION_T,
291     SCH_NO_CONNECT_T,
292     SCH_BUS_BUS_ENTRY_T,
293     SCH_BUS_WIRE_ENTRY_T,
294     SCH_LINE_T,
295     SCH_BITMAP_T,
296     SCH_TEXT_T,
297     SCH_LABEL_T,
298     SCH_GLOBAL_LABEL_T,
299     SCH_HIER_LABEL_T,
300     SCH_FIELD_T,
301     SCH_SYMBOL_T,
302     SCH_SHEET_PIN_T,
303     SCH_SHEET_T,
304     EOT
305 };
306 
307 
308 const KICAD_T movableSymbolItems[] =
309 {
310     LIB_SHAPE_T,
311     LIB_TEXT_T,
312     LIB_PIN_T,
313     LIB_FIELD_T,
314     EOT
315 };
316 
317 
318 const KICAD_T movableSymbolAliasItems[] =
319 {
320     LIB_FIELD_T,
321     EOT
322 };
323 
324 
Main(const TOOL_EVENT & aEvent)325 int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
326 {
327     m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
328 
329     KIID lastRolloverItem = niluuid;
330 
331     // Main loop: keep receiving events
332     while( TOOL_EVENT* evt = Wait() )
333     {
334         bool displayWireCursor = false;
335         bool displayBusCursor = false;
336         bool displayLineCursor = false;
337         KIID rolloverItem = lastRolloverItem;
338 
339         // on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
340         setModifiersState( evt->Modifier( MD_SHIFT ), evt->Modifier( MD_CTRL ),
341                            evt->Modifier( MD_ALT ) );
342 
343         bool modifier_enabled = m_subtractive || m_additive || m_exclusive_or;
344 
345         MOUSE_DRAG_ACTION drag_action = m_frame->GetDragAction();
346         EE_GRID_HELPER grid( m_toolMgr );
347 
348         if( evt->IsMouseDown( BUT_LEFT ) )
349         {
350             // Avoid triggering when running under other tools
351             EE_POINT_EDITOR *pt_tool = m_toolMgr->GetTool<EE_POINT_EDITOR>();
352 
353             if( m_frame->ToolStackIsEmpty() && pt_tool && !pt_tool->HasPoint() )
354             {
355                 m_originalCursor = m_toolMgr->GetMousePosition();
356                 m_disambiguateTimer.StartOnce( 500 );
357             }
358         }
359         // Single click? Select single object
360         else if( evt->IsClick( BUT_LEFT ) )
361         {
362             // If the timer has stopped, then we have already run the disambiguate routine
363             // and we don't want to register an extra click here
364             if( !m_disambiguateTimer.IsRunning() )
365             {
366                 evt->SetPassEvent();
367                 continue;
368             }
369 
370             m_disambiguateTimer.Stop();
371 
372             if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
373                 schframe->FocusOnItem( nullptr );
374 
375             EE_COLLECTOR collector;
376             bool continueSelect = true;
377 
378             // Collect items at the clicked location (doesn't select them yet)
379             if( CollectHits( collector, evt->Position() ) )
380             {
381                 narrowSelection( collector, evt->Position(), false, false );
382 
383                 if( collector.GetCount() == 1 && !m_isSymbolEditor && !modifier_enabled )
384                 {
385                     // Check if we want to auto start wires
386                     VECTOR2I snappedCursorPos = grid.BestSnapAnchor( evt->Position(),
387                                                                      LAYER_CONNECTABLE, nullptr );
388 
389                     EE_POINT_EDITOR *pt_tool = m_toolMgr->GetTool<EE_POINT_EDITOR>();
390 
391                     if( m_frame->eeconfig()->m_Drawing.auto_start_wires
392                             && pt_tool && !pt_tool->HasPoint()
393                             && collector[0]->IsPointClickableAnchor( (wxPoint) snappedCursorPos ) )
394                     {
395                         OPT_TOOL_EVENT newEvt;
396                         SCH_CONNECTION* connection = collector[0]->Connection();
397 
398                         if( ( connection && ( connection->IsNet() || connection->IsUnconnected() ) )
399                             || collector[0]->Type() == SCH_SYMBOL_T )
400                         {
401                             newEvt = EE_ACTIONS::drawWire.MakeEvent();
402                         }
403                         else if( connection && connection->IsBus() )
404                         {
405                             newEvt = EE_ACTIONS::drawBus.MakeEvent();
406                         }
407                         else if( collector[0]->Type() == SCH_LINE_T
408                                  && static_cast<SCH_LINE*>( collector[0] )->IsGraphicLine() )
409                         {
410                             newEvt = EE_ACTIONS::drawLines.MakeEvent();
411                         }
412 
413                         auto* params = newEvt->Parameter<DRAW_SEGMENT_EVENT_PARAMS*>();
414                         auto* newParams = new DRAW_SEGMENT_EVENT_PARAMS();
415 
416                         *newParams= *params;
417                         newParams->quitOnDraw = true;
418                         newEvt->SetParameter( newParams );
419 
420                         getViewControls()->ForceCursorPosition( true, snappedCursorPos );
421                         newEvt->SetMousePosition( snappedCursorPos );
422                         newEvt->SetHasPosition( true );
423                         newEvt->SetForceImmediate( true );
424                         m_toolMgr->ProcessEvent( *newEvt );
425 
426                         continueSelect = false;
427                     }
428                     else if( collector[0]->IsHypertext() )
429                     {
430                         collector[0]->DoHypertextMenu( m_frame );
431                         continueSelect = false;
432                     }
433                 }
434             }
435 
436             if( continueSelect )
437             {
438                 // If we didn't click on an anchor, we perform a normal select, pass in the
439                 // items we previously collected
440                 selectPoint( collector, nullptr, nullptr, m_additive, m_subtractive,
441                              m_exclusive_or );
442 
443                 m_selection.SetIsHover( false );
444             }
445         }
446         else if( evt->IsClick( BUT_RIGHT ) )
447         {
448             m_disambiguateTimer.Stop();
449 
450             // right click? if there is any object - show the context menu
451             bool selectionCancelled = false;
452 
453             if( m_selection.Empty() )
454             {
455                 ClearSelection();
456                 SelectPoint( evt->Position(), EE_COLLECTOR::AllItems, nullptr,
457                              &selectionCancelled );
458                 m_selection.SetIsHover( true );
459             }
460             // If the cursor has moved off the bounding box of the selection by more than
461             // a grid square, check to see if there is another item available for selection
462             // under the cursor.  If there is, the user likely meant to get the context menu
463             // for that item.  If there is no new item, then keep the original selection and
464             // show the context menu for it.
465             else if( !m_selection.GetBoundingBox().Inflate(
466                         grid.GetGrid().x, grid.GetGrid().y ).Contains(
467                                 (wxPoint) evt->Position() ) )
468             {
469                 EE_SELECTION saved_selection = m_selection;
470 
471                 for( const auto& item : saved_selection )
472                     RemoveItemFromSel( item, true );
473 
474                 SelectPoint( evt->Position(), EE_COLLECTOR::AllItems, nullptr,
475                              &selectionCancelled );
476 
477                 if( m_selection.Empty() )
478                 {
479                     m_selection.SetIsHover( false );
480 
481                     for( const auto& item : saved_selection )
482                         AddItemToSel( item,  true);
483                 }
484                 else
485                 {
486                     m_selection.SetIsHover( true );
487                 }
488             }
489 
490             if( !selectionCancelled )
491                 m_menu.ShowContextMenu( m_selection );
492         }
493         else if( evt->IsDblClick( BUT_LEFT ) )
494         {
495             m_disambiguateTimer.Stop();
496 
497             // double click? Display the properties window
498             if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
499                 schframe->FocusOnItem( nullptr );
500 
501             if( m_selection.Empty() )
502                 SelectPoint( evt->Position() );
503 
504             EDA_ITEM* item = m_selection.Front();
505 
506             if( item && item->Type() == SCH_SHEET_T )
507                 m_toolMgr->RunAction( EE_ACTIONS::enterSheet );
508             else
509                 m_toolMgr->RunAction( EE_ACTIONS::properties );
510         }
511         else if( evt->IsDblClick( BUT_MIDDLE ) )
512         {
513             m_disambiguateTimer.Stop();
514 
515             // Middle double click?  Do zoom to fit or zoom to objects
516             if( evt->Modifier( MD_CTRL ) ) // Is CTRL key down?
517                 m_toolMgr->RunAction( ACTIONS::zoomFitObjects, true );
518             else
519                 m_toolMgr->RunAction( ACTIONS::zoomFitScreen, true );
520         }
521         else if( evt->IsDrag( BUT_LEFT ) )
522         {
523             m_disambiguateTimer.Stop();
524 
525             // Is another tool already moving a new object?  Don't allow a drag start
526             if( !m_selection.Empty() && m_selection[0]->HasFlag( IS_NEW | IS_MOVING ) )
527             {
528                 evt->SetPassEvent();
529                 continue;
530             }
531 
532             // drag with LMB? Select multiple objects (or at least draw a selection box) or
533             // drag them
534             if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
535                 schframe->FocusOnItem( nullptr );
536 
537             if( modifier_enabled || drag_action == MOUSE_DRAG_ACTION::SELECT )
538             {
539                 selectMultiple();
540             }
541             else if( m_selection.Empty() && drag_action != MOUSE_DRAG_ACTION::DRAG_ANY )
542             {
543                 selectMultiple();
544             }
545             else
546             {
547                 if( m_isSymbolEditor )
548                 {
549                     if( static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->IsSymbolAlias() )
550                         m_selection = RequestSelection( movableSymbolAliasItems );
551                     else
552                         m_selection = RequestSelection( movableSymbolItems );
553                 }
554                 else
555                 {
556                     m_selection = RequestSelection( movableSchematicItems );
557                 }
558 
559                 // Check if dragging has started within any of selected items bounding box
560                 if( selectionContains( evt->Position() ) )
561                 {
562                     // Yes -> run the move tool and wait till it finishes
563                     if( m_isSymbolEditor )
564                         m_toolMgr->InvokeTool( "eeschema.SymbolMoveTool" );
565                     else
566                         m_toolMgr->InvokeTool( "eeschema.InteractiveMove" );
567                 }
568                 else
569                 {
570                     // No -> drag a selection box
571                     selectMultiple();
572                 }
573             }
574         }
575         else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CHOICE_MENU_CHOICE )
576         {
577             m_disambiguateTimer.Stop();
578 
579             // context sub-menu selection?  Handle unit selection or bus unfolding
580             if( evt->GetCommandId().get() >= ID_POPUP_SCH_SELECT_UNIT_CMP
581                 && evt->GetCommandId().get() <= ID_POPUP_SCH_SELECT_UNIT_SYM_MAX )
582             {
583                 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( m_selection.Front() );
584                 int unit = evt->GetCommandId().get() - ID_POPUP_SCH_SELECT_UNIT_CMP;
585 
586                 if( symbol )
587                     static_cast<SCH_EDIT_FRAME*>( m_frame )->SelectUnit( symbol, unit );
588             }
589             else if( evt->GetCommandId().get() >= ID_POPUP_SCH_UNFOLD_BUS
590                      && evt->GetCommandId().get() <= ID_POPUP_SCH_UNFOLD_BUS_END )
591             {
592                 wxString* net = new wxString( *evt->Parameter<wxString*>() );
593                 m_toolMgr->RunAction( EE_ACTIONS::unfoldBus, true, net );
594             }
595 
596         }
597         else if( evt->IsCancelInteractive() )
598         {
599             m_disambiguateTimer.Stop();
600 
601             if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
602                 schframe->FocusOnItem( nullptr );
603 
604             ClearSelection();
605         }
606         else if( evt->Action() == TA_UNDO_REDO_PRE )
607         {
608             if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
609                 schframe->FocusOnItem( nullptr );
610 
611             ClearSelection();
612         }
613         else if( evt->IsMotion() && !m_isSymbolEditor && m_frame->ToolStackIsEmpty() )
614         {
615             rolloverItem = niluuid;
616             EE_COLLECTOR collector;
617 
618             // We are checking if we should display a pencil when hovering over anchors
619             // for "auto starting" wires when clicked
620             getViewControls()->ForceCursorPosition( false );
621 
622             if( CollectHits( collector, evt->Position() ) )
623             {
624                 narrowSelection( collector, evt->Position(), false, false );
625 
626                 if( collector.GetCount() == 1 && !modifier_enabled )
627                 {
628                     VECTOR2I snappedCursorPos = grid.BestSnapAnchor( evt->Position(),
629                                                                      LAYER_CONNECTABLE, nullptr );
630 
631                     EE_POINT_EDITOR *pt_tool = m_toolMgr->GetTool<EE_POINT_EDITOR>();
632 
633                     if( m_frame->eeconfig()->m_Drawing.auto_start_wires
634                             && pt_tool && !pt_tool->HasPoint()
635                             && !collector[0]->IsConnectivityDirty()
636                             && collector[0]->IsPointClickableAnchor( (wxPoint) snappedCursorPos ) )
637                     {
638                         SCH_CONNECTION* connection = collector[0]->Connection();
639 
640                         if( ( connection && ( connection->IsNet() || connection->IsUnconnected() ) )
641                             || collector[0]->Type() == SCH_SYMBOL_T )
642                         {
643                             displayWireCursor = true;
644                         }
645                         else if( connection && connection->IsBus() )
646                         {
647                             displayBusCursor = true;
648                         }
649                         else if( collector[0]->Type() == SCH_LINE_T
650                                  && static_cast<SCH_LINE*>( collector[0] )->IsGraphicLine() )
651                         {
652                             displayLineCursor = true;
653                         }
654 
655                         getViewControls()->ForceCursorPosition( true, snappedCursorPos );
656                     }
657                     else if( collector[0]->IsHypertext()
658                                 && !collector[0]->IsSelected()
659                                 && !m_additive && !m_subtractive && !m_exclusive_or )
660                     {
661                         rolloverItem = collector[0]->m_Uuid;
662                     }
663                 }
664             }
665         }
666         else
667         {
668             evt->SetPassEvent();
669         }
670 
671         if( rolloverItem != lastRolloverItem )
672         {
673             EDA_ITEM* item = m_frame->GetItem( lastRolloverItem );
674 
675             if( item  )
676             {
677                 item->ClearFlags( IS_ROLLOVER );
678                 lastRolloverItem = niluuid;
679 
680                 if( item->Type() == SCH_FIELD_T )
681                     m_frame->GetCanvas()->GetView()->Update( item->GetParent() );
682                 else
683                     m_frame->GetCanvas()->GetView()->Update( item );
684             }
685 
686             item = m_frame->GetItem( rolloverItem );
687 
688             if( item )
689             {
690                 item->SetFlags( IS_ROLLOVER );
691                 lastRolloverItem = rolloverItem;
692 
693                 if( item->Type() == SCH_FIELD_T )
694                     m_frame->GetCanvas()->GetView()->Update( item->GetParent() );
695                 else
696                     m_frame->GetCanvas()->GetView()->Update( item );
697             }
698         }
699 
700         if( m_frame->ToolStackIsEmpty() )
701         {
702             if( displayWireCursor )
703             {
704                 m_nonModifiedCursor = KICURSOR::LINE_WIRE_ADD;
705             }
706             else if( displayBusCursor )
707             {
708                 m_nonModifiedCursor = KICURSOR::LINE_BUS;
709             }
710             else if( displayLineCursor )
711             {
712                 m_nonModifiedCursor = KICURSOR::LINE_GRAPHIC;
713             }
714             else if( rolloverItem != niluuid )
715             {
716                 m_nonModifiedCursor = KICURSOR::HAND;
717             }
718             else if( !m_selection.Empty()
719                         && drag_action == MOUSE_DRAG_ACTION::DRAG_SELECTED
720                         && evt->HasPosition()
721                         && selectionContains( evt->Position() ) ) //move/drag option prediction
722             {
723                 m_nonModifiedCursor = KICURSOR::MOVING;
724             }
725             else
726             {
727                 m_nonModifiedCursor = KICURSOR::ARROW;
728             }
729         }
730     }
731 
732     m_disambiguateTimer.Stop();
733 
734     // Shutting down; clear the selection
735     m_selection.Clear();
736 
737     return 0;
738 }
739 
740 
disambiguateCursor(const TOOL_EVENT & aEvent)741 int EE_SELECTION_TOOL::disambiguateCursor( const TOOL_EVENT& aEvent )
742 {
743     m_skip_heuristics = true;
744     SelectPoint( m_originalCursor, EE_COLLECTOR::AllItems, nullptr, &m_canceledMenu, false,
745                  m_additive, m_subtractive, m_exclusive_or );
746     m_skip_heuristics = false;
747 
748     return 0;
749 }
750 
751 
onDisambiguationExpire(wxTimerEvent & aEvent)752 void EE_SELECTION_TOOL::onDisambiguationExpire( wxTimerEvent& aEvent )
753 {
754     m_toolMgr->ProcessEvent( EVENTS::DisambiguatePoint );
755 }
756 
757 
OnIdle(wxIdleEvent & aEvent)758 void EE_SELECTION_TOOL::OnIdle( wxIdleEvent& aEvent )
759 {
760     if( m_frame->ToolStackIsEmpty() && !m_multiple )
761     {
762         wxMouseState keyboardState = wxGetMouseState();
763 
764         setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
765                            keyboardState.AltDown() );
766 
767         if( m_additive )
768             m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ADD );
769         else if( m_subtractive )
770             m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SUBTRACT );
771         else if( m_exclusive_or )
772             m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::XOR );
773         else
774             m_frame->GetCanvas()->SetCurrentCursor( m_nonModifiedCursor );
775     }
776 }
777 
778 
GetSelection()779 EE_SELECTION& EE_SELECTION_TOOL::GetSelection()
780 {
781     return m_selection;
782 }
783 
784 
CollectHits(EE_COLLECTOR & aCollector,const VECTOR2I & aWhere,const KICAD_T * aFilterList)785 bool EE_SELECTION_TOOL::CollectHits( EE_COLLECTOR& aCollector, const VECTOR2I& aWhere,
786                                      const KICAD_T* aFilterList )
787 {
788     int pixelThreshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
789     int gridThreshold = KiROUND( getView()->GetGAL()->GetGridSize().EuclideanNorm() / 2 );
790     aCollector.m_Threshold = std::max( pixelThreshold, gridThreshold );
791 
792     if( m_isSymbolEditor )
793     {
794         LIB_SYMBOL* symbol = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
795 
796         if( !symbol )
797             return false;
798 
799         aCollector.Collect( symbol->GetDrawItems(), aFilterList, (wxPoint) aWhere, m_unit,
800                             m_convert );
801     }
802     else
803     {
804         aCollector.Collect( m_frame->GetScreen(), aFilterList, (wxPoint) aWhere, m_unit,
805                             m_convert );
806     }
807 
808     return aCollector.GetCount() > 0;
809 }
810 
811 
narrowSelection(EE_COLLECTOR & collector,const VECTOR2I & aWhere,bool aCheckLocked,bool aSelectPoints)812 void EE_SELECTION_TOOL::narrowSelection( EE_COLLECTOR& collector, const VECTOR2I& aWhere,
813                                          bool aCheckLocked, bool aSelectPoints )
814 {
815     for( int i = collector.GetCount() - 1; i >= 0; --i )
816     {
817         if( !Selectable( collector[i], &aWhere ) )
818         {
819             collector.Remove( i );
820             continue;
821         }
822 
823         if( aCheckLocked && collector[i]->IsLocked() )
824         {
825             collector.Remove( i );
826             continue;
827         }
828 
829         // SelectPoint, unlike other selection routines, can select line ends
830         if( aSelectPoints && collector[i]->Type() == SCH_LINE_T )
831         {
832             SCH_LINE* line = (SCH_LINE*) collector[i];
833             line->ClearFlags( STARTPOINT | ENDPOINT );
834 
835             if( HitTestPoints( line->GetStartPoint(), (wxPoint) aWhere, collector.m_Threshold ) )
836                 line->SetFlags( STARTPOINT );
837             else if( HitTestPoints( line->GetEndPoint(), (wxPoint) aWhere, collector.m_Threshold ) )
838                 line->SetFlags( ENDPOINT );
839             else
840                 line->SetFlags( STARTPOINT | ENDPOINT );
841         }
842     }
843 
844     // Apply some ugly heuristics to avoid disambiguation menus whenever possible
845     if( collector.GetCount() > 1 && !m_skip_heuristics )
846     {
847         GuessSelectionCandidates( collector, aWhere );
848     }
849 }
850 
851 
selectPoint(EE_COLLECTOR & aCollector,EDA_ITEM ** aItem,bool * aSelectionCancelledFlag,bool aAdd,bool aSubtract,bool aExclusiveOr)852 bool EE_SELECTION_TOOL::selectPoint( EE_COLLECTOR& aCollector, EDA_ITEM** aItem,
853                                      bool* aSelectionCancelledFlag, bool aAdd, bool aSubtract,
854                                      bool aExclusiveOr )
855 {
856     m_selection.ClearReferencePoint();
857 
858     // If still more than one item we're going to have to ask the user.
859     if( aCollector.GetCount() > 1 )
860     {
861         // Try to call selectionMenu via RunAction() to avoid event-loop contention
862         // But it we cannot handle the event, then we don't have an active tool loop, so
863         // handle it directly.
864         if( !m_toolMgr->RunAction( EE_ACTIONS::selectionMenu, true, &aCollector ) )
865         {
866             if( !doSelectionMenu( &aCollector ) )
867                 aCollector.m_MenuCancelled = true;
868         }
869 
870         if( aCollector.m_MenuCancelled )
871         {
872             if( aSelectionCancelledFlag )
873                 *aSelectionCancelledFlag = true;
874 
875             return false;
876         }
877     }
878 
879     if( !aAdd && !aSubtract && !aExclusiveOr )
880         ClearSelection();
881 
882     bool anyAdded      = false;
883     bool anySubtracted = false;
884 
885     if( aCollector.GetCount() > 0 )
886     {
887         for( int i = 0; i < aCollector.GetCount(); ++i )
888         {
889             if( aSubtract || ( aExclusiveOr && aCollector[i]->IsSelected() ) )
890             {
891                 unselect( aCollector[i] );
892                 anySubtracted = true;
893             }
894             else
895             {
896                 select( aCollector[i] );
897                 anyAdded = true;
898             }
899         }
900     }
901 
902     if( anyAdded )
903     {
904         m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
905 
906         if( aItem && aCollector.GetCount() == 1 )
907             *aItem = aCollector[0];
908 
909         return true;
910     }
911     else if( anySubtracted )
912     {
913         m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
914         return true;
915     }
916 
917     return false;
918 }
919 
920 
SelectPoint(const VECTOR2I & aWhere,const KICAD_T * aFilterList,EDA_ITEM ** aItem,bool * aSelectionCancelledFlag,bool aCheckLocked,bool aAdd,bool aSubtract,bool aExclusiveOr)921 bool EE_SELECTION_TOOL::SelectPoint( const VECTOR2I& aWhere, const KICAD_T* aFilterList,
922                                      EDA_ITEM** aItem, bool* aSelectionCancelledFlag,
923                                      bool aCheckLocked, bool aAdd, bool aSubtract,
924                                      bool aExclusiveOr )
925 {
926     EE_COLLECTOR collector;
927 
928     if( !CollectHits( collector, aWhere, aFilterList ) )
929         return false;
930 
931     narrowSelection( collector, aWhere, aCheckLocked, true );
932 
933     return selectPoint( collector, aItem, aSelectionCancelledFlag, aAdd, aSubtract, aExclusiveOr );
934 }
935 
936 
SelectAll(const TOOL_EVENT & aEvent)937 int EE_SELECTION_TOOL::SelectAll( const TOOL_EVENT& aEvent )
938 {
939     m_multiple = true;          // Multiple selection mode is active
940     KIGFX::VIEW* view = getView();
941 
942     // hold all visible items
943     std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
944     std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> sheetPins;
945 
946     // Filter the view items based on the selection box
947     BOX2I selectionBox;
948 
949     selectionBox.SetMaximum();
950     view->Query( selectionBox, selectedItems );         // Get the list of selected items
951 
952     // Sheet pins aren't in the view; add them by hand
953     for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : selectedItems )
954     {
955         SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( pair.first );
956 
957         if( sheet )
958         {
959             int layer = pair.second;
960 
961             for( SCH_SHEET_PIN* pin : sheet->GetPins() )
962                 sheetPins.emplace_back( KIGFX::VIEW::LAYER_ITEM_PAIR( pin, layer ) );
963         }
964     }
965 
966     selectedItems.insert( selectedItems.end(), sheetPins.begin(), sheetPins.end() );
967 
968     for( const std::pair<KIGFX::VIEW_ITEM*, int>& item_pair : selectedItems )
969     {
970         if( EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( item_pair.first ) )
971         {
972             if( Selectable( item ) )
973                 select( item );
974         }
975     }
976 
977     m_multiple = false;
978 
979     return 0;
980 }
981 
982 
GuessSelectionCandidates(EE_COLLECTOR & collector,const VECTOR2I & aPos)983 void EE_SELECTION_TOOL::GuessSelectionCandidates( EE_COLLECTOR& collector, const VECTOR2I& aPos )
984 {
985     // Prefer exact hits to sloppy ones
986     std::set<EDA_ITEM*> exactHits;
987 
988     for( int i = collector.GetCount() - 1; i >= 0; --i )
989     {
990         EDA_ITEM*  item = collector[ i ];
991         SCH_LINE*  line = dynamic_cast<SCH_LINE*>( item );
992         LIB_SHAPE* shape = dynamic_cast<LIB_SHAPE*>( item );
993 
994         // Lines are hard to hit.  Give them a bit more slop to still be considered "exact".
995 
996         if( line || ( shape && shape->GetShape() == SHAPE_T::POLY ) )
997         {
998             if( item->HitTest( (wxPoint) aPos, Mils2iu( DANGLING_SYMBOL_SIZE ) ) )
999                 exactHits.insert( item );
1000         }
1001         else
1002         {
1003             if( item->HitTest( (wxPoint) aPos, 0 ) )
1004                 exactHits.insert( item );
1005         }
1006     }
1007 
1008     if( exactHits.size() > 0 && exactHits.size() < (unsigned) collector.GetCount() )
1009     {
1010         for( int i = collector.GetCount() - 1; i >= 0; --i )
1011         {
1012             EDA_ITEM* item = collector[ i ];
1013 
1014             if( !exactHits.count( item ) )
1015                 collector.Transfer( item );
1016         }
1017     }
1018 
1019     // Find the closest item.  (Note that at this point all hits are either exact or non-exact.)
1020     wxPoint   pos( aPos );
1021     SEG       poss( m_isSymbolEditor ? mapCoords( pos ) : pos,
1022                     m_isSymbolEditor ? mapCoords( pos ) : pos );
1023     EDA_ITEM* closest = nullptr;
1024     int       closestDist = INT_MAX / 2;
1025 
1026     for( EDA_ITEM* item : collector )
1027     {
1028         EDA_RECT bbox = item->GetBoundingBox();
1029         int      dist = INT_MAX / 2;
1030 
1031         if( exactHits.count( item ) )
1032         {
1033             if( item->Type() == SCH_PIN_T || item->Type() == SCH_JUNCTION_T )
1034             {
1035                 closest = item;
1036                 break;
1037             }
1038 
1039             SCH_LINE*   line = dynamic_cast<SCH_LINE*>( item );
1040             EDA_TEXT*   text = dynamic_cast<EDA_TEXT*>( item );
1041             SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
1042 
1043             if( line )
1044             {
1045                 dist = DistanceLinePoint( line->GetStartPoint(), line->GetEndPoint(), pos );
1046             }
1047             else if( text )
1048             {
1049                 text->GetEffectiveTextShape()->Collide( poss, closestDist, &dist );
1050             }
1051             else if( symbol )
1052             {
1053                 try
1054                 {
1055                     bbox = symbol->GetBodyBoundingBox();
1056                 }
1057                 catch( const boost::bad_pointer& exc )
1058                 {
1059                     // This may be overkill and could be an assertion but we are more likely to
1060                     // find any boost pointer container errors this way.
1061                     wxLogError( wxT( "Boost bad pointer exception '%s' occurred." ), exc.what() );
1062                 }
1063 
1064                 SHAPE_RECT rect( bbox.GetPosition(), bbox.GetWidth(), bbox.GetHeight() );
1065 
1066                 if( bbox.Contains( pos ) )
1067                     dist = EuclideanNorm( bbox.GetCenter() - pos );
1068                 else
1069                     rect.Collide( poss, closestDist, &dist );
1070             }
1071             else
1072             {
1073                 dist = EuclideanNorm( bbox.GetCenter() - pos );
1074             }
1075         }
1076         else
1077         {
1078             SHAPE_RECT rect( bbox.GetPosition(), bbox.GetWidth(), bbox.GetHeight() );
1079             rect.Collide( poss, collector.m_Threshold, &dist );
1080         }
1081 
1082         if( dist < closestDist )
1083         {
1084             closestDist = dist;
1085             closest = item;
1086         }
1087     }
1088 
1089     // Construct a tight box (1/2 height and width) around the center of the closest item.
1090     // All items which exist at least partly outside this box have sufficient other areas
1091     // for selection and can be dropped.
1092     if( closest ) // Don't try and get a tight bbox if nothing is near the mouse pointer
1093     {
1094         EDA_RECT tightBox = closest->GetBoundingBox();
1095         tightBox.Inflate( -tightBox.GetWidth() / 4, -tightBox.GetHeight() / 4 );
1096 
1097         for( int i = collector.GetCount() - 1; i >= 0; --i )
1098         {
1099             EDA_ITEM* item = collector[i];
1100 
1101             if( item == closest )
1102                 continue;
1103 
1104             if( !item->HitTest( tightBox, true ) )
1105                 collector.Transfer( item );
1106         }
1107     }
1108 }
1109 
1110 
RequestSelection(const KICAD_T aFilterList[])1111 EE_SELECTION& EE_SELECTION_TOOL::RequestSelection( const KICAD_T aFilterList[] )
1112 {
1113     if( m_selection.Empty() )
1114     {
1115         VECTOR2D cursorPos = getViewControls()->GetCursorPosition( true );
1116 
1117         ClearSelection();
1118         SelectPoint( cursorPos, aFilterList );
1119         m_selection.SetIsHover( true );
1120         m_selection.ClearReferencePoint();
1121     }
1122     else        // Trim an existing selection by aFilterList
1123     {
1124         bool isMoving = false;
1125 
1126         for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
1127         {
1128             EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
1129             isMoving |= static_cast<SCH_ITEM*>( item )->IsMoving();
1130 
1131             if( !item->IsType( aFilterList ) )
1132             {
1133                 unselect( item );
1134                 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1135             }
1136         }
1137 
1138         if( !isMoving )
1139             updateReferencePoint();
1140     }
1141 
1142     return m_selection;
1143 }
1144 
1145 
updateReferencePoint()1146 void EE_SELECTION_TOOL::updateReferencePoint()
1147 {
1148     VECTOR2I refP( 0, 0 );
1149 
1150     if( m_selection.Size() > 0 )
1151     {
1152         if( m_isSymbolEditor )
1153             refP = static_cast<LIB_ITEM*>( m_selection.GetTopLeftItem() )->GetPosition();
1154         else
1155             refP = static_cast<SCH_ITEM*>( m_selection.GetTopLeftItem() )->GetPosition();
1156     }
1157 
1158     m_selection.SetReferencePoint( refP );
1159 }
1160 
1161 
1162 // Some navigation actions are allowed in selectMultiple
1163 const TOOL_ACTION* allowedActions[] = { &ACTIONS::panUp,          &ACTIONS::panDown,
1164                                         &ACTIONS::panLeft,        &ACTIONS::panRight,
1165                                         &ACTIONS::cursorUp,       &ACTIONS::cursorDown,
1166                                         &ACTIONS::cursorLeft,     &ACTIONS::cursorRight,
1167                                         &ACTIONS::cursorUpFast,   &ACTIONS::cursorDownFast,
1168                                         &ACTIONS::cursorLeftFast, &ACTIONS::cursorRightFast,
1169                                         &ACTIONS::zoomIn,         &ACTIONS::zoomOut,
1170                                         &ACTIONS::zoomInCenter,   &ACTIONS::zoomOutCenter,
1171                                         &ACTIONS::zoomCenter,     &ACTIONS::zoomFitScreen,
1172                                         &ACTIONS::zoomFitObjects, nullptr };
1173 
1174 
selectMultiple()1175 bool EE_SELECTION_TOOL::selectMultiple()
1176 {
1177     bool cancelled = false;     // Was the tool canceled while it was running?
1178     m_multiple = true;          // Multiple selection mode is active
1179     KIGFX::VIEW* view = getView();
1180 
1181     KIGFX::PREVIEW::SELECTION_AREA area;
1182     view->Add( &area );
1183 
1184     while( TOOL_EVENT* evt = Wait() )
1185     {
1186         int width = area.GetEnd().x - area.GetOrigin().x;
1187         int height = area.GetEnd().y - area.GetOrigin().y;
1188 
1189         /* Selection mode depends on direction of drag-selection:
1190          * Left > Right : Select objects that are fully enclosed by selection
1191          * Right > Left : Select objects that are crossed by selection
1192          */
1193         bool isWindowSelection = width >= 0;
1194 
1195         if( view->IsMirroredX() )
1196             isWindowSelection = !isWindowSelection;
1197 
1198         m_frame->GetCanvas()->SetCurrentCursor( isWindowSelection ? KICURSOR::SELECT_WINDOW
1199                                                                   : KICURSOR::SELECT_LASSO );
1200 
1201         if( evt->IsCancelInteractive() || evt->IsActivate() )
1202         {
1203             cancelled = true;
1204             break;
1205         }
1206 
1207         if( evt->IsDrag( BUT_LEFT ) )
1208         {
1209             if( !m_drag_additive && !m_drag_subtractive )
1210                 ClearSelection();
1211 
1212             // Start drawing a selection box
1213             area.SetOrigin( evt->DragOrigin() );
1214             area.SetEnd( evt->Position() );
1215             area.SetAdditive( m_drag_additive );
1216             area.SetSubtractive( m_drag_subtractive );
1217             area.SetExclusiveOr( false );
1218 
1219             view->SetVisible( &area, true );
1220             view->Update( &area );
1221             getViewControls()->SetAutoPan( true );
1222         }
1223 
1224         if( evt->IsMouseUp( BUT_LEFT ) )
1225         {
1226             getViewControls()->SetAutoPan( false );
1227 
1228             // End drawing the selection box
1229             view->SetVisible( &area, false );
1230 
1231             // Fetch items from the RTree that are in our area of interest
1232             std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> nearbyViewItems;
1233             view->Query( area.ViewBBox(), nearbyViewItems );
1234 
1235             // Build lists of nearby items and their children
1236             std::vector<EDA_ITEM*> nearbyItems;
1237             std::vector<EDA_ITEM*> nearbyChildren;
1238 
1239             for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : nearbyViewItems )
1240             {
1241                 EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( pair.first );
1242 
1243                 if( item )
1244                 {
1245                     item->ClearFlags( TEMP_SELECTED | STARTPOINT | ENDPOINT );
1246                     nearbyItems.push_back( item );
1247                 }
1248 
1249                 if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( item ) )
1250                 {
1251                     sch_item->RunOnChildren(
1252                             [&]( SCH_ITEM* aChild )
1253                             {
1254                                 nearbyChildren.push_back( aChild );
1255                             } );
1256                 }
1257             }
1258 
1259             EDA_RECT selectionRect( (wxPoint) area.GetOrigin(), wxSize( width, height ) );
1260             selectionRect.Normalize();
1261 
1262             bool anyAdded = false;
1263             bool anySubtracted = false;
1264             auto selectItem =
1265                     [&]( EDA_ITEM* aItem )
1266                     {
1267                         if( m_subtractive || ( m_exclusive_or && aItem->IsSelected() ) )
1268                         {
1269                             unselect( aItem );
1270                             anySubtracted = true;
1271                         }
1272                         else
1273                         {
1274                             select( aItem );
1275                             aItem->SetFlags( STARTPOINT | ENDPOINT );
1276                             anyAdded = true;
1277                         }
1278                     };
1279 
1280             for( EDA_ITEM* item : nearbyItems )
1281             {
1282                 if( Selectable( item ) && item->HitTest( selectionRect, isWindowSelection ) )
1283                 {
1284                     item->SetFlags( TEMP_SELECTED );
1285                     selectItem( item );
1286                 }
1287             }
1288 
1289             for( EDA_ITEM* item : nearbyChildren )
1290             {
1291                 if( Selectable( item )
1292                         && !item->GetParent()->HasFlag( TEMP_SELECTED )
1293                         && item->HitTest( selectionRect, isWindowSelection ) )
1294                 {
1295                     selectItem( item );
1296                 }
1297             }
1298 
1299             m_selection.SetIsHover( false );
1300 
1301             // Inform other potentially interested tools
1302             if( anyAdded )
1303                 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1304 
1305             if( anySubtracted )
1306                 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1307 
1308             break;  // Stop waiting for events
1309         }
1310 
1311         // Allow some actions for navigation
1312         for( int i = 0; allowedActions[i]; ++i )
1313         {
1314             if( evt->IsAction( allowedActions[i] ) )
1315             {
1316                 evt->SetPassEvent();
1317                 break;
1318             }
1319         }
1320     }
1321 
1322     getViewControls()->SetAutoPan( false );
1323 
1324     // Stop drawing the selection box
1325     view->Remove( &area );
1326     m_multiple = false;         // Multiple selection mode is inactive
1327 
1328     if( !cancelled )
1329         m_selection.ClearReferencePoint();
1330 
1331     return cancelled;
1332 }
1333 
1334 
1335 static KICAD_T nodeTypes[] =
1336 {
1337     SCH_SYMBOL_LOCATE_POWER_T,
1338     SCH_PIN_T,
1339     SCH_LINE_LOCATE_WIRE_T,
1340     SCH_LINE_LOCATE_BUS_T,
1341     SCH_BUS_WIRE_ENTRY_T,
1342     SCH_BUS_BUS_ENTRY_T,
1343     SCH_LABEL_T,
1344     SCH_HIER_LABEL_T,
1345     SCH_GLOBAL_LABEL_T,
1346     SCH_SHEET_PIN_T,
1347     SCH_JUNCTION_T,
1348     EOT
1349 };
1350 
1351 
GetNode(VECTOR2I aPosition)1352 EDA_ITEM* EE_SELECTION_TOOL::GetNode( VECTOR2I aPosition )
1353 {
1354     EE_COLLECTOR collector;
1355 
1356     //TODO(snh): Reimplement after exposing KNN interface
1357     int pixelThreshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
1358     int gridThreshold = KiROUND( getView()->GetGAL()->GetGridSize().EuclideanNorm() );
1359     int thresholdMax = std::max( pixelThreshold, gridThreshold );
1360 
1361     for( int threshold : { 0, thresholdMax/4, thresholdMax/2, thresholdMax } )
1362     {
1363         collector.m_Threshold = threshold;
1364         collector.Collect( m_frame->GetScreen(), nodeTypes, (wxPoint) aPosition );
1365 
1366         if( collector.GetCount() > 0 )
1367             break;
1368     }
1369 
1370     return collector.GetCount() ? collector[ 0 ] : nullptr;
1371 }
1372 
1373 
SelectNode(const TOOL_EVENT & aEvent)1374 int EE_SELECTION_TOOL::SelectNode( const TOOL_EVENT& aEvent )
1375 {
1376     VECTOR2I cursorPos = getViewControls()->GetCursorPosition( false );
1377 
1378     SelectPoint( cursorPos, nodeTypes  );
1379 
1380     return 0;
1381 }
1382 
1383 
SelectConnection(const TOOL_EVENT & aEvent)1384 int EE_SELECTION_TOOL::SelectConnection( const TOOL_EVENT& aEvent )
1385 {
1386     static KICAD_T wiresAndBuses[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT };
1387 
1388     RequestSelection( wiresAndBuses );
1389 
1390     if( m_selection.Empty() )
1391         return 0;
1392 
1393     SCH_LINE* line = (SCH_LINE*) m_selection.Front();
1394     EDA_ITEMS items;
1395 
1396     m_frame->GetScreen()->ClearDrawingState();
1397     std::set<SCH_ITEM*> conns = m_frame->GetScreen()->MarkConnections( line );
1398 
1399     for( SCH_ITEM* item : conns )
1400         select( item );
1401 
1402     if( m_selection.GetSize() > 1 )
1403         m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1404 
1405     return 0;
1406 }
1407 
1408 
AddItemToSel(const TOOL_EVENT & aEvent)1409 int EE_SELECTION_TOOL::AddItemToSel( const TOOL_EVENT& aEvent )
1410 {
1411     AddItemToSel( aEvent.Parameter<EDA_ITEM*>() );
1412     m_selection.SetIsHover( false );
1413     return 0;
1414 }
1415 
1416 
AddItemToSel(EDA_ITEM * aItem,bool aQuietMode)1417 void EE_SELECTION_TOOL::AddItemToSel( EDA_ITEM* aItem, bool aQuietMode )
1418 {
1419     if( aItem )
1420     {
1421         select( aItem );
1422 
1423         // Inform other potentially interested tools
1424         if( !aQuietMode )
1425             m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1426     }
1427 }
1428 
1429 
AddItemsToSel(const TOOL_EVENT & aEvent)1430 int EE_SELECTION_TOOL::AddItemsToSel( const TOOL_EVENT& aEvent )
1431 {
1432     AddItemsToSel( aEvent.Parameter<EDA_ITEMS*>(), false );
1433     m_selection.SetIsHover( false );
1434     return 0;
1435 }
1436 
1437 
AddItemsToSel(EDA_ITEMS * aList,bool aQuietMode)1438 void EE_SELECTION_TOOL::AddItemsToSel( EDA_ITEMS* aList, bool aQuietMode )
1439 {
1440     if( aList )
1441     {
1442         for( EDA_ITEM* item : *aList )
1443             select( item );
1444 
1445         // Inform other potentially interested tools
1446         if( !aQuietMode )
1447             m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1448     }
1449 }
1450 
1451 
RemoveItemFromSel(const TOOL_EVENT & aEvent)1452 int EE_SELECTION_TOOL::RemoveItemFromSel( const TOOL_EVENT& aEvent )
1453 {
1454     RemoveItemFromSel( aEvent.Parameter<EDA_ITEM*>() );
1455     m_selection.SetIsHover( false );
1456     return 0;
1457 }
1458 
1459 
RemoveItemFromSel(EDA_ITEM * aItem,bool aQuietMode)1460 void EE_SELECTION_TOOL::RemoveItemFromSel( EDA_ITEM* aItem, bool aQuietMode )
1461 {
1462     if( aItem )
1463     {
1464         unselect( aItem );
1465 
1466         // Inform other potentially interested tools
1467         if( !aQuietMode )
1468             m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1469     }
1470 }
1471 
1472 
RemoveItemsFromSel(const TOOL_EVENT & aEvent)1473 int EE_SELECTION_TOOL::RemoveItemsFromSel( const TOOL_EVENT& aEvent )
1474 {
1475     RemoveItemsFromSel( aEvent.Parameter<EDA_ITEMS*>(), false );
1476     m_selection.SetIsHover( false );
1477     return 0;
1478 }
1479 
1480 
RemoveItemsFromSel(EDA_ITEMS * aList,bool aQuietMode)1481 void EE_SELECTION_TOOL::RemoveItemsFromSel( EDA_ITEMS* aList, bool aQuietMode )
1482 {
1483     if( aList )
1484     {
1485         for( EDA_ITEM* item : *aList )
1486             unselect( item );
1487 
1488         // Inform other potentially interested tools
1489         if( !aQuietMode )
1490             m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1491     }
1492 }
1493 
1494 
RemoveItemsFromSel(std::vector<KIID> * aList,bool aQuietMode)1495 void EE_SELECTION_TOOL::RemoveItemsFromSel( std::vector<KIID>* aList, bool aQuietMode )
1496 {
1497     EDA_ITEMS removeItems;
1498 
1499     for( EDA_ITEM* item : m_selection )
1500     {
1501         if( alg::contains( *aList, item->m_Uuid ) )
1502             removeItems.push_back( item );
1503     }
1504 
1505     RemoveItemsFromSel( &removeItems, aQuietMode );
1506 }
1507 
1508 
BrightenItem(EDA_ITEM * aItem)1509 void EE_SELECTION_TOOL::BrightenItem( EDA_ITEM* aItem )
1510 {
1511     highlight( aItem, BRIGHTENED );
1512 }
1513 
1514 
UnbrightenItem(EDA_ITEM * aItem)1515 void EE_SELECTION_TOOL::UnbrightenItem( EDA_ITEM* aItem )
1516 {
1517     unhighlight( aItem, BRIGHTENED );
1518 }
1519 
1520 
ClearSelection(const TOOL_EVENT & aEvent)1521 int EE_SELECTION_TOOL::ClearSelection( const TOOL_EVENT& aEvent )
1522 {
1523     ClearSelection();
1524 
1525     return 0;
1526 }
1527 
1528 
RebuildSelection()1529 void EE_SELECTION_TOOL::RebuildSelection()
1530 {
1531     m_selection.Clear();
1532 
1533     if( m_isSymbolEditor )
1534     {
1535         LIB_SYMBOL* start = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
1536 
1537         for( LIB_ITEM& item : start->GetDrawItems() )
1538         {
1539             if( item.IsSelected() )
1540                 select( static_cast<EDA_ITEM*>( &item ) );
1541         }
1542     }
1543     else
1544     {
1545         for( SCH_ITEM* item : m_frame->GetScreen()->Items() )
1546         {
1547             // If the field and symbol are selected, only use the symbol
1548             if( item->IsSelected() )
1549             {
1550                 select( item );
1551             }
1552             else
1553             {
1554                 item->RunOnChildren(
1555                         [&]( SCH_ITEM* aChild )
1556                         {
1557                             if( aChild->IsSelected() )
1558                                 select( aChild );
1559                         } );
1560             }
1561         }
1562     }
1563 
1564     updateReferencePoint();
1565 
1566     // Inform other potentially interested tools
1567     m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1568 }
1569 
1570 
SelectionMenu(const TOOL_EVENT & aEvent)1571 int EE_SELECTION_TOOL::SelectionMenu( const TOOL_EVENT& aEvent )
1572 {
1573     EE_COLLECTOR* collector = aEvent.Parameter<EE_COLLECTOR*>();
1574 
1575     if( !doSelectionMenu( collector ) )
1576         collector->m_MenuCancelled = true;
1577 
1578     return 0;
1579 }
1580 
1581 
doSelectionMenu(EE_COLLECTOR * aCollector)1582 bool EE_SELECTION_TOOL::doSelectionMenu( EE_COLLECTOR* aCollector )
1583 {
1584     EDA_ITEM*   current = nullptr;
1585     bool        selectAll = false;
1586     bool        expandSelection = false;
1587 
1588     do
1589     {
1590         /// The user has requested the full, non-limited list of selection items
1591         if( expandSelection )
1592             aCollector->Combine();
1593 
1594         expandSelection = false;
1595 
1596         int         limit = std::min( 9, aCollector->GetCount() );
1597         ACTION_MENU menu( true );
1598 
1599         for( int i = 0; i < limit; ++i )
1600         {
1601             wxString  text;
1602             EDA_ITEM* item = ( *aCollector )[i];
1603             text           = item->GetSelectMenuText( m_frame->GetUserUnits() );
1604 
1605             wxString menuText = wxString::Format( "&%d. %s\t%d", i + 1, text, i + 1 );
1606             menu.Add( menuText, i + 1, item->GetMenuImage() );
1607         }
1608 
1609         menu.AppendSeparator();
1610         menu.Add( _( "Select &All\tA" ), limit + 1, BITMAPS::INVALID_BITMAP );
1611 
1612         if( !expandSelection && aCollector->HasAdditionalItems() )
1613             menu.Add( _( "&Expand Selection\tE" ), limit + 2, BITMAPS::INVALID_BITMAP );
1614 
1615         if( aCollector->m_MenuTitle.Length() )
1616         {
1617             menu.SetTitle( aCollector->m_MenuTitle );
1618             menu.SetIcon( BITMAPS::info );
1619             menu.DisplayTitle( true );
1620         }
1621         else
1622         {
1623             menu.DisplayTitle( false );
1624         }
1625 
1626         SetContextMenu( &menu, CMENU_NOW );
1627 
1628         while( TOOL_EVENT* evt = Wait() )
1629         {
1630             if( evt->Action() == TA_CHOICE_MENU_UPDATE )
1631             {
1632                 if( selectAll )
1633                 {
1634                     for( int i = 0; i < aCollector->GetCount(); ++i )
1635                         unhighlight( ( *aCollector )[i], BRIGHTENED );
1636                 }
1637                 else if( current )
1638                 {
1639                     unhighlight( current, BRIGHTENED );
1640                 }
1641 
1642                 int id = *evt->GetCommandId();
1643 
1644                 // User has pointed an item, so show it in a different way
1645                 if( id > 0 && id <= limit )
1646                 {
1647                     current = ( *aCollector )[id - 1];
1648                     highlight( current, BRIGHTENED );
1649                 }
1650                 else
1651                 {
1652                     current = nullptr;
1653                 }
1654 
1655                 // User has pointed on the "Select All" option
1656                 if( id == limit + 1 )
1657                 {
1658                     for( int i = 0; i < aCollector->GetCount(); ++i )
1659                         highlight( ( *aCollector )[i], BRIGHTENED );
1660                     selectAll = true;
1661                 }
1662                 else
1663                 {
1664                     selectAll = false;
1665                 }
1666             }
1667             else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
1668             {
1669                 if( selectAll )
1670                 {
1671                     for( int i = 0; i < aCollector->GetCount(); ++i )
1672                         unhighlight( ( *aCollector )[i], BRIGHTENED );
1673                 }
1674                 else if( current )
1675                     unhighlight( current, BRIGHTENED );
1676 
1677                 OPT<int> id = evt->GetCommandId();
1678 
1679                 // User has selected the "Select All" option
1680                 if( id == limit + 1 )
1681                 {
1682                     selectAll = true;
1683                     current   = nullptr;
1684                 }
1685                 else if( id == limit + 2 )
1686                 {
1687                     selectAll       = false;
1688                     current         = nullptr;
1689                     expandSelection = true;
1690                 }
1691                 // User has selected an item, so this one will be returned
1692                 else if( id && ( *id > 0 ) && ( *id <= limit ) )
1693                 {
1694                     selectAll = false;
1695                     current   = ( *aCollector )[*id - 1];
1696                 }
1697                 // User has cancelled the menu (either by <esc> or clicking out of it)
1698                 else
1699                 {
1700                     selectAll = false;
1701                     current   = nullptr;
1702                 }
1703             }
1704             else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
1705             {
1706                 break;
1707             }
1708 
1709             getView()->UpdateItems();
1710             m_frame->GetCanvas()->Refresh();
1711         }
1712     } while( expandSelection );
1713 
1714     if( selectAll )
1715         return true;
1716     else if( current )
1717     {
1718         unhighlight( current, BRIGHTENED );
1719 
1720         getView()->UpdateItems();
1721         m_frame->GetCanvas()->Refresh();
1722 
1723         aCollector->Empty();
1724         aCollector->Append( current );
1725         return true;
1726     }
1727 
1728     return false;
1729 }
1730 
1731 
Selectable(const EDA_ITEM * aItem,const VECTOR2I * aPos,bool checkVisibilityOnly) const1732 bool EE_SELECTION_TOOL::Selectable( const EDA_ITEM* aItem, const VECTOR2I* aPos,
1733                                     bool checkVisibilityOnly ) const
1734 {
1735     // NOTE: in the future this is where Eeschema layer/itemtype visibility will be handled
1736 
1737     SYMBOL_EDIT_FRAME* symEditFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
1738 
1739     // Do not allow selection of anything except fields when the current symbol in the symbol
1740     // editor is a derived symbol.
1741     if( symEditFrame && symEditFrame->IsSymbolAlias() && aItem->Type() != LIB_FIELD_T )
1742         return false;
1743 
1744     switch( aItem->Type() )
1745     {
1746     case SCH_PIN_T:
1747     {
1748         const SCH_PIN* pin = static_cast<const SCH_PIN*>( aItem );
1749 
1750         if( !pin->IsVisible() && !m_frame->GetShowAllPins() )
1751             return false;
1752 
1753         if( m_frame->eeconfig()->m_Selection.select_pin_selects_symbol )
1754         {
1755             // Pin anchors have to be allowed for auto-starting wires.
1756             if( aPos )
1757             {
1758                 EE_GRID_HELPER grid( m_toolMgr );
1759                 VECTOR2I       cursorPos = grid.BestSnapAnchor( *aPos, LAYER_CONNECTABLE, nullptr );
1760 
1761                 if( pin->IsPointClickableAnchor( (wxPoint) cursorPos ) )
1762                     return true;
1763             }
1764 
1765             return false;
1766         }
1767     }
1768         break;
1769 
1770     case LIB_SYMBOL_T:    // In symbol_editor we do not want to select the symbol itself.
1771         return false;
1772 
1773     case LIB_FIELD_T:     // LIB_FIELD object can always be edited.
1774         break;
1775 
1776     case LIB_SHAPE_T:
1777     case LIB_TEXT_T:
1778     case LIB_PIN_T:
1779         if( symEditFrame )
1780         {
1781             LIB_ITEM* lib_item = (LIB_ITEM*) aItem;
1782 
1783             if( lib_item->GetUnit() && lib_item->GetUnit() != symEditFrame->GetUnit() )
1784                 return false;
1785 
1786             if( lib_item->GetConvert() && lib_item->GetConvert() != symEditFrame->GetConvert() )
1787                 return false;
1788         }
1789 
1790         break;
1791 
1792     case SCH_MARKER_T:  // Always selectable
1793         return true;
1794 
1795     default:            // Suppress warnings
1796         break;
1797     }
1798 
1799     return true;
1800 }
1801 
1802 
ClearSelection()1803 void EE_SELECTION_TOOL::ClearSelection()
1804 {
1805     if( m_selection.Empty() )
1806         return;
1807 
1808     while( m_selection.GetSize() )
1809         unhighlight( (EDA_ITEM*) m_selection.Front(), SELECTED, &m_selection );
1810 
1811     getView()->Update( &m_selection );
1812 
1813     m_selection.SetIsHover( false );
1814     m_selection.ClearReferencePoint();
1815 
1816     // Inform other potentially interested tools
1817     m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
1818 }
1819 
1820 
select(EDA_ITEM * aItem)1821 void EE_SELECTION_TOOL::select( EDA_ITEM* aItem )
1822 {
1823     highlight( aItem, SELECTED, &m_selection );
1824 }
1825 
1826 
unselect(EDA_ITEM * aItem)1827 void EE_SELECTION_TOOL::unselect( EDA_ITEM* aItem )
1828 {
1829     unhighlight( aItem, SELECTED, &m_selection );
1830 }
1831 
1832 
highlight(EDA_ITEM * aItem,int aMode,EE_SELECTION * aGroup)1833 void EE_SELECTION_TOOL::highlight( EDA_ITEM* aItem, int aMode, EE_SELECTION* aGroup )
1834 {
1835     KICAD_T itemType = aItem->Type();
1836 
1837     if( aMode == SELECTED )
1838         aItem->SetSelected();
1839     else if( aMode == BRIGHTENED )
1840         aItem->SetBrightened();
1841 
1842     if( aGroup )
1843         aGroup->Add( aItem );
1844 
1845     // Highlight pins and fields.  (All the other symbol children are currently only
1846     // represented in the LIB_SYMBOL and will inherit the settings of the parent symbol.)
1847     if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( aItem ) )
1848     {
1849         sch_item->RunOnChildren(
1850             [&]( SCH_ITEM* aChild )
1851             {
1852                 if( aMode == SELECTED )
1853                     aChild->SetSelected();
1854                 else if( aMode == BRIGHTENED )
1855                     aChild->SetBrightened();
1856             } );
1857     }
1858 
1859     if( itemType == SCH_PIN_T || itemType == SCH_FIELD_T || itemType == SCH_SHEET_PIN_T )
1860         getView()->Update( aItem->GetParent() );
1861     else
1862         getView()->Update( aItem );
1863 }
1864 
1865 
unhighlight(EDA_ITEM * aItem,int aMode,EE_SELECTION * aGroup)1866 void EE_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, EE_SELECTION* aGroup )
1867 {
1868     KICAD_T itemType = aItem->Type();
1869 
1870     if( aMode == SELECTED )
1871         aItem->ClearSelected();
1872     else if( aMode == BRIGHTENED )
1873         aItem->ClearBrightened();
1874 
1875     if( aGroup )
1876         aGroup->Remove( aItem );
1877 
1878     // Unhighlight pins and fields.  (All the other symbol children are currently only
1879     // represented in the LIB_SYMBOL.)
1880     if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( aItem ) )
1881     {
1882         sch_item->RunOnChildren(
1883             [&]( SCH_ITEM* aChild )
1884             {
1885                 if( aMode == SELECTED )
1886                     aChild->ClearSelected();
1887                 else if( aMode == BRIGHTENED )
1888                     aChild->ClearBrightened();
1889             } );
1890     }
1891 
1892     if( itemType == SCH_PIN_T || itemType == SCH_FIELD_T || itemType == SCH_SHEET_PIN_T )
1893         getView()->Update( aItem->GetParent() );
1894     else
1895         getView()->Update( aItem );
1896 }
1897 
1898 
selectionContains(const VECTOR2I & aPoint) const1899 bool EE_SELECTION_TOOL::selectionContains( const VECTOR2I& aPoint ) const
1900 {
1901     const unsigned GRIP_MARGIN = 20;
1902     VECTOR2I margin = getView()->ToWorld( VECTOR2I( GRIP_MARGIN, GRIP_MARGIN ), false );
1903 
1904     // Check if the point is located within any of the currently selected items bounding boxes
1905     for( EDA_ITEM* item : m_selection )
1906     {
1907         BOX2I itemBox = item->ViewBBox();
1908         itemBox.Inflate( margin.x, margin.y );    // Give some margin for gripping an item
1909 
1910         if( itemBox.Contains( aPoint ) )
1911             return true;
1912     }
1913 
1914     return false;
1915 }
1916 
1917 
setTransitions()1918 void EE_SELECTION_TOOL::setTransitions()
1919 {
1920     Go( &EE_SELECTION_TOOL::UpdateMenu,          ACTIONS::updateMenu.MakeEvent() );
1921 
1922     Go( &EE_SELECTION_TOOL::Main,                EE_ACTIONS::selectionActivate.MakeEvent() );
1923     Go( &EE_SELECTION_TOOL::SelectNode,          EE_ACTIONS::selectNode.MakeEvent() );
1924     Go( &EE_SELECTION_TOOL::SelectConnection,    EE_ACTIONS::selectConnection.MakeEvent() );
1925     Go( &EE_SELECTION_TOOL::ClearSelection,      EE_ACTIONS::clearSelection.MakeEvent() );
1926 
1927     Go( &EE_SELECTION_TOOL::AddItemToSel,        EE_ACTIONS::addItemToSel.MakeEvent() );
1928     Go( &EE_SELECTION_TOOL::AddItemsToSel,       EE_ACTIONS::addItemsToSel.MakeEvent() );
1929     Go( &EE_SELECTION_TOOL::RemoveItemFromSel,   EE_ACTIONS::removeItemFromSel.MakeEvent() );
1930     Go( &EE_SELECTION_TOOL::RemoveItemsFromSel,  EE_ACTIONS::removeItemsFromSel.MakeEvent() );
1931     Go( &EE_SELECTION_TOOL::SelectionMenu,       EE_ACTIONS::selectionMenu.MakeEvent() );
1932 
1933     Go( &EE_SELECTION_TOOL::SelectAll,           EE_ACTIONS::selectAll.MakeEvent() );
1934 
1935     Go( &EE_SELECTION_TOOL::disambiguateCursor,  EVENTS::DisambiguatePoint );
1936 }
1937 
1938 
1939