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 they're inside a group that we haven't entered
1079 else if( CollectHits( collector, evt->DragOrigin(), { SCH_TABLECELL_T } )
1080 && ( collector[0]->GetParent()->GetParentGroup() == nullptr
1081 || collector[0]->GetParent()->GetParentGroup() == m_enteredGroup ) )
1082 {
1083 selectTableCells( static_cast<SCH_TABLE*>( collector[0]->GetParent() ) );
1084 }
1085 else if( hasModifier() || drag_action == MOUSE_DRAG_ACTION::SELECT )
1086 {
1089 selectLasso();
1090 else
1092 }
1093 else if( m_selection.Empty() && drag_action != MOUSE_DRAG_ACTION::DRAG_ANY )
1094 {
1097 selectLasso();
1098 else
1100 }
1101 else
1102 {
1103 if( m_isSymbolEditor )
1104 {
1105 if( static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->IsSymbolAlias() )
1106 {
1108 }
1109 else
1110 {
1112 SCH_TEXT_T,
1114 SCH_PIN_T,
1115 SCH_FIELD_T } );
1116 }
1117 }
1118 else
1119 {
1121 }
1122
1123 // Check if dragging has started within any of selected items bounding box
1124 if( evt->HasPosition() && selectionContains( evt->DragOrigin() ) )
1125 {
1126 // drag_is_move option exists only in schematic editor, not in symbol editor
1127 // (m_frame->eeconfig() returns nullptr in Symbol Editor)
1128 if( m_isSymbolEditor || m_frame->eeconfig()->m_Input.drag_is_move )
1129 m_toolMgr->RunAction( SCH_ACTIONS::move );
1130 else
1131 m_toolMgr->RunAction( SCH_ACTIONS::drag );
1132 }
1133 else
1134 {
1135 // No -> drag a selection box
1138 selectLasso();
1139 else
1141 }
1142 }
1143 }
1144 else if( evt->IsMouseDown( BUT_AUX1 ) )
1145 {
1147 }
1148 else if( evt->IsMouseDown( BUT_AUX2 ) )
1149 {
1151 }
1152 else if( evt->Action() == TA_MOUSE_WHEEL )
1153 {
1154 int field = -1;
1155
1156 if( evt->Modifier() == ( MD_SHIFT | MD_ALT ) )
1157 field = 0;
1158 else if( evt->Modifier() == ( MD_CTRL | MD_ALT ) )
1159 field = 1;
1160 // any more?
1161
1162 if( field >= 0 )
1163 {
1164 const int delta = evt->Parameter<int>();
1165 ACTIONS::INCREMENT incParams{ delta > 0 ? 1 : -1, field };
1166
1167 m_toolMgr->RunAction( ACTIONS::increment, incParams );
1168 }
1169 }
1170 else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CHOICE_MENU_CHOICE )
1171 {
1172 m_disambiguateTimer.Stop();
1173
1174 // context sub-menu selection? Handle unit selection or bus unfolding
1175 if( *evt->GetCommandId() >= ID_POPUP_SCH_SELECT_UNIT
1176 && *evt->GetCommandId() <= ID_POPUP_SCH_SELECT_UNIT_END )
1177 {
1178 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( m_selection.Front() );
1179 int unit = *evt->GetCommandId() - ID_POPUP_SCH_SELECT_UNIT;
1180
1181 if( symbol )
1182 static_cast<SCH_EDIT_FRAME*>( m_frame )->SelectUnit( symbol, unit );
1183 }
1184 else if( *evt->GetCommandId() >= ID_POPUP_SCH_PLACE_UNIT
1185 && *evt->GetCommandId() <= ID_POPUP_SCH_PLACE_UNIT_END )
1186 {
1187 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( m_selection.Front() );
1188 int unit = *evt->GetCommandId() - ID_POPUP_SCH_PLACE_UNIT;
1189
1190 if( symbol )
1192 SCH_ACTIONS::PLACE_SYMBOL_UNIT_PARAMS{ symbol, unit } );
1193 }
1194 else if( *evt->GetCommandId() >= ID_POPUP_SCH_SELECT_BODY_STYLE
1195 && *evt->GetCommandId() <= ID_POPUP_SCH_SELECT_BODY_STYLE_END )
1196 {
1197 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( m_selection.Front() );
1198 int bodyStyle = ( *evt->GetCommandId() - ID_POPUP_SCH_SELECT_BODY_STYLE ) + 1;
1199
1200 if( symbol && symbol->GetBodyStyle() != bodyStyle )
1201 static_cast<SCH_EDIT_FRAME*>( m_frame )->SelectBodyStyle( symbol, bodyStyle );
1202 }
1203 else if( *evt->GetCommandId() >= ID_POPUP_SCH_ALT_PIN_FUNCTION
1204 && *evt->GetCommandId() <= ID_POPUP_SCH_ALT_PIN_FUNCTION_END )
1205 {
1206 SCH_PIN* pin = dynamic_cast<SCH_PIN*>( m_selection.Front() );
1207 wxString alt = *evt->Parameter<wxString*>();
1208
1209 if( pin )
1210 static_cast<SCH_EDIT_FRAME*>( m_frame )->SetAltPinFunction( pin, alt );
1211 }
1212 else if( *evt->GetCommandId() >= ID_POPUP_SCH_PIN_TRICKS_START
1213 && *evt->GetCommandId() <= ID_POPUP_SCH_PIN_TRICKS_END
1215 {
1216 SCH_EDIT_FRAME* sch_frame = static_cast<SCH_EDIT_FRAME*>( m_frame );
1217
1218 // Keep track of new items so we make them the new selection at the end
1219 EDA_ITEMS newItems;
1220 SCH_COMMIT commit( sch_frame );
1221
1222 if( *evt->GetCommandId() == ID_POPUP_SCH_PIN_TRICKS_NO_CONNECT )
1223 {
1224 for( EDA_ITEM* item : m_selection )
1225 {
1226 if( item->Type() != SCH_PIN_T && item->Type() != SCH_SHEET_PIN_T )
1227 continue;
1228
1229 SCH_NO_CONNECT* nc = new SCH_NO_CONNECT( item->GetPosition() );
1230 commit.Add( nc, sch_frame->GetScreen() );
1231 newItems.push_back( nc );
1232 }
1233
1234 if( !commit.Empty() )
1235 {
1236 commit.Push( wxS( "No Connect Pins" ) );
1238 }
1239 }
1240 else if( *evt->GetCommandId() == ID_POPUP_SCH_PIN_TRICKS_WIRE )
1241 {
1242 VECTOR2I wireGrid = grid.GetGridSize( GRID_HELPER_GRIDS::GRID_WIRES );
1243
1244 for( EDA_ITEM* item : m_selection )
1245 {
1246 if( item->Type() != SCH_PIN_T && item->Type() != SCH_SHEET_PIN_T )
1247 continue;
1248
1249 SCH_LINE* wire = new SCH_LINE( item->GetPosition(), LAYER_WIRE );
1250
1251 // Add some length to the wire as nothing in our code base handles
1252 // 0 length wires very well, least of all the ortho drag algorithm
1253 VECTOR2I stub;
1254
1255 switch( pinOrientation( item ) )
1256 {
1257 default:
1259 stub = VECTOR2I( -1 * wireGrid.x, 0 );
1260 break;
1262 stub = VECTOR2I( 1 * wireGrid.x, 0 );
1263 break;
1265 stub = VECTOR2I( 0, 1 * wireGrid.y );
1266 break;
1268 stub = VECTOR2I( 0, -1 * wireGrid.y );
1269 break;
1270 }
1271
1272 wire->SetEndPoint( item->GetPosition() + stub );
1273
1274 m_frame->AddToScreen( wire, sch_frame->GetScreen() );
1275 commit.Added( wire, sch_frame->GetScreen() );
1276 newItems.push_back( wire );
1277 }
1278
1279 if( !commit.Empty() )
1280 {
1282 AddItemsToSel( &newItems );
1283
1284 // Select only the ends so we can immediately start dragging them
1285 for( EDA_ITEM* item : newItems )
1286 static_cast<SCH_LINE*>( item )->SetFlags( ENDPOINT );
1287
1289
1290 // Put the mouse on the nearest point of the first wire
1291 SCH_LINE* first = static_cast<SCH_LINE*>( newItems[0] );
1292 vc->SetCrossHairCursorPosition( first->GetEndPoint(), false );
1293 vc->WarpMouseCursor( vc->GetCursorPosition(), true );
1294
1295 // Start the drag tool, canceling will remove the wires
1296 if( m_toolMgr->RunSynchronousAction( SCH_ACTIONS::drag, &commit ) )
1297 commit.Push( wxS( "Wire Pins" ) );
1298 else
1299 commit.Revert();
1300 }
1301 }
1302 else
1303 {
1304 // For every pin in the selection, add a label according to menu item
1305 // selected by the user
1306 for( EDA_ITEM* item : m_selection )
1307 {
1308 SCH_PIN* pin = dynamic_cast<SCH_PIN*>( item );
1309 SCH_SHEET_PIN* sheetPin = dynamic_cast<SCH_SHEET_PIN*>( item );
1310 SCH_LABEL_BASE* label = nullptr;
1311 SCH_SHEET_PATH& sheetPath = sch_frame->GetCurrentSheet();
1312
1313 wxString labelText;
1314
1315 if( pin )
1316 {
1317 labelText = pin->GetShownName();
1318
1319 if( labelText.IsEmpty() )
1320 {
1321 labelText.Printf( "%s_%s",
1322 pin->GetParentSymbol()->GetRef( &sheetPath ),
1323 pin->GetNumber() );
1324 }
1325 }
1326 else if( sheetPin )
1327 {
1328 labelText = sheetPin->GetShownText( &sheetPath, false );
1329 }
1330 else
1331 {
1332 continue;
1333 }
1334
1335 switch( *evt->GetCommandId() )
1336 {
1338 label = new SCH_LABEL( item->GetPosition(), labelText );
1339 break;
1341 label = new SCH_HIERLABEL( item->GetPosition(), labelText );
1342 break;
1344 label = new SCH_GLOBALLABEL( item->GetPosition(), labelText );
1345 break;
1346 default:
1347 continue;
1348 }
1349
1350 switch( pinOrientation( item ) )
1351 {
1352 default:
1355 break;
1358 break;
1361 break;
1364 break;
1365 }
1366
1368
1369 if( pin )
1370 {
1371 pinType = pin->GetType();
1372 }
1373 else if( sheetPin )
1374 {
1375 switch( sheetPin->GetLabelShape() )
1376 {
1377 case LABEL_INPUT: pinType = ELECTRICAL_PINTYPE::PT_INPUT; break;
1378 case LABEL_OUTPUT: pinType = ELECTRICAL_PINTYPE::PT_OUTPUT; break;
1379 case LABEL_BIDI: pinType = ELECTRICAL_PINTYPE::PT_BIDI; break;
1380 case LABEL_TRISTATE: pinType = ELECTRICAL_PINTYPE::PT_TRISTATE; break;
1381 case LABEL_PASSIVE: pinType = ELECTRICAL_PINTYPE::PT_PASSIVE; break;
1382 }
1383 }
1384
1385 switch( pinType )
1386 {
1389 break;
1392 break;
1395 break;
1398 break;
1401 break;
1402 default:
1404 }
1405
1406 commit.Add( label, sch_frame->GetScreen() );
1407 newItems.push_back( label );
1408 }
1409
1410 if( !commit.Empty() )
1411 {
1412 commit.Push( wxS( "Label Pins" ) );
1413
1414 // Many users will want to drag these items to wire off of the pins, so
1415 // pre-select them.
1417 AddItemsToSel( &newItems );
1418 }
1419 }
1420 }
1421 else if( *evt->GetCommandId() >= ID_POPUP_SCH_UNFOLD_BUS
1422 && *evt->GetCommandId() <= ID_POPUP_SCH_UNFOLD_BUS_END )
1423 {
1424 wxString* net = new wxString( *evt->Parameter<wxString*>() );
1425 m_toolMgr->RunAction<wxString*>( SCH_ACTIONS::unfoldBus, net );
1426 }
1427 }
1428 else if( evt->IsCancelInteractive() )
1429 {
1430 m_disambiguateTimer.Stop();
1431
1432 // We didn't set these, but we have reports that they leak out of some other tools,
1433 // so we clear them here.
1434 getViewControls()->SetAutoPan( false );
1435 getViewControls()->CaptureCursor( false );
1436
1437 if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
1438 schframe->ClearFocus();
1439
1440 if( !GetSelection().Empty() )
1441 {
1443 }
1444 else if( evt->FirstResponder() == this && evt->GetCommandId() == (int) WXK_ESCAPE )
1445 {
1446 if( m_enteredGroup )
1447 {
1448 ExitGroup();
1449 }
1450 else
1451 {
1453
1454 if( editor && m_frame->eeconfig()->m_Input.esc_clears_net_highlight )
1455 editor->ClearHighlight( *evt );
1456 }
1457 }
1458 }
1459 else if( evt->Action() == TA_UNDO_REDO_PRE )
1460 {
1461 if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
1462 schframe->ClearFocus();
1463 }
1464 else if( evt->IsMotion() && !m_isSymbolEditor && evt->FirstResponder() == this )
1465 {
1466 // Update cursor and rollover item
1467 rolloverItemId = niluuid;
1468 SCH_COLLECTOR collector;
1469
1471
1472 if( CollectHits( collector, evt->Position() ) )
1473 {
1474 narrowSelection( collector, evt->Position(), false, false, nullptr );
1475
1476 if( collector.GetCount() == 1 && !hasModifier() )
1477 {
1478 OPT_TOOL_EVENT autostartEvt = autostartEvent( evt, grid, collector[0] );
1479
1480 if( autostartEvt )
1481 {
1482 if( autostartEvt->Matches( SCH_ACTIONS::drawBus.MakeEvent() ) )
1483 displayBusCursor = true;
1484 else if( autostartEvt->Matches( SCH_ACTIONS::drawWire.MakeEvent() ) )
1485 displayWireCursor = true;
1486 else if( autostartEvt->Matches( SCH_ACTIONS::drawLines.MakeEvent() ) )
1487 displayLineCursor = true;
1488 }
1489 else if( collector[0]->HasHypertext() && !collector[0]->IsSelected() )
1490 {
1491 rolloverItemId = collector[0]->m_Uuid;
1492 }
1493 }
1494 }
1495 }
1496 else
1497 {
1498 evt->SetPassEvent();
1499 }
1500
1501 if( lastRolloverItemId != niluuid && lastRolloverItemId != rolloverItemId )
1502 {
1503 EDA_ITEM* item = m_frame->ResolveItem( lastRolloverItemId );
1504
1505 item->SetIsRollover( false, { 0, 0 } );
1506
1507 if( item->Type() == SCH_FIELD_T || item->Type() == SCH_TABLECELL_T )
1508 m_frame->GetCanvas()->GetView()->Update( item->GetParent() );
1509 else
1510 m_frame->GetCanvas()->GetView()->Update( item );
1511 }
1512
1513 SCH_ITEM* rolloverItem = nullptr;
1514
1515 if( rolloverItemId != niluuid )
1516 {
1517 rolloverItem = static_cast<SCH_ITEM*>( m_frame->ResolveItem( rolloverItemId ) );
1518
1519 rolloverItem->SetIsRollover( true, getViewControls()->GetMousePosition() );
1520
1521 if( rolloverItem->Type() == SCH_FIELD_T || rolloverItem->Type() == SCH_TABLECELL_T )
1522 m_frame->GetCanvas()->GetView()->Update( rolloverItem->GetParent() );
1523 else
1524 m_frame->GetCanvas()->GetView()->Update( rolloverItem );
1525 }
1526
1527 lastRolloverItemId = rolloverItemId;
1528
1529 if( m_frame->ToolStackIsEmpty() )
1530 {
1531 if( displayWireCursor )
1532 {
1534 }
1535 else if( displayBusCursor )
1536 {
1538 }
1539 else if( displayLineCursor )
1540 {
1542 }
1543 else if( rolloverItem && rolloverItem->HasHoveredHypertext() )
1544 {
1546 }
1547 else if( !m_selection.Empty()
1548 && drag_action == MOUSE_DRAG_ACTION::DRAG_SELECTED
1549 && evt->HasPosition()
1550 && selectionContains( evt->Position() ) //move/drag option prediction
1551 )
1552 {
1554 }
1555 else
1556 {
1558 }
1559 }
1560 }
1561
1562 m_disambiguateTimer.Stop();
1563
1564 // Shutting down; clear the selection
1565 m_selection.Clear();
1566
1567 return 0;
1568}
1569
1570
1572{
1573 wxCHECK_RET( m_selection.GetSize() == 1 && m_selection[0]->Type() == SCH_GROUP_T,
1574 wxT( "EnterGroup called when selection is not a single group" ) );
1575 SCH_GROUP* aGroup = static_cast<SCH_GROUP*>( m_selection[0] );
1576
1577 if( m_enteredGroup != nullptr )
1578 ExitGroup();
1579
1581 m_enteredGroup = aGroup;
1582 m_enteredGroup->SetFlags( ENTERED );
1583 m_enteredGroup->RunOnChildren(
1584 [&]( SCH_ITEM* aChild )
1585 {
1586 if( aChild->Type() == SCH_LINE_T )
1587 aChild->SetFlags( STARTPOINT | ENDPOINT );
1588
1589 select( aChild );
1590 },
1592
1593 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1594
1595 // Processing the selection event can re-enter the tool and ExitGroup(), which clears
1596 // m_enteredGroup. If that happened, don't operate on the now-stale (possibly null) group
1597 // or we would hide/overlay a null item and crash (issue #24778).
1598 if( m_enteredGroup != aGroup )
1599 return;
1600
1601 getView()->Hide( m_enteredGroup, true );
1604}
1605
1606
1607void SCH_SELECTION_TOOL::ExitGroup( bool aSelectGroup )
1608{
1609 // Only continue if there is a group entered
1610 if( m_enteredGroup == nullptr )
1611 return;
1612
1613 m_enteredGroup->ClearFlags( ENTERED );
1614 getView()->Hide( m_enteredGroup, false );
1616
1617 if( aSelectGroup )
1618 {
1620 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1621 }
1622
1623 m_enteredGroupOverlay.Clear();
1624 m_enteredGroup = nullptr;
1626}
1627
1628
1630 SCH_ITEM* aItem )
1631{
1632 VECTOR2I pos = aGrid.BestSnapAnchor( aEvent->Position(), aGrid.GetItemGrid( aItem ) );
1633
1634 if( m_frame->eeconfig()->m_Drawing.auto_start_wires
1635 && !m_toolMgr->GetTool<SCH_POINT_EDITOR>()->HasPoint()
1636 && aItem->IsPointClickableAnchor( pos ) )
1637 {
1638 OPT_TOOL_EVENT newEvt = SCH_ACTIONS::drawWire.MakeEvent();
1639
1640 if( aItem->Type() == SCH_BUS_BUS_ENTRY_T )
1641 {
1642 newEvt = SCH_ACTIONS::drawBus.MakeEvent();
1643 }
1644 else if( aItem->Type() == SCH_BUS_WIRE_ENTRY_T )
1645 {
1646 SCH_BUS_WIRE_ENTRY* busEntry = static_cast<SCH_BUS_WIRE_ENTRY*>( aItem );
1647
1648 if( !busEntry->m_connected_bus_item )
1649 newEvt = SCH_ACTIONS::drawBus.MakeEvent();
1650 }
1651 else if( aItem->Type() == SCH_LINE_T )
1652 {
1653 SCH_LINE* line = static_cast<SCH_LINE*>( aItem );
1654
1655 if( line->IsBus() )
1656 newEvt = SCH_ACTIONS::drawBus.MakeEvent();
1657 else if( line->IsGraphicLine() )
1658 newEvt = SCH_ACTIONS::drawLines.MakeEvent();
1659 }
1660 else if( aItem->Type() == SCH_LABEL_T || aItem->Type() == SCH_HIER_LABEL_T
1661 || aItem->Type() == SCH_SHEET_PIN_T || aItem->Type() == SCH_GLOBAL_LABEL_T )
1662 {
1663 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( aItem );
1664 SCH_CONNECTION possibleConnection( label->Schematic()->ConnectionGraph() );
1665 possibleConnection.ConfigureFromLabel( label->GetShownText( false ) );
1666
1667 if( possibleConnection.IsBus() )
1668 newEvt = SCH_ACTIONS::drawBus.MakeEvent();
1669 }
1670 else if( aItem->Type() == SCH_SYMBOL_T )
1671 {
1672 const SCH_SYMBOL* symbol = static_cast<const SCH_SYMBOL*>( aItem );
1673 const SCH_PIN* pin = symbol->GetPin( pos );
1674
1675 if( !pin || !pin->IsPointClickableAnchor( pos ) )
1676 return OPT_TOOL_EVENT();
1677
1678 if( !pin->IsVisible()
1679 && !( m_frame->eeconfig()->m_Appearance.show_hidden_pins
1680 || m_frame->GetRenderSettings()->m_ShowHiddenPins ) )
1681 {
1682 return OPT_TOOL_EVENT();
1683 }
1684 }
1685
1686 newEvt->SetMousePosition( pos );
1687 newEvt->SetHasPosition( true );
1688 newEvt->SetForceImmediate( true );
1689
1690 getViewControls()->ForceCursorPosition( true, pos );
1691
1692 return newEvt;
1693 }
1694
1695 return OPT_TOOL_EVENT();
1696}
1697
1698
1700{
1701 wxMouseState keyboardState = wxGetMouseState();
1702
1703 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
1704 keyboardState.AltDown() );
1705
1706 m_skip_heuristics = true;
1709 m_skip_heuristics = false;
1710
1711 return 0;
1712}
1713
1714
1715void SCH_SELECTION_TOOL::OnIdle( wxIdleEvent& aEvent )
1716{
1717 if( m_frame->ToolStackIsEmpty() && !m_multiple )
1718 {
1719 wxMouseState keyboardState = wxGetMouseState();
1720
1721 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
1722 keyboardState.AltDown() );
1723
1724 if( m_additive )
1725 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ADD );
1726 else if( m_subtractive )
1727 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SUBTRACT );
1728 else if( m_exclusive_or )
1729 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::XOR );
1730 else
1731 m_frame->GetCanvas()->SetCurrentCursor( m_nonModifiedCursor );
1732 }
1733}
1734
1735
1740
1741
1743 const std::vector<KICAD_T>& aScanTypes )
1744{
1745 int pixelThreshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
1746 int gridThreshold = KiROUND( getView()->GetGAL()->GetGridSize().EuclideanNorm() / 2.0 );
1747 aCollector.m_Threshold = std::max( pixelThreshold, gridThreshold );
1748 aCollector.m_ShowPinElectricalTypes = m_frame->GetRenderSettings()->m_ShowPinsElectricalType;
1749
1750 if( m_isSymbolEditor )
1751 {
1752 LIB_SYMBOL* symbol = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
1753
1754 if( !symbol )
1755 return false;
1756
1757 aCollector.Collect( symbol->GetDrawItems(), aScanTypes, aWhere, m_unit, m_bodyStyle );
1758 }
1759 else
1760 {
1761 aCollector.Collect( m_frame->GetScreen(), aScanTypes, aWhere, m_unit, m_bodyStyle );
1762
1763 // If pins are disabled in the filter, they will be removed later. Let's add the parent
1764 // so that people can use pins to select symbols in this case.
1765 if( !m_filter.pins )
1766 {
1767 int originalCount = aCollector.GetCount();
1768
1769 for( int ii = 0; ii < originalCount; ++ii )
1770 {
1771 if( aCollector[ii]->Type() == SCH_PIN_T )
1772 {
1773 SCH_PIN* pin = static_cast<SCH_PIN*>( aCollector[ii] );
1774
1775 if( !aCollector.HasItem( pin->GetParentSymbol() ) )
1776 aCollector.Append( pin->GetParentSymbol() );
1777 }
1778 }
1779 }
1780 }
1781
1782 return aCollector.GetCount() > 0;
1783}
1784
1785
1787 bool aCheckLocked, bool aSelectedOnly,
1788 SCH_SELECTION_FILTER_OPTIONS* aRejected )
1789{
1790 SYMBOL_EDIT_FRAME* symbolEditorFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
1791
1792 for( int i = collector.GetCount() - 1; i >= 0; --i )
1793 {
1794 if( symbolEditorFrame )
1795 {
1796 // Do not select invisible items if they are not displayed
1797 EDA_ITEM* item = collector[i];
1798
1799 if( item->Type() == SCH_FIELD_T )
1800 {
1801 if( !static_cast<SCH_FIELD*>( item )->IsVisible()
1802 && !symbolEditorFrame->GetShowInvisibleFields() )
1803 {
1804 collector.Remove( i );
1805 continue;
1806 }
1807 }
1808 else if( item->Type() == SCH_PIN_T )
1809 {
1810 if( !static_cast<SCH_PIN*>( item )->IsVisible()
1811 && !symbolEditorFrame->GetShowInvisiblePins() )
1812 {
1813 collector.Remove( i );
1814 continue;
1815 }
1816 }
1817 }
1818
1819 if( !Selectable( collector[i], &aWhere ) )
1820 {
1821 collector.Remove( i );
1822 continue;
1823 }
1824
1825 if( aCheckLocked && collector[i]->IsLocked() )
1826 {
1827 if( aRejected )
1828 aRejected->lockedItems = true;
1829 collector.Remove( i );
1830 continue;
1831 }
1832
1833 if( !itemPassesFilter( collector[i], aRejected ) )
1834 {
1835 collector.Remove( i );
1836 continue;
1837 }
1838
1839 if( aSelectedOnly && !collector[i]->IsSelected() )
1840 {
1841 collector.Remove( i );
1842 continue;
1843 }
1844 }
1845
1846 filterCollectorForHierarchy( collector, false );
1847
1848 // Apply some ugly heuristics to avoid disambiguation menus whenever possible
1849 if( collector.GetCount() > 1 && !m_skip_heuristics )
1850 GuessSelectionCandidates( collector, aWhere );
1851}
1852
1853
1855 EDA_ITEM** aItem, bool* aSelectionCancelledFlag, bool aAdd,
1856 bool aSubtract, bool aExclusiveOr )
1857{
1858 m_selection.ClearReferencePoint();
1859
1860 // If still more than one item we're going to have to ask the user.
1861 if( aCollector.GetCount() > 1 )
1862 {
1863 // Try to call selectionMenu via RunAction() to avoid event-loop contention
1864 // But it we cannot handle the event, then we don't have an active tool loop, so
1865 // handle it directly.
1866 if( !m_toolMgr->RunAction<COLLECTOR*>( ACTIONS::selectionMenu, &aCollector ) )
1867 {
1868 if( !doSelectionMenu( &aCollector ) )
1869 aCollector.m_MenuCancelled = true;
1870 }
1871
1872 if( aCollector.m_MenuCancelled )
1873 {
1874 if( aSelectionCancelledFlag )
1875 *aSelectionCancelledFlag = true;
1876
1877 return false;
1878 }
1879 }
1880
1881 if( !aAdd && !aSubtract && !aExclusiveOr )
1883
1884 // It is possible for slop in the selection model to cause us to be outside the group,
1885 // but also selecting an item within the group, so only exit if the selection doesn't
1886 // have an item belonging to the group
1887 if( m_enteredGroup && !m_enteredGroup->GetBoundingBox().Contains( aWhere ) )
1888 {
1889 bool foundEnteredGroup = false;
1890 for( EDA_ITEM* item : aCollector )
1891 {
1892 if( item->GetParentGroup() == m_enteredGroup )
1893 {
1894 foundEnteredGroup = true;
1895 break;
1896 }
1897 }
1898
1899 if( !foundEnteredGroup )
1900 ExitGroup();
1901 }
1902
1903 filterCollectorForHierarchy( aCollector, true );
1904
1905 int addedCount = 0;
1906 bool anySubtracted = false;
1907
1908 if( aCollector.GetCount() > 0 )
1909 {
1910 for( int i = 0; i < aCollector.GetCount(); ++i )
1911 {
1912 EDA_ITEM_FLAGS flags = 0;
1913 bool isLine = aCollector[i]->Type() == SCH_LINE_T;
1914
1915 // Handle line ends specially
1916 if( isLine )
1917 {
1918 SCH_LINE* line = (SCH_LINE*) aCollector[i];
1919
1920 if( line->GetStartPoint().Distance( aWhere ) <= aCollector.m_Threshold )
1921 flags = STARTPOINT;
1922 else if( line->GetEndPoint().Distance( aWhere ) <= aCollector.m_Threshold )
1923 flags = ENDPOINT;
1924 else
1925 flags = STARTPOINT | ENDPOINT;
1926 }
1927
1928 if( aSubtract
1929 || ( aExclusiveOr && aCollector[i]->IsSelected()
1930 && ( !isLine || ( isLine && aCollector[i]->HasFlag( flags ) ) ) ) )
1931 {
1932 aCollector[i]->ClearFlags( flags );
1933
1934 // Need to update end shadows after ctrl-click unselecting one of two selected
1935 // endpoints.
1936 if( isLine )
1937 getView()->Update( aCollector[i] );
1938
1939 if( !aCollector[i]->HasFlag( STARTPOINT ) && !aCollector[i]->HasFlag( ENDPOINT ) )
1940 {
1941 unselect( aCollector[i] );
1942 anySubtracted = true;
1943 }
1944 }
1945 else
1946 {
1947 aCollector[i]->SetFlags( flags );
1948 select( aCollector[i] );
1949 addedCount++;
1950 }
1951 }
1952 }
1953
1954 if( addedCount == 1 )
1955 {
1956 m_toolMgr->ProcessEvent( EVENTS::PointSelectedEvent );
1957
1958 if( aItem && aCollector.GetCount() == 1 )
1959 *aItem = aCollector[0];
1960
1961 return true;
1962 }
1963 else if( addedCount > 1 )
1964 {
1965 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1966 return true;
1967 }
1968 else if( anySubtracted )
1969 {
1970 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1971 return true;
1972 }
1973
1974 m_frame->GetCanvas()->ForceRefresh();
1975 return false;
1976}
1977
1978
1980 const std::vector<KICAD_T>& aScanTypes,
1981 EDA_ITEM** aItem, bool* aSelectionCancelledFlag,
1982 bool aCheckLocked, bool aAdd, bool aSubtract,
1983 bool aExclusiveOr )
1984{
1985 SCH_COLLECTOR collector;
1986
1987 if( !CollectHits( collector, aWhere, aScanTypes ) )
1988 return false;
1989
1990 size_t preFilterCount = collector.GetCount();
1992 rejected.SetAll( false );
1993 narrowSelection( collector, aWhere, aCheckLocked, aSubtract, &rejected );
1994
1995 if( collector.GetCount() == 0 && preFilterCount > 0 )
1996 {
1997 if( SCH_BASE_FRAME* frame = dynamic_cast<SCH_BASE_FRAME*>( m_frame ) )
1998 frame->HighlightSelectionFilter( rejected );
1999
2000 if( !aAdd && !aSubtract && !aExclusiveOr && m_selection.GetSize() > 0 )
2001 {
2002 ClearSelection( true /*quiet mode*/ );
2003 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2004 }
2005
2006 return false;
2007 }
2008
2009 return selectPoint( collector, aWhere, aItem, aSelectionCancelledFlag, aAdd, aSubtract,
2010 aExclusiveOr );
2011}
2012
2013
2015{
2016 SCH_COLLECTOR collection;
2017 m_multiple = true; // Multiple selection mode is active
2018 KIGFX::VIEW* view = getView();
2019
2020 std::vector<EDA_ITEM*> sheetPins;
2021
2022 // Filter the view items based on the selection box
2023 BOX2I selectionBox;
2024
2025 selectionBox.SetMaximum();
2026 view->Query( selectionBox,
2027 [&]( KIGFX::VIEW_ITEM* viewItem ) -> bool
2028 {
2029 SCH_ITEM* item = static_cast<SCH_ITEM*>( viewItem );
2030
2031 if( !item )
2032 return true;
2033
2034 collection.Append( item );
2035 return true;
2036 } );
2037
2038 filterCollectorForHierarchy( collection, true );
2039
2040 // Sheet pins aren't in the view; add them by hand
2041 for( EDA_ITEM* item : collection )
2042 {
2043 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item );
2044
2045 if( sheet )
2046 {
2047 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
2048 sheetPins.emplace_back( pin );
2049 }
2050 }
2051
2052 for( EDA_ITEM* pin : sheetPins )
2053 collection.Append( pin );
2054
2055 for( EDA_ITEM* item : collection )
2056 {
2057 if( Selectable( item ) && itemPassesFilter( item, nullptr ) )
2058 {
2059 if( item->Type() == SCH_LINE_T )
2060 item->SetFlags( STARTPOINT | ENDPOINT );
2061
2062 select( item );
2063 }
2064 }
2065
2066 m_multiple = false;
2067
2068 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2069 m_frame->GetCanvas()->ForceRefresh();
2070 return 0;
2071}
2072
2074{
2075 m_multiple = true; // Multiple selection mode is active
2076 KIGFX::VIEW* view = getView();
2077
2078 // hold all visible items
2079 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
2080
2081 // Filter the view items based on the selection box
2082 BOX2I selectionBox;
2083
2084 selectionBox.SetMaximum();
2085 view->Query( selectionBox, selectedItems ); // Get the list of selected items
2086
2087 for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : selectedItems )
2088 {
2089 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( pair.first );
2090
2091 if( sheet )
2092 {
2093 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
2094 {
2095 EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( pin );
2096
2097 if( item && Selectable( item ) )
2098 unselect( item );
2099 }
2100 }
2101
2102 if( EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( pair.first ) )
2103 {
2104 if( Selectable( item ) )
2105 unselect( item );
2106 }
2107 }
2108
2109 m_multiple = false;
2110
2111 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2112 m_frame->GetCanvas()->ForceRefresh();
2113 return 0;
2114}
2115
2116
2118{
2119 // Prefer exact hits to sloppy ones
2120 std::set<EDA_ITEM*> exactHits;
2121
2122 for( int i = collector.GetCount() - 1; i >= 0; --i )
2123 {
2124 EDA_ITEM* item = collector[ i ];
2125 SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
2126 SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( item );
2127 SCH_TABLE* table = dynamic_cast<SCH_TABLE*>( item );
2128
2129 // Lines are hard to hit. Give them a bit more slop to still be considered "exact".
2130 if( line || ( shape && shape->GetShape() == SHAPE_T::POLY )
2131 || ( shape && shape->GetShape() == SHAPE_T::ARC ) )
2132 {
2133 int pixelThreshold = KiROUND( getView()->ToWorld( 6 ) );
2134
2135 if( item->HitTest( aPos, pixelThreshold ) )
2136 exactHits.insert( item );
2137 }
2138 else if( table )
2139 {
2140 // Consider table cells exact, but not the table itself
2141 }
2142 else
2143 {
2144
2145 if( m_frame->GetRenderSettings()->m_ShowPinsElectricalType )
2146 item->SetFlags( SHOW_ELEC_TYPE );
2147
2148 if( item->HitTest( aPos, 0 ) )
2149 exactHits.insert( item );
2150
2151 item->ClearFlags( SHOW_ELEC_TYPE );
2152 }
2153 }
2154
2155 if( exactHits.size() > 0 && exactHits.size() < (unsigned) collector.GetCount() )
2156 {
2157 for( int i = collector.GetCount() - 1; i >= 0; --i )
2158 {
2159 EDA_ITEM* item = collector[ i ];
2160
2161 if( !exactHits.contains( item ) )
2162 collector.Transfer( item );
2163 }
2164 }
2165
2166 // Find the closest item. (Note that at this point all hits are either exact or non-exact.)
2167 SEG poss( aPos, aPos );
2168 EDA_ITEM* closest = nullptr;
2169 int closestDist = INT_MAX / 4;
2170
2171 for( EDA_ITEM* item : collector )
2172 {
2173 BOX2I bbox = item->GetBoundingBox();
2174 int dist = INT_MAX / 4;
2175
2176 // A dominating item is one that would unfairly win distance tests
2177 // and mask out other items. For example, a filled rectangle "wins"
2178 // with a zero distance over anything inside it.
2179 bool dominating = false;
2180
2181 if( exactHits.contains( item ) )
2182 {
2183 if( item->Type() == SCH_PIN_T || item->Type() == SCH_JUNCTION_T )
2184 {
2185 closest = item;
2186 break;
2187 }
2188
2189 SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
2190 SCH_FIELD* field = dynamic_cast<SCH_FIELD*>( item );
2191 EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item );
2192 EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( item );
2193 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
2194
2195 if( line )
2196 {
2197 dist = line->GetSeg().Distance( aPos );
2198 }
2199 else if( field )
2200 {
2201 BOX2I box = field->GetBoundingBox();
2202 EDA_ANGLE orient = field->GetTextAngle();
2203
2204 if( field->GetParent() && field->GetParent()->Type() == SCH_SYMBOL_T )
2205 {
2206 if( static_cast<SCH_SYMBOL*>( field->GetParent() )->GetTransform().y1 )
2207 {
2208 if( orient.IsHorizontal() )
2209 orient = ANGLE_VERTICAL;
2210 else
2211 orient = ANGLE_HORIZONTAL;
2212 }
2213 }
2214
2215 field->GetEffectiveTextShape( false, box, orient )->Collide( poss, INT_MAX / 4, &dist );
2216 }
2217 else if( text )
2218 {
2219 text->GetEffectiveTextShape( false )->Collide( poss, INT_MAX / 4, &dist );
2220 }
2221 else if( shape )
2222 {
2223 auto shapes = std::make_shared<SHAPE_COMPOUND>( shape->MakeEffectiveShapesForHitTesting() );
2224
2225 shapes->Collide( poss, INT_MAX / 4, &dist );
2226
2227 // Filled shapes win hit tests anywhere inside them
2228 dominating = shape->IsFilledForHitTesting();
2229 }
2230 else if( symbol )
2231 {
2232 bbox = symbol->GetBodyBoundingBox();
2233
2234 SHAPE_RECT rect( bbox.GetPosition(), bbox.GetWidth(), bbox.GetHeight() );
2235
2236 if( bbox.Contains( aPos ) )
2237 dist = bbox.GetCenter().Distance( aPos );
2238 else
2239 rect.Collide( poss, closestDist, &dist );
2240 }
2241 else
2242 {
2243 dist = bbox.GetCenter().Distance( aPos );
2244 }
2245 }
2246 else
2247 {
2248 SHAPE_RECT rect( bbox.GetPosition(), bbox.GetWidth(), bbox.GetHeight() );
2249 rect.Collide( poss, collector.m_Threshold, &dist );
2250 }
2251
2252 // Don't promote dominating items to be the closest item
2253 // (they'll always win) - they'll still be available for selection, but they
2254 // won't boot out worthy competitors.
2255 if ( !dominating )
2256 {
2257 if( dist == closestDist )
2258 {
2259 if( item->GetParent() == closest )
2260 closest = item;
2261 }
2262 else if( dist < closestDist )
2263 {
2264 closestDist = dist;
2265 closest = item;
2266 }
2267 }
2268 }
2269
2270 // Construct a tight box (1/2 height and width) around the center of the closest item.
2271 // All items which exist at least partly outside this box have sufficient other areas
2272 // for selection and can be dropped.
2273 if( closest ) // Don't try and get a tight bbox if nothing is near the mouse pointer
2274 {
2275 BOX2I tightBox = closest->GetBoundingBox();
2276 tightBox.Inflate( -tightBox.GetWidth() / 4, -tightBox.GetHeight() / 4 );
2277
2278 for( int i = collector.GetCount() - 1; i >= 0; --i )
2279 {
2280 EDA_ITEM* item = collector[i];
2281
2282 if( item == closest )
2283 continue;
2284
2285 if( !item->HitTest( tightBox, true ) )
2286 collector.Transfer( item );
2287 }
2288 }
2289}
2290
2291
2292SCH_SELECTION& SCH_SELECTION_TOOL::RequestSelection( const std::vector<KICAD_T>& aScanTypes,
2293 bool aPromoteCellSelections,
2294 bool aPromoteGroups )
2295{
2296 bool anyUnselected = false;
2297 bool anySelected = false;
2298
2299 if( m_selection.Empty() )
2300 {
2301 VECTOR2D cursorPos = getViewControls()->GetCursorPosition( true );
2302
2304 SelectPoint( cursorPos, aScanTypes );
2305 m_selection.SetIsHover( true );
2306 m_selection.ClearReferencePoint();
2307 }
2308 else // Trim an existing selection by aFilterList
2309 {
2310 bool isMoving = false;
2311
2312 for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
2313 {
2314 EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
2315 isMoving |= static_cast<SCH_ITEM*>( item )->IsMoving();
2316
2317 if( !item->IsType( aScanTypes ) )
2318 {
2319 unselect( item );
2320 anyUnselected = true;
2321 }
2322 }
2323
2324 if( !isMoving )
2326 }
2327
2328 if( aPromoteGroups )
2329 {
2330 for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
2331 {
2332 EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
2333
2334 std::set<EDA_ITEM*> selectedChildren;
2335
2336 if( item->Type() == SCH_GROUP_T )
2337 {
2338 static_cast<SCH_ITEM*>(item)->RunOnChildren( [&]( SCH_ITEM* aChild )
2339 {
2340 if( aChild->IsType( aScanTypes ) )
2341 selectedChildren.insert( aChild );
2342 },
2344 unselect( item );
2345 anyUnselected = true;
2346 }
2347
2348 for( EDA_ITEM* child : selectedChildren )
2349 {
2350 if( !child->IsSelected() )
2351 {
2352 if( child->Type() == SCH_LINE_T )
2353 static_cast<SCH_LINE*>( child )->SetFlags( STARTPOINT | ENDPOINT );
2354
2355 select( child );
2356 anySelected = true;
2357 }
2358 }
2359 }
2360 }
2361
2362 if( aPromoteCellSelections )
2363 {
2364 std::set<EDA_ITEM*> parents;
2365
2366 for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
2367 {
2368 EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
2369
2370 if( item->Type() == SCH_TABLECELL_T )
2371 {
2372 parents.insert( item->GetParent() );
2373 unselect( item );
2374 anyUnselected = true;
2375 }
2376 }
2377
2378 for( EDA_ITEM* parent : parents )
2379 {
2380 if( !parent->IsSelected() )
2381 {
2382 select( parent );
2383 anySelected = true;
2384 }
2385 }
2386 }
2387
2388 if( anyUnselected )
2389 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2390
2391 if( anySelected )
2392 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2393
2394 return m_selection;
2395}
2396
2397
2398void SCH_SELECTION_TOOL::filterCollectedItems( SCH_COLLECTOR& aCollector, bool aMultiSelect )
2399{
2400 if( aCollector.GetCount() == 0 )
2401 return;
2402
2403 std::set<EDA_ITEM*> rejected;
2404
2405 for( EDA_ITEM* item : aCollector )
2406 {
2407 if( !itemPassesFilter( item, nullptr ) )
2408 rejected.insert( item );
2409 }
2410
2411 for( EDA_ITEM* item : rejected )
2412 aCollector.Remove( item );
2413}
2414
2415
2417{
2418 if( !aItem )
2419 return false;
2420
2421 if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( aItem ) )
2422 {
2423 if( schItem->IsLocked() && !m_filter.lockedItems )
2424 {
2425 if( aRejected )
2426 aRejected->lockedItems = true;
2427
2428 return false;
2429 }
2430 }
2431
2432 switch( aItem->Type() )
2433 {
2434 case SCH_SYMBOL_T:
2435 case SCH_SHEET_T:
2436 if( !m_filter.symbols )
2437 {
2438 if( aRejected )
2439 aRejected->symbols = true;
2440 return false;
2441 }
2442
2443 break;
2444
2445 case SCH_PIN_T:
2446 case SCH_SHEET_PIN_T:
2447 if( !m_filter.pins )
2448 {
2449 if( aRejected )
2450 aRejected->pins = true;
2451 return false;
2452 }
2453
2454 break;
2455
2456 case SCH_JUNCTION_T:
2457 if( !m_filter.wires )
2458 {
2459 if( aRejected )
2460 aRejected->wires = true;
2461 return false;
2462 }
2463
2464 break;
2465
2466 case SCH_LINE_T:
2467 {
2468 switch( static_cast<SCH_LINE*>( aItem )->GetLayer() )
2469 {
2470 case LAYER_WIRE:
2471 case LAYER_BUS:
2472 if( !m_filter.wires )
2473 {
2474 if( aRejected )
2475 aRejected->wires = true;
2476 return false;
2477 }
2478
2479 break;
2480
2481 default:
2482 if( !m_filter.graphics )
2483 {
2484 if( aRejected )
2485 aRejected->graphics = true;
2486 return false;
2487 }
2488 }
2489
2490 break;
2491 }
2492
2493 case SCH_SHAPE_T:
2494 if( !m_filter.graphics )
2495 {
2496 if( aRejected )
2497 aRejected->graphics = true;
2498 return false;
2499 }
2500
2501 break;
2502
2503 case SCH_TEXT_T:
2504 case SCH_TEXTBOX_T:
2505 case SCH_TABLE_T:
2506 case SCH_TABLECELL_T:
2507 case SCH_FIELD_T:
2508 if( !m_filter.text )
2509 {
2510 if( aRejected )
2511 aRejected->text = true;
2512 return false;
2513 }
2514
2515 break;
2516
2517 case SCH_LABEL_T:
2518 case SCH_GLOBAL_LABEL_T:
2519 case SCH_HIER_LABEL_T:
2520 if( !m_filter.labels )
2521 {
2522 if( aRejected )
2523 aRejected->labels = true;
2524 return false;
2525 }
2526
2527 break;
2528
2529 case SCH_BITMAP_T:
2530 if( !m_filter.images )
2531 {
2532 if( aRejected )
2533 aRejected->images = true;
2534 return false;
2535 }
2536
2537 break;
2538
2539 case SCH_RULE_AREA_T:
2540 if( !m_filter.ruleAreas )
2541 {
2542 if( aRejected )
2543 aRejected->ruleAreas = true;
2544 return false;
2545 }
2546
2547 break;
2548
2549 default:
2550 if( !m_filter.otherItems )
2551 {
2552 if( aRejected )
2553 aRejected->otherItems = true;
2554 return false;
2555 }
2556
2557 break;
2558 }
2559
2560 return true;
2561}
2562
2563
2565{
2566 VECTOR2I refP( 0, 0 );
2567
2568 if( m_selection.Size() > 0 )
2569 refP = static_cast<SCH_ITEM*>( m_selection.GetTopLeftItem() )->GetPosition();
2570
2571 m_selection.SetReferencePoint( refP );
2572}
2573
2574
2576{
2578 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
2579 m_toolMgr->PostAction( ACTIONS::selectionTool );
2580 return 0;
2581}
2582
2583
2585{
2587 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2588 m_toolMgr->PostAction( ACTIONS::selectionTool );
2589 return 0;
2590}
2591
2592
2593// Some navigation actions are allowed in selectMultiple
2604
2605
2607{
2608 // Block selection not allowed in symbol viewer frame: no actual code to handle
2609 // a selection, so return to avoid to draw a selection rectangle, and to avoid crashes.
2610 if( m_frame->IsType( FRAME_T::FRAME_SCH_VIEWER ) )
2611 return false;
2612
2613 bool cancelled = false; // Was the tool canceled while it was running?
2614 m_multiple = true; // Multiple selection mode is active
2615 KIGFX::VIEW* view = getView();
2616
2618 view->Add( &area );
2619
2620 while( TOOL_EVENT* evt = Wait() )
2621 {
2622 /* Selection mode depends on direction of drag-selection:
2623 * Left > Right : Select objects that are fully enclosed by selection
2624 * Right > Left : Select objects that are crossed by selection
2625 */
2626 bool isGreedy = area.GetEnd().x < area.GetOrigin().x;
2627
2628 if( view->IsMirroredX() )
2629 isGreedy = !isGreedy;
2630
2631 m_frame->GetCanvas()->SetCurrentCursor( isGreedy ? KICURSOR::SELECT_LASSO
2633
2634 if( evt->IsCancelInteractive() || evt->IsActivate() )
2635 {
2636 cancelled = true;
2637 break;
2638 }
2639
2640 if( evt->IsDrag( BUT_LEFT ) )
2641 {
2644
2645 // Start drawing a selection box
2646 area.SetOrigin( evt->DragOrigin() );
2647 area.SetEnd( evt->Position() );
2650 area.SetExclusiveOr( false );
2653
2654 view->SetVisible( &area, true );
2655 view->Update( &area );
2656 getViewControls()->SetAutoPan( true );
2657 }
2658
2659 if( evt->IsMouseUp( BUT_LEFT ) )
2660 {
2661 getViewControls()->SetAutoPan( false );
2662 view->SetVisible( &area, false );
2663 SelectMultiple( area, m_drag_subtractive, false );
2664 evt->SetPassEvent( false );
2665 break;
2666 }
2667
2668 passEvent( evt, allowedActions );
2669 }
2670
2671 getViewControls()->SetAutoPan( false );
2672
2673 // Stop drawing the selection box
2674 view->Remove( &area );
2675 m_multiple = false; // Multiple selection mode is inactive
2676
2677 if( !cancelled )
2678 m_selection.ClearReferencePoint();
2679
2680 return cancelled;
2681}
2682
2683
2685{
2686 bool cancelled = false;
2687 m_multiple = true;
2689 getView()->Add( &area );
2690 getView()->SetVisible( &area, true );
2691 getViewControls()->SetAutoPan( true );
2692
2693 SHAPE_LINE_CHAIN points;
2694 points.SetClosed( true );
2695
2697 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
2698
2699 while( TOOL_EVENT* evt = Wait() )
2700 {
2701 double shapeArea = area.GetPoly().Area( false );
2702 bool isClockwise = shapeArea > 0 ? true : false;
2703
2704 if( getView()->IsMirroredX() && shapeArea != 0 )
2705 isClockwise = !isClockwise;
2706
2707 if( isClockwise )
2708 {
2709 selectionMode = SELECTION_MODE::INSIDE_LASSO;
2710 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_WINDOW );
2711 }
2712 else
2713 {
2714 selectionMode = SELECTION_MODE::TOUCHING_LASSO;
2715 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
2716 }
2717
2718 if( evt->IsCancelInteractive() || evt->IsActivate() )
2719 {
2720 cancelled = true;
2721 break;
2722 }
2723 else if( evt->IsDrag( BUT_LEFT )
2724 || evt->IsClick( BUT_LEFT )
2725 || evt->IsAction( &ACTIONS::cursorClick ) )
2726 {
2727 points.Append( evt->Position() );
2728 }
2729 else if( evt->IsDblClick( BUT_LEFT )
2730 || evt->IsAction( &ACTIONS::cursorDblClick )
2731 || evt->IsAction( &ACTIONS::finishInteractive ) )
2732 {
2733 area.GetPoly().GenerateBBoxCache();
2734 SelectMultiple( area, m_drag_subtractive, false );
2735 break;
2736 }
2737 else if( evt->IsAction( &ACTIONS::doDelete )
2738 || evt->IsAction( &ACTIONS::undo ) )
2739 {
2740 if( points.GetPointCount() > 0 )
2741 {
2743 points.Remove( points.GetPointCount() - 1 );
2744 }
2745 }
2746 else
2747 {
2748 passEvent( evt, allowedActions );
2749 }
2750
2751 if( points.PointCount() > 0 )
2752 {
2754 {
2755 if( m_selection.GetSize() > 0 )
2756 {
2757 ClearSelection( true );
2758 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2759 }
2760 }
2761 }
2762
2763 area.SetPoly( points );
2764 area.GetPoly().Append( m_toolMgr->GetMousePosition() );
2767 area.SetExclusiveOr( false );
2768 area.SetMode( selectionMode );
2769 getView()->Update( &area );
2770 }
2771
2772 getViewControls()->SetAutoPan( false );
2773 getView()->SetVisible( &area, false );
2774 getView()->Remove( &area );
2775 m_multiple = false;
2776
2777 if( !cancelled )
2778 m_selection.ClearReferencePoint();
2779
2780 return cancelled;
2781}
2782
2783
2785 bool aExclusiveOr )
2786{
2787 KIGFX::VIEW* view = getView();
2788
2789 SELECTION_MODE selectionMode = aArea.GetMode();
2790 bool containedMode = ( selectionMode == SELECTION_MODE::INSIDE_RECTANGLE
2791 || selectionMode == SELECTION_MODE::INSIDE_LASSO );
2792 bool boxMode = ( selectionMode == SELECTION_MODE::INSIDE_RECTANGLE
2793 || selectionMode == SELECTION_MODE::TOUCHING_RECTANGLE );
2794
2795 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> candidates;
2796 BOX2I selectionRect = aArea.ViewBBox();
2797 view->Query( selectionRect, candidates );
2798
2799 std::set<SCH_ITEM*> uniqueCandidates;
2800
2801 for( const auto& [viewItem, layer] : candidates )
2802 {
2803 if( viewItem->IsSCH_ITEM() )
2804 uniqueCandidates.insert( static_cast<SCH_ITEM*>( viewItem ) );
2805 }
2806
2807 for( KIGFX::VIEW_ITEM* item : uniqueCandidates )
2808 {
2809 if( SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item ) )
2810 {
2811 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
2812 {
2813 if( boxMode ? selectionRect.Intersects( pin->GetBoundingBox() )
2814 : KIGEOM::BoxHitTest( aArea.GetPoly(), pin->GetBoundingBox(), true ) )
2815 uniqueCandidates.insert( pin );
2816 }
2817 }
2818 else if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item ) )
2819 {
2820 for( SCH_PIN* pin : symbol->GetPins() )
2821 {
2822 if( boxMode ? selectionRect.Intersects( pin->GetBoundingBox() )
2823 : KIGEOM::BoxHitTest( aArea.GetPoly(), pin->GetBoundingBox(), true ) )
2824 uniqueCandidates.insert( pin );
2825 }
2826
2827 for( SCH_FIELD& field : symbol->GetFields() )
2828 {
2829 if( field.IsVisible()
2830 && ( boxMode ? selectionRect.Intersects( field.GetBoundingBox() )
2831 : KIGEOM::BoxHitTest( aArea.GetPoly(), field.GetBoundingBox(), true ) ) )
2832 {
2833 uniqueCandidates.insert( &field );
2834 }
2835 }
2836 }
2837 }
2838
2839 SCH_COLLECTOR collector;
2840 SCH_COLLECTOR pinsCollector;
2841 std::set<EDA_ITEM*> group_items;
2842
2843 for( EDA_ITEM* item : m_frame->GetScreen()->Items().OfType( SCH_GROUP_T ) )
2844 {
2845 SCH_GROUP* group = static_cast<SCH_GROUP*>( item );
2846
2847 if( m_enteredGroup == group )
2848 continue;
2849
2850 std::unordered_set<EDA_ITEM*>& newset = group->GetItems();
2851
2852 auto boxContained =
2853 [&]( const BOX2I& aBox )
2854 {
2855 return boxMode ? selectionRect.Contains( aBox )
2856 : KIGEOM::BoxHitTest( aArea.GetPoly(), aBox, true );
2857 };
2858
2859 if( containedMode && boxContained( group->GetBoundingBox() ) && newset.size() )
2860 {
2861 for( EDA_ITEM* group_item : newset )
2862 {
2863 if( !group_item->IsSCH_ITEM() )
2864 continue;
2865
2866 if( Selectable( static_cast<SCH_ITEM*>( group_item ) ) )
2867 collector.Append( group_item );
2868 }
2869 }
2870
2871 for( EDA_ITEM* group_item : newset )
2872 group_items.emplace( group_item );
2873 }
2874
2875 auto hitTest =
2876 [&]( SCH_ITEM* aItem )
2877 {
2878 return boxMode ? aItem->HitTest( selectionRect, containedMode )
2879 : aItem->HitTest( aArea.GetPoly(), containedMode );
2880 };
2881
2882 for( SCH_ITEM* item : uniqueCandidates )
2883 {
2884 if( Selectable( item ) && ( hitTest( item ) || item->Type() == SCH_LINE_T )
2885 && ( !containedMode || !group_items.count( item ) ) )
2886 {
2887 if( item->Type() == SCH_PIN_T && !m_isSymbolEditor )
2888 pinsCollector.Append( item );
2889 else
2890 collector.Append( item );
2891 }
2892 }
2893
2894 filterCollectedItems( collector, true );
2895 filterCollectorForHierarchy( collector, true );
2896
2897 if( collector.GetCount() == 0 )
2898 {
2899 collector = pinsCollector;
2900 filterCollectedItems( collector, true );
2901 filterCollectorForHierarchy( collector, true );
2902 }
2903
2904 std::sort( collector.begin(), collector.end(),
2905 []( EDA_ITEM* a, EDA_ITEM* b )
2906 {
2907 VECTOR2I aPos = a->GetPosition();
2908 VECTOR2I bPos = b->GetPosition();
2909
2910 if( aPos.y == bPos.y )
2911 return aPos.x < bPos.x;
2912
2913 return aPos.y < bPos.y;
2914 } );
2915
2916 bool anyAdded = false;
2917 bool anySubtracted = false;
2918
2919 auto selectItem =
2920 [&]( EDA_ITEM* aItem, EDA_ITEM_FLAGS flags )
2921 {
2922 if( aSubtractive || ( aExclusiveOr && aItem->IsSelected() ) )
2923 {
2924 if( aExclusiveOr )
2925 aItem->XorFlags( flags );
2926 else
2927 aItem->ClearFlags( flags );
2928
2929 if( !aItem->HasFlag( STARTPOINT ) && !aItem->HasFlag( ENDPOINT ) )
2930 {
2931 unselect( aItem );
2932 anySubtracted = true;
2933 }
2934
2935 if( flags && !anySubtracted )
2936 getView()->Update( aItem );
2937 }
2938 else
2939 {
2940 aItem->SetFlags( flags );
2941 select( aItem );
2942 anyAdded = true;
2943 }
2944 };
2945
2946 std::vector<EDA_ITEM*> flaggedItems;
2947
2948 auto shapeContains =
2949 [&]( const VECTOR2I& aPoint )
2950 {
2951 return boxMode ? selectionRect.Contains( aPoint )
2952 : aArea.GetPoly().PointInside( aPoint );
2953 };
2954
2955 for( EDA_ITEM* item : collector )
2956 {
2957 EDA_ITEM_FLAGS flags = 0;
2958
2959 item->SetFlags( SELECTION_CANDIDATE );
2960 flaggedItems.push_back( item );
2961
2962 if( m_frame->GetRenderSettings()->m_ShowPinsElectricalType )
2963 item->SetFlags( SHOW_ELEC_TYPE );
2964
2965 if( item->Type() == SCH_LINE_T )
2966 {
2967 SCH_LINE* line = static_cast<SCH_LINE*>( item );
2968 bool hits = false;
2969
2970 if( boxMode )
2971 hits = line->HitTest( selectionRect, false );
2972 else
2973 hits = line->HitTest( aArea.GetPoly(), false );
2974
2975 if( ( !containedMode && hits )
2976 || ( shapeContains( line->GetEndPoint() ) && shapeContains( line->GetStartPoint() ) ) )
2977 {
2978 flags |= STARTPOINT | ENDPOINT;
2979 }
2980 else if( containedMode )
2981 {
2982 if( shapeContains( line->GetStartPoint() ) && line->IsStartDangling() )
2983 flags |= STARTPOINT;
2984
2985 if( shapeContains( line->GetEndPoint() ) && line->IsEndDangling() )
2986 flags |= ENDPOINT;
2987 }
2988
2989 if( flags & ( STARTPOINT | ENDPOINT ) )
2990 selectItem( item, flags );
2991 }
2992 else
2993 selectItem( item, flags );
2994
2995 item->ClearFlags( SHOW_ELEC_TYPE );
2996 }
2997
2998 for( EDA_ITEM* item : pinsCollector )
2999 {
3000 if( m_frame->GetRenderSettings()->m_ShowPinsElectricalType )
3001 item->SetFlags( SHOW_ELEC_TYPE );
3002
3003 // If the pin lives inside a group that is already being selected, don't also select the pin.
3004 if( EDA_GROUP* group =
3006 {
3007 if( collector.HasItem( group->AsEdaItem() ) )
3008 {
3009 item->ClearFlags( SHOW_ELEC_TYPE );
3010 continue;
3011 }
3012 }
3013
3014 if( Selectable( item ) && itemPassesFilter( item, nullptr )
3015 && !item->GetParent()->HasFlag( SELECTION_CANDIDATE ) && hitTest( static_cast<SCH_ITEM*>( item ) ) )
3016 {
3017 selectItem( item, 0 );
3018 }
3019
3020 item->ClearFlags( SHOW_ELEC_TYPE );
3021 }
3022
3023 for( EDA_ITEM* item : flaggedItems )
3024 item->ClearFlags( SELECTION_CANDIDATE );
3025
3026 m_selection.SetIsHover( false );
3027
3028 if( anyAdded )
3029 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3030 else if( anySubtracted )
3031 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
3032}
3033
3034
3036 bool aMultiselect ) const
3037{
3038 std::unordered_set<EDA_ITEM*> toAdd;
3039
3040 // Set SELECTION_CANDIDATE on all parents which are included in the GENERAL_COLLECTOR. This
3041 // algorithm is O(3n), whereas checking for the parent inclusion could potentially be O(n^2).
3042 for( int j = 0; j < aCollector.GetCount(); j++ )
3043 {
3044 if( aCollector[j]->GetParent() )
3045 aCollector[j]->GetParent()->ClearFlags( SELECTION_CANDIDATE );
3046
3047 if( aCollector[j]->GetParentSymbol() )
3048 aCollector[j]->GetParentSymbol()->ClearFlags( SELECTION_CANDIDATE );
3049 }
3050
3051 if( aMultiselect )
3052 {
3053 for( int j = 0; j < aCollector.GetCount(); j++ )
3054 aCollector[j]->SetFlags( SELECTION_CANDIDATE );
3055 }
3056
3057 for( int j = 0; j < aCollector.GetCount(); )
3058 {
3059 SCH_ITEM* item = aCollector[j];
3060 SYMBOL* sym = item->GetParentSymbol();
3061 SCH_ITEM* start = item;
3062
3063 if( !m_isSymbolEditor && sym )
3064 start = sym;
3065
3066 // If a group is entered, disallow selections of objects outside the group.
3068 {
3069 aCollector.Remove( item );
3070 continue;
3071 }
3072
3073 // If any element is a member of a group, replace those elements with the top containing
3074 // group.
3076 {
3077 if( top->AsEdaItem() != item )
3078 {
3079 toAdd.insert( top->AsEdaItem() );
3080 top->AsEdaItem()->SetFlags( SELECTION_CANDIDATE );
3081
3082 aCollector.Remove( item );
3083 continue;
3084 }
3085 }
3086
3087 // Symbols are a bit easier as they can't be nested.
3088 if( sym && ( sym->GetFlags() & SELECTION_CANDIDATE ) )
3089 {
3090 // Remove children of selected items
3091 aCollector.Remove( item );
3092 continue;
3093 }
3094
3095 ++j;
3096 }
3097
3098 for( EDA_ITEM* item : toAdd )
3099 {
3100 if( !aCollector.HasItem( item ) )
3101 aCollector.Append( item );
3102 }
3103}
3104
3105
3107{
3108 if( m_frame && m_frame->GetOverrideLocks() )
3109 return;
3110
3111 std::vector<EDA_ITEM*> toRemove;
3112
3113 for( EDA_ITEM* item : m_selection )
3114 {
3115 if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( item ) )
3116 {
3117 bool lockedDescendant = false;
3118
3119 schItem->RunOnChildren(
3120 [&]( SCH_ITEM* child )
3121 {
3122 if( child->IsLocked() )
3123 lockedDescendant = true;
3124 },
3126
3127 if( schItem->IsLocked() || lockedDescendant )
3128 toRemove.push_back( item );
3129 }
3130 }
3131
3132 for( EDA_ITEM* item : toRemove )
3133 RemoveItemFromSel( item, true /* quiet mode */ );
3134}
3135
3136
3138{
3139 getView()->Update( &m_selection );
3141
3142 return 0;
3143}
3144
3145
3147{
3148 for( SCH_TABLECELL* cell : aTable->GetCells() )
3149 {
3150 if( cell->IsSelected() )
3151 cell->SetFlags( SELECTION_CANDIDATE );
3152 else
3153 cell->ClearFlags( SELECTION_CANDIDATE );
3154 }
3155}
3156
3158{
3159 BOX2I selectionRect( start, end );
3160 selectionRect.Normalize();
3161
3162 auto wasSelected = []( EDA_ITEM* aItem )
3163 {
3164 return ( aItem->GetFlags() & SELECTION_CANDIDATE ) > 0;
3165 };
3166
3167 for( SCH_TABLECELL* cell : aTable->GetCells() )
3168 {
3169 bool doSelect = false;
3170
3171 if( cell->HitTest( selectionRect, false ) )
3172 {
3173 if( m_subtractive )
3174 doSelect = false;
3175 else if( m_exclusive_or )
3176 doSelect = !wasSelected( cell );
3177 else
3178 doSelect = true;
3179 }
3180 else if( wasSelected( cell ) )
3181 {
3182 doSelect = m_additive || m_subtractive || m_exclusive_or;
3183 }
3184
3185 if( doSelect && !cell->IsSelected() )
3186 select( cell );
3187 else if( !doSelect && cell->IsSelected() )
3188 unselect( cell );
3189 }
3190}
3191
3193{
3194 bool cancelled = false;
3195 m_multiple = true;
3196
3197 InitializeSelectionState( aTable );
3198
3199 while( TOOL_EVENT* evt = Wait() )
3200 {
3201 if( evt->IsCancelInteractive() || evt->IsActivate() )
3202 {
3203 cancelled = true;
3204 break;
3205 }
3206 else if( evt->IsDrag( BUT_LEFT ) )
3207 {
3208 getViewControls()->SetAutoPan( true );
3209 SelectCellsBetween( evt->DragOrigin(), evt->Position() - evt->DragOrigin(), aTable );
3210 }
3211 else if( evt->IsMouseUp( BUT_LEFT ) )
3212 {
3213 m_selection.SetIsHover( false );
3214
3215 bool anyAdded = false;
3216 bool anySubtracted = false;
3217
3218 for( SCH_TABLECELL* cell : aTable->GetCells() )
3219 {
3220 if( cell->IsSelected() && ( cell->GetFlags() & SELECTION_CANDIDATE ) <= 0 )
3221 anyAdded = true;
3222 else if( ( cell->GetFlags() & SELECTION_CANDIDATE ) > 0 && !cell->IsSelected() )
3223 anySubtracted = true;
3224 }
3225
3226 if( anyAdded )
3227 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3228 if( anySubtracted )
3229 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
3230
3231 break;
3232 }
3233 else
3234 {
3235 for( int i = 0; allowedActions[i]; ++i )
3236 {
3237 if( evt->IsAction( allowedActions[i] ) )
3238 {
3239 evt->SetPassEvent();
3240 break;
3241 }
3242 }
3243 }
3244 }
3245
3246 getViewControls()->SetAutoPan( false );
3247
3248 m_multiple = false;
3249
3250 if( !cancelled )
3251 m_selection.ClearReferencePoint();
3252
3253 return cancelled;
3254}
3255
3256
3258{
3259 SCH_COLLECTOR collector;
3260
3261 //TODO(snh): Reimplement after exposing KNN interface
3262 int pixelThreshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
3263 int gridThreshold = KiROUND( getView()->GetGAL()->GetGridSize().EuclideanNorm() );
3264 int thresholdMax = std::max( pixelThreshold, gridThreshold );
3265
3266 for( int threshold : { 0, thresholdMax/4, thresholdMax/2, thresholdMax } )
3267 {
3268 collector.m_Threshold = threshold;
3269 collector.Collect( m_frame->GetScreen(), connectedTypes, aPosition );
3270
3271 if( collector.GetCount() > 0 )
3272 break;
3273 }
3274
3275 return collector.GetCount() ? collector[ 0 ] : nullptr;
3276}
3277
3278
3280{
3281 VECTOR2I cursorPos = getViewControls()->GetCursorPosition( false );
3282
3283 SelectPoint( cursorPos, connectedTypes );
3284 return 0;
3285}
3286
3287
3288std::set<SCH_ITEM*>
3290 STOP_CONDITION aStopCondition )
3291{
3292 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
3293
3294 if( m_isSymbolEditor || m_isSymbolViewer || !editFrame )
3295 return {};
3296
3297 CONNECTION_GRAPH* graph = editFrame->Schematic().ConnectionGraph();
3298
3299 if( !graph )
3300 return {};
3301
3302 SCH_SCREEN* screen = m_frame->GetScreen();
3303 SCH_SHEET_PATH& currentSheet = editFrame->GetCurrentSheet();
3304 std::vector<SCH_ITEM*> startItems;
3305 std::set<SCH_ITEM*> added;
3306
3307 for( auto item : aItems )
3308 {
3309 if( !item->IsSCH_ITEM() )
3310 continue;
3311
3312 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
3313
3314 if( schItem->Type() == SCH_SYMBOL_T )
3315 {
3316 for( SCH_PIN* pin : static_cast<SCH_SYMBOL*>( schItem )->GetPins( &currentSheet ) )
3317 {
3318 if( pin )
3319 startItems.push_back( pin );
3320 }
3321 }
3322 else if( schItem->IsConnectable() )
3323 {
3324 startItems.push_back( schItem );
3325 }
3326 }
3327
3328 if( startItems.empty() )
3329 return {};
3330
3331 // Pre-compute which start items belong to symbols already in the original selection so that
3332 // pin-stop traversal can step away from those symbols without immediately bouncing back.
3333 std::unordered_set<SCH_SYMBOL*> startSymbols;
3334
3335 for( SCH_ITEM* item : startItems )
3336 {
3337 if( SCH_PIN* pin = dynamic_cast<SCH_PIN*>( item ) )
3338 {
3339 if( SCH_SYMBOL* parent = dynamic_cast<SCH_SYMBOL*>( pin->GetParent() ) )
3340 startSymbols.insert( parent );
3341 }
3342 }
3343
3344 // Cache every pin position on the sheet so endpoint tests are O(log n) lookups instead of
3345 // an R-tree query plus a full pin iteration per call.
3346 std::set<VECTOR2I> pinPositions;
3347
3348 for( SCH_ITEM* it : screen->Items().OfType( SCH_SYMBOL_T ) )
3349 {
3350 for( SCH_PIN* pin : static_cast<SCH_SYMBOL*>( it )->GetPins( &currentSheet ) )
3351 {
3352 if( pin )
3353 pinPositions.insert( pin->GetPosition() );
3354 }
3355 }
3356
3357 auto isStopPoint = [&]( const VECTOR2I& aPoint ) -> bool
3358 {
3359 if( aStopCondition == STOP_CONDITION::STOP_NEVER )
3360 return false;
3361
3362 if( pinPositions.count( aPoint ) )
3363 return true;
3364
3365 if( aStopCondition == STOP_CONDITION::STOP_AT_PIN )
3366 return false;
3367
3368 if( screen->IsJunction( aPoint ) || screen->IsExplicitJunction( aPoint ) )
3369 return true;
3370
3371 for( SCH_ITEM* it : screen->Items().Overlapping( aPoint ) )
3372 {
3373 switch( it->Type() )
3374 {
3375 case SCH_LABEL_T:
3376 case SCH_GLOBAL_LABEL_T:
3377 case SCH_HIER_LABEL_T:
3379 case SCH_SHEET_PIN_T:
3380 case SCH_NO_CONNECT_T:
3381 if( it->IsConnected( aPoint ) )
3382 return true;
3383
3384 break;
3385
3386 default:
3387 break;
3388 }
3389 }
3390
3391 return false;
3392 };
3393
3394 // STOP_AT_JUNCTION refuses to pull a symbol into the selection unless the user already had
3395 // a symbol selected; later passes accept every reachable symbol.
3396 auto shouldPullInSymbol = [&]()
3397 {
3398 return aStopCondition != STOP_CONDITION::STOP_AT_JUNCTION || !startSymbols.empty();
3399 };
3400
3401 std::deque<SCH_ITEM*> queue;
3402 std::unordered_set<SCH_ITEM*> visited;
3403
3404 auto enqueue = [&]( SCH_ITEM* aItem )
3405 {
3406 if( !aItem )
3407 return;
3408
3409 if( visited.insert( aItem ).second )
3410 queue.push_back( aItem );
3411 };
3412
3413 for( SCH_ITEM* item : startItems )
3414 enqueue( item );
3415
3416 while( !queue.empty() )
3417 {
3418 SCH_ITEM* item = queue.front();
3419 queue.pop_front();
3420
3421 if( SCH_PIN* pin = dynamic_cast<SCH_PIN*>( item ) )
3422 {
3423 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParent() );
3424
3425 if( shouldPullInSymbol() && symbol && Selectable( symbol )
3426 && itemPassesFilter( symbol, nullptr ) && !symbol->IsSelected() )
3427 {
3428 added.insert( symbol );
3429 }
3430 }
3431
3432 SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
3433 std::vector<VECTOR2I> openPoints;
3434
3435 if( line && aStopCondition != STOP_CONDITION::STOP_NEVER )
3436 {
3437 for( const VECTOR2I& pt : { line->GetStartPoint(), line->GetEndPoint() } )
3438 {
3439 if( !isStopPoint( pt ) )
3440 openPoints.push_back( pt );
3441 }
3442 }
3443
3444 for( SCH_ITEM* neighbor : item->ConnectedItems( currentSheet ) )
3445 {
3446 if( !neighbor )
3447 continue;
3448
3449 if( neighbor->Type() == SCH_SYMBOL_T )
3450 {
3451 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( neighbor );
3452
3453 if( shouldPullInSymbol() && Selectable( symbol ) && itemPassesFilter( symbol, nullptr )
3454 && !symbol->IsSelected() )
3455 {
3456 added.insert( symbol );
3457 }
3458
3459 continue;
3460 }
3461
3462 // Wires gate traversal on open endpoints; items without distinct endpoints always flow.
3463 if( line && aStopCondition != STOP_CONDITION::STOP_NEVER )
3464 {
3465 bool sharesOpenPoint = false;
3466
3467 for( const VECTOR2I& pt : openPoints )
3468 {
3469 if( neighbor->IsConnected( pt ) )
3470 {
3471 sharesOpenPoint = true;
3472 break;
3473 }
3474 }
3475
3476 if( !sharesOpenPoint )
3477 continue;
3478 }
3479
3480 enqueue( neighbor );
3481 }
3482
3483 if( !Selectable( item ) || !itemPassesFilter( item, nullptr ) )
3484 continue;
3485
3486 added.insert( item );
3487 }
3488
3489 return added;
3490}
3491
3492
3494{
3495 std::set<SCH_ITEM*> added;
3496
3497 for( auto item : aItems )
3498 {
3499 if( !item->IsSCH_ITEM() )
3500 continue;
3501
3502 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
3503
3504 std::set<SCH_ITEM*> conns = m_frame->GetScreen()->MarkConnections( schItem, schItem->IsConnectable() );
3505
3506 // Make sure we don't add things the user has disabled in the selection filter
3507 for( SCH_ITEM* connItem : conns )
3508 {
3509 if( !Selectable( connItem ) || !itemPassesFilter( connItem, nullptr ) )
3510 continue;
3511
3512 added.insert( connItem );
3513 }
3514 }
3515
3516 return added;
3517}
3518
3519
3521{
3523
3524 if( m_selection.Empty() )
3525 return 0;
3526
3527 SCH_SELECTION connectableSelection;
3528 SCH_SELECTION graphicalSelection;
3529
3530 // We need to filter the selection into connectable items (wires, pins, symbols)
3531 // and non-connectable items (shapes, unconnectable lines) for processing
3532 // with the graph or by the graphical are-endpoints-touching method.
3533 for( EDA_ITEM* selItem : originalSelection.GetItems() )
3534 {
3535 if( !selItem->IsSCH_ITEM() )
3536 continue;
3537
3538 SCH_ITEM* item = static_cast<SCH_ITEM*>( selItem );
3539
3540 if( item->Type() == SCH_LINE_T && !item->IsConnectable() )
3541 graphicalSelection.Add( item );
3542 else if( item->Type() == SCH_SHAPE_T )
3543 graphicalSelection.Add( item );
3544 else
3545 connectableSelection.Add( item );
3546 }
3547
3548 // Repeated Ctrl+4 must advance to the next stop condition if the current stage did not pull
3549 // in any items beyond what was already selected, matching PCBNew's "Select/Expand Connection".
3550 std::unordered_set<const SCH_ITEM*> originalConnectableSet;
3551
3552 for( EDA_ITEM* selItem : connectableSelection.GetItems() )
3553 originalConnectableSet.insert( static_cast<const SCH_ITEM*>( selItem ) );
3554
3555 ClearSelection( true );
3556
3557 std::set<SCH_ITEM*> graphAdded;
3558 std::set<SCH_ITEM*> graphicalAdded;
3559
3560 if( !connectableSelection.Empty() )
3561 {
3565 {
3566 graphAdded = expandConnectionWithGraph( connectableSelection, stop );
3567
3568 bool grew = std::any_of( graphAdded.begin(), graphAdded.end(),
3569 [&]( const SCH_ITEM* c )
3570 {
3571 return !originalConnectableSet.count( c );
3572 } );
3573
3574 if( grew )
3575 break;
3576 }
3577 }
3578
3579 if( !graphicalSelection.Empty() )
3580 graphicalAdded = expandConnectionGraphically( graphicalSelection );
3581
3582 // For whatever reason, the connection graph isn't working (e.g. in symbol editor )
3583 // so fall back to graphical expansion for those items if nothing was added.
3584 if( graphAdded.empty() && !connectableSelection.Empty() )
3585 {
3586 SCH_SELECTION combinedSelection = connectableSelection;
3587
3588 for( EDA_ITEM* selItem : graphicalSelection.GetItems() )
3589 combinedSelection.Add( selItem );
3590
3591 graphicalSelection = combinedSelection;
3592 }
3593
3594 graphicalAdded = expandConnectionGraphically( graphicalSelection );
3595
3596 auto smartAddToSel = [&]( EDA_ITEM* aItem )
3597 {
3598 AddItemToSel( aItem, true );
3599
3600 if( aItem->Type() == SCH_LINE_T )
3601 aItem->SetFlags( STARTPOINT | ENDPOINT );
3602 };
3603
3604 // Add everything to the selection, including the original selection
3605 for( auto item : graphAdded )
3606 smartAddToSel( item );
3607
3608 for( auto item : graphicalAdded )
3609 smartAddToSel( item );
3610
3611 for( auto item : originalSelection )
3612 smartAddToSel( item );
3613
3614 m_selection.SetIsHover( originalSelection.IsHover() );
3615
3616 if( originalSelection.HasReferencePoint() )
3617 m_selection.SetReferencePoint( originalSelection.GetReferencePoint() );
3618 else
3619 m_selection.ClearReferencePoint();
3620
3621 getView()->Update( &m_selection );
3622
3623 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3624
3625 return 0;
3626}
3627
3628
3630{
3631 std::set<std::pair<SCH_TABLE*, int>> columns;
3632 bool added = false;
3633
3634 for( EDA_ITEM* item : m_selection )
3635 {
3636 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( item ) )
3637 {
3638 SCH_TABLE* table = static_cast<SCH_TABLE*>( cell->GetParent() );
3639 columns.insert( std::make_pair( table, cell->GetColumn() ) );
3640 }
3641 }
3642
3643 for( auto& [ table, col ] : columns )
3644 {
3645 for( int row = 0; row < table->GetRowCount(); ++row )
3646 {
3647 SCH_TABLECELL* cell = table->GetCell( row, col );
3648
3649 if( !cell->IsSelected() )
3650 {
3651 select( table->GetCell( row, col ) );
3652 added = true;
3653 }
3654 }
3655 }
3656
3657 if( added )
3658 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3659
3660 return 0;
3661}
3662
3663
3665{
3666 std::set<std::pair<SCH_TABLE*, int>> rows;
3667 bool added = false;
3668
3669 for( EDA_ITEM* item : m_selection )
3670 {
3671 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( item ) )
3672 {
3673 SCH_TABLE* table = static_cast<SCH_TABLE*>( cell->GetParent() );
3674 rows.insert( std::make_pair( table, cell->GetRow() ) );
3675 }
3676 }
3677
3678 for( auto& [ table, row ] : rows )
3679 {
3680 for( int col = 0; col < table->GetColCount(); ++col )
3681 {
3682 SCH_TABLECELL* cell = table->GetCell( row, col );
3683
3684 if( !cell->IsSelected() )
3685 {
3686 select( table->GetCell( row, col ) );
3687 added = true;
3688 }
3689 }
3690 }
3691
3692 if( added )
3693 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3694
3695 return 0;
3696}
3697
3698
3700{
3701 std::set<SCH_TABLE*> tables;
3702 bool added = false;
3703
3704 for( EDA_ITEM* item : m_selection )
3705 {
3706 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( item ) )
3707 tables.insert( static_cast<SCH_TABLE*>( cell->GetParent() ) );
3708 }
3709
3711
3712 for( SCH_TABLE* table : tables )
3713 {
3714 if( !table->IsSelected() )
3715 {
3716 select( table );
3717 added = true;
3718 }
3719 }
3720
3721 if( added )
3722 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3723
3724 return 0;
3725}
3726
3727
3729{
3731 return 0;
3732}
3733
3734
3736{
3737 if( aBBox.GetWidth() == 0 )
3738 return;
3739
3740 BOX2I bbox = aBBox;
3741 bbox.Normalize();
3742
3743 VECTOR2I bbSize = bbox.Inflate( KiROUND( bbox.GetWidth() * 0.2f ) ).GetSize();
3744 VECTOR2D screenSize = getView()->GetViewport().GetSize();
3745
3746 // This code tries to come up with a zoom factor that doesn't simply zoom in to the cross
3747 // probed symbol, but instead shows a reasonable amount of the circuit around it to provide
3748 // context. This reduces the need to manually change the zoom because it's too close.
3749
3750 // Using the default text height as a constant to compare against, use the height of the
3751 // bounding box of visible items for a footprint to figure out if this is a big symbol (like
3752 // a processor) or a small symbol (like a resistor). This ratio is not useful by itself as a
3753 // scaling factor. It must be "bent" to provide good scaling at varying symbol sizes. Bigger
3754 // symbols need less scaling than small ones.
3755 double currTextHeight = schIUScale.MilsToIU( DEFAULT_TEXT_SIZE );
3756
3757 double compRatio = bbSize.y / currTextHeight; // Ratio of symbol to text height
3758 double compRatioBent = 1.0;
3759
3760 // LUT to scale zoom ratio to provide reasonable schematic context. Must work with symbols
3761 // of varying sizes (e.g. 0402 package and 200 pin BGA).
3762 // Each entry represents a compRatio (symbol height / default text height) and an amount to
3763 // scale by.
3764 std::vector<std::pair<double, double>> lut{ { 1.25, 16 },
3765 { 2.5, 12 },
3766 { 5, 8 },
3767 { 6, 6 },
3768 { 10, 4 },
3769 { 20, 2 },
3770 { 40, 1.5 },
3771 { 100, 1 } };
3772
3773 std::vector<std::pair<double, double>>::iterator it;
3774
3775 // Large symbol default is last LUT entry (1:1).
3776 compRatioBent = lut.back().second;
3777
3778 // Use LUT to do linear interpolation of "compRatio" within "first", then use that result to
3779 // linearly interpolate "second" which gives the scaling factor needed.
3780 if( compRatio >= lut.front().first )
3781 {
3782 for( it = lut.begin(); it < lut.end() - 1; ++it )
3783 {
3784 if( it->first <= compRatio && next( it )->first >= compRatio )
3785 {
3786 double diffx = compRatio - it->first;
3787 double diffn = next( it )->first - it->first;
3788
3789 compRatioBent = it->second + ( next( it )->second - it->second ) * diffx / diffn;
3790 break; // We have our interpolated value
3791 }
3792 }
3793 }
3794 else
3795 {
3796 compRatioBent = lut.front().second; // Small symbol default is first entry
3797 }
3798
3799 // This is similar to the original KiCad code that scaled the zoom to make sure symbols were
3800 // visible on screen. It's simply a ratio of screen size to symbol size, and its job is to
3801 // zoom in to make the component fullscreen. Earlier in the code the symbol BBox is given a
3802 // 20% margin to add some breathing room. We compare the height of this enlarged symbol bbox
3803 // to the default text height. If a symbol will end up with the sides clipped, we adjust
3804 // later to make sure it fits on screen.
3805 screenSize.x = std::max( 10.0, screenSize.x );
3806 screenSize.y = std::max( 10.0, screenSize.y );
3807 double ratio = std::max( -1.0, fabs( bbSize.y / screenSize.y ) );
3808
3809 // Original KiCad code for how much to scale the zoom
3810 double kicadRatio = std::max( fabs( bbSize.x / screenSize.x ),
3811 fabs( bbSize.y / screenSize.y ) );
3812
3813 // If the width of the part we're probing is bigger than what the screen width will be after
3814 // the zoom, then punt and use the KiCad zoom algorithm since it guarantees the part's width
3815 // will be encompassed within the screen.
3816 if( bbSize.x > screenSize.x * ratio * compRatioBent )
3817 {
3818 // Use standard KiCad zoom for parts too wide to fit on screen/
3819 ratio = kicadRatio;
3820 compRatioBent = 1.0; // Reset so we don't modify the "KiCad" ratio
3821 wxLogTrace( "CROSS_PROBE_SCALE",
3822 "Part TOO WIDE for screen. Using normal KiCad zoom ratio: %1.5f", ratio );
3823 }
3824
3825 // Now that "compRatioBent" holds our final scaling factor we apply it to the original
3826 // fullscreen zoom ratio to arrive at the final ratio itself.
3827 ratio *= compRatioBent;
3828
3829 bool alwaysZoom = false; // DEBUG - allows us to minimize zooming or not
3830
3831 // Try not to zoom on every cross-probe; it gets very noisy
3832 if( ( ratio < 0.5 || ratio > 1.0 ) || alwaysZoom )
3833 getView()->SetScale( getView()->GetScale() / ratio );
3834}
3835
3836
3837void SCH_SELECTION_TOOL::SyncSelection( const std::optional<SCH_SHEET_PATH>& targetSheetPath,
3838 SCH_ITEM* focusItem, const std::vector<SCH_ITEM*>& items )
3839{
3840 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
3841
3842 if( !editFrame )
3843 return;
3844
3845 double targetZoom = 0.0;
3846 VECTOR2D targetCenter;
3847 bool targetZoomValid = false;
3848 bool changedSheet = false;
3849
3850 if( targetSheetPath )
3851 {
3852 SCH_SHEET_PATH path = targetSheetPath.value();
3853
3854 if( SCH_SCREEN* screen = path.LastScreen() )
3855 {
3856 targetZoom = screen->m_LastZoomLevel;
3857 targetCenter = screen->m_ScrollCenter;
3858 targetZoomValid = screen->IsZoomInitialized();
3859 }
3860
3861 if( path != editFrame->Schematic().CurrentSheet() )
3862 {
3863 m_frame->GetToolManager()->RunAction<SCH_SHEET_PATH*>( SCH_ACTIONS::changeSheet, &path );
3864 changedSheet = true;
3865 }
3866 }
3867
3868 if( changedSheet && targetZoomValid && !m_frame->eeconfig()->m_CrossProbing.zoom_to_fit )
3869 {
3870 getView()->SetScale( targetZoom );
3871 getView()->SetCenter( targetCenter );
3872 }
3873
3874 ClearSelection( items.size() > 0 ? true /*quiet mode*/ : false );
3875
3876 // Perform individual selection of each item before processing the event.
3877 for( SCH_ITEM* item : items )
3878 {
3879 SCH_ITEM* parent = dynamic_cast<SCH_ITEM*>( item->GetParent() );
3880
3881 // Make sure we only select items on the current screen
3882 if( m_frame->GetScreen()->CheckIfOnDrawList( item )
3883 || ( parent && m_frame->GetScreen()->CheckIfOnDrawList( parent ) ) )
3884 {
3885 select( item );
3886 }
3887 }
3888
3889 BOX2I bbox = m_selection.GetBoundingBox();
3890
3891 if( bbox.GetWidth() != 0 && bbox.GetHeight() != 0 )
3892 {
3893 if( m_frame->eeconfig()->m_CrossProbing.center_on_items )
3894 {
3895 if( m_frame->eeconfig()->m_CrossProbing.zoom_to_fit )
3896 ZoomFitCrossProbeBBox( bbox );
3897
3898 editFrame->FocusOnItem( focusItem );
3899
3900 if( !focusItem )
3901 editFrame->FocusOnLocation( bbox.Centre() );
3902 }
3903 }
3904
3905 if( m_selection.Size() > 0 )
3906 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3907}
3908
3909
3911{
3912 m_selection.Clear();
3913
3914 bool enteredGroupFound = false;
3915
3916 if( m_isSymbolEditor )
3917 {
3918 LIB_SYMBOL* start = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
3919
3920 for( SCH_ITEM& item : start->GetDrawItems() )
3921 {
3922 if( item.IsSelected() )
3923 select( &item );
3924
3925 if( item.Type() == SCH_GROUP_T )
3926 {
3927 if( &item == m_enteredGroup )
3928 {
3929 item.SetFlags( ENTERED );
3930 enteredGroupFound = true;
3931 }
3932 else
3933 {
3934 item.ClearFlags( ENTERED );
3935 }
3936 }
3937 }
3938 }
3939 else
3940 {
3941 for( SCH_ITEM* item : m_frame->GetScreen()->Items() )
3942 {
3943 // If the field and symbol are selected, only use the symbol
3944 if( item->IsSelected() )
3945 {
3946 select( item );
3947 }
3948 else
3949 {
3950 item->RunOnChildren(
3951 [&]( SCH_ITEM* aChild )
3952 {
3953 if( aChild->IsSelected() )
3954 select( aChild );
3955 },
3957 }
3958
3959 if( item->Type() == SCH_GROUP_T )
3960 {
3961 if( item == m_enteredGroup )
3962 {
3963 item->SetFlags( ENTERED );
3964 enteredGroupFound = true;
3965 }
3966 else
3967 {
3968 item->ClearFlags( ENTERED );
3969 }
3970 }
3971 }
3972 }
3973
3975
3976 if( !enteredGroupFound )
3977 {
3978 m_enteredGroupOverlay.Clear();
3979 m_enteredGroup = nullptr;
3980 }
3981
3982 // Inform other potentially interested tools
3983 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3984}
3985
3986
3987bool SCH_SELECTION_TOOL::Selectable( const EDA_ITEM* aItem, const VECTOR2I* aPos,
3988 bool checkVisibilityOnly ) const
3989{
3990 // NOTE: in the future this is where Eeschema layer/itemtype visibility will be handled
3991
3992 SYMBOL_EDIT_FRAME* symEditFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
3993
3994 // Do not allow selection of anything except fields when the current symbol in the symbol
3995 // editor is a derived symbol.
3996 if( symEditFrame && symEditFrame->IsSymbolAlias() && aItem->Type() != SCH_FIELD_T )
3997 return false;
3998
3999 switch( aItem->Type() )
4000 {
4001 case SCH_PIN_T:
4002 {
4003 const SCH_PIN* pin = static_cast<const SCH_PIN*>( aItem );
4004
4005 if( symEditFrame )
4006 {
4007 if( pin->GetUnit() && pin->GetUnit() != symEditFrame->GetUnit() )
4008 return false;
4009
4010 if( pin->GetBodyStyle() && pin->GetBodyStyle() != symEditFrame->GetBodyStyle() )
4011 return false;
4012 }
4013
4014 if( !pin->IsVisible() && !m_frame->GetShowAllPins() )
4015 return false;
4016
4017 if( !m_filter.pins )
4018 {
4019 // Pin anchors have to be allowed for auto-starting wires.
4020 if( aPos )
4021 {
4023 GRID_HELPER_GRIDS pinGrid = grid.GetItemGrid( pin );
4024
4025 if( pin->IsPointClickableAnchor( grid.BestSnapAnchor( *aPos, pinGrid ) ) )
4026 return true;
4027 }
4028
4029 return false;
4030 }
4031
4032 break;
4033 }
4034
4036 if( !m_frame->eeconfig()->m_Appearance.show_directive_labels )
4037 return false;
4038
4039 break;
4040
4041 case SCH_RULE_AREA_T:
4042 // A rule area that exists solely to carry directive labels is hidden along with those
4043 // labels, so it must not remain selectable while invisible.
4044 if( !m_frame->eeconfig()->m_Appearance.show_directive_labels
4045 && static_cast<const SCH_RULE_AREA*>( aItem )->IsDirectiveLabelOnlyArea() )
4046 {
4047 return false;
4048 }
4049
4050 break;
4051
4052 case LIB_SYMBOL_T: // In symbol_editor we do not want to select the symbol itself.
4053 return false;
4054
4055 case SCH_FIELD_T: // SCH_FIELD objects are not unit/body-style-specific.
4056 {
4057 const SCH_FIELD* field = static_cast<const SCH_FIELD*>( aItem );
4058
4059 if( !field->IsVisible() && !( symEditFrame && symEditFrame->GetShowInvisibleFields() ) )
4060 return false;
4061
4062 break;
4063 }
4064
4065 case SCH_SHAPE_T:
4066 case SCH_TEXT_T:
4067 case SCH_TEXTBOX_T:
4068 if( symEditFrame )
4069 {
4070 const SCH_ITEM* sch_item = static_cast<const SCH_ITEM*>( aItem );
4071
4072 if( sch_item->GetUnit() && sch_item->GetUnit() != symEditFrame->GetUnit() )
4073 return false;
4074
4075 if( sch_item->GetBodyStyle() && sch_item->GetBodyStyle() != symEditFrame->GetBodyStyle() )
4076 return false;
4077 }
4078
4079 break;
4080
4081 case SCH_MARKER_T: // Always selectable
4082 return true;
4083
4084 case SCH_TABLECELL_T:
4085 {
4086 const SCH_TABLECELL* cell = static_cast<const SCH_TABLECELL*>( aItem );
4087
4088 if( cell->GetColSpan() == 0 || cell->GetRowSpan() == 0 )
4089 return false;
4090
4091 break;
4092 }
4093
4094 case NOT_USED: // Things like CONSTRUCTION_GEOM that aren't part of the model
4095 return false;
4096
4097 default: // Suppress warnings
4098 break;
4099 }
4100
4101 return true;
4102}
4103
4104
4106{
4107 if( m_selection.Empty() )
4108 return;
4109
4110 while( m_selection.GetSize() )
4112
4113 getView()->Update( &m_selection );
4114
4115 m_selection.SetIsHover( false );
4116 m_selection.ClearReferencePoint();
4117
4118 // Inform other potentially interested tools
4119 if( !aQuietMode )
4120 m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
4121}
4122
4123
4125{
4126 // Don't group when we select new items, the schematic editor selects all new items for moving.
4127 // The PCB editor doesn't need this logic because it doesn't select new items for moving.
4128 if( m_enteredGroup && !aItem->IsNew()
4129 && !SCH_GROUP::WithinScope( static_cast<SCH_ITEM*>( aItem ), m_enteredGroup, m_isSymbolEditor ) )
4130 {
4131 ExitGroup();
4132 }
4133
4134 highlight( aItem, SELECTED, &m_selection );
4135}
4136
4137
4139{
4140 unhighlight( aItem, SELECTED, &m_selection );
4141}
4142
4143
4144void SCH_SELECTION_TOOL::highlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
4145{
4146 if( aMode == SELECTED )
4147 aItem->SetSelected();
4148 else if( aMode == BRIGHTENED )
4149 aItem->SetBrightened();
4150
4151 if( aGroup )
4152 aGroup->Add( aItem );
4153
4154 // Highlight pins and fields. (All the other symbol children are currently only
4155 // represented in the LIB_SYMBOL and will inherit the settings of the parent symbol.)
4156 if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( aItem ) )
4157 {
4158 // We don't want to select group children if the group itself is selected,
4159 // we can only select them when the group is entered
4160 if( sch_item->Type() != SCH_GROUP_T )
4161 {
4162 sch_item->RunOnChildren(
4163 [&]( SCH_ITEM* aChild )
4164 {
4165 if( aMode == SELECTED )
4166 {
4167 aChild->SetSelected();
4168 getView()->Hide( aChild, true );
4169 }
4170 else if( aMode == BRIGHTENED )
4171 {
4172 aChild->SetBrightened();
4173 }
4174 },
4176 }
4177 }
4178
4179 if( aGroup && aMode != BRIGHTENED )
4180 getView()->Hide( aItem, true );
4181
4182 if( aItem->GetParent() && aItem->GetParent()->Type() != SCHEMATIC_T )
4183 getView()->Update( aItem->GetParent(), KIGFX::REPAINT );
4184
4185 getView()->Update( aItem, KIGFX::REPAINT );
4186}
4187
4188
4189void SCH_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
4190{
4191 if( aMode == SELECTED )
4192 {
4193 aItem->ClearSelected();
4194 // Lines need endpoints cleared here
4195 if( aItem->Type() == SCH_LINE_T )
4196 aItem->ClearFlags( STARTPOINT | ENDPOINT );
4197
4198 if( aMode != BRIGHTENED )
4199 getView()->Hide( aItem, false );
4200 }
4201 else if( aMode == BRIGHTENED )
4202 {
4203 aItem->ClearBrightened();
4204 }
4205
4206 if( aGroup )
4207 aGroup->Remove( aItem );
4208
4209 // Unhighlight pins and fields. (All the other symbol children are currently only
4210 // represented in the LIB_SYMBOL.)
4211 if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( aItem ) )
4212 {
4213 sch_item->RunOnChildren(
4214 [&]( SCH_ITEM* aChild )
4215 {
4216 if( aMode == SELECTED )
4217 {
4218 aChild->ClearSelected();
4219 getView()->Hide( aChild, false );
4220 }
4221 else if( aMode == BRIGHTENED )
4222 {
4223 aChild->ClearBrightened();
4224 }
4225
4226 if( aGroup )
4227 aGroup->Remove( aChild );
4228 },
4230 }
4231
4232 if( aItem->GetParent() && aItem->GetParent()->Type() != SCHEMATIC_T )
4233 getView()->Update( aItem->GetParent(), KIGFX::REPAINT );
4234
4235 getView()->Update( aItem, KIGFX::REPAINT );
4236}
4237
4238
4240{
4241 const unsigned GRIP_MARGIN = 20;
4242 int margin = KiROUND( getView()->ToWorld( GRIP_MARGIN ) );
4243
4244 // Check if the point is located within any of the currently selected items bounding boxes
4245 for( EDA_ITEM* item : m_selection )
4246 {
4247 BOX2I itemBox = item->ViewBBox();
4248 itemBox.Inflate( margin ); // Give some margin for gripping an item
4249
4250 if( itemBox.Contains( aPoint ) )
4251 return true;
4252 }
4253
4254 return false;
4255}
4256
4257
4259{
4260 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
4261
4262 if( !editFrame || !editFrame->GetNetNavigator() || m_selection.Size() == 0 )
4263 return 0;
4264
4265 if( !m_selection.Front()->IsBrightened() )
4266 return 0;
4267
4268 const SCH_ITEM* item = editFrame->SelectNextPrevNetNavigatorItem( true );
4269
4270 if( item )
4271 {
4273 select( const_cast<SCH_ITEM*>( item ) );
4274 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
4275 }
4276
4277 return 0;
4278}
4279
4280
4282{
4283 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
4284
4285 if( !editFrame || !editFrame->GetNetNavigator() || m_selection.Size() == 0 )
4286 return 0;
4287
4288 if( !m_selection.Front()->IsBrightened() )
4289 return 0;
4290
4291 const SCH_ITEM* item = editFrame->SelectNextPrevNetNavigatorItem( false );
4292
4293 if( item )
4294 {
4296 select( const_cast<SCH_ITEM*>( item ) );
4297 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
4298 }
4299
4300 return 0;
4301}
4302
4303
4305{
4307
4314
4316
4319
4325
4328
4331
4334
4336}
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
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:235
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:232
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.
VECTOR2I BestSnapAnchor(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:358
static const TOOL_EVENT ClearedEvent
Definition actions.h:343
static const TOOL_EVENT SelectedEvent
Definition actions.h:341
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:348
static const TOOL_EVENT PointSelectedEvent
Definition actions.h:340
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition actions.h:351
static const TOOL_EVENT UnselectedEvent
Definition actions.h:342
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:44
wxString AsString() const
Definition kiid.cpp:242
Define a library symbol object.
Definition lib_symbol.h:80
bool IsPower() const override
bool IsMultiBodyStyle() const override
Definition lib_symbol.h:821
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:759
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)
void filterCollectorForHierarchy(SCH_COLLECTOR &aCollector, bool aMultiselect) const
In general we don't want to select both a parent and any of it's children.
bool selectMultiple()
Handle drawing a selection box that allows one to select many items at the same time.
SELECTION_MODE m_selectionMode
bool Selectable(const EDA_ITEM *aItem, const VECTOR2I *aPos=nullptr, bool checkVisibilityOnly=false) const
Check conditions for an item to be selected.
@ 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)
int SelectTable(const TOOL_EVENT &aEvent)
Clear current selection event handler.
int disambiguateCursor(const TOOL_EVENT &aEvent)
Handle disambiguation actions including displaying the menu.
SCH_SELECTION_FILTER_OPTIONS m_filter
int SelectConnection(const TOOL_EVENT &aEvent)
If a connected item is selected then expand the selection to the entire connection,...
void ZoomFitCrossProbeBBox(const BOX2I &aBBox)
void OnIdle(wxIdleEvent &aEvent)
Zoom the screen to fit the bounding box for cross probing/selection sync.
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
int updateSelection(const TOOL_EVENT &aEvent)
Event handler to update the selection VIEW_ITEM.
void filterCollectedItems(SCH_COLLECTOR &aCollector, bool aMultiSelect)
Set up handlers for various events.
bool selectionContains(const VECTOR2I &aPoint) const
VECTOR2I GetCenter() const
Definition sch_shape.h: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)
Definition selection.cpp:38
const std::deque< EDA_ITEM * > GetItems() const
Definition selection.h:122
VECTOR2I GetReferencePoint() const
virtual void Remove(EDA_ITEM *aItem)
Definition selection.cpp:56
bool IsHover() const
Definition selection.h:85
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition selection.h:101
EDA_ITEM * Front() const
Definition selection.h:173
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:111
bool HasReferencePoint() const
Definition selection.h:212
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:40
@ GRID_WIRES
Definition grid_helper.h:45
KIID niluuid(0)
@ LAYER_WIRE
Definition layer_ids.h:450
@ LAYER_BUS
Definition layer_ids.h:451
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
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.
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
std::vector< std::vector< std::string > > table
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