KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_selection_tool.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2019 CERN
5 * Copyright The 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 <advanced_config.h>
26#include <core/typeinfo.h>
27#include <core/kicad_algo.h>
30#include <sch_actions.h>
31#include <sch_collectors.h>
32#include <sch_selection_tool.h>
33#include <sch_base_frame.h>
34#include <eeschema_id.h>
35#include <symbol_edit_frame.h>
36#include <symbol_viewer_frame.h>
37#include <math/util.h>
38#include <deque>
39#include <unordered_set>
41#include <geometry/shape_rect.h>
43#include <sch_painter.h>
45#include <sch_commit.h>
46#include <sch_edit_frame.h>
47#include <connection_graph.h>
48#include <sch_line.h>
49#include <sch_bus_entry.h>
50#include <sch_pin.h>
51#include <sch_group.h>
52#include <sch_marker.h>
53#include <sch_no_connect.h>
54#include <sch_sheet_pin.h>
55#include <sch_table.h>
56#include <tool/tool_event.h>
57#include <tool/tool_manager.h>
59#include <tools/sch_move_tool.h>
64#include <trigo.h>
65#include <view/view.h>
66#include <view/view_controls.h>
67#include <wx/log.h>
68
70
71
73{
74 if( aSel.GetSize() == 1 )
75 {
76 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aSel.Front() );
77
78 if( symbol )
79 return !symbol->GetLibSymbolRef() || !symbol->GetLibSymbolRef()->IsPower();
80 }
81
82 return false;
83};
84
85
87{
88 return aSel.GetSize() == 1 && aSel.Front()->Type() == SCH_SYMBOL_T;
89};
90
91
93{
94 if( aSel.GetSize() == 1 )
95 {
96 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aSel.Front() );
97
98 if( symbol )
99 return symbol->GetLibSymbolRef() && symbol->GetLibSymbolRef()->IsMultiBodyStyle();
100 }
101
102 return false;
103};
104
105
107{
108 if( aSel.GetSize() == 1 )
109 {
110 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aSel.Front() );
111
112 if( symbol )
113 return symbol->GetLibSymbolRef() && symbol->GetLibSymbolRef()->GetUnitCount() >= 2;
114 }
115
116 return false;
117};
118
119
121{
122 if( aSel.GetSize() == 1 )
123 {
124 SCH_PIN* pin = dynamic_cast<SCH_PIN*>( aSel.Front() );
125
126 if( pin && pin->GetLibPin() )
127 return !pin->GetLibPin()->GetAlternates().empty();
128 }
129
130 return false;
131};
132
133
135{
136 if( aSel.CountType( SCH_MARKER_T ) != 1 )
137 return false;
138
139 return !static_cast<SCH_MARKER*>( aSel.Front() )->IsExcluded();
140};
141
142
144{
145 return aSel.GetSize() > 1 && aSel.OnlyContains( { SCH_SYMBOL_T } );
146};
147
148
150{
151 return aSel.GetSize() >= 1 && aSel.OnlyContains( { SCH_PIN_T } );
152};
153
154
156{
157 return aSel.GetSize() >= 1 && aSel.OnlyContains( { SCH_PIN_T, SCH_SHEET_PIN_T } );
158};
159
160
161static void passEvent( TOOL_EVENT* const aEvent, const TOOL_ACTION* const aAllowedActions[] )
162{
163 for( int i = 0; aAllowedActions[i]; ++i )
164 {
165 if( aEvent->IsAction( aAllowedActions[i] ) )
166 {
167 aEvent->SetPassEvent();
168 break;
169 }
170 }
171}
172
173
174#define HITTEST_THRESHOLD_PIXELS 5
175
176
178 SELECTION_TOOL( "common.InteractiveSelection" ),
179 m_frame( nullptr ),
181 m_isSymbolEditor( false ),
182 m_isSymbolViewer( false ),
183 m_unit( 0 ),
184 m_bodyStyle( 0 ),
185 m_enteredGroup( nullptr ),
187 m_previous_first_cell( nullptr )
188{
189 m_filter.SetDefaults();
190 m_selection.Clear();
191}
192
193
199
200
216
217static std::vector<KICAD_T> connectedLineTypes =
218{
221};
222
242
243static std::vector<KICAD_T> crossProbingTypes =
244{
246 SCH_PIN_T,
248};
249
250static std::vector<KICAD_T> lineTypes = { SCH_LINE_T };
251static std::vector<KICAD_T> sheetTypes = { SCH_SHEET_T };
252static std::vector<KICAD_T> tableCellTypes = { SCH_TABLECELL_T };
253
255{
257
258 SYMBOL_VIEWER_FRAME* symbolViewerFrame = dynamic_cast<SYMBOL_VIEWER_FRAME*>( m_frame );
259 SYMBOL_EDIT_FRAME* symbolEditorFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
260
261 if( symbolEditorFrame )
262 {
263 m_isSymbolEditor = true;
264 m_unit = symbolEditorFrame->GetUnit();
265 m_bodyStyle = symbolEditorFrame->GetBodyStyle();
266 }
267 else
268 {
269 m_isSymbolViewer = symbolViewerFrame != nullptr;
270 }
271
272 // clang-format off
273 auto linesSelection = SCH_CONDITIONS::MoreThan( 0 ) && SCH_CONDITIONS::OnlyTypes( lineTypes );
274 auto wireOrBusSelection = SCH_CONDITIONS::Count( 1 ) && SCH_CONDITIONS::OnlyTypes( connectedLineTypes );
275 auto connectedSelection = SCH_CONDITIONS::MoreThan( 0 ) && SCH_CONDITIONS::OnlyTypes( connectedTypes );
276 auto expandableSelection =
278 auto sheetSelection = SCH_CONDITIONS::Count( 1 ) && SCH_CONDITIONS::OnlyTypes( sheetTypes );
279 auto crossProbingSelection = SCH_CONDITIONS::MoreThan( 0 ) && SCH_CONDITIONS::HasTypes( crossProbingTypes );
280 auto tableCellSelection = SCH_CONDITIONS::MoreThan( 0 ) && SCH_CONDITIONS::OnlyTypes( tableCellTypes );
281 auto multiplePinsSelection = SCH_CONDITIONS::MoreThan( 1 ) && SCH_CONDITIONS::OnlyTypes( { SCH_PIN_T } );
282 // clang-format on
283
284 auto schEditSheetPageNumberCondition =
285 [this] ( const SELECTION& aSel )
286 {
288 return false;
289
290 return SCH_CONDITIONS::LessThan( 2 )( aSel )
292 };
293
294 auto schEditCondition =
295 [this] ( const SELECTION& aSel )
296 {
298 };
299
300 auto belowRootSheetCondition =
301 [this]( const SELECTION& aSel )
302 {
303 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
304
305 return editFrame
306 && editFrame->GetCurrentSheet().Last() != &editFrame->Schematic().Root();
307 };
308
309 auto haveHighlight =
310 [this]( const SELECTION& sel )
311 {
312 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
313
314 return editFrame && !editFrame->GetHighlightedConnection().IsEmpty();
315 };
316
317 auto haveSymbol =
318 [this]( const SELECTION& sel )
319 {
320 return m_isSymbolEditor &&
321 static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
322 };
323
324 auto groupEnterCondition =
326
327 auto inGroupCondition =
328 [this] ( const SELECTION& )
329 {
330 return m_enteredGroup != nullptr;
331 };
332
333 auto multipleUnitsSelection = []( const SELECTION& aSel )
334 {
335 return !GetSameSymbolMultiUnitSelection( aSel ).empty();
336 };
337
338 auto allowPinSwaps =
339 [this]( const SELECTION& )
340 {
341 return m_frame->eeconfig() &&
342 m_frame->eeconfig()->m_Input.allow_unconstrained_pin_swaps;
343 };
344
345
346 auto& menu = m_menu->GetMenu();
347
348 // clang-format off
349 menu.AddItem( ACTIONS::groupEnter, groupEnterCondition, 1 );
350 menu.AddItem( ACTIONS::groupLeave, inGroupCondition, 1 );
351 menu.AddItem( SCH_ACTIONS::placeLinkedDesignBlock, groupEnterCondition, 1 );
352 menu.AddItem( SCH_ACTIONS::saveToLinkedDesignBlock, groupEnterCondition, 1 );
353 menu.AddItem( SCH_ACTIONS::clearHighlight, haveHighlight && SCH_CONDITIONS::Idle, 1 );
354 menu.AddSeparator( haveHighlight && SCH_CONDITIONS::Idle, 1 );
355
356 menu.AddItem( SCH_ACTIONS::selectConnection, expandableSelection && SCH_CONDITIONS::Idle, 2 );
357 menu.AddItem( ACTIONS::selectColumns, tableCellSelection && SCH_CONDITIONS::Idle, 2 );
358 menu.AddItem( ACTIONS::selectRows, tableCellSelection && SCH_CONDITIONS::Idle, 2 );
359 menu.AddItem( ACTIONS::selectTable, tableCellSelection && SCH_CONDITIONS::Idle, 2 );
360
361 menu.AddSeparator( 100 );
362 menu.AddItem( SCH_ACTIONS::drawWire, schEditCondition && SCH_CONDITIONS::Empty, 100 );
363 menu.AddItem( SCH_ACTIONS::drawBus, schEditCondition && SCH_CONDITIONS::Empty, 100 );
364
365 menu.AddSeparator( 100 );
367
368 menu.AddItem( SCH_ACTIONS::enterSheet, sheetSelection && SCH_CONDITIONS::Idle, 150 );
369 menu.AddItem( SCH_ACTIONS::selectOnPCB, crossProbingSelection && schEditCondition && SCH_CONDITIONS::Idle, 150 );
370 menu.AddItem( SCH_ACTIONS::leaveSheet, belowRootSheetCondition, 150 );
371
372 menu.AddSeparator( 200 );
373 menu.AddItem( SCH_ACTIONS::placeJunction, wireOrBusSelection && SCH_CONDITIONS::Idle, 250 );
374 menu.AddItem( SCH_ACTIONS::placeLabel, wireOrBusSelection && SCH_CONDITIONS::Idle, 250 );
375 menu.AddItem( SCH_ACTIONS::placeClassLabel, wireOrBusSelection && SCH_CONDITIONS::Idle, 250 );
376 menu.AddItem( SCH_ACTIONS::placeGlobalLabel, wireOrBusSelection && SCH_CONDITIONS::Idle, 250 );
377 menu.AddItem( SCH_ACTIONS::placeHierLabel, wireOrBusSelection && SCH_CONDITIONS::Idle, 250 );
378 menu.AddItem( SCH_ACTIONS::breakWire, linesSelection && SCH_CONDITIONS::Idle, 250 );
379 menu.AddItem( SCH_ACTIONS::slice, linesSelection && SCH_CONDITIONS::Idle, 250 );
380 menu.AddItem( SCH_ACTIONS::placeSheetPin, sheetSelection && SCH_CONDITIONS::Idle, 250 );
381 menu.AddItem( SCH_ACTIONS::autoplaceAllSheetPins, sheetSelection && SCH_CONDITIONS::Idle, 250 );
382 menu.AddItem( SCH_ACTIONS::syncSheetPins, sheetSelection && SCH_CONDITIONS::Idle, 250 );
383 menu.AddItem( SCH_ACTIONS::swapPinLabels, multiplePinsSelection && schEditCondition && SCH_CONDITIONS::Idle, 250 );
384 menu.AddItem( SCH_ACTIONS::swapUnitLabels, multipleUnitsSelection && schEditCondition && SCH_CONDITIONS::Idle, 250 );
385 menu.AddItem( SCH_ACTIONS::swapPins, multiplePinsSelection && schEditCondition && SCH_CONDITIONS::Idle && allowPinSwaps, 250 );
386 menu.AddItem( SCH_ACTIONS::assignNetclass, connectedSelection && SCH_CONDITIONS::Idle, 250 );
387 menu.AddItem( SCH_ACTIONS::findNetInInspector, connectedSelection && SCH_CONDITIONS::Idle, 250 );
388 menu.AddItem( SCH_ACTIONS::editPageNumber, schEditSheetPageNumberCondition, 250 );
389
390 menu.AddSeparator( 400 );
391 menu.AddItem( SCH_ACTIONS::symbolProperties, haveSymbol && SCH_CONDITIONS::Empty, 400 );
392 menu.AddItem( SCH_ACTIONS::pinTable, haveSymbol && SCH_CONDITIONS::Empty, 400 );
393
394 menu.AddSeparator( 1000 );
395 m_frame->AddStandardSubMenus( *m_menu.get() );
396 // clang-format on
397
398 m_disambiguateTimer.SetOwner( this );
399 Connect( m_disambiguateTimer.GetId(), wxEVT_TIMER,
400 wxTimerEventHandler( SCH_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
401
402 return true;
403}
404
405
407{
409
410 if( aReason != TOOL_BASE::REDRAW )
411 {
412 if( m_enteredGroup )
413 ExitGroup();
414
415 // Remove pointers to the selected items from containers without changing their
416 // properties (as they are already deleted while a new sheet is loaded)
417 m_selection.Clear();
418 }
419
420 if( aReason == RESET_REASON::SHUTDOWN )
421 return;
422
423 if( aReason == TOOL_BASE::MODEL_RELOAD || aReason == TOOL_BASE::SUPERMODEL_RELOAD )
424 {
425 getView()->GetPainter()->GetSettings()->SetHighlight( false );
426
427 SYMBOL_EDIT_FRAME* symbolEditFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
428 SYMBOL_VIEWER_FRAME* symbolViewerFrame = dynamic_cast<SYMBOL_VIEWER_FRAME*>( m_frame );
429
430 if( symbolEditFrame )
431 {
432 m_isSymbolEditor = true;
433 m_unit = symbolEditFrame->GetUnit();
434 m_bodyStyle = symbolEditFrame->GetBodyStyle();
435 }
436 else
437 {
438 m_isSymbolViewer = symbolViewerFrame != nullptr;
439 }
440 }
441
442 // Reinsert the VIEW_GROUP, in case it was removed from the VIEW
444 getView()->Add( &m_selection );
445
448}
449
451{
452 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
453
454 KIID lastRolloverItemId = niluuid;
456
457 auto pinOrientation =
458 []( EDA_ITEM* aItem )
459 {
460 SCH_PIN* pin = dynamic_cast<SCH_PIN*>( aItem );
461
462 if( pin )
463 {
464 const SCH_SYMBOL* parent = dynamic_cast<const SCH_SYMBOL*>( pin->GetParentSymbol() );
465
466 if( !parent )
467 return pin->GetOrientation();
468 else
469 {
470 SCH_PIN dummy( *pin );
472 return dummy.GetOrientation();
473 }
474 }
475
476 SCH_SHEET_PIN* sheetPin = dynamic_cast<SCH_SHEET_PIN*>( aItem );
477
478 if( sheetPin )
479 {
480 switch( sheetPin->GetSide() )
481 {
482 default:
487 }
488 }
489
491 };
492
493 // Main loop: keep receiving events
494 while( TOOL_EVENT* evt = Wait() )
495 {
496 bool selCancelled = false;
497 bool displayWireCursor = false;
498 bool displayBusCursor = false;
499 bool displayLineCursor = false;
500 KIID rolloverItemId = lastRolloverItemId;
501
502 // on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
503 setModifiersState( evt->Modifier( MD_SHIFT ), evt->Modifier( MD_CTRL ),
504 evt->Modifier( MD_ALT ) );
505
506 MOUSE_DRAG_ACTION drag_action = m_frame->GetDragAction();
507
508 if( evt->IsMouseDown( BUT_LEFT ) )
509 {
510 if( !m_frame->ToolStackIsEmpty() )
511 {
512 // Avoid triggering when running under other tools
513 }
514 else if( m_toolMgr->GetTool<SCH_POINT_EDITOR>()
515 && m_toolMgr->GetTool<SCH_POINT_EDITOR>()->HasPoint() )
516 {
517 // Distinguish point editor from selection modification by checking modifiers
518 if( hasModifier() )
519 {
520 m_originalCursor = m_toolMgr->GetMousePosition();
521 m_disambiguateTimer.StartOnce( ADVANCED_CFG::GetCfg().m_DisambiguationMenuDelay );
522 }
523 }
524 else
525 {
526 m_originalCursor = m_toolMgr->GetMousePosition();
527 m_disambiguateTimer.StartOnce( ADVANCED_CFG::GetCfg().m_DisambiguationMenuDelay );
528 }
529 }
530 // Single click? Select single object
531 else if( evt->IsClick( BUT_LEFT ) )
532 {
533 // If the timer has stopped, then we have already run the disambiguate routine
534 // and we don't want to register an extra click here
535 if( !m_disambiguateTimer.IsRunning() )
536 {
537 evt->SetPassEvent();
538 continue;
539 }
540
541 m_disambiguateTimer.Stop();
542
543 if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
544 schframe->ClearFocus();
545
546 // Collect items at the clicked location (doesn't select them yet)
547 SCH_COLLECTOR collector;
549
550 CollectHits( collector, evt->Position() );
551 size_t preFilterCount = collector.GetCount();
552 rejected.SetAll( false );
553 narrowSelection( collector, evt->Position(), false, false, &rejected );
554
555 if( m_selection.GetSize() != 0 && dynamic_cast<SCH_TABLECELL*>( m_selection.GetItem( 0 ) ) && m_additive
556 && collector.GetCount() == 1 && dynamic_cast<SCH_TABLECELL*>( collector[0] ) )
557 {
558 SCH_TABLECELL* firstCell = static_cast<SCH_TABLECELL*>( m_selection.GetItem( 0 ) );
559 SCH_TABLECELL* clickedCell = static_cast<SCH_TABLECELL*>( collector[0] );
560 bool allCellsFromSameTable = true;
561
562 if( m_previous_first_cell == nullptr || m_selection.GetSize() == 1)
563 {
564 m_previous_first_cell = firstCell;
565 }
566
568 {
569 if( !static_cast<SCH_TABLECELL*>( selection )
570 || selection->GetParent() != clickedCell->GetParent() )
571 {
572 allCellsFromSameTable = false;
573 }
574 }
575
576 if( m_previous_first_cell && clickedCell && allCellsFromSameTable )
577 {
579 selection->ClearSelected();
580
581 m_selection.Clear();
582 SCH_TABLE* parentTable = dynamic_cast<SCH_TABLE*>( m_previous_first_cell->GetParent() );
583
584 VECTOR2D start = m_previous_first_cell->GetCenter();
585 VECTOR2D end = clickedCell->GetCenter();
586
587 if( parentTable )
588 {
589 InitializeSelectionState( parentTable );
590
591 VECTOR2D topLeft( std::min( start.x, end.x ), std::min( start.y, end.y ) );
592 VECTOR2D bottomRight( std::max( start.x, end.x ), std::max( start.y, end.y ) );
593
594 SelectCellsBetween( topLeft, bottomRight - topLeft, parentTable );
595 }
596 }
597 }
598 else if( collector.GetCount() == 1 && !m_isSymbolEditor && !hasModifier() )
599 {
600 OPT_TOOL_EVENT autostart = autostartEvent( evt, grid, collector[0] );
601
602 if( autostart )
603 {
605
606 params->layer = autostart->Parameter<const DRAW_SEGMENT_EVENT_PARAMS*>()->layer;
607 params->quitOnDraw = true;
608 params->sourceSegment = dynamic_cast<SCH_LINE*>( collector[0] );
609
610 autostart->SetParameter<const DRAW_SEGMENT_EVENT_PARAMS*>( params );
611 m_toolMgr->ProcessEvent( *autostart );
612
613 selCancelled = true;
614 }
615 else if( collector[0]->HasHoveredHypertext() )
616 {
617 collector[ 0 ]->DoHypertextAction( m_frame, evt->Position() );
618 selCancelled = true;
619 }
620 else if( collector[0]->IsBrightened() )
621 {
622 if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
623 {
624 NET_NAVIGATOR_ITEM_DATA itemData( schframe->GetCurrentSheet(), collector[0] );
625
626 schframe->SelectNetNavigatorItem( &itemData );
627 }
628 }
629 }
630
631 if( !selCancelled )
632 {
633 if( collector.GetCount() == 0 && preFilterCount > 0 )
634 {
635 if( SCH_BASE_FRAME* frame = dynamic_cast<SCH_BASE_FRAME*>( m_frame ) )
636 frame->HighlightSelectionFilter( rejected );
637 }
638
639 selectPoint( collector, evt->Position(), nullptr, nullptr, m_additive, m_subtractive, m_exclusive_or );
640 m_selection.SetIsHover( false );
641 }
642 }
643 else if( evt->IsClick( BUT_RIGHT ) )
644 {
645 m_disambiguateTimer.Stop();
646
647 // right click? if there is any object - show the context menu
648 if( m_selection.Empty() )
649 {
651 SelectPoint( evt->Position(), { SCH_LOCATE_ANY_T }, nullptr, &selCancelled );
652 m_selection.SetIsHover( true );
653 }
654 // If the cursor has moved off the bounding box of the selection by more than
655 // a grid square, check to see if there is another item available for selection
656 // under the cursor. If there is, the user likely meant to get the context menu
657 // for that item. If there is no new item, then keep the original selection and
658 // show the context menu for it.
659 else if( !m_selection.GetBoundingBox().Inflate( grid.GetGrid().x, grid.GetGrid().y )
660 .Contains( evt->Position() ) )
661 {
662 SCH_COLLECTOR collector;
663
664 if( CollectHits( collector, evt->Position(), { SCH_LOCATE_ANY_T } ) )
665 {
667
668 SelectPoint( evt->Position(), { SCH_LOCATE_ANY_T }, nullptr, &selCancelled );
669 m_selection.SetIsHover( true );
670 }
671 }
672
673 if( !selCancelled )
674 m_menu->ShowContextMenu( m_selection );
675 }
676 else if( evt->IsDblClick( BUT_LEFT ) )
677 {
678 m_disambiguateTimer.Stop();
679
680 // double click? Display the properties window
681 if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
682 schframe->ClearFocus();
683
684 if( m_selection.Empty() )
685 SelectPoint( evt->Position() );
686
687 EDA_ITEM* item = m_selection.Front();
688
689 if( item && item->Type() == SCH_SHEET_T )
690 m_toolMgr->PostAction( SCH_ACTIONS::enterSheet );
691 else if( m_selection.GetSize() == 1 && m_selection[0]->Type() == SCH_GROUP_T )
692 EnterGroup();
693 else
694 m_toolMgr->PostAction( SCH_ACTIONS::properties );
695 }
696 else if( evt->IsDblClick( BUT_MIDDLE ) )
697 {
698 m_disambiguateTimer.Stop();
699
700 // Middle double click? Do zoom to fit or zoom to objects
701 if( evt->Modifier( MD_CTRL ) ) // Is CTRL key down?
703 else
704 m_toolMgr->RunAction( ACTIONS::zoomFitScreen );
705 }
706 else if( evt->IsDrag( BUT_LEFT ) )
707 {
708 m_disambiguateTimer.Stop();
709
710 // Is another tool already moving a new object? Don't allow a drag start
711 if( !m_selection.Empty() && m_selection[0]->HasFlag( IS_NEW | IS_MOVING ) )
712 {
713 evt->SetPassEvent();
714 continue;
715 }
716
717 // drag with LMB? Select multiple objects (or at least draw a selection box) or
718 // drag them
719 if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
720 schframe->ClearFocus();
721
722 SCH_COLLECTOR collector;
723
724 if( m_selection.GetSize() == 1 && dynamic_cast<SCH_TABLE*>( m_selection.GetItem( 0 ) ) )
725 {
726 m_toolMgr->RunAction( SCH_ACTIONS::move );
727 }
728 // Allow drag selecting table cells, except when they're inside a group that we haven't entered
729 else if( CollectHits( collector, evt->DragOrigin(), { SCH_TABLECELL_T } )
730 && ( collector[0]->GetParent()->GetParentGroup() == nullptr
731 || collector[0]->GetParent()->GetParentGroup() == m_enteredGroup ) )
732 {
733 selectTableCells( static_cast<SCH_TABLE*>( collector[0]->GetParent() ) );
734 }
735 else if( hasModifier() || drag_action == MOUSE_DRAG_ACTION::SELECT )
736 {
739 selectLasso();
740 else
742 }
743 else if( m_selection.Empty() && drag_action != MOUSE_DRAG_ACTION::DRAG_ANY )
744 {
747 selectLasso();
748 else
750 }
751 else
752 {
753 if( m_isSymbolEditor )
754 {
755 if( static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->IsSymbolAlias() )
756 {
758 }
759 else
760 {
764 SCH_PIN_T,
765 SCH_FIELD_T } );
766 }
767 }
768 else
769 {
771 }
772
773 // Check if dragging has started within any of selected items bounding box
774 if( evt->HasPosition() && selectionContains( evt->DragOrigin() ) )
775 {
776 // drag_is_move option exists only in schematic editor, not in symbol editor
777 // (m_frame->eeconfig() returns nullptr in Symbol Editor)
778 if( m_isSymbolEditor || m_frame->eeconfig()->m_Input.drag_is_move )
779 m_toolMgr->RunAction( SCH_ACTIONS::move );
780 else
781 m_toolMgr->RunAction( SCH_ACTIONS::drag );
782 }
783 else
784 {
785 // No -> drag a selection box
788 selectLasso();
789 else
791 }
792 }
793 }
794 else if( evt->IsMouseDown( BUT_AUX1 ) )
795 {
797 }
798 else if( evt->IsMouseDown( BUT_AUX2 ) )
799 {
801 }
802 else if( evt->Action() == TA_MOUSE_WHEEL )
803 {
804 int field = -1;
805
806 if( evt->Modifier() == ( MD_SHIFT | MD_ALT ) )
807 field = 0;
808 else if( evt->Modifier() == ( MD_CTRL | MD_ALT ) )
809 field = 1;
810 // any more?
811
812 if( field >= 0 )
813 {
814 const int delta = evt->Parameter<int>();
815 ACTIONS::INCREMENT incParams{ delta > 0 ? 1 : -1, field };
816
817 m_toolMgr->RunAction( ACTIONS::increment, incParams );
818 }
819 }
820 else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CHOICE_MENU_CHOICE )
821 {
822 m_disambiguateTimer.Stop();
823
824 // context sub-menu selection? Handle unit selection or bus unfolding
825 if( *evt->GetCommandId() >= ID_POPUP_SCH_SELECT_UNIT
826 && *evt->GetCommandId() <= ID_POPUP_SCH_SELECT_UNIT_END )
827 {
828 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( m_selection.Front() );
829 int unit = *evt->GetCommandId() - ID_POPUP_SCH_SELECT_UNIT;
830
831 if( symbol )
832 static_cast<SCH_EDIT_FRAME*>( m_frame )->SelectUnit( symbol, unit );
833 }
834 else if( *evt->GetCommandId() >= ID_POPUP_SCH_PLACE_UNIT
835 && *evt->GetCommandId() <= ID_POPUP_SCH_PLACE_UNIT_END )
836 {
837 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( m_selection.Front() );
838 int unit = *evt->GetCommandId() - ID_POPUP_SCH_PLACE_UNIT;
839
840 if( symbol )
843 }
844 else if( *evt->GetCommandId() >= ID_POPUP_SCH_SELECT_BODY_STYLE
845 && *evt->GetCommandId() <= ID_POPUP_SCH_SELECT_BODY_STYLE_END )
846 {
847 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( m_selection.Front() );
848 int bodyStyle = ( *evt->GetCommandId() - ID_POPUP_SCH_SELECT_BODY_STYLE ) + 1;
849
850 if( symbol && symbol->GetBodyStyle() != bodyStyle )
851 static_cast<SCH_EDIT_FRAME*>( m_frame )->SelectBodyStyle( symbol, bodyStyle );
852 }
853 else if( *evt->GetCommandId() >= ID_POPUP_SCH_ALT_PIN_FUNCTION
854 && *evt->GetCommandId() <= ID_POPUP_SCH_ALT_PIN_FUNCTION_END )
855 {
856 SCH_PIN* pin = dynamic_cast<SCH_PIN*>( m_selection.Front() );
857 wxString alt = *evt->Parameter<wxString*>();
858
859 if( pin )
860 static_cast<SCH_EDIT_FRAME*>( m_frame )->SetAltPinFunction( pin, alt );
861 }
862 else if( *evt->GetCommandId() >= ID_POPUP_SCH_PIN_TRICKS_START
863 && *evt->GetCommandId() <= ID_POPUP_SCH_PIN_TRICKS_END
865 {
866 SCH_EDIT_FRAME* sch_frame = static_cast<SCH_EDIT_FRAME*>( m_frame );
867
868 // Keep track of new items so we make them the new selection at the end
869 EDA_ITEMS newItems;
870 SCH_COMMIT commit( sch_frame );
871
872 if( *evt->GetCommandId() == ID_POPUP_SCH_PIN_TRICKS_NO_CONNECT )
873 {
874 for( EDA_ITEM* item : m_selection )
875 {
876 if( item->Type() != SCH_PIN_T && item->Type() != SCH_SHEET_PIN_T )
877 continue;
878
879 SCH_NO_CONNECT* nc = new SCH_NO_CONNECT( item->GetPosition() );
880 commit.Add( nc, sch_frame->GetScreen() );
881 newItems.push_back( nc );
882 }
883
884 if( !commit.Empty() )
885 {
886 commit.Push( wxS( "No Connect Pins" ) );
888 }
889 }
890 else if( *evt->GetCommandId() == ID_POPUP_SCH_PIN_TRICKS_WIRE )
891 {
892 VECTOR2I wireGrid = grid.GetGridSize( GRID_HELPER_GRIDS::GRID_WIRES );
893
894 for( EDA_ITEM* item : m_selection )
895 {
896 if( item->Type() != SCH_PIN_T && item->Type() != SCH_SHEET_PIN_T )
897 continue;
898
899 SCH_LINE* wire = new SCH_LINE( item->GetPosition(), LAYER_WIRE );
900
901 // Add some length to the wire as nothing in our code base handles
902 // 0 length wires very well, least of all the ortho drag algorithm
903 VECTOR2I stub;
904
905 switch( pinOrientation( item ) )
906 {
907 default:
909 stub = VECTOR2I( -1 * wireGrid.x, 0 );
910 break;
912 stub = VECTOR2I( 1 * wireGrid.x, 0 );
913 break;
915 stub = VECTOR2I( 0, 1 * wireGrid.y );
916 break;
918 stub = VECTOR2I( 0, -1 * wireGrid.y );
919 break;
920 }
921
922 wire->SetEndPoint( item->GetPosition() + stub );
923
924 m_frame->AddToScreen( wire, sch_frame->GetScreen() );
925 commit.Added( wire, sch_frame->GetScreen() );
926 newItems.push_back( wire );
927 }
928
929 if( !commit.Empty() )
930 {
932 AddItemsToSel( &newItems );
933
934 // Select only the ends so we can immediately start dragging them
935 for( EDA_ITEM* item : newItems )
936 static_cast<SCH_LINE*>( item )->SetFlags( ENDPOINT );
937
939
940 // Put the mouse on the nearest point of the first wire
941 SCH_LINE* first = static_cast<SCH_LINE*>( newItems[0] );
942 vc->SetCrossHairCursorPosition( first->GetEndPoint(), false );
943 vc->WarpMouseCursor( vc->GetCursorPosition(), true );
944
945 // Start the drag tool, canceling will remove the wires
946 if( m_toolMgr->RunSynchronousAction( SCH_ACTIONS::drag, &commit ) )
947 commit.Push( wxS( "Wire Pins" ) );
948 else
949 commit.Revert();
950 }
951 }
952 else
953 {
954 // For every pin in the selection, add a label according to menu item
955 // selected by the user
956 for( EDA_ITEM* item : m_selection )
957 {
958 SCH_PIN* pin = dynamic_cast<SCH_PIN*>( item );
959 SCH_SHEET_PIN* sheetPin = dynamic_cast<SCH_SHEET_PIN*>( item );
960 SCH_LABEL_BASE* label = nullptr;
961 SCH_SHEET_PATH& sheetPath = sch_frame->GetCurrentSheet();
962
963 wxString labelText;
964
965 if( pin )
966 {
967 labelText = pin->GetShownName();
968
969 if( labelText.IsEmpty() )
970 {
971 labelText.Printf( "%s_%s",
972 pin->GetParentSymbol()->GetRef( &sheetPath ),
973 pin->GetNumber() );
974 }
975 }
976 else if( sheetPin )
977 {
978 labelText = sheetPin->GetShownText( &sheetPath, false );
979 }
980 else
981 {
982 continue;
983 }
984
985 switch( *evt->GetCommandId() )
986 {
988 label = new SCH_LABEL( item->GetPosition(), labelText );
989 break;
991 label = new SCH_HIERLABEL( item->GetPosition(), labelText );
992 break;
994 label = new SCH_GLOBALLABEL( item->GetPosition(), labelText );
995 break;
996 default:
997 continue;
998 }
999
1000 switch( pinOrientation( item ) )
1001 {
1002 default:
1005 break;
1008 break;
1011 break;
1014 break;
1015 }
1016
1018
1019 if( pin )
1020 {
1021 pinType = pin->GetType();
1022 }
1023 else if( sheetPin )
1024 {
1025 switch( sheetPin->GetLabelShape() )
1026 {
1027 case LABEL_INPUT: pinType = ELECTRICAL_PINTYPE::PT_INPUT; break;
1028 case LABEL_OUTPUT: pinType = ELECTRICAL_PINTYPE::PT_OUTPUT; break;
1029 case LABEL_BIDI: pinType = ELECTRICAL_PINTYPE::PT_BIDI; break;
1030 case LABEL_TRISTATE: pinType = ELECTRICAL_PINTYPE::PT_TRISTATE; break;
1031 case LABEL_PASSIVE: pinType = ELECTRICAL_PINTYPE::PT_PASSIVE; break;
1032 }
1033 }
1034
1035 switch( pinType )
1036 {
1039 break;
1042 break;
1045 break;
1048 break;
1051 break;
1052 default:
1054 }
1055
1056 commit.Add( label, sch_frame->GetScreen() );
1057 newItems.push_back( label );
1058 }
1059
1060 if( !commit.Empty() )
1061 {
1062 commit.Push( wxS( "Label Pins" ) );
1063
1064 // Many users will want to drag these items to wire off of the pins, so
1065 // pre-select them.
1067 AddItemsToSel( &newItems );
1068 }
1069 }
1070 }
1071 else if( *evt->GetCommandId() >= ID_POPUP_SCH_UNFOLD_BUS
1072 && *evt->GetCommandId() <= ID_POPUP_SCH_UNFOLD_BUS_END )
1073 {
1074 wxString* net = new wxString( *evt->Parameter<wxString*>() );
1075 m_toolMgr->RunAction<wxString*>( SCH_ACTIONS::unfoldBus, net );
1076 }
1077 }
1078 else if( evt->IsCancelInteractive() )
1079 {
1080 m_disambiguateTimer.Stop();
1081
1082 // We didn't set these, but we have reports that they leak out of some other tools,
1083 // so we clear them here.
1084 getViewControls()->SetAutoPan( false );
1085 getViewControls()->CaptureCursor( false );
1086
1087 if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
1088 schframe->ClearFocus();
1089
1090 if( !GetSelection().Empty() )
1091 {
1093 }
1094 else if( evt->FirstResponder() == this && evt->GetCommandId() == (int) WXK_ESCAPE )
1095 {
1096 if( m_enteredGroup )
1097 {
1098 ExitGroup();
1099 }
1100 else
1101 {
1103
1104 if( editor && m_frame->eeconfig()->m_Input.esc_clears_net_highlight )
1105 editor->ClearHighlight( *evt );
1106 }
1107 }
1108 }
1109 else if( evt->Action() == TA_UNDO_REDO_PRE )
1110 {
1111 if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
1112 schframe->ClearFocus();
1113 }
1114 else if( evt->IsMotion() && !m_isSymbolEditor && evt->FirstResponder() == this )
1115 {
1116 // Update cursor and rollover item
1117 rolloverItemId = niluuid;
1118 SCH_COLLECTOR collector;
1119
1121
1122 if( CollectHits( collector, evt->Position() ) )
1123 {
1124 narrowSelection( collector, evt->Position(), false, false, nullptr );
1125
1126 if( collector.GetCount() == 1 && !hasModifier() )
1127 {
1128 OPT_TOOL_EVENT autostartEvt = autostartEvent( evt, grid, collector[0] );
1129
1130 if( autostartEvt )
1131 {
1132 if( autostartEvt->Matches( SCH_ACTIONS::drawBus.MakeEvent() ) )
1133 displayBusCursor = true;
1134 else if( autostartEvt->Matches( SCH_ACTIONS::drawWire.MakeEvent() ) )
1135 displayWireCursor = true;
1136 else if( autostartEvt->Matches( SCH_ACTIONS::drawLines.MakeEvent() ) )
1137 displayLineCursor = true;
1138 }
1139 else if( collector[0]->HasHypertext() && !collector[0]->IsSelected() )
1140 {
1141 rolloverItemId = collector[0]->m_Uuid;
1142 }
1143 }
1144 }
1145 }
1146 else
1147 {
1148 evt->SetPassEvent();
1149 }
1150
1151 if( lastRolloverItemId != niluuid && lastRolloverItemId != rolloverItemId )
1152 {
1153 EDA_ITEM* item = m_frame->ResolveItem( lastRolloverItemId );
1154
1155 item->SetIsRollover( false, { 0, 0 } );
1156
1157 if( item->Type() == SCH_FIELD_T || item->Type() == SCH_TABLECELL_T )
1158 m_frame->GetCanvas()->GetView()->Update( item->GetParent() );
1159 else
1160 m_frame->GetCanvas()->GetView()->Update( item );
1161 }
1162
1163 SCH_ITEM* rolloverItem = nullptr;
1164
1165 if( rolloverItemId != niluuid )
1166 {
1167 rolloverItem = static_cast<SCH_ITEM*>( m_frame->ResolveItem( rolloverItemId ) );
1168
1169 rolloverItem->SetIsRollover( true, getViewControls()->GetMousePosition() );
1170
1171 if( rolloverItem->Type() == SCH_FIELD_T || rolloverItem->Type() == SCH_TABLECELL_T )
1172 m_frame->GetCanvas()->GetView()->Update( rolloverItem->GetParent() );
1173 else
1174 m_frame->GetCanvas()->GetView()->Update( rolloverItem );
1175 }
1176
1177 lastRolloverItemId = rolloverItemId;
1178
1179 if( m_frame->ToolStackIsEmpty() )
1180 {
1181 if( displayWireCursor )
1182 {
1184 }
1185 else if( displayBusCursor )
1186 {
1188 }
1189 else if( displayLineCursor )
1190 {
1192 }
1193 else if( rolloverItem && rolloverItem->HasHoveredHypertext() )
1194 {
1196 }
1197 else if( !m_selection.Empty()
1198 && drag_action == MOUSE_DRAG_ACTION::DRAG_SELECTED
1199 && evt->HasPosition()
1200 && selectionContains( evt->Position() ) //move/drag option prediction
1201 )
1202 {
1204 }
1205 else
1206 {
1208 }
1209 }
1210 }
1211
1212 m_disambiguateTimer.Stop();
1213
1214 // Shutting down; clear the selection
1215 m_selection.Clear();
1216
1217 return 0;
1218}
1219
1220
1222{
1223 wxCHECK_RET( m_selection.GetSize() == 1 && m_selection[0]->Type() == SCH_GROUP_T,
1224 wxT( "EnterGroup called when selection is not a single group" ) );
1225 SCH_GROUP* aGroup = static_cast<SCH_GROUP*>( m_selection[0] );
1226
1227 if( m_enteredGroup != nullptr )
1228 ExitGroup();
1229
1231 m_enteredGroup = aGroup;
1232 m_enteredGroup->SetFlags( ENTERED );
1233 m_enteredGroup->RunOnChildren(
1234 [&]( SCH_ITEM* aChild )
1235 {
1236 if( aChild->Type() == SCH_LINE_T )
1237 aChild->SetFlags( STARTPOINT | ENDPOINT );
1238
1239 select( aChild );
1240 },
1242
1243 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1244
1245 getView()->Hide( m_enteredGroup, true );
1248}
1249
1250
1251void SCH_SELECTION_TOOL::ExitGroup( bool aSelectGroup )
1252{
1253 // Only continue if there is a group entered
1254 if( m_enteredGroup == nullptr )
1255 return;
1256
1257 m_enteredGroup->ClearFlags( ENTERED );
1258 getView()->Hide( m_enteredGroup, false );
1260
1261 if( aSelectGroup )
1262 {
1264 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1265 }
1266
1267 m_enteredGroupOverlay.Clear();
1268 m_enteredGroup = nullptr;
1270}
1271
1272
1274 SCH_ITEM* aItem )
1275{
1276 VECTOR2I pos = aGrid.BestSnapAnchor( aEvent->Position(), aGrid.GetItemGrid( aItem ) );
1277
1278 if( m_frame->eeconfig()->m_Drawing.auto_start_wires
1279 && !m_toolMgr->GetTool<SCH_POINT_EDITOR>()->HasPoint()
1280 && aItem->IsPointClickableAnchor( pos ) )
1281 {
1282 OPT_TOOL_EVENT newEvt = SCH_ACTIONS::drawWire.MakeEvent();
1283
1284 if( aItem->Type() == SCH_BUS_BUS_ENTRY_T )
1285 {
1286 newEvt = SCH_ACTIONS::drawBus.MakeEvent();
1287 }
1288 else if( aItem->Type() == SCH_BUS_WIRE_ENTRY_T )
1289 {
1290 SCH_BUS_WIRE_ENTRY* busEntry = static_cast<SCH_BUS_WIRE_ENTRY*>( aItem );
1291
1292 if( !busEntry->m_connected_bus_item )
1293 newEvt = SCH_ACTIONS::drawBus.MakeEvent();
1294 }
1295 else if( aItem->Type() == SCH_LINE_T )
1296 {
1297 SCH_LINE* line = static_cast<SCH_LINE*>( aItem );
1298
1299 if( line->IsBus() )
1300 newEvt = SCH_ACTIONS::drawBus.MakeEvent();
1301 else if( line->IsGraphicLine() )
1302 newEvt = SCH_ACTIONS::drawLines.MakeEvent();
1303 }
1304 else if( aItem->Type() == SCH_LABEL_T || aItem->Type() == SCH_HIER_LABEL_T
1305 || aItem->Type() == SCH_SHEET_PIN_T || aItem->Type() == SCH_GLOBAL_LABEL_T )
1306 {
1307 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( aItem );
1308 SCH_CONNECTION possibleConnection( label->Schematic()->ConnectionGraph() );
1309 possibleConnection.ConfigureFromLabel( label->GetShownText( false ) );
1310
1311 if( possibleConnection.IsBus() )
1312 newEvt = SCH_ACTIONS::drawBus.MakeEvent();
1313 }
1314 else if( aItem->Type() == SCH_SYMBOL_T )
1315 {
1316 const SCH_SYMBOL* symbol = static_cast<const SCH_SYMBOL*>( aItem );
1317 const SCH_PIN* pin = symbol->GetPin( pos );
1318
1319 if( !pin || !pin->IsPointClickableAnchor( pos ) )
1320 return OPT_TOOL_EVENT();
1321
1322 if( !pin->IsVisible()
1323 && !( m_frame->eeconfig()->m_Appearance.show_hidden_pins
1324 || m_frame->GetRenderSettings()->m_ShowHiddenPins ) )
1325 {
1326 return OPT_TOOL_EVENT();
1327 }
1328 }
1329
1330 newEvt->SetMousePosition( pos );
1331 newEvt->SetHasPosition( true );
1332 newEvt->SetForceImmediate( true );
1333
1334 getViewControls()->ForceCursorPosition( true, pos );
1335
1336 return newEvt;
1337 }
1338
1339 return OPT_TOOL_EVENT();
1340}
1341
1342
1344{
1345 wxMouseState keyboardState = wxGetMouseState();
1346
1347 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
1348 keyboardState.AltDown() );
1349
1350 m_skip_heuristics = true;
1353 m_skip_heuristics = false;
1354
1355 return 0;
1356}
1357
1358
1359void SCH_SELECTION_TOOL::OnIdle( wxIdleEvent& aEvent )
1360{
1361 if( m_frame->ToolStackIsEmpty() && !m_multiple )
1362 {
1363 wxMouseState keyboardState = wxGetMouseState();
1364
1365 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
1366 keyboardState.AltDown() );
1367
1368 if( m_additive )
1369 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ADD );
1370 else if( m_subtractive )
1371 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SUBTRACT );
1372 else if( m_exclusive_or )
1373 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::XOR );
1374 else
1375 m_frame->GetCanvas()->SetCurrentCursor( m_nonModifiedCursor );
1376 }
1377}
1378
1379
1384
1385
1387 const std::vector<KICAD_T>& aScanTypes )
1388{
1389 int pixelThreshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
1390 int gridThreshold = KiROUND( getView()->GetGAL()->GetGridSize().EuclideanNorm() / 2.0 );
1391 aCollector.m_Threshold = std::max( pixelThreshold, gridThreshold );
1392 aCollector.m_ShowPinElectricalTypes = m_frame->GetRenderSettings()->m_ShowPinsElectricalType;
1393
1394 if( m_isSymbolEditor )
1395 {
1396 LIB_SYMBOL* symbol = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
1397
1398 if( !symbol )
1399 return false;
1400
1401 aCollector.Collect( symbol->GetDrawItems(), aScanTypes, aWhere, m_unit, m_bodyStyle );
1402 }
1403 else
1404 {
1405 aCollector.Collect( m_frame->GetScreen(), aScanTypes, aWhere, m_unit, m_bodyStyle );
1406
1407 // If pins are disabled in the filter, they will be removed later. Let's add the parent
1408 // so that people can use pins to select symbols in this case.
1409 if( !m_filter.pins )
1410 {
1411 int originalCount = aCollector.GetCount();
1412
1413 for( int ii = 0; ii < originalCount; ++ii )
1414 {
1415 if( aCollector[ii]->Type() == SCH_PIN_T )
1416 {
1417 SCH_PIN* pin = static_cast<SCH_PIN*>( aCollector[ii] );
1418
1419 if( !aCollector.HasItem( pin->GetParentSymbol() ) )
1420 aCollector.Append( pin->GetParentSymbol() );
1421 }
1422 }
1423 }
1424 }
1425
1426 return aCollector.GetCount() > 0;
1427}
1428
1429
1431 bool aCheckLocked, bool aSelectedOnly,
1432 SCH_SELECTION_FILTER_OPTIONS* aRejected )
1433{
1434 SYMBOL_EDIT_FRAME* symbolEditorFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
1435
1436 for( int i = collector.GetCount() - 1; i >= 0; --i )
1437 {
1438 if( symbolEditorFrame )
1439 {
1440 // Do not select invisible items if they are not displayed
1441 EDA_ITEM* item = collector[i];
1442
1443 if( item->Type() == SCH_FIELD_T )
1444 {
1445 if( !static_cast<SCH_FIELD*>( item )->IsVisible()
1446 && !symbolEditorFrame->GetShowInvisibleFields() )
1447 {
1448 collector.Remove( i );
1449 continue;
1450 }
1451 }
1452 else if( item->Type() == SCH_PIN_T )
1453 {
1454 if( !static_cast<SCH_PIN*>( item )->IsVisible()
1455 && !symbolEditorFrame->GetShowInvisiblePins() )
1456 {
1457 collector.Remove( i );
1458 continue;
1459 }
1460 }
1461 }
1462
1463 if( !Selectable( collector[i], &aWhere ) )
1464 {
1465 collector.Remove( i );
1466 continue;
1467 }
1468
1469 if( aCheckLocked && collector[i]->IsLocked() )
1470 {
1471 if( aRejected )
1472 aRejected->lockedItems = true;
1473 collector.Remove( i );
1474 continue;
1475 }
1476
1477 if( !itemPassesFilter( collector[i], aRejected ) )
1478 {
1479 collector.Remove( i );
1480 continue;
1481 }
1482
1483 if( aSelectedOnly && !collector[i]->IsSelected() )
1484 {
1485 collector.Remove( i );
1486 continue;
1487 }
1488 }
1489
1490 filterCollectorForHierarchy( collector, false );
1491
1492 // Apply some ugly heuristics to avoid disambiguation menus whenever possible
1493 if( collector.GetCount() > 1 && !m_skip_heuristics )
1494 GuessSelectionCandidates( collector, aWhere );
1495}
1496
1497
1499 EDA_ITEM** aItem, bool* aSelectionCancelledFlag, bool aAdd,
1500 bool aSubtract, bool aExclusiveOr )
1501{
1502 m_selection.ClearReferencePoint();
1503
1504 // If still more than one item we're going to have to ask the user.
1505 if( aCollector.GetCount() > 1 )
1506 {
1507 // Try to call selectionMenu via RunAction() to avoid event-loop contention
1508 // But it we cannot handle the event, then we don't have an active tool loop, so
1509 // handle it directly.
1510 if( !m_toolMgr->RunAction<COLLECTOR*>( ACTIONS::selectionMenu, &aCollector ) )
1511 {
1512 if( !doSelectionMenu( &aCollector ) )
1513 aCollector.m_MenuCancelled = true;
1514 }
1515
1516 if( aCollector.m_MenuCancelled )
1517 {
1518 if( aSelectionCancelledFlag )
1519 *aSelectionCancelledFlag = true;
1520
1521 return false;
1522 }
1523 }
1524
1525 if( !aAdd && !aSubtract && !aExclusiveOr )
1527
1528 // It is possible for slop in the selection model to cause us to be outside the group,
1529 // but also selecting an item within the group, so only exit if the selection doesn't
1530 // have an item belonging to the group
1531 if( m_enteredGroup && !m_enteredGroup->GetBoundingBox().Contains( aWhere ) )
1532 {
1533 bool foundEnteredGroup = false;
1534 for( EDA_ITEM* item : aCollector )
1535 {
1536 if( item->GetParentGroup() == m_enteredGroup )
1537 {
1538 foundEnteredGroup = true;
1539 break;
1540 }
1541 }
1542
1543 if( !foundEnteredGroup )
1544 ExitGroup();
1545 }
1546
1547 filterCollectorForHierarchy( aCollector, true );
1548
1549 int addedCount = 0;
1550 bool anySubtracted = false;
1551
1552 if( aCollector.GetCount() > 0 )
1553 {
1554 for( int i = 0; i < aCollector.GetCount(); ++i )
1555 {
1556 EDA_ITEM_FLAGS flags = 0;
1557 bool isLine = aCollector[i]->Type() == SCH_LINE_T;
1558
1559 // Handle line ends specially
1560 if( isLine )
1561 {
1562 SCH_LINE* line = (SCH_LINE*) aCollector[i];
1563
1564 if( line->GetStartPoint().Distance( aWhere ) <= aCollector.m_Threshold )
1565 flags = STARTPOINT;
1566 else if( line->GetEndPoint().Distance( aWhere ) <= aCollector.m_Threshold )
1567 flags = ENDPOINT;
1568 else
1569 flags = STARTPOINT | ENDPOINT;
1570 }
1571
1572 if( aSubtract
1573 || ( aExclusiveOr && aCollector[i]->IsSelected()
1574 && ( !isLine || ( isLine && aCollector[i]->HasFlag( flags ) ) ) ) )
1575 {
1576 aCollector[i]->ClearFlags( flags );
1577
1578 // Need to update end shadows after ctrl-click unselecting one of two selected
1579 // endpoints.
1580 if( isLine )
1581 getView()->Update( aCollector[i] );
1582
1583 if( !aCollector[i]->HasFlag( STARTPOINT ) && !aCollector[i]->HasFlag( ENDPOINT ) )
1584 {
1585 unselect( aCollector[i] );
1586 anySubtracted = true;
1587 }
1588 }
1589 else
1590 {
1591 aCollector[i]->SetFlags( flags );
1592 select( aCollector[i] );
1593 addedCount++;
1594 }
1595 }
1596 }
1597
1598 if( addedCount == 1 )
1599 {
1600 m_toolMgr->ProcessEvent( EVENTS::PointSelectedEvent );
1601
1602 if( aItem && aCollector.GetCount() == 1 )
1603 *aItem = aCollector[0];
1604
1605 return true;
1606 }
1607 else if( addedCount > 1 )
1608 {
1609 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1610 return true;
1611 }
1612 else if( anySubtracted )
1613 {
1614 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1615 return true;
1616 }
1617
1618 m_frame->GetCanvas()->ForceRefresh();
1619 return false;
1620}
1621
1622
1624 const std::vector<KICAD_T>& aScanTypes,
1625 EDA_ITEM** aItem, bool* aSelectionCancelledFlag,
1626 bool aCheckLocked, bool aAdd, bool aSubtract,
1627 bool aExclusiveOr )
1628{
1629 SCH_COLLECTOR collector;
1630
1631 if( !CollectHits( collector, aWhere, aScanTypes ) )
1632 return false;
1633
1634 size_t preFilterCount = collector.GetCount();
1636 rejected.SetAll( false );
1637 narrowSelection( collector, aWhere, aCheckLocked, aSubtract, &rejected );
1638
1639 if( collector.GetCount() == 0 && preFilterCount > 0 )
1640 {
1641 if( SCH_BASE_FRAME* frame = dynamic_cast<SCH_BASE_FRAME*>( m_frame ) )
1642 frame->HighlightSelectionFilter( rejected );
1643
1644 return false;
1645 }
1646
1647 return selectPoint( collector, aWhere, aItem, aSelectionCancelledFlag, aAdd, aSubtract,
1648 aExclusiveOr );
1649}
1650
1651
1653{
1654 SCH_COLLECTOR collection;
1655 m_multiple = true; // Multiple selection mode is active
1656 KIGFX::VIEW* view = getView();
1657
1658 std::vector<EDA_ITEM*> sheetPins;
1659
1660 // Filter the view items based on the selection box
1661 BOX2I selectionBox;
1662
1663 selectionBox.SetMaximum();
1664 view->Query( selectionBox,
1665 [&]( KIGFX::VIEW_ITEM* viewItem ) -> bool
1666 {
1667 SCH_ITEM* item = static_cast<SCH_ITEM*>( viewItem );
1668
1669 if( !item )
1670 return true;
1671
1672 collection.Append( item );
1673 return true;
1674 } );
1675
1676 filterCollectorForHierarchy( collection, true );
1677
1678 // Sheet pins aren't in the view; add them by hand
1679 for( EDA_ITEM* item : collection )
1680 {
1681 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item );
1682
1683 if( sheet )
1684 {
1685 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
1686 sheetPins.emplace_back( pin );
1687 }
1688 }
1689
1690 for( EDA_ITEM* pin : sheetPins )
1691 collection.Append( pin );
1692
1693 for( EDA_ITEM* item : collection )
1694 {
1695 if( Selectable( item ) && itemPassesFilter( item, nullptr ) )
1696 {
1697 if( item->Type() == SCH_LINE_T )
1698 item->SetFlags( STARTPOINT | ENDPOINT );
1699
1700 select( item );
1701 }
1702 }
1703
1704 m_multiple = false;
1705
1706 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1707 m_frame->GetCanvas()->ForceRefresh();
1708 return 0;
1709}
1710
1712{
1713 m_multiple = true; // Multiple selection mode is active
1714 KIGFX::VIEW* view = getView();
1715
1716 // hold all visible items
1717 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
1718
1719 // Filter the view items based on the selection box
1720 BOX2I selectionBox;
1721
1722 selectionBox.SetMaximum();
1723 view->Query( selectionBox, selectedItems ); // Get the list of selected items
1724
1725 for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : selectedItems )
1726 {
1727 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( pair.first );
1728
1729 if( sheet )
1730 {
1731 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
1732 {
1733 EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( pin );
1734
1735 if( item && Selectable( item ) )
1736 unselect( item );
1737 }
1738 }
1739
1740 if( EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( pair.first ) )
1741 {
1742 if( Selectable( item ) )
1743 unselect( item );
1744 }
1745 }
1746
1747 m_multiple = false;
1748
1749 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1750 m_frame->GetCanvas()->ForceRefresh();
1751 return 0;
1752}
1753
1754
1756{
1757 // Prefer exact hits to sloppy ones
1758 std::set<EDA_ITEM*> exactHits;
1759
1760 for( int i = collector.GetCount() - 1; i >= 0; --i )
1761 {
1762 EDA_ITEM* item = collector[ i ];
1763 SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
1764 SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( item );
1765 SCH_TABLE* table = dynamic_cast<SCH_TABLE*>( item );
1766
1767 // Lines are hard to hit. Give them a bit more slop to still be considered "exact".
1768 if( line || ( shape && shape->GetShape() == SHAPE_T::POLY )
1769 || ( shape && shape->GetShape() == SHAPE_T::ARC ) )
1770 {
1771 int pixelThreshold = KiROUND( getView()->ToWorld( 6 ) );
1772
1773 if( item->HitTest( aPos, pixelThreshold ) )
1774 exactHits.insert( item );
1775 }
1776 else if( table )
1777 {
1778 // Consider table cells exact, but not the table itself
1779 }
1780 else
1781 {
1782
1783 if( m_frame->GetRenderSettings()->m_ShowPinsElectricalType )
1784 item->SetFlags( SHOW_ELEC_TYPE );
1785
1786 if( item->HitTest( aPos, 0 ) )
1787 exactHits.insert( item );
1788
1789 item->ClearFlags( SHOW_ELEC_TYPE );
1790 }
1791 }
1792
1793 if( exactHits.size() > 0 && exactHits.size() < (unsigned) collector.GetCount() )
1794 {
1795 for( int i = collector.GetCount() - 1; i >= 0; --i )
1796 {
1797 EDA_ITEM* item = collector[ i ];
1798
1799 if( !exactHits.contains( item ) )
1800 collector.Transfer( item );
1801 }
1802 }
1803
1804 // Find the closest item. (Note that at this point all hits are either exact or non-exact.)
1805 SEG poss( aPos, aPos );
1806 EDA_ITEM* closest = nullptr;
1807 int closestDist = INT_MAX / 4;
1808
1809 for( EDA_ITEM* item : collector )
1810 {
1811 BOX2I bbox = item->GetBoundingBox();
1812 int dist = INT_MAX / 4;
1813
1814 // A dominating item is one that would unfairly win distance tests
1815 // and mask out other items. For example, a filled rectangle "wins"
1816 // with a zero distance over anything inside it.
1817 bool dominating = false;
1818
1819 if( exactHits.contains( item ) )
1820 {
1821 if( item->Type() == SCH_PIN_T || item->Type() == SCH_JUNCTION_T )
1822 {
1823 closest = item;
1824 break;
1825 }
1826
1827 SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
1828 SCH_FIELD* field = dynamic_cast<SCH_FIELD*>( item );
1829 EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item );
1830 EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( item );
1831 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
1832
1833 if( line )
1834 {
1835 dist = line->GetSeg().Distance( aPos );
1836 }
1837 else if( field )
1838 {
1839 BOX2I box = field->GetBoundingBox();
1840 EDA_ANGLE orient = field->GetTextAngle();
1841
1842 if( field->GetParent() && field->GetParent()->Type() == SCH_SYMBOL_T )
1843 {
1844 if( static_cast<SCH_SYMBOL*>( field->GetParent() )->GetTransform().y1 )
1845 {
1846 if( orient.IsHorizontal() )
1847 orient = ANGLE_VERTICAL;
1848 else
1849 orient = ANGLE_HORIZONTAL;
1850 }
1851 }
1852
1853 field->GetEffectiveTextShape( false, box, orient )->Collide( poss, INT_MAX / 4, &dist );
1854 }
1855 else if( text )
1856 {
1857 text->GetEffectiveTextShape( false )->Collide( poss, INT_MAX / 4, &dist );
1858 }
1859 else if( shape )
1860 {
1861 auto shapes = std::make_shared<SHAPE_COMPOUND>( shape->MakeEffectiveShapesForHitTesting() );
1862
1863 shapes->Collide( poss, INT_MAX / 4, &dist );
1864
1865 // Filled shapes win hit tests anywhere inside them
1866 dominating = shape->IsFilledForHitTesting();
1867 }
1868 else if( symbol )
1869 {
1870 bbox = symbol->GetBodyBoundingBox();
1871
1872 SHAPE_RECT rect( bbox.GetPosition(), bbox.GetWidth(), bbox.GetHeight() );
1873
1874 if( bbox.Contains( aPos ) )
1875 dist = bbox.GetCenter().Distance( aPos );
1876 else
1877 rect.Collide( poss, closestDist, &dist );
1878 }
1879 else
1880 {
1881 dist = bbox.GetCenter().Distance( aPos );
1882 }
1883 }
1884 else
1885 {
1886 SHAPE_RECT rect( bbox.GetPosition(), bbox.GetWidth(), bbox.GetHeight() );
1887 rect.Collide( poss, collector.m_Threshold, &dist );
1888 }
1889
1890 // Don't promote dominating items to be the closest item
1891 // (they'll always win) - they'll still be available for selection, but they
1892 // won't boot out worthy competitors.
1893 if ( !dominating )
1894 {
1895 if( dist == closestDist )
1896 {
1897 if( item->GetParent() == closest )
1898 closest = item;
1899 }
1900 else if( dist < closestDist )
1901 {
1902 closestDist = dist;
1903 closest = item;
1904 }
1905 }
1906 }
1907
1908 // Construct a tight box (1/2 height and width) around the center of the closest item.
1909 // All items which exist at least partly outside this box have sufficient other areas
1910 // for selection and can be dropped.
1911 if( closest ) // Don't try and get a tight bbox if nothing is near the mouse pointer
1912 {
1913 BOX2I tightBox = closest->GetBoundingBox();
1914 tightBox.Inflate( -tightBox.GetWidth() / 4, -tightBox.GetHeight() / 4 );
1915
1916 for( int i = collector.GetCount() - 1; i >= 0; --i )
1917 {
1918 EDA_ITEM* item = collector[i];
1919
1920 if( item == closest )
1921 continue;
1922
1923 if( !item->HitTest( tightBox, true ) )
1924 collector.Transfer( item );
1925 }
1926 }
1927}
1928
1929
1930SCH_SELECTION& SCH_SELECTION_TOOL::RequestSelection( const std::vector<KICAD_T>& aScanTypes,
1931 bool aPromoteCellSelections,
1932 bool aPromoteGroups )
1933{
1934 bool anyUnselected = false;
1935 bool anySelected = false;
1936
1937 if( m_selection.Empty() )
1938 {
1939 VECTOR2D cursorPos = getViewControls()->GetCursorPosition( true );
1940
1942 SelectPoint( cursorPos, aScanTypes );
1943 m_selection.SetIsHover( true );
1944 m_selection.ClearReferencePoint();
1945 }
1946 else // Trim an existing selection by aFilterList
1947 {
1948 bool isMoving = false;
1949
1950 for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
1951 {
1952 EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
1953 isMoving |= static_cast<SCH_ITEM*>( item )->IsMoving();
1954
1955 if( !item->IsType( aScanTypes ) )
1956 {
1957 unselect( item );
1958 anyUnselected = true;
1959 }
1960 }
1961
1962 if( !isMoving )
1964 }
1965
1966 if( aPromoteGroups )
1967 {
1968 for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
1969 {
1970 EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
1971
1972 std::set<EDA_ITEM*> selectedChildren;
1973
1974 if( item->Type() == SCH_GROUP_T )
1975 {
1976 static_cast<SCH_ITEM*>(item)->RunOnChildren( [&]( SCH_ITEM* aChild )
1977 {
1978 if( aChild->IsType( aScanTypes ) )
1979 selectedChildren.insert( aChild );
1980 },
1982 unselect( item );
1983 anyUnselected = true;
1984 }
1985
1986 for( EDA_ITEM* child : selectedChildren )
1987 {
1988 if( !child->IsSelected() )
1989 {
1990 if( child->Type() == SCH_LINE_T )
1991 static_cast<SCH_LINE*>( child )->SetFlags( STARTPOINT | ENDPOINT );
1992
1993 select( child );
1994 anySelected = true;
1995 }
1996 }
1997 }
1998 }
1999
2000 if( aPromoteCellSelections )
2001 {
2002 std::set<EDA_ITEM*> parents;
2003
2004 for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
2005 {
2006 EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
2007
2008 if( item->Type() == SCH_TABLECELL_T )
2009 {
2010 parents.insert( item->GetParent() );
2011 unselect( item );
2012 anyUnselected = true;
2013 }
2014 }
2015
2016 for( EDA_ITEM* parent : parents )
2017 {
2018 if( !parent->IsSelected() )
2019 {
2020 select( parent );
2021 anySelected = true;
2022 }
2023 }
2024 }
2025
2026 if( anyUnselected )
2027 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2028
2029 if( anySelected )
2030 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2031
2032 return m_selection;
2033}
2034
2035
2036void SCH_SELECTION_TOOL::filterCollectedItems( SCH_COLLECTOR& aCollector, bool aMultiSelect )
2037{
2038 if( aCollector.GetCount() == 0 )
2039 return;
2040
2041 std::set<EDA_ITEM*> rejected;
2042
2043 for( EDA_ITEM* item : aCollector )
2044 {
2045 if( !itemPassesFilter( item, nullptr ) )
2046 rejected.insert( item );
2047 }
2048
2049 for( EDA_ITEM* item : rejected )
2050 aCollector.Remove( item );
2051}
2052
2053
2055{
2056 if( !aItem )
2057 return false;
2058
2059 // Locking is not yet exposed uniformly in the schematic
2060#if 0
2061 if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( aItem ) )
2062 {
2063 if( schItem->IsLocked() && !m_filter.lockedItems )
2064 return false;
2065 }
2066#endif
2067
2068 switch( aItem->Type() )
2069 {
2070 case SCH_SYMBOL_T:
2071 case SCH_SHEET_T:
2072 if( !m_filter.symbols )
2073 {
2074 if( aRejected )
2075 aRejected->symbols = true;
2076 return false;
2077 }
2078
2079 break;
2080
2081 case SCH_PIN_T:
2082 case SCH_SHEET_PIN_T:
2083 if( !m_filter.pins )
2084 {
2085 if( aRejected )
2086 aRejected->pins = true;
2087 return false;
2088 }
2089
2090 break;
2091
2092 case SCH_JUNCTION_T:
2093 if( !m_filter.wires )
2094 {
2095 if( aRejected )
2096 aRejected->wires = true;
2097 return false;
2098 }
2099
2100 break;
2101
2102 case SCH_LINE_T:
2103 {
2104 switch( static_cast<SCH_LINE*>( aItem )->GetLayer() )
2105 {
2106 case LAYER_WIRE:
2107 case LAYER_BUS:
2108 if( !m_filter.wires )
2109 {
2110 if( aRejected )
2111 aRejected->wires = true;
2112 return false;
2113 }
2114
2115 break;
2116
2117 default:
2118 if( !m_filter.graphics )
2119 {
2120 if( aRejected )
2121 aRejected->graphics = true;
2122 return false;
2123 }
2124 }
2125
2126 break;
2127 }
2128
2129 case SCH_SHAPE_T:
2130 if( !m_filter.graphics )
2131 {
2132 if( aRejected )
2133 aRejected->graphics = true;
2134 return false;
2135 }
2136
2137 break;
2138
2139 case SCH_TEXT_T:
2140 case SCH_TEXTBOX_T:
2141 case SCH_TABLE_T:
2142 case SCH_TABLECELL_T:
2143 case SCH_FIELD_T:
2144 if( !m_filter.text )
2145 {
2146 if( aRejected )
2147 aRejected->text = true;
2148 return false;
2149 }
2150
2151 break;
2152
2153 case SCH_LABEL_T:
2154 case SCH_GLOBAL_LABEL_T:
2155 case SCH_HIER_LABEL_T:
2156 if( !m_filter.labels )
2157 {
2158 if( aRejected )
2159 aRejected->labels = true;
2160 return false;
2161 }
2162
2163 break;
2164
2165 case SCH_BITMAP_T:
2166 if( !m_filter.images )
2167 {
2168 if( aRejected )
2169 aRejected->images = true;
2170 return false;
2171 }
2172
2173 break;
2174
2175 case SCH_RULE_AREA_T:
2176 if( !m_filter.ruleAreas )
2177 {
2178 if( aRejected )
2179 aRejected->ruleAreas = true;
2180 return false;
2181 }
2182
2183 break;
2184
2185 default:
2186 if( !m_filter.otherItems )
2187 {
2188 if( aRejected )
2189 aRejected->otherItems = true;
2190 return false;
2191 }
2192
2193 break;
2194 }
2195
2196 return true;
2197}
2198
2199
2201{
2202 VECTOR2I refP( 0, 0 );
2203
2204 if( m_selection.Size() > 0 )
2205 refP = static_cast<SCH_ITEM*>( m_selection.GetTopLeftItem() )->GetPosition();
2206
2207 m_selection.SetReferencePoint( refP );
2208}
2209
2210
2212{
2214 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
2215 m_toolMgr->PostAction( ACTIONS::selectionTool );
2216 return 0;
2217}
2218
2219
2221{
2223 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2224 m_toolMgr->PostAction( ACTIONS::selectionTool );
2225 return 0;
2226}
2227
2228
2229// Some navigation actions are allowed in selectMultiple
2240
2241
2243{
2244 // Block selection not allowed in symbol viewer frame: no actual code to handle
2245 // a selection, so return to avoid to draw a selection rectangle, and to avoid crashes.
2246 if( m_frame->IsType( FRAME_T::FRAME_SCH_VIEWER ) )
2247 return false;
2248
2249 bool cancelled = false; // Was the tool canceled while it was running?
2250 m_multiple = true; // Multiple selection mode is active
2251 KIGFX::VIEW* view = getView();
2252
2254 view->Add( &area );
2255
2256 while( TOOL_EVENT* evt = Wait() )
2257 {
2258 /* Selection mode depends on direction of drag-selection:
2259 * Left > Right : Select objects that are fully enclosed by selection
2260 * Right > Left : Select objects that are crossed by selection
2261 */
2262 bool isGreedy = area.GetEnd().x < area.GetOrigin().x;
2263
2264 if( view->IsMirroredX() )
2265 isGreedy = !isGreedy;
2266
2267 m_frame->GetCanvas()->SetCurrentCursor( isGreedy ? KICURSOR::SELECT_LASSO
2269
2270 if( evt->IsCancelInteractive() || evt->IsActivate() )
2271 {
2272 cancelled = true;
2273 break;
2274 }
2275
2276 if( evt->IsDrag( BUT_LEFT ) )
2277 {
2280
2281 // Start drawing a selection box
2282 area.SetOrigin( evt->DragOrigin() );
2283 area.SetEnd( evt->Position() );
2286 area.SetExclusiveOr( false );
2289
2290 view->SetVisible( &area, true );
2291 view->Update( &area );
2292 getViewControls()->SetAutoPan( true );
2293 }
2294
2295 if( evt->IsMouseUp( BUT_LEFT ) )
2296 {
2297 getViewControls()->SetAutoPan( false );
2298 view->SetVisible( &area, false );
2299 SelectMultiple( area, m_drag_subtractive, false );
2300 evt->SetPassEvent( false );
2301 break;
2302 }
2303
2304 passEvent( evt, allowedActions );
2305 }
2306
2307 getViewControls()->SetAutoPan( false );
2308
2309 // Stop drawing the selection box
2310 view->Remove( &area );
2311 m_multiple = false; // Multiple selection mode is inactive
2312
2313 if( !cancelled )
2314 m_selection.ClearReferencePoint();
2315
2316 return cancelled;
2317}
2318
2319
2321{
2322 bool cancelled = false;
2323 m_multiple = true;
2325 getView()->Add( &area );
2326 getView()->SetVisible( &area, true );
2327 getViewControls()->SetAutoPan( true );
2328
2329 SHAPE_LINE_CHAIN points;
2330 points.SetClosed( true );
2331
2333 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
2334
2335 while( TOOL_EVENT* evt = Wait() )
2336 {
2337 double shapeArea = area.GetPoly().Area( false );
2338 bool isClockwise = shapeArea > 0 ? true : false;
2339
2340 if( getView()->IsMirroredX() && shapeArea != 0 )
2341 isClockwise = !isClockwise;
2342
2343 if( isClockwise )
2344 {
2345 selectionMode = SELECTION_MODE::INSIDE_LASSO;
2346 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_WINDOW );
2347 }
2348 else
2349 {
2350 selectionMode = SELECTION_MODE::TOUCHING_LASSO;
2351 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
2352 }
2353
2354 if( evt->IsCancelInteractive() || evt->IsActivate() )
2355 {
2356 cancelled = true;
2357 break;
2358 }
2359 else if( evt->IsDrag( BUT_LEFT )
2360 || evt->IsClick( BUT_LEFT )
2361 || evt->IsAction( &ACTIONS::cursorClick ) )
2362 {
2363 points.Append( evt->Position() );
2364 }
2365 else if( evt->IsDblClick( BUT_LEFT )
2366 || evt->IsAction( &ACTIONS::cursorDblClick )
2367 || evt->IsAction( &ACTIONS::finishInteractive ) )
2368 {
2369 area.GetPoly().GenerateBBoxCache();
2370 SelectMultiple( area, m_drag_subtractive, false );
2371 break;
2372 }
2373 else if( evt->IsAction( &ACTIONS::doDelete )
2374 || evt->IsAction( &ACTIONS::undo ) )
2375 {
2376 if( points.GetPointCount() > 0 )
2377 {
2379 points.Remove( points.GetPointCount() - 1 );
2380 }
2381 }
2382 else
2383 {
2384 passEvent( evt, allowedActions );
2385 }
2386
2387 if( points.PointCount() > 0 )
2388 {
2390 {
2391 if( m_selection.GetSize() > 0 )
2392 {
2393 ClearSelection( true );
2394 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2395 }
2396 }
2397 }
2398
2399 area.SetPoly( points );
2400 area.GetPoly().Append( m_toolMgr->GetMousePosition() );
2403 area.SetExclusiveOr( false );
2404 area.SetMode( selectionMode );
2405 getView()->Update( &area );
2406 }
2407
2408 getViewControls()->SetAutoPan( false );
2409 getView()->SetVisible( &area, false );
2410 getView()->Remove( &area );
2411 m_multiple = false;
2412
2413 if( !cancelled )
2414 m_selection.ClearReferencePoint();
2415
2416 return cancelled;
2417}
2418
2419
2421 bool aExclusiveOr )
2422{
2423 KIGFX::VIEW* view = getView();
2424
2425 SELECTION_MODE selectionMode = aArea.GetMode();
2426 bool containedMode = ( selectionMode == SELECTION_MODE::INSIDE_RECTANGLE
2427 || selectionMode == SELECTION_MODE::INSIDE_LASSO );
2428 bool boxMode = ( selectionMode == SELECTION_MODE::INSIDE_RECTANGLE
2429 || selectionMode == SELECTION_MODE::TOUCHING_RECTANGLE );
2430
2431 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> candidates;
2432 BOX2I selectionRect = aArea.ViewBBox();
2433 view->Query( selectionRect, candidates );
2434
2435 std::set<SCH_ITEM*> uniqueCandidates;
2436
2437 for( const auto& [viewItem, layer] : candidates )
2438 {
2439 if( viewItem->IsSCH_ITEM() )
2440 uniqueCandidates.insert( static_cast<SCH_ITEM*>( viewItem ) );
2441 }
2442
2443 for( KIGFX::VIEW_ITEM* item : uniqueCandidates )
2444 {
2445 if( SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item ) )
2446 {
2447 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
2448 {
2449 if( boxMode ? selectionRect.Intersects( pin->GetBoundingBox() )
2450 : KIGEOM::BoxHitTest( aArea.GetPoly(), pin->GetBoundingBox(), true ) )
2451 uniqueCandidates.insert( pin );
2452 }
2453 }
2454 else if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item ) )
2455 {
2456 for( SCH_PIN* pin : symbol->GetPins() )
2457 {
2458 if( boxMode ? selectionRect.Intersects( pin->GetBoundingBox() )
2459 : KIGEOM::BoxHitTest( aArea.GetPoly(), pin->GetBoundingBox(), true ) )
2460 uniqueCandidates.insert( pin );
2461 }
2462
2463 for( SCH_FIELD& field : symbol->GetFields() )
2464 {
2465 if( field.IsVisible()
2466 && ( boxMode ? selectionRect.Intersects( field.GetBoundingBox() )
2467 : KIGEOM::BoxHitTest( aArea.GetPoly(), field.GetBoundingBox(), true ) ) )
2468 {
2469 uniqueCandidates.insert( &field );
2470 }
2471 }
2472 }
2473 }
2474
2475 SCH_COLLECTOR collector;
2476 SCH_COLLECTOR pinsCollector;
2477 std::set<EDA_ITEM*> group_items;
2478
2479 for( EDA_ITEM* item : m_frame->GetScreen()->Items().OfType( SCH_GROUP_T ) )
2480 {
2481 SCH_GROUP* group = static_cast<SCH_GROUP*>( item );
2482
2483 if( m_enteredGroup == group )
2484 continue;
2485
2486 std::unordered_set<EDA_ITEM*>& newset = group->GetItems();
2487
2488 auto boxContained =
2489 [&]( const BOX2I& aBox )
2490 {
2491 return boxMode ? selectionRect.Contains( aBox )
2492 : KIGEOM::BoxHitTest( aArea.GetPoly(), aBox, true );
2493 };
2494
2495 if( containedMode && boxContained( group->GetBoundingBox() ) && newset.size() )
2496 {
2497 for( EDA_ITEM* group_item : newset )
2498 {
2499 if( !group_item->IsSCH_ITEM() )
2500 continue;
2501
2502 if( Selectable( static_cast<SCH_ITEM*>( group_item ) ) )
2503 collector.Append( group_item );
2504 }
2505 }
2506
2507 for( EDA_ITEM* group_item : newset )
2508 group_items.emplace( group_item );
2509 }
2510
2511 auto hitTest =
2512 [&]( SCH_ITEM* aItem )
2513 {
2514 return boxMode ? aItem->HitTest( selectionRect, containedMode )
2515 : aItem->HitTest( aArea.GetPoly(), containedMode );
2516 };
2517
2518 for( SCH_ITEM* item : uniqueCandidates )
2519 {
2520 if( Selectable( item ) && ( hitTest( item ) || item->Type() == SCH_LINE_T )
2521 && ( !containedMode || !group_items.count( item ) ) )
2522 {
2523 if( item->Type() == SCH_PIN_T && !m_isSymbolEditor )
2524 pinsCollector.Append( item );
2525 else
2526 collector.Append( item );
2527 }
2528 }
2529
2530 filterCollectedItems( collector, true );
2531 filterCollectorForHierarchy( collector, true );
2532
2533 if( collector.GetCount() == 0 )
2534 {
2535 collector = pinsCollector;
2536 filterCollectedItems( collector, true );
2537 filterCollectorForHierarchy( collector, true );
2538 }
2539
2540 std::sort( collector.begin(), collector.end(),
2541 []( EDA_ITEM* a, EDA_ITEM* b )
2542 {
2543 VECTOR2I aPos = a->GetPosition();
2544 VECTOR2I bPos = b->GetPosition();
2545
2546 if( aPos.y == bPos.y )
2547 return aPos.x < bPos.x;
2548
2549 return aPos.y < bPos.y;
2550 } );
2551
2552 bool anyAdded = false;
2553 bool anySubtracted = false;
2554
2555 auto selectItem =
2556 [&]( EDA_ITEM* aItem, EDA_ITEM_FLAGS flags )
2557 {
2558 if( aSubtractive || ( aExclusiveOr && aItem->IsSelected() ) )
2559 {
2560 if( aExclusiveOr )
2561 aItem->XorFlags( flags );
2562 else
2563 aItem->ClearFlags( flags );
2564
2565 if( !aItem->HasFlag( STARTPOINT ) && !aItem->HasFlag( ENDPOINT ) )
2566 {
2567 unselect( aItem );
2568 anySubtracted = true;
2569 }
2570
2571 if( flags && !anySubtracted )
2572 getView()->Update( aItem );
2573 }
2574 else
2575 {
2576 aItem->SetFlags( flags );
2577 select( aItem );
2578 anyAdded = true;
2579 }
2580 };
2581
2582 std::vector<EDA_ITEM*> flaggedItems;
2583
2584 auto shapeContains =
2585 [&]( const VECTOR2I& aPoint )
2586 {
2587 return boxMode ? selectionRect.Contains( aPoint )
2588 : aArea.GetPoly().PointInside( aPoint );
2589 };
2590
2591 for( EDA_ITEM* item : collector )
2592 {
2593 EDA_ITEM_FLAGS flags = 0;
2594
2595 item->SetFlags( SELECTION_CANDIDATE );
2596 flaggedItems.push_back( item );
2597
2598 if( m_frame->GetRenderSettings()->m_ShowPinsElectricalType )
2599 item->SetFlags( SHOW_ELEC_TYPE );
2600
2601 if( item->Type() == SCH_LINE_T )
2602 {
2603 SCH_LINE* line = static_cast<SCH_LINE*>( item );
2604 bool hits = false;
2605
2606 if( boxMode )
2607 hits = line->HitTest( selectionRect, false );
2608 else
2609 hits = line->HitTest( aArea.GetPoly(), false );
2610
2611 if( ( !containedMode && hits )
2612 || ( shapeContains( line->GetEndPoint() ) && shapeContains( line->GetStartPoint() ) ) )
2613 {
2614 flags |= STARTPOINT | ENDPOINT;
2615 }
2616 else if( containedMode )
2617 {
2618 if( shapeContains( line->GetStartPoint() ) && line->IsStartDangling() )
2619 flags |= STARTPOINT;
2620
2621 if( shapeContains( line->GetEndPoint() ) && line->IsEndDangling() )
2622 flags |= ENDPOINT;
2623 }
2624
2625 if( flags & ( STARTPOINT | ENDPOINT ) )
2626 selectItem( item, flags );
2627 }
2628 else
2629 selectItem( item, flags );
2630
2631 item->ClearFlags( SHOW_ELEC_TYPE );
2632 }
2633
2634 for( EDA_ITEM* item : pinsCollector )
2635 {
2636 if( m_frame->GetRenderSettings()->m_ShowPinsElectricalType )
2637 item->SetFlags( SHOW_ELEC_TYPE );
2638
2639 // If the pin lives inside a group that is already being selected, don't also select the pin.
2640 if( EDA_GROUP* group =
2642 {
2643 if( collector.HasItem( group->AsEdaItem() ) )
2644 {
2645 item->ClearFlags( SHOW_ELEC_TYPE );
2646 continue;
2647 }
2648 }
2649
2650 if( Selectable( item ) && itemPassesFilter( item, nullptr )
2651 && !item->GetParent()->HasFlag( SELECTION_CANDIDATE ) && hitTest( static_cast<SCH_ITEM*>( item ) ) )
2652 {
2653 selectItem( item, 0 );
2654 }
2655
2656 item->ClearFlags( SHOW_ELEC_TYPE );
2657 }
2658
2659 for( EDA_ITEM* item : flaggedItems )
2660 item->ClearFlags( SELECTION_CANDIDATE );
2661
2662 m_selection.SetIsHover( false );
2663
2664 if( anyAdded )
2665 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2666 else if( anySubtracted )
2667 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2668}
2669
2670
2672 bool aMultiselect ) const
2673{
2674 std::unordered_set<EDA_ITEM*> toAdd;
2675
2676 // Set SELECTION_CANDIDATE on all parents which are included in the GENERAL_COLLECTOR. This
2677 // algorithm is O(3n), whereas checking for the parent inclusion could potentially be O(n^2).
2678 for( int j = 0; j < aCollector.GetCount(); j++ )
2679 {
2680 if( aCollector[j]->GetParent() )
2681 aCollector[j]->GetParent()->ClearFlags( SELECTION_CANDIDATE );
2682
2683 if( aCollector[j]->GetParentSymbol() )
2684 aCollector[j]->GetParentSymbol()->ClearFlags( SELECTION_CANDIDATE );
2685 }
2686
2687 if( aMultiselect )
2688 {
2689 for( int j = 0; j < aCollector.GetCount(); j++ )
2690 aCollector[j]->SetFlags( SELECTION_CANDIDATE );
2691 }
2692
2693 for( int j = 0; j < aCollector.GetCount(); )
2694 {
2695 SCH_ITEM* item = aCollector[j];
2696 SYMBOL* sym = item->GetParentSymbol();
2697 SCH_ITEM* start = item;
2698
2699 if( !m_isSymbolEditor && sym )
2700 start = sym;
2701
2702 // If a group is entered, disallow selections of objects outside the group.
2704 {
2705 aCollector.Remove( item );
2706 continue;
2707 }
2708
2709 // If any element is a member of a group, replace those elements with the top containing
2710 // group.
2712 {
2713 if( top->AsEdaItem() != item )
2714 {
2715 toAdd.insert( top->AsEdaItem() );
2716 top->AsEdaItem()->SetFlags( SELECTION_CANDIDATE );
2717
2718 aCollector.Remove( item );
2719 continue;
2720 }
2721 }
2722
2723 // Symbols are a bit easier as they can't be nested.
2724 if( sym && ( sym->GetFlags() & SELECTION_CANDIDATE ) )
2725 {
2726 // Remove children of selected items
2727 aCollector.Remove( item );
2728 continue;
2729 }
2730
2731 ++j;
2732 }
2733
2734 for( EDA_ITEM* item : toAdd )
2735 {
2736 if( !aCollector.HasItem( item ) )
2737 aCollector.Append( item );
2738 }
2739}
2740
2741
2743{
2744 getView()->Update( &m_selection );
2746
2747 return 0;
2748}
2749
2750
2752{
2753 for( SCH_TABLECELL* cell : aTable->GetCells() )
2754 {
2755 if( cell->IsSelected() )
2756 cell->SetFlags( SELECTION_CANDIDATE );
2757 else
2758 cell->ClearFlags( SELECTION_CANDIDATE );
2759 }
2760}
2761
2763{
2764 BOX2I selectionRect( start, end );
2765 selectionRect.Normalize();
2766
2767 auto wasSelected = []( EDA_ITEM* aItem )
2768 {
2769 return ( aItem->GetFlags() & SELECTION_CANDIDATE ) > 0;
2770 };
2771
2772 for( SCH_TABLECELL* cell : aTable->GetCells() )
2773 {
2774 bool doSelect = false;
2775
2776 if( cell->HitTest( selectionRect, false ) )
2777 {
2778 if( m_subtractive )
2779 doSelect = false;
2780 else if( m_exclusive_or )
2781 doSelect = !wasSelected( cell );
2782 else
2783 doSelect = true;
2784 }
2785 else if( wasSelected( cell ) )
2786 {
2787 doSelect = m_additive || m_subtractive || m_exclusive_or;
2788 }
2789
2790 if( doSelect && !cell->IsSelected() )
2791 select( cell );
2792 else if( !doSelect && cell->IsSelected() )
2793 unselect( cell );
2794 }
2795}
2796
2798{
2799 bool cancelled = false;
2800 m_multiple = true;
2801
2802 InitializeSelectionState( aTable );
2803
2804 while( TOOL_EVENT* evt = Wait() )
2805 {
2806 if( evt->IsCancelInteractive() || evt->IsActivate() )
2807 {
2808 cancelled = true;
2809 break;
2810 }
2811 else if( evt->IsDrag( BUT_LEFT ) )
2812 {
2813 getViewControls()->SetAutoPan( true );
2814 SelectCellsBetween( evt->DragOrigin(), evt->Position() - evt->DragOrigin(), aTable );
2815 }
2816 else if( evt->IsMouseUp( BUT_LEFT ) )
2817 {
2818 m_selection.SetIsHover( false );
2819
2820 bool anyAdded = false;
2821 bool anySubtracted = false;
2822
2823 for( SCH_TABLECELL* cell : aTable->GetCells() )
2824 {
2825 if( cell->IsSelected() && ( cell->GetFlags() & SELECTION_CANDIDATE ) <= 0 )
2826 anyAdded = true;
2827 else if( ( cell->GetFlags() & SELECTION_CANDIDATE ) > 0 && !cell->IsSelected() )
2828 anySubtracted = true;
2829 }
2830
2831 if( anyAdded )
2832 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2833 if( anySubtracted )
2834 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2835
2836 break;
2837 }
2838 else
2839 {
2840 for( int i = 0; allowedActions[i]; ++i )
2841 {
2842 if( evt->IsAction( allowedActions[i] ) )
2843 {
2844 evt->SetPassEvent();
2845 break;
2846 }
2847 }
2848 }
2849 }
2850
2851 getViewControls()->SetAutoPan( false );
2852
2853 m_multiple = false;
2854
2855 if( !cancelled )
2856 m_selection.ClearReferencePoint();
2857
2858 return cancelled;
2859}
2860
2861
2863{
2864 SCH_COLLECTOR collector;
2865
2866 //TODO(snh): Reimplement after exposing KNN interface
2867 int pixelThreshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
2868 int gridThreshold = KiROUND( getView()->GetGAL()->GetGridSize().EuclideanNorm() );
2869 int thresholdMax = std::max( pixelThreshold, gridThreshold );
2870
2871 for( int threshold : { 0, thresholdMax/4, thresholdMax/2, thresholdMax } )
2872 {
2873 collector.m_Threshold = threshold;
2874 collector.Collect( m_frame->GetScreen(), connectedTypes, aPosition );
2875
2876 if( collector.GetCount() > 0 )
2877 break;
2878 }
2879
2880 return collector.GetCount() ? collector[ 0 ] : nullptr;
2881}
2882
2883
2885{
2886 VECTOR2I cursorPos = getViewControls()->GetCursorPosition( false );
2887
2888 SelectPoint( cursorPos, connectedTypes );
2889 return 0;
2890}
2891
2892
2894{
2895 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
2896
2897 if( m_isSymbolEditor || m_isSymbolViewer || !editFrame )
2898 return {};
2899
2900 CONNECTION_GRAPH* graph = editFrame->Schematic().ConnectionGraph();
2901
2902 if( !graph )
2903 return {};
2904
2905 SCH_SHEET_PATH& currentSheet = editFrame->GetCurrentSheet();
2906 std::vector<SCH_ITEM*> startItems;
2907 std::set<SCH_ITEM*> added;
2908
2909 // Build the list of starting items for the connection graph traversal
2910 for( auto item : aItems )
2911 {
2912 if( !item->IsSCH_ITEM() )
2913 continue;
2914
2915 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
2916
2917 // If we're a symbol, start from all its pins
2918 if( schItem->Type() == SCH_SYMBOL_T )
2919 {
2920 for( SCH_PIN* pin : static_cast<SCH_SYMBOL*>( schItem )->GetPins( &currentSheet ) )
2921 {
2922 if( pin )
2923 startItems.push_back( pin );
2924 }
2925 }
2926 else if( schItem->IsConnectable() )
2927 {
2928 startItems.push_back( schItem );
2929 }
2930 }
2931
2932 if( startItems.empty() )
2933 return {};
2934
2935 std::deque<SCH_ITEM*> queue;
2936 std::unordered_set<SCH_ITEM*> visited;
2937
2938 auto enqueue = [&]( SCH_ITEM* aItem )
2939 {
2940 if( !aItem )
2941 return;
2942
2943 if( visited.insert( aItem ).second )
2944 queue.push_back( aItem );
2945 };
2946
2947 for( SCH_ITEM* item : startItems )
2948 enqueue( item );
2949
2950 while( !queue.empty() )
2951 {
2952 SCH_ITEM* item = queue.front();
2953 queue.pop_front();
2954
2955 if( SCH_PIN* pin = dynamic_cast<SCH_PIN*>( item ) )
2956 {
2957 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParent() );
2958
2959 if( symbol && Selectable( symbol ) && itemPassesFilter( symbol, nullptr ) && !symbol->IsSelected() )
2960 added.insert( symbol );
2961 }
2962
2963 const SCH_ITEM_VEC& neighbors = item->ConnectedItems( currentSheet );
2964
2965 for( SCH_ITEM* neighbor : neighbors )
2966 {
2967 if( !neighbor )
2968 continue;
2969
2970 if( neighbor->Type() == SCH_SYMBOL_T )
2971 {
2972 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( neighbor );
2973
2974 if( Selectable( symbol ) && itemPassesFilter( symbol, nullptr ) && !symbol->IsSelected() )
2975 added.insert( symbol );
2976
2977 continue;
2978 }
2979
2980 enqueue( neighbor );
2981 }
2982
2983 if( !Selectable( item ) || !itemPassesFilter( item, nullptr ) )
2984 continue;
2985
2986 added.insert( item );
2987 }
2988
2989 return added;
2990}
2991
2992
2994{
2995 std::set<SCH_ITEM*> added;
2996
2997 for( auto item : aItems )
2998 {
2999 if( !item->IsSCH_ITEM() )
3000 continue;
3001
3002 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
3003
3004 std::set<SCH_ITEM*> conns = m_frame->GetScreen()->MarkConnections( schItem, schItem->IsConnectable() );
3005
3006 // Make sure we don't add things the user has disabled in the selection filter
3007 for( SCH_ITEM* connItem : conns )
3008 {
3009 if( !Selectable( connItem ) || !itemPassesFilter( connItem, nullptr ) )
3010 continue;
3011
3012 added.insert( connItem );
3013 }
3014 }
3015
3016 return added;
3017}
3018
3019
3021{
3023
3024 if( m_selection.Empty() )
3025 return 0;
3026
3027 SCH_SELECTION connectableSelection;
3028 SCH_SELECTION graphicalSelection;
3029
3030 // We need to filter the selection into connectable items (wires, pins, symbols)
3031 // and non-connectable items (shapes, unconnectable lines) for processing
3032 // with the graph or by the graphical are-endpoints-touching method.
3033 for( EDA_ITEM* selItem : originalSelection.GetItems() )
3034 {
3035 if( !selItem->IsSCH_ITEM() )
3036 continue;
3037
3038 SCH_ITEM* item = static_cast<SCH_ITEM*>( selItem );
3039
3040 if( item->Type() == SCH_LINE_T && !item->IsConnectable() )
3041 graphicalSelection.Add( item );
3042 else if( item->Type() == SCH_SHAPE_T )
3043 graphicalSelection.Add( item );
3044 else
3045 connectableSelection.Add( item );
3046 }
3047
3048 ClearSelection( true );
3049
3050 std::set<SCH_ITEM*> graphAdded;
3051 std::set<SCH_ITEM*> graphicalAdded;
3052
3053 if( !connectableSelection.Empty() )
3054 graphAdded = expandConnectionWithGraph( connectableSelection );
3055
3056 if( !graphicalSelection.Empty() )
3057 graphicalAdded = expandConnectionGraphically( graphicalSelection );
3058
3059 // For whatever reason, the connection graph isn't working (e.g. in symbol editor )
3060 // so fall back to graphical expansion for those items if nothing was added.
3061 if( graphAdded.empty() && !connectableSelection.Empty() )
3062 {
3063 SCH_SELECTION combinedSelection = connectableSelection;
3064
3065 for( EDA_ITEM* selItem : graphicalSelection.GetItems() )
3066 combinedSelection.Add( selItem );
3067
3068 graphicalSelection = combinedSelection;
3069 }
3070
3071 graphicalAdded = expandConnectionGraphically( graphicalSelection );
3072
3073 auto smartAddToSel = [&]( EDA_ITEM* aItem )
3074 {
3075 AddItemToSel( aItem, true );
3076
3077 if( aItem->Type() == SCH_LINE_T )
3078 aItem->SetFlags( STARTPOINT | ENDPOINT );
3079 };
3080
3081 // Add everything to the selection, including the original selection
3082 for( auto item : graphAdded )
3083 smartAddToSel( item );
3084
3085 for( auto item : graphicalAdded )
3086 smartAddToSel( item );
3087
3088 for( auto item : originalSelection )
3089 smartAddToSel( item );
3090
3091 m_selection.SetIsHover( originalSelection.IsHover() );
3092
3093 if( originalSelection.HasReferencePoint() )
3094 m_selection.SetReferencePoint( originalSelection.GetReferencePoint() );
3095 else
3096 m_selection.ClearReferencePoint();
3097
3098 getView()->Update( &m_selection );
3099
3100 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3101
3102 return 0;
3103}
3104
3105
3107{
3108 std::set<std::pair<SCH_TABLE*, int>> columns;
3109 bool added = false;
3110
3111 for( EDA_ITEM* item : m_selection )
3112 {
3113 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( item ) )
3114 {
3115 SCH_TABLE* table = static_cast<SCH_TABLE*>( cell->GetParent() );
3116 columns.insert( std::make_pair( table, cell->GetColumn() ) );
3117 }
3118 }
3119
3120 for( auto& [ table, col ] : columns )
3121 {
3122 for( int row = 0; row < table->GetRowCount(); ++row )
3123 {
3124 SCH_TABLECELL* cell = table->GetCell( row, col );
3125
3126 if( !cell->IsSelected() )
3127 {
3128 select( table->GetCell( row, col ) );
3129 added = true;
3130 }
3131 }
3132 }
3133
3134 if( added )
3135 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3136
3137 return 0;
3138}
3139
3140
3142{
3143 std::set<std::pair<SCH_TABLE*, int>> rows;
3144 bool added = false;
3145
3146 for( EDA_ITEM* item : m_selection )
3147 {
3148 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( item ) )
3149 {
3150 SCH_TABLE* table = static_cast<SCH_TABLE*>( cell->GetParent() );
3151 rows.insert( std::make_pair( table, cell->GetRow() ) );
3152 }
3153 }
3154
3155 for( auto& [ table, row ] : rows )
3156 {
3157 for( int col = 0; col < table->GetColCount(); ++col )
3158 {
3159 SCH_TABLECELL* cell = table->GetCell( row, col );
3160
3161 if( !cell->IsSelected() )
3162 {
3163 select( table->GetCell( row, col ) );
3164 added = true;
3165 }
3166 }
3167 }
3168
3169 if( added )
3170 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3171
3172 return 0;
3173}
3174
3175
3177{
3178 std::set<SCH_TABLE*> tables;
3179 bool added = false;
3180
3181 for( EDA_ITEM* item : m_selection )
3182 {
3183 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( item ) )
3184 tables.insert( static_cast<SCH_TABLE*>( cell->GetParent() ) );
3185 }
3186
3188
3189 for( SCH_TABLE* table : tables )
3190 {
3191 if( !table->IsSelected() )
3192 {
3193 select( table );
3194 added = true;
3195 }
3196 }
3197
3198 if( added )
3199 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3200
3201 return 0;
3202}
3203
3204
3206{
3208 return 0;
3209}
3210
3211
3213{
3214 if( aBBox.GetWidth() == 0 )
3215 return;
3216
3217 BOX2I bbox = aBBox;
3218 bbox.Normalize();
3219
3220 VECTOR2I bbSize = bbox.Inflate( KiROUND( bbox.GetWidth() * 0.2f ) ).GetSize();
3221 VECTOR2D screenSize = getView()->GetViewport().GetSize();
3222
3223 // This code tries to come up with a zoom factor that doesn't simply zoom in to the cross
3224 // probed symbol, but instead shows a reasonable amount of the circuit around it to provide
3225 // context. This reduces the need to manually change the zoom because it's too close.
3226
3227 // Using the default text height as a constant to compare against, use the height of the
3228 // bounding box of visible items for a footprint to figure out if this is a big symbol (like
3229 // a processor) or a small symbol (like a resistor). This ratio is not useful by itself as a
3230 // scaling factor. It must be "bent" to provide good scaling at varying symbol sizes. Bigger
3231 // symbols need less scaling than small ones.
3232 double currTextHeight = schIUScale.MilsToIU( DEFAULT_TEXT_SIZE );
3233
3234 double compRatio = bbSize.y / currTextHeight; // Ratio of symbol to text height
3235 double compRatioBent = 1.0;
3236
3237 // LUT to scale zoom ratio to provide reasonable schematic context. Must work with symbols
3238 // of varying sizes (e.g. 0402 package and 200 pin BGA).
3239 // Each entry represents a compRatio (symbol height / default text height) and an amount to
3240 // scale by.
3241 std::vector<std::pair<double, double>> lut{ { 1.25, 16 },
3242 { 2.5, 12 },
3243 { 5, 8 },
3244 { 6, 6 },
3245 { 10, 4 },
3246 { 20, 2 },
3247 { 40, 1.5 },
3248 { 100, 1 } };
3249
3250 std::vector<std::pair<double, double>>::iterator it;
3251
3252 // Large symbol default is last LUT entry (1:1).
3253 compRatioBent = lut.back().second;
3254
3255 // Use LUT to do linear interpolation of "compRatio" within "first", then use that result to
3256 // linearly interpolate "second" which gives the scaling factor needed.
3257 if( compRatio >= lut.front().first )
3258 {
3259 for( it = lut.begin(); it < lut.end() - 1; ++it )
3260 {
3261 if( it->first <= compRatio && next( it )->first >= compRatio )
3262 {
3263 double diffx = compRatio - it->first;
3264 double diffn = next( it )->first - it->first;
3265
3266 compRatioBent = it->second + ( next( it )->second - it->second ) * diffx / diffn;
3267 break; // We have our interpolated value
3268 }
3269 }
3270 }
3271 else
3272 {
3273 compRatioBent = lut.front().second; // Small symbol default is first entry
3274 }
3275
3276 // This is similar to the original KiCad code that scaled the zoom to make sure symbols were
3277 // visible on screen. It's simply a ratio of screen size to symbol size, and its job is to
3278 // zoom in to make the component fullscreen. Earlier in the code the symbol BBox is given a
3279 // 20% margin to add some breathing room. We compare the height of this enlarged symbol bbox
3280 // to the default text height. If a symbol will end up with the sides clipped, we adjust
3281 // later to make sure it fits on screen.
3282 screenSize.x = std::max( 10.0, screenSize.x );
3283 screenSize.y = std::max( 10.0, screenSize.y );
3284 double ratio = std::max( -1.0, fabs( bbSize.y / screenSize.y ) );
3285
3286 // Original KiCad code for how much to scale the zoom
3287 double kicadRatio = std::max( fabs( bbSize.x / screenSize.x ),
3288 fabs( bbSize.y / screenSize.y ) );
3289
3290 // If the width of the part we're probing is bigger than what the screen width will be after
3291 // the zoom, then punt and use the KiCad zoom algorithm since it guarantees the part's width
3292 // will be encompassed within the screen.
3293 if( bbSize.x > screenSize.x * ratio * compRatioBent )
3294 {
3295 // Use standard KiCad zoom for parts too wide to fit on screen/
3296 ratio = kicadRatio;
3297 compRatioBent = 1.0; // Reset so we don't modify the "KiCad" ratio
3298 wxLogTrace( "CROSS_PROBE_SCALE",
3299 "Part TOO WIDE for screen. Using normal KiCad zoom ratio: %1.5f", ratio );
3300 }
3301
3302 // Now that "compRatioBent" holds our final scaling factor we apply it to the original
3303 // fullscreen zoom ratio to arrive at the final ratio itself.
3304 ratio *= compRatioBent;
3305
3306 bool alwaysZoom = false; // DEBUG - allows us to minimize zooming or not
3307
3308 // Try not to zoom on every cross-probe; it gets very noisy
3309 if( ( ratio < 0.5 || ratio > 1.0 ) || alwaysZoom )
3310 getView()->SetScale( getView()->GetScale() / ratio );
3311}
3312
3313
3314void SCH_SELECTION_TOOL::SyncSelection( const std::optional<SCH_SHEET_PATH>& targetSheetPath,
3315 SCH_ITEM* focusItem, const std::vector<SCH_ITEM*>& items )
3316{
3317 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
3318
3319 if( !editFrame )
3320 return;
3321
3322 double targetZoom = 0.0;
3323 VECTOR2D targetCenter;
3324 bool targetZoomValid = false;
3325 bool changedSheet = false;
3326
3327 if( targetSheetPath )
3328 {
3329 SCH_SHEET_PATH path = targetSheetPath.value();
3330
3331 if( SCH_SCREEN* screen = path.LastScreen() )
3332 {
3333 targetZoom = screen->m_LastZoomLevel;
3334 targetCenter = screen->m_ScrollCenter;
3335 targetZoomValid = screen->IsZoomInitialized();
3336 }
3337
3338 if( path != editFrame->Schematic().CurrentSheet() )
3339 {
3340 m_frame->GetToolManager()->RunAction<SCH_SHEET_PATH*>( SCH_ACTIONS::changeSheet, &path );
3341 changedSheet = true;
3342 }
3343 }
3344
3345 if( changedSheet && targetZoomValid && !m_frame->eeconfig()->m_CrossProbing.zoom_to_fit )
3346 {
3347 getView()->SetScale( targetZoom );
3348 getView()->SetCenter( targetCenter );
3349 }
3350
3351 ClearSelection( items.size() > 0 ? true /*quiet mode*/ : false );
3352
3353 // Perform individual selection of each item before processing the event.
3354 for( SCH_ITEM* item : items )
3355 {
3356 SCH_ITEM* parent = dynamic_cast<SCH_ITEM*>( item->GetParent() );
3357
3358 // Make sure we only select items on the current screen
3359 if( m_frame->GetScreen()->CheckIfOnDrawList( item )
3360 || ( parent && m_frame->GetScreen()->CheckIfOnDrawList( parent ) ) )
3361 {
3362 select( item );
3363 }
3364 }
3365
3366 BOX2I bbox = m_selection.GetBoundingBox();
3367
3368 if( bbox.GetWidth() != 0 && bbox.GetHeight() != 0 )
3369 {
3370 if( m_frame->eeconfig()->m_CrossProbing.center_on_items )
3371 {
3372 if( m_frame->eeconfig()->m_CrossProbing.zoom_to_fit )
3373 ZoomFitCrossProbeBBox( bbox );
3374
3375 editFrame->FocusOnItem( focusItem );
3376
3377 if( !focusItem )
3378 editFrame->FocusOnLocation( bbox.Centre() );
3379 }
3380 }
3381
3382 if( m_selection.Size() > 0 )
3383 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3384}
3385
3386
3388{
3389 m_selection.Clear();
3390
3391 bool enteredGroupFound = false;
3392
3393 if( m_isSymbolEditor )
3394 {
3395 LIB_SYMBOL* start = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
3396
3397 for( SCH_ITEM& item : start->GetDrawItems() )
3398 {
3399 if( item.IsSelected() )
3400 select( &item );
3401
3402 if( item.Type() == SCH_GROUP_T )
3403 {
3404 if( &item == m_enteredGroup )
3405 {
3406 item.SetFlags( ENTERED );
3407 enteredGroupFound = true;
3408 }
3409 else
3410 {
3411 item.ClearFlags( ENTERED );
3412 }
3413 }
3414 }
3415 }
3416 else
3417 {
3418 for( SCH_ITEM* item : m_frame->GetScreen()->Items() )
3419 {
3420 // If the field and symbol are selected, only use the symbol
3421 if( item->IsSelected() )
3422 {
3423 select( item );
3424 }
3425 else
3426 {
3427 item->RunOnChildren(
3428 [&]( SCH_ITEM* aChild )
3429 {
3430 if( aChild->IsSelected() )
3431 select( aChild );
3432 },
3434 }
3435
3436 if( item->Type() == SCH_GROUP_T )
3437 {
3438 if( item == m_enteredGroup )
3439 {
3440 item->SetFlags( ENTERED );
3441 enteredGroupFound = true;
3442 }
3443 else
3444 {
3445 item->ClearFlags( ENTERED );
3446 }
3447 }
3448 }
3449 }
3450
3452
3453 if( !enteredGroupFound )
3454 {
3455 m_enteredGroupOverlay.Clear();
3456 m_enteredGroup = nullptr;
3457 }
3458
3459 // Inform other potentially interested tools
3460 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3461}
3462
3463
3464bool SCH_SELECTION_TOOL::Selectable( const EDA_ITEM* aItem, const VECTOR2I* aPos,
3465 bool checkVisibilityOnly ) const
3466{
3467 // NOTE: in the future this is where Eeschema layer/itemtype visibility will be handled
3468
3469 SYMBOL_EDIT_FRAME* symEditFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
3470
3471 // Do not allow selection of anything except fields when the current symbol in the symbol
3472 // editor is a derived symbol.
3473 if( symEditFrame && symEditFrame->IsSymbolAlias() && aItem->Type() != SCH_FIELD_T )
3474 return false;
3475
3476 switch( aItem->Type() )
3477 {
3478 case SCH_PIN_T:
3479 {
3480 const SCH_PIN* pin = static_cast<const SCH_PIN*>( aItem );
3481
3482 if( symEditFrame )
3483 {
3484 if( pin->GetUnit() && pin->GetUnit() != symEditFrame->GetUnit() )
3485 return false;
3486
3487 if( pin->GetBodyStyle() && pin->GetBodyStyle() != symEditFrame->GetBodyStyle() )
3488 return false;
3489 }
3490
3491 if( !pin->IsVisible() && !m_frame->GetShowAllPins() )
3492 return false;
3493
3494 if( !m_filter.pins )
3495 {
3496 // Pin anchors have to be allowed for auto-starting wires.
3497 if( aPos )
3498 {
3500 GRID_HELPER_GRIDS pinGrid = grid.GetItemGrid( pin );
3501
3502 if( pin->IsPointClickableAnchor( grid.BestSnapAnchor( *aPos, pinGrid ) ) )
3503 return true;
3504 }
3505
3506 return false;
3507 }
3508
3509 break;
3510 }
3511
3513 if( !m_frame->eeconfig()->m_Appearance.show_directive_labels )
3514 return false;
3515
3516 break;
3517
3518 case LIB_SYMBOL_T: // In symbol_editor we do not want to select the symbol itself.
3519 return false;
3520
3521 case SCH_FIELD_T: // SCH_FIELD objects are not unit/body-style-specific.
3522 {
3523 const SCH_FIELD* field = static_cast<const SCH_FIELD*>( aItem );
3524
3525 if( !field->IsVisible() && !( symEditFrame && symEditFrame->GetShowInvisibleFields() ) )
3526 return false;
3527
3528 break;
3529 }
3530
3531 case SCH_SHAPE_T:
3532 case SCH_TEXT_T:
3533 case SCH_TEXTBOX_T:
3534 if( symEditFrame )
3535 {
3536 const SCH_ITEM* sch_item = static_cast<const SCH_ITEM*>( aItem );
3537
3538 if( sch_item->GetUnit() && sch_item->GetUnit() != symEditFrame->GetUnit() )
3539 return false;
3540
3541 if( sch_item->GetBodyStyle() && sch_item->GetBodyStyle() != symEditFrame->GetBodyStyle() )
3542 return false;
3543 }
3544
3545 break;
3546
3547 case SCH_MARKER_T: // Always selectable
3548 return true;
3549
3550 case SCH_TABLECELL_T:
3551 {
3552 const SCH_TABLECELL* cell = static_cast<const SCH_TABLECELL*>( aItem );
3553
3554 if( cell->GetColSpan() == 0 || cell->GetRowSpan() == 0 )
3555 return false;
3556
3557 break;
3558 }
3559
3560 case NOT_USED: // Things like CONSTRUCTION_GEOM that aren't part of the model
3561 return false;
3562
3563 default: // Suppress warnings
3564 break;
3565 }
3566
3567 return true;
3568}
3569
3570
3572{
3573 if( m_selection.Empty() )
3574 return;
3575
3576 while( m_selection.GetSize() )
3578
3579 getView()->Update( &m_selection );
3580
3581 m_selection.SetIsHover( false );
3582 m_selection.ClearReferencePoint();
3583
3584 // Inform other potentially interested tools
3585 if( !aQuietMode )
3586 m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
3587}
3588
3589
3591{
3592 // Don't group when we select new items, the schematic editor selects all new items for moving.
3593 // The PCB editor doesn't need this logic because it doesn't select new items for moving.
3594 if( m_enteredGroup && !aItem->IsNew()
3595 && !SCH_GROUP::WithinScope( static_cast<SCH_ITEM*>( aItem ), m_enteredGroup, m_isSymbolEditor ) )
3596 {
3597 ExitGroup();
3598 }
3599
3600 highlight( aItem, SELECTED, &m_selection );
3601}
3602
3603
3605{
3606 unhighlight( aItem, SELECTED, &m_selection );
3607}
3608
3609
3610void SCH_SELECTION_TOOL::highlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
3611{
3612 if( aMode == SELECTED )
3613 aItem->SetSelected();
3614 else if( aMode == BRIGHTENED )
3615 aItem->SetBrightened();
3616
3617 if( aGroup )
3618 aGroup->Add( aItem );
3619
3620 // Highlight pins and fields. (All the other symbol children are currently only
3621 // represented in the LIB_SYMBOL and will inherit the settings of the parent symbol.)
3622 if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( aItem ) )
3623 {
3624 // We don't want to select group children if the group itself is selected,
3625 // we can only select them when the group is entered
3626 if( sch_item->Type() != SCH_GROUP_T )
3627 {
3628 sch_item->RunOnChildren(
3629 [&]( SCH_ITEM* aChild )
3630 {
3631 if( aMode == SELECTED )
3632 {
3633 aChild->SetSelected();
3634 getView()->Hide( aChild, true );
3635 }
3636 else if( aMode == BRIGHTENED )
3637 {
3638 aChild->SetBrightened();
3639 }
3640 },
3642 }
3643 }
3644
3645 if( aGroup && aMode != BRIGHTENED )
3646 getView()->Hide( aItem, true );
3647
3648 if( aItem->GetParent() && aItem->GetParent()->Type() != SCHEMATIC_T )
3649 getView()->Update( aItem->GetParent(), KIGFX::REPAINT );
3650
3651 getView()->Update( aItem, KIGFX::REPAINT );
3652}
3653
3654
3655void SCH_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
3656{
3657 if( aMode == SELECTED )
3658 {
3659 aItem->ClearSelected();
3660 // Lines need endpoints cleared here
3661 if( aItem->Type() == SCH_LINE_T )
3662 aItem->ClearFlags( STARTPOINT | ENDPOINT );
3663
3664 if( aMode != BRIGHTENED )
3665 getView()->Hide( aItem, false );
3666 }
3667 else if( aMode == BRIGHTENED )
3668 {
3669 aItem->ClearBrightened();
3670 }
3671
3672 if( aGroup )
3673 aGroup->Remove( aItem );
3674
3675 // Unhighlight pins and fields. (All the other symbol children are currently only
3676 // represented in the LIB_SYMBOL.)
3677 if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( aItem ) )
3678 {
3679 sch_item->RunOnChildren(
3680 [&]( SCH_ITEM* aChild )
3681 {
3682 if( aMode == SELECTED )
3683 {
3684 aChild->ClearSelected();
3685 getView()->Hide( aChild, false );
3686 }
3687 else if( aMode == BRIGHTENED )
3688 {
3689 aChild->ClearBrightened();
3690 }
3691
3692 if( aGroup )
3693 aGroup->Remove( aChild );
3694 },
3696 }
3697
3698 if( aItem->GetParent() && aItem->GetParent()->Type() != SCHEMATIC_T )
3699 getView()->Update( aItem->GetParent(), KIGFX::REPAINT );
3700
3701 getView()->Update( aItem, KIGFX::REPAINT );
3702}
3703
3704
3706{
3707 const unsigned GRIP_MARGIN = 20;
3708 int margin = KiROUND( getView()->ToWorld( GRIP_MARGIN ) );
3709
3710 // Check if the point is located within any of the currently selected items bounding boxes
3711 for( EDA_ITEM* item : m_selection )
3712 {
3713 BOX2I itemBox = item->ViewBBox();
3714 itemBox.Inflate( margin ); // Give some margin for gripping an item
3715
3716 if( itemBox.Contains( aPoint ) )
3717 return true;
3718 }
3719
3720 return false;
3721}
3722
3723
3725{
3726 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
3727
3728 if( !editFrame || !editFrame->GetNetNavigator() || m_selection.Size() == 0 )
3729 return 0;
3730
3731 if( !m_selection.Front()->IsBrightened() )
3732 return 0;
3733
3734 const SCH_ITEM* item = editFrame->SelectNextPrevNetNavigatorItem( true );
3735
3736 if( item )
3737 {
3739 select( const_cast<SCH_ITEM*>( item ) );
3740 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3741 }
3742
3743 return 0;
3744}
3745
3746
3748{
3749 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
3750
3751 if( !editFrame || !editFrame->GetNetNavigator() || m_selection.Size() == 0 )
3752 return 0;
3753
3754 if( !m_selection.Front()->IsBrightened() )
3755 return 0;
3756
3757 const SCH_ITEM* item = editFrame->SelectNextPrevNetNavigatorItem( false );
3758
3759 if( item )
3760 {
3762 select( const_cast<SCH_ITEM*>( item ) );
3763 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3764 }
3765
3766 return 0;
3767}
3768
3769
3771{
3773
3780
3782
3785
3791
3794
3797
3800
3802}
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:114
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
static TOOL_ACTION unselectAll
Definition actions.h:83
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition actions.h:227
static TOOL_ACTION cursorLeft
Definition actions.h:172
static TOOL_ACTION zoomOutCenter
Definition actions.h:136
static TOOL_ACTION unselectItem
Definition actions.h:228
static TOOL_ACTION zoomIn
Definition actions.h:133
static TOOL_ACTION cursorLeftFast
Definition actions.h:177
static TOOL_ACTION selectSetLasso
Definition actions.h:221
static TOOL_ACTION selectSetRect
Set lasso selection mode.
Definition actions.h:220
static TOOL_ACTION groupEnter
Definition actions.h:243
static TOOL_ACTION selectColumns
Definition actions.h:102
static TOOL_ACTION cursorDown
Definition actions.h:171
static TOOL_ACTION zoomOut
Definition actions.h:134
static TOOL_ACTION cursorRightFast
Definition actions.h:178
static TOOL_ACTION zoomCenter
Definition actions.h:141
static TOOL_ACTION panDown
Definition actions.h:185
static TOOL_ACTION cursorDblClick
Definition actions.h:181
static TOOL_ACTION undo
Definition actions.h:75
static TOOL_ACTION selectionActivate
Activation of the selection tool.
Definition actions.h:214
static TOOL_ACTION cursorDownFast
Definition actions.h:176
static TOOL_ACTION selectionMenu
Run a selection menu to select from a list of items.
Definition actions.h:236
static TOOL_ACTION selectRows
Definition actions.h:101
static TOOL_ACTION cursorUpFast
Definition actions.h:175
static TOOL_ACTION panLeft
Definition actions.h:186
static TOOL_ACTION updateMenu
Definition actions.h:270
static TOOL_ACTION doDelete
Definition actions.h:85
static TOOL_ACTION selectionTool
Definition actions.h:251
static TOOL_ACTION cursorClick
Definition actions.h:180
static TOOL_ACTION zoomFitScreen
Definition actions.h:142
static TOOL_ACTION increment
Definition actions.h:94
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:224
static TOOL_ACTION panUp
Definition actions.h:184
static TOOL_ACTION zoomFitObjects
Definition actions.h:143
static TOOL_ACTION zoomInCenter
Definition actions.h:135
static TOOL_ACTION panRight
Definition actions.h:187
static TOOL_ACTION selectTable
Definition actions.h:103
static TOOL_ACTION cursorUp
Cursor control with keyboard.
Definition actions.h:170
static TOOL_ACTION groupLeave
Definition actions.h:244
static TOOL_ACTION finishInteractive
Definition actions.h:73
static TOOL_ACTION cursorRight
Definition actions.h:173
static TOOL_ACTION selectAll
Definition actions.h:82
static TOOL_ACTION unselectItems
Definition actions.h:233
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition actions.h:232
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
constexpr const Vec & GetPosition() const
Definition box2.h:211
constexpr void SetMaximum()
Definition box2.h:80
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:558
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:146
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr Vec Centre() const
Definition box2.h:97
constexpr const Vec GetCenter() const
Definition box2.h:230
constexpr size_type GetHeight() const
Definition box2.h:215
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:168
constexpr const SizeVec & GetSize() const
Definition box2.h:206
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:311
An abstract class that will find and hold all the objects according to an inspection done by the Insp...
Definition collector.h:49
void Transfer(int aIndex)
Move the item at aIndex (first position is 0) to the backup list.
Definition collector.h:153
bool m_MenuCancelled
Definition collector.h:239
ITER begin()
Definition collector.h:75
int GetCount() const
Return the number of objects in the list.
Definition collector.h:83
bool HasItem(const EDA_ITEM *aItem) const
Tests if aItem has already been collected.
Definition collector.h:197
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition collector.h:111
int m_Threshold
Definition collector.h:236
ITER end()
Definition collector.h:76
void Append(EDA_ITEM *item)
Add an item to the end of the list.
Definition collector.h:101
COMMIT & Added(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition commit.h:84
bool Empty() const
Definition commit.h:137
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:78
Calculate the connectivity of a schematic and generates netlists.
bool IsHorizontal() const
Definition eda_angle.h:142
void FocusOnLocation(const VECTOR2I &aPos, bool aAllowScroll=true)
Useful to focus on a particular location, in find functions.
A set of EDA_ITEMs (i.e., without duplicates).
Definition eda_group.h:46
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
virtual VECTOR2I GetPosition() const
Definition eda_item.h:278
void SetIsRollover(bool aIsRollover, const VECTOR2I &aMousePos)
Definition eda_item.h:133
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:110
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:148
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void ClearSelected()
Definition eda_item.h:143
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:150
bool IsSelected() const
Definition eda_item.h:127
void SetSelected()
Definition eda_item.h:140
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition eda_item.h:198
void ClearBrightened()
Definition eda_item.h:144
void SetBrightened()
Definition eda_item.h:141
virtual bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const
Test if aPosition is inside or on the boundary of this item.
Definition eda_item.h:239
EDA_ITEM * GetParent() const
Definition eda_item.h:112
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:152
void XorFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:149
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:151
bool IsNew() const
Definition eda_item.h:124
SHAPE_T GetShape() const
Definition eda_shape.h:169
virtual bool IsFilledForHitTesting() const
Definition eda_shape.h:131
virtual std::vector< SHAPE * > MakeEffectiveShapesForHitTesting() const
Definition eda_shape.h:384
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:80
const EDA_ANGLE & GetTextAngle() const
Definition eda_text.h:147
virtual bool IsVisible() const
Definition eda_text.h:187
std::shared_ptr< SHAPE_COMPOUND > GetEffectiveTextShape(bool aTriangulate=true, const BOX2I &aBBox=BOX2I(), const EDA_ANGLE &aAngle=ANGLE_0) const
build a list of segments (SHAPE_SEGMENT) to describe a text shape.
GRID_HELPER_GRIDS GetItemGrid(const EDA_ITEM *aItem) const override
Get the coarsest grid that applies to an item.
VECTOR2I BestSnapAnchor(const VECTOR2I &aOrigin, GRID_HELPER_GRIDS aGrid, SCH_ITEM *aSkip)
static const TOOL_EVENT DisambiguatePoint
Used for hotkey feedback.
Definition actions.h:362
static const TOOL_EVENT ClearedEvent
Definition actions.h:347
static const TOOL_EVENT SelectedEvent
Definition actions.h:345
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:352
static const TOOL_EVENT PointSelectedEvent
Definition actions.h:344
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition actions.h:355
static const TOOL_EVENT UnselectedEvent
Definition actions.h:346
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
Represent a selection area (currently a rectangle) in a VIEW, drawn corner-to-corner between two poin...
void SetMode(SELECTION_MODE aMode)
void SetSubtractive(bool aSubtractive)
SELECTION_MODE GetMode() const
void SetAdditive(bool aAdditive)
void SetPoly(SHAPE_LINE_CHAIN &aPoly)
void SetOrigin(const VECTOR2I &aOrigin)
const BOX2I ViewBBox() const override
Set the origin of the rectangle (the fixed corner)
SHAPE_LINE_CHAIN & GetPoly()
void SetExclusiveOr(bool aExclusiveOr)
void SetEnd(const VECTOR2I &aEnd)
Set the current end of the rectangle (the corner that moves with the cursor.
void SetHighlight(bool aEnabled, int aNetcode=-1, bool aMulti=false)
Turns on/off highlighting.
An interface for classes handling user events controlling the view behavior such as zooming,...
virtual void CaptureCursor(bool aEnabled)
Force the cursor to stay within the drawing panel area.
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
virtual void WarpMouseCursor(const VECTOR2D &aPosition, bool aWorldCoordinates=false, bool aWarpView=false)=0
If enabled (.
virtual void SetCrossHairCursorPosition(const VECTOR2D &aPosition, bool aWarpView=true)=0
Move the graphic crosshair cursor to the requested position expressed in world coordinates.
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
virtual void SetCursorPosition(const VECTOR2D &aPosition, bool aWarpView=true, bool aTriggeredByArrows=false, long aArrowCommand=0)=0
Move cursor to the requested position expressed in world coordinates.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
An abstract base class for deriving all objects that can be added to a VIEW.
Definition view_item.h:86
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:66
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition view.cpp:530
virtual void SetScale(double aScale, VECTOR2D aAnchor={ 0, 0 })
Set the scaling factor, zooming around a given anchor point.
Definition view.cpp:570
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition view.cpp:298
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition view.cpp:341
int Query(const BOX2I &aRect, std::vector< LAYER_ITEM_PAIR > &aResult) const
Find all visible items that touch or are within the rectangle aRect.
Definition view.cpp:420
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition view.cpp:1685
bool IsMirroredX() const
Return true if view is flipped across the X axis.
Definition view.h:250
std::pair< VIEW_ITEM *, int > LAYER_ITEM_PAIR
Definition view.h:70
void Hide(VIEW_ITEM *aItem, bool aHide=true, bool aHideOverlay=false)
Temporarily hide the item in the view (e.g.
Definition view.cpp:1633
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition view.h:220
void SetCenter(const VECTOR2D &aCenter)
Set the center point of the VIEW (i.e.
Definition view.cpp:596
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition view.cpp:1612
Definition kiid.h:49
Define a library symbol object.
Definition lib_symbol.h:83
bool IsPower() const override
bool IsMultiBodyStyle() const override
Definition lib_symbol.h:761
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:699
int GetUnitCount() const override
bool IsExcluded() const
Definition marker_base.h:93
Tree view item data for the net navigator.
CONNECTION_GRAPH * ConnectionGraph() const
Definition schematic.h:199
SCH_SHEET & Root() const
Definition schematic.h:132
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:187
static TOOL_ACTION placeClassLabel
Definition sch_actions.h:79
static TOOL_ACTION placeSheetPin
Definition sch_actions.h:85
static TOOL_ACTION placeNextSymbolUnit
Definition sch_actions.h:67
static TOOL_ACTION swapPins
static TOOL_ACTION saveToLinkedDesignBlock
Definition sch_actions.h:71
static TOOL_ACTION clearHighlight
static TOOL_ACTION placeGlobalLabel
Definition sch_actions.h:80
static TOOL_ACTION pinTable
static TOOL_ACTION navigateBack
static TOOL_ACTION properties
static TOOL_ACTION leaveSheet
static TOOL_ACTION breakWire
static TOOL_ACTION findNetInInspector
static TOOL_ACTION autoplaceAllSheetPins
Definition sch_actions.h:86
static TOOL_ACTION drawLines
Definition sch_actions.h:99
static TOOL_ACTION placeHierLabel
Definition sch_actions.h:81
static TOOL_ACTION selectConnection
If current selection is a wire or bus, expand to entire connection.
Definition sch_actions.h:51
static TOOL_ACTION placeLabel
Definition sch_actions.h:78
static TOOL_ACTION nextNetItem
static TOOL_ACTION drawWire
Definition sch_actions.h:72
static TOOL_ACTION drag
static TOOL_ACTION placeJunction
Definition sch_actions.h:76
static TOOL_ACTION previousNetItem
static TOOL_ACTION swapUnitLabels
static TOOL_ACTION navigateForward
static TOOL_ACTION placeLinkedDesignBlock
Definition sch_actions.h:70
static TOOL_ACTION selectNode
Select the junction, wire or bus segment under the cursor.
Definition sch_actions.h:47
static TOOL_ACTION unfoldBus
Definition sch_actions.h:74
static TOOL_ACTION drawBus
Definition sch_actions.h:73
static TOOL_ACTION symbolProperties
static TOOL_ACTION slice
static TOOL_ACTION changeSheet
static TOOL_ACTION assignNetclass
static TOOL_ACTION swapPinLabels
static TOOL_ACTION enterSheet
static TOOL_ACTION editPageNumber
static TOOL_ACTION selectOnPCB
static TOOL_ACTION move
static TOOL_ACTION syncSheetPins
Definition sch_actions.h:89
A shim class between EDA_DRAW_FRAME and several derived classes: SYMBOL_EDIT_FRAME,...
Class for a wire to bus entry.
SCH_ITEM * m_connected_bus_item
Pointer to the bus item (usually a bus wire) connected to this bus-wire entry, if it is connected to ...
void Collect(SCH_SCREEN *aScreen, const std::vector< KICAD_T > &aScanTypes, const VECTOR2I &aPos, int aUnit=0, int aBodyStyle=0)
Scan a EDA_ITEM using this class's Inspector method which does the collection.
bool m_ShowPinElectricalTypes
static const std::vector< KICAD_T > MovableItems
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Execute the changes.
virtual void Revert() override
Revert the commit by restoring the modified items state.
static SELECTION_CONDITION SingleMultiFunctionPin
static SELECTION_CONDITION SingleSymbol
static SELECTION_CONDITION MultipleSymbolsOrPower
static SELECTION_CONDITION AllPinsOrSheetPins
static SELECTION_CONDITION SingleSymbolOrPower
static SELECTION_CONDITION SingleNonExcludedMarker
static SELECTION_CONDITION SingleMultiUnitSymbol
static SELECTION_CONDITION SingleMultiBodyStyleSymbol
static SELECTION_CONDITION AllPins
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
void ConfigureFromLabel(const wxString &aLabel)
Configures the connection given a label.
bool IsBus() const
Handle actions specific to the schematic editor.
Schematic editor (Eeschema) main window.
void FocusOnItem(EDA_ITEM *aItem, bool aAllowScroll=true) override
Focus on a particular canvas item.
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
SCH_SHEET_PATH & GetCurrentSheet() const
SCHEMATIC & Schematic() const
wxTreeCtrl * GetNetNavigator()
const SCH_ITEM * SelectNextPrevNetNavigatorItem(bool aNext)
const wxString & GetHighlightedConnection() const
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
A set of SCH_ITEMs (i.e., without duplicates).
Definition sch_group.h:52
static bool WithinScope(SCH_ITEM *aItem, SCH_GROUP *aScope, bool isSymbolEditor)
static EDA_GROUP * TopLevelGroup(SCH_ITEM *aItem, EDA_GROUP *aScope, bool isSymbolEditor)
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:167
virtual bool IsConnectable() const
Definition sch_item.h:526
virtual bool HasHoveredHypertext() const
Indicates that a hypertext link is currently active.
Definition sch_item.h:333
const SCH_ITEM_VEC & ConnectedItems(const SCH_SHEET_PATH &aPath)
Retrieve the set of items connected to this item on the given sheet.
Definition sch_item.cpp:532
const SYMBOL * GetParentSymbol() const
Definition sch_item.cpp:253
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:247
int GetBodyStyle() const
Definition sch_item.h:247
virtual bool IsPointClickableAnchor(const VECTOR2I &aPos) const
Definition sch_item.h:532
int GetUnit() const
Definition sch_item.h:238
bool IsType(const std::vector< KICAD_T > &aScanTypes) const override
Check whether the item is one of the listed types.
Definition sch_item.h:182
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const override
void SetShape(LABEL_FLAG_SHAPE aShape)
Definition sch_label.h:181
LABEL_SHAPE GetLabelShape() const
Definition sch_label.h:177
virtual void SetSpinStyle(SPIN_STYLE aSpinStyle)
static bool IsDrawingLineWireOrBus(const SELECTION &aSelection)
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:42
bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
Definition sch_line.cpp:816
bool IsStartDangling() const
Definition sch_line.h:303
VECTOR2I GetEndPoint() const
Definition sch_line.h:148
VECTOR2I GetStartPoint() const
Definition sch_line.h:139
SEG GetSeg() const
Get the geometric aspect of the wire as a SEG.
Definition sch_line.h:158
bool IsEndDangling() const
Definition sch_line.h:304
bool IsBus() const
Return true if the line is a bus.
Definition sch_line.cpp:997
bool IsGraphicLine() const
Return if the line is a graphic (non electrical line)
Definition sch_line.cpp:985
void SetEndPoint(const VECTOR2I &aPosition)
Definition sch_line.h:149
Tool that displays edit points allowing to modify items by dragging the points.
bool HasPoint()
Indicate the cursor is over an edit point.
SELECTION & selection() override
Return a reference to the selection.
int SelectPrevious(const TOOL_EVENT &aEvent)
void highlight(EDA_ITEM *aItem, int aMode, SELECTION *aGroup=nullptr) override
Highlight the item visually.
void SyncSelection(const std::optional< SCH_SHEET_PATH > &targetSheetPath, SCH_ITEM *focusItem, const std::vector< SCH_ITEM * > &items)
int SelectAll(const TOOL_EVENT &aEvent)
Unselect all visible items in sheet.
void narrowSelection(SCH_COLLECTOR &collector, const VECTOR2I &aWhere, bool aCheckLocked, bool aSelectedOnly=false, SCH_SELECTION_FILTER_OPTIONS *aRejected=nullptr)
Apply rules to narrow the collection down to selectable objects, and then heuristics to try and narro...
std::set< SCH_ITEM * > expandConnectionGraphically(const SCH_SELECTION &aItems)
KIGFX::VIEW_GROUP m_enteredGroupOverlay
EDA_ITEM * GetNode(const VECTOR2I &aPosition)
Finds a connected item at a point (usually the cursor position).
bool itemPassesFilter(EDA_ITEM *aItem, SCH_SELECTION_FILTER_OPTIONS *aRejected=nullptr)
Return true if the given item passes the stateful selection filter.
SCH_BASE_FRAME * m_frame
void EnterGroup() override
Enter the group at the head of the current selection.
void unhighlight(EDA_ITEM *aItem, int aMode, SELECTION *aGroup=nullptr) override
Unhighlight the item visually.
bool selectTableCells(SCH_TABLE *aTable)
Handle a table cell drag selection within a table.
int SetSelectRect(const TOOL_EVENT &aEvent)
int Main(const TOOL_EVENT &aEvent)
The main loop.
bool SelectPoint(const VECTOR2I &aWhere, const std::vector< KICAD_T > &aScanTypes={ SCH_LOCATE_ANY_T }, EDA_ITEM **aItem=nullptr, bool *aSelectionCancelledFlag=nullptr, bool aCheckLocked=false, bool aAdd=false, bool aSubtract=false, bool aExclusiveOr=false)
Perform a click-type selection at a point (usually the cursor position).
int ClearSelection(const TOOL_EVENT &aEvent)
Select all visible items in sheet.
bool CollectHits(SCH_COLLECTOR &aCollector, const VECTOR2I &aWhere, const std::vector< KICAD_T > &aScanTypes={ SCH_LOCATE_ANY_T })
Collect one or more items at a given point.
void GuessSelectionCandidates(SCH_COLLECTOR &collector, const VECTOR2I &aPos)
Apply heuristics to try and determine a single object when multiple are found under the cursor.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
void RebuildSelection()
Rebuild the selection from the EDA_ITEMs' selection flags.
SCH_TABLECELL * m_previous_first_cell
SCH_SELECTION & GetSelection()
SCH_SELECTION & RequestSelection(const std::vector< KICAD_T > &aScanTypes={ SCH_LOCATE_ANY_T }, bool aPromoteCellSelections=false, bool aPromoteGroups=false)
Return either an existing selection (filtered), or the selection at the current cursor position if th...
int SelectNext(const TOOL_EVENT &aEvent)
Select previous net item.
int UnselectAll(const TOOL_EVENT &aEvent)
Select next net item.
bool Init() override
Init() is called once upon a registration of the tool.
bool selectPoint(SCH_COLLECTOR &aCollector, const VECTOR2I &aWhere, EDA_ITEM **aItem=nullptr, bool *aSelectionCancelledFlag=nullptr, bool aAdd=false, bool aSubtract=false, bool aExclusiveOr=false)
Perform a click-type selection at a point (usually the cursor position).
std::set< SCH_ITEM * > expandConnectionWithGraph(const SCH_SELECTION &aItems)
OPT_TOOL_EVENT autostartEvent(TOOL_EVENT *aEvent, EE_GRID_HELPER &aGrid, SCH_ITEM *aItem)
void filterCollectorForHierarchy(SCH_COLLECTOR &aCollector, bool aMultiselect) const
In general we don't want to select both a parent and any of it's children.
bool selectMultiple()
Handle drawing a selection box that allows one to select many items at the same time.
SELECTION_MODE m_selectionMode
bool Selectable(const EDA_ITEM *aItem, const VECTOR2I *aPos=nullptr, bool checkVisibilityOnly=false) const
Check conditions for an item to be selected.
void updateReferencePoint()
Set the reference point to the anchor of the top-left item.
int SelectNode(const TOOL_EVENT &aEvent)
Selects the connected item at the current cursor position.
void InitializeSelectionState(SCH_TABLE *aTable)
Initialize the selection state of table cells.
void ExitGroup(bool aSelectGroup=false) override
Leave the currently-entered group.
int SelectRows(const TOOL_EVENT &aEvent)
void SelectCellsBetween(const VECTOR2D &start, const VECTOR2D &end, SCH_TABLE *aTable)
Select table cells within a rectangular area between two points.
void unselect(EDA_ITEM *aItem) override
Take necessary action to mark an item as unselected.
void select(EDA_ITEM *aItem) override
Take necessary action to mark an item as selected.
void SelectMultiple(KIGFX::PREVIEW::SELECTION_AREA &aArea, bool aSubtractive=false, bool aExclusiveOr=false)
int SelectColumns(const TOOL_EVENT &aEvent)
int SetSelectPoly(const TOOL_EVENT &aEvent)
int SelectTable(const TOOL_EVENT &aEvent)
Clear current selection event handler.
int disambiguateCursor(const TOOL_EVENT &aEvent)
Handle disambiguation actions including displaying the menu.
SCH_SELECTION_FILTER_OPTIONS m_filter
int SelectConnection(const TOOL_EVENT &aEvent)
If a connected item is selected then expand the selection to the entire connection,...
void ZoomFitCrossProbeBBox(const BOX2I &aBBox)
void OnIdle(wxIdleEvent &aEvent)
Zoom the screen to fit the bounding box for cross probing/selection sync.
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
int updateSelection(const TOOL_EVENT &aEvent)
Event handler to update the selection VIEW_ITEM.
void filterCollectedItems(SCH_COLLECTOR &aCollector, bool aMultiSelect)
Set up handlers for various events.
bool selectionContains(const VECTOR2I &aPoint) const
VECTOR2I GetCenter() const
Definition sch_shape.h:88
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
SCH_SHEET * Last() const
Return a pointer to the last SCH_SHEET of the list.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
SHEET_SIDE GetSide() const
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition sch_sheet.h:233
Schematic symbol object.
Definition sch_symbol.h:76
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
std::vector< SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
SCH_PIN * GetPin(const wxString &number) const
Find a symbol pin by number.
int GetOrientation() const override
Get the display symbol orientation.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:184
BOX2I GetBodyBoundingBox() const override
Return a bounding box for the symbol body but not the pins or fields.
int GetColSpan() const
int GetRowSpan() const
std::vector< SCH_TABLECELL * > GetCells() const
Definition sch_table.h:157
Definition seg.h:42
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition seg.cpp:702
static SELECTION_CONDITION HasTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if among the selected items there is at least one of a given types.
static SELECTION_CONDITION HasType(KICAD_T aType)
Create a functor that tests if among the selected items there is at least one of a given type.
static bool Empty(const SELECTION &aSelection)
Test if there are no items selected.
static SELECTION_CONDITION MoreThan(int aNumber)
Create a functor that tests if the number of selected items is greater than the value given as parame...
static bool Idle(const SELECTION &aSelection)
Test if there no items selected or being edited.
static SELECTION_CONDITION LessThan(int aNumber)
Create a functor that tests if the number of selected items is smaller than the value given as parame...
static SELECTION_CONDITION Count(int aNumber)
Create a functor that tests if the number of selected items is equal to the value given as parameter.
static SELECTION_CONDITION OnlyTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if the selected items are only of given types.
bool m_multiple
Multiple selection mode is active.
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
bool doSelectionMenu(COLLECTOR *aCollector)
wxTimer m_disambiguateTimer
Timer to show the disambiguate menu.
bool m_drag_additive
Add multiple items to selection.
bool m_exclusive_or
Items' selection state should be toggled.
int AddItemsToSel(const TOOL_EVENT &aEvent)
int AddItemToSel(const TOOL_EVENT &aEvent)
int UpdateMenu(const TOOL_EVENT &aEvent)
Update a menu's state based on the current selection.
void setModifiersState(bool aShiftState, bool aCtrlState, bool aAltState)
Set the configuration of m_additive, m_subtractive, m_exclusive_or, m_skip_heuristics from the state ...
VECTOR2I m_originalCursor
Location of original cursor when starting click.
int SelectionMenu(const TOOL_EVENT &aEvent)
Show a popup menu to trim the COLLECTOR passed as aEvent's parameter down to a single item.
int RemoveItemsFromSel(const TOOL_EVENT &aEvent)
bool m_subtractive
Items should be removed from selection.
SELECTION_TOOL(const std::string &aName)
bool m_skip_heuristics
Show disambiguation menu for all items under the cursor rather than trying to narrow them down first ...
bool m_drag_subtractive
Remove multiple from selection.
bool m_additive
Items should be added to sel (instead of replacing).
bool hasModifier()
True if a selection modifier is enabled, false otherwise.
bool m_canceledMenu
Sets to true if the disambiguation menu was canceled.
void onDisambiguationExpire(wxTimerEvent &aEvent)
Start the process to show our disambiguation menu once the user has kept the mouse down for the minim...
virtual void Add(EDA_ITEM *aItem)
Definition selection.cpp:42
const std::deque< EDA_ITEM * > GetItems() const
Definition selection.h:126
VECTOR2I GetReferencePoint() const
virtual void Remove(EDA_ITEM *aItem)
Definition selection.cpp:60
bool IsHover() const
Definition selection.h:89
bool Empty() const
Checks if there is anything selected.
Definition selection.h:115
bool HasReferencePoint() const
Definition selection.h:216
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void GenerateBBoxCache() const
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
double Area(bool aAbsolute=true) const
Return the area of this chain.
virtual size_t GetPointCount() const override
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CLastPoint() const
Return the last point in the line chain.
void Remove(int aStartIndex, int aEndIndex)
Remove the range of points [start_index, end_index] from the line chain.
bool Collide(const SHAPE *aShape, int aClearance, VECTOR2I *aMTV) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
Definition shape_rect.h:151
The symbol library editor main window.
bool IsSymbolAlias() const
Return true if aLibId is an alias for the editor screen symbol.
Symbol library viewer main window.
A base class for LIB_SYMBOL and SCH_SYMBOL.
Definition symbol.h:63
const TRANSFORM & GetTransform() const
Definition symbol.h:247
Represent a single user action.
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:186
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition tool_base.cpp:44
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition tool_base.cpp:38
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:78
@ REDRAW
Full drawing refresh.
Definition tool_base.h:83
@ SHUTDOWN
Tool is being shut down.
Definition tool_base.h:84
@ MODEL_RELOAD
Model changes (the sheet for a schematic)
Definition tool_base.h:80
@ SUPERMODEL_RELOAD
For schematics, the entire schematic changed, not just the sheet.
Definition tool_base.h:81
Generic, UI-independent tool event.
Definition tool_event.h:171
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition tool_event.h:293
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
void SetPassEvent(bool aPass=true)
Definition tool_event.h:256
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
std::unique_ptr< TOOL_MENU > m_menu
The functions below are not yet implemented - their interface may change.
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition vector2d.h:561
MOUSE_DRAG_ACTION
KICURSOR
Definition cursors.h:44
@ LINE_BUS
Definition cursors.h:90
@ LINE_WIRE
Definition cursors.h:94
@ SUBTRACT
Definition cursors.h:72
@ SELECT_WINDOW
Definition cursors.h:86
@ SELECT_LASSO
Definition cursors.h:88
@ MOVING
Definition cursors.h:48
@ ARROW
Definition cursors.h:46
@ LINE_GRAPHIC
Definition cursors.h:92
#define DEFAULT_TEXT_SIZE
Ratio of the font height to the baseline of the text above the wire.
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:408
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:407
@ RECURSE
Definition eda_item.h:51
@ NO_RECURSE
Definition eda_item.h:52
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
Definition eda_item.h:575
#define BRIGHTENED
item is drawn with a bright contour
#define IS_NEW
New item, just created.
#define SELECTED
Item was manually selected by the user.
#define SELECTION_CANDIDATE
indicates an item is a candidate for selection
#define ENTERED
indicates a group has been entered
#define ENDPOINT
ends. (Used to support dragging.)
std::uint32_t EDA_ITEM_FLAGS
#define IS_MOVING
Item being moved.
#define SHOW_ELEC_TYPE
Show pin electrical type.
#define STARTPOINT
When a line is selected, these flags indicate which.
@ ID_POPUP_SCH_PIN_TRICKS_START
@ ID_POPUP_SCH_PIN_TRICKS_HIER_LABEL
@ ID_POPUP_SCH_PIN_TRICKS_WIRE
@ ID_POPUP_SCH_PLACE_UNIT_END
Definition eeschema_id.h:97
@ ID_POPUP_SCH_ALT_PIN_FUNCTION
@ ID_POPUP_SCH_UNFOLD_BUS_END
Definition eeschema_id.h:86
@ ID_POPUP_SCH_SELECT_UNIT
Definition eeschema_id.h:89
@ ID_POPUP_SCH_SELECT_BODY_STYLE
Definition eeschema_id.h:99
@ ID_POPUP_SCH_PIN_TRICKS_NET_LABEL
@ ID_POPUP_SCH_SELECT_BODY_STYLE_END
@ ID_POPUP_SCH_PIN_TRICKS_NO_CONNECT
@ ID_POPUP_SCH_UNFOLD_BUS
Definition eeschema_id.h:85
@ ID_POPUP_SCH_SELECT_UNIT_END
Definition eeschema_id.h:93
@ ID_POPUP_SCH_ALT_PIN_FUNCTION_END
@ ID_POPUP_SCH_PLACE_UNIT
Definition eeschema_id.h:95
@ ID_POPUP_SCH_PIN_TRICKS_GLOBAL_LABEL
@ ID_POPUP_SCH_PIN_TRICKS_END
@ FRAME_SCH_VIEWER
Definition frame_type.h:36
a few functions useful in geometry calculations.
GRID_HELPER_GRIDS
Definition grid_helper.h:44
@ GRID_WIRES
Definition grid_helper.h:49
KIID niluuid(0)
@ LAYER_WIRE
Definition layer_ids.h:452
@ LAYER_BUS
Definition layer_ids.h:453
bool BoxHitTest(const VECTOR2I &aHitPoint, const BOX2I &aHittee, int aAccuracy)
Perform a point-to-box hit test.
@ REPAINT
Item needs to be redrawn.
Definition view_item.h:58
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition pin_type.h:36
@ PT_INPUT
usual pin input: must be connected
Definition pin_type.h:37
@ PT_OUTPUT
usual output
Definition pin_type.h:38
@ PT_TRISTATE
tri state bus pin
Definition pin_type.h:40
@ PT_BIDI
input or output (like port for a microprocessor)
Definition pin_type.h:39
@ PT_UNSPECIFIED
unknown electrical properties: creates always a warning when connected
Definition pin_type.h:45
@ PT_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin.
Definition pin_type.h:43
@ PIN_UP
The pin extends upwards from the connection point: Probably on the bottom side of the symbol.
Definition pin_type.h:127
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:111
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:118
@ PIN_DOWN
The pin extends downwards from the connection: Probably on the top side of the symbol.
Definition pin_type.h:135
CITER next(CITER it)
Definition ptree.cpp:124
#define HITTEST_THRESHOLD_PIXELS
Class to handle a set of SCH_ITEMs.
std::vector< SCH_ITEM * > SCH_ITEM_VEC
Definition sch_item.h:156
@ L_BIDI
Definition sch_label.h:102
@ L_TRISTATE
Definition sch_label.h:103
@ L_UNSPECIFIED
Definition sch_label.h:104
@ L_OUTPUT
Definition sch_label.h:101
@ L_INPUT
Definition sch_label.h:100
@ LABEL_BIDI
Definition sch_label.h:120
@ LABEL_INPUT
Definition sch_label.h:118
@ LABEL_OUTPUT
Definition sch_label.h:119
@ LABEL_PASSIVE
Definition sch_label.h:122
@ LABEL_TRISTATE
Definition sch_label.h:121
static std::vector< KICAD_T > connectedTypes
static std::vector< KICAD_T > tableCellTypes
static std::vector< KICAD_T > expandConnectionGraphTypes
static std::vector< KICAD_T > lineTypes
static std::vector< KICAD_T > sheetTypes
const TOOL_ACTION * allowedActions[]
static void passEvent(TOOL_EVENT *const aEvent, const TOOL_ACTION *const aAllowedActions[])
static std::vector< KICAD_T > connectedLineTypes
static std::vector< KICAD_T > crossProbingTypes
std::vector< SCH_SYMBOL * > GetSameSymbolMultiUnitSelection(const SELECTION &aSel)
Validates and gathers a selection containing multiple symbol units that all belong to the same refere...
std::function< bool(const SELECTION &)> SELECTION_CONDITION
Functor type that checks a specific condition for selected items.
SELECTION_MODE
std::vector< FAB_LAYER_COLOR > dummy
bool symbols
Allow selecting symbols and sheet symbols.
bool labels
Net and bus labels.
bool pins
Symbol and sheet pins.
bool graphics
Graphic lines, shapes, polygons.
bool lockedItems
Allow selecting locked items.
bool images
Bitmap/vector images.
bool otherItems
Anything not fitting one of the above categories.
bool wires
Net and bus wires and junctions.
void RotateAndMirrorPin(SCH_PIN &aPin, int aOrientMirror)
Rotate and/or mirror a SCH_PIN according to aOrientMirror.
std::string path
KIBIS top(path, &reporter)
KIBIS_PIN * pin
VECTOR2I end
int delta
@ TA_CHOICE_MENU_CHOICE
Context menu choice.
Definition tool_event.h:98
@ TA_UNDO_REDO_PRE
This event is sent before undo/redo command is performed.
Definition tool_event.h:106
@ TA_MOUSE_WHEEL
Definition tool_event.h:73
std::optional< TOOL_EVENT > OPT_TOOL_EVENT
Definition tool_event.h:641
@ MD_ALT
Definition tool_event.h:145
@ MD_CTRL
Definition tool_event.h:144
@ MD_SHIFT
Definition tool_event.h:143
@ TC_COMMAND
Definition tool_event.h:57
@ BUT_AUX1
Definition tool_event.h:135
@ BUT_MIDDLE
Definition tool_event.h:134
@ BUT_LEFT
Definition tool_event.h:132
@ BUT_RIGHT
Definition tool_event.h:133
@ BUT_AUX2
Definition tool_event.h:136
@ SCH_GROUP_T
Definition typeinfo.h:177
@ SCH_TABLE_T
Definition typeinfo.h:169
@ SCH_LINE_T
Definition typeinfo.h:167
@ LIB_SYMBOL_T
Definition typeinfo.h:152
@ SCH_NO_CONNECT_T
Definition typeinfo.h:164
@ SCH_SYMBOL_T
Definition typeinfo.h:176
@ SCH_TABLECELL_T
Definition typeinfo.h:170
@ SCH_ITEM_LOCATE_WIRE_T
Definition typeinfo.h:190
@ SCH_FIELD_T
Definition typeinfo.h:154
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:175
@ SCH_LABEL_T
Definition typeinfo.h:171
@ SCH_LOCATE_ANY_T
Definition typeinfo.h:203
@ SCH_SHEET_T
Definition typeinfo.h:179
@ SCH_ITEM_LOCATE_BUS_T
Definition typeinfo.h:191
@ SCH_MARKER_T
Definition typeinfo.h:162
@ SCH_SHAPE_T
Definition typeinfo.h:153
@ SCH_RULE_AREA_T
Definition typeinfo.h:174
@ SCH_HIER_LABEL_T
Definition typeinfo.h:173
@ NOT_USED
the 3d code uses this value
Definition typeinfo.h:79
@ SCH_BUS_BUS_ENTRY_T
Definition typeinfo.h:166
@ SCH_ITEM_LOCATE_GRAPHIC_LINE_T
Definition typeinfo.h:192
@ SCHEMATIC_T
Definition typeinfo.h:208
@ SCH_SHEET_PIN_T
Definition typeinfo.h:178
@ SCH_TEXT_T
Definition typeinfo.h:155
@ SCH_SYMBOL_LOCATE_POWER_T
Definition typeinfo.h:200
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:165
@ SCH_BITMAP_T
Definition typeinfo.h:168
@ SCH_TEXTBOX_T
Definition typeinfo.h:156
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:172
@ SCH_JUNCTION_T
Definition typeinfo.h:163
@ SCH_PIN_T
Definition typeinfo.h:157
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR2< double > VECTOR2D
Definition vector2d.h:694