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 return false;
790 }
791
792 // Allow the client to do tool- or action-specific filtering to see if we can get down
793 // to a single item
794 if( aClientFilter )
795 aClientFilter( aWhere, collector, this );
796
797 FilterCollectorForHierarchy( collector, false );
798
799 FilterCollectorForFootprints( collector, aWhere );
800
801 // For subtracting, we only want items that are selected
802 if( m_subtractive )
803 {
804 for( int i = collector.GetCount() - 1; i >= 0; --i )
805 {
806 if( !collector[i]->IsSelected() )
807 collector.Remove( i );
808 }
809 }
810
811 // Apply some ugly heuristics to avoid disambiguation menus whenever possible
812 if( collector.GetCount() > 1 && !m_skip_heuristics )
813 {
814 try
815 {
816 GuessSelectionCandidates( collector, aWhere );
817 }
818 catch( const std::exception& exc )
819 {
820 wxLogWarning( wxS( "Exception '%s' occurred attempting to guess selection candidates." ),
821 exc.what() );
822 return false;
823 }
824 }
825
826 // If still more than one item we're going to have to ask the user.
827 if( collector.GetCount() > 1 )
828 {
829 if( aOnDrag )
831
832 if( !doSelectionMenu( &collector ) )
833 {
834 if( aSelectionCancelledFlag )
835 *aSelectionCancelledFlag = true;
836
837 return false;
838 }
839 }
840
841 int addedCount = 0;
842 bool anySubtracted = false;
843
845 {
846 if( m_selection.GetSize() > 0 )
847 {
848 ClearSelection( true /*quiet mode*/ );
849 anySubtracted = true;
850 }
851 }
852
853 if( collector.GetCount() > 0 )
854 {
855 for( int i = 0; i < collector.GetCount(); ++i )
856 {
857 if( m_subtractive || ( m_exclusive_or && collector[i]->IsSelected() ) )
858 {
859 unselect( collector[i] );
860 anySubtracted = true;
861 }
862 else
863 {
864 select( collector[i] );
865 addedCount++;
866 }
867 }
868 }
869
870 if( addedCount == 1 )
871 {
872 m_toolMgr->ProcessEvent( EVENTS::PointSelectedEvent );
873 return true;
874 }
875 else if( addedCount > 1 )
876 {
877 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
878 return true;
879 }
880 else if( anySubtracted )
881 {
882 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
883 return true;
884 }
885
886 return false;
887}
888
889
890bool PCB_SELECTION_TOOL::selectCursor( bool aForceSelect, CLIENT_SELECTION_FILTER aClientFilter )
891{
892 if( aForceSelect || m_selection.Empty() )
893 {
894 ClearSelection( true /*quiet mode*/ );
895 selectPoint( getViewControls()->GetCursorPosition( false ), false, nullptr, aClientFilter );
896 }
897
898 return !m_selection.Empty();
899}
900
901
902// Some navigation actions are allowed in selectMultiple
913
914
915static void passEvent( TOOL_EVENT* const aEvent, const TOOL_ACTION* const aAllowedActions[] )
916{
917 for( int i = 0; aAllowedActions[i]; ++i )
918 {
919 if( aEvent->IsAction( aAllowedActions[i] ) )
920 {
921 aEvent->SetPassEvent();
922 break;
923 }
924 }
925}
926
927
929{
930 bool cancelled = false; // Was the tool canceled while it was running?
931 m_multiple = true; // Multiple selection mode is active
932
933 for( PCB_TABLECELL* cell : aTable->GetCells() )
934 {
935 if( cell->IsSelected() )
936 cell->SetFlags( CANDIDATE );
937 else
938 cell->ClearFlags( CANDIDATE );
939 }
940
941 auto wasSelected =
942 []( EDA_ITEM* aItem )
943 {
944 return ( aItem->GetFlags() & CANDIDATE ) > 0;
945 };
946
947 while( TOOL_EVENT* evt = Wait() )
948 {
949 if( evt->IsCancelInteractive() || evt->IsActivate() )
950 {
951 cancelled = true;
952 break;
953 }
954 else if( evt->IsDrag( BUT_LEFT ) )
955 {
956 getViewControls()->SetAutoPan( true );
957
958 BOX2I selectionRect( evt->DragOrigin(), evt->Position() - evt->DragOrigin() );
959 selectionRect.Normalize();
960
961 for( PCB_TABLECELL* cell : aTable->GetCells() )
962 {
963 bool doSelect = false;
964
965 if( cell->HitTest( selectionRect, false ) )
966 {
967 if( m_subtractive )
968 doSelect = false;
969 else if( m_exclusive_or )
970 doSelect = !wasSelected( cell );
971 else
972 doSelect = true;
973 }
974 else if( wasSelected( cell ) )
975 {
976 doSelect = m_additive || m_subtractive || m_exclusive_or;
977 }
978
979 if( doSelect && !cell->IsSelected() )
980 select( cell );
981 else if( !doSelect && cell->IsSelected() )
982 unselect( cell );
983 }
984 }
985 else if( evt->IsMouseUp( BUT_LEFT ) )
986 {
987 m_selection.SetIsHover( false );
988
989 bool anyAdded = false;
990 bool anySubtracted = false;
991
992 for( PCB_TABLECELL* cell : aTable->GetCells() )
993 {
994 if( cell->IsSelected() && !wasSelected( cell ) )
995 anyAdded = true;
996 else if( wasSelected( cell ) && !cell->IsSelected() )
997 anySubtracted = true;
998 }
999
1000 // Inform other potentially interested tools
1001 if( anyAdded )
1002 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1003
1004 if( anySubtracted )
1005 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1006
1007 break; // Stop waiting for events
1008 }
1009 else
1010 {
1011 // Allow some actions for navigation
1012 passEvent( evt, allowedActions );
1013 }
1014 }
1015
1016 getViewControls()->SetAutoPan( false );
1017
1018 m_multiple = false; // Multiple selection mode is inactive
1019
1020 if( !cancelled )
1021 m_selection.ClearReferencePoint();
1022
1023 return cancelled;
1024}
1025
1026
1028{
1029 bool cancelled = false; // Was the tool canceled while it was running?
1030 m_multiple = true; // Multiple selection mode is active
1032
1034 view->Add( &area );
1035
1036 while( TOOL_EVENT* evt = Wait() )
1037 {
1038 /* Selection mode depends on direction of drag-selection:
1039 * Left > Right : Select objects that are fully enclosed by selection
1040 * Right > Left : Select objects that are crossed by selection
1041 */
1042 bool greedySelection = area.GetEnd().x < area.GetOrigin().x;
1043
1044 if( view->IsMirroredX() )
1045 greedySelection = !greedySelection;
1046
1047 m_frame->GetCanvas()->SetCurrentCursor( !greedySelection ? KICURSOR::SELECT_WINDOW
1049
1050 if( evt->IsCancelInteractive() || evt->IsActivate() )
1051 {
1052 cancelled = true;
1053 break;
1054 }
1055
1056 if( evt->IsDrag( BUT_LEFT ) )
1057 {
1059 {
1060 if( m_selection.GetSize() > 0 )
1061 {
1062 ClearSelection( true /*quiet mode*/ );
1063 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1064 }
1065 }
1066
1067 // Start drawing a selection box
1068 area.SetOrigin( evt->DragOrigin() );
1069 area.SetEnd( evt->Position() );
1072 area.SetExclusiveOr( false );
1074
1075 view->SetVisible( &area, true );
1076 view->Update( &area );
1077 getViewControls()->SetAutoPan( true );
1078 }
1079
1080 if( evt->IsMouseUp( BUT_LEFT ) )
1081 {
1082 getViewControls()->SetAutoPan( false );
1083
1084 // End drawing the selection box
1085 view->SetVisible( &area, false );
1086
1088
1089 break; // Stop waiting for events
1090 }
1091
1092 // Allow some actions for navigation
1093 passEvent( evt, allowedActions );
1094 }
1095
1096 getViewControls()->SetAutoPan( false );
1097
1098 // Stop drawing the selection box
1099 view->Remove( &area );
1100 m_multiple = false; // Multiple selection mode is inactive
1101
1102 if( !cancelled )
1103 m_selection.ClearReferencePoint();
1104
1106
1107 return cancelled;
1108}
1109
1110
1112{
1114 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
1115 m_toolMgr->PostAction( ACTIONS::selectionTool );
1116 return 0; // No need to wait for an event, just set the mode
1117}
1118
1119
1121{
1123 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1124 m_toolMgr->PostAction( ACTIONS::selectionTool );
1125 return 0; // No need to wait for an event, just set the mode
1126}
1127
1128
1130{
1131 bool cancelled = false; // Was the tool canceled while it was running?
1132
1134 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SELECT_LASSO );
1135
1136
1137 SHAPE_LINE_CHAIN points;
1138 points.SetClosed( true );
1139
1141 getView()->Add( &area );
1142 getView()->SetVisible( &area, true );
1143 getViewControls()->SetAutoPan( true );
1144
1145 while( TOOL_EVENT* evt = Wait() )
1146 {
1147 // Auto mode: clockwise = inside, counterclockwise = touching
1148 double shapeArea = area.GetPoly().Area( false );
1149 bool isClockwise = shapeArea > 0 ? true : false;
1150
1151 if( getView()->IsMirroredX() && shapeArea != 0 )
1152 isClockwise = !isClockwise;
1153
1154 selectionMode = isClockwise ? SELECTION_MODE::INSIDE_LASSO : SELECTION_MODE::TOUCHING_LASSO;
1155
1156 if( evt->IsCancelInteractive() || evt->IsActivate() )
1157 {
1158 cancelled = true;
1159 evt->SetPassEvent( false );
1160 break;
1161 }
1162 else if( evt->IsDrag( BUT_LEFT )
1163 || evt->IsClick( BUT_LEFT )
1164 || evt->IsAction( &ACTIONS::cursorClick ) )
1165 {
1166 points.Append( evt->Position() );
1167 }
1168 else if( evt->IsDblClick( BUT_LEFT )
1169 || evt->IsAction( &ACTIONS::cursorDblClick )
1170 || evt->IsAction( &ACTIONS::finishInteractive ) )
1171 {
1172 area.GetPoly().GenerateBBoxCache();
1174 evt->SetPassEvent( false );
1175 break;
1176 }
1177 else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint )
1178 || evt->IsAction( &ACTIONS::doDelete )
1179 || evt->IsAction( &ACTIONS::undo ) )
1180 {
1181 if( points.GetPointCount() > 0 )
1182 {
1184 points.Remove( points.GetPointCount() - 1 );
1185 }
1186 }
1187 else
1188 {
1189 // Allow navigation actions
1190 passEvent( evt, allowedActions );
1191 }
1192
1193 if( points.PointCount() > 0 )
1194 {
1196 {
1197 if( m_selection.GetSize() > 0 )
1198 {
1199 ClearSelection( true /*quiet mode*/ );
1200 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1201 }
1202 }
1203 }
1204
1205 area.SetPoly( points );
1206 area.GetPoly().Append( m_toolMgr->GetMousePosition() );
1207 area.SetAdditive( m_additive );
1209 area.SetExclusiveOr( false );
1210 area.SetMode( selectionMode );
1211 getView()->Update( &area );
1212 }
1213
1214 getViewControls()->SetAutoPan( false );
1215 getView()->SetVisible( &area, false );
1216 getView()->Remove( &area );
1217 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1218
1219 if( !cancelled )
1221
1223
1224 return cancelled;
1225}
1226
1227
1229 bool aExclusiveOr )
1230{
1232
1233 bool anyAdded = false;
1234 bool anySubtracted = false;
1235
1236 SELECTION_MODE selectionMode = aArea.GetMode();
1237 bool containedMode = ( selectionMode == SELECTION_MODE::INSIDE_RECTANGLE
1238 || selectionMode == SELECTION_MODE::INSIDE_LASSO ) ? true : false;
1239 bool boxMode = ( selectionMode == SELECTION_MODE::INSIDE_RECTANGLE
1240 || selectionMode == SELECTION_MODE::TOUCHING_RECTANGLE ) ? true : false;
1241
1242 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> candidates;
1243 BOX2I selectionBox = aArea.ViewBBox();
1244 view->Query( selectionBox, candidates ); // Get the list of nearby items
1245
1246 GENERAL_COLLECTOR collector;
1247 GENERAL_COLLECTOR padsCollector;
1248 std::set<EDA_ITEM*> group_items;
1249
1250 for( PCB_GROUP* group : board()->Groups() )
1251 {
1252 // The currently entered group does not get limited
1253 if( m_enteredGroup == group )
1254 continue;
1255
1256 std::unordered_set<EDA_ITEM*>& newset = group->GetItems();
1257
1258 auto boxContained =
1259 [&]( const BOX2I& aBox )
1260 {
1261 return boxMode ? selectionBox.Contains( aBox )
1262 : KIGEOM::BoxHitTest( aArea.GetPoly(), aBox, true );
1263 };
1264
1265 // If we are not greedy and have selected the whole group, add just one item
1266 // to allow it to be promoted to the group later
1267 if( containedMode && boxContained( group->GetBoundingBox() ) && newset.size() )
1268 {
1269 for( EDA_ITEM* group_item : newset )
1270 {
1271 if( !group_item->IsBOARD_ITEM() )
1272 continue;
1273
1274 if( Selectable( static_cast<BOARD_ITEM*>( group_item ) ) )
1275 collector.Append( *newset.begin() );
1276 }
1277 }
1278
1279 for( EDA_ITEM* group_item : newset )
1280 group_items.emplace( group_item );
1281 }
1282
1283 auto hitTest =
1284 [&]( const EDA_ITEM* aItem )
1285 {
1286 return boxMode ? aItem->HitTest( selectionBox, containedMode )
1287 : aItem->HitTest( aArea.GetPoly(), containedMode );
1288 };
1289
1290 for( const auto& [item, layer] : candidates )
1291 {
1292 if( !item->IsBOARD_ITEM() )
1293 continue;
1294
1295 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
1296
1297 if( Selectable( boardItem ) && hitTest( boardItem )
1298 && ( !containedMode || !group_items.count( boardItem ) ) )
1299 {
1300 if( boardItem->Type() == PCB_PAD_T && !m_isFootprintEditor )
1301 padsCollector.Append( boardItem );
1302 else
1303 collector.Append( boardItem );
1304 }
1305 }
1306
1307 // Apply the stateful filter
1308 FilterCollectedItems( collector, true, nullptr );
1309
1310 FilterCollectorForHierarchy( collector, true );
1311
1312 // If we selected nothing but pads, allow them to be selected
1313 if( collector.GetCount() == 0 )
1314 {
1315 collector = padsCollector;
1316 FilterCollectedItems( collector, true, nullptr );
1317 FilterCollectorForHierarchy( collector, true );
1318 }
1319
1320 // Sort the filtered selection by rows and columns to have a nice default
1321 // for tools that can use it.
1322 std::sort( collector.begin(), collector.end(),
1323 []( EDA_ITEM* a, EDA_ITEM* b )
1324 {
1325 VECTOR2I aPos = a->GetPosition();
1326 VECTOR2I bPos = b->GetPosition();
1327
1328 if( aPos.y == bPos.y )
1329 return aPos.x < bPos.x;
1330
1331 return aPos.y < bPos.y;
1332 } );
1333
1334 for( EDA_ITEM* i : collector )
1335 {
1336 if( !i->IsBOARD_ITEM() )
1337 continue;
1338
1339 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
1340
1341 if( aSubtractive || ( aExclusiveOr && item->IsSelected() ) )
1342 {
1343 unselect( item );
1344 anySubtracted = true;
1345 }
1346 else
1347 {
1348 select( item );
1349 anyAdded = true;
1350 }
1351 }
1352
1353 m_selection.SetIsHover( false );
1354
1355 // Inform other potentially interested tools
1356 if( anyAdded )
1357 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1358 else if( anySubtracted )
1359 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1360}
1361
1362
1364{
1365 wxMouseState keyboardState = wxGetMouseState();
1366
1367 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(), keyboardState.AltDown() );
1368
1369 m_skip_heuristics = true;
1371 m_skip_heuristics = false;
1372
1373 return 0;
1374}
1375
1376
1377
1379{
1381
1382 selectCursor( false, aClientFilter );
1383
1384 return 0;
1385}
1386
1387
1389{
1391
1392 return 0;
1393}
1394
1395
1397{
1398 GENERAL_COLLECTOR collection;
1399 BOX2I selectionBox;
1400
1401 selectionBox.SetMaximum();
1402
1403 getView()->Query( selectionBox,
1404 [&]( KIGFX::VIEW_ITEM* viewItem ) -> bool
1405 {
1406 if( viewItem->IsBOARD_ITEM() )
1407 {
1408 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( viewItem );
1409
1410 if( item && Selectable( item ) && itemPassesFilter( item, true, nullptr ) )
1411 collection.Append( item );
1412 }
1413
1414 return true;
1415 } );
1416
1417 FilterCollectorForHierarchy( collection, true );
1418
1419 for( EDA_ITEM* item : collection )
1420 select( item );
1421
1422 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1423
1424 m_frame->GetCanvas()->ForceRefresh();
1425
1426 return 0;
1427}
1428
1429
1431{
1432 BOX2I selectionBox;
1433
1434 selectionBox.SetMaximum();
1435
1436 getView()->Query( selectionBox,
1437 [&]( KIGFX::VIEW_ITEM* viewItem ) -> bool
1438 {
1439 if( viewItem->IsBOARD_ITEM() )
1440 {
1441 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( viewItem );
1442
1443 if( item && Selectable( item ) )
1444 unselect( item );
1445 }
1446
1447 return true;
1448 } );
1449
1450 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1451
1452 m_frame->GetCanvas()->ForceRefresh();
1453
1454 return 0;
1455}
1456
1457
1459{
1460 // Narrow the collection down to a single BOARD_CONNECTED_ITEM for each represented net.
1461 // All other items types are removed.
1462 std::set<int> representedNets;
1463
1464 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
1465 {
1466 BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( aCollector[i] );
1467
1468 if( !item )
1469 aCollector.Remove( i );
1470 else if ( representedNets.count( item->GetNetCode() ) )
1471 aCollector.Remove( i );
1472 else
1473 representedNets.insert( item->GetNetCode() );
1474 }
1475}
1476
1477
1479{
1480 std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
1481
1482 // Get all footprints and pads
1483 std::vector<BOARD_CONNECTED_ITEM*> toUnroute;
1484 std::set<EDA_ITEM*> toDelete;
1485
1486 for( EDA_ITEM* item : selectedItems )
1487 {
1488 if( item->Type() == PCB_FOOTPRINT_T )
1489 {
1490 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
1491 toUnroute.push_back( pad );
1492 }
1493 else if( item->Type() == PCB_GENERATOR_T )
1494 {
1495 toDelete.insert( item );
1496
1497 for( BOARD_ITEM* generatedItem : static_cast<PCB_GENERATOR*>( item )->GetBoardItems() )
1498 {
1499 if( BOARD_CONNECTED_ITEM::ClassOf( generatedItem ) )
1500 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( generatedItem ) );
1501 }
1502 }
1503 else if( BOARD_CONNECTED_ITEM::ClassOf( item ) )
1504 {
1505 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
1506 }
1507 }
1508
1509 // Find generators connected to tracks and add their children to toUnroute
1510 // so selectAllConnectedTracks can traverse through meanders
1511 std::set<int> selectedNets;
1512
1513 for( BOARD_CONNECTED_ITEM* item : toUnroute )
1514 if( item->GetNetCode() > 0 )
1515 selectedNets.insert( item->GetNetCode() );
1516
1517 std::set<VECTOR2I> endpointSet;
1518
1519 for( BOARD_CONNECTED_ITEM* item : toUnroute )
1520 {
1521 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
1522 {
1523 endpointSet.insert( track->GetStart() );
1524 endpointSet.insert( track->GetEnd() );
1525 }
1526 else if( item->Type() == PCB_VIA_T )
1527 {
1528 endpointSet.insert( item->GetPosition() );
1529 }
1530 else if( item->Type() == PCB_PAD_T )
1531 {
1532 endpointSet.insert( item->GetPosition() );
1533 }
1534 }
1535
1536 bool expanded = true;
1537
1538 while( expanded )
1539 {
1540 expanded = false;
1541
1542 for( PCB_GENERATOR* gen : board()->Generators() )
1543 {
1544 if( toDelete.count( gen ) )
1545 continue;
1546
1547 // Find this generator's external endpoints (meander entry/exit)
1548 std::map<VECTOR2I, int> epCount;
1549
1550 for( BOARD_ITEM* child : gen->GetBoardItems() )
1551 {
1552 PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( child );
1553
1554 if( track && selectedNets.count( track->GetNetCode() ) )
1555 {
1556 epCount[track->GetStart()]++;
1557 epCount[track->GetEnd()]++;
1558 }
1559 }
1560
1561 // Check if any external endpoint matches our track endpoints
1562 bool connected = false;
1563
1564 for( auto& [pt, count] : epCount )
1565 {
1566 if( count == 1 && endpointSet.count( pt ) )
1567 {
1568 connected = true;
1569 break;
1570 }
1571 }
1572
1573 if( connected )
1574 {
1575 toDelete.insert( gen );
1576
1577 for( BOARD_ITEM* c : gen->GetBoardItems() )
1578 {
1580 continue;
1581
1582 if( !selectedNets.count( static_cast<BOARD_CONNECTED_ITEM*>( c )->GetNetCode() ) )
1583 continue;
1584
1585 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( c ) );
1586
1587 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( c ) )
1588 {
1589 endpointSet.insert( track->GetStart() );
1590 endpointSet.insert( track->GetEnd() );
1591 }
1592
1593 expanded = true;
1594 }
1595 }
1596 }
1597 }
1598
1599 // Because selectAllConnectedTracks() use m_filter to collect connected tracks
1600 // to pads, enable filter for these items, regardless the curent filter options
1601 // via filter is not changed, because it can be useful to keep via filter disabled
1602 struct PCB_SELECTION_FILTER_OPTIONS save_filter = m_filter;
1603 m_filter.tracks = true;
1604 m_filter.pads = true;
1605
1606 // Clear selection so we don't delete our footprints/pads
1607 ClearSelection( true );
1608
1609 // Get the tracks on our list of pads, then delete them
1611
1612 BOARD_COMMIT commit( m_toolMgr );
1613 std::set<BOARD_ITEM*> removed;
1614
1615 for( EDA_ITEM* item : m_selection )
1616 {
1617 if( !item->IsBOARD_ITEM() )
1618 continue;
1619
1620 BOARD_ITEM* bi = static_cast<BOARD_ITEM*>( item );
1621
1622 if( bi->Type() == PCB_GENERATOR_T )
1623 toDelete.insert( bi );
1624
1625 commit.Remove( bi );
1626 removed.insert( bi );
1627 }
1628
1629 // Find generators whose children were removed
1630 for( PCB_GENERATOR* gen : board()->Generators() )
1631 {
1632 for( BOARD_ITEM* child : gen->GetBoardItems() )
1633 {
1634 if( removed.count( child ) )
1635 {
1636 toDelete.insert( gen );
1637 break;
1638 }
1639 }
1640 }
1641
1642 for( EDA_ITEM* item : toDelete )
1643 {
1644 if( !item->IsBOARD_ITEM() )
1645 continue;
1646
1647 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
1648
1649 boardItem->RunOnChildren(
1650 [&commit, &removed]( BOARD_ITEM* aItem )
1651 {
1652 if( removed.find( aItem ) == removed.end() )
1653 {
1654 commit.Remove( aItem );
1655 removed.insert( aItem );
1656 }
1657 },
1659
1660 if( removed.find( boardItem ) == removed.end() )
1661 commit.Remove( boardItem );
1662 }
1663
1664 ClearSelection( true );
1665 commit.Push( _( "Unroute Selected" ) );
1666
1667 m_filter = save_filter; // restore current filter options
1668
1669 // Reselect our footprint/pads as they were in our original selection
1670 for( EDA_ITEM* item : selectedItems )
1671 {
1672 if( item->Type() == PCB_FOOTPRINT_T || item->Type() == PCB_PAD_T )
1673 select( item );
1674 }
1675
1676 return 0;
1677}
1678
1679
1681{
1682 std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
1683
1684 // Get all footprints and pads
1685 std::vector<BOARD_CONNECTED_ITEM*> toUnroute;
1686
1687 std::set<EDA_ITEM*> toDelete;
1688
1689 for( EDA_ITEM* item : selectedItems )
1690 {
1691 if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T || item->Type() == PCB_VIA_T )
1692 {
1693 BOARD_ITEM* bi = static_cast<BOARD_ITEM*>( item );
1694 EDA_GROUP* parentGroup = bi->GetParentGroup();
1695
1696 if( parentGroup && parentGroup->AsEdaItem()->Type() == PCB_GENERATOR_T )
1697 {
1698 PCB_GENERATOR* gen = static_cast<PCB_GENERATOR*>( parentGroup->AsEdaItem() );
1699
1700 if( !toDelete.count( gen ) )
1701 {
1702 toDelete.insert( gen );
1703
1704 for( BOARD_ITEM* generatedItem : gen->GetBoardItems() )
1705 {
1706 toDelete.insert( generatedItem );
1707
1708 if( BOARD_CONNECTED_ITEM::ClassOf( generatedItem ) )
1709 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( generatedItem ) );
1710 }
1711 }
1712 }
1713 else
1714 {
1715 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
1716 toDelete.insert( item );
1717 }
1718 }
1719 else if( item->Type() == PCB_GENERATOR_T )
1720 {
1721 toDelete.insert( item );
1722
1723 for( BOARD_ITEM* generatedItem : static_cast<PCB_GENERATOR*>( item )->GetBoardItems() )
1724 {
1725 toDelete.insert( generatedItem );
1726
1727 if( BOARD_CONNECTED_ITEM::ClassOf( generatedItem ) )
1728 toUnroute.push_back( static_cast<BOARD_CONNECTED_ITEM*>( generatedItem ) );
1729 }
1730 }
1731 }
1732
1733 // Get the tracks connecting to our starting objects
1736 std::deque<EDA_ITEM*> toSelectAfter;
1737 // This will select the unroute items too, so filter them out
1738 for( EDA_ITEM* item : m_selection.GetItemsSortedByTypeAndXY() )
1739 {
1740 if( !item->IsBOARD_ITEM() )
1741 continue;
1742
1743 if( toDelete.find( item ) == toDelete.end() )
1744 toSelectAfter.push_back( item );
1745 }
1746
1747 BOARD_COMMIT commit( m_toolMgr );
1748 std::set<BOARD_ITEM*> removed;
1749
1750 for( EDA_ITEM* item : toDelete )
1751 {
1752 if( !item->IsBOARD_ITEM() )
1753 continue;
1754
1755 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
1756
1757 if( item->Type() == PCB_GENERATOR_T )
1758 {
1759 boardItem->RunOnChildren(
1760 [&commit, &removed]( BOARD_ITEM* aItem )
1761 {
1762 if( removed.insert( aItem ).second )
1763 commit.Remove( aItem );
1764 },
1766
1767 if( removed.insert( boardItem ).second )
1768 commit.Remove( boardItem );
1769 }
1770 else
1771 {
1772 if( removed.insert( boardItem ).second )
1773 commit.Remove( boardItem );
1774 }
1775 }
1776
1777 commit.Push( _( "Unroute Segment" ) );
1778
1779 // Now our after tracks so the user can continue backing up as desired
1780 ClearSelection( true );
1781
1782 for( EDA_ITEM* item : toSelectAfter )
1783 {
1784 if( !toDelete.count( item ) )
1785 select( item );
1786 }
1787
1788 return 0;
1789}
1790
1791
1793{
1794 // expandConnection will get called no matter whether the user selected a connected item or a
1795 // non-connected shape (graphic on a non-copper layer). The algorithm for expanding to connected
1796 // items is different from graphics, so they need to be handled separately.
1797 unsigned initialCount = 0;
1798
1799 for( const EDA_ITEM* item : m_selection.GetItems() )
1800 {
1801 if( item->Type() == PCB_FOOTPRINT_T
1802 || item->Type() == PCB_GENERATOR_T
1803 || ( static_cast<const BOARD_ITEM*>( item )->IsConnected() ) )
1804 {
1805 initialCount++;
1806 }
1807 }
1808
1809 if( initialCount == 0 )
1810 {
1811 // First, process any graphic shapes we have
1812 std::vector<PCB_SHAPE*> startShapes;
1813
1814 for( EDA_ITEM* item : m_selection.GetItems() )
1815 {
1816 if( isExpandableGraphicShape( item ) )
1817 startShapes.push_back( static_cast<PCB_SHAPE*>( item ) );
1818 }
1819
1820 // If no non-copper shapes; fall back to looking for connected items
1821 if( !startShapes.empty() )
1822 selectAllConnectedShapes( startShapes );
1823 else
1825 }
1826
1827 m_frame->SetStatusText( _( "Select/Expand Connection..." ) );
1828
1829 for( STOP_CONDITION stopCondition : { STOP_AT_JUNCTION, STOP_AT_PAD, STOP_NEVER } )
1830 {
1831 std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
1832
1833 for( EDA_ITEM* item : selectedItems )
1834 item->ClearTempFlags();
1835
1836 std::vector<BOARD_CONNECTED_ITEM*> startItems;
1837
1838 for( EDA_ITEM* item : selectedItems )
1839 {
1840 if( item->Type() == PCB_FOOTPRINT_T )
1841 {
1842 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
1843
1844 for( PAD* pad : footprint->Pads() )
1845 startItems.push_back( pad );
1846 }
1847 else if( item->Type() == PCB_GENERATOR_T )
1848 {
1849 for( BOARD_ITEM* generatedItem : static_cast<PCB_GENERATOR*>( item )->GetBoardItems() )
1850 {
1851 if( BOARD_CONNECTED_ITEM::ClassOf( generatedItem ) )
1852 startItems.push_back( static_cast<BOARD_CONNECTED_ITEM*>( generatedItem ) );
1853 }
1854 }
1855 else if( BOARD_CONNECTED_ITEM::ClassOf( item ) )
1856 {
1857 startItems.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
1858 }
1859 }
1860
1861 selectAllConnectedTracks( startItems, stopCondition );
1862
1863 if( m_selection.GetItems().size() > initialCount )
1864 break;
1865 }
1866
1867 m_frame->SetStatusText( wxEmptyString );
1868
1869 // Inform other potentially interested tools
1870 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1871
1872 return 0;
1873}
1874
1875
1876void PCB_SELECTION_TOOL::selectAllConnectedTracks( const std::vector<BOARD_CONNECTED_ITEM*>& aStartItems,
1877 STOP_CONDITION aStopCondition )
1878{
1879 PROF_TIMER refreshTimer;
1880 double refreshIntervalMs = 500; // Refresh display with this interval to indicate progress
1881 int lastSelectionSize = (int) m_selection.GetSize();
1882
1883 auto connectivity = board()->GetConnectivity();
1884
1885 std::set<PAD*> startPadSet;
1886 std::vector<BOARD_CONNECTED_ITEM*> cleanupItems;
1887
1888 for( BOARD_CONNECTED_ITEM* startItem : aStartItems )
1889 {
1890 // Register starting pads
1891 if( startItem->Type() == PCB_PAD_T )
1892 startPadSet.insert( static_cast<PAD*>( startItem ) );
1893
1894 // Select any starting track items
1895 if( startItem->IsType( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } ) )
1896 {
1897 if( itemPassesFilter( startItem, true ) )
1898 select( startItem );
1899 }
1900 }
1901
1902 for( BOARD_CONNECTED_ITEM* startItem : aStartItems )
1903 {
1904 std::map<VECTOR2I, std::vector<PCB_TRACK*>> trackMap;
1905 std::map<VECTOR2I, PCB_VIA*> viaMap;
1906 std::map<VECTOR2I, PAD*> padMap;
1907 std::map<VECTOR2I, std::vector<PCB_SHAPE*>> shapeMap;
1908 std::vector<std::pair<VECTOR2I, LSET>> activePts;
1909
1910 if( startItem->HasFlag( SKIP_STRUCT ) ) // Skip already visited items
1911 continue;
1912
1913 auto connectedItems = connectivity->GetConnectedItems( startItem, EXCLUDE_ZONES | IGNORE_NETS );
1914
1915 // Build maps of connected items
1916 for( BOARD_CONNECTED_ITEM* item : connectedItems )
1917 {
1918 switch( item->Type() )
1919 {
1920 case PCB_ARC_T:
1921 case PCB_TRACE_T:
1922 {
1923 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
1924 trackMap[track->GetStart()].push_back( track );
1925 trackMap[track->GetEnd()].push_back( track );
1926 break;
1927 }
1928
1929 case PCB_VIA_T:
1930 {
1931 PCB_VIA* via = static_cast<PCB_VIA*>( item );
1932 viaMap[via->GetStart()] = via;
1933 break;
1934 }
1935
1936 case PCB_PAD_T:
1937 {
1938 PAD* pad = static_cast<PAD*>( item );
1939 padMap[pad->GetPosition()] = pad;
1940 break;
1941 }
1942
1943 case PCB_SHAPE_T:
1944 {
1945 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
1946
1947 for( const auto& point : shape->GetConnectionPoints() )
1948 shapeMap[point].push_back( shape );
1949
1950 break;
1951 }
1952
1953 default:
1954 break;
1955 }
1956 }
1957
1958 // Set up the initial active points
1959 switch( startItem->Type() )
1960 {
1961 case PCB_ARC_T:
1962 case PCB_TRACE_T:
1963 {
1964 PCB_TRACK* track = static_cast<PCB_TRACK*>( startItem );
1965
1966 activePts.push_back( { track->GetStart(), track->GetLayerSet() } );
1967 activePts.push_back( { track->GetEnd(), track->GetLayerSet() } );
1968 break;
1969 }
1970
1971 case PCB_VIA_T:
1972 activePts.push_back( { startItem->GetPosition(), startItem->GetLayerSet() } );
1973 break;
1974
1975 case PCB_PAD_T:
1976 activePts.push_back( { startItem->GetPosition(), startItem->GetLayerSet() } );
1977 break;
1978
1979 case PCB_SHAPE_T:
1980 {
1981 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( startItem );
1982
1983 for( const auto& point : shape->GetConnectionPoints() )
1984 activePts.push_back( { point, startItem->GetLayerSet() } );
1985
1986 break;
1987 }
1988
1989 default:
1990 break;
1991 }
1992
1993 bool expand = true;
1994 int failSafe = 0;
1995
1996 // Iterative push from all active points
1997 while( expand && failSafe++ < 100000 )
1998 {
1999 expand = false;
2000
2001 for( int i = (int) activePts.size() - 1; i >= 0; --i )
2002 {
2003 VECTOR2I pt = activePts[i].first;
2004 LSET layerSetCu = activePts[i].second & LSET::AllCuMask();
2005
2006 auto viaIt = viaMap.find( pt );
2007 auto padIt = padMap.find( pt );
2008
2009 bool gotVia = viaIt != viaMap.end() && ( viaIt->second->GetLayerSet() & layerSetCu ).any();
2010 bool gotPad = padIt != padMap.end() && ( padIt->second->GetLayerSet() & layerSetCu ).any();
2011 bool gotNonStartPad = gotPad && ( startPadSet.find( padIt->second ) == startPadSet.end() );
2012
2013 if( gotPad && !itemPassesFilter( padIt->second, true ) )
2014 {
2015 activePts.erase( activePts.begin() + i );
2016 continue;
2017 }
2018
2019 if( gotVia && !itemPassesFilter( viaIt->second, true ) )
2020 {
2021 activePts.erase( activePts.begin() + i );
2022 continue;
2023 }
2024
2025 if( aStopCondition == STOP_AT_JUNCTION )
2026 {
2027 size_t pt_count = 0;
2028
2029 for( PCB_TRACK* track : trackMap[pt] )
2030 {
2031 if( track->GetStart() != track->GetEnd() && layerSetCu.Contains( track->GetLayer() ) )
2032 pt_count++;
2033 }
2034
2035 if( pt_count > 2 || gotVia || gotNonStartPad )
2036 {
2037 activePts.erase( activePts.begin() + i );
2038 continue;
2039 }
2040 }
2041 else if( aStopCondition == STOP_AT_PAD )
2042 {
2043 if( gotNonStartPad )
2044 {
2045 activePts.erase( activePts.begin() + i );
2046 continue;
2047 }
2048 }
2049
2050 if( gotPad )
2051 {
2052 PAD* pad = padIt->second;
2053
2054 if( !pad->HasFlag( SKIP_STRUCT ) )
2055 {
2056 pad->SetFlags( SKIP_STRUCT );
2057 cleanupItems.push_back( pad );
2058
2059 activePts.push_back( { pad->GetPosition(), pad->GetLayerSet() } );
2060 expand = true;
2061 }
2062 }
2063
2064 for( PCB_TRACK* track : trackMap[pt] )
2065 {
2066 if( !layerSetCu.Contains( track->GetLayer() ) )
2067 continue;
2068
2069 if( !itemPassesFilter( track, true ) )
2070 continue;
2071
2072 if( !track->IsSelected() )
2073 select( track );
2074
2075 if( !track->HasFlag( SKIP_STRUCT ) )
2076 {
2077 track->SetFlags( SKIP_STRUCT );
2078 cleanupItems.push_back( track );
2079
2080 if( track->GetStart() == pt )
2081 activePts.push_back( { track->GetEnd(), track->GetLayerSet() } );
2082 else
2083 activePts.push_back( { track->GetStart(), track->GetLayerSet() } );
2084
2085 if( aStopCondition != STOP_AT_SEGMENT )
2086 expand = true;
2087 }
2088 }
2089
2090 for( PCB_SHAPE* shape : shapeMap[pt] )
2091 {
2092 if( !layerSetCu.Contains( shape->GetLayer() ) )
2093 continue;
2094
2095 if( !itemPassesFilter( shape, true ) )
2096 continue;
2097
2098 if( !shape->IsSelected() )
2099 select( shape );
2100
2101 if( !shape->HasFlag( SKIP_STRUCT ) )
2102 {
2103 shape->SetFlags( SKIP_STRUCT );
2104 cleanupItems.push_back( shape );
2105
2106 for( const VECTOR2I& newPoint : shape->GetConnectionPoints() )
2107 {
2108 if( newPoint == pt )
2109 continue;
2110
2111 activePts.push_back( { newPoint, shape->GetLayerSet() } );
2112 }
2113
2114 if( aStopCondition != STOP_AT_SEGMENT )
2115 expand = true;
2116 }
2117 }
2118
2119 if( viaMap.count( pt ) )
2120 {
2121 PCB_VIA* via = viaMap[pt];
2122
2123 if( !itemPassesFilter( via, true ) )
2124 {
2125 activePts.erase( activePts.begin() + i );
2126 continue;
2127 }
2128
2129 if( !via->IsSelected() )
2130 select( via );
2131
2132 if( !via->HasFlag( SKIP_STRUCT ) )
2133 {
2134 via->SetFlags( SKIP_STRUCT );
2135 cleanupItems.push_back( via );
2136
2137 activePts.push_back( { via->GetPosition(), via->GetLayerSet() } );
2138
2139 if( aStopCondition != STOP_AT_SEGMENT )
2140 expand = true;
2141 }
2142 }
2143
2144 activePts.erase( activePts.begin() + i );
2145 }
2146
2147 // Refresh display for the feel of progress
2148 if( refreshTimer.msecs() >= refreshIntervalMs )
2149 {
2150 if( m_selection.Size() != lastSelectionSize )
2151 {
2152 m_frame->GetCanvas()->ForceRefresh();
2153 lastSelectionSize = m_selection.Size();
2154 }
2155
2156 refreshTimer.Start();
2157 }
2158 }
2159 }
2160
2161 std::set<EDA_ITEM*> toDeselect;
2162 std::set<EDA_ITEM*> toSelect;
2163
2164 // Promote generated members to their PCB_GENERATOR parents
2165 for( EDA_ITEM* item : m_selection )
2166 {
2167 if( !item->IsBOARD_ITEM() )
2168 continue;
2169
2170 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
2171 EDA_GROUP* parent = boardItem->GetParentGroup();
2172
2173 if( parent && parent->AsEdaItem()->Type() == PCB_GENERATOR_T )
2174 {
2175 toDeselect.insert( item );
2176
2177 if( !parent->AsEdaItem()->IsSelected() )
2178 toSelect.insert( parent->AsEdaItem() );
2179 }
2180 }
2181
2182 for( EDA_ITEM* item : toDeselect )
2183 unselect( item );
2184
2185 for( EDA_ITEM* item : toSelect )
2186 select( item );
2187
2188 for( BOARD_CONNECTED_ITEM* item : cleanupItems )
2189 item->ClearFlags( SKIP_STRUCT );
2190}
2191
2192
2194{
2195 if( aItem->Type() == PCB_SHAPE_T )
2196 {
2197 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
2198
2199 switch( shape->GetShape() )
2200 {
2201 case SHAPE_T::SEGMENT:
2202 case SHAPE_T::ARC:
2203 case SHAPE_T::BEZIER:
2204 return !shape->IsOnCopperLayer();
2205
2206 case SHAPE_T::POLY:
2207 return !shape->IsOnCopperLayer() && !shape->IsClosed();
2208
2209 default:
2210 return false;
2211 }
2212 }
2213
2214 return false;
2215}
2216
2217
2218void PCB_SELECTION_TOOL::selectAllConnectedShapes( const std::vector<PCB_SHAPE*>& aStartItems )
2219{
2220 std::stack<PCB_SHAPE*> toSearch;
2221 std::set<PCB_SHAPE*> toCleanup;
2222
2223 for( PCB_SHAPE* startItem : aStartItems )
2224 toSearch.push( startItem );
2225
2226 GENERAL_COLLECTOR collector;
2228
2229 auto searchPoint =
2230 [&]( const VECTOR2I& aWhere )
2231 {
2232 collector.Collect( board(), { PCB_SHAPE_T }, aWhere, guide );
2233
2234 for( EDA_ITEM* item : collector )
2235 {
2236 if( isExpandableGraphicShape( item ) )
2237 toSearch.push( static_cast<PCB_SHAPE*>( item ) );
2238 }
2239 };
2240
2241 while( !toSearch.empty() )
2242 {
2243 PCB_SHAPE* shape = toSearch.top();
2244 toSearch.pop();
2245
2246 if( shape->HasFlag( SKIP_STRUCT ) )
2247 continue;
2248
2249 shape->SetFlags( SKIP_STRUCT );
2250 toCleanup.insert( shape );
2251
2252 if( !itemPassesFilter( shape, true ) )
2253 continue;
2254
2255 select( shape );
2256 guide.SetLayerVisibleBits( shape->GetLayerSet() );
2257
2258 searchPoint( shape->GetStart() );
2259 searchPoint( shape->GetEnd() );
2260 }
2261
2262 for( PCB_SHAPE* shape : toCleanup )
2263 shape->ClearFlags( SKIP_STRUCT );
2264}
2265
2266
2268{
2269 // Get all pads
2270 std::vector<PAD*> pads;
2271
2272 for( EDA_ITEM* item : m_selection.GetItems() )
2273 {
2274 if( item->Type() == PCB_FOOTPRINT_T )
2275 {
2276 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
2277 pads.push_back( pad );
2278 }
2279 else if( item->Type() == PCB_PAD_T )
2280 {
2281 pads.push_back( static_cast<PAD*>( item ) );
2282 }
2283 }
2284
2285 // Select every footprint on the end of the ratsnest for each pad in our selection
2286 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
2287
2288 for( PAD* pad : pads )
2289 {
2290 for( const CN_EDGE& edge : conn->GetRatsnestForPad( pad ) )
2291 {
2292 wxCHECK2( edge.GetSourceNode() && !edge.GetSourceNode()->Dirty(), continue );
2293 wxCHECK2( edge.GetTargetNode() && !edge.GetTargetNode()->Dirty(), continue );
2294
2295 BOARD_CONNECTED_ITEM* sourceParent = edge.GetSourceNode()->Parent();
2296 BOARD_CONNECTED_ITEM* targetParent = edge.GetTargetNode()->Parent();
2297
2298 if( sourceParent == pad )
2299 {
2300 if( targetParent->Type() == PCB_PAD_T )
2301 select( static_cast<PAD*>( targetParent )->GetParent() );
2302 }
2303 else if( targetParent == pad )
2304 {
2305 if( sourceParent->Type() == PCB_PAD_T )
2306 select( static_cast<PAD*>( sourceParent )->GetParent() );
2307 }
2308 }
2309 }
2310
2311 return 0;
2312}
2313
2314
2316{
2317 PCB_SELECTION originalSelection = m_selection;
2318
2319 // Get all pads
2320 std::vector<PAD*> pads;
2321
2322 for( EDA_ITEM* item : m_selection.GetItems() )
2323 {
2324 if( item->Type() == PCB_FOOTPRINT_T )
2325 {
2326 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
2327 pads.push_back( pad );
2328 }
2329 else if( item->Type() == PCB_PAD_T )
2330 {
2331 pads.push_back( static_cast<PAD*>( item ) );
2332 }
2333 }
2334
2336
2337 // Select every footprint on the end of the ratsnest for each pad in our selection
2338 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
2339
2340 for( PAD* pad : pads )
2341 {
2342 const std::vector<CN_EDGE> edges = conn->GetRatsnestForPad( pad );
2343
2344 // Need to have something unconnected to grab
2345 if( edges.size() == 0 )
2346 continue;
2347
2348 double currentDistance = DBL_MAX;
2349 FOOTPRINT* nearest = nullptr;
2350
2351 // Check every ratsnest line for the nearest one
2352 for( const CN_EDGE& edge : edges )
2353 {
2354 if( edge.GetSourceNode()->Parent()->GetParentFootprint()
2355 == edge.GetTargetNode()->Parent()->GetParentFootprint() )
2356 {
2357 continue; // This edge is a loop on the same footprint
2358 }
2359
2360 // Figure out if we are the source or the target node on the ratnest
2361 const CN_ANCHOR* other = edge.GetSourceNode()->Parent() == pad ? edge.GetTargetNode().get()
2362 : edge.GetSourceNode().get();
2363
2364 wxCHECK2( other && !other->Dirty(), continue );
2365
2366 // We only want to grab footprints, so the ratnest has to point to a pad
2367 if( other->Parent()->Type() != PCB_PAD_T )
2368 continue;
2369
2370 if( edge.GetLength() < currentDistance )
2371 {
2372 currentDistance = edge.GetLength();
2373 nearest = other->Parent()->GetParentFootprint();
2374 }
2375 }
2376
2377 if( nearest != nullptr )
2378 select( nearest );
2379 }
2380
2382
2383 return 0;
2384}
2385
2386
2387void PCB_SELECTION_TOOL::SelectAllItemsOnNet( int aNetCode, bool aSelect )
2388{
2389 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
2390
2391 for( BOARD_ITEM* item : conn->GetNetItems( aNetCode, { PCB_TRACE_T,
2392 PCB_ARC_T,
2393 PCB_VIA_T,
2394 PCB_SHAPE_T } ) )
2395 {
2396 if( itemPassesFilter( item, true, nullptr ) )
2397 aSelect ? select( item ) : unselect( item );
2398 }
2399}
2400
2401
2403{
2404 bool select = aEvent.IsAction( &PCB_ACTIONS::selectNet );
2405
2406 // If we've been passed an argument, just select that netcode1
2407 int netcode = aEvent.Parameter<int>();
2408
2409 if( netcode > 0 )
2410 {
2411 SelectAllItemsOnNet( netcode, select );
2412
2413 // Inform other potentially interested tools
2414 if( m_selection.Size() > 0 )
2415 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2416 else
2417 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2418
2419 return 0;
2420 }
2421
2422 if( !selectCursor() )
2423 return 0;
2424
2425 // copy the selection, since we're going to iterate and modify
2426 auto selection = m_selection.GetItems();
2427
2428 for( EDA_ITEM* i : selection )
2429 {
2430 BOARD_CONNECTED_ITEM* connItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( i );
2431
2432 if( connItem )
2433 SelectAllItemsOnNet( connItem->GetNetCode(), select );
2434 }
2435
2436 // Inform other potentially interested tools
2437 if( m_selection.Size() > 0 )
2438 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2439 else
2440 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
2441
2442 return 0;
2443}
2444
2445
2447{
2448 std::vector<BOARD_ITEM*> footprints;
2449
2450 // store all footprints that are on that sheet path
2451 for( FOOTPRINT* footprint : board()->Footprints() )
2452 {
2453 if( footprint == nullptr )
2454 continue;
2455
2456 wxString footprint_path = footprint->GetPath().AsString().BeforeLast( '/' );
2457
2458 if( footprint_path.IsEmpty() )
2459 footprint_path += '/';
2460
2461 if( footprint_path == aSheetPath )
2462 footprints.push_back( footprint );
2463 }
2464
2465 for( BOARD_ITEM* i : footprints )
2466 {
2467 if( i != nullptr )
2468 select( i );
2469 }
2470
2471 selectConnections( footprints );
2472}
2473
2474
2475void PCB_SELECTION_TOOL::selectConnections( const std::vector<BOARD_ITEM*>& aItems )
2476{
2477 // Generate a list of all pads, and of all nets they belong to.
2478 std::list<int> netcodeList;
2479 std::vector<BOARD_CONNECTED_ITEM*> padList;
2480
2481 for( BOARD_ITEM* item : aItems )
2482 {
2483 switch( item->Type() )
2484 {
2485 case PCB_FOOTPRINT_T:
2486 {
2487 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
2488 {
2489 if( pad->IsConnected() )
2490 {
2491 netcodeList.push_back( pad->GetNetCode() );
2492 padList.push_back( pad );
2493 }
2494 }
2495
2496 break;
2497 }
2498
2499 case PCB_PAD_T:
2500 {
2501 PAD* pad = static_cast<PAD*>( item );
2502
2503 if( pad->IsConnected() )
2504 {
2505 netcodeList.push_back( pad->GetNetCode() );
2506 padList.push_back( pad );
2507 }
2508
2509 break;
2510 }
2511
2512 default:
2513 break;
2514 }
2515 }
2516
2517 // Sort for binary search
2518 std::sort( padList.begin(), padList.end() );
2519
2520 // remove all duplicates
2521 netcodeList.sort();
2522 netcodeList.unique();
2523
2525
2526 // now we need to find all footprints that are connected to each of these nets then we need
2527 // to determine if these footprints are in the list of footprints
2528 std::vector<int> removeCodeList;
2529 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
2530
2531 for( int netCode : netcodeList )
2532 {
2533 for( BOARD_CONNECTED_ITEM* pad : conn->GetNetItems( netCode, { PCB_PAD_T } ) )
2534 {
2535 if( !std::binary_search( padList.begin(), padList.end(), pad ) )
2536 {
2537 // if we cannot find the pad in the padList then we can assume that that pad
2538 // should not be used, therefore invalidate this netcode.
2539 removeCodeList.push_back( netCode );
2540 break;
2541 }
2542 }
2543 }
2544
2545 for( int removeCode : removeCodeList )
2546 netcodeList.remove( removeCode );
2547
2548 std::unordered_set<BOARD_ITEM*> localConnectionList;
2549
2550 for( int netCode : netcodeList )
2551 {
2552 for( BOARD_ITEM* item : conn->GetNetItems( netCode, { PCB_TRACE_T,
2553 PCB_ARC_T,
2554 PCB_VIA_T,
2555 PCB_SHAPE_T } ) )
2556 {
2557 localConnectionList.insert( item );
2558 }
2559 }
2560
2561 for( BOARD_ITEM* item : localConnectionList )
2562 select( item );
2563}
2564
2565
2567{
2568 std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
2569
2570 if( items )
2571 doSyncSelection( *items, false );
2572
2573 return 0;
2574}
2575
2576
2578{
2579 std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
2580
2581 if( items )
2582 doSyncSelection( *items, true );
2583
2584 return 0;
2585}
2586
2587
2588void PCB_SELECTION_TOOL::doSyncSelection( const std::vector<BOARD_ITEM*>& aItems, bool aWithNets )
2589{
2590 if( m_selection.Front() && m_selection.Front()->IsMoving() )
2591 return;
2592
2593 // Also check the incoming items. If the cross-probe flash timer cleared the selection
2594 // during a move, Front() would be null but the items are still being actively moved.
2595 for( const BOARD_ITEM* item : aItems )
2596 {
2597 if( item->IsMoving() )
2598 return;
2599 }
2600
2601 ClearSelection( true /*quiet mode*/ );
2602
2603 // Perform individual selection of each item before processing the event.
2604 for( BOARD_ITEM* item : aItems )
2605 select( item );
2606
2607 if( aWithNets )
2608 selectConnections( aItems );
2609
2610 BOX2I bbox = m_selection.GetBoundingBox();
2611
2612 if( bbox.GetWidth() != 0 && bbox.GetHeight() != 0 )
2613 {
2614 if( m_frame->GetPcbNewSettings()->m_CrossProbing.center_on_items )
2615 {
2616 if( m_frame->GetPcbNewSettings()->m_CrossProbing.zoom_to_fit )
2617 ZoomFitCrossProbeBBox( bbox );
2618
2619 m_frame->FocusOnLocation( bbox.Centre() );
2620 }
2621 }
2622
2624
2625 m_frame->GetCanvas()->ForceRefresh();
2626
2627 if( m_selection.Size() > 0 )
2628 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2629}
2630
2631
2633{
2634 ClearSelection( true /*quiet mode*/ );
2635 wxString sheetPath = *aEvent.Parameter<wxString*>();
2636
2637 selectAllItemsOnSheet( sheetPath );
2638
2640
2641 if( m_selection.Size() > 0 )
2642 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2643
2644 return 0;
2645}
2646
2647
2649{
2650 // this function currently only supports footprints since they are only on one sheet.
2651 EDA_ITEM* item = m_selection.Front();
2652
2653 if( !item )
2654 return 0;
2655
2656 if( item->Type() != PCB_FOOTPRINT_T )
2657 return 0;
2658
2659 FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( item );
2660
2661 if( !footprint || footprint->GetPath().empty() )
2662 return 0;
2663
2664 ClearSelection( true /*quiet mode*/ );
2665
2666 // get the sheet path only.
2667 wxString sheetPath = footprint->GetPath().AsString().BeforeLast( '/' );
2668
2669 if( sheetPath.IsEmpty() )
2670 sheetPath += '/';
2671
2672 selectAllItemsOnSheet( sheetPath );
2673
2674 // Inform other potentially interested tools
2675 if( m_selection.Size() > 0 )
2676 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2677
2678 return 0;
2679}
2680
2681
2683{
2684 // Should recalculate the view to zoom in on the selection.
2685 BOX2I selectionBox = m_selection.GetBoundingBox();
2687
2688 VECTOR2D screenSize = view->ToWorld( ToVECTOR2D( m_frame->GetCanvas()->GetClientSize() ),
2689 false );
2690 screenSize.x = std::max( 10.0, screenSize.x );
2691 screenSize.y = std::max( 10.0, screenSize.y );
2692
2693 if( selectionBox.GetWidth() != 0 || selectionBox.GetHeight() != 0 )
2694 {
2695 VECTOR2D vsize = selectionBox.GetSize();
2696 double scale = view->GetScale() / std::max( fabs( vsize.x / screenSize.x ),
2697 fabs( vsize.y / screenSize.y ) );
2698 view->SetScale( scale );
2699 view->SetCenter( selectionBox.Centre() );
2700 view->Add( &m_selection );
2701 }
2702
2703 m_frame->GetCanvas()->ForceRefresh();
2704}
2705
2706
2708{
2709 // Should recalculate the view to zoom in on the bbox.
2711
2712 if( aBBox.GetWidth() == 0 )
2713 return;
2714
2715 BOX2I bbox = aBBox;
2716 bbox.Normalize();
2717
2718 //#define DEFAULT_PCBNEW_CODE // Un-comment for normal full zoom KiCad algorithm
2719#ifdef DEFAULT_PCBNEW_CODE
2720 auto bbSize = bbox.Inflate( bbox.GetWidth() * 0.2f ).GetSize();
2721 auto screenSize = view->ToWorld( GetCanvas()->GetClientSize(), false );
2722
2723 // The "fabs" on x ensures the right answer when the view is flipped
2724 screenSize.x = std::max( 10.0, fabs( screenSize.x ) );
2725 screenSize.y = std::max( 10.0, screenSize.y );
2726 double ratio = std::max( fabs( bbSize.x / screenSize.x ), fabs( bbSize.y / screenSize.y ) );
2727
2728 // Try not to zoom on every cross-probe; it gets very noisy
2729 if( crossProbingSettings.zoom_to_fit && ( ratio < 0.5 || ratio > 1.0 ) )
2730 view->SetScale( view->GetScale() / ratio );
2731#endif // DEFAULT_PCBNEW_CODE
2732
2733#ifndef DEFAULT_PCBNEW_CODE // Do the scaled zoom
2734 auto bbSize = bbox.Inflate( KiROUND( bbox.GetWidth() * 0.2 ) ).GetSize();
2735 VECTOR2D screenSize = view->ToWorld( ToVECTOR2D( m_frame->GetCanvas()->GetClientSize() ), false );
2736
2737 // This code tries to come up with a zoom factor that doesn't simply zoom in
2738 // to the cross probed component, but instead shows a reasonable amount of the
2739 // circuit around it to provide context. This reduces or eliminates the need
2740 // to manually change the zoom because it's too close.
2741
2742 // Using the default text height as a constant to compare against, use the
2743 // height of the bounding box of visible items for a footprint to figure out
2744 // if this is a big footprint (like a processor) or a small footprint (like a resistor).
2745 // This ratio is not useful by itself as a scaling factor. It must be "bent" to
2746 // provide good scaling at varying component sizes. Bigger components need less
2747 // scaling than small ones.
2748 double currTextHeight = pcbIUScale.mmToIU( DEFAULT_TEXT_SIZE );
2749
2750 double compRatio = bbSize.y / currTextHeight; // Ratio of component to text height
2751
2752 // This will end up as the scaling factor we apply to "ratio".
2753 double compRatioBent = 1.0;
2754
2755 // This is similar to the original KiCad code that scaled the zoom to make sure
2756 // components were visible on screen. It's simply a ratio of screen size to
2757 // component size, and its job is to zoom in to make the component fullscreen.
2758 // Earlier in the code the component BBox is given a 20% margin to add some
2759 // breathing room. We compare the height of this enlarged component bbox to the
2760 // default text height. If a component will end up with the sides clipped, we
2761 // adjust later to make sure it fits on screen.
2762 //
2763 // The "fabs" on x ensures the right answer when the view is flipped
2764 screenSize.x = std::max( 10.0, fabs( screenSize.x ) );
2765 screenSize.y = std::max( 10.0, screenSize.y );
2766 double ratio = std::max( -1.0, fabs( bbSize.y / screenSize.y ) );
2767
2768 // Original KiCad code for how much to scale the zoom
2769 double kicadRatio = std::max( fabs( bbSize.x / screenSize.x ),
2770 fabs( bbSize.y / screenSize.y ) );
2771
2772 // LUT to scale zoom ratio to provide reasonable schematic context. Must work
2773 // with footprints of varying sizes (e.g. 0402 package and 200 pin BGA).
2774 // "first" is used as the input and "second" as the output
2775 //
2776 // "first" = compRatio (footprint height / default text height)
2777 // "second" = Amount to scale ratio by
2778 std::vector<std::pair<double, double>> lut {
2779 { 1, 8 },
2780 { 1.5, 5 },
2781 { 3, 3 },
2782 { 4.5, 2.5 },
2783 { 8, 2.0 },
2784 { 12, 1.7 },
2785 { 16, 1.5 },
2786 { 24, 1.3 },
2787 { 32, 1.0 },
2788 };
2789
2790
2791 std::vector<std::pair<double, double>>::iterator it;
2792
2793 compRatioBent = lut.back().second; // Large component default
2794
2795 if( compRatio >= lut.front().first )
2796 {
2797 // Use LUT to do linear interpolation of "compRatio" within "first", then
2798 // use that result to linearly interpolate "second" which gives the scaling
2799 // factor needed.
2800
2801 for( it = lut.begin(); it < lut.end() - 1; it++ )
2802 {
2803 if( it->first <= compRatio && next( it )->first >= compRatio )
2804 {
2805 double diffx = compRatio - it->first;
2806 double diffn = next( it )->first - it->first;
2807
2808 compRatioBent = it->second + ( next( it )->second - it->second ) * diffx / diffn;
2809 break; // We have our interpolated value
2810 }
2811 }
2812 }
2813 else
2814 {
2815 compRatioBent = lut.front().second; // Small component default
2816 }
2817
2818 // If the width of the part we're probing is bigger than what the screen width will be
2819 // after the zoom, then punt and use the KiCad zoom algorithm since it guarantees the
2820 // part's width will be encompassed within the screen. This will apply to parts that
2821 // are much wider than they are tall.
2822
2823 if( bbSize.x > screenSize.x * ratio * compRatioBent )
2824 {
2825 // Use standard KiCad zoom algorithm for parts too wide to fit screen/
2826 ratio = kicadRatio;
2827 compRatioBent = 1.0; // Reset so we don't modify the "KiCad" ratio
2828 wxLogTrace( "CROSS_PROBE_SCALE", "Part TOO WIDE for screen. Using normal KiCad zoom ratio: %1.5f", ratio );
2829 }
2830
2831 // Now that "compRatioBent" holds our final scaling factor we apply it to the original
2832 // fullscreen zoom ratio to arrive at the final ratio itself.
2833 ratio *= compRatioBent;
2834
2835 bool alwaysZoom = false; // DEBUG - allows us to minimize zooming or not
2836
2837 // Try not to zoom on every cross-probe; it gets very noisy
2838 if( ( ratio < 0.5 || ratio > 1.0 ) || alwaysZoom )
2839 view->SetScale( view->GetScale() / ratio );
2840#endif // ifndef DEFAULT_PCBNEW_CODE
2841}
2842
2843
2845{
2846 bool cleared = false;
2847
2848 if( m_selection.GetSize() > 0 )
2849 {
2850 // Don't fire an event now; most of the time it will be redundant as we're about to
2851 // fire a SelectedEvent.
2852 cleared = true;
2853 ClearSelection( true /*quiet mode*/ );
2854 }
2855
2856 if( aItem )
2857 {
2858 switch( aItem->Type() )
2859 {
2860 case PCB_NETINFO_T:
2861 {
2862 int netCode = static_cast<NETINFO_ITEM*>( aItem )->GetNetCode();
2863
2864 if( netCode > 0 )
2865 {
2866 SelectAllItemsOnNet( netCode, true );
2867 m_frame->FocusOnLocation( aItem->GetCenter() );
2868 }
2869 break;
2870 }
2871
2872 default:
2873 select( aItem );
2874 m_frame->FocusOnLocation( aItem->GetPosition() );
2875 }
2876
2877 // If the item has a bounding box, then zoom out if needed
2878 if( aItem->GetBoundingBox().GetHeight() > 0 && aItem->GetBoundingBox().GetWidth() > 0 )
2879 {
2880 // This adds some margin
2881 double marginFactor = 2;
2882
2883 KIGFX::PCB_VIEW* pcbView = canvas()->GetView();
2884 BOX2D screenBox = pcbView->GetViewport();
2885 VECTOR2D screenSize = screenBox.GetSize();
2886 BOX2I screenRect = BOX2ISafe( screenBox.GetOrigin(), screenSize / marginFactor );
2887
2888 if( !screenRect.Contains( aItem->GetBoundingBox() ) )
2889 {
2890 double scaleX = screenSize.x / static_cast<double>( aItem->GetBoundingBox().GetWidth() );
2891 double scaleY = screenSize.y / static_cast<double>( aItem->GetBoundingBox().GetHeight() );
2892
2893 scaleX /= marginFactor;
2894 scaleY /= marginFactor;
2895
2896 double scale = scaleX > scaleY ? scaleY : scaleX;
2897
2898 if( scale < 1 ) // Don't zoom in, only zoom out
2899 {
2900 pcbView->SetScale( pcbView->GetScale() * ( scale ) );
2901
2902 //Let's refocus because there is an algorithm to avoid dialogs in there.
2903 m_frame->FocusOnLocation( aItem->GetCenter() );
2904 }
2905 }
2906 }
2907 // Inform other potentially interested tools
2908 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
2909 }
2910 else if( cleared )
2911 {
2912 m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
2913 }
2914
2915 m_frame->GetCanvas()->ForceRefresh();
2916}
2917
2918
2924static bool itemIsIncludedByFilter( const BOARD_ITEM& aItem, const BOARD& aBoard,
2925 const DIALOG_FILTER_SELECTION::OPTIONS& aFilterOptions )
2926{
2927 switch( aItem.Type() )
2928 {
2929 case PCB_FOOTPRINT_T:
2930 {
2931 const FOOTPRINT& footprint = static_cast<const FOOTPRINT&>( aItem );
2932
2933 return aFilterOptions.includeFootprints && ( aFilterOptions.includeLockedFootprints
2934 || !footprint.IsLocked() );
2935 }
2936
2937 case PCB_TRACE_T:
2938 case PCB_ARC_T:
2939 return aFilterOptions.includeTracks;
2940
2941 case PCB_VIA_T:
2942 return aFilterOptions.includeVias;
2943
2944 case PCB_ZONE_T:
2945 return aFilterOptions.includeZones;
2946
2947 case PCB_SHAPE_T:
2948 case PCB_TARGET_T:
2949 case PCB_DIM_ALIGNED_T:
2950 case PCB_DIM_CENTER_T:
2951 case PCB_DIM_RADIAL_T:
2953 case PCB_DIM_LEADER_T:
2954 if( aItem.GetLayer() == Edge_Cuts )
2955 return aFilterOptions.includeBoardOutlineLayer;
2956 else
2957 return aFilterOptions.includeItemsOnTechLayers;
2958
2959 case PCB_FIELD_T:
2960 case PCB_TEXT_T:
2961 case PCB_TEXTBOX_T:
2962 case PCB_TABLE_T:
2963 case PCB_TABLECELL_T:
2964 return aFilterOptions.includePcbTexts;
2965
2966 default:
2967 // Filter dialog is inclusive, not exclusive. If it's not included, then it doesn't
2968 // get selected.
2969 return false;
2970 }
2971}
2972
2973
2975{
2976 const BOARD& board = *getModel<BOARD>();
2977 DIALOG_FILTER_SELECTION::OPTIONS& opts = m_priv->m_filterOpts;
2978 DIALOG_FILTER_SELECTION dlg( m_frame, opts );
2979
2980 const int cmd = dlg.ShowModal();
2981
2982 if( cmd != wxID_OK )
2983 return 0;
2984
2985 // copy current selection
2986 std::deque<EDA_ITEM*> selection = m_selection.GetItems();
2987
2988 ClearSelection( true /*quiet mode*/ );
2989
2990 // re-select items from the saved selection according to the dialog options
2991 for( EDA_ITEM* i : selection )
2992 {
2993 if( !i->IsBOARD_ITEM() )
2994 continue;
2995
2996 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
2997 bool include = itemIsIncludedByFilter( *item, board, opts );
2998
2999 if( include )
3000 select( item );
3001 }
3002
3003 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
3004
3005 return 0;
3006}
3007
3008
3010 PCB_SELECTION_FILTER_OPTIONS* aRejected )
3011{
3012 if( aCollector.GetCount() == 0 )
3013 return;
3014
3015 std::set<BOARD_ITEM*> rejected;
3016
3017 for( EDA_ITEM* i : aCollector )
3018 {
3019 if( !i->IsBOARD_ITEM() )
3020 continue;
3021
3022 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
3023
3024 if( !itemPassesFilter( item, aMultiSelect, aRejected ) )
3025 rejected.insert( item );
3026 }
3027
3028 for( BOARD_ITEM* item : rejected )
3029 aCollector.Remove( item );
3030}
3031
3032
3033bool PCB_SELECTION_TOOL::itemPassesFilter( BOARD_ITEM* aItem, bool aMultiSelect,
3034 PCB_SELECTION_FILTER_OPTIONS* aRejected )
3035{
3036 if( !m_filter.lockedItems )
3037 {
3038 if( aItem->IsLocked() || ( aItem->GetParent() && aItem->GetParent()->IsLocked() ) )
3039 {
3040 if( aItem->Type() == PCB_PAD_T && !aMultiSelect )
3041 {
3042 // allow a single pad to be selected -- there are a lot of operations that
3043 // require this so we allow this one inconsistency
3044 }
3045 else
3046 {
3047 if( aRejected )
3048 aRejected->lockedItems = true;
3049 return false;
3050 }
3051 }
3052 }
3053
3054 if( !aItem )
3055 return false;
3056
3057 KICAD_T itemType = aItem->Type();
3058
3059 if( itemType == PCB_GENERATOR_T )
3060 {
3061 if( static_cast<PCB_GENERATOR*>( aItem )->GetItems().empty() )
3062 {
3063 if( !m_filter.otherItems )
3064 {
3065 if( aRejected )
3066 aRejected->otherItems = true;
3067
3068 return false;
3069 }
3070 }
3071 else
3072 {
3073 itemType = ( *static_cast<PCB_GENERATOR*>( aItem )->GetItems().begin() )->Type();
3074 }
3075 }
3076
3077 switch( itemType )
3078 {
3079 case PCB_FOOTPRINT_T:
3080 if( !m_filter.footprints )
3081 {
3082 if( aRejected )
3083 aRejected->footprints = true;
3084
3085 return false;
3086 }
3087
3088 break;
3089
3090 case PCB_PAD_T:
3091 if( !m_filter.pads )
3092 {
3093 if( aRejected )
3094 aRejected->pads = true;
3095
3096 return false;
3097 }
3098
3099 break;
3100
3101 case PCB_TRACE_T:
3102 case PCB_ARC_T:
3103 if( !m_filter.tracks )
3104 {
3105 if( aRejected )
3106 aRejected->tracks = true;
3107
3108 return false;
3109 }
3110
3111 break;
3112
3113 case PCB_VIA_T:
3114 if( !m_filter.vias )
3115 {
3116 if( aRejected )
3117 aRejected->vias = true;
3118
3119 return false;
3120 }
3121
3122 break;
3123
3124 case PCB_ZONE_T:
3125 {
3126 ZONE* zone = static_cast<ZONE*>( aItem );
3127
3128 if( ( !m_filter.zones && !zone->GetIsRuleArea() )
3129 || ( !m_filter.keepouts && zone->GetIsRuleArea() ) )
3130 {
3131 if( aRejected )
3132 {
3133 if( zone->GetIsRuleArea() )
3134 aRejected->keepouts = true;
3135 else
3136 aRejected->zones = true;
3137 }
3138
3139 return false;
3140 }
3141
3142 // m_SolderMaskBridges zone is a special zone, only used to showsolder mask briges
3143 // after running DRC. it is not really a board item.
3144 // Never select it or delete by a Commit.
3145 if( zone == m_frame->GetBoard()->m_SolderMaskBridges )
3146 return false;
3147
3148 break;
3149 }
3150
3151 case PCB_SHAPE_T:
3152 case PCB_TARGET_T:
3153 if( !m_filter.graphics )
3154 {
3155 if( aRejected )
3156 aRejected->graphics = true;
3157
3158 return false;
3159 }
3160
3161 break;
3162
3164 if( !m_filter.graphics )
3165 {
3166 if( aRejected )
3167 aRejected->graphics = true;
3168
3169 return false;
3170 }
3171
3172 // a reference image living in a footprint must not be selected inside the board editor
3173 if( !m_isFootprintEditor && aItem->GetParentFootprint() )
3174 {
3175 if( aRejected )
3176 aRejected->text = true;
3177
3178 return false;
3179 }
3180
3181 break;
3182
3183 case PCB_FIELD_T:
3184 case PCB_TEXT_T:
3185 case PCB_TEXTBOX_T:
3186 case PCB_TABLE_T:
3187 case PCB_TABLECELL_T:
3188 if( !m_filter.text )
3189 return false;
3190
3191 break;
3192
3193 case PCB_DIM_ALIGNED_T:
3194 case PCB_DIM_CENTER_T:
3195 case PCB_DIM_RADIAL_T:
3197 case PCB_DIM_LEADER_T:
3198 if( !m_filter.dimensions )
3199 {
3200 if( aRejected )
3201 aRejected->dimensions = true;
3202
3203 return false;
3204 }
3205
3206 break;
3207
3208 case PCB_POINT_T:
3209 if( !m_filter.points )
3210 {
3211 if( aRejected )
3212 aRejected->points = true;
3213
3214 return false;
3215 }
3216
3217 break;
3218
3219 case PCB_BARCODE_T:
3220 default:
3221 if( !m_filter.otherItems )
3222 {
3223 if( aRejected )
3224 aRejected->otherItems = true;
3225
3226 return false;
3227 }
3228 }
3229
3230 return true;
3231}
3232
3233
3235{
3236 if( m_selection.Empty() )
3237 return;
3238
3239 while( m_selection.GetSize() )
3241
3242 view()->Update( &m_selection );
3243
3244 m_selection.SetIsHover( false );
3245 m_selection.ClearReferencePoint();
3246
3247 // Inform other potentially interested tools
3248 if( !aQuietMode )
3249 {
3250 m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
3252 }
3253}
3254
3255
3257{
3258 m_selection.Clear();
3259
3260 bool enteredGroupFound = false;
3261
3262 INSPECTOR_FUNC inspector =
3263 [&]( EDA_ITEM* item, void* testData )
3264 {
3265 if( item->IsSelected() )
3266 {
3267 EDA_ITEM* parent = item->GetParent();
3268
3269 // Let selected parents handle their children.
3270 if( parent && parent->IsSelected() )
3272
3273 highlight( item, SELECTED, &m_selection );
3274 }
3275
3276 if( item->Type() == PCB_GROUP_T )
3277 {
3278 if( item == m_enteredGroup )
3279 {
3280 item->SetFlags( ENTERED );
3281 enteredGroupFound = true;
3282 }
3283 else
3284 {
3285 item->ClearFlags( ENTERED );
3286 }
3287 }
3288
3290 };
3291
3294
3295 if( !enteredGroupFound )
3296 {
3297 m_enteredGroupOverlay.Clear();
3298 m_enteredGroup = nullptr;
3299 }
3300}
3301
3302
3303bool PCB_SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibilityOnly ) const
3304{
3305 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
3306 const PCB_DISPLAY_OPTIONS& options = frame()->GetDisplayOptions();
3307
3308 auto visibleLayers =
3309 [&]() -> LSET
3310 {
3312 {
3313 LSET set;
3314
3315 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
3316 set.set( layer, view()->IsLayerVisible( layer ) );
3317
3318 return set;
3319 }
3320 else
3321 {
3322 return board()->GetVisibleLayers();
3323 }
3324 };
3325
3326 auto layerVisible =
3327 [&]( PCB_LAYER_ID aLayer )
3328 {
3330 return view()->IsLayerVisible( aLayer );
3331 else
3332 return board()->IsLayerVisible( aLayer );
3333 };
3334
3335 if( settings->GetHighContrast() )
3336 {
3337 const std::set<int> activeLayers = settings->GetHighContrastLayers();
3338 bool onActiveLayer = false;
3339
3340 for( int layer : activeLayers )
3341 {
3342 // NOTE: Only checking the regular layers (not GAL meta-layers)
3343 if( layer < PCB_LAYER_ID_COUNT && aItem->IsOnLayer( ToLAYER_ID( layer ) ) )
3344 {
3345 onActiveLayer = true;
3346 break;
3347 }
3348 }
3349
3350 if( !onActiveLayer && aItem->Type() != PCB_MARKER_T )
3351 {
3352 // We do not want to select items that are in the background
3353 return false;
3354 }
3355 }
3356
3357 if( aItem->Type() == PCB_FOOTPRINT_T )
3358 {
3359 const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
3360
3361 // In footprint editor, we do not want to select the footprint itself.
3363 return false;
3364
3365 // If the footprint has no items except the reference and value fields, include the
3366 // footprint in the selections.
3367 if( footprint->GraphicalItems().empty()
3368 && footprint->Pads().empty()
3369 && footprint->Zones().empty() )
3370 {
3371 return true;
3372 }
3373
3374 for( const BOARD_ITEM* item : footprint->GraphicalItems() )
3375 {
3376 if( Selectable( item, true ) )
3377 return true;
3378 }
3379
3380 for( const PAD* pad : footprint->Pads() )
3381 {
3382 if( Selectable( pad, true ) )
3383 return true;
3384 }
3385
3386 for( const ZONE* zone : footprint->Zones() )
3387 {
3388 if( Selectable( zone, true ) )
3389 return true;
3390 }
3391
3392 for( const PCB_POINT* point: footprint->Points() )
3393 {
3394 if( Selectable( point, true ) )
3395 return true;
3396 }
3397
3398 return false;
3399 }
3400 else if( aItem->Type() == PCB_GROUP_T )
3401 {
3402 PCB_GROUP* group = const_cast<PCB_GROUP*>( static_cast<const PCB_GROUP*>( aItem ) );
3403
3404 // Similar to logic for footprint, a group is selectable if any of its members are.
3405 // (This recurses.)
3406 for( BOARD_ITEM* item : group->GetBoardItems() )
3407 {
3408 if( Selectable( item, true ) )
3409 return true;
3410 }
3411
3412 return false;
3413 }
3414
3415 if( aItem->GetParentGroup() && aItem->GetParentGroup()->AsEdaItem()->Type() == PCB_GENERATOR_T )
3416 return false;
3417
3418 const ZONE* zone = nullptr;
3419 const PCB_VIA* via = nullptr;
3420 const PAD* pad = nullptr;
3421 const PCB_TEXT* text = nullptr;
3422 const PCB_FIELD* field = nullptr;
3423 const PCB_MARKER* marker = nullptr;
3424 const PCB_TABLECELL* cell = nullptr;
3425
3426 // Most footprint children can only be selected in the footprint editor.
3427 if( aItem->GetParentFootprint() && !m_isFootprintEditor && !checkVisibilityOnly )
3428 {
3429 if( aItem->Type() != PCB_FIELD_T && aItem->Type() != PCB_PAD_T && aItem->Type() != PCB_TEXT_T )
3430 return false;
3431 }
3432
3433 switch( aItem->Type() )
3434 {
3435 case PCB_ZONE_T:
3436 if( !board()->IsElementVisible( LAYER_ZONES ) || ( options.m_ZoneOpacity == 0.00 ) )
3437 return false;
3438
3439 zone = static_cast<const ZONE*>( aItem );
3440
3441 // A teardrop is modelled as a property of a via, pad or the board (for track-to-track
3442 // teardrops). The underlying zone is only an implementation detail.
3443 if( zone->IsTeardropArea() && !board()->LegacyTeardrops() )
3444 return false;
3445
3446 // zones can exist on multiple layers!
3447 if( !( zone->GetLayerSet() & visibleLayers() ).any() )
3448 return false;
3449
3450 break;
3451
3452 case PCB_TRACE_T:
3453 case PCB_ARC_T:
3454 if( !board()->IsElementVisible( LAYER_TRACKS ) || ( options.m_TrackOpacity == 0.00 ) )
3455 return false;
3456
3457 if( !layerVisible( aItem->GetLayer() ) )
3458 return false;
3459
3460 break;
3461
3462 case PCB_VIA_T:
3463 if( !board()->IsElementVisible( LAYER_VIAS ) || ( options.m_ViaOpacity == 0.00 ) )
3464 return false;
3465
3466 via = static_cast<const PCB_VIA*>( aItem );
3467
3468 // For vias it is enough if only one of its layers is visible
3469 if( !( visibleLayers() & via->GetLayerSet() ).any() )
3470 return false;
3471
3472 break;
3473
3474 case PCB_FIELD_T:
3475 field = static_cast<const PCB_FIELD*>( aItem );
3476
3477 if( !field->IsVisible() )
3478 return false;
3479
3480 if( field->IsReference() && !view()->IsLayerVisible( LAYER_FP_REFERENCES ) )
3481 return false;
3482
3483 if( field->IsValue() && !view()->IsLayerVisible( LAYER_FP_VALUES ) )
3484 return false;
3485
3486 // Handle all other fields with normal text visibility controls
3488 case PCB_TEXT_T:
3489 text = static_cast<const PCB_TEXT*>( aItem );
3490
3491 if( !layerVisible( text->GetLayer() ) )
3492 return false;
3493
3494 // Apply the LOD visibility test as well
3495 if( !view()->IsVisible( text ) )
3496 return false;
3497
3498 if( aItem->GetParentFootprint() )
3499 {
3500 int controlLayer = LAYER_FP_TEXT;
3501
3502 if( text->GetText() == wxT( "${REFERENCE}" ) )
3503 controlLayer = LAYER_FP_REFERENCES;
3504 else if( text->GetText() == wxT( "${VALUE}" ) )
3505 controlLayer = LAYER_FP_VALUES;
3506
3507 if( !view()->IsLayerVisible( controlLayer ) )
3508 return false;
3509 }
3510
3511 break;
3512
3514 if( options.m_ImageOpacity == 0.00 )
3515 return false;
3516
3517 // Bitmap images on board are hidden if LAYER_DRAW_BITMAPS is not visible
3518 if( !view()->IsLayerVisible( LAYER_DRAW_BITMAPS ) )
3519 return false;
3520
3521 if( !layerVisible( aItem->GetLayer() ) )
3522 return false;
3523
3524 break;
3525
3526 case PCB_SHAPE_T:
3527 if( options.m_FilledShapeOpacity == 0.0 && static_cast<const PCB_SHAPE*>( aItem )->IsAnyFill() )
3528 return false;
3529
3530 if( !layerVisible( aItem->GetLayer() ) )
3531 return false;
3532
3533 break;
3534
3535 case PCB_BARCODE_T:
3536 if( !layerVisible( aItem->GetLayer() ) )
3537 return false;
3538
3539 break;
3540
3541 case PCB_TEXTBOX_T:
3542 case PCB_TABLE_T:
3543 if( !layerVisible( aItem->GetLayer() ) )
3544 return false;
3545
3546 break;
3547
3548 case PCB_TABLECELL_T:
3549 cell = static_cast<const PCB_TABLECELL*>( aItem );
3550
3551 if( !layerVisible( aItem->GetLayer() ) )
3552 return false;
3553
3554 if( cell->GetRowSpan() == 0 || cell->GetColSpan() == 0 )
3555 return false;
3556
3557 break;
3558
3559 case PCB_DIM_ALIGNED_T:
3560 case PCB_DIM_LEADER_T:
3561 case PCB_DIM_CENTER_T:
3562 case PCB_DIM_RADIAL_T:
3564 if( !layerVisible( aItem->GetLayer() ) )
3565 return false;
3566
3567 break;
3568
3569 case PCB_PAD_T:
3570 if( options.m_PadOpacity == 0.00 )
3571 return false;
3572
3573 pad = static_cast<const PAD*>( aItem );
3574
3575 if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
3576 {
3577 // A pad's hole is visible on every layer the pad is visible on plus many layers the
3578 // pad is not visible on -- so we only need to check for any visible hole layers.
3579 if( !( visibleLayers() & LSET::PhysicalLayersMask() ).any() )
3580 return false;
3581 }
3582 else
3583 {
3584 if( !( pad->GetLayerSet() & visibleLayers() ).any() )
3585 return false;
3586 }
3587
3588 break;
3589
3590 case PCB_MARKER_T:
3591 marker = static_cast<const PCB_MARKER*>( aItem );
3592
3593 if( marker && marker->IsExcluded() && !board()->IsElementVisible( LAYER_DRC_EXCLUSION ) )
3594 return false;
3595
3596 break;
3597
3598 case PCB_POINT_T:
3599 if( !layerVisible( aItem->GetLayer() ) )
3600 return false;
3601
3602 if( !board()->IsElementVisible( LAYER_POINTS ) )
3603 return false;
3604
3605 break;
3606
3607 // These are not selectable
3608 case PCB_NETINFO_T:
3609 case NOT_USED:
3610 case TYPE_NOT_INIT:
3611 return false;
3612
3613 default: // Suppress warnings
3614 break;
3615 }
3616
3617 return true;
3618}
3619
3620
3622{
3623 if( !aItem || aItem->IsSelected() || !aItem->IsBOARD_ITEM() )
3624 return;
3625
3626 if( aItem->Type() == PCB_PAD_T )
3627 {
3628 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem->GetParent() );
3629
3630 if( m_selection.Contains( footprint ) )
3631 return;
3632 }
3633
3634 if( m_enteredGroup && !PCB_GROUP::WithinScope( static_cast<BOARD_ITEM*>( aItem ), m_enteredGroup,
3636 {
3637 ExitGroup();
3638 }
3639
3640 highlight( aItem, SELECTED, &m_selection );
3641}
3642
3643
3645{
3646 unhighlight( aItem, SELECTED, &m_selection );
3647}
3648
3649
3650void PCB_SELECTION_TOOL::highlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
3651{
3652 if( aGroup )
3653 aGroup->Add( aItem );
3654
3655 highlightInternal( aItem, aMode, aGroup != nullptr );
3656 view()->Update( aItem, KIGFX::REPAINT );
3657
3658 // Many selections are very temporal and updating the display each time just
3659 // creates noise.
3660 if( aMode == BRIGHTENED )
3662}
3663
3664
3665void PCB_SELECTION_TOOL::highlightInternal( EDA_ITEM* aItem, int aMode, bool aUsingOverlay )
3666{
3667 if( aMode == SELECTED )
3668 aItem->SetSelected();
3669 else if( aMode == BRIGHTENED )
3670 aItem->SetBrightened();
3671
3672 if( aUsingOverlay && aMode != BRIGHTENED )
3673 view()->Hide( aItem, true ); // Hide the original item, so it is shown only on overlay
3674
3675 if( aItem->IsBOARD_ITEM() )
3676 {
3677 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( aItem );
3678 boardItem->RunOnChildren( std::bind( &PCB_SELECTION_TOOL::highlightInternal, this, _1, aMode, aUsingOverlay ),
3680 }
3681}
3682
3683
3684void PCB_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
3685{
3686 if( aGroup )
3687 aGroup->Remove( aItem );
3688
3689 unhighlightInternal( aItem, aMode, aGroup != nullptr );
3690 view()->Update( aItem, KIGFX::REPAINT );
3691
3692 // Many selections are very temporal and updating the display each time just creates noise.
3693 if( aMode == BRIGHTENED )
3695}
3696
3697
3698void PCB_SELECTION_TOOL::unhighlightInternal( EDA_ITEM* aItem, int aMode, bool aUsingOverlay )
3699{
3700 if( aMode == SELECTED )
3701 aItem->ClearSelected();
3702 else if( aMode == BRIGHTENED )
3703 aItem->ClearBrightened();
3704
3705 if( aUsingOverlay && aMode != BRIGHTENED )
3706 {
3707 view()->Hide( aItem, false ); // Restore original item visibility...
3708 view()->Update( aItem ); // ... and make sure it's redrawn un-selected
3709 }
3710
3711 if( aItem->IsBOARD_ITEM() )
3712 {
3713 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( aItem );
3714 boardItem->RunOnChildren( std::bind( &PCB_SELECTION_TOOL::unhighlightInternal, this, _1, aMode, aUsingOverlay ),
3716 }
3717}
3718
3719
3721{
3722 const unsigned GRIP_MARGIN = 20;
3723 int margin = KiROUND( getView()->ToWorld( GRIP_MARGIN ) );
3724
3725 // Check if the point is located close to any of the currently selected items
3726 for( EDA_ITEM* item : m_selection )
3727 {
3728 if( !item->IsBOARD_ITEM() )
3729 continue;
3730
3731 BOX2I itemBox = item->ViewBBox();
3732 itemBox.Inflate( margin ); // Give some margin for gripping an item
3733
3734 if( itemBox.Contains( aPoint ) )
3735 {
3736 if( item->HitTest( aPoint, margin ) )
3737 return true;
3738
3739 bool found = false;
3740
3741 if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( item ) )
3742 {
3743 group->RunOnChildren(
3744 [&]( BOARD_ITEM* aItem )
3745 {
3746 if( aItem->HitTest( aPoint, margin ) )
3747 found = true;
3748 },
3750 }
3751
3752 if( found )
3753 return true;
3754 }
3755 }
3756
3757 return false;
3758}
3759
3760
3761int PCB_SELECTION_TOOL::hitTestDistance( const VECTOR2I& aWhere, BOARD_ITEM* aItem, int aMaxDistance ) const
3762{
3763 BOX2D viewportD = getView()->GetViewport();
3764 BOX2I viewport = BOX2ISafe( viewportD );
3765 int distance = INT_MAX;
3766 SEG loc( aWhere, aWhere );
3767
3768 switch( aItem->Type() )
3769 {
3770 case PCB_FIELD_T:
3771 case PCB_TEXT_T:
3772 {
3773 PCB_TEXT* text = static_cast<PCB_TEXT*>( aItem );
3774
3775 // Add a bit of slop to text-shapes
3776 if( text->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance ) )
3777 distance = std::clamp( distance - ( aMaxDistance / 2 ), 0, distance );
3778
3779 break;
3780 }
3781
3782 case PCB_TEXTBOX_T:
3783 {
3784 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( aItem );
3785
3786 // Add a bit of slop to text-shapes
3787 if( textbox->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance ) )
3788 distance = std::clamp( distance - ( aMaxDistance / 2 ), 0, distance );
3789
3790 break;
3791 }
3792
3793 case PCB_TABLECELL_T:
3794 {
3795 PCB_TABLECELL* tablecell = static_cast<PCB_TABLECELL*>( aItem );
3796 auto shape = std::make_shared<SHAPE_COMPOUND>( tablecell->MakeEffectiveShapesForHitTesting() );
3797
3798 shape->Collide( loc, aMaxDistance, &distance );
3799
3800 break;
3801 }
3802
3803 case PCB_TABLE_T:
3804 {
3805 PCB_TABLE* table = static_cast<PCB_TABLE*>( aItem );
3806 distance = aMaxDistance;
3807
3808 for( PCB_TABLECELL* cell : table->GetCells() )
3809 distance = std::min( distance, hitTestDistance( aWhere, cell, aMaxDistance ) );
3810
3811 // Tables should defer to their table cells. Never consider them exact.
3812 distance = std::clamp( distance + ( aMaxDistance / 4 ), 0, aMaxDistance );
3813 break;
3814 }
3815
3816 case PCB_ZONE_T:
3817 {
3818 ZONE* zone = static_cast<ZONE*>( aItem );
3819
3820 // Zone borders are very specific
3821 if( zone->HitTestForEdge( aWhere, aMaxDistance / 2 ) )
3822 distance = 0;
3823 else if( zone->HitTestForEdge( aWhere, aMaxDistance ) )
3824 distance = aMaxDistance / 2;
3825 else
3826 aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
3827
3828 break;
3829 }
3830
3831 case PCB_FOOTPRINT_T:
3832 {
3833 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
3834 BOX2I bbox = footprint->GetBoundingBox( false );
3835
3836 try
3837 {
3838 footprint->GetBoundingHull().Collide( loc, aMaxDistance, &distance );
3839 }
3840 catch( const std::exception& e )
3841 {
3842 wxFAIL_MSG( wxString::Format( wxT( "Clipper exception occurred: %s" ), e.what() ) );
3843 }
3844
3845 // Consider footprints larger than the viewport only as a last resort
3846 if( bbox.GetHeight() > viewport.GetHeight() || bbox.GetWidth() > viewport.GetWidth() )
3847 distance = INT_MAX / 2;
3848
3849 break;
3850 }
3851
3852 case PCB_MARKER_T:
3853 {
3854 PCB_MARKER* marker = static_cast<PCB_MARKER*>( aItem );
3855 SHAPE_LINE_CHAIN polygon;
3856
3857 marker->ShapeToPolygon( polygon );
3858 polygon.Move( marker->GetPos() );
3859 polygon.Collide( loc, aMaxDistance, &distance );
3860 break;
3861 }
3862
3863 case PCB_GROUP_T:
3864 case PCB_GENERATOR_T:
3865 {
3866 PCB_GROUP* group = static_cast<PCB_GROUP*>( aItem );
3867
3868 for( BOARD_ITEM* member : group->GetBoardItems() )
3869 distance = std::min( distance, hitTestDistance( aWhere, member, aMaxDistance ) );
3870
3871 break;
3872 }
3873
3874 case PCB_PAD_T:
3875 {
3876 static_cast<PAD*>( aItem )->Padstack().ForEachUniqueLayer(
3877 [&]( PCB_LAYER_ID aLayer )
3878 {
3879 int layerDistance = INT_MAX;
3880 aItem->GetEffectiveShape( aLayer )->Collide( loc, aMaxDistance, &layerDistance );
3881 distance = std::min( distance, layerDistance );
3882 } );
3883
3884 break;
3885 }
3886
3887 default:
3888 aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
3889 break;
3890 }
3891
3892 return distance;
3893}
3894
3895
3897{
3898 wxCHECK( m_frame, /* void */ );
3899
3900 if( aCollector.GetCount() < 2 )
3901 return;
3902
3903 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
3904
3905 wxCHECK( settings, /* void */ );
3906
3907 PCB_LAYER_ID activeLayer = m_frame->GetActiveLayer();
3908 LSET visibleLayers = m_frame->GetBoard()->GetVisibleLayers();
3909 LSET enabledLayers = m_frame->GetBoard()->GetEnabledLayers();
3910 LSEQ enabledLayerStack = enabledLayers.SeqStackupTop2Bottom( activeLayer );
3911
3912 wxCHECK( !enabledLayerStack.empty(), /* void */ );
3913
3914 auto isZoneFillKeepout =
3915 []( const BOARD_ITEM* aItem ) -> bool
3916 {
3917 if( aItem->Type() == PCB_ZONE_T )
3918 {
3919 const ZONE* zone = static_cast<const ZONE*>( aItem );
3920
3921 if( zone->GetIsRuleArea() && zone->GetDoNotAllowZoneFills() )
3922 return true;
3923 }
3924
3925 return false;
3926 };
3927
3928 std::vector<LAYER_OPACITY_ITEM> opacityStackup;
3929
3930 for( int i = 0; i < aCollector.GetCount(); i++ )
3931 {
3932 const BOARD_ITEM* item = aCollector[i];
3933
3934 LSET itemLayers = item->GetLayerSet() & enabledLayers & visibleLayers;
3935 LSEQ itemLayerSeq = itemLayers.Seq( enabledLayerStack );
3936
3937 for( PCB_LAYER_ID layer : itemLayerSeq )
3938 {
3939 COLOR4D color = settings->GetColor( item, layer );
3940
3941 if( color.a == 0 )
3942 continue;
3943
3944 LAYER_OPACITY_ITEM opacityItem;
3945
3946 opacityItem.m_Layer = layer;
3947 opacityItem.m_Opacity = color.a;
3948 opacityItem.m_Item = item;
3949
3950 if( isZoneFillKeepout( item ) )
3951 opacityItem.m_Opacity = 0.0;
3952
3953 opacityStackup.emplace_back( opacityItem );
3954 }
3955 }
3956
3957 std::sort( opacityStackup.begin(), opacityStackup.end(),
3958 [&]( const LAYER_OPACITY_ITEM& aLhs, const LAYER_OPACITY_ITEM& aRhs ) -> bool
3959 {
3960 int retv = enabledLayerStack.TestLayers( aLhs.m_Layer, aRhs.m_Layer );
3961
3962 if( retv )
3963 return retv > 0;
3964
3965 return aLhs.m_Opacity > aRhs.m_Opacity;
3966 } );
3967
3968 std::set<const BOARD_ITEM*> visibleItems;
3969 std::set<const BOARD_ITEM*> itemsToRemove;
3970 double minAlphaLimit = ADVANCED_CFG::GetCfg().m_PcbSelectionVisibilityRatio;
3971 double currentStackupOpacity = 0.0;
3973
3974 for( const LAYER_OPACITY_ITEM& opacityItem : opacityStackup )
3975 {
3976 if( lastVisibleLayer == PCB_LAYER_ID::UNDEFINED_LAYER )
3977 {
3978 currentStackupOpacity = opacityItem.m_Opacity;
3979 lastVisibleLayer = opacityItem.m_Layer;
3980 visibleItems.emplace( opacityItem.m_Item );
3981 continue;
3982 }
3983
3984 // Objects to ignore and fallback to the old selection behavior.
3985 auto ignoreItem =
3986 [&]()
3987 {
3988 const BOARD_ITEM* item = opacityItem.m_Item;
3989
3990 wxCHECK( item, false );
3991
3992 // Check items that span multiple layers for visibility.
3993 if( visibleItems.count( item ) )
3994 return true;
3995
3996 // Don't prune child items of a footprint that is already visible.
3997 if( item->GetParent()
3998 && ( item->GetParent()->Type() == PCB_FOOTPRINT_T )
3999 && visibleItems.count( item->GetParent() ) )
4000 {
4001 return true;
4002 }
4003
4004 // Keepout zones are transparent but for some reason, PCB_PAINTER::GetColor()
4005 // returns the color of the zone it prevents from filling.
4006 if( isZoneFillKeepout( item ) )
4007 return true;
4008
4009 return false;
4010 };
4011
4012 // Everything on the currently selected layer is visible;
4013 if( opacityItem.m_Layer == enabledLayerStack[0] )
4014 {
4015 visibleItems.emplace( opacityItem.m_Item );
4016 }
4017 else
4018 {
4019 double itemVisibility = opacityItem.m_Opacity * ( 1.0 - currentStackupOpacity );
4020
4021 if( ( itemVisibility <= minAlphaLimit ) && !ignoreItem() )
4022 itemsToRemove.emplace( opacityItem.m_Item );
4023 else
4024 visibleItems.emplace( opacityItem.m_Item );
4025 }
4026
4027 if( opacityItem.m_Layer != lastVisibleLayer )
4028 {
4029 currentStackupOpacity += opacityItem.m_Opacity * ( 1.0 - currentStackupOpacity );
4030 currentStackupOpacity = std::min( currentStackupOpacity, 1.0 );
4031 lastVisibleLayer = opacityItem.m_Layer;
4032 }
4033 }
4034
4035 for( const BOARD_ITEM* itemToRemove : itemsToRemove )
4036 {
4037 wxCHECK( aCollector.GetCount() > 1, /* void */ );
4038 aCollector.Remove( itemToRemove );
4039 }
4040}
4041
4042
4043// The general idea here is that if the user clicks directly on a small item inside a larger
4044// one, then they want the small item. The quintessential case of this is clicking on a pad
4045// within a footprint, but we also apply it for text within a footprint, footprints within
4046// larger footprints, and vias within either larger pads or longer tracks.
4047//
4048// These "guesses" presume there is area within the larger item to click in to select it. If
4049// an item is mostly covered by smaller items within it, then the guesses are inappropriate as
4050// there might not be any area left to click to select the larger item. In this case we must
4051// leave the items in the collector and bring up a Selection Clarification menu.
4052//
4053// We currently check for pads and text mostly covering a footprint, but we don't check for
4054// smaller footprints mostly covering a larger footprint.
4055//
4057 const VECTOR2I& aWhere ) const
4058{
4059 static const LSET silkLayers( { B_SilkS, F_SilkS } );
4060 static const LSET courtyardLayers( { B_CrtYd, F_CrtYd } );
4061 static std::vector<KICAD_T> singleLayerSilkTypes = { PCB_FIELD_T,
4065 PCB_BARCODE_T };
4066
4067 if( ADVANCED_CFG::GetCfg().m_PcbSelectionVisibilityRatio != 1.0 )
4069
4070 if( aCollector.GetCount() == 1 )
4071 return;
4072
4073 std::set<BOARD_ITEM*> preferred;
4074 std::set<BOARD_ITEM*> rejected;
4075 VECTOR2I where( aWhere.x, aWhere.y );
4076 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
4077 PCB_LAYER_ID activeLayer = m_frame->GetActiveLayer();
4078
4079 // If a silk layer is in front, we assume the user is working with silk and give preferential
4080 // treatment to single-layer items on *either* silk layer.
4081 if( silkLayers[activeLayer] )
4082 {
4083 for( int i = 0; i < aCollector.GetCount(); ++i )
4084 {
4085 BOARD_ITEM* item = aCollector[i];
4086
4087 if( item->IsType( singleLayerSilkTypes ) && silkLayers[ item->GetLayer() ] )
4088 preferred.insert( item );
4089 }
4090 }
4091 // Similarly, if a courtyard layer is in front, we assume the user is positioning footprints
4092 // and give preferential treatment to footprints on *both* top and bottom.
4093 else if( courtyardLayers[activeLayer] && settings->GetHighContrast() )
4094 {
4095 for( int i = 0; i < aCollector.GetCount(); ++i )
4096 {
4097 BOARD_ITEM* item = aCollector[i];
4098
4099 if( item->Type() == PCB_FOOTPRINT_T )
4100 preferred.insert( item );
4101 }
4102 }
4103
4104 if( preferred.size() > 0 )
4105 {
4106 aCollector.Empty();
4107
4108 for( BOARD_ITEM* item : preferred )
4109 aCollector.Append( item );
4110
4111 if( preferred.size() == 1 )
4112 return;
4113 }
4114
4115 // Prefer exact hits to sloppy ones
4116 constexpr int MAX_SLOP = 5;
4117
4118 int singlePixel = KiROUND( aCollector.GetGuide()->OnePixelInIU() );
4119 int maxSlop = KiROUND( MAX_SLOP * aCollector.GetGuide()->OnePixelInIU() );
4120 int minSlop = INT_MAX;
4121
4122 std::map<BOARD_ITEM*, int> itemsBySloppiness;
4123
4124 for( int i = 0; i < aCollector.GetCount(); ++i )
4125 {
4126 BOARD_ITEM* item = aCollector[i];
4127 int itemSlop = hitTestDistance( where, item, maxSlop );
4128
4129 itemsBySloppiness[ item ] = itemSlop;
4130
4131 if( itemSlop < minSlop )
4132 minSlop = itemSlop;
4133 }
4134
4135 // Prune sloppier items
4136 if( minSlop < INT_MAX )
4137 {
4138 for( std::pair<BOARD_ITEM*, int> pair : itemsBySloppiness )
4139 {
4140 if( pair.second > minSlop + singlePixel )
4141 aCollector.Transfer( pair.first );
4142 }
4143 }
4144
4145 // If the user clicked on a small item within a much larger one then it's pretty clear
4146 // they're trying to select the smaller one.
4147 constexpr double sizeRatio = 1.5;
4148
4149 std::vector<std::pair<BOARD_ITEM*, double>> itemsByArea;
4150
4151 for( int i = 0; i < aCollector.GetCount(); ++i )
4152 {
4153 BOARD_ITEM* item = aCollector[i];
4154 double area = 0.0;
4155
4156 if( item->Type() == PCB_ZONE_T
4157 && static_cast<ZONE*>( item )->HitTestForEdge( where, maxSlop / 2 ) )
4158 {
4159 // Zone borders are very specific, so make them "small"
4160 area = (double) SEG::Square( singlePixel ) * MAX_SLOP;
4161 }
4162 else if( item->Type() == PCB_VIA_T )
4163 {
4164 // Vias rarely hide other things, and we don't want them deferring to short track
4165 // segments underneath them -- so artificially reduce their size from πr² to r².
4166 area = (double) SEG::Square( static_cast<PCB_VIA*>( item )->GetDrill() / 2 );
4167 }
4168 else if( item->Type() == PCB_REFERENCE_IMAGE_T )
4169 {
4170 BOX2I box = item->GetBoundingBox();
4171 area = (double) box.GetWidth() * box.GetHeight();
4172 }
4173 else
4174 {
4175 try
4176 {
4177 area = FOOTPRINT::GetCoverageArea( item, aCollector );
4178 }
4179 catch( const std::exception& e )
4180 {
4181 wxFAIL_MSG( wxString::Format( wxT( "Clipper exception occurred: %s" ), e.what() ) );
4182 }
4183 }
4184
4185 itemsByArea.emplace_back( item, area );
4186 }
4187
4188 std::sort( itemsByArea.begin(), itemsByArea.end(),
4189 []( const std::pair<BOARD_ITEM*, double>& lhs,
4190 const std::pair<BOARD_ITEM*, double>& rhs ) -> bool
4191 {
4192 return lhs.second < rhs.second;
4193 } );
4194
4195 bool rejecting = false;
4196
4197 for( int i = 1; i < (int) itemsByArea.size(); ++i )
4198 {
4199 if( itemsByArea[i].second > itemsByArea[i-1].second * sizeRatio )
4200 rejecting = true;
4201
4202 if( rejecting )
4203 rejected.insert( itemsByArea[i].first );
4204 }
4205
4206 // Special case: if a footprint is completely covered with other features then there's no
4207 // way to select it -- so we need to leave it in the list for user disambiguation.
4208 constexpr double maxCoverRatio = 0.70;
4209
4210 for( int i = 0; i < aCollector.GetCount(); ++i )
4211 {
4212 if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aCollector[i] ) )
4213 {
4214 if( footprint->CoverageRatio( aCollector ) > maxCoverRatio )
4215 rejected.erase( footprint );
4216 }
4217 }
4218
4219 // Hopefully we've now got what the user wanted.
4220 if( (unsigned) aCollector.GetCount() > rejected.size() ) // do not remove everything
4221 {
4222 for( BOARD_ITEM* item : rejected )
4223 aCollector.Transfer( item );
4224 }
4225
4226 // Finally, what we are left with is a set of items of similar coverage area. We now reject
4227 // any that are not on the active layer, to reduce the number of disambiguation menus shown.
4228 // If the user wants to force-disambiguate, they can either switch layers or use the modifier
4229 // key to force the menu.
4230 if( aCollector.GetCount() > 1 )
4231 {
4232 bool haveItemOnActive = false;
4233 rejected.clear();
4234
4235 for( int i = 0; i < aCollector.GetCount(); ++i )
4236 {
4237 if( !aCollector[i]->IsOnLayer( activeLayer ) )
4238 rejected.insert( aCollector[i] );
4239 else
4240 haveItemOnActive = true;
4241 }
4242
4243 if( haveItemOnActive )
4244 {
4245 for( BOARD_ITEM* item : rejected )
4246 aCollector.Transfer( item );
4247 }
4248 }
4249}
4250
4251
4253{
4254 if( m_frame && m_frame->IsType( FRAME_PCB_EDITOR ) && !m_frame->GetOverrideLocks() )
4255 {
4256 // Iterate from the back so we don't have to worry about removals.
4257 for( int i = (int) aCollector.GetCount() - 1; i >= 0; --i )
4258 {
4259 BOARD_ITEM* item = aCollector[i];
4260 bool lockedDescendant = false;
4261
4262 item->RunOnChildren(
4263 [&]( BOARD_ITEM* curr_item )
4264 {
4265 if( curr_item->IsLocked() )
4266 lockedDescendant = true;
4267 },
4269
4270 if( item->IsLocked() || lockedDescendant )
4271 aCollector.Remove( item );
4272 }
4273 }
4274}
4275
4276
4277void PCB_SELECTION_TOOL::FilterCollectorForHierarchy( GENERAL_COLLECTOR& aCollector, bool aMultiselect ) const
4278{
4279 std::unordered_set<EDA_ITEM*> toAdd;
4280
4281 // Set CANDIDATE on all parents which are included in the GENERAL_COLLECTOR. This
4282 // algorithm is O(3n), whereas checking for the parent inclusion could potentially be O(n^2).
4283 for( int j = 0; j < aCollector.GetCount(); j++ )
4284 {
4285 if( aCollector[j]->GetParent() )
4286 aCollector[j]->GetParent()->ClearFlags( CANDIDATE );
4287
4288 if( aCollector[j]->GetParentFootprint() )
4289 aCollector[j]->GetParentFootprint()->ClearFlags( CANDIDATE );
4290 }
4291
4292 if( aMultiselect )
4293 {
4294 for( int j = 0; j < aCollector.GetCount(); j++ )
4295 aCollector[j]->SetFlags( CANDIDATE );
4296 }
4297
4298 for( int j = 0; j < aCollector.GetCount(); )
4299 {
4300 BOARD_ITEM* item = aCollector[j];
4301 FOOTPRINT* fp = item->GetParentFootprint();
4302 BOARD_ITEM* start = item;
4303
4304 if( !m_isFootprintEditor && fp )
4305 start = fp;
4306
4307 // If a group is entered, disallow selections of objects outside the group.
4309 {
4310 aCollector.Remove( item );
4311 continue;
4312 }
4313
4314 // If any element is a member of a group, replace those elements with the top containing
4315 // group.
4317 {
4318 if( top->AsEdaItem() != item )
4319 {
4320 toAdd.insert( top->AsEdaItem() );
4321 top->AsEdaItem()->SetFlags( CANDIDATE );
4322
4323 aCollector.Remove( item );
4324 continue;
4325 }
4326 }
4327
4328 // Footprints are a bit easier as they can't be nested.
4329 if( fp && ( fp->GetFlags() & CANDIDATE ) )
4330 {
4331 // Remove children of selected items
4332 aCollector.Remove( item );
4333 continue;
4334 }
4335
4336 ++j;
4337 }
4338
4339 for( EDA_ITEM* item : toAdd )
4340 {
4341 if( !aCollector.HasItem( item ) )
4342 aCollector.Append( item );
4343 }
4344}
4345
4346
4348{
4349 std::set<BOARD_ITEM*> to_add;
4350
4351 // Iterate from the back so we don't have to worry about removals.
4352 for( int i = (int) aCollector.GetCount() - 1; i >= 0; --i )
4353 {
4354 BOARD_ITEM* item = aCollector[i];
4355
4356 if( item->Type() == PCB_TABLECELL_T )
4357 {
4358 if( !aCollector.HasItem( item->GetParent() ) )
4359 to_add.insert( item->GetParent() );
4360
4361 aCollector.Remove( item );
4362 }
4363 }
4364
4365 for( BOARD_ITEM* item : to_add )
4366 aCollector.Append( item );
4367}
4368
4369
4371 bool aForcePromotion ) const
4372{
4373 std::set<BOARD_ITEM*> to_add;
4374
4375 // Iterate from the back so we don't have to worry about removals.
4376 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
4377 {
4378 BOARD_ITEM* item = aCollector[i];
4379
4380 if( !m_isFootprintEditor && item->Type() == PCB_PAD_T
4381 && ( !frame()->GetPcbNewSettings()->m_AllowFreePads || aForcePromotion ) )
4382 {
4383 if( !aCollector.HasItem( item->GetParent() ) )
4384 to_add.insert( item->GetParent() );
4385
4386 aCollector.Remove( item );
4387 }
4388 }
4389
4390 for( BOARD_ITEM* item : to_add )
4391 aCollector.Append( item );
4392}
4393
4394
4396{
4397 // Iterate from the back so we don't have to worry about removals.
4398 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
4399 {
4400 BOARD_ITEM* item = aCollector[i];
4401
4402 if( item->Type() == PCB_MARKER_T )
4403 aCollector.Remove( item );
4404 }
4405}
4406
4407
4409 const VECTOR2I& aWhere ) const
4410{
4411 const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings();
4412 BOX2D viewport = getView()->GetViewport();
4413 BOX2I extents = BOX2ISafe( viewport );
4414
4415 bool need_direct_hit = false;
4416 FOOTPRINT* single_fp = nullptr;
4417
4418 // If the designer is not modifying the existing selection AND we already have
4419 // a selection, then we only want to select items that are directly under the cursor.
4420 // This prevents us from being unable to clear the selection when zoomed into a footprint
4421 if( !m_additive && !m_subtractive && !m_exclusive_or && m_selection.GetSize() > 0 )
4422 {
4423 need_direct_hit = true;
4424
4425 for( EDA_ITEM* item : m_selection )
4426 {
4427 FOOTPRINT* fp = nullptr;
4428
4429 if( item->Type() == PCB_FOOTPRINT_T )
4430 fp = static_cast<FOOTPRINT*>( item );
4431 else if( item->IsBOARD_ITEM() )
4432 fp = static_cast<BOARD_ITEM*>( item )->GetParentFootprint();
4433
4434 // If the selection contains items that are not footprints, then don't restrict
4435 // whether we deselect the item or not.
4436 if( !fp )
4437 {
4438 single_fp = nullptr;
4439 break;
4440 }
4441 else if( !single_fp )
4442 {
4443 single_fp = fp;
4444 }
4445 // If the selection contains items from multiple footprints, then don't restrict
4446 // whether we deselect the item or not.
4447 else if( single_fp != fp )
4448 {
4449 single_fp = nullptr;
4450 break;
4451 }
4452 }
4453 }
4454
4455 auto visibleLayers =
4456 [&]() -> LSET
4457 {
4459 {
4460 LSET set;
4461
4462 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
4463 set.set( layer, view()->IsLayerVisible( layer ) );
4464
4465 return set;
4466 }
4467 else
4468 {
4469 return board()->GetVisibleLayers();
4470 }
4471 };
4472
4473 LSET layers = visibleLayers();
4474
4475 if( settings->GetHighContrast() )
4476 {
4477 layers.reset();
4478
4479 const std::set<int> activeLayers = settings->GetHighContrastLayers();
4480
4481 for( int layer : activeLayers )
4482 {
4483 if( layer >= 0 && layer < PCB_LAYER_ID_COUNT )
4484 layers.set( layer );
4485 }
4486 }
4487
4488 // Iterate from the back so we don't have to worry about removals.
4489 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
4490 {
4491 BOARD_ITEM* item = aCollector[i];
4492 FOOTPRINT* fp = dyn_cast<FOOTPRINT*>( item );
4493
4494 if( !fp )
4495 continue;
4496
4497 // Make footprints not difficult to select in high-contrast modes.
4498 if( layers[fp->GetLayer()] )
4499 continue;
4500
4501 BOX2I bbox = fp->GetLayerBoundingBox( layers );
4502
4503 // If the point clicked is not inside the visible bounding box, we can also remove it.
4504 if( !bbox.Contains( aWhere) )
4505 aCollector.Remove( item );
4506
4507 bool has_hit = false;
4508
4509 for( PCB_LAYER_ID layer : layers )
4510 {
4511 if( fp->HitTestOnLayer( extents, false, layer ) )
4512 {
4513 has_hit = true;
4514 break;
4515 }
4516 }
4517
4518 // If the point is outside of the visible bounding box, we can remove it.
4519 if( !has_hit )
4520 {
4521 aCollector.Remove( item );
4522 }
4523 // Do not require a direct hit on this fp if the existing selection only contains
4524 // this fp's items. This allows you to have a selection of pads from a single
4525 // footprint and still click in the center of the footprint to select it.
4526 else if( single_fp )
4527 {
4528 if( fp == single_fp )
4529 continue;
4530 }
4531 else if( need_direct_hit )
4532 {
4533 has_hit = false;
4534
4535 for( PCB_LAYER_ID layer : layers )
4536 {
4537 if( fp->HitTestOnLayer( aWhere, layer ) )
4538 {
4539 has_hit = true;
4540 break;
4541 }
4542 }
4543
4544 if( !has_hit )
4545 aCollector.Remove( item );
4546 }
4547 }
4548}
4549
4550
4552{
4553 getView()->Update( &m_selection );
4555
4556 return 0;
4557}
4558
4559
4561{
4562 std::set<std::pair<PCB_TABLE*, int>> columns;
4563 bool added = false;
4564
4565 for( EDA_ITEM* item : m_selection )
4566 {
4567 if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( item ) )
4568 {
4569 PCB_TABLE* table = static_cast<PCB_TABLE*>( cell->GetParent() );
4570 columns.insert( std::make_pair( table, cell->GetColumn() ) );
4571 }
4572 }
4573
4574 for( auto& [ table, col ] : columns )
4575 {
4576 for( int row = 0; row < table->GetRowCount(); ++row )
4577 {
4578 PCB_TABLECELL* cell = table->GetCell( row, col );
4579
4580 if( !cell->IsSelected() )
4581 {
4582 select( table->GetCell( row, col ) );
4583 added = true;
4584 }
4585 }
4586 }
4587
4588 if( added )
4589 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
4590
4591 return 0;
4592}
4593
4594
4596{
4597 std::set<std::pair<PCB_TABLE*, int>> rows;
4598 bool added = false;
4599
4600 for( EDA_ITEM* item : m_selection )
4601 {
4602 if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( item ) )
4603 {
4604 PCB_TABLE* table = static_cast<PCB_TABLE*>( cell->GetParent() );
4605 rows.insert( std::make_pair( table, cell->GetRow() ) );
4606 }
4607 }
4608
4609 for( auto& [ table, row ] : rows )
4610 {
4611 for( int col = 0; col < table->GetColCount(); ++col )
4612 {
4613 PCB_TABLECELL* cell = table->GetCell( row, col );
4614
4615 if( !cell->IsSelected() )
4616 {
4617 select( table->GetCell( row, col ) );
4618 added = true;
4619 }
4620 }
4621 }
4622
4623 if( added )
4624 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
4625
4626 return 0;
4627}
4628
4629
4631{
4632 std::set<PCB_TABLE*> tables;
4633 bool added = false;
4634
4635 for( EDA_ITEM* item : m_selection )
4636 {
4637 if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( item ) )
4638 tables.insert( static_cast<PCB_TABLE*>( cell->GetParent() ) );
4639 }
4640
4642
4643 for( PCB_TABLE* table : tables )
4644 {
4645 if( !table->IsSelected() )
4646 {
4647 select( table );
4648 added = true;
4649 }
4650 }
4651
4652 if( added )
4653 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
4654
4655 return 0;
4656}
4657
4658
4660{
4662
4666
4673
4691
4696
4698}
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:237
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition board_item.h:139
bool IsLocked() const override
virtual VECTOR2I GetCenter() const
This defaults to the center of the bounding box if not overridden.
Definition board_item.h:117
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:257
virtual void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const
Invoke a function on all children.
Definition board_item.h:213
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:215
virtual bool IsOnCopperLayer() const
Definition board_item.h:156
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
INSPECT_RESULT Visit(INSPECTOR inspector, void *testData, const std::vector< KICAD_T > &scanTypes) override
May be re-implemented for each derived class in order to handle all the types given by its member dat...
Definition board.cpp:2224
bool IsElementVisible(GAL_LAYER_ID aLayer) const
Test whether a given element category is visible.
Definition board.cpp:1034
const LSET & GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition board.cpp:982
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:974
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:563
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:99
virtual VECTOR2I GetPosition() const
Definition eda_item.h:278
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:148
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:117
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:111
void ClearSelected()
Definition eda_item.h:143
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:150
bool IsSelected() const
Definition eda_item.h:128
void SetSelected()
Definition eda_item.h:140
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition eda_item.h:198
void ClearBrightened()
Definition eda_item.h:144
void SetBrightened()
Definition eda_item.h:141
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:239
EDA_ITEM * GetParent() const
Definition eda_item.h:113
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:152
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:151
SHAPE_T GetShape() const
Definition eda_shape.h:169
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:216
bool IsClosed() const
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:174
bool IsAnyFill() const
Definition eda_shape.h:112
virtual std::vector< SHAPE * > MakeEffectiveShapesForHitTesting() const
Definition eda_shape.h:384
virtual bool IsVisible() const
Definition eda_text.h:187
std::shared_ptr< SHAPE_COMPOUND > GetEffectiveTextShape(bool aTriangulate=true, const BOX2I &aBBox=BOX2I(), const EDA_ANGLE &aAngle=ANGLE_0) const
build a list of segments (SHAPE_SEGMENT) to describe a text shape.
static const TOOL_EVENT DisambiguatePoint
Used for hotkey feedback.
Definition actions.h: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:312
PCB_POINTS & Points()
Definition footprint.h:318
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:306
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition footprint.h:339
SHAPE_POLY_SET GetBoundingHull() const
Return a bounding polygon for the shapes and pads in the footprint.
bool IsLocked() const override
Definition footprint.h:544
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:374
DRAWINGS & GraphicalItems()
Definition footprint.h:309
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:277
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition view.cpp:536
virtual void SetScale(double aScale, VECTOR2D aAnchor={ 0, 0 })
Set the scaling factor, zooming around a given anchor point.
Definition view.cpp:576
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition view.cpp:300
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition view.cpp:343
void UpdateAllLayersColor()
Apply the new coloring scheme to all layers.
Definition view.cpp:783
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:426
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:1727
void Hide(VIEW_ITEM *aItem, bool aHide=true, bool aHideOverlay=false)
Temporarily hide the item in the view (e.g.
Definition view.cpp:1675
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition view.h:423
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition view.h:221
void MarkTargetDirty(int aTarget)
Set or clear target 'dirty' flag.
Definition view.h:648
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition view.cpp:1654
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 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:54
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:159
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
const VECTOR2I & GetStart() const
Definition pcb_track.h:97
const VECTOR2I & GetEnd() const
Definition pcb_track.h:94
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:73
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:719
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:694
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:136
bool GetDoNotAllowZoneFills() const
Definition zone.h:729
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:52
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:89
#define BRIGHTENED
item is drawn with a bright contour
#define IS_NEW
New item, just created.
#define SELECTED
Item was manually selected by the user.
#define ENTERED
indicates a group has been entered
#define SKIP_STRUCT
flag indicating that the structure should be ignored
#define CANDIDATE
flag indicating that the structure is connected
#define IS_MOVING
Item being moved.
@ SEGMENT
Definition eda_shape.h:45
@ FRAME_PCB_EDITOR
Definition frame_type.h:42
@ FRAME_FOOTPRINT_VIEWER
Definition frame_type.h:45
@ FRAME_FOOTPRINT_EDITOR
Definition frame_type.h:43
a few functions useful in geometry calculations.
double m_PcbSelectionVisibilityRatio
Board object selection visibility limit.
@ LAYER_POINTS
PCB reference/manual snap points visibility.
Definition layer_ids.h:321
@ LAYER_FOOTPRINTS_FR
Show footprints on front.
Definition layer_ids.h:259
@ LAYER_DRAW_BITMAPS
Draw images.
Definition layer_ids.h:284
@ LAYER_FP_REFERENCES
Show footprints references (when texts are visible).
Definition layer_ids.h:266
@ LAYER_DRC_EXCLUSION
Layer for DRC markers which have been individually excluded.
Definition layer_ids.h:304
@ LAYER_ZONES
Control for copper zone opacity/visibility (color ignored).
Definition layer_ids.h:295
@ LAYER_PADS
Meta control for all pads opacity/visibility (color ignored).
Definition layer_ids.h:292
@ LAYER_TRACKS
Definition layer_ids.h:267
@ LAYER_FP_TEXT
Definition layer_ids.h:240
@ LAYER_FOOTPRINTS_BK
Show footprints on back.
Definition layer_ids.h:260
@ LAYER_FP_VALUES
Show footprints values (when texts are visible).
Definition layer_ids.h:263
@ LAYER_VIAS
Meta control for all vias opacity/visibility.
Definition layer_ids.h:232
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ F_CrtYd
Definition layer_ids.h:116
@ Edge_Cuts
Definition layer_ids.h:112
@ F_SilkS
Definition layer_ids.h:100
@ B_CrtYd
Definition layer_ids.h:115
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ B_SilkS
Definition layer_ids.h:101
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:171
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition lset.cpp: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)
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:78
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:88
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:106
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:103
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:91
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
@ TYPE_NOT_INIT
Definition typeinfo.h:81
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:104
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:111
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:93
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:108
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:92
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:89
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:90
@ NOT_USED
the 3d code uses this value
Definition typeinfo.h:79
@ PCB_MARKER_T
class PCB_MARKER, a marker used to show something
Definition typeinfo.h:99
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:101
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition typeinfo.h:107
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:95
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:86
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:102
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:98
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:94
@ PCB_NETINFO_T
class NETINFO_ITEM, a description of a net
Definition typeinfo.h:110
@ PCB_POINT_T
class PCB_POINT, a 0-dimensional point
Definition typeinfo.h:113
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:96
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:105
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:61
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR2< double > VECTOR2D
Definition vector2d.h:694
VECTOR2D ToVECTOR2D(const wxPoint &aPoint)
Definition vector2wx.h:40