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