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