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