KiCad PCB EDA Suite
Loading...
Searching...
No Matches
eeschema.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) 2004 Jean-Pierre Charras, [email protected]
5 * Copyright (C) 2008 Wayne Stambaugh <[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, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <algorithm>
23
24#include <api/api_handler_sch.h>
25#include <api/api_server.h>
26#include <api/api_utils.h>
29#include <pgm_base.h>
30#include <kiface_base.h>
33#include <confirm.h>
34#include <gestfich.h>
35#include <eda_dde.h>
37#include "eeschema_helpers.h"
39#include <reporter.h>
40#include "git/kigit_sch_merge.h"
43#include <eeschema_settings.h>
44#include <sch_edit_frame.h>
46#include <symbol_edit_frame.h>
47#include <symbol_viewer_frame.h>
54#include <kiway.h>
55#include <project_sch.h>
56#include <richio.h>
59#include <sexpr/sexpr.h>
60#include <sexpr/sexpr_parser.h>
61#include <string_utils.h>
62#include <trace_helpers.h>
63#include <thread_pool.h>
64#include <kiface_ids.h>
65#include <widgets/kistatusbar.h>
67#include <wx/ffile.h>
68#include <wx/tokenzr.h>
70
71#include <schematic.h>
72#include <connection_graph.h>
83#include <sim/simulator_frame.h>
84
86#include <toolbars_sch_editor.h>
88
89#include <sch_io/sch_io.h>
90#include <sch_io/sch_io_mgr.h>
91
92#include <wx/crt.h>
93
94// The main sheet of the project
96
97
98namespace SCH {
99
100// Non-job kiface exports for diff/merge (returned by IfaceOrAddress). Defined
101// after the kiface instance so they can route into its jobs handler.
102static int eeschemaMergeExport( int aKind, const wxString& aAncestor, const wxString& aOurs,
103 const wxString& aTheirs, const wxString& aOutput, bool aInteractive,
104 bool aSingleFile, REPORTER* aReporter );
105static int eeschemaOpenDiffDialogExport( int aKind, const wxString& aFileA, const wxString& aFileB,
106 const wxString& aLabelA, const wxString& aLabelB,
107 wxWindow* aParent, REPORTER* aReporter );
108
109
110
111// TODO: This should move out of this file
112static std::unique_ptr<SCHEMATIC> readSchematicFromFile( const std::string& aFilename )
113{
114 SCH_IO* pi = SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD );
115 std::unique_ptr<SCHEMATIC> schematic = std::make_unique<SCHEMATIC>( nullptr );
116
118
119 wxFileName pro( aFilename );
120 pro.SetExt( FILEEXT::ProjectFileExtension );
121 pro.MakeAbsolute();
122 wxString projectPath = pro.GetFullPath();
123
124 PROJECT* project = manager.GetProject( projectPath );
125
126 if( !project )
127 {
128 manager.LoadProject( projectPath, true );
129 project = manager.GetProject( projectPath );
130 }
131
132 schematic->Reset();
133 schematic->SetProject( project );
134 SCH_SHEET* rootSheet = pi->LoadSchematicFile( aFilename, schematic.get() );
135
136 if( !rootSheet )
137 return nullptr;
138
139 std::vector<SCH_SHEET*> topLevelSheets = schematic->GetTopLevelSheets();
140 bool rootIsTopLevel = std::find( topLevelSheets.begin(), topLevelSheets.end(), rootSheet )
141 != topLevelSheets.end();
142 bool rootIsVirtualRoot = rootSheet == &schematic->Root() || rootSheet->IsVirtualRootSheet();
143
144 if( !rootIsTopLevel && !rootIsVirtualRoot )
145 schematic->SetTopLevelSheets( { rootSheet } );
146
147 SCH_SCREENS screens( schematic->Root() );
148
149 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
150 screen->UpdateLocalLibSymbolLinks();
151
152 SCH_SHEET_LIST sheets = schematic->Hierarchy();
153
154 // Restore all of the loaded symbol instances from the root sheet screen.
155 sheets.UpdateSymbolInstanceData( schematic->RootScreen()->GetSymbolInstances() );
156
157 if( schematic->RootScreen()->GetFileFormatVersionAtLoad() < 20230221 )
158 {
159 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
160 screen->FixLegacyPowerSymbolMismatches();
161 }
162
163 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
164 screen->MigrateSimModels();
165
166 sheets.AnnotatePowerSymbols();
167
168 // NOTE: This is required for multi-unit symbols to be correct
169 for( SCH_SHEET_PATH& sheet : sheets )
170 sheet.UpdateAllScreenReferences();
171
172 // TODO: this must handle SchematicCleanup somehow. The original version didn't because
173 // it knew that QA test cases were saved in a clean state.
174
175 // TODO: does this need to handle PruneOrphanedSymbolInstances() and
176 // PruneOrphanedSheetInstances()?
177
178 schematic->ConnectionGraph()->Recalculate( sheets, true );
179
180 return schematic;
181}
182
183
184// TODO: This should move out of this file
185bool generateSchematicNetlist( const wxString& aFilename, std::string& aNetlist )
186{
187 std::unique_ptr<SCHEMATIC> schematic = readSchematicFromFile( aFilename.ToStdString() );
188 NETLIST_EXPORTER_KICAD exporter( schematic.get() );
189 STRING_FORMATTER formatter;
190
191 exporter.Format( &formatter, GNL_ALL | GNL_OPT_KICAD );
192 aNetlist = formatter.GetString();
193
194 return true;
195}
196
197
198static struct IFACE : public KIFACE_BASE, public UNITS_PROVIDER
199{
200 // Of course all are virtual overloads, implementations of the KIFACE.
201
202 IFACE( const char* aName, KIWAY::FACE_T aType ) :
203 KIFACE_BASE( aName, aType ),
206 {}
207
208 bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway ) override;
209
210 void Reset() override;
211
212 void OnKifaceEnd() override;
213
214 wxWindow* CreateKiWindow( wxWindow* aParent, int aClassId, KIWAY* aKiway, int aCtlBits = 0 ) override
215 {
216 switch( aClassId )
217 {
218 case FRAME_SCH:
219 {
220 SCH_EDIT_FRAME* frame = new SCH_EDIT_FRAME( aKiway, aParent );
221
223
224 if( Kiface().IsSingle() )
225 {
226 // only run this under single_top, not under a project manager.
228 }
229
230 return frame;
231 }
234 return new SYMBOL_EDIT_FRAME( aKiway, aParent );
235
236 case FRAME_SIMULATOR:
237 {
238 try
239 {
240 SIMULATOR_FRAME* frame = new SIMULATOR_FRAME( aKiway, aParent );
241 return frame;
242 }
243 catch( const SIMULATOR_INIT_ERR& )
244 {
245 // catch the init err exception as we don't want it to bubble up
246 // its going to be some ngspice install issue but we don't want to log that
247 return nullptr;
248 }
249 }
251 case FRAME_SCH_VIEWER:
252 return new SYMBOL_VIEWER_FRAME( aKiway, aParent );
253
255 {
256 bool cancelled = false;
257 SYMBOL_CHOOSER_FRAME* chooser = new SYMBOL_CHOOSER_FRAME( aKiway, aParent, cancelled );
258
259 if( cancelled )
260 {
261 chooser->Destroy();
262 return nullptr;
263 }
265 return chooser;
267
269 InvokeSchEditSymbolLibTable( aKiway, aParent );
270 // Dialog has completed; nothing to return.
271 return nullptr;
272
275 // Dialog has completed; nothing to return.
276 return nullptr;
277
279 return new PANEL_SYM_DISPLAY_OPTIONS( aParent, GetAppSettings<SYMBOL_EDITOR_SETTINGS>( "symbol_editor" ) );
284 EDA_BASE_FRAME* frame = aKiway->Player( FRAME_SCH_SYMBOL_EDITOR, false );
286 if( !frame )
287 frame = aKiway->Player( FRAME_SCH_VIEWER, false );
289 if( !frame )
290 frame = aKiway->Player( FRAME_SCH, false );
291
292 if( frame )
293 SetUserUnits( frame->GetUserUnits() );
294
295 return new PANEL_GRID_SETTINGS( aParent, this, frame, cfg, FRAME_SCH_SYMBOL_EDITOR );
296 }
297
299 return CreateSnappingPanel( aParent, GetAppSettings<SYMBOL_EDITOR_SETTINGS>( "symbol_editor" ),
301
303 {
304 EDA_BASE_FRAME* frame = aKiway->Player( FRAME_SCH_SYMBOL_EDITOR, false );
305
306 if( !frame )
307 frame = aKiway->Player( FRAME_SCH_VIEWER, false );
308
309 if( !frame )
310 frame = aKiway->Player( FRAME_SCH, false );
311
312 if( frame )
313 SetUserUnits( frame->GetUserUnits() );
314
315 return new PANEL_SYM_EDITING_OPTIONS( aParent, this, frame );
316 }
317
319 {
320 APP_SETTINGS_BASE* cfg = GetAppSettings<SYMBOL_EDITOR_SETTINGS>( "symbol_editor" );
321 TOOLBAR_SETTINGS* tb = GetToolbarSettings<SYMBOL_EDIT_TOOLBAR_SETTINGS>( "symbol_editor-toolbars" );
322
323 std::vector<TOOL_ACTION*> actions;
324 std::vector<ACTION_TOOLBAR_CONTROL*> controls;
325
326 for( TOOL_ACTION* action : ACTION_MANAGER::GetActionList() )
327 actions.push_back( action );
328
329 for( ACTION_TOOLBAR_CONTROL* control : ACTION_TOOLBAR::GetCustomControlList( FRAME_SCH_SYMBOL_EDITOR ) )
330 controls.push_back( control );
331
332 return new PANEL_TOOLBAR_CUSTOMIZATION( aParent, cfg, tb, FRAME_SCH_SYMBOL_EDITOR, actions, controls );
333 }
334
335 case PANEL_SYM_COLORS:
336 return new PANEL_SYM_COLOR_SETTINGS( aParent );
337
339 return new PANEL_EESCHEMA_DISPLAY_OPTIONS( aParent, GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" ) );
340
341 case PANEL_SCH_GRIDS:
342 {
343 EESCHEMA_SETTINGS* cfg = GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" );
344 EDA_BASE_FRAME* frame = aKiway->Player( FRAME_SCH, false );
345
346 if( !frame )
347 frame = aKiway->Player( FRAME_SCH_SYMBOL_EDITOR, false );
348
349 if( !frame )
350 frame = aKiway->Player( FRAME_SCH_VIEWER, false );
351
352 if( frame )
353 SetUserUnits( frame->GetUserUnits() );
354
355 return new PANEL_GRID_SETTINGS( aParent, this, frame, cfg, FRAME_SCH );
356 }
357
361
363 {
364 EDA_BASE_FRAME* frame = aKiway->Player( FRAME_SCH, false );
365
366 if( !frame )
367 frame = aKiway->Player( FRAME_SCH_SYMBOL_EDITOR, false );
368
369 if( !frame )
370 frame = aKiway->Player( FRAME_SCH_VIEWER, false );
371
372 if( frame )
373 SetUserUnits( frame->GetUserUnits() );
374
375 return new PANEL_EESCHEMA_EDITING_OPTIONS( aParent, this, frame );
376 }
377
379 {
380 APP_SETTINGS_BASE* cfg = GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" );
381 TOOLBAR_SETTINGS* tb = GetToolbarSettings<SCH_EDIT_TOOLBAR_SETTINGS>( "eeschema-toolbars" );
382
383 std::vector<TOOL_ACTION*> actions;
384 std::vector<ACTION_TOOLBAR_CONTROL*> controls;
385
386 for( TOOL_ACTION* action : ACTION_MANAGER::GetActionList() )
387 actions.push_back( action );
388
389 for( ACTION_TOOLBAR_CONTROL* control : ACTION_TOOLBAR::GetCustomControlList( FRAME_SCH ) )
390 controls.push_back( control );
391
392 return new PANEL_TOOLBAR_CUSTOMIZATION( aParent, cfg, tb, FRAME_SCH, actions, controls );
393 }
394
395 case PANEL_SCH_COLORS:
396 return new PANEL_EESCHEMA_COLOR_SETTINGS( aParent );
397
399 return new PANEL_TEMPLATE_FIELDNAMES( aParent, nullptr );
400
402 {
403 EDA_BASE_FRAME* frame = aKiway->Player( FRAME_SCH, false );
404
405 if( !frame )
406 frame = aKiway->Player( FRAME_SCH_SYMBOL_EDITOR, false );
407
408 if( !frame )
409 frame = aKiway->Player( FRAME_SCH_VIEWER, false );
410
411 return new class PANEL_SCH_DATA_SOURCES( aParent, frame );
412 }
413
415 return new PANEL_SIMULATOR_PREFERENCES( aParent );
416
417 default:
418 return nullptr;
419 }
420 }
421
432 void* IfaceOrAddress( int aDataId ) override
433 {
434 switch( aDataId )
435 {
437 return (void*) generateSchematicNetlist;
438
440 return reinterpret_cast<void*>( &eeschemaMergeExport );
441
443 return reinterpret_cast<void*>( &eeschemaOpenDiffDialogExport );
444 }
445
446 return nullptr;
447 }
448
451
457 void SaveFileAs( const wxString& aProjectBasePath, const wxString& aProjectName,
458 const wxString& aNewProjectBasePath, const wxString& aNewProjectName,
459 const wxString& aSrcFilePath, wxString& aErrors ) override;
460
461
462 int HandleJob( JOB* aJob, REPORTER* aReporter, PROGRESS_REPORTER* aProgressReporter ) override;
463
464 bool HandleJobConfig( JOB* aJob, wxWindow* aParent ) override;
465
466 bool HandleApiOpenDocument( const wxString& aPath,
467 KICAD_API_SERVER* aServer,
468 wxString* aError ) override;
469
470 bool HandleApiCloseDocument( const wxString& aSchFileName,
471 KICAD_API_SERVER* aServer,
472 wxString* aError ) override;
473
474 void PreloadLibraries( KIWAY* aKiway ) override;
475 void CancelPreload( bool aBlock = true ) override;
476 void ProjectChanged() override;
477
478private:
479 std::unique_ptr<EESCHEMA_JOBS_HANDLER> m_jobHandler;
480 std::shared_ptr<BACKGROUND_JOB> m_libraryPreloadBackgroundJob;
481 std::future<void> m_libraryPreloadReturn;
483 std::atomic_bool m_libraryPreloadAbort;
484
485 void closeCurrentDocument( KICAD_API_SERVER* aServer );
486
487 KIWAY* m_kiway = nullptr;
489 std::shared_ptr<HEADLESS_SCH_CONTEXT> m_openContext;
490 std::unique_ptr<API_HANDLER_SCH> m_openHandler;
491
492} kiface( "eeschema", KIWAY::FACE_SCH );
493
494
495int eeschemaMergeExport( int aKind, const wxString& aAncestor, const wxString& aOurs,
496 const wxString& aTheirs, const wxString& aOutput, bool aInteractive,
497 bool aSingleFile, REPORTER* aReporter )
498{
499 return kiface.JobHandler()->RunMerge( static_cast<KICAD_DIFF::DOC_KIND>( aKind ), aAncestor,
500 aOurs, aTheirs, aOutput, aInteractive, aSingleFile,
501 aReporter );
502}
503
504
505int eeschemaOpenDiffDialogExport( int aKind, const wxString& aFileA, const wxString& aFileB,
506 const wxString& aLabelA, const wxString& aLabelB,
507 wxWindow* aParent, REPORTER* aReporter )
508{
509 return kiface.JobHandler()->OpenDiffDialog( static_cast<KICAD_DIFF::DOC_KIND>( aKind ), aFileA,
510 aFileB, aLabelA, aLabelB, aParent, aReporter );
511}
512
513} // namespace
514
515using namespace SCH;
516
517
519
520
521// KIFACE_GETTER's actual spelling is a substitution macro found in kiway.h.
522// KIFACE_GETTER will not have name mangling due to declaration in kiway.h.
523KIFACE_API KIFACE* KIFACE_GETTER( int* aKIFACEversion, int aKiwayVersion, PGM_BASE* aProgram )
524{
525 return &kiface;
526}
527
528
529bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway )
530{
531 // This is process-level-initialization, not project-level-initialization of the DSO.
532 // Do nothing in here pertinent to a project!
534
535 // Register the symbol editor settings as well because they share a KiFACE and need to be
536 // loaded prior to use to avoid threading deadlocks
538 aProgram->GetSettingsManager().RegisterSettings( symSettings ); // manager takes ownership
539
540 // We intentionally register KifaceSettings after SYMBOL_EDITOR_SETTINGS
541 // In legacy configs, many settings were in a single editor config nd the migration routine
542 // for the main editor file will try and call into the now separate settings stores
543 // to move the settings into them
545
546 start_common( aCtlBits );
547
548 m_kiway = aKiway;
549
550 m_jobHandler = std::make_unique<EESCHEMA_JOBS_HANDLER>( aKiway );
551
553 {
554 m_jobHandler->SetReporter( &CLI_REPORTER::GetInstance() );
555 m_jobHandler->SetProgressReporter( &CLI_PROGRESS_REPORTER::GetInstance() );
556 }
557
558 // Register the schematic and symbol-library merge drivers with libgit2 so
559 // `.gitattributes` entries `merge=kicad-sch` and `merge=kicad-sym-lib`
560 // route through KiCad-aware merge logic.
563
564 return true;
565}
566
567
569{
570}
571
572
574{
575 constexpr static int interval = 150;
576 constexpr static int timeLimit = 120000;
577
578 wxCHECK( aKiway, /* void */ );
579
580 // Use compare_exchange to atomically check and set the flag to prevent race conditions
581 // when PreloadLibraries is called multiple times concurrently (e.g., from project manager
582 // and schematic editor both scheduling via CallAfter)
583 bool expected = false;
584
585 if( !m_libraryPreloadInProgress.compare_exchange_strong( expected, true ) )
586 return;
587
589
591 Pgm().GetBackgroundJobMonitor().Create( _( "Loading Symbol Libraries" ) );
592
593 auto preload =
594 [this, aKiway]() -> void
595 {
596 std::shared_ptr<BACKGROUND_JOB_REPORTER> reporter =
598
600
601 int elapsed = 0;
602 bool aborted = false;
603
604 reporter->Report( _( "Loading Symbol Libraries" ) );
605 adapter->AsyncLoad();
606
607 while( true )
608 {
609 if( m_libraryPreloadAbort.load() )
610 {
611 m_libraryPreloadAbort.store( false );
612 aborted = true;
613 break;
614 }
615
616 std::this_thread::sleep_for( std::chrono::milliseconds( interval ) );
617
618 if( std::optional<float> loadStatus = adapter->AsyncLoadProgress() )
619 {
620 float progress = *loadStatus;
621 reporter->SetCurrentProgress( progress );
622
623 if( progress >= 1 )
624 break;
625 }
626 else
627 {
628 reporter->SetCurrentProgress( 1 );
629 break;
630 }
631
632 elapsed += interval;
633
634 if( elapsed > timeLimit )
635 break;
636 }
637
638 // AbortAsyncLoad() sets the adapter's worker abort flag and then blocks,
639 // so workers exit at their next checkpoint. BlockUntilLoaded() alone just
640 // waits for each future to complete naturally, which can hang indefinitely
641 // if a worker is stuck on a stalled network or filesystem operation.
642 if( aborted )
643 adapter->AbortAsyncLoad();
644 else
645 adapter->BlockUntilLoaded();
646
647 // If aborted, skip operations that use the adapter since the project may have changed
648 // and the adapter's project reference could be stale. This prevents use-after-free
649 // crashes when switching projects during library preload.
650 if( !aborted )
651 {
652 // Collect library load errors for async reporting
653 wxString errors = adapter->GetLibraryLoadErrors();
654
655 wxLogTrace( traceLibraries, "eeschema PreloadLibraries: errors.IsEmpty()=%d, length=%zu",
656 errors.IsEmpty(), errors.length() );
657
658 std::vector<LOAD_MESSAGE> messages = ExtractLibraryLoadErrors( errors, RPT_SEVERITY_ERROR );
659
660 if( !messages.empty() )
661 {
662 wxLogTrace( traceLibraries, " -> collected %zu messages, calling AddLibraryLoadMessages",
663 messages.size() );
664 Pgm().AddLibraryLoadMessages( messages );
665 }
666 else
667 {
668 wxLogTrace( traceLibraries, " -> no errors from symbol libraries" );
669 }
670 }
671 else
672 {
673 wxLogTrace( traceLibraries, "eeschema PreloadLibraries: aborted, skipping symbol processing" );
674 }
675
678 m_libraryPreloadInProgress.store( false );
679
680 // Only send reload notifications if we weren't aborted
681 if( !aborted )
682 {
683 std::string payload = "";
684 aKiway->ExpressMail( FRAME_SCH, MAIL_RELOAD_LIB, payload, nullptr, true );
685 aKiway->ExpressMail( FRAME_SCH_SYMBOL_EDITOR, MAIL_RELOAD_LIB, payload, nullptr, true );
686 aKiway->ExpressMail( FRAME_SCH_VIEWER, MAIL_RELOAD_LIB, payload, nullptr, true );
687 }
688 };
689
690 std::future<void> preloadFuture = std::async( std::launch::async, preload );
691 m_libraryPreloadReturn = std::move( preloadFuture );
692}
693
694
695void IFACE::CancelPreload( bool aBlock )
696{
697 if( m_libraryPreloadInProgress.load() )
698 {
699 m_libraryPreloadAbort.store( true );
700
701 if( aBlock )
703 }
704}
705
706
708{
709 if( m_libraryPreloadInProgress.load() )
710 m_libraryPreloadAbort.store( true );
711}
712
713
715{
716 // Release the CLI-cached schematic while the static ERC_ITEM tables it serializes against are
717 // still alive; deferring to static teardown crashes reading dangling severity keys
718 if( m_jobHandler )
719 m_jobHandler->ClearCachedSchematic();
720
721 end_common();
722}
723
724
725void IFACE::SaveFileAs( const wxString& aProjectBasePath, const wxString& aProjectName,
726 const wxString& aNewProjectBasePath, const wxString& aNewProjectName,
727 const wxString& aSrcFilePath, wxString& aErrors )
728{
729 wxFileName destFile( aSrcFilePath );
730 wxString destPath = destFile.GetPathWithSep();
731 wxUniChar pathSep = wxFileName::GetPathSeparator();
732 wxString ext = destFile.GetExt();
733
734 if( destPath.StartsWith( aProjectBasePath + pathSep ) )
735 destPath.Replace( aProjectBasePath, aNewProjectBasePath, false );
736
737 destFile.SetPath( destPath );
738
743 {
744 if( destFile.GetName() == aProjectName )
745 {
746 destFile.SetName( aNewProjectName );
747 }
748 else if( destFile.GetName() == aNewProjectName )
749 {
750 wxString msg;
751
752 if( !aErrors.empty() )
753 aErrors += wxS( "\n" );
754
755 msg.Printf( _( "Cannot copy file '%s' as it will be overwritten by the new root "
756 "sheet file." ), destFile.GetFullPath() );
757 aErrors += msg;
758 return;
759 }
760
761 CopySexprFile( aSrcFilePath, destFile.GetFullPath(),
762 [&]( const std::string& token, wxString& value ) -> bool
763 {
764 if( token == "project" && value == aProjectName )
765 {
766 value = aNewProjectName;
767 return true;
768 }
769
770 return false;
771 },
772 aErrors );
773 }
775 {
776 // Symbols are not project-specific. Keep their source names.
777 KiCopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
778 }
782 {
783 if( destFile.GetName() == aProjectName + wxS( "-cache" ) )
784 destFile.SetName( aNewProjectName + wxS( "-cache" ) );
785
786 if( destFile.GetName() == aProjectName + wxS( "-rescue" ) )
787 destFile.SetName( aNewProjectName + wxS( "-rescue" ) );
788
789 KiCopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
790 }
791 else if( ext == FILEEXT::NetlistFileExtension )
792 {
793 if( destFile.GetName() == aProjectName )
794 destFile.SetName( aNewProjectName );
795
796 CopySexprFile( aSrcFilePath, destFile.GetFullPath(),
797 [&]( const std::string& token, wxString& value ) -> bool
798 {
799 if( token == "source" )
800 {
801 for( const wxString& extension : { wxString( wxT( ".sch" ) ), wxString( wxT( ".kicad_sch" ) ) } )
802 {
803 if( value == aProjectName + extension )
804 {
805 value = aNewProjectName + extension;
806 return true;
807 }
808 else if( value == aProjectBasePath + "/" + aProjectName + extension )
809 {
810 value = aNewProjectBasePath + "/" + aNewProjectName + extension;
811 return true;
812 }
813 else if( value.StartsWith( aProjectBasePath ) )
814 {
815 value.Replace( aProjectBasePath, aNewProjectBasePath, false );
816 return true;
817 }
818 }
819 }
820
821 return false;
822 },
823 aErrors );
824 }
825 else if( destFile.GetName() == FILEEXT::SymbolLibraryTableFileName )
826 {
827 wxFileName libTableFn( aSrcFilePath );
828 LIBRARY_TABLE libTable( libTableFn, LIBRARY_TABLE_SCOPE::PROJECT );
829 libTable.SetPath( destFile.GetFullPath() );
830 libTable.SetType( LIBRARY_TABLE_TYPE::SYMBOL );
831
832 for( LIBRARY_TABLE_ROW& row : libTable.Rows() )
833 {
834 wxString uri = row.URI();
835
836 uri.Replace( wxS( "/" ) + aProjectName + wxS( "-cache.lib" ),
837 wxS( "/" ) + aNewProjectName + wxS( "-cache.lib" ) );
838 uri.Replace( wxS( "/" ) + aProjectName + wxS( "-rescue.lib" ),
839 wxS( "/" ) + aNewProjectName + wxS( "-rescue.lib" ) );
840 uri.Replace( wxS( "/" ) + aProjectName + wxS( ".lib" ),
841 wxS( "/" ) + aNewProjectName + wxS( ".lib" ) );
842
843 row.SetURI( uri );
844 }
845
846 libTable.Save().map_error(
847 [&]( const LIBRARY_ERROR& aError )
848 {
849 wxString msg;
850
851 if( !aErrors.empty() )
852 aErrors += wxT( "\n" );
853
854 msg.Printf( _( "Cannot copy file '%s'." ), destFile.GetFullPath() );
855 aErrors += msg;
856 } );
857 }
858 else
859 {
860 wxFAIL_MSG( wxS( "Unexpected filetype for Eeschema::SaveFileAs()" ) );
861 }
862}
863
864
865int IFACE::HandleJob( JOB* aJob, REPORTER* aReporter, PROGRESS_REPORTER* aProgressReporter )
866{
867 return m_jobHandler->RunJob( aJob, aReporter, aProgressReporter );
868}
869
870
871bool IFACE::HandleJobConfig( JOB* aJob, wxWindow* aParent )
872{
873 return m_jobHandler->HandleJobConfig( aJob, aParent );
874}
875
876
877// TODO(JE) some of the below methods can probably be factored out and shared between sch/pcb
879{
880 if( m_openHandler )
881 {
882 if( aServer )
883 aServer->DeregisterHandler( m_openHandler.get() );
884
885 m_openHandler.reset();
886 }
887
888 m_openContext.reset();
889
890 delete m_openSchematic;
891 m_openSchematic = nullptr;
892
893 // The jobs handler caches the last-loaded schematic. Clear it so the next job
894 // uses the schematic from the newly opened document rather than a stale copy.
895 m_jobHandler->ClearCachedSchematic();
896}
897
898
899bool IFACE::HandleApiOpenDocument( const wxString& aPath, KICAD_API_SERVER* aServer,
900 wxString* aError )
901{
902 wxCHECK( aServer, false );
903
904 if( aPath.IsEmpty() )
905 {
906 if( aError )
907 *aError = wxS( "No path specified to open" );
908
909 return false;
910 }
911
912 wxFileName projectPath( aPath );
913
914 if( projectPath.GetExt() == FILEEXT::KiCadSchematicFileExtension )
915 projectPath.SetExt( FILEEXT::ProjectFileExtension );
916 else if( projectPath.GetExt() != FILEEXT::ProjectFileExtension )
917 projectPath.SetExt( FILEEXT::ProjectFileExtension );
918
919 projectPath.MakeAbsolute();
920
921 // Close any existing document before loading a new project. LoadProject with
922 // aSetActive=true destroys the old PROJECT, which would leave the old schematic
923 // and context holding dangling project pointers.
924 closeCurrentDocument( aServer );
925
926 SETTINGS_MANAGER& settingsManager = Pgm().GetSettingsManager();
927
928 if( !settingsManager.LoadProject( projectPath.GetFullPath(), true ) )
929 {
930 wxLogTrace( traceApi, "Warning: no project file found for %s", aPath );
931 }
932
933 PROJECT* project = settingsManager.GetProject( projectPath.GetFullPath() );
934
935 if( !project )
936 {
937 if( aError )
938 *aError = wxString::Format( wxS( "Error loading project for %s" ), aPath );
939
940 return false;
941 }
942
943 wxFileName schPath( projectPath );
944 schPath.SetExt( FILEEXT::KiCadSchematicFileExtension );
945
946 if( !schPath.FileExists() )
947 {
948 if( aError )
949 *aError = wxString::Format( wxS( "File not found: %s" ), schPath.GetFullPath() );
950
951 return false;
952 }
953
954 SCHEMATIC* schematic = nullptr;
955
956 try
957 {
958 schematic = EESCHEMA_HELPERS::LoadSchematic( schPath.GetFullPath(), false, false, project );
959
960 if( !schematic )
961 {
962 if( aError )
963 *aError = wxS( "Failed to load schematic" );
964
965 return false;
966 }
967 }
968 catch( ... )
969 {
970 if( aError )
971 *aError = wxS( "Failed to load schematic" );
972
973 return false;
974 }
975
976 m_openSchematic = schematic;
977
978 m_openContext = std::make_shared<HEADLESS_SCH_CONTEXT>( m_openSchematic, project, m_kiway );
979 m_openHandler = std::make_unique<API_HANDLER_SCH>( m_openContext );
980 aServer->RegisterHandler( m_openHandler.get() );
981
982 return true;
983}
984
985
986bool IFACE::HandleApiCloseDocument( const wxString& aSchFileName, KICAD_API_SERVER* aServer,
987 wxString* aError )
988{
989 wxCHECK( aServer, false );
990
991 if( !m_openContext )
992 {
993 if( aError )
994 *aError = wxS( "No document is currently open" );
995
996 return false;
997 }
998
999 if( !aSchFileName.IsEmpty() )
1000 {
1001 wxFileName currentSch( m_openContext->GetCurrentFileName() );
1002
1003 if( currentSch.GetFullName() != aSchFileName )
1004 {
1005 if( aError )
1006 *aError = wxS( "Requested document does not match the open document" );
1007
1008 return false;
1009 }
1010 }
1011
1012 closeCurrentDocument( aServer );
1013 return true;
1014}
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
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.
APP_SETTINGS_BASE is a settings class that should be derived for each standalone KiCad application.
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 CLI_PROGRESS_REPORTER & GetInstance()
static CLI_REPORTER & GetInstance()
Definition reporter.cpp:216
The base frame for deriving all KiCad main window classes.
static void SetSchEditFrame(SCH_EDIT_FRAME *aSchEditFrame)
static SCHEMATIC * LoadSchematic(const wxString &aFileName, bool aSetActive, bool aForceDefaultProject, PROJECT *aProject=nullptr, bool aCalculateConnectivity=true)
Handle Eeschema job dispatches.
SNAP_INFERENCE_SETTINGS m_SnapInference
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:35
KIFACE_BASE(const char *aKifaceName, KIWAY::FACE_T aId)
Definition kiface_base.h:63
void InitSettings(APP_SETTINGS_BASE *aSettings)
Definition kiface_base.h:93
void end_common()
Common things to do for a top program module, during OnKifaceEnd();.
APP_SETTINGS_BASE * KifaceSettings() const
Definition kiface_base.h:91
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?
static int Apply(const git_merge_driver_source *aSrc, const char **aPathOut, unsigned int *aModeOut, git_buf *aMergedOut)
void CreateServer(int service, bool local=true)
bool Destroy() override
Our version of Destroy() which is virtual from wxWidgets.
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:311
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition kiway.cpp:388
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:486
FACE_T
Known KIFACE implementations.
Definition kiway.h:317
@ FACE_SCH
eeschema DSO
Definition kiway.h:318
virtual PROJECT & Prj() const
Return the PROJECT associated with this KIWAY.
Definition kiway.cpp:201
std::optional< float > AsyncLoadProgress() const
Returns async load progress between 0.0 and 1.0, or nullopt if load is not in progress.
void AbortAsyncLoad()
Aborts any async load in progress; blocks until fully done aborting.
wxString GetLibraryLoadErrors() const
Returns all library load errors as newline-separated strings for display.
void AsyncLoad()
Loads all available libraries for this adapter type in the background.
Generate the KiCad netlist format supported by Pcbnew.
void Format(OUTPUTFORMATTER *aOutputFormatter, int aCtl)
Output this s-expression netlist into aOutputFormatter.
Container for data for KiCad programs.
Definition pgm_base.h:102
virtual BACKGROUND_JOBS_MONITOR & GetBackgroundJobMonitor() const
Definition pgm_base.h:130
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.
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:124
A progress reporter interface for use in multi-threaded environments.
static SYMBOL_LIBRARY_ADAPTER * SymbolLibAdapter(PROJECT *aProject)
Accessor for project symbol library manager adapter.
Container for project specific data.
Definition project.h:63
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:72
Holds all the data relating to one schematic.
Definition schematic.h:90
Schematic editor (Eeschema) main window.
Base class that schematic file and library loading and saving plugins should derive from.
Definition sch_io.h:59
virtual SCH_SHEET * LoadSchematicFile(const wxString &aFileName, SCHEMATIC *aSchematic, SCH_SHEET *aAppendToMe=nullptr, const std::map< std::string, UTF8 > *aProperties=nullptr)
Load information from some input file format that this SCH_IO implementation knows about,...
Definition sch_io.cpp:67
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:746
SCH_SCREEN * GetNext()
SCH_SCREEN * GetFirst()
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
void AnnotatePowerSymbols()
Silently annotate the not yet annotated power symbols of the entire hierarchy of the sheet path list.
void UpdateSymbolInstanceData(const std::vector< SCH_SYMBOL_INSTANCE > &aSymbolInstances)
Update all of the symbol instance information using aSymbolInstances.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:44
bool IsVirtualRootSheet() const
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.
The SIMULATOR_FRAME holds the main user-interface for running simulations.
Implement an OUTPUTFORMATTER to a memory buffer.
Definition richio.h:418
const std::string & GetString()
Definition richio.h:441
Symbol library viewer main window.
SNAP_INFERENCE_SETTINGS m_SnapInference
The symbol library editor main window.
An interface to the global shared library manager that is schematic-specific and linked to one projec...
Symbol library viewer main window.
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_SCH_PORT_SERVICE_NUMBER
Eeschema listens on this port for commands from Pcbnew.
Definition eda_dde.h:39
EDA_UNITS
Definition eda_units.h:44
SCH_SHEET * g_RootSheet
Definition eeschema.cpp:95
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
Definition eeschema.cpp:518
@ PANEL_SYM_EDIT_GRIDS
Definition frame_type.h:74
@ FRAME_SCH_SYMBOL_EDITOR
Definition frame_type.h:31
@ PANEL_SCH_FIELD_NAME_TEMPLATES
Definition frame_type.h:86
@ PANEL_SCH_TOOLBARS
Definition frame_type.h:85
@ PANEL_SYM_SNAPPING
Definition frame_type.h:75
@ FRAME_SCH_VIEWER
Definition frame_type.h:32
@ PANEL_SCH_DISP_OPTIONS
Definition frame_type.h:80
@ PANEL_SCH_SIMULATOR
Definition frame_type.h:87
@ FRAME_SCH
Definition frame_type.h:30
@ PANEL_SYM_TOOLBARS
Definition frame_type.h:78
@ FRAME_SIMULATOR
Definition frame_type.h:34
@ PANEL_SYM_EDIT_OPTIONS
Definition frame_type.h:76
@ PANEL_SCH_EDIT_OPTIONS
Definition frame_type.h:83
@ PANEL_SYM_DISP_OPTIONS
Definition frame_type.h:73
@ DIALOG_SCH_LIBRARY_TABLE
Definition frame_type.h:132
@ PANEL_SCH_DATA_SOURCES
Definition frame_type.h:88
@ PANEL_SYM_COLORS
Definition frame_type.h:77
@ PANEL_SCH_SNAPPING
Definition frame_type.h:82
@ PANEL_SCH_GRIDS
Definition frame_type.h:81
@ PANEL_SCH_COLORS
Definition frame_type.h:84
@ DIALOG_DESIGN_BLOCK_LIBRARY_TABLE
Definition frame_type.h:131
@ FRAME_SYMBOL_CHOOSER
Definition frame_type.h:33
void CopySexprFile(const wxString &aSrcPath, const wxString &aDestPath, std::function< bool(const std::string &token, wxString &value)> aCallback, wxString &aErrors)
Definition gestfich.cpp:370
void KiCopyFile(const wxString &aSrcPath, const wxString &aDestPath, wxString &aErrors)
Definition gestfich.cpp:343
static const std::string LegacySchematicFileExtension
static const std::string NetlistFileExtension
static const std::string SymbolLibraryTableFileName
static const std::string ProjectFileExtension
static const std::string SchematicSymbolFileExtension
static const std::string KiCadSchematicFileExtension
static const std::string LegacySymbolLibFileExtension
static const std::string KiCadSymbolLibFileExtension
static const std::string BackupFileSuffix
static const std::string LegacySymbolDocumentFileExtension
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:29
#define KIFACE_API
@ KIFACE_MERGE_DOCUMENT
int (*)( int aKind, const wxString& aAncestor, const wxString& aOurs, const wxString& aTheirs,...
Definition kiface_ids.h:45
@ KIFACE_NETLIST_SCHEMATIC
Definition kiface_ids.h:38
@ KIFACE_OPEN_DIFF_DIALOG
int (*)( int aKind, const wxString& aFileA, const wxString& aFileB, const wxString& aLabelA,...
Definition kiface_ids.h:52
#define KFCTL_CLI
Running as CLI app.
Definition kiway.h:161
#define KIFACE_GETTER
Definition kiway.h:109
@ MAIL_RELOAD_LIB
Definition mail_type.h:54
DOC_KIND
Document type a diff/merge entry point should route to, derived from a file path's extension.
bool RegisterMergeDriver(const char *aName, MERGE_APPLY_FN aApply)
Register a KiCad merge driver with libgit2.
static int eeschemaMergeExport(int aKind, const wxString &aAncestor, const wxString &aOurs, const wxString &aTheirs, const wxString &aOutput, bool aInteractive, bool aSingleFile, REPORTER *aReporter)
Definition eeschema.cpp:495
static std::unique_ptr< SCHEMATIC > readSchematicFromFile(const std::string &aFilename)
Definition eeschema.cpp:112
SCH::IFACE KIFACE_BASE, UNITS_PROVIDER kiface("eeschema", KIWAY::FACE_SCH)
bool generateSchematicNetlist(const wxString &aFilename, std::string &aNetlist)
Definition eeschema.cpp:185
static int eeschemaOpenDiffDialogExport(int aKind, const wxString &aFileA, const wxString &aFileB, const wxString &aLabelA, const wxString &aLabelB, wxWindow *aParent, REPORTER *aReporter)
Definition eeschema.cpp:505
#define GNL_ALL
@ GNL_OPT_KICAD
void InvokeEditDesignBlockLibTable(KIWAY *aKiway, wxWindow *aParent)
PANEL_SNAPPING * CreateSnappingPanel(wxWindow *aParent, SETTINGS_T *aCfg, FRAME_T aFrameType, SNAP_INFERENCE_SETTINGS SETTINGS_T::*aInference=nullptr, MAGNETIC_SETTINGS SETTINGS_T::*aMagnetics=nullptr)
Build the snapping page for an editor.
void InvokeSchEditSymbolLibTable(KIWAY *aKiway, wxWindow *aParent)
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:152
std::future< void > m_libraryPreloadReturn
Definition eeschema.cpp:481
bool OnKifaceStart(PGM_BASE *aProgram, int aCtlBits, KIWAY *aKiway) override
Typically start_common() is called from here.
Definition eeschema.cpp:529
std::shared_ptr< BACKGROUND_JOB > m_libraryPreloadBackgroundJob
Definition eeschema.cpp:480
std::unique_ptr< API_HANDLER_SCH > m_openHandler
Definition eeschema.cpp:490
void PreloadLibraries(KIWAY *aKiway) override
Definition eeschema.cpp:573
void SaveFileAs(const wxString &aProjectBasePath, const wxString &aProjectName, 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 eeschema.cpp:725
void ProjectChanged() override
Definition eeschema.cpp:707
void CancelPreload(bool aBlock=true) override
Definition eeschema.cpp:695
void closeCurrentDocument(KICAD_API_SERVER *aServer)
Definition eeschema.cpp:878
wxWindow * CreateKiWindow(wxWindow *aParent, int aClassId, KIWAY *aKiway, int aCtlBits=0) override
Create a wxWindow for the current project.
Definition eeschema.cpp:214
EESCHEMA_JOBS_HANDLER * JobHandler() const
Accessor for the non-job diff/merge exports (eeschemaMergeExport etc.).
Definition eeschema.cpp:450
std::atomic_bool m_libraryPreloadAbort
Definition eeschema.cpp:483
KIWAY * m_kiway
Definition eeschema.cpp:487
IFACE(const char *aName, KIWAY::FACE_T aType)
Definition eeschema.cpp:202
void Reset() override
Reloads global state.
Definition eeschema.cpp:568
bool HandleApiOpenDocument(const wxString &aPath, KICAD_API_SERVER *aServer, wxString *aError) override
Definition eeschema.cpp:899
bool HandleApiCloseDocument(const wxString &aSchFileName, KICAD_API_SERVER *aServer, wxString *aError) override
Definition eeschema.cpp:986
void * IfaceOrAddress(int aDataId) override
Return a pointer to the requested object.
Definition eeschema.cpp:432
int HandleJob(JOB *aJob, REPORTER *aReporter, PROGRESS_REPORTER *aProgressReporter) override
Definition eeschema.cpp:865
std::unique_ptr< EESCHEMA_JOBS_HANDLER > m_jobHandler
Definition eeschema.cpp:479
std::atomic_bool m_libraryPreloadInProgress
Definition eeschema.cpp:482
bool HandleJobConfig(JOB *aJob, wxWindow *aParent) override
Definition eeschema.cpp:871
std::shared_ptr< HEADLESS_SCH_CONTEXT > m_openContext
Definition eeschema.cpp:489
SCHEMATIC * m_openSchematic
Definition eeschema.cpp:488
void OnKifaceEnd() override
Called just once just before the DSO is to be unloaded.
Definition eeschema.cpp:714
IbisParser parser & reporter
VECTOR3I expected(15, 30, 45)
static const long long MM
wxLogTrace helper definitions.
Definition of file extensions used in Kicad.