KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_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) 2013-2017 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Tomasz Wlostowski <[email protected]>
7 * @author Maciej Suminski <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
27#include <cmath>
28#include <functional>
29#include <stack>
30using namespace std::placeholders;
31
32#include <advanced_config.h>
33#include <macros.h>
34#include <board.h>
36#include <footprint.h>
37#include <pad.h>
38#include <pcb_point.h>
39#include <pcb_table.h>
40#include <pcb_tablecell.h>
41#include <pcb_track.h>
42#include <pcb_marker.h>
43#include <pad.h>
44#include <pcb_generator.h>
45#include <pcb_base_edit_frame.h>
46#include <zone.h>
47#include <collectors.h>
49#include <view/view_controls.h>
50#include <gal/painter.h>
51#include <router/router_tool.h>
52#include <pcbnew_settings.h>
53#include <tool/tool_event.h>
54#include <tool/tool_manager.h>
58#include <tools/pcb_actions.h>
62#include <wx/event.h>
63#include <wx/timer.h>
64#include <wx/log.h>
65#include <wx/debug.h>
66#include <core/profile.h>
67#include <math/vector2wx.h>
68
75
76
78{
79public:
81 ACTION_MENU( true )
82 {
83 SetTitle( _( "Select" ) );
84
86
87 AppendSeparator();
88
92
93 // This could be enabled if we have better logic for picking the target net with the mouse
94 // Add( PCB_ACTIONS::deselectNet );
97
100 }
101
102private:
103 ACTION_MENU* create() const override
104 {
105 return new SELECT_MENU();
106 }
107};
108
109enum
110{
111 ID_REPLACE_TERMINAL_PAD_A = wxID_HIGHEST + 3000,
113};
114
116{
117public:
119 {
120 SetTitle( _( "Set terminal pad" ) );
121 }
122
123protected:
124 ACTION_MENU* create() const override { return new REPLACE_TERMINAL_PAD_MENU(); }
125
126 void update() override
127 {
128 Clear();
129
130 TOOL_MANAGER* toolMgr = getToolManager();
131 if( !toolMgr )
132 return;
133
134 PCB_SELECTION_TOOL* selTool = toolMgr->GetTool<PCB_SELECTION_TOOL>();
135 if( !selTool )
136 return;
137
138 const SELECTION& sel = selTool->GetSelection();
139 if( sel.Empty() )
140 return;
141
142 PAD* pad = dynamic_cast<PAD*>( sel.Front() );
143 PCB_EDIT_FRAME* frame = static_cast<PCB_EDIT_FRAME*>( toolMgr->GetToolHolder() );
144
145 if( !pad || !frame )
146 return;
147
148 NETINFO_ITEM* net = pad->GetNet();
149
150 if( !net || net->GetNetChain().IsEmpty() )
151 return;
152
153 PAD* oldA = net->GetTerminalPad( 0 );
154 PAD* oldB = net->GetTerminalPad( 1 );
155 KIID newId = pad->m_Uuid;
156
157 wxMenuItem* itemA = Append( ID_REPLACE_TERMINAL_PAD_A, _( "Terminal A" ) );
158 wxMenuItem* itemB = Append( ID_REPLACE_TERMINAL_PAD_B, _( "Terminal B" ) );
159
160 if( oldA && oldA->m_Uuid == newId )
161 itemA->Enable( false );
162
163 if( oldB && oldB->m_Uuid == newId )
164 itemB->Enable( false );
165
166 m_oldA = oldA ? oldA->m_Uuid : niluuid;
167 m_oldB = oldB ? oldB->m_Uuid : niluuid;
168 m_new = newId;
169 }
170
171 OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
172 {
173 if( aEvent.GetId() == ID_REPLACE_TERMINAL_PAD_A )
174 {
176 te.SetParameter( std::make_pair( m_oldA.AsString(), m_new.AsString() ) );
177 return te;
178 }
179 else if( aEvent.GetId() == ID_REPLACE_TERMINAL_PAD_B )
180 {
182 te.SetParameter( std::make_pair( m_oldB.AsString(), m_new.AsString() ) );
183 return te;
184 }
185
186 return OPT_TOOL_EVENT();
187 }
188
189private:
193};
194
196{
197public:
199 {
200 SetTitle( _( "Net Chains..." ) );
204 }
205
206protected:
207 ACTION_MENU* create() const override { return new NET_CHAINS_MENU(); }
208
209private:
211};
212
213
222
223
225 SELECTION_TOOL( "common.InteractiveSelection" ),
226 m_frame( nullptr ),
227 m_isFootprintEditor( false ),
229 m_enteredGroup( nullptr ),
231 m_lockedItemsFiltered( false ),
232 m_priv( std::make_unique<PRIV>() )
233{
234 m_filter.lockedItems = false;
235 m_filter.footprints = true;
236 m_filter.text = true;
237 m_filter.tracks = true;
238 m_filter.vias = true;
239 m_filter.pads = true;
240 m_filter.graphics = true;
241 m_filter.zones = true;
242 m_filter.keepouts = true;
243 m_filter.dimensions = true;
244 m_filter.points = true;
245 m_filter.otherItems = true;
246}
247
248
250{
253
254 Disconnect( wxEVT_TIMER, wxTimerEventHandler( PCB_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
255}
256
257
259{
261
262 if( frame && frame->IsType( FRAME_FOOTPRINT_VIEWER ) )
263 {
264 frame->AddStandardSubMenus( *m_menu.get() );
265 return true;
266 }
267
268 std::shared_ptr<SELECT_MENU> selectMenu = std::make_shared<SELECT_MENU>();
269 selectMenu->SetTool( this );
270 m_menu->RegisterSubMenu( selectMenu );
271
272 std::shared_ptr<NET_CHAINS_MENU> netChainsMenu = std::make_shared<NET_CHAINS_MENU>();
273 netChainsMenu->SetTool( this );
274 m_menu->RegisterSubMenu( netChainsMenu );
275
276 static const std::vector<KICAD_T> tableCellTypes = { PCB_TABLECELL_T };
277
278 auto& menu = m_menu->GetMenu();
279
280 auto activeToolCondition =
281 [this] ( const SELECTION& aSel )
282 {
284 return pcbFrame && !pcbFrame->ToolStackIsEmpty();
285 };
286
287 auto haveHighlight =
288 [this]( const SELECTION& sel )
289 {
290 KIGFX::RENDER_SETTINGS* cfg = m_toolMgr->GetView()->GetPainter()->GetSettings();
291
292 return !cfg->GetHighlightNetCodes().empty();
293 };
294
295 auto groupEnterCondition =
297
298 auto inGroupCondition =
299 [this] ( const SELECTION& )
300 {
301 return m_enteredGroup != nullptr;
302 };
303
304 auto tableCellSelection = SELECTION_CONDITIONS::MoreThan( 0 )
306
307 SELECTION_CONDITION netItemSelection = []( const SELECTION& aSel )
308 {
309 if( aSel.GetSize() != 1 )
310 return false;
311 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( *aSel.begin() );
312 return item->Type() == PCB_PAD_T || item->Type() == PCB_VIA_T || item->Type() == PCB_TRACE_T
313 || item->Type() == PCB_ARC_T;
314 };
315
316 if( frame && frame->IsType( FRAME_PCB_EDITOR ) )
317 {
318 menu.AddMenu( selectMenu.get(), SELECTION_CONDITIONS::NotEmpty );
319 menu.AddMenu( netChainsMenu.get(), netItemSelection );
320 menu.AddSeparator( 1000 );
321 }
322
323 // "Cancel" goes at the top of the context menu when a tool is active
324 menu.AddItem( ACTIONS::cancelInteractive, activeToolCondition, 1 );
325 menu.AddItem( ACTIONS::groupEnter, groupEnterCondition, 1 );
326 menu.AddItem( ACTIONS::groupLeave, inGroupCondition, 1 );
327 menu.AddItem( PCB_ACTIONS::applyDesignBlockLayout, groupEnterCondition, 1 );
328 menu.AddItem( PCB_ACTIONS::placeLinkedDesignBlock, groupEnterCondition, 1 );
329 menu.AddItem( PCB_ACTIONS::saveToLinkedDesignBlock, groupEnterCondition, 1 );
330 menu.AddItem( PCB_ACTIONS::clearHighlight, haveHighlight, 1 );
331 menu.AddSeparator( haveHighlight, 1 );
332
333 menu.AddItem( ACTIONS::selectColumns, tableCellSelection, 2 );
334 menu.AddItem( ACTIONS::selectRows, tableCellSelection, 2 );
335 menu.AddItem( ACTIONS::selectTable, tableCellSelection, 2 );
336
337 menu.AddSeparator( 1 );
338
339 if( frame )
340 frame->AddStandardSubMenus( *m_menu.get() );
341
342 m_disambiguateTimer.SetOwner( this );
343 Connect( m_disambiguateTimer.GetId(), wxEVT_TIMER,
344 wxTimerEventHandler( PCB_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
345
346 return true;
347}
348
349
351{
354
355 if( aReason != TOOL_BASE::REDRAW )
356 {
357 if( m_enteredGroup )
358 ExitGroup();
359
360 // Deselect any item being currently in edit, to avoid unexpected behavior and remove
361 // pointers to the selected items from containers.
362 ClearSelection( true );
363 }
364
365 if( aReason == TOOL_BASE::MODEL_RELOAD )
366 getView()->GetPainter()->GetSettings()->SetHighlight( false );
367
368 // Reinsert the VIEW_GROUP, in case it was removed from the VIEW
369 view()->Remove( &m_selection );
370 view()->Add( &m_selection );
371
374}
375
376
377void PCB_SELECTION_TOOL::OnIdle( wxIdleEvent& aEvent )
378{
379 if( m_frame->ToolStackIsEmpty() && !m_multiple )
380 {
381 wxMouseState keyboardState = wxGetMouseState();
382
383 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(), keyboardState.AltDown() );
384
385 if( m_additive )
386 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ADD );
387 else if( m_subtractive )
388 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SUBTRACT );
389 else if( m_exclusive_or )
390 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::XOR );
391 else
392 m_frame->GetCanvas()->SetCurrentCursor( m_nonModifiedCursor );
393 }
394}
395
396
398{
399 // Main loop: keep receiving events
400 while( TOOL_EVENT* evt = Wait() )
401 {
402 MOUSE_DRAG_ACTION dragAction = m_frame->GetDragAction();
404
405 if( PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings() )
406 trackDragAction = cfg->m_TrackDragAction;
407
408 // on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
409 setModifiersState( evt->Modifier( MD_SHIFT ), evt->Modifier( MD_CTRL ), evt->Modifier( MD_ALT ) );
410
412 bool brd_editor = frame && frame->IsType( FRAME_PCB_EDITOR );
413 ROUTER_TOOL* router = m_toolMgr->GetTool<ROUTER_TOOL>();
414
415 // If the router tool is active, don't override
416 if( router && router->IsToolActive() && router->RoutingInProgress() )
417 {
418 evt->SetPassEvent();
419 }
420 else if( evt->IsMouseDown( BUT_LEFT ) )
421 {
422 // Avoid triggering when running under other tools
423 PCB_POINT_EDITOR *pt_tool = m_toolMgr->GetTool<PCB_POINT_EDITOR>();
424
425 if( m_frame->ToolStackIsEmpty() && pt_tool && !pt_tool->HasPoint() )
426 {
427 m_originalCursor = m_toolMgr->GetMousePosition();
428 m_disambiguateTimer.StartOnce( ADVANCED_CFG::GetCfg().m_DisambiguationMenuDelay );
429 }
430 }
431 else if( evt->IsClick( BUT_LEFT ) )
432 {
433 // If there is no disambiguation, this routine is still running and will
434 // register a `click` event when released
435 if( m_disambiguateTimer.IsRunning() )
436 {
437 m_disambiguateTimer.Stop();
438
439 // Single click? Select single object
440 if( m_highlight_modifier && brd_editor )
441 {
443 }
444 else
445 {
446 m_frame->ClearFocus();
447 selectPoint( evt->Position() );
448 }
449 }
450
451 m_canceledMenu = false;
452 }
453 else if( evt->IsClick( BUT_RIGHT ) )
454 {
455 m_disambiguateTimer.Stop();
456
457 // Right click? if there is any object - show the context menu
458 bool selectionCancelled = false;
459
460 if( m_selection.Empty() )
461 {
462 selectPoint( evt->Position(), false, &selectionCancelled );
463 m_selection.SetIsHover( true );
464 }
465
466 // Show selection before opening menu
467 m_frame->GetCanvas()->ForceRefresh();
468
469 if( !selectionCancelled )
470 {
471 m_toolMgr->VetoContextMenuMouseWarp();
472 m_menu->ShowContextMenu( m_selection );
473 }
474 }
475 else if( evt->IsDblClick( BUT_LEFT ) )
476 {
477 m_disambiguateTimer.Stop();
478
479 // Double clicks make no sense in the footprint viewer
480 if( frame && frame->IsType( FRAME_FOOTPRINT_VIEWER ) )
481 {
482 evt->SetPassEvent();
483 continue;
484 }
485
486 // Double click? Display the properties window
487 m_frame->ClearFocus();
488
489 if( m_selection.Empty() )
490 selectPoint( evt->Position() );
491
492 if( m_selection.GetSize() == 1 && m_selection[0]->Type() == PCB_GROUP_T )
493 EnterGroup();
494 else
496 }
497 else if( evt->IsDblClick( BUT_MIDDLE ) )
498 {
499 // Middle double click? Do zoom to fit or zoom to objects
500 if( evt->Modifier( MD_CTRL ) ) // Is CTRL key down?
502 else
503 m_toolMgr->RunAction( ACTIONS::zoomFitScreen );
504 }
505 else if( evt->Action() == TA_MOUSE_WHEEL )
506 {
507 int field = -1;
508
509 if( evt->Modifier() == ( MD_SHIFT | MD_ALT ) )
510 field = 0;
511 else if( evt->Modifier() == ( MD_CTRL | MD_ALT ) )
512 field = 1;
513 // any more?
514
515 if( field >= 0 )
516 {
517 const int delta = evt->Parameter<int>();
518 ACTIONS::INCREMENT params { delta > 0 ? 1 : -1, field };
519
520 m_toolMgr->RunAction( ACTIONS::increment, params );
521 }
522 }
523 else if( evt->IsDrag( BUT_LEFT ) )
524 {
525 m_disambiguateTimer.Stop();
526
527 // Is another tool already moving a new object? Don't allow a drag start
528 if( !m_selection.Empty() && m_selection[0]->HasFlag( IS_NEW | IS_MOVING ) )
529 {
530 evt->SetPassEvent();
531 continue;
532 }
533
534 // Drag with LMB? Select multiple objects (or at least draw a selection box)
535 // or drag them
536 m_frame->ClearFocus();
538
539 GENERAL_COLLECTOR hoverCells;
540
541 if( m_isFootprintEditor && board()->GetFirstFootprint() )
542 {
543 hoverCells.Collect( board()->GetFirstFootprint(), { PCB_TABLECELL_T }, evt->DragOrigin(),
545 }
546 else
547 {
548 hoverCells.Collect( board(), { PCB_TABLECELL_T }, evt->DragOrigin(), getCollectorsGuide() );
549 }
550
551 if( hoverCells.GetCount() )
552 {
553 if( m_selection.Empty() || SELECTION_CONDITIONS::OnlyTypes( { PCB_TABLECELL_T } )( m_selection ) )
554 {
555 selectTableCells( static_cast<PCB_TABLE*>( hoverCells[0]->GetParent() ) );
556 }
557 else
558 {
559 m_toolMgr->RunAction( PCB_ACTIONS::move );
560 }
561 }
562 else if( ( hasModifier() || dragAction == MOUSE_DRAG_ACTION::SELECT )
563 || ( m_selection.Empty() && dragAction != MOUSE_DRAG_ACTION::DRAG_ANY ) )
564 {
567 {
568 SelectRectArea( aEvent );
569 }
572 {
573 SelectPolyArea( aEvent );
574 }
575 else
576 {
577 wxASSERT_MSG( false, wxT( "Unknown selection mode" ) );
578 SelectRectArea( aEvent );
579 }
580 }
581 else
582 {
583 // Don't allow starting a drag from a zone filled area that isn't already selected
584 auto zoneFilledAreaFilter =
585 []( const VECTOR2I& aWhere, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* aTool )
586 {
587 int accuracy = aCollector.GetGuide()->Accuracy();
588 std::set<EDA_ITEM*> remove;
589
590 for( EDA_ITEM* item : aCollector )
591 {
592 if( item->Type() == PCB_ZONE_T )
593 {
594 ZONE* zone = static_cast<ZONE*>( item );
595
596 if( !zone->HitTestForCorner( aWhere, accuracy * 2 )
597 && !zone->HitTestForEdge( aWhere, accuracy ) )
598 {
599 remove.insert( zone );
600 }
601 }
602 }
603
604 for( EDA_ITEM* item : remove )
605 aCollector.Remove( item );
606 };
607
608 // See if we can drag before falling back to SelectRectArea()
609 bool doDrag = false;
610
611 if( evt->HasPosition() )
612 {
613 if( m_selection.Empty()
614 && selectPoint( evt->DragOrigin(), false, nullptr, zoneFilledAreaFilter ) )
615 {
616 m_selection.SetIsHover( true );
617 doDrag = true;
618 }
619 // Check if dragging has started within any of selected items bounding box.
620 else if( evt->HasPosition() && selectionContains( evt->DragOrigin() ) )
621 {
622 doDrag = true;
623 }
624 }
625
626 if( doDrag )
627 {
628 size_t segs = m_selection.CountType( PCB_TRACE_T );
629 size_t arcs = m_selection.CountType( PCB_ARC_T );
630 size_t vias = m_selection.CountType( PCB_VIA_T );
631 // Note: multi-track dragging is currently supported, but not multi-via
632 bool routable = ( segs >= 1 || arcs >= 1 || vias == 1 )
633 && ( segs + arcs + vias == m_selection.GetSize() );
634
635 if( routable && trackDragAction == TRACK_DRAG_ACTION::DRAG )
637 else if( routable && trackDragAction == TRACK_DRAG_ACTION::DRAG_FREE_ANGLE )
639 else
640 m_toolMgr->RunAction( PCB_ACTIONS::move );
641 }
642 else
643 {
644 // Otherwise drag a selection box
647 {
648 SelectPolyArea( aEvent );
649 }
650 else
651 {
652 SelectRectArea( aEvent );
653 }
654 }
655 }
656 }
657 else if( evt->IsCancel() )
658 {
659 m_disambiguateTimer.Stop();
660 m_frame->ClearFocus();
661
662 if( !GetSelection().Empty() )
663 {
665 }
666 else if( evt->FirstResponder() == this && evt->GetCommandId() == (int) WXK_ESCAPE )
667 {
668 if( m_enteredGroup )
669 {
670 ExitGroup();
671 }
672 else
673 {
674 BOARD_INSPECTION_TOOL* controller = m_toolMgr->GetTool<BOARD_INSPECTION_TOOL>();
675
676 try
677 {
678 if( controller && m_frame->GetPcbNewSettings()->m_ESCClearsNetHighlight )
679 controller->ClearHighlight( *evt );
680 }
681 catch( const std::runtime_error& e )
682 {
683 wxCHECK_MSG( false, 0, e.what() );
684 }
685 }
686 }
687 }
688 else
689 {
690 evt->SetPassEvent();
691 }
692
693
694 if( m_frame->ToolStackIsEmpty() )
695 {
696 // move cursor prediction
697 if( !hasModifier()
698 && dragAction == MOUSE_DRAG_ACTION::DRAG_SELECTED
699 && !m_selection.Empty()
700 && evt->HasPosition()
701 && selectionContains( evt->Position() ) )
702 {
704 }
705 else
706 {
708 }
709 }
710 }
711
712 // Shutting down; clear the selection
713 m_selection.Clear();
714 m_disambiguateTimer.Stop();
715
716 return 0;
717}
718
719
721{
722 wxCHECK_RET( m_selection.GetSize() == 1 && m_selection[0]->Type() == PCB_GROUP_T,
723 wxT( "EnterGroup called when selection is not a single group" ) );
724 PCB_GROUP* aGroup = static_cast<PCB_GROUP*>( m_selection[0] );
725
726 if( m_enteredGroup != nullptr )
727 ExitGroup();
728
730 m_enteredGroup = aGroup;
731 m_enteredGroup->SetFlags( ENTERED );
732
733 for( EDA_ITEM* member : m_enteredGroup->GetItems() )
734 select( member );
735
736 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
737
738 view()->Hide( m_enteredGroup, true );
741}
742
743
744void PCB_SELECTION_TOOL::ExitGroup( bool aSelectGroup )
745{
746 // Only continue if there is a group entered
747 if( m_enteredGroup == nullptr )
748 return;
749
750 m_enteredGroup->ClearFlags( ENTERED );
751 view()->Hide( m_enteredGroup, false );
753
754 if( aSelectGroup )
755 {
757 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
758 }
759
760 m_enteredGroupOverlay.Clear();
761 m_enteredGroup = nullptr;
763}
764
765
770
771
773{
774 bool selectionEmpty = m_selection.Empty();
775 m_selection.SetIsHover( selectionEmpty );
776 m_lockedItemsFiltered = false;
777
778 if( selectionEmpty )
779 {
780 m_toolMgr->RunAction( ACTIONS::selectionCursor, aClientFilter );
781 m_selection.ClearReferencePoint();
782 }
783
784 if( aClientFilter )
785 {
786 enum DISPOSITION { BEFORE = 1, AFTER, BOTH };
787
788 std::map<EDA_ITEM*, DISPOSITION> itemDispositions;
790 GENERAL_COLLECTOR collector;
791
792 collector.SetGuide( &guide );
793
794 for( EDA_ITEM* item : m_selection )
795 {
796 collector.Append( item );
797 itemDispositions[ item ] = BEFORE;
798 }
799
800 aClientFilter( VECTOR2I(), collector, this );
801
802 for( EDA_ITEM* item : collector )
803 {
804 if( itemDispositions.count( item ) )
805 itemDispositions[ item ] = BOTH;
806 else
807 itemDispositions[ item ] = AFTER;
808 }
809
810 // Unhighlight the BEFORE items before highlighting the AFTER items.
811 // This is so that in the case of groups, if aClientFilter replaces a selection
812 // with the enclosing group, the unhighlight of the element doesn't undo the
813 // recursive highlighting of that element by the group.
814
815 for( std::pair<EDA_ITEM* const, DISPOSITION> itemDisposition : itemDispositions )
816 {
817 EDA_ITEM* item = itemDisposition.first;
818 DISPOSITION disposition = itemDisposition.second;
819
820 if( disposition == BEFORE )
822 }
823
824 for( std::pair<EDA_ITEM* const, DISPOSITION> itemDisposition : itemDispositions )
825 {
826 EDA_ITEM* item = itemDisposition.first;
827 DISPOSITION disposition = itemDisposition.second;
828
829 // Note that we must re-highlight even previously-highlighted items
830 // (ie: disposition BOTH) in case we removed any of their children.
831 if( disposition == AFTER || disposition == BOTH )
832 highlight( item, SELECTED, &m_selection );
833 }
834
835 m_frame->GetCanvas()->ForceRefresh();
836 }
837
838 return m_selection;
839}
840
841
843{
844 GENERAL_COLLECTORS_GUIDE guide( board()->GetVisibleLayers(), (PCB_LAYER_ID) view()->GetTopLayer(),
845 view() );
846
847 bool padsDisabled = !board()->IsElementVisible( LAYER_PADS );
848
849 // account for the globals
850 guide.SetIgnoreFPTextOnBack( !board()->IsElementVisible( LAYER_FP_TEXT ) );
851 guide.SetIgnoreFPTextOnFront( !board()->IsElementVisible( LAYER_FP_TEXT ) );
852 guide.SetIgnoreFootprintsOnBack( !board()->IsElementVisible( LAYER_FOOTPRINTS_BK ) );
853 guide.SetIgnoreFootprintsOnFront( !board()->IsElementVisible( LAYER_FOOTPRINTS_FR ) );
854 guide.SetIgnorePadsOnBack( padsDisabled );
855 guide.SetIgnorePadsOnFront( padsDisabled );
856 guide.SetIgnoreThroughHolePads( padsDisabled );
857 guide.SetIgnoreFPValues( !board()->IsElementVisible( LAYER_FP_VALUES ) );
858 guide.SetIgnoreFPReferences( !board()->IsElementVisible( LAYER_FP_REFERENCES ) );
859 guide.SetIgnoreThroughVias( ! board()->IsElementVisible( LAYER_VIAS ) );
860 guide.SetIgnoreBlindBuriedVias( ! board()->IsElementVisible( LAYER_VIAS ) );
861 guide.SetIgnoreMicroVias( ! board()->IsElementVisible( LAYER_VIAS ) );
862 guide.SetIgnoreTracks( ! board()->IsElementVisible( LAYER_TRACKS ) );
863
864 return guide;
865}
866
867
869{
870 return m_frame && m_frame->GetPcbNewSettings() && m_frame->GetPcbNewSettings()->m_CtrlClickHighlight && !m_isFootprintEditor;
871}
872
873
874bool PCB_SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag, bool* aSelectionCancelledFlag,
875 CLIENT_SELECTION_FILTER aClientFilter )
876{
878 GENERAL_COLLECTOR collector;
879 const PCB_DISPLAY_OPTIONS& displayOpts = m_frame->GetDisplayOptions();
880
882
883 if( m_enteredGroup && !m_enteredGroup->GetBoundingBox().Contains( aWhere ) )
884 ExitGroup();
885
888 aWhere, guide );
889
890 // Remove unselectable items
891 for( int i = collector.GetCount() - 1; i >= 0; --i )
892 {
893 if( !Selectable( collector[ i ] ) || ( aOnDrag && collector[i]->IsLocked() ) )
894 collector.Remove( i );
895 }
896
897 m_selection.ClearReferencePoint();
898
899 size_t preFilterCount = collector.GetCount();
901 rejected.SetAll( false );
902
903 // Apply the stateful filter (remove items disabled by the Selection Filter)
904 FilterCollectedItems( collector, false, &rejected );
905
906 if( collector.GetCount() == 0 && preFilterCount > 0 )
907 {
909 editFrame->HighlightSelectionFilter( rejected );
910
911 if( !m_additive && !m_subtractive && !m_exclusive_or && m_selection.GetSize() > 0 )
912 {
913 ClearSelection( true /*quiet mode*/ );
914 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
915 return false;
916 }
917
918 return false;
919 }
920
921 // Allow the client to do tool- or action-specific filtering to see if we can get down
922 // to a single item
923 if( aClientFilter )
924 aClientFilter( aWhere, collector, this );
925
926 FilterCollectorForHierarchy( collector, false );
927
928 FilterCollectorForFootprints( collector, aWhere );
929
930 // For subtracting, we only want items that are selected
931 if( m_subtractive )
932 {
933 for( int i = collector.GetCount() - 1; i >= 0; --i )
934 {
935 if( !collector[i]->IsSelected() )
936 collector.Remove( i );
937 }
938 }
939
940 // Apply some ugly heuristics to avoid disambiguation menus whenever possible
941 if( collector.GetCount() > 1 && !m_skip_heuristics )
942 {
943 try
944 {
945 GuessSelectionCandidates( collector, aWhere );
946 }
947 catch( const std::exception& exc )
948 {
949 wxLogWarning( wxS( "Exception '%s' occurred attempting to guess selection candidates." ),
950 exc.what() );
951 return false;
952 }
953 }
954
955 // If still more than one item we're going to have to ask the user.
956 if( collector.GetCount() > 1 )
957 {
958 if( aOnDrag )
960
961 if( !doSelectionMenu( &collector ) )
962 {
963 if( aSelectionCancelledFlag )
964 *aSelectionCancelledFlag = true;
965
966 return false;
967 }
968 }
969
970 int addedCount = 0;
971 bool anySubtracted = false;
972
974 {
975 if( m_selection.GetSize() > 0 )
976 {
977 ClearSelection( true /*quiet mode*/ );
978 anySubtracted = true;
979 }
980 }
981
982 if( collector.GetCount() > 0 )
983 {
984 for( int i = 0; i < collector.GetCount(); ++i )
985 {
986 if( m_subtractive || ( m_exclusive_or && collector[i]->IsSelected() ) )
987 {
988 unselect( collector[i] );
989 anySubtracted = true;
990 }
991 else
992 {
993 select( collector[i] );
994 addedCount++;
995 }
996 }
997 }
998
999 if( addedCount == 1 )
1000 {
1001 m_toolMgr->ProcessEvent( EVENTS::PointSelectedEvent );
1002 return true;
1003 }
1004 else if( addedCount > 1 )
1005 {
1006 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1007 return true;
1008 }
1009 else if( anySubtracted )
1010 {
1011 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1012 return true;
1013 }
1014
1015 return false;
1016}
1017
1018
1019bool PCB_SELECTION_TOOL::selectCursor( bool aForceSelect, CLIENT_SELECTION_FILTER aClientFilter )
1020{
1021 if( aForceSelect || m_selection.Empty() )
1022 {
1023 ClearSelection( true /*quiet mode*/ );
1024 selectPoint( getViewControls()->GetCursorPosition( false ), false, nullptr, aClientFilter );
1025 }
1026
1027 return !m_selection.Empty();
1028}
1029
1030
1031// Some navigation actions are allowed in selectMultiple
1042
1043
1044static void passEvent( TOOL_EVENT* const aEvent, const TOOL_ACTION* const aAllowedActions[] )
1045{
1046 for( int i = 0; aAllowedActions[i]; ++i )
1047 {
1048 if( aEvent->IsAction( aAllowedActions[i] ) )
1049 {
1050 aEvent->SetPassEvent();
1051 break;
1052 }
1053 }
1054}
1055
1056
1058{
1059 bool cancelled = false; // Was the tool canceled while it was running?
1060 m_multiple = true; // Multiple selection mode is active
1061
1062 for( PCB_TABLECELL* cell : aTable->GetCells() )
1063 {
1064 if( cell->IsSelected() )
1065 cell->SetFlags( CANDIDATE );
1066 else
1067 cell->ClearFlags( CANDIDATE );
1068 }
1069
1070 auto wasSelected =
1071 []( EDA_ITEM* aItem )
1072 {
1073 return ( aItem->GetFlags() & CANDIDATE ) > 0;
1074 };
1075
1076 while( TOOL_EVENT* evt = Wait() )
1077 {
1078 if( evt->IsCancelInteractive() || evt->IsActivate() )
1079 {
1080 cancelled = true;
1081 break;
1082 }
1083 else if( evt->IsDrag( BUT_LEFT ) )
1084 {
1085 getViewControls()->SetAutoPan( true );
1086
1087 BOX2I selectionRect( evt->DragOrigin(), evt->Position() - evt->DragOrigin() );
1088 selectionRect.Normalize();
1089
1090 for( PCB_TABLECELL* cell : aTable->GetCells() )
1091 {
1092 bool doSelect = false;
1093
1094 if( cell->HitTest( selectionRect, false ) )
1095 {
1096 if( m_subtractive )
1097 doSelect = false;
1098 else if( m_exclusive_or )
1099 doSelect = !wasSelected( cell );
1100 else
1101 doSelect = true;
1102 }
1103 else if( wasSelected( cell ) )
1104 {
1105 doSelect = m_additive || m_subtractive || m_exclusive_or;
1106 }
1107
1108 if( doSelect && !cell->IsSelected() )
1109 select( cell );
1110 else if( !doSelect && cell->IsSelected() )
1111 unselect( cell );
1112 }
1113 }
1114 else if( evt->IsMouseUp( BUT_LEFT ) )
1115 {
1116 m_selection.SetIsHover( false );
1117
1118 bool anyAdded = false;
1119 bool anySubtracted = false;
1120
1121 for( PCB_TABLECELL* cell : aTable->GetCells() )
1122 {
1123 if( cell->IsSelected() && !wasSelected( cell ) )
1124 anyAdded = true;
1125 else if( wasSelected( cell ) && !cell->IsSelected() )
1126 anySubtracted = true;
1127 }
1128
1129 // Inform other potentially interested tools
1130 if( anyAdded )
1131 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1132
1133 if( anySubtracted )
1134 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1135
1136 break; // Stop waiting for events
1137 }
1138 else
1139 {
1140 // Allow some actions for navigation
1141 passEvent( evt, allowedActions );
1142 }
1143 }
1144
1145 getViewControls()->SetAutoPan( false );
1146
1147 m_multiple = false; // Multiple selection mode is inactive
1148
1149 if( !cancelled )
1150 m_selection.ClearReferencePoint();
1151
1152 return cancelled;
1153}
1154
1155
1157{
1158 bool cancelled = false; // Was the tool canceled while it was running?
1159 m_multiple = true; // Multiple selection mode is active
1161
1163 view->Add( &area );
1164
1165 while( TOOL_EVENT* evt = Wait() )
1166 {
1167 /* Selection mode depends on direction of drag-selection:
1168 * Left > Right : Select objects that are fully enclosed by selection
1169 * Right > Left : Select objects that are crossed by selection
1170 */
1171 bool greedySelection = area.GetEnd().x < area.GetOrigin().x;
1172
1173 if( view->IsMirroredX() )
1174 greedySelection = !greedySelection;
1175
1176 m_frame->GetCanvas()->SetCurrentCursor( !greedySelection ? KICURSOR::SELECT_WINDOW
1178
1179 if( evt->IsCancelInteractive() || evt->IsActivate() )
1180 {
1181 cancelled = true;
1182 break;
1183 }
1184
1185 if( evt->IsDrag( BUT_LEFT ) )
1186 {
1188 {
1189 if( m_selection.GetSize() > 0 )
1190 {
1191 ClearSelection( true /*quiet mode*/ );
1192 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1193 }
1194 }
1195
1196 // Start drawing a selection box
1197 area.SetOrigin( evt->DragOrigin() );
1198 area.SetEnd( evt->Position() );
1201 area.SetExclusiveOr( false );
1203
1204 view->SetVisible( &area, true );
1205 view->Update( &area );
1206 getViewControls()->SetAutoPan( true );
1207 }
1208
1209 if( evt->IsMouseUp( BUT_LEFT ) )
1210 {
1211 getViewControls()->SetAutoPan( false );
1212
1213 // End drawing the selection box
1214 view->SetVisible( &area, false );
1215
1217
1218 break; // Stop waiting for events
1219 }
1220
1221 // Allow some actions for navigation
1222 passEvent( evt, allowedActions );
1223 }
1224
1225 getViewControls()->SetAutoPan( false );
1226
1227 // Stop drawing the selection box
1228 view->Remove( &area );
1229 m_multiple = false; // Multiple selection mode is inactive
1230
1231 if( !cancelled )
1232 m_selection.ClearReferencePoint();
1233
1235
1236 return cancelled;
1237}
1238
1239
1241{
1243 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
1244 m_toolMgr->PostAction( ACTIONS::selectionTool );
1245 return 0; // No need to wait for an event, just set the mode
1246}
1247
1248
1250{
1252 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1253 m_toolMgr->PostAction( ACTIONS::selectionTool );
1254 return 0; // No need to wait for an event, just set the mode
1255}
1256
1257
1259{
1260 bool cancelled = false; // Was the tool canceled while it was running?
1261
1263 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
1264
1265
1266 SHAPE_LINE_CHAIN points;
1267 points.SetClosed( true );
1268
1270 getView()->Add( &area );
1271 getView()->SetVisible( &area, true );
1272 getViewControls()->SetAutoPan( true );
1273
1274 while( TOOL_EVENT* evt = Wait() )
1275 {
1276 // Auto mode: clockwise = inside, counterclockwise = touching
1277 double shapeArea = area.GetPoly().Area( false );
1278 bool isClockwise = shapeArea > 0 ? true : false;
1279
1280 if( getView()->IsMirroredX() && shapeArea != 0 )
1281 isClockwise = !isClockwise;
1282
1283 selectionMode = isClockwise ? SELECTION_MODE::INSIDE_LASSO : SELECTION_MODE::TOUCHING_LASSO;
1284
1285 if( evt->IsCancelInteractive() || evt->IsActivate() )
1286 {
1287 cancelled = true;
1288 evt->SetPassEvent( false );
1289 break;
1290 }
1291 else if( evt->IsDrag( BUT_LEFT )
1292 || evt->IsClick( BUT_LEFT )
1293 || evt->IsAction( &ACTIONS::cursorClick ) )
1294 {
1295 points.Append( evt->Position() );
1296 }
1297 else if( evt->IsDblClick( BUT_LEFT )
1298 || evt->IsAction( &ACTIONS::cursorDblClick )
1299 || evt->IsAction( &ACTIONS::finishInteractive ) )
1300 {
1301 area.GetPoly().GenerateBBoxCache();
1303 evt->SetPassEvent( false );
1304 break;
1305 }
1306 else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint )
1307 || evt->IsAction( &ACTIONS::doDelete )
1308 || evt->IsAction( &ACTIONS::undo ) )
1309 {
1310 if( points.GetPointCount() > 0 )
1311 {
1313 points.Remove( points.GetPointCount() - 1 );
1314 }
1315 }
1316 else
1317 {
1318 // Allow navigation actions
1319 passEvent( evt, allowedActions );
1320 }
1321
1322 if( points.PointCount() > 0 )
1323 {
1325 {
1326 if( m_selection.GetSize() > 0 )
1327 {
1328 ClearSelection( true /*quiet mode*/ );
1329 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1330 }
1331 }
1332 }
1333
1334 area.SetPoly( points );
1335 area.GetPoly().Append( m_toolMgr->GetMousePosition() );
1336 area.SetAdditive( m_additive );
1338 area.SetExclusiveOr( false );
1339 area.SetMode( selectionMode );
1340 getView()->Update( &area );
1341 }
1342
1343 getViewControls()->SetAutoPan( false );
1344 getView()->SetVisible( &area, false );
1345 getView()->Remove( &area );
1346 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1347
1348 if( !cancelled )
1350
1352
1353 return cancelled;
1354}
1355
1356
1358 bool aExclusiveOr )
1359{
1361
1362 bool anyAdded = false;
1363 bool anySubtracted = false;
1364
1365 SELECTION_MODE selectionMode = aArea.GetMode();
1366 bool containedMode = ( selectionMode == SELECTION_MODE::INSIDE_RECTANGLE
1367 || selectionMode == SELECTION_MODE::INSIDE_LASSO ) ? true : false;
1368 bool boxMode = ( selectionMode == SELECTION_MODE::INSIDE_RECTANGLE
1369 || selectionMode == SELECTION_MODE::TOUCHING_RECTANGLE ) ? true : false;
1370
1371 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> candidates;
1372 BOX2I selectionBox = aArea.ViewBBox();
1373 view->Query( selectionBox, candidates ); // Get the list of nearby items
1374
1375 GENERAL_COLLECTOR collector;
1376 GENERAL_COLLECTOR padsCollector;
1377 std::set<EDA_ITEM*> group_items;
1378
1379 for( PCB_GROUP* group : board()->Groups() )
1380 {
1381 // The currently entered group does not get limited
1382 if( m_enteredGroup == group )
1383 continue;
1384
1385 std::unordered_set<EDA_ITEM*>& newset = group->GetItems();
1386
1387 auto boxContained =
1388 [&]( const BOX2I& aBox )
1389 {
1390 return boxMode ? selectionBox.Contains( aBox )
1391 : KIGEOM::BoxHitTest( aArea.GetPoly(), aBox, true );
1392 };
1393
1394 // If we are not greedy and have selected the whole group, add just one item
1395 // to allow it to be promoted to the group later
1396 if( containedMode && boxContained( group->GetBoundingBox() ) && newset.size() )
1397 {
1398 for( EDA_ITEM* group_item : newset )
1399 {
1400 if( !group_item->IsBOARD_ITEM() )
1401 continue;
1402
1403 if( Selectable( static_cast<BOARD_ITEM*>( group_item ) ) )
1404 collector.Append( *newset.begin() );
1405 }
1406 }
1407
1408 for( EDA_ITEM* group_item : newset )
1409 group_items.emplace( group_item );
1410 }
1411
1412 auto hitTest =
1413 [&]( const EDA_ITEM* aItem )
1414 {
1415 return boxMode ? aItem->HitTest( selectionBox, containedMode )
1416 : aItem->HitTest( aArea.GetPoly(), containedMode );
1417 };
1418
1419 for( const auto& [item, layer] : candidates )
1420 {
1421 if( !item->IsBOARD_ITEM() )
1422 continue;
1423
1424 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
1425
1426 if( Selectable( boardItem ) && hitTest( boardItem )
1427 && ( !containedMode || !group_items.count( boardItem ) ) )
1428 {
1429 if( boardItem->Type() == PCB_PAD_T && !m_isFootprintEditor )
1430 padsCollector.Append( boardItem );
1431 else
1432 collector.Append( boardItem );
1433 }
1434 }
1435
1436 // Apply the stateful filter
1437 FilterCollectedItems( collector, true, nullptr );
1438
1439 FilterCollectorForHierarchy( collector, true );
1440
1441 // If we selected nothing but pads, allow them to be selected
1442 if( collector.GetCount() == 0 )
1443 {
1444 collector = padsCollector;
1445 FilterCollectedItems( collector, true, nullptr );
1446 FilterCollectorForHierarchy( collector, true );
1447 }
1448
1449 // Sort the filtered selection by rows and columns to have a nice default
1450 // for tools that can use it.
1451 std::sort( collector.begin(), collector.end(),
1452 []( EDA_ITEM* a, EDA_ITEM* b )
1453 {
1454 VECTOR2I aPos = a->GetPosition();
1455 VECTOR2I bPos = b->GetPosition();
1456
1457 if( aPos.y == bPos.y )
1458 return aPos.x < bPos.x;
1459
1460 return aPos.y < bPos.y;
1461 } );
1462
1463 for( EDA_ITEM* i : collector )
1464 {
1465 if( !i->IsBOARD_ITEM() )
1466 continue;
1467
1468 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
1469
1470 if( aSubtractive || ( aExclusiveOr && item->IsSelected() ) )
1471 {
1472 unselect( item );
1473 anySubtracted = true;
1474 }
1475 else
1476 {
1477 select( item );
1478 anyAdded = true;
1479 }
1480 }
1481
1482 m_selection.SetIsHover( false );
1483
1484 // Inform other potentially interested tools
1485 if( anyAdded )
1486 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1487 else if( anySubtracted )
1488 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1489}
1490
1491
1493{
1494 wxMouseState keyboardState = wxGetMouseState();
1495
1496 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(), keyboardState.AltDown() );
1497
1498 m_skip_heuristics = true;
1500 m_skip_heuristics = false;
1501
1502 return 0;
1503}
1504
1505
1506
1508{
1510
1511 selectCursor( false, aClientFilter );
1512
1513 return 0;
1514}
1515
1516
1518{
1520
1521 return 0;
1522}
1523
1524
1526{
1527 GENERAL_COLLECTOR collection;
1528 BOX2I selectionBox;
1529
1530 selectionBox.SetMaximum();
1531
1532 getView()->Query( selectionBox,
1533 [&]( KIGFX::VIEW_ITEM* viewItem ) -> bool
1534 {
1535 if( viewItem->IsBOARD_ITEM() )
1536 {
1537 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( viewItem );
1538
1539 if( item && Selectable( item ) && itemPassesFilter( item, true, nullptr ) )
1540 collection.Append( item );
1541 }
1542
1543 return true;
1544 } );
1545
1546 FilterCollectorForHierarchy( collection, true );
1547
1548 for( EDA_ITEM* item : collection )
1549 select( item );
1550
1551 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1552
1553 m_frame->GetCanvas()->ForceRefresh();
1554
1555 return 0;
1556}
1557
1558
1560{
1561 BOX2I selectionBox;
1562
1563 selectionBox.SetMaximum();
1564
1565 getView()->Query( selectionBox,
1566 [&]( KIGFX::VIEW_ITEM* viewItem ) -> bool
1567 {
1568 if( viewItem->IsBOARD_ITEM() )
1569 {
1570 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( viewItem );
1571
1572 if( item && Selectable( item ) )
1573 unselect( item );
1574 }
1575
1576 return true;
1577 } );
1578
1579 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1580
1581 m_frame->GetCanvas()->ForceRefresh();
1582
1583 return 0;
1584}
1585
1586
1588{
1589 // Narrow the collection down to a single BOARD_CONNECTED_ITEM for each represented net.
1590 // All other items types are removed.
1591 std::set<int> representedNets;
1592
1593 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
1594 {
1595 BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( aCollector[i] );
1596
1597 if( !item )
1598 aCollector.Remove( i );
1599 else if ( representedNets.count( item->GetNetCode() ) )
1600 aCollector.Remove( i );
1601 else
1602 representedNets.insert( item->GetNetCode() );
1603 }
1604}
1605
1606
1608{
1609 std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
1610
1611 // Get all footprints and pads
1612 std::vector<BOARD_CONNECTED_ITEM*> toUnroute;
1613 std::set<EDA_ITEM*> toDelete;
1614
1615 for( EDA_ITEM* item : selectedItems )
1616 {
1617 if( item->Type() == PCB_FOOTPRINT_T )
1618 {
1619 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
1620 toUnroute.push_back( pad );
1621 }
1622 else if( item->Type() == PCB_GENERATOR_T )
1623 {
1624 toDelete.insert( item );
1625
1626 for( BOARD_ITEM* generatedItem : static_cast<PCB_GENERATOR*>( item )->GetBoardItems() )
1627 {
1628 if( BOARD_CONNECTED_ITEM::ClassOf( generatedItem ) )
1629 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( generatedItem ) );
1630 }
1631 }
1632 else if( BOARD_CONNECTED_ITEM::ClassOf( item ) )
1633 {
1634 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
1635 }
1636 }
1637
1638 // Find generators connected to tracks and add their children to toUnroute
1639 // so selectAllConnectedTracks can traverse through meanders
1640 std::set<int> selectedNets;
1641
1642 for( BOARD_CONNECTED_ITEM* item : toUnroute )
1643 if( item->GetNetCode() > 0 )
1644 selectedNets.insert( item->GetNetCode() );
1645
1646 std::set<VECTOR2I> endpointSet;
1647
1648 for( BOARD_CONNECTED_ITEM* item : toUnroute )
1649 {
1650 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
1651 {
1652 endpointSet.insert( track->GetStart() );
1653 endpointSet.insert( track->GetEnd() );
1654 }
1655 else if( item->Type() == PCB_VIA_T )
1656 {
1657 endpointSet.insert( item->GetPosition() );
1658 }
1659 else if( item->Type() == PCB_PAD_T )
1660 {
1661 endpointSet.insert( item->GetPosition() );
1662 }
1663 }
1664
1665 bool expanded = true;
1666
1667 while( expanded )
1668 {
1669 expanded = false;
1670
1671 for( PCB_GENERATOR* gen : board()->Generators() )
1672 {
1673 if( toDelete.count( gen ) )
1674 continue;
1675
1676 // Find this generator's external endpoints (meander entry/exit)
1677 std::map<VECTOR2I, int> epCount;
1678
1679 for( BOARD_ITEM* child : gen->GetBoardItems() )
1680 {
1681 PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( child );
1682
1683 if( track && selectedNets.count( track->GetNetCode() ) )
1684 {
1685 epCount[track->GetStart()]++;
1686 epCount[track->GetEnd()]++;
1687 }
1688 }
1689
1690 // Check if any external endpoint matches our track endpoints
1691 bool connected = false;
1692
1693 for( auto& [pt, count] : epCount )
1694 {
1695 if( count == 1 && endpointSet.count( pt ) )
1696 {
1697 connected = true;
1698 break;
1699 }
1700 }
1701
1702 if( connected )
1703 {
1704 toDelete.insert( gen );
1705
1706 for( BOARD_ITEM* c : gen->GetBoardItems() )
1707 {
1709 continue;
1710
1711 if( !selectedNets.count( static_cast<BOARD_CONNECTED_ITEM*>( c )->GetNetCode() ) )
1712 continue;
1713
1714 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( c ) );
1715
1716 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( c ) )
1717 {
1718 endpointSet.insert( track->GetStart() );
1719 endpointSet.insert( track->GetEnd() );
1720 }
1721
1722 expanded = true;
1723 }
1724 }
1725 }
1726 }
1727
1728 // Because selectAllConnectedTracks() use m_filter to collect connected tracks
1729 // to pads, enable filter for these items, regardless the curent filter options
1730 // via filter is not changed, because it can be useful to keep via filter disabled
1731 struct PCB_SELECTION_FILTER_OPTIONS save_filter = m_filter;
1732 m_filter.tracks = true;
1733 m_filter.pads = true;
1734
1735 // Clear selection so we don't delete our footprints/pads
1736 ClearSelection( true );
1737
1738 // Get the tracks on our list of pads, then delete them
1740
1741 BOARD_COMMIT commit( m_toolMgr );
1742 std::set<BOARD_ITEM*> removed;
1743
1744 for( EDA_ITEM* item : m_selection )
1745 {
1746 if( !item->IsBOARD_ITEM() )
1747 continue;
1748
1749 BOARD_ITEM* bi = static_cast<BOARD_ITEM*>( item );
1750
1751 if( bi->Type() == PCB_GENERATOR_T )
1752 toDelete.insert( bi );
1753
1754 commit.Remove( bi );
1755 removed.insert( bi );
1756 }
1757
1758 // Find generators whose children were removed
1759 for( PCB_GENERATOR* gen : board()->Generators() )
1760 {
1761 for( BOARD_ITEM* child : gen->GetBoardItems() )
1762 {
1763 if( removed.count( child ) )
1764 {
1765 toDelete.insert( gen );
1766 break;
1767 }
1768 }
1769 }
1770
1771 for( EDA_ITEM* item : toDelete )
1772 {
1773 if( !item->IsBOARD_ITEM() )
1774 continue;
1775
1776 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
1777
1778 boardItem->RunOnChildren(
1779 [&commit, &removed]( BOARD_ITEM* aItem )
1780 {
1781 if( removed.find( aItem ) == removed.end() )
1782 {
1783 commit.Remove( aItem );
1784 removed.insert( aItem );
1785 }
1786 },
1788
1789 if( removed.find( boardItem ) == removed.end() )
1790 commit.Remove( boardItem );
1791 }
1792
1793 ClearSelection( true );
1794 commit.Push( _( "Unroute Selected" ) );
1795
1796 m_filter = save_filter; // restore current filter options
1797
1798 // Reselect our footprint/pads as they were in our original selection
1799 for( EDA_ITEM* item : selectedItems )
1800 {
1801 if( item->Type() == PCB_FOOTPRINT_T || item->Type() == PCB_PAD_T )
1802 select( item );
1803 }
1804
1805 return 0;
1806}
1807
1808
1810{
1811 std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
1812
1813 // Get all footprints and pads
1814 std::vector<BOARD_CONNECTED_ITEM*> toUnroute;
1815
1816 std::set<EDA_ITEM*> toDelete;
1817
1818 for( EDA_ITEM* item : selectedItems )
1819 {
1820 if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T || item->Type() == PCB_VIA_T )
1821 {
1822 BOARD_ITEM* bi = static_cast<BOARD_ITEM*>( item );
1823 EDA_GROUP* parentGroup = bi->GetParentGroup();
1824
1825 if( parentGroup && parentGroup->AsEdaItem()->Type() == PCB_GENERATOR_T )
1826 {
1827 PCB_GENERATOR* gen = static_cast<PCB_GENERATOR*>( parentGroup->AsEdaItem() );
1828
1829 if( !toDelete.count( gen ) )
1830 {
1831 toDelete.insert( gen );
1832
1833 for( BOARD_ITEM* generatedItem : gen->GetBoardItems() )
1834 {
1835 toDelete.insert( generatedItem );
1836
1837 if( BOARD_CONNECTED_ITEM::ClassOf( generatedItem ) )
1838 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( generatedItem ) );
1839 }
1840 }
1841 }
1842 else
1843 {
1844 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
1845 toDelete.insert( item );
1846 }
1847 }
1848 else if( item->Type() == PCB_GENERATOR_T )
1849 {
1850 toDelete.insert( item );
1851
1852 for( BOARD_ITEM* generatedItem : static_cast<PCB_GENERATOR*>( item )->GetBoardItems() )
1853 {
1854 toDelete.insert( generatedItem );
1855
1856 if( BOARD_CONNECTED_ITEM::ClassOf( generatedItem ) )
1857 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( generatedItem ) );
1858 }
1859 }
1860 }
1861
1862 // Get the tracks connecting to our starting objects
1865 std::deque<EDA_ITEM*> toSelectAfter;
1866 // This will select the unroute items too, so filter them out
1867 for( EDA_ITEM* item : m_selection.GetItemsSortedByTypeAndXY() )
1868 {
1869 if( !item->IsBOARD_ITEM() )
1870 continue;
1871
1872 if( toDelete.find( item ) == toDelete.end() )
1873 toSelectAfter.push_back( item );
1874 }
1875
1876 BOARD_COMMIT commit( m_toolMgr );
1877 std::set<BOARD_ITEM*> removed;
1878
1879 for( EDA_ITEM* item : toDelete )
1880 {
1881 if( !item->IsBOARD_ITEM() )
1882 continue;
1883
1884 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
1885
1886 if( item->Type() == PCB_GENERATOR_T )
1887 {
1888 boardItem->RunOnChildren(
1889 [&commit, &removed]( BOARD_ITEM* aItem )
1890 {
1891 if( removed.insert( aItem ).second )
1892 commit.Remove( aItem );
1893 },
1895
1896 if( removed.insert( boardItem ).second )
1897 commit.Remove( boardItem );
1898 }
1899 else
1900 {
1901 if( removed.insert( boardItem ).second )
1902 commit.Remove( boardItem );
1903 }
1904 }
1905
1906 commit.Push( _( "Unroute Segment" ) );
1907
1908 // Now our after tracks so the user can continue backing up as desired
1909 ClearSelection( true );
1910
1911 for( EDA_ITEM* item : toSelectAfter )
1912 {
1913 if( !toDelete.count( item ) )
1914 select( item );
1915 }
1916
1917 return 0;
1918}
1919
1920
1922{
1923 // expandConnection will get called no matter whether the user selected a connected item or a
1924 // non-connected shape (graphic on a non-copper layer). The algorithm for expanding to connected
1925 // items is different from graphics, so they need to be handled separately.
1926 unsigned initialCount = 0;
1927
1928 for( const EDA_ITEM* item : m_selection.GetItems() )
1929 {
1930 if( item->Type() == PCB_FOOTPRINT_T
1931 || item->Type() == PCB_GENERATOR_T
1932 || ( static_cast<const BOARD_ITEM*>( item )->IsConnected() ) )
1933 {
1934 initialCount++;
1935 }
1936 }
1937
1938 if( initialCount == 0 )
1939 {
1940 // First, process any graphic shapes we have
1941 std::vector<PCB_SHAPE*> startShapes;
1942
1943 for( EDA_ITEM* item : m_selection.GetItems() )
1944 {
1945 if( isExpandableGraphicShape( item ) )
1946 startShapes.push_back( static_cast<PCB_SHAPE*>( item ) );
1947 }
1948
1949 // If no non-copper shapes; fall back to looking for connected items
1950 if( !startShapes.empty() )
1951 selectAllConnectedShapes( startShapes );
1952 else
1954 }
1955
1956 m_frame->SetStatusText( _( "Select/Expand Connection..." ) );
1957
1958 for( STOP_CONDITION stopCondition : { STOP_AT_JUNCTION, STOP_AT_PAD, STOP_NEVER } )
1959 {
1960 std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
1961
1962 for( EDA_ITEM* item : selectedItems )
1963 item->ClearTempFlags();
1964
1965 std::vector<BOARD_CONNECTED_ITEM*> startItems;
1966
1967 for( EDA_ITEM* item : selectedItems )
1968 {
1969 if( item->Type() == PCB_FOOTPRINT_T )
1970 {
1971 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
1972
1973 for( PAD* pad : footprint->Pads() )
1974 startItems.push_back( pad );
1975 }
1976 else if( item->Type() == PCB_GENERATOR_T )
1977 {
1978 for( BOARD_ITEM* generatedItem : static_cast<PCB_GENERATOR*>( item )->GetBoardItems() )
1979 {
1980 if( BOARD_CONNECTED_ITEM::ClassOf( generatedItem ) )
1981 startItems.push_back( static_cast<BOARD_CONNECTED_ITEM*>( generatedItem ) );
1982 }
1983 }
1984 else if( BOARD_CONNECTED_ITEM::ClassOf( item ) )
1985 {
1986 startItems.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
1987 }
1988 }
1989
1990 selectAllConnectedTracks( startItems, stopCondition );
1991
1992 if( m_selection.GetItems().size() > initialCount )
1993 break;
1994 }
1995
1996 m_frame->SetStatusText( wxEmptyString );
1997
1998 // Inform other potentially interested tools
1999 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2000
2001 return 0;
2002}
2003
2004
2005void PCB_SELECTION_TOOL::selectAllConnectedTracks( const std::vector<BOARD_CONNECTED_ITEM*>& aStartItems,
2006 STOP_CONDITION aStopCondition )
2007{
2008 PROF_TIMER refreshTimer;
2009 double refreshIntervalMs = 500; // Refresh display with this interval to indicate progress
2010 int lastSelectionSize = (int) m_selection.GetSize();
2011
2012 auto connectivity = board()->GetConnectivity();
2013
2014 std::set<PAD*> startPadSet;
2015 std::vector<BOARD_CONNECTED_ITEM*> cleanupItems;
2016
2017 for( BOARD_CONNECTED_ITEM* startItem : aStartItems )
2018 {
2019 // Register starting pads
2020 if( startItem->Type() == PCB_PAD_T )
2021 startPadSet.insert( static_cast<PAD*>( startItem ) );
2022
2023 // Select any starting track items
2024 if( startItem->IsType( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } ) )
2025 {
2026 if( itemPassesFilter( startItem, true ) )
2027 select( startItem );
2028 }
2029 }
2030
2031 for( BOARD_CONNECTED_ITEM* startItem : aStartItems )
2032 {
2033 std::map<VECTOR2I, std::vector<PCB_TRACK*>> trackMap;
2034 std::map<VECTOR2I, PCB_VIA*> viaMap;
2035 std::map<VECTOR2I, PAD*> padMap;
2036 std::map<VECTOR2I, std::vector<PCB_SHAPE*>> shapeMap;
2037 std::vector<std::pair<VECTOR2I, LSET>> activePts;
2038
2039 if( startItem->HasFlag( SKIP_STRUCT ) ) // Skip already visited items
2040 continue;
2041
2042 auto connectedItems = connectivity->GetConnectedItems( startItem, EXCLUDE_ZONES | IGNORE_NETS );
2043
2044 // Build maps of connected items
2045 for( BOARD_CONNECTED_ITEM* item : connectedItems )
2046 {
2047 switch( item->Type() )
2048 {
2049 case PCB_ARC_T:
2050 case PCB_TRACE_T:
2051 {
2052 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
2053 trackMap[track->GetStart()].push_back( track );
2054 trackMap[track->GetEnd()].push_back( track );
2055 break;
2056 }
2057
2058 case PCB_VIA_T:
2059 {
2060 PCB_VIA* via = static_cast<PCB_VIA*>( item );
2061 viaMap[via->GetStart()] = via;
2062 break;
2063 }
2064
2065 case PCB_PAD_T:
2066 {
2067 PAD* pad = static_cast<PAD*>( item );
2068 padMap[pad->GetPosition()] = pad;
2069 break;
2070 }
2071
2072 case PCB_SHAPE_T:
2073 {
2074 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2075
2076 for( const auto& point : shape->GetConnectionPoints() )
2077 shapeMap[point].push_back( shape );
2078
2079 break;
2080 }
2081
2082 default:
2083 break;
2084 }
2085 }
2086
2087 // Set up the initial active points
2088 switch( startItem->Type() )
2089 {
2090 case PCB_ARC_T:
2091 case PCB_TRACE_T:
2092 {
2093 PCB_TRACK* track = static_cast<PCB_TRACK*>( startItem );
2094
2095 activePts.push_back( { track->GetStart(), track->GetLayerSet() } );
2096 activePts.push_back( { track->GetEnd(), track->GetLayerSet() } );
2097 break;
2098 }
2099
2100 case PCB_VIA_T:
2101 activePts.push_back( { startItem->GetPosition(), startItem->GetLayerSet() } );
2102 break;
2103
2104 case PCB_PAD_T:
2105 activePts.push_back( { startItem->GetPosition(), startItem->GetLayerSet() } );
2106 break;
2107
2108 case PCB_SHAPE_T:
2109 {
2110 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( startItem );
2111
2112 for( const auto& point : shape->GetConnectionPoints() )
2113 activePts.push_back( { point, startItem->GetLayerSet() } );
2114
2115 break;
2116 }
2117
2118 default:
2119 break;
2120 }
2121
2122 bool expand = true;
2123 int failSafe = 0;
2124
2125 // Iterative push from all active points
2126 while( expand && failSafe++ < 100000 )
2127 {
2128 expand = false;
2129
2130 for( int i = (int) activePts.size() - 1; i >= 0; --i )
2131 {
2132 VECTOR2I pt = activePts[i].first;
2133 LSET layerSetCu = activePts[i].second & LSET::AllCuMask();
2134
2135 PCB_VIA* hitVia = nullptr;
2136
2137 // exact position match (common case)
2138 auto exactIt = viaMap.find( pt );
2139
2140 if( exactIt != viaMap.end() && ( exactIt->second->GetLayerSet() & layerSetCu ).any() )
2141 {
2142 hitVia = exactIt->second;
2143 }
2144 else
2145 {
2146 // off-center VIA connection
2147 for( auto& [pos, via] : viaMap )
2148 {
2149 if( !( via->GetLayerSet() & layerSetCu ).any() )
2150 continue;
2151
2152 bool hit = false;
2153
2154 for( PCB_LAYER_ID layer : LSET( via->GetLayerSet() & layerSetCu ).CuStack() )
2155 {
2156 int radius = via->GetWidth( layer ) / 2;
2157 int64_t radiusSq = static_cast<int64_t>( radius ) * radius;
2158
2159 if( ( pt - pos ).SquaredEuclideanNorm() <= radiusSq )
2160 {
2161 hit = true;
2162 break;
2163 }
2164 }
2165
2166 if( hit )
2167 {
2168 hitVia = via;
2169 break;
2170 }
2171 }
2172 }
2173
2174 auto padIt = padMap.find( pt );
2175
2176 bool gotVia = hitVia != nullptr;
2177 bool gotPad = padIt != padMap.end() && ( padIt->second->GetLayerSet() & layerSetCu ).any();
2178 bool gotNonStartPad = gotPad && ( startPadSet.find( padIt->second ) == startPadSet.end() );
2179
2180 if( gotPad && !itemPassesFilter( padIt->second, true ) )
2181 {
2182 activePts.erase( activePts.begin() + i );
2183 continue;
2184 }
2185
2186 if( gotVia && !itemPassesFilter( hitVia, true ) )
2187 {
2188 activePts.erase( activePts.begin() + i );
2189 continue;
2190 }
2191
2192 if( aStopCondition == STOP_AT_JUNCTION )
2193 {
2194 size_t pt_count = 0;
2195
2196 for( PCB_TRACK* track : trackMap[pt] )
2197 {
2198 if( track->GetStart() != track->GetEnd() && layerSetCu.Contains( track->GetLayer() ) )
2199 pt_count++;
2200 }
2201
2202 if( pt_count > 2 || gotVia || gotNonStartPad )
2203 {
2204 activePts.erase( activePts.begin() + i );
2205 continue;
2206 }
2207 }
2208 else if( aStopCondition == STOP_AT_PAD )
2209 {
2210 if( gotNonStartPad )
2211 {
2212 activePts.erase( activePts.begin() + i );
2213 continue;
2214 }
2215 }
2216
2217 if( gotPad )
2218 {
2219 PAD* pad = padIt->second;
2220
2221 if( !pad->HasFlag( SKIP_STRUCT ) )
2222 {
2223 pad->SetFlags( SKIP_STRUCT );
2224 cleanupItems.push_back( pad );
2225
2226 activePts.push_back( { pad->GetPosition(), pad->GetLayerSet() } );
2227 expand = true;
2228 }
2229 }
2230
2231 for( PCB_TRACK* track : trackMap[pt] )
2232 {
2233 if( !layerSetCu.Contains( track->GetLayer() ) )
2234 continue;
2235
2236 if( !itemPassesFilter( track, true ) )
2237 continue;
2238
2239 if( !track->IsSelected() )
2240 select( track );
2241
2242 if( !track->HasFlag( SKIP_STRUCT ) )
2243 {
2244 track->SetFlags( SKIP_STRUCT );
2245 cleanupItems.push_back( track );
2246
2247 if( track->GetStart() == pt )
2248 activePts.push_back( { track->GetEnd(), track->GetLayerSet() } );
2249 else
2250 activePts.push_back( { track->GetStart(), track->GetLayerSet() } );
2251
2252 if( aStopCondition != STOP_AT_SEGMENT )
2253 expand = true;
2254 }
2255 }
2256
2257 for( PCB_SHAPE* shape : shapeMap[pt] )
2258 {
2259 if( !layerSetCu.Contains( shape->GetLayer() ) )
2260 continue;
2261
2262 if( !itemPassesFilter( shape, true ) )
2263 continue;
2264
2265 if( !shape->IsSelected() )
2266 select( shape );
2267
2268 if( !shape->HasFlag( SKIP_STRUCT ) )
2269 {
2270 shape->SetFlags( SKIP_STRUCT );
2271 cleanupItems.push_back( shape );
2272
2273 for( const VECTOR2I& newPoint : shape->GetConnectionPoints() )
2274 {
2275 if( newPoint == pt )
2276 continue;
2277
2278 activePts.push_back( { newPoint, shape->GetLayerSet() } );
2279 }
2280
2281 if( aStopCondition != STOP_AT_SEGMENT )
2282 expand = true;
2283 }
2284 }
2285
2286 if( hitVia )
2287 {
2288 if( !hitVia->IsSelected() )
2289 select( hitVia );
2290
2291 if( !hitVia->HasFlag( SKIP_STRUCT ) )
2292 {
2293 hitVia->SetFlags( SKIP_STRUCT );
2294 cleanupItems.push_back( hitVia );
2295
2296 VECTOR2I viaPos = hitVia->GetPosition();
2297
2298 int maxRadius = 0;
2299
2300 for( PCB_LAYER_ID layer : hitVia->GetLayerSet().CuStack() )
2301 maxRadius = std::max( maxRadius, hitVia->GetWidth( layer ) / 2 );
2302
2303 int64_t maxRadiusSq = static_cast<int64_t>( maxRadius ) * maxRadius;
2304
2305 for( auto& [trkPt, tracks] : trackMap )
2306 {
2307 if( ( trkPt - viaPos ).SquaredEuclideanNorm() > maxRadiusSq )
2308 continue;
2309
2310 // Verify point is inside the VIA pad on at least one track layer
2311 bool inside = false;
2312
2313 for( PCB_TRACK* trk : tracks )
2314 {
2315 PCB_LAYER_ID trkLayer = trk->GetLayer();
2316
2317 if( !hitVia->GetLayerSet().Contains( trkLayer ) )
2318 continue;
2319
2320 int r = hitVia->GetWidth( trkLayer ) / 2;
2321 int64_t rSq = static_cast<int64_t>( r ) * r;
2322
2323 if( ( trkPt - viaPos ).SquaredEuclideanNorm() <= rSq )
2324 {
2325 inside = true;
2326 break;
2327 }
2328 }
2329
2330 if( inside )
2331 activePts.push_back( { trkPt, hitVia->GetLayerSet() } );
2332 }
2333
2334 if( aStopCondition != STOP_AT_SEGMENT )
2335 expand = true;
2336 }
2337 }
2338
2339 activePts.erase( activePts.begin() + i );
2340 }
2341
2342 // Refresh display for the feel of progress
2343 if( refreshTimer.msecs() >= refreshIntervalMs )
2344 {
2345 if( m_selection.Size() != lastSelectionSize )
2346 {
2347 m_frame->GetCanvas()->ForceRefresh();
2348 lastSelectionSize = m_selection.Size();
2349 }
2350
2351 refreshTimer.Start();
2352 }
2353 }
2354 }
2355
2356 std::set<EDA_ITEM*> toDeselect;
2357 std::set<EDA_ITEM*> toSelect;
2358
2359 // Promote generated members to their PCB_GENERATOR parents
2360 for( EDA_ITEM* item : m_selection )
2361 {
2362 if( !item->IsBOARD_ITEM() )
2363 continue;
2364
2365 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
2366 EDA_GROUP* parent = boardItem->GetParentGroup();
2367
2368 if( parent && parent->AsEdaItem()->Type() == PCB_GENERATOR_T )
2369 {
2370 toDeselect.insert( item );
2371
2372 if( !parent->AsEdaItem()->IsSelected() )
2373 toSelect.insert( parent->AsEdaItem() );
2374 }
2375 }
2376
2377 for( EDA_ITEM* item : toDeselect )
2378 unselect( item );
2379
2380 for( EDA_ITEM* item : toSelect )
2381 select( item );
2382
2383 for( BOARD_CONNECTED_ITEM* item : cleanupItems )
2384 item->ClearFlags( SKIP_STRUCT );
2385}
2386
2387
2389{
2390 if( aItem->Type() == PCB_SHAPE_T )
2391 {
2392 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
2393
2394 switch( shape->GetShape() )
2395 {
2396 case SHAPE_T::SEGMENT:
2397 case SHAPE_T::ARC:
2398 case SHAPE_T::BEZIER:
2399 return !shape->IsOnCopperLayer();
2400
2401 case SHAPE_T::POLY:
2402 return !shape->IsOnCopperLayer() && !shape->IsClosed();
2403
2404 default:
2405 return false;
2406 }
2407 }
2408
2409 return false;
2410}
2411
2412
2413void PCB_SELECTION_TOOL::selectAllConnectedShapes( const std::vector<PCB_SHAPE*>& aStartItems )
2414{
2415 std::stack<PCB_SHAPE*> toSearch;
2416 std::set<PCB_SHAPE*> toCleanup;
2417
2418 for( PCB_SHAPE* startItem : aStartItems )
2419 toSearch.push( startItem );
2420
2421 GENERAL_COLLECTOR collector;
2423
2424 auto searchPoint =
2425 [&]( const VECTOR2I& aWhere )
2426 {
2427 collector.Collect( board(), { PCB_SHAPE_T }, aWhere, guide );
2428
2429 for( EDA_ITEM* item : collector )
2430 {
2431 if( isExpandableGraphicShape( item ) )
2432 toSearch.push( static_cast<PCB_SHAPE*>( item ) );
2433 }
2434 };
2435
2436 while( !toSearch.empty() )
2437 {
2438 PCB_SHAPE* shape = toSearch.top();
2439 toSearch.pop();
2440
2441 if( shape->HasFlag( SKIP_STRUCT ) )
2442 continue;
2443
2444 shape->SetFlags( SKIP_STRUCT );
2445 toCleanup.insert( shape );
2446
2447 if( !itemPassesFilter( shape, true ) )
2448 continue;
2449
2450 select( shape );
2451 guide.SetLayerVisibleBits( shape->GetLayerSet() );
2452
2453 searchPoint( shape->GetStart() );
2454 searchPoint( shape->GetEnd() );
2455 }
2456
2457 for( PCB_SHAPE* shape : toCleanup )
2458 shape->ClearFlags( SKIP_STRUCT );
2459}
2460
2461
2463{
2464 // Get all pads
2465 std::vector<PAD*> pads;
2466
2467 for( EDA_ITEM* item : m_selection.GetItems() )
2468 {
2469 if( item->Type() == PCB_FOOTPRINT_T )
2470 {
2471 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
2472 pads.push_back( pad );
2473 }
2474 else if( item->Type() == PCB_PAD_T )
2475 {
2476 pads.push_back( static_cast<PAD*>( item ) );
2477 }
2478 }
2479
2480 // Select every footprint on the end of the ratsnest for each pad in our selection
2481 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
2482
2483 for( PAD* pad : pads )
2484 {
2485 for( const CN_EDGE& edge : conn->GetRatsnestForPad( pad ) )
2486 {
2487 wxCHECK2( edge.GetSourceNode() && !edge.GetSourceNode()->Dirty(), continue );
2488 wxCHECK2( edge.GetTargetNode() && !edge.GetTargetNode()->Dirty(), continue );
2489
2490 BOARD_CONNECTED_ITEM* sourceParent = edge.GetSourceNode()->Parent();
2491 BOARD_CONNECTED_ITEM* targetParent = edge.GetTargetNode()->Parent();
2492
2493 if( sourceParent == pad )
2494 {
2495 if( targetParent->Type() == PCB_PAD_T )
2496 select( static_cast<PAD*>( targetParent )->GetParent() );
2497 }
2498 else if( targetParent == pad )
2499 {
2500 if( sourceParent->Type() == PCB_PAD_T )
2501 select( static_cast<PAD*>( sourceParent )->GetParent() );
2502 }
2503 }
2504 }
2505
2506 return 0;
2507}
2508
2509
2511{
2512 PCB_SELECTION originalSelection = m_selection;
2513
2514 // Get all pads
2515 std::vector<PAD*> pads;
2516
2517 for( EDA_ITEM* item : m_selection.GetItems() )
2518 {
2519 if( item->Type() == PCB_FOOTPRINT_T )
2520 {
2521 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
2522 pads.push_back( pad );
2523 }
2524 else if( item->Type() == PCB_PAD_T )
2525 {
2526 pads.push_back( static_cast<PAD*>( item ) );
2527 }
2528 }
2529
2531
2532 // Select every footprint on the end of the ratsnest for each pad in our selection
2533 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
2534
2535 for( PAD* pad : pads )
2536 {
2537 const std::vector<CN_EDGE> edges = conn->GetRatsnestForPad( pad );
2538
2539 // Need to have something unconnected to grab
2540 if( edges.size() == 0 )
2541 continue;
2542
2543 double currentDistance = DBL_MAX;
2544 FOOTPRINT* nearest = nullptr;
2545
2546 // Check every ratsnest line for the nearest one
2547 for( const CN_EDGE& edge : edges )
2548 {
2549 if( edge.GetSourceNode()->Parent()->GetParentFootprint()
2550 == edge.GetTargetNode()->Parent()->GetParentFootprint() )
2551 {
2552 continue; // This edge is a loop on the same footprint
2553 }
2554
2555 // Figure out if we are the source or the target node on the ratnest
2556 const CN_ANCHOR* other = edge.GetSourceNode()->Parent() == pad ? edge.GetTargetNode().get()
2557 : edge.GetSourceNode().get();
2558
2559 wxCHECK2( other && !other->Dirty(), continue );
2560
2561 // We only want to grab footprints, so the ratnest has to point to a pad
2562 if( other->Parent()->Type() != PCB_PAD_T )
2563 continue;
2564
2565 if( edge.GetLength() < currentDistance )
2566 {
2567 currentDistance = edge.GetLength();
2568 nearest = other->Parent()->GetParentFootprint();
2569 }
2570 }
2571
2572 if( nearest != nullptr )
2573 select( nearest );
2574 }
2575
2577
2578 return 0;
2579}
2580
2581
2582void PCB_SELECTION_TOOL::SelectAllItemsOnNet( int aNetCode, bool aSelect )
2583{
2584 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
2585
2586 for( BOARD_ITEM* item : conn->GetNetItems( aNetCode, { PCB_TRACE_T,
2587 PCB_ARC_T,
2588 PCB_VIA_T,
2589 PCB_SHAPE_T } ) )
2590 {
2591 if( itemPassesFilter( item, true, nullptr ) )
2592 aSelect ? select( item ) : unselect( item );
2593 }
2594}
2595
2596
2598{
2599 bool select = aEvent.IsAction( &PCB_ACTIONS::selectNet );
2600
2601 // If we've been passed an argument, just select that netcode1
2602 int netcode = aEvent.Parameter<int>();
2603
2604 if( netcode > 0 )
2605 {
2606 SelectAllItemsOnNet( netcode, select );
2607
2608 // Inform other potentially interested tools
2609 if( m_selection.Size() > 0 )
2610 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2611 else
2612 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2613
2614 return 0;
2615 }
2616
2617 if( !selectCursor() )
2618 return 0;
2619
2620 // copy the selection, since we're going to iterate and modify
2621 auto selection = m_selection.GetItems();
2622
2623 for( EDA_ITEM* i : selection )
2624 {
2625 BOARD_CONNECTED_ITEM* connItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( i );
2626
2627 if( connItem )
2628 SelectAllItemsOnNet( connItem->GetNetCode(), select );
2629 }
2630
2631 // Inform other potentially interested tools
2632 if( m_selection.Size() > 0 )
2633 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2634 else
2635 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2636
2637 return 0;
2638}
2639
2640
2642{
2643 if( !selectCursor() )
2644 return 0;
2645
2646 auto selection = m_selection.GetItems();
2647
2648 for( EDA_ITEM* i : selection )
2649 {
2650 BOARD_CONNECTED_ITEM* connItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( i );
2651
2652 if( !connItem )
2653 continue;
2654
2655 NETINFO_ITEM* netInfo = connItem->GetNet();
2656
2657 if( !netInfo )
2658 continue;
2659
2660 const wxString& chainName = netInfo->GetNetChain();
2661
2662 if( chainName.IsEmpty() )
2663 {
2664 // Net is not part of any chain; fall back to single-net behaviour.
2665 SelectAllItemsOnNet( connItem->GetNetCode(), true );
2666 continue;
2667 }
2668
2669 for( NETINFO_ITEM* candidate : board()->GetNetInfo() )
2670 {
2671 if( candidate && candidate->GetNetChain() == chainName )
2672 SelectAllItemsOnNet( candidate->GetNetCode(), true );
2673 }
2674 }
2675
2676 if( m_selection.Size() > 0 )
2677 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2678 else
2679 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2680
2681 return 0;
2682}
2683
2684
2686{
2687 std::vector<BOARD_ITEM*> footprints;
2688
2689 // store all footprints that are on that sheet path
2690 for( FOOTPRINT* footprint : board()->Footprints() )
2691 {
2692 if( footprint == nullptr )
2693 continue;
2694
2695 wxString footprint_path = footprint->GetPath().AsString().BeforeLast( '/' );
2696
2697 if( footprint_path.IsEmpty() )
2698 footprint_path += '/';
2699
2700 if( footprint_path == aSheetPath )
2701 footprints.push_back( footprint );
2702 }
2703
2704 for( BOARD_ITEM* i : footprints )
2705 {
2706 if( i != nullptr )
2707 select( i );
2708 }
2709
2710 selectConnections( footprints );
2711}
2712
2713
2714void PCB_SELECTION_TOOL::selectConnections( const std::vector<BOARD_ITEM*>& aItems )
2715{
2716 // Generate a list of all pads, and of all nets they belong to.
2717 std::list<int> netcodeList;
2718 std::vector<BOARD_CONNECTED_ITEM*> padList;
2719
2720 for( BOARD_ITEM* item : aItems )
2721 {
2722 switch( item->Type() )
2723 {
2724 case PCB_FOOTPRINT_T:
2725 {
2726 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
2727 {
2728 if( pad->IsConnected() )
2729 {
2730 netcodeList.push_back( pad->GetNetCode() );
2731 padList.push_back( pad );
2732 }
2733 }
2734
2735 break;
2736 }
2737
2738 case PCB_PAD_T:
2739 {
2740 PAD* pad = static_cast<PAD*>( item );
2741
2742 if( pad->IsConnected() )
2743 {
2744 netcodeList.push_back( pad->GetNetCode() );
2745 padList.push_back( pad );
2746 }
2747
2748 break;
2749 }
2750
2751 default:
2752 break;
2753 }
2754 }
2755
2756 // Sort for binary search
2757 std::sort( padList.begin(), padList.end() );
2758
2759 // remove all duplicates
2760 netcodeList.sort();
2761 netcodeList.unique();
2762
2764
2765 // now we need to find all footprints that are connected to each of these nets then we need
2766 // to determine if these footprints are in the list of footprints
2767 std::vector<int> removeCodeList;
2768 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
2769
2770 for( int netCode : netcodeList )
2771 {
2772 for( BOARD_CONNECTED_ITEM* pad : conn->GetNetItems( netCode, { PCB_PAD_T } ) )
2773 {
2774 if( !std::binary_search( padList.begin(), padList.end(), pad ) )
2775 {
2776 // if we cannot find the pad in the padList then we can assume that that pad
2777 // should not be used, therefore invalidate this netcode.
2778 removeCodeList.push_back( netCode );
2779 break;
2780 }
2781 }
2782 }
2783
2784 for( int removeCode : removeCodeList )
2785 netcodeList.remove( removeCode );
2786
2787 std::unordered_set<BOARD_ITEM*> localConnectionList;
2788
2789 for( int netCode : netcodeList )
2790 {
2791 for( BOARD_ITEM* item : conn->GetNetItems( netCode, { PCB_TRACE_T,
2792 PCB_ARC_T,
2793 PCB_VIA_T,
2794 PCB_SHAPE_T } ) )
2795 {
2796 localConnectionList.insert( item );
2797 }
2798 }
2799
2800 for( BOARD_ITEM* item : localConnectionList )
2801 select( item );
2802}
2803
2804
2806{
2807 std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
2808
2809 if( items )
2810 doSyncSelection( *items, false );
2811
2812 return 0;
2813}
2814
2815
2817{
2818 std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
2819
2820 if( items )
2821 doSyncSelection( *items, true );
2822
2823 return 0;
2824}
2825
2826
2827void PCB_SELECTION_TOOL::doSyncSelection( const std::vector<BOARD_ITEM*>& aItems, bool aWithNets )
2828{
2829 if( m_selection.Front() && m_selection.Front()->IsMoving() )
2830 return;
2831
2832 // Also check the incoming items. If the cross-probe flash timer cleared the selection
2833 // during a move, Front() would be null but the items are still being actively moved.
2834 for( const BOARD_ITEM* item : aItems )
2835 {
2836 if( item->IsMoving() )
2837 return;
2838 }
2839
2840 ClearSelection( true /*quiet mode*/ );
2841
2842 // Perform individual selection of each item before processing the event.
2843 for( BOARD_ITEM* item : aItems )
2844 select( item );
2845
2846 if( aWithNets )
2847 selectConnections( aItems );
2848
2849 BOX2I bbox = m_selection.GetBoundingBox();
2850
2851 if( bbox.GetWidth() != 0 && bbox.GetHeight() != 0 )
2852 {
2853 if( m_frame->GetPcbNewSettings()->m_CrossProbing.center_on_items )
2854 {
2855 if( m_frame->GetPcbNewSettings()->m_CrossProbing.zoom_to_fit )
2856 ZoomFitCrossProbeBBox( bbox );
2857
2858 m_frame->FocusOnLocation( bbox.Centre() );
2859 }
2860 }
2861
2863
2864 m_frame->GetCanvas()->ForceRefresh();
2865
2866 if( m_selection.Size() > 0 )
2867 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2868}
2869
2870
2872{
2873 ClearSelection( true /*quiet mode*/ );
2874 wxString sheetPath = *aEvent.Parameter<wxString*>();
2875
2876 selectAllItemsOnSheet( sheetPath );
2877
2879
2880 if( m_selection.Size() > 0 )
2881 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2882
2883 return 0;
2884}
2885
2886
2888{
2889 // this function currently only supports footprints since they are only on one sheet.
2890 EDA_ITEM* item = m_selection.Front();
2891
2892 if( !item )
2893 return 0;
2894
2895 if( item->Type() != PCB_FOOTPRINT_T )
2896 return 0;
2897
2898 FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( item );
2899
2900 if( !footprint || footprint->GetPath().empty() )
2901 return 0;
2902
2903 ClearSelection( true /*quiet mode*/ );
2904
2905 // get the sheet path only.
2906 wxString sheetPath = footprint->GetPath().AsString().BeforeLast( '/' );
2907
2908 if( sheetPath.IsEmpty() )
2909 sheetPath += '/';
2910
2911 selectAllItemsOnSheet( sheetPath );
2912
2913 // Inform other potentially interested tools
2914 if( m_selection.Size() > 0 )
2915 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2916
2917 return 0;
2918}
2919
2920
2922{
2923 // Should recalculate the view to zoom in on the selection.
2924 BOX2I selectionBox = m_selection.GetBoundingBox();
2926
2927 VECTOR2D screenSize = view->ToWorld( ToVECTOR2D( m_frame->GetCanvas()->GetClientSize() ),
2928 false );
2929 screenSize.x = std::max( 10.0, screenSize.x );
2930 screenSize.y = std::max( 10.0, screenSize.y );
2931
2932 if( selectionBox.GetWidth() != 0 || selectionBox.GetHeight() != 0 )
2933 {
2934 VECTOR2D vsize = selectionBox.GetSize();
2935 double scale = view->GetScale() / std::max( fabs( vsize.x / screenSize.x ),
2936 fabs( vsize.y / screenSize.y ) );
2937 view->SetScale( scale );
2938 view->SetCenter( selectionBox.Centre() );
2939 view->Add( &m_selection );
2940 }
2941
2942 m_frame->GetCanvas()->ForceRefresh();
2943}
2944
2945
2947{
2948 // Should recalculate the view to zoom in on the bbox.
2950
2951 if( aBBox.GetWidth() == 0 )
2952 return;
2953
2954 BOX2I bbox = aBBox;
2955 bbox.Normalize();
2956
2957 //#define DEFAULT_PCBNEW_CODE // Un-comment for normal full zoom KiCad algorithm
2958#ifdef DEFAULT_PCBNEW_CODE
2959 auto bbSize = bbox.Inflate( bbox.GetWidth() * 0.2f ).GetSize();
2960 auto screenSize = view->ToWorld( GetCanvas()->GetClientSize(), false );
2961
2962 // The "fabs" on x ensures the right answer when the view is flipped
2963 screenSize.x = std::max( 10.0, fabs( screenSize.x ) );
2964 screenSize.y = std::max( 10.0, screenSize.y );
2965 double ratio = std::max( fabs( bbSize.x / screenSize.x ), fabs( bbSize.y / screenSize.y ) );
2966
2967 // Try not to zoom on every cross-probe; it gets very noisy
2968 if( crossProbingSettings.zoom_to_fit && ( ratio < 0.5 || ratio > 1.0 ) )
2969 view->SetScale( view->GetScale() / ratio );
2970#endif // DEFAULT_PCBNEW_CODE
2971
2972#ifndef DEFAULT_PCBNEW_CODE // Do the scaled zoom
2973 auto bbSize = bbox.Inflate( KiROUND( bbox.GetWidth() * 0.2 ) ).GetSize();
2974 VECTOR2D screenSize = view->ToWorld( ToVECTOR2D( m_frame->GetCanvas()->GetClientSize() ), false );
2975
2976 // This code tries to come up with a zoom factor that doesn't simply zoom in
2977 // to the cross probed component, but instead shows a reasonable amount of the
2978 // circuit around it to provide context. This reduces or eliminates the need
2979 // to manually change the zoom because it's too close.
2980
2981 // Using the default text height as a constant to compare against, use the
2982 // height of the bounding box of visible items for a footprint to figure out
2983 // if this is a big footprint (like a processor) or a small footprint (like a resistor).
2984 // This ratio is not useful by itself as a scaling factor. It must be "bent" to
2985 // provide good scaling at varying component sizes. Bigger components need less
2986 // scaling than small ones.
2987 double currTextHeight = pcbIUScale.mmToIU( DEFAULT_TEXT_SIZE );
2988
2989 double compRatio = bbSize.y / currTextHeight; // Ratio of component to text height
2990
2991 // This will end up as the scaling factor we apply to "ratio".
2992 double compRatioBent = 1.0;
2993
2994 // This is similar to the original KiCad code that scaled the zoom to make sure
2995 // components were visible on screen. It's simply a ratio of screen size to
2996 // component size, and its job is to zoom in to make the component fullscreen.
2997 // Earlier in the code the component BBox is given a 20% margin to add some
2998 // breathing room. We compare the height of this enlarged component bbox to the
2999 // default text height. If a component will end up with the sides clipped, we
3000 // adjust later to make sure it fits on screen.
3001 //
3002 // The "fabs" on x ensures the right answer when the view is flipped
3003 screenSize.x = std::max( 10.0, fabs( screenSize.x ) );
3004 screenSize.y = std::max( 10.0, screenSize.y );
3005 double ratio = std::max( -1.0, fabs( bbSize.y / screenSize.y ) );
3006
3007 // Original KiCad code for how much to scale the zoom
3008 double kicadRatio = std::max( fabs( bbSize.x / screenSize.x ),
3009 fabs( bbSize.y / screenSize.y ) );
3010
3011 // LUT to scale zoom ratio to provide reasonable schematic context. Must work
3012 // with footprints of varying sizes (e.g. 0402 package and 200 pin BGA).
3013 // "first" is used as the input and "second" as the output
3014 //
3015 // "first" = compRatio (footprint height / default text height)
3016 // "second" = Amount to scale ratio by
3017 std::vector<std::pair<double, double>> lut {
3018 { 1, 8 },
3019 { 1.5, 5 },
3020 { 3, 3 },
3021 { 4.5, 2.5 },
3022 { 8, 2.0 },
3023 { 12, 1.7 },
3024 { 16, 1.5 },
3025 { 24, 1.3 },
3026 { 32, 1.0 },
3027 };
3028
3029
3030 std::vector<std::pair<double, double>>::iterator it;
3031
3032 compRatioBent = lut.back().second; // Large component default
3033
3034 if( compRatio >= lut.front().first )
3035 {
3036 // Use LUT to do linear interpolation of "compRatio" within "first", then
3037 // use that result to linearly interpolate "second" which gives the scaling
3038 // factor needed.
3039
3040 for( it = lut.begin(); it < lut.end() - 1; it++ )
3041 {
3042 if( it->first <= compRatio && next( it )->first >= compRatio )
3043 {
3044 double diffx = compRatio - it->first;
3045 double diffn = next( it )->first - it->first;
3046
3047 compRatioBent = it->second + ( next( it )->second - it->second ) * diffx / diffn;
3048 break; // We have our interpolated value
3049 }
3050 }
3051 }
3052 else
3053 {
3054 compRatioBent = lut.front().second; // Small component default
3055 }
3056
3057 // If the width of the part we're probing is bigger than what the screen width will be
3058 // after the zoom, then punt and use the KiCad zoom algorithm since it guarantees the
3059 // part's width will be encompassed within the screen. This will apply to parts that
3060 // are much wider than they are tall.
3061
3062 if( bbSize.x > screenSize.x * ratio * compRatioBent )
3063 {
3064 // Use standard KiCad zoom algorithm for parts too wide to fit screen/
3065 ratio = kicadRatio;
3066 compRatioBent = 1.0; // Reset so we don't modify the "KiCad" ratio
3067 wxLogTrace( "CROSS_PROBE_SCALE", "Part TOO WIDE for screen. Using normal KiCad zoom ratio: %1.5f", ratio );
3068 }
3069
3070 // Now that "compRatioBent" holds our final scaling factor we apply it to the original
3071 // fullscreen zoom ratio to arrive at the final ratio itself.
3072 ratio *= compRatioBent;
3073
3074 bool alwaysZoom = false; // DEBUG - allows us to minimize zooming or not
3075
3076 // Try not to zoom on every cross-probe; it gets very noisy
3077 if( ( ratio < 0.5 || ratio > 1.0 ) || alwaysZoom )
3078 view->SetScale( view->GetScale() / ratio );
3079#endif // ifndef DEFAULT_PCBNEW_CODE
3080}
3081
3082
3084{
3085 bool cleared = false;
3086
3087 if( m_selection.GetSize() > 0 )
3088 {
3089 // Don't fire an event now; most of the time it will be redundant as we're about to
3090 // fire a SelectedEvent.
3091 cleared = true;
3092 ClearSelection( true /*quiet mode*/ );
3093 }
3094
3095 if( aItem )
3096 {
3097 switch( aItem->Type() )
3098 {
3099 case PCB_NETINFO_T:
3100 {
3101 int netCode = static_cast<NETINFO_ITEM*>( aItem )->GetNetCode();
3102
3103 if( netCode > 0 )
3104 {
3105 SelectAllItemsOnNet( netCode, true );
3106 m_frame->FocusOnLocation( aItem->GetCenter() );
3107 }
3108 break;
3109 }
3110
3111 default:
3112 select( aItem );
3113 m_frame->FocusOnLocation( aItem->GetPosition() );
3114 }
3115
3116 // If the item has a bounding box, then zoom out if needed
3117 if( aItem->GetBoundingBox().GetHeight() > 0 && aItem->GetBoundingBox().GetWidth() > 0 )
3118 {
3119 // This adds some margin
3120 double marginFactor = 2;
3121
3122 KIGFX::PCB_VIEW* pcbView = canvas()->GetView();
3123 BOX2D screenBox = pcbView->GetViewport();
3124 VECTOR2D screenSize = screenBox.GetSize();
3125 BOX2I screenRect = BOX2ISafe( screenBox.GetOrigin(), screenSize / marginFactor );
3126
3127 if( !screenRect.Contains( aItem->GetBoundingBox() ) )
3128 {
3129 double scaleX = screenSize.x / static_cast<double>( aItem->GetBoundingBox().GetWidth() );
3130 double scaleY = screenSize.y / static_cast<double>( aItem->GetBoundingBox().GetHeight() );
3131
3132 scaleX /= marginFactor;
3133 scaleY /= marginFactor;
3134
3135 double scale = scaleX > scaleY ? scaleY : scaleX;
3136
3137 if( scale < 1 ) // Don't zoom in, only zoom out
3138 {
3139 pcbView->SetScale( pcbView->GetScale() * ( scale ) );
3140
3141 //Let's refocus because there is an algorithm to avoid dialogs in there.
3142 m_frame->FocusOnLocation( aItem->GetCenter() );
3143 }
3144 }
3145 }
3146 // Inform other potentially interested tools
3147 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3148 }
3149 else if( cleared )
3150 {
3151 m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
3152 }
3153
3154 m_frame->GetCanvas()->ForceRefresh();
3155}
3156
3157
3163static bool itemIsIncludedByFilter( const BOARD_ITEM& aItem, const BOARD& aBoard,
3164 const DIALOG_FILTER_SELECTION::OPTIONS& aFilterOptions )
3165{
3166 switch( aItem.Type() )
3167 {
3168 case PCB_FOOTPRINT_T:
3169 {
3170 const FOOTPRINT& footprint = static_cast<const FOOTPRINT&>( aItem );
3171
3172 return aFilterOptions.includeFootprints && ( aFilterOptions.includeLockedFootprints
3173 || !footprint.IsLocked() );
3174 }
3175
3176 case PCB_TRACE_T:
3177 case PCB_ARC_T:
3178 return aFilterOptions.includeTracks;
3179
3180 case PCB_VIA_T:
3181 return aFilterOptions.includeVias;
3182
3183 case PCB_ZONE_T:
3184 return aFilterOptions.includeZones;
3185
3186 case PCB_SHAPE_T:
3187 case PCB_TARGET_T:
3188 case PCB_DIM_ALIGNED_T:
3189 case PCB_DIM_CENTER_T:
3190 case PCB_DIM_RADIAL_T:
3192 case PCB_DIM_LEADER_T:
3193 if( aItem.GetLayer() == Edge_Cuts )
3194 return aFilterOptions.includeBoardOutlineLayer;
3195 else
3196 return aFilterOptions.includeItemsOnTechLayers;
3197
3198 case PCB_FIELD_T:
3199 case PCB_TEXT_T:
3200 case PCB_TEXTBOX_T:
3201 case PCB_TABLE_T:
3202 case PCB_TABLECELL_T:
3203 return aFilterOptions.includePcbTexts;
3204
3205 default:
3206 // Filter dialog is inclusive, not exclusive. If it's not included, then it doesn't
3207 // get selected.
3208 return false;
3209 }
3210}
3211
3212
3214{
3215 const BOARD& board = *getModel<BOARD>();
3216 DIALOG_FILTER_SELECTION::OPTIONS& opts = m_priv->m_filterOpts;
3217 DIALOG_FILTER_SELECTION dlg( m_frame, opts );
3218
3219 const int cmd = dlg.ShowModal();
3220
3221 if( cmd != wxID_OK )
3222 return 0;
3223
3224 // copy current selection
3225 std::deque<EDA_ITEM*> selection = m_selection.GetItems();
3226
3227 ClearSelection( true /*quiet mode*/ );
3228
3229 // re-select items from the saved selection according to the dialog options
3230 for( EDA_ITEM* i : selection )
3231 {
3232 if( !i->IsBOARD_ITEM() )
3233 continue;
3234
3235 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
3236 bool include = itemIsIncludedByFilter( *item, board, opts );
3237
3238 if( include )
3239 select( item );
3240 }
3241
3242 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3243
3244 return 0;
3245}
3246
3247
3249 PCB_SELECTION_FILTER_OPTIONS* aRejected )
3250{
3251 if( aCollector.GetCount() == 0 )
3252 return;
3253
3254 std::set<BOARD_ITEM*> rejected;
3255
3256 for( EDA_ITEM* i : aCollector )
3257 {
3258 if( !i->IsBOARD_ITEM() )
3259 continue;
3260
3261 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
3262
3263 if( !itemPassesFilter( item, aMultiSelect, aRejected ) )
3264 rejected.insert( item );
3265 }
3266
3267 for( BOARD_ITEM* item : rejected )
3268 aCollector.Remove( item );
3269}
3270
3271
3272bool PCB_SELECTION_TOOL::itemPassesFilter( BOARD_ITEM* aItem, bool aMultiSelect,
3273 PCB_SELECTION_FILTER_OPTIONS* aRejected )
3274{
3275 if( !m_filter.lockedItems )
3276 {
3277 if( aItem->IsLocked() || ( aItem->GetParent() && aItem->GetParent()->IsLocked() ) )
3278 {
3279 if( aItem->Type() == PCB_PAD_T && !aMultiSelect )
3280 {
3281 // allow a single pad to be selected -- there are a lot of operations that
3282 // require this so we allow this one inconsistency
3283 }
3284 else
3285 {
3286 if( aRejected )
3287 aRejected->lockedItems = true;
3288 return false;
3289 }
3290 }
3291 }
3292
3293 if( !aItem )
3294 return false;
3295
3296 KICAD_T itemType = aItem->Type();
3297
3298 if( itemType == PCB_GENERATOR_T )
3299 {
3300 if( static_cast<PCB_GENERATOR*>( aItem )->GetItems().empty() )
3301 {
3302 if( !m_filter.otherItems )
3303 {
3304 if( aRejected )
3305 aRejected->otherItems = true;
3306
3307 return false;
3308 }
3309 }
3310 else
3311 {
3312 itemType = ( *static_cast<PCB_GENERATOR*>( aItem )->GetItems().begin() )->Type();
3313 }
3314 }
3315
3316 switch( itemType )
3317 {
3318 case PCB_FOOTPRINT_T:
3319 if( !m_filter.footprints )
3320 {
3321 if( aRejected )
3322 aRejected->footprints = true;
3323
3324 return false;
3325 }
3326
3327 break;
3328
3329 case PCB_PAD_T:
3330 if( !m_filter.pads )
3331 {
3332 if( aRejected )
3333 aRejected->pads = true;
3334
3335 return false;
3336 }
3337
3338 break;
3339
3340 case PCB_TRACE_T:
3341 case PCB_ARC_T:
3342 if( !m_filter.tracks )
3343 {
3344 if( aRejected )
3345 aRejected->tracks = true;
3346
3347 return false;
3348 }
3349
3350 break;
3351
3352 case PCB_VIA_T:
3353 if( !m_filter.vias )
3354 {
3355 if( aRejected )
3356 aRejected->vias = true;
3357
3358 return false;
3359 }
3360
3361 break;
3362
3363 case PCB_ZONE_T:
3364 {
3365 ZONE* zone = static_cast<ZONE*>( aItem );
3366
3367 if( ( !m_filter.zones && !zone->GetIsRuleArea() )
3368 || ( !m_filter.keepouts && zone->GetIsRuleArea() ) )
3369 {
3370 if( aRejected )
3371 {
3372 if( zone->GetIsRuleArea() )
3373 aRejected->keepouts = true;
3374 else
3375 aRejected->zones = true;
3376 }
3377
3378 return false;
3379 }
3380
3381 // m_SolderMaskBridges zone is a special zone, only used to showsolder mask briges
3382 // after running DRC. it is not really a board item.
3383 // Never select it or delete by a Commit.
3384 if( zone == m_frame->GetBoard()->m_SolderMaskBridges )
3385 return false;
3386
3387 break;
3388 }
3389
3390 case PCB_SHAPE_T:
3391 case PCB_TARGET_T:
3392 if( !m_filter.graphics )
3393 {
3394 if( aRejected )
3395 aRejected->graphics = true;
3396
3397 return false;
3398 }
3399
3400 break;
3401
3403 if( !m_filter.graphics )
3404 {
3405 if( aRejected )
3406 aRejected->graphics = true;
3407
3408 return false;
3409 }
3410
3411 // a reference image living in a footprint must not be selected inside the board editor
3412 if( !m_isFootprintEditor && aItem->GetParentFootprint() )
3413 {
3414 if( aRejected )
3415 aRejected->text = true;
3416
3417 return false;
3418 }
3419
3420 break;
3421
3422 case PCB_FIELD_T:
3423 case PCB_TEXT_T:
3424 case PCB_TEXTBOX_T:
3425 case PCB_TABLE_T:
3426 case PCB_TABLECELL_T:
3427 if( !m_filter.text )
3428 return false;
3429
3430 break;
3431
3432 case PCB_DIM_ALIGNED_T:
3433 case PCB_DIM_CENTER_T:
3434 case PCB_DIM_RADIAL_T:
3436 case PCB_DIM_LEADER_T:
3437 if( !m_filter.dimensions )
3438 {
3439 if( aRejected )
3440 aRejected->dimensions = true;
3441
3442 return false;
3443 }
3444
3445 break;
3446
3447 case PCB_POINT_T:
3448 if( !m_filter.points )
3449 {
3450 if( aRejected )
3451 aRejected->points = true;
3452
3453 return false;
3454 }
3455
3456 break;
3457
3458 case PCB_BARCODE_T:
3459 default:
3460 if( !m_filter.otherItems )
3461 {
3462 if( aRejected )
3463 aRejected->otherItems = true;
3464
3465 return false;
3466 }
3467 }
3468
3469 return true;
3470}
3471
3472
3474{
3475 if( m_selection.Empty() )
3476 return;
3477
3478 while( m_selection.GetSize() )
3480
3481 view()->Update( &m_selection );
3482
3483 m_selection.SetIsHover( false );
3484 m_selection.ClearReferencePoint();
3485
3486 // Inform other potentially interested tools
3487 if( !aQuietMode )
3488 {
3489 m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
3491 }
3492}
3493
3494
3496{
3497 m_selection.Clear();
3498
3499 bool enteredGroupFound = false;
3500
3501 INSPECTOR_FUNC inspector =
3502 [&]( EDA_ITEM* item, void* testData )
3503 {
3504 if( item->IsSelected() )
3505 {
3506 EDA_ITEM* parent = item->GetParent();
3507
3508 // Let selected parents handle their children.
3509 if( parent && parent->IsSelected() )
3511
3512 highlight( item, SELECTED, &m_selection );
3513 }
3514
3515 if( item->Type() == PCB_GROUP_T )
3516 {
3517 if( item == m_enteredGroup )
3518 {
3519 item->SetFlags( ENTERED );
3520 enteredGroupFound = true;
3521 }
3522 else
3523 {
3524 item->ClearFlags( ENTERED );
3525 }
3526 }
3527
3529 };
3530
3533
3534 if( !enteredGroupFound )
3535 {
3536 m_enteredGroupOverlay.Clear();
3537 m_enteredGroup = nullptr;
3538 }
3539}
3540
3541
3542bool PCB_SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibilityOnly ) const
3543{
3544 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
3545 const PCB_DISPLAY_OPTIONS& options = frame()->GetDisplayOptions();
3546
3547 auto visibleLayers =
3548 [&]() -> LSET
3549 {
3551 {
3552 LSET set;
3553
3554 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
3555 set.set( layer, view()->IsLayerVisible( layer ) );
3556
3557 return set;
3558 }
3559 else
3560 {
3561 return board()->GetVisibleLayers();
3562 }
3563 };
3564
3565 auto layerVisible =
3566 [&]( PCB_LAYER_ID aLayer )
3567 {
3569 return view()->IsLayerVisible( aLayer );
3570 else
3571 return board()->IsLayerVisible( aLayer );
3572 };
3573
3574 if( settings->GetHighContrast() )
3575 {
3576 const std::set<int> activeLayers = settings->GetHighContrastLayers();
3577 bool onActiveLayer = false;
3578
3579 for( int layer : activeLayers )
3580 {
3581 // NOTE: Only checking the regular layers (not GAL meta-layers)
3582 if( layer < PCB_LAYER_ID_COUNT && aItem->IsOnLayer( ToLAYER_ID( layer ) ) )
3583 {
3584 onActiveLayer = true;
3585 break;
3586 }
3587 }
3588
3589 if( !onActiveLayer && aItem->Type() != PCB_MARKER_T )
3590 {
3591 // We do not want to select items that are in the background
3592 return false;
3593 }
3594 }
3595
3596 if( aItem->Type() == PCB_FOOTPRINT_T )
3597 {
3598 const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
3599
3600 // In footprint editor, we do not want to select the footprint itself.
3602 return false;
3603
3604 // If the footprint has no items except the reference and value fields, include the
3605 // footprint in the selections.
3606 if( footprint->GraphicalItems().empty()
3607 && footprint->Pads().empty()
3608 && footprint->Zones().empty() )
3609 {
3610 return true;
3611 }
3612
3613 for( const BOARD_ITEM* item : footprint->GraphicalItems() )
3614 {
3615 if( Selectable( item, true ) )
3616 return true;
3617 }
3618
3619 for( const PAD* pad : footprint->Pads() )
3620 {
3621 if( Selectable( pad, true ) )
3622 return true;
3623 }
3624
3625 for( const ZONE* zone : footprint->Zones() )
3626 {
3627 if( Selectable( zone, true ) )
3628 return true;
3629 }
3630
3631 for( const PCB_POINT* point: footprint->Points() )
3632 {
3633 if( Selectable( point, true ) )
3634 return true;
3635 }
3636
3637 return false;
3638 }
3639 else if( aItem->Type() == PCB_GROUP_T )
3640 {
3641 PCB_GROUP* group = const_cast<PCB_GROUP*>( static_cast<const PCB_GROUP*>( aItem ) );
3642
3643 // Similar to logic for footprint, a group is selectable if any of its members are.
3644 // (This recurses.)
3645 for( BOARD_ITEM* item : group->GetBoardItems() )
3646 {
3647 if( Selectable( item, true ) )
3648 return true;
3649 }
3650
3651 return false;
3652 }
3653
3654 if( aItem->GetParentGroup() && aItem->GetParentGroup()->AsEdaItem()->Type() == PCB_GENERATOR_T )
3655 return false;
3656
3657 const ZONE* zone = nullptr;
3658 const PCB_VIA* via = nullptr;
3659 const PAD* pad = nullptr;
3660 const PCB_TEXT* text = nullptr;
3661 const PCB_FIELD* field = nullptr;
3662 const PCB_MARKER* marker = nullptr;
3663 const PCB_TABLECELL* cell = nullptr;
3664
3665 // Most footprint children can only be selected in the footprint editor.
3666 if( aItem->GetParentFootprint() && !m_isFootprintEditor && !checkVisibilityOnly )
3667 {
3668 if( aItem->Type() != PCB_FIELD_T && aItem->Type() != PCB_PAD_T && aItem->Type() != PCB_TEXT_T )
3669 return false;
3670 }
3671
3672 switch( aItem->Type() )
3673 {
3674 case PCB_ZONE_T:
3675 if( !board()->IsElementVisible( LAYER_ZONES ) || ( options.m_ZoneOpacity == 0.00 ) )
3676 return false;
3677
3678 zone = static_cast<const ZONE*>( aItem );
3679
3680 // A teardrop is modelled as a property of a via, pad or the board (for track-to-track
3681 // teardrops). The underlying zone is only an implementation detail.
3682 if( zone->IsTeardropArea() && !board()->LegacyTeardrops() )
3683 return false;
3684
3685 // zones can exist on multiple layers!
3686 if( !( zone->GetLayerSet() & visibleLayers() ).any() )
3687 return false;
3688
3689 break;
3690
3691 case PCB_TRACE_T:
3692 case PCB_ARC_T:
3693 if( !board()->IsElementVisible( LAYER_TRACKS ) || ( options.m_TrackOpacity == 0.00 ) )
3694 return false;
3695
3696 if( !layerVisible( aItem->GetLayer() ) )
3697 return false;
3698
3699 break;
3700
3701 case PCB_VIA_T:
3702 if( !board()->IsElementVisible( LAYER_VIAS ) || ( options.m_ViaOpacity == 0.00 ) )
3703 return false;
3704
3705 via = static_cast<const PCB_VIA*>( aItem );
3706
3707 // For vias it is enough if only one of its layers is visible
3708 if( !( visibleLayers() & via->GetLayerSet() ).any() )
3709 return false;
3710
3711 break;
3712
3713 case PCB_FIELD_T:
3714 field = static_cast<const PCB_FIELD*>( aItem );
3715
3716 if( !field->IsVisible() )
3717 return false;
3718
3719 if( field->IsReference() && !view()->IsLayerVisible( LAYER_FP_REFERENCES ) )
3720 return false;
3721
3722 if( field->IsValue() && !view()->IsLayerVisible( LAYER_FP_VALUES ) )
3723 return false;
3724
3725 // Handle all other fields with normal text visibility controls
3727 case PCB_TEXT_T:
3728 text = static_cast<const PCB_TEXT*>( aItem );
3729
3730 if( !layerVisible( text->GetLayer() ) )
3731 return false;
3732
3733 // Apply the LOD visibility test as well
3734 if( !view()->IsVisible( text ) )
3735 return false;
3736
3737 if( aItem->GetParentFootprint() )
3738 {
3739 int controlLayer = LAYER_FP_TEXT;
3740
3741 if( text->GetText() == wxT( "${REFERENCE}" ) )
3742 controlLayer = LAYER_FP_REFERENCES;
3743 else if( text->GetText() == wxT( "${VALUE}" ) )
3744 controlLayer = LAYER_FP_VALUES;
3745
3746 if( !view()->IsLayerVisible( controlLayer ) )
3747 return false;
3748 }
3749
3750 break;
3751
3753 if( options.m_ImageOpacity == 0.00 )
3754 return false;
3755
3756 // Bitmap images on board are hidden if LAYER_DRAW_BITMAPS is not visible
3757 if( !view()->IsLayerVisible( LAYER_DRAW_BITMAPS ) )
3758 return false;
3759
3760 if( !layerVisible( aItem->GetLayer() ) )
3761 return false;
3762
3763 break;
3764
3765 case PCB_SHAPE_T:
3766 if( options.m_FilledShapeOpacity == 0.0 && static_cast<const PCB_SHAPE*>( aItem )->IsAnyFill() )
3767 return false;
3768
3769 if( !layerVisible( aItem->GetLayer() ) )
3770 return false;
3771
3772 break;
3773
3774 case PCB_BARCODE_T:
3775 if( !layerVisible( aItem->GetLayer() ) )
3776 return false;
3777
3778 break;
3779
3780 case PCB_TEXTBOX_T:
3781 case PCB_TABLE_T:
3782 if( !layerVisible( aItem->GetLayer() ) )
3783 return false;
3784
3785 break;
3786
3787 case PCB_TABLECELL_T:
3788 cell = static_cast<const PCB_TABLECELL*>( aItem );
3789
3790 if( !layerVisible( aItem->GetLayer() ) )
3791 return false;
3792
3793 if( cell->GetRowSpan() == 0 || cell->GetColSpan() == 0 )
3794 return false;
3795
3796 break;
3797
3798 case PCB_DIM_ALIGNED_T:
3799 case PCB_DIM_LEADER_T:
3800 case PCB_DIM_CENTER_T:
3801 case PCB_DIM_RADIAL_T:
3803 if( !layerVisible( aItem->GetLayer() ) )
3804 return false;
3805
3806 break;
3807
3808 case PCB_PAD_T:
3809 if( options.m_PadOpacity == 0.00 )
3810 return false;
3811
3812 pad = static_cast<const PAD*>( aItem );
3813
3814 if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
3815 {
3816 // A pad's hole is visible on every layer the pad is visible on plus many layers the
3817 // pad is not visible on -- so we only need to check for any visible hole layers.
3818 if( !( visibleLayers() & LSET::PhysicalLayersMask() ).any() )
3819 return false;
3820 }
3821 else
3822 {
3823 if( !( pad->GetLayerSet() & visibleLayers() ).any() )
3824 return false;
3825 }
3826
3827 break;
3828
3829 case PCB_MARKER_T:
3830 marker = static_cast<const PCB_MARKER*>( aItem );
3831
3832 if( marker && marker->IsExcluded() && !board()->IsElementVisible( LAYER_DRC_EXCLUSION ) )
3833 return false;
3834
3835 break;
3836
3837 case PCB_POINT_T:
3838 if( !layerVisible( aItem->GetLayer() ) )
3839 return false;
3840
3841 if( !board()->IsElementVisible( LAYER_POINTS ) )
3842 return false;
3843
3844 break;
3845
3846 // These are not selectable
3847 case PCB_NETINFO_T:
3848 case NOT_USED:
3849 case TYPE_NOT_INIT:
3850 return false;
3851
3852 default: // Suppress warnings
3853 break;
3854 }
3855
3856 return true;
3857}
3858
3859
3861{
3862 if( !aItem || aItem->IsSelected() || !aItem->IsBOARD_ITEM() )
3863 return;
3864
3865 if( aItem->Type() == PCB_PAD_T )
3866 {
3867 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem->GetParent() );
3868
3869 if( m_selection.Contains( footprint ) )
3870 return;
3871 }
3872
3873 if( m_enteredGroup && !PCB_GROUP::WithinScope( static_cast<BOARD_ITEM*>( aItem ), m_enteredGroup,
3875 {
3876 ExitGroup();
3877 }
3878
3879 highlight( aItem, SELECTED, &m_selection );
3880}
3881
3882
3884{
3885 unhighlight( aItem, SELECTED, &m_selection );
3886}
3887
3888
3889void PCB_SELECTION_TOOL::highlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
3890{
3891 if( aGroup )
3892 aGroup->Add( aItem );
3893
3894 highlightInternal( aItem, aMode, aGroup != nullptr );
3895 view()->Update( aItem, KIGFX::REPAINT );
3896
3897 // Many selections are very temporal and updating the display each time just
3898 // creates noise.
3899 if( aMode == BRIGHTENED )
3901}
3902
3903
3904void PCB_SELECTION_TOOL::highlightInternal( EDA_ITEM* aItem, int aMode, bool aUsingOverlay )
3905{
3906 if( aMode == SELECTED )
3907 aItem->SetSelected();
3908 else if( aMode == BRIGHTENED )
3909 aItem->SetBrightened();
3910
3911 if( aUsingOverlay && aMode != BRIGHTENED )
3912 view()->Hide( aItem, true ); // Hide the original item, so it is shown only on overlay
3913
3914 if( aItem->IsBOARD_ITEM() )
3915 {
3916 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( aItem );
3917 boardItem->RunOnChildren( std::bind( &PCB_SELECTION_TOOL::highlightInternal, this, _1, aMode, aUsingOverlay ),
3919 }
3920}
3921
3922
3923void PCB_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
3924{
3925 if( aGroup )
3926 aGroup->Remove( aItem );
3927
3928 unhighlightInternal( aItem, aMode, aGroup != nullptr );
3929 view()->Update( aItem, KIGFX::REPAINT );
3930
3931 // Many selections are very temporal and updating the display each time just creates noise.
3932 if( aMode == BRIGHTENED )
3934}
3935
3936
3937void PCB_SELECTION_TOOL::unhighlightInternal( EDA_ITEM* aItem, int aMode, bool aUsingOverlay )
3938{
3939 if( aMode == SELECTED )
3940 aItem->ClearSelected();
3941 else if( aMode == BRIGHTENED )
3942 aItem->ClearBrightened();
3943
3944 if( aUsingOverlay && aMode != BRIGHTENED )
3945 {
3946 view()->Hide( aItem, false ); // Restore original item visibility...
3947 view()->Update( aItem ); // ... and make sure it's redrawn un-selected
3948 }
3949
3950 if( aItem->IsBOARD_ITEM() )
3951 {
3952 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( aItem );
3953 boardItem->RunOnChildren( std::bind( &PCB_SELECTION_TOOL::unhighlightInternal, this, _1, aMode, aUsingOverlay ),
3955 }
3956}
3957
3958
3960{
3961 const unsigned GRIP_MARGIN = 20;
3962 int margin = KiROUND( getView()->ToWorld( GRIP_MARGIN ) );
3963
3964 // Check if the point is located close to any of the currently selected items
3965 for( EDA_ITEM* item : m_selection )
3966 {
3967 if( !item->IsBOARD_ITEM() )
3968 continue;
3969
3970 BOX2I itemBox = item->ViewBBox();
3971 itemBox.Inflate( margin ); // Give some margin for gripping an item
3972
3973 if( itemBox.Contains( aPoint ) )
3974 {
3975 if( item->HitTest( aPoint, margin ) )
3976 return true;
3977
3978 bool found = false;
3979
3980 if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( item ) )
3981 {
3982 group->RunOnChildren(
3983 [&]( BOARD_ITEM* aItem )
3984 {
3985 if( aItem->HitTest( aPoint, margin ) )
3986 found = true;
3987 },
3989 }
3990
3991 if( found )
3992 return true;
3993 }
3994 }
3995
3996 return false;
3997}
3998
3999
4000int PCB_SELECTION_TOOL::hitTestDistance( const VECTOR2I& aWhere, BOARD_ITEM* aItem, int aMaxDistance ) const
4001{
4002 BOX2D viewportD = getView()->GetViewport();
4003 BOX2I viewport = BOX2ISafe( viewportD );
4004 int distance = INT_MAX;
4005 SEG loc( aWhere, aWhere );
4006
4007 switch( aItem->Type() )
4008 {
4009 case PCB_FIELD_T:
4010 case PCB_TEXT_T:
4011 {
4012 PCB_TEXT* text = static_cast<PCB_TEXT*>( aItem );
4013
4014 // Add a bit of slop to text-shapes
4015 if( text->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance ) )
4016 distance = std::clamp( distance - ( aMaxDistance / 2 ), 0, distance );
4017
4018 break;
4019 }
4020
4021 case PCB_TEXTBOX_T:
4022 {
4023 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( aItem );
4024
4025 // Add a bit of slop to text-shapes
4026 if( textbox->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance ) )
4027 distance = std::clamp( distance - ( aMaxDistance / 2 ), 0, distance );
4028
4029 break;
4030 }
4031
4032 case PCB_TABLECELL_T:
4033 {
4034 PCB_TABLECELL* tablecell = static_cast<PCB_TABLECELL*>( aItem );
4035 auto shape = std::make_shared<SHAPE_COMPOUND>( tablecell->MakeEffectiveShapesForHitTesting() );
4036
4037 shape->Collide( loc, aMaxDistance, &distance );
4038
4039 break;
4040 }
4041
4042 case PCB_TABLE_T:
4043 {
4044 PCB_TABLE* table = static_cast<PCB_TABLE*>( aItem );
4045 distance = aMaxDistance;
4046
4047 for( PCB_TABLECELL* cell : table->GetCells() )
4048 distance = std::min( distance, hitTestDistance( aWhere, cell, aMaxDistance ) );
4049
4050 // Tables should defer to their table cells. Never consider them exact.
4051 distance = std::clamp( distance + ( aMaxDistance / 4 ), 0, aMaxDistance );
4052 break;
4053 }
4054
4055 case PCB_ZONE_T:
4056 {
4057 ZONE* zone = static_cast<ZONE*>( aItem );
4058
4059 // Zone borders are very specific
4060 if( zone->HitTestForEdge( aWhere, aMaxDistance / 2 ) )
4061 distance = 0;
4062 else if( zone->HitTestForEdge( aWhere, aMaxDistance ) )
4063 distance = aMaxDistance / 2;
4064 else
4065 aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
4066
4067 break;
4068 }
4069
4070 case PCB_FOOTPRINT_T:
4071 {
4072 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
4073 BOX2I bbox = footprint->GetBoundingBox( false );
4074
4075 try
4076 {
4077 footprint->GetBoundingHull().Collide( loc, aMaxDistance, &distance );
4078 }
4079 catch( const std::exception& e )
4080 {
4081 wxFAIL_MSG( wxString::Format( wxT( "Clipper exception occurred: %s" ), e.what() ) );
4082 }
4083
4084 // Consider footprints larger than the viewport only as a last resort
4085 if( bbox.GetHeight() > viewport.GetHeight() || bbox.GetWidth() > viewport.GetWidth() )
4086 distance = INT_MAX / 2;
4087
4088 break;
4089 }
4090
4091 case PCB_MARKER_T:
4092 {
4093 PCB_MARKER* marker = static_cast<PCB_MARKER*>( aItem );
4094 SHAPE_LINE_CHAIN polygon;
4095
4096 marker->ShapeToPolygon( polygon );
4097 polygon.Move( marker->GetPos() );
4098 polygon.Collide( loc, aMaxDistance, &distance );
4099 break;
4100 }
4101
4102 case PCB_GROUP_T:
4103 case PCB_GENERATOR_T:
4104 {
4105 PCB_GROUP* group = static_cast<PCB_GROUP*>( aItem );
4106
4107 for( BOARD_ITEM* member : group->GetBoardItems() )
4108 distance = std::min( distance, hitTestDistance( aWhere, member, aMaxDistance ) );
4109
4110 break;
4111 }
4112
4113 case PCB_PAD_T:
4114 {
4115 static_cast<PAD*>( aItem )->Padstack().ForEachUniqueLayer(
4116 [&]( PCB_LAYER_ID aLayer )
4117 {
4118 int layerDistance = INT_MAX;
4119 aItem->GetEffectiveShape( aLayer )->Collide( loc, aMaxDistance, &layerDistance );
4120 distance = std::min( distance, layerDistance );
4121 } );
4122
4123 break;
4124 }
4125
4126 default:
4127 aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
4128 break;
4129 }
4130
4131 return distance;
4132}
4133
4134
4136{
4137 wxCHECK( m_frame, /* void */ );
4138
4139 if( aCollector.GetCount() < 2 )
4140 return;
4141
4142 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
4143
4144 wxCHECK( settings, /* void */ );
4145
4146 PCB_LAYER_ID activeLayer = m_frame->GetActiveLayer();
4147 LSET visibleLayers = m_frame->GetBoard()->GetVisibleLayers();
4148 LSET enabledLayers = m_frame->GetBoard()->GetEnabledLayers();
4149 LSEQ enabledLayerStack = enabledLayers.SeqStackupTop2Bottom( activeLayer );
4150
4151 wxCHECK( !enabledLayerStack.empty(), /* void */ );
4152
4153 auto isZoneFillKeepout =
4154 []( const BOARD_ITEM* aItem ) -> bool
4155 {
4156 if( aItem->Type() == PCB_ZONE_T )
4157 {
4158 const ZONE* zone = static_cast<const ZONE*>( aItem );
4159
4160 if( zone->GetIsRuleArea() && zone->GetDoNotAllowZoneFills() )
4161 return true;
4162 }
4163
4164 return false;
4165 };
4166
4167 std::vector<LAYER_OPACITY_ITEM> opacityStackup;
4168
4169 for( int i = 0; i < aCollector.GetCount(); i++ )
4170 {
4171 const BOARD_ITEM* item = aCollector[i];
4172
4173 LSET itemLayers = item->GetLayerSet() & enabledLayers & visibleLayers;
4174 LSEQ itemLayerSeq = itemLayers.Seq( enabledLayerStack );
4175
4176 for( PCB_LAYER_ID layer : itemLayerSeq )
4177 {
4178 COLOR4D color = settings->GetColor( item, layer );
4179
4180 if( color.a == 0 )
4181 continue;
4182
4183 LAYER_OPACITY_ITEM opacityItem;
4184
4185 opacityItem.m_Layer = layer;
4186 opacityItem.m_Opacity = color.a;
4187 opacityItem.m_Item = item;
4188
4189 if( isZoneFillKeepout( item ) )
4190 opacityItem.m_Opacity = 0.0;
4191
4192 opacityStackup.emplace_back( opacityItem );
4193 }
4194 }
4195
4196 std::sort( opacityStackup.begin(), opacityStackup.end(),
4197 [&]( const LAYER_OPACITY_ITEM& aLhs, const LAYER_OPACITY_ITEM& aRhs ) -> bool
4198 {
4199 int retv = enabledLayerStack.TestLayers( aLhs.m_Layer, aRhs.m_Layer );
4200
4201 if( retv )
4202 return retv > 0;
4203
4204 return aLhs.m_Opacity > aRhs.m_Opacity;
4205 } );
4206
4207 std::set<const BOARD_ITEM*> visibleItems;
4208 std::set<const BOARD_ITEM*> itemsToRemove;
4209 double minAlphaLimit = ADVANCED_CFG::GetCfg().m_PcbSelectionVisibilityRatio;
4210 double currentStackupOpacity = 0.0;
4212
4213 for( const LAYER_OPACITY_ITEM& opacityItem : opacityStackup )
4214 {
4215 if( lastVisibleLayer == PCB_LAYER_ID::UNDEFINED_LAYER )
4216 {
4217 currentStackupOpacity = opacityItem.m_Opacity;
4218 lastVisibleLayer = opacityItem.m_Layer;
4219 visibleItems.emplace( opacityItem.m_Item );
4220 continue;
4221 }
4222
4223 // Objects to ignore and fallback to the old selection behavior.
4224 auto ignoreItem =
4225 [&]()
4226 {
4227 const BOARD_ITEM* item = opacityItem.m_Item;
4228
4229 wxCHECK( item, false );
4230
4231 // Check items that span multiple layers for visibility.
4232 if( visibleItems.count( item ) )
4233 return true;
4234
4235 // Don't prune child items of a footprint that is already visible.
4236 if( item->GetParent()
4237 && ( item->GetParent()->Type() == PCB_FOOTPRINT_T )
4238 && visibleItems.count( item->GetParent() ) )
4239 {
4240 return true;
4241 }
4242
4243 // Keepout zones are transparent but for some reason, PCB_PAINTER::GetColor()
4244 // returns the color of the zone it prevents from filling.
4245 if( isZoneFillKeepout( item ) )
4246 return true;
4247
4248 return false;
4249 };
4250
4251 // Everything on the currently selected layer is visible;
4252 if( opacityItem.m_Layer == enabledLayerStack[0] )
4253 {
4254 visibleItems.emplace( opacityItem.m_Item );
4255 }
4256 else
4257 {
4258 double itemVisibility = opacityItem.m_Opacity * ( 1.0 - currentStackupOpacity );
4259
4260 if( ( itemVisibility <= minAlphaLimit ) && !ignoreItem() )
4261 itemsToRemove.emplace( opacityItem.m_Item );
4262 else
4263 visibleItems.emplace( opacityItem.m_Item );
4264 }
4265
4266 if( opacityItem.m_Layer != lastVisibleLayer )
4267 {
4268 currentStackupOpacity += opacityItem.m_Opacity * ( 1.0 - currentStackupOpacity );
4269 currentStackupOpacity = std::min( currentStackupOpacity, 1.0 );
4270 lastVisibleLayer = opacityItem.m_Layer;
4271 }
4272 }
4273
4274 for( const BOARD_ITEM* itemToRemove : itemsToRemove )
4275 {
4276 wxCHECK( aCollector.GetCount() > 1, /* void */ );
4277 aCollector.Remove( itemToRemove );
4278 }
4279}
4280
4281
4282// The general idea here is that if the user clicks directly on a small item inside a larger
4283// one, then they want the small item. The quintessential case of this is clicking on a pad
4284// within a footprint, but we also apply it for text within a footprint, footprints within
4285// larger footprints, and vias within either larger pads or longer tracks.
4286//
4287// These "guesses" presume there is area within the larger item to click in to select it. If
4288// an item is mostly covered by smaller items within it, then the guesses are inappropriate as
4289// there might not be any area left to click to select the larger item. In this case we must
4290// leave the items in the collector and bring up a Selection Clarification menu.
4291//
4292// We currently check for pads and text mostly covering a footprint, but we don't check for
4293// smaller footprints mostly covering a larger footprint.
4294//
4296 const VECTOR2I& aWhere ) const
4297{
4298 static const LSET silkLayers( { B_SilkS, F_SilkS } );
4299 static const LSET courtyardLayers( { B_CrtYd, F_CrtYd } );
4300 static std::vector<KICAD_T> singleLayerSilkTypes = { PCB_FIELD_T,
4304 PCB_BARCODE_T };
4305
4306 if( ADVANCED_CFG::GetCfg().m_PcbSelectionVisibilityRatio != 1.0 )
4308
4309 if( aCollector.GetCount() == 1 )
4310 return;
4311
4312 std::set<BOARD_ITEM*> preferred;
4313 std::set<BOARD_ITEM*> rejected;
4314 VECTOR2I where( aWhere.x, aWhere.y );
4315 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
4316 PCB_LAYER_ID activeLayer = m_frame->GetActiveLayer();
4317
4318 // If a silk layer is in front, we assume the user is working with silk and give preferential
4319 // treatment to single-layer items on *either* silk layer.
4320 if( silkLayers[activeLayer] )
4321 {
4322 for( int i = 0; i < aCollector.GetCount(); ++i )
4323 {
4324 BOARD_ITEM* item = aCollector[i];
4325
4326 if( item->IsType( singleLayerSilkTypes ) && silkLayers[ item->GetLayer() ] )
4327 preferred.insert( item );
4328 }
4329 }
4330 // Similarly, if a courtyard layer is in front, we assume the user is positioning footprints
4331 // and give preferential treatment to footprints on *both* top and bottom.
4332 else if( courtyardLayers[activeLayer] && settings->GetHighContrast() )
4333 {
4334 for( int i = 0; i < aCollector.GetCount(); ++i )
4335 {
4336 BOARD_ITEM* item = aCollector[i];
4337
4338 if( item->Type() == PCB_FOOTPRINT_T )
4339 preferred.insert( item );
4340 }
4341 }
4342
4343 if( preferred.size() > 0 )
4344 {
4345 aCollector.Empty();
4346
4347 for( BOARD_ITEM* item : preferred )
4348 aCollector.Append( item );
4349
4350 if( preferred.size() == 1 )
4351 return;
4352 }
4353
4354 // Prefer exact hits to sloppy ones
4355 constexpr int MAX_SLOP = 5;
4356
4357 int singlePixel = KiROUND( aCollector.GetGuide()->OnePixelInIU() );
4358 int maxSlop = KiROUND( MAX_SLOP * aCollector.GetGuide()->OnePixelInIU() );
4359 int minSlop = INT_MAX;
4360
4361 std::map<BOARD_ITEM*, int> itemsBySloppiness;
4362
4363 for( int i = 0; i < aCollector.GetCount(); ++i )
4364 {
4365 BOARD_ITEM* item = aCollector[i];
4366 int itemSlop = hitTestDistance( where, item, maxSlop );
4367
4368 itemsBySloppiness[ item ] = itemSlop;
4369
4370 if( itemSlop < minSlop )
4371 minSlop = itemSlop;
4372 }
4373
4374 // Prune sloppier items
4375 if( minSlop < INT_MAX )
4376 {
4377 for( std::pair<BOARD_ITEM*, int> pair : itemsBySloppiness )
4378 {
4379 if( pair.second > minSlop + singlePixel )
4380 aCollector.Transfer( pair.first );
4381 }
4382 }
4383
4384 // If the user clicked on a small item within a much larger one then it's pretty clear
4385 // they're trying to select the smaller one.
4386 constexpr double sizeRatio = 1.5;
4387
4388 std::vector<std::pair<BOARD_ITEM*, double>> itemsByArea;
4389
4390 for( int i = 0; i < aCollector.GetCount(); ++i )
4391 {
4392 BOARD_ITEM* item = aCollector[i];
4393 double area = 0.0;
4394
4395 if( item->Type() == PCB_ZONE_T
4396 && static_cast<ZONE*>( item )->HitTestForEdge( where, maxSlop / 2 ) )
4397 {
4398 // Zone borders are very specific, so make them "small"
4399 area = (double) SEG::Square( singlePixel ) * MAX_SLOP;
4400 }
4401 else if( item->Type() == PCB_VIA_T )
4402 {
4403 // Vias rarely hide other things, and we don't want them deferring to short track
4404 // segments underneath them -- so artificially reduce their size from πr² to r².
4405 area = (double) SEG::Square( static_cast<PCB_VIA*>( item )->GetDrill() / 2 );
4406 }
4407 else if( item->Type() == PCB_REFERENCE_IMAGE_T )
4408 {
4409 BOX2I box = item->GetBoundingBox();
4410 area = (double) box.GetWidth() * box.GetHeight();
4411 }
4412 else
4413 {
4414 try
4415 {
4416 area = FOOTPRINT::GetCoverageArea( item, aCollector );
4417 }
4418 catch( const std::exception& e )
4419 {
4420 wxFAIL_MSG( wxString::Format( wxT( "Clipper exception occurred: %s" ), e.what() ) );
4421 }
4422 }
4423
4424 itemsByArea.emplace_back( item, area );
4425 }
4426
4427 std::sort( itemsByArea.begin(), itemsByArea.end(),
4428 []( const std::pair<BOARD_ITEM*, double>& lhs,
4429 const std::pair<BOARD_ITEM*, double>& rhs ) -> bool
4430 {
4431 return lhs.second < rhs.second;
4432 } );
4433
4434 bool rejecting = false;
4435
4436 for( int i = 1; i < (int) itemsByArea.size(); ++i )
4437 {
4438 if( itemsByArea[i].second > itemsByArea[i-1].second * sizeRatio )
4439 rejecting = true;
4440
4441 if( rejecting )
4442 rejected.insert( itemsByArea[i].first );
4443 }
4444
4445 // Special case: if a footprint is completely covered with other features then there's no
4446 // way to select it -- so we need to leave it in the list for user disambiguation.
4447 constexpr double maxCoverRatio = 0.70;
4448
4449 for( int i = 0; i < aCollector.GetCount(); ++i )
4450 {
4451 if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aCollector[i] ) )
4452 {
4453 if( footprint->CoverageRatio( aCollector ) > maxCoverRatio )
4454 rejected.erase( footprint );
4455 }
4456 }
4457
4458 // Hopefully we've now got what the user wanted.
4459 if( (unsigned) aCollector.GetCount() > rejected.size() ) // do not remove everything
4460 {
4461 for( BOARD_ITEM* item : rejected )
4462 aCollector.Transfer( item );
4463 }
4464
4465 // Finally, what we are left with is a set of items of similar coverage area. We now reject
4466 // any that are not on the active layer, to reduce the number of disambiguation menus shown.
4467 // If the user wants to force-disambiguate, they can either switch layers or use the modifier
4468 // key to force the menu.
4469 if( aCollector.GetCount() > 1 )
4470 {
4471 bool haveItemOnActive = false;
4472 rejected.clear();
4473
4474 for( int i = 0; i < aCollector.GetCount(); ++i )
4475 {
4476 if( !aCollector[i]->IsOnLayer( activeLayer ) )
4477 rejected.insert( aCollector[i] );
4478 else
4479 haveItemOnActive = true;
4480 }
4481
4482 if( haveItemOnActive )
4483 {
4484 for( BOARD_ITEM* item : rejected )
4485 aCollector.Transfer( item );
4486 }
4487 }
4488}
4489
4490
4492{
4494 {
4495 m_frame->ShowInfoBarWarning( _( "Selection contains locked items. "
4496 "Enable 'Override locks' to operate on them." ),
4497 true );
4498 }
4499}
4500
4501
4503{
4504 m_lockedItemsFiltered = false;
4505
4506 if( m_frame && m_frame->IsType( FRAME_PCB_EDITOR ) && !m_frame->GetOverrideLocks() )
4507 {
4508 // Iterate from the back so we don't have to worry about removals.
4509 for( int i = (int) aCollector.GetCount() - 1; i >= 0; --i )
4510 {
4511 BOARD_ITEM* item = aCollector[i];
4512 bool lockedDescendant = false;
4513
4514 item->RunOnChildren(
4515 [&]( BOARD_ITEM* curr_item )
4516 {
4517 if( curr_item->IsLocked() )
4518 lockedDescendant = true;
4519 },
4521
4522 if( item->IsLocked() || lockedDescendant )
4523 {
4524 aCollector.Remove( item );
4525 m_lockedItemsFiltered = true;
4526 }
4527 }
4528 }
4529}
4530
4531
4532void PCB_SELECTION_TOOL::FilterCollectorForHierarchy( GENERAL_COLLECTOR& aCollector, bool aMultiselect ) const
4533{
4534 std::unordered_set<EDA_ITEM*> toAdd;
4535
4536 // Set CANDIDATE on all parents which are included in the GENERAL_COLLECTOR. This
4537 // algorithm is O(3n), whereas checking for the parent inclusion could potentially be O(n^2).
4538 for( int j = 0; j < aCollector.GetCount(); j++ )
4539 {
4540 if( aCollector[j]->GetParent() )
4541 aCollector[j]->GetParent()->ClearFlags( CANDIDATE );
4542
4543 if( aCollector[j]->GetParentFootprint() )
4544 aCollector[j]->GetParentFootprint()->ClearFlags( CANDIDATE );
4545 }
4546
4547 if( aMultiselect )
4548 {
4549 for( int j = 0; j < aCollector.GetCount(); j++ )
4550 aCollector[j]->SetFlags( CANDIDATE );
4551 }
4552
4553 for( int j = 0; j < aCollector.GetCount(); )
4554 {
4555 BOARD_ITEM* item = aCollector[j];
4556 FOOTPRINT* fp = item->GetParentFootprint();
4557 BOARD_ITEM* start = item;
4558
4559 if( !m_isFootprintEditor && fp )
4560 start = fp;
4561
4562 // If a group is entered, disallow selections of objects outside the group.
4564 {
4565 aCollector.Remove( item );
4566 continue;
4567 }
4568
4569 // If any element is a member of a group, replace those elements with the top containing
4570 // group.
4572 {
4573 if( top->AsEdaItem() != item )
4574 {
4575 toAdd.insert( top->AsEdaItem() );
4576 top->AsEdaItem()->SetFlags( CANDIDATE );
4577
4578 aCollector.Remove( item );
4579 continue;
4580 }
4581 }
4582
4583 // Footprints are a bit easier as they can't be nested.
4584 if( fp && ( fp->GetFlags() & CANDIDATE ) )
4585 {
4586 // Remove children of selected items
4587 aCollector.Remove( item );
4588 continue;
4589 }
4590
4591 ++j;
4592 }
4593
4594 for( EDA_ITEM* item : toAdd )
4595 {
4596 if( !aCollector.HasItem( item ) )
4597 aCollector.Append( item );
4598 }
4599}
4600
4601
4603{
4604 std::set<BOARD_ITEM*> to_add;
4605
4606 // Iterate from the back so we don't have to worry about removals.
4607 for( int i = (int) aCollector.GetCount() - 1; i >= 0; --i )
4608 {
4609 BOARD_ITEM* item = aCollector[i];
4610
4611 if( item->Type() == PCB_TABLECELL_T )
4612 {
4613 if( !aCollector.HasItem( item->GetParent() ) )
4614 to_add.insert( item->GetParent() );
4615
4616 aCollector.Remove( item );
4617 }
4618 }
4619
4620 for( BOARD_ITEM* item : to_add )
4621 aCollector.Append( item );
4622}
4623
4624
4626 bool aForcePromotion ) const
4627{
4628 std::set<BOARD_ITEM*> to_add;
4629
4630 // Iterate from the back so we don't have to worry about removals.
4631 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
4632 {
4633 BOARD_ITEM* item = aCollector[i];
4634
4635 if( !m_isFootprintEditor && item->Type() == PCB_PAD_T
4636 && ( !frame()->GetPcbNewSettings()->m_AllowFreePads || aForcePromotion ) )
4637 {
4638 if( !aCollector.HasItem( item->GetParent() ) )
4639 to_add.insert( item->GetParent() );
4640
4641 aCollector.Remove( item );
4642 }
4643 }
4644
4645 for( BOARD_ITEM* item : to_add )
4646 aCollector.Append( item );
4647}
4648
4649
4651{
4652 // Iterate from the back so we don't have to worry about removals.
4653 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
4654 {
4655 BOARD_ITEM* item = aCollector[i];
4656
4657 if( item->Type() == PCB_MARKER_T )
4658 aCollector.Remove( item );
4659 }
4660}
4661
4662
4664 const VECTOR2I& aWhere ) const
4665{
4666 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
4667 BOX2D viewport = getView()->GetViewport();
4668 BOX2I extents = BOX2ISafe( viewport );
4669
4670 bool need_direct_hit = false;
4671 FOOTPRINT* single_fp = nullptr;
4672
4673 // If the designer is not modifying the existing selection AND we already have
4674 // a selection, then we only want to select items that are directly under the cursor.
4675 // This prevents us from being unable to clear the selection when zoomed into a footprint
4676 if( !m_additive && !m_subtractive && !m_exclusive_or && m_selection.GetSize() > 0 )
4677 {
4678 need_direct_hit = true;
4679
4680 for( EDA_ITEM* item : m_selection )
4681 {
4682 FOOTPRINT* fp = nullptr;
4683
4684 if( item->Type() == PCB_FOOTPRINT_T )
4685 fp = static_cast<FOOTPRINT*>( item );
4686 else if( item->IsBOARD_ITEM() )
4687 fp = static_cast<BOARD_ITEM*>( item )->GetParentFootprint();
4688
4689 // If the selection contains items that are not footprints, then don't restrict
4690 // whether we deselect the item or not.
4691 if( !fp )
4692 {
4693 single_fp = nullptr;
4694 break;
4695 }
4696 else if( !single_fp )
4697 {
4698 single_fp = fp;
4699 }
4700 // If the selection contains items from multiple footprints, then don't restrict
4701 // whether we deselect the item or not.
4702 else if( single_fp != fp )
4703 {
4704 single_fp = nullptr;
4705 break;
4706 }
4707 }
4708 }
4709
4710 auto visibleLayers =
4711 [&]() -> LSET
4712 {
4714 {
4715 LSET set;
4716
4717 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
4718 set.set( layer, view()->IsLayerVisible( layer ) );
4719
4720 return set;
4721 }
4722 else
4723 {
4724 return board()->GetVisibleLayers();
4725 }
4726 };
4727
4728 LSET layers = visibleLayers();
4729
4730 if( settings->GetHighContrast() )
4731 {
4732 layers.reset();
4733
4734 const std::set<int> activeLayers = settings->GetHighContrastLayers();
4735
4736 for( int layer : activeLayers )
4737 {
4738 if( layer >= 0 && layer < PCB_LAYER_ID_COUNT )
4739 layers.set( layer );
4740 }
4741 }
4742
4743 // Iterate from the back so we don't have to worry about removals.
4744 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
4745 {
4746 BOARD_ITEM* item = aCollector[i];
4747 FOOTPRINT* fp = dyn_cast<FOOTPRINT*>( item );
4748
4749 if( !fp )
4750 continue;
4751
4752 // Make footprints not difficult to select in high-contrast modes.
4753 if( layers[fp->GetLayer()] )
4754 continue;
4755
4756 BOX2I bbox = fp->GetLayerBoundingBox( layers );
4757
4758 // If the point clicked is not inside the visible bounding box, we can also remove it.
4759 if( !bbox.Contains( aWhere) )
4760 aCollector.Remove( item );
4761
4762 bool has_hit = false;
4763
4764 for( PCB_LAYER_ID layer : layers )
4765 {
4766 if( fp->HitTestOnLayer( extents, false, layer ) )
4767 {
4768 has_hit = true;
4769 break;
4770 }
4771 }
4772
4773 // If the point is outside of the visible bounding box, we can remove it.
4774 if( !has_hit )
4775 {
4776 aCollector.Remove( item );
4777 }
4778 // Do not require a direct hit on this fp if the existing selection only contains
4779 // this fp's items. This allows you to have a selection of pads from a single
4780 // footprint and still click in the center of the footprint to select it.
4781 else if( single_fp )
4782 {
4783 if( fp == single_fp )
4784 continue;
4785 }
4786 else if( need_direct_hit )
4787 {
4788 has_hit = false;
4789
4790 for( PCB_LAYER_ID layer : layers )
4791 {
4792 if( fp->HitTestOnLayer( aWhere, layer ) )
4793 {
4794 has_hit = true;
4795 break;
4796 }
4797 }
4798
4799 if( !has_hit )
4800 aCollector.Remove( item );
4801 }
4802 }
4803}
4804
4805
4807{
4808 getView()->Update( &m_selection );
4810
4811 return 0;
4812}
4813
4814
4816{
4817 std::set<std::pair<PCB_TABLE*, int>> columns;
4818 bool added = false;
4819
4820 for( EDA_ITEM* item : m_selection )
4821 {
4822 if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( item ) )
4823 {
4824 PCB_TABLE* table = static_cast<PCB_TABLE*>( cell->GetParent() );
4825 columns.insert( std::make_pair( table, cell->GetColumn() ) );
4826 }
4827 }
4828
4829 for( auto& [ table, col ] : columns )
4830 {
4831 for( int row = 0; row < table->GetRowCount(); ++row )
4832 {
4833 PCB_TABLECELL* cell = table->GetCell( row, col );
4834
4835 if( !cell->IsSelected() )
4836 {
4837 select( table->GetCell( row, col ) );
4838 added = true;
4839 }
4840 }
4841 }
4842
4843 if( added )
4844 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
4845
4846 return 0;
4847}
4848
4849
4851{
4852 std::set<std::pair<PCB_TABLE*, int>> rows;
4853 bool added = false;
4854
4855 for( EDA_ITEM* item : m_selection )
4856 {
4857 if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( item ) )
4858 {
4859 PCB_TABLE* table = static_cast<PCB_TABLE*>( cell->GetParent() );
4860 rows.insert( std::make_pair( table, cell->GetRow() ) );
4861 }
4862 }
4863
4864 for( auto& [ table, row ] : rows )
4865 {
4866 for( int col = 0; col < table->GetColCount(); ++col )
4867 {
4868 PCB_TABLECELL* cell = table->GetCell( row, col );
4869
4870 if( !cell->IsSelected() )
4871 {
4872 select( table->GetCell( row, col ) );
4873 added = true;
4874 }
4875 }
4876 }
4877
4878 if( added )
4879 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
4880
4881 return 0;
4882}
4883
4884
4886{
4887 std::set<PCB_TABLE*> tables;
4888 bool added = false;
4889
4890 for( EDA_ITEM* item : m_selection )
4891 {
4892 if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( item ) )
4893 tables.insert( static_cast<PCB_TABLE*>( cell->GetParent() ) );
4894 }
4895
4897
4898 for( PCB_TABLE* table : tables )
4899 {
4900 if( !table->IsSelected() )
4901 {
4902 select( table );
4903 added = true;
4904 }
4905 }
4906
4907 if( added )
4908 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
4909
4910 return 0;
4911}
4912
4913
4915{
4917
4921
4928
4947
4952
4954}
std::function< void(const VECTOR2I &, GENERAL_COLLECTOR &, PCB_SELECTION_TOOL *)> CLIENT_SELECTION_FILTER
Definition actions.h:37
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:125
constexpr BOX2I BOX2ISafe(const BOX2D &aInput)
Definition box2.h:929
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
BOX2< VECTOR2D > BOX2D
Definition box2.h:923
static TOOL_ACTION cancelInteractive
Definition actions.h:72
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 selectionCursor
Select a single item under the cursor position.
Definition actions.h:217
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 reselectItem
Definition actions.h:229
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.
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
BASE_SET & reset(size_t pos)
Definition base_set.h:143
BASE_SET & set(size_t pos)
Definition base_set.h:116
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
static bool ClassOf(const EDA_ITEM *aItem)
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
Tool for pcb inspection.
int ClearHighlight(const TOOL_EVENT &aEvent)
Perform the appropriate action in response to an Eeschema cross-probe.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:268
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition board_item.h:158
bool IsLocked() const override
virtual VECTOR2I GetCenter() const
This defaults to the center of the bounding box if not overridden.
Definition board_item.h:136
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
FOOTPRINT * GetParentFootprint() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:288
virtual void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const
Invoke a function on all children.
Definition board_item.h:232
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:234
virtual bool IsOnCopperLayer() const
Definition board_item.h:175
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
INSPECT_RESULT Visit(INSPECTOR inspector, void *testData, const std::vector< KICAD_T > &scanTypes) override
May be re-implemented for each derived class in order to handle all the types given by its member dat...
Definition board.cpp:2455
bool IsElementVisible(GAL_LAYER_ID aLayer) const
Test whether a given element category is visible.
Definition board.cpp:1052
const LSET & GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition board.cpp:1000
bool IsLayerVisible(PCB_LAYER_ID aLayer) const
A proxy function that calls the correspondent function in m_BoardSettings tests whether a given layer...
Definition board.cpp:992
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:571
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 size_type GetHeight() const
Definition box2.h:215
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:168
constexpr const Vec & GetOrigin() const
Definition box2.h:210
constexpr const SizeVec & GetSize() const
Definition box2.h:206
CN_ANCHOR represents a physical location that can be connected: a pad or a track/arc/via endpoint.
bool Dirty() const
BOARD_CONNECTED_ITEM * Parent() const
CN_EDGE represents a point-to-point connection, whether realized or unrealized (ie: tracks etc.
virtual double OnePixelInIU() const =0
void Transfer(int aIndex)
Move the item at aIndex (first position is 0) to the backup list.
Definition collector.h:153
void Empty()
Clear the list.
Definition collector.h:91
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
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 & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:90
int ShowModal() override
A set of EDA_ITEMs (i.e., without duplicates).
Definition eda_group.h:46
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:54
virtual EDA_ITEM * AsEdaItem()=0
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:100
virtual VECTOR2I GetPosition() const
Definition eda_item.h:279
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:120
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:149
const KIID m_Uuid
Definition eda_item.h:528
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:118
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
void ClearSelected()
Definition eda_item.h:144
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:151
bool IsSelected() const
Definition eda_item.h:129
void SetSelected()
Definition eda_item.h:141
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition eda_item.h:199
void ClearBrightened()
Definition eda_item.h:145
void SetBrightened()
Definition eda_item.h:142
virtual bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const
Test if aPosition is inside or on the boundary of this item.
Definition eda_item.h:240
EDA_ITEM * GetParent() const
Definition eda_item.h:114
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:153
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:152
SHAPE_T GetShape() const
Definition eda_shape.h:189
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:236
bool IsClosed() const
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:194
bool IsAnyFill() const
Definition eda_shape.h:132
virtual std::vector< SHAPE * > MakeEffectiveShapesForHitTesting() const
Definition eda_shape.h:462
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.
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 InhibitSelectionEditing
Definition actions.h:358
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 UninhibitSelectionEditing
Used to inform tool that it should display the disambiguation menu.
Definition actions.h:359
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
ZONES & Zones()
Definition footprint.h:383
PCB_POINTS & Points()
Definition footprint.h:389
static double GetCoverageArea(const BOARD_ITEM *aItem, const GENERAL_COLLECTOR &aCollector)
const BOX2I GetLayerBoundingBox(const LSET &aLayers) const
Return the bounding box of the footprint on a given set of layers.
std::deque< PAD * > & Pads()
Definition footprint.h:377
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition footprint.h:417
SHAPE_POLY_SET GetBoundingHull() const
Return a bounding polygon for the shapes and pads in the footprint.
bool IsLocked() const override
Definition footprint.h:622
bool HitTestOnLayer(const VECTOR2I &aPosition, PCB_LAYER_ID aLayer, int aAccuracy=0) const
Test if the point hits one or more of the footprint elements on a given layer.
const KIID_PATH & GetPath() const
Definition footprint.h:452
DRAWINGS & GraphicalItems()
Definition footprint.h:380
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
A general implementation of a COLLECTORS_GUIDE.
Definition collectors.h:324
void SetIgnoreBlindBuriedVias(bool ignore)
Definition collectors.h:464
void SetIgnoreTracks(bool ignore)
Definition collectors.h:470
void SetIgnoreFootprintsOnFront(bool ignore)
Definition collectors.h:428
void SetIgnoreFPTextOnFront(bool ignore)
Definition collectors.h:416
void SetIgnoreMicroVias(bool ignore)
Definition collectors.h:467
void SetIgnoreZoneFills(bool ignore)
Definition collectors.h:473
void SetIgnorePadsOnBack(bool ignore)
Definition collectors.h:434
void SetIgnoreFPTextOnBack(bool ignore)
Definition collectors.h:410
void SetLayerVisibleBits(const LSET &aLayerBits)
Definition collectors.h:384
void SetIgnoreThroughVias(bool ignore)
Definition collectors.h:461
void SetIgnoreThroughHolePads(bool ignore)
Definition collectors.h:446
void SetIgnoreFPReferences(bool ignore)
Definition collectors.h:458
void SetIgnoreFPValues(bool ignore)
Definition collectors.h:452
void SetIgnorePadsOnFront(bool ignore)
Definition collectors.h:440
void SetIgnoreFootprintsOnBack(bool ignore)
Definition collectors.h:422
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:207
void SetGuide(const COLLECTORS_GUIDE *aGuide)
Record which COLLECTORS_GUIDE to use.
Definition collectors.h:291
const COLLECTORS_GUIDE * GetGuide() const
Definition collectors.h:293
static const std::vector< KICAD_T > AllBoardItems
A scan list for all editable board items.
Definition collectors.h:41
void Collect(BOARD_ITEM *aItem, const std::vector< KICAD_T > &aScanList, const VECTOR2I &aRefPos, const COLLECTORS_GUIDE &aGuide)
Scan a BOARD_ITEM using this class's Inspector method, which does the collection.
static const std::vector< KICAD_T > FootprintItems
A scan list for primary footprint items.
Definition collectors.h:107
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
double a
Alpha component.
Definition color4d.h:396
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const override
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition pcb_view.cpp:91
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1) override
Add a VIEW_ITEM to the view.
Definition pcb_view.cpp:57
virtual void Remove(VIEW_ITEM *aItem) override
Remove a VIEW_ITEM from the view.
Definition pcb_view.cpp:74
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.
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
const std::set< int > & GetHighlightNetCodes() const
Return the netcode of currently highlighted net.
const std::set< int > GetHighContrastLayers() const
Returns the set of currently high-contrast layers.
virtual COLOR4D GetColor(const VIEW_ITEM *aItem, int aLayer) const =0
Returns the color that should be used to draw the specific VIEW_ITEM on the specific layer using curr...
void SetHighlight(bool aEnabled, int aNetcode=-1, bool aMulti=false)
Turns on/off highlighting.
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
bool IsBOARD_ITEM() const
Definition view_item.h:102
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:67
double GetScale() const
Definition view.h:285
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
void UpdateAllLayersColor()
Apply the new coloring scheme to all layers.
Definition view.cpp:848
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
void Hide(VIEW_ITEM *aItem, bool aHide=true, bool aHideOverlay=false)
Temporarily hide the item in the view (e.g.
Definition view.cpp:1776
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition view.h:431
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition view.h:229
void MarkTargetDirty(int aTarget)
Set or clear target 'dirty' flag.
Definition view.h:661
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition view.cpp:1755
wxString AsString() const
Definition kiid.cpp:365
Definition kiid.h:48
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition lseq.h:47
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
LSEQ CuStack() const
Return a sequence of copper layers in starting from the front/top and extending to the back/bottom.
Definition lset.cpp:263
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:313
LSEQ SeqStackupTop2Bottom(PCB_LAYER_ID aSelectedLayer=UNDEFINED_LAYER) const
Generate a sequence of layers that represent a top to bottom stack of this set of layers.
Definition lset.cpp:343
static const LSET & AllLayersMask()
Definition lset.cpp:641
static const LSET & PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition lset.cpp:697
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
bool IsExcluded() const
Definition marker_base.h:93
const VECTOR2I & GetPos() const
Definition marker_base.h:83
void ShapeToPolygon(SHAPE_LINE_CHAIN &aPolygon, int aScale=-1) const
Return the shape polygon in internal units in a SHAPE_LINE_CHAIN the coordinates are relatives to the...
Handle the data for a net.
Definition netinfo.h:50
const wxString & GetNetChain() const
Definition netinfo.h:115
PAD * GetTerminalPad(int aIndex) const
Definition netinfo.h:118
REPLACE_TERMINAL_PAD_MENU * m_replaceMenu
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
Definition pad.h:55
static TOOL_ACTION deleteLastPoint
static TOOL_ACTION drag45Degree
static TOOL_ACTION selectNetChain
Select all connections belonging to every net in the current item's net chain.
Definition pcb_actions.h:88
static TOOL_ACTION unrouteSelected
Removes all tracks from the selected items to the first pad.
Definition pcb_actions.h:76
static TOOL_ACTION saveToLinkedDesignBlock
static TOOL_ACTION grabUnconnected
Select and move nearest unconnected footprint from ratsnest of selection.
Definition pcb_actions.h:94
static TOOL_ACTION filterSelection
Filter the items in the current selection (invokes dialog)
static TOOL_ACTION setTerminalPad
static TOOL_ACTION highlightNet
static TOOL_ACTION hideLocalRatsnest
static TOOL_ACTION properties
Activation of the edit tool.
static TOOL_ACTION highlightNetChain
static TOOL_ACTION selectOnSheetFromEeschema
Select all components on sheet from Eeschema crossprobing.
Definition pcb_actions.h:97
static TOOL_ACTION selectConnection
Select tracks between junctions or expands an existing selection to pads or the entire connection.
Definition pcb_actions.h:73
static TOOL_ACTION applyDesignBlockLayout
static TOOL_ACTION dragFreeAngle
static TOOL_ACTION clearHighlight
static TOOL_ACTION selectUnconnected
Select unconnected footprints from ratsnest of selection.
Definition pcb_actions.h:91
static TOOL_ACTION unrouteSegment
Removes track segment from the selected item to the next segment.
Definition pcb_actions.h:79
static TOOL_ACTION moveIndividually
move items one-by-one
static TOOL_ACTION syncSelection
Sets selection to specified items, zooms to fit, if enabled.
Definition pcb_actions.h:63
static TOOL_ACTION selectSameSheet
Select all components on the same sheet as the selected footprint.
static TOOL_ACTION selectNet
Select all connections belonging to a single net.
Definition pcb_actions.h:82
static TOOL_ACTION move
move or drag an item
static TOOL_ACTION syncSelectionWithNets
Sets selection to specified items with connected nets, zooms to fit, if enabled.
Definition pcb_actions.h:66
static TOOL_ACTION deselectNet
Remove all connections belonging to a single net from the active selection.
Definition pcb_actions.h:85
static TOOL_ACTION placeLinkedDesignBlock
static TOOL_ACTION selectOnSchematic
Select symbols/pins on schematic corresponding to selected footprints/pads.
Common, abstract interface for edit frames.
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
const PCB_DISPLAY_OPTIONS & GetDisplayOptions() const
Display options control the way tracks, vias, outlines and other things are shown (for instance solid...
double m_TrackOpacity
Opacity override for all tracks.
double m_FilledShapeOpacity
Opacity override for graphic shapes.
double m_ZoneOpacity
Opacity override for filled zone areas.
double m_ImageOpacity
Opacity override for user images.
double m_PadOpacity
Opacity override for SMD pads and PTHs.
double m_ViaOpacity
Opacity override for all types of via.
ZONE_DISPLAY_MODE m_ZoneDisplayMode
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
The main frame for Pcbnew.
bool IsReference() const
Definition pcb_field.h:68
bool IsValue() const
Definition pcb_field.h:69
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:53
static bool WithinScope(BOARD_ITEM *aItem, PCB_GROUP *aScope, bool isFootprintEditor)
static EDA_GROUP * TopLevelGroup(BOARD_ITEM *aItem, EDA_GROUP *aScope, bool isFootprintEditor)
std::unordered_set< BOARD_ITEM * > GetBoardItems() const
Tool that displays edit points allowing to modify items by dragging the points.
bool HasPoint()
Indicate the cursor is over an edit point.
A PCB_POINT is a 0-dimensional point that is used to mark a position on a PCB, or more usually a foot...
Definition pcb_point.h:43
Private implementation of firewalled private data.
DIALOG_FILTER_SELECTION::OPTIONS m_filterOpts
The selection tool: currently supports:
void highlight(EDA_ITEM *aItem, int aHighlightMode, SELECTION *aGroup=nullptr) override
Highlight the item visually.
int syncSelectionWithNets(const TOOL_EVENT &aEvent)
int SelectTable(const TOOL_EVENT &aEvent)
Clear current selection event handler.
PCB_BASE_FRAME * frame() const
int syncSelection(const TOOL_EVENT &aEvent)
int selectNet(const TOOL_EVENT &aEvent)
Select all copper connections belonging to the same net(s) as the items in the selection.
int filterSelection(const TOOL_EVENT &aEvent)
Return true if the given item passes the current SELECTION_FILTER_OPTIONS.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
void ZoomFitCrossProbeBBox(const BOX2I &bbox)
void EnterGroup() override
Enter the group at the head of the current selection.
void doSyncSelection(const std::vector< BOARD_ITEM * > &aItems, bool aWithNets)
Invoke filter dialog and modify current selection.
SELECTION_MODE m_selectionMode
void GuessSelectionCandidates(GENERAL_COLLECTOR &aCollector, const VECTOR2I &aWhere) const
Try to guess best selection candidates in case multiple items are clicked, by doing some brain-dead h...
int selectNetChain(const TOOL_EVENT &aEvent)
Select all copper connections belonging to every net in the selected item's net chain.
void ExitGroup(bool aSelectGroup=false) override
Leave the currently-entered group.
bool isExpandableGraphicShape(const EDA_ITEM *aItem) const
int disambiguateCursor(const TOOL_EVENT &aEvent)
Handle disambiguation actions including displaying the menu.
int SelectPolyArea(const TOOL_EVENT &aEvent)
Handles drawing a lasso selection area that allows multiple items to be selected simultaneously.
void FilterCollectorForMarkers(GENERAL_COLLECTOR &aCollector) const
Drop any PCB_MARKERs from the collector.
int UnselectAll(const TOOL_EVENT &aEvent)
Change the selection mode.
void unhighlightInternal(EDA_ITEM *aItem, int aHighlightMode, bool aUsingOverlay)
bool selectionContains(const VECTOR2I &aPoint) const
void select(EDA_ITEM *aItem) override
Take necessary action mark an item as selected.
bool selectCursor(bool aForceSelect=false, CLIENT_SELECTION_FILTER aClientFilter=nullptr)
Select an item under the cursor unless there is something already selected.
int SetSelectPoly(const TOOL_EVENT &aEvent)
int SetSelectRect(const TOOL_EVENT &aEvent)
int SelectColumns(const TOOL_EVENT &aEvent)
std::unique_ptr< PRIV > m_priv
int unrouteSelected(const TOOL_EVENT &aEvent)
Unroute the selected board connected items.
void ReportFilteredLockedItems()
If the most recent FilterCollectorForLockedItems call removed at least one item, show an InfoBar warn...
int unrouteSegment(const TOOL_EVENT &aEvent)
Unroute the selected track connected item.
SELECTION & selection() override
Return a reference to the selection.
int grabUnconnected(const TOOL_EVENT &aEvent)
Select and move other nearest footprint unconnected on same net as selected items.
PCB_SELECTION & RequestSelection(CLIENT_SELECTION_FILTER aClientFilter)
Return the current selection, filtered according to aClientFilter.
void FilterCollectorForFreePads(GENERAL_COLLECTOR &aCollector, bool aForcePromotion=false) const
Check the "allow free pads" setting and if disabled, replace any pads in the collector with their par...
bool itemPassesFilter(BOARD_ITEM *aItem, bool aMultiSelect, PCB_SELECTION_FILTER_OPTIONS *aRejected=nullptr)
const GENERAL_COLLECTORS_GUIDE getCollectorsGuide() const
bool Selectable(const BOARD_ITEM *aItem, bool checkVisibilityOnly=false) const
bool selectPoint(const VECTOR2I &aWhere, bool aOnDrag=false, bool *aSelectionCancelledFlag=nullptr, CLIENT_SELECTION_FILTER aClientFilter=nullptr)
Select an item pointed by the parameter aWhere.
KIGFX::PCB_VIEW * view() const
void selectAllItemsOnSheet(wxString &aSheetPath)
Select all items with the given sheet timestamp/UUID name (the sheet path).
void FilterCollectorForHierarchy(GENERAL_COLLECTOR &aCollector, bool aMultiselect) const
In general we don't want to select both a parent and any of it's children.
void SelectMultiple(KIGFX::PREVIEW::SELECTION_AREA &aArea, bool aSubtractive=false, bool aExclusiveOr=false)
Selects multiple PCB items within a specified area.
void setTransitions() override
Zoom the screen to center and fit the current selection.
PCB_BASE_EDIT_FRAME * editFrame() const
int expandConnection(const TOOL_EVENT &aEvent)
Expand the current connected-item selection to the next boundary (junctions, pads,...
int selectUnconnected(const TOOL_EVENT &aEvent)
Select nearest unconnected footprints on same net as selected items.
virtual bool ctrlClickHighlights() override
Determine if ctrl-click is highlight net or XOR selection.
int selectSheetContents(const TOOL_EVENT &aEvent)
Select all footprints belonging to same hierarchical sheet as the selected footprint (same sheet path...
int SelectRows(const TOOL_EVENT &aEvent)
int selectSameSheet(const TOOL_EVENT &aEvent)
Set selection to items passed by parameter and connected nets (optionally).
void zoomFitSelection()
Zoom the screen to fit the bounding box for cross probing/selection sync.
void selectAllConnectedTracks(const std::vector< BOARD_CONNECTED_ITEM * > &aStartItems, STOP_CONDITION aStopCondition)
Select connected tracks and vias.
int CursorSelection(const TOOL_EVENT &aEvent)
int ClearSelection(const TOOL_EVENT &aEvent)
void highlightInternal(EDA_ITEM *aItem, int aHighlightMode, bool aUsingOverlay)
PCB_BASE_FRAME * m_frame
PCB_SELECTION_FILTER_OPTIONS m_filter
PCB_SELECTION & GetSelection()
@ STOP_AT_SEGMENT
Stop when reaching a segment (next track/arc/via).
@ STOP_AT_PAD
Stop when reaching a pad.
@ STOP_NEVER
Select the entire net.
@ STOP_AT_JUNCTION
Stop at any place where more than two traces meet.
int Main(const TOOL_EVENT &aEvent)
The main loop.
void RebuildSelection()
Rebuild the selection from the EDA_ITEMs' selection flags.
void pruneObscuredSelectionCandidates(GENERAL_COLLECTOR &aCollector) const
void OnIdle(wxIdleEvent &aEvent)
PCB_DRAW_PANEL_GAL * canvas() const
void FilterCollectorForFootprints(GENERAL_COLLECTOR &aCollector, const VECTOR2I &aWhere) const
Drop footprints that are not directly selected.
void FindItem(BOARD_ITEM *aItem)
Take necessary actions to mark an item as found.
void FilterCollectorForLockedItems(GENERAL_COLLECTOR &aCollector)
In the PCB editor strip out any locked items unless the OverrideLocks checkbox is set.
int SelectRectArea(const TOOL_EVENT &aEvent)
Handles drawing a selection box that allows multiple items to be selected simultaneously.
int updateSelection(const TOOL_EVENT &aEvent)
Event handler to update the selection VIEW_ITEM.
bool Init() override
Init() is called once upon a registration of the tool.
KIGFX::VIEW_GROUP m_enteredGroupOverlay
void selectConnections(const std::vector< BOARD_ITEM * > &aItems)
int hitTestDistance(const VECTOR2I &aWhere, BOARD_ITEM *aItem, int aMaxDistance) const
void selectAllConnectedShapes(const std::vector< PCB_SHAPE * > &aStartItems)
Select all non-closed shapes that are graphically connected to the given start items.
void SelectAllItemsOnNet(int aNetCode, bool aSelect=true)
Select all items with the given net code.
void FilterCollectorForTableCells(GENERAL_COLLECTOR &aCollector) const
Promote any table cell selections to the whole table.
void unselect(EDA_ITEM *aItem) override
Take necessary action mark an item as unselected.
int SelectAll(const TOOL_EVENT &aEvent)
Unselect all items on the board.
void FilterCollectedItems(GENERAL_COLLECTOR &aCollector, bool aMultiSelect, PCB_SELECTION_FILTER_OPTIONS *aRejected=nullptr)
Apply the SELECTION_FITLER_OPTIONS to the collector.
bool selectTableCells(PCB_TABLE *aTable)
void unhighlight(EDA_ITEM *aItem, int aHighlightMode, SELECTION *aGroup=nullptr) override
Unhighlight the item visually.
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
std::vector< VECTOR2I > GetConnectionPoints() const
int GetRowSpan() const
int GetColSpan() const
std::vector< PCB_TABLECELL * > GetCells() const
Definition pcb_table.h:160
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
const VECTOR2I & GetStart() const
Definition pcb_track.h:97
const VECTOR2I & GetEnd() const
Definition pcb_track.h:94
VECTOR2I GetPosition() const override
Definition pcb_track.h:557
int GetWidth() const override
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
A small class to help profiling.
Definition profile.h:50
void Start()
Start or restart the counter.
Definition profile.h:78
double msecs(bool aSinceLast=false)
Definition profile.h:151
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.
bool RoutingInProgress()
Returns whether routing is currently active.
Definition seg.h:42
static SEG::ecoord Square(int a)
Definition seg.h:123
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 NotEmpty(const SELECTION &aSelection)
Test if there are any 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 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_highlight_modifier
Select highlight net on left click.
bool m_skip_heuristics
Show disambiguation menu for all items under the cursor rather than trying to narrow them down first ...
int ReselectItem(const TOOL_EVENT &aEvent)
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
virtual void Remove(EDA_ITEM *aItem)
Definition selection.cpp:60
EDA_ITEM * Front() const
Definition selection.h:177
void ClearReferencePoint()
bool Empty() const
Checks if there is anything selected.
Definition selection.h:115
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Move(const VECTOR2I &aVector) override
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.
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if point aP lies closer to us than aClearance.
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 Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const
Check if the boundary of shape (this) lies closer to the point aP than aClearance,...
Definition shape.h:183
bool ToolStackIsEmpty()
Represent a single user action.
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:186
T * getModel() const
Return the model object if it matches the requested type.
Definition tool_base.h:198
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition tool_base.cpp:44
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition tool_base.cpp:38
bool IsToolActive() const
Definition tool_base.cpp:32
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:78
@ REDRAW
Full drawing refresh.
Definition tool_base.h:83
@ MODEL_RELOAD
Model changes (the sheet for a schematic)
Definition tool_base.h:80
Generic, UI-independent tool event.
Definition tool_event.h:171
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.
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:473
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
Handle a list of polygons defining a copper zone.
Definition zone.h:74
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:802
bool HitTestForCorner(const VECTOR2I &refPos, int aAccuracy, SHAPE_POLY_SET::VERTEX_INDEX *aCornerHit=nullptr) const
Test if the given VECTOR2I is near a corner.
Definition zone.cpp:835
bool HitTestForEdge(const VECTOR2I &refPos, int aAccuracy, SHAPE_POLY_SET::VERTEX_INDEX *aCornerHit=nullptr) const
Test if the given VECTOR2I is near a segment defined by 2 corners.
Definition zone.cpp:842
bool IsTeardropArea() const
Definition zone.h:777
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:137
bool GetDoNotAllowZoneFills() const
Definition zone.h:812
A type-safe container of any type.
Definition ki_any.h:93
#define EXCLUDE_ZONES
#define IGNORE_NETS
Function GetConnectedItems() Returns a list of items connected to a source item aItem.
KICURSOR
Definition cursors.h:44
@ 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
#define DEFAULT_TEXT_SIZE
Ratio of the font height to the baseline of the text above the wire.
static bool empty(const wxTextEntryBase *aCtrl)
#define _(s)
@ RECURSE
Definition eda_item.h:53
std::function< INSPECT_RESULT(EDA_ITEM *aItem, void *aTestData) > INSPECTOR_FUNC
Used to inspect and possibly collect the (search) results of iterating over a list or tree of KICAD_T...
Definition eda_item.h:90
#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 ENTERED
indicates a group has been entered
#define SKIP_STRUCT
flag indicating that the structure should be ignored
#define CANDIDATE
flag indicating that the structure is connected
#define IS_MOVING
Item being moved.
@ SEGMENT
Definition eda_shape.h:50
@ FRAME_PCB_EDITOR
Definition frame_type.h:42
@ FRAME_FOOTPRINT_VIEWER
Definition frame_type.h:45
@ FRAME_FOOTPRINT_EDITOR
Definition frame_type.h:43
a few functions useful in geometry calculations.
double m_PcbSelectionVisibilityRatio
Board object selection visibility limit.
KIID niluuid(0)
@ LAYER_POINTS
PCB reference/manual snap points visibility.
Definition layer_ids.h:321
@ LAYER_FOOTPRINTS_FR
Show footprints on front.
Definition layer_ids.h:259
@ LAYER_DRAW_BITMAPS
Draw images.
Definition layer_ids.h:284
@ LAYER_FP_REFERENCES
Show footprints references (when texts are visible).
Definition layer_ids.h:266
@ LAYER_DRC_EXCLUSION
Layer for DRC markers which have been individually excluded.
Definition layer_ids.h:304
@ LAYER_ZONES
Control for copper zone opacity/visibility (color ignored).
Definition layer_ids.h:295
@ LAYER_PADS
Meta control for all pads opacity/visibility (color ignored).
Definition layer_ids.h:292
@ LAYER_TRACKS
Definition layer_ids.h:267
@ LAYER_FP_TEXT
Definition layer_ids.h:240
@ LAYER_FOOTPRINTS_BK
Show footprints on back.
Definition layer_ids.h:260
@ LAYER_FP_VALUES
Show footprints values (when texts are visible).
Definition layer_ids.h:263
@ LAYER_VIAS
Meta control for all vias opacity/visibility.
Definition layer_ids.h:232
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ F_CrtYd
Definition layer_ids.h:116
@ Edge_Cuts
Definition layer_ids.h:112
@ F_SilkS
Definition layer_ids.h:100
@ B_CrtYd
Definition layer_ids.h:115
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ B_SilkS
Definition layer_ids.h:101
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:171
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition lset.cpp:754
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:83
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
@ TARGET_OVERLAY
Items that may change while the view stays the same (noncached)
Definition definitions.h:39
STL namespace.
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ PTH
Plated through hole pad.
Definition padstack.h:98
void connectedItemFilter(const VECTOR2I &, GENERAL_COLLECTOR &aCollector, PCB_SELECTION_TOOL *sTool)
static void passEvent(TOOL_EVENT *const aEvent, const TOOL_ACTION *const aAllowedActions[])
static bool itemIsIncludedByFilter(const BOARD_ITEM &aItem, const BOARD &aBoard, const DIALOG_FILTER_SELECTION::OPTIONS &aFilterOptions)
Determine if an item is included by the filter specified.
@ ID_REPLACE_TERMINAL_PAD_A
@ ID_REPLACE_TERMINAL_PAD_B
TRACK_DRAG_ACTION
CITER next(CITER it)
Definition ptree.cpp:124
Class that computes missing connections on a PCB.
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
static std::vector< KICAD_T > tableCellTypes
const TOOL_ACTION * allowedActions[]
static void passEvent(TOOL_EVENT *const aEvent, const TOOL_ACTION *const aAllowedActions[])
std::function< bool(const SELECTION &)> SELECTION_CONDITION
Functor type that checks a specific condition for selected items.
SELECTION_MODE
const int scale
Struct that will be set with the result of the user choices in the dialog.
const BOARD_ITEM * m_Item
This file contains data structures that are saved in the project file or project local settings file ...
bool otherItems
Anything not fitting one of the above categories.
bool graphics
Graphic lines, shapes, polygons.
bool footprints
Allow selecting entire footprints.
bool text
Text (free or attached to a footprint)
bool lockedItems
Allow selecting locked items.
KIBIS top(path, &reporter)
std::vector< std::vector< std::string > > table
int radius
const int accuracy
int delta
@ TA_MOUSE_UP
Definition tool_event.h:69
@ 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_ANY
Definition tool_event.h:60
@ BUT_MIDDLE
Definition tool_event.h:134
@ BUT_LEFT
Definition tool_event.h:132
@ BUT_RIGHT
Definition tool_event.h:133
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:75
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:85
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:103
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:100
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:88
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:94
@ TYPE_NOT_INIT
Definition typeinfo.h:78
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:101
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:108
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:90
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:105
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:89
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:86
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:87
@ NOT_USED
the 3d code uses this value
Definition typeinfo.h:76
@ PCB_MARKER_T
class PCB_MARKER, a marker used to show something
Definition typeinfo.h:96
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:98
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition typeinfo.h:104
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:92
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:83
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:99
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:84
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:95
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:91
@ PCB_NETINFO_T
class NETINFO_ITEM, a description of a net
Definition typeinfo.h:107
@ PCB_POINT_T
class PCB_POINT, a 0-dimensional point
Definition typeinfo.h:110
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:93
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:102
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:60
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
VECTOR2< double > VECTOR2D
Definition vector2d.h:686
VECTOR2D ToVECTOR2D(const wxPoint &aPoint)
Definition vector2wx.h:40