KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcbnew.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) 2016 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
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 <pgm_base.h>
27#include <eda_pattern_match.h>
30#include <confirm.h>
31#include <kiface_base.h>
32#include <kiface_ids.h>
33#include <kiway_holder.h>
34#include <pcb_edit_frame.h>
35#include <eda_dde.h>
36#include <macros.h>
37#include <wx/snglinst.h>
38#include <gestfich.h>
39#include <paths.h>
40#include <pcbnew_settings.h>
41#include <footprint.h>
51#include <footprint_info_impl.h>
52#include <footprint.h>
53#include <nlohmann/json.hpp>
57#include <panel_edit_options.h>
68#include <project_pcb.h>
69#include <string_utils.h>
70#include <thread_pool.h>
71#include <trace_helpers.h>
72#include <widgets/kistatusbar.h>
73
74#include <wx/tokenzr.h>
75
76#include "invoke_pcb_dialog.h"
78#include "pcbnew_jobs_handler.h"
79
83#include <toolbars_pcb_editor.h>
84
85#include <wx/crt.h>
86
87#if defined( KICAD_IPC_API )
88#include <api/api_handler_pcb.h>
89#include <api/api_server.h>
90#include <api/api_utils.h>
92#include <board.h>
93#include <board_loader.h>
94#endif
95
96
109static wxString filterFootprints( const wxString& aFilterJson )
110{
111 using json = nlohmann::json;
112
113 try
114 {
115 json input = json::parse( aFilterJson.ToStdString() );
116
117 int pinCount = input.value( "pin_count", 0 );
118 bool zeroFilters = input.value( "zero_filters", true );
119 int maxResults = input.value( "max_results", 400 );
120
121 std::vector<std::unique_ptr<EDA_PATTERN_MATCH>> filterMatchers;
122
123 if( input.contains( "filters" ) && input["filters"].is_array() )
124 {
125 for( const auto& f : input["filters"] )
126 {
127 if( f.is_string() )
128 {
129 wxString pattern = wxString::FromUTF8( f.get<std::string>() );
130 auto matcher = std::make_unique<EDA_PATTERN_MATCH_WILDCARD_ANCHORED>();
131 matcher->SetPattern( pattern.Lower() );
132 filterMatchers.push_back( std::move( matcher ) );
133 }
134 }
135 }
136
137 bool hasFilters = ( pinCount > 0 || !filterMatchers.empty() );
138
139 if( zeroFilters && !hasFilters )
140 return wxS( "[]" );
141
142 PROJECT* project = nullptr;
143
144 if( wxTheApp )
145 {
146 wxWindow* focus = wxWindow::FindFocus();
147 wxWindow* top = focus ? wxGetTopLevelParent( focus ) : wxTheApp->GetTopWindow();
148
149 if( top )
150 {
151 if( KIWAY_HOLDER* holder = dynamic_cast<KIWAY_HOLDER*>( top ) )
152 project = &holder->Prj();
153 }
154 }
155
156 if( !project )
158
160
161 if( !adapter )
162 return wxS( "[]" );
163
164 adapter->AsyncLoad();
165 adapter->BlockUntilLoaded();
166
167 // Iterate through preloaded footprints directly instead of re-reading from disk
168 json output = json::array();
169 int count = 0;
170
171 for( const wxString& nickname : adapter->GetLibraryNames() )
172 {
173 std::vector<FOOTPRINT*> footprints = adapter->GetFootprints( nickname, true );
174
175 for( FOOTPRINT* fp : footprints )
176 {
177 if( !fp )
178 continue;
179
180 // Pin count filter
181 if( pinCount > 0 )
182 {
183 int fpPadCount = fp->GetUniquePadCount( DO_NOT_INCLUDE_NPTH );
184
185 if( fpPadCount != pinCount )
186 continue;
187 }
188
189 // Footprint filter patterns with case-insensitive matching
190 if( !filterMatchers.empty() )
191 {
192 bool matches = false;
193
194 for( const auto& matcher : filterMatchers )
195 {
196 wxString name;
197
198 // If filter contains ':', include library nickname in match string
199 if( matcher->GetPattern().Contains( wxS( ":" ) ) )
200 name = fp->GetFPID().GetLibNickname().wx_str().Lower() + wxS( ":" );
201
202 name += fp->GetFPID().GetLibItemName().wx_str().Lower();
203
204 if( matcher->Find( name ) )
205 {
206 matches = true;
207 break;
208 }
209 }
210
211 if( !matches )
212 continue;
213 }
214
215 wxString libId = fp->GetFPID().Format();
216 output.push_back( libId.ToStdString() );
217
218 if( ++count >= maxResults )
219 break;
220 }
221
222 if( count >= maxResults )
223 break;
224 }
225
226 return wxString::FromUTF8( output.dump() );
227 }
228 catch( const std::exception& e )
229 {
230 return wxS( "[]" );
231 }
232}
233
234
235namespace PCB {
236
237static struct IFACE : public KIFACE_BASE, public UNITS_PROVIDER
238{
239 // Of course all are virtual overloads, implementations of the KIFACE.
240
241 IFACE( const char* aName, KIWAY::FACE_T aType ) :
242 KIFACE_BASE( aName, aType ),
244 {}
245
246 bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway ) override;
247
248 void Reset() override;
249
250 void OnKifaceEnd() override;
251
252 wxWindow* CreateKiWindow( wxWindow* aParent, int aClassId, KIWAY* aKiway, int aCtlBits = 0 ) override
253 {
254 switch( aClassId )
255 {
256 case FRAME_PCB_EDITOR:
257 {
258 auto frame = new PCB_EDIT_FRAME( aKiway, aParent );
259
260 if( Kiface().IsSingle() )
261 {
262 // only run this under single_top, not under a project manager.
263 frame->CreateServer( KICAD_PCB_PORT_SERVICE_NUMBER );
264 }
265
266 return frame;
267 }
268
270 return new FOOTPRINT_EDIT_FRAME( aKiway, aParent );
271
273 return new FOOTPRINT_VIEWER_FRAME( aKiway, aParent );
276 return new FOOTPRINT_CHOOSER_FRAME( aKiway, aParent );
277
279 return new FOOTPRINT_WIZARD_FRAME( aKiway, aParent, FRAME_T( aClassId ) );
280
282 return FOOTPRINT_PREVIEW_PANEL::New( aKiway, aParent, this );
283
285 {
286 DIALOG_CONFIGURE_PATHS dlg( aParent );
287
288 // The dialog's constructor probably failed to set its Kiway because the
289 // dynamic_cast fails when aParent was allocated by a separate compilation
290 // module. So set it directly.
291 dlg.SetKiway( &dlg, aKiway );
292
293 // Use QuasiModal so that HTML help window will work
294 if( dlg.ShowQuasiModal() == wxID_OK )
296
297 // Dialog has completed; nothing to return.
298 return nullptr;
299 }
300
302 InvokePcbLibTableEditor( aKiway, aParent );
303 // Dialog has completed; nothing to return.
304 return nullptr;
305
307 return new PANEL_DISPLAY_OPTIONS( aParent, GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" ) );
308
309 case PANEL_FP_GRIDS:
310 {
311 FOOTPRINT_EDITOR_SETTINGS* cfg = GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" );
312 EDA_BASE_FRAME* frame = aKiway->Player( FRAME_FOOTPRINT_EDITOR, false );
313
314 if( !frame )
315 frame = aKiway->Player( FRAME_FOOTPRINT_VIEWER, false );
316
317 if( !frame )
318 frame = aKiway->Player( FRAME_PCB_EDITOR, false );
320 if( frame )
321 SetUserUnits( frame->GetUserUnits() );
322
323 return new PANEL_GRID_SETTINGS( aParent, this, frame, cfg, FRAME_FOOTPRINT_EDITOR );
324 }
329
331 {
332 EDA_BASE_FRAME* frame = aKiway->Player( FRAME_FOOTPRINT_EDITOR, false );
333
334 if( !frame )
335 frame = aKiway->Player( FRAME_FOOTPRINT_VIEWER, false );
336
337 if( !frame )
338 frame = aKiway->Player( FRAME_PCB_EDITOR, false );
340 if( frame )
341 SetUserUnits( frame->GetUserUnits() );
343 return new PANEL_EDIT_OPTIONS( aParent, this, frame, true );
347 {
348 EDA_BASE_FRAME* frame = aKiway->Player( FRAME_FOOTPRINT_EDITOR, false );
349
350 if( !frame )
351 frame = aKiway->Player( FRAME_FOOTPRINT_VIEWER, false );
352
353 if( !frame )
354 frame = aKiway->Player( FRAME_PCB_EDITOR, false );
355
356 if( frame )
357 SetUserUnits( frame->GetUserUnits() );
358
359 return new PANEL_FP_EDITOR_FIELD_DEFAULTS( aParent );
360 }
361
363 {
364 EDA_BASE_FRAME* frame = aKiway->Player( FRAME_FOOTPRINT_EDITOR, false );
365
366 if( !frame )
367 frame = aKiway->Player( FRAME_FOOTPRINT_VIEWER, false );
368
369 if( !frame )
370 frame = aKiway->Player( FRAME_PCB_EDITOR, false );
371
372 if( frame )
373 SetUserUnits( frame->GetUserUnits() );
374
375 return new PANEL_FP_EDITOR_GRAPHICS_DEFAULTS( aParent, this );
376 }
377
379 return new class PANEL_FP_USER_LAYER_NAMES( aParent );
380
382 {
383 APP_SETTINGS_BASE* cfg = GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" );
384 TOOLBAR_SETTINGS* tb = GetToolbarSettings<FOOTPRINT_EDIT_TOOLBAR_SETTINGS>( "fpedit-toolbars" );
385
386 std::vector<TOOL_ACTION*> actions;
387 std::vector<ACTION_TOOLBAR_CONTROL*> controls;
388
389 for( TOOL_ACTION* action : ACTION_MANAGER::GetActionList() )
390 actions.push_back( action );
391
392 for( ACTION_TOOLBAR_CONTROL* control : ACTION_TOOLBAR::GetCustomControlList( FRAME_FOOTPRINT_EDITOR ) )
393 controls.push_back( control );
394
395 return new PANEL_TOOLBAR_CUSTOMIZATION( aParent, cfg, tb, FRAME_FOOTPRINT_EDITOR, actions, controls );
396 }
397
398 case PANEL_FP_COLORS:
399 return new PANEL_FP_EDITOR_COLOR_SETTINGS( aParent );
400
402 return new PANEL_DISPLAY_OPTIONS( aParent, GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" ) );
403
404 case PANEL_PCB_GRIDS:
405 {
406 PCBNEW_SETTINGS* cfg = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" );
407 EDA_BASE_FRAME* frame = aKiway->Player( FRAME_PCB_EDITOR, false );
408
409 if( !frame )
410 frame = aKiway->Player( FRAME_FOOTPRINT_EDITOR, false );
411
412 if( !frame )
413 frame = aKiway->Player( FRAME_FOOTPRINT_VIEWER, false );
414
415 if( frame )
416 SetUserUnits( frame->GetUserUnits() );
417
418 return new PANEL_GRID_SETTINGS( aParent, this, frame, cfg, FRAME_PCB_EDITOR );
419 }
420
422 return new PANEL_PCBNEW_DISPLAY_ORIGIN( aParent, GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" ),
424
426 {
427 EDA_BASE_FRAME* frame = aKiway->Player( FRAME_PCB_EDITOR, false );
428
429 if( !frame )
430 frame = aKiway->Player( FRAME_FOOTPRINT_EDITOR, false );
431
432 if( !frame )
433 frame = aKiway->Player( FRAME_FOOTPRINT_VIEWER, false );
434
435 if( frame )
436 SetUserUnits( frame->GetUserUnits() );
437
438 return new PANEL_EDIT_OPTIONS( aParent, this, frame, false );
439 }
440
441 case PANEL_PCB_COLORS:
442 {
443 BOARD* board = nullptr;
444 EDA_BASE_FRAME* boardProvider = aKiway->Player( FRAME_PCB_EDITOR, false );
445
446 if( boardProvider )
447 board = static_cast<PCB_EDIT_FRAME*>( boardProvider )->GetBoard();
448
449 return new PANEL_PCBNEW_COLOR_SETTINGS( aParent, board );
450 }
451
453 {
454 APP_SETTINGS_BASE* cfg = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" );
455 TOOLBAR_SETTINGS* tb = GetToolbarSettings<PCB_EDIT_TOOLBAR_SETTINGS>( "pcbnew-toolbars" );
456
457 std::vector<TOOL_ACTION*> actions;
458 std::vector<ACTION_TOOLBAR_CONTROL*> controls;
459
460 for( TOOL_ACTION* action : ACTION_MANAGER::GetActionList() )
461 actions.push_back( action );
462
463 for( ACTION_TOOLBAR_CONTROL* control : ACTION_TOOLBAR::GetCustomControlList( FRAME_PCB_EDITOR ) )
464 controls.push_back( control );
465
466 return new PANEL_TOOLBAR_CUSTOMIZATION( aParent, cfg, tb, FRAME_PCB_EDITOR, actions, controls );
467 }
468
470 return new PANEL_PCBNEW_ACTION_PLUGINS( aParent );
471
473 return new PANEL_3D_DISPLAY_OPTIONS( aParent );
474
475 case PANEL_3DV_OPENGL:
476 return new PANEL_3D_OPENGL_OPTIONS( aParent );
477
479 return new PANEL_3D_RAYTRACING_OPTIONS( aParent );
480
482 {
483 APP_SETTINGS_BASE* cfg = GetAppSettings<EDA_3D_VIEWER_SETTINGS>( "3d_viewer" );
484 TOOLBAR_SETTINGS* tb = GetToolbarSettings<EDA_3D_VIEWER_TOOLBAR_SETTINGS>( "3d_viewer-toolbars" );
485
486 std::vector<TOOL_ACTION*> actions;
487 std::vector<ACTION_TOOLBAR_CONTROL*> controls;
488
489 for( TOOL_ACTION* action : ACTION_MANAGER::GetActionList() )
490 actions.push_back( action );
491
492 for( ACTION_TOOLBAR_CONTROL* control : ACTION_TOOLBAR::GetCustomControlList( FRAME_PCB_DISPLAY3D ) )
493 controls.push_back( control );
494
495 return new PANEL_TOOLBAR_CUSTOMIZATION( aParent, cfg, tb, FRAME_PCB_DISPLAY3D, actions, controls );
496 }
497
498 default:
499 return nullptr;
500 }
501 }
502
513 void* IfaceOrAddress( int aDataId ) override
514 {
515 switch( aDataId )
516 {
518 {
519 // This is the mechanism by which FOOTPRINT_SELECT_WIDGET can get access to the adapter
520 // without directly linking to pcbnew or pcbcommon, going through PROJECT::FootprintLibAdapter
521 PROJECT* project = nullptr;
522
523 if( wxTheApp )
524 {
525 wxWindow* focus = wxWindow::FindFocus();
526 wxWindow* top = focus ? wxGetTopLevelParent( focus ) : wxTheApp->GetTopWindow();
527
528 if( top )
529 {
530 if( KIWAY_HOLDER* holder = dynamic_cast<KIWAY_HOLDER*>( top ) )
531 project = &holder->Prj();
532 }
533 }
534
535 if( !project )
537
539 }
540
542 {
543 // Return function pointer for filtering footprints
544 // Signature: wxString (*)(const wxString& aFilterJson)
545 return reinterpret_cast<void*>( &filterFootprints );
546 }
547
548 default:
549 return nullptr;
550 }
551 }
552
558 void SaveFileAs( const wxString& aProjectBasePath, const wxString& aSrcProjectName,
559 const wxString& aNewProjectBasePath, const wxString& aNewProjectName,
560 const wxString& aSrcFilePath, wxString& aErrors ) override;
561
562 int HandleJob( JOB* aJob, REPORTER* aReporter, PROGRESS_REPORTER* aProgressReporter ) override;
563
564 bool HandleJobConfig( JOB* aJob, wxWindow* aParent ) override;
565
566#if defined( KICAD_IPC_API )
567 bool HandleApiOpenDocument( const wxString& aPath,
568 KICAD_API_SERVER* aServer,
569 wxString* aError ) override;
570
571 bool HandleApiCloseDocument( const wxString& aBoardFileName,
572 KICAD_API_SERVER* aServer,
573 wxString* aError ) override;
574#endif
575
576 void PreloadLibraries( KIWAY* aKiway ) override;
577 void ProjectChanged() override;
578 void CancelPreload( bool aBlock = true ) override;
579
580private:
581 std::unique_ptr<PCBNEW_JOBS_HANDLER> m_jobHandler;
582 std::shared_ptr<BACKGROUND_JOB> m_libraryPreloadBackgroundJob;
583 std::future<void> m_libraryPreloadReturn;
585 std::atomic_bool m_libraryPreloadAbort;
586
587#if defined( KICAD_IPC_API )
588 void closeCurrentDocument( KICAD_API_SERVER* aServer );
589
590 KIWAY* m_kiway = nullptr;
591 std::shared_ptr<HEADLESS_BOARD_CONTEXT> m_openContext;
592 std::unique_ptr<API_HANDLER_PCB> m_openHandler;
593#endif
594
595} kiface( "pcbnew", KIWAY::FACE_PCB );
596
597} // namespace
598
599
600using namespace PCB;
601
602
604
605
606// KIFACE_GETTER's actual spelling is a substitution macro found in kiway.h.
607// KIFACE_GETTER will not have name mangling due to declaration in kiway.h.
608KIFACE_API KIFACE* KIFACE_GETTER( int* aKIFACEversion, int aKiwayVersion, PGM_BASE* aProgram )
609{
610 return &kiface;
611}
612
613
614bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway )
615{
616 // This is process-level-initialization, not project-level-initialization of the DSO.
617 // Do nothing in here pertinent to a project!
619
620 SETTINGS_MANAGER& mgr = aProgram->GetSettingsManager();
621
624
625 // We intentionally register KifaceSettings after FOOTPRINT_EDITOR_SETTINGS and EDA_3D_VIEWER_SETTINGS
626 // In legacy configs, many settings were in a single editor config and the migration routine
627 // for the main editor file will try and call into the now separate settings stores
628 // to move the settings into them
630
631 // Register the footprint editor settings as well because they share a KiFACE and need to be
632 // loaded prior to use to avoid threading deadlocks
634
635 start_common( aCtlBits );
636
637#if defined( KICAD_IPC_API )
638 m_kiway = aKiway;
639#endif
640
641 m_jobHandler = std::make_unique<PCBNEW_JOBS_HANDLER>( aKiway );
642
644 {
645 m_jobHandler->SetReporter( &CLI_REPORTER::GetInstance() );
646 m_jobHandler->SetProgressReporter( &CLI_PROGRESS_REPORTER::GetInstance() );
647 }
648
649 return true;
650}
651
652
654{
655}
656
657
659{
660 end_common();
661}
662
663
664void IFACE::SaveFileAs( const wxString& aProjectBasePath, const wxString& aSrcProjectName,
665 const wxString& aNewProjectBasePath, const wxString& aNewProjectName,
666 const wxString& aSrcFilePath, wxString& aErrors )
667{
668 wxFileName destFile( aSrcFilePath );
669 wxString destPath = destFile.GetPathWithSep();
670 wxUniChar pathSep = wxFileName::GetPathSeparator();
671 wxString ext = destFile.GetExt();
672
673 if( destPath.StartsWith( aProjectBasePath + pathSep ) )
674 destPath.Replace( aProjectBasePath, aNewProjectBasePath, false );
675
676 wxString srcProjectFootprintLib = pathSep + aSrcProjectName + wxT( ".pretty" ) + pathSep;
677 wxString newProjectFootprintLib = pathSep + aNewProjectName + wxT( ".pretty" ) + pathSep;
678
679 destPath.Replace( srcProjectFootprintLib, newProjectFootprintLib, true );
680
681 destFile.SetPath( destPath );
682
685 {
686 if( destFile.GetName() == aSrcProjectName )
687 destFile.SetName( aNewProjectName );
688
689 CopySexprFile( aSrcFilePath, destFile.GetFullPath(),
690 [&]( const std::string& token, wxString& value )
691 {
692 if( token == "sheetfile" )
693 {
694 for( const wxString extension : { wxT( ".sch" ), wxT( ".kicad_sch" ) } )
695 {
696 if( value == aSrcProjectName + extension )
697 {
698 value = aNewProjectName + extension;
699 return true;
700 }
701 else if( value == aProjectBasePath + "/" + aSrcProjectName + extension )
702 {
703 value = aNewProjectBasePath + "/" + aNewProjectName + extension;
704 return true;
705 }
706 else if( value.StartsWith( aProjectBasePath ) )
707 {
708 value.Replace( aProjectBasePath, aNewProjectBasePath, false );
709 return true;
710 }
711 }
712 }
713
714 return false;
715 },
716 aErrors );
717 }
718 else if( ext == FILEEXT::LegacyPcbFileExtension )
719 {
720 if( destFile.GetName() == aSrcProjectName )
721 destFile.SetName( aNewProjectName );
722
723 KiCopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
724 }
727 {
728 // Footprints are not project-specific. Keep their source names.
729 KiCopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
730 }
732 {
733 // TODO
734 }
735 else if( ext == wxT( "rpt" ) )
736 {
737 // DRC must be the "gold standard". Since we can't guarantee that there aren't
738 // any non-deterministic cases in the save-as algorithm, we don't want to certify
739 // the result with the source's DRC report. Therefore copy it under the old
740 // name.
741 KiCopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
742 }
743 else if( destFile.GetName() == FILEEXT::FootprintLibraryTableFileName )
744 {
745 wxFileName libTableFn( aSrcFilePath );
746 LIBRARY_TABLE libTable( libTableFn, LIBRARY_TABLE_SCOPE::PROJECT );
747 libTable.SetPath( destFile.GetFullPath() );
748 libTable.SetType( LIBRARY_TABLE_TYPE::FOOTPRINT );
749
750 for( LIBRARY_TABLE_ROW& row : libTable.Rows() )
751 {
752 wxString uri = row.URI();
753
754 uri.Replace( wxT( "/" ) + aSrcProjectName + wxT( ".pretty" ),
755 wxT( "/" ) + aNewProjectName + wxT( ".pretty" ) );
756
757 row.SetURI( uri );
758 }
759
760 libTable.Save().map_error(
761 [&]( const LIBRARY_ERROR& aError )
762 {
763 wxString msg;
764
765 if( !aErrors.empty() )
766 aErrors += wxT( "\n" );
767
768 msg.Printf( _( "Cannot copy file '%s'." ), destFile.GetFullPath() );
769 aErrors += msg;
770 } );
771 }
772 else
773 {
774 wxFAIL_MSG( wxT( "Unexpected filetype for Pcbnew::SaveFileAs()" ) );
775 }
776}
777
778
779int IFACE::HandleJob( JOB* aJob, REPORTER* aReporter, PROGRESS_REPORTER* aProgressReporter )
780{
781 return m_jobHandler->RunJob( aJob, aReporter, aProgressReporter );
782}
783
784
785bool IFACE::HandleJobConfig( JOB* aJob, wxWindow* aParent )
786{
787 return m_jobHandler->HandleJobConfig( aJob, aParent );
788}
789
790
791#if defined( KICAD_IPC_API )
792void IFACE::closeCurrentDocument( KICAD_API_SERVER* aServer )
793{
794 if( m_openHandler )
795 {
796 if( aServer )
797 aServer->DeregisterHandler( m_openHandler.get() );
798
799 m_openHandler.reset();
800 }
801
802 m_openContext.reset();
803}
804
805
806bool IFACE::HandleApiOpenDocument( const wxString& aPath, KICAD_API_SERVER* aServer, wxString* aError )
807{
808 wxCHECK( aServer, false );
809
810 if( aPath.IsEmpty() )
811 {
812 if( aError )
813 *aError = wxS( "No path specified to open" );
814
815 return false;
816 }
817
818 wxFileName projectPath( aPath );
819
820 if( projectPath.GetExt() == FILEEXT::KiCadPcbFileExtension )
821 projectPath.SetExt( FILEEXT::ProjectFileExtension );
822 else if( projectPath.GetExt() != FILEEXT::ProjectFileExtension )
823 projectPath.SetExt( FILEEXT::ProjectFileExtension );
824
825 projectPath.MakeAbsolute();
826
827 SETTINGS_MANAGER& settingsManager = Pgm().GetSettingsManager();
828
829 if( !settingsManager.LoadProject( projectPath.GetFullPath(), true ) )
830 {
831 wxLogTrace( traceApi, "Warning: no project file found for %s", aPath );
832 }
833
834 PROJECT* project = settingsManager.GetProject( projectPath.GetFullPath() );
835
836 if( !project )
837 {
838 if( aError )
839 *aError = wxString::Format( wxS( "Error loading project for %s" ), aPath );
840
841 return false;
842 }
843
844 wxFileName boardPath( projectPath );
845 boardPath.SetExt( FILEEXT::KiCadPcbFileExtension );
846
847 if( !boardPath.FileExists() )
848 {
849 if( aError )
850 *aError = wxString::Format( wxS( "File not found: %s" ), aPath );
851
852 return false;
853 }
854
855 PCB_IO_MGR::PCB_FILE_T pluginType =
857
858 if( pluginType == PCB_IO_MGR::FILE_TYPE_NONE )
859 {
860 if( aError )
861 *aError = wxString::Format( wxS( "%s is not a recognized file type" ), aPath );
862
863 return false;
864 }
865
866 std::shared_ptr<HEADLESS_BOARD_CONTEXT> newContext;
867
868 try
869 {
870 std::unique_ptr<BOARD> loadedBoard = BOARD_LOADER::Load( boardPath.GetFullPath(), pluginType, project );
871
872 if( !loadedBoard )
873 {
874 if( aError )
875 *aError = wxS( "Failed to load board" );
876
877 return false;
878 }
879
880 newContext = std::make_shared<HEADLESS_BOARD_CONTEXT>( std::move( loadedBoard ), project,
882 m_kiway );
883 }
884 catch( ... )
885 {
886 if( aError )
887 *aError = wxS( "Failed to load board" );
888
889 return false;
890 }
891
892 closeCurrentDocument( aServer );
893 m_openContext = std::move( newContext );
894
895 m_openHandler = std::make_unique<API_HANDLER_PCB>( m_openContext, nullptr );
896 aServer->RegisterHandler( m_openHandler.get() );
897
898 return true;
899}
900
901
902bool IFACE::HandleApiCloseDocument( const wxString& aFileName, KICAD_API_SERVER* aServer, wxString* aError )
903{
904 wxCHECK( aServer, false );
905
906 if( !m_openContext )
907 {
908 if( aError )
909 *aError = wxS( "No document is currently open" );
910
911 return false;
912 }
913
914 if( !aFileName.IsEmpty() )
915 {
916 wxFileName currentBoard( m_openContext->GetCurrentFileName() );
917
918 if( currentBoard.GetFullName() != aFileName )
919 {
920 if( aError )
921 *aError = wxS( "Requested document does not match the open document" );
922
923 return false;
924 }
925 }
926
927 closeCurrentDocument( aServer );
928 return true;
929}
930#endif
931
932
934{
935 constexpr static int interval = 150;
936 constexpr static int timeLimit = 120000;
937
938 wxCHECK( aKiway, /* void */ );
939
940 // Use compare_exchange to atomically check and set the flag to prevent race conditions
941 // when PreloadLibraries is called multiple times concurrently (e.g., from project manager
942 // and pcb editor both scheduling via CallAfter)
943 bool expected = false;
944
945 if( !m_libraryPreloadInProgress.compare_exchange_strong( expected, true ) )
946 return;
947
949
951 Pgm().GetBackgroundJobMonitor().Create( _( "Loading Footprint Libraries" ) );
952
953 auto preload =
954 [this, aKiway]() -> void
955 {
956 std::shared_ptr<BACKGROUND_JOB_REPORTER> reporter =
958
960
961 int elapsed = 0;
962 bool aborted = false;
963
964 reporter->Report( _( "Loading Footprint Libraries" ) );
965 adapter->AsyncLoad();
966
967 while( true )
968 {
969 if( m_libraryPreloadAbort.load() )
970 {
971 aborted = true;
972 break;
973 }
974
975 std::this_thread::sleep_for( std::chrono::milliseconds( interval ) );
976
977 if( std::optional<float> loadStatus = adapter->AsyncLoadProgress() )
978 {
979 float progress = *loadStatus;
980 reporter->SetCurrentProgress( progress );
981
982 if( progress >= 1 )
983 break;
984 }
985 else
986 {
987 reporter->SetCurrentProgress( 1 );
988 break;
989 }
990
991 elapsed += interval;
992
993 if( elapsed > timeLimit )
994 break;
995 }
996
997 adapter->BlockUntilLoaded();
998
999 // Check again after blocking - abort may have been requested while we were waiting
1000 if( m_libraryPreloadAbort.load() )
1001 aborted = true;
1002
1003 // If aborted, skip operations that use the adapter since the project may have changed
1004 // and the adapter's project reference could be stale. This prevents use-after-free
1005 // crashes when switching projects during library preload.
1006 if( !aborted )
1007 {
1008 // Collect and report library load errors from adapter
1009 wxString errors = adapter->GetLibraryLoadErrors();
1010
1011 wxLogTrace( traceLibraries,
1012 "pcbnew PreloadLibraries: adapter errors.IsEmpty()=%d, length=%zu",
1013 errors.IsEmpty(), errors.length() );
1014
1015 if( !errors.IsEmpty() )
1016 {
1017 std::vector<LOAD_MESSAGE> messages =
1019
1020 wxLogTrace( traceLibraries, " -> adapter: collected %zu messages",
1021 messages.size() );
1022
1023 if( !messages.empty() )
1024 Pgm().AddLibraryLoadMessages( messages );
1025 }
1026 else
1027 {
1028 wxLogTrace( traceLibraries, " -> no errors from footprint adapter" );
1029 }
1030 }
1031 else
1032 {
1033 wxLogTrace( traceLibraries, "pcbnew PreloadLibraries: aborted, skipping footprint processing" );
1034 }
1035
1036 m_libraryPreloadAbort.store( false );
1039 m_libraryPreloadInProgress.store( false );
1040
1041 // Only send reload notifications if we weren't aborted
1042 if( !aborted )
1043 {
1044 std::string payload = "";
1045 aKiway->ExpressMail( FRAME_PCB_EDITOR, MAIL_RELOAD_LIB, payload, nullptr, true );
1046 aKiway->ExpressMail( FRAME_FOOTPRINT_EDITOR, MAIL_RELOAD_LIB, payload, nullptr, true );
1047 aKiway->ExpressMail( FRAME_CVPCB, MAIL_RELOAD_LIB, payload, nullptr, true );
1048 }
1049 };
1050
1052 m_libraryPreloadReturn = tp.submit_task( preload );
1053}
1054
1055
1057{
1058 if( m_libraryPreloadInProgress.load() )
1059 m_libraryPreloadAbort.store( true );
1060}
1061
1062
1063void IFACE::CancelPreload( bool aBlock )
1064{
1065 if( m_libraryPreloadInProgress.load() )
1066 {
1067 m_libraryPreloadAbort.store( true );
1068
1069 if( aBlock )
1071 }
1072}
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
static std::list< TOOL_ACTION * > & GetActionList()
Return list of TOOL_ACTIONs.
static std::list< ACTION_TOOLBAR_CONTROL * > GetCustomControlList(FRAME_T aContext)
Get the list of custom controls that could be used on a particular frame type.
std::shared_ptr< BACKGROUND_JOB > Create(const wxString &aName)
Creates a background job with the given name.
void Remove(std::shared_ptr< BACKGROUND_JOB > job)
Removes the given background job from any lists and frees it.
static std::unique_ptr< BOARD > Load(const wxString &aFileName, PCB_IO_MGR::PCB_FILE_T aFormat, PROJECT *aProject, const OPTIONS &aOptions)
static CLI_PROGRESS_REPORTER & GetInstance()
static CLI_REPORTER & GetInstance()
Definition reporter.cpp:134
The base frame for deriving all KiCad main window classes.
An interface to the global shared library manager that is schematic-specific and linked to one projec...
std::vector< FOOTPRINT * > GetFootprints(const wxString &aNickname, bool aBestEfforts=false)
Retrieves a list of footprints contained in a given loaded library.
static FOOTPRINT_PREVIEW_PANEL * New(KIWAY *aKiway, wxWindow *aParent, UNITS_PROVIDER *aUnitsProvider)
Component library viewer main window.
An simple container class that lets us dispatch output jobs to kifaces.
Definition job.h:184
void RegisterHandler(API_HANDLER *aHandler)
Adds a new request handler to the server.
void DeregisterHandler(API_HANDLER *aHandler)
A KIFACE implementation.
Definition kiface_base.h:39
KIFACE_BASE(const char *aKifaceName, KIWAY::FACE_T aId)
Definition kiface_base.h:67
void InitSettings(APP_SETTINGS_BASE *aSettings)
Definition kiface_base.h:97
void end_common()
Common things to do for a top program module, during OnKifaceEnd();.
APP_SETTINGS_BASE * KifaceSettings() const
Definition kiface_base.h:95
bool start_common(int aCtlBits)
Common things to do for a top program module, during OnKifaceStart().
int m_start_flags
flags provided in OnKifaceStart()
bool IsSingle() const
Is this KIFACE running under single_top?
A mix in class which holds the location of a wxWindow's KIWAY.
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:315
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition kiway.cpp:402
virtual void ExpressMail(FRAME_T aDestination, MAIL_T aCommand, std::string &aPayload, wxWindow *aSource=nullptr, bool aFromOtherThread=false)
Send aPayload to aDestination from aSource.
Definition kiway.cpp:500
FACE_T
Known KIFACE implementations.
Definition kiway.h:321
@ FACE_PCB
pcbnew DSO
Definition kiway.h:323
virtual void CommonSettingsChanged(int aFlags=0)
Call CommonSettingsChanged() on all KIWAY_PLAYERs.
Definition kiway.cpp:594
virtual PROJECT & Prj() const
Return the PROJECT associated with this KIWAY.
Definition kiway.cpp:205
std::optional< float > AsyncLoadProgress() const
Returns async load progress between 0.0 and 1.0, or nullopt if load is not in progress.
wxString GetLibraryLoadErrors() const
Returns all library load errors as newline-separated strings for display.
std::vector< wxString > GetLibraryNames() const
Returns a list of library nicknames that are available (skips any that failed to load)
void AsyncLoad()
Loads all available libraries for this adapter type in the background.
The main frame for Pcbnew.
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
static PCB_FILE_T FindPluginTypeFromBoardPath(const wxString &aFileName, int aCtl=0)
Return a plugin type given a path for a board file.
Container for data for KiCad programs.
Definition pgm_base.h:108
virtual BACKGROUND_JOBS_MONITOR & GetBackgroundJobMonitor() const
Definition pgm_base.h:136
void ClearLibraryLoadMessages()
Clear library load messages from all registered status bars.
void AddLibraryLoadMessages(const std::vector< LOAD_MESSAGE > &aMessages)
Add library load messages to all registered status bars.
Definition pgm_base.cpp:994
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:130
A progress reporter interface for use in multi-threaded environments.
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
Container for project specific data.
Definition project.h:66
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
T * RegisterSettings(T *aSettings, bool aLoadNow=true)
Take ownership of the pointer passed in.
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
PROJECT * GetProject(const wxString &aFullPath) const
Retrieve a loaded project by name.
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
UNITS_PROVIDER(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits)
EDA_UNITS GetUserUnits() const
void SetUserUnits(EDA_UNITS aUnits)
This file is part of the common library.
#define _(s)
DDE server & client.
#define KICAD_PCB_PORT_SERVICE_NUMBER
Pcbnew listens on this port for commands from Eeschema.
Definition eda_dde.h:40
Abstract pattern-matching tool and implementations.
EDA_UNITS
Definition eda_units.h:48
@ DO_NOT_INCLUDE_NPTH
Definition footprint.h:73
FRAME_T
The set of EDA_BASE_FRAME derivatives, typically stored in EDA_BASE_FRAME::m_Ident.
Definition frame_type.h:33
@ PANEL_PCB_GRIDS
Definition frame_type.h:98
@ FRAME_PCB_EDITOR
Definition frame_type.h:42
@ PANEL_3DV_TOOLBARS
Definition frame_type.h:108
@ FRAME_FOOTPRINT_VIEWER
Definition frame_type.h:45
@ PANEL_3DV_OPENGL
Definition frame_type.h:106
@ PANEL_FP_DEFAULT_GRAPHICS_VALUES
Definition frame_type.h:93
@ PANEL_PCB_TOOLBARS
Definition frame_type.h:101
@ PANEL_PCB_ORIGINS_AXES
Definition frame_type.h:103
@ FRAME_FOOTPRINT_WIZARD
Definition frame_type.h:46
@ PANEL_PCB_EDIT_OPTIONS
Definition frame_type.h:99
@ PANEL_FP_DISPLAY_OPTIONS
Definition frame_type.h:87
@ PANEL_PCB_COLORS
Definition frame_type.h:100
@ PANEL_FP_USER_LAYER_NAMES
Definition frame_type.h:94
@ PANEL_3DV_RAYTRACING
Definition frame_type.h:107
@ DIALOG_PCB_LIBRARY_TABLE
Definition frame_type.h:126
@ FRAME_FOOTPRINT_PREVIEW
Definition frame_type.h:48
@ FRAME_FOOTPRINT_CHOOSER
Definition frame_type.h:44
@ PANEL_FP_GRIDS
Definition frame_type.h:88
@ PANEL_FP_ORIGINS_AXES
Definition frame_type.h:95
@ PANEL_PCB_DISPLAY_OPTS
Definition frame_type.h:97
@ DIALOG_CONFIGUREPATHS
Definition frame_type.h:123
@ PANEL_FP_COLORS
Definition frame_type.h:90
@ PANEL_FP_DEFAULT_FIELDS
Definition frame_type.h:92
@ FRAME_FOOTPRINT_EDITOR
Definition frame_type.h:43
@ FRAME_PCB_DISPLAY3D
Definition frame_type.h:47
@ PANEL_FP_EDIT_OPTIONS
Definition frame_type.h:89
@ PANEL_FP_TOOLBARS
Definition frame_type.h:91
@ PANEL_PCB_ACTION_PLUGINS
Definition frame_type.h:102
@ PANEL_3DV_DISPLAY_OPTIONS
Definition frame_type.h:105
@ FRAME_CVPCB
Definition frame_type.h:52
nlohmann::json json
Definition gerbview.cpp:50
void CopySexprFile(const wxString &aSrcPath, const wxString &aDestPath, std::function< bool(const std::string &token, wxString &value)> aCallback, wxString &aErrors)
Definition gestfich.cpp:320
void KiCopyFile(const wxString &aSrcPath, const wxString &aDestPath, wxString &aErrors)
Definition gestfich.cpp:293
static const std::string ProjectFileExtension
static const std::string LegacyPcbFileExtension
static const std::string FootprintLibraryTableFileName
static const std::string BackupFileSuffix
static const std::string LegacyFootprintLibPathExtension
static const std::string FootprintAssignmentFileExtension
static const std::string KiCadFootprintFileExtension
static const std::string KiCadPcbFileExtension
const wxChar *const traceLibraries
Flag to enable library table and library manager tracing.
const wxChar *const traceApi
Flag to enable debug output related to the IPC API and its plugin system.
Definition api_utils.cpp:27
#define KIFACE_API
@ KIFACE_FOOTPRINT_LIBRARY_ADAPTER
Definition kiface_ids.h:34
@ KIFACE_FILTER_FOOTPRINTS
Function pointer type: wxString (*)(const wxString& aFilterJson) Input JSON: {"pin_count": N,...
Definition kiface_ids.h:39
#define KFCTL_CLI
Running as CLI app.
Definition kiway.h:165
#define KIFACE_GETTER
Definition kiway.h:113
#define KICTL_KICAD_ONLY
chosen file is from KiCad according to user
This file contains miscellaneous commonly used macros and functions.
@ MAIL_RELOAD_LIB
Definition mail_type.h:57
PCB::IFACE KIFACE_BASE, UNITS_PROVIDER kiface("pcbnew", KIWAY::FACE_PCB)
void InvokePcbLibTableEditor(KIWAY *aKiway, wxWindow *aCaller)
Function InvokePcbLibTableEditor shows the modal DIALOG_FP_LIB_TABLE for purposes of editing the glob...
static wxString filterFootprints(const wxString &aFilterJson)
Filter footprints based on criteria passed as JSON.
Definition pcbnew.cpp:109
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
Definition pcbnew.cpp:603
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
@ RPT_SEVERITY_ERROR
T * GetToolbarSettings(const wxString &aFilename)
T * GetAppSettings(const char *aFilename)
std::vector< LOAD_MESSAGE > ExtractLibraryLoadErrors(const wxString &aErrorString, int aSeverity)
Parse library load error messages, extracting user-facing information while stripping internal code l...
bool OnKifaceStart(PGM_BASE *aProgram, int aCtlBits, KIWAY *aKiway) override
Typically start_common() is called from here.
Implement a participant in the KIWAY alchemy.
Definition kiway.h:156
virtual bool HandleApiCloseDocument(const wxString &aBoardFileName, KICAD_API_SERVER *aServer, wxString *aError)
Definition kiway.h:269
virtual bool HandleApiOpenDocument(const wxString &aPath, KICAD_API_SERVER *aServer, wxString *aError)
Definition kiway.h:259
std::atomic_bool m_libraryPreloadInProgress
Definition pcbnew.cpp:584
bool OnKifaceStart(PGM_BASE *aProgram, int aCtlBits, KIWAY *aKiway) override
Typically start_common() is called from here.
Definition pcbnew.cpp:614
void PreloadLibraries(KIWAY *aKiway) override
Definition pcbnew.cpp:933
void ProjectChanged() override
Definition pcbnew.cpp:1056
void SaveFileAs(const wxString &aProjectBasePath, const wxString &aSrcProjectName, const wxString &aNewProjectBasePath, const wxString &aNewProjectName, const wxString &aSrcFilePath, wxString &aErrors) override
Saving a file under a different name is delegated to the various KIFACEs because the project doesn't ...
Definition pcbnew.cpp:664
void CancelPreload(bool aBlock=true) override
Definition pcbnew.cpp:1063
std::shared_ptr< BACKGROUND_JOB > m_libraryPreloadBackgroundJob
Definition pcbnew.cpp:582
void * IfaceOrAddress(int aDataId) override
Return a pointer to the requested object.
Definition pcbnew.cpp:513
void Reset() override
Reloads global state.
Definition pcbnew.cpp:653
wxWindow * CreateKiWindow(wxWindow *aParent, int aClassId, KIWAY *aKiway, int aCtlBits=0) override
Create a wxWindow for the current project.
Definition pcbnew.cpp:252
IFACE(const char *aName, KIWAY::FACE_T aType)
Definition pcbnew.cpp:241
std::atomic_bool m_libraryPreloadAbort
Definition pcbnew.cpp:585
int HandleJob(JOB *aJob, REPORTER *aReporter, PROGRESS_REPORTER *aProgressReporter) override
Definition pcbnew.cpp:779
std::unique_ptr< PCBNEW_JOBS_HANDLER > m_jobHandler
Definition pcbnew.cpp:581
bool HandleJobConfig(JOB *aJob, wxWindow *aParent) override
Definition pcbnew.cpp:785
void OnKifaceEnd() override
Called just once just before the DSO is to be unloaded.
Definition pcbnew.cpp:658
std::future< void > m_libraryPreloadReturn
Definition pcbnew.cpp:583
KIBIS top(path, &reporter)
VECTOR3I expected(15, 30, 45)
nlohmann::json output
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
static thread_pool * tp
BS::priority_thread_pool thread_pool
Definition thread_pool.h:31
#define ENVVARS_CHANGED
wxLogTrace helper definitions.
Definition of file extensions used in Kicad.