KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_control.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) 2014-2016 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include "pcb_control.h"
28
29#include <kiplatform/ui.h>
30#include <kiway.h>
31#include <tools/edit_tool.h>
33#include <router/router_tool.h>
34#include <pgm_base.h>
35#include <tools/pcb_actions.h>
40#include <board_commit.h>
41#include <board.h>
43#include <board_item.h>
45#include <clipboard.h>
46#include <design_block.h>
48#include <pcb_dimension.h>
52#include <footprint.h>
53#include <pad.h>
54#include <layer_pairs.h>
55#include <pcb_group.h>
57#include <pcb_reference_image.h>
58#include <pcb_textbox.h>
59#include <pcb_table.h>
60#include <pcb_tablecell.h>
61#include <pcb_track.h>
62#include <pcb_generator.h>
64#include <project_pcb.h>
66#include <filename_resolver.h>
67#include <3d_cache/3d_cache.h>
68#include <embedded_files.h>
69#include <wx/filename.h>
70#include <zone.h>
71#include <confirm.h>
72#include <kidialog.h>
74#include <core/kicad_algo.h>
76#include <kicad_clipboard.h>
77#include <origin_viewitem.h>
78#include <pcb_edit_frame.h>
79#include <pcb_painter.h>
81#include <string>
82#include <tool/tool_manager.h>
90#include <widgets/wx_infobar.h>
91#include <wx/hyperlink.h>
92
93
94using namespace std::placeholders;
95
96
97// files.cpp
98extern bool AskLoadBoardFileName( PCB_EDIT_FRAME* aParent, wxString* aFileName, int aCtl = 0 );
99
100// board_tables/board_stackup_table.cpp
101extern PCB_TABLE* Build_Board_Stackup_Table( BOARD* aBoard, EDA_UNITS aDisplayUnits );
102// board_tables/board_characteristics_table.cpp
103extern PCB_TABLE* Build_Board_Characteristics_Table( BOARD* aBoard, EDA_UNITS aDisplayUnits );
104
105
107 PCB_TOOL_BASE( "pcbnew.Control" ),
108 m_frame( nullptr ),
109 m_pickerItem( nullptr )
110{
112}
113
114
118
119
121{
123
124 if( aReason == MODEL_RELOAD || aReason == GAL_SWITCH || aReason == REDRAW )
125 {
126 m_gridOrigin->SetPosition( board()->GetDesignSettings().GetGridOrigin() );
127
128 double backgroundBrightness = m_frame->GetCanvas()->GetGAL()->GetClearColor().GetBrightness();
129 COLOR4D color = m_frame->GetGridColor();
130
131 if( backgroundBrightness > 0.5 )
132 color.Darken( 0.25 );
133 else
134 color.Brighten( 0.25 );
135
136 m_gridOrigin->SetColor( color );
137
138 getView()->Remove( m_gridOrigin.get() );
139 getView()->Add( m_gridOrigin.get() );
140 }
141}
142
143
145{
146 if( m_frame->IsType( FRAME_FOOTPRINT_EDITOR ) || m_frame->IsType( FRAME_PCB_EDITOR ) )
147 {
148 if( aEvent.IsAction( &ACTIONS::newLibrary ) )
149 static_cast<PCB_BASE_EDIT_FRAME*>( m_frame )->CreateNewLibrary( _( "New Footprint Library" ) );
150 else if( aEvent.IsAction( &ACTIONS::addLibrary ) )
151 static_cast<PCB_BASE_EDIT_FRAME*>( m_frame )->AddLibrary( _( "Add Footprint Library" ) );
152 }
153
154 return 0;
155}
156
157
159{
160 if( m_frame->IsType( FRAME_FOOTPRINT_EDITOR ) )
161 static_cast<FOOTPRINT_EDIT_FRAME*>( m_frame )->LoadFootprintFromBoard( nullptr );
162
163 return 0;
164}
165
166
168{
169 if( m_frame->IsType( FRAME_FOOTPRINT_EDITOR ) )
170 static_cast<FOOTPRINT_EDIT_FRAME*>( m_frame )->SaveFootprintToBoard( true );
171 else if( m_frame->IsType( FRAME_FOOTPRINT_VIEWER ) )
172 static_cast<FOOTPRINT_VIEWER_FRAME*>( m_frame )->AddFootprintToPCB();
173
174 return 0;
175}
176
177
179{
180 const wxString fn = *aEvent.Parameter<wxString*>();
181 static_cast<PCB_BASE_EDIT_FRAME*>( m_frame )->AddLibrary( _( "Add Footprint Library" ), fn,
183 return 0;
184}
185
186
188{
189 const wxString fn = *aEvent.Parameter<wxString*>();
190 static_cast<FOOTPRINT_EDIT_FRAME*>( m_frame )->ImportFootprint( fn );
191 m_frame->Zoom_Automatique( false );
192 return 0;
193}
194
195
197{
198 if( m_frame->IsType( FRAME_FOOTPRINT_VIEWER ) )
199 static_cast<FOOTPRINT_VIEWER_FRAME*>( m_frame )->SelectAndViewFootprint( aEvent.Parameter<FPVIEWER_CONSTANTS>() );
200
201 return 0;
202}
203
204
205template<class T>
206void Flip( T& aValue )
207{
208 aValue = !aValue;
209}
210
211
213{
214 Flip( displayOptions().m_DisplayPcbTrackFill );
215
216 for( PCB_TRACK* track : board()->Tracks() )
217 {
218 if( track->Type() == PCB_TRACE_T || track->Type() == PCB_ARC_T )
219 view()->Update( track, KIGFX::REPAINT );
220 }
221
222 for( BOARD_ITEM* shape : board()->Drawings() )
223 {
224 if( shape->Type() == PCB_SHAPE_T && static_cast<PCB_SHAPE*>( shape )->IsOnCopperLayer() )
225 view()->Update( shape, KIGFX::REPAINT );
226 }
227
228 canvas()->Refresh();
229
230 return 0;
231}
232
233
235{
236 if( PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame ) )
237 {
238 if( aEvent.IsAction( &PCB_ACTIONS::showRatsnest ) )
239 {
240 // N.B. Do not disable the Ratsnest layer here. We use it for local ratsnest
241 Flip( displayOptions().m_ShowGlobalRatsnest );
242 editFrame->SetElementVisibility( LAYER_RATSNEST, displayOptions().m_ShowGlobalRatsnest );
243 }
244 else if( aEvent.IsAction( &PCB_ACTIONS::ratsnestLineMode ) )
245 {
246 Flip( displayOptions().m_DisplayRatsnestLinesCurved );
247 }
248
249 editFrame->OnDisplayOptionsChanged();
250
252 canvas()->Refresh();
253 }
254
255 return 0;
256}
257
258
260{
261 Flip( displayOptions().m_DisplayViaFill );
262
263 for( PCB_TRACK* track : board()->Tracks() )
264 {
265 if( track->Type() == PCB_VIA_T )
266 view()->Update( track, KIGFX::REPAINT );
267 }
268
269 canvas()->Refresh();
270 return 0;
271}
272
273
280{
281 if( Pgm().GetCommonSettings()->m_DoNotShowAgain.zone_fill_warning )
282 return;
283
284 bool unfilledZones = false;
285
286 for( const ZONE* zone : board()->Zones() )
287 {
288 if( !zone->GetIsRuleArea() && !zone->IsFilled() )
289 {
290 unfilledZones = true;
291 break;
292 }
293 }
294
295 if( unfilledZones )
296 {
297 WX_INFOBAR* infobar = m_frame->GetInfoBar();
298 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Don't show again" ), wxEmptyString );
299
300 button->Bind( wxEVT_COMMAND_HYPERLINK, std::function<void( wxHyperlinkEvent& aEvent )>(
301 [&]( wxHyperlinkEvent& aEvent )
302 {
304 m_frame->GetInfoBar()->Dismiss();
305 } ) );
306
307 infobar->RemoveAllButtons();
308 infobar->AddButton( button );
309
310 wxString msg;
311 msg.Printf( _( "Not all zones are filled. Use Edit > Fill All Zones (%s) "
312 "if you wish to see all fills." ),
314
315 infobar->ShowMessageFor( msg, 5000, wxICON_WARNING );
316 }
317}
318
319
321{
322 PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
323
324 // Apply new display options to the GAL canvas
326 {
328
330 }
331 else if( aEvent.IsAction( &PCB_ACTIONS::zoneDisplayOutline ) )
332 {
334 }
335 else if( aEvent.IsAction( &PCB_ACTIONS::zoneDisplayFractured ) )
336 {
338 }
340 {
342 }
343 else if( aEvent.IsAction( &PCB_ACTIONS::zoneDisplayToggle ) )
344 {
347 else
349 }
350 else
351 {
352 wxFAIL;
353 }
354
355 m_frame->SetDisplayOptions( opts );
356
357 for( ZONE* zone : board()->Zones() )
358 view()->Update( zone, KIGFX::REPAINT );
359
360 canvas()->Refresh();
361
362 return 0;
363}
364
365
367{
368 PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
369
372
373 m_frame->SetDisplayOptions( opts );
374 return 0;
375}
376
377
379{
380 PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
381
382 switch( opts.m_ContrastModeDisplay )
383 {
387 }
388
389 m_frame->SetDisplayOptions( opts );
390
392 return 0;
393}
394
395
397{
398 if( !Pgm().GetCommonSettings()->m_Input.hotkey_feedback )
399 return 0;
400
401 PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
402
403 wxArrayString labels;
404 labels.Add( _( "Normal" ) );
405 labels.Add( _( "Dimmed" ) );
406 labels.Add( _( "Hidden" ) );
407
408 if( !m_frame->GetHotkeyPopup() )
409 m_frame->CreateHotkeyPopup();
410
411 HOTKEY_CYCLE_POPUP* popup = m_frame->GetHotkeyPopup();
412
413 if( popup )
414 {
415 popup->Popup( _( "Inactive Layer Display" ), labels, static_cast<int>( opts.m_ContrastModeDisplay ) );
416 }
417
418 return 0;
419}
420
421
423{
424 PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
425
426 switch( opts.m_NetColorMode )
427 {
431 }
432
433 m_frame->SetDisplayOptions( opts );
434 return 0;
435}
436
437
439{
440 if( PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame ) )
441 {
442 if( !displayOptions().m_ShowGlobalRatsnest )
443 {
446 }
447 else if( displayOptions().m_RatsnestMode == RATSNEST_MODE::ALL )
448 {
450 }
451 else
452 {
454 }
455
456 editFrame->SetElementVisibility( LAYER_RATSNEST, displayOptions().m_ShowGlobalRatsnest );
457
458 editFrame->OnDisplayOptionsChanged();
459
461 canvas()->Refresh();
462 }
463
464 return 0;
465}
466
467
469{
470 m_frame->SwitchLayer( aEvent.Parameter<PCB_LAYER_ID>() );
471
472 return 0;
473}
474
475
477{
478 BOARD* brd = board();
479 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
480 bool wraparound = false;
481
482 if( !IsCopperLayer( layer ) )
483 {
484 m_frame->SwitchLayer( B_Cu );
485 return 0;
486 }
487
488 LSET cuMask = LSET::AllCuMask( brd->GetCopperLayerCount() );
489 LSEQ layerStack = cuMask.UIOrder();
490
491 int ii = 0;
492
493 // Find the active layer in list
494 for( ; ii < (int) layerStack.size(); ii++ )
495 {
496 if( layer == layerStack[ii] )
497 break;
498 }
499
500 // Find the next visible layer in list
501 for( ; ii < (int) layerStack.size(); ii++ )
502 {
503 int jj = ii + 1;
504
505 if( jj >= (int) layerStack.size() )
506 jj = 0;
507
508 layer = layerStack[jj];
509
510 if( brd->IsLayerVisible( layer ) )
511 break;
512
513 if( jj == 0 ) // the end of list is reached. Try from the beginning
514 {
515 if( wraparound )
516 {
517 wxBell();
518 return 0;
519 }
520 else
521 {
522 wraparound = true;
523 ii = -1;
524 }
525 }
526 }
527
528 wxCHECK( IsCopperLayer( layer ), 0 );
529 m_frame->SwitchLayer( layer );
530
531 return 0;
532}
533
534
536{
537 BOARD* brd = board();
538 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
539 bool wraparound = false;
540
541 if( !IsCopperLayer( layer ) )
542 {
543 m_frame->SwitchLayer( F_Cu );
544 return 0;
545 }
546
547 LSET cuMask = LSET::AllCuMask( brd->GetCopperLayerCount() );
548 LSEQ layerStack = cuMask.UIOrder();
549
550 int ii = 0;
551
552 // Find the active layer in list
553 for( ; ii < (int) layerStack.size(); ii++ )
554 {
555 if( layer == layerStack[ii] )
556 break;
557 }
558
559 // Find the previous visible layer in list
560 for( ; ii >= 0; ii-- )
561 {
562 int jj = ii - 1;
563
564 if( jj < 0 )
565 jj = (int) layerStack.size() - 1;
566
567 layer = layerStack[jj];
568
569 if( brd->IsLayerVisible( layer ) )
570 break;
571
572 if( ii == 0 ) // the start of list is reached. Try from the last
573 {
574 if( wraparound )
575 {
576 wxBell();
577 return 0;
578 }
579 else
580 {
581 wraparound = true;
582 ii = 1;
583 }
584 }
585 }
586
587 wxCHECK( IsCopperLayer( layer ), 0 );
588 m_frame->SwitchLayer( layer );
589
590 return 0;
591}
592
593
595{
596 int currentLayer = m_frame->GetActiveLayer();
597 PCB_SCREEN* screen = m_frame->GetScreen();
598
599 if( currentLayer == screen->m_Route_Layer_TOP )
600 m_frame->SwitchLayer( screen->m_Route_Layer_BOTTOM );
601 else
602 m_frame->SwitchLayer( screen->m_Route_Layer_TOP );
603
604 return 0;
605}
606
607
608// It'd be nice to share the min/max with the DIALOG_COLOR_PICKER, but those are
609// set in wxFormBuilder.
610#define ALPHA_MIN 0.20
611#define ALPHA_MAX 1.00
612#define ALPHA_STEP 0.05
613
614
616{
617 COLOR_SETTINGS* settings = m_frame->GetColorSettings();
618 int currentLayer = m_frame->GetActiveLayer();
619 KIGFX::COLOR4D currentColor = settings->GetColor( currentLayer );
620
621 if( currentColor.a <= ALPHA_MAX - ALPHA_STEP )
622 {
623 currentColor.a += ALPHA_STEP;
624 settings->SetColor( currentLayer, currentColor );
625 m_frame->GetCanvas()->UpdateColors();
626
627 KIGFX::VIEW* view = m_frame->GetCanvas()->GetView();
628 view->UpdateLayerColor( currentLayer );
629 view->UpdateLayerColor( GetNetnameLayer( currentLayer ) );
630
631 if( IsCopperLayer( currentLayer ) )
632 view->UpdateLayerColor( ZONE_LAYER_FOR( currentLayer ) );
633
634 m_frame->GetCanvas()->ForceRefresh();
635 }
636 else
637 {
638 wxBell();
639 }
640
641 return 0;
642}
643
644
646{
647 COLOR_SETTINGS* settings = m_frame->GetColorSettings();
648 int currentLayer = m_frame->GetActiveLayer();
649 KIGFX::COLOR4D currentColor = settings->GetColor( currentLayer );
650
651 if( currentColor.a >= ALPHA_MIN + ALPHA_STEP )
652 {
653 currentColor.a -= ALPHA_STEP;
654 settings->SetColor( currentLayer, currentColor );
655 m_frame->GetCanvas()->UpdateColors();
656
657 KIGFX::VIEW* view = m_frame->GetCanvas()->GetView();
658 view->UpdateLayerColor( currentLayer );
659 view->UpdateLayerColor( GetNetnameLayer( currentLayer ) );
660
661 if( IsCopperLayer( currentLayer ) )
662 view->UpdateLayerColor( ZONE_LAYER_FOR( currentLayer ) );
663
664 m_frame->GetCanvas()->ForceRefresh();
665 }
666 else
667 {
668 wxBell();
669 }
670
671 return 0;
672}
673
674
676{
677 if( PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame ) )
678 {
679 LAYER_PAIR_SETTINGS* settings = editFrame->GetLayerPairSettings();
680
681 if( !settings )
682 return 0;
683
684 int currentIndex;
685 std::vector<LAYER_PAIR_INFO> presets = settings->GetEnabledLayerPairs( currentIndex );
686
687 if( presets.size() < 2 )
688 return 0;
689
690 if( currentIndex < 0 )
691 {
692 wxASSERT_MSG( false, "Current layer pair not found in layer settings" );
693 currentIndex = 0;
694 }
695
696 const int nextIndex = ( currentIndex + 1 ) % presets.size();
697 const LAYER_PAIR& nextPair = presets[nextIndex].GetLayerPair();
698
699 settings->SetCurrentLayerPair( nextPair );
700
702 }
703
704 return 0;
705}
706
707
709{
710 if( !Pgm().GetCommonSettings()->m_Input.hotkey_feedback )
711 return 0;
712
713 if( PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame ) )
714 {
715 LAYER_PAIR_SETTINGS* settings = editFrame->GetLayerPairSettings();
716
717 if( !settings )
718 return 0;
719
720 PCB_LAYER_PRESENTATION layerPresentation( editFrame );
721
722 int currentIndex;
723 std::vector<LAYER_PAIR_INFO> presets = settings->GetEnabledLayerPairs( currentIndex );
724
725 wxArrayString labels;
726 for( const LAYER_PAIR_INFO& layerPairInfo : presets )
727 {
728 wxString label = layerPresentation.getLayerPairName( layerPairInfo.GetLayerPair() );
729
730 if( layerPairInfo.GetName() )
731 label += wxT( " (" ) + *layerPairInfo.GetName() + wxT( ")" );
732
733 labels.Add( label );
734 }
735
736 if( !editFrame->GetHotkeyPopup() )
737 editFrame->CreateHotkeyPopup();
738
739 HOTKEY_CYCLE_POPUP* popup = editFrame->GetHotkeyPopup();
740
741 if( popup )
742 {
743 int selection = currentIndex;
744 popup->Popup( _( "Preset Layer Pairs" ), labels, selection );
745 }
746 }
747
748 return 0;
749}
750
751
753 const VECTOR2D& aPoint )
754{
755 aFrame->GetDesignSettings().SetGridOrigin( VECTOR2I( aPoint ) );
756 aView->GetGAL()->SetGridOrigin( aPoint );
757 originViewItem->SetPosition( aPoint );
758 aView->MarkDirty();
759 aFrame->OnModify();
760}
761
762
764{
765 VECTOR2D* origin = aEvent.Parameter<VECTOR2D*>();
766
767 if( origin )
768 {
769 // We can't undo the other grid dialog settings, so no sense undoing just the origin
770 DoSetGridOrigin( getView(), m_frame, m_gridOrigin.get(), *origin );
771 delete origin;
772 }
773 else
774 {
776 return 0;
777
778 PCB_PICKER_TOOL* picker = m_toolMgr->GetTool<PCB_PICKER_TOOL>();
779
780 if( !picker ) // Happens in footprint wizard
781 return 0;
782
783 // Deactivate other tools; particularly important if another PICKER is currently running
784 Activate();
785
786 picker->SetCursor( KICURSOR::PLACE );
787 picker->ClearHandlers();
788
789 picker->SetClickHandler(
790 [this]( const VECTOR2D& pt ) -> bool
791 {
792 m_frame->SaveCopyInUndoList( m_gridOrigin.get(), UNDO_REDO::GRIDORIGIN );
794 return false; // drill origin is a one-shot; don't continue with tool
795 } );
796
797 m_toolMgr->RunAction( ACTIONS::pickerTool, &aEvent );
798 }
799
800 return 0;
801}
802
803
805{
806 m_frame->SaveCopyInUndoList( m_gridOrigin.get(), UNDO_REDO::GRIDORIGIN );
808 return 0;
809}
810
811
812#define HITTEST_THRESHOLD_PIXELS 5
813
814
816{
817 if( m_isFootprintEditor && !m_frame->GetBoard()->GetFirstFootprint() )
818 return 0;
819
820 PCB_PICKER_TOOL* picker = m_toolMgr->GetTool<PCB_PICKER_TOOL>();
821
822 m_pickerItem = nullptr;
824
825 // Deactivate other tools; particularly important if another PICKER is currently running
826 Activate();
827
828 picker->SetCursor( KICURSOR::REMOVE );
829 picker->SetSnapping( false );
830 picker->ClearHandlers();
831
832 picker->SetClickHandler(
833 [this]( const VECTOR2D& aPosition ) -> bool
834 {
835 if( m_pickerItem )
836 {
837 if( m_pickerItem && m_pickerItem->IsLocked() )
838 {
840 m_statusPopup->SetText( _( "Item locked." ) );
841 m_statusPopup->PopupFor( 2000 );
842 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
843 return true;
844 }
845
846 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
847 selectionTool->UnbrightenItem( m_pickerItem );
848
849 PCB_SELECTION items;
850 items.Add( m_pickerItem );
851
852 EDIT_TOOL* editTool = m_toolMgr->GetTool<EDIT_TOOL>();
853 editTool->DeleteItems( items, false );
854
855 m_pickerItem = nullptr;
856 }
857
858 return true;
859 } );
860
861 picker->SetMotionHandler(
862 [this]( const VECTOR2D& aPos )
863 {
864 BOARD* board = m_frame->GetBoard();
865 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
866 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
867 GENERAL_COLLECTOR collector;
868 collector.m_Threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
869
871 collector.Collect( board, GENERAL_COLLECTOR::FootprintItems, aPos, guide );
872 else
873 collector.Collect( board, GENERAL_COLLECTOR::BoardLevelItems, aPos, guide );
874
875 // Remove unselectable items
876 for( int i = collector.GetCount() - 1; i >= 0; --i )
877 {
878 if( !selectionTool->Selectable( collector[i] ) )
879 collector.Remove( i );
880 }
881
882 selectionTool->FilterCollectorForHierarchy( collector, false );
883 selectionTool->FilterCollectedItems( collector, false, nullptr );
884
885 if( collector.GetCount() > 1 )
886 selectionTool->GuessSelectionCandidates( collector, aPos );
887
888 BOARD_ITEM* item = collector.GetCount() == 1 ? collector[0] : nullptr;
889
890 if( m_pickerItem != item )
891 {
892 if( m_pickerItem )
893 selectionTool->UnbrightenItem( m_pickerItem );
894
895 m_pickerItem = item;
896
897 if( m_pickerItem )
898 selectionTool->BrightenItem( m_pickerItem );
899 }
900 } );
901
902 picker->SetFinalizeHandler(
903 [this]( const int& aFinalState )
904 {
905 if( m_pickerItem )
906 m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->UnbrightenItem( m_pickerItem );
907
908 m_statusPopup.reset();
909
910 // Ensure the cursor gets changed&updated
911 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
912 m_frame->GetCanvas()->Refresh();
913 } );
914
915 m_toolMgr->RunAction( ACTIONS::pickerTool, &aEvent );
916
917 return 0;
918}
919
920
921static void pasteFootprintItemsToFootprintEditor( FOOTPRINT* aClipFootprint, BOARD* aBoard,
922 std::vector<BOARD_ITEM*>& aPastedItems )
923{
924 FOOTPRINT* editorFootprint = aBoard->GetFirstFootprint();
925
926 aClipFootprint->SetParent( aBoard );
927
928 for( PAD* pad : aClipFootprint->Pads() )
929 {
930 pad->SetParent( editorFootprint );
931 aPastedItems.push_back( pad );
932 }
933
934 aClipFootprint->Pads().clear();
935
936 // Not all items can be added to the current footprint: mandatory fields are already existing
937 // in the current footprint.
938 //
939 for( PCB_FIELD* field : aClipFootprint->GetFields() )
940 {
941 wxCHECK2( field, continue );
942
943 if( field->IsMandatory() )
944 {
945 if( EDA_GROUP* parentGroup = field->GetParentGroup() )
946 parentGroup->RemoveItem( field );
947 }
948 else
949 {
950 PCB_TEXT* text = static_cast<PCB_TEXT*>( field );
951
952 text->SetTextAngle( text->GetTextAngle() - aClipFootprint->GetOrientation() );
953 text->SetTextAngle( text->GetTextAngle() + editorFootprint->GetOrientation() );
954
955 VECTOR2I pos = field->GetFPRelativePosition();
956 field->SetParent( editorFootprint );
957 field->SetFPRelativePosition( pos );
958
959 aPastedItems.push_back( field );
960 }
961 }
962
963 aClipFootprint->GetFields().clear();
964
965 for( BOARD_ITEM* item : aClipFootprint->GraphicalItems() )
966 {
967 if( item->Type() == PCB_TEXT_T )
968 {
969 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
970
971 text->SetTextAngle( text->GetTextAngle() - aClipFootprint->GetOrientation() );
972 text->SetTextAngle( text->GetTextAngle() + editorFootprint->GetOrientation() );
973 }
974
975 item->Rotate( item->GetPosition(), -aClipFootprint->GetOrientation() );
976 item->Rotate( item->GetPosition(), editorFootprint->GetOrientation() );
977
978 VECTOR2I pos = item->GetFPRelativePosition();
979 item->SetParent( editorFootprint );
980 item->SetFPRelativePosition( pos );
981
982 aPastedItems.push_back( item );
983 }
984
985 aClipFootprint->GraphicalItems().clear();
986
987 for( ZONE* zone : aClipFootprint->Zones() )
988 {
989 zone->SetParent( editorFootprint );
990 aPastedItems.push_back( zone );
991 }
992
993 aClipFootprint->Zones().clear();
994
995 for( PCB_GROUP* group : aClipFootprint->Groups() )
996 {
997 group->SetParent( editorFootprint );
998 aPastedItems.push_back( group );
999 }
1000
1001 aClipFootprint->Groups().clear();
1002}
1003
1004
1005void PCB_CONTROL::pruneItemLayers( std::vector<BOARD_ITEM*>& aItems )
1006{
1007 // Do not prune items or layers when copying to the FP editor, because all
1008 // layers are accepted, even if they are not enabled in the dummy board
1009 // This is mainly true for internal copper layers: all are allowed but only one
1010 // (In1.cu) is enabled for the GUI.
1012 return;
1013
1014 LSET enabledLayers = board()->GetEnabledLayers();
1015 std::vector<BOARD_ITEM*> returnItems;
1016 bool fpItemDeleted = false;
1017
1018 for( BOARD_ITEM* item : aItems )
1019 {
1020 if( item->Type() == PCB_FOOTPRINT_T )
1021 {
1022 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
1023
1024 // Items living in a parent footprint are never removed, even if their
1025 // layer does not exist in the board editor
1026 // Otherwise the parent footprint could be seriously broken especially
1027 // if some layers are later re-enabled.
1028 // Moreover a fp lives in a fp library, that does not know the enabled
1029 // layers of a given board, so fp items are just ignored when on not
1030 // enabled layers in board editor
1031 returnItems.push_back( fp );
1032 }
1033 else if( item->Type() == PCB_GROUP_T || item->Type() == PCB_GENERATOR_T )
1034 {
1035 returnItems.push_back( item );
1036 }
1037 else
1038 {
1039 LSET allowed = item->GetLayerSet() & enabledLayers;
1040 bool item_valid = true;
1041
1042 // Ensure, for vias, the top and bottom layers are compatible with
1043 // the current board copper layers.
1044 // Otherwise they must be skipped, even is one layer is valid
1045 if( item->Type() == PCB_VIA_T )
1046 item_valid = static_cast<PCB_VIA*>( item )->HasValidLayerPair( board()->GetCopperLayerCount() );
1047
1048 if( allowed.any() && item_valid )
1049 {
1050 item->SetLayerSet( allowed );
1051 returnItems.push_back( item );
1052 }
1053 else
1054 {
1055 if( EDA_GROUP* parentGroup = item->GetParentGroup() )
1056 parentGroup->RemoveItem( item );
1057 }
1058 }
1059 }
1060
1061 if( ( returnItems.size() < aItems.size() ) || fpItemDeleted )
1062 {
1063 DisplayError( m_frame, _( "Warning: some pasted items were on layers which are not "
1064 "present in the current board.\n"
1065 "These items could not be pasted.\n" ) );
1066 }
1067
1068 aItems = returnItems;
1069}
1070
1071
1072int PCB_CONTROL::Paste( const TOOL_EVENT& aEvent )
1073{
1074 // The viewer frames cannot paste
1075 if( !m_frame->IsType( FRAME_FOOTPRINT_EDITOR ) && !m_frame->IsType( FRAME_PCB_EDITOR ) )
1076 return 0;
1077
1078 bool isFootprintEditor = m_isFootprintEditor || m_frame->IsType( FRAME_FOOTPRINT_EDITOR );
1079
1080 // The clipboard can contain two different things, an entire kicad_pcb or a single footprint
1081 if( isFootprintEditor && ( !board() || !footprint() ) )
1082 return 0;
1083
1084 // We should never get here if a modal dialog is up... but we do on MacOS.
1085 // https://gitlab.com/kicad/code/kicad/-/issues/18912
1086#ifdef __WXMAC__
1087 if( wxDialog::OSXHasModalDialogsOpen() )
1088 {
1089 wxBell();
1090 return 0;
1091 }
1092#endif
1093
1094 BOARD_COMMIT commit( m_frame );
1095
1096 CLIPBOARD_IO pi;
1097 BOARD_ITEM* clipItem = pi.Parse();
1098
1099 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
1100
1101 if( selTool && clipItem )
1102 {
1103 PCB_SELECTION& selection = selTool->GetSelection();
1104
1105 bool hasTableCells = false;
1106
1107 for( EDA_ITEM* item : selection )
1108 {
1109 if( item->Type() == PCB_TABLECELL_T )
1110 {
1111 hasTableCells = true;
1112 break;
1113 }
1114 }
1115
1116 if( hasTableCells )
1117 {
1118 PCB_TABLE* clipboardTable = nullptr;
1119
1120 if( clipItem->Type() == PCB_T )
1121 {
1122 BOARD* clipBoard = static_cast<BOARD*>( clipItem );
1123
1124 for( BOARD_ITEM* item : clipBoard->Drawings() )
1125 {
1126 if( item->Type() == PCB_TABLE_T )
1127 {
1128 clipboardTable = static_cast<PCB_TABLE*>( item );
1129 break;
1130 }
1131 }
1132 }
1133
1134 if( clipboardTable )
1135 {
1136 PCB_EDIT_TABLE_TOOL* tableEditTool = m_toolMgr->GetTool<PCB_EDIT_TABLE_TOOL>();
1137
1138 if( tableEditTool )
1139 {
1140 wxString errorMsg;
1141
1142 if( !tableEditTool->validatePasteIntoSelection( selection, errorMsg ) )
1143 {
1144 DisplayError( m_frame, errorMsg );
1145 return 0;
1146 }
1147
1148 if( tableEditTool->pasteCellsIntoSelection( selection, clipboardTable, commit ) )
1149 {
1150 commit.Push( _( "Paste Cells" ) );
1151 return 0;
1152 }
1153 else
1154 {
1155 DisplayError( m_frame, _( "Failed to paste cells" ) );
1156 return 0;
1157 }
1158 }
1159 }
1160 }
1161 }
1162
1163 if( !clipItem )
1164 {
1165 // When the clipboard doesn't parse, create a PCB item with the clipboard contents
1166 std::vector<BOARD_ITEM*> newItems;
1167
1168 if( std::unique_ptr<wxBitmap> clipImg = GetImageFromClipboard() )
1169 {
1170 auto refImg = std::make_unique<PCB_REFERENCE_IMAGE>( m_frame->GetModel() );
1171
1172 if( refImg->GetReferenceImage().SetImage( clipImg->ConvertToImage() ) )
1173 newItems.push_back( refImg.release() );
1174 }
1175 else
1176 {
1177 const wxString clipText = GetClipboardUTF8();
1178
1179 if( clipText.empty() )
1180 return 0;
1181
1182 // If it wasn't content, then paste as a text object.
1183 if( clipText.size() > static_cast<size_t>( ADVANCED_CFG::GetCfg().m_MaxPastedTextLength ) )
1184 {
1185 int result = IsOK( m_frame, _( "Pasting a long text text string may be very slow. "
1186 "Do you want to continue?" ) );
1187 if( !result )
1188 return 0;
1189 }
1190
1191 std::unique_ptr<PCB_TEXT> item = std::make_unique<PCB_TEXT>( m_frame->GetModel() );
1192 item->SetText( clipText );
1193 item->SetLayer( m_frame->GetActiveLayer() );
1194
1195 newItems.push_back( item.release() );
1196 }
1197
1198 bool cancelled = !placeBoardItems( &commit, newItems, true, false, false, false );
1199
1200 if( cancelled )
1201 commit.Revert();
1202 else
1203 commit.Push( _( "Paste Text" ) );
1204 return 0;
1205 }
1206
1207 // If we get here, we have a parsed board/FP to paste
1208
1210 bool clear_nets = false;
1211 const wxString defaultRef = wxT( "REF**" );
1212
1213 if( aEvent.IsAction( &ACTIONS::pasteSpecial ) )
1214 {
1215 DIALOG_PASTE_SPECIAL dlg( m_frame, &mode, defaultRef );
1216
1217 if( clipItem->Type() != PCB_T )
1218 dlg.HideClearNets();
1219
1220 if( dlg.ShowModal() == wxID_CANCEL )
1221 return 0;
1222
1223 clear_nets = dlg.GetClearNets();
1224 }
1225
1226 if( clipItem->Type() == PCB_T )
1227 {
1228 BOARD* clipBoard = static_cast<BOARD*>( clipItem );
1229
1230 if( isFootprintEditor || clear_nets )
1231 {
1232 for( BOARD_CONNECTED_ITEM* item : clipBoard->AllConnectedItems() )
1233 item->SetNet( NETINFO_LIST::OrphanedItem() );
1234 }
1235 else
1236 {
1237 clipBoard->MapNets( m_frame->GetBoard() );
1238 }
1239 }
1240
1241 bool cancelled = false;
1242
1243 switch( clipItem->Type() )
1244 {
1245 case PCB_T:
1246 {
1247 BOARD* clipBoard = static_cast<BOARD*>( clipItem );
1248
1249 if( isFootprintEditor )
1250 {
1251 FOOTPRINT* editorFootprint = board()->GetFirstFootprint();
1252 std::vector<BOARD_ITEM*> pastedItems;
1253
1254 for( PCB_GROUP* group : clipBoard->Groups() )
1255 {
1256 group->SetParent( editorFootprint );
1257 pastedItems.push_back( group );
1258 }
1259
1260 clipBoard->RemoveAll( { PCB_GROUP_T } );
1261
1262 for( FOOTPRINT* clipFootprint : clipBoard->Footprints() )
1263 pasteFootprintItemsToFootprintEditor( clipFootprint, board(), pastedItems );
1264
1265 for( BOARD_ITEM* clipDrawItem : clipBoard->Drawings() )
1266 {
1267 switch( clipDrawItem->Type() )
1268 {
1269 case PCB_TEXT_T:
1270 case PCB_TEXTBOX_T:
1271 case PCB_TABLE_T:
1272 case PCB_SHAPE_T:
1273 case PCB_BARCODE_T:
1274 case PCB_DIM_ALIGNED_T:
1275 case PCB_DIM_CENTER_T:
1276 case PCB_DIM_LEADER_T:
1278 case PCB_DIM_RADIAL_T:
1279 clipDrawItem->SetParent( editorFootprint );
1280 pastedItems.push_back( clipDrawItem );
1281 break;
1282
1283 default:
1284 // Everything we *didn't* put into pastedItems is going to get nuked, so
1285 // make sure it's not still included in its parent group.
1286 if( EDA_GROUP* parentGroup = clipDrawItem->GetParentGroup() )
1287 parentGroup->RemoveItem( clipDrawItem );
1288
1289 break;
1290 }
1291 }
1292
1293 // NB: PCB_SHAPE_T actually removes everything in Drawings() (including PCB_TEXTs,
1294 // PCB_TABLEs, PCB_BARCODEs, dimensions, etc.), not just PCB_SHAPEs.)
1295 clipBoard->RemoveAll( { PCB_SHAPE_T } );
1296
1297 clipBoard->Visit(
1298 [&]( EDA_ITEM* item, void* testData )
1299 {
1300 if( item->IsBOARD_ITEM() )
1301 {
1302 // Anything still on the clipboard didn't get copied and needs to be
1303 // removed from the pasted groups.
1304 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
1305 EDA_GROUP* parentGroup = boardItem->GetParentGroup();
1306
1307 if( parentGroup )
1308 parentGroup->RemoveItem( boardItem );
1309 }
1310
1312 },
1314
1315 delete clipBoard;
1316
1317 pruneItemLayers( pastedItems );
1318
1319 cancelled = !placeBoardItems( &commit, pastedItems, true, true, mode == PASTE_MODE::UNIQUE_ANNOTATIONS,
1320 false );
1321 }
1322 else // isBoardEditor
1323 {
1324 // Fixup footprint component classes
1325 for( FOOTPRINT* fp : clipBoard->Footprints() )
1326 {
1327 fp->ResolveComponentClassNames( board(), fp->GetTransientComponentClassNames() );
1328 fp->ClearTransientComponentClassNames();
1329 }
1330
1331 if( mode == PASTE_MODE::REMOVE_ANNOTATIONS )
1332 {
1333 for( FOOTPRINT* fp : clipBoard->Footprints() )
1334 fp->SetReference( defaultRef );
1335 }
1336
1337 cancelled = !placeBoardItems( &commit, clipBoard, true, mode == PASTE_MODE::UNIQUE_ANNOTATIONS, false );
1338 }
1339
1340 break;
1341 }
1342
1343 case PCB_FOOTPRINT_T:
1344 {
1345 FOOTPRINT* clipFootprint = static_cast<FOOTPRINT*>( clipItem );
1346 std::vector<BOARD_ITEM*> pastedItems;
1347
1348 if( isFootprintEditor )
1349 {
1350 pasteFootprintItemsToFootprintEditor( clipFootprint, board(), pastedItems );
1351 delete clipFootprint;
1352 }
1353 else
1354 {
1355 if( mode == PASTE_MODE::REMOVE_ANNOTATIONS )
1356 clipFootprint->SetReference( defaultRef );
1357
1358 clipFootprint->SetParent( board() );
1359 clipFootprint->ResolveComponentClassNames( board(), clipFootprint->GetTransientComponentClassNames() );
1360 clipFootprint->ClearTransientComponentClassNames();
1361 pastedItems.push_back( clipFootprint );
1362 }
1363
1364 pruneItemLayers( pastedItems );
1365
1366 cancelled = !placeBoardItems( &commit, pastedItems, true, true, mode == PASTE_MODE::UNIQUE_ANNOTATIONS, false );
1367 break;
1368 }
1369
1370 default:
1371 m_frame->DisplayToolMsg( _( "Invalid clipboard contents" ) );
1372 break;
1373 }
1374
1375 if( cancelled )
1376 commit.Revert();
1377 else
1378 commit.Push( _( "Paste" ) );
1379
1380 return 1;
1381}
1382
1383
1385{
1386 wxString fileName;
1387
1388 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
1389
1390 if( !editFrame )
1391 return 1;
1392
1393 // Pick a file to append
1394 if( !AskLoadBoardFileName( editFrame, &fileName, KICTL_KICAD_ONLY ) )
1395 return 1;
1396
1398 IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::FindPlugin( pluginType ) );
1399
1400 if( !pi )
1401 return 1;
1402
1403 return AppendBoard( *pi, fileName );
1404}
1405
1406
1408{
1409 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
1410
1411 if( !editFrame )
1412 return 1;
1413
1414 if( !editFrame->GetDesignBlockPane()->GetSelectedLibId().IsValid() )
1415 return 1;
1416
1417 DESIGN_BLOCK_PANE* designBlockPane = editFrame->GetDesignBlockPane();
1418 const LIB_ID selectedLibId = designBlockPane->GetSelectedLibId();
1419 std::unique_ptr<DESIGN_BLOCK> designBlock( designBlockPane->GetDesignBlock( selectedLibId, true, true ) );
1420
1421 if( !designBlock )
1422 {
1423 wxString msg;
1424 msg.Printf( _( "Could not find design block %s." ), selectedLibId.GetUniStringLibId() );
1425 editFrame->ShowInfoBarError( msg, true );
1426 return 1;
1427 }
1428
1429 if( designBlock->GetBoardFile().IsEmpty() || !wxFileName::FileExists( designBlock->GetBoardFile() ) )
1430 {
1431 editFrame->ShowInfoBarError( _( "Design block has no layout to place." ), true );
1432 return 1;
1433 }
1434
1436 IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::FindPlugin( pluginType ) );
1437
1438 if( !pi )
1439 return 1;
1440
1441 bool repeatPlacement = false;
1442
1443 if( APP_SETTINGS_BASE* cfg = editFrame->config() )
1444 repeatPlacement = cfg->m_DesignBlockChooserPanel.repeated_placement;
1445
1446 int ret = 0;
1447
1448 do
1449 {
1450 ret = AppendBoard( *pi, designBlock->GetBoardFile(), designBlock.get() );
1451 } while( repeatPlacement && ret == 0 );
1452
1453 return ret;
1454}
1455
1457{
1458 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
1459
1460 if( !editFrame )
1461 return 1;
1462
1463 BOARD* brd = board();
1464
1465 if( !brd )
1466 return 1;
1467
1468 // Need to have a group selected and it needs to have a linked design block
1469 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
1471
1472 if( selection.Size() != 1 || selection[0]->Type() != PCB_GROUP_T )
1473 return 1;
1474
1475 PCB_GROUP* group = static_cast<PCB_GROUP*>( selection[0] );
1476
1477 if( !group->HasDesignBlockLink() )
1478 return 1;
1479
1480 // Get the associated design block
1481 DESIGN_BLOCK_PANE* designBlockPane = editFrame->GetDesignBlockPane();
1482 std::unique_ptr<DESIGN_BLOCK> designBlock( designBlockPane->GetDesignBlock( group->GetDesignBlockLibId(),
1483 true, true ) );
1484
1485 if( !designBlock )
1486 {
1487 wxString msg;
1488 msg.Printf( _( "Could not find design block %s." ), group->GetDesignBlockLibId().GetUniStringLibId() );
1489 m_frame->GetInfoBar()->ShowMessageFor( msg, 5000, wxICON_WARNING );
1490 return 1;
1491 }
1492
1493 if( designBlock->GetBoardFile().IsEmpty() )
1494 {
1495 wxString msg;
1496 msg.Printf( _( "Design block %s does not have a board file." ),
1497 group->GetDesignBlockLibId().GetUniStringLibId() );
1498 m_frame->GetInfoBar()->ShowMessageFor( msg, 5000, wxICON_WARNING );
1499 return 1;
1500 }
1501
1502 BOARD_COMMIT tempCommit( m_frame );
1503
1504 std::set<EDA_ITEM*> originalItems;
1505 // Apply MCT_SKIP_STRUCT to every EDA_ITEM on the board so we know what is not part of the design block
1506 // Can't use SKIP_STRUCT as that is used and cleared by the temporary board appending
1507 brd->Visit(
1508 []( EDA_ITEM* item, void* )
1509 {
1510 item->SetFlags( MCT_SKIP_STRUCT );
1512 },
1514
1515 int ret = 1;
1516
1517 bool skipMove = true;
1518
1519 // Lambda to perform the design block layout application with proper cleanup on failure
1520 auto applyLayout = [&]() -> int
1521 {
1522 if( !m_toolMgr->RunSynchronousAction( PCB_ACTIONS::placeLinkedDesignBlock, &tempCommit,
1523 &skipMove ) )
1524 {
1525 return 1;
1526 }
1527
1528 // Lambda for the bounding box of all the components
1529 auto generateBoundingBox = []( const std::unordered_set<EDA_ITEM*>& aItems )
1530 {
1531 std::vector<VECTOR2I> bbCorners;
1532 bbCorners.reserve( aItems.size() * 4 );
1533
1534 for( EDA_ITEM* item : aItems )
1535 {
1536 const BOX2I bb = item->GetBoundingBox().GetInflated( 100000 );
1537 KIGEOM::CollectBoxCorners( bb, bbCorners );
1538 }
1539
1540 std::vector<VECTOR2I> hullVertices;
1541 BuildConvexHull( hullVertices, bbCorners );
1542
1543 SHAPE_LINE_CHAIN hull( hullVertices );
1544
1545 // Make the newly computed convex hull use only 90 degree segments
1546 return KIGEOM::RectifyPolygon( hull );
1547 };
1548
1549 // Build a rule area that contains all the components in the design block,
1550 // meaning all items without MCT_SKIP_STRUCT set.
1551 RULE_AREA dbRA;
1552
1554 dbRA.m_generateEnabled = true;
1555
1556 // Add all components that aren't marked MCT_SKIP_STRUCT to ra.m_components
1557 brd->Visit(
1558 [&]( EDA_ITEM* item, void* data )
1559 {
1560 if( !item->HasFlag( MCT_SKIP_STRUCT ) )
1561 {
1562 dbRA.m_designBlockItems.insert( item );
1563
1564 if( item->Type() == PCB_FOOTPRINT_T )
1565 dbRA.m_components.insert( static_cast<FOOTPRINT*>( item ) );
1566 }
1567
1569 },
1571
1572 // Verify that the design block placement actually added items
1573 if( dbRA.m_designBlockItems.empty() || dbRA.m_components.empty() )
1574 {
1575 tempCommit.Revert();
1576 m_frame->GetInfoBar()->ShowMessageFor(
1577 _( "Design block placement failed - no footprints were placed." ), 5000,
1578 wxICON_WARNING );
1579 return 1;
1580 }
1581
1582 dbRA.m_zone = new ZONE( board() );
1583 dbRA.m_zone->SetIsRuleArea( true );
1585 dbRA.m_zone->SetPlacementAreaEnabled( true );
1586 dbRA.m_zone->SetDoNotAllowZoneFills( false );
1587 dbRA.m_zone->SetDoNotAllowVias( false );
1588 dbRA.m_zone->SetDoNotAllowTracks( false );
1589 dbRA.m_zone->SetDoNotAllowPads( false );
1590 dbRA.m_zone->SetDoNotAllowFootprints( false );
1592 dbRA.m_zone->SetPlacementAreaSource( group->GetDesignBlockLibId().GetUniStringLibId() );
1594 dbRA.m_zone->AddPolygon( generateBoundingBox( dbRA.m_designBlockItems ) );
1595 dbRA.m_center = dbRA.m_zone->Outline()->COutline( 0 ).Centre();
1596
1597 // Create the destination rule area for the group
1598 RULE_AREA destRA;
1599
1601
1602 // Check for locked footprints and collect destination components
1603 for( EDA_ITEM* item : group->GetItems() )
1604 {
1605 if( item->Type() == PCB_FOOTPRINT_T )
1606 {
1607 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
1608
1609 if( fp->IsLocked() )
1610 {
1611 wxString msg;
1612 msg.Printf( _( "Footprint %s is locked and cannot be placed." ), fp->GetReference() );
1613 m_frame->GetInfoBar()->ShowMessageFor( msg, 5000, wxICON_WARNING );
1614 tempCommit.Revert();
1615 delete dbRA.m_zone;
1616 return 1;
1617 }
1618
1619 destRA.m_components.insert( fp );
1620 }
1621 }
1622
1623 // Verify the group has footprints to match
1624 if( destRA.m_components.empty() )
1625 {
1626 tempCommit.Revert();
1627 delete dbRA.m_zone;
1628 m_frame->GetInfoBar()->ShowMessageFor(
1629 _( "Selected group contains no footprints to place." ), 5000, wxICON_WARNING );
1630 return 1;
1631 }
1632
1633 destRA.m_zone = new ZONE( board() );
1634 destRA.m_zone->SetZoneName( wxString::Format( wxT( "design-block-dest-%s" ),
1635 group->GetDesignBlockLibId().GetUniStringLibId() ) );
1636 destRA.m_zone->SetIsRuleArea( true );
1637 destRA.m_zone->SetLayerSet( LSET::AllCuMask() );
1638 destRA.m_zone->SetPlacementAreaEnabled( true );
1639 destRA.m_zone->SetDoNotAllowZoneFills( false );
1640 destRA.m_zone->SetDoNotAllowVias( false );
1641 destRA.m_zone->SetDoNotAllowTracks( false );
1642 destRA.m_zone->SetDoNotAllowPads( false );
1643 destRA.m_zone->SetDoNotAllowFootprints( false );
1645 destRA.m_zone->SetPlacementAreaSource( group->GetName() );
1647 destRA.m_zone->AddPolygon( generateBoundingBox( group->GetItems() ) );
1648 destRA.m_center = destRA.m_zone->Outline()->COutline( 0 ).Centre();
1649
1650 // Use the multichannel tool to repeat the layout
1652
1653 int result = mct->RepeatLayout( aEvent, dbRA, destRA );
1654
1655 // Get rid of the temporary design blocks and rule areas
1656 tempCommit.Revert();
1657
1658 delete dbRA.m_zone;
1659 delete destRA.m_zone;
1660
1661 return result;
1662 };
1663
1664 ret = applyLayout();
1665
1666 // We're done, remove MCT_SKIP_STRUCT
1667 brd->Visit(
1668 []( EDA_ITEM* item, void* )
1669 {
1670 item->ClearFlags( MCT_SKIP_STRUCT );
1672 },
1674
1675 return ret;
1676}
1677
1679{
1680 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
1681
1682 if( !editFrame )
1683 return 1;
1684
1685 // Need to have a group selected and it needs to have a linked design block
1686 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
1688
1689 if( selection.Size() != 1 || selection[0]->Type() != PCB_GROUP_T )
1690 return 1;
1691
1692 PCB_GROUP* group = static_cast<PCB_GROUP*>( selection[0] );
1693
1694 if( !group->HasDesignBlockLink() )
1695 return 1;
1696
1697 // Get the associated design block
1698 DESIGN_BLOCK_PANE* designBlockPane = editFrame->GetDesignBlockPane();
1699 std::unique_ptr<DESIGN_BLOCK> designBlock( designBlockPane->GetDesignBlock( group->GetDesignBlockLibId(),
1700 true, true ) );
1701
1702 if( !designBlock )
1703 {
1704 wxString msg;
1705 msg.Printf( _( "Could not find design block %s." ), group->GetDesignBlockLibId().GetUniStringLibId() );
1706 m_frame->GetInfoBar()->ShowMessageFor( msg, 5000, wxICON_WARNING );
1707 return 1;
1708 }
1709
1710 if( designBlock->GetBoardFile().IsEmpty() )
1711 {
1712 wxString msg;
1713 msg.Printf( _( "Design block %s does not have a board file." ),
1714 group->GetDesignBlockLibId().GetUniStringLibId() );
1715 m_frame->GetInfoBar()->ShowMessageFor( msg, 5000, wxICON_WARNING );
1716 return 1;
1717 }
1718
1719
1721 IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::FindPlugin( pluginType ) );
1722
1723 if( !pi )
1724 return 1;
1725
1726 if( aEvent.Parameter<bool*>() != nullptr )
1727 return AppendBoard( *pi, designBlock->GetBoardFile(), designBlock.get(),
1728 static_cast<BOARD_COMMIT*>( aEvent.Commit() ), *aEvent.Parameter<bool*>() );
1729 else
1730 return AppendBoard( *pi, designBlock->GetBoardFile(), designBlock.get() );
1731}
1732
1733
1735{
1736 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
1737
1738 if( !editFrame )
1739 return 1;
1740
1741 // Need to have a group selected and it needs to have a linked design block
1742 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
1744
1745 if( selection.Size() != 1 || selection[0]->Type() != PCB_GROUP_T )
1746 return 1;
1747
1748 PCB_GROUP* group = static_cast<PCB_GROUP*>( selection[0] );
1749
1750 if( !group->HasDesignBlockLink() )
1751 return 1;
1752
1753 // Get the associated design block
1754 DESIGN_BLOCK_PANE* designBlockPane = editFrame->GetDesignBlockPane();
1755 std::unique_ptr<DESIGN_BLOCK> designBlock( designBlockPane->GetDesignBlock( group->GetDesignBlockLibId(),
1756 true, true ) );
1757
1758 if( !designBlock )
1759 {
1760 wxString msg;
1761 msg.Printf( _( "Could not find design block %s." ), group->GetDesignBlockLibId().GetUniStringLibId() );
1762 m_frame->GetInfoBar()->ShowMessageFor( msg, 5000, wxICON_WARNING );
1763 return 1;
1764 }
1765
1766 editFrame->GetDesignBlockPane()->SelectLibId( group->GetDesignBlockLibId() );
1767
1768 return m_toolMgr->RunAction( PCB_ACTIONS::updateDesignBlockFromSelection ) ? 1 : 0;
1769}
1770
1771
1772template<typename T>
1773static void moveUnflaggedItems( const std::deque<T>& aList, std::vector<BOARD_ITEM*>& aTarget, bool aIsNew )
1774{
1775 std::copy_if( aList.begin(), aList.end(), std::back_inserter( aTarget ),
1776 [aIsNew]( T aItem )
1777 {
1778 bool doCopy = ( aItem->GetFlags() & SKIP_STRUCT ) == 0;
1779
1780 aItem->ClearFlags( SKIP_STRUCT );
1781 aItem->SetFlags( aIsNew ? IS_NEW : 0 );
1782
1783 return doCopy;
1784 } );
1785}
1786
1787
1788template<typename T>
1789static void moveUnflaggedItems( const std::vector<T>& aList, std::vector<BOARD_ITEM*>& aTarget, bool aIsNew )
1790{
1791 std::copy_if( aList.begin(), aList.end(), std::back_inserter( aTarget ),
1792 [aIsNew]( T aItem )
1793 {
1794 bool doCopy = ( aItem->GetFlags() & SKIP_STRUCT ) == 0;
1795
1796 aItem->ClearFlags( SKIP_STRUCT );
1797 aItem->SetFlags( aIsNew ? IS_NEW : 0 );
1798
1799 return doCopy;
1800 } );
1801}
1802
1803
1804bool PCB_CONTROL::placeBoardItems( BOARD_COMMIT* aCommit, BOARD* aBoard, bool aAnchorAtOrigin,
1805 bool aReannotateDuplicates, bool aSkipMove )
1806{
1807 // items are new if the current board is not the board source
1808 bool isNew = board() != aBoard;
1809 std::vector<BOARD_ITEM*> items;
1810
1811 moveUnflaggedItems( aBoard->Tracks(), items, isNew );
1812 moveUnflaggedItems( aBoard->Footprints(), items, isNew );
1813 moveUnflaggedItems( aBoard->Drawings(), items, isNew );
1814 moveUnflaggedItems( aBoard->Zones(), items, isNew );
1815
1816 // Subtlety: When selecting a group via the mouse,
1817 // PCB_SELECTION_TOOL::highlightInternal runs, which does a SetSelected() on all
1818 // descendants. In PCB_CONTROL::placeBoardItems, below, we skip that and
1819 // mark items non-recursively. That works because the saving of the
1820 // selection created aBoard that has the group and all descendants in it.
1821 moveUnflaggedItems( aBoard->Groups(), items, isNew );
1822
1823 moveUnflaggedItems( aBoard->Generators(), items, isNew );
1824
1825 if( isNew )
1826 aBoard->RemoveAll();
1827
1828 // Reparent before calling pruneItemLayers, as SetLayer can have a dependence on the
1829 // item's parent board being set correctly.
1830 if( isNew )
1831 {
1832 for( BOARD_ITEM* item : items )
1833 item->SetParent( board() );
1834 }
1835
1836 pruneItemLayers( items );
1837
1838 return placeBoardItems( aCommit, items, isNew, aAnchorAtOrigin, aReannotateDuplicates, aSkipMove );
1839}
1840
1841
1842bool PCB_CONTROL::placeBoardItems( BOARD_COMMIT* aCommit, std::vector<BOARD_ITEM*>& aItems, bool aIsNew,
1843 bool aAnchorAtOrigin, bool aReannotateDuplicates, bool aSkipMove )
1844{
1845 m_toolMgr->RunAction( ACTIONS::selectionClear );
1846
1847 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
1848
1849 std::vector<BOARD_ITEM*> itemsToSel;
1850 itemsToSel.reserve( aItems.size() );
1851
1852 for( BOARD_ITEM* item : aItems )
1853 {
1854 if( aIsNew )
1855 {
1856 const_cast<KIID&>( item->m_Uuid ) = KIID();
1857
1858 item->RunOnChildren(
1859 []( BOARD_ITEM* aChild )
1860 {
1861 const_cast<KIID&>( aChild->m_Uuid ) = KIID();
1862 },
1864
1865 // While BOARD_COMMIT::Push() will add any new items to the entered group,
1866 // we need to do it earlier so that the previews while moving are correct.
1867 if( PCB_GROUP* enteredGroup = selectionTool->GetEnteredGroup() )
1868 {
1869 if( item->IsGroupableType() && !item->GetParentGroup() )
1870 {
1871 aCommit->Modify( enteredGroup, nullptr, RECURSE_MODE::NO_RECURSE );
1872 enteredGroup->AddItem( item );
1873 }
1874 }
1875
1876 item->SetParent( board() );
1877 }
1878
1879 // Update item attributes if needed
1880 if( BaseType( item->Type() ) == PCB_DIMENSION_T )
1881 {
1882 static_cast<PCB_DIMENSION_BASE*>( item )->UpdateUnits();
1883 }
1884 else if( item->Type() == PCB_FOOTPRINT_T )
1885 {
1886 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
1887
1888 // Update the footprint path with the new KIID path if the footprint is new
1889 if( aIsNew )
1890 footprint->SetPath( KIID_PATH() );
1891
1892 for( BOARD_ITEM* dwg : footprint->GraphicalItems() )
1893 {
1894 if( BaseType( dwg->Type() ) == PCB_DIMENSION_T )
1895 static_cast<PCB_DIMENSION_BASE*>( dwg )->UpdateUnits();
1896 }
1897 }
1898
1899 // We only need to add the items that aren't inside a group currently selected
1900 // to the selection. If an item is inside a group and that group is selected,
1901 // then the selection tool will select it for us.
1902 if( !item->GetParentGroup() || !alg::contains( aItems, item->GetParentGroup()->AsEdaItem() ) )
1903 itemsToSel.push_back( item );
1904 }
1905
1906 // Select the items that should be selected
1907 EDA_ITEMS toSel( itemsToSel.begin(), itemsToSel.end() );
1908 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &toSel );
1909
1910 // Reannotate duplicate footprints (make sense only in board editor )
1911 if( aReannotateDuplicates && m_isBoardEditor )
1912 m_toolMgr->GetTool<BOARD_REANNOTATE_TOOL>()->ReannotateDuplicatesInSelection();
1913
1914 for( BOARD_ITEM* item : aItems )
1915 {
1916 if( aIsNew )
1917 aCommit->Add( item );
1918 else
1919 aCommit->Added( item );
1920 }
1921
1922 PCB_SELECTION& selection = selectionTool->GetSelection();
1923
1924 if( selection.Size() > 0 )
1925 {
1926 if( aAnchorAtOrigin )
1927 {
1928 selection.SetReferencePoint( VECTOR2I( 0, 0 ) );
1929 }
1930 else if( BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( selection.GetTopLeftItem() ) )
1931 {
1932 selection.SetReferencePoint( item->GetPosition() );
1933 }
1934
1935 getViewControls()->SetCursorPosition( getViewControls()->GetMousePosition(), false );
1936
1937 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1938
1939 if( !aSkipMove )
1940 return m_toolMgr->RunSynchronousAction( PCB_ACTIONS::move, aCommit );
1941 }
1942
1943 return true;
1944}
1945
1946
1947int PCB_CONTROL::AppendBoard( PCB_IO& pi, const wxString& fileName, DESIGN_BLOCK* aDesignBlock, BOARD_COMMIT* aCommit,
1948 bool aSkipMove )
1949{
1950 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
1951
1952 if( !editFrame )
1953 return 1;
1954
1955 BOARD* brd = board();
1956
1957 if( !brd )
1958 return 1;
1959
1960 // Give ourselves a commit to work with if we weren't provided one
1961 std::unique_ptr<BOARD_COMMIT> tempCommit;
1962 BOARD_COMMIT* commit = aCommit;
1963
1964 if( !commit )
1965 {
1966 tempCommit = std::make_unique<BOARD_COMMIT>( editFrame );
1967 commit = tempCommit.get();
1968 }
1969
1970 // Mark existing items, in order to know what are the new items so we can select only
1971 // the new items after loading
1972 for( PCB_TRACK* track : brd->Tracks() )
1973 track->SetFlags( SKIP_STRUCT );
1974
1975 for( FOOTPRINT* footprint : brd->Footprints() )
1976 footprint->SetFlags( SKIP_STRUCT );
1977
1978 for( PCB_GROUP* group : brd->Groups() )
1979 group->SetFlags( SKIP_STRUCT );
1980
1981 for( BOARD_ITEM* drawing : brd->Drawings() )
1982 drawing->SetFlags( SKIP_STRUCT );
1983
1984 for( ZONE* zone : brd->Zones() )
1985 zone->SetFlags( SKIP_STRUCT );
1986
1987 for( PCB_GENERATOR* generator : brd->Generators() )
1988 generator->SetFlags( SKIP_STRUCT );
1989
1990 std::map<wxString, wxString> oldProperties = brd->GetProperties();
1991 std::map<wxString, wxString> newProperties;
1992
1993 PAGE_INFO oldPageInfo = brd->GetPageSettings();
1994 TITLE_BLOCK oldTitleBlock = brd->GetTitleBlock();
1995
1996 // Keep also the count of copper layers, to adjust if necessary
1997 int initialCopperLayerCount = brd->GetCopperLayerCount();
1998 LSET initialEnabledLayers = brd->GetEnabledLayers();
1999
2000 // Load the data
2001 try
2002 {
2003 std::map<std::string, UTF8> props;
2004
2005 // PCB_IO_EAGLE can use this info to center the BOARD, but it does not yet.
2006
2007 props["page_width"] = std::to_string( editFrame->GetPageSizeIU().x );
2008 props["page_height"] = std::to_string( editFrame->GetPageSizeIU().y );
2009
2011 [&]( wxString aTitle, int aIcon, wxString aMessage, wxString aAction ) -> bool
2012 {
2013 KIDIALOG dlg( editFrame, aMessage, aTitle, wxOK | wxCANCEL | aIcon );
2014
2015 if( !aAction.IsEmpty() )
2016 dlg.SetOKLabel( aAction );
2017
2018 dlg.DoNotShowCheckbox( aMessage, 0 );
2019
2020 return dlg.ShowModal() == wxID_OK;
2021 } );
2022
2023 WX_PROGRESS_REPORTER progressReporter( editFrame, _( "Load PCB" ), 1, PR_CAN_ABORT );
2024
2025 pi.SetProgressReporter( &progressReporter );
2026 pi.LoadBoard( fileName, brd, &props, nullptr );
2027 }
2028 catch( const IO_ERROR& ioe )
2029 {
2030 DisplayErrorMessage( editFrame, _( "Error loading board." ), ioe.What() );
2031
2032 return 0;
2033 }
2034
2035 newProperties = brd->GetProperties();
2036
2037 for( const std::pair<const wxString, wxString>& prop : oldProperties )
2038 newProperties[prop.first] = prop.second;
2039
2040 brd->SetProperties( newProperties );
2041
2042 brd->SetPageSettings( oldPageInfo );
2043 brd->SetTitleBlock( oldTitleBlock );
2044
2045 // rebuild nets and ratsnest before any use of nets
2046 brd->BuildListOfNets();
2047 brd->SynchronizeNetsAndNetClasses( true );
2048 brd->BuildConnectivity();
2049
2050 // Synchronize layers
2051 // we should not ask PLUGINs to do these items:
2052 int copperLayerCount = brd->GetCopperLayerCount();
2053
2054 if( copperLayerCount > initialCopperLayerCount )
2055 brd->SetCopperLayerCount( copperLayerCount );
2056
2057 // Enable all used layers, and make them visible:
2058 LSET enabledLayers = brd->GetEnabledLayers();
2059 enabledLayers |= initialEnabledLayers;
2060 brd->SetEnabledLayers( enabledLayers );
2061 brd->SetVisibleLayers( enabledLayers );
2062
2063 int ret = 0;
2064
2065 bool placeAsGroup = false;
2066
2067 if( APP_SETTINGS_BASE* cfg = editFrame->config() )
2068 placeAsGroup = cfg->m_DesignBlockChooserPanel.place_as_group;
2069
2070 if( placeBoardItems( commit, brd, false, false /* Don't reannotate dupes on Append Board */, aSkipMove ) )
2071 {
2072 if( placeAsGroup )
2073 {
2074 PCB_GROUP* group = new PCB_GROUP( brd );
2075
2076 if( aDesignBlock )
2077 {
2078 group->SetName( aDesignBlock->GetLibId().GetLibItemName() );
2079 group->SetDesignBlockLibId( aDesignBlock->GetLibId() );
2080 }
2081 else
2082 {
2083 group->SetName( wxFileName( fileName ).GetName() );
2084 }
2085
2086 // Get the selection tool selection
2087 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
2089
2090 for( EDA_ITEM* eda_item : selection )
2091 {
2092 if( eda_item->IsBOARD_ITEM() )
2093 {
2094 if( static_cast<BOARD_ITEM*>( eda_item )->IsLocked() )
2095 group->SetLocked( true );
2096 }
2097 }
2098
2099 commit->Add( group );
2100
2101 for( EDA_ITEM* eda_item : selection )
2102 {
2103 if( eda_item->IsBOARD_ITEM() && !static_cast<BOARD_ITEM*>( eda_item )->GetParentFootprint() )
2104 {
2105 commit->Modify( eda_item );
2106 group->AddItem( eda_item );
2107 }
2108 }
2109
2110 selTool->ClearSelection();
2111 selTool->select( group );
2112
2114 m_frame->OnModify();
2115 m_frame->Refresh();
2116 }
2117
2118 // If we were provided a commit, let the caller control when to push it
2119 if( !aCommit )
2120 commit->Push( aDesignBlock ? _( "Place Design Block" ) : _( "Append Board" ) );
2121
2122 editFrame->GetBoard()->BuildConnectivity();
2123 ret = 0;
2124 }
2125 else
2126 {
2127 // If we were provided a commit, let the caller control when to revert it
2128 if( !aCommit )
2129 commit->Revert();
2130
2131 ret = 1;
2132 }
2133
2134 // Refresh the UI for the updated board properties
2135 editFrame->GetAppearancePanel()->OnBoardChanged();
2136
2137 return ret;
2138}
2139
2140
2141int PCB_CONTROL::Undo( const TOOL_EVENT& aEvent )
2142{
2143 PCB_BASE_EDIT_FRAME* editFrame = dynamic_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
2144 wxCommandEvent dummy;
2145
2146 if( editFrame )
2147 editFrame->RestoreCopyFromUndoList( dummy );
2148
2149 return 0;
2150}
2151
2152
2153int PCB_CONTROL::Redo( const TOOL_EVENT& aEvent )
2154{
2155 PCB_BASE_EDIT_FRAME* editFrame = dynamic_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
2156 wxCommandEvent dummy;
2157
2158 if( editFrame )
2159 editFrame->RestoreCopyFromRedoList( dummy );
2160
2161 return 0;
2162}
2163
2164
2166{
2167 MAGNETIC_SETTINGS& settings = m_isFootprintEditor ? m_frame->GetFootprintEditorSettings()->m_MagneticItems
2168 : m_frame->GetPcbNewSettings()->m_MagneticItems;
2169 bool& snapMode = settings.allLayers;
2170
2172 snapMode = false;
2173 else if( aEvent.IsAction( &PCB_ACTIONS::magneticSnapAllLayers ) )
2174 snapMode = true;
2175 else
2176 snapMode = !snapMode;
2177
2179
2180 return 0;
2181}
2182
2183
2185{
2186 if( !Pgm().GetCommonSettings()->m_Input.hotkey_feedback )
2187 return 0;
2188
2189 wxArrayString labels;
2190 labels.Add( _( "Active Layer" ) );
2191 labels.Add( _( "All Layers" ) );
2192
2193 if( !m_frame->GetHotkeyPopup() )
2194 m_frame->CreateHotkeyPopup();
2195
2196 HOTKEY_CYCLE_POPUP* popup = m_frame->GetHotkeyPopup();
2197
2198 MAGNETIC_SETTINGS& settings = m_isFootprintEditor ? m_frame->GetFootprintEditorSettings()->m_MagneticItems
2199 : m_frame->GetPcbNewSettings()->m_MagneticItems;
2200
2201 if( popup )
2202 popup->Popup( _( "Object Snapping" ), labels, static_cast<int>( settings.allLayers ) );
2203
2204 return 0;
2205}
2206
2207
2209{
2210 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
2211 ROUTER_TOOL* routerTool = m_toolMgr->GetTool<ROUTER_TOOL>();
2212 PCB_SELECTION& selection = selTool->GetSelection();
2213 PCB_EDIT_FRAME* pcbFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
2214 std::shared_ptr<DRC_ENGINE> drcEngine = m_frame->GetBoard()->GetDesignSettings().m_DRCEngine;
2215 DRC_CONSTRAINT constraint;
2216
2217 std::vector<MSG_PANEL_ITEM> msgItems;
2218
2219 if( routerTool && routerTool->RoutingInProgress() )
2220 {
2221 routerTool->UpdateMessagePanel();
2222 return 0;
2223 }
2224
2225 if( !pcbFrame && !m_frame->GetModel() )
2226 return 0;
2227
2228 if( selection.Empty() )
2229 {
2230 if( !pcbFrame )
2231 {
2232 FOOTPRINT* fp = static_cast<FOOTPRINT*>( m_frame->GetModel() );
2233 fp->GetMsgPanelInfo( m_frame, msgItems );
2234 }
2235 else
2236 {
2237 m_frame->SetMsgPanel( m_frame->GetBoard() );
2238 }
2239 }
2240 else if( selection.GetSize() == 1 )
2241 {
2242 EDA_ITEM* item = selection.Front();
2243
2244 if( std::optional<wxString> uuid = GetMsgPanelDisplayUuid( item->m_Uuid ) )
2245 msgItems.emplace_back( _( "UUID" ), *uuid );
2246
2247 item->GetMsgPanelInfo( m_frame, msgItems );
2248
2249 PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item );
2250 NETINFO_ITEM* net = track ? track->GetNet() : nullptr;
2251 NETINFO_ITEM* coupledNet = net ? m_frame->GetBoard()->DpCoupledNet( net ) : nullptr;
2252
2253 if( coupledNet )
2254 {
2255 SEG trackSeg( track->GetStart(), track->GetEnd() );
2256 PCB_TRACK* coupledItem = nullptr;
2257 SEG::ecoord closestDist_sq = VECTOR2I::ECOORD_MAX;
2258
2259 for( PCB_TRACK* candidate : m_frame->GetBoard()->Tracks() )
2260 {
2261 if( candidate->GetNet() != coupledNet )
2262 continue;
2263
2264 SEG::ecoord dist_sq = trackSeg.SquaredDistance( SEG( candidate->GetStart(), candidate->GetEnd() ) );
2265
2266 if( !coupledItem || dist_sq < closestDist_sq )
2267 {
2268 coupledItem = candidate;
2269 closestDist_sq = dist_sq;
2270 }
2271 }
2272
2273 constraint = drcEngine->EvalRules( DIFF_PAIR_GAP_CONSTRAINT, track, coupledItem, track->GetLayer() );
2274
2275 wxString msg = m_frame->MessageTextFromMinOptMax( constraint.Value() );
2276
2277 if( !msg.IsEmpty() )
2278 {
2279 msgItems.emplace_back( wxString::Format( _( "DP Gap Constraints: %s" ), msg ),
2280 wxString::Format( _( "(from %s)" ), constraint.GetName() ) );
2281 }
2282
2283 constraint = drcEngine->EvalRules( MAX_UNCOUPLED_CONSTRAINT, track, coupledItem, track->GetLayer() );
2284
2285 if( constraint.Value().HasMax() )
2286 {
2287 msg = m_frame->MessageTextFromValue( constraint.Value().Max() );
2288 msgItems.emplace_back( wxString::Format( _( "DP Max Uncoupled-length: %s" ), msg ),
2289 wxString::Format( _( "(from %s)" ), constraint.GetName() ) );
2290 }
2291 }
2292 }
2293 else if( pcbFrame && selection.GetSize() == 2 )
2294 {
2295 // Pair selection broken into multiple, optional data, starting with the selected item
2296 // names
2297
2298 BOARD_ITEM* a = dynamic_cast<BOARD_ITEM*>( selection[0] );
2299 BOARD_ITEM* b = dynamic_cast<BOARD_ITEM*>( selection[1] );
2300
2301 if( a && b )
2302 {
2303 msgItems.emplace_back( MSG_PANEL_ITEM( a->GetItemDescription( m_frame, false ),
2304 b->GetItemDescription( m_frame, false ) ) );
2305 }
2306
2307 BOARD_CONNECTED_ITEM* a_conn = dynamic_cast<BOARD_CONNECTED_ITEM*>( a );
2308 BOARD_CONNECTED_ITEM* b_conn = dynamic_cast<BOARD_CONNECTED_ITEM*>( b );
2309
2310 if( a_conn && b_conn )
2311 {
2312 LSET overlap = a_conn->GetLayerSet() & b_conn->GetLayerSet() & LSET::AllCuMask();
2313 int a_netcode = a_conn->GetNetCode();
2314 int b_netcode = b_conn->GetNetCode();
2315
2316 if( overlap.count() > 0 )
2317 {
2318 PCB_LAYER_ID layer = overlap.CuStack().front();
2319
2320 if( a_netcode != b_netcode || a_netcode < 0 || b_netcode < 0 )
2321 {
2322 constraint = drcEngine->EvalRules( CLEARANCE_CONSTRAINT, a, b, layer );
2323 msgItems.emplace_back( _( "Resolved Clearance" ),
2324 m_frame->MessageTextFromValue( constraint.m_Value.Min() ) );
2325 }
2326
2327 std::shared_ptr<SHAPE> a_shape( a_conn->GetEffectiveShape( layer ) );
2328 std::shared_ptr<SHAPE> b_shape( b_conn->GetEffectiveShape( layer ) );
2329
2330 int actual_clearance = a_shape->GetClearance( b_shape.get() );
2331
2332 if( actual_clearance > -1 && actual_clearance < std::numeric_limits<int>::max() )
2333 {
2334 msgItems.emplace_back( _( "Actual Clearance" ),
2335 m_frame->MessageTextFromValue( actual_clearance ) );
2336 }
2337 }
2338 }
2339
2340 if( a && b && ( a->HasHole() || b->HasHole() ) )
2341 {
2342 PCB_LAYER_ID active = m_frame->GetActiveLayer();
2344
2345 if( b->IsOnLayer( active ) && IsCopperLayer( active ) )
2346 layer = active;
2347 else if( b->HasHole() && a->IsOnLayer( active ) && IsCopperLayer( active ) )
2348 layer = active;
2349 else if( a->HasHole() && b->IsOnCopperLayer() )
2350 layer = b->GetLayer();
2351 else if( b->HasHole() && a->IsOnCopperLayer() )
2352 layer = a->GetLayer();
2353
2354 if( IsCopperLayer( layer ) )
2355 {
2356 int actual = std::numeric_limits<int>::max();
2357
2358 if( a->HasHole() && b->IsOnCopperLayer() )
2359 {
2360 std::shared_ptr<SHAPE_SEGMENT> hole = a->GetEffectiveHoleShape();
2361 std::shared_ptr<SHAPE> other( b->GetEffectiveShape( layer ) );
2362
2363 actual = std::min( actual, hole->GetClearance( other.get() ) );
2364 }
2365
2366 if( b->HasHole() && a->IsOnCopperLayer() )
2367 {
2368 std::shared_ptr<SHAPE_SEGMENT> hole = b->GetEffectiveHoleShape();
2369 std::shared_ptr<SHAPE> other( a->GetEffectiveShape( layer ) );
2370
2371 actual = std::min( actual, hole->GetClearance( other.get() ) );
2372 }
2373
2374 if( actual < std::numeric_limits<int>::max() )
2375 {
2376 constraint = drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, a, b, layer );
2377 msgItems.emplace_back( _( "Resolved Hole Clearance" ),
2378 m_frame->MessageTextFromValue( constraint.m_Value.Min() ) );
2379
2380 if( actual > -1 && actual < std::numeric_limits<int>::max() )
2381 {
2382 msgItems.emplace_back( _( "Actual Hole Clearance" ),
2383 m_frame->MessageTextFromValue( actual ) );
2384 }
2385 }
2386 }
2387 }
2388
2389 if( a && b )
2390 {
2391 for( PCB_LAYER_ID edgeLayer : { Edge_Cuts, Margin } )
2392 {
2393 PCB_LAYER_ID active = m_frame->GetActiveLayer();
2395
2396 if( a->IsOnLayer( edgeLayer ) && b->Type() != PCB_FOOTPRINT_T )
2397 {
2398 if( b->IsOnLayer( active ) && IsCopperLayer( active ) )
2399 layer = active;
2400 else if( IsCopperLayer( b->GetLayer() ) )
2401 layer = b->GetLayer();
2402 }
2403 else if( b->IsOnLayer( edgeLayer ) && a->Type() != PCB_FOOTPRINT_T )
2404 {
2405 if( a->IsOnLayer( active ) && IsCopperLayer( active ) )
2406 layer = active;
2407 else if( IsCopperLayer( a->GetLayer() ) )
2408 layer = a->GetLayer();
2409 }
2410
2411 if( layer >= 0 )
2412 {
2413 constraint = drcEngine->EvalRules( EDGE_CLEARANCE_CONSTRAINT, a, b, layer );
2414
2415 if( edgeLayer == Edge_Cuts )
2416 {
2417 msgItems.emplace_back( _( "Resolved Edge Clearance" ),
2418 m_frame->MessageTextFromValue( constraint.m_Value.Min() ) );
2419 }
2420 else
2421 {
2422 msgItems.emplace_back( _( "Resolved Margin Clearance" ),
2423 m_frame->MessageTextFromValue( constraint.m_Value.Min() ) );
2424 }
2425 }
2426 }
2427 }
2428 }
2429
2430 if( selection.GetSize() )
2431 {
2432 if( msgItems.empty() )
2433 {
2434 // Count items by type
2435 std::map<KICAD_T, int> typeCounts;
2436
2437 for( EDA_ITEM* item : selection )
2438 typeCounts[item->Type()]++;
2439
2440 // Check if all items are the same type
2441 bool allSameType = ( typeCounts.size() == 1 );
2442 KICAD_T commonType = allSameType ? typeCounts.begin()->first : NOT_USED;
2443
2444 if( allSameType )
2445 {
2446 // Show "Type: N" for homogeneous selections
2447 wxString typeName = selection.Front()->GetFriendlyName();
2448 msgItems.emplace_back( typeName,
2449 wxString::Format( wxT( "%d" ), selection.GetSize() ) );
2450
2451 // For pads, show common properties
2452 if( commonType == PCB_PAD_T )
2453 {
2454 std::set<wxString> layers;
2455 std::set<PAD_SHAPE> shapes;
2456 std::set<VECTOR2I> sizes;
2457
2458 for( EDA_ITEM* item : selection )
2459 {
2460 PAD* pad = static_cast<PAD*>( item );
2461 layers.insert( pad->LayerMaskDescribe() );
2462 shapes.insert( pad->GetShape( PADSTACK::ALL_LAYERS ) );
2463 sizes.insert( pad->GetSize( PADSTACK::ALL_LAYERS ) );
2464 }
2465
2466 if( layers.size() == 1 )
2467 msgItems.emplace_back( _( "Layer" ), *layers.begin() );
2468
2469 if( shapes.size() == 1 )
2470 {
2471 PAD* firstPad = static_cast<PAD*>( selection.Front() );
2472 msgItems.emplace_back( _( "Pad Shape" ),
2473 firstPad->ShowPadShape( PADSTACK::ALL_LAYERS ) );
2474 }
2475
2476 if( sizes.size() == 1 )
2477 {
2478 VECTOR2I size = *sizes.begin();
2479 msgItems.emplace_back( _( "Pad Size" ),
2480 wxString::Format( wxT( "%s x %s" ),
2481 m_frame->MessageTextFromValue( size.x ),
2482 m_frame->MessageTextFromValue( size.y ) ) );
2483 }
2484 }
2485 }
2486 else
2487 {
2488 // Show type breakdown for mixed selections
2489 wxString breakdown;
2490
2491 for( const auto& [type, count] : typeCounts )
2492 {
2493 if( !breakdown.IsEmpty() )
2494 breakdown += wxT( ", " );
2495
2496 // Get friendly name from first item of this type
2497 wxString typeName;
2498
2499 for( EDA_ITEM* item : selection )
2500 {
2501 if( item->Type() == type )
2502 {
2503 typeName = item->GetFriendlyName();
2504 break;
2505 }
2506 }
2507
2508 breakdown += wxString::Format( wxT( "%s: %d" ), typeName, count );
2509 }
2510
2511 msgItems.emplace_back( _( "Selected Items" ),
2512 wxString::Format( wxT( "%d (%s)" ),
2513 selection.GetSize(), breakdown ) );
2514 }
2515
2516 if( m_isBoardEditor )
2517 {
2518 std::set<wxString> netNames;
2519 std::set<wxString> netClasses;
2520
2521 for( EDA_ITEM* item : selection )
2522 {
2523 if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
2524 {
2525 if( !bci->GetNet() || bci->GetNetCode() <= NETINFO_LIST::UNCONNECTED )
2526 continue;
2527
2528 netNames.insert( UnescapeString( bci->GetNetname() ) );
2529 netClasses.insert( UnescapeString( bci->GetEffectiveNetClass()->GetHumanReadableName() ) );
2530
2531 if( netNames.size() > 1 && netClasses.size() > 1 )
2532 break;
2533 }
2534 }
2535
2536 if( netNames.size() == 1 )
2537 msgItems.emplace_back( _( "Net" ), *netNames.begin() );
2538
2539 if( netClasses.size() == 1 )
2540 msgItems.emplace_back( _( "Resolved Netclass" ), *netClasses.begin() );
2541 }
2542 }
2543
2544 if( selection.GetSize() >= 2 )
2545 {
2546 bool lengthValid = true;
2547 double selectedLength = 0;
2548
2549 // Lambda to accumulate track length if item is a track or arc, otherwise mark invalid
2550 std::function<void( EDA_ITEM* )> accumulateTrackLength;
2551
2552 accumulateTrackLength =
2553 [&]( EDA_ITEM* aItem )
2554 {
2555 if( aItem->Type() == PCB_TRACE_T || aItem->Type() == PCB_ARC_T )
2556 {
2557 selectedLength += static_cast<PCB_TRACK*>( aItem )->GetLength();
2558 }
2559 else if( aItem->Type() == PCB_VIA_T )
2560 {
2561 // zero 2D length
2562 }
2563 else if( aItem->Type() == PCB_SHAPE_T )
2564 {
2565 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
2566
2567 if( shape->GetShape() == SHAPE_T::SEGMENT
2568 || shape->GetShape() == SHAPE_T::ARC
2569 || shape->GetShape() == SHAPE_T::BEZIER )
2570 {
2571 selectedLength += shape->GetLength();
2572 }
2573 else
2574 {
2575 lengthValid = false;
2576 }
2577 }
2578 // Use dynamic_cast to include PCB_GENERATORs.
2579 else if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( aItem ) )
2580 {
2581 group->RunOnChildren( accumulateTrackLength, RECURSE_MODE::RECURSE );
2582 }
2583 else
2584 {
2585 lengthValid = false;
2586 }
2587 };
2588
2589 for( EDA_ITEM* item : selection )
2590 {
2591 if( lengthValid )
2592 accumulateTrackLength( item );
2593 }
2594
2595 if( lengthValid )
2596 {
2597 msgItems.emplace_back( _( "Selected 2D Length" ),
2598 m_frame->MessageTextFromValue( selectedLength ) );
2599 }
2600 }
2601
2602 if( selection.GetSize() >= 2 && selection.GetSize() < 100 )
2603 {
2604 LSET enabledLayers = m_frame->GetBoard()->GetEnabledLayers();
2605 LSET enabledCopper = LSET::AllCuMask( m_frame->GetBoard()->GetCopperLayerCount() );
2606 bool areaValid = true;
2607 bool hasCopper = false;
2608 bool hasNonCopper = false;
2609
2610 std::map<PCB_LAYER_ID, SHAPE_POLY_SET> layerPolys;
2611 SHAPE_POLY_SET holes;
2612
2613 std::function<void( EDA_ITEM* )> accumulateArea;
2614
2615 accumulateArea =
2616 [&]( EDA_ITEM* aItem )
2617 {
2618 if( aItem->Type() == PCB_FOOTPRINT_T || aItem->Type() == PCB_MARKER_T )
2619 {
2620 areaValid = false;
2621 return;
2622 }
2623
2624 if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( aItem ) )
2625 {
2626 group->RunOnChildren( accumulateArea, RECURSE_MODE::RECURSE );
2627 return;
2628 }
2629
2630 if( BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( aItem ) )
2631 {
2632 boardItem->RunOnChildren( accumulateArea, RECURSE_MODE::NO_RECURSE );
2633
2634 LSET itemLayers = boardItem->GetLayerSet() & enabledLayers;
2635
2636 for( PCB_LAYER_ID layer : itemLayers )
2637 {
2638 boardItem->TransformShapeToPolySet( layerPolys[layer], layer, 0,
2640
2641 if( enabledCopper.Contains( layer ) )
2642 hasCopper = true;
2643 else
2644 hasNonCopper = true;
2645 }
2646
2647 if( aItem->Type() == PCB_PAD_T && static_cast<PAD*>( aItem )->HasHole() )
2648 {
2649 static_cast<PAD*>( aItem )->TransformHoleToPolygon( holes, 0, ARC_LOW_DEF,
2650 ERROR_OUTSIDE );
2651 }
2652 else if( aItem->Type() == PCB_VIA_T )
2653 {
2654 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
2655 VECTOR2I center = via->GetPosition();
2656 int R = via->GetDrillValue() / 2;
2657
2659 }
2660 }
2661 };
2662
2663 for( EDA_ITEM* item : selection )
2664 {
2665 if( areaValid )
2666 accumulateArea( item );
2667 }
2668
2669 if( areaValid )
2670 {
2671 double area = 0.0;
2672
2673 for( auto& [layer, layerPoly] : layerPolys )
2674 {
2675 // Only subtract holes from copper layers
2676 if( enabledCopper.Contains( layer ) )
2677 layerPoly.BooleanSubtract( holes );
2678
2679 area += layerPoly.Area();
2680 }
2681
2682 // Choose appropriate label based on what layers are involved
2683 wxString areaLabel;
2684
2685 if( hasCopper && !hasNonCopper )
2686 areaLabel = _( "Selected 2D Copper Area" );
2687 else if( !hasCopper && hasNonCopper )
2688 areaLabel = _( "Selected 2D Area" );
2689 else
2690 areaLabel = _( "Selected 2D Total Area" );
2691
2692 msgItems.emplace_back( areaLabel,
2693 m_frame->MessageTextFromValue( area, true, EDA_DATA_TYPE::AREA ) );
2694 }
2695 }
2696 }
2697 else
2698 {
2699 m_frame->GetBoard()->GetMsgPanelInfo( m_frame, msgItems );
2700 }
2701
2702 m_frame->SetMsgPanel( msgItems );
2703
2704 // Update vertex editor if it exists
2705 PCB_BASE_EDIT_FRAME* editFrame = dynamic_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
2706 if( editFrame )
2707 {
2708 BOARD_ITEM* selectedItem = ( selection.GetSize() == 1 ) ? dynamic_cast<BOARD_ITEM*>( selection.Front() )
2709 : nullptr;
2710 editFrame->UpdateVertexEditorSelection( selectedItem );
2711 }
2712
2713 return 0;
2714}
2715
2716
2718{
2719 wxFileName fileName = wxFileName( *aEvent.Parameter<wxString*>() );
2720
2721 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
2722
2723 if( !editFrame )
2724 return 1;
2725
2726 wxString filePath = fileName.GetFullPath();
2728 IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::FindPlugin( pluginType ) );
2729
2730 if( !pi )
2731 return 1;
2732
2733 return AppendBoard( *pi, filePath );
2734}
2735
2736
2738{
2739 BOARD_COMMIT commit( this );
2740 EDA_UNITS displayUnit = m_frame->GetUserUnits();
2741 PCB_TABLE* table = Build_Board_Characteristics_Table( m_frame->GetBoard(), displayUnit );
2742 table->SetLayer( m_frame->GetActiveLayer() );
2743
2744 std::vector<BOARD_ITEM*> items;
2745 items.push_back( table );
2746
2747 if( placeBoardItems( &commit, items, true, true, false, false ) )
2748 commit.Push( _( "Place Board Characteristics" ) );
2749 else
2750 delete table;
2751
2752 return 0;
2753}
2754
2755
2757{
2758 BOARD_COMMIT commit( this );
2759 EDA_UNITS displayUnit = m_frame->GetUserUnits();
2760
2761 PCB_TABLE* table = Build_Board_Stackup_Table( m_frame->GetBoard(), displayUnit );
2762 table->SetLayer( m_frame->GetActiveLayer() );
2763
2764 std::vector<BOARD_ITEM*> items;
2765 items.push_back( table );
2766
2767 if( placeBoardItems( &commit, items, true, true, false, false ) )
2768 commit.Push( _( "Place Board Stackup Table" ) );
2769 else
2770 delete table;
2771
2772 return 0;
2773}
2774
2775
2777{
2778 view()->SetMirror( !view()->IsMirroredX(), false );
2779 view()->RecacheAllItems();
2780 m_frame->GetCanvas()->ForceRefresh();
2781 m_frame->OnDisplayOptionsChanged();
2782 return 0;
2783}
2784
2785
2787{
2788 if( aItem->Type() == PCB_SHAPE_T )
2789 {
2790 static_cast<PCB_SHAPE*>( aItem )->UpdateHatching();
2791
2792 if( view() )
2793 view()->Update( aItem );
2794 }
2795}
2796
2797
2799{
2800 for( FOOTPRINT* footprint : board()->Footprints() )
2801 footprint->RunOnChildren( std::bind( &PCB_CONTROL::rehatchBoardItem, this, _1 ), NO_RECURSE );
2802
2803 for( BOARD_ITEM* item : board()->Drawings() )
2804 rehatchBoardItem( item );
2805
2806 return 0;
2807}
2808
2809
2811{
2812 BOARD* brd = board();
2813
2814 if( !brd )
2815 return 0;
2816
2817 PROJECT& prj = m_frame->Prj();
2819 FILENAME_RESOLVER* resolver = cache ? cache->GetResolver() : nullptr;
2820
2821 wxString workingPath = prj.GetProjectPath();
2822 std::vector<const EMBEDDED_FILES*> stack;
2823 stack.push_back( brd->GetEmbeddedFiles() );
2824
2825 BOARD_COMMIT commit( m_frame );
2826 int embeddedCount = 0;
2827
2828 for( FOOTPRINT* fp : brd->Footprints() )
2829 {
2830 bool fpModified = false;
2831
2832 for( FP_3DMODEL& model : fp->Models() )
2833 {
2834 if( model.m_Filename.StartsWith( FILEEXT::KiCadUriPrefix ) )
2835 continue;
2836
2837 wxString fullPath =
2838 resolver ? resolver->ResolvePath( model.m_Filename, workingPath, stack )
2839 : model.m_Filename;
2840
2841 wxFileName fname( fullPath );
2842 wxString ext = fname.GetExt().Upper();
2843
2844 if( fname.Exists() )
2845 {
2847 brd->GetEmbeddedFiles()->AddFile( fname, false ) )
2848 {
2849 model.m_Filename = file->GetLink();
2850 fpModified = true;
2851 embeddedCount++;
2852
2853 // Store STEP along with WRL for the OCCT(STEP) exporter.
2854 if( ext == "WRL" || ext == "WRZ" )
2855 {
2856 wxArrayString alts;
2857
2858 // Step files
2859 alts.Add( wxT( "stp" ) );
2860 alts.Add( wxT( "step" ) );
2861 alts.Add( wxT( "STP" ) );
2862 alts.Add( wxT( "STEP" ) );
2863 alts.Add( wxT( "Stp" ) );
2864 alts.Add( wxT( "Step" ) );
2865 alts.Add( wxT( "stpz" ) );
2866 alts.Add( wxT( "stpZ" ) );
2867 alts.Add( wxT( "STPZ" ) );
2868
2869 for( const auto& alt : alts )
2870 {
2871 wxFileName altFile( fname.GetPath(),
2872 fname.GetName() + wxT( "." ) + alt );
2873
2874 if( altFile.IsOk() && altFile.FileExists() )
2875 {
2876 brd->GetEmbeddedFiles()->AddFile( altFile, false );
2877 break;
2878 }
2879 }
2880 }
2881 }
2882 }
2883 }
2884
2885 if( fpModified )
2886 commit.Modify( fp );
2887 }
2888
2889 if( embeddedCount > 0 )
2890 {
2891 commit.Push( _( "Embed 3D Models" ) );
2892 wxString msg = wxString::Format( _( "%d 3D model(s) successfully embedded." ), embeddedCount );
2893 m_frame->GetInfoBar()->ShowMessageFor( msg, 5000 );
2894 }
2895
2896 return 0;
2897}
2898
2899
2900// clang-format off
2902{
2905 Go( &PCB_CONTROL::Print, ACTIONS::print.MakeEvent() );
2906
2907 // Footprint library actions
2912
2913 // Display modes
2930
2931 // Layer control
2969
2972
2973 // Grid control
2976
2977 Go( &PCB_CONTROL::Undo, ACTIONS::undo.MakeEvent() );
2978 Go( &PCB_CONTROL::Redo, ACTIONS::redo.MakeEvent() );
2979
2980 // Snapping control
2985
2986 // Miscellaneous
2989
2990 // Append control
2999
3000 Go( &PCB_CONTROL::Paste, ACTIONS::paste.MakeEvent() );
3002
3009
3010 // Add library by dropping file
3013}
3014// clang-format on
@ ERROR_OUTSIDE
@ ERROR_INSIDE
constexpr int ARC_LOW_DEF
Definition base_units.h:128
PCB_TABLE * Build_Board_Characteristics_Table(BOARD *aBoard, EDA_UNITS aDisplayUnits)
@ NORMAL
Inactive layers are shown normally (no high-contrast mode)
@ HIDDEN
Inactive layers are hidden.
@ DIMMED
Inactive layers are dimmed (old high-contrast mode)
@ RATSNEST
Net/netclass colors are shown on ratsnest lines only.
@ ALL
Net/netclass colors are shown on all net copper.
@ OFF
Net (and netclass) colors are not shown.
@ VISIBLE
Ratsnest lines are drawn to items on visible layers only.
@ ALL
Ratsnest lines are drawn to items on all layers (default)
PCB_TABLE * Build_Board_Stackup_Table(BOARD *aBoard, EDA_UNITS aDisplayUnits)
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
static TOOL_ACTION paste
Definition actions.h:80
static TOOL_ACTION addLibrary
Definition actions.h:56
static TOOL_ACTION pickerTool
Definition actions.h:253
static TOOL_ACTION gridResetOrigin
Definition actions.h:196
static TOOL_ACTION pasteSpecial
Definition actions.h:81
static TOOL_ACTION highContrastModeCycle
Definition actions.h:156
static TOOL_ACTION undo
Definition actions.h:75
static TOOL_ACTION highContrastMode
Definition actions.h:155
static TOOL_ACTION redo
Definition actions.h:76
static TOOL_ACTION deleteTool
Definition actions.h:86
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:224
static TOOL_ACTION print
Definition actions.h:64
static TOOL_ACTION newLibrary
Definition actions.h:55
static TOOL_ACTION gridSetOrigin
Definition actions.h:195
static TOOL_ACTION ddAddLibrary
Definition actions.h:67
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition actions.h:232
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
APP_SETTINGS_BASE is a settings class that should be derived for each standalone KiCad application.
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
virtual void Revert() override
Revert the commit by restoring the modified items state.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
void SetGridOrigin(const VECTOR2I &aOrigin)
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:83
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:236
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition board_item.h:318
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:256
virtual bool IsOnCopperLayer() const
Definition board_item.h:155
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
virtual bool HasHole() const
Definition board_item.h:160
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:2211
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition board.cpp:3077
void SetVisibleLayers(const LSET &aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings changes the bit-mask of vis...
Definition board.cpp:1001
void MapNets(BOARD *aDestBoard)
Map all nets in the given board to nets with the same name (if any) in the destination board.
Definition board.cpp:3172
void BuildListOfNets()
Definition board.h:959
const GENERATORS & Generators() const
Definition board.h:369
const std::vector< BOARD_CONNECTED_ITEM * > AllConnectedItems()
Definition board.cpp:3146
const PAGE_INFO & GetPageSettings() const
Definition board.h:799
void SetProperties(const std::map< wxString, wxString > &aProps)
Definition board.h:401
const ZONES & Zones() const
Definition board.h:367
const GROUPS & Groups() const
The groups must maintain the following invariants.
Definition board.h:396
bool BuildConnectivity(PROGRESS_REPORTER *aReporter=nullptr)
Build or rebuild the board connectivity database for the board, especially the list of connected item...
Definition board.cpp:192
void SynchronizeNetsAndNetClasses(bool aResetTrackAndViaSizes)
Copy NETCLASS info to each NET, based on NET membership in a NETCLASS.
Definition board.cpp:2668
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition board.h:530
TITLE_BLOCK & GetTitleBlock()
Definition board.h:805
int GetCopperLayerCount() const
Definition board.cpp:921
const std::map< wxString, wxString > & GetProperties() const
Definition board.h:400
const FOOTPRINTS & Footprints() const
Definition board.h:363
void RemoveAll(std::initializer_list< KICAD_T > aTypes={ PCB_NETINFO_T, PCB_MARKER_T, PCB_GROUP_T, PCB_ZONE_T, PCB_GENERATOR_T, PCB_FOOTPRINT_T, PCB_TRACE_T, PCB_SHAPE_T })
An efficient way to remove all items of a certain type from the board.
Definition board.cpp:1485
const TRACKS & Tracks() const
Definition board.h:361
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition board.h:800
void SetCopperLayerCount(int aCount)
Definition board.cpp:927
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:975
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:969
void SetEnabledLayers(const LSET &aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition board.cpp:989
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition board.h:807
const DRAWINGS & Drawings() const
Definition board.h:365
constexpr BOX2< Vec > GetInflated(coord_type aDx, coord_type aDy) const
Get a new rectangle that is this one, inflated by aDx and aDy.
Definition box2.h:638
BOARD_ITEM * Parse()
int GetCount() const
Return the number of objects in the list.
Definition collector.h:83
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition collector.h:111
int m_Threshold
Definition collector.h:236
Color settings are a bit different than most of the settings objects in that there can be more than o...
void SetColor(int aLayer, const COLOR4D &aColor)
COLOR4D GetColor(int aLayer) const
COMMIT & Added(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition commit.h:84
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:106
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:78
DO_NOT_SHOW_AGAIN m_DoNotShowAgain
void SelectLibId(const LIB_ID &aLibId)
LIB_ID GetSelectedLibId(int *aUnit=nullptr) const
DESIGN_BLOCK * GetDesignBlock(const LIB_ID &aLibId, bool aUseCacheLib, bool aShowErrorMsg)
Load design block from design block library table.
const LIB_ID & GetLibId() const
int ShowModal() override
wxString GetName() const
Definition drc_rule.h:194
MINOPTMAX< int > & Value()
Definition drc_rule.h:187
MINOPTMAX< int > m_Value
Definition drc_rule.h:228
virtual APP_SETTINGS_BASE * config() const
Return the settings object used in SaveSettings(), and is overloaded in KICAD_MANAGER_FRAME.
void ShowInfoBarError(const wxString &aErrorMsg, bool aShowCloseButton=false, WX_INFOBAR::MESSAGE_TYPE aType=WX_INFOBAR::MESSAGE_TYPE::GENERIC)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an error icon on the left o...
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
A set of EDA_ITEMs (i.e., without duplicates).
Definition eda_group.h:46
void RemoveItem(EDA_ITEM *aItem)
Remove item from group.
Definition eda_group.cpp:40
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
virtual void SetPosition(const VECTOR2I &aPos)
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:110
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:147
virtual wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const
Return a user-visible description string of this item.
Definition eda_item.cpp:144
const KIID m_Uuid
Definition eda_item.h:521
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:116
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:149
virtual void GetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList)
Populate aList of MSG_PANEL_ITEM objects with it's internal state for display purposes.
Definition eda_item.h:225
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.h:113
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:151
SHAPE_T GetShape() const
Definition eda_shape.h:169
double GetLength() const
bool validatePasteIntoSelection(const SELECTION &aSel, wxString &aErrorMsg)
Validate if paste-into-cells is possible for the given selection.
bool pasteCellsIntoSelection(const SELECTION &aSel, T_TABLE *aSourceTable, T_COMMIT &aCommit)
Paste text content from source table into selected cells.
The interactive edit tool.
Definition edit_tool.h:56
void DeleteItems(const PCB_SELECTION &aItem, bool aIsCut)
EMBEDDED_FILE * AddFile(const wxFileName &aName, bool aOverwrite)
Load a file from disk and adds it to the collection.
static const TOOL_EVENT ClearedEvent
Definition actions.h:347
static const TOOL_EVENT SelectedEvent
Definition actions.h:345
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:352
static const TOOL_EVENT PointSelectedEvent
Definition actions.h:344
static const TOOL_EVENT ContrastModeChangedByKeyEvent
Definition actions.h:366
static const TOOL_EVENT ConnectivityChangedEvent
Selected item had a property changed (except movement)
Definition actions.h:349
static const TOOL_EVENT UnselectedEvent
Definition actions.h:346
Provide an extensible class to resolve 3D model paths.
Component library viewer main window.
EDA_ANGLE GetOrientation() const
Definition footprint.h:330
ZONES & Zones()
Definition footprint.h:312
std::deque< PAD * > & Pads()
Definition footprint.h:306
void ResolveComponentClassNames(BOARD *aBoard, const std::unordered_set< wxString > &aComponentClassNames)
Resolves a set of component class names to this footprint's actual component class.
const std::unordered_set< wxString > & GetTransientComponentClassNames()
Gets the transient component class names.
Definition footprint.h:1252
void SetReference(const wxString &aReference)
Definition footprint.h:757
bool IsLocked() const override
Definition footprint.h:544
void ClearTransientComponentClassNames()
Remove the transient component class names.
Definition footprint.h:1258
void GetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList) override
Populate aList of MSG_PANEL_ITEM objects with it's internal state for display purposes.
GROUPS & Groups()
Definition footprint.h:315
void GetFields(std::vector< PCB_FIELD * > &aVector, bool aVisibleOnly) const
Populate a std::vector with PCB_TEXTs.
const wxString & GetReference() const
Definition footprint.h:751
DRAWINGS & GraphicalItems()
Definition footprint.h:309
A general implementation of a COLLECTORS_GUIDE.
Definition collectors.h:324
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:207
static const std::vector< KICAD_T > BoardLevelItems
A scan list for all primary board items, omitting items which are subordinate to a FOOTPRINT,...
Definition collectors.h:69
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
Similar to EDA_VIEW_SWITCHER, this dialog is a popup that shows feedback when using a hotkey to cycle...
void Popup(const wxString &aTitle, const wxArrayString &aItems, int aSelection)
virtual void SetProgressReporter(PROGRESS_REPORTER *aReporter)
Set an optional progress reporter.
Definition io_base.h:94
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition kidialog.h:42
void DoNotShowCheckbox(wxString file, int line)
Shows the 'do not show again' checkbox.
Definition kidialog.cpp:55
int ShowModal() override
Definition kidialog.cpp:93
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
COLOR4D & Darken(double aFactor)
Makes the color darker by a given factor.
Definition color4d.h:227
COLOR4D & Brighten(double aFactor)
Makes the color brighter by a given factor.
Definition color4d.h:210
double a
Alpha component.
Definition color4d.h:396
void SetGridOrigin(const VECTOR2D &aGridOrigin)
Set the origin point for the grid.
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 SetCursorPosition(const VECTOR2D &aPosition, bool aWarpView=true, bool aTriggeredByArrows=false, long aArrowCommand=0)=0
Move cursor to the requested position expressed in world coordinates.
bool IsBOARD_ITEM() const
Definition view_item.h:102
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:66
void SetMirror(bool aMirrorX, bool aMirrorY)
Control the mirroring of the VIEW.
Definition view.cpp:558
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition view.cpp:298
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition view.cpp:341
GAL * GetGAL() const
Return the GAL this view is using to draw graphical primitives.
Definition view.h:202
void RecacheAllItems()
Rebuild GAL display lists.
Definition view.cpp:1462
void MarkDirty()
Force redraw of view on the next rendering.
Definition view.h:659
Definition kiid.h:49
All information about a layer pair as stored in the layer pair store.
Management class for layer pairs in a PCB.
Definition layer_pairs.h:47
std::vector< LAYER_PAIR_INFO > GetEnabledLayerPairs(int &aCurrentIndex) const
Get a vector of all enabled layer pairs, in order.
void SetCurrentLayerPair(const LAYER_PAIR &aPair)
Set the "active" layer pair.
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
bool IsValid() const
Check if this LID_ID is valid.
Definition lib_id.h:172
wxString GetUniStringLibId() const
Definition lib_id.h:148
const UTF8 & GetLibItemName() const
Definition lib_id.h:102
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
LSEQ UIOrder() const
Return the copper, technical and user layers in the order shown in layer widget.
Definition lset.cpp:743
LSEQ CuStack() const
Return a sequence of copper layers in starting from the front/top and extending to the back/bottom.
Definition lset.cpp:263
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:599
static LSET AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
T Min() const
Definition minoptmax.h:33
bool HasMax() const
Definition minoptmax.h:38
T Max() const
Definition minoptmax.h:34
EDA_MSG_PANEL items for displaying messages.
Definition msgpanel.h:54
int RepeatLayout(const TOOL_EVENT &aEvent, ZONE *aRefZone)
Handle the data for a net.
Definition netinfo.h:54
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition netinfo.h:247
static NETINFO_ITEM * OrphanedItem()
NETINFO_ITEM meaning that there was no net assigned for an item, as there was no board storing net li...
Definition netinfo.h:255
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
Definition pad.h:55
static wxString ShowPadShape(PAD_SHAPE aShape)
Definition pad.cpp:2046
bool HasHole() const override
Definition pad.h:107
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:79
static TOOL_ACTION layerToggle
static TOOL_ACTION layerInner12
static TOOL_ACTION nextFootprint
static TOOL_ACTION layerInner8
static TOOL_ACTION zoneDisplayToggle
static TOOL_ACTION previousFootprint
static TOOL_ACTION layerInner3
static TOOL_ACTION layerPrev
static TOOL_ACTION showRatsnest
static TOOL_ACTION zoneFillAll
static TOOL_ACTION layerInner2
static TOOL_ACTION magneticSnapAllLayers
static TOOL_ACTION collect3DModels
static TOOL_ACTION saveToLinkedDesignBlock
static TOOL_ACTION ddAppendBoard
Drag and drop.
static TOOL_ACTION layerInner25
static TOOL_ACTION magneticSnapActiveLayer
Snapping controls.
static TOOL_ACTION layerAlphaDec
static TOOL_ACTION zoneDisplayFilled
static TOOL_ACTION layerInner24
static TOOL_ACTION viaDisplayMode
static TOOL_ACTION layerInner29
static TOOL_ACTION placeCharacteristics
static TOOL_ACTION layerInner11
static TOOL_ACTION layerAlphaInc
static TOOL_ACTION layerPairPresetsCycle
static TOOL_ACTION layerInner16
static TOOL_ACTION layerInner26
static TOOL_ACTION layerInner18
static TOOL_ACTION layerInner14
static TOOL_ACTION trackDisplayMode
static TOOL_ACTION magneticSnapToggle
static TOOL_ACTION layerInner6
static TOOL_ACTION applyDesignBlockLayout
static TOOL_ACTION ddImportFootprint
static TOOL_ACTION zoneDisplayTriangulated
static TOOL_ACTION rehatchShapes
static TOOL_ACTION layerInner22
static TOOL_ACTION placeDesignBlock
static TOOL_ACTION layerInner5
static TOOL_ACTION zoneDisplayFractured
static TOOL_ACTION ratsnestModeCycle
static TOOL_ACTION layerInner20
static TOOL_ACTION layerInner7
static TOOL_ACTION layerInner27
static TOOL_ACTION loadFpFromBoard
static TOOL_ACTION appendBoard
static TOOL_ACTION netColorModeCycle
static TOOL_ACTION layerInner1
static TOOL_ACTION layerInner10
static TOOL_ACTION layerInner15
static TOOL_ACTION layerInner17
static TOOL_ACTION flipBoard
static TOOL_ACTION layerBottom
static TOOL_ACTION zoneDisplayOutline
static TOOL_ACTION ratsnestLineMode
static TOOL_ACTION layerInner19
static TOOL_ACTION layerInner9
static TOOL_ACTION move
move or drag an item
static TOOL_ACTION layerInner30
static TOOL_ACTION layerTop
static TOOL_ACTION updateDesignBlockFromSelection
static TOOL_ACTION layerInner4
static TOOL_ACTION layerInner13
static TOOL_ACTION layerInner21
static TOOL_ACTION saveFpToBoard
static TOOL_ACTION layerNext
static TOOL_ACTION placeLinkedDesignBlock
static TOOL_ACTION placeStackup
static TOOL_ACTION layerInner23
static TOOL_ACTION layerInner28
Common, abstract interface for edit frames.
void RestoreCopyFromUndoList(wxCommandEvent &aEvent)
Undo the last edit:
APPEARANCE_CONTROLS * GetAppearancePanel()
void RestoreCopyFromRedoList(wxCommandEvent &aEvent)
Redo the last edit:
void UpdateVertexEditorSelection(BOARD_ITEM *aItem)
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
const VECTOR2I GetPageSizeIU() const override
Works off of GetPageSettings() to return the size of the paper page in the internal units of this par...
void OnModify() override
Must be called after a change in order to set the "modify" flag and update other data structures and ...
BOARD * GetBoard() const
virtual BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Return the BOARD_DESIGN_SETTINGS for the open project.
int RehatchShapes(const TOOL_EVENT &aEvent)
void setTransitions() override
< Sets up handlers for various events.
int AppendBoardFromFile(const TOOL_EVENT &aEvent)
int AddLibrary(const TOOL_EVENT &aEvent)
int DdAppendBoard(const TOOL_EVENT &aEvent)
int LoadFpFromBoard(const TOOL_EVENT &aEvent)
int SaveToLinkedDesignBlock(const TOOL_EVENT &aEvent)
int DdImportFootprint(const TOOL_EVENT &aEvent)
int SnapModeFeedback(const TOOL_EVENT &aEvent)
int NetColorModeCycle(const TOOL_EVENT &aEvent)
int SaveFpToBoard(const TOOL_EVENT &aEvent)
int RatsnestModeCycle(const TOOL_EVENT &aEvent)
int TrackDisplayMode(const TOOL_EVENT &aEvent)
int DdAddLibrary(const TOOL_EVENT &aEvent)
int Redo(const TOOL_EVENT &aEvent)
int PlaceLinkedDesignBlock(const TOOL_EVENT &aEvent)
bool placeBoardItems(BOARD_COMMIT *aCommit, std::vector< BOARD_ITEM * > &aItems, bool aIsNew, bool aAnchorAtOrigin, bool aReannotateDuplicates, bool aSkipMove)
Add and select or just select for move/place command a list of board items.
int LayerPresetFeedback(const TOOL_EVENT &aEvent)
int UpdateMessagePanel(const TOOL_EVENT &aEvent)
int LayerAlphaDec(const TOOL_EVENT &aEvent)
int LayerNext(const TOOL_EVENT &aEvent)
int PlaceStackup(const TOOL_EVENT &aEvent)
std::unique_ptr< STATUS_TEXT_POPUP > m_statusPopup
int ToggleRatsnest(const TOOL_EVENT &aEvent)
int LayerAlphaInc(const TOOL_EVENT &aEvent)
int HighContrastModeCycle(const TOOL_EVENT &aEvent)
std::unique_ptr< KIGFX::ORIGIN_VIEWITEM > m_gridOrigin
int HighContrastMode(const TOOL_EVENT &aEvent)
int Undo(const TOOL_EVENT &aEvent)
int ViaDisplayMode(const TOOL_EVENT &aEvent)
PCB_BASE_FRAME * m_frame
static void DoSetGridOrigin(KIGFX::VIEW *aView, PCB_BASE_FRAME *aFrame, EDA_ITEM *originViewItem, const VECTOR2D &aPoint)
int CollectAndEmbed3DModels(const TOOL_EVENT &aEvent)
void pruneItemLayers(std::vector< BOARD_ITEM * > &aItems)
Helper for pasting.
int GridPlaceOrigin(const TOOL_EVENT &aEvent)
int FlipPcbView(const TOOL_EVENT &aEvent)
int PlaceCharacteristics(const TOOL_EVENT &aEvent)
int ApplyDesignBlockLayout(const TOOL_EVENT &aEvent)
int SnapMode(const TOOL_EVENT &aEvent)
int ContrastModeFeedback(const TOOL_EVENT &aEvent)
int LayerToggle(const TOOL_EVENT &aEvent)
int AppendBoard(PCB_IO &pi, const wxString &fileName, DESIGN_BLOCK *aDesignBlock=nullptr, BOARD_COMMIT *aCommit=nullptr, bool aSkipMove=false)
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
int IterateFootprint(const TOOL_EVENT &aEvent)
int Print(const TOOL_EVENT &aEvent)
void rehatchBoardItem(BOARD_ITEM *aItem)
int ZoneDisplayMode(const TOOL_EVENT &aEvent)
int GridResetOrigin(const TOOL_EVENT &aEvent)
BOARD_ITEM * m_pickerItem
int InteractiveDelete(const TOOL_EVENT &aEvent)
int AppendDesignBlock(const TOOL_EVENT &aEvent)
int LayerPrev(const TOOL_EVENT &aEvent)
int CycleLayerPresets(const TOOL_EVENT &aEvent)
int Paste(const TOOL_EVENT &aEvent)
void unfilledZoneCheck()
We have bug reports indicating that some new users confuse zone filling/unfilling with the display mo...
int LayerSwitch(const TOOL_EVENT &aEvent)
Abstract dimension API.
HIGH_CONTRAST_MODE m_ContrastModeDisplay
How inactive layers are displayed.
NET_COLOR_MODE m_NetColorMode
How to use color overrides on specific nets and netclasses.
ZONE_DISPLAY_MODE m_ZoneDisplayMode
void RedrawRatsnest()
Return the bounding box of the view that should be used if model is not valid.
The main frame for Pcbnew.
PCB_DESIGN_BLOCK_PANE * GetDesignBlockPane() const
static const TOOL_EVENT & SnappingModeChangedByKeyEvent()
Hotkey feedback.
static const TOOL_EVENT & LayerPairPresetChangedByKeyEvent()
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:53
PCB_FILE_T
The set of file types that the PCB_IO_MGR knows about, and for which there has been a plugin written,...
Definition pcb_io_mgr.h:56
@ KICAD_SEXP
S-expression Pcbnew file format.
Definition pcb_io_mgr.h:58
static PCB_IO * FindPlugin(PCB_FILE_T aFileType)
Return a #PLUGIN which the caller can use to import, export, save, or load design documents.
static PCB_FILE_T FindPluginTypeFromBoardPath(const wxString &aFileName, int aCtl=0)
Return a plugin type given a path for a board file.
A base class that BOARD loading and saving plugins should derive from.
Definition pcb_io.h:70
virtual void SetQueryUserCallback(std::function< bool(wxString aTitle, int aIcon, wxString aMessage, wxString aAction)> aCallback)
Registers a KIDIALOG callback for collecting info from the user.
Definition pcb_io.h:100
virtual BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr)
Load information from some input file format that this PCB_IO implementation knows about into either ...
Definition pcb_io.cpp:74
Class that manages the presentation of PCB layers in a PCB frame.
wxString getLayerPairName(const LAYER_PAIR &aPair) const
Definition sel_layer.cpp:89
Generic tool for picking an item.
PCB_LAYER_ID m_Route_Layer_TOP
Definition pcb_screen.h:43
PCB_LAYER_ID m_Route_Layer_BOTTOM
Definition pcb_screen.h:44
The selection tool: currently supports:
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 select(EDA_ITEM *aItem) override
Take necessary action mark an item as selected.
bool Selectable(const BOARD_ITEM *aItem, bool checkVisibilityOnly=false) const
PCB_GROUP * GetEnteredGroup()
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.
int ClearSelection(const TOOL_EVENT &aEvent)
PCB_SELECTION & GetSelection()
void FilterCollectedItems(GENERAL_COLLECTOR &aCollector, bool aMultiSelect, PCB_SELECTION_FILTER_OPTIONS *aRejected=nullptr)
Apply the SELECTION_FITLER_OPTIONS to the collector.
KIGFX::PCB_VIEW * view() const
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
BOARD * board() const
PCB_DRAW_PANEL_GAL * canvas() const
PCBNEW_SETTINGS::DISPLAY_OPTIONS & displayOptions() const
const PCB_SELECTION & selection() const
FOOTPRINT * footprint() const
const VECTOR2I & GetStart() const
Definition pcb_track.h:154
const VECTOR2I & GetEnd() const
Definition pcb_track.h:151
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:535
void SetMotionHandler(MOTION_HANDLER aHandler)
Set a handler for mouse motion.
Definition picker_tool.h:92
void SetClickHandler(CLICK_HANDLER aHandler)
Set a handler for mouse click event.
Definition picker_tool.h:81
void SetSnapping(bool aSnap)
Definition picker_tool.h:66
void SetCursor(KICURSOR aCursor)
Definition picker_tool.h:64
void SetFinalizeHandler(FINALIZE_HANDLER aHandler)
Set a handler for the finalize event.
static S3D_CACHE * Get3DCacheManager(PROJECT *aProject, bool updateProjDir=false)
Return a pointer to an instance of the 3D cache manager.
Container for project specific data.
Definition project.h:65
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:168
Cache for storing the 3D shapes.
Definition 3d_cache.h:55
FILENAME_RESOLVER * GetResolver() noexcept
Definition 3d_cache.cpp:513
Definition seg.h:42
ecoord SquaredDistance(const SEG &aSeg) const
Definition seg.cpp:80
VECTOR2I::extended_type ecoord
Definition seg.h:44
void BrightenItem(EDA_ITEM *aItem)
void UnbrightenItem(EDA_ITEM *aItem)
virtual void Add(EDA_ITEM *aItem)
Definition selection.cpp:42
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
Represent a set of closed polygons.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
virtual VECTOR2I Centre() const
Compute a center-of-mass of the shape.
Definition shape.h:232
Extension of STATUS_POPUP for displaying a single line text.
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition title_block.h:41
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:186
const std::string & GetName() const
Return the name of the tool.
Definition tool_base.h:136
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
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
@ GAL_SWITCH
Rendering engine changes.
Definition tool_base.h:82
Generic, UI-independent tool event.
Definition tool_event.h:171
COMMIT * Commit() const
Definition tool_event.h:283
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 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).
void Activate()
Run the tool.
static constexpr extended_type ECOORD_MAX
Definition vector2d.h:76
A modified version of the wxInfoBar class that allows us to:
Definition wx_infobar.h:76
void RemoveAllButtons()
Remove all the buttons that have been added by the user.
void ShowMessageFor(const wxString &aMessage, int aTime, int aFlags=wxICON_INFORMATION, MESSAGE_TYPE aType=WX_INFOBAR::MESSAGE_TYPE::GENERIC)
Show the infobar with the provided message and icon for a specific period of time.
void AddButton(wxButton *aButton)
Add an already created button to the infobar.
Multi-thread safe progress reporter dialog, intended for use of tasks that parallel reporting back of...
Handle a list of polygons defining a copper zone.
Definition zone.h:74
void SetDoNotAllowPads(bool aEnable)
Definition zone.h:734
void AddPolygon(std::vector< VECTOR2I > &aPolygon)
Add a polygon to the zone outline.
Definition zone.cpp:1161
void SetPlacementAreaSource(const wxString &aSource)
Definition zone.h:721
void SetPlacementAreaSourceType(PLACEMENT_SOURCE_T aType)
Definition zone.h:723
SHAPE_POLY_SET * Outline()
Definition zone.h:341
void SetHatchStyle(ZONE_BORDER_DISPLAY_STYLE aStyle)
Definition zone.h:596
void SetIsRuleArea(bool aEnable)
Definition zone.h:716
void SetDoNotAllowTracks(bool aEnable)
Definition zone.h:733
void SetLayerSet(const LSET &aLayerSet) override
Definition zone.cpp:551
void SetDoNotAllowVias(bool aEnable)
Definition zone.h:732
void SetDoNotAllowFootprints(bool aEnable)
Definition zone.h:735
void SetDoNotAllowZoneFills(bool aEnable)
Definition zone.h:731
void SetZoneName(const wxString &aName)
Definition zone.h:165
void SetPlacementAreaEnabled(bool aEnabled)
Definition zone.h:718
std::unique_ptr< wxBitmap > GetImageFromClipboard()
Get image data from the clipboard, if there is any.
std::string GetClipboardUTF8()
Return the information currently stored in the system clipboard.
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition confirm.cpp:259
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:202
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:177
This file is part of the common library.
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
void BuildConvexHull(std::vector< VECTOR2I > &aResult, const std::vector< VECTOR2I > &aPoly)
Calculate the convex hull of a list of points in counter-clockwise order.
@ REMOVE
Definition cursors.h:54
@ PLACE
Definition cursors.h:98
@ ARROW
Definition cursors.h:46
#define ALPHA_MAX
@ DIFF_PAIR_GAP_CONSTRAINT
Definition drc_rule.h:73
@ EDGE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:53
@ CLEARANCE_CONSTRAINT
Definition drc_rule.h:49
@ MAX_UNCOUPLED_CONSTRAINT
Definition drc_rule.h:74
@ HOLE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:51
#define _(s)
Declaration of the eda_3d_viewer class.
@ RECURSE
Definition eda_item.h:51
@ NO_RECURSE
Definition eda_item.h:52
#define MCT_SKIP_STRUCT
flag used by the multichannel tool to mark items that should be skipped
#define SKIP_STRUCT
flag indicating that the structure should be ignored
@ SEGMENT
Definition eda_shape.h:45
EDA_UNITS
Definition eda_units.h:48
static FILENAME_RESOLVER * resolver
@ FRAME_PCB_EDITOR
Definition frame_type.h:42
@ FRAME_FOOTPRINT_VIEWER
Definition frame_type.h:45
@ FRAME_FOOTPRINT_EDITOR
Definition frame_type.h:43
static const std::string KiCadUriPrefix
wxString KeyNameFromKeyCode(int aKeycode, bool *aIsFound)
Return the key name from the key code.
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
#define KICTL_KICAD_ONLY
chosen file is from KiCad according to user
int GetNetnameLayer(int aLayer)
Return a netname layer corresponding to the given layer.
Definition layer_ids.h:854
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:677
@ LAYER_RATSNEST
Definition layer_ids.h:253
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ Edge_Cuts
Definition layer_ids.h:112
@ B_Cu
Definition layer_ids.h:65
@ Margin
Definition layer_ids.h:113
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ F_Cu
Definition layer_ids.h:64
#define ZONE_LAYER_FOR(boardLayer)
Definition layer_ids.h:368
std::optional< wxString > GetMsgPanelDisplayUuid(const KIID &aKiid)
Get a formatted UUID string for display in the message panel, according to the current advanced confi...
Definition msgpanel.cpp:220
SHAPE_LINE_CHAIN RectifyPolygon(const SHAPE_LINE_CHAIN &aPoly)
void CollectBoxCorners(const BOX2I &aBox, std::vector< VECTOR2I > &aCorners)
Add the 4 corners of a BOX2I to a vector.
@ REPAINT
Item needs to be redrawn.
Definition view_item.h:58
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition wxgtk/ui.cpp:706
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:100
bool AskLoadBoardFileName(PCB_EDIT_FRAME *aParent, wxString *aFileName, int aCtl=0)
Show a wxFileDialog asking for a BOARD filename to open.
PCB_TABLE * Build_Board_Characteristics_Table(BOARD *aBoard, EDA_UNITS aDisplayUnits)
PCB_TABLE * Build_Board_Stackup_Table(BOARD *aBoard, EDA_UNITS aDisplayUnits)
#define ALPHA_STEP
static void pasteFootprintItemsToFootprintEditor(FOOTPRINT *aClipFootprint, BOARD *aBoard, std::vector< BOARD_ITEM * > &aPastedItems)
#define ALPHA_MIN
void Flip(T &aValue)
static void moveUnflaggedItems(const std::deque< T > &aList, std::vector< BOARD_ITEM * > &aTarget, bool aIsNew)
Class to handle a set of BOARD_ITEMs.
bool AskLoadBoardFileName(PCB_EDIT_FRAME *aParent, wxString *aFileName, int aCtl=0)
Show a wxFileDialog asking for a BOARD filename to open.
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
#define HITTEST_THRESHOLD_PIXELS
std::vector< EDA_ITEM * > EDA_ITEMS
Utility functions for working with shapes.
std::vector< FAB_LAYER_COLOR > dummy
wxString UnescapeString(const wxString &aSource)
VECTOR2I m_center
std::unordered_set< EDA_ITEM * > m_designBlockItems
PLACEMENT_SOURCE_T m_sourceType
std::set< FOOTPRINT * > m_components
KIBIS_MODEL * model
VECTOR2I center
int actual
wxString result
Test unit parsing edge cases and error handling.
constexpr KICAD_T BaseType(const KICAD_T aType)
Return the underlying type of the given type.
Definition typeinfo.h:254
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:78
@ PCB_T
Definition typeinfo.h:82
@ 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
@ 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_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:92
@ 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_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_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:100
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:94
@ 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
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR2< double > VECTOR2D
Definition vector2d.h:694
Definition of file extensions used in Kicad.
#define PR_CAN_ABORT