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