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