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