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 <advanced_config.h>
30#include <collectors.h>
31#include <kiplatform/ui.h>
32#include <kiway.h>
33#include <tools/edit_tool.h>
35#include <router/router_tool.h>
36#include <pgm_base.h>
37#include <tools/pcb_actions.h>
42#include <board_commit.h>
43#include <board.h>
45#include <board_item.h>
47#include <clipboard.h>
48#include <design_block.h>
50#include <pcb_dimension.h>
54#include <footprint.h>
55#include <pad.h>
56#include <layer_pairs.h>
57#include <pcb_group.h>
59#include <pcb_reference_image.h>
60#include <pcb_textbox.h>
61#include <pcb_table.h>
62#include <pcb_tablecell.h>
63#include <pcb_track.h>
64#include <pcb_generator.h>
66#include <project_pcb.h>
68#include <filename_resolver.h>
69#include <3d_cache/3d_cache.h>
70#include <embedded_files.h>
71#include <wx/filename.h>
72#include <zone.h>
73#include <confirm.h>
74#include <kidialog.h>
76#include <core/kicad_algo.h>
78#include <kicad_clipboard.h>
79#include <origin_viewitem.h>
80#include <pcb_edit_frame.h>
81#include <pcb_painter.h>
83#include <string>
84#include <tool/tool_manager.h>
92#include <widgets/wx_infobar.h>
93#include <wx/hyperlink.h>
94
95
96using namespace std::placeholders;
97
98
99// files.cpp
100extern bool AskLoadBoardFileName( PCB_EDIT_FRAME* aParent, wxString* aFileName, int aCtl = 0 );
101
102// board_tables/board_stackup_table.cpp
103extern PCB_TABLE* Build_Board_Stackup_Table( BOARD* aBoard, EDA_UNITS aDisplayUnits );
104// board_tables/board_characteristics_table.cpp
105extern PCB_TABLE* Build_Board_Characteristics_Table( BOARD* aBoard, EDA_UNITS aDisplayUnits );
106
107
109 PCB_TOOL_BASE( "pcbnew.Control" ),
110 m_frame( nullptr ),
111 m_pickerItem( nullptr )
112{
114}
115
116
120
121
123{
125
126 if( aReason == MODEL_RELOAD || aReason == GAL_SWITCH || aReason == REDRAW )
127 {
128 m_gridOrigin->SetPosition( board()->GetDesignSettings().GetGridOrigin() );
129
130 double backgroundBrightness = m_frame->GetCanvas()->GetGAL()->GetClearColor().GetBrightness();
131 COLOR4D color = m_frame->GetGridColor();
132
133 if( backgroundBrightness > 0.5 )
134 color.Darken( 0.25 );
135 else
136 color.Brighten( 0.25 );
137
138 m_gridOrigin->SetColor( color );
139
140 getView()->Remove( m_gridOrigin.get() );
141 getView()->Add( m_gridOrigin.get() );
142 }
143}
144
145
147{
148 if( m_frame->IsType( FRAME_FOOTPRINT_EDITOR ) || m_frame->IsType( FRAME_PCB_EDITOR ) )
149 {
150 if( aEvent.IsAction( &ACTIONS::newLibrary ) )
151 static_cast<PCB_BASE_EDIT_FRAME*>( m_frame )->CreateNewLibrary( _( "New Footprint Library" ) );
152 else if( aEvent.IsAction( &ACTIONS::addLibrary ) )
153 static_cast<PCB_BASE_EDIT_FRAME*>( m_frame )->AddLibrary( _( "Add Footprint Library" ) );
154 }
155
156 return 0;
157}
158
159
161{
162 if( m_frame->IsType( FRAME_FOOTPRINT_EDITOR ) )
163 static_cast<FOOTPRINT_EDIT_FRAME*>( m_frame )->LoadFootprintFromBoard( nullptr );
164
165 return 0;
166}
167
168
170{
171 if( m_frame->IsType( FRAME_FOOTPRINT_EDITOR ) )
172 static_cast<FOOTPRINT_EDIT_FRAME*>( m_frame )->SaveFootprintToBoard( true );
173 else if( m_frame->IsType( FRAME_FOOTPRINT_VIEWER ) )
174 static_cast<FOOTPRINT_VIEWER_FRAME*>( m_frame )->AddFootprintToPCB();
175
176 return 0;
177}
178
179
181{
182 const wxString fn = *aEvent.Parameter<wxString*>();
183 static_cast<PCB_BASE_EDIT_FRAME*>( m_frame )->AddLibrary( _( "Add Footprint Library" ), fn,
185 return 0;
186}
187
188
190{
191 const wxString fn = *aEvent.Parameter<wxString*>();
192 static_cast<FOOTPRINT_EDIT_FRAME*>( m_frame )->ImportFootprint( fn );
193 m_frame->Zoom_Automatique( false );
194 return 0;
195}
196
197
199{
200 if( m_frame->IsType( FRAME_FOOTPRINT_VIEWER ) )
201 static_cast<FOOTPRINT_VIEWER_FRAME*>( m_frame )->SelectAndViewFootprint( aEvent.Parameter<FPVIEWER_CONSTANTS>() );
202
203 return 0;
204}
205
206
207template<class T>
208void Flip( T& aValue )
209{
210 aValue = !aValue;
211}
212
213
215{
216 Flip( displayOptions().m_DisplayPcbTrackFill );
217
218 for( PCB_TRACK* track : board()->Tracks() )
219 {
220 if( track->Type() == PCB_TRACE_T || track->Type() == PCB_ARC_T )
221 view()->Update( track, KIGFX::REPAINT );
222 }
223
224 for( BOARD_ITEM* shape : board()->Drawings() )
225 {
226 if( shape->Type() == PCB_SHAPE_T && static_cast<PCB_SHAPE*>( shape )->IsOnCopperLayer() )
227 view()->Update( shape, KIGFX::REPAINT );
228 }
229
230 canvas()->Refresh();
231
232 return 0;
233}
234
235
237{
238 if( PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame ) )
239 {
240 if( aEvent.IsAction( &PCB_ACTIONS::showRatsnest ) )
241 {
242 // N.B. Do not disable the Ratsnest layer here. We use it for local ratsnest
243 Flip( displayOptions().m_ShowGlobalRatsnest );
244 editFrame->SetElementVisibility( LAYER_RATSNEST, displayOptions().m_ShowGlobalRatsnest );
245 }
246 else if( aEvent.IsAction( &PCB_ACTIONS::ratsnestLineMode ) )
247 {
248 Flip( displayOptions().m_DisplayRatsnestLinesCurved );
249 }
250
251 editFrame->OnDisplayOptionsChanged();
252
254 canvas()->Refresh();
255 }
256
257 return 0;
258}
259
260
262{
263 Flip( displayOptions().m_DisplayViaFill );
264
265 for( PCB_TRACK* track : board()->Tracks() )
266 {
267 if( track->Type() == PCB_VIA_T )
268 view()->Update( track, KIGFX::REPAINT );
269 }
270
271 canvas()->Refresh();
272 return 0;
273}
274
275
282{
283 if( Pgm().GetCommonSettings()->m_DoNotShowAgain.zone_fill_warning )
284 return;
285
286 bool unfilledZones = false;
287
288 for( const ZONE* zone : board()->Zones() )
289 {
290 if( !zone->GetIsRuleArea() && !zone->IsFilled() )
291 {
292 unfilledZones = true;
293 break;
294 }
295 }
296
297 if( unfilledZones )
298 {
299 WX_INFOBAR* infobar = m_frame->GetInfoBar();
300 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Don't show again" ), wxEmptyString );
301
302 button->Bind( wxEVT_COMMAND_HYPERLINK, std::function<void( wxHyperlinkEvent& aEvent )>(
303 [&]( wxHyperlinkEvent& aEvent )
304 {
306 m_frame->GetInfoBar()->Dismiss();
307 } ) );
308
309 infobar->RemoveAllButtons();
310 infobar->AddButton( button );
311
312 wxString msg;
313 msg.Printf( _( "Not all zones are filled. Use Edit > Fill All Zones (%s) "
314 "if you wish to see all fills." ),
316
317 infobar->ShowMessageFor( msg, 5000, wxICON_WARNING );
318 }
319}
320
321
323{
324 PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
325
326 // Apply new display options to the GAL canvas
328 {
330
332 }
333 else if( aEvent.IsAction( &PCB_ACTIONS::zoneDisplayOutline ) )
334 {
336 }
337 else if( aEvent.IsAction( &PCB_ACTIONS::zoneDisplayFractured ) )
338 {
340 }
342 {
344 }
345 else if( aEvent.IsAction( &PCB_ACTIONS::zoneDisplayToggle ) )
346 {
349 else
351 }
352 else
353 {
354 wxFAIL;
355 }
356
357 m_frame->SetDisplayOptions( opts );
358
359 for( ZONE* zone : board()->Zones() )
360 view()->Update( zone, KIGFX::REPAINT );
361
362 canvas()->Refresh();
363
364 return 0;
365}
366
367
369{
370 PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
371
374
375 m_frame->SetDisplayOptions( opts );
376 return 0;
377}
378
379
381{
382 PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
383
384 switch( opts.m_ContrastModeDisplay )
385 {
389 }
390
391 m_frame->SetDisplayOptions( opts );
392
394 return 0;
395}
396
397
399{
400 if( !Pgm().GetCommonSettings()->m_Input.hotkey_feedback )
401 return 0;
402
403 PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
404
405 wxArrayString labels;
406 labels.Add( _( "Normal" ) );
407 labels.Add( _( "Dimmed" ) );
408 labels.Add( _( "Hidden" ) );
409
410 if( !m_frame->GetHotkeyPopup() )
411 m_frame->CreateHotkeyPopup();
412
413 HOTKEY_CYCLE_POPUP* popup = m_frame->GetHotkeyPopup();
414
415 if( popup )
416 {
417 popup->Popup( _( "Inactive Layer Display" ), labels, static_cast<int>( opts.m_ContrastModeDisplay ) );
418 }
419
420 return 0;
421}
422
423
425{
426 PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
427
428 switch( opts.m_NetColorMode )
429 {
433 }
434
435 m_frame->SetDisplayOptions( opts );
436 return 0;
437}
438
439
441{
442 if( PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame ) )
443 {
444 if( !displayOptions().m_ShowGlobalRatsnest )
445 {
448 }
449 else if( displayOptions().m_RatsnestMode == RATSNEST_MODE::ALL )
450 {
452 }
453 else
454 {
456 }
457
458 editFrame->SetElementVisibility( LAYER_RATSNEST, displayOptions().m_ShowGlobalRatsnest );
459
460 editFrame->OnDisplayOptionsChanged();
461
463 canvas()->Refresh();
464 }
465
466 return 0;
467}
468
469
471{
472 m_frame->SwitchLayer( aEvent.Parameter<PCB_LAYER_ID>() );
473
474 return 0;
475}
476
477
479{
480 BOARD* brd = board();
481 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
482 bool wraparound = false;
483
484 if( !IsCopperLayer( layer ) )
485 {
486 m_frame->SwitchLayer( B_Cu );
487 return 0;
488 }
489
490 LSET cuMask = LSET::AllCuMask( brd->GetCopperLayerCount() );
491 LSEQ layerStack = cuMask.UIOrder();
492
493 int ii = 0;
494
495 // Find the active layer in list
496 for( ; ii < (int) layerStack.size(); ii++ )
497 {
498 if( layer == layerStack[ii] )
499 break;
500 }
501
502 // Find the next visible layer in list
503 for( ; ii < (int) layerStack.size(); ii++ )
504 {
505 int jj = ii + 1;
506
507 if( jj >= (int) layerStack.size() )
508 jj = 0;
509
510 layer = layerStack[jj];
511
512 if( brd->IsLayerVisible( layer ) )
513 break;
514
515 if( jj == 0 ) // the end of list is reached. Try from the beginning
516 {
517 if( wraparound )
518 {
519 wxBell();
520 return 0;
521 }
522 else
523 {
524 wraparound = true;
525 ii = -1;
526 }
527 }
528 }
529
530 wxCHECK( IsCopperLayer( layer ), 0 );
531 m_frame->SwitchLayer( layer );
532
533 return 0;
534}
535
536
538{
539 BOARD* brd = board();
540 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
541 bool wraparound = false;
542
543 if( !IsCopperLayer( layer ) )
544 {
545 m_frame->SwitchLayer( F_Cu );
546 return 0;
547 }
548
549 LSET cuMask = LSET::AllCuMask( brd->GetCopperLayerCount() );
550 LSEQ layerStack = cuMask.UIOrder();
551
552 int ii = 0;
553
554 // Find the active layer in list
555 for( ; ii < (int) layerStack.size(); ii++ )
556 {
557 if( layer == layerStack[ii] )
558 break;
559 }
560
561 // Find the previous visible layer in list
562 for( ; ii >= 0; ii-- )
563 {
564 int jj = ii - 1;
565
566 if( jj < 0 )
567 jj = (int) layerStack.size() - 1;
568
569 layer = layerStack[jj];
570
571 if( brd->IsLayerVisible( layer ) )
572 break;
573
574 if( ii == 0 ) // the start of list is reached. Try from the last
575 {
576 if( wraparound )
577 {
578 wxBell();
579 return 0;
580 }
581 else
582 {
583 wraparound = true;
584 ii = 1;
585 }
586 }
587 }
588
589 wxCHECK( IsCopperLayer( layer ), 0 );
590 m_frame->SwitchLayer( layer );
591
592 return 0;
593}
594
595
597{
598 int currentLayer = m_frame->GetActiveLayer();
599 PCB_SCREEN* screen = m_frame->GetScreen();
600
601 if( currentLayer == screen->m_Route_Layer_TOP )
602 m_frame->SwitchLayer( screen->m_Route_Layer_BOTTOM );
603 else
604 m_frame->SwitchLayer( screen->m_Route_Layer_TOP );
605
606 return 0;
607}
608
609
610// It'd be nice to share the min/max with the DIALOG_COLOR_PICKER, but those are
611// set in wxFormBuilder.
612#define ALPHA_MIN 0.20
613#define ALPHA_MAX 1.00
614#define ALPHA_STEP 0.05
615
616
618{
619 COLOR_SETTINGS* settings = m_frame->GetColorSettings();
620 int currentLayer = m_frame->GetActiveLayer();
621 KIGFX::COLOR4D currentColor = settings->GetColor( currentLayer );
622
623 if( currentColor.a <= ALPHA_MAX - ALPHA_STEP )
624 {
625 currentColor.a += ALPHA_STEP;
626 settings->SetColor( currentLayer, currentColor );
627 m_frame->GetCanvas()->UpdateColors();
628
629 KIGFX::VIEW* view = m_frame->GetCanvas()->GetView();
630 view->UpdateLayerColor( currentLayer );
631 view->UpdateLayerColor( GetNetnameLayer( currentLayer ) );
632
633 if( IsCopperLayer( currentLayer ) )
634 view->UpdateLayerColor( ZONE_LAYER_FOR( currentLayer ) );
635
636 m_frame->GetCanvas()->ForceRefresh();
637 }
638 else
639 {
640 wxBell();
641 }
642
643 return 0;
644}
645
646
648{
649 COLOR_SETTINGS* settings = m_frame->GetColorSettings();
650 int currentLayer = m_frame->GetActiveLayer();
651 KIGFX::COLOR4D currentColor = settings->GetColor( currentLayer );
652
653 if( currentColor.a >= ALPHA_MIN + ALPHA_STEP )
654 {
655 currentColor.a -= ALPHA_STEP;
656 settings->SetColor( currentLayer, currentColor );
657 m_frame->GetCanvas()->UpdateColors();
658
659 KIGFX::VIEW* view = m_frame->GetCanvas()->GetView();
660 view->UpdateLayerColor( currentLayer );
661 view->UpdateLayerColor( GetNetnameLayer( currentLayer ) );
662
663 if( IsCopperLayer( currentLayer ) )
664 view->UpdateLayerColor( ZONE_LAYER_FOR( currentLayer ) );
665
666 m_frame->GetCanvas()->ForceRefresh();
667 }
668 else
669 {
670 wxBell();
671 }
672
673 return 0;
674}
675
676
678{
679 if( PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame ) )
680 {
681 LAYER_PAIR_SETTINGS* settings = editFrame->GetLayerPairSettings();
682
683 if( !settings )
684 return 0;
685
686 int currentIndex;
687 std::vector<LAYER_PAIR_INFO> presets = settings->GetEnabledLayerPairs( currentIndex );
688
689 if( presets.size() < 2 )
690 return 0;
691
692 if( currentIndex < 0 )
693 {
694 wxASSERT_MSG( false, "Current layer pair not found in layer settings" );
695 currentIndex = 0;
696 }
697
698 const int nextIndex = ( currentIndex + 1 ) % presets.size();
699 const LAYER_PAIR& nextPair = presets[nextIndex].GetLayerPair();
700
701 settings->SetCurrentLayerPair( nextPair );
702
704 }
705
706 return 0;
707}
708
709
711{
712 if( !Pgm().GetCommonSettings()->m_Input.hotkey_feedback )
713 return 0;
714
715 if( PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame ) )
716 {
717 LAYER_PAIR_SETTINGS* settings = editFrame->GetLayerPairSettings();
718
719 if( !settings )
720 return 0;
721
722 PCB_LAYER_PRESENTATION layerPresentation( editFrame );
723
724 int currentIndex;
725 std::vector<LAYER_PAIR_INFO> presets = settings->GetEnabledLayerPairs( currentIndex );
726
727 wxArrayString labels;
728 for( const LAYER_PAIR_INFO& layerPairInfo : presets )
729 {
730 wxString label = layerPresentation.getLayerPairName( layerPairInfo.GetLayerPair() );
731
732 if( layerPairInfo.GetName() )
733 label += wxT( " (" ) + *layerPairInfo.GetName() + wxT( ")" );
734
735 labels.Add( label );
736 }
737
738 if( !editFrame->GetHotkeyPopup() )
739 editFrame->CreateHotkeyPopup();
740
741 HOTKEY_CYCLE_POPUP* popup = editFrame->GetHotkeyPopup();
742
743 if( popup )
744 {
745 int selection = currentIndex;
746 popup->Popup( _( "Preset Layer Pairs" ), labels, selection );
747 }
748 }
749
750 return 0;
751}
752
753
755 const VECTOR2D& aPoint )
756{
757 aFrame->GetDesignSettings().SetGridOrigin( VECTOR2I( aPoint ) );
758 aView->GetGAL()->SetGridOrigin( aPoint );
759 originViewItem->SetPosition( aPoint );
760 aView->MarkDirty();
761 aFrame->OnModify();
762}
763
764
766{
767 VECTOR2D* origin = aEvent.Parameter<VECTOR2D*>();
768
769 if( origin )
770 {
771 // We can't undo the other grid dialog settings, so no sense undoing just the origin
772 DoSetGridOrigin( getView(), m_frame, m_gridOrigin.get(), *origin );
773 delete origin;
774 }
775 else
776 {
778 return 0;
779
780 PCB_PICKER_TOOL* picker = m_toolMgr->GetTool<PCB_PICKER_TOOL>();
781
782 if( !picker ) // Happens in footprint wizard
783 return 0;
784
785 // Deactivate other tools; particularly important if another PICKER is currently running
786 Activate();
787
788 picker->SetCursor( KICURSOR::PLACE );
789 picker->ClearHandlers();
790
791 picker->SetClickHandler(
792 [this]( const VECTOR2D& pt ) -> bool
793 {
794 m_frame->SaveCopyInUndoList( m_gridOrigin.get(), UNDO_REDO::GRIDORIGIN );
796 return false; // drill origin is a one-shot; don't continue with tool
797 } );
798
799 m_toolMgr->RunAction( ACTIONS::pickerTool, &aEvent );
800 }
801
802 return 0;
803}
804
805
807{
808 m_frame->SaveCopyInUndoList( m_gridOrigin.get(), UNDO_REDO::GRIDORIGIN );
810 return 0;
811}
812
813
814#define HITTEST_THRESHOLD_PIXELS 5
815
816
818{
819 if( m_isFootprintEditor && !m_frame->GetBoard()->GetFirstFootprint() )
820 return 0;
821
822 PCB_PICKER_TOOL* picker = m_toolMgr->GetTool<PCB_PICKER_TOOL>();
823
824 m_pickerItem = nullptr;
826
827 // Deactivate other tools; particularly important if another PICKER is currently running
828 Activate();
829
830 picker->SetCursor( KICURSOR::REMOVE );
831 picker->SetSnapping( false );
832 picker->ClearHandlers();
833
834 picker->SetClickHandler(
835 [this]( const VECTOR2D& aPosition ) -> bool
836 {
837 if( m_pickerItem )
838 {
839 if( m_pickerItem && m_pickerItem->IsLocked() )
840 {
842 m_statusPopup->SetText( _( "Item locked." ) );
843 m_statusPopup->PopupFor( 2000 );
844 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
845 return true;
846 }
847
848 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
849 selectionTool->UnbrightenItem( m_pickerItem );
850
851 PCB_SELECTION items;
852 items.Add( m_pickerItem );
853
854 EDIT_TOOL* editTool = m_toolMgr->GetTool<EDIT_TOOL>();
855 editTool->DeleteItems( items, false );
856
857 m_pickerItem = nullptr;
858 }
859
860 return true;
861 } );
862
863 picker->SetMotionHandler(
864 [this]( const VECTOR2D& aPos )
865 {
866 BOARD* board = m_frame->GetBoard();
867 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
868 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
869 GENERAL_COLLECTOR collector;
870 collector.m_Threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
871
873 collector.Collect( board, GENERAL_COLLECTOR::FootprintItems, aPos, guide );
874 else
875 collector.Collect( board, GENERAL_COLLECTOR::BoardLevelItems, aPos, guide );
876
877 // Remove unselectable items
878 for( int i = collector.GetCount() - 1; i >= 0; --i )
879 {
880 if( !selectionTool->Selectable( collector[i] ) )
881 collector.Remove( i );
882 }
883
884 selectionTool->FilterCollectorForHierarchy( collector, false );
885 selectionTool->FilterCollectedItems( collector, false, nullptr );
886
887 if( collector.GetCount() > 1 )
888 selectionTool->GuessSelectionCandidates( collector, aPos );
889
890 BOARD_ITEM* item = collector.GetCount() == 1 ? collector[0] : nullptr;
891
892 if( m_pickerItem != item )
893 {
894 if( m_pickerItem )
895 selectionTool->UnbrightenItem( m_pickerItem );
896
897 m_pickerItem = item;
898
899 if( m_pickerItem )
900 selectionTool->BrightenItem( m_pickerItem );
901 }
902 } );
903
904 picker->SetFinalizeHandler(
905 [this]( const int& aFinalState )
906 {
907 if( m_pickerItem )
908 m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->UnbrightenItem( m_pickerItem );
909
910 m_statusPopup.reset();
911
912 // Ensure the cursor gets changed&updated
913 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
914 m_frame->GetCanvas()->Refresh();
915 } );
916
917 m_toolMgr->RunAction( ACTIONS::pickerTool, &aEvent );
918
919 return 0;
920}
921
922
923static void pasteFootprintItemsToFootprintEditor( FOOTPRINT* aClipFootprint, BOARD* aBoard,
924 std::vector<BOARD_ITEM*>& aPastedItems )
925{
926 FOOTPRINT* editorFootprint = aBoard->GetFirstFootprint();
927
928 aClipFootprint->SetParent( aBoard );
929
930 for( PAD* pad : aClipFootprint->Pads() )
931 {
932 pad->SetParent( editorFootprint );
933 aPastedItems.push_back( pad );
934 }
935
936 aClipFootprint->Pads().clear();
937
938 // Not all items can be added to the current footprint: mandatory fields are already existing
939 // in the current footprint.
940 //
941 for( PCB_FIELD* field : aClipFootprint->GetFields() )
942 {
943 wxCHECK2( field, continue );
944
945 if( field->IsMandatory() )
946 {
947 if( EDA_GROUP* parentGroup = field->GetParentGroup() )
948 parentGroup->RemoveItem( field );
949 }
950 else
951 {
952 PCB_TEXT* text = static_cast<PCB_TEXT*>( field );
953
954 text->SetTextAngle( text->GetTextAngle() - aClipFootprint->GetOrientation() );
955 text->SetTextAngle( text->GetTextAngle() + editorFootprint->GetOrientation() );
956
957 VECTOR2I pos = field->GetFPRelativePosition();
958 field->SetParent( editorFootprint );
959 field->SetFPRelativePosition( pos );
960
961 aPastedItems.push_back( field );
962 }
963 }
964
965 aClipFootprint->GetFields().clear();
966
967 for( BOARD_ITEM* item : aClipFootprint->GraphicalItems() )
968 {
969 if( item->Type() == PCB_TEXT_T )
970 {
971 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
972
973 text->SetTextAngle( text->GetTextAngle() - aClipFootprint->GetOrientation() );
974 text->SetTextAngle( text->GetTextAngle() + editorFootprint->GetOrientation() );
975 }
976
977 item->Rotate( item->GetPosition(), -aClipFootprint->GetOrientation() );
978 item->Rotate( item->GetPosition(), editorFootprint->GetOrientation() );
979
980 VECTOR2I pos = item->GetFPRelativePosition();
981 item->SetParent( editorFootprint );
982 item->SetFPRelativePosition( pos );
983
984 aPastedItems.push_back( item );
985 }
986
987 aClipFootprint->GraphicalItems().clear();
988
989 for( ZONE* zone : aClipFootprint->Zones() )
990 {
991 zone->SetParent( editorFootprint );
992 aPastedItems.push_back( zone );
993 }
994
995 aClipFootprint->Zones().clear();
996
997 for( PCB_GROUP* group : aClipFootprint->Groups() )
998 {
999 group->SetParent( editorFootprint );
1000 aPastedItems.push_back( group );
1001 }
1002
1003 aClipFootprint->Groups().clear();
1004}
1005
1006
1007void PCB_CONTROL::pruneItemLayers( std::vector<BOARD_ITEM*>& aItems )
1008{
1009 // Do not prune items or layers when copying to the FP editor, because all
1010 // layers are accepted, even if they are not enabled in the dummy board
1011 // This is mainly true for internal copper layers: all are allowed but only one
1012 // (In1.cu) is enabled for the GUI.
1014 return;
1015
1016 LSET enabledLayers = board()->GetEnabledLayers();
1017 std::vector<BOARD_ITEM*> returnItems;
1018 bool fpItemDeleted = false;
1019
1020 for( BOARD_ITEM* item : aItems )
1021 {
1022 if( item->Type() == PCB_FOOTPRINT_T )
1023 {
1024 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
1025
1026 // Items living in a parent footprint are never removed, even if their
1027 // layer does not exist in the board editor
1028 // Otherwise the parent footprint could be seriously broken especially
1029 // if some layers are later re-enabled.
1030 // Moreover a fp lives in a fp library, that does not know the enabled
1031 // layers of a given board, so fp items are just ignored when on not
1032 // enabled layers in board editor
1033 returnItems.push_back( fp );
1034 }
1035 else if( item->Type() == PCB_GROUP_T || item->Type() == PCB_GENERATOR_T )
1036 {
1037 returnItems.push_back( item );
1038 }
1039 else
1040 {
1041 LSET allowed = item->GetLayerSet() & enabledLayers;
1042 bool item_valid = true;
1043
1044 // Ensure, for vias, the top and bottom layers are compatible with
1045 // the current board copper layers.
1046 // Otherwise they must be skipped, even is one layer is valid
1047 if( item->Type() == PCB_VIA_T )
1048 item_valid = static_cast<PCB_VIA*>( item )->HasValidLayerPair( board()->GetCopperLayerCount() );
1049
1050 if( allowed.any() && item_valid )
1051 {
1052 item->SetLayerSet( allowed );
1053 returnItems.push_back( item );
1054 }
1055 else
1056 {
1057 if( EDA_GROUP* parentGroup = item->GetParentGroup() )
1058 parentGroup->RemoveItem( item );
1059 }
1060 }
1061 }
1062
1063 if( ( returnItems.size() < aItems.size() ) || fpItemDeleted )
1064 {
1065 DisplayError( m_frame, _( "Warning: some pasted items were on layers which are not "
1066 "present in the current board.\n"
1067 "These items could not be pasted.\n" ) );
1068 }
1069
1070 aItems = returnItems;
1071}
1072
1073
1074int PCB_CONTROL::Paste( const TOOL_EVENT& aEvent )
1075{
1076 // The viewer frames cannot paste
1077 if( !m_frame->IsType( FRAME_FOOTPRINT_EDITOR ) && !m_frame->IsType( FRAME_PCB_EDITOR ) )
1078 return 0;
1079
1080 bool isFootprintEditor = m_isFootprintEditor || m_frame->IsType( FRAME_FOOTPRINT_EDITOR );
1081
1082 // The clipboard can contain two different things, an entire kicad_pcb or a single footprint
1083 if( isFootprintEditor && ( !board() || !footprint() ) )
1084 return 0;
1085
1086 // We should never get here if a modal dialog is up... but we do on MacOS.
1087 // https://gitlab.com/kicad/code/kicad/-/issues/18912
1088#ifdef __WXMAC__
1089 if( wxDialog::OSXHasModalDialogsOpen() )
1090 {
1091 wxBell();
1092 return 0;
1093 }
1094#endif
1095
1096 BOARD_COMMIT commit( m_frame );
1097
1098 CLIPBOARD_IO pi;
1099 BOARD_ITEM* clipItem = pi.Parse();
1100
1101 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
1102
1103 if( selTool && clipItem )
1104 {
1105 PCB_SELECTION& selection = selTool->GetSelection();
1106
1107 bool hasTableCells = false;
1108
1109 for( EDA_ITEM* item : selection )
1110 {
1111 if( item->Type() == PCB_TABLECELL_T )
1112 {
1113 hasTableCells = true;
1114 break;
1115 }
1116 }
1117
1118 if( hasTableCells )
1119 {
1120 PCB_TABLE* clipboardTable = nullptr;
1121
1122 if( clipItem->Type() == PCB_T )
1123 {
1124 BOARD* clipBoard = static_cast<BOARD*>( clipItem );
1125
1126 for( BOARD_ITEM* item : clipBoard->Drawings() )
1127 {
1128 if( item->Type() == PCB_TABLE_T )
1129 {
1130 clipboardTable = static_cast<PCB_TABLE*>( item );
1131 break;
1132 }
1133 }
1134 }
1135
1136 if( clipboardTable )
1137 {
1138 PCB_EDIT_TABLE_TOOL* tableEditTool = m_toolMgr->GetTool<PCB_EDIT_TABLE_TOOL>();
1139
1140 if( tableEditTool )
1141 {
1142 wxString errorMsg;
1143
1144 if( !tableEditTool->validatePasteIntoSelection( selection, errorMsg ) )
1145 {
1146 DisplayError( m_frame, errorMsg );
1147 return 0;
1148 }
1149
1150 if( tableEditTool->pasteCellsIntoSelection( selection, clipboardTable, commit ) )
1151 {
1152 commit.Push( _( "Paste Cells" ) );
1153 return 0;
1154 }
1155 else
1156 {
1157 DisplayError( m_frame, _( "Failed to paste cells" ) );
1158 return 0;
1159 }
1160 }
1161 }
1162 }
1163 }
1164
1165 if( !clipItem )
1166 {
1167 // When the clipboard doesn't parse, create a PCB item with the clipboard contents
1168 std::vector<BOARD_ITEM*> newItems;
1169
1170 if( std::unique_ptr<wxBitmap> clipImg = GetImageFromClipboard() )
1171 {
1172 auto refImg = std::make_unique<PCB_REFERENCE_IMAGE>( m_frame->GetModel() );
1173
1174 if( refImg->GetReferenceImage().SetImage( clipImg->ConvertToImage() ) )
1175 newItems.push_back( refImg.release() );
1176 }
1177 else
1178 {
1179 const wxString clipText = GetClipboardUTF8();
1180
1181 if( clipText.empty() )
1182 return 0;
1183
1184 // If it wasn't content, then paste as a text object.
1185 if( clipText.size() > static_cast<size_t>( ADVANCED_CFG::GetCfg().m_MaxPastedTextLength ) )
1186 {
1187 int result = IsOK( m_frame, _( "Pasting a long text text string may be very slow. "
1188 "Do you want to continue?" ) );
1189 if( !result )
1190 return 0;
1191 }
1192
1193 std::unique_ptr<PCB_TEXT> item = std::make_unique<PCB_TEXT>( m_frame->GetModel() );
1194 item->SetText( clipText );
1195 item->SetLayer( m_frame->GetActiveLayer() );
1196
1197 newItems.push_back( item.release() );
1198 }
1199
1200 bool cancelled = !placeBoardItems( &commit, newItems, true, false, false, false );
1201
1202 if( cancelled )
1203 commit.Revert();
1204 else
1205 commit.Push( _( "Paste Text" ) );
1206 return 0;
1207 }
1208
1209 // If we get here, we have a parsed board/FP to paste
1210
1212 bool clear_nets = false;
1213 const wxString defaultRef = wxT( "REF**" );
1214
1215 if( aEvent.IsAction( &ACTIONS::pasteSpecial ) )
1216 {
1217 DIALOG_PASTE_SPECIAL dlg( m_frame, &mode, defaultRef );
1218
1219 if( clipItem->Type() != PCB_T )
1220 dlg.HideClearNets();
1221
1222 if( dlg.ShowModal() == wxID_CANCEL )
1223 return 0;
1224
1225 clear_nets = dlg.GetClearNets();
1226 }
1227
1228 if( clipItem->Type() == PCB_T )
1229 {
1230 BOARD* clipBoard = static_cast<BOARD*>( clipItem );
1231
1232 if( isFootprintEditor || clear_nets )
1233 {
1234 for( BOARD_CONNECTED_ITEM* item : clipBoard->AllConnectedItems() )
1235 item->SetNet( NETINFO_LIST::OrphanedItem() );
1236 }
1237 else
1238 {
1239 clipBoard->MapNets( m_frame->GetBoard() );
1240 }
1241 }
1242
1243 bool cancelled = false;
1244
1245 switch( clipItem->Type() )
1246 {
1247 case PCB_T:
1248 {
1249 BOARD* clipBoard = static_cast<BOARD*>( clipItem );
1250
1251 if( isFootprintEditor )
1252 {
1253 FOOTPRINT* editorFootprint = board()->GetFirstFootprint();
1254 std::vector<BOARD_ITEM*> pastedItems;
1255
1256 for( PCB_GROUP* group : clipBoard->Groups() )
1257 {
1258 group->SetParent( editorFootprint );
1259 pastedItems.push_back( group );
1260 }
1261
1262 clipBoard->RemoveAll( { PCB_GROUP_T } );
1263
1264 for( FOOTPRINT* clipFootprint : clipBoard->Footprints() )
1265 pasteFootprintItemsToFootprintEditor( clipFootprint, board(), pastedItems );
1266
1267 for( BOARD_ITEM* clipDrawItem : clipBoard->Drawings() )
1268 {
1269 switch( clipDrawItem->Type() )
1270 {
1271 case PCB_TEXT_T:
1272 case PCB_TEXTBOX_T:
1273 case PCB_TABLE_T:
1274 case PCB_SHAPE_T:
1275 case PCB_BARCODE_T:
1276 case PCB_DIM_ALIGNED_T:
1277 case PCB_DIM_CENTER_T:
1278 case PCB_DIM_LEADER_T:
1280 case PCB_DIM_RADIAL_T:
1281 clipDrawItem->SetParent( editorFootprint );
1282 pastedItems.push_back( clipDrawItem );
1283 break;
1284
1285 default:
1286 // Everything we *didn't* put into pastedItems is going to get nuked, so
1287 // make sure it's not still included in its parent group.
1288 if( EDA_GROUP* parentGroup = clipDrawItem->GetParentGroup() )
1289 parentGroup->RemoveItem( clipDrawItem );
1290
1291 break;
1292 }
1293 }
1294
1295 // NB: PCB_SHAPE_T actually removes everything in Drawings() (including PCB_TEXTs,
1296 // PCB_TABLEs, PCB_BARCODEs, dimensions, etc.), not just PCB_SHAPEs.)
1297 clipBoard->RemoveAll( { PCB_SHAPE_T } );
1298
1299 clipBoard->Visit(
1300 [&]( EDA_ITEM* item, void* testData )
1301 {
1302 if( item->IsBOARD_ITEM() )
1303 {
1304 // Anything still on the clipboard didn't get copied and needs to be
1305 // removed from the pasted groups.
1306 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
1307 EDA_GROUP* parentGroup = boardItem->GetParentGroup();
1308
1309 if( parentGroup )
1310 parentGroup->RemoveItem( boardItem );
1311 }
1312
1314 },
1316
1317 delete clipBoard;
1318
1319 pruneItemLayers( pastedItems );
1320
1321 cancelled = !placeBoardItems( &commit, pastedItems, true, true, mode == PASTE_MODE::UNIQUE_ANNOTATIONS,
1322 false );
1323 }
1324 else // isBoardEditor
1325 {
1326 // Fixup footprint component classes
1327 for( FOOTPRINT* fp : clipBoard->Footprints() )
1328 {
1329 fp->ResolveComponentClassNames( board(), fp->GetTransientComponentClassNames() );
1330 fp->ClearTransientComponentClassNames();
1331 }
1332
1333 if( mode == PASTE_MODE::REMOVE_ANNOTATIONS )
1334 {
1335 for( FOOTPRINT* fp : clipBoard->Footprints() )
1336 fp->SetReference( defaultRef );
1337 }
1338
1339 cancelled = !placeBoardItems( &commit, clipBoard, true, mode == PASTE_MODE::UNIQUE_ANNOTATIONS, false );
1340 }
1341
1342 break;
1343 }
1344
1345 case PCB_FOOTPRINT_T:
1346 {
1347 FOOTPRINT* clipFootprint = static_cast<FOOTPRINT*>( clipItem );
1348 std::vector<BOARD_ITEM*> pastedItems;
1349
1350 if( isFootprintEditor )
1351 {
1352 pasteFootprintItemsToFootprintEditor( clipFootprint, board(), pastedItems );
1353 delete clipFootprint;
1354 }
1355 else
1356 {
1357 if( mode == PASTE_MODE::REMOVE_ANNOTATIONS )
1358 clipFootprint->SetReference( defaultRef );
1359
1360 clipFootprint->SetParent( board() );
1361 clipFootprint->ResolveComponentClassNames( board(), clipFootprint->GetTransientComponentClassNames() );
1362 clipFootprint->ClearTransientComponentClassNames();
1363 pastedItems.push_back( clipFootprint );
1364 }
1365
1366 pruneItemLayers( pastedItems );
1367
1368 cancelled = !placeBoardItems( &commit, pastedItems, true, true, mode == PASTE_MODE::UNIQUE_ANNOTATIONS, false );
1369 break;
1370 }
1371
1372 default:
1373 m_frame->DisplayToolMsg( _( "Invalid clipboard contents" ) );
1374 break;
1375 }
1376
1377 if( cancelled )
1378 commit.Revert();
1379 else
1380 commit.Push( _( "Paste" ) );
1381
1382 return 1;
1383}
1384
1385
1387{
1388 wxString fileName;
1389
1390 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
1391
1392 if( !editFrame )
1393 return 1;
1394
1395 // Pick a file to append
1396 if( !AskLoadBoardFileName( editFrame, &fileName, KICTL_KICAD_ONLY ) )
1397 return 1;
1398
1400 IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::FindPlugin( pluginType ) );
1401
1402 if( !pi )
1403 return 1;
1404
1405 return AppendBoard( *pi, fileName );
1406}
1407
1408
1410{
1411 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
1412
1413 if( !editFrame )
1414 return 1;
1415
1416 if( !editFrame->GetDesignBlockPane()->GetSelectedLibId().IsValid() )
1417 return 1;
1418
1419 DESIGN_BLOCK_PANE* designBlockPane = editFrame->GetDesignBlockPane();
1420 const LIB_ID selectedLibId = designBlockPane->GetSelectedLibId();
1421 std::unique_ptr<DESIGN_BLOCK> designBlock( designBlockPane->GetDesignBlock( selectedLibId, true, true ) );
1422
1423 if( !designBlock )
1424 {
1425 wxString msg;
1426 msg.Printf( _( "Could not find design block %s." ), selectedLibId.GetUniStringLibId() );
1427 editFrame->ShowInfoBarError( msg, true );
1428 return 1;
1429 }
1430
1431 if( designBlock->GetBoardFile().IsEmpty() || !wxFileName::FileExists( designBlock->GetBoardFile() ) )
1432 {
1433 editFrame->ShowInfoBarError( _( "Design block has no layout to place." ), true );
1434 return 1;
1435 }
1436
1438 IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::FindPlugin( pluginType ) );
1439
1440 if( !pi )
1441 return 1;
1442
1443 bool repeatPlacement = false;
1444
1445 if( APP_SETTINGS_BASE* cfg = editFrame->config() )
1446 repeatPlacement = cfg->m_DesignBlockChooserPanel.repeated_placement;
1447
1448 int ret = 0;
1449
1450 do
1451 {
1452 ret = AppendBoard( *pi, designBlock->GetBoardFile(), designBlock.get() );
1453 } while( repeatPlacement && ret == 0 );
1454
1455 return ret;
1456}
1457
1459{
1460 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
1461
1462 if( !editFrame )
1463 return 1;
1464
1465 BOARD* brd = board();
1466
1467 if( !brd )
1468 return 1;
1469
1470 // Need to have a group selected and it needs to have a linked design block
1471 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
1473
1474 if( selection.Size() != 1 || selection[0]->Type() != PCB_GROUP_T )
1475 return 1;
1476
1477 PCB_GROUP* group = static_cast<PCB_GROUP*>( selection[0] );
1478
1479 if( !group->HasDesignBlockLink() )
1480 return 1;
1481
1482 // Get the associated design block
1483 DESIGN_BLOCK_PANE* designBlockPane = editFrame->GetDesignBlockPane();
1484 std::unique_ptr<DESIGN_BLOCK> designBlock( designBlockPane->GetDesignBlock( group->GetDesignBlockLibId(),
1485 true, true ) );
1486
1487 if( !designBlock )
1488 {
1489 wxString msg;
1490 msg.Printf( _( "Could not find design block %s." ), group->GetDesignBlockLibId().GetUniStringLibId() );
1491 m_frame->GetInfoBar()->ShowMessageFor( msg, 5000, wxICON_WARNING );
1492 return 1;
1493 }
1494
1495 if( designBlock->GetBoardFile().IsEmpty() )
1496 {
1497 wxString msg;
1498 msg.Printf( _( "Design block %s does not have a board file." ),
1499 group->GetDesignBlockLibId().GetUniStringLibId() );
1500 m_frame->GetInfoBar()->ShowMessageFor( msg, 5000, wxICON_WARNING );
1501 return 1;
1502 }
1503
1504 BOARD_COMMIT tempCommit( m_frame );
1505
1506 std::set<EDA_ITEM*> originalItems;
1507 // Apply MCT_SKIP_STRUCT to every EDA_ITEM on the board so we know what is not part of the design block
1508 // Can't use SKIP_STRUCT as that is used and cleared by the temporary board appending
1509 brd->Visit(
1510 []( EDA_ITEM* item, void* )
1511 {
1512 item->SetFlags( MCT_SKIP_STRUCT );
1514 },
1516
1517 int ret = 1;
1518
1519 bool skipMove = true;
1520
1521 // Lambda to perform the design block layout application with proper cleanup on failure
1522 auto applyLayout = [&]() -> int
1523 {
1524 if( !m_toolMgr->RunSynchronousAction( PCB_ACTIONS::placeLinkedDesignBlock, &tempCommit,
1525 &skipMove ) )
1526 {
1527 return 1;
1528 }
1529
1530 // Lambda for the bounding box of all the components
1531 auto generateBoundingBox = []( const std::unordered_set<EDA_ITEM*>& aItems )
1532 {
1533 std::vector<VECTOR2I> bbCorners;
1534 bbCorners.reserve( aItems.size() * 4 );
1535
1536 for( EDA_ITEM* item : aItems )
1537 {
1538 const BOX2I bb = item->GetBoundingBox().GetInflated( 100000 );
1539 KIGEOM::CollectBoxCorners( bb, bbCorners );
1540 }
1541
1542 std::vector<VECTOR2I> hullVertices;
1543 BuildConvexHull( hullVertices, bbCorners );
1544
1545 SHAPE_LINE_CHAIN hull( hullVertices );
1546
1547 // Make the newly computed convex hull use only 90 degree segments
1548 return KIGEOM::RectifyPolygon( hull );
1549 };
1550
1551 // Build a rule area that contains all the components in the design block,
1552 // meaning all items without MCT_SKIP_STRUCT set.
1553 RULE_AREA dbRA;
1554
1556 dbRA.m_generateEnabled = true;
1557
1558 // Add all components that aren't marked MCT_SKIP_STRUCT to ra.m_components
1559 brd->Visit(
1560 [&]( EDA_ITEM* item, void* data )
1561 {
1562 if( !item->HasFlag( MCT_SKIP_STRUCT ) )
1563 {
1564 dbRA.m_designBlockItems.insert( item );
1565
1566 if( item->Type() == PCB_FOOTPRINT_T )
1567 dbRA.m_components.insert( static_cast<FOOTPRINT*>( item ) );
1568 }
1569
1571 },
1573
1574 // Verify that the design block placement actually added items
1575 if( dbRA.m_designBlockItems.empty() || dbRA.m_components.empty() )
1576 {
1577 tempCommit.Revert();
1578 m_frame->GetInfoBar()->ShowMessageFor(
1579 _( "Design block placement failed - no footprints were placed." ), 5000,
1580 wxICON_WARNING );
1581 return 1;
1582 }
1583
1584 dbRA.m_zone = new ZONE( board() );
1585 dbRA.m_zone->SetIsRuleArea( true );
1587 dbRA.m_zone->SetPlacementAreaEnabled( true );
1588 dbRA.m_zone->SetDoNotAllowZoneFills( false );
1589 dbRA.m_zone->SetDoNotAllowVias( false );
1590 dbRA.m_zone->SetDoNotAllowTracks( false );
1591 dbRA.m_zone->SetDoNotAllowPads( false );
1592 dbRA.m_zone->SetDoNotAllowFootprints( false );
1594 dbRA.m_zone->SetPlacementAreaSource( group->GetDesignBlockLibId().GetUniStringLibId() );
1596 dbRA.m_zone->AddPolygon( generateBoundingBox( dbRA.m_designBlockItems ) );
1597 dbRA.m_center = dbRA.m_zone->Outline()->COutline( 0 ).Centre();
1598
1599 // Create the destination rule area for the group
1600 RULE_AREA destRA;
1601
1603
1604 // Check for locked footprints and collect destination components
1605 for( EDA_ITEM* item : group->GetItems() )
1606 {
1607 if( item->Type() == PCB_FOOTPRINT_T )
1608 {
1609 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
1610
1611 if( fp->IsLocked() )
1612 {
1613 wxString msg;
1614 msg.Printf( _( "Footprint %s is locked and cannot be placed." ), fp->GetReference() );
1615 m_frame->GetInfoBar()->ShowMessageFor( msg, 5000, wxICON_WARNING );
1616 tempCommit.Revert();
1617 delete dbRA.m_zone;
1618 return 1;
1619 }
1620
1621 destRA.m_components.insert( fp );
1622 }
1623 }
1624
1625 // Verify the group has footprints to match
1626 if( destRA.m_components.empty() )
1627 {
1628 tempCommit.Revert();
1629 delete dbRA.m_zone;
1630 m_frame->GetInfoBar()->ShowMessageFor(
1631 _( "Selected group contains no footprints to place." ), 5000, wxICON_WARNING );
1632 return 1;
1633 }
1634
1635 destRA.m_zone = new ZONE( board() );
1636 destRA.m_zone->SetZoneName( wxString::Format( wxT( "design-block-dest-%s" ),
1637 group->GetDesignBlockLibId().GetUniStringLibId() ) );
1638 destRA.m_zone->SetIsRuleArea( true );
1639 destRA.m_zone->SetLayerSet( LSET::AllCuMask() );
1640 destRA.m_zone->SetPlacementAreaEnabled( true );
1641 destRA.m_zone->SetDoNotAllowZoneFills( false );
1642 destRA.m_zone->SetDoNotAllowVias( false );
1643 destRA.m_zone->SetDoNotAllowTracks( false );
1644 destRA.m_zone->SetDoNotAllowPads( false );
1645 destRA.m_zone->SetDoNotAllowFootprints( false );
1647 destRA.m_zone->SetPlacementAreaSource( group->GetName() );
1649 destRA.m_zone->AddPolygon( generateBoundingBox( group->GetItems() ) );
1650 destRA.m_center = destRA.m_zone->Outline()->COutline( 0 ).Centre();
1651
1652 // Use the multichannel tool to repeat the layout
1654
1655 REPEAT_LAYOUT_OPTIONS options = { .m_copyRouting = true,
1656 .m_connectedRoutingOnly = false,
1657 .m_copyPlacement = true,
1658 .m_copyOtherItems = true,
1659 .m_groupItems = false,
1660 .m_includeLockedItems = true,
1661 .m_anchorFp = nullptr };
1662
1663 int result = mct->RepeatLayout( aEvent, dbRA, destRA, options );
1664
1665 // Get rid of the temporary design blocks and rule areas
1666 tempCommit.Revert();
1667
1668 delete dbRA.m_zone;
1669 delete destRA.m_zone;
1670
1671 return result;
1672 };
1673
1674 ret = applyLayout();
1675
1676 // We're done, remove MCT_SKIP_STRUCT
1677 brd->Visit(
1678 []( EDA_ITEM* item, void* )
1679 {
1680 item->ClearFlags( MCT_SKIP_STRUCT );
1682 },
1684
1685 return ret;
1686}
1687
1689{
1690 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
1691
1692 if( !editFrame )
1693 return 1;
1694
1695 // Need to have a group selected and it needs to have a linked design block
1696 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
1698
1699 if( selection.Size() != 1 || selection[0]->Type() != PCB_GROUP_T )
1700 return 1;
1701
1702 PCB_GROUP* group = static_cast<PCB_GROUP*>( selection[0] );
1703
1704 if( !group->HasDesignBlockLink() )
1705 return 1;
1706
1707 // Get the associated design block
1708 DESIGN_BLOCK_PANE* designBlockPane = editFrame->GetDesignBlockPane();
1709 std::unique_ptr<DESIGN_BLOCK> designBlock( designBlockPane->GetDesignBlock( group->GetDesignBlockLibId(),
1710 true, true ) );
1711
1712 if( !designBlock )
1713 {
1714 wxString msg;
1715 msg.Printf( _( "Could not find design block %s." ), group->GetDesignBlockLibId().GetUniStringLibId() );
1716 m_frame->GetInfoBar()->ShowMessageFor( msg, 5000, wxICON_WARNING );
1717 return 1;
1718 }
1719
1720 if( designBlock->GetBoardFile().IsEmpty() )
1721 {
1722 wxString msg;
1723 msg.Printf( _( "Design block %s does not have a board file." ),
1724 group->GetDesignBlockLibId().GetUniStringLibId() );
1725 m_frame->GetInfoBar()->ShowMessageFor( msg, 5000, wxICON_WARNING );
1726 return 1;
1727 }
1728
1729
1731 IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::FindPlugin( pluginType ) );
1732
1733 if( !pi )
1734 return 1;
1735
1736 if( aEvent.Parameter<bool*>() != nullptr )
1737 return AppendBoard( *pi, designBlock->GetBoardFile(), designBlock.get(),
1738 static_cast<BOARD_COMMIT*>( aEvent.Commit() ), *aEvent.Parameter<bool*>() );
1739 else
1740 return AppendBoard( *pi, designBlock->GetBoardFile(), designBlock.get() );
1741}
1742
1743
1745{
1746 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
1747
1748 if( !editFrame )
1749 return 1;
1750
1751 // Need to have a group selected and it needs to have a linked design block
1752 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
1754
1755 if( selection.Size() != 1 || selection[0]->Type() != PCB_GROUP_T )
1756 return 1;
1757
1758 PCB_GROUP* group = static_cast<PCB_GROUP*>( selection[0] );
1759
1760 if( !group->HasDesignBlockLink() )
1761 return 1;
1762
1763 // Get the associated design block
1764 DESIGN_BLOCK_PANE* designBlockPane = editFrame->GetDesignBlockPane();
1765 std::unique_ptr<DESIGN_BLOCK> designBlock( designBlockPane->GetDesignBlock( group->GetDesignBlockLibId(),
1766 true, true ) );
1767
1768 if( !designBlock )
1769 {
1770 wxString msg;
1771 msg.Printf( _( "Could not find design block %s." ), group->GetDesignBlockLibId().GetUniStringLibId() );
1772 m_frame->GetInfoBar()->ShowMessageFor( msg, 5000, wxICON_WARNING );
1773 return 1;
1774 }
1775
1776 editFrame->GetDesignBlockPane()->SelectLibId( group->GetDesignBlockLibId() );
1777
1778 return m_toolMgr->RunAction( PCB_ACTIONS::updateDesignBlockFromSelection ) ? 1 : 0;
1779}
1780
1781
1782bool PCB_CONTROL::placeBoardItems( BOARD_COMMIT* aCommit, BOARD* aBoard, bool aAnchorAtOrigin,
1783 bool aReannotateDuplicates, bool aSkipMove )
1784{
1785 // items are new if the current board is not the board source
1786 bool isNew = board() != aBoard;
1787 std::vector<BOARD_ITEM*> items;
1788
1789 for( BOARD_ITEM* item : aBoard->GetItemSet() )
1790 {
1791 // Marker transfer is intentionally not part of append/paste item placement.
1792 if( item->Type() == PCB_MARKER_T )
1793 continue;
1794
1795 bool doCopy = ( item->GetFlags() & SKIP_STRUCT ) == 0;
1796
1797 item->ClearFlags( SKIP_STRUCT );
1798 item->SetFlags( isNew ? IS_NEW : 0 );
1799
1800 if( doCopy )
1801 items.push_back( item );
1802 }
1803
1804 if( isNew )
1805 aBoard->RemoveAll();
1806
1807 // Reparent before calling pruneItemLayers, as SetLayer can have a dependence on the
1808 // item's parent board being set correctly.
1809 if( isNew )
1810 {
1811 for( BOARD_ITEM* item : items )
1812 item->SetParent( board() );
1813 }
1814
1815 pruneItemLayers( items );
1816
1817 return placeBoardItems( aCommit, items, isNew, aAnchorAtOrigin, aReannotateDuplicates, aSkipMove );
1818}
1819
1820
1821bool PCB_CONTROL::placeBoardItems( BOARD_COMMIT* aCommit, std::vector<BOARD_ITEM*>& aItems, bool aIsNew,
1822 bool aAnchorAtOrigin, bool aReannotateDuplicates, bool aSkipMove )
1823{
1824 m_toolMgr->RunAction( ACTIONS::selectionClear );
1825
1826 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
1827
1828 std::vector<BOARD_ITEM*> itemsToSel;
1829 itemsToSel.reserve( aItems.size() );
1830
1831 for( BOARD_ITEM* item : aItems )
1832 {
1833 if( aIsNew )
1834 {
1835 const_cast<KIID&>( item->m_Uuid ) = KIID();
1836
1837 item->RunOnChildren(
1838 []( BOARD_ITEM* aChild )
1839 {
1840 const_cast<KIID&>( aChild->m_Uuid ) = KIID();
1841 },
1843
1844 // While BOARD_COMMIT::Push() will add any new items to the entered group,
1845 // we need to do it earlier so that the previews while moving are correct.
1846 if( PCB_GROUP* enteredGroup = selectionTool->GetEnteredGroup() )
1847 {
1848 if( item->IsGroupableType() && !item->GetParentGroup() )
1849 {
1850 aCommit->Modify( enteredGroup, nullptr, RECURSE_MODE::NO_RECURSE );
1851 enteredGroup->AddItem( item );
1852 }
1853 }
1854
1855 item->SetParent( board() );
1856 }
1857
1858 // Update item attributes if needed
1859 if( BaseType( item->Type() ) == PCB_DIMENSION_T )
1860 {
1861 static_cast<PCB_DIMENSION_BASE*>( item )->UpdateUnits();
1862 }
1863 else if( item->Type() == PCB_FOOTPRINT_T )
1864 {
1865 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
1866
1867 // Update the footprint path with the new KIID path if the footprint is new
1868 if( aIsNew )
1869 footprint->SetPath( KIID_PATH() );
1870
1871 for( BOARD_ITEM* dwg : footprint->GraphicalItems() )
1872 {
1873 if( BaseType( dwg->Type() ) == PCB_DIMENSION_T )
1874 static_cast<PCB_DIMENSION_BASE*>( dwg )->UpdateUnits();
1875 }
1876 }
1877
1878 // We only need to add the items that aren't inside a group currently selected
1879 // to the selection. If an item is inside a group and that group is selected,
1880 // then the selection tool will select it for us.
1881 if( !item->GetParentGroup() || !alg::contains( aItems, item->GetParentGroup()->AsEdaItem() ) )
1882 itemsToSel.push_back( item );
1883 }
1884
1885 // Select the items that should be selected
1886 EDA_ITEMS toSel( itemsToSel.begin(), itemsToSel.end() );
1887 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &toSel );
1888
1889 // Reannotate duplicate footprints (make sense only in board editor )
1890 if( aReannotateDuplicates && m_isBoardEditor )
1891 m_toolMgr->GetTool<BOARD_REANNOTATE_TOOL>()->ReannotateDuplicatesInSelection();
1892
1893 for( BOARD_ITEM* item : aItems )
1894 {
1895 if( aIsNew )
1896 aCommit->Add( item );
1897 else
1898 aCommit->Added( item );
1899 }
1900
1901 PCB_SELECTION& selection = selectionTool->GetSelection();
1902
1903 if( selection.Size() > 0 )
1904 {
1905 if( aAnchorAtOrigin )
1906 {
1907 selection.SetReferencePoint( VECTOR2I( 0, 0 ) );
1908 }
1909 else if( BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( selection.GetTopLeftItem() ) )
1910 {
1911 selection.SetReferencePoint( item->GetPosition() );
1912 }
1913
1914 getViewControls()->SetCursorPosition( getViewControls()->GetMousePosition(), false );
1915
1916 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1917
1918 if( !aSkipMove )
1919 return m_toolMgr->RunSynchronousAction( PCB_ACTIONS::move, aCommit );
1920 }
1921
1922 return true;
1923}
1924
1925
1926int PCB_CONTROL::AppendBoard( PCB_IO& pi, const wxString& fileName, DESIGN_BLOCK* aDesignBlock, BOARD_COMMIT* aCommit,
1927 bool aSkipMove )
1928{
1929 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
1930
1931 if( !editFrame )
1932 return 1;
1933
1934 BOARD* brd = board();
1935
1936 if( !brd )
1937 return 1;
1938
1939 // Give ourselves a commit to work with if we weren't provided one
1940 std::unique_ptr<BOARD_COMMIT> tempCommit;
1941 BOARD_COMMIT* commit = aCommit;
1942
1943 if( !commit )
1944 {
1945 tempCommit = std::make_unique<BOARD_COMMIT>( editFrame );
1946 commit = tempCommit.get();
1947 }
1948
1949 // Mark existing items, in order to know what are the new items so we can select only
1950 // the new items after loading
1951 BOARD_ITEM_SET existingItems = brd->GetItemSet();
1952
1953 for( BOARD_ITEM* item : existingItems )
1954 item->SetFlags( SKIP_STRUCT );
1955
1956 auto clearSkipStructOnExistingItems =
1957 [&existingItems]()
1958 {
1959 for( BOARD_ITEM* item : existingItems )
1960 item->ClearFlags( SKIP_STRUCT );
1961 };
1962
1963 std::map<wxString, wxString> oldProperties = brd->GetProperties();
1964 std::map<wxString, wxString> newProperties;
1965
1966 PAGE_INFO oldPageInfo = brd->GetPageSettings();
1967 TITLE_BLOCK oldTitleBlock = brd->GetTitleBlock();
1968
1969 // Keep also the count of copper layers, to adjust if necessary
1970 int initialCopperLayerCount = brd->GetCopperLayerCount();
1971 LSET initialEnabledLayers = brd->GetEnabledLayers();
1972
1973 // Load the data
1974 try
1975 {
1976 std::map<std::string, UTF8> props;
1977
1978 // PCB_IO_EAGLE can use this info to center the BOARD, but it does not yet.
1979
1980 props["page_width"] = std::to_string( editFrame->GetPageSizeIU().x );
1981 props["page_height"] = std::to_string( editFrame->GetPageSizeIU().y );
1982
1984 [&]( wxString aTitle, int aIcon, wxString aMessage, wxString aAction ) -> bool
1985 {
1986 KIDIALOG dlg( editFrame, aMessage, aTitle, wxOK | wxCANCEL | aIcon );
1987
1988 if( !aAction.IsEmpty() )
1989 dlg.SetOKLabel( aAction );
1990
1991 dlg.DoNotShowCheckbox( aMessage, 0 );
1992
1993 return dlg.ShowModal() == wxID_OK;
1994 } );
1995
1996 WX_PROGRESS_REPORTER progressReporter( editFrame, _( "Load PCB" ), 1, PR_CAN_ABORT );
1997
1998 pi.SetProgressReporter( &progressReporter );
1999 pi.LoadBoard( fileName, brd, &props, nullptr );
2000 }
2001 catch( const IO_ERROR& ioe )
2002 {
2003 DisplayErrorMessage( editFrame, _( "Error loading board." ), ioe.What() );
2004 clearSkipStructOnExistingItems();
2005
2006 return 0;
2007 }
2008
2009 newProperties = brd->GetProperties();
2010
2011 for( const std::pair<const wxString, wxString>& prop : oldProperties )
2012 newProperties[prop.first] = prop.second;
2013
2014 brd->SetProperties( newProperties );
2015
2016 brd->SetPageSettings( oldPageInfo );
2017 brd->SetTitleBlock( oldTitleBlock );
2018
2019 // rebuild nets and ratsnest before any use of nets
2020 brd->BuildListOfNets();
2021 brd->SynchronizeNetsAndNetClasses( true );
2022 brd->BuildConnectivity();
2023
2024 // New appended items need to inherit the current global ratsnest state.
2025 // Existing items are marked SKIP_STRUCT and are handled elsewhere.
2026 const bool showGlobalRatsnest = displayOptions().m_ShowGlobalRatsnest;
2027
2028 for( BOARD_ITEM* item : brd->GetItemSet() )
2029 {
2030 if( item->GetFlags() & SKIP_STRUCT )
2031 continue;
2032
2033 if( BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
2034 connectedItem->SetLocalRatsnestVisible( showGlobalRatsnest );
2035
2036 if( item->Type() == PCB_FOOTPRINT_T )
2037 {
2038 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
2039 pad->SetLocalRatsnestVisible( showGlobalRatsnest );
2040 }
2041 }
2042
2043 // Synchronize layers
2044 // we should not ask PLUGINs to do these items:
2045 int copperLayerCount = brd->GetCopperLayerCount();
2046
2047 if( copperLayerCount > initialCopperLayerCount )
2048 brd->SetCopperLayerCount( copperLayerCount );
2049
2050 // Enable all used layers, and make them visible:
2051 LSET enabledLayers = brd->GetEnabledLayers();
2052 enabledLayers |= initialEnabledLayers;
2053 brd->SetEnabledLayers( enabledLayers );
2054 brd->SetVisibleLayers( enabledLayers );
2055
2056 int ret = 0;
2057
2058 bool placeAsGroup = false;
2059
2060 if( APP_SETTINGS_BASE* cfg = editFrame->config() )
2061 placeAsGroup = cfg->m_DesignBlockChooserPanel.place_as_group;
2062
2063 if( placeBoardItems( commit, brd, false, false /* Don't reannotate dupes on Append Board */, aSkipMove ) )
2064 {
2065 if( placeAsGroup )
2066 {
2067 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
2069
2070 // Count items that would be added to the group
2071 int groupableCount = 0;
2072
2073 for( EDA_ITEM* eda_item : selection )
2074 {
2075 if( eda_item->IsBOARD_ITEM()
2076 && !static_cast<BOARD_ITEM*>( eda_item )->GetParentFootprint() )
2077 {
2078 groupableCount++;
2079 }
2080 }
2081
2082 if( groupableCount >= 2 )
2083 {
2084 PCB_GROUP* group = new PCB_GROUP( brd );
2085
2086 if( aDesignBlock )
2087 {
2088 group->SetName( aDesignBlock->GetLibId().GetLibItemName() );
2089 group->SetDesignBlockLibId( aDesignBlock->GetLibId() );
2090 }
2091 else
2092 {
2093 group->SetName( wxFileName( fileName ).GetName() );
2094 }
2095
2096 for( EDA_ITEM* eda_item : selection )
2097 {
2098 if( eda_item->IsBOARD_ITEM() )
2099 {
2100 if( static_cast<BOARD_ITEM*>( eda_item )->IsLocked() )
2101 group->SetLocked( true );
2102 }
2103 }
2104
2105 commit->Add( group );
2106
2107 for( EDA_ITEM* eda_item : selection )
2108 {
2109 if( eda_item->IsBOARD_ITEM()
2110 && !static_cast<BOARD_ITEM*>( eda_item )->GetParentFootprint() )
2111 {
2112 commit->Modify( eda_item );
2113 group->AddItem( eda_item );
2114 }
2115 }
2116
2117 selTool->ClearSelection();
2118 selTool->select( group );
2119
2121 m_frame->OnModify();
2122 m_frame->Refresh();
2123 }
2124 }
2125
2126 // If we were provided a commit, let the caller control when to push it
2127 if( !aCommit )
2128 commit->Push( aDesignBlock ? _( "Place Design Block" ) : _( "Append Board" ) );
2129
2130 editFrame->GetBoard()->BuildConnectivity();
2131 ret = 0;
2132 }
2133 else
2134 {
2135 // If we were provided a commit, let the caller control when to revert it
2136 if( !aCommit )
2137 commit->Revert();
2138
2139 ret = 1;
2140 }
2141
2142 // Refresh the UI for the updated board properties
2143 editFrame->GetAppearancePanel()->OnBoardChanged();
2144 clearSkipStructOnExistingItems();
2145
2146 return ret;
2147}
2148
2149
2150int PCB_CONTROL::Undo( const TOOL_EVENT& aEvent )
2151{
2152 PCB_BASE_EDIT_FRAME* editFrame = dynamic_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
2153 wxCommandEvent dummy;
2154
2155 if( editFrame )
2156 editFrame->RestoreCopyFromUndoList( dummy );
2157
2158 return 0;
2159}
2160
2161
2162int PCB_CONTROL::Redo( const TOOL_EVENT& aEvent )
2163{
2164 PCB_BASE_EDIT_FRAME* editFrame = dynamic_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
2165 wxCommandEvent dummy;
2166
2167 if( editFrame )
2168 editFrame->RestoreCopyFromRedoList( dummy );
2169
2170 return 0;
2171}
2172
2173
2175{
2176 MAGNETIC_SETTINGS& settings = m_isFootprintEditor ? m_frame->GetFootprintEditorSettings()->m_MagneticItems
2177 : m_frame->GetPcbNewSettings()->m_MagneticItems;
2178 bool& snapMode = settings.allLayers;
2179
2181 snapMode = false;
2182 else if( aEvent.IsAction( &PCB_ACTIONS::magneticSnapAllLayers ) )
2183 snapMode = true;
2184 else
2185 snapMode = !snapMode;
2186
2188
2189 return 0;
2190}
2191
2192
2194{
2195 if( !Pgm().GetCommonSettings()->m_Input.hotkey_feedback )
2196 return 0;
2197
2198 wxArrayString labels;
2199 labels.Add( _( "Active Layer" ) );
2200 labels.Add( _( "All Layers" ) );
2201
2202 if( !m_frame->GetHotkeyPopup() )
2203 m_frame->CreateHotkeyPopup();
2204
2205 HOTKEY_CYCLE_POPUP* popup = m_frame->GetHotkeyPopup();
2206
2207 MAGNETIC_SETTINGS& settings = m_isFootprintEditor ? m_frame->GetFootprintEditorSettings()->m_MagneticItems
2208 : m_frame->GetPcbNewSettings()->m_MagneticItems;
2209
2210 if( popup )
2211 popup->Popup( _( "Object Snapping" ), labels, static_cast<int>( settings.allLayers ) );
2212
2213 return 0;
2214}
2215
2216
2218{
2219 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
2220 ROUTER_TOOL* routerTool = m_toolMgr->GetTool<ROUTER_TOOL>();
2221 PCB_SELECTION& selection = selTool->GetSelection();
2222 PCB_EDIT_FRAME* pcbFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
2223 std::shared_ptr<DRC_ENGINE> drcEngine = m_frame->GetBoard()->GetDesignSettings().m_DRCEngine;
2224 DRC_CONSTRAINT constraint;
2225
2226 std::vector<MSG_PANEL_ITEM> msgItems;
2227
2228 if( routerTool && routerTool->RoutingInProgress() )
2229 {
2230 routerTool->UpdateMessagePanel();
2231 return 0;
2232 }
2233
2234 if( !pcbFrame && !m_frame->GetModel() )
2235 return 0;
2236
2237 if( selection.Empty() )
2238 {
2239 if( !pcbFrame )
2240 {
2241 FOOTPRINT* fp = static_cast<FOOTPRINT*>( m_frame->GetModel() );
2242 fp->GetMsgPanelInfo( m_frame, msgItems );
2243 }
2244 else
2245 {
2246 m_frame->SetMsgPanel( m_frame->GetBoard() );
2247 }
2248 }
2249 else if( selection.GetSize() == 1 )
2250 {
2251 EDA_ITEM* item = selection.Front();
2252
2253 if( std::optional<wxString> uuid = GetMsgPanelDisplayUuid( item->m_Uuid ) )
2254 msgItems.emplace_back( _( "UUID" ), *uuid );
2255
2256 item->GetMsgPanelInfo( m_frame, msgItems );
2257
2258 PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item );
2259 NETINFO_ITEM* net = track ? track->GetNet() : nullptr;
2260 NETINFO_ITEM* coupledNet = net ? m_frame->GetBoard()->DpCoupledNet( net ) : nullptr;
2261
2262 if( coupledNet )
2263 {
2264 SEG trackSeg( track->GetStart(), track->GetEnd() );
2265 PCB_TRACK* coupledItem = nullptr;
2266 SEG::ecoord closestDist_sq = VECTOR2I::ECOORD_MAX;
2267
2268 for( PCB_TRACK* candidate : m_frame->GetBoard()->Tracks() )
2269 {
2270 if( candidate->GetNet() != coupledNet )
2271 continue;
2272
2273 SEG::ecoord dist_sq = trackSeg.SquaredDistance( SEG( candidate->GetStart(), candidate->GetEnd() ) );
2274
2275 if( !coupledItem || dist_sq < closestDist_sq )
2276 {
2277 coupledItem = candidate;
2278 closestDist_sq = dist_sq;
2279 }
2280 }
2281
2282 constraint = drcEngine->EvalRules( DIFF_PAIR_GAP_CONSTRAINT, track, coupledItem, track->GetLayer() );
2283
2284 wxString msg = m_frame->MessageTextFromMinOptMax( constraint.Value() );
2285
2286 if( !msg.IsEmpty() )
2287 {
2288 msgItems.emplace_back( wxString::Format( _( "DP Gap Constraints: %s" ), msg ),
2289 wxString::Format( _( "(from %s)" ), constraint.GetName() ) );
2290 }
2291
2292 constraint = drcEngine->EvalRules( MAX_UNCOUPLED_CONSTRAINT, track, coupledItem, track->GetLayer() );
2293
2294 if( constraint.Value().HasMax() )
2295 {
2296 msg = m_frame->MessageTextFromValue( constraint.Value().Max() );
2297 msgItems.emplace_back( wxString::Format( _( "DP Max Uncoupled-length: %s" ), msg ),
2298 wxString::Format( _( "(from %s)" ), constraint.GetName() ) );
2299 }
2300 }
2301 }
2302 else if( pcbFrame && selection.GetSize() == 2 )
2303 {
2304 // Pair selection broken into multiple, optional data, starting with the selected item
2305 // names
2306
2307 BOARD_ITEM* a = dynamic_cast<BOARD_ITEM*>( selection[0] );
2308 BOARD_ITEM* b = dynamic_cast<BOARD_ITEM*>( selection[1] );
2309
2310 if( a && b )
2311 {
2312 msgItems.emplace_back( MSG_PANEL_ITEM( a->GetItemDescription( m_frame, false ),
2313 b->GetItemDescription( m_frame, false ) ) );
2314 }
2315
2316 BOARD_CONNECTED_ITEM* a_conn = dynamic_cast<BOARD_CONNECTED_ITEM*>( a );
2317 BOARD_CONNECTED_ITEM* b_conn = dynamic_cast<BOARD_CONNECTED_ITEM*>( b );
2318
2319 if( a_conn && b_conn )
2320 {
2321 LSET overlap = a_conn->GetLayerSet() & b_conn->GetLayerSet() & LSET::AllCuMask();
2322 int a_netcode = a_conn->GetNetCode();
2323 int b_netcode = b_conn->GetNetCode();
2324
2325 if( overlap.count() > 0 )
2326 {
2327 PCB_LAYER_ID layer = overlap.CuStack().front();
2328
2329 if( a_netcode != b_netcode || a_netcode < 0 || b_netcode < 0 )
2330 {
2331 constraint = drcEngine->EvalRules( CLEARANCE_CONSTRAINT, a, b, layer );
2332 msgItems.emplace_back( _( "Resolved Clearance" ),
2333 m_frame->MessageTextFromValue( constraint.m_Value.Min() ) );
2334 }
2335
2336 std::shared_ptr<SHAPE> a_shape( a_conn->GetEffectiveShape( layer ) );
2337 std::shared_ptr<SHAPE> b_shape( b_conn->GetEffectiveShape( layer ) );
2338
2339 int actual_clearance = a_shape->GetClearance( b_shape.get() );
2340
2341 if( actual_clearance > -1 && actual_clearance < std::numeric_limits<int>::max() )
2342 {
2343 msgItems.emplace_back( _( "Actual Clearance" ),
2344 m_frame->MessageTextFromValue( actual_clearance ) );
2345 }
2346 }
2347 }
2348
2349 if( a && b && ( a->HasHole() || b->HasHole() ) )
2350 {
2351 PCB_LAYER_ID active = m_frame->GetActiveLayer();
2353
2354 if( b->IsOnLayer( active ) && IsCopperLayer( active ) )
2355 layer = active;
2356 else if( b->HasHole() && a->IsOnLayer( active ) && IsCopperLayer( active ) )
2357 layer = active;
2358 else if( a->HasHole() && b->IsOnCopperLayer() )
2359 layer = b->GetLayer();
2360 else if( b->HasHole() && a->IsOnCopperLayer() )
2361 layer = a->GetLayer();
2362
2363 if( IsCopperLayer( layer ) )
2364 {
2365 int actual = std::numeric_limits<int>::max();
2366
2367 if( a->HasHole() && b->IsOnCopperLayer() )
2368 {
2369 std::shared_ptr<SHAPE_SEGMENT> hole = a->GetEffectiveHoleShape();
2370 std::shared_ptr<SHAPE> other( b->GetEffectiveShape( layer ) );
2371
2372 actual = std::min( actual, hole->GetClearance( other.get() ) );
2373 }
2374
2375 if( b->HasHole() && a->IsOnCopperLayer() )
2376 {
2377 std::shared_ptr<SHAPE_SEGMENT> hole = b->GetEffectiveHoleShape();
2378 std::shared_ptr<SHAPE> other( a->GetEffectiveShape( layer ) );
2379
2380 actual = std::min( actual, hole->GetClearance( other.get() ) );
2381 }
2382
2383 if( actual < std::numeric_limits<int>::max() )
2384 {
2385 constraint = drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, a, b, layer );
2386 msgItems.emplace_back( _( "Resolved Hole Clearance" ),
2387 m_frame->MessageTextFromValue( constraint.m_Value.Min() ) );
2388
2389 if( actual > -1 && actual < std::numeric_limits<int>::max() )
2390 {
2391 msgItems.emplace_back( _( "Actual Hole Clearance" ),
2392 m_frame->MessageTextFromValue( actual ) );
2393 }
2394 }
2395 }
2396 }
2397
2398 if( a && b )
2399 {
2400 for( PCB_LAYER_ID edgeLayer : { Edge_Cuts, Margin } )
2401 {
2402 PCB_LAYER_ID active = m_frame->GetActiveLayer();
2404
2405 if( a->IsOnLayer( edgeLayer ) && b->Type() != PCB_FOOTPRINT_T )
2406 {
2407 if( b->IsOnLayer( active ) && IsCopperLayer( active ) )
2408 layer = active;
2409 else if( IsCopperLayer( b->GetLayer() ) )
2410 layer = b->GetLayer();
2411 }
2412 else if( b->IsOnLayer( edgeLayer ) && a->Type() != PCB_FOOTPRINT_T )
2413 {
2414 if( a->IsOnLayer( active ) && IsCopperLayer( active ) )
2415 layer = active;
2416 else if( IsCopperLayer( a->GetLayer() ) )
2417 layer = a->GetLayer();
2418 }
2419
2420 if( layer >= 0 )
2421 {
2422 constraint = drcEngine->EvalRules( EDGE_CLEARANCE_CONSTRAINT, a, b, layer );
2423
2424 if( edgeLayer == Edge_Cuts )
2425 {
2426 msgItems.emplace_back( _( "Resolved Edge Clearance" ),
2427 m_frame->MessageTextFromValue( constraint.m_Value.Min() ) );
2428 }
2429 else
2430 {
2431 msgItems.emplace_back( _( "Resolved Margin Clearance" ),
2432 m_frame->MessageTextFromValue( constraint.m_Value.Min() ) );
2433 }
2434 }
2435 }
2436 }
2437 }
2438
2439 if( selection.GetSize() )
2440 {
2441 if( msgItems.empty() )
2442 {
2443 // Count items by type
2444 std::map<KICAD_T, int> typeCounts;
2445
2446 for( EDA_ITEM* item : selection )
2447 typeCounts[item->Type()]++;
2448
2449 // Check if all items are the same type
2450 bool allSameType = ( typeCounts.size() == 1 );
2451 KICAD_T commonType = allSameType ? typeCounts.begin()->first : NOT_USED;
2452
2453 if( allSameType )
2454 {
2455 // Show "Type: N" for homogeneous selections
2456 wxString typeName = selection.Front()->GetFriendlyName();
2457 msgItems.emplace_back( typeName,
2458 wxString::Format( wxT( "%d" ), selection.GetSize() ) );
2459
2460 // For pads, show common properties
2461 if( commonType == PCB_PAD_T )
2462 {
2463 std::set<wxString> layers;
2464 std::set<PAD_SHAPE> shapes;
2465 std::set<VECTOR2I> sizes;
2466
2467 for( EDA_ITEM* item : selection )
2468 {
2469 PAD* pad = static_cast<PAD*>( item );
2470 layers.insert( pad->LayerMaskDescribe() );
2471 shapes.insert( pad->GetShape( PADSTACK::ALL_LAYERS ) );
2472 sizes.insert( pad->GetSize( PADSTACK::ALL_LAYERS ) );
2473 }
2474
2475 if( layers.size() == 1 )
2476 msgItems.emplace_back( _( "Layer" ), *layers.begin() );
2477
2478 if( shapes.size() == 1 )
2479 {
2480 PAD* firstPad = static_cast<PAD*>( selection.Front() );
2481 msgItems.emplace_back( _( "Pad Shape" ),
2482 firstPad->ShowPadShape( PADSTACK::ALL_LAYERS ) );
2483 }
2484
2485 if( sizes.size() == 1 )
2486 {
2487 VECTOR2I size = *sizes.begin();
2488 msgItems.emplace_back( _( "Pad Size" ),
2489 wxString::Format( wxT( "%s x %s" ),
2490 m_frame->MessageTextFromValue( size.x ),
2491 m_frame->MessageTextFromValue( size.y ) ) );
2492 }
2493 }
2494 }
2495 else
2496 {
2497 // Show type breakdown for mixed selections
2498 wxString breakdown;
2499
2500 for( const auto& [type, count] : typeCounts )
2501 {
2502 if( !breakdown.IsEmpty() )
2503 breakdown += wxT( ", " );
2504
2505 // Get friendly name from first item of this type
2506 wxString typeName;
2507
2508 for( EDA_ITEM* item : selection )
2509 {
2510 if( item->Type() == type )
2511 {
2512 typeName = item->GetFriendlyName();
2513 break;
2514 }
2515 }
2516
2517 breakdown += wxString::Format( wxT( "%s: %d" ), typeName, count );
2518 }
2519
2520 msgItems.emplace_back( _( "Selected Items" ),
2521 wxString::Format( wxT( "%d (%s)" ),
2522 selection.GetSize(), breakdown ) );
2523 }
2524
2525 if( m_isBoardEditor )
2526 {
2527 std::set<wxString> netNames;
2528 std::set<wxString> netClasses;
2529
2530 for( EDA_ITEM* item : selection )
2531 {
2532 if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
2533 {
2534 if( !bci->GetNet() || bci->GetNetCode() <= NETINFO_LIST::UNCONNECTED )
2535 continue;
2536
2537 netNames.insert( UnescapeString( bci->GetNetname() ) );
2538 netClasses.insert( UnescapeString( bci->GetEffectiveNetClass()->GetHumanReadableName() ) );
2539
2540 if( netNames.size() > 1 && netClasses.size() > 1 )
2541 break;
2542 }
2543 }
2544
2545 if( netNames.size() == 1 )
2546 msgItems.emplace_back( _( "Net" ), *netNames.begin() );
2547
2548 if( netClasses.size() == 1 )
2549 msgItems.emplace_back( _( "Resolved Netclass" ), *netClasses.begin() );
2550 }
2551 }
2552
2553 if( selection.GetSize() >= 2 )
2554 {
2555 bool lengthValid = true;
2556 double selectedLength = 0;
2557
2558 // Lambda to accumulate track length if item is a track or arc, otherwise mark invalid
2559 std::function<void( EDA_ITEM* )> accumulateTrackLength;
2560
2561 accumulateTrackLength =
2562 [&]( EDA_ITEM* aItem )
2563 {
2564 if( aItem->Type() == PCB_TRACE_T || aItem->Type() == PCB_ARC_T )
2565 {
2566 selectedLength += static_cast<PCB_TRACK*>( aItem )->GetLength();
2567 }
2568 else if( aItem->Type() == PCB_VIA_T )
2569 {
2570 // zero 2D length
2571 }
2572 else if( aItem->Type() == PCB_SHAPE_T )
2573 {
2574 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
2575
2576 if( shape->GetShape() == SHAPE_T::SEGMENT
2577 || shape->GetShape() == SHAPE_T::ARC
2578 || shape->GetShape() == SHAPE_T::BEZIER )
2579 {
2580 selectedLength += shape->GetLength();
2581 }
2582 else
2583 {
2584 lengthValid = false;
2585 }
2586 }
2587 // Use dynamic_cast to include PCB_GENERATORs.
2588 else if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( aItem ) )
2589 {
2590 group->RunOnChildren( accumulateTrackLength, RECURSE_MODE::RECURSE );
2591 }
2592 else
2593 {
2594 lengthValid = false;
2595 }
2596 };
2597
2598 for( EDA_ITEM* item : selection )
2599 {
2600 if( lengthValid )
2601 accumulateTrackLength( item );
2602 }
2603
2604 if( lengthValid )
2605 {
2606 msgItems.emplace_back( _( "Selected 2D Length" ),
2607 m_frame->MessageTextFromValue( selectedLength ) );
2608 }
2609 }
2610
2611 if( selection.GetSize() >= 2 && selection.GetSize() < 100 )
2612 {
2613 LSET enabledLayers = m_frame->GetBoard()->GetEnabledLayers();
2614 LSET enabledCopper = LSET::AllCuMask( m_frame->GetBoard()->GetCopperLayerCount() );
2615 bool areaValid = true;
2616 bool hasCopper = false;
2617 bool hasNonCopper = false;
2618
2619 std::map<PCB_LAYER_ID, SHAPE_POLY_SET> layerPolys;
2620 SHAPE_POLY_SET holes;
2621
2622 std::function<void( EDA_ITEM* )> accumulateArea;
2623
2624 accumulateArea =
2625 [&]( EDA_ITEM* aItem )
2626 {
2627 if( aItem->Type() == PCB_FOOTPRINT_T || aItem->Type() == PCB_MARKER_T )
2628 {
2629 areaValid = false;
2630 return;
2631 }
2632
2633 if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( aItem ) )
2634 {
2635 group->RunOnChildren( accumulateArea, RECURSE_MODE::RECURSE );
2636 return;
2637 }
2638
2639 if( BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( aItem ) )
2640 {
2641 boardItem->RunOnChildren( accumulateArea, RECURSE_MODE::NO_RECURSE );
2642
2643 LSET itemLayers = boardItem->GetLayerSet() & enabledLayers;
2644
2645 for( PCB_LAYER_ID layer : itemLayers )
2646 {
2647 boardItem->TransformShapeToPolySet( layerPolys[layer], layer, 0,
2649
2650 if( enabledCopper.Contains( layer ) )
2651 hasCopper = true;
2652 else
2653 hasNonCopper = true;
2654 }
2655
2656 if( aItem->Type() == PCB_PAD_T && static_cast<PAD*>( aItem )->HasHole() )
2657 {
2658 static_cast<PAD*>( aItem )->TransformHoleToPolygon( holes, 0, ARC_LOW_DEF,
2659 ERROR_OUTSIDE );
2660 }
2661 else if( aItem->Type() == PCB_VIA_T )
2662 {
2663 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
2664 VECTOR2I center = via->GetPosition();
2665 int R = via->GetDrillValue() / 2;
2666
2668 }
2669 }
2670 };
2671
2672 for( EDA_ITEM* item : selection )
2673 {
2674 if( areaValid )
2675 accumulateArea( item );
2676 }
2677
2678 if( areaValid )
2679 {
2680 double area = 0.0;
2681
2682 for( auto& [layer, layerPoly] : layerPolys )
2683 {
2684 // Only subtract holes from copper layers
2685 if( enabledCopper.Contains( layer ) )
2686 layerPoly.BooleanSubtract( holes );
2687
2688 area += layerPoly.Area();
2689 }
2690
2691 // Choose appropriate label based on what layers are involved
2692 wxString areaLabel;
2693
2694 if( hasCopper && !hasNonCopper )
2695 areaLabel = _( "Selected 2D Copper Area" );
2696 else if( !hasCopper && hasNonCopper )
2697 areaLabel = _( "Selected 2D Area" );
2698 else
2699 areaLabel = _( "Selected 2D Total Area" );
2700
2701 msgItems.emplace_back( areaLabel,
2702 m_frame->MessageTextFromValue( area, true, EDA_DATA_TYPE::AREA ) );
2703 }
2704 }
2705 }
2706 else
2707 {
2708 m_frame->GetBoard()->GetMsgPanelInfo( m_frame, msgItems );
2709 }
2710
2711 m_frame->SetMsgPanel( msgItems );
2712
2713 // Update vertex editor if it exists
2714 PCB_BASE_EDIT_FRAME* editFrame = dynamic_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
2715 if( editFrame )
2716 {
2717 BOARD_ITEM* selectedItem = ( selection.GetSize() == 1 ) ? dynamic_cast<BOARD_ITEM*>( selection.Front() )
2718 : nullptr;
2719 editFrame->UpdateVertexEditorSelection( selectedItem );
2720 }
2721
2722 return 0;
2723}
2724
2725
2727{
2728 wxFileName fileName = wxFileName( *aEvent.Parameter<wxString*>() );
2729
2730 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
2731
2732 if( !editFrame )
2733 return 1;
2734
2735 wxString filePath = fileName.GetFullPath();
2737 IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::FindPlugin( pluginType ) );
2738
2739 if( !pi )
2740 return 1;
2741
2742 return AppendBoard( *pi, filePath );
2743}
2744
2745
2747{
2748 BOARD_COMMIT commit( this );
2749 EDA_UNITS displayUnit = m_frame->GetUserUnits();
2750 PCB_TABLE* table = Build_Board_Characteristics_Table( m_frame->GetBoard(), displayUnit );
2751 table->SetLayer( m_frame->GetActiveLayer() );
2752
2753 std::vector<BOARD_ITEM*> items;
2754 items.push_back( table );
2755
2756 if( placeBoardItems( &commit, items, true, true, false, false ) )
2757 commit.Push( _( "Place Board Characteristics" ) );
2758 else
2759 delete table;
2760
2761 return 0;
2762}
2763
2764
2766{
2767 BOARD_COMMIT commit( this );
2768 EDA_UNITS displayUnit = m_frame->GetUserUnits();
2769
2770 PCB_TABLE* table = Build_Board_Stackup_Table( m_frame->GetBoard(), displayUnit );
2771 table->SetLayer( m_frame->GetActiveLayer() );
2772
2773 std::vector<BOARD_ITEM*> items;
2774 items.push_back( table );
2775
2776 if( placeBoardItems( &commit, items, true, true, false, false ) )
2777 commit.Push( _( "Place Board Stackup Table" ) );
2778 else
2779 delete table;
2780
2781 return 0;
2782}
2783
2784
2786{
2787 view()->SetMirror( !view()->IsMirroredX(), false );
2788 view()->RecacheAllItems();
2789 m_frame->GetCanvas()->ForceRefresh();
2790 m_frame->OnDisplayOptionsChanged();
2791 return 0;
2792}
2793
2794
2796{
2797 if( aItem->Type() == PCB_SHAPE_T )
2798 {
2799 static_cast<PCB_SHAPE*>( aItem )->UpdateHatching();
2800
2801 if( view() )
2802 view()->Update( aItem );
2803 }
2804}
2805
2806
2808{
2809 for( FOOTPRINT* footprint : board()->Footprints() )
2810 footprint->RunOnChildren( std::bind( &PCB_CONTROL::rehatchBoardItem, this, _1 ), NO_RECURSE );
2811
2812 for( BOARD_ITEM* item : board()->Drawings() )
2813 rehatchBoardItem( item );
2814
2815 return 0;
2816}
2817
2818
2820{
2821 BOARD* brd = board();
2822
2823 if( !brd )
2824 return 0;
2825
2826 PROJECT& prj = m_frame->Prj();
2828 FILENAME_RESOLVER* resolver = cache ? cache->GetResolver() : nullptr;
2829
2830 wxString workingPath = prj.GetProjectPath();
2831 std::vector<const EMBEDDED_FILES*> stack;
2832 stack.push_back( brd->GetEmbeddedFiles() );
2833
2834 BOARD_COMMIT commit( m_frame );
2835 int embeddedCount = 0;
2836
2837 for( FOOTPRINT* fp : brd->Footprints() )
2838 {
2839 bool fpModified = false;
2840
2841 for( FP_3DMODEL& model : fp->Models() )
2842 {
2843 if( model.m_Filename.StartsWith( FILEEXT::KiCadUriPrefix ) )
2844 continue;
2845
2846 wxString fullPath =
2847 resolver ? resolver->ResolvePath( model.m_Filename, workingPath, stack )
2848 : model.m_Filename;
2849
2850 wxFileName fname( fullPath );
2851 wxString ext = fname.GetExt().Upper();
2852
2853 if( fname.Exists() )
2854 {
2856 brd->GetEmbeddedFiles()->AddFile( fname, false ) )
2857 {
2858 model.m_Filename = file->GetLink();
2859 fpModified = true;
2860 embeddedCount++;
2861
2862 // Store STEP along with WRL for the OCCT(STEP) exporter.
2863 if( ext == "WRL" || ext == "WRZ" )
2864 {
2865 wxArrayString alts;
2866
2867 // Step files
2868 alts.Add( wxT( "stp" ) );
2869 alts.Add( wxT( "step" ) );
2870 alts.Add( wxT( "STP" ) );
2871 alts.Add( wxT( "STEP" ) );
2872 alts.Add( wxT( "Stp" ) );
2873 alts.Add( wxT( "Step" ) );
2874 alts.Add( wxT( "stpz" ) );
2875 alts.Add( wxT( "stpZ" ) );
2876 alts.Add( wxT( "STPZ" ) );
2877
2878 for( const auto& alt : alts )
2879 {
2880 wxFileName altFile( fname.GetPath(),
2881 fname.GetName() + wxT( "." ) + alt );
2882
2883 if( altFile.IsOk() && altFile.FileExists() )
2884 {
2885 brd->GetEmbeddedFiles()->AddFile( altFile, false );
2886 break;
2887 }
2888 }
2889 }
2890 }
2891 }
2892 }
2893
2894 if( fpModified )
2895 commit.Modify( fp );
2896 }
2897
2898 if( embeddedCount > 0 )
2899 {
2900 commit.Push( _( "Embed 3D Models" ) );
2901 wxString msg = wxString::Format( _( "%d 3D model(s) successfully embedded." ), embeddedCount );
2902 m_frame->GetInfoBar()->ShowMessageFor( msg, 5000 );
2903 }
2904
2905 return 0;
2906}
2907
2908
2909// clang-format off
2911{
2914 Go( &PCB_CONTROL::Print, ACTIONS::print.MakeEvent() );
2915
2916 // Footprint library actions
2921
2922 // Display modes
2939
2940 // Layer control
2978
2981
2982 // Grid control
2985
2986 Go( &PCB_CONTROL::Undo, ACTIONS::undo.MakeEvent() );
2987 Go( &PCB_CONTROL::Redo, ACTIONS::redo.MakeEvent() );
2988
2989 // Snapping control
2994
2995 // Miscellaneous
2998
2999 // Append control
3008
3009 Go( &PCB_CONTROL::Paste, ACTIONS::paste.MakeEvent() );
3011
3018
3019 // Add library by dropping file
3022}
3023// clang-format on
@ ERROR_OUTSIDE
@ ERROR_INSIDE
constexpr int ARC_LOW_DEF
Definition base_units.h:128
std::set< BOARD_ITEM *, CompareByUuid > BOARD_ITEM_SET
Set of BOARD_ITEMs ordered by UUID.
Definition board.h:306
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:84
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:237
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition board_item.h:319
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
FOOTPRINT * GetParentFootprint() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:257
virtual bool IsOnCopperLayer() const
Definition board_item.h:156
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
virtual bool HasHole() const
Definition board_item.h:161
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:2220
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition board.cpp:3086
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:999
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:3190
void BuildListOfNets()
Definition board.h:959
const std::vector< BOARD_CONNECTED_ITEM * > AllConnectedItems()
Definition board.cpp:3155
const PAGE_INFO & GetPageSettings() const
Definition board.h:799
void SetProperties(const std::map< wxString, wxString > &aProps)
Definition board.h:401
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:190
void SynchronizeNetsAndNetClasses(bool aResetTrackAndViaSizes)
Copy NETCLASS info to each NET, based on NET membership in a NETCLASS.
Definition board.cpp:2677
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:919
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:1483
const BOARD_ITEM_SET GetItemSet()
Definition board.cpp:3557
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition board.h:800
void SetCopperLayerCount(int aCount)
Definition board.cpp:925
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:973
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:967
void SetEnabledLayers(const LSET &aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition board.cpp:987
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:195
MINOPTMAX< int > & Value()
Definition drc_rule.h:188
MINOPTMAX< int > m_Value
Definition drc_rule.h:229
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, INFOBAR_MESSAGE_TYPE aType=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:99
virtual void SetPosition(const VECTOR2I &aPos)
Definition eda_item.h:279
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:120
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:148
virtual wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const
Return a user-visible description string of this item.
Definition eda_item.cpp:154
const KIID m_Uuid
Definition eda_item.h:522
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:117
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:111
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:150
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:226
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:152
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:93
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:67
void SetMirror(bool aMirrorX, bool aMirrorY)
Control the mirroring of the VIEW.
Definition view.cpp:562
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition view.cpp:299
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition view.cpp:342
GAL * GetGAL() const
Return the GAL this view is using to draw graphical primitives.
Definition view.h:203
void RecacheAllItems()
Rebuild GAL display lists.
Definition view.cpp:1466
void MarkDirty()
Force redraw of view on the next rendering.
Definition view.h:660
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:2082
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:541
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:177
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:77
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:73
void SetDoNotAllowPads(bool aEnable)
Definition zone.h:738
void AddPolygon(std::vector< VECTOR2I > &aPolygon)
Add a polygon to the zone outline.
Definition zone.cpp:1166
void SetPlacementAreaSource(const wxString &aSource)
Definition zone.h:725
void SetPlacementAreaSourceType(PLACEMENT_SOURCE_T aType)
Definition zone.h:727
SHAPE_POLY_SET * Outline()
Definition zone.h:340
void SetHatchStyle(ZONE_BORDER_DISPLAY_STYLE aStyle)
Definition zone.h:595
void SetIsRuleArea(bool aEnable)
Definition zone.h:720
void SetDoNotAllowTracks(bool aEnable)
Definition zone.h:737
void SetLayerSet(const LSET &aLayerSet) override
Definition zone.cpp:556
void SetDoNotAllowVias(bool aEnable)
Definition zone.h:736
void SetDoNotAllowFootprints(bool aEnable)
Definition zone.h:739
void SetDoNotAllowZoneFills(bool aEnable)
Definition zone.h:735
void SetZoneName(const wxString &aName)
Definition zone.h:164
void SetPlacementAreaEnabled(bool aEnabled)
Definition zone.h:722
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:52
@ NO_RECURSE
Definition eda_item.h:53
#define IS_NEW
New item, just created.
#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:721
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)
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