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 && evt->HasPosition() && selectionContains( evt->DragOrigin() ) )
726 {
727 m_toolMgr->RunAction( SCH_ACTIONS::move );
728 }
729 // Allow drag selecting table cells, except when they're inside a group that we haven't entered
730 else if( CollectHits( collector, evt->DragOrigin(), { SCH_TABLECELL_T } )
731 && ( collector[0]->GetParent()->GetParentGroup() == nullptr
732 || collector[0]->GetParent()->GetParentGroup() == m_enteredGroup ) )
733 {
734 selectTableCells( static_cast<SCH_TABLE*>( collector[0]->GetParent() ) );
735 }
736 else if( hasModifier() || drag_action == MOUSE_DRAG_ACTION::SELECT )
737 {
740 selectLasso();
741 else
743 }
744 else if( m_selection.Empty() && drag_action != MOUSE_DRAG_ACTION::DRAG_ANY )
745 {
748 selectLasso();
749 else
751 }
752 else
753 {
754 if( m_isSymbolEditor )
755 {
756 if( static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->IsSymbolAlias() )
757 {
759 }
760 else
761 {
765 SCH_PIN_T,
766 SCH_FIELD_T } );
767 }
768 }
769 else
770 {
772 }
773
774 // Check if dragging has started within any of selected items bounding box
775 if( evt->HasPosition() && selectionContains( evt->DragOrigin() ) )
776 {
777 // drag_is_move option exists only in schematic editor, not in symbol editor
778 // (m_frame->eeconfig() returns nullptr in Symbol Editor)
779 if( m_isSymbolEditor || m_frame->eeconfig()->m_Input.drag_is_move )
780 m_toolMgr->RunAction( SCH_ACTIONS::move );
781 else
782 m_toolMgr->RunAction( SCH_ACTIONS::drag );
783 }
784 else
785 {
786 // No -> drag a selection box
789 selectLasso();
790 else
792 }
793 }
794 }
795 else if( evt->IsMouseDown( BUT_AUX1 ) )
796 {
798 }
799 else if( evt->IsMouseDown( BUT_AUX2 ) )
800 {
802 }
803 else if( evt->Action() == TA_MOUSE_WHEEL )
804 {
805 int field = -1;
806
807 if( evt->Modifier() == ( MD_SHIFT | MD_ALT ) )
808 field = 0;
809 else if( evt->Modifier() == ( MD_CTRL | MD_ALT ) )
810 field = 1;
811 // any more?
812
813 if( field >= 0 )
814 {
815 const int delta = evt->Parameter<int>();
816 ACTIONS::INCREMENT incParams{ delta > 0 ? 1 : -1, field };
817
818 m_toolMgr->RunAction( ACTIONS::increment, incParams );
819 }
820 }
821 else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CHOICE_MENU_CHOICE )
822 {
823 m_disambiguateTimer.Stop();
824
825 // context sub-menu selection? Handle unit selection or bus unfolding
826 if( *evt->GetCommandId() >= ID_POPUP_SCH_SELECT_UNIT
827 && *evt->GetCommandId() <= ID_POPUP_SCH_SELECT_UNIT_END )
828 {
829 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( m_selection.Front() );
830 int unit = *evt->GetCommandId() - ID_POPUP_SCH_SELECT_UNIT;
831
832 if( symbol )
833 static_cast<SCH_EDIT_FRAME*>( m_frame )->SelectUnit( symbol, unit );
834 }
835 else if( *evt->GetCommandId() >= ID_POPUP_SCH_PLACE_UNIT
836 && *evt->GetCommandId() <= ID_POPUP_SCH_PLACE_UNIT_END )
837 {
838 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( m_selection.Front() );
839 int unit = *evt->GetCommandId() - ID_POPUP_SCH_PLACE_UNIT;
840
841 if( symbol )
844 }
845 else if( *evt->GetCommandId() >= ID_POPUP_SCH_SELECT_BODY_STYLE
846 && *evt->GetCommandId() <= ID_POPUP_SCH_SELECT_BODY_STYLE_END )
847 {
848 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( m_selection.Front() );
849 int bodyStyle = ( *evt->GetCommandId() - ID_POPUP_SCH_SELECT_BODY_STYLE ) + 1;
850
851 if( symbol && symbol->GetBodyStyle() != bodyStyle )
852 static_cast<SCH_EDIT_FRAME*>( m_frame )->SelectBodyStyle( symbol, bodyStyle );
853 }
854 else if( *evt->GetCommandId() >= ID_POPUP_SCH_ALT_PIN_FUNCTION
855 && *evt->GetCommandId() <= ID_POPUP_SCH_ALT_PIN_FUNCTION_END )
856 {
857 SCH_PIN* pin = dynamic_cast<SCH_PIN*>( m_selection.Front() );
858 wxString alt = *evt->Parameter<wxString*>();
859
860 if( pin )
861 static_cast<SCH_EDIT_FRAME*>( m_frame )->SetAltPinFunction( pin, alt );
862 }
863 else if( *evt->GetCommandId() >= ID_POPUP_SCH_PIN_TRICKS_START
864 && *evt->GetCommandId() <= ID_POPUP_SCH_PIN_TRICKS_END
866 {
867 SCH_EDIT_FRAME* sch_frame = static_cast<SCH_EDIT_FRAME*>( m_frame );
868
869 // Keep track of new items so we make them the new selection at the end
870 EDA_ITEMS newItems;
871 SCH_COMMIT commit( sch_frame );
872
873 if( *evt->GetCommandId() == ID_POPUP_SCH_PIN_TRICKS_NO_CONNECT )
874 {
875 for( EDA_ITEM* item : m_selection )
876 {
877 if( item->Type() != SCH_PIN_T && item->Type() != SCH_SHEET_PIN_T )
878 continue;
879
880 SCH_NO_CONNECT* nc = new SCH_NO_CONNECT( item->GetPosition() );
881 commit.Add( nc, sch_frame->GetScreen() );
882 newItems.push_back( nc );
883 }
884
885 if( !commit.Empty() )
886 {
887 commit.Push( wxS( "No Connect Pins" ) );
889 }
890 }
891 else if( *evt->GetCommandId() == ID_POPUP_SCH_PIN_TRICKS_WIRE )
892 {
893 VECTOR2I wireGrid = grid.GetGridSize( GRID_HELPER_GRIDS::GRID_WIRES );
894
895 for( EDA_ITEM* item : m_selection )
896 {
897 if( item->Type() != SCH_PIN_T && item->Type() != SCH_SHEET_PIN_T )
898 continue;
899
900 SCH_LINE* wire = new SCH_LINE( item->GetPosition(), LAYER_WIRE );
901
902 // Add some length to the wire as nothing in our code base handles
903 // 0 length wires very well, least of all the ortho drag algorithm
904 VECTOR2I stub;
905
906 switch( pinOrientation( item ) )
907 {
908 default:
910 stub = VECTOR2I( -1 * wireGrid.x, 0 );
911 break;
913 stub = VECTOR2I( 1 * wireGrid.x, 0 );
914 break;
916 stub = VECTOR2I( 0, 1 * wireGrid.y );
917 break;
919 stub = VECTOR2I( 0, -1 * wireGrid.y );
920 break;
921 }
922
923 wire->SetEndPoint( item->GetPosition() + stub );
924
925 m_frame->AddToScreen( wire, sch_frame->GetScreen() );
926 commit.Added( wire, sch_frame->GetScreen() );
927 newItems.push_back( wire );
928 }
929
930 if( !commit.Empty() )
931 {
933 AddItemsToSel( &newItems );
934
935 // Select only the ends so we can immediately start dragging them
936 for( EDA_ITEM* item : newItems )
937 static_cast<SCH_LINE*>( item )->SetFlags( ENDPOINT );
938
940
941 // Put the mouse on the nearest point of the first wire
942 SCH_LINE* first = static_cast<SCH_LINE*>( newItems[0] );
943 vc->SetCrossHairCursorPosition( first->GetEndPoint(), false );
944 vc->WarpMouseCursor( vc->GetCursorPosition(), true );
945
946 // Start the drag tool, canceling will remove the wires
947 if( m_toolMgr->RunSynchronousAction( SCH_ACTIONS::drag, &commit ) )
948 commit.Push( wxS( "Wire Pins" ) );
949 else
950 commit.Revert();
951 }
952 }
953 else
954 {
955 // For every pin in the selection, add a label according to menu item
956 // selected by the user
957 for( EDA_ITEM* item : m_selection )
958 {
959 SCH_PIN* pin = dynamic_cast<SCH_PIN*>( item );
960 SCH_SHEET_PIN* sheetPin = dynamic_cast<SCH_SHEET_PIN*>( item );
961 SCH_LABEL_BASE* label = nullptr;
962 SCH_SHEET_PATH& sheetPath = sch_frame->GetCurrentSheet();
963
964 wxString labelText;
965
966 if( pin )
967 {
968 labelText = pin->GetShownName();
969
970 if( labelText.IsEmpty() )
971 {
972 labelText.Printf( "%s_%s",
973 pin->GetParentSymbol()->GetRef( &sheetPath ),
974 pin->GetNumber() );
975 }
976 }
977 else if( sheetPin )
978 {
979 labelText = sheetPin->GetShownText( &sheetPath, false );
980 }
981 else
982 {
983 continue;
984 }
985
986 switch( *evt->GetCommandId() )
987 {
989 label = new SCH_LABEL( item->GetPosition(), labelText );
990 break;
992 label = new SCH_HIERLABEL( item->GetPosition(), labelText );
993 break;
995 label = new SCH_GLOBALLABEL( item->GetPosition(), labelText );
996 break;
997 default:
998 continue;
999 }
1000
1001 switch( pinOrientation( item ) )
1002 {
1003 default:
1006 break;
1009 break;
1012 break;
1015 break;
1016 }
1017
1019
1020 if( pin )
1021 {
1022 pinType = pin->GetType();
1023 }
1024 else if( sheetPin )
1025 {
1026 switch( sheetPin->GetLabelShape() )
1027 {
1028 case LABEL_INPUT: pinType = ELECTRICAL_PINTYPE::PT_INPUT; break;
1029 case LABEL_OUTPUT: pinType = ELECTRICAL_PINTYPE::PT_OUTPUT; break;
1030 case LABEL_BIDI: pinType = ELECTRICAL_PINTYPE::PT_BIDI; break;
1031 case LABEL_TRISTATE: pinType = ELECTRICAL_PINTYPE::PT_TRISTATE; break;
1032 case LABEL_PASSIVE: pinType = ELECTRICAL_PINTYPE::PT_PASSIVE; break;
1033 }
1034 }
1035
1036 switch( pinType )
1037 {
1040 break;
1043 break;
1046 break;
1049 break;
1052 break;
1053 default:
1055 }
1056
1057 commit.Add( label, sch_frame->GetScreen() );
1058 newItems.push_back( label );
1059 }
1060
1061 if( !commit.Empty() )
1062 {
1063 commit.Push( wxS( "Label Pins" ) );
1064
1065 // Many users will want to drag these items to wire off of the pins, so
1066 // pre-select them.
1068 AddItemsToSel( &newItems );
1069 }
1070 }
1071 }
1072 else if( *evt->GetCommandId() >= ID_POPUP_SCH_UNFOLD_BUS
1073 && *evt->GetCommandId() <= ID_POPUP_SCH_UNFOLD_BUS_END )
1074 {
1075 wxString* net = new wxString( *evt->Parameter<wxString*>() );
1076 m_toolMgr->RunAction<wxString*>( SCH_ACTIONS::unfoldBus, net );
1077 }
1078 }
1079 else if( evt->IsCancelInteractive() )
1080 {
1081 m_disambiguateTimer.Stop();
1082
1083 // We didn't set these, but we have reports that they leak out of some other tools,
1084 // so we clear them here.
1085 getViewControls()->SetAutoPan( false );
1086 getViewControls()->CaptureCursor( false );
1087
1088 if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
1089 schframe->ClearFocus();
1090
1091 if( !GetSelection().Empty() )
1092 {
1094 }
1095 else if( evt->FirstResponder() == this && evt->GetCommandId() == (int) WXK_ESCAPE )
1096 {
1097 if( m_enteredGroup )
1098 {
1099 ExitGroup();
1100 }
1101 else
1102 {
1104
1105 if( editor && m_frame->eeconfig()->m_Input.esc_clears_net_highlight )
1106 editor->ClearHighlight( *evt );
1107 }
1108 }
1109 }
1110 else if( evt->Action() == TA_UNDO_REDO_PRE )
1111 {
1112 if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
1113 schframe->ClearFocus();
1114 }
1115 else if( evt->IsMotion() && !m_isSymbolEditor && evt->FirstResponder() == this )
1116 {
1117 // Update cursor and rollover item
1118 rolloverItemId = niluuid;
1119 SCH_COLLECTOR collector;
1120
1122
1123 if( CollectHits( collector, evt->Position() ) )
1124 {
1125 narrowSelection( collector, evt->Position(), false, false, nullptr );
1126
1127 if( collector.GetCount() == 1 && !hasModifier() )
1128 {
1129 OPT_TOOL_EVENT autostartEvt = autostartEvent( evt, grid, collector[0] );
1130
1131 if( autostartEvt )
1132 {
1133 if( autostartEvt->Matches( SCH_ACTIONS::drawBus.MakeEvent() ) )
1134 displayBusCursor = true;
1135 else if( autostartEvt->Matches( SCH_ACTIONS::drawWire.MakeEvent() ) )
1136 displayWireCursor = true;
1137 else if( autostartEvt->Matches( SCH_ACTIONS::drawLines.MakeEvent() ) )
1138 displayLineCursor = true;
1139 }
1140 else if( collector[0]->HasHypertext() && !collector[0]->IsSelected() )
1141 {
1142 rolloverItemId = collector[0]->m_Uuid;
1143 }
1144 }
1145 }
1146 }
1147 else
1148 {
1149 evt->SetPassEvent();
1150 }
1151
1152 if( lastRolloverItemId != niluuid && lastRolloverItemId != rolloverItemId )
1153 {
1154 EDA_ITEM* item = m_frame->ResolveItem( lastRolloverItemId );
1155
1156 item->SetIsRollover( false, { 0, 0 } );
1157
1158 if( item->Type() == SCH_FIELD_T || item->Type() == SCH_TABLECELL_T )
1159 m_frame->GetCanvas()->GetView()->Update( item->GetParent() );
1160 else
1161 m_frame->GetCanvas()->GetView()->Update( item );
1162 }
1163
1164 SCH_ITEM* rolloverItem = nullptr;
1165
1166 if( rolloverItemId != niluuid )
1167 {
1168 rolloverItem = static_cast<SCH_ITEM*>( m_frame->ResolveItem( rolloverItemId ) );
1169
1170 rolloverItem->SetIsRollover( true, getViewControls()->GetMousePosition() );
1171
1172 if( rolloverItem->Type() == SCH_FIELD_T || rolloverItem->Type() == SCH_TABLECELL_T )
1173 m_frame->GetCanvas()->GetView()->Update( rolloverItem->GetParent() );
1174 else
1175 m_frame->GetCanvas()->GetView()->Update( rolloverItem );
1176 }
1177
1178 lastRolloverItemId = rolloverItemId;
1179
1180 if( m_frame->ToolStackIsEmpty() )
1181 {
1182 if( displayWireCursor )
1183 {
1185 }
1186 else if( displayBusCursor )
1187 {
1189 }
1190 else if( displayLineCursor )
1191 {
1193 }
1194 else if( rolloverItem && rolloverItem->HasHoveredHypertext() )
1195 {
1197 }
1198 else if( !m_selection.Empty()
1199 && drag_action == MOUSE_DRAG_ACTION::DRAG_SELECTED
1200 && evt->HasPosition()
1201 && selectionContains( evt->Position() ) //move/drag option prediction
1202 )
1203 {
1205 }
1206 else
1207 {
1209 }
1210 }
1211 }
1212
1213 m_disambiguateTimer.Stop();
1214
1215 // Shutting down; clear the selection
1216 m_selection.Clear();
1217
1218 return 0;
1219}
1220
1221
1223{
1224 wxCHECK_RET( m_selection.GetSize() == 1 && m_selection[0]->Type() == SCH_GROUP_T,
1225 wxT( "EnterGroup called when selection is not a single group" ) );
1226 SCH_GROUP* aGroup = static_cast<SCH_GROUP*>( m_selection[0] );
1227
1228 if( m_enteredGroup != nullptr )
1229 ExitGroup();
1230
1232 m_enteredGroup = aGroup;
1233 m_enteredGroup->SetFlags( ENTERED );
1234 m_enteredGroup->RunOnChildren(
1235 [&]( SCH_ITEM* aChild )
1236 {
1237 if( aChild->Type() == SCH_LINE_T )
1238 aChild->SetFlags( STARTPOINT | ENDPOINT );
1239
1240 select( aChild );
1241 },
1243
1244 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1245
1246 getView()->Hide( m_enteredGroup, true );
1249}
1250
1251
1252void SCH_SELECTION_TOOL::ExitGroup( bool aSelectGroup )
1253{
1254 // Only continue if there is a group entered
1255 if( m_enteredGroup == nullptr )
1256 return;
1257
1258 m_enteredGroup->ClearFlags( ENTERED );
1259 getView()->Hide( m_enteredGroup, false );
1261
1262 if( aSelectGroup )
1263 {
1265 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1266 }
1267
1268 m_enteredGroupOverlay.Clear();
1269 m_enteredGroup = nullptr;
1271}
1272
1273
1275 SCH_ITEM* aItem )
1276{
1277 VECTOR2I pos = aGrid.BestSnapAnchor( aEvent->Position(), aGrid.GetItemGrid( aItem ) );
1278
1279 if( m_frame->eeconfig()->m_Drawing.auto_start_wires
1280 && !m_toolMgr->GetTool<SCH_POINT_EDITOR>()->HasPoint()
1281 && aItem->IsPointClickableAnchor( pos ) )
1282 {
1283 OPT_TOOL_EVENT newEvt = SCH_ACTIONS::drawWire.MakeEvent();
1284
1285 if( aItem->Type() == SCH_BUS_BUS_ENTRY_T )
1286 {
1287 newEvt = SCH_ACTIONS::drawBus.MakeEvent();
1288 }
1289 else if( aItem->Type() == SCH_BUS_WIRE_ENTRY_T )
1290 {
1291 SCH_BUS_WIRE_ENTRY* busEntry = static_cast<SCH_BUS_WIRE_ENTRY*>( aItem );
1292
1293 if( !busEntry->m_connected_bus_item )
1294 newEvt = SCH_ACTIONS::drawBus.MakeEvent();
1295 }
1296 else if( aItem->Type() == SCH_LINE_T )
1297 {
1298 SCH_LINE* line = static_cast<SCH_LINE*>( aItem );
1299
1300 if( line->IsBus() )
1301 newEvt = SCH_ACTIONS::drawBus.MakeEvent();
1302 else if( line->IsGraphicLine() )
1303 newEvt = SCH_ACTIONS::drawLines.MakeEvent();
1304 }
1305 else if( aItem->Type() == SCH_LABEL_T || aItem->Type() == SCH_HIER_LABEL_T
1306 || aItem->Type() == SCH_SHEET_PIN_T || aItem->Type() == SCH_GLOBAL_LABEL_T )
1307 {
1308 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( aItem );
1309 SCH_CONNECTION possibleConnection( label->Schematic()->ConnectionGraph() );
1310 possibleConnection.ConfigureFromLabel( label->GetShownText( false ) );
1311
1312 if( possibleConnection.IsBus() )
1313 newEvt = SCH_ACTIONS::drawBus.MakeEvent();
1314 }
1315 else if( aItem->Type() == SCH_SYMBOL_T )
1316 {
1317 const SCH_SYMBOL* symbol = static_cast<const SCH_SYMBOL*>( aItem );
1318 const SCH_PIN* pin = symbol->GetPin( pos );
1319
1320 if( !pin || !pin->IsPointClickableAnchor( pos ) )
1321 return OPT_TOOL_EVENT();
1322
1323 if( !pin->IsVisible()
1324 && !( m_frame->eeconfig()->m_Appearance.show_hidden_pins
1325 || m_frame->GetRenderSettings()->m_ShowHiddenPins ) )
1326 {
1327 return OPT_TOOL_EVENT();
1328 }
1329 }
1330
1331 newEvt->SetMousePosition( pos );
1332 newEvt->SetHasPosition( true );
1333 newEvt->SetForceImmediate( true );
1334
1335 getViewControls()->ForceCursorPosition( true, pos );
1336
1337 return newEvt;
1338 }
1339
1340 return OPT_TOOL_EVENT();
1341}
1342
1343
1345{
1346 wxMouseState keyboardState = wxGetMouseState();
1347
1348 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
1349 keyboardState.AltDown() );
1350
1351 m_skip_heuristics = true;
1354 m_skip_heuristics = false;
1355
1356 return 0;
1357}
1358
1359
1360void SCH_SELECTION_TOOL::OnIdle( wxIdleEvent& aEvent )
1361{
1362 if( m_frame->ToolStackIsEmpty() && !m_multiple )
1363 {
1364 wxMouseState keyboardState = wxGetMouseState();
1365
1366 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
1367 keyboardState.AltDown() );
1368
1369 if( m_additive )
1370 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ADD );
1371 else if( m_subtractive )
1372 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SUBTRACT );
1373 else if( m_exclusive_or )
1374 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::XOR );
1375 else
1376 m_frame->GetCanvas()->SetCurrentCursor( m_nonModifiedCursor );
1377 }
1378}
1379
1380
1385
1386
1388 const std::vector<KICAD_T>& aScanTypes )
1389{
1390 int pixelThreshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
1391 int gridThreshold = KiROUND( getView()->GetGAL()->GetGridSize().EuclideanNorm() / 2.0 );
1392 aCollector.m_Threshold = std::max( pixelThreshold, gridThreshold );
1393 aCollector.m_ShowPinElectricalTypes = m_frame->GetRenderSettings()->m_ShowPinsElectricalType;
1394
1395 if( m_isSymbolEditor )
1396 {
1397 LIB_SYMBOL* symbol = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
1398
1399 if( !symbol )
1400 return false;
1401
1402 aCollector.Collect( symbol->GetDrawItems(), aScanTypes, aWhere, m_unit, m_bodyStyle );
1403 }
1404 else
1405 {
1406 aCollector.Collect( m_frame->GetScreen(), aScanTypes, aWhere, m_unit, m_bodyStyle );
1407
1408 // If pins are disabled in the filter, they will be removed later. Let's add the parent
1409 // so that people can use pins to select symbols in this case.
1410 if( !m_filter.pins )
1411 {
1412 int originalCount = aCollector.GetCount();
1413
1414 for( int ii = 0; ii < originalCount; ++ii )
1415 {
1416 if( aCollector[ii]->Type() == SCH_PIN_T )
1417 {
1418 SCH_PIN* pin = static_cast<SCH_PIN*>( aCollector[ii] );
1419
1420 if( !aCollector.HasItem( pin->GetParentSymbol() ) )
1421 aCollector.Append( pin->GetParentSymbol() );
1422 }
1423 }
1424 }
1425 }
1426
1427 return aCollector.GetCount() > 0;
1428}
1429
1430
1432 bool aCheckLocked, bool aSelectedOnly,
1433 SCH_SELECTION_FILTER_OPTIONS* aRejected )
1434{
1435 SYMBOL_EDIT_FRAME* symbolEditorFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
1436
1437 for( int i = collector.GetCount() - 1; i >= 0; --i )
1438 {
1439 if( symbolEditorFrame )
1440 {
1441 // Do not select invisible items if they are not displayed
1442 EDA_ITEM* item = collector[i];
1443
1444 if( item->Type() == SCH_FIELD_T )
1445 {
1446 if( !static_cast<SCH_FIELD*>( item )->IsVisible()
1447 && !symbolEditorFrame->GetShowInvisibleFields() )
1448 {
1449 collector.Remove( i );
1450 continue;
1451 }
1452 }
1453 else if( item->Type() == SCH_PIN_T )
1454 {
1455 if( !static_cast<SCH_PIN*>( item )->IsVisible()
1456 && !symbolEditorFrame->GetShowInvisiblePins() )
1457 {
1458 collector.Remove( i );
1459 continue;
1460 }
1461 }
1462 }
1463
1464 if( !Selectable( collector[i], &aWhere ) )
1465 {
1466 collector.Remove( i );
1467 continue;
1468 }
1469
1470 if( aCheckLocked && collector[i]->IsLocked() )
1471 {
1472 if( aRejected )
1473 aRejected->lockedItems = true;
1474 collector.Remove( i );
1475 continue;
1476 }
1477
1478 if( !itemPassesFilter( collector[i], aRejected ) )
1479 {
1480 collector.Remove( i );
1481 continue;
1482 }
1483
1484 if( aSelectedOnly && !collector[i]->IsSelected() )
1485 {
1486 collector.Remove( i );
1487 continue;
1488 }
1489 }
1490
1491 filterCollectorForHierarchy( collector, false );
1492
1493 // Apply some ugly heuristics to avoid disambiguation menus whenever possible
1494 if( collector.GetCount() > 1 && !m_skip_heuristics )
1495 GuessSelectionCandidates( collector, aWhere );
1496}
1497
1498
1500 EDA_ITEM** aItem, bool* aSelectionCancelledFlag, bool aAdd,
1501 bool aSubtract, bool aExclusiveOr )
1502{
1503 m_selection.ClearReferencePoint();
1504
1505 // If still more than one item we're going to have to ask the user.
1506 if( aCollector.GetCount() > 1 )
1507 {
1508 // Try to call selectionMenu via RunAction() to avoid event-loop contention
1509 // But it we cannot handle the event, then we don't have an active tool loop, so
1510 // handle it directly.
1511 if( !m_toolMgr->RunAction<COLLECTOR*>( ACTIONS::selectionMenu, &aCollector ) )
1512 {
1513 if( !doSelectionMenu( &aCollector ) )
1514 aCollector.m_MenuCancelled = true;
1515 }
1516
1517 if( aCollector.m_MenuCancelled )
1518 {
1519 if( aSelectionCancelledFlag )
1520 *aSelectionCancelledFlag = true;
1521
1522 return false;
1523 }
1524 }
1525
1526 if( !aAdd && !aSubtract && !aExclusiveOr )
1528
1529 // It is possible for slop in the selection model to cause us to be outside the group,
1530 // but also selecting an item within the group, so only exit if the selection doesn't
1531 // have an item belonging to the group
1532 if( m_enteredGroup && !m_enteredGroup->GetBoundingBox().Contains( aWhere ) )
1533 {
1534 bool foundEnteredGroup = false;
1535 for( EDA_ITEM* item : aCollector )
1536 {
1537 if( item->GetParentGroup() == m_enteredGroup )
1538 {
1539 foundEnteredGroup = true;
1540 break;
1541 }
1542 }
1543
1544 if( !foundEnteredGroup )
1545 ExitGroup();
1546 }
1547
1548 filterCollectorForHierarchy( aCollector, true );
1549
1550 int addedCount = 0;
1551 bool anySubtracted = false;
1552
1553 if( aCollector.GetCount() > 0 )
1554 {
1555 for( int i = 0; i < aCollector.GetCount(); ++i )
1556 {
1557 EDA_ITEM_FLAGS flags = 0;
1558 bool isLine = aCollector[i]->Type() == SCH_LINE_T;
1559
1560 // Handle line ends specially
1561 if( isLine )
1562 {
1563 SCH_LINE* line = (SCH_LINE*) aCollector[i];
1564
1565 if( line->GetStartPoint().Distance( aWhere ) <= aCollector.m_Threshold )
1566 flags = STARTPOINT;
1567 else if( line->GetEndPoint().Distance( aWhere ) <= aCollector.m_Threshold )
1568 flags = ENDPOINT;
1569 else
1570 flags = STARTPOINT | ENDPOINT;
1571 }
1572
1573 if( aSubtract
1574 || ( aExclusiveOr && aCollector[i]->IsSelected()
1575 && ( !isLine || ( isLine && aCollector[i]->HasFlag( flags ) ) ) ) )
1576 {
1577 aCollector[i]->ClearFlags( flags );
1578
1579 // Need to update end shadows after ctrl-click unselecting one of two selected
1580 // endpoints.
1581 if( isLine )
1582 getView()->Update( aCollector[i] );
1583
1584 if( !aCollector[i]->HasFlag( STARTPOINT ) && !aCollector[i]->HasFlag( ENDPOINT ) )
1585 {
1586 unselect( aCollector[i] );
1587 anySubtracted = true;
1588 }
1589 }
1590 else
1591 {
1592 aCollector[i]->SetFlags( flags );
1593 select( aCollector[i] );
1594 addedCount++;
1595 }
1596 }
1597 }
1598
1599 if( addedCount == 1 )
1600 {
1601 m_toolMgr->ProcessEvent( EVENTS::PointSelectedEvent );
1602
1603 if( aItem && aCollector.GetCount() == 1 )
1604 *aItem = aCollector[0];
1605
1606 return true;
1607 }
1608 else if( addedCount > 1 )
1609 {
1610 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1611 return true;
1612 }
1613 else if( anySubtracted )
1614 {
1615 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1616 return true;
1617 }
1618
1619 m_frame->GetCanvas()->ForceRefresh();
1620 return false;
1621}
1622
1623
1625 const std::vector<KICAD_T>& aScanTypes,
1626 EDA_ITEM** aItem, bool* aSelectionCancelledFlag,
1627 bool aCheckLocked, bool aAdd, bool aSubtract,
1628 bool aExclusiveOr )
1629{
1630 SCH_COLLECTOR collector;
1631
1632 if( !CollectHits( collector, aWhere, aScanTypes ) )
1633 return false;
1634
1635 size_t preFilterCount = collector.GetCount();
1637 rejected.SetAll( false );
1638 narrowSelection( collector, aWhere, aCheckLocked, aSubtract, &rejected );
1639
1640 if( collector.GetCount() == 0 && preFilterCount > 0 )
1641 {
1642 if( SCH_BASE_FRAME* frame = dynamic_cast<SCH_BASE_FRAME*>( m_frame ) )
1643 frame->HighlightSelectionFilter( rejected );
1644
1645 return false;
1646 }
1647
1648 return selectPoint( collector, aWhere, aItem, aSelectionCancelledFlag, aAdd, aSubtract,
1649 aExclusiveOr );
1650}
1651
1652
1654{
1655 SCH_COLLECTOR collection;
1656 m_multiple = true; // Multiple selection mode is active
1657 KIGFX::VIEW* view = getView();
1658
1659 std::vector<EDA_ITEM*> sheetPins;
1660
1661 // Filter the view items based on the selection box
1662 BOX2I selectionBox;
1663
1664 selectionBox.SetMaximum();
1665 view->Query( selectionBox,
1666 [&]( KIGFX::VIEW_ITEM* viewItem ) -> bool
1667 {
1668 SCH_ITEM* item = static_cast<SCH_ITEM*>( viewItem );
1669
1670 if( !item )
1671 return true;
1672
1673 collection.Append( item );
1674 return true;
1675 } );
1676
1677 filterCollectorForHierarchy( collection, true );
1678
1679 // Sheet pins aren't in the view; add them by hand
1680 for( EDA_ITEM* item : collection )
1681 {
1682 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item );
1683
1684 if( sheet )
1685 {
1686 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
1687 sheetPins.emplace_back( pin );
1688 }
1689 }
1690
1691 for( EDA_ITEM* pin : sheetPins )
1692 collection.Append( pin );
1693
1694 for( EDA_ITEM* item : collection )
1695 {
1696 if( Selectable( item ) && itemPassesFilter( item, nullptr ) )
1697 {
1698 if( item->Type() == SCH_LINE_T )
1699 item->SetFlags( STARTPOINT | ENDPOINT );
1700
1701 select( item );
1702 }
1703 }
1704
1705 m_multiple = false;
1706
1707 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1708 m_frame->GetCanvas()->ForceRefresh();
1709 return 0;
1710}
1711
1713{
1714 m_multiple = true; // Multiple selection mode is active
1715 KIGFX::VIEW* view = getView();
1716
1717 // hold all visible items
1718 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
1719
1720 // Filter the view items based on the selection box
1721 BOX2I selectionBox;
1722
1723 selectionBox.SetMaximum();
1724 view->Query( selectionBox, selectedItems ); // Get the list of selected items
1725
1726 for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : selectedItems )
1727 {
1728 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( pair.first );
1729
1730 if( sheet )
1731 {
1732 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
1733 {
1734 EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( pin );
1735
1736 if( item && Selectable( item ) )
1737 unselect( item );
1738 }
1739 }
1740
1741 if( EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( pair.first ) )
1742 {
1743 if( Selectable( item ) )
1744 unselect( item );
1745 }
1746 }
1747
1748 m_multiple = false;
1749
1750 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1751 m_frame->GetCanvas()->ForceRefresh();
1752 return 0;
1753}
1754
1755
1757{
1758 // Prefer exact hits to sloppy ones
1759 std::set<EDA_ITEM*> exactHits;
1760
1761 for( int i = collector.GetCount() - 1; i >= 0; --i )
1762 {
1763 EDA_ITEM* item = collector[ i ];
1764 SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
1765 SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( item );
1766 SCH_TABLE* table = dynamic_cast<SCH_TABLE*>( item );
1767
1768 // Lines are hard to hit. Give them a bit more slop to still be considered "exact".
1769 if( line || ( shape && shape->GetShape() == SHAPE_T::POLY )
1770 || ( shape && shape->GetShape() == SHAPE_T::ARC ) )
1771 {
1772 int pixelThreshold = KiROUND( getView()->ToWorld( 6 ) );
1773
1774 if( item->HitTest( aPos, pixelThreshold ) )
1775 exactHits.insert( item );
1776 }
1777 else if( table )
1778 {
1779 // Consider table cells exact, but not the table itself
1780 }
1781 else
1782 {
1783
1784 if( m_frame->GetRenderSettings()->m_ShowPinsElectricalType )
1785 item->SetFlags( SHOW_ELEC_TYPE );
1786
1787 if( item->HitTest( aPos, 0 ) )
1788 exactHits.insert( item );
1789
1790 item->ClearFlags( SHOW_ELEC_TYPE );
1791 }
1792 }
1793
1794 if( exactHits.size() > 0 && exactHits.size() < (unsigned) collector.GetCount() )
1795 {
1796 for( int i = collector.GetCount() - 1; i >= 0; --i )
1797 {
1798 EDA_ITEM* item = collector[ i ];
1799
1800 if( !exactHits.contains( item ) )
1801 collector.Transfer( item );
1802 }
1803 }
1804
1805 // Find the closest item. (Note that at this point all hits are either exact or non-exact.)
1806 SEG poss( aPos, aPos );
1807 EDA_ITEM* closest = nullptr;
1808 int closestDist = INT_MAX / 4;
1809
1810 for( EDA_ITEM* item : collector )
1811 {
1812 BOX2I bbox = item->GetBoundingBox();
1813 int dist = INT_MAX / 4;
1814
1815 // A dominating item is one that would unfairly win distance tests
1816 // and mask out other items. For example, a filled rectangle "wins"
1817 // with a zero distance over anything inside it.
1818 bool dominating = false;
1819
1820 if( exactHits.contains( item ) )
1821 {
1822 if( item->Type() == SCH_PIN_T || item->Type() == SCH_JUNCTION_T )
1823 {
1824 closest = item;
1825 break;
1826 }
1827
1828 SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
1829 SCH_FIELD* field = dynamic_cast<SCH_FIELD*>( item );
1830 EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item );
1831 EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( item );
1832 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
1833
1834 if( line )
1835 {
1836 dist = line->GetSeg().Distance( aPos );
1837 }
1838 else if( field )
1839 {
1840 BOX2I box = field->GetBoundingBox();
1841 EDA_ANGLE orient = field->GetTextAngle();
1842
1843 if( field->GetParent() && field->GetParent()->Type() == SCH_SYMBOL_T )
1844 {
1845 if( static_cast<SCH_SYMBOL*>( field->GetParent() )->GetTransform().y1 )
1846 {
1847 if( orient.IsHorizontal() )
1848 orient = ANGLE_VERTICAL;
1849 else
1850 orient = ANGLE_HORIZONTAL;
1851 }
1852 }
1853
1854 field->GetEffectiveTextShape( false, box, orient )->Collide( poss, INT_MAX / 4, &dist );
1855 }
1856 else if( text )
1857 {
1858 text->GetEffectiveTextShape( false )->Collide( poss, INT_MAX / 4, &dist );
1859 }
1860 else if( shape )
1861 {
1862 auto shapes = std::make_shared<SHAPE_COMPOUND>( shape->MakeEffectiveShapesForHitTesting() );
1863
1864 shapes->Collide( poss, INT_MAX / 4, &dist );
1865
1866 // Filled shapes win hit tests anywhere inside them
1867 dominating = shape->IsFilledForHitTesting();
1868 }
1869 else if( symbol )
1870 {
1871 bbox = symbol->GetBodyBoundingBox();
1872
1873 SHAPE_RECT rect( bbox.GetPosition(), bbox.GetWidth(), bbox.GetHeight() );
1874
1875 if( bbox.Contains( aPos ) )
1876 dist = bbox.GetCenter().Distance( aPos );
1877 else
1878 rect.Collide( poss, closestDist, &dist );
1879 }
1880 else
1881 {
1882 dist = bbox.GetCenter().Distance( aPos );
1883 }
1884 }
1885 else
1886 {
1887 SHAPE_RECT rect( bbox.GetPosition(), bbox.GetWidth(), bbox.GetHeight() );
1888 rect.Collide( poss, collector.m_Threshold, &dist );
1889 }
1890
1891 // Don't promote dominating items to be the closest item
1892 // (they'll always win) - they'll still be available for selection, but they
1893 // won't boot out worthy competitors.
1894 if ( !dominating )
1895 {
1896 if( dist == closestDist )
1897 {
1898 if( item->GetParent() == closest )
1899 closest = item;
1900 }
1901 else if( dist < closestDist )
1902 {
1903 closestDist = dist;
1904 closest = item;
1905 }
1906 }
1907 }
1908
1909 // Construct a tight box (1/2 height and width) around the center of the closest item.
1910 // All items which exist at least partly outside this box have sufficient other areas
1911 // for selection and can be dropped.
1912 if( closest ) // Don't try and get a tight bbox if nothing is near the mouse pointer
1913 {
1914 BOX2I tightBox = closest->GetBoundingBox();
1915 tightBox.Inflate( -tightBox.GetWidth() / 4, -tightBox.GetHeight() / 4 );
1916
1917 for( int i = collector.GetCount() - 1; i >= 0; --i )
1918 {
1919 EDA_ITEM* item = collector[i];
1920
1921 if( item == closest )
1922 continue;
1923
1924 if( !item->HitTest( tightBox, true ) )
1925 collector.Transfer( item );
1926 }
1927 }
1928}
1929
1930
1931SCH_SELECTION& SCH_SELECTION_TOOL::RequestSelection( const std::vector<KICAD_T>& aScanTypes,
1932 bool aPromoteCellSelections,
1933 bool aPromoteGroups )
1934{
1935 bool anyUnselected = false;
1936 bool anySelected = false;
1937
1938 if( m_selection.Empty() )
1939 {
1940 VECTOR2D cursorPos = getViewControls()->GetCursorPosition( true );
1941
1943 SelectPoint( cursorPos, aScanTypes );
1944 m_selection.SetIsHover( true );
1945 m_selection.ClearReferencePoint();
1946 }
1947 else // Trim an existing selection by aFilterList
1948 {
1949 bool isMoving = false;
1950
1951 for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
1952 {
1953 EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
1954 isMoving |= static_cast<SCH_ITEM*>( item )->IsMoving();
1955
1956 if( !item->IsType( aScanTypes ) )
1957 {
1958 unselect( item );
1959 anyUnselected = true;
1960 }
1961 }
1962
1963 if( !isMoving )
1965 }
1966
1967 if( aPromoteGroups )
1968 {
1969 for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
1970 {
1971 EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
1972
1973 std::set<EDA_ITEM*> selectedChildren;
1974
1975 if( item->Type() == SCH_GROUP_T )
1976 {
1977 static_cast<SCH_ITEM*>(item)->RunOnChildren( [&]( SCH_ITEM* aChild )
1978 {
1979 if( aChild->IsType( aScanTypes ) )
1980 selectedChildren.insert( aChild );
1981 },
1983 unselect( item );
1984 anyUnselected = true;
1985 }
1986
1987 for( EDA_ITEM* child : selectedChildren )
1988 {
1989 if( !child->IsSelected() )
1990 {
1991 if( child->Type() == SCH_LINE_T )
1992 static_cast<SCH_LINE*>( child )->SetFlags( STARTPOINT | ENDPOINT );
1993
1994 select( child );
1995 anySelected = true;
1996 }
1997 }
1998 }
1999 }
2000
2001 if( aPromoteCellSelections )
2002 {
2003 std::set<EDA_ITEM*> parents;
2004
2005 for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
2006 {
2007 EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
2008
2009 if( item->Type() == SCH_TABLECELL_T )
2010 {
2011 parents.insert( item->GetParent() );
2012 unselect( item );
2013 anyUnselected = true;
2014 }
2015 }
2016
2017 for( EDA_ITEM* parent : parents )
2018 {
2019 if( !parent->IsSelected() )
2020 {
2021 select( parent );
2022 anySelected = true;
2023 }
2024 }
2025 }
2026
2027 if( anyUnselected )
2028 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2029
2030 if( anySelected )
2031 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2032
2033 return m_selection;
2034}
2035
2036
2037void SCH_SELECTION_TOOL::filterCollectedItems( SCH_COLLECTOR& aCollector, bool aMultiSelect )
2038{
2039 if( aCollector.GetCount() == 0 )
2040 return;
2041
2042 std::set<EDA_ITEM*> rejected;
2043
2044 for( EDA_ITEM* item : aCollector )
2045 {
2046 if( !itemPassesFilter( item, nullptr ) )
2047 rejected.insert( item );
2048 }
2049
2050 for( EDA_ITEM* item : rejected )
2051 aCollector.Remove( item );
2052}
2053
2054
2056{
2057 if( !aItem )
2058 return false;
2059
2060 // Locking is not yet exposed uniformly in the schematic
2061#if 0
2062 if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( aItem ) )
2063 {
2064 if( schItem->IsLocked() && !m_filter.lockedItems )
2065 return false;
2066 }
2067#endif
2068
2069 switch( aItem->Type() )
2070 {
2071 case SCH_SYMBOL_T:
2072 case SCH_SHEET_T:
2073 if( !m_filter.symbols )
2074 {
2075 if( aRejected )
2076 aRejected->symbols = true;
2077 return false;
2078 }
2079
2080 break;
2081
2082 case SCH_PIN_T:
2083 case SCH_SHEET_PIN_T:
2084 if( !m_filter.pins )
2085 {
2086 if( aRejected )
2087 aRejected->pins = true;
2088 return false;
2089 }
2090
2091 break;
2092
2093 case SCH_JUNCTION_T:
2094 if( !m_filter.wires )
2095 {
2096 if( aRejected )
2097 aRejected->wires = true;
2098 return false;
2099 }
2100
2101 break;
2102
2103 case SCH_LINE_T:
2104 {
2105 switch( static_cast<SCH_LINE*>( aItem )->GetLayer() )
2106 {
2107 case LAYER_WIRE:
2108 case LAYER_BUS:
2109 if( !m_filter.wires )
2110 {
2111 if( aRejected )
2112 aRejected->wires = true;
2113 return false;
2114 }
2115
2116 break;
2117
2118 default:
2119 if( !m_filter.graphics )
2120 {
2121 if( aRejected )
2122 aRejected->graphics = true;
2123 return false;
2124 }
2125 }
2126
2127 break;
2128 }
2129
2130 case SCH_SHAPE_T:
2131 if( !m_filter.graphics )
2132 {
2133 if( aRejected )
2134 aRejected->graphics = true;
2135 return false;
2136 }
2137
2138 break;
2139
2140 case SCH_TEXT_T:
2141 case SCH_TEXTBOX_T:
2142 case SCH_TABLE_T:
2143 case SCH_TABLECELL_T:
2144 case SCH_FIELD_T:
2145 if( !m_filter.text )
2146 {
2147 if( aRejected )
2148 aRejected->text = true;
2149 return false;
2150 }
2151
2152 break;
2153
2154 case SCH_LABEL_T:
2155 case SCH_GLOBAL_LABEL_T:
2156 case SCH_HIER_LABEL_T:
2157 if( !m_filter.labels )
2158 {
2159 if( aRejected )
2160 aRejected->labels = true;
2161 return false;
2162 }
2163
2164 break;
2165
2166 case SCH_BITMAP_T:
2167 if( !m_filter.images )
2168 {
2169 if( aRejected )
2170 aRejected->images = true;
2171 return false;
2172 }
2173
2174 break;
2175
2176 case SCH_RULE_AREA_T:
2177 if( !m_filter.ruleAreas )
2178 {
2179 if( aRejected )
2180 aRejected->ruleAreas = true;
2181 return false;
2182 }
2183
2184 break;
2185
2186 default:
2187 if( !m_filter.otherItems )
2188 {
2189 if( aRejected )
2190 aRejected->otherItems = true;
2191 return false;
2192 }
2193
2194 break;
2195 }
2196
2197 return true;
2198}
2199
2200
2202{
2203 VECTOR2I refP( 0, 0 );
2204
2205 if( m_selection.Size() > 0 )
2206 refP = static_cast<SCH_ITEM*>( m_selection.GetTopLeftItem() )->GetPosition();
2207
2208 m_selection.SetReferencePoint( refP );
2209}
2210
2211
2213{
2215 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
2216 m_toolMgr->PostAction( ACTIONS::selectionTool );
2217 return 0;
2218}
2219
2220
2222{
2224 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2225 m_toolMgr->PostAction( ACTIONS::selectionTool );
2226 return 0;
2227}
2228
2229
2230// Some navigation actions are allowed in selectMultiple
2241
2242
2244{
2245 // Block selection not allowed in symbol viewer frame: no actual code to handle
2246 // a selection, so return to avoid to draw a selection rectangle, and to avoid crashes.
2247 if( m_frame->IsType( FRAME_T::FRAME_SCH_VIEWER ) )
2248 return false;
2249
2250 bool cancelled = false; // Was the tool canceled while it was running?
2251 m_multiple = true; // Multiple selection mode is active
2252 KIGFX::VIEW* view = getView();
2253
2255 view->Add( &area );
2256
2257 while( TOOL_EVENT* evt = Wait() )
2258 {
2259 /* Selection mode depends on direction of drag-selection:
2260 * Left > Right : Select objects that are fully enclosed by selection
2261 * Right > Left : Select objects that are crossed by selection
2262 */
2263 bool isGreedy = area.GetEnd().x < area.GetOrigin().x;
2264
2265 if( view->IsMirroredX() )
2266 isGreedy = !isGreedy;
2267
2268 m_frame->GetCanvas()->SetCurrentCursor( isGreedy ? KICURSOR::SELECT_LASSO
2270
2271 if( evt->IsCancelInteractive() || evt->IsActivate() )
2272 {
2273 cancelled = true;
2274 break;
2275 }
2276
2277 if( evt->IsDrag( BUT_LEFT ) )
2278 {
2281
2282 // Start drawing a selection box
2283 area.SetOrigin( evt->DragOrigin() );
2284 area.SetEnd( evt->Position() );
2287 area.SetExclusiveOr( false );
2290
2291 view->SetVisible( &area, true );
2292 view->Update( &area );
2293 getViewControls()->SetAutoPan( true );
2294 }
2295
2296 if( evt->IsMouseUp( BUT_LEFT ) )
2297 {
2298 getViewControls()->SetAutoPan( false );
2299 view->SetVisible( &area, false );
2300 SelectMultiple( area, m_drag_subtractive, false );
2301 evt->SetPassEvent( false );
2302 break;
2303 }
2304
2305 passEvent( evt, allowedActions );
2306 }
2307
2308 getViewControls()->SetAutoPan( false );
2309
2310 // Stop drawing the selection box
2311 view->Remove( &area );
2312 m_multiple = false; // Multiple selection mode is inactive
2313
2314 if( !cancelled )
2315 m_selection.ClearReferencePoint();
2316
2317 return cancelled;
2318}
2319
2320
2322{
2323 bool cancelled = false;
2324 m_multiple = true;
2326 getView()->Add( &area );
2327 getView()->SetVisible( &area, true );
2328 getViewControls()->SetAutoPan( true );
2329
2330 SHAPE_LINE_CHAIN points;
2331 points.SetClosed( true );
2332
2334 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
2335
2336 while( TOOL_EVENT* evt = Wait() )
2337 {
2338 double shapeArea = area.GetPoly().Area( false );
2339 bool isClockwise = shapeArea > 0 ? true : false;
2340
2341 if( getView()->IsMirroredX() && shapeArea != 0 )
2342 isClockwise = !isClockwise;
2343
2344 if( isClockwise )
2345 {
2346 selectionMode = SELECTION_MODE::INSIDE_LASSO;
2347 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_WINDOW );
2348 }
2349 else
2350 {
2351 selectionMode = SELECTION_MODE::TOUCHING_LASSO;
2352 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
2353 }
2354
2355 if( evt->IsCancelInteractive() || evt->IsActivate() )
2356 {
2357 cancelled = true;
2358 break;
2359 }
2360 else if( evt->IsDrag( BUT_LEFT )
2361 || evt->IsClick( BUT_LEFT )
2362 || evt->IsAction( &ACTIONS::cursorClick ) )
2363 {
2364 points.Append( evt->Position() );
2365 }
2366 else if( evt->IsDblClick( BUT_LEFT )
2367 || evt->IsAction( &ACTIONS::cursorDblClick )
2368 || evt->IsAction( &ACTIONS::finishInteractive ) )
2369 {
2370 area.GetPoly().GenerateBBoxCache();
2371 SelectMultiple( area, m_drag_subtractive, false );
2372 break;
2373 }
2374 else if( evt->IsAction( &ACTIONS::doDelete )
2375 || evt->IsAction( &ACTIONS::undo ) )
2376 {
2377 if( points.GetPointCount() > 0 )
2378 {
2380 points.Remove( points.GetPointCount() - 1 );
2381 }
2382 }
2383 else
2384 {
2385 passEvent( evt, allowedActions );
2386 }
2387
2388 if( points.PointCount() > 0 )
2389 {
2391 {
2392 if( m_selection.GetSize() > 0 )
2393 {
2394 ClearSelection( true );
2395 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2396 }
2397 }
2398 }
2399
2400 area.SetPoly( points );
2401 area.GetPoly().Append( m_toolMgr->GetMousePosition() );
2404 area.SetExclusiveOr( false );
2405 area.SetMode( selectionMode );
2406 getView()->Update( &area );
2407 }
2408
2409 getViewControls()->SetAutoPan( false );
2410 getView()->SetVisible( &area, false );
2411 getView()->Remove( &area );
2412 m_multiple = false;
2413
2414 if( !cancelled )
2415 m_selection.ClearReferencePoint();
2416
2417 return cancelled;
2418}
2419
2420
2422 bool aExclusiveOr )
2423{
2424 KIGFX::VIEW* view = getView();
2425
2426 SELECTION_MODE selectionMode = aArea.GetMode();
2427 bool containedMode = ( selectionMode == SELECTION_MODE::INSIDE_RECTANGLE
2428 || selectionMode == SELECTION_MODE::INSIDE_LASSO );
2429 bool boxMode = ( selectionMode == SELECTION_MODE::INSIDE_RECTANGLE
2430 || selectionMode == SELECTION_MODE::TOUCHING_RECTANGLE );
2431
2432 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> candidates;
2433 BOX2I selectionRect = aArea.ViewBBox();
2434 view->Query( selectionRect, candidates );
2435
2436 std::set<SCH_ITEM*> uniqueCandidates;
2437
2438 for( const auto& [viewItem, layer] : candidates )
2439 {
2440 if( viewItem->IsSCH_ITEM() )
2441 uniqueCandidates.insert( static_cast<SCH_ITEM*>( viewItem ) );
2442 }
2443
2444 for( KIGFX::VIEW_ITEM* item : uniqueCandidates )
2445 {
2446 if( SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item ) )
2447 {
2448 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
2449 {
2450 if( boxMode ? selectionRect.Intersects( pin->GetBoundingBox() )
2451 : KIGEOM::BoxHitTest( aArea.GetPoly(), pin->GetBoundingBox(), true ) )
2452 uniqueCandidates.insert( pin );
2453 }
2454 }
2455 else if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item ) )
2456 {
2457 for( SCH_PIN* pin : symbol->GetPins() )
2458 {
2459 if( boxMode ? selectionRect.Intersects( pin->GetBoundingBox() )
2460 : KIGEOM::BoxHitTest( aArea.GetPoly(), pin->GetBoundingBox(), true ) )
2461 uniqueCandidates.insert( pin );
2462 }
2463
2464 for( SCH_FIELD& field : symbol->GetFields() )
2465 {
2466 if( field.IsVisible()
2467 && ( boxMode ? selectionRect.Intersects( field.GetBoundingBox() )
2468 : KIGEOM::BoxHitTest( aArea.GetPoly(), field.GetBoundingBox(), true ) ) )
2469 {
2470 uniqueCandidates.insert( &field );
2471 }
2472 }
2473 }
2474 }
2475
2476 SCH_COLLECTOR collector;
2477 SCH_COLLECTOR pinsCollector;
2478 std::set<EDA_ITEM*> group_items;
2479
2480 for( EDA_ITEM* item : m_frame->GetScreen()->Items().OfType( SCH_GROUP_T ) )
2481 {
2482 SCH_GROUP* group = static_cast<SCH_GROUP*>( item );
2483
2484 if( m_enteredGroup == group )
2485 continue;
2486
2487 std::unordered_set<EDA_ITEM*>& newset = group->GetItems();
2488
2489 auto boxContained =
2490 [&]( const BOX2I& aBox )
2491 {
2492 return boxMode ? selectionRect.Contains( aBox )
2493 : KIGEOM::BoxHitTest( aArea.GetPoly(), aBox, true );
2494 };
2495
2496 if( containedMode && boxContained( group->GetBoundingBox() ) && newset.size() )
2497 {
2498 for( EDA_ITEM* group_item : newset )
2499 {
2500 if( !group_item->IsSCH_ITEM() )
2501 continue;
2502
2503 if( Selectable( static_cast<SCH_ITEM*>( group_item ) ) )
2504 collector.Append( group_item );
2505 }
2506 }
2507
2508 for( EDA_ITEM* group_item : newset )
2509 group_items.emplace( group_item );
2510 }
2511
2512 auto hitTest =
2513 [&]( SCH_ITEM* aItem )
2514 {
2515 return boxMode ? aItem->HitTest( selectionRect, containedMode )
2516 : aItem->HitTest( aArea.GetPoly(), containedMode );
2517 };
2518
2519 for( SCH_ITEM* item : uniqueCandidates )
2520 {
2521 if( Selectable( item ) && ( hitTest( item ) || item->Type() == SCH_LINE_T )
2522 && ( !containedMode || !group_items.count( item ) ) )
2523 {
2524 if( item->Type() == SCH_PIN_T && !m_isSymbolEditor )
2525 pinsCollector.Append( item );
2526 else
2527 collector.Append( item );
2528 }
2529 }
2530
2531 filterCollectedItems( collector, true );
2532 filterCollectorForHierarchy( collector, true );
2533
2534 if( collector.GetCount() == 0 )
2535 {
2536 collector = pinsCollector;
2537 filterCollectedItems( collector, true );
2538 filterCollectorForHierarchy( collector, true );
2539 }
2540
2541 std::sort( collector.begin(), collector.end(),
2542 []( EDA_ITEM* a, EDA_ITEM* b )
2543 {
2544 VECTOR2I aPos = a->GetPosition();
2545 VECTOR2I bPos = b->GetPosition();
2546
2547 if( aPos.y == bPos.y )
2548 return aPos.x < bPos.x;
2549
2550 return aPos.y < bPos.y;
2551 } );
2552
2553 bool anyAdded = false;
2554 bool anySubtracted = false;
2555
2556 auto selectItem =
2557 [&]( EDA_ITEM* aItem, EDA_ITEM_FLAGS flags )
2558 {
2559 if( aSubtractive || ( aExclusiveOr && aItem->IsSelected() ) )
2560 {
2561 if( aExclusiveOr )
2562 aItem->XorFlags( flags );
2563 else
2564 aItem->ClearFlags( flags );
2565
2566 if( !aItem->HasFlag( STARTPOINT ) && !aItem->HasFlag( ENDPOINT ) )
2567 {
2568 unselect( aItem );
2569 anySubtracted = true;
2570 }
2571
2572 if( flags && !anySubtracted )
2573 getView()->Update( aItem );
2574 }
2575 else
2576 {
2577 aItem->SetFlags( flags );
2578 select( aItem );
2579 anyAdded = true;
2580 }
2581 };
2582
2583 std::vector<EDA_ITEM*> flaggedItems;
2584
2585 auto shapeContains =
2586 [&]( const VECTOR2I& aPoint )
2587 {
2588 return boxMode ? selectionRect.Contains( aPoint )
2589 : aArea.GetPoly().PointInside( aPoint );
2590 };
2591
2592 for( EDA_ITEM* item : collector )
2593 {
2594 EDA_ITEM_FLAGS flags = 0;
2595
2596 item->SetFlags( SELECTION_CANDIDATE );
2597 flaggedItems.push_back( item );
2598
2599 if( m_frame->GetRenderSettings()->m_ShowPinsElectricalType )
2600 item->SetFlags( SHOW_ELEC_TYPE );
2601
2602 if( item->Type() == SCH_LINE_T )
2603 {
2604 SCH_LINE* line = static_cast<SCH_LINE*>( item );
2605 bool hits = false;
2606
2607 if( boxMode )
2608 hits = line->HitTest( selectionRect, false );
2609 else
2610 hits = line->HitTest( aArea.GetPoly(), false );
2611
2612 if( ( !containedMode && hits )
2613 || ( shapeContains( line->GetEndPoint() ) && shapeContains( line->GetStartPoint() ) ) )
2614 {
2615 flags |= STARTPOINT | ENDPOINT;
2616 }
2617 else if( containedMode )
2618 {
2619 if( shapeContains( line->GetStartPoint() ) && line->IsStartDangling() )
2620 flags |= STARTPOINT;
2621
2622 if( shapeContains( line->GetEndPoint() ) && line->IsEndDangling() )
2623 flags |= ENDPOINT;
2624 }
2625
2626 if( flags & ( STARTPOINT | ENDPOINT ) )
2627 selectItem( item, flags );
2628 }
2629 else
2630 selectItem( item, flags );
2631
2632 item->ClearFlags( SHOW_ELEC_TYPE );
2633 }
2634
2635 for( EDA_ITEM* item : pinsCollector )
2636 {
2637 if( m_frame->GetRenderSettings()->m_ShowPinsElectricalType )
2638 item->SetFlags( SHOW_ELEC_TYPE );
2639
2640 // If the pin lives inside a group that is already being selected, don't also select the pin.
2641 if( EDA_GROUP* group =
2643 {
2644 if( collector.HasItem( group->AsEdaItem() ) )
2645 {
2646 item->ClearFlags( SHOW_ELEC_TYPE );
2647 continue;
2648 }
2649 }
2650
2651 if( Selectable( item ) && itemPassesFilter( item, nullptr )
2652 && !item->GetParent()->HasFlag( SELECTION_CANDIDATE ) && hitTest( static_cast<SCH_ITEM*>( item ) ) )
2653 {
2654 selectItem( item, 0 );
2655 }
2656
2657 item->ClearFlags( SHOW_ELEC_TYPE );
2658 }
2659
2660 for( EDA_ITEM* item : flaggedItems )
2661 item->ClearFlags( SELECTION_CANDIDATE );
2662
2663 m_selection.SetIsHover( false );
2664
2665 if( anyAdded )
2666 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2667 else if( anySubtracted )
2668 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2669}
2670
2671
2673 bool aMultiselect ) const
2674{
2675 std::unordered_set<EDA_ITEM*> toAdd;
2676
2677 // Set SELECTION_CANDIDATE on all parents which are included in the GENERAL_COLLECTOR. This
2678 // algorithm is O(3n), whereas checking for the parent inclusion could potentially be O(n^2).
2679 for( int j = 0; j < aCollector.GetCount(); j++ )
2680 {
2681 if( aCollector[j]->GetParent() )
2682 aCollector[j]->GetParent()->ClearFlags( SELECTION_CANDIDATE );
2683
2684 if( aCollector[j]->GetParentSymbol() )
2685 aCollector[j]->GetParentSymbol()->ClearFlags( SELECTION_CANDIDATE );
2686 }
2687
2688 if( aMultiselect )
2689 {
2690 for( int j = 0; j < aCollector.GetCount(); j++ )
2691 aCollector[j]->SetFlags( SELECTION_CANDIDATE );
2692 }
2693
2694 for( int j = 0; j < aCollector.GetCount(); )
2695 {
2696 SCH_ITEM* item = aCollector[j];
2697 SYMBOL* sym = item->GetParentSymbol();
2698 SCH_ITEM* start = item;
2699
2700 if( !m_isSymbolEditor && sym )
2701 start = sym;
2702
2703 // If a group is entered, disallow selections of objects outside the group.
2705 {
2706 aCollector.Remove( item );
2707 continue;
2708 }
2709
2710 // If any element is a member of a group, replace those elements with the top containing
2711 // group.
2713 {
2714 if( top->AsEdaItem() != item )
2715 {
2716 toAdd.insert( top->AsEdaItem() );
2717 top->AsEdaItem()->SetFlags( SELECTION_CANDIDATE );
2718
2719 aCollector.Remove( item );
2720 continue;
2721 }
2722 }
2723
2724 // Symbols are a bit easier as they can't be nested.
2725 if( sym && ( sym->GetFlags() & SELECTION_CANDIDATE ) )
2726 {
2727 // Remove children of selected items
2728 aCollector.Remove( item );
2729 continue;
2730 }
2731
2732 ++j;
2733 }
2734
2735 for( EDA_ITEM* item : toAdd )
2736 {
2737 if( !aCollector.HasItem( item ) )
2738 aCollector.Append( item );
2739 }
2740}
2741
2742
2744{
2745 getView()->Update( &m_selection );
2747
2748 return 0;
2749}
2750
2751
2753{
2754 for( SCH_TABLECELL* cell : aTable->GetCells() )
2755 {
2756 if( cell->IsSelected() )
2757 cell->SetFlags( SELECTION_CANDIDATE );
2758 else
2759 cell->ClearFlags( SELECTION_CANDIDATE );
2760 }
2761}
2762
2764{
2765 BOX2I selectionRect( start, end );
2766 selectionRect.Normalize();
2767
2768 auto wasSelected = []( EDA_ITEM* aItem )
2769 {
2770 return ( aItem->GetFlags() & SELECTION_CANDIDATE ) > 0;
2771 };
2772
2773 for( SCH_TABLECELL* cell : aTable->GetCells() )
2774 {
2775 bool doSelect = false;
2776
2777 if( cell->HitTest( selectionRect, false ) )
2778 {
2779 if( m_subtractive )
2780 doSelect = false;
2781 else if( m_exclusive_or )
2782 doSelect = !wasSelected( cell );
2783 else
2784 doSelect = true;
2785 }
2786 else if( wasSelected( cell ) )
2787 {
2788 doSelect = m_additive || m_subtractive || m_exclusive_or;
2789 }
2790
2791 if( doSelect && !cell->IsSelected() )
2792 select( cell );
2793 else if( !doSelect && cell->IsSelected() )
2794 unselect( cell );
2795 }
2796}
2797
2799{
2800 bool cancelled = false;
2801 m_multiple = true;
2802
2803 InitializeSelectionState( aTable );
2804
2805 while( TOOL_EVENT* evt = Wait() )
2806 {
2807 if( evt->IsCancelInteractive() || evt->IsActivate() )
2808 {
2809 cancelled = true;
2810 break;
2811 }
2812 else if( evt->IsDrag( BUT_LEFT ) )
2813 {
2814 getViewControls()->SetAutoPan( true );
2815 SelectCellsBetween( evt->DragOrigin(), evt->Position() - evt->DragOrigin(), aTable );
2816 }
2817 else if( evt->IsMouseUp( BUT_LEFT ) )
2818 {
2819 m_selection.SetIsHover( false );
2820
2821 bool anyAdded = false;
2822 bool anySubtracted = false;
2823
2824 for( SCH_TABLECELL* cell : aTable->GetCells() )
2825 {
2826 if( cell->IsSelected() && ( cell->GetFlags() & SELECTION_CANDIDATE ) <= 0 )
2827 anyAdded = true;
2828 else if( ( cell->GetFlags() & SELECTION_CANDIDATE ) > 0 && !cell->IsSelected() )
2829 anySubtracted = true;
2830 }
2831
2832 if( anyAdded )
2833 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2834 if( anySubtracted )
2835 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2836
2837 break;
2838 }
2839 else
2840 {
2841 for( int i = 0; allowedActions[i]; ++i )
2842 {
2843 if( evt->IsAction( allowedActions[i] ) )
2844 {
2845 evt->SetPassEvent();
2846 break;
2847 }
2848 }
2849 }
2850 }
2851
2852 getViewControls()->SetAutoPan( false );
2853
2854 m_multiple = false;
2855
2856 if( !cancelled )
2857 m_selection.ClearReferencePoint();
2858
2859 return cancelled;
2860}
2861
2862
2864{
2865 SCH_COLLECTOR collector;
2866
2867 //TODO(snh): Reimplement after exposing KNN interface
2868 int pixelThreshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
2869 int gridThreshold = KiROUND( getView()->GetGAL()->GetGridSize().EuclideanNorm() );
2870 int thresholdMax = std::max( pixelThreshold, gridThreshold );
2871
2872 for( int threshold : { 0, thresholdMax/4, thresholdMax/2, thresholdMax } )
2873 {
2874 collector.m_Threshold = threshold;
2875 collector.Collect( m_frame->GetScreen(), connectedTypes, aPosition );
2876
2877 if( collector.GetCount() > 0 )
2878 break;
2879 }
2880
2881 return collector.GetCount() ? collector[ 0 ] : nullptr;
2882}
2883
2884
2886{
2887 VECTOR2I cursorPos = getViewControls()->GetCursorPosition( false );
2888
2889 SelectPoint( cursorPos, connectedTypes );
2890 return 0;
2891}
2892
2893
2895{
2896 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
2897
2898 if( m_isSymbolEditor || m_isSymbolViewer || !editFrame )
2899 return {};
2900
2901 CONNECTION_GRAPH* graph = editFrame->Schematic().ConnectionGraph();
2902
2903 if( !graph )
2904 return {};
2905
2906 SCH_SHEET_PATH& currentSheet = editFrame->GetCurrentSheet();
2907 std::vector<SCH_ITEM*> startItems;
2908 std::set<SCH_ITEM*> added;
2909
2910 // Build the list of starting items for the connection graph traversal
2911 for( auto item : aItems )
2912 {
2913 if( !item->IsSCH_ITEM() )
2914 continue;
2915
2916 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
2917
2918 // If we're a symbol, start from all its pins
2919 if( schItem->Type() == SCH_SYMBOL_T )
2920 {
2921 for( SCH_PIN* pin : static_cast<SCH_SYMBOL*>( schItem )->GetPins( &currentSheet ) )
2922 {
2923 if( pin )
2924 startItems.push_back( pin );
2925 }
2926 }
2927 else if( schItem->IsConnectable() )
2928 {
2929 startItems.push_back( schItem );
2930 }
2931 }
2932
2933 if( startItems.empty() )
2934 return {};
2935
2936 std::deque<SCH_ITEM*> queue;
2937 std::unordered_set<SCH_ITEM*> visited;
2938
2939 auto enqueue = [&]( SCH_ITEM* aItem )
2940 {
2941 if( !aItem )
2942 return;
2943
2944 if( visited.insert( aItem ).second )
2945 queue.push_back( aItem );
2946 };
2947
2948 for( SCH_ITEM* item : startItems )
2949 enqueue( item );
2950
2951 while( !queue.empty() )
2952 {
2953 SCH_ITEM* item = queue.front();
2954 queue.pop_front();
2955
2956 if( SCH_PIN* pin = dynamic_cast<SCH_PIN*>( item ) )
2957 {
2958 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParent() );
2959
2960 if( symbol && Selectable( symbol ) && itemPassesFilter( symbol, nullptr ) && !symbol->IsSelected() )
2961 added.insert( symbol );
2962 }
2963
2964 const SCH_ITEM_VEC& neighbors = item->ConnectedItems( currentSheet );
2965
2966 for( SCH_ITEM* neighbor : neighbors )
2967 {
2968 if( !neighbor )
2969 continue;
2970
2971 if( neighbor->Type() == SCH_SYMBOL_T )
2972 {
2973 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( neighbor );
2974
2975 if( Selectable( symbol ) && itemPassesFilter( symbol, nullptr ) && !symbol->IsSelected() )
2976 added.insert( symbol );
2977
2978 continue;
2979 }
2980
2981 enqueue( neighbor );
2982 }
2983
2984 if( !Selectable( item ) || !itemPassesFilter( item, nullptr ) )
2985 continue;
2986
2987 added.insert( item );
2988 }
2989
2990 return added;
2991}
2992
2993
2995{
2996 std::set<SCH_ITEM*> added;
2997
2998 for( auto item : aItems )
2999 {
3000 if( !item->IsSCH_ITEM() )
3001 continue;
3002
3003 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
3004
3005 std::set<SCH_ITEM*> conns = m_frame->GetScreen()->MarkConnections( schItem, schItem->IsConnectable() );
3006
3007 // Make sure we don't add things the user has disabled in the selection filter
3008 for( SCH_ITEM* connItem : conns )
3009 {
3010 if( !Selectable( connItem ) || !itemPassesFilter( connItem, nullptr ) )
3011 continue;
3012
3013 added.insert( connItem );
3014 }
3015 }
3016
3017 return added;
3018}
3019
3020
3022{
3024
3025 if( m_selection.Empty() )
3026 return 0;
3027
3028 SCH_SELECTION connectableSelection;
3029 SCH_SELECTION graphicalSelection;
3030
3031 // We need to filter the selection into connectable items (wires, pins, symbols)
3032 // and non-connectable items (shapes, unconnectable lines) for processing
3033 // with the graph or by the graphical are-endpoints-touching method.
3034 for( EDA_ITEM* selItem : originalSelection.GetItems() )
3035 {
3036 if( !selItem->IsSCH_ITEM() )
3037 continue;
3038
3039 SCH_ITEM* item = static_cast<SCH_ITEM*>( selItem );
3040
3041 if( item->Type() == SCH_LINE_T && !item->IsConnectable() )
3042 graphicalSelection.Add( item );
3043 else if( item->Type() == SCH_SHAPE_T )
3044 graphicalSelection.Add( item );
3045 else
3046 connectableSelection.Add( item );
3047 }
3048
3049 ClearSelection( true );
3050
3051 std::set<SCH_ITEM*> graphAdded;
3052 std::set<SCH_ITEM*> graphicalAdded;
3053
3054 if( !connectableSelection.Empty() )
3055 graphAdded = expandConnectionWithGraph( connectableSelection );
3056
3057 if( !graphicalSelection.Empty() )
3058 graphicalAdded = expandConnectionGraphically( graphicalSelection );
3059
3060 // For whatever reason, the connection graph isn't working (e.g. in symbol editor )
3061 // so fall back to graphical expansion for those items if nothing was added.
3062 if( graphAdded.empty() && !connectableSelection.Empty() )
3063 {
3064 SCH_SELECTION combinedSelection = connectableSelection;
3065
3066 for( EDA_ITEM* selItem : graphicalSelection.GetItems() )
3067 combinedSelection.Add( selItem );
3068
3069 graphicalSelection = combinedSelection;
3070 }
3071
3072 graphicalAdded = expandConnectionGraphically( graphicalSelection );
3073
3074 auto smartAddToSel = [&]( EDA_ITEM* aItem )
3075 {
3076 AddItemToSel( aItem, true );
3077
3078 if( aItem->Type() == SCH_LINE_T )
3079 aItem->SetFlags( STARTPOINT | ENDPOINT );
3080 };
3081
3082 // Add everything to the selection, including the original selection
3083 for( auto item : graphAdded )
3084 smartAddToSel( item );
3085
3086 for( auto item : graphicalAdded )
3087 smartAddToSel( item );
3088
3089 for( auto item : originalSelection )
3090 smartAddToSel( item );
3091
3092 m_selection.SetIsHover( originalSelection.IsHover() );
3093
3094 if( originalSelection.HasReferencePoint() )
3095 m_selection.SetReferencePoint( originalSelection.GetReferencePoint() );
3096 else
3097 m_selection.ClearReferencePoint();
3098
3099 getView()->Update( &m_selection );
3100
3101 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3102
3103 return 0;
3104}
3105
3106
3108{
3109 std::set<std::pair<SCH_TABLE*, int>> columns;
3110 bool added = false;
3111
3112 for( EDA_ITEM* item : m_selection )
3113 {
3114 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( item ) )
3115 {
3116 SCH_TABLE* table = static_cast<SCH_TABLE*>( cell->GetParent() );
3117 columns.insert( std::make_pair( table, cell->GetColumn() ) );
3118 }
3119 }
3120
3121 for( auto& [ table, col ] : columns )
3122 {
3123 for( int row = 0; row < table->GetRowCount(); ++row )
3124 {
3125 SCH_TABLECELL* cell = table->GetCell( row, col );
3126
3127 if( !cell->IsSelected() )
3128 {
3129 select( table->GetCell( row, col ) );
3130 added = true;
3131 }
3132 }
3133 }
3134
3135 if( added )
3136 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3137
3138 return 0;
3139}
3140
3141
3143{
3144 std::set<std::pair<SCH_TABLE*, int>> rows;
3145 bool added = false;
3146
3147 for( EDA_ITEM* item : m_selection )
3148 {
3149 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( item ) )
3150 {
3151 SCH_TABLE* table = static_cast<SCH_TABLE*>( cell->GetParent() );
3152 rows.insert( std::make_pair( table, cell->GetRow() ) );
3153 }
3154 }
3155
3156 for( auto& [ table, row ] : rows )
3157 {
3158 for( int col = 0; col < table->GetColCount(); ++col )
3159 {
3160 SCH_TABLECELL* cell = table->GetCell( row, col );
3161
3162 if( !cell->IsSelected() )
3163 {
3164 select( table->GetCell( row, col ) );
3165 added = true;
3166 }
3167 }
3168 }
3169
3170 if( added )
3171 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3172
3173 return 0;
3174}
3175
3176
3178{
3179 std::set<SCH_TABLE*> tables;
3180 bool added = false;
3181
3182 for( EDA_ITEM* item : m_selection )
3183 {
3184 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( item ) )
3185 tables.insert( static_cast<SCH_TABLE*>( cell->GetParent() ) );
3186 }
3187
3189
3190 for( SCH_TABLE* table : tables )
3191 {
3192 if( !table->IsSelected() )
3193 {
3194 select( table );
3195 added = true;
3196 }
3197 }
3198
3199 if( added )
3200 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3201
3202 return 0;
3203}
3204
3205
3207{
3209 return 0;
3210}
3211
3212
3214{
3215 if( aBBox.GetWidth() == 0 )
3216 return;
3217
3218 BOX2I bbox = aBBox;
3219 bbox.Normalize();
3220
3221 VECTOR2I bbSize = bbox.Inflate( KiROUND( bbox.GetWidth() * 0.2f ) ).GetSize();
3222 VECTOR2D screenSize = getView()->GetViewport().GetSize();
3223
3224 // This code tries to come up with a zoom factor that doesn't simply zoom in to the cross
3225 // probed symbol, but instead shows a reasonable amount of the circuit around it to provide
3226 // context. This reduces the need to manually change the zoom because it's too close.
3227
3228 // Using the default text height as a constant to compare against, use the height of the
3229 // bounding box of visible items for a footprint to figure out if this is a big symbol (like
3230 // a processor) or a small symbol (like a resistor). This ratio is not useful by itself as a
3231 // scaling factor. It must be "bent" to provide good scaling at varying symbol sizes. Bigger
3232 // symbols need less scaling than small ones.
3233 double currTextHeight = schIUScale.MilsToIU( DEFAULT_TEXT_SIZE );
3234
3235 double compRatio = bbSize.y / currTextHeight; // Ratio of symbol to text height
3236 double compRatioBent = 1.0;
3237
3238 // LUT to scale zoom ratio to provide reasonable schematic context. Must work with symbols
3239 // of varying sizes (e.g. 0402 package and 200 pin BGA).
3240 // Each entry represents a compRatio (symbol height / default text height) and an amount to
3241 // scale by.
3242 std::vector<std::pair<double, double>> lut{ { 1.25, 16 },
3243 { 2.5, 12 },
3244 { 5, 8 },
3245 { 6, 6 },
3246 { 10, 4 },
3247 { 20, 2 },
3248 { 40, 1.5 },
3249 { 100, 1 } };
3250
3251 std::vector<std::pair<double, double>>::iterator it;
3252
3253 // Large symbol default is last LUT entry (1:1).
3254 compRatioBent = lut.back().second;
3255
3256 // Use LUT to do linear interpolation of "compRatio" within "first", then use that result to
3257 // linearly interpolate "second" which gives the scaling factor needed.
3258 if( compRatio >= lut.front().first )
3259 {
3260 for( it = lut.begin(); it < lut.end() - 1; ++it )
3261 {
3262 if( it->first <= compRatio && next( it )->first >= compRatio )
3263 {
3264 double diffx = compRatio - it->first;
3265 double diffn = next( it )->first - it->first;
3266
3267 compRatioBent = it->second + ( next( it )->second - it->second ) * diffx / diffn;
3268 break; // We have our interpolated value
3269 }
3270 }
3271 }
3272 else
3273 {
3274 compRatioBent = lut.front().second; // Small symbol default is first entry
3275 }
3276
3277 // This is similar to the original KiCad code that scaled the zoom to make sure symbols were
3278 // visible on screen. It's simply a ratio of screen size to symbol size, and its job is to
3279 // zoom in to make the component fullscreen. Earlier in the code the symbol BBox is given a
3280 // 20% margin to add some breathing room. We compare the height of this enlarged symbol bbox
3281 // to the default text height. If a symbol will end up with the sides clipped, we adjust
3282 // later to make sure it fits on screen.
3283 screenSize.x = std::max( 10.0, screenSize.x );
3284 screenSize.y = std::max( 10.0, screenSize.y );
3285 double ratio = std::max( -1.0, fabs( bbSize.y / screenSize.y ) );
3286
3287 // Original KiCad code for how much to scale the zoom
3288 double kicadRatio = std::max( fabs( bbSize.x / screenSize.x ),
3289 fabs( bbSize.y / screenSize.y ) );
3290
3291 // If the width of the part we're probing is bigger than what the screen width will be after
3292 // the zoom, then punt and use the KiCad zoom algorithm since it guarantees the part's width
3293 // will be encompassed within the screen.
3294 if( bbSize.x > screenSize.x * ratio * compRatioBent )
3295 {
3296 // Use standard KiCad zoom for parts too wide to fit on screen/
3297 ratio = kicadRatio;
3298 compRatioBent = 1.0; // Reset so we don't modify the "KiCad" ratio
3299 wxLogTrace( "CROSS_PROBE_SCALE",
3300 "Part TOO WIDE for screen. Using normal KiCad zoom ratio: %1.5f", ratio );
3301 }
3302
3303 // Now that "compRatioBent" holds our final scaling factor we apply it to the original
3304 // fullscreen zoom ratio to arrive at the final ratio itself.
3305 ratio *= compRatioBent;
3306
3307 bool alwaysZoom = false; // DEBUG - allows us to minimize zooming or not
3308
3309 // Try not to zoom on every cross-probe; it gets very noisy
3310 if( ( ratio < 0.5 || ratio > 1.0 ) || alwaysZoom )
3311 getView()->SetScale( getView()->GetScale() / ratio );
3312}
3313
3314
3315void SCH_SELECTION_TOOL::SyncSelection( const std::optional<SCH_SHEET_PATH>& targetSheetPath,
3316 SCH_ITEM* focusItem, const std::vector<SCH_ITEM*>& items )
3317{
3318 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
3319
3320 if( !editFrame )
3321 return;
3322
3323 double targetZoom = 0.0;
3324 VECTOR2D targetCenter;
3325 bool targetZoomValid = false;
3326 bool changedSheet = false;
3327
3328 if( targetSheetPath )
3329 {
3330 SCH_SHEET_PATH path = targetSheetPath.value();
3331
3332 if( SCH_SCREEN* screen = path.LastScreen() )
3333 {
3334 targetZoom = screen->m_LastZoomLevel;
3335 targetCenter = screen->m_ScrollCenter;
3336 targetZoomValid = screen->IsZoomInitialized();
3337 }
3338
3339 if( path != editFrame->Schematic().CurrentSheet() )
3340 {
3341 m_frame->GetToolManager()->RunAction<SCH_SHEET_PATH*>( SCH_ACTIONS::changeSheet, &path );
3342 changedSheet = true;
3343 }
3344 }
3345
3346 if( changedSheet && targetZoomValid && !m_frame->eeconfig()->m_CrossProbing.zoom_to_fit )
3347 {
3348 getView()->SetScale( targetZoom );
3349 getView()->SetCenter( targetCenter );
3350 }
3351
3352 ClearSelection( items.size() > 0 ? true /*quiet mode*/ : false );
3353
3354 // Perform individual selection of each item before processing the event.
3355 for( SCH_ITEM* item : items )
3356 {
3357 SCH_ITEM* parent = dynamic_cast<SCH_ITEM*>( item->GetParent() );
3358
3359 // Make sure we only select items on the current screen
3360 if( m_frame->GetScreen()->CheckIfOnDrawList( item )
3361 || ( parent && m_frame->GetScreen()->CheckIfOnDrawList( parent ) ) )
3362 {
3363 select( item );
3364 }
3365 }
3366
3367 BOX2I bbox = m_selection.GetBoundingBox();
3368
3369 if( bbox.GetWidth() != 0 && bbox.GetHeight() != 0 )
3370 {
3371 if( m_frame->eeconfig()->m_CrossProbing.center_on_items )
3372 {
3373 if( m_frame->eeconfig()->m_CrossProbing.zoom_to_fit )
3374 ZoomFitCrossProbeBBox( bbox );
3375
3376 editFrame->FocusOnItem( focusItem );
3377
3378 if( !focusItem )
3379 editFrame->FocusOnLocation( bbox.Centre() );
3380 }
3381 }
3382
3383 if( m_selection.Size() > 0 )
3384 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3385}
3386
3387
3389{
3390 m_selection.Clear();
3391
3392 bool enteredGroupFound = false;
3393
3394 if( m_isSymbolEditor )
3395 {
3396 LIB_SYMBOL* start = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
3397
3398 for( SCH_ITEM& item : start->GetDrawItems() )
3399 {
3400 if( item.IsSelected() )
3401 select( &item );
3402
3403 if( item.Type() == SCH_GROUP_T )
3404 {
3405 if( &item == m_enteredGroup )
3406 {
3407 item.SetFlags( ENTERED );
3408 enteredGroupFound = true;
3409 }
3410 else
3411 {
3412 item.ClearFlags( ENTERED );
3413 }
3414 }
3415 }
3416 }
3417 else
3418 {
3419 for( SCH_ITEM* item : m_frame->GetScreen()->Items() )
3420 {
3421 // If the field and symbol are selected, only use the symbol
3422 if( item->IsSelected() )
3423 {
3424 select( item );
3425 }
3426 else
3427 {
3428 item->RunOnChildren(
3429 [&]( SCH_ITEM* aChild )
3430 {
3431 if( aChild->IsSelected() )
3432 select( aChild );
3433 },
3435 }
3436
3437 if( item->Type() == SCH_GROUP_T )
3438 {
3439 if( item == m_enteredGroup )
3440 {
3441 item->SetFlags( ENTERED );
3442 enteredGroupFound = true;
3443 }
3444 else
3445 {
3446 item->ClearFlags( ENTERED );
3447 }
3448 }
3449 }
3450 }
3451
3453
3454 if( !enteredGroupFound )
3455 {
3456 m_enteredGroupOverlay.Clear();
3457 m_enteredGroup = nullptr;
3458 }
3459
3460 // Inform other potentially interested tools
3461 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3462}
3463
3464
3465bool SCH_SELECTION_TOOL::Selectable( const EDA_ITEM* aItem, const VECTOR2I* aPos,
3466 bool checkVisibilityOnly ) const
3467{
3468 // NOTE: in the future this is where Eeschema layer/itemtype visibility will be handled
3469
3470 SYMBOL_EDIT_FRAME* symEditFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
3471
3472 // Do not allow selection of anything except fields when the current symbol in the symbol
3473 // editor is a derived symbol.
3474 if( symEditFrame && symEditFrame->IsSymbolAlias() && aItem->Type() != SCH_FIELD_T )
3475 return false;
3476
3477 switch( aItem->Type() )
3478 {
3479 case SCH_PIN_T:
3480 {
3481 const SCH_PIN* pin = static_cast<const SCH_PIN*>( aItem );
3482
3483 if( symEditFrame )
3484 {
3485 if( pin->GetUnit() && pin->GetUnit() != symEditFrame->GetUnit() )
3486 return false;
3487
3488 if( pin->GetBodyStyle() && pin->GetBodyStyle() != symEditFrame->GetBodyStyle() )
3489 return false;
3490 }
3491
3492 if( !pin->IsVisible() && !m_frame->GetShowAllPins() )
3493 return false;
3494
3495 if( !m_filter.pins )
3496 {
3497 // Pin anchors have to be allowed for auto-starting wires.
3498 if( aPos )
3499 {
3501 GRID_HELPER_GRIDS pinGrid = grid.GetItemGrid( pin );
3502
3503 if( pin->IsPointClickableAnchor( grid.BestSnapAnchor( *aPos, pinGrid ) ) )
3504 return true;
3505 }
3506
3507 return false;
3508 }
3509
3510 break;
3511 }
3512
3514 if( !m_frame->eeconfig()->m_Appearance.show_directive_labels )
3515 return false;
3516
3517 break;
3518
3519 case LIB_SYMBOL_T: // In symbol_editor we do not want to select the symbol itself.
3520 return false;
3521
3522 case SCH_FIELD_T: // SCH_FIELD objects are not unit/body-style-specific.
3523 {
3524 const SCH_FIELD* field = static_cast<const SCH_FIELD*>( aItem );
3525
3526 if( !field->IsVisible() && !( symEditFrame && symEditFrame->GetShowInvisibleFields() ) )
3527 return false;
3528
3529 break;
3530 }
3531
3532 case SCH_SHAPE_T:
3533 case SCH_TEXT_T:
3534 case SCH_TEXTBOX_T:
3535 if( symEditFrame )
3536 {
3537 const SCH_ITEM* sch_item = static_cast<const SCH_ITEM*>( aItem );
3538
3539 if( sch_item->GetUnit() && sch_item->GetUnit() != symEditFrame->GetUnit() )
3540 return false;
3541
3542 if( sch_item->GetBodyStyle() && sch_item->GetBodyStyle() != symEditFrame->GetBodyStyle() )
3543 return false;
3544 }
3545
3546 break;
3547
3548 case SCH_MARKER_T: // Always selectable
3549 return true;
3550
3551 case SCH_TABLECELL_T:
3552 {
3553 const SCH_TABLECELL* cell = static_cast<const SCH_TABLECELL*>( aItem );
3554
3555 if( cell->GetColSpan() == 0 || cell->GetRowSpan() == 0 )
3556 return false;
3557
3558 break;
3559 }
3560
3561 case NOT_USED: // Things like CONSTRUCTION_GEOM that aren't part of the model
3562 return false;
3563
3564 default: // Suppress warnings
3565 break;
3566 }
3567
3568 return true;
3569}
3570
3571
3573{
3574 if( m_selection.Empty() )
3575 return;
3576
3577 while( m_selection.GetSize() )
3579
3580 getView()->Update( &m_selection );
3581
3582 m_selection.SetIsHover( false );
3583 m_selection.ClearReferencePoint();
3584
3585 // Inform other potentially interested tools
3586 if( !aQuietMode )
3587 m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
3588}
3589
3590
3592{
3593 // Don't group when we select new items, the schematic editor selects all new items for moving.
3594 // The PCB editor doesn't need this logic because it doesn't select new items for moving.
3595 if( m_enteredGroup && !aItem->IsNew()
3596 && !SCH_GROUP::WithinScope( static_cast<SCH_ITEM*>( aItem ), m_enteredGroup, m_isSymbolEditor ) )
3597 {
3598 ExitGroup();
3599 }
3600
3601 highlight( aItem, SELECTED, &m_selection );
3602}
3603
3604
3606{
3607 unhighlight( aItem, SELECTED, &m_selection );
3608}
3609
3610
3611void SCH_SELECTION_TOOL::highlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
3612{
3613 if( aMode == SELECTED )
3614 aItem->SetSelected();
3615 else if( aMode == BRIGHTENED )
3616 aItem->SetBrightened();
3617
3618 if( aGroup )
3619 aGroup->Add( aItem );
3620
3621 // Highlight pins and fields. (All the other symbol children are currently only
3622 // represented in the LIB_SYMBOL and will inherit the settings of the parent symbol.)
3623 if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( aItem ) )
3624 {
3625 // We don't want to select group children if the group itself is selected,
3626 // we can only select them when the group is entered
3627 if( sch_item->Type() != SCH_GROUP_T )
3628 {
3629 sch_item->RunOnChildren(
3630 [&]( SCH_ITEM* aChild )
3631 {
3632 if( aMode == SELECTED )
3633 {
3634 aChild->SetSelected();
3635 getView()->Hide( aChild, true );
3636 }
3637 else if( aMode == BRIGHTENED )
3638 {
3639 aChild->SetBrightened();
3640 }
3641 },
3643 }
3644 }
3645
3646 if( aGroup && aMode != BRIGHTENED )
3647 getView()->Hide( aItem, true );
3648
3649 if( aItem->GetParent() && aItem->GetParent()->Type() != SCHEMATIC_T )
3650 getView()->Update( aItem->GetParent(), KIGFX::REPAINT );
3651
3652 getView()->Update( aItem, KIGFX::REPAINT );
3653}
3654
3655
3656void SCH_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
3657{
3658 if( aMode == SELECTED )
3659 {
3660 aItem->ClearSelected();
3661 // Lines need endpoints cleared here
3662 if( aItem->Type() == SCH_LINE_T )
3663 aItem->ClearFlags( STARTPOINT | ENDPOINT );
3664
3665 if( aMode != BRIGHTENED )
3666 getView()->Hide( aItem, false );
3667 }
3668 else if( aMode == BRIGHTENED )
3669 {
3670 aItem->ClearBrightened();
3671 }
3672
3673 if( aGroup )
3674 aGroup->Remove( aItem );
3675
3676 // Unhighlight pins and fields. (All the other symbol children are currently only
3677 // represented in the LIB_SYMBOL.)
3678 if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( aItem ) )
3679 {
3680 sch_item->RunOnChildren(
3681 [&]( SCH_ITEM* aChild )
3682 {
3683 if( aMode == SELECTED )
3684 {
3685 aChild->ClearSelected();
3686 getView()->Hide( aChild, false );
3687 }
3688 else if( aMode == BRIGHTENED )
3689 {
3690 aChild->ClearBrightened();
3691 }
3692
3693 if( aGroup )
3694 aGroup->Remove( aChild );
3695 },
3697 }
3698
3699 if( aItem->GetParent() && aItem->GetParent()->Type() != SCHEMATIC_T )
3700 getView()->Update( aItem->GetParent(), KIGFX::REPAINT );
3701
3702 getView()->Update( aItem, KIGFX::REPAINT );
3703}
3704
3705
3707{
3708 const unsigned GRIP_MARGIN = 20;
3709 int margin = KiROUND( getView()->ToWorld( GRIP_MARGIN ) );
3710
3711 // Check if the point is located within any of the currently selected items bounding boxes
3712 for( EDA_ITEM* item : m_selection )
3713 {
3714 BOX2I itemBox = item->ViewBBox();
3715 itemBox.Inflate( margin ); // Give some margin for gripping an item
3716
3717 if( itemBox.Contains( aPoint ) )
3718 return true;
3719 }
3720
3721 return false;
3722}
3723
3724
3726{
3727 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
3728
3729 if( !editFrame || !editFrame->GetNetNavigator() || m_selection.Size() == 0 )
3730 return 0;
3731
3732 if( !m_selection.Front()->IsBrightened() )
3733 return 0;
3734
3735 const SCH_ITEM* item = editFrame->SelectNextPrevNetNavigatorItem( true );
3736
3737 if( item )
3738 {
3740 select( const_cast<SCH_ITEM*>( item ) );
3741 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3742 }
3743
3744 return 0;
3745}
3746
3747
3749{
3750 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
3751
3752 if( !editFrame || !editFrame->GetNetNavigator() || m_selection.Size() == 0 )
3753 return 0;
3754
3755 if( !m_selection.Front()->IsBrightened() )
3756 return 0;
3757
3758 const SCH_ITEM* item = editFrame->SelectNextPrevNetNavigatorItem( false );
3759
3760 if( item )
3761 {
3763 select( const_cast<SCH_ITEM*>( item ) );
3764 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3765 }
3766
3767 return 0;
3768}
3769
3770
3772{
3774
3781
3783
3786
3792
3795
3798
3801
3803}
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:277
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:147
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void ClearSelected()
Definition eda_item.h:142
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:149
bool IsSelected() const
Definition eda_item.h:127
void SetSelected()
Definition eda_item.h:139
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition eda_item.h:197
void ClearBrightened()
Definition eda_item.h:143
void SetBrightened()
Definition eda_item.h:140
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:238
EDA_ITEM * GetParent() const
Definition eda_item.h:112
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:151
void XorFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:148
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:150
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:531
virtual void SetScale(double aScale, VECTOR2D aAnchor={ 0, 0 })
Set the scaling factor, zooming around a given anchor point.
Definition view.cpp:571
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:421
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:1696
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:1644
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:597
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition view.cpp:1623
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:774
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:712
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
const SCH_ITEM * SelectNextPrevNetNavigatorItem(bool aNext)
const wxString & GetHighlightedConnection() const
wxGenericTreeCtrl * GetNetNavigator()
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:537
const SYMBOL * GetParentSymbol() const
Definition sch_item.cpp:258
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:252
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
std::vector< const SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
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
#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
std::vector< EDA_ITEM * > EDA_ITEMS
@ 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