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