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, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <advanced_config.h>
22#include <core/typeinfo.h>
23#include <core/kicad_algo.h>
26#include <sch_actions.h>
27#include <sch_collectors.h>
28#include <sch_selection_tool.h>
29#include <sch_base_frame.h>
30#include <connection_graph.h>
31#include <sch_netchain.h>
32#include <eeschema_id.h>
33#include <symbol_edit_frame.h>
34#include <symbol_viewer_frame.h>
35#include <math/util.h>
36#include <deque>
37#include <unordered_set>
39#include <geometry/shape_rect.h>
41#include <sch_painter.h>
43#include <sch_commit.h>
44#include <sch_edit_frame.h>
45#include <connection_graph.h>
46#include <sch_line.h>
47#include <sch_bus_entry.h>
48#include <sch_pin.h>
49#include <sch_group.h>
50#include <sch_marker.h>
51#include <sch_no_connect.h>
52#include <sch_rule_area.h>
53#include <sch_sheet_pin.h>
54#include <sch_table.h>
55#include <tool/tool_event.h>
56#include <tool/tool_manager.h>
58#include <tools/sch_move_tool.h>
63#include <trigo.h>
64#include <view/view.h>
65#include <view/view_controls.h>
66#include <wx/log.h>
67
69
70
72{
73 if( aSel.GetSize() == 1 )
74 {
75 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aSel.Front() );
76
77 if( symbol )
78 return !symbol->GetLibSymbolRef() || !symbol->GetLibSymbolRef()->IsPower();
79 }
80
81 return false;
82};
83
84
86{
87 return aSel.GetSize() == 1 && aSel.Front()->Type() == SCH_SYMBOL_T;
88};
89
90
92{
93 if( aSel.GetSize() == 1 )
94 {
95 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aSel.Front() );
96
97 if( symbol )
98 return symbol->GetLibSymbolRef() && symbol->GetLibSymbolRef()->IsMultiBodyStyle();
99 }
100
101 return false;
102};
103
104
106{
107 if( aSel.GetSize() == 1 )
108 {
109 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aSel.Front() );
110
111 if( symbol )
112 return symbol->GetLibSymbolRef() && symbol->GetLibSymbolRef()->GetUnitCount() >= 2;
113 }
114
115 return false;
116};
117
118
120{
121 if( aSel.GetSize() == 1 )
122 {
123 SCH_PIN* pin = dynamic_cast<SCH_PIN*>( aSel.Front() );
124
125 if( pin && pin->GetLibPin() )
126 return !pin->GetLibPin()->GetAlternates().empty();
127 }
128
129 return false;
130};
131
132
134{
135 if( aSel.CountType( SCH_MARKER_T ) != 1 )
136 return false;
137
138 return !static_cast<SCH_MARKER*>( aSel.Front() )->IsExcluded();
139};
140
141
143{
144 return aSel.GetSize() > 1 && aSel.OnlyContains( { SCH_SYMBOL_T } );
145};
146
147
149{
150 return aSel.GetSize() >= 1 && aSel.OnlyContains( { SCH_PIN_T } );
151};
152
153
155{
156 return aSel.GetSize() >= 1 && aSel.OnlyContains( { SCH_PIN_T, SCH_SHEET_PIN_T } );
157};
158
159enum
160{
161 ID_REPLACE_TERMINAL_PIN_A = wxID_HIGHEST + 2000,
163};
164
166{
167public:
169 {
170 SetTitle( _( "Replace terminal pin" ) );
171 }
172
173protected:
174 ACTION_MENU* create() const override { return new REPLACE_TERMINAL_PIN_MENU(); }
175
176 void update() override
177 {
178 Clear();
179
180 TOOL_MANAGER* toolMgr = getToolManager();
181 if( !toolMgr )
182 return;
183
184 SCH_SELECTION_TOOL* selTool = toolMgr->GetTool<SCH_SELECTION_TOOL>();
185 if( !selTool )
186 return;
187
188 const SELECTION& sel = selTool->GetSelection();
189 if( sel.Empty() )
190 return;
191
192 SCH_PIN* pin = dynamic_cast<SCH_PIN*>( sel.Front() );
193 SCH_EDIT_FRAME* frame = static_cast<SCH_EDIT_FRAME*>( toolMgr->GetToolHolder() );
194
195 if( !pin || !frame || !pin->Connection() )
196 return;
197
198 CONNECTION_GRAPH* graph = frame->Schematic().ConnectionGraph();
199 if( !graph )
200 return;
201
202 if( SCH_NETCHAIN* sig = graph->GetNetChainForNet( pin->Connection()->Name() ) )
203 {
204 m_oldA = sig->GetTerminalPinA();
205 m_oldB = sig->GetTerminalPinB();
206 m_new = pin->m_Uuid;
207
208 wxMenuItem* itemA = Append( ID_REPLACE_TERMINAL_PIN_A, _( "Terminal A" ) );
209 wxMenuItem* itemB = Append( ID_REPLACE_TERMINAL_PIN_B, _( "Terminal B" ) );
210
211 if( m_oldA == m_new )
212 itemA->Enable( false );
213
214 if( m_oldB == m_new )
215 itemB->Enable( false );
216 }
217 }
218
219 OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
220 {
221 if( aEvent.GetId() == ID_REPLACE_TERMINAL_PIN_A )
222 {
224 te.SetParameter( std::make_pair( m_oldA.AsString(), m_new.AsString() ) );
225 return te;
226 }
227 else if( aEvent.GetId() == ID_REPLACE_TERMINAL_PIN_B )
228 {
230 te.SetParameter( std::make_pair( m_oldB.AsString(), m_new.AsString() ) );
231 return te;
232 }
233
234 return OPT_TOOL_EVENT();
235 }
236
237private:
241};
242
243// Forward declaration of helper used inside NET_CHAIN_MENU::update
244class NET_CHAIN_MENU;
246
248{
249public:
251 {
252 SetTitle( _( "Net Chain..." ) );
254 }
255
256protected:
257 ACTION_MENU* create() const override { return new NET_CHAIN_MENU(); }
258
259 void update() override
260 {
261 Clear();
262
263 wxLogTrace( "KICAD_NET_CHAIN_MENU", "[NetChainMenu] update() invoked" );
264
265 TOOL_MANAGER* toolMgr = getToolManager();
266 if( !toolMgr )
267 {
268 // Defer population; parent UpdateAll() will re-run us after SetTool().
269 SetDirty();
270 wxLogTrace( "KICAD_NET_CHAIN_MENU", "[NetChainMenu] defer: toolMgr not yet available" );
271 return;
272 }
273
274 SCH_SELECTION_TOOL* selTool = static_cast<SCH_SELECTION_TOOL*>( m_tool );
275 EDA_BASE_FRAME* baseFrame = static_cast<EDA_BASE_FRAME*>( toolMgr->GetToolHolder() );
276
277 if( !selTool || !baseFrame || !baseFrame->IsType( FRAME_SCH ) )
278 {
279 wxLogTrace( "KICAD_NET_CHAIN_MENU", "[NetChainMenu] abort: selTool=%p baseFrame=%p", (void*) selTool,
280 (void*) baseFrame );
281 return;
282 }
283
284 SCH_EDIT_FRAME* frame = static_cast<SCH_EDIT_FRAME*>( baseFrame );
285
286 const SCH_SELECTION& sel = selTool->GetSelection();
287 if( sel.Empty() )
288 {
289 wxLogTrace( "KICAD_NET_CHAIN_MENU", "[NetChainMenu] abort: empty selection" );
290 return; // nothing to show
291 }
292
293 wxLogTrace( "KICAD_NET_CHAIN_MENU", "[NetChainMenu] selection size=%u", sel.GetSize() );
294
295 CONNECTION_GRAPH* graph = frame->Schematic().ConnectionGraph();
296 if( !graph )
297 {
298 wxLogTrace( "KICAD_NET_CHAIN_MENU", "[NetChainMenu] abort: no connection graph" );
299 return;
300 }
301
302 if( sel.OnlyContains( { SCH_SYMBOL_T } ) )
303 {
305 wxLogTrace( "KICAD_NET_CHAIN_MENU", "[NetChainMenu] added createNetChain" );
306 return;
307 }
308
309 auto pinFrom = [&]( int idx ) -> SCH_PIN*
310 {
311 if( idx >= (int) sel.GetSize() )
312 return nullptr;
313 return dynamic_cast<SCH_PIN*>( static_cast<SCH_ITEM*>( sel[idx] ) );
314 };
315
316 // Determine context flags
317 bool singlePin = sel.GetSize() == 1 && pinFrom( 0 ) && pinFrom( 0 )->Connection();
318 bool inSignal = false; // at least one selected item participates in a committed chain
319 bool canName = false; // we can rename a chain (single pin with committed chain)
320 bool canRemove = false; // we can remove an item from its chain
321
322 // Evaluate selection items
323 for( size_t i = 0; i < sel.GetSize(); ++i )
324 {
325 SCH_PIN* p = dynamic_cast<SCH_PIN*>( static_cast<SCH_ITEM*>( sel[i] ) );
326 if( !p || !p->Connection() )
327 {
328 wxLogTrace( "KICAD_NET_CHAIN_MENU", "[NetChainMenu] sel[%zu]: not a pin or no connection", i );
329 continue;
330 }
331
332 wxString netName = p->Connection()->Name();
333 bool hasSignal = graph->GetNetChainForNet( netName );
334 wxLogTrace( "KICAD_NET_CHAIN_MENU", "[NetChainMenu] sel[%zu]: pin uuid=%s net=%s committedSignal=%d", i,
335 p->m_Uuid.AsString(), netName, hasSignal );
336 if( graph->GetNetChainForNet( p->Connection()->Name() ) )
337 {
338 inSignal = true;
339 canRemove = true; // current remove handler works on a pin in a chain
340 if( sel.GetSize() == 1 )
341 canName = true; // nameNetChain expects single pin selected
342 }
343 }
344
345 wxLogTrace( "KICAD_NET_CHAIN_MENU", "[NetChainMenu] flags: singlePin=%d inSignal=%d canName=%d canRemove=%d", singlePin, inSignal, canName, canRemove );
346
347 // highlightNetChain action: only if we have a committed chain context
348 if( inSignal )
349 {
351 wxLogTrace( "KICAD_NET_CHAIN_MENU", "[NetChainMenu] added highlightNetChain" );
352 }
353
354 // removeFromNetChain action
355 if( canRemove )
356 {
358 wxLogTrace( "KICAD_NET_CHAIN_MENU", "[NetChainMenu] added removeFromNetChain" );
359 }
360
361 // Replace terminal pin submenu only when a single pin belonging to a chain is selected
362 if( singlePin && inSignal )
363 {
365 wxLogTrace( "KICAD_NET_CHAIN_MENU", "[NetChainMenu] added replaceTerminalPin submenu" );
366 }
367
368 // nameNetChain action
369 if( canName )
370 {
372 wxLogTrace( "KICAD_NET_CHAIN_MENU", "[NetChainMenu] added nameNetChain" );
373 }
374
375 // createNetChainBetweenPins: two pins selected, share potential (uncommitted) chain
376 if( sel.GetSize() == 2 )
377 {
378 bool added = addCreateNetChainBetweenPinsIfApplicable( this, frame, sel );
379 wxLogTrace( "KICAD_NET_CHAIN_MENU", "[NetChainMenu] createNetChainBetweenPins attempted added=%d", added );
380 }
381
382 // Allow opening the Create Net Chain dialog (with the dialog filtered by selection)
383 // for any single connected pin or wire/bus. The dialog itself surfaces the empty-state
384 // case if the net is already on a committed chain.
385 if( sel.GetSize() == 1 )
386 {
387 SCH_ITEM* item = static_cast<SCH_ITEM*>( sel.Front() );
388 bool isConnectedPin = dynamic_cast<SCH_PIN*>( item ) && item->Connection();
389 bool isConnectedWireOrBus = item && item->Type() == SCH_LINE_T
391 && item->Connection();
392
393 if( isConnectedPin || isConnectedWireOrBus )
394 {
396 wxLogTrace( "KICAD_NET_CHAIN_MENU", "[NetChainMenu] added createNetChain for single connected item" );
397 }
398 }
399
400 // If nothing ended up enabled, leave a placeholder disabled item to make it
401 // obvious to the user that the submenu exists but just has no applicable
402 // actions for the current selection.
403 if( !HasEnabledItems() )
404 {
405 wxMenuItem* placeholder = Append( wxID_ANY, _( "(No net chain actions)" ) );
406 placeholder->Enable( false );
407 }
408 }
409
410private:
412};
413
414// Extend net-chains menu dynamically with createNetChainBetweenPins when two pins are selected
416{
417 if( aSel.GetSize() != 2 )
418 return false;
419
420 SCH_PIN* pa = dynamic_cast<SCH_PIN*>( static_cast<SCH_ITEM*>( aSel[0] ) );
421 SCH_PIN* pb = dynamic_cast<SCH_PIN*>( static_cast<SCH_ITEM*>( aSel[1] ) );
422 if( !pa || !pb )
423 return false;
424
425 CONNECTION_GRAPH* graph = aFrame->Schematic().ConnectionGraph();
426
427 if( graph->FindPotentialNetChainBetweenPins( pa, pb ) )
428 {
429 wxString label = wxString::Format( _( "Create Net Chain between %s:%s and %s:%s" ),
430 pa->GetParentSymbol()->GetRef( &aFrame->GetCurrentSheet() ), pa->GetNumber(),
431 pb->GetParentSymbol()->GetRef( &aFrame->GetCurrentSheet() ), pb->GetNumber() );
432 aMenu->Add( SCH_ACTIONS::createNetChainBetweenPins )->SetItemLabel( label );
433 return true;
434 }
435 return false;
436}
437
438
440{
441 for( EDA_ITEM* item : aSel.Items() )
442 {
443 if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( item ) )
444 {
445 if( schItem->IsLocked() )
446 return true;
447 }
448 }
449
450 return false;
451};
452
453
455{
456 for( EDA_ITEM* item : aSel.Items() )
457 {
458 if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( item ) )
459 {
460 if( !schItem->IsLocked() )
461 return true;
462 }
463 }
464
465 return false;
466};
467
468
469static void passEvent( TOOL_EVENT* const aEvent, const TOOL_ACTION* const aAllowedActions[] )
470{
471 for( int i = 0; aAllowedActions[i]; ++i )
472 {
473 if( aEvent->IsAction( aAllowedActions[i] ) )
474 {
475 aEvent->SetPassEvent();
476 break;
477 }
478 }
479}
480
481
482#define HITTEST_THRESHOLD_PIXELS 5
483
484
486 SELECTION_TOOL( "common.InteractiveSelection" ),
487 m_frame( nullptr ),
489 m_isSymbolEditor( false ),
490 m_isSymbolViewer( false ),
491 m_unit( 0 ),
492 m_bodyStyle( 0 ),
493 m_enteredGroup( nullptr ),
495 m_previous_first_cell( nullptr )
496{
497 m_filter.SetDefaults();
498 m_selection.Clear();
499}
500
501
507
508
524
525static std::vector<KICAD_T> connectedLineTypes =
526{
529};
530
550
551static std::vector<KICAD_T> crossProbingTypes =
552{
554 SCH_PIN_T,
556};
557
558static std::vector<KICAD_T> lineTypes = { SCH_LINE_T };
559static std::vector<KICAD_T> sheetTypes = { SCH_SHEET_T };
560static std::vector<KICAD_T> tableCellTypes = { SCH_TABLECELL_T };
561
563{
565
566 SYMBOL_VIEWER_FRAME* symbolViewerFrame = dynamic_cast<SYMBOL_VIEWER_FRAME*>( m_frame );
567 SYMBOL_EDIT_FRAME* symbolEditorFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
568
569 if( symbolEditorFrame )
570 {
571 m_isSymbolEditor = true;
572 m_unit = symbolEditorFrame->GetUnit();
573 m_bodyStyle = symbolEditorFrame->GetBodyStyle();
574 }
575 else
576 {
577 m_isSymbolViewer = symbolViewerFrame != nullptr;
578 }
579
580 // clang-format off
581 auto linesSelection = SCH_CONDITIONS::MoreThan( 0 ) && SCH_CONDITIONS::OnlyTypes( lineTypes );
582 auto wireOrBusSelection = SCH_CONDITIONS::Count( 1 ) && SCH_CONDITIONS::OnlyTypes( connectedLineTypes );
583 auto connectedSelection = SCH_CONDITIONS::MoreThan( 0 ) && SCH_CONDITIONS::OnlyTypes( connectedTypes );
584 auto expandableSelection =
586 auto sheetSelection = SCH_CONDITIONS::Count( 1 ) && SCH_CONDITIONS::OnlyTypes( sheetTypes );
587 auto crossProbingSelection = SCH_CONDITIONS::MoreThan( 0 ) && SCH_CONDITIONS::HasTypes( crossProbingTypes );
588 auto tableCellSelection = SCH_CONDITIONS::MoreThan( 0 ) && SCH_CONDITIONS::OnlyTypes( tableCellTypes );
589 auto multiplePinsSelection = SCH_CONDITIONS::MoreThan( 1 ) && SCH_CONDITIONS::OnlyTypes( { SCH_PIN_T } );
590 // clang-format on
591
592 auto schEditSheetPageNumberCondition =
593 [this] ( const SELECTION& aSel )
594 {
596 return false;
597
598 return SCH_CONDITIONS::LessThan( 2 )( aSel )
600 };
601
602 auto schEditCondition =
603 [this] ( const SELECTION& aSel )
604 {
606 };
607
608 auto belowRootSheetCondition =
609 [this]( const SELECTION& aSel )
610 {
611 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
612
613 return editFrame
614 && editFrame->GetCurrentSheet().Last() != &editFrame->Schematic().Root();
615 };
616
617 auto haveHighlight =
618 [this]( const SELECTION& sel )
619 {
620 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
621
622 return editFrame && !editFrame->GetHighlightedConnection().IsEmpty();
623 };
624
625 auto haveSymbol =
626 [this]( const SELECTION& sel )
627 {
628 return m_isSymbolEditor &&
629 static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
630 };
631
632 auto groupEnterCondition =
634
635 auto inGroupCondition =
636 [this] ( const SELECTION& )
637 {
638 return m_enteredGroup != nullptr;
639 };
640
641 auto multipleUnitsSelection = []( const SELECTION& aSel )
642 {
643 return !GetSameSymbolMultiUnitSelection( aSel ).empty();
644 };
645
646 auto allowPinSwaps =
647 [this]( const SELECTION& )
648 {
649 return m_frame->eeconfig() &&
650 m_frame->eeconfig()->m_Input.allow_unconstrained_pin_swaps;
651 };
652
653
654 auto& menu = m_menu->GetMenu();
655
656 // Allow the Net Chain submenu for any selection consisting solely of pins (one or more).
657 // Restricting to exactly one pin would prevent showing the menu (and thus the
658 // "Create Net Chain between ..." action) when exactly two pins were selected.
659 SELECTION_CONDITION pinSelection = []( const SELECTION& aSel )
660 {
661 return aSel.GetSize() >= 1 && aSel.OnlyContains( { SCH_PIN_T } );
662 };
663
664 // Also expose the Net Chain menu when right-clicking a single wire or bus. SCH_LINE_T
665 // covers both; IsType against the scan-aliases discriminates wire vs bus.
666 SELECTION_CONDITION wireOrBusInSignal = []( const SELECTION& aSel )
667 {
668 if( aSel.GetSize() != 1 )
669 return false;
670
671 EDA_ITEM* item = aSel.Front();
672
673 if( !item || item->Type() != SCH_LINE_T )
674 return false;
675
676 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
677
678 if( !schItem->IsType( { SCH_ITEM_LOCATE_WIRE_T, SCH_ITEM_LOCATE_BUS_T } ) )
679 return false;
680
681 if( !schItem->Connection() )
682 return false;
683
684 return true; // Allow menu; handlers will rebuild/validate as needed
685 };
686
687 SELECTION_CONDITION symbolSelection = []( const SELECTION& aSel )
688 {
689 return aSel.GetSize() >= 1 && aSel.OnlyContains( { SCH_SYMBOL_T } );
690 };
691
692 std::shared_ptr<NET_CHAIN_MENU> netChainMenu = std::make_shared<NET_CHAIN_MENU>();
693 netChainMenu->SetTool( this );
694 m_menu->RegisterSubMenu( netChainMenu );
695
696 // clang-format off
697 menu.AddItem( ACTIONS::groupEnter, groupEnterCondition, 1 );
698 menu.AddItem( ACTIONS::groupLeave, inGroupCondition, 1 );
699 menu.AddItem( SCH_ACTIONS::placeLinkedDesignBlock, groupEnterCondition, 1 );
700 menu.AddItem( SCH_ACTIONS::saveToLinkedDesignBlock, groupEnterCondition, 1 );
701 menu.AddItem( SCH_ACTIONS::clearHighlight, haveHighlight && SCH_CONDITIONS::Idle, 1 );
702 menu.AddSeparator( haveHighlight && SCH_CONDITIONS::Idle, 1 );
703
704 menu.AddItem( SCH_ACTIONS::selectConnection, expandableSelection && SCH_CONDITIONS::Idle, 2 );
705 menu.AddItem( ACTIONS::selectColumns, tableCellSelection && SCH_CONDITIONS::Idle, 2 );
706 menu.AddItem( ACTIONS::selectRows, tableCellSelection && SCH_CONDITIONS::Idle, 2 );
707 menu.AddItem( ACTIONS::selectTable, tableCellSelection && SCH_CONDITIONS::Idle, 2 );
708
709 menu.AddSeparator( 100 );
710 menu.AddItem( SCH_ACTIONS::drawWire, schEditCondition && SCH_CONDITIONS::Empty, 100 );
711 menu.AddItem( SCH_ACTIONS::drawBus, schEditCondition && SCH_CONDITIONS::Empty, 100 );
712
713 menu.AddSeparator( 100 );
715
716 menu.AddItem( SCH_ACTIONS::enterSheet, sheetSelection && SCH_CONDITIONS::Idle, 150 );
717 menu.AddItem( SCH_ACTIONS::selectOnPCB, crossProbingSelection && schEditCondition && SCH_CONDITIONS::Idle, 150 );
718 menu.AddItem( SCH_ACTIONS::leaveSheet, belowRootSheetCondition, 150 );
719
720 menu.AddSeparator( 200 );
721 menu.AddItem( SCH_ACTIONS::placeJunction, wireOrBusSelection && SCH_CONDITIONS::Idle, 250 );
722 menu.AddItem( SCH_ACTIONS::placeLabel, wireOrBusSelection && SCH_CONDITIONS::Idle, 250 );
723 menu.AddItem( SCH_ACTIONS::placeClassLabel, wireOrBusSelection && SCH_CONDITIONS::Idle, 250 );
724 menu.AddItem( SCH_ACTIONS::placeGlobalLabel, wireOrBusSelection && SCH_CONDITIONS::Idle, 250 );
725 menu.AddItem( SCH_ACTIONS::placeHierLabel, wireOrBusSelection && SCH_CONDITIONS::Idle, 250 );
726 menu.AddItem( SCH_ACTIONS::breakWire, linesSelection && SCH_CONDITIONS::Idle, 250 );
727 menu.AddItem( SCH_ACTIONS::slice, linesSelection && SCH_CONDITIONS::Idle, 250 );
728 menu.AddItem( SCH_ACTIONS::placeSheetPin, sheetSelection && SCH_CONDITIONS::Idle, 250 );
729 menu.AddItem( SCH_ACTIONS::autoplaceAllSheetPins, sheetSelection && SCH_CONDITIONS::Idle, 250 );
730 menu.AddItem( SCH_ACTIONS::syncSheetPins, sheetSelection && SCH_CONDITIONS::Idle, 250 );
731 menu.AddItem( SCH_ACTIONS::swapPinLabels, multiplePinsSelection && schEditCondition && SCH_CONDITIONS::Idle, 250 );
732 menu.AddItem( SCH_ACTIONS::swapUnitLabels, multipleUnitsSelection && schEditCondition && SCH_CONDITIONS::Idle, 250 );
733 menu.AddItem( SCH_ACTIONS::swapPins, multiplePinsSelection && schEditCondition && SCH_CONDITIONS::Idle && allowPinSwaps, 250 );
734 menu.AddItem( SCH_ACTIONS::assignNetclass, connectedSelection && SCH_CONDITIONS::Idle, 250 );
735 menu.AddItem( SCH_ACTIONS::findNetInInspector, connectedSelection && SCH_CONDITIONS::Idle, 250 );
736 menu.AddItem( SCH_ACTIONS::editPageNumber, schEditSheetPageNumberCondition, 250 );
737
738 menu.AddSeparator( 400 );
739 menu.AddItem( SCH_ACTIONS::symbolProperties, haveSymbol && SCH_CONDITIONS::Empty, 400 );
740 menu.AddItem( SCH_ACTIONS::pinTable, haveSymbol && SCH_CONDITIONS::Empty, 400 );
741 menu.AddMenu( netChainMenu.get(), ( pinSelection || wireOrBusInSignal || symbolSelection ) && SCH_CONDITIONS::Idle, 400 );
742
743 menu.AddSeparator( 1000 );
744 m_frame->AddStandardSubMenus( *m_menu.get() );
745 // clang-format on
746
747 m_disambiguateTimer.SetOwner( this );
748 Connect( m_disambiguateTimer.GetId(), wxEVT_TIMER,
749 wxTimerEventHandler( SCH_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
750
751 return true;
752}
753
754
756{
758
759 if( aReason != TOOL_BASE::REDRAW )
760 {
761 if( m_enteredGroup )
762 ExitGroup();
763
764 // Remove pointers to the selected items from containers without changing their
765 // properties (as they are already deleted while a new sheet is loaded)
766 m_selection.Clear();
767 }
768
769 if( aReason == RESET_REASON::SHUTDOWN )
770 return;
771
772 if( aReason == TOOL_BASE::MODEL_RELOAD || aReason == TOOL_BASE::SUPERMODEL_RELOAD )
773 {
774 getView()->GetPainter()->GetSettings()->SetHighlight( false );
775
776 SYMBOL_EDIT_FRAME* symbolEditFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
777 SYMBOL_VIEWER_FRAME* symbolViewerFrame = dynamic_cast<SYMBOL_VIEWER_FRAME*>( m_frame );
778
779 if( symbolEditFrame )
780 {
781 m_isSymbolEditor = true;
782 m_unit = symbolEditFrame->GetUnit();
783 m_bodyStyle = symbolEditFrame->GetBodyStyle();
784 }
785 else
786 {
787 m_isSymbolViewer = symbolViewerFrame != nullptr;
788 }
789 }
790
791 // Reinsert the VIEW_GROUP, in case it was removed from the VIEW
793 getView()->Add( &m_selection );
794
797}
798
800{
801 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
802
803 KIID lastRolloverItemId = niluuid;
805
806 auto pinOrientation =
807 []( EDA_ITEM* aItem )
808 {
809 SCH_PIN* pin = dynamic_cast<SCH_PIN*>( aItem );
810
811 if( pin )
812 {
813 const SCH_SYMBOL* parent = dynamic_cast<const SCH_SYMBOL*>( pin->GetParentSymbol() );
814
815 if( !parent )
816 return pin->GetOrientation();
817 else
818 {
819 SCH_PIN dummy( *pin );
821 return dummy.GetOrientation();
822 }
823 }
824
825 SCH_SHEET_PIN* sheetPin = dynamic_cast<SCH_SHEET_PIN*>( aItem );
826
827 if( sheetPin )
828 {
829 switch( sheetPin->GetSide() )
830 {
831 default:
836 }
837 }
838
840 };
841
842 // Main loop: keep receiving events
843 while( TOOL_EVENT* evt = Wait() )
844 {
845 bool selCancelled = false;
846 bool displayWireCursor = false;
847 bool displayBusCursor = false;
848 bool displayLineCursor = false;
849 KIID rolloverItemId = lastRolloverItemId;
850
851 // on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
852 setModifiersState( evt->Modifier( MD_SHIFT ), evt->Modifier( MD_CTRL ),
853 evt->Modifier( MD_ALT ) );
854
855 MOUSE_DRAG_ACTION drag_action = m_frame->GetDragAction();
856
857 if( evt->IsMouseDown( BUT_LEFT ) )
858 {
859 if( !m_frame->ToolStackIsEmpty() )
860 {
861 // Avoid triggering when running under other tools
862 }
863 else if( m_toolMgr->GetTool<SCH_POINT_EDITOR>()
864 && m_toolMgr->GetTool<SCH_POINT_EDITOR>()->HasPoint() )
865 {
866 // Distinguish point editor from selection modification by checking modifiers
867 if( hasModifier() )
868 {
869 m_originalCursor = m_toolMgr->GetMousePosition();
870 m_disambiguateTimer.StartOnce( ADVANCED_CFG::GetCfg().m_DisambiguationMenuDelay );
871 }
872 }
873 else
874 {
875 m_originalCursor = m_toolMgr->GetMousePosition();
876 m_disambiguateTimer.StartOnce( ADVANCED_CFG::GetCfg().m_DisambiguationMenuDelay );
877 }
878 }
879 // Single click? Select single object
880 else if( evt->IsClick( BUT_LEFT ) )
881 {
882 // If the timer has stopped, then we have already run the disambiguate routine
883 // and we don't want to register an extra click here
884 if( !m_disambiguateTimer.IsRunning() )
885 {
886 evt->SetPassEvent();
887 continue;
888 }
889
890 m_disambiguateTimer.Stop();
891
892 if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
893 schframe->ClearFocus();
894
895 // Collect items at the clicked location (doesn't select them yet)
896 SCH_COLLECTOR collector;
898
899 CollectHits( collector, evt->Position() );
900 size_t preFilterCount = collector.GetCount();
901 rejected.SetAll( false );
902 narrowSelection( collector, evt->Position(), false, false, &rejected );
903
904 if( m_selection.GetSize() != 0 && dynamic_cast<SCH_TABLECELL*>( m_selection.GetItem( 0 ) ) && m_additive
905 && collector.GetCount() == 1 && dynamic_cast<SCH_TABLECELL*>( collector[0] ) )
906 {
907 SCH_TABLECELL* firstCell = static_cast<SCH_TABLECELL*>( m_selection.GetItem( 0 ) );
908 SCH_TABLECELL* clickedCell = static_cast<SCH_TABLECELL*>( collector[0] );
909 bool allCellsFromSameTable = true;
910
911 if( m_previous_first_cell == nullptr || m_selection.GetSize() == 1)
912 {
913 m_previous_first_cell = firstCell;
914 }
915
917 {
918 if( !static_cast<SCH_TABLECELL*>( selection )
919 || selection->GetParent() != clickedCell->GetParent() )
920 {
921 allCellsFromSameTable = false;
922 }
923 }
924
925 if( m_previous_first_cell && clickedCell && allCellsFromSameTable )
926 {
928 selection->ClearSelected();
929
930 m_selection.Clear();
931 SCH_TABLE* parentTable = dynamic_cast<SCH_TABLE*>( m_previous_first_cell->GetParent() );
932
933 VECTOR2D start = m_previous_first_cell->GetCenter();
934 VECTOR2D end = clickedCell->GetCenter();
935
936 if( parentTable )
937 {
938 InitializeSelectionState( parentTable );
939
940 VECTOR2D topLeft( std::min( start.x, end.x ), std::min( start.y, end.y ) );
941 VECTOR2D bottomRight( std::max( start.x, end.x ), std::max( start.y, end.y ) );
942
943 SelectCellsBetween( topLeft, bottomRight - topLeft, parentTable );
944 }
945 }
946 }
947 else if( collector.GetCount() == 1 && !m_isSymbolEditor && !hasModifier() )
948 {
949 OPT_TOOL_EVENT autostart = autostartEvent( evt, grid, collector[0] );
950
951 if( autostart )
952 {
954
955 params->layer = autostart->Parameter<const DRAW_SEGMENT_EVENT_PARAMS*>()->layer;
956 params->quitOnDraw = true;
957 params->sourceSegment = dynamic_cast<SCH_LINE*>( collector[0] );
958
959 autostart->SetParameter<const DRAW_SEGMENT_EVENT_PARAMS*>( params );
960 m_toolMgr->ProcessEvent( *autostart );
961
962 selCancelled = true;
963 }
964 else if( collector[0]->HasHoveredHypertext() )
965 {
966 collector[ 0 ]->DoHypertextAction( m_frame, evt->Position() );
967 selCancelled = true;
968 }
969 else if( collector[0]->IsBrightened() )
970 {
971 if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
972 {
973 NET_NAVIGATOR_ITEM_DATA itemData( schframe->GetCurrentSheet(), collector[0] );
974
975 schframe->SelectNetNavigatorItem( &itemData );
976 }
977 }
978 }
979
980 if( !selCancelled )
981 {
982 if( collector.GetCount() == 0 && preFilterCount > 0 )
983 {
984 if( SCH_BASE_FRAME* frame = dynamic_cast<SCH_BASE_FRAME*>( m_frame ) )
985 frame->HighlightSelectionFilter( rejected );
986 }
987
988 selectPoint( collector, evt->Position(), nullptr, nullptr, m_additive, m_subtractive, m_exclusive_or );
989 m_selection.SetIsHover( false );
990 }
991 }
992 else if( evt->IsClick( BUT_RIGHT ) )
993 {
994 m_disambiguateTimer.Stop();
995
996 // right click? if there is any object - show the context menu
997 if( m_selection.Empty() )
998 {
1000 SelectPoint( evt->Position(), { SCH_LOCATE_ANY_T }, nullptr, &selCancelled );
1001 m_selection.SetIsHover( true );
1002 }
1003 // If the cursor has moved off the bounding box of the selection by more than
1004 // a grid square, check to see if there is another item available for selection
1005 // under the cursor. If there is, the user likely meant to get the context menu
1006 // for that item. If there is no new item, then keep the original selection and
1007 // show the context menu for it.
1008 else if( !m_selection.GetBoundingBox().Inflate( grid.GetGrid().x, grid.GetGrid().y )
1009 .Contains( evt->Position() ) )
1010 {
1011 SCH_COLLECTOR collector;
1012
1013 if( CollectHits( collector, evt->Position(), { SCH_LOCATE_ANY_T } ) )
1014 {
1016
1017 SelectPoint( evt->Position(), { SCH_LOCATE_ANY_T }, nullptr, &selCancelled );
1018 m_selection.SetIsHover( true );
1019 }
1020 }
1021
1022 if( !selCancelled )
1023 m_menu->ShowContextMenu( m_selection );
1024 }
1025 else if( evt->IsDblClick( BUT_LEFT ) )
1026 {
1027 m_disambiguateTimer.Stop();
1028
1029 // double click? Display the properties window
1030 if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
1031 schframe->ClearFocus();
1032
1033 if( m_selection.Empty() )
1034 SelectPoint( evt->Position() );
1035
1036 EDA_ITEM* item = m_selection.Front();
1037
1038 if( item && item->Type() == SCH_SHEET_T )
1039 m_toolMgr->PostAction( SCH_ACTIONS::enterSheet );
1040 else if( m_selection.GetSize() == 1 && m_selection[0]->Type() == SCH_GROUP_T )
1041 EnterGroup();
1042 else
1043 m_toolMgr->PostAction( SCH_ACTIONS::properties );
1044 }
1045 else if( evt->IsDblClick( BUT_MIDDLE ) )
1046 {
1047 m_disambiguateTimer.Stop();
1048
1049 // Middle double click? Do zoom to fit or zoom to objects
1050 if( evt->Modifier( MD_CTRL ) ) // Is CTRL key down?
1051 m_toolMgr->RunAction( ACTIONS::zoomFitObjects );
1052 else
1053 m_toolMgr->RunAction( ACTIONS::zoomFitScreen );
1054 }
1055 else if( evt->IsDrag( BUT_LEFT ) )
1056 {
1057 m_disambiguateTimer.Stop();
1058
1059 // Is another tool already moving a new object? Don't allow a drag start
1060 if( !m_selection.Empty() && m_selection[0]->HasFlag( IS_NEW | IS_MOVING ) )
1061 {
1062 evt->SetPassEvent();
1063 continue;
1064 }
1065
1066 // drag with LMB? Select multiple objects (or at least draw a selection box) or
1067 // drag them
1068 if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
1069 schframe->ClearFocus();
1070
1071 SCH_COLLECTOR collector;
1072
1073 if( m_selection.GetSize() == 1 && dynamic_cast<SCH_TABLE*>( m_selection.GetItem( 0 ) )
1074 && evt->HasPosition() && selectionContains( evt->DragOrigin() ) )
1075 {
1076 m_toolMgr->RunAction( SCH_ACTIONS::move );
1077 }
1078 // Allow drag selecting table cells, except when the table is already selected
1079 // or inside a group that we haven't entered
1080 else if( CollectHits( collector, evt->DragOrigin(), { SCH_TABLECELL_T } )
1081 && !collector[0]->GetParent()->IsSelected()
1082 && ( collector[0]->GetParent()->GetParentGroup() == nullptr
1083 || collector[0]->GetParent()->GetParentGroup() == m_enteredGroup ) )
1084 {
1085 selectTableCells( static_cast<SCH_TABLE*>( collector[0]->GetParent() ) );
1086 }
1087 else if( hasModifier() || drag_action == MOUSE_DRAG_ACTION::SELECT )
1088 {
1091 selectLasso();
1092 else
1094 }
1095 else if( m_selection.Empty() && drag_action != MOUSE_DRAG_ACTION::DRAG_ANY )
1096 {
1099 selectLasso();
1100 else
1102 }
1103 else
1104 {
1105 if( m_isSymbolEditor )
1106 {
1107 if( static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->IsSymbolAlias() )
1108 {
1110 }
1111 else
1112 {
1114 SCH_TEXT_T,
1116 SCH_PIN_T,
1117 SCH_FIELD_T } );
1118 }
1119 }
1120 else
1121 {
1123 }
1124
1125 // Check if dragging has started within any of selected items bounding box
1126 if( evt->HasPosition() && selectionContains( evt->DragOrigin() ) )
1127 {
1128 // drag_is_move option exists only in schematic editor, not in symbol editor
1129 // (m_frame->eeconfig() returns nullptr in Symbol Editor)
1130 if( m_isSymbolEditor || m_frame->eeconfig()->m_Input.drag_is_move )
1131 m_toolMgr->RunAction( SCH_ACTIONS::move );
1132 else
1133 m_toolMgr->RunAction( SCH_ACTIONS::drag );
1134 }
1135 else
1136 {
1137 // No -> drag a selection box
1140 selectLasso();
1141 else
1143 }
1144 }
1145 }
1146 else if( evt->IsMouseDown( BUT_AUX1 ) )
1147 {
1149 }
1150 else if( evt->IsMouseDown( BUT_AUX2 ) )
1151 {
1153 }
1154 else if( evt->Action() == TA_MOUSE_WHEEL )
1155 {
1156 int field = -1;
1157
1158 if( evt->Modifier() == ( MD_SHIFT | MD_ALT ) )
1159 field = 0;
1160 else if( evt->Modifier() == ( MD_CTRL | MD_ALT ) )
1161 field = 1;
1162 // any more?
1163
1164 if( field >= 0 )
1165 {
1166 const int delta = evt->Parameter<int>();
1167 ACTIONS::INCREMENT incParams{ delta > 0 ? 1 : -1, field };
1168
1169 m_toolMgr->RunAction( ACTIONS::increment, incParams );
1170 }
1171 }
1172 else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CHOICE_MENU_CHOICE )
1173 {
1174 m_disambiguateTimer.Stop();
1175
1176 // context sub-menu selection? Handle unit selection or bus unfolding
1177 if( *evt->GetCommandId() >= ID_POPUP_SCH_SELECT_UNIT
1178 && *evt->GetCommandId() <= ID_POPUP_SCH_SELECT_UNIT_END )
1179 {
1180 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( m_selection.Front() );
1181 int unit = *evt->GetCommandId() - ID_POPUP_SCH_SELECT_UNIT;
1182
1183 if( symbol )
1184 static_cast<SCH_EDIT_FRAME*>( m_frame )->SelectUnit( symbol, unit );
1185 }
1186 else if( *evt->GetCommandId() >= ID_POPUP_SCH_PLACE_UNIT
1187 && *evt->GetCommandId() <= ID_POPUP_SCH_PLACE_UNIT_END )
1188 {
1189 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( m_selection.Front() );
1190 int unit = *evt->GetCommandId() - ID_POPUP_SCH_PLACE_UNIT;
1191
1192 if( symbol )
1194 SCH_ACTIONS::PLACE_SYMBOL_UNIT_PARAMS{ symbol, unit } );
1195 }
1196 else if( *evt->GetCommandId() >= ID_POPUP_SCH_SELECT_BODY_STYLE
1197 && *evt->GetCommandId() <= ID_POPUP_SCH_SELECT_BODY_STYLE_END )
1198 {
1199 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( m_selection.Front() );
1200 int bodyStyle = ( *evt->GetCommandId() - ID_POPUP_SCH_SELECT_BODY_STYLE ) + 1;
1201
1202 if( symbol && symbol->GetBodyStyle() != bodyStyle )
1203 static_cast<SCH_EDIT_FRAME*>( m_frame )->SelectBodyStyle( symbol, bodyStyle );
1204 }
1205 else if( *evt->GetCommandId() >= ID_POPUP_SCH_ALT_PIN_FUNCTION
1206 && *evt->GetCommandId() <= ID_POPUP_SCH_ALT_PIN_FUNCTION_END )
1207 {
1208 SCH_PIN* pin = dynamic_cast<SCH_PIN*>( m_selection.Front() );
1209 wxString alt = *evt->Parameter<wxString*>();
1210
1211 if( pin )
1212 static_cast<SCH_EDIT_FRAME*>( m_frame )->SetAltPinFunction( pin, alt );
1213 }
1214 else if( *evt->GetCommandId() >= ID_POPUP_SCH_PIN_TRICKS_START
1215 && *evt->GetCommandId() <= ID_POPUP_SCH_PIN_TRICKS_END
1217 {
1218 SCH_EDIT_FRAME* sch_frame = static_cast<SCH_EDIT_FRAME*>( m_frame );
1219
1220 // Keep track of new items so we make them the new selection at the end
1221 EDA_ITEMS newItems;
1222 SCH_COMMIT commit( sch_frame );
1223
1224 if( *evt->GetCommandId() == ID_POPUP_SCH_PIN_TRICKS_NO_CONNECT )
1225 {
1226 for( EDA_ITEM* item : m_selection )
1227 {
1228 if( item->Type() != SCH_PIN_T && item->Type() != SCH_SHEET_PIN_T )
1229 continue;
1230
1231 SCH_NO_CONNECT* nc = new SCH_NO_CONNECT( item->GetPosition() );
1232 commit.Add( nc, sch_frame->GetScreen() );
1233 newItems.push_back( nc );
1234 }
1235
1236 if( !commit.Empty() )
1237 {
1238 commit.Push( wxS( "No Connect Pins" ) );
1240 }
1241 }
1242 else if( *evt->GetCommandId() == ID_POPUP_SCH_PIN_TRICKS_WIRE )
1243 {
1244 VECTOR2I wireGrid = grid.GetGridSize( GRID_HELPER_GRIDS::GRID_WIRES );
1245
1246 for( EDA_ITEM* item : m_selection )
1247 {
1248 if( item->Type() != SCH_PIN_T && item->Type() != SCH_SHEET_PIN_T )
1249 continue;
1250
1251 SCH_LINE* wire = new SCH_LINE( item->GetPosition(), LAYER_WIRE );
1252
1253 // Add some length to the wire as nothing in our code base handles
1254 // 0 length wires very well, least of all the ortho drag algorithm
1255 VECTOR2I stub;
1256
1257 switch( pinOrientation( item ) )
1258 {
1259 default:
1261 stub = VECTOR2I( -1 * wireGrid.x, 0 );
1262 break;
1264 stub = VECTOR2I( 1 * wireGrid.x, 0 );
1265 break;
1267 stub = VECTOR2I( 0, 1 * wireGrid.y );
1268 break;
1270 stub = VECTOR2I( 0, -1 * wireGrid.y );
1271 break;
1272 }
1273
1274 wire->SetEndPoint( item->GetPosition() + stub );
1275
1276 m_frame->AddToScreen( wire, sch_frame->GetScreen() );
1277 commit.Added( wire, sch_frame->GetScreen() );
1278 newItems.push_back( wire );
1279 }
1280
1281 if( !commit.Empty() )
1282 {
1284 AddItemsToSel( &newItems );
1285
1286 // Select only the ends so we can immediately start dragging them
1287 for( EDA_ITEM* item : newItems )
1288 static_cast<SCH_LINE*>( item )->SetFlags( ENDPOINT );
1289
1291
1292 // Put the mouse on the nearest point of the first wire
1293 SCH_LINE* first = static_cast<SCH_LINE*>( newItems[0] );
1294 vc->SetCrossHairCursorPosition( first->GetEndPoint(), false );
1295 vc->WarpMouseCursor( vc->GetCursorPosition(), true );
1296
1297 // Start the drag tool, canceling will remove the wires
1298 if( m_toolMgr->RunSynchronousAction( SCH_ACTIONS::drag, &commit ) )
1299 commit.Push( wxS( "Wire Pins" ) );
1300 else
1301 commit.Revert();
1302 }
1303 }
1304 else
1305 {
1306 // For every pin in the selection, add a label according to menu item
1307 // selected by the user
1308 for( EDA_ITEM* item : m_selection )
1309 {
1310 SCH_PIN* pin = dynamic_cast<SCH_PIN*>( item );
1311 SCH_SHEET_PIN* sheetPin = dynamic_cast<SCH_SHEET_PIN*>( item );
1312 SCH_LABEL_BASE* label = nullptr;
1313 SCH_SHEET_PATH& sheetPath = sch_frame->GetCurrentSheet();
1314
1315 wxString labelText;
1316
1317 if( pin )
1318 {
1319 labelText = pin->GetShownName();
1320
1321 if( labelText.IsEmpty() )
1322 {
1323 labelText.Printf( "%s_%s",
1324 pin->GetParentSymbol()->GetRef( &sheetPath ),
1325 pin->GetNumber() );
1326 }
1327 }
1328 else if( sheetPin )
1329 {
1330 labelText = sheetPin->GetShownText( &sheetPath, false );
1331 }
1332 else
1333 {
1334 continue;
1335 }
1336
1337 switch( *evt->GetCommandId() )
1338 {
1340 label = new SCH_LABEL( item->GetPosition(), labelText );
1341 break;
1343 label = new SCH_HIERLABEL( item->GetPosition(), labelText );
1344 break;
1346 label = new SCH_GLOBALLABEL( item->GetPosition(), labelText );
1347 break;
1348 default:
1349 continue;
1350 }
1351
1352 switch( pinOrientation( item ) )
1353 {
1354 default:
1357 break;
1360 break;
1363 break;
1366 break;
1367 }
1368
1370
1371 if( pin )
1372 {
1373 pinType = pin->GetType();
1374 }
1375 else if( sheetPin )
1376 {
1377 switch( sheetPin->GetLabelShape() )
1378 {
1379 case LABEL_INPUT: pinType = ELECTRICAL_PINTYPE::PT_INPUT; break;
1380 case LABEL_OUTPUT: pinType = ELECTRICAL_PINTYPE::PT_OUTPUT; break;
1381 case LABEL_BIDI: pinType = ELECTRICAL_PINTYPE::PT_BIDI; break;
1382 case LABEL_TRISTATE: pinType = ELECTRICAL_PINTYPE::PT_TRISTATE; break;
1383 case LABEL_PASSIVE: pinType = ELECTRICAL_PINTYPE::PT_PASSIVE; break;
1384 }
1385 }
1386
1387 switch( pinType )
1388 {
1391 break;
1394 break;
1397 break;
1400 break;
1403 break;
1404 default:
1406 }
1407
1408 commit.Add( label, sch_frame->GetScreen() );
1409 newItems.push_back( label );
1410 }
1411
1412 if( !commit.Empty() )
1413 {
1414 commit.Push( wxS( "Label Pins" ) );
1415
1416 // Many users will want to drag these items to wire off of the pins, so
1417 // pre-select them.
1419 AddItemsToSel( &newItems );
1420 }
1421 }
1422 }
1423 else if( *evt->GetCommandId() >= ID_POPUP_SCH_UNFOLD_BUS
1424 && *evt->GetCommandId() <= ID_POPUP_SCH_UNFOLD_BUS_END )
1425 {
1426 wxString* net = new wxString( *evt->Parameter<wxString*>() );
1427 m_toolMgr->RunAction<wxString*>( SCH_ACTIONS::unfoldBus, net );
1428 }
1429 }
1430 else if( evt->IsCancelInteractive() )
1431 {
1432 m_disambiguateTimer.Stop();
1433
1434 // We didn't set these, but we have reports that they leak out of some other tools,
1435 // so we clear them here.
1436 getViewControls()->SetAutoPan( false );
1437 getViewControls()->CaptureCursor( false );
1438
1439 if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
1440 schframe->ClearFocus();
1441
1442 if( !GetSelection().Empty() )
1443 {
1445 }
1446 else if( evt->FirstResponder() == this && evt->GetCommandId() == (int) WXK_ESCAPE )
1447 {
1448 if( m_enteredGroup )
1449 {
1450 ExitGroup();
1451 }
1452 else
1453 {
1455
1456 if( editor && m_frame->eeconfig()->m_Input.esc_clears_net_highlight )
1457 editor->ClearHighlight( *evt );
1458 }
1459 }
1460 }
1461 else if( evt->Action() == TA_UNDO_REDO_PRE )
1462 {
1463 if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
1464 schframe->ClearFocus();
1465 }
1466 else if( evt->IsMotion() && !m_isSymbolEditor && evt->FirstResponder() == this )
1467 {
1468 // Update cursor and rollover item
1469 rolloverItemId = niluuid;
1470 SCH_COLLECTOR collector;
1471
1473
1474 if( CollectHits( collector, evt->Position() ) )
1475 {
1476 narrowSelection( collector, evt->Position(), false, false, nullptr );
1477
1478 if( collector.GetCount() == 1 && !hasModifier() )
1479 {
1480 OPT_TOOL_EVENT autostartEvt = autostartEvent( evt, grid, collector[0] );
1481
1482 if( autostartEvt )
1483 {
1484 if( autostartEvt->Matches( SCH_ACTIONS::drawBus.MakeEvent() ) )
1485 displayBusCursor = true;
1486 else if( autostartEvt->Matches( SCH_ACTIONS::drawWire.MakeEvent() ) )
1487 displayWireCursor = true;
1488 else if( autostartEvt->Matches( SCH_ACTIONS::drawLines.MakeEvent() ) )
1489 displayLineCursor = true;
1490 }
1491 else if( collector[0]->HasHypertext() && !collector[0]->IsSelected() )
1492 {
1493 rolloverItemId = collector[0]->m_Uuid;
1494 }
1495 }
1496 }
1497 }
1498 else
1499 {
1500 evt->SetPassEvent();
1501 }
1502
1503 if( lastRolloverItemId != niluuid && lastRolloverItemId != rolloverItemId )
1504 {
1505 EDA_ITEM* item = m_frame->ResolveItem( lastRolloverItemId );
1506
1507 item->SetIsRollover( false, { 0, 0 } );
1508
1509 if( item->Type() == SCH_FIELD_T || item->Type() == SCH_TABLECELL_T )
1510 m_frame->GetCanvas()->GetView()->Update( item->GetParent() );
1511 else
1512 m_frame->GetCanvas()->GetView()->Update( item );
1513 }
1514
1515 SCH_ITEM* rolloverItem = nullptr;
1516
1517 if( rolloverItemId != niluuid )
1518 {
1519 rolloverItem = static_cast<SCH_ITEM*>( m_frame->ResolveItem( rolloverItemId ) );
1520
1521 rolloverItem->SetIsRollover( true, getViewControls()->GetMousePosition() );
1522
1523 if( rolloverItem->Type() == SCH_FIELD_T || rolloverItem->Type() == SCH_TABLECELL_T )
1524 m_frame->GetCanvas()->GetView()->Update( rolloverItem->GetParent() );
1525 else
1526 m_frame->GetCanvas()->GetView()->Update( rolloverItem );
1527 }
1528
1529 lastRolloverItemId = rolloverItemId;
1530
1531 if( m_frame->ToolStackIsEmpty() )
1532 {
1533 if( displayWireCursor )
1534 {
1536 }
1537 else if( displayBusCursor )
1538 {
1540 }
1541 else if( displayLineCursor )
1542 {
1544 }
1545 else if( rolloverItem && rolloverItem->HasHoveredHypertext() )
1546 {
1548 }
1549 else if( !m_selection.Empty()
1550 && drag_action == MOUSE_DRAG_ACTION::DRAG_SELECTED
1551 && evt->HasPosition()
1552 && selectionContains( evt->Position() ) //move/drag option prediction
1553 )
1554 {
1556 }
1557 else
1558 {
1560 }
1561 }
1562 }
1563
1564 m_disambiguateTimer.Stop();
1565
1566 // Shutting down; clear the selection
1567 m_selection.Clear();
1568
1569 return 0;
1570}
1571
1572
1574{
1575 wxCHECK_RET( m_selection.GetSize() == 1 && m_selection[0]->Type() == SCH_GROUP_T,
1576 wxT( "EnterGroup called when selection is not a single group" ) );
1577 SCH_GROUP* aGroup = static_cast<SCH_GROUP*>( m_selection[0] );
1578
1579 if( m_enteredGroup != nullptr )
1580 ExitGroup();
1581
1583 m_enteredGroup = aGroup;
1584 m_enteredGroup->SetFlags( ENTERED );
1585 m_enteredGroup->RunOnChildren(
1586 [&]( SCH_ITEM* aChild )
1587 {
1588 if( aChild->Type() == SCH_LINE_T )
1589 aChild->SetFlags( STARTPOINT | ENDPOINT );
1590
1591 select( aChild );
1592 },
1594
1595 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1596
1597 // Processing the selection event can re-enter the tool and ExitGroup(), which clears
1598 // m_enteredGroup. If that happened, don't operate on the now-stale (possibly null) group
1599 // or we would hide/overlay a null item and crash (issue #24778).
1600 if( m_enteredGroup != aGroup )
1601 return;
1602
1603 getView()->Hide( m_enteredGroup, true );
1606}
1607
1608
1609void SCH_SELECTION_TOOL::ExitGroup( bool aSelectGroup )
1610{
1611 // Only continue if there is a group entered
1612 if( m_enteredGroup == nullptr )
1613 return;
1614
1615 m_enteredGroup->ClearFlags( ENTERED );
1616 getView()->Hide( m_enteredGroup, false );
1618
1619 if( aSelectGroup )
1620 {
1622 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1623 }
1624
1625 m_enteredGroupOverlay.Clear();
1626 m_enteredGroup = nullptr;
1628}
1629
1630
1632 SCH_ITEM* aItem )
1633{
1634 VECTOR2I pos = aGrid.ResolveSnap( aEvent->Position(), aGrid.GetItemGrid( aItem ) ).position;
1635
1636 if( m_frame->eeconfig()->m_Drawing.auto_start_wires
1637 && !m_toolMgr->GetTool<SCH_POINT_EDITOR>()->HasPoint()
1638 && aItem->IsPointClickableAnchor( pos ) )
1639 {
1640 OPT_TOOL_EVENT newEvt = SCH_ACTIONS::drawWire.MakeEvent();
1641
1642 if( aItem->Type() == SCH_BUS_BUS_ENTRY_T )
1643 {
1644 newEvt = SCH_ACTIONS::drawBus.MakeEvent();
1645 }
1646 else if( aItem->Type() == SCH_BUS_WIRE_ENTRY_T )
1647 {
1648 SCH_BUS_WIRE_ENTRY* busEntry = static_cast<SCH_BUS_WIRE_ENTRY*>( aItem );
1649
1650 if( !busEntry->m_connected_bus_item )
1651 newEvt = SCH_ACTIONS::drawBus.MakeEvent();
1652 }
1653 else if( aItem->Type() == SCH_LINE_T )
1654 {
1655 SCH_LINE* line = static_cast<SCH_LINE*>( aItem );
1656
1657 if( line->IsBus() )
1658 newEvt = SCH_ACTIONS::drawBus.MakeEvent();
1659 else if( line->IsGraphicLine() )
1660 newEvt = SCH_ACTIONS::drawLines.MakeEvent();
1661 }
1662 else if( aItem->Type() == SCH_LABEL_T || aItem->Type() == SCH_HIER_LABEL_T
1663 || aItem->Type() == SCH_SHEET_PIN_T || aItem->Type() == SCH_GLOBAL_LABEL_T )
1664 {
1665 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( aItem );
1666 SCH_CONNECTION possibleConnection( label->Schematic()->ConnectionGraph() );
1667 possibleConnection.ConfigureFromLabel( label->GetShownText( false ) );
1668
1669 if( possibleConnection.IsBus() )
1670 newEvt = SCH_ACTIONS::drawBus.MakeEvent();
1671 }
1672 else if( aItem->Type() == SCH_SYMBOL_T )
1673 {
1674 const SCH_SYMBOL* symbol = static_cast<const SCH_SYMBOL*>( aItem );
1675 const SCH_PIN* pin = symbol->GetPin( pos );
1676
1677 if( !pin || !pin->IsPointClickableAnchor( pos ) )
1678 return OPT_TOOL_EVENT();
1679
1680 if( !pin->IsVisible()
1681 && !( m_frame->eeconfig()->m_Appearance.show_hidden_pins
1682 || m_frame->GetRenderSettings()->m_ShowHiddenPins ) )
1683 {
1684 return OPT_TOOL_EVENT();
1685 }
1686 }
1687
1688 newEvt->SetMousePosition( pos );
1689 newEvt->SetHasPosition( true );
1690 newEvt->SetForceImmediate( true );
1691
1692 getViewControls()->ForceCursorPosition( true, pos );
1693
1694 return newEvt;
1695 }
1696
1697 return OPT_TOOL_EVENT();
1698}
1699
1700
1702{
1703 wxMouseState keyboardState = wxGetMouseState();
1704
1705 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
1706 keyboardState.AltDown() );
1707
1708 m_skip_heuristics = true;
1711 m_skip_heuristics = false;
1712
1713 return 0;
1714}
1715
1716
1717void SCH_SELECTION_TOOL::OnIdle( wxIdleEvent& aEvent )
1718{
1719 if( m_frame->ToolStackIsEmpty() && !m_multiple )
1720 {
1721 wxMouseState keyboardState = wxGetMouseState();
1722
1723 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
1724 keyboardState.AltDown() );
1725
1726 if( m_additive )
1727 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ADD );
1728 else if( m_subtractive )
1729 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SUBTRACT );
1730 else if( m_exclusive_or )
1731 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::XOR );
1732 else
1733 m_frame->GetCanvas()->SetCurrentCursor( m_nonModifiedCursor );
1734 }
1735}
1736
1737
1742
1743
1745 const std::vector<KICAD_T>& aScanTypes )
1746{
1747 int pixelThreshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
1748 int gridThreshold = KiROUND( getView()->GetGAL()->GetGridSize().EuclideanNorm() / 2.0 );
1749 aCollector.m_Threshold = std::max( pixelThreshold, gridThreshold );
1750 aCollector.m_ShowPinElectricalTypes = m_frame->GetRenderSettings()->m_ShowPinsElectricalType;
1751
1752 if( m_isSymbolEditor )
1753 {
1754 LIB_SYMBOL* symbol = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
1755
1756 if( !symbol )
1757 return false;
1758
1759 aCollector.Collect( symbol->GetDrawItems(), aScanTypes, aWhere, m_unit, m_bodyStyle );
1760 }
1761 else
1762 {
1763 aCollector.Collect( m_frame->GetScreen(), aScanTypes, aWhere, m_unit, m_bodyStyle );
1764
1765 // If pins are disabled in the filter, they will be removed later. Let's add the parent
1766 // so that people can use pins to select symbols in this case.
1767 if( !m_filter.pins )
1768 {
1769 int originalCount = aCollector.GetCount();
1770
1771 for( int ii = 0; ii < originalCount; ++ii )
1772 {
1773 if( aCollector[ii]->Type() == SCH_PIN_T )
1774 {
1775 SCH_PIN* pin = static_cast<SCH_PIN*>( aCollector[ii] );
1776
1777 if( !aCollector.HasItem( pin->GetParentSymbol() ) )
1778 aCollector.Append( pin->GetParentSymbol() );
1779 }
1780 }
1781 }
1782 }
1783
1784 return aCollector.GetCount() > 0;
1785}
1786
1787
1789 bool aCheckLocked, bool aSelectedOnly,
1790 SCH_SELECTION_FILTER_OPTIONS* aRejected )
1791{
1792 SYMBOL_EDIT_FRAME* symbolEditorFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
1793
1794 for( int i = collector.GetCount() - 1; i >= 0; --i )
1795 {
1796 if( symbolEditorFrame )
1797 {
1798 // Do not select invisible items if they are not displayed
1799 EDA_ITEM* item = collector[i];
1800
1801 if( item->Type() == SCH_FIELD_T )
1802 {
1803 if( !static_cast<SCH_FIELD*>( item )->IsVisible()
1804 && !symbolEditorFrame->GetShowInvisibleFields() )
1805 {
1806 collector.Remove( i );
1807 continue;
1808 }
1809 }
1810 else if( item->Type() == SCH_PIN_T )
1811 {
1812 if( !static_cast<SCH_PIN*>( item )->IsVisible()
1813 && !symbolEditorFrame->GetShowInvisiblePins() )
1814 {
1815 collector.Remove( i );
1816 continue;
1817 }
1818 }
1819 }
1820
1821 if( !Selectable( collector[i], &aWhere ) )
1822 {
1823 collector.Remove( i );
1824 continue;
1825 }
1826
1827 if( aCheckLocked && collector[i]->IsLocked() )
1828 {
1829 if( aRejected )
1830 aRejected->lockedItems = true;
1831 collector.Remove( i );
1832 continue;
1833 }
1834
1835 if( !itemPassesFilter( collector[i], aRejected ) )
1836 {
1837 collector.Remove( i );
1838 continue;
1839 }
1840
1841 if( aSelectedOnly && !collector[i]->IsSelected() )
1842 {
1843 collector.Remove( i );
1844 continue;
1845 }
1846 }
1847
1848 FilterCollectorForHierarchy( collector, false );
1849
1850 // Apply some ugly heuristics to avoid disambiguation menus whenever possible
1851 if( collector.GetCount() > 1 && !m_skip_heuristics )
1852 GuessSelectionCandidates( collector, aWhere );
1853}
1854
1855
1857 EDA_ITEM** aItem, bool* aSelectionCancelledFlag, bool aAdd,
1858 bool aSubtract, bool aExclusiveOr )
1859{
1860 m_selection.ClearReferencePoint();
1861
1862 // If still more than one item we're going to have to ask the user.
1863 if( aCollector.GetCount() > 1 )
1864 {
1865 // Try to call selectionMenu via RunAction() to avoid event-loop contention
1866 // But it we cannot handle the event, then we don't have an active tool loop, so
1867 // handle it directly.
1868 if( !m_toolMgr->RunAction<COLLECTOR*>( ACTIONS::selectionMenu, &aCollector ) )
1869 {
1870 if( !doSelectionMenu( &aCollector ) )
1871 aCollector.m_MenuCancelled = true;
1872 }
1873
1874 if( aCollector.m_MenuCancelled )
1875 {
1876 if( aSelectionCancelledFlag )
1877 *aSelectionCancelledFlag = true;
1878
1879 return false;
1880 }
1881 }
1882
1883 if( !aAdd && !aSubtract && !aExclusiveOr )
1885
1886 // It is possible for slop in the selection model to cause us to be outside the group,
1887 // but also selecting an item within the group, so only exit if the selection doesn't
1888 // have an item belonging to the group
1889 if( m_enteredGroup && !m_enteredGroup->GetBoundingBox().Contains( aWhere ) )
1890 {
1891 bool foundEnteredGroup = false;
1892 for( EDA_ITEM* item : aCollector )
1893 {
1894 if( item->GetParentGroup() == m_enteredGroup )
1895 {
1896 foundEnteredGroup = true;
1897 break;
1898 }
1899 }
1900
1901 if( !foundEnteredGroup )
1902 ExitGroup();
1903 }
1904
1905 FilterCollectorForHierarchy( aCollector, true );
1906
1907 int addedCount = 0;
1908 bool anySubtracted = false;
1909
1910 if( aCollector.GetCount() > 0 )
1911 {
1912 for( int i = 0; i < aCollector.GetCount(); ++i )
1913 {
1914 EDA_ITEM_FLAGS flags = 0;
1915 bool isLine = aCollector[i]->Type() == SCH_LINE_T;
1916
1917 // Handle line ends specially
1918 if( isLine )
1919 {
1920 SCH_LINE* line = (SCH_LINE*) aCollector[i];
1921
1922 if( line->GetStartPoint().Distance( aWhere ) <= aCollector.m_Threshold )
1923 flags = STARTPOINT;
1924 else if( line->GetEndPoint().Distance( aWhere ) <= aCollector.m_Threshold )
1925 flags = ENDPOINT;
1926 else
1927 flags = STARTPOINT | ENDPOINT;
1928 }
1929
1930 if( aSubtract
1931 || ( aExclusiveOr && aCollector[i]->IsSelected()
1932 && ( !isLine || ( isLine && aCollector[i]->HasFlag( flags ) ) ) ) )
1933 {
1934 aCollector[i]->ClearFlags( flags );
1935
1936 // Need to update end shadows after ctrl-click unselecting one of two selected
1937 // endpoints.
1938 if( isLine )
1939 getView()->Update( aCollector[i] );
1940
1941 if( !aCollector[i]->HasFlag( STARTPOINT ) && !aCollector[i]->HasFlag( ENDPOINT ) )
1942 {
1943 unselect( aCollector[i] );
1944 anySubtracted = true;
1945 }
1946 }
1947 else
1948 {
1949 aCollector[i]->SetFlags( flags );
1950 select( aCollector[i] );
1951 addedCount++;
1952 }
1953 }
1954 }
1955
1956 if( addedCount == 1 )
1957 {
1958 m_toolMgr->ProcessEvent( EVENTS::PointSelectedEvent );
1959
1960 if( aItem && aCollector.GetCount() == 1 )
1961 *aItem = aCollector[0];
1962
1963 return true;
1964 }
1965 else if( addedCount > 1 )
1966 {
1967 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1968 return true;
1969 }
1970 else if( anySubtracted )
1971 {
1972 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1973 return true;
1974 }
1975
1976 m_frame->GetCanvas()->ForceRefresh();
1977 return false;
1978}
1979
1980
1982 const std::vector<KICAD_T>& aScanTypes,
1983 EDA_ITEM** aItem, bool* aSelectionCancelledFlag,
1984 bool aCheckLocked, bool aAdd, bool aSubtract,
1985 bool aExclusiveOr )
1986{
1987 SCH_COLLECTOR collector;
1988
1989 if( !CollectHits( collector, aWhere, aScanTypes ) )
1990 return false;
1991
1992 size_t preFilterCount = collector.GetCount();
1994 rejected.SetAll( false );
1995 narrowSelection( collector, aWhere, aCheckLocked, aSubtract, &rejected );
1996
1997 if( collector.GetCount() == 0 && preFilterCount > 0 )
1998 {
1999 if( SCH_BASE_FRAME* frame = dynamic_cast<SCH_BASE_FRAME*>( m_frame ) )
2000 frame->HighlightSelectionFilter( rejected );
2001
2002 if( !aAdd && !aSubtract && !aExclusiveOr && m_selection.GetSize() > 0 )
2003 {
2004 ClearSelection( true /*quiet mode*/ );
2005 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2006 }
2007
2008 return false;
2009 }
2010
2011 return selectPoint( collector, aWhere, aItem, aSelectionCancelledFlag, aAdd, aSubtract,
2012 aExclusiveOr );
2013}
2014
2015
2017{
2018 SCH_COLLECTOR collection;
2019 m_multiple = true; // Multiple selection mode is active
2020 KIGFX::VIEW* view = getView();
2021
2022 std::vector<EDA_ITEM*> sheetPins;
2023
2024 // Filter the view items based on the selection box
2025 BOX2I selectionBox;
2026
2027 selectionBox.SetMaximum();
2028 view->Query( selectionBox,
2029 [&]( KIGFX::VIEW_ITEM* viewItem ) -> bool
2030 {
2031 SCH_ITEM* item = static_cast<SCH_ITEM*>( viewItem );
2032
2033 if( !item )
2034 return true;
2035
2036 collection.Append( item );
2037 return true;
2038 } );
2039
2040 FilterCollectorForHierarchy( collection, true );
2041
2042 // Sheet pins aren't in the view; add them by hand
2043 for( EDA_ITEM* item : collection )
2044 {
2045 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item );
2046
2047 if( sheet )
2048 {
2049 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
2050 sheetPins.emplace_back( pin );
2051 }
2052 }
2053
2054 for( EDA_ITEM* pin : sheetPins )
2055 collection.Append( pin );
2056
2057 for( EDA_ITEM* item : collection )
2058 {
2059 if( Selectable( item ) && itemPassesFilter( item, nullptr ) )
2060 {
2061 if( item->Type() == SCH_LINE_T )
2062 item->SetFlags( STARTPOINT | ENDPOINT );
2063
2064 select( item );
2065 }
2066 }
2067
2068 m_multiple = false;
2069
2070 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2071 m_frame->GetCanvas()->ForceRefresh();
2072 return 0;
2073}
2074
2076{
2077 m_multiple = true; // Multiple selection mode is active
2078 KIGFX::VIEW* view = getView();
2079
2080 // hold all visible items
2081 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
2082
2083 // Filter the view items based on the selection box
2084 BOX2I selectionBox;
2085
2086 selectionBox.SetMaximum();
2087 view->Query( selectionBox, selectedItems ); // Get the list of selected items
2088
2089 for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : selectedItems )
2090 {
2091 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( pair.first );
2092
2093 if( sheet )
2094 {
2095 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
2096 {
2097 EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( pin );
2098
2099 if( item && Selectable( item ) )
2100 unselect( item );
2101 }
2102 }
2103
2104 if( EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( pair.first ) )
2105 {
2106 if( Selectable( item ) )
2107 unselect( item );
2108 }
2109 }
2110
2111 m_multiple = false;
2112
2113 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2114 m_frame->GetCanvas()->ForceRefresh();
2115 return 0;
2116}
2117
2118
2120{
2121 // Prefer exact hits to sloppy ones
2122 std::set<EDA_ITEM*> exactHits;
2123
2124 for( int i = collector.GetCount() - 1; i >= 0; --i )
2125 {
2126 EDA_ITEM* item = collector[ i ];
2127 SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
2128 SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( item );
2129 SCH_TABLE* table = dynamic_cast<SCH_TABLE*>( item );
2130
2131 // Lines are hard to hit. Give them a bit more slop to still be considered "exact".
2132 if( line || ( shape && shape->GetShape() == SHAPE_T::POLY )
2133 || ( shape && shape->GetShape() == SHAPE_T::ARC ) )
2134 {
2135 int pixelThreshold = KiROUND( getView()->ToWorld( 6 ) );
2136
2137 if( item->HitTest( aPos, pixelThreshold ) )
2138 exactHits.insert( item );
2139 }
2140 else if( table )
2141 {
2142 // Consider table cells exact, but not the table itself
2143 }
2144 else
2145 {
2146
2147 if( m_frame->GetRenderSettings()->m_ShowPinsElectricalType )
2148 item->SetFlags( SHOW_ELEC_TYPE );
2149
2150 if( item->HitTest( aPos, 0 ) )
2151 exactHits.insert( item );
2152
2153 item->ClearFlags( SHOW_ELEC_TYPE );
2154 }
2155 }
2156
2157 if( exactHits.size() > 0 && exactHits.size() < (unsigned) collector.GetCount() )
2158 {
2159 for( int i = collector.GetCount() - 1; i >= 0; --i )
2160 {
2161 EDA_ITEM* item = collector[ i ];
2162
2163 if( !exactHits.contains( item ) )
2164 collector.Transfer( item );
2165 }
2166 }
2167
2168 // Find the closest item. (Note that at this point all hits are either exact or non-exact.)
2169 SEG poss( aPos, aPos );
2170 EDA_ITEM* closest = nullptr;
2171 int closestDist = INT_MAX / 4;
2172
2173 for( EDA_ITEM* item : collector )
2174 {
2175 BOX2I bbox = item->GetBoundingBox();
2176 int dist = INT_MAX / 4;
2177
2178 // A dominating item is one that would unfairly win distance tests
2179 // and mask out other items. For example, a filled rectangle "wins"
2180 // with a zero distance over anything inside it.
2181 bool dominating = false;
2182
2183 if( exactHits.contains( item ) )
2184 {
2185 if( item->Type() == SCH_PIN_T || item->Type() == SCH_JUNCTION_T )
2186 {
2187 closest = item;
2188 break;
2189 }
2190
2191 SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
2192 SCH_FIELD* field = dynamic_cast<SCH_FIELD*>( item );
2193 EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item );
2194 EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( item );
2195 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
2196
2197 if( line )
2198 {
2199 dist = line->GetSeg().Distance( aPos );
2200 }
2201 else if( field )
2202 {
2203 BOX2I box = field->GetBoundingBox();
2204 EDA_ANGLE orient = field->GetTextAngle();
2205
2206 if( field->GetParent() && field->GetParent()->Type() == SCH_SYMBOL_T )
2207 {
2208 if( static_cast<SCH_SYMBOL*>( field->GetParent() )->GetTransform().y1 )
2209 {
2210 if( orient.IsHorizontal() )
2211 orient = ANGLE_VERTICAL;
2212 else
2213 orient = ANGLE_HORIZONTAL;
2214 }
2215 }
2216
2217 field->GetEffectiveTextShape( false, box, orient )->Collide( poss, INT_MAX / 4, &dist );
2218 }
2219 else if( text )
2220 {
2221 text->GetEffectiveTextShape( false )->Collide( poss, INT_MAX / 4, &dist );
2222 }
2223 else if( shape )
2224 {
2225 auto shapes = std::make_shared<SHAPE_COMPOUND>( shape->MakeEffectiveShapesForHitTesting() );
2226
2227 shapes->Collide( poss, INT_MAX / 4, &dist );
2228
2229 // Filled shapes win hit tests anywhere inside them
2230 dominating = shape->IsFilledForHitTesting();
2231 }
2232 else if( symbol )
2233 {
2234 bbox = symbol->GetBodyBoundingBox();
2235
2236 SHAPE_RECT rect( bbox.GetPosition(), bbox.GetWidth(), bbox.GetHeight() );
2237
2238 if( bbox.Contains( aPos ) )
2239 dist = bbox.GetCenter().Distance( aPos );
2240 else
2241 rect.Collide( poss, closestDist, &dist );
2242 }
2243 else
2244 {
2245 dist = bbox.GetCenter().Distance( aPos );
2246 }
2247 }
2248 else
2249 {
2250 SHAPE_RECT rect( bbox.GetPosition(), bbox.GetWidth(), bbox.GetHeight() );
2251 rect.Collide( poss, collector.m_Threshold, &dist );
2252 }
2253
2254 // Don't promote dominating items to be the closest item
2255 // (they'll always win) - they'll still be available for selection, but they
2256 // won't boot out worthy competitors.
2257 if ( !dominating )
2258 {
2259 if( dist == closestDist )
2260 {
2261 if( item->GetParent() == closest )
2262 closest = item;
2263 }
2264 else if( dist < closestDist )
2265 {
2266 closestDist = dist;
2267 closest = item;
2268 }
2269 }
2270 }
2271
2272 // Construct a tight box (1/2 height and width) around the center of the closest item.
2273 // All items which exist at least partly outside this box have sufficient other areas
2274 // for selection and can be dropped.
2275 if( closest ) // Don't try and get a tight bbox if nothing is near the mouse pointer
2276 {
2277 BOX2I tightBox = closest->GetBoundingBox();
2278 tightBox.Inflate( -tightBox.GetWidth() / 4, -tightBox.GetHeight() / 4 );
2279
2280 for( int i = collector.GetCount() - 1; i >= 0; --i )
2281 {
2282 EDA_ITEM* item = collector[i];
2283
2284 if( item == closest )
2285 continue;
2286
2287 if( !item->HitTest( tightBox, true ) )
2288 collector.Transfer( item );
2289 }
2290 }
2291}
2292
2293
2294SCH_SELECTION& SCH_SELECTION_TOOL::RequestSelection( const std::vector<KICAD_T>& aScanTypes,
2295 bool aPromoteCellSelections,
2296 bool aPromoteGroups )
2297{
2298 bool anyUnselected = false;
2299 bool anySelected = false;
2300
2301 if( m_selection.Empty() )
2302 {
2303 VECTOR2D cursorPos = getViewControls()->GetCursorPosition( true );
2304
2306 SelectPoint( cursorPos, aScanTypes );
2307 m_selection.SetIsHover( true );
2308 m_selection.ClearReferencePoint();
2309 }
2310 else // Trim an existing selection by aFilterList
2311 {
2312 bool isMoving = false;
2313
2314 for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
2315 {
2316 EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
2317 isMoving |= static_cast<SCH_ITEM*>( item )->IsMoving();
2318
2319 if( !item->IsType( aScanTypes ) )
2320 {
2321 unselect( item );
2322 anyUnselected = true;
2323 }
2324 }
2325
2326 if( !isMoving )
2328 }
2329
2330 if( aPromoteGroups )
2331 {
2332 for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
2333 {
2334 EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
2335
2336 std::set<EDA_ITEM*> selectedChildren;
2337
2338 if( item->Type() == SCH_GROUP_T )
2339 {
2340 static_cast<SCH_ITEM*>(item)->RunOnChildren( [&]( SCH_ITEM* aChild )
2341 {
2342 if( aChild->IsType( aScanTypes ) )
2343 selectedChildren.insert( aChild );
2344 },
2346 unselect( item );
2347 anyUnselected = true;
2348 }
2349
2350 for( EDA_ITEM* child : selectedChildren )
2351 {
2352 if( !child->IsSelected() )
2353 {
2354 if( child->Type() == SCH_LINE_T )
2355 static_cast<SCH_LINE*>( child )->SetFlags( STARTPOINT | ENDPOINT );
2356
2357 select( child );
2358 anySelected = true;
2359 }
2360 }
2361 }
2362 }
2363
2364 if( aPromoteCellSelections )
2365 {
2366 std::set<EDA_ITEM*> parents;
2367
2368 for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
2369 {
2370 EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
2371
2372 if( item->Type() == SCH_TABLECELL_T )
2373 {
2374 parents.insert( item->GetParent() );
2375 unselect( item );
2376 anyUnselected = true;
2377 }
2378 }
2379
2380 for( EDA_ITEM* parent : parents )
2381 {
2382 if( !parent->IsSelected() )
2383 {
2384 select( parent );
2385 anySelected = true;
2386 }
2387 }
2388 }
2389
2390 if( anyUnselected )
2391 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2392
2393 if( anySelected )
2394 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2395
2396 return m_selection;
2397}
2398
2399
2400void SCH_SELECTION_TOOL::filterCollectedItems( SCH_COLLECTOR& aCollector, bool aMultiSelect )
2401{
2402 if( aCollector.GetCount() == 0 )
2403 return;
2404
2405 std::set<EDA_ITEM*> rejected;
2406
2407 for( EDA_ITEM* item : aCollector )
2408 {
2409 if( !itemPassesFilter( item, nullptr ) )
2410 rejected.insert( item );
2411 }
2412
2413 for( EDA_ITEM* item : rejected )
2414 aCollector.Remove( item );
2415}
2416
2417
2419{
2420 if( !aItem )
2421 return false;
2422
2423 if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( aItem ) )
2424 {
2425 if( schItem->IsLocked() && !m_filter.lockedItems )
2426 {
2427 if( aRejected )
2428 aRejected->lockedItems = true;
2429
2430 return false;
2431 }
2432 }
2433
2434 switch( aItem->Type() )
2435 {
2436 case SCH_SYMBOL_T:
2437 case SCH_SHEET_T:
2438 if( !m_filter.symbols )
2439 {
2440 if( aRejected )
2441 aRejected->symbols = true;
2442 return false;
2443 }
2444
2445 break;
2446
2447 case SCH_PIN_T:
2448 case SCH_SHEET_PIN_T:
2449 if( !m_filter.pins )
2450 {
2451 if( aRejected )
2452 aRejected->pins = true;
2453 return false;
2454 }
2455
2456 break;
2457
2458 case SCH_JUNCTION_T:
2459 if( !m_filter.wires )
2460 {
2461 if( aRejected )
2462 aRejected->wires = true;
2463 return false;
2464 }
2465
2466 break;
2467
2468 case SCH_LINE_T:
2469 {
2470 switch( static_cast<SCH_LINE*>( aItem )->GetLayer() )
2471 {
2472 case LAYER_WIRE:
2473 case LAYER_BUS:
2474 if( !m_filter.wires )
2475 {
2476 if( aRejected )
2477 aRejected->wires = true;
2478 return false;
2479 }
2480
2481 break;
2482
2483 default:
2484 if( !m_filter.graphics )
2485 {
2486 if( aRejected )
2487 aRejected->graphics = true;
2488 return false;
2489 }
2490 }
2491
2492 break;
2493 }
2494
2495 case SCH_SHAPE_T:
2496 if( !m_filter.graphics )
2497 {
2498 if( aRejected )
2499 aRejected->graphics = true;
2500 return false;
2501 }
2502
2503 break;
2504
2505 case SCH_TEXT_T:
2506 case SCH_TEXTBOX_T:
2507 case SCH_TABLE_T:
2508 case SCH_TABLECELL_T:
2509 case SCH_FIELD_T:
2510 if( !m_filter.text )
2511 {
2512 if( aRejected )
2513 aRejected->text = true;
2514 return false;
2515 }
2516
2517 break;
2518
2519 case SCH_LABEL_T:
2520 case SCH_GLOBAL_LABEL_T:
2521 case SCH_HIER_LABEL_T:
2522 if( !m_filter.labels )
2523 {
2524 if( aRejected )
2525 aRejected->labels = true;
2526 return false;
2527 }
2528
2529 break;
2530
2531 case SCH_BITMAP_T:
2532 if( !m_filter.images )
2533 {
2534 if( aRejected )
2535 aRejected->images = true;
2536 return false;
2537 }
2538
2539 break;
2540
2541 case SCH_RULE_AREA_T:
2542 if( !m_filter.ruleAreas )
2543 {
2544 if( aRejected )
2545 aRejected->ruleAreas = true;
2546 return false;
2547 }
2548
2549 break;
2550
2551 default:
2552 if( !m_filter.otherItems )
2553 {
2554 if( aRejected )
2555 aRejected->otherItems = true;
2556 return false;
2557 }
2558
2559 break;
2560 }
2561
2562 return true;
2563}
2564
2565
2567{
2568 VECTOR2I refP( 0, 0 );
2569
2570 if( m_selection.Size() > 0 )
2571 refP = static_cast<SCH_ITEM*>( m_selection.GetTopLeftItem() )->GetPosition();
2572
2573 m_selection.SetReferencePoint( refP );
2574}
2575
2576
2578{
2580 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
2581 m_toolMgr->PostAction( ACTIONS::selectionTool );
2582 return 0;
2583}
2584
2585
2587{
2589 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2590 m_toolMgr->PostAction( ACTIONS::selectionTool );
2591 return 0;
2592}
2593
2594
2595// Some navigation actions are allowed in selectMultiple
2606
2607
2609{
2610 // Block selection not allowed in symbol viewer frame: no actual code to handle
2611 // a selection, so return to avoid to draw a selection rectangle, and to avoid crashes.
2612 if( m_frame->IsType( FRAME_T::FRAME_SCH_VIEWER ) )
2613 return false;
2614
2615 bool cancelled = false; // Was the tool canceled while it was running?
2616 m_multiple = true; // Multiple selection mode is active
2617 KIGFX::VIEW* view = getView();
2618
2620 view->Add( &area );
2621
2622 while( TOOL_EVENT* evt = Wait() )
2623 {
2624 /* Selection mode depends on direction of drag-selection:
2625 * Left > Right : Select objects that are fully enclosed by selection
2626 * Right > Left : Select objects that are crossed by selection
2627 */
2628 bool isGreedy = area.GetEnd().x < area.GetOrigin().x;
2629
2630 if( view->IsMirroredX() )
2631 isGreedy = !isGreedy;
2632
2633 m_frame->GetCanvas()->SetCurrentCursor( isGreedy ? KICURSOR::SELECT_LASSO
2635
2636 if( evt->IsCancelInteractive() || evt->IsActivate() )
2637 {
2638 cancelled = true;
2639 break;
2640 }
2641
2642 if( evt->IsDrag( BUT_LEFT ) )
2643 {
2646
2647 // Start drawing a selection box
2648 area.SetOrigin( evt->DragOrigin() );
2649 area.SetEnd( evt->Position() );
2652 area.SetExclusiveOr( false );
2655
2656 view->SetVisible( &area, true );
2657 view->Update( &area );
2658 getViewControls()->SetAutoPan( true );
2659 }
2660
2661 if( evt->IsMouseUp( BUT_LEFT ) )
2662 {
2663 getViewControls()->SetAutoPan( false );
2664 view->SetVisible( &area, false );
2665 SelectMultiple( area, m_drag_subtractive, false );
2666 evt->SetPassEvent( false );
2667 break;
2668 }
2669
2670 passEvent( evt, allowedActions );
2671 }
2672
2673 getViewControls()->SetAutoPan( false );
2674
2675 // Stop drawing the selection box
2676 view->Remove( &area );
2677 m_multiple = false; // Multiple selection mode is inactive
2678
2679 if( !cancelled )
2680 m_selection.ClearReferencePoint();
2681
2682 return cancelled;
2683}
2684
2685
2687{
2688 bool cancelled = false;
2689 m_multiple = true;
2691 getView()->Add( &area );
2692 getView()->SetVisible( &area, true );
2693 getViewControls()->SetAutoPan( true );
2694
2695 SHAPE_LINE_CHAIN points;
2696 points.SetClosed( true );
2697
2699 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
2700
2701 while( TOOL_EVENT* evt = Wait() )
2702 {
2703 double shapeArea = area.GetPoly().Area( false );
2704 bool isClockwise = shapeArea > 0 ? true : false;
2705
2706 if( getView()->IsMirroredX() && shapeArea != 0 )
2707 isClockwise = !isClockwise;
2708
2709 if( isClockwise )
2710 {
2711 selectionMode = SELECTION_MODE::INSIDE_LASSO;
2712 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_WINDOW );
2713 }
2714 else
2715 {
2716 selectionMode = SELECTION_MODE::TOUCHING_LASSO;
2717 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
2718 }
2719
2720 if( evt->IsCancelInteractive() || evt->IsActivate() )
2721 {
2722 cancelled = true;
2723 break;
2724 }
2725 else if( evt->IsDrag( BUT_LEFT )
2726 || evt->IsClick( BUT_LEFT )
2727 || evt->IsAction( &ACTIONS::cursorClick ) )
2728 {
2729 points.Append( evt->Position() );
2730 }
2731 else if( evt->IsDblClick( BUT_LEFT )
2732 || evt->IsAction( &ACTIONS::cursorDblClick )
2733 || evt->IsAction( &ACTIONS::finishInteractive ) )
2734 {
2735 area.GetPoly().GenerateBBoxCache();
2736 SelectMultiple( area, m_drag_subtractive, false );
2737 break;
2738 }
2739 else if( evt->IsAction( &ACTIONS::doDelete )
2740 || evt->IsAction( &ACTIONS::undo ) )
2741 {
2742 if( points.GetPointCount() > 0 )
2743 {
2745 points.Remove( points.GetPointCount() - 1 );
2746 }
2747 }
2748 else
2749 {
2750 passEvent( evt, allowedActions );
2751 }
2752
2753 if( points.PointCount() > 0 )
2754 {
2756 {
2757 if( m_selection.GetSize() > 0 )
2758 {
2759 ClearSelection( true );
2760 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2761 }
2762 }
2763 }
2764
2765 area.SetPoly( points );
2766 area.GetPoly().Append( m_toolMgr->GetMousePosition() );
2769 area.SetExclusiveOr( false );
2770 area.SetMode( selectionMode );
2771 getView()->Update( &area );
2772 }
2773
2774 getViewControls()->SetAutoPan( false );
2775 getView()->SetVisible( &area, false );
2776 getView()->Remove( &area );
2777 m_multiple = false;
2778
2779 if( !cancelled )
2780 m_selection.ClearReferencePoint();
2781
2782 return cancelled;
2783}
2784
2785
2787 bool aExclusiveOr )
2788{
2789 KIGFX::VIEW* view = getView();
2790
2791 SELECTION_MODE selectionMode = aArea.GetMode();
2792 bool containedMode = ( selectionMode == SELECTION_MODE::INSIDE_RECTANGLE
2793 || selectionMode == SELECTION_MODE::INSIDE_LASSO );
2794 bool boxMode = ( selectionMode == SELECTION_MODE::INSIDE_RECTANGLE
2795 || selectionMode == SELECTION_MODE::TOUCHING_RECTANGLE );
2796
2797 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> candidates;
2798 BOX2I selectionRect = aArea.ViewBBox();
2799 view->Query( selectionRect, candidates );
2800
2801 std::set<SCH_ITEM*> uniqueCandidates;
2802
2803 for( const auto& [viewItem, layer] : candidates )
2804 {
2805 if( viewItem->IsSCH_ITEM() )
2806 uniqueCandidates.insert( static_cast<SCH_ITEM*>( viewItem ) );
2807 }
2808
2809 for( KIGFX::VIEW_ITEM* item : uniqueCandidates )
2810 {
2811 if( SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item ) )
2812 {
2813 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
2814 {
2815 if( boxMode ? selectionRect.Intersects( pin->GetBoundingBox() )
2816 : KIGEOM::BoxHitTest( aArea.GetPoly(), pin->GetBoundingBox(), true ) )
2817 uniqueCandidates.insert( pin );
2818 }
2819 }
2820 else if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item ) )
2821 {
2822 for( SCH_PIN* pin : symbol->GetPins() )
2823 {
2824 if( boxMode ? selectionRect.Intersects( pin->GetBoundingBox() )
2825 : KIGEOM::BoxHitTest( aArea.GetPoly(), pin->GetBoundingBox(), true ) )
2826 uniqueCandidates.insert( pin );
2827 }
2828
2829 for( SCH_FIELD& field : symbol->GetFields() )
2830 {
2831 if( field.IsVisible()
2832 && ( boxMode ? selectionRect.Intersects( field.GetBoundingBox() )
2833 : KIGEOM::BoxHitTest( aArea.GetPoly(), field.GetBoundingBox(), true ) ) )
2834 {
2835 uniqueCandidates.insert( &field );
2836 }
2837 }
2838 }
2839 }
2840
2841 SCH_COLLECTOR collector;
2842 SCH_COLLECTOR pinsCollector;
2843 std::set<EDA_ITEM*> group_items;
2844
2845 for( EDA_ITEM* item : m_frame->GetScreen()->Items().OfType( SCH_GROUP_T ) )
2846 {
2847 SCH_GROUP* group = static_cast<SCH_GROUP*>( item );
2848
2849 if( m_enteredGroup == group )
2850 continue;
2851
2852 std::unordered_set<EDA_ITEM*>& newset = group->GetItems();
2853
2854 auto boxContained =
2855 [&]( const BOX2I& aBox )
2856 {
2857 return boxMode ? selectionRect.Contains( aBox )
2858 : KIGEOM::BoxHitTest( aArea.GetPoly(), aBox, true );
2859 };
2860
2861 if( containedMode && boxContained( group->GetBoundingBox() ) && newset.size() )
2862 {
2863 for( EDA_ITEM* group_item : newset )
2864 {
2865 if( !group_item->IsSCH_ITEM() )
2866 continue;
2867
2868 if( Selectable( static_cast<SCH_ITEM*>( group_item ) ) )
2869 collector.Append( group_item );
2870 }
2871 }
2872
2873 for( EDA_ITEM* group_item : newset )
2874 group_items.emplace( group_item );
2875 }
2876
2877 auto hitTest =
2878 [&]( SCH_ITEM* aItem )
2879 {
2880 return boxMode ? aItem->HitTest( selectionRect, containedMode )
2881 : aItem->HitTest( aArea.GetPoly(), containedMode );
2882 };
2883
2884 for( SCH_ITEM* item : uniqueCandidates )
2885 {
2886 if( Selectable( item ) && ( hitTest( item ) || item->Type() == SCH_LINE_T )
2887 && ( !containedMode || !group_items.count( item ) ) )
2888 {
2889 if( item->Type() == SCH_PIN_T && !m_isSymbolEditor )
2890 pinsCollector.Append( item );
2891 else
2892 collector.Append( item );
2893 }
2894 }
2895
2896 filterCollectedItems( collector, true );
2897 FilterCollectorForHierarchy( collector, true );
2898
2899 if( collector.GetCount() == 0 )
2900 {
2901 collector = pinsCollector;
2902 filterCollectedItems( collector, true );
2903 FilterCollectorForHierarchy( collector, true );
2904 }
2905
2906 std::sort( collector.begin(), collector.end(),
2907 []( EDA_ITEM* a, EDA_ITEM* b )
2908 {
2909 VECTOR2I aPos = a->GetPosition();
2910 VECTOR2I bPos = b->GetPosition();
2911
2912 if( aPos.y == bPos.y )
2913 return aPos.x < bPos.x;
2914
2915 return aPos.y < bPos.y;
2916 } );
2917
2918 bool anyAdded = false;
2919 bool anySubtracted = false;
2920
2921 auto selectItem =
2922 [&]( EDA_ITEM* aItem, EDA_ITEM_FLAGS flags )
2923 {
2924 if( aSubtractive || ( aExclusiveOr && aItem->IsSelected() ) )
2925 {
2926 if( aExclusiveOr )
2927 aItem->XorFlags( flags );
2928 else
2929 aItem->ClearFlags( flags );
2930
2931 if( !aItem->HasFlag( STARTPOINT ) && !aItem->HasFlag( ENDPOINT ) )
2932 {
2933 unselect( aItem );
2934 anySubtracted = true;
2935 }
2936
2937 if( flags && !anySubtracted )
2938 getView()->Update( aItem );
2939 }
2940 else
2941 {
2942 aItem->SetFlags( flags );
2943 select( aItem );
2944 anyAdded = true;
2945 }
2946 };
2947
2948 std::vector<EDA_ITEM*> flaggedItems;
2949
2950 auto shapeContains =
2951 [&]( const VECTOR2I& aPoint )
2952 {
2953 return boxMode ? selectionRect.Contains( aPoint )
2954 : aArea.GetPoly().PointInside( aPoint );
2955 };
2956
2957 for( EDA_ITEM* item : collector )
2958 {
2959 EDA_ITEM_FLAGS flags = 0;
2960
2961 item->SetFlags( SELECTION_CANDIDATE );
2962 flaggedItems.push_back( item );
2963
2964 if( m_frame->GetRenderSettings()->m_ShowPinsElectricalType )
2965 item->SetFlags( SHOW_ELEC_TYPE );
2966
2967 if( item->Type() == SCH_LINE_T )
2968 {
2969 SCH_LINE* line = static_cast<SCH_LINE*>( item );
2970 bool hits = false;
2971
2972 if( boxMode )
2973 hits = line->HitTest( selectionRect, false );
2974 else
2975 hits = line->HitTest( aArea.GetPoly(), false );
2976
2977 if( ( !containedMode && hits )
2978 || ( shapeContains( line->GetEndPoint() ) && shapeContains( line->GetStartPoint() ) ) )
2979 {
2980 flags |= STARTPOINT | ENDPOINT;
2981 }
2982 else if( containedMode )
2983 {
2984 if( shapeContains( line->GetStartPoint() ) && line->IsStartDangling() )
2985 flags |= STARTPOINT;
2986
2987 if( shapeContains( line->GetEndPoint() ) && line->IsEndDangling() )
2988 flags |= ENDPOINT;
2989 }
2990
2991 if( flags & ( STARTPOINT | ENDPOINT ) )
2992 selectItem( item, flags );
2993 }
2994 else
2995 selectItem( item, flags );
2996
2997 item->ClearFlags( SHOW_ELEC_TYPE );
2998 }
2999
3000 for( EDA_ITEM* item : pinsCollector )
3001 {
3002 if( m_frame->GetRenderSettings()->m_ShowPinsElectricalType )
3003 item->SetFlags( SHOW_ELEC_TYPE );
3004
3005 // If the pin lives inside a group that is already being selected, don't also select the pin.
3006 if( EDA_GROUP* group =
3008 {
3009 if( collector.HasItem( group->AsEdaItem() ) )
3010 {
3011 item->ClearFlags( SHOW_ELEC_TYPE );
3012 continue;
3013 }
3014 }
3015
3016 if( Selectable( item ) && itemPassesFilter( item, nullptr )
3017 && !item->GetParent()->HasFlag( SELECTION_CANDIDATE ) && hitTest( static_cast<SCH_ITEM*>( item ) ) )
3018 {
3019 selectItem( item, 0 );
3020 }
3021
3022 item->ClearFlags( SHOW_ELEC_TYPE );
3023 }
3024
3025 for( EDA_ITEM* item : flaggedItems )
3026 item->ClearFlags( SELECTION_CANDIDATE );
3027
3028 m_selection.SetIsHover( false );
3029
3030 if( anyAdded )
3031 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3032 else if( anySubtracted )
3033 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
3034}
3035
3036
3037void SCH_SELECTION_TOOL::FilterCollectorForHierarchy( SCH_COLLECTOR& aCollector, bool aMultiselect ) const
3038{
3039 std::unordered_set<EDA_ITEM*> toAdd;
3040
3041 // Set SELECTION_CANDIDATE on all parents which are included in the GENERAL_COLLECTOR. This
3042 // algorithm is O(3n), whereas checking for the parent inclusion could potentially be O(n^2).
3043 for( int j = 0; j < aCollector.GetCount(); j++ )
3044 {
3045 if( aCollector[j]->GetParent() )
3046 aCollector[j]->GetParent()->ClearFlags( SELECTION_CANDIDATE );
3047
3048 if( aCollector[j]->GetParentSymbol() )
3049 aCollector[j]->GetParentSymbol()->ClearFlags( SELECTION_CANDIDATE );
3050 }
3051
3052 if( aMultiselect )
3053 {
3054 for( int j = 0; j < aCollector.GetCount(); j++ )
3055 aCollector[j]->SetFlags( SELECTION_CANDIDATE );
3056 }
3057
3058 // Skip group promotion when the caller asked for specific types that exclude groups
3059 const std::vector<KICAD_T>& scanTypes = aCollector.GetScanTypes();
3060 bool promoteToGroups = scanTypes.empty() || alg::contains( scanTypes, SCH_LOCATE_ANY_T )
3061 || alg::contains( scanTypes, SCH_GROUP_T );
3062
3063 for( int j = 0; j < aCollector.GetCount(); )
3064 {
3065 SCH_ITEM* item = aCollector[j];
3066 SYMBOL* sym = item->GetParentSymbol();
3067 SCH_ITEM* start = item;
3068
3069 if( !m_isSymbolEditor && sym )
3070 start = sym;
3071
3072 // If a group is entered, disallow selections of objects outside the group.
3074 {
3075 aCollector.Remove( item );
3076 continue;
3077 }
3078
3079 // If any element is a member of a group, replace those elements with the top containing
3080 // group.
3081 if( EDA_GROUP* top =
3082 promoteToGroups ? SCH_GROUP::TopLevelGroup( start, m_enteredGroup, m_isSymbolEditor ) : nullptr )
3083 {
3084 if( top->AsEdaItem() != item )
3085 {
3086 toAdd.insert( top->AsEdaItem() );
3087 top->AsEdaItem()->SetFlags( SELECTION_CANDIDATE );
3088
3089 aCollector.Remove( item );
3090 continue;
3091 }
3092 }
3093
3094 // Symbols are a bit easier as they can't be nested.
3095 if( sym && ( sym->GetFlags() & SELECTION_CANDIDATE ) )
3096 {
3097 // Remove children of selected items
3098 aCollector.Remove( item );
3099 continue;
3100 }
3101
3102 ++j;
3103 }
3104
3105 for( EDA_ITEM* item : toAdd )
3106 {
3107 if( !aCollector.HasItem( item ) )
3108 aCollector.Append( item );
3109 }
3110}
3111
3112
3114{
3115 if( m_frame && m_frame->GetOverrideLocks() )
3116 return;
3117
3118 std::vector<EDA_ITEM*> toRemove;
3119
3120 for( EDA_ITEM* item : m_selection )
3121 {
3122 if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( item ) )
3123 {
3124 bool lockedDescendant = false;
3125
3126 schItem->RunOnChildren(
3127 [&]( SCH_ITEM* child )
3128 {
3129 if( child->IsLocked() )
3130 lockedDescendant = true;
3131 },
3133
3134 if( schItem->IsLocked() || lockedDescendant )
3135 toRemove.push_back( item );
3136 }
3137 }
3138
3139 for( EDA_ITEM* item : toRemove )
3140 RemoveItemFromSel( item, true /* quiet mode */ );
3141}
3142
3143
3145{
3146 getView()->Update( &m_selection );
3148
3149 return 0;
3150}
3151
3152
3154{
3155 for( SCH_TABLECELL* cell : aTable->GetCells() )
3156 {
3157 if( cell->IsSelected() )
3158 cell->SetFlags( SELECTION_CANDIDATE );
3159 else
3160 cell->ClearFlags( SELECTION_CANDIDATE );
3161 }
3162}
3163
3165{
3166 BOX2I selectionRect( start, end );
3167 selectionRect.Normalize();
3168
3169 auto wasSelected = []( EDA_ITEM* aItem )
3170 {
3171 return ( aItem->GetFlags() & SELECTION_CANDIDATE ) > 0;
3172 };
3173
3174 for( SCH_TABLECELL* cell : aTable->GetCells() )
3175 {
3176 bool doSelect = false;
3177
3178 if( cell->HitTest( selectionRect, false ) )
3179 {
3180 if( m_subtractive )
3181 doSelect = false;
3182 else if( m_exclusive_or )
3183 doSelect = !wasSelected( cell );
3184 else
3185 doSelect = true;
3186 }
3187 else if( wasSelected( cell ) )
3188 {
3189 doSelect = m_additive || m_subtractive || m_exclusive_or;
3190 }
3191
3192 if( doSelect && !cell->IsSelected() )
3193 select( cell );
3194 else if( !doSelect && cell->IsSelected() )
3195 unselect( cell );
3196 }
3197}
3198
3200{
3201 bool cancelled = false;
3202 m_multiple = true;
3203
3204 InitializeSelectionState( aTable );
3205
3206 while( TOOL_EVENT* evt = Wait() )
3207 {
3208 if( evt->IsCancelInteractive() || evt->IsActivate() )
3209 {
3210 cancelled = true;
3211 break;
3212 }
3213 else if( evt->IsDrag( BUT_LEFT ) )
3214 {
3215 getViewControls()->SetAutoPan( true );
3216 SelectCellsBetween( evt->DragOrigin(), evt->Position() - evt->DragOrigin(), aTable );
3217 }
3218 else if( evt->IsMouseUp( BUT_LEFT ) )
3219 {
3220 m_selection.SetIsHover( false );
3221
3222 bool anyAdded = false;
3223 bool anySubtracted = false;
3224
3225 for( SCH_TABLECELL* cell : aTable->GetCells() )
3226 {
3227 if( cell->IsSelected() && ( cell->GetFlags() & SELECTION_CANDIDATE ) <= 0 )
3228 anyAdded = true;
3229 else if( ( cell->GetFlags() & SELECTION_CANDIDATE ) > 0 && !cell->IsSelected() )
3230 anySubtracted = true;
3231 }
3232
3233 if( anyAdded )
3234 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3235 if( anySubtracted )
3236 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
3237
3238 break;
3239 }
3240 else
3241 {
3242 for( int i = 0; allowedActions[i]; ++i )
3243 {
3244 if( evt->IsAction( allowedActions[i] ) )
3245 {
3246 evt->SetPassEvent();
3247 break;
3248 }
3249 }
3250 }
3251 }
3252
3253 getViewControls()->SetAutoPan( false );
3254
3255 m_multiple = false;
3256
3257 if( !cancelled )
3258 m_selection.ClearReferencePoint();
3259
3260 return cancelled;
3261}
3262
3263
3265{
3266 SCH_COLLECTOR collector;
3267
3268 //TODO(snh): Reimplement after exposing KNN interface
3269 int pixelThreshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
3270 int gridThreshold = KiROUND( getView()->GetGAL()->GetGridSize().EuclideanNorm() );
3271 int thresholdMax = std::max( pixelThreshold, gridThreshold );
3272
3273 for( int threshold : { 0, thresholdMax/4, thresholdMax/2, thresholdMax } )
3274 {
3275 collector.m_Threshold = threshold;
3276 collector.Collect( m_frame->GetScreen(), connectedTypes, aPosition );
3277
3278 if( collector.GetCount() > 0 )
3279 break;
3280 }
3281
3282 return collector.GetCount() ? collector[ 0 ] : nullptr;
3283}
3284
3285
3287{
3288 VECTOR2I cursorPos = getViewControls()->GetCursorPosition( false );
3289
3290 SelectPoint( cursorPos, connectedTypes );
3291 return 0;
3292}
3293
3294
3295std::set<SCH_ITEM*>
3297 STOP_CONDITION aStopCondition )
3298{
3299 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
3300
3301 if( m_isSymbolEditor || m_isSymbolViewer || !editFrame )
3302 return {};
3303
3304 CONNECTION_GRAPH* graph = editFrame->Schematic().ConnectionGraph();
3305
3306 if( !graph )
3307 return {};
3308
3309 SCH_SCREEN* screen = m_frame->GetScreen();
3310 SCH_SHEET_PATH& currentSheet = editFrame->GetCurrentSheet();
3311 std::vector<SCH_ITEM*> startItems;
3312 std::set<SCH_ITEM*> added;
3313
3314 for( auto item : aItems )
3315 {
3316 if( !item->IsSCH_ITEM() )
3317 continue;
3318
3319 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
3320
3321 if( schItem->Type() == SCH_SYMBOL_T )
3322 {
3323 for( SCH_PIN* pin : static_cast<SCH_SYMBOL*>( schItem )->GetPins( &currentSheet ) )
3324 {
3325 if( pin )
3326 startItems.push_back( pin );
3327 }
3328 }
3329 else if( schItem->IsConnectable() )
3330 {
3331 startItems.push_back( schItem );
3332 }
3333 }
3334
3335 if( startItems.empty() )
3336 return {};
3337
3338 // Pre-compute which start items belong to symbols already in the original selection so that
3339 // pin-stop traversal can step away from those symbols without immediately bouncing back.
3340 std::unordered_set<SCH_SYMBOL*> startSymbols;
3341
3342 for( SCH_ITEM* item : startItems )
3343 {
3344 if( SCH_PIN* pin = dynamic_cast<SCH_PIN*>( item ) )
3345 {
3346 if( SCH_SYMBOL* parent = dynamic_cast<SCH_SYMBOL*>( pin->GetParent() ) )
3347 startSymbols.insert( parent );
3348 }
3349 }
3350
3351 // Cache every pin position on the sheet so endpoint tests are O(log n) lookups instead of
3352 // an R-tree query plus a full pin iteration per call.
3353 std::set<VECTOR2I> pinPositions;
3354
3355 for( SCH_ITEM* it : screen->Items().OfType( SCH_SYMBOL_T ) )
3356 {
3357 for( SCH_PIN* pin : static_cast<SCH_SYMBOL*>( it )->GetPins( &currentSheet ) )
3358 {
3359 if( pin )
3360 pinPositions.insert( pin->GetPosition() );
3361 }
3362 }
3363
3364 auto isStopPoint = [&]( const VECTOR2I& aPoint ) -> bool
3365 {
3366 if( aStopCondition == STOP_CONDITION::STOP_NEVER )
3367 return false;
3368
3369 if( pinPositions.count( aPoint ) )
3370 return true;
3371
3372 if( aStopCondition == STOP_CONDITION::STOP_AT_PIN )
3373 return false;
3374
3375 if( screen->IsJunction( aPoint ) || screen->IsExplicitJunction( aPoint ) )
3376 return true;
3377
3378 for( SCH_ITEM* it : screen->Items().Overlapping( aPoint ) )
3379 {
3380 switch( it->Type() )
3381 {
3382 case SCH_LABEL_T:
3383 case SCH_GLOBAL_LABEL_T:
3384 case SCH_HIER_LABEL_T:
3386 case SCH_SHEET_PIN_T:
3387 case SCH_NO_CONNECT_T:
3388 if( it->IsConnected( aPoint ) )
3389 return true;
3390
3391 break;
3392
3393 default:
3394 break;
3395 }
3396 }
3397
3398 return false;
3399 };
3400
3401 // STOP_AT_JUNCTION refuses to pull a symbol into the selection unless the user already had
3402 // a symbol selected; later passes accept every reachable symbol.
3403 auto shouldPullInSymbol = [&]()
3404 {
3405 return aStopCondition != STOP_CONDITION::STOP_AT_JUNCTION || !startSymbols.empty();
3406 };
3407
3408 std::deque<SCH_ITEM*> queue;
3409 std::unordered_set<SCH_ITEM*> visited;
3410
3411 auto enqueue = [&]( SCH_ITEM* aItem )
3412 {
3413 if( !aItem )
3414 return;
3415
3416 if( visited.insert( aItem ).second )
3417 queue.push_back( aItem );
3418 };
3419
3420 for( SCH_ITEM* item : startItems )
3421 enqueue( item );
3422
3423 while( !queue.empty() )
3424 {
3425 SCH_ITEM* item = queue.front();
3426 queue.pop_front();
3427
3428 if( SCH_PIN* pin = dynamic_cast<SCH_PIN*>( item ) )
3429 {
3430 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParent() );
3431
3432 if( shouldPullInSymbol() && symbol && Selectable( symbol )
3433 && itemPassesFilter( symbol, nullptr ) && !symbol->IsSelected() )
3434 {
3435 added.insert( symbol );
3436 }
3437 }
3438
3439 SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
3440 std::vector<VECTOR2I> openPoints;
3441
3442 if( line && aStopCondition != STOP_CONDITION::STOP_NEVER )
3443 {
3444 for( const VECTOR2I& pt : { line->GetStartPoint(), line->GetEndPoint() } )
3445 {
3446 if( !isStopPoint( pt ) )
3447 openPoints.push_back( pt );
3448 }
3449 }
3450
3451 for( SCH_ITEM* neighbor : item->ConnectedItems( currentSheet ) )
3452 {
3453 if( !neighbor )
3454 continue;
3455
3456 if( neighbor->Type() == SCH_SYMBOL_T )
3457 {
3458 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( neighbor );
3459
3460 if( shouldPullInSymbol() && Selectable( symbol ) && itemPassesFilter( symbol, nullptr )
3461 && !symbol->IsSelected() )
3462 {
3463 added.insert( symbol );
3464 }
3465
3466 continue;
3467 }
3468
3469 // Wires gate traversal on open endpoints; items without distinct endpoints always flow.
3470 if( line && aStopCondition != STOP_CONDITION::STOP_NEVER )
3471 {
3472 bool sharesOpenPoint = false;
3473
3474 for( const VECTOR2I& pt : openPoints )
3475 {
3476 if( neighbor->IsConnected( pt ) )
3477 {
3478 sharesOpenPoint = true;
3479 break;
3480 }
3481 }
3482
3483 if( !sharesOpenPoint )
3484 continue;
3485 }
3486
3487 enqueue( neighbor );
3488 }
3489
3490 if( !Selectable( item ) || !itemPassesFilter( item, nullptr ) )
3491 continue;
3492
3493 added.insert( item );
3494 }
3495
3496 return added;
3497}
3498
3499
3501{
3502 std::set<SCH_ITEM*> added;
3503
3504 for( auto item : aItems )
3505 {
3506 if( !item->IsSCH_ITEM() )
3507 continue;
3508
3509 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
3510
3511 std::set<SCH_ITEM*> conns = m_frame->GetScreen()->MarkConnections( schItem, schItem->IsConnectable() );
3512
3513 // Make sure we don't add things the user has disabled in the selection filter
3514 for( SCH_ITEM* connItem : conns )
3515 {
3516 if( !Selectable( connItem ) || !itemPassesFilter( connItem, nullptr ) )
3517 continue;
3518
3519 added.insert( connItem );
3520 }
3521 }
3522
3523 return added;
3524}
3525
3526
3528{
3530
3531 if( m_selection.Empty() )
3532 return 0;
3533
3534 SCH_SELECTION connectableSelection;
3535 SCH_SELECTION graphicalSelection;
3536
3537 // We need to filter the selection into connectable items (wires, pins, symbols)
3538 // and non-connectable items (shapes, unconnectable lines) for processing
3539 // with the graph or by the graphical are-endpoints-touching method.
3540 for( EDA_ITEM* selItem : originalSelection.GetItems() )
3541 {
3542 if( !selItem->IsSCH_ITEM() )
3543 continue;
3544
3545 SCH_ITEM* item = static_cast<SCH_ITEM*>( selItem );
3546
3547 if( item->Type() == SCH_LINE_T && !item->IsConnectable() )
3548 graphicalSelection.Add( item );
3549 else if( item->Type() == SCH_SHAPE_T )
3550 graphicalSelection.Add( item );
3551 else
3552 connectableSelection.Add( item );
3553 }
3554
3555 // Repeated Ctrl+4 must advance to the next stop condition if the current stage did not pull
3556 // in any items beyond what was already selected, matching PCBNew's "Select/Expand Connection".
3557 std::unordered_set<const SCH_ITEM*> originalConnectableSet;
3558
3559 for( EDA_ITEM* selItem : connectableSelection.GetItems() )
3560 originalConnectableSet.insert( static_cast<const SCH_ITEM*>( selItem ) );
3561
3562 ClearSelection( true );
3563
3564 std::set<SCH_ITEM*> graphAdded;
3565 std::set<SCH_ITEM*> graphicalAdded;
3566
3567 if( !connectableSelection.Empty() )
3568 {
3572 {
3573 graphAdded = expandConnectionWithGraph( connectableSelection, stop );
3574
3575 bool grew = std::any_of( graphAdded.begin(), graphAdded.end(),
3576 [&]( const SCH_ITEM* c )
3577 {
3578 return !originalConnectableSet.count( c );
3579 } );
3580
3581 if( grew )
3582 break;
3583 }
3584 }
3585
3586 if( !graphicalSelection.Empty() )
3587 graphicalAdded = expandConnectionGraphically( graphicalSelection );
3588
3589 // For whatever reason, the connection graph isn't working (e.g. in symbol editor )
3590 // so fall back to graphical expansion for those items if nothing was added.
3591 if( graphAdded.empty() && !connectableSelection.Empty() )
3592 {
3593 SCH_SELECTION combinedSelection = connectableSelection;
3594
3595 for( EDA_ITEM* selItem : graphicalSelection.GetItems() )
3596 combinedSelection.Add( selItem );
3597
3598 graphicalSelection = combinedSelection;
3599 }
3600
3601 graphicalAdded = expandConnectionGraphically( graphicalSelection );
3602
3603 auto smartAddToSel = [&]( EDA_ITEM* aItem )
3604 {
3605 AddItemToSel( aItem, true );
3606
3607 if( aItem->Type() == SCH_LINE_T )
3608 aItem->SetFlags( STARTPOINT | ENDPOINT );
3609 };
3610
3611 // Add everything to the selection, including the original selection
3612 for( auto item : graphAdded )
3613 smartAddToSel( item );
3614
3615 for( auto item : graphicalAdded )
3616 smartAddToSel( item );
3617
3618 for( auto item : originalSelection )
3619 smartAddToSel( item );
3620
3621 m_selection.SetIsHover( originalSelection.IsHover() );
3622
3623 if( originalSelection.HasReferencePoint() )
3624 m_selection.SetReferencePoint( originalSelection.GetReferencePoint() );
3625 else
3626 m_selection.ClearReferencePoint();
3627
3628 getView()->Update( &m_selection );
3629
3630 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3631
3632 return 0;
3633}
3634
3635
3637{
3638 std::set<std::pair<SCH_TABLE*, int>> columns;
3639 bool added = false;
3640
3641 for( EDA_ITEM* item : m_selection )
3642 {
3643 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( item ) )
3644 {
3645 SCH_TABLE* table = static_cast<SCH_TABLE*>( cell->GetParent() );
3646 columns.insert( std::make_pair( table, cell->GetColumn() ) );
3647 }
3648 }
3649
3650 for( auto& [ table, col ] : columns )
3651 {
3652 for( int row = 0; row < table->GetRowCount(); ++row )
3653 {
3654 SCH_TABLECELL* cell = table->GetCell( row, col );
3655
3656 if( !cell->IsSelected() )
3657 {
3658 select( table->GetCell( row, col ) );
3659 added = true;
3660 }
3661 }
3662 }
3663
3664 if( added )
3665 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3666
3667 return 0;
3668}
3669
3670
3672{
3673 std::set<std::pair<SCH_TABLE*, int>> rows;
3674 bool added = false;
3675
3676 for( EDA_ITEM* item : m_selection )
3677 {
3678 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( item ) )
3679 {
3680 SCH_TABLE* table = static_cast<SCH_TABLE*>( cell->GetParent() );
3681 rows.insert( std::make_pair( table, cell->GetRow() ) );
3682 }
3683 }
3684
3685 for( auto& [ table, row ] : rows )
3686 {
3687 for( int col = 0; col < table->GetColCount(); ++col )
3688 {
3689 SCH_TABLECELL* cell = table->GetCell( row, col );
3690
3691 if( !cell->IsSelected() )
3692 {
3693 select( table->GetCell( row, col ) );
3694 added = true;
3695 }
3696 }
3697 }
3698
3699 if( added )
3700 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3701
3702 return 0;
3703}
3704
3705
3707{
3708 std::set<SCH_TABLE*> tables;
3709 bool added = false;
3710
3711 for( EDA_ITEM* item : m_selection )
3712 {
3713 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( item ) )
3714 tables.insert( static_cast<SCH_TABLE*>( cell->GetParent() ) );
3715 }
3716
3718
3719 for( SCH_TABLE* table : tables )
3720 {
3721 if( !table->IsSelected() )
3722 {
3723 select( table );
3724 added = true;
3725 }
3726 }
3727
3728 if( added )
3729 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3730
3731 return 0;
3732}
3733
3734
3736{
3738 return 0;
3739}
3740
3741
3743{
3744 if( aBBox.GetWidth() == 0 )
3745 return;
3746
3747 BOX2I bbox = aBBox;
3748 bbox.Normalize();
3749
3750 VECTOR2I bbSize = bbox.Inflate( KiROUND( bbox.GetWidth() * 0.2f ) ).GetSize();
3751 VECTOR2D screenSize = getView()->GetViewport().GetSize();
3752
3753 // This code tries to come up with a zoom factor that doesn't simply zoom in to the cross
3754 // probed symbol, but instead shows a reasonable amount of the circuit around it to provide
3755 // context. This reduces the need to manually change the zoom because it's too close.
3756
3757 // Using the default text height as a constant to compare against, use the height of the
3758 // bounding box of visible items for a footprint to figure out if this is a big symbol (like
3759 // a processor) or a small symbol (like a resistor). This ratio is not useful by itself as a
3760 // scaling factor. It must be "bent" to provide good scaling at varying symbol sizes. Bigger
3761 // symbols need less scaling than small ones.
3762 double currTextHeight = schIUScale.MilsToIU( DEFAULT_TEXT_SIZE );
3763
3764 double compRatio = bbSize.y / currTextHeight; // Ratio of symbol to text height
3765 double compRatioBent = 1.0;
3766
3767 // LUT to scale zoom ratio to provide reasonable schematic context. Must work with symbols
3768 // of varying sizes (e.g. 0402 package and 200 pin BGA).
3769 // Each entry represents a compRatio (symbol height / default text height) and an amount to
3770 // scale by.
3771 std::vector<std::pair<double, double>> lut{ { 1.25, 16 },
3772 { 2.5, 12 },
3773 { 5, 8 },
3774 { 6, 6 },
3775 { 10, 4 },
3776 { 20, 2 },
3777 { 40, 1.5 },
3778 { 100, 1 } };
3779
3780 std::vector<std::pair<double, double>>::iterator it;
3781
3782 // Large symbol default is last LUT entry (1:1).
3783 compRatioBent = lut.back().second;
3784
3785 // Use LUT to do linear interpolation of "compRatio" within "first", then use that result to
3786 // linearly interpolate "second" which gives the scaling factor needed.
3787 if( compRatio >= lut.front().first )
3788 {
3789 for( it = lut.begin(); it < lut.end() - 1; ++it )
3790 {
3791 if( it->first <= compRatio && next( it )->first >= compRatio )
3792 {
3793 double diffx = compRatio - it->first;
3794 double diffn = next( it )->first - it->first;
3795
3796 compRatioBent = it->second + ( next( it )->second - it->second ) * diffx / diffn;
3797 break; // We have our interpolated value
3798 }
3799 }
3800 }
3801 else
3802 {
3803 compRatioBent = lut.front().second; // Small symbol default is first entry
3804 }
3805
3806 // This is similar to the original KiCad code that scaled the zoom to make sure symbols were
3807 // visible on screen. It's simply a ratio of screen size to symbol size, and its job is to
3808 // zoom in to make the component fullscreen. Earlier in the code the symbol BBox is given a
3809 // 20% margin to add some breathing room. We compare the height of this enlarged symbol bbox
3810 // to the default text height. If a symbol will end up with the sides clipped, we adjust
3811 // later to make sure it fits on screen.
3812 screenSize.x = std::max( 10.0, screenSize.x );
3813 screenSize.y = std::max( 10.0, screenSize.y );
3814 double ratio = std::max( -1.0, fabs( bbSize.y / screenSize.y ) );
3815
3816 // Original KiCad code for how much to scale the zoom
3817 double kicadRatio = std::max( fabs( bbSize.x / screenSize.x ),
3818 fabs( bbSize.y / screenSize.y ) );
3819
3820 // If the width of the part we're probing is bigger than what the screen width will be after
3821 // the zoom, then punt and use the KiCad zoom algorithm since it guarantees the part's width
3822 // will be encompassed within the screen.
3823 if( bbSize.x > screenSize.x * ratio * compRatioBent )
3824 {
3825 // Use standard KiCad zoom for parts too wide to fit on screen/
3826 ratio = kicadRatio;
3827 compRatioBent = 1.0; // Reset so we don't modify the "KiCad" ratio
3828 wxLogTrace( "CROSS_PROBE_SCALE",
3829 "Part TOO WIDE for screen. Using normal KiCad zoom ratio: %1.5f", ratio );
3830 }
3831
3832 // Now that "compRatioBent" holds our final scaling factor we apply it to the original
3833 // fullscreen zoom ratio to arrive at the final ratio itself.
3834 ratio *= compRatioBent;
3835
3836 bool alwaysZoom = false; // DEBUG - allows us to minimize zooming or not
3837
3838 // Try not to zoom on every cross-probe; it gets very noisy
3839 if( ( ratio < 0.5 || ratio > 1.0 ) || alwaysZoom )
3840 getView()->SetScale( getView()->GetScale() / ratio );
3841}
3842
3843
3844void SCH_SELECTION_TOOL::SyncSelection( const std::optional<SCH_SHEET_PATH>& targetSheetPath,
3845 SCH_ITEM* focusItem, const std::vector<SCH_ITEM*>& items )
3846{
3847 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
3848
3849 if( !editFrame )
3850 return;
3851
3852 double targetZoom = 0.0;
3853 VECTOR2D targetCenter;
3854 bool targetZoomValid = false;
3855 bool changedSheet = false;
3856
3857 if( targetSheetPath )
3858 {
3859 SCH_SHEET_PATH path = targetSheetPath.value();
3860
3861 if( SCH_SCREEN* screen = path.LastScreen() )
3862 {
3863 targetZoom = screen->m_LastZoomLevel;
3864 targetCenter = screen->m_ScrollCenter;
3865 targetZoomValid = screen->IsZoomInitialized();
3866 }
3867
3868 if( path != editFrame->Schematic().CurrentSheet() )
3869 {
3870 m_frame->GetToolManager()->RunAction<SCH_SHEET_PATH*>( SCH_ACTIONS::changeSheet, &path );
3871 changedSheet = true;
3872 }
3873 }
3874
3875 if( changedSheet && targetZoomValid && !m_frame->eeconfig()->m_CrossProbing.zoom_to_fit )
3876 {
3877 getView()->SetScale( targetZoom );
3878 getView()->SetCenter( targetCenter );
3879 }
3880
3881 ClearSelection( items.size() > 0 ? true /*quiet mode*/ : false );
3882
3883 // Perform individual selection of each item before processing the event.
3884 for( SCH_ITEM* item : items )
3885 {
3886 SCH_ITEM* parent = dynamic_cast<SCH_ITEM*>( item->GetParent() );
3887
3888 // Make sure we only select items on the current screen
3889 if( m_frame->GetScreen()->CheckIfOnDrawList( item )
3890 || ( parent && m_frame->GetScreen()->CheckIfOnDrawList( parent ) ) )
3891 {
3892 select( item );
3893 }
3894 }
3895
3896 BOX2I bbox = m_selection.GetBoundingBox();
3897
3898 if( bbox.GetWidth() != 0 && bbox.GetHeight() != 0 )
3899 {
3900 if( m_frame->eeconfig()->m_CrossProbing.center_on_items )
3901 {
3902 if( m_frame->eeconfig()->m_CrossProbing.zoom_to_fit )
3903 ZoomFitCrossProbeBBox( bbox );
3904
3905 editFrame->FocusOnItem( focusItem );
3906
3907 if( !focusItem )
3908 editFrame->FocusOnLocation( bbox.Centre() );
3909 }
3910 }
3911
3912 if( m_selection.Size() > 0 )
3913 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3914}
3915
3916
3918{
3919 m_selection.Clear();
3920
3921 bool enteredGroupFound = false;
3922
3923 if( m_isSymbolEditor )
3924 {
3925 LIB_SYMBOL* start = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
3926
3927 for( SCH_ITEM& item : start->GetDrawItems() )
3928 {
3929 if( item.IsSelected() )
3930 select( &item );
3931
3932 if( item.Type() == SCH_GROUP_T )
3933 {
3934 if( &item == m_enteredGroup )
3935 {
3936 item.SetFlags( ENTERED );
3937 enteredGroupFound = true;
3938 }
3939 else
3940 {
3941 item.ClearFlags( ENTERED );
3942 }
3943 }
3944 }
3945 }
3946 else
3947 {
3948 for( SCH_ITEM* item : m_frame->GetScreen()->Items() )
3949 {
3950 // If the field and symbol are selected, only use the symbol
3951 if( item->IsSelected() )
3952 {
3953 select( item );
3954 }
3955 else
3956 {
3957 item->RunOnChildren(
3958 [&]( SCH_ITEM* aChild )
3959 {
3960 if( aChild->IsSelected() )
3961 select( aChild );
3962 },
3964 }
3965
3966 if( item->Type() == SCH_GROUP_T )
3967 {
3968 if( item == m_enteredGroup )
3969 {
3970 item->SetFlags( ENTERED );
3971 enteredGroupFound = true;
3972 }
3973 else
3974 {
3975 item->ClearFlags( ENTERED );
3976 }
3977 }
3978 }
3979 }
3980
3982
3983 if( !enteredGroupFound )
3984 {
3985 m_enteredGroupOverlay.Clear();
3986 m_enteredGroup = nullptr;
3987 }
3988
3989 // Inform other potentially interested tools
3990 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3991}
3992
3993
3994bool SCH_SELECTION_TOOL::Selectable( const EDA_ITEM* aItem, const VECTOR2I* aPos,
3995 bool checkVisibilityOnly ) const
3996{
3997 // NOTE: in the future this is where Eeschema layer/itemtype visibility will be handled
3998
3999 SYMBOL_EDIT_FRAME* symEditFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
4000
4001 // Do not allow selection of anything except fields when the current symbol in the symbol
4002 // editor is a derived symbol.
4003 if( symEditFrame && symEditFrame->IsSymbolAlias() && aItem->Type() != SCH_FIELD_T )
4004 return false;
4005
4006 switch( aItem->Type() )
4007 {
4008 case SCH_PIN_T:
4009 {
4010 const SCH_PIN* pin = static_cast<const SCH_PIN*>( aItem );
4011
4012 if( symEditFrame )
4013 {
4014 if( pin->GetUnit() && pin->GetUnit() != symEditFrame->GetUnit() )
4015 return false;
4016
4017 if( pin->GetBodyStyle() && pin->GetBodyStyle() != symEditFrame->GetBodyStyle() )
4018 return false;
4019 }
4020
4021 if( !pin->IsVisible() && !m_frame->GetShowAllPins() )
4022 return false;
4023
4024 if( !m_filter.pins )
4025 {
4026 // Pin anchors have to be allowed for auto-starting wires.
4027 if( aPos )
4028 {
4030 GRID_HELPER_GRIDS pinGrid = grid.GetItemGrid( pin );
4031
4032 if( pin->IsPointClickableAnchor( grid.ResolveSnap( *aPos, pinGrid ).position ) )
4033 return true;
4034 }
4035
4036 return false;
4037 }
4038
4039 break;
4040 }
4041
4043 if( !m_frame->eeconfig()->m_Appearance.show_directive_labels )
4044 return false;
4045
4046 break;
4047
4048 case SCH_RULE_AREA_T:
4049 // A rule area that exists solely to carry directive labels is hidden along with those
4050 // labels, so it must not remain selectable while invisible.
4051 if( !m_frame->eeconfig()->m_Appearance.show_directive_labels
4052 && static_cast<const SCH_RULE_AREA*>( aItem )->IsDirectiveLabelOnlyArea() )
4053 {
4054 return false;
4055 }
4056
4057 break;
4058
4059 case LIB_SYMBOL_T: // In symbol_editor we do not want to select the symbol itself.
4060 return false;
4061
4062 case SCH_FIELD_T: // SCH_FIELD objects are not unit/body-style-specific.
4063 {
4064 const SCH_FIELD* field = static_cast<const SCH_FIELD*>( aItem );
4065
4066 if( !field->IsVisible() && !( symEditFrame && symEditFrame->GetShowInvisibleFields() ) )
4067 return false;
4068
4069 break;
4070 }
4071
4072 case SCH_SHAPE_T:
4073 case SCH_TEXT_T:
4074 case SCH_TEXTBOX_T:
4075 if( symEditFrame )
4076 {
4077 const SCH_ITEM* sch_item = static_cast<const SCH_ITEM*>( aItem );
4078
4079 if( sch_item->GetUnit() && sch_item->GetUnit() != symEditFrame->GetUnit() )
4080 return false;
4081
4082 if( sch_item->GetBodyStyle() && sch_item->GetBodyStyle() != symEditFrame->GetBodyStyle() )
4083 return false;
4084 }
4085
4086 break;
4087
4088 case SCH_MARKER_T: // Always selectable
4089 return true;
4090
4091 case SCH_TABLECELL_T:
4092 {
4093 const SCH_TABLECELL* cell = static_cast<const SCH_TABLECELL*>( aItem );
4094
4095 if( cell->GetColSpan() == 0 || cell->GetRowSpan() == 0 )
4096 return false;
4097
4098 break;
4099 }
4100
4101 case NOT_USED: // Things like CONSTRUCTION_GEOM that aren't part of the model
4102 return false;
4103
4104 default: // Suppress warnings
4105 break;
4106 }
4107
4108 return true;
4109}
4110
4111
4113{
4114 if( m_selection.Empty() )
4115 return;
4116
4117 while( m_selection.GetSize() )
4119
4120 getView()->Update( &m_selection );
4121
4122 m_selection.SetIsHover( false );
4123 m_selection.ClearReferencePoint();
4124
4125 // Inform other potentially interested tools
4126 if( !aQuietMode )
4127 m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
4128}
4129
4130
4132{
4133 // Don't group when we select new items, the schematic editor selects all new items for moving.
4134 // The PCB editor doesn't need this logic because it doesn't select new items for moving.
4135 if( m_enteredGroup && !aItem->IsNew()
4136 && !SCH_GROUP::WithinScope( static_cast<SCH_ITEM*>( aItem ), m_enteredGroup, m_isSymbolEditor ) )
4137 {
4138 ExitGroup();
4139 }
4140
4141 highlight( aItem, SELECTED, &m_selection );
4142}
4143
4144
4146{
4147 unhighlight( aItem, SELECTED, &m_selection );
4148}
4149
4150
4151void SCH_SELECTION_TOOL::highlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
4152{
4153 if( aMode == SELECTED )
4154 aItem->SetSelected();
4155 else if( aMode == BRIGHTENED )
4156 aItem->SetBrightened();
4157
4158 if( aGroup )
4159 aGroup->Add( aItem );
4160
4161 // Highlight pins and fields. (All the other symbol children are currently only
4162 // represented in the LIB_SYMBOL and will inherit the settings of the parent symbol.)
4163 if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( aItem ) )
4164 {
4165 // We don't want to select group children if the group itself is selected,
4166 // we can only select them when the group is entered
4167 if( sch_item->Type() != SCH_GROUP_T )
4168 {
4169 sch_item->RunOnChildren(
4170 [&]( SCH_ITEM* aChild )
4171 {
4172 if( aMode == SELECTED )
4173 {
4174 aChild->SetSelected();
4175 getView()->Hide( aChild, true );
4176 }
4177 else if( aMode == BRIGHTENED )
4178 {
4179 aChild->SetBrightened();
4180 }
4181 },
4183 }
4184 }
4185
4186 if( aGroup && aMode != BRIGHTENED )
4187 getView()->Hide( aItem, true );
4188
4189 if( aItem->GetParent() && aItem->GetParent()->Type() != SCHEMATIC_T )
4190 getView()->Update( aItem->GetParent(), KIGFX::REPAINT );
4191
4192 getView()->Update( aItem, KIGFX::REPAINT );
4193}
4194
4195
4196void SCH_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
4197{
4198 if( aMode == SELECTED )
4199 {
4200 aItem->ClearSelected();
4201 // Lines need endpoints cleared here
4202 if( aItem->Type() == SCH_LINE_T )
4203 aItem->ClearFlags( STARTPOINT | ENDPOINT );
4204
4205 if( aMode != BRIGHTENED )
4206 getView()->Hide( aItem, false );
4207 }
4208 else if( aMode == BRIGHTENED )
4209 {
4210 aItem->ClearBrightened();
4211 }
4212
4213 if( aGroup )
4214 aGroup->Remove( aItem );
4215
4216 // Unhighlight pins and fields. (All the other symbol children are currently only
4217 // represented in the LIB_SYMBOL.)
4218 if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( aItem ) )
4219 {
4220 sch_item->RunOnChildren(
4221 [&]( SCH_ITEM* aChild )
4222 {
4223 if( aMode == SELECTED )
4224 {
4225 aChild->ClearSelected();
4226 getView()->Hide( aChild, false );
4227 }
4228 else if( aMode == BRIGHTENED )
4229 {
4230 aChild->ClearBrightened();
4231 }
4232
4233 if( aGroup )
4234 aGroup->Remove( aChild );
4235 },
4237 }
4238
4239 if( aItem->GetParent() && aItem->GetParent()->Type() != SCHEMATIC_T )
4240 getView()->Update( aItem->GetParent(), KIGFX::REPAINT );
4241
4242 getView()->Update( aItem, KIGFX::REPAINT );
4243}
4244
4245
4247{
4248 const unsigned GRIP_MARGIN = 20;
4249 int margin = KiROUND( getView()->ToWorld( GRIP_MARGIN ) );
4250
4251 // Check if the point is located within any of the currently selected items bounding boxes
4252 for( EDA_ITEM* item : m_selection )
4253 {
4254 BOX2I itemBox = item->ViewBBox();
4255 itemBox.Inflate( margin ); // Give some margin for gripping an item
4256
4257 if( itemBox.Contains( aPoint ) )
4258 return true;
4259 }
4260
4261 return false;
4262}
4263
4264
4266{
4267 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
4268
4269 if( !editFrame || !editFrame->GetNetNavigator() || m_selection.Size() == 0 )
4270 return 0;
4271
4272 if( !m_selection.Front()->IsBrightened() )
4273 return 0;
4274
4275 const SCH_ITEM* item = editFrame->SelectNextPrevNetNavigatorItem( true );
4276
4277 if( item )
4278 {
4280 select( const_cast<SCH_ITEM*>( item ) );
4281 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
4282 }
4283
4284 return 0;
4285}
4286
4287
4289{
4290 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
4291
4292 if( !editFrame || !editFrame->GetNetNavigator() || m_selection.Size() == 0 )
4293 return 0;
4294
4295 if( !m_selection.Front()->IsBrightened() )
4296 return 0;
4297
4298 const SCH_ITEM* item = editFrame->SelectNextPrevNetNavigatorItem( false );
4299
4300 if( item )
4301 {
4303 select( const_cast<SCH_ITEM*>( item ) );
4304 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
4305 }
4306
4307 return 0;
4308}
4309
4310
4312{
4314
4321
4323
4326
4332
4335
4338
4341
4343}
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
static TOOL_ACTION unselectAll
Definition actions.h:79
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition actions.h:223
static TOOL_ACTION cursorLeft
Definition actions.h:168
static TOOL_ACTION zoomOutCenter
Definition actions.h:132
static TOOL_ACTION unselectItem
Definition actions.h:224
static TOOL_ACTION zoomIn
Definition actions.h:129
static TOOL_ACTION cursorLeftFast
Definition actions.h:173
static TOOL_ACTION selectSetLasso
Definition actions.h:217
static TOOL_ACTION selectSetRect
Set lasso selection mode.
Definition actions.h:216
static TOOL_ACTION groupEnter
Definition actions.h:239
static TOOL_ACTION selectColumns
Definition actions.h:98
static TOOL_ACTION cursorDown
Definition actions.h:167
static TOOL_ACTION zoomOut
Definition actions.h:130
static TOOL_ACTION cursorRightFast
Definition actions.h:174
static TOOL_ACTION zoomCenter
Definition actions.h:137
static TOOL_ACTION panDown
Definition actions.h:181
static TOOL_ACTION cursorDblClick
Definition actions.h:177
static TOOL_ACTION undo
Definition actions.h:71
static TOOL_ACTION selectionActivate
Activation of the selection tool.
Definition actions.h:210
static TOOL_ACTION cursorDownFast
Definition actions.h:172
static TOOL_ACTION selectionMenu
Run a selection menu to select from a list of items.
Definition actions.h:232
static TOOL_ACTION selectRows
Definition actions.h:97
static TOOL_ACTION cursorUpFast
Definition actions.h:171
static TOOL_ACTION panLeft
Definition actions.h:182
static TOOL_ACTION updateMenu
Definition actions.h:266
static TOOL_ACTION doDelete
Definition actions.h:81
static TOOL_ACTION selectionTool
Definition actions.h:247
static TOOL_ACTION cursorClick
Definition actions.h:176
static TOOL_ACTION zoomFitScreen
Definition actions.h:138
static TOOL_ACTION increment
Definition actions.h:90
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
static TOOL_ACTION panUp
Definition actions.h:180
static TOOL_ACTION zoomFitObjects
Definition actions.h:139
static TOOL_ACTION zoomInCenter
Definition actions.h:131
static TOOL_ACTION panRight
Definition actions.h:183
static TOOL_ACTION selectTable
Definition actions.h:99
static TOOL_ACTION cursorUp
Cursor control with keyboard.
Definition actions.h:166
static TOOL_ACTION groupLeave
Definition actions.h:240
static TOOL_ACTION finishInteractive
Definition actions.h:69
static TOOL_ACTION cursorRight
Definition actions.h:169
static TOOL_ACTION selectAll
Definition actions.h:78
static TOOL_ACTION unselectItems
Definition actions.h:229
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition actions.h:228
ACTION_MENU(bool isContextMenu, TOOL_INTERACTIVE *aTool=nullptr)
Default constructor.
TOOL_MANAGER * getToolManager() const
Return an instance of TOOL_MANAGER class.
void Clear()
Remove all the entries from the menu (as well as its title).
void SetTitle(const wxString &aTitle) override
Set title for the menu.
wxMenuItem * Add(const wxString &aLabel, int aId, BITMAPS aIcon)
Add a wxWidgets-style entry to the menu.
bool HasEnabledItems() const
Return true if the menu has any enabled items.
TOOL_INTERACTIVE * m_tool
Creator of the menu.
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:207
constexpr void SetMaximum()
Definition box2.h:76
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:554
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:142
constexpr size_type GetWidth() const
Definition box2.h:210
constexpr Vec Centre() const
Definition box2.h:93
constexpr const Vec GetCenter() const
Definition box2.h:226
constexpr size_type GetHeight() const
Definition box2.h:211
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:164
constexpr const SizeVec & GetSize() const
Definition box2.h:202
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:307
An abstract class that will find and hold all the objects according to an inspection done by the Insp...
Definition collector.h:45
const std::vector< KICAD_T > & GetScanTypes() const
Definition collector.h:211
void Transfer(int aIndex)
Move the item at aIndex (first position is 0) to the backup list.
Definition collector.h:149
bool m_MenuCancelled
Definition collector.h:237
ITER begin()
Definition collector.h:71
int GetCount() const
Return the number of objects in the list.
Definition collector.h:79
bool HasItem(const EDA_ITEM *aItem) const
Tests if aItem has already been collected.
Definition collector.h:193
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition collector.h:107
int m_Threshold
Definition collector.h:234
ITER end()
Definition collector.h:72
void Append(EDA_ITEM *item)
Add an item to the end of the list.
Definition collector.h:97
COMMIT & Added(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition commit.h:80
bool Empty() const
Definition commit.h:134
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:74
Calculate the connectivity of a schematic and generates netlists.
SCH_NETCHAIN * GetNetChainForNet(const wxString &aNet)
SCH_NETCHAIN * FindPotentialNetChainBetweenPins(SCH_PIN *aPinA, SCH_PIN *aPinB)
Locate a potential net chain that contains both pins (by subgraph net membership).
bool IsHorizontal() const
Definition eda_angle.h:142
The base frame for deriving all KiCad main window classes.
bool IsType(FRAME_T aType) const
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:42
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
virtual VECTOR2I GetPosition() const
Definition eda_item.h:282
void SetIsRollover(bool aIsRollover, const VECTOR2I &aMousePos)
Definition eda_item.h:138
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:135
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:152
const KIID m_Uuid
Definition eda_item.h:531
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
void ClearSelected()
Definition eda_item.h:147
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:154
bool IsSelected() const
Definition eda_item.h:132
void SetSelected()
Definition eda_item.h:144
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition eda_item.h:202
void ClearBrightened()
Definition eda_item.h:148
void SetBrightened()
Definition eda_item.h:145
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:243
EDA_ITEM * GetParent() const
Definition eda_item.h:110
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:156
void XorFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:153
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:155
bool IsNew() const
Definition eda_item.h:129
SHAPE_T GetShape() const
Definition eda_shape.h:185
virtual bool IsFilledForHitTesting() const
Definition eda_shape.h:147
virtual std::vector< SHAPE * > MakeEffectiveShapesForHitTesting() const
Definition eda_shape.h:467
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:89
virtual bool IsVisible() const
Definition eda_text.h:208
virtual EDA_ANGLE GetTextAngle() const
Definition eda_text.h:168
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.
SNAP_RESULT ResolveSnap(const VECTOR2I &aOrigin, GRID_HELPER_GRIDS aGrid, SCH_ITEM *aSkip)
EE_TYPE Overlapping(const BOX2I &aRect) const
Definition sch_rtree.h:226
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:221
static const TOOL_EVENT DisambiguatePoint
Used for hotkey feedback.
Definition actions.h:360
static const TOOL_EVENT ClearedEvent
Definition actions.h:345
static const TOOL_EVENT SelectedEvent
Definition actions.h:343
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:350
static const TOOL_EVENT PointSelectedEvent
Definition actions.h:342
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition actions.h:353
static const TOOL_EVENT UnselectedEvent
Definition actions.h:344
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:82
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:63
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition view.cpp:597
virtual void SetScale(double aScale, VECTOR2D aAnchor={ 0, 0 })
Set the scaling factor, zooming around a given anchor point.
Definition view.cpp:637
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition view.cpp:300
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition view.cpp:404
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:487
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:1835
bool IsMirroredX() const
Return true if view is flipped across the X axis.
Definition view.h:255
std::pair< VIEW_ITEM *, int > LAYER_ITEM_PAIR
Definition view.h:67
void Hide(VIEW_ITEM *aItem, bool aHide=true, bool aHideOverlay=false)
Temporarily hide the item in the view (e.g.
Definition view.cpp:1780
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition view.h:225
void SetCenter(const VECTOR2D &aCenter)
Set the center point of the VIEW (i.e.
Definition view.cpp:663
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition view.cpp:1756
Definition kiid.h:46
wxString AsString() const
Definition kiid.cpp:264
Define a library symbol object.
Definition lib_symbol.h:114
bool IsPower() const override
bool IsMultiBodyStyle() const override
Definition lib_symbol.h:871
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:809
int GetUnitCount() const override
bool IsExcluded() const
Definition marker_base.h:89
REPLACE_TERMINAL_PIN_MENU * m_replaceMenu
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
void update() override
Update menu state stub.
Tree view item data for the net navigator.
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
OPT_TOOL_EVENT eventHandler(const wxMenuEvent &aEvent) override
Event handler stub.
void update() override
Update menu state stub.
CONNECTION_GRAPH * ConnectionGraph() const
Definition schematic.h:201
SCH_SHEET & Root() const
Definition schematic.h:134
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:189
static TOOL_ACTION placeClassLabel
Definition sch_actions.h:75
static TOOL_ACTION placeSheetPin
Definition sch_actions.h:81
static TOOL_ACTION createNetChain
static TOOL_ACTION placeNextSymbolUnit
Definition sch_actions.h:63
static TOOL_ACTION swapPins
static TOOL_ACTION saveToLinkedDesignBlock
Definition sch_actions.h:67
static TOOL_ACTION clearHighlight
static TOOL_ACTION placeGlobalLabel
Definition sch_actions.h:76
static TOOL_ACTION pinTable
static TOOL_ACTION navigateBack
static TOOL_ACTION properties
static TOOL_ACTION leaveSheet
static TOOL_ACTION breakWire
static TOOL_ACTION removeFromNetChain
static TOOL_ACTION findNetInInspector
static TOOL_ACTION autoplaceAllSheetPins
Definition sch_actions.h:82
static TOOL_ACTION drawLines
Definition sch_actions.h:97
static TOOL_ACTION placeHierLabel
Definition sch_actions.h:77
static TOOL_ACTION selectConnection
If current selection is a wire or bus, expand to entire connection.
Definition sch_actions.h:47
static TOOL_ACTION placeLabel
Definition sch_actions.h:74
static TOOL_ACTION nextNetItem
static TOOL_ACTION drawWire
Definition sch_actions.h:68
static TOOL_ACTION drag
static TOOL_ACTION highlightNetChain
static TOOL_ACTION placeJunction
Definition sch_actions.h:72
static TOOL_ACTION previousNetItem
static TOOL_ACTION swapUnitLabels
static TOOL_ACTION navigateForward
static TOOL_ACTION placeLinkedDesignBlock
Definition sch_actions.h:66
static TOOL_ACTION selectNode
Select the junction, wire or bus segment under the cursor.
Definition sch_actions.h:43
static TOOL_ACTION unfoldBus
Definition sch_actions.h:70
static TOOL_ACTION drawBus
Definition sch_actions.h:69
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 replaceTerminalPin
static TOOL_ACTION createNetChainBetweenPins
static TOOL_ACTION editPageNumber
static TOOL_ACTION selectOnPCB
static TOOL_ACTION move
static TOOL_ACTION syncSheetPins
Definition sch_actions.h:85
static TOOL_ACTION nameNetChain
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 HasLockedItems
static SELECTION_CONDITION AllPinsOrSheetPins
static SELECTION_CONDITION SingleSymbolOrPower
static SELECTION_CONDITION HasUnlockedItems
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.
wxString Name(bool aIgnoreSheet=false) const
bool IsBus() const
Handle actions specific to the schematic editor.
Schematic editor (Eeschema) main window.
void FocusOnItem(EDA_ITEM *aItem, bool aAllowScroll=true) override
Focus on a particular canvas item.
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
SCH_SHEET_PATH & GetCurrentSheet() const
SCHEMATIC & Schematic() const
const SCH_ITEM * SelectNextPrevNetNavigatorItem(bool aNext)
const wxString & GetHighlightedConnection() const
wxGenericTreeCtrl * GetNetNavigator()
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
A set of SCH_ITEMs (i.e., without duplicates).
Definition sch_group.h:48
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:162
virtual bool IsConnectable() const
Definition sch_item.h:524
virtual bool HasHoveredHypertext() const
Indicates that a hypertext link is currently active.
Definition sch_item.h:331
const SYMBOL * GetParentSymbol() const
Definition sch_item.cpp:274
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:268
int GetBodyStyle() const
Definition sch_item.h:242
virtual bool IsPointClickableAnchor(const VECTOR2I &aPos) const
Definition sch_item.h:530
bool IsLocked() const override
Definition sch_item.cpp:148
int GetUnit() const
Definition sch_item.h:233
const std::vector< SCH_ITEM * > & ConnectedItems(const SCH_SHEET_PATH &aPath)
Retrieve the set of items connected to this item on the given sheet.
Definition sch_item.cpp:558
SCH_CONNECTION * Connection(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieve the connection associated with this object in the given sheet.
Definition sch_item.cpp:487
bool IsType(const std::vector< KICAD_T > &aScanTypes) const override
Check whether the item is one of the listed types.
Definition sch_item.h:177
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const override
void SetShape(LABEL_FLAG_SHAPE aShape)
Definition sch_label.h:179
LABEL_SHAPE GetLabelShape() const
Definition sch_label.h:175
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:38
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:855
bool IsStartDangling() const
Definition sch_line.h:299
VECTOR2I GetEndPoint() const
Definition sch_line.h:144
VECTOR2I GetStartPoint() const
Definition sch_line.h:135
SEG GetSeg() const
Get the geometric aspect of the wire as a SEG.
Definition sch_line.h:154
bool IsEndDangling() const
Definition sch_line.h:300
bool IsBus() const
Return true if the line is a bus.
bool IsGraphicLine() const
Return if the line is a graphic (non electrical line)
void SetEndPoint(const VECTOR2I &aPosition)
Definition sch_line.h:145
A net chain is a collection of nets that are connected together through passive components.
const wxString & GetNumber() const
Definition sch_pin.h:130
Tool that displays edit points allowing to modify items by dragging the points.
bool HasPoint()
Indicate the cursor is over an edit point.
bool IsDirectiveLabelOnlyArea() const
Return true when the rule area exists only as a container for attached directive labels.
bool IsExplicitJunction(const VECTOR2I &aPosition) const
Indicate that a junction dot is necessary at the given location.
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:115
bool IsJunction(const VECTOR2I &aPosition) const
Test if a junction is required for the items at aPosition on the screen.
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...
void FilterSelectionForLockedItems()
Remove locked items from the current selection.
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()
std::set< SCH_ITEM * > expandConnectionWithGraph(const SCH_SELECTION &aItems, STOP_CONDITION aStopCondition)
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).
OPT_TOOL_EVENT autostartEvent(TOOL_EVENT *aEvent, EE_GRID_HELPER &aGrid, SCH_ITEM *aItem)
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.
@ STOP_AT_PIN
Walk through junctions and labels but stop at pins.
@ STOP_NEVER
Walk the entire connected sub-net.
@ STOP_AT_JUNCTION
Stop at the first junction, label, or pin reached.
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)
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.
int SelectTable(const TOOL_EVENT &aEvent)
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:92
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:44
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition sch_sheet.h:227
Schematic symbol object.
Definition sch_symbol.h:69
std::vector< const SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
SCH_PIN * GetPin(const wxString &number) const
Find a symbol pin by number.
int GetOrientation() const override
Get the display symbol orientation.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:177
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:153
Definition seg.h:38
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition seg.cpp:698
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)
A null aItem is ignored; the selection never holds null members.
Definition selection.cpp:38
const std::deque< EDA_ITEM * > GetItems() const
Definition selection.h:125
VECTOR2I GetReferencePoint() const
virtual void Remove(EDA_ITEM *aItem)
Definition selection.cpp:60
bool IsHover() const
Definition selection.h:85
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition selection.h:104
EDA_ITEM * Front() const
Definition selection.h:176
bool OnlyContains(std::vector< KICAD_T > aList) const
Checks if all items in the selection have a type in aList.
bool Empty() const
Checks if there is anything selected.
Definition selection.h:114
bool HasReferencePoint() const
Definition selection.h:215
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 PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
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:147
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:59
virtual const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const =0
const TRANSFORM & GetTransform() const
Definition symbol.h:243
Represent a single user action.
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:182
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition tool_base.cpp:40
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:34
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:74
@ REDRAW
Full drawing refresh.
Definition tool_base.h:79
@ SHUTDOWN
Tool is being shut down.
Definition tool_base.h:80
@ MODEL_RELOAD
Model changes (the sheet for a schematic)
Definition tool_base.h:76
@ SUPERMODEL_RELOAD
For schematics, the entire schematic changed, not just the sheet.
Definition tool_base.h:77
Generic, UI-independent tool event.
Definition tool_event.h:167
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition tool_event.h:289
void SetParameter(T aParam)
Set a non-standard parameter assigned to the event.
Definition tool_event.h:524
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:252
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.
Master controller class:
TOOLS_HOLDER * GetToolHolder() const
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition vector2d.h:549
KICURSOR
Definition cursors.h:40
@ LINE_BUS
Definition cursors.h:86
@ LINE_WIRE
Definition cursors.h:90
@ SUBTRACT
Definition cursors.h:68
@ SELECT_WINDOW
Definition cursors.h:82
@ SELECT_LASSO
Definition cursors.h:84
@ MOVING
Definition cursors.h:44
@ ARROW
Definition cursors.h:42
@ LINE_GRAPHIC
Definition cursors.h:88
#define DEFAULT_TEXT_SIZE
Ratio of the font height to the baseline of the text above the wire.
#define _(s)
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:49
@ NO_RECURSE
Definition eda_item.h:50
#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
Definition eeschema_id.h:95
@ ID_POPUP_SCH_PIN_TRICKS_HIER_LABEL
Definition eeschema_id.h:99
@ ID_POPUP_SCH_PIN_TRICKS_WIRE
Definition eeschema_id.h:97
@ ID_POPUP_SCH_PLACE_UNIT_END
Definition eeschema_id.h:89
@ ID_POPUP_SCH_ALT_PIN_FUNCTION
@ ID_POPUP_SCH_UNFOLD_BUS_END
Definition eeschema_id.h:78
@ ID_POPUP_SCH_SELECT_UNIT
Definition eeschema_id.h:81
@ ID_POPUP_SCH_SELECT_BODY_STYLE
Definition eeschema_id.h:91
@ ID_POPUP_SCH_PIN_TRICKS_NET_LABEL
Definition eeschema_id.h:98
@ ID_POPUP_SCH_SELECT_BODY_STYLE_END
Definition eeschema_id.h:93
@ ID_POPUP_SCH_PIN_TRICKS_NO_CONNECT
Definition eeschema_id.h:96
@ ID_POPUP_SCH_UNFOLD_BUS
Definition eeschema_id.h:77
@ ID_POPUP_SCH_SELECT_UNIT_END
Definition eeschema_id.h:85
@ ID_POPUP_SCH_ALT_PIN_FUNCTION_END
@ ID_POPUP_SCH_PLACE_UNIT
Definition eeschema_id.h:87
@ ID_POPUP_SCH_PIN_TRICKS_GLOBAL_LABEL
@ ID_POPUP_SCH_PIN_TRICKS_END
@ FRAME_SCH_VIEWER
Definition frame_type.h:32
@ FRAME_SCH
Definition frame_type.h:30
a few functions useful in geometry calculations.
GRID_HELPER_GRIDS
Definition grid_helper.h:55
@ GRID_WIRES
Definition grid_helper.h:60
KIID niluuid(0)
@ LAYER_WIRE
Definition layer_ids.h:458
@ LAYER_BUS
Definition layer_ids.h:459
MOUSE_DRAG_ACTION
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:54
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition pin_type.h:32
@ PT_INPUT
usual pin input: must be connected
Definition pin_type.h:33
@ PT_OUTPUT
usual output
Definition pin_type.h:34
@ PT_TRISTATE
tri state bus pin
Definition pin_type.h:36
@ PT_BIDI
input or output (like port for a microprocessor)
Definition pin_type.h:35
@ PT_UNSPECIFIED
unknown electrical properties: creates always a warning when connected
Definition pin_type.h:41
@ PT_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin.
Definition pin_type.h:39
@ PIN_UP
The pin extends upwards from the connection point: Probably on the bottom side of the symbol.
Definition pin_type.h:123
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:107
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:114
@ PIN_DOWN
The pin extends downwards from the connection: Probably on the top side of the symbol.
Definition pin_type.h:131
CITER next(CITER it)
Definition ptree.cpp:120
#define HITTEST_THRESHOLD_PIXELS
Class to handle a set of SCH_ITEMs.
std::vector< EDA_ITEM * > EDA_ITEMS
@ L_BIDI
Definition sch_label.h:100
@ L_TRISTATE
Definition sch_label.h:101
@ L_UNSPECIFIED
Definition sch_label.h:102
@ L_OUTPUT
Definition sch_label.h:99
@ L_INPUT
Definition sch_label.h:98
@ LABEL_BIDI
Definition sch_label.h:118
@ LABEL_INPUT
Definition sch_label.h:116
@ LABEL_OUTPUT
Definition sch_label.h:117
@ LABEL_PASSIVE
Definition sch_label.h:120
@ LABEL_TRISTATE
Definition sch_label.h:119
static std::vector< KICAD_T > connectedTypes
@ ID_REPLACE_TERMINAL_PIN_A
@ ID_REPLACE_TERMINAL_PIN_B
static std::vector< KICAD_T > tableCellTypes
static std::vector< KICAD_T > expandConnectionGraphTypes
static std::vector< KICAD_T > lineTypes
static std::vector< KICAD_T > sheetTypes
static bool addCreateNetChainBetweenPinsIfApplicable(NET_CHAIN_MENU *aMenu, SCH_EDIT_FRAME *aFrame, const SCH_SELECTION &aSel)
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.
VECTOR2I position
void RotateAndMirrorPin(SCH_PIN &aPin, int aOrientMirror)
Rotate and/or mirror a SCH_PIN according to aOrientMirror.
std::string path
KIBIS top(path, &reporter)
KIBIS_PIN * pin
VECTOR2I end
int delta
@ TA_CHOICE_MENU_CHOICE
Context menu choice.
Definition tool_event.h:94
@ TA_UNDO_REDO_PRE
This event is sent before undo/redo command is performed.
Definition tool_event.h:102
@ TA_MOUSE_WHEEL
Definition tool_event.h:69
std::optional< TOOL_EVENT > OPT_TOOL_EVENT
Definition tool_event.h:637
@ MD_ALT
Definition tool_event.h:141
@ MD_CTRL
Definition tool_event.h:140
@ MD_SHIFT
Definition tool_event.h:139
@ TC_COMMAND
Definition tool_event.h:53
@ BUT_AUX1
Definition tool_event.h:131
@ BUT_MIDDLE
Definition tool_event.h:130
@ BUT_LEFT
Definition tool_event.h:128
@ BUT_RIGHT
Definition tool_event.h:129
@ BUT_AUX2
Definition tool_event.h:132
@ SCH_GROUP_T
Definition typeinfo.h:170
@ SCH_TABLE_T
Definition typeinfo.h:162
@ SCH_LINE_T
Definition typeinfo.h:160
@ LIB_SYMBOL_T
Definition typeinfo.h:145
@ SCH_NO_CONNECT_T
Definition typeinfo.h:157
@ SCH_SYMBOL_T
Definition typeinfo.h:169
@ SCH_TABLECELL_T
Definition typeinfo.h:163
@ SCH_ITEM_LOCATE_WIRE_T
Definition typeinfo.h:183
@ SCH_FIELD_T
Definition typeinfo.h:147
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:168
@ SCH_LABEL_T
Definition typeinfo.h:164
@ SCH_LOCATE_ANY_T
Definition typeinfo.h:196
@ SCH_SHEET_T
Definition typeinfo.h:172
@ SCH_ITEM_LOCATE_BUS_T
Definition typeinfo.h:184
@ SCH_MARKER_T
Definition typeinfo.h:155
@ SCH_SHAPE_T
Definition typeinfo.h:146
@ SCH_RULE_AREA_T
Definition typeinfo.h:167
@ SCH_HIER_LABEL_T
Definition typeinfo.h:166
@ NOT_USED
the 3d code uses this value
Definition typeinfo.h:72
@ SCH_BUS_BUS_ENTRY_T
Definition typeinfo.h:159
@ SCH_ITEM_LOCATE_GRAPHIC_LINE_T
Definition typeinfo.h:185
@ SCHEMATIC_T
Definition typeinfo.h:201
@ SCH_SHEET_PIN_T
Definition typeinfo.h:171
@ SCH_TEXT_T
Definition typeinfo.h:148
@ SCH_SYMBOL_LOCATE_POWER_T
Definition typeinfo.h:193
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:158
@ SCH_BITMAP_T
Definition typeinfo.h:161
@ SCH_TEXTBOX_T
Definition typeinfo.h:149
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:165
@ SCH_JUNCTION_T
Definition typeinfo.h:156
@ SCH_PIN_T
Definition typeinfo.h:150
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682