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