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