KiCad PCB EDA Suite
Loading...
Searching...
No Matches
eeschema/files-io.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2013 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2013 Wayne Stambaugh <[email protected]>
6 * Copyright (C) 2013-2023 CERN (www.cern.ch)
7 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23
24#include <algorithm>
25
26#include <confirm.h>
27#include <common.h>
28#include <connection_graph.h>
30#include <dialog_symbol_remap.h>
32#include <embedded_files.h>
33#include <eeschema_settings.h>
34#include <id.h>
35#include <kiface_base.h>
36#include <kiplatform/app.h>
37#include <kiplatform/ui.h>
41#include <local_history.h>
42#include <lockfile.h>
43#include <pgm_base.h>
44#include <core/profile.h>
46#include <project_rescue.h>
47#include <project_sch.h>
51#include <reporter.h>
52#include <richio.h>
54#include <sch_bus_entry.h>
55#include <sch_commit.h>
56#include <sch_edit_frame.h>
57#include <sch_draw_panel.h>
59#include <sch_file_versions.h>
60#include <sch_line.h>
61#include <sch_sheet.h>
62#include <sch_sheet_path.h>
63#include <schematic.h>
65#include <sim/simulator_frame.h>
67#include <tool/actions.h>
68#include <tool/tool_manager.h>
71#include <trace_helpers.h>
73#include <widgets/kistatusbar.h>
74#include <widgets/wx_infobar.h>
76#include <local_history.h>
78#include <wx/app.h>
79#include <wx/ffile.h>
80#include <wx/filedlg.h>
81#include <wx/log.h>
82#include <wx/richmsgdlg.h>
83#include <wx/stdpaths.h>
86#include <paths.h>
87#include <wx_filename.h> // For ::ResolvePossibleSymlinks
90
91#include <kiplatform/io.h>
92
95#include "save_project_utils.h"
96
97bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
98{
99 // ensure the splash screen does not obscure any dialog at startup
100 Pgm().HideSplash();
101
102 // implement the pseudo code from KIWAY_PLAYER.h:
103 wxString msg;
104
105 EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
106
107 // This is for python:
108 if( aFileSet.size() != 1 )
109 {
110 msg.Printf( "Eeschema:%s() takes only a single filename.", __WXFUNCTION__ );
111 DisplayError( this, msg );
112 return false;
113 }
114
115 wxString fullFileName( aFileSet[0] );
116 wxFileName wx_filename( fullFileName );
117
118 // We insist on caller sending us an absolute path, if it does not, we say it's a bug.
119 wxASSERT_MSG( wx_filename.IsAbsolute(), wxS( "Path is not absolute!" ) );
120
121 if( !LockFile( fullFileName ) )
122 {
123 // If project-level lock override was already granted, silently override this file's lock
124 if( Prj().IsLockOverrideGranted() )
125 {
126 m_file_checker->OverrideLock();
127 }
128 else
129 {
130 msg.Printf( _( "Schematic '%s' is already open by '%s' at '%s'." ), fullFileName,
131 m_file_checker->GetUsername(), m_file_checker->GetHostname() );
132
133 if( !AskOverrideLock( this, msg ) )
134 return false;
135
136 m_file_checker->OverrideLock();
137 }
138 }
139
140 if( !AskToSaveChanges() )
141 return false;
142
143#ifdef PROFILE
144 PROF_TIMER openFiles( "OpenProjectFile" );
145#endif
146
147 wxFileName pro = fullFileName;
148 pro.SetExt( FILEEXT::ProjectFileExtension );
149
150 bool is_new = !wxFileName::IsFileReadable( fullFileName );
151
152 // If its a non-existent schematic and caller thinks it exists
153 if( is_new && !( aCtl & KICTL_CREATE ) )
154 {
155 // notify user that fullFileName does not exist, ask if user wants to create it.
156 msg.Printf( _( "Schematic '%s' does not exist. Do you wish to create it?" ), fullFileName );
157
158 if( !IsOK( this, msg ) )
159 return false;
160 }
161
162 wxCommandEvent e( EDA_EVT_SCHEMATIC_CHANGING );
163 ProcessEventLocally( e );
164
165 // unload current project file before loading new
166 {
169 SetScreen( nullptr );
171 }
172
173 SetStatusText( wxEmptyString );
174 m_infoBar->Dismiss();
175
176 if( KISTATUSBAR* statusBar = dynamic_cast<KISTATUSBAR*>( GetStatusBar() ) )
177 statusBar->ClearWarningMessages( "load" );
178
179 WX_PROGRESS_REPORTER progressReporter( this, is_new ? _( "Create Schematic" ) : _( "Load Schematic" ), 1,
180 PR_CAN_ABORT );
181 WX_STRING_REPORTER loadReporter;
182 LOAD_INFO_REPORTER_SCOPE loadReporterScope( &loadReporter );
183
184 bool differentProject = pro.GetFullPath() != Prj().GetProjectFullName();
185
186 // This is for handling standalone mode schematic changes
187 if( differentProject )
188 {
189 if( !Prj().IsNullProject() )
190 {
193 }
194
195 // disconnect existing project from schematic before we unload the project
196 Schematic().SetProject( nullptr );
197 GetSettingsManager()->UnloadProject( &Prj(), false );
198
199 GetSettingsManager()->LoadProject( pro.GetFullPath() );
200
201 wxFileName legacyPro( pro );
202 legacyPro.SetExt( FILEEXT::LegacyProjectFileExtension );
203
204 // Do not allow saving a project if one doesn't exist. This normally happens if we are
205 // standalone and opening a schematic that has been moved from its project folder.
206 if( !pro.Exists() && !legacyPro.Exists() && !( aCtl & KICTL_CREATE ) )
207 Prj().SetReadOnly();
208 }
209
210 // Crash-recovery: when zip-format autosave is active, look for autosave files newer
211 // than the saved schematic and offer to recover them before any sheet is loaded.
212 if( !is_new )
213 CheckForAutosaveFiles( wx_filename.GetPath(), { FILEEXT::KiCadSchematicFileExtension } );
214
215 // Start a new schematic object now that we sorted out our project
216 std::unique_ptr<SCHEMATIC> newSchematic = std::make_unique<SCHEMATIC>( &Prj() );
217
218 SCH_IO_MGR::SCH_FILE_T schFileType = SCH_IO_MGR::GuessPluginTypeFromSchPath( fullFileName, aCtl );
219
220 bool isNonKicadImport = schFileType != SCH_IO_MGR::SCH_KICAD
221 && schFileType != SCH_IO_MGR::SCH_LEGACY
222 && schFileType != SCH_IO_MGR::SCH_FILE_UNKNOWN;
223
224 // If this is a recognised non-KiCad format, delegate to the import path.
225 // Callers that pass KICTL_KICAD_ONLY (e.g. GUI open) won't reach
226 // this branch because GuessPluginTypeFromSchPath already returns SCH_FILE_UNKNOWN
227 // for these formats.
228 if( isNonKicadImport )
229 {
230 progressReporter.Hide();
231 return importFile( fullFileName, schFileType, nullptr );
232 }
233
234 if( schFileType == SCH_IO_MGR::SCH_LEGACY )
235 {
236 // Don't reload the symbol libraries if we are just launching Eeschema from KiCad again.
237 // They are already saved in the kiface project object.
238 if( differentProject || !Prj().GetElem( PROJECT::ELEM::LEGACY_SYMBOL_LIBS ) )
239 {
240 // load the libraries here, not in SCH_SCREEN::Draw() which is a context
241 // that will not tolerate DisplayError() dialog since we're already in an
242 // event handler in there.
243 // And when a schematic file is loaded, we need these libs to initialize
244 // some parameters (links to PART LIB, dangling ends ...)
247 }
248 }
249 else
250 {
251 // No legacy symbol libraries including the cache are loaded with the new file format.
253 }
254
255 wxFileName rfn( fullFileName );
256 rfn.MakeRelativeTo( Prj().GetProjectPath() );
257 LoadWindowState( rfn.GetFullPath() );
258
259 KIPLATFORM::APP::SetShutdownBlockReason( this, _( "Schematic file changes are unsaved" ) );
260
261 if( Kiface().IsSingle() )
262 {
264 }
265
266 if( is_new || schFileType == SCH_IO_MGR::SCH_FILE_T::SCH_FILE_UNKNOWN )
267 {
268 newSchematic->CreateDefaultScreens();
269 SetSchematic( newSchematic.release() );
270
271 // mark new, unsaved file as modified.
273 GetScreen()->SetFileName( fullFileName );
274
275 if( schFileType == SCH_IO_MGR::SCH_FILE_T::SCH_FILE_UNKNOWN )
276 {
277 if( aCtl & KICTL_KICAD_ONLY )
278 {
279 msg.Printf( _( "'%s' is not a KiCad schematic file.\n"
280 "Use File -> Import for non-KiCad schematic files." ), fullFileName );
281 }
282 else
283 {
284 // Even the non-KiCad plugin type didn't know
285 msg.Printf( _( "'%s' is not a recognised schematic format." ), fullFileName );
286 }
287
288 progressReporter.Hide();
289 DisplayErrorMessage( this, msg );
290 }
291 }
292 else
293 {
294 SetScreen( nullptr );
295
296 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( schFileType ) );
297
298 pi->SetProgressReporter( &progressReporter );
299
300 bool failedLoad = false;
301
302 try
303 {
304 {
305 wxBusyCursor busy;
306 WINDOW_DISABLER raii( this );
307
308 // Check if project file has top-level sheets defined
309 PROJECT_FILE& projectFile = Prj().GetProjectFile();
310 const std::vector<TOP_LEVEL_SHEET_INFO>& topLevelSheets = projectFile.GetTopLevelSheets();
311
312 if( !topLevelSheets.empty() )
313 {
314 std::vector<SCH_SHEET*> loadedSheets;
315
316 // Load each top-level sheet
317 for( const TOP_LEVEL_SHEET_INFO& sheetInfo : topLevelSheets )
318 {
319 wxFileName sheetFileName( Prj().GetProjectPath(), sheetInfo.filename );
320
321 // When loading legacy schematic files, ensure we are referencing the correct extension
322 if( schFileType == SCH_IO_MGR::SCH_LEGACY )
323 sheetFileName.SetExt( FILEEXT::LegacySchematicFileExtension );
324
325 wxString sheetPath = sheetFileName.GetFullPath();
326
327 if( !wxFileName::FileExists( sheetPath ) )
328 {
329 wxLogWarning( wxT( "Top-level sheet file not found: %s" ), sheetPath );
330 continue;
331 }
332
333 SCH_SHEET* sheet = pi->LoadSchematicFile( sheetPath, newSchematic.get() );
334
335 if( sheet )
336 {
337 // Preserve the UUID from the project file, unless it's niluuid which is
338 // just a placeholder meaning "use the UUID from the file"
339 if( sheetInfo.uuid != niluuid )
340 {
341 const_cast<KIID&>( sheet->m_Uuid ) = sheetInfo.uuid;
342 }
343
344 sheet->SetName( sheetInfo.name );
345 loadedSheets.push_back( sheet );
346
347 wxLogTrace( tracePathsAndFiles, wxS( "Loaded top-level sheet '%s' (UUID %s) from %s" ),
348 sheet->GetName(), sheet->m_Uuid.AsString(), sheetPath );
349 }
350 }
351
352 if( !loadedSheets.empty() )
353 {
354 newSchematic->SetTopLevelSheets( loadedSheets );
355 }
356 else
357 {
358 wxLogTrace( tracePathsAndFiles,
359 wxS( "Loaded multi-root schematic with no top-level sheets!" ) );
360 newSchematic->CreateDefaultScreens();
361 }
362 }
363 else
364 {
365 // Legacy single-root format: Load the single root sheet
366 SCH_SHEET* rootSheet = pi->LoadSchematicFile( fullFileName, newSchematic.get() );
367
368 if( rootSheet )
369 {
370 newSchematic->SetTopLevelSheets( { rootSheet } );
371
372 // Make ${SHEETNAME} work on the root sheet until we properly support
373 // naming the root sheet
374 if( SCH_SHEET* topSheet = newSchematic->GetTopLevelSheet() )
375 topSheet->SetName( _( "Root" ) );
376
377 wxLogTrace( tracePathsAndFiles, wxS( "Loaded schematic with root sheet UUID %s" ),
378 rootSheet->m_Uuid.AsString() );
379 wxLogTrace( traceSchCurrentSheet,
380 wxS( "After loading: Current sheet path='%s', size=%zu, empty=%d" ),
381 newSchematic->CurrentSheet().Path().AsString(),
382 newSchematic->CurrentSheet().size(),
383 newSchematic->CurrentSheet().empty() ? 1 : 0 );
384 }
385 else
386 {
387 newSchematic->CreateDefaultScreens();
388 }
389
390 }
391 }
392
393 if( !pi->GetError().IsEmpty() )
394 {
395 DisplayErrorMessage( this, _( "The entire schematic could not be loaded. Errors "
396 "occurred attempting to load hierarchical sheets." ),
397 pi->GetError() );
398 }
399 }
400 catch( const FUTURE_FORMAT_ERROR& ffe )
401 {
402 newSchematic->CreateDefaultScreens();
403 msg.Printf( _( "Error loading schematic '%s'." ), fullFileName );
404 progressReporter.Hide();
405 DisplayErrorMessage( this, msg, ffe.Problem() );
406
407 failedLoad = true;
408 }
409 catch( const IO_ERROR& ioe )
410 {
411 newSchematic->CreateDefaultScreens();
412 msg.Printf( _( "Error loading schematic '%s'." ), fullFileName );
413 progressReporter.Hide();
414 DisplayErrorMessage( this, msg, ioe.What() );
415
416 failedLoad = true;
417 }
418 catch( const std::bad_alloc& )
419 {
420 newSchematic->CreateDefaultScreens();
421 msg.Printf( _( "Memory exhausted loading schematic '%s'." ), fullFileName );
422 progressReporter.Hide();
423 DisplayErrorMessage( this, msg, wxEmptyString );
424
425 failedLoad = true;
426 }
427
428 SetSchematic( newSchematic.release() );
429
430 // This fixes a focus issue after the progress reporter is done on GTK. It shouldn't
431 // cause any issues on macOS and Windows. If it does, it will have to be conditionally
432 // compiled.
433 Raise();
434
435 if( failedLoad )
436 {
437 // Do not leave g_RootSheet == NULL because it is expected to be
438 // a valid sheet. Therefore create a dummy empty root sheet and screen.
441
442 // Show any messages collected before the failure
443 if( KISTATUSBAR* statusBar = dynamic_cast<KISTATUSBAR*>( GetStatusBar() ) )
444 statusBar->AddWarningMessages( "load", loadReporter.GetMessages() );
445
446 msg.Printf( _( "Failed to load '%s'." ), fullFileName );
447 SetMsgPanel( wxEmptyString, msg );
448
449 return false;
450 }
451
452 // Load project settings after schematic has been set up with the project link, since this will
453 // update some of the needed schematic settings such as drawing defaults
455
456 SCH_SHEET_LIST sheetList = Schematic().Hierarchy();
457
458 bool repairedPageNumbers = false;
459
460 if( sheetList.AllSheetPageNumbersEmpty() )
461 sheetList.SetInitialPageNumbers();
462 else
463 repairedPageNumbers = sheetList.RepairPageNumbers();
464
465 // It's possible the schematic parser fixed errors due to bugs, or that we reassigned
466 // duplicate or blank sheet page numbers, so warn the user that the schematic has been
467 // fixed (modified).
468 if( sheetList.IsModified() || repairedPageNumbers )
469 {
470 DisplayInfoMessage( this, _( "An error was found when loading the schematic that has been "
471 "automatically fixed. Please save the schematic to repair the "
472 "broken file or it may not be usable with other versions of "
473 "KiCad." ) );
474 }
475
476 UpdateFileHistory( fullFileName );
477
478 if( KISTATUSBAR* statusBar = dynamic_cast<KISTATUSBAR*>( GetStatusBar() ) )
479 statusBar->AddWarningMessages( "load", loadReporter.GetMessages() );
480
481 SCH_SCREENS schematic( Schematic().Root() );
482
483 // LIB_ID checks and symbol rescue only apply to the legacy file formats.
484 if( schFileType == SCH_IO_MGR::SCH_LEGACY )
485 {
486 // Convert any legacy bus-bus entries to just be bus wires
487 for( SCH_SCREEN* screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )
488 {
489 std::vector<SCH_ITEM*> deleted;
490
491 for( SCH_ITEM* item : screen->Items() )
492 {
493 if( item->Type() == SCH_BUS_BUS_ENTRY_T )
494 {
495 SCH_BUS_BUS_ENTRY* entry = static_cast<SCH_BUS_BUS_ENTRY*>( item );
496 std::unique_ptr<SCH_LINE> wire = std::make_unique<SCH_LINE>();
497
498 wire->SetLayer( LAYER_BUS );
499 wire->SetStartPoint( entry->GetPosition() );
500 wire->SetEndPoint( entry->GetEnd() );
501
502 screen->Append( wire.release() );
503 deleted.push_back( item );
504 }
505 }
506
507 for( SCH_ITEM* item : deleted )
508 screen->Remove( item );
509 }
510
511
512 // Convert old projects over to use symbol library table.
513 if( schematic.HasNoFullyDefinedLibIds() )
514 {
515 DIALOG_SYMBOL_REMAP dlgRemap( this );
516
517 dlgRemap.ShowQuasiModal();
518 }
519 else
520 {
521 // Double check to ensure no legacy library list entries have been
522 // added to the project file symbol library list.
523 wxString paths;
524 wxArrayString libNames;
525
526 LEGACY_SYMBOL_LIBS::GetLibNamesAndPaths( &Prj(), &paths, &libNames );
527
528 if( !libNames.IsEmpty() )
529 {
530 if( eeconfig()->m_Appearance.show_illegal_symbol_lib_dialog )
531 {
532 wxRichMessageDialog dlg( this,
533 _( "Illegal entry found in project file symbol library list." ),
534 _( "Project Load Warning" ),
535 wxOK | wxCENTER | wxICON_EXCLAMATION );
536 dlg.ShowDetailedText( _( "Symbol libraries defined in the project file symbol library "
537 "list are no longer supported and will be removed.\n\n"
538 "This may cause broken symbol library links under certain "
539 "conditions." ) );
540 dlg.ShowCheckBox( _( "Do not show this dialog again." ) );
541 dlg.ShowModal();
542 eeconfig()->m_Appearance.show_illegal_symbol_lib_dialog = !dlg.IsCheckBoxChecked();
543 }
544
545 libNames.Clear();
546 paths.Clear();
547 LEGACY_SYMBOL_LIBS::SetLibNamesAndPaths( &Prj(), paths, libNames );
548 }
549
550 // Check for cache file
551 wxFileName cacheFn( fullFileName );
552 cacheFn.SetName( cacheFn.GetName() + "-cache" );
554 bool cacheExists = cacheFn.FileExists();
555
556 if( cacheExists )
557 {
559 std::optional<LIBRARY_TABLE*> table = adapter->ProjectTable();
560
561 if( table && *table )
562 {
563 wxString nickname = Prj().GetProjectName() + "-cache";
564
565 if( !(*table)->HasRow( nickname ) )
566 {
567 LIBRARY_TABLE_ROW& row = (*table)->InsertRow();
568 row.SetNickname( nickname );
569 row.SetURI( cacheFn.GetFullPath() );
570 row.SetType( SCH_IO_MGR::ShowType( SCH_IO_MGR::SCH_LEGACY ) );
571 row.SetDescription( _( "Legacy project cache library" ) );
572 (*table)->Save();
573 }
574
575 std::vector<KI_ERROR> libErrors;
576
577 ReconcileLegacyCacheSymbols( *adapter, nickname, schematic, libErrors );
578
579 if( KISTATUSBAR* statusBar = dynamic_cast<KISTATUSBAR*>( GetStatusBar() ) )
580 statusBar->AddWarningMessages( "load", libErrors );
581 }
582 }
583
584 if( ( !cfg || !cfg->m_RescueNeverShow ) && !cacheExists )
585 {
587 editor->RescueSymbolLibTableProject( false );
588 }
589 }
590
591 // Ensure there is only one legacy library loaded and that it is the cache library.
592 LEGACY_SYMBOL_LIBS* legacyLibs = PROJECT_SCH::LegacySchLibs( &Schematic().Project() );
593
594 if( legacyLibs->GetLibraryCount() == 0 )
595 {
596 wxFileName cacheFn = pro;
597
598 wxLogTrace( traceAutoSave, "[SetName dbg] cacheFn BEFORE path='%s' name='%s' full='%s' arg='%s'",
599 cacheFn.GetPath(), cacheFn.GetName(), cacheFn.GetFullPath(), cacheFn.GetName() + "-cache" );
600 cacheFn.SetName( cacheFn.GetName() + "-cache" );
601 wxLogTrace( traceAutoSave, "[SetName dbg] cacheFn AFTER path='%s' name='%s' full='%s'",
602 cacheFn.GetPath(), cacheFn.GetName(), cacheFn.GetFullPath() );
604
605 msg.Printf( _( "The project symbol library cache file '%s' was not found." ), cacheFn.GetFullName() );
606
607 KICAD_MESSAGE_DIALOG dlg( this, msg, _( "Warning" ), wxOK | wxCANCEL | wxICON_EXCLAMATION | wxCENTER );
608 dlg.SetExtendedMessage( _( "This can result in a broken schematic under certain conditions. If the "
609 "schematic does not have any missing symbols upon opening, save it "
610 "immediately before making any changes to prevent data loss. If there "
611 "are missing symbols, either manual recovery of the schematic or "
612 "recovery of the symbol cache library file and reloading the schematic is "
613 "required." ) );
614 dlg.SetOKCancelLabels( KICAD_MESSAGE_DIALOG::ButtonLabel( _( "Load Without Cache File" ) ),
615 KICAD_MESSAGE_DIALOG::ButtonLabel( _( "Abort" ) ) );
616
617 if( dlg.ShowModal() == wxID_CANCEL )
618 {
619 Schematic().Reset();
621 return false;
622 }
623 }
624
625 // Update all symbol library links for all sheets.
626 schematic.UpdateSymbolLinks( &loadReporter );
627
628 m_infoBar->RemoveAllButtons();
629 m_infoBar->AddCloseButton();
630 m_infoBar->ShowMessage( _( "This file was created by an older version of KiCad. "
631 "It will be converted to the new format when saved." ),
632 wxICON_WARNING, WX_INFOBAR::MESSAGE_TYPE::OUTDATED_SAVE );
633
634 // Legacy schematic can have duplicate time stamps so fix that before converting
635 // to the s-expression format.
636 schematic.ReplaceDuplicateTimeStamps();
637
638 for( SCH_SCREEN* screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )
639 screen->FixLegacyPowerSymbolMismatches();
640
641 // Allow the schematic to be saved to new file format without making any edits.
642 OnModify();
643 }
644 else // S-expression schematic.
645 {
646 SCH_SCREEN* first_screen = schematic.GetFirst();
647
648 // Skip the first screen as it is a virtual root with no version info.
649 if( first_screen && first_screen->GetFileFormatVersionAtLoad() == 0 )
650 first_screen = schematic.GetNext();
651
652 if( first_screen && first_screen->GetFileFormatVersionAtLoad() < SEXPR_SCHEMATIC_FILE_VERSION )
653 {
654 m_infoBar->RemoveAllButtons();
655 m_infoBar->AddCloseButton();
656 m_infoBar->ShowMessage( _( "This file was created by an older version of KiCad. "
657 "It will be converted to the new format when saved." ),
658 wxICON_WARNING, WX_INFOBAR::MESSAGE_TYPE::OUTDATED_SAVE );
659 }
660
661 for( SCH_SCREEN* screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )
663
664 SCH_SCREEN* rootScreen = Schematic().RootScreen();
665
666 // Restore all of the loaded symbol and sheet instances from the root sheet.
667 if( rootScreen && rootScreen->GetFileFormatVersionAtLoad() < 20221002 )
668 sheetList.UpdateSymbolInstanceData( rootScreen->GetSymbolInstances() );
669
670 if( rootScreen && rootScreen->GetFileFormatVersionAtLoad() < 20221110 )
671 sheetList.UpdateSheetInstanceData( rootScreen->GetSheetInstances());
672
673 if( rootScreen && rootScreen->GetFileFormatVersionAtLoad() < 20230221 )
674 for( SCH_SCREEN* screen = schematic.GetFirst(); screen;
675 screen = schematic.GetNext() )
676 screen->FixLegacyPowerSymbolMismatches();
677
678 for( SCH_SCREEN* screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )
679 screen->MigrateSimModels();
680
682 UpdateVariantSelectionCtrl( Schematic().GetVariantNamesForUI() );
683 }
684
685 // After the schematic is successfully loaded, we load the drawing sheet.
686 // This allows us to use the drawing sheet embedded in the schematic (if any)
687 // instead of the default one.
689
690 wxLogTrace( traceSchCurrentSheet, "Before CheckForMissingSymbolInstances: Current sheet path='%s', size=%zu",
691 GetCurrentSheet().Path().AsString(), GetCurrentSheet().size() );
692
693 // Check must run before pruning so variant data on a stale instance path is migrated
694 // onto the new instance before the orphan is removed.
695 sheetList.CheckForMissingSymbolInstances( Prj().GetProjectName() );
696
697 schematic.PruneOrphanedSymbolInstances( Prj().GetProjectName(), sheetList );
698 schematic.PruneOrphanedSheetInstances( Prj().GetProjectName(), sheetList );
699
701
702 SetScreen( GetCurrentSheet().LastScreen() );
703
704 // Repaired page numbers changed in-memory sheet instances; flag the schematic so the
705 // fixed numbering can be saved instead of silently reverting on the next load.
706 if( repairedPageNumbers )
707 OnModify();
708
709 wxLogTrace( traceSchCurrentSheet, "After SetScreen: Current sheet path='%s', size=%zu",
710 GetCurrentSheet().Path().AsString(), GetCurrentSheet().size() );
711
712 // Older files can omit implied junctions. Repair them before publishing connectivity;
713 // repairing current files could instead connect an intentional wire crossing.
714 const bool nativeNeedsFixup = schFileType == SCH_IO_MGR::SCH_KICAD
717
718 if( schFileType == SCH_IO_MGR::SCH_LEGACY || nativeNeedsFixup )
720
721 SCH_COMMIT dummy( this );
722
723 progressReporter.Report( _( "Updating connections..." ) );
724 progressReporter.KeepRefreshing();
725
726 RecalculateConnections( &dummy, GLOBAL_CLEANUP, &progressReporter );
727 dummy.Push( _( "Schematic Cleanup" ), SKIP_UNDO | SKIP_SET_DIRTY | SKIP_CONNECTIVITY | DELETE_REMOVED_ITEMS );
728
729 // Migrate conflicting bus definitions, but only for files old enough to store them.
730 // The connection graph must be rebuilt first so GetBusesNeedingMigration() can see the
731 // conflicting subgraphs; the earlier Reset() clears them.
732 SCH_SCREEN* schRootScreen = Schematic().RootScreen();
733 int schFileVersion = schRootScreen ? schRootScreen->GetFileFormatVersionAtLoad() : 0;
734
736 schFileVersion, Schematic().ConnectionGraph()->GetBusesNeedingMigration().size() ) )
737 {
738 progressReporter.Hide();
739
740 // The rebuild below frees the subgraphs the dialog caches, and a progress update can
741 // dispatch queued UI events into its still-bound handlers, so destroy the dialog first.
742 {
743 DIALOG_MIGRATE_BUSES dlg( this );
744 dlg.ShowQuasiModal();
745 }
746
747 OnModify();
748 progressReporter.Show();
749
750 // Relabeling the conflicting buses changes connectivity, so rebuild it.
751 RecalculateConnections( &dummy, GLOBAL_CLEANUP, &progressReporter );
752 dummy.Push( _( "Schematic Cleanup" ),
754 }
755
756 if( schematic.HasSymbolFieldNamesWithWhiteSpace() )
757 {
758 m_infoBar->QueueShowMessage( _( "This schematic contains symbols that have leading "
759 "and/or trailing white space field names." ),
760 wxICON_WARNING );
761 }
762 }
763
764 // Load any exclusions from the project file
766
769
772
773 SyncView();
775
777
778 UpdateHierarchyNavigator( false, true );
779 CallAfter(
780 [this]()
781 {
784 } );
785
786 wxCommandEvent changedEvt( EDA_EVT_SCHEMATIC_CHANGED );
787 ProcessEventLocally( changedEvt );
788
789 if( !differentProject )
790 {
791 // If we didn't reload the project, we still need to call ProjectChanged() to ensure
792 // frame-specific initialization happens (like registering the autosave saver).
793 // When running under the project manager, KIWAY::ProjectChanged() was called before
794 // this frame existed, so we need to call our own ProjectChanged() now.
796 }
797
798 for( wxEvtHandler* listener : m_schematicChangeListeners )
799 {
800 wxCHECK2( listener, continue );
801
802 // Use the windows variant when handling event messages in case there is any special
803 // event handler pre and/or post processing specific to windows.
804 wxWindow* win = dynamic_cast<wxWindow*>( listener );
805
806 if( win )
807 win->HandleWindowEvent( e );
808 else
809 listener->SafelyProcessEvent( e );
810 }
811
812 updateTitle();
813 m_toolManager->GetTool<SCH_NAVIGATE_TOOL>()->ResetHistory();
814
815 wxFileName fn = Prj().AbsolutePath( GetScreen()->GetFileName() );
816
817 if( fn.FileExists() && !fn.IsFileWritable() )
818 {
819 m_infoBar->RemoveAllButtons();
820 m_infoBar->AddCloseButton();
821 m_infoBar->ShowMessage( _( "Schematic is read only." ), wxICON_WARNING,
822 WX_INFOBAR::MESSAGE_TYPE::OUTDATED_SAVE );
823 }
824
825#ifdef PROFILE
826 openFiles.Show();
827#endif
828 // Ensure all items are redrawn (especially the drawing-sheet items):
829 if( GetCanvas() )
830 GetCanvas()->DisplaySheet( GetCurrentSheet().LastScreen() );
831
832 // Trigger a library load to handle any project-specific libraries
833 CallAfter(
834 [&]()
835 {
836 KIFACE *schface = Kiway().KiFACE( KIWAY::FACE_SCH );
837 schface->PreloadLibraries( &Kiway() );
838
840 } );
841
842 m_remoteSymbolPane->BindWebViewLoaded();
843
844 return true;
845}
846
847
849{
850 if( Schematic().RootScreen() && !Schematic().RootScreen()->Items().empty() )
851 {
852 wxString msg = _( "This operation replaces the contents of the current schematic, "
853 "which will be permanently lost.\n\n"
854 "Do you want to proceed?" );
855
856 if( !IsOK( this, msg ) )
857 return;
858 }
859
860 // Set the project location if none is set or if we are running in standalone mode
861 bool setProject = Prj().GetProjectFullName().IsEmpty() || Kiface().IsSingle();
862 wxString path = wxPathOnly( Prj().GetProjectFullName() );
863
864 wxString fileFiltersStr;
865 wxString allWildcardsStr;
866
867 for( const SCH_IO_MGR::SCH_FILE_T& fileType : SCH_IO_MGR::SCH_FILE_T_vector )
868 {
869 if( fileType == SCH_IO_MGR::SCH_KICAD || fileType == SCH_IO_MGR::SCH_LEGACY )
870 continue; // this is "Import non-KiCad schematic"
871
872 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( fileType ) );
873
874 if( !pi )
875 continue;
876
877 const IO_BASE::IO_FILE_DESC& desc = pi->GetSchematicFileDesc();
878
879 if( desc.m_FileExtensions.empty() || !desc.m_CanRead )
880 continue;
881
882 if( !fileFiltersStr.IsEmpty() )
883 fileFiltersStr += wxChar( '|' );
884
885 fileFiltersStr += desc.FileFilter();
886
887 for( const std::string& ext : desc.m_FileExtensions )
888 allWildcardsStr << wxS( "*." ) << formatWildcardExt( ext ) << wxS( ";" );
889 }
890
891 fileFiltersStr = _( "All supported formats" ) + wxS( "|" ) + allWildcardsStr + wxS( "|" ) + fileFiltersStr;
892
893 wxFileDialog dlg( this, _( "Import Schematic" ), path, wxEmptyString, fileFiltersStr,
894 wxFD_OPEN | wxFD_FILE_MUST_EXIST );
895
896 FILEDLG_IMPORT_NON_KICAD importOptions( eeconfig()->m_System.show_import_issues );
897 dlg.SetCustomizeHook( importOptions );
898
900
901 if( dlg.ShowModal() == wxID_CANCEL )
902 return;
903
905
906 // Don't leave dangling pointers to previously-opened document.
907 m_toolManager->GetTool<SCH_SELECTION_TOOL>()->ClearSelection();
910
911 if( setProject )
912 {
913 Schematic().SetProject( nullptr );
914 GetSettingsManager()->UnloadProject( &Prj(), false );
915
916 // Clear view before destroying schematic as repaints depend on schematic being valid
917 SetScreen( nullptr );
918
919 Schematic().Reset();
920
921 wxFileName projectFn( dlg.GetPath() );
922 projectFn.SetExt( FILEEXT::ProjectFileExtension );
923 GetSettingsManager()->LoadProject( projectFn.GetFullPath() );
924 }
925
926 wxFileName fn = dlg.GetPath();
927
928 if( !fn.IsFileReadable() )
929 {
930 wxLogError( _( "Insufficient permissions to read file '%s'." ), fn.GetFullPath() );
931 return;
932 }
933
934 SCH_IO_MGR::SCH_FILE_T pluginType = SCH_IO_MGR::SCH_FILE_T::SCH_FILE_UNKNOWN;
935
936 for( const SCH_IO_MGR::SCH_FILE_T& fileType : SCH_IO_MGR::SCH_FILE_T_vector )
937 {
938 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( fileType ) );
939
940 if( !pi )
941 continue;
942
943 if( pi->CanReadSchematicFile( fn.GetFullPath() ) )
944 {
945 pluginType = fileType;
946 break;
947 }
948 }
949
950 if( pluginType == SCH_IO_MGR::SCH_FILE_T::SCH_FILE_UNKNOWN )
951 {
952 wxLogError( _( "No loader can read the specified file: '%s'." ), fn.GetFullPath() );
954 SetScreen( Schematic().RootScreen() );
955 return;
956 }
957
958 importFile( dlg.GetPath(), pluginType );
959
961}
962
963
964bool SCH_EDIT_FRAME::saveSchematicFile( SCH_SHEET* aSheet, const wxString& aSavePath, bool isSaveAs )
965{
966 wxString msg;
967 wxFileName schematicFileName;
968 wxFileName oldFileName;
969 bool success;
970
971 SCH_SCREEN* screen = aSheet->GetScreen();
972
973 wxCHECK( screen, false );
974
975 // Cannot save to nowhere
976 if( aSavePath.IsEmpty() )
977 return false;
978
979 // Construct the name of the file to be saved
980 schematicFileName = Prj().AbsolutePath( aSavePath );
981 oldFileName = schematicFileName;
982
983 // Write through symlinks, don't replace them
984 WX_FILENAME::ResolvePossibleSymlinks( schematicFileName );
985
986 if( !schematicFileName.DirExists() )
987 {
988 if( !wxMkdir( schematicFileName.GetPath() ) )
989 {
990 msg.Printf( _( "Error saving schematic file '%s'.\n%s" ),
991 schematicFileName.GetFullPath(),
992 "Could not create directory: %s" + schematicFileName.GetPath() );
993 DisplayError( this, msg );
994
995 return false;
996 }
997 }
998
999 if( !IsWritable( schematicFileName ) )
1000 return false;
1001
1002 wxFileName projectFile( schematicFileName );
1003
1004 projectFile.SetExt( FILEEXT::ProjectFileExtension );
1005
1006 if( projectFile.FileExists() )
1007 {
1008 // Save various ERC settings, such as violation severities (which may have been edited
1009 // via the ERC dialog as well as the Schematic Setup dialog), ERC exclusions, etc.
1011 }
1012
1013 // Save
1014 wxLogTrace( traceAutoSave, wxS( "Saving file " ) + schematicFileName.GetFullPath() );
1015
1016 if( m_infoBar->GetMessageType() == WX_INFOBAR::MESSAGE_TYPE::OUTDATED_SAVE )
1017 m_infoBar->Dismiss();
1018
1019 SCH_IO_MGR::SCH_FILE_T pluginType = SCH_IO_MGR::GuessPluginTypeFromSchPath( schematicFileName.GetFullPath() );
1020
1021 if( pluginType == SCH_IO_MGR::SCH_FILE_UNKNOWN )
1022 pluginType = SCH_IO_MGR::SCH_KICAD;
1023
1024 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( pluginType ) );
1025
1026 // On Windows, ensure the target file is writeable by clearing problematic attributes like
1027 // hidden or read-only. This can happen when files are synced via cloud services.
1028 if( schematicFileName.FileExists() )
1029 KIPLATFORM::IO::MakeWriteable( schematicFileName.GetFullPath() );
1030
1031 try
1032 {
1033 pi->SaveSchematicFile( schematicFileName.GetFullPath(), aSheet, &Schematic() );
1034 success = true;
1035 }
1036 catch( const IO_ERROR& ioe )
1037 {
1038 msg.Printf( _( "Error saving schematic file '%s'.\n%s" ),
1039 schematicFileName.GetFullPath(),
1040 ioe.What() );
1041 DisplayError( this, msg );
1042
1043 success = false;
1044 }
1045
1046 if( success )
1047 {
1048 if( !isSaveAs )
1049 screen->SetContentModified( false );
1050
1051 msg.Printf( _( "File '%s' saved." ), screen->GetFileName() );
1052 SetStatusText( msg, 0 );
1053 }
1054
1055 return success;
1056}
1057
1058
1059bool PrepareSaveAsFiles( SCHEMATIC& aSchematic, SCH_SCREENS& aScreens, const wxFileName& aOldRoot,
1060 const wxFileName& aNewRoot, bool aSaveCopy, bool aCopySubsheets, bool aIncludeExternSheets,
1061 std::unordered_map<SCH_SCREEN*, wxString>& aFilenameMap, wxString& aErrorMsg )
1062{
1063 SCH_SCREEN* screen;
1064
1065 for( size_t i = 0; i < aScreens.GetCount(); i++ )
1066 {
1067 screen = aScreens.GetScreen( i );
1068
1069 wxCHECK2( screen, continue );
1070
1071 if( screen == aSchematic.RootScreen() )
1072 continue;
1073
1074 // The virtual root's screen only holds the top-level sheets and has no file of its own
1075 // A destination here would be a nameless path that callers turn into a stray ".kicad_sch"
1076 if( screen == aSchematic.Root().GetScreen() )
1077 continue;
1078
1079 wxFileName src = screen->GetFileName();
1080
1081 if( !src.IsAbsolute() )
1082 src.MakeAbsolute( aOldRoot.GetPath() );
1083
1084 bool internalSheet = src.GetPath().StartsWith( aOldRoot.GetPath() );
1085
1086 if( aCopySubsheets && ( internalSheet || aIncludeExternSheets ) )
1087 {
1088 wxFileName dest = src;
1089
1090 if( internalSheet && dest.MakeRelativeTo( aOldRoot.GetPath() ) )
1091 dest.MakeAbsolute( aNewRoot.GetPath() );
1092 else
1093 dest.Assign( aNewRoot.GetPath(), dest.GetFullName() );
1094
1095 wxLogTrace( tracePathsAndFiles, wxS( "Moving schematic from '%s' to '%s'." ),
1096 screen->GetFileName(), dest.GetFullPath() );
1097
1098 if( !dest.DirExists() && !dest.Mkdir() )
1099 {
1100 aErrorMsg.Printf( _( "Folder '%s' could not be created.\n\n"
1101 "Make sure you have write permissions and try again." ),
1102 dest.GetPath() );
1103 return false;
1104 }
1105
1106 if( aSaveCopy )
1107 aFilenameMap[screen] = dest.GetFullPath();
1108 else
1109 screen->SetFileName( dest.GetFullPath() );
1110 }
1111 else
1112 {
1113 if( aSaveCopy )
1114 aFilenameMap[screen] = wxString();
1115
1116 screen->SetFileName( src.GetFullPath() );
1117 }
1118 }
1119
1120 for( SCH_SHEET_PATH& sheet : aSchematic.Hierarchy() )
1121 {
1122 if( !sheet.Last()->IsTopLevelSheet() )
1123 sheet.MakeFilePathRelativeToParentSheet();
1124 }
1125
1126 return true;
1127}
1128
1130{
1131 wxString msg;
1132 SCH_SCREEN* screen;
1133 SCH_SCREENS screens( Schematic().Root() );
1134 bool saveCopy = aSaveAs && !Kiface().IsSingle();
1135 bool success = true;
1136 bool updateFileHistory = false;
1137 bool createNewProject = false;
1138 bool copySubsheets = false;
1139 bool includeExternSheets = false;
1140
1141 // I want to see it in the debugger, show me the string! Can't do that with wxFileName.
1142 wxString fileName = Prj().AbsolutePath( Schematic().Root().GetFileName() );
1143 wxFileName fn = fileName;
1144
1145 // Path to save each screen to: will be the stored filename by default, but is overwritten by
1146 // a Save As Copy operation.
1147 std::unordered_map<SCH_SCREEN*, wxString> filenameMap;
1148
1149 // Handle "Save As" and saving a new project/schematic for the first time in standalone
1150 if( Prj().IsNullProject() || aSaveAs )
1151 {
1152 // Null project should only be possible in standalone mode.
1153 wxCHECK( Kiface().IsSingle() || aSaveAs, false );
1154
1155 wxFileName newFileName;
1156 wxFileName savePath( Prj().GetProjectFullName() );
1157
1158 if( !savePath.IsOk() || !savePath.IsDirWritable() )
1159 {
1160 savePath = GetMruPath();
1161
1162 if( !savePath.IsOk() || !savePath.IsDirWritable() )
1164 }
1165
1166 if( savePath.HasExt() )
1167 savePath.SetExt( FILEEXT::KiCadSchematicFileExtension );
1168 else
1169 savePath.SetName( wxEmptyString );
1170
1171 wxFileDialog dlg( this, _( "Schematic Files" ), savePath.GetPath(), savePath.GetFullName(),
1172 FILEEXT::KiCadSchematicFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1173
1174 FILEDLG_HOOK_SAVE_PROJECT newProjectHook;
1175
1176 // Add a "Create a project" checkbox in standalone mode and one isn't loaded
1177 if( Kiface().IsSingle() || aSaveAs )
1178 {
1179 dlg.SetCustomizeHook( newProjectHook );
1180 }
1181
1183
1184 if( dlg.ShowModal() == wxID_CANCEL )
1185 return false;
1186
1187 newFileName = EnsureFileExtension( dlg.GetPath(), FILEEXT::KiCadSchematicFileExtension );
1188
1189 if( ( !newFileName.DirExists() && !newFileName.Mkdir() )
1190 || !newFileName.IsDirWritable() )
1191 {
1192 msg.Printf( _( "Folder '%s' could not be created.\n\n"
1193 "Make sure you have write permissions and try again." ),
1194 newFileName.GetPath() );
1195
1196 KICAD_MESSAGE_DIALOG dlgBadPath( this, msg, _( "Error" ), wxOK | wxICON_EXCLAMATION | wxCENTER );
1197
1198 dlgBadPath.ShowModal();
1199 return false;
1200 }
1201
1202 if( newProjectHook.IsAttachedToDialog() )
1203 {
1204 createNewProject = newProjectHook.GetCreateNewProject();
1205 copySubsheets = newProjectHook.GetCopySubsheets();
1206 includeExternSheets = newProjectHook.GetIncludeExternSheets();
1207 }
1208
1209 if( !saveCopy )
1210 {
1211 Schematic().Root().SetFileName( newFileName.GetFullName() );
1212 Schematic().RootScreen()->SetFileName( newFileName.GetFullPath() );
1213 updateFileHistory = true;
1214 }
1215 else
1216 {
1217 filenameMap[Schematic().RootScreen()] = newFileName.GetFullPath();
1218 }
1219
1220 if( !PrepareSaveAsFiles( Schematic(), screens, fn, newFileName, saveCopy, copySubsheets, includeExternSheets,
1221 filenameMap, msg ) )
1222 {
1223 KICAD_MESSAGE_DIALOG dlgBadFilePath( this, msg, _( "Error" ), wxOK | wxICON_EXCLAMATION | wxCENTER );
1224
1225 dlgBadFilePath.ShowModal();
1226 return false;
1227 }
1228 }
1229 else if( !fn.FileExists() )
1230 {
1231 // File doesn't exist yet; true if we just imported something
1232 updateFileHistory = true;
1233 }
1234 else if( screens.GetFirst() && screens.GetFirst()->GetFileFormatVersionAtLoad() < SEXPR_SCHEMATIC_FILE_VERSION )
1235 {
1236 // Allow the user to save un-edited files in new format
1237 }
1238 else if( !IsContentModified() )
1239 {
1240 return true;
1241 }
1242
1243 if( filenameMap.empty() || !saveCopy )
1244 {
1245 for( size_t i = 0; i < screens.GetCount(); i++ )
1246 filenameMap[screens.GetScreen( i )] = screens.GetScreen( i )->GetFileName();
1247 }
1248
1249 // Warn user on potential file overwrite. This can happen on shared sheets.
1250 wxArrayString overwrittenFiles;
1251 wxArrayString lockedFiles;
1252
1253 for( size_t i = 0; i < screens.GetCount(); i++ )
1254 {
1255 screen = screens.GetScreen( i );
1256
1257 wxCHECK2( screen, continue );
1258
1259 // Convert legacy schematics file name extensions for the new format.
1260 wxFileName tmpFn = filenameMap[screen];
1261
1262 if( !tmpFn.IsOk() )
1263 continue;
1264
1265 if( tmpFn.FileExists() && !tmpFn.IsFileWritable() )
1266 lockedFiles.Add( tmpFn.GetFullPath() );
1267
1268 if( tmpFn.GetExt() == FILEEXT::KiCadSchematicFileExtension )
1269 continue;
1270
1272
1273 if( tmpFn.FileExists() )
1274 overwrittenFiles.Add( tmpFn.GetFullPath() );
1275 }
1276
1277 if( !lockedFiles.IsEmpty() )
1278 {
1279 for( const wxString& lockedFile : lockedFiles )
1280 {
1281 if( msg.IsEmpty() )
1282 msg = lockedFile;
1283 else
1284 msg += "\n" + lockedFile;
1285 }
1286
1287 wxRichMessageDialog dlg( this, wxString::Format( _( "Failed to save %s." ), Schematic().Root().GetFileName() ),
1288 _( "Locked File Warning" ), wxOK | wxICON_WARNING | wxCENTER );
1289 dlg.SetExtendedMessage( _( "You do not have write permissions to:\n\n" ) + msg );
1290
1291 dlg.ShowModal();
1292 return false;
1293 }
1294
1295 if( !overwrittenFiles.IsEmpty() )
1296 {
1297 for( const wxString& overwrittenFile : overwrittenFiles )
1298 {
1299 if( msg.IsEmpty() )
1300 msg = overwrittenFile;
1301 else
1302 msg += "\n" + overwrittenFile;
1303 }
1304
1305 wxRichMessageDialog dlg( this, _( "Saving will overwrite existing files." ), _( "Save Warning" ),
1306 wxOK | wxCANCEL | wxCANCEL_DEFAULT | wxCENTER | wxICON_EXCLAMATION );
1307 dlg.ShowDetailedText( _( "The following files will be overwritten:\n\n" ) + msg );
1308 dlg.SetOKCancelLabels( KICAD_MESSAGE_DIALOG::ButtonLabel( _( "Overwrite Files" ) ),
1309 KICAD_MESSAGE_DIALOG::ButtonLabel( _( "Abort Project Save" ) ) );
1310
1311 if( dlg.ShowModal() == wxID_CANCEL )
1312 return false;
1313 }
1314
1315 screens.BuildClientSheetPathList();
1316
1317 std::vector<wxString> savedSheetPaths;
1318
1319 for( size_t i = 0; i < screens.GetCount(); i++ )
1320 {
1321 screen = screens.GetScreen( i );
1322
1323 wxCHECK2( screen, continue );
1324
1325 // Convert legacy schematics file name extensions for the new format.
1326 wxFileName tmpFn = filenameMap[screen];
1327
1328 if( tmpFn.IsOk() && tmpFn.GetExt() != FILEEXT::KiCadSchematicFileExtension )
1329 {
1330 updateFileHistory = true;
1332
1333 for( EDA_ITEM* item : screen->Items().OfType( SCH_SHEET_T ) )
1334 {
1335 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
1336 wxFileName sheetFileName = sheet->GetFileName();
1337
1338 if( !sheetFileName.IsOk() || sheetFileName.GetExt() == FILEEXT::KiCadSchematicFileExtension )
1339 continue;
1340
1341 sheetFileName.SetExt( FILEEXT::KiCadSchematicFileExtension );
1342 sheet->SetFileName( sheetFileName.GetFullPath() );
1343 UpdateItem( sheet );
1344 }
1345
1346 filenameMap[screen] = tmpFn.GetFullPath();
1347
1348 if( !saveCopy )
1349 screen->SetFileName( tmpFn.GetFullPath() );
1350 }
1351
1352 // Do not save sheet symbols with no valid filename set
1353 if( !tmpFn.IsOk() )
1354 continue;
1355
1356 std::vector<SCH_SHEET_PATH>& sheets = screen->GetClientSheetPaths();
1357
1358 if( sheets.size() == 1 )
1359 screen->SetVirtualPageNumber( 1 );
1360 else
1361 screen->SetVirtualPageNumber( 0 ); // multiple uses; no way to store the real sheet #
1362
1363 // This is a new schematic file so make sure it has a unique ID.
1364 if( !saveCopy && tmpFn.GetFullPath() != screen->GetFileName() )
1365 screen->AssignNewUuid();
1366
1367 bool savedThisSheet = saveSchematicFile( screens.GetSheet( i ), tmpFn.GetFullPath(), false );
1368
1369 if( savedThisSheet )
1370 savedSheetPaths.push_back( tmpFn.GetFullPath() );
1371
1372 success &= savedThisSheet;
1373 }
1374
1375 if( success )
1376 {
1377 if( m_autoSaveTimer )
1378 m_autoSaveTimer->Stop();
1379
1380 m_autoSavePending = false;
1381 m_autoSaveRequired = false;
1382
1383 LockFile( Schematic().RootScreen()->GetFileName() );
1384 }
1385
1386 if( updateFileHistory )
1387 UpdateFileHistory( Schematic().RootScreen()->GetFileName() );
1388
1389 // Save the sheet name map to the project file
1390 std::vector<FILE_INFO_PAIR>& sheets = Prj().GetProjectFile().GetSheets();
1391 sheets.clear();
1392
1393 for( SCH_SHEET_PATH& sheetPath : Schematic().Hierarchy() )
1394 {
1395 SCH_SHEET* sheet = sheetPath.Last();
1396
1397 wxCHECK2( sheet, continue );
1398
1399 // Do not save the virtual root sheet
1400 if( !sheet->IsVirtualRootSheet() )
1401 sheets.emplace_back( std::make_pair( sheet->m_Uuid, sheet->GetName() ) );
1402 }
1403
1404 wxASSERT( filenameMap.count( Schematic().RootScreen() ) );
1405 wxFileName projectPath( filenameMap.at( Schematic().RootScreen() ) );
1406 projectPath.SetExt( FILEEXT::ProjectFileExtension );
1407
1408 if( Prj().IsNullProject() || ( aSaveAs && !saveCopy ) )
1409 {
1410 Prj().SetReadOnly( !createNewProject );
1411 GetSettingsManager()->SaveProjectAs( projectPath.GetFullPath() );
1412 }
1413 else if( saveCopy && createNewProject )
1414 {
1415 GetSettingsManager()->SaveProjectCopy( projectPath.GetFullPath() );
1416 }
1417 else
1418 {
1421 }
1422
1423 // Record a full project snapshot so related files (symbols, libs, sheets) are captured.
1424 // Skip when running standalone without a project loaded - the save path can land
1425 // anywhere on the filesystem and there is no project context for a snapshot.
1426 if( success && !Prj().IsNullProject() )
1427 {
1428 Kiway().LocalHistory().RunRegisteredSaversAndCommit( Prj().GetProjectPath(), wxS( "SCH Save" ), wxS( "sch" ) );
1429
1430 // Drop the autosave files for the sheets we just persisted. Scope to those
1431 // sources so other dirty sheets (and any open PCB) keep their autosaves until
1432 // they are saved themselves; otherwise a Save All across editors would lose
1433 // recovery data for files this save did not write.
1434 // RunRegisteredSaversAndCommit above is a no-op when format is ZIP, and
1435 // RemoveAutosaveFiles is conversely a no-op in INCREMENTAL mode.
1436 Kiway().LocalHistory().RemoveAutosaveFiles( Prj().GetProjectPath(), savedSheetPaths );
1437 }
1438
1439 WX_STRING_REPORTER backupReporter;
1440
1441 if( !Kiface().IsSingle() )
1442 GetSettingsManager()->TriggerBackupIfNeeded( backupReporter );
1443
1444 // Restore the virtual page numbers that were modified during save. When saving, screens are
1445 // assigned page number 1 (single use) or 0 (multiple uses) for serialization purposes.
1446 // We restore all screens here, not just the current one, because other code paths (e.g.
1447 // ERC tree model, temporary sheet switches) may read any screen's virtual page number.
1448 for( const SCH_SHEET_PATH& sheet : Schematic().Hierarchy() )
1449 sheet.LastScreen()->SetVirtualPageNumber( sheet.GetVirtualPageNumber() );
1450
1452
1453 if( GetCanvas() && GetCanvas()->GetView() )
1454 {
1456 GetCanvas()->Refresh();
1457 }
1458
1459 updateTitle();
1460
1461 if( m_infoBar->GetMessageType() == WX_INFOBAR::MESSAGE_TYPE::OUTDATED_SAVE )
1462 m_infoBar->Dismiss();
1463
1464 if( backupReporter.HasMessage() )
1465 {
1466 wxString backupMsg = backupReporter.GetMessages();
1467 m_infoBar->ShowMessageFor( backupMsg.Trim(), 10000, wxICON_WARNING );
1468 }
1469
1470 return success;
1471}
1472
1473
1474bool SCH_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType,
1475 const std::map<std::string, UTF8>* aProperties )
1476{
1477 bool success = true;
1478 wxFileName filename( aFileName );
1479 wxFileName newfilename;
1480 SCH_IO_MGR::SCH_FILE_T fileType = (SCH_IO_MGR::SCH_FILE_T) aFileType;
1481
1482 wxCommandEvent changingEvt( EDA_EVT_SCHEMATIC_CHANGING );
1483 ProcessEventLocally( changingEvt );
1484
1485 if( KISTATUSBAR* statusBar = dynamic_cast<KISTATUSBAR*>( GetStatusBar() ) )
1486 statusBar->ClearWarningMessages( "load" );
1487
1488 WX_STRING_REPORTER loadReporter;
1489 LOAD_INFO_REPORTER_SCOPE loadReporterScope( &loadReporter );
1490
1491 std::unique_ptr<SCHEMATIC> newSchematic = std::make_unique<SCHEMATIC>( &Prj() );
1492
1493 switch( fileType )
1494 {
1495 case SCH_IO_MGR::SCH_ALTIUM:
1496 case SCH_IO_MGR::SCH_CADSTAR_ARCHIVE:
1497 case SCH_IO_MGR::SCH_EAGLE:
1498 case SCH_IO_MGR::SCH_LTSPICE:
1499 case SCH_IO_MGR::SCH_EASYEDA:
1500 case SCH_IO_MGR::SCH_EASYEDAPRO:
1501 case SCH_IO_MGR::SCH_EASYEDAPRO_V3:
1502 case SCH_IO_MGR::SCH_PADS:
1503 case SCH_IO_MGR::SCH_GEDA:
1504 case SCH_IO_MGR::SCH_DIPTRACE:
1505 case SCH_IO_MGR::SCH_PCAD:
1506 case SCH_IO_MGR::SCH_ORCAD:
1507 case SCH_IO_MGR::SCH_EASYPC:
1508 {
1509 // We insist on caller sending us an absolute path, if it does not, we say it's a bug.
1510 // Unless we are passing the files in aproperties, in which case aFileName can be empty.
1511 wxCHECK_MSG( aFileName.IsEmpty() || filename.IsAbsolute(), false,
1512 wxS( "Import schematic: path is not absolute!" ) );
1513
1514 try
1515 {
1516 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( fileType ) );
1517 DIALOG_HTML_REPORTER errorReporter( this );
1518 WX_PROGRESS_REPORTER progressReporter( this, _( "Import Schematic" ), 1, PR_CAN_ABORT );
1519
1520 if( PROJECT_CHOOSER_PLUGIN* c_pi = dynamic_cast<PROJECT_CHOOSER_PLUGIN*>( pi.get() ) )
1521 {
1522 c_pi->RegisterCallback( std::bind( DIALOG_IMPORT_CHOOSE_PROJECT::RunModal,
1523 this, std::placeholders::_1 ) );
1524 }
1525
1526 if( eeconfig()->m_System.show_import_issues )
1527 pi->SetReporter( errorReporter.m_Reporter );
1528 else
1529 pi->SetReporter( &NULL_REPORTER::GetInstance() );
1530
1531 pi->SetProgressReporter( &progressReporter );
1532
1533 SCH_SHEET* loadedSheet = pi->LoadSchematicFile( aFileName, newSchematic.get(), nullptr, aProperties );
1534
1535 SetSchematic( newSchematic.release() );
1536
1538
1539 // SetSchematic() killed the previous schematic's history saver
1540 // So we need a new one for the new schematic
1542
1543 if( loadedSheet )
1544 {
1545 std::vector<SCH_SHEET*> topLevelSheets = Schematic().GetTopLevelSheets();
1546 bool loadedIsTopLevel = std::find( topLevelSheets.begin(), topLevelSheets.end(), loadedSheet )
1547 != topLevelSheets.end();
1548 bool loadedIsVirtualRoot = loadedSheet == &Schematic().Root()
1549 || loadedSheet->IsVirtualRootSheet();
1550
1551 // Some importers create the full top-level sheet set themselves. Do not collapse
1552 // that back to the returned sheet.
1553 if( !loadedIsTopLevel && !loadedIsVirtualRoot )
1554 Schematic().SetTopLevelSheets( { loadedSheet } );
1555
1556 // extract a project symbol library and re-link LIB_IDs so every symbol resolves
1557 ReconcileImportedSymbols( *pi, Schematic(), Prj(), aFileName, aProperties, loadReporter );
1558
1559 // re-link footprint fields to the project lib so update-from-schematic works
1560 {
1561 wxString cacheNick;
1562 std::vector<wxString> sourceFpLibs;
1563 IMPORT_PROJ_PROPS::ReadFootprintProps( aProperties, cacheNick, sourceFpLibs );
1564
1565 SCH_FOOTPRINT_FIELD_RECONCILER fpReconciler( cacheNick, sourceFpLibs, &loadReporter );
1566 fpReconciler.Reconcile( Schematic() );
1567 }
1568
1569 if( errorReporter.m_Reporter->HasMessage() )
1570 {
1571 errorReporter.m_Reporter->Flush(); // Build HTML messages
1572 errorReporter.ShowModal();
1573
1574 // These are not catastrophic errors: leave success set to true so the caller
1575 // doesn't destroy the frame.
1576 }
1577
1578 const wxString drawingSheetName = Schematic().Settings().m_SchDrawingSheetFileName;
1579 const wxString embeddedPrefix = wxS( "kicad-embed://" );
1580 const EMBEDDED_FILES::EMBEDDED_FILE* embeddedWorksheet = nullptr;
1581
1582 if( drawingSheetName.StartsWith( embeddedPrefix ) )
1583 {
1584 wxString sheetFile = drawingSheetName.Mid( embeddedPrefix.length() );
1585 embeddedWorksheet = Schematic().GetEmbeddedFiles()->GetEmbeddedFile( sheetFile );
1586 }
1587
1588 if( !embeddedWorksheet
1590 {
1593 }
1594 else
1595 {
1598 }
1599
1600 newfilename.SetPath( Prj().GetProjectPath() );
1601 newfilename.SetName( Prj().GetProjectName() );
1602 newfilename.SetExt( FILEEXT::KiCadSchematicFileExtension );
1603
1604 SetScreen( Schematic().RootScreen() );
1605
1606 if( SCH_SHEET* topSheet = Schematic().GetTopLevelSheet() )
1607 topSheet->SetFileName( newfilename.GetFullName() );
1608
1609 GetScreen()->SetFileName( newfilename.GetFullPath() );
1611
1612 progressReporter.Report( _( "Updating connections..." ) );
1613
1614 if( !progressReporter.KeepRefreshing() )
1616
1617 RecalculateConnections( nullptr, GLOBAL_CLEANUP, &progressReporter );
1618
1619 // Only perform the dangling end test on root sheet.
1621 }
1622 else
1623 {
1625 }
1626 }
1627 catch( const IO_ERROR& ioe )
1628 {
1629 // Do not leave g_RootSheet == NULL because it is expected to be
1630 // a valid sheet. Therefore create a dummy empty root sheet and screen.
1633
1634 wxString msg = wxString::Format( _( "Error loading schematic '%s'." ), aFileName );
1635 DisplayErrorMessage( this, msg, ioe.What() );
1636
1637 msg.Printf( _( "Failed to load '%s'." ), aFileName );
1638 SetMsgPanel( wxEmptyString, msg );
1639
1640 success = false;
1641 }
1642 catch( const std::exception& exc )
1643 {
1646
1647 wxString msg = wxString::Format( _( "Unhandled exception occurred loading schematic '%s'." ), aFileName );
1648 DisplayErrorMessage( this, msg, exc.what() );
1649
1650 msg.Printf( _( "Failed to load '%s'." ), aFileName );
1651 SetMsgPanel( wxEmptyString, msg );
1652
1653 success = false;
1654 }
1655
1658
1661 SyncView();
1662
1663 UpdateHierarchyNavigator( false, true );
1664 UpdateVariantSelectionCtrl( m_schematic->GetVariantNamesForUI() );
1665 SetCurrentVariant( m_schematic->GetCurrentVariant() );
1666
1667 CallAfter(
1668 [this]()
1669 {
1672 } );
1673
1674 wxCommandEvent e( EDA_EVT_SCHEMATIC_CHANGED );
1675 ProcessEventLocally( e );
1676
1677 for( wxEvtHandler* listener : m_schematicChangeListeners )
1678 {
1679 wxCHECK2( listener, continue );
1680
1681 // Use the windows variant when handling event messages in case there is any
1682 // special event handler pre and/or post processing specific to windows.
1683 wxWindow* win = dynamic_cast<wxWindow*>( listener );
1684
1685 if( win )
1686 win->HandleWindowEvent( e );
1687 else
1688 listener->SafelyProcessEvent( e );
1689 }
1690
1691 updateTitle();
1692
1693 if( KISTATUSBAR* statusBar = dynamic_cast<KISTATUSBAR*>( GetStatusBar() ) )
1694 statusBar->AddWarningMessages( "load", loadReporter.GetMessages() );
1695
1696 break;
1697 }
1698
1699 default:
1700 break;
1701 }
1702
1703 return success;
1704}
1705
1706
1708{
1709 SCH_SCREENS screenList( Schematic().Root() );
1710
1711 // Save any currently open and modified project files.
1712 for( SCH_SCREEN* screen = screenList.GetFirst(); screen; screen = screenList.GetNext() )
1713 {
1714 SIMULATOR_FRAME* simFrame = (SIMULATOR_FRAME*) Kiway().Player( FRAME_SIMULATOR, false );
1715
1716 // Simulator must be closed before loading another schematic, otherwise it may crash.
1717 // If there are any changes in the simulator the user will be prompted to save them.
1718 if( simFrame && !simFrame->Close() )
1719 return false;
1720
1721 if( screen->IsContentModified() )
1722 {
1723 if( !HandleUnsavedChanges( this, _( "The current schematic has been modified. Save changes?" ),
1724 [&]() -> bool
1725 {
1726 return SaveProject();
1727 } ) )
1728 {
1729 return false;
1730 }
1731 }
1732 }
1733
1734 return true;
1735}
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
static TOOL_ACTION zoomFitScreen
Definition actions.h:138
void SetVirtualPageNumber(int aPageNumber)
Definition base_screen.h:72
static wxString m_DrawingSheetFileName
the name of the drawing sheet file, or empty to use the default drawing sheet
Definition base_screen.h:81
void SetContentModified(bool aModified=true)
Definition base_screen.h:55
Class DIALOG_HTML_REPORTER.
WX_HTML_REPORT_BOX * m_Reporter
static std::vector< IMPORT_PROJECT_DESC > RunModal(wxWindow *aParent, const std::vector< IMPORT_PROJECT_DESC > &aProjectDesc)
Create and show a dialog (modal) and returns the data from it after completion.
static bool ShouldPrompt(int aFileFormatVersionAtLoad, size_t aBusesNeedingMigration)
Multiple differently-named labels on a single bus subgraph were only permitted before KiCad 6....
int ShowModal() override
static DS_DATA_MODEL & GetTheInstance()
Return the instance of DS_DATA_MODEL used in the application.
static const wxString & EmptyLayoutName()
Return the reserved drawing-sheet name that marks a design as deliberately having no sheet,...
void LoadWindowState(const wxString &aFileName)
void CheckForAutosaveFiles(const wxString &aProjectPath, const std::vector< wxString > &aExtensions)
Check for autosave files newer than their source files for the given project.
virtual void ClearUndoRedoList()
Clear the undo and redo list using ClearUndoORRedoList()
SETTINGS_MANAGER * GetSettingsManager() const
WX_INFOBAR * m_infoBar
wxTimer * m_autoSaveTimer
void UpdateFileHistory(const wxString &FullFileName, FILE_HISTORY *aFileHistory=nullptr)
Update the list of recently opened files.
wxString GetMruPath() const
bool IsWritable(const wxFileName &aFileName, bool aVerbose=true)
Check if aFileName can be written.
std::unique_ptr< LOCKFILE > m_file_checker
Prevents opening same file multiple times.
void SetMsgPanel(const std::vector< MSG_PANEL_ITEM > &aList)
Clear the message panel and populates it with the contents of aList.
void RefreshCanvas() override
bool LockFile(const wxString &aFileName)
Mark a schematic file as being in use.
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
const KIID m_Uuid
Definition eda_item.h:599
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:248
EMBEDDED_FILE * GetEmbeddedFile(const wxString &aName) const
Returns the embedded file with the given name or nullptr if it does not exist.
bool GetCreateNewProject() const
Gets the selected state of the create new project option.
bool GetCopySubsheets() const
Gets the selected state of the copy subsheets option.
bool IsAttachedToDialog() const
Gets if this hook has attached controls to a dialog box.
bool GetIncludeExternSheets() const
Gets the selected state of the include external sheets option.
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()
virtual const wxString Problem() const
what was the problem?
APP_SETTINGS_BASE * KifaceSettings() const
Definition kiface_base.h:92
bool IsSingle() const
Is this KIFACE running under single_top?
void RefreshDrawingSheetPageInfo()
Update the drawing sheet proxy's page number and first-page flag from the current edit frame state.
Definition sch_view.cpp:235
Definition kiid.h:46
wxString AsString() const
Definition kiid.cpp:264
KISTATUSBAR is a wxStatusBar suitable for Kicad manager.
Definition kistatusbar.h:50
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 KIFACE * KiFACE(FACE_T aFaceId, bool doLoad=true)
Return the KIFACE* given a FACE_T.
Definition kiway.cpp:207
@ FACE_SCH
eeschema DSO
Definition kiway.h:355
LOCAL_HISTORY & LocalHistory()
Return the LOCAL_HISTORY associated with this KIWAY.
Definition kiway.h:459
A collection of LIB_SYMBOL objects.
static void GetLibNamesAndPaths(PROJECT *aProject, wxString *aPaths, wxArrayString *aNames=nullptr)
static void SetLibNamesAndPaths(PROJECT *aProject, const wxString &aPaths, const wxArrayString &aNames)
std::optional< LIBRARY_TABLE * > ProjectTable() const
Retrieves the project library table for this adapter type, or nullopt if one doesn't exist.
void SetNickname(const wxString &aNickname)
void SetType(const wxString &aType)
void SetDescription(const wxString &aDescription)
void SetURI(const wxString &aUri)
bool RunRegisteredSaversAndCommit(const wxString &aProjectPath, const wxString &aTitle, const wxString &aTagFileType=wxEmptyString)
Run all registered savers and, if any staged changes differ from HEAD, create a commit.
void RemoveAutosaveFiles(const wxString &aProjectPath) const
Remove every autosave file under the project at aProjectPath regardless of which source it shadowed.
static REPORTER & GetInstance()
Definition reporter.cpp:207
static wxString GetDefaultUserProjectsPath()
Gets the default path we point users to create projects.
Definition paths.cpp:137
void PreloadDesignBlockLibraries(KIWAY *aKiway)
Starts a background job to preload the global and project design block libraries.
Definition pgm_base.cpp:892
void HideSplash()
Definition pgm_base.cpp:307
A small class to help profiling.
Definition profile.h:46
void Show(std::ostream &aStream=std::cerr)
Print the elapsed time (in a suitable unit) to a stream.
Definition profile.h:104
virtual void Report(const wxString &aMessage) override
Display aMessage in the progress bar dialog.
bool KeepRefreshing(bool aWait=false) override
Update the UI dialog.
Plugin class for import plugins that support choosing a project.
The backing store for a PROJECT, in JSON format.
std::vector< FILE_INFO_PAIR > & GetSheets()
std::vector< TOP_LEVEL_SHEET_INFO > & GetTopLevelSheets()
static SYMBOL_LIBRARY_ADAPTER * SymbolLibAdapter(PROJECT *aProject)
Accessor for project symbol library manager adapter.
static LEGACY_SYMBOL_LIBS * LegacySchLibs(PROJECT *aProject)
Returns the list of symbol libraries from a legacy (pre-5.x) design This is only used from the remapp...
virtual void SetReadOnly(bool aReadOnly=true)
Definition project.h:161
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
Definition project.cpp:177
virtual void SetElem(PROJECT::ELEM aIndex, _ELEM *aElem)
Definition project.cpp:396
virtual const wxString GetProjectName() const
Return the short name of the project.
Definition project.cpp:195
virtual PROJECT_FILE & GetProjectFile() const
Definition project.h:201
virtual const wxString AbsolutePath(const wxString &aFileName) const
Fix up aFileName if it is relative to the project's directory to be an absolute path and filename.
Definition project.cpp:407
@ LEGACY_SYMBOL_LIBS
Definition project.h:70
virtual bool HasMessage() const
Returns true if any messages were reported.
Definition reporter.h:143
Holds all the data relating to one schematic.
Definition schematic.h:149
void Reset()
Initialize this schematic to a blank one, unloading anything existing.
void ResolveERCExclusionsPostUpdate()
Update markers to match recorded exclusions.
void LoadVariants()
This is a throw away method for variant testing.
SCHEMATIC_SETTINGS & Settings() const
SCH_SHEET_LIST Hierarchy() const
Return the full schematic flattened hierarchical sheet list.
void SetProject(PROJECT *aPrj)
EMBEDDED_FILES * GetEmbeddedFiles() override
CONNECTION_GRAPH * ConnectionGraph() const
Definition schematic.h:319
SCH_SCREEN * RootScreen() const
Helper to retrieve the screen of the root sheet.
void SetTopLevelSheets(const std::vector< SCH_SHEET * > &aSheets)
Replace the top level sheets, rebuilding the hierarchy and connectivity around them.
SCH_SHEET & Root() const
Definition schematic.h:200
std::vector< SCH_SHEET * > GetTopLevelSheets() const
Get the list of top-level sheets.
int FixupJunctionsAfterImport(const std::function< void(SCH_LINE *, SCH_LINE *)> &aOnSplit={})
Add junctions to this schematic where required.
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
void SyncView()
Mark all items for refresh.
EESCHEMA_SETTINGS * eeconfig() const
Class for a bus to bus entry.
VECTOR2I GetPosition() const override
VECTOR2I GetEnd() const
Commit for the schematic and symbol editors.
Definition sch_commit.h:54
KIGFX::SCH_VIEW * GetView() const override
Return a pointer to the KIGFX::VIEW instance used in the panel.
void DisplaySheet(SCH_SCREEN *aScreen)
bool IsContentModified() const override
Get if the current schematic has been modified but not saved.
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag and update other data struc...
void SaveProjectLocalSettings() override
Save changes to the project settings to the project (.pro) file.
bool OpenProjectFiles(const std::vector< wxString > &aFileSet, int aCtl=0) override
Open a project or set of files given by aFileList.
NET_NAVIGATOR_TREE * m_netNavigator
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
void RefreshNetNavigator(const NET_NAVIGATOR_ITEM_DATA *aSelection=nullptr, const std::vector< wxString > *aChangedNets=nullptr)
void SetScreen(BASE_SCREEN *aScreen) override
bool AskToSaveChanges()
Check if any of the screens has unsaved changes and asks the user whether to save or drop them.
friend class SCH_EDITOR_CONTROL
void SetCurrentVariant(const wxString &aVariantName)
void UpdateVariantSelectionCtrl(const wxArrayString &aVariantNames)
Update the variant name control on the main toolbar.
bool RecalculateConnections(SCH_COMMIT *aCommit, SCH_CLEANUP_FLAGS aCleanupFlags, PROGRESS_REPORTER *aProgressReporter=nullptr, bool aCleanupDone=false)
Generate the connection data for the entire schematic hierarchy.
std::vector< wxEvtHandler * > m_schematicChangeListeners
PANEL_REMOTE_SYMBOL * m_remoteSymbolPane
bool saveSchematicFile(SCH_SHEET *aSheet, const wxString &aSavePath, bool isSaveAs)
Save aSheet to a schematic file.
SCHEMATIC * m_schematic
The currently loaded schematic.
SCH_SHEET_PATH & GetCurrentSheet() const
void ProjectChanged() override
Notification event that the project has changed.
SCHEMATIC & Schematic() const
void updateTitle()
Set the main window title bar text.
void SetSchematic(SCHEMATIC *aSchematic)
bool LoadProjectSettings()
Load the KiCad project file (*.pro) settings specific to Eeschema.
void RecomputeIntersheetRefs()
Update the schematic's page reference map for all global labels, and refresh the labels so that they ...
void UpdateHierarchyNavigator(bool aRefreshNetNavigator=true, bool aClear=false)
Update the hierarchy navigation tree and history.
bool importFile(const wxString &aFileName, int aFileType, const std::map< std::string, UTF8 > *aProperties=nullptr)
Load the given filename but sets the path to the current project path.
void LoadDrawingSheet()
Load the drawing sheet file.
void SetSheetNumberAndCount()
Set the m_ScreenNumber and m_NumberOfScreens members for screens.
void ClearRepeatItemsList()
Clear the list of items which are to be repeated with the insert key.
void initScreenZoom()
Initialize the zoom value of the current screen and mark the screen as zoom-initialized.
void UpdateItem(EDA_ITEM *aItem, bool isAddOrDelete=false, bool aUpdateRtree=false) override
Mark an item for refresh.
void TestDanglingEnds()
Test all of the connectable objects in the schematic for unused connection points.
bool SaveProject(bool aSaveAs=false)
Save the currently-open schematic (including its hierarchy) and associated project.
void saveProjectSettings() override
Save any design-related project settings associated with this frame.
Frame-independent, non-interactive service that rewrites the library nickname of every schematic symb...
SCH_FP_FIELD_RECONCILE_RESULT Reconcile(SCHEMATIC &aSchematic)
static const wxString ShowType(SCH_FILE_T aFileType)
Return a brief name for a plugin, given aFileType enum.
static SCH_FILE_T GuessPluginTypeFromSchPath(const wxString &aSchematicPath, int aCtl=0)
Return a plugin type given a schematic using the file extension of aSchematicPath.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
Handle actions specific to the schematic editor.
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:818
SCH_SCREEN * GetNext()
SCH_SCREEN * GetScreen(unsigned int aIndex) const
void UpdateSymbolLinks(REPORTER *aReporter=nullptr)
Initialize the LIB_SYMBOL reference for each SCH_SYMBOL found in the full schematic.
SCH_SCREEN * GetFirst()
void PruneOrphanedSheetInstances(const wxString &aProjectName, const SCH_SHEET_LIST &aValidSheetPaths)
void BuildClientSheetPathList()
Build the list of sheet paths sharing a screen for each screen in use.
bool HasSymbolFieldNamesWithWhiteSpace() const
size_t GetCount() const
Definition sch_screen.h:823
void PruneOrphanedSymbolInstances(const wxString &aProjectName, const SCH_SHEET_LIST &aValidSheetPaths)
bool HasNoFullyDefinedLibIds()
Test all of the schematic symbols to see if all LIB_ID objects library nickname is not set.
SCH_SHEET * GetSheet(unsigned int aIndex) const
int ReplaceDuplicateTimeStamps()
Test all sheet and symbol objects in the schematic for duplicate time stamps and replaces them as nec...
void ClearDrawingState()
Clear the state flags of all the items in the screen.
std::vector< SCH_SHEET_PATH > & GetClientSheetPaths()
Return the number of times this screen is used.
Definition sch_screen.h:226
void TestDanglingEnds(const SCH_SHEET_PATH *aPath=nullptr, std::function< void(SCH_ITEM *)> *aChangedHandler=nullptr) const
Test all of the connectable objects in the schematic for unused connection points.
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:122
const wxString & GetFileName() const
Definition sch_screen.h:157
void UpdateLocalLibSymbolLinks()
Initialize the LIB_SYMBOL reference for each SCH_SYMBOL found in this schematic with the local projec...
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
const std::vector< SCH_SYMBOL_INSTANCE > & GetSymbolInstances() const
Definition sch_screen.h:584
int GetFileFormatVersionAtLoad() const
Definition sch_screen.h:142
void AssignNewUuid()
Definition sch_screen.h:596
const std::vector< SCH_SHEET_INSTANCE > & GetSheetInstances() const
Definition sch_screen.h:589
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
void UpdateSheetInstanceData(const std::vector< SCH_SHEET_INSTANCE > &aSheetInstances)
Update all of the sheet instance information using aSheetInstances.
void SetInitialPageNumbers()
Set initial sheet page numbers.
bool AllSheetPageNumbersEmpty() const
Check all of the sheet instance for empty page numbers.
bool IsModified() const
Check the entire hierarchy for any modifications.
void UpdateSymbolInstanceData(const std::vector< SCH_SYMBOL_INSTANCE > &aSymbolInstances)
Update all of the symbol instance information using aSymbolInstances.
bool RepairPageNumbers()
Assign valid page numbers to sheet paths whose stored page number is missing or collides with an earl...
void CheckForMissingSymbolInstances(const wxString &aProjectName)
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
void UpdateAllScreenReferences() const
Update all the symbol references for this sheet path.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
void SetFileName(const wxString &aFilename)
Definition sch_sheet.h:392
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition sch_sheet.h:386
wxString GetName() const
Definition sch_sheet.h:142
void SetName(const wxString &aName)
Definition sch_sheet.h:143
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:145
bool IsVirtualRootSheet() const
void SaveProjectAs(const wxString &aFullPath, PROJECT *aProject=nullptr)
Set the currently loaded project path and saves it (pointers remain valid).
bool SaveProject(const wxString &aFullPath=wxEmptyString, PROJECT *aProject=nullptr)
Save a loaded project.
bool SaveProjectCopy(const wxString &aFullPath, PROJECT *aProject=nullptr)
Save a copy of the current project under the given path.
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
bool UnloadProject(PROJECT *aProject, bool aSave=true)
Save, unload and unregister the given PROJECT.
bool TriggerBackupIfNeeded(REPORTER &aReporter) const
Call BackupProject() if a new backup is needed according to the current backup policy.
The SIMULATOR_FRAME holds the main user-interface for running simulations.
An interface to the global shared library manager that is schematic-specific and linked to one projec...
TOOL_MANAGER * m_toolManager
@ SUPERMODEL_RELOAD
For schematics, the entire schematic changed, not just the sheet.
Definition tool_base.h:77
Temporarily disable a window, and then re-enable on destruction.
Definition raii.h:83
static void ResolvePossibleSymlinks(wxFileName &aFilename)
void Flush()
Build the HTML messages page.
bool HasMessage() const override
Returns true if any messages were reported.
Multi-thread safe progress reporter dialog, intended for use of tasks that parallel reporting back of...
A wrapper for reporting to a wxString object.
Definition reporter.h:242
const wxString & GetMessages() const
Definition reporter.cpp:188
wxString EnsureFileExtension(const wxString &aFilename, const wxString &aExtension)
It's annoying to throw up nag dialogs when the extension isn't right.
Definition common.cpp:848
bool AskOverrideLock(wxWindow *aParent, const wxString &aMessage)
Display a dialog indicating the file is already open, with an option to reset the lock.
Definition confirm.cpp:38
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition confirm.cpp:274
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
Definition confirm.cpp:245
bool HandleUnsavedChanges(wxWindow *aParent, const wxString &aMessage, const std::function< bool()> &aSaveFunction)
Display a dialog with Save, Cancel and Discard Changes buttons.
Definition confirm.cpp:146
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:192
This file is part of the common library.
#define KICAD_MESSAGE_DIALOG
Definition confirm.h:48
static bool empty(const wxTextEntryBase *aCtrl)
#define _(s)
bool PrepareSaveAsFiles(SCHEMATIC &aSchematic, SCH_SCREENS &aScreens, const wxFileName &aOldRoot, const wxFileName &aNewRoot, bool aSaveCopy, bool aCopySubsheets, bool aIncludeExternSheets, std::unordered_map< SCH_SCREEN *, wxString > &aFilenameMap, wxString &aErrorMsg)
void Reset() override
@ FRAME_SIMULATOR
Definition frame_type.h:34
#define THROW_IO_CANCELLED()
static const std::string LegacySchematicFileExtension
static const std::string ProjectFileExtension
static const std::string LegacyProjectFileExtension
static const std::string KiCadSchematicFileExtension
static const std::string LegacySymbolLibFileExtension
static wxString KiCadSchematicFileWildcard()
const wxChar *const traceSchCurrentSheet
Flag to enable debug output of current sheet tracking in the schematic editor.
const wxChar *const traceAutoSave
Flag to enable auto save feature debug tracing.
const wxChar *const tracePathsAndFiles
Flag to enable path and file name debug output.
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
PROJECT & Prj()
Definition kicad.cpp:733
KIID niluuid(0)
#define KICTL_CREATE
caller thinks requested project files may not exist.
#define KICTL_KICAD_ONLY
chosen file is from KiCad according to user
@ LAYER_BUS
Definition layer_ids.h:475
int ReconcileLegacyCacheSymbols(SYMBOL_LIBRARY_ADAPTER &aAdapter, const wxString &aCacheNickname, SCH_SCREENS &aScreens, std::vector< KI_ERROR > &aErrors)
Point symbols that are only available in a legacy project cache library at that cache.
Definition for symbol library class.
void ReadFootprintProps(const std::map< std::string, UTF8 > *aProps, wxString &aCacheNickname, std::vector< wxString > &aSourceFpLibs)
Read the footprint-import coordination properties out of a properties map.
void SetShutdownBlockReason(wxWindow *aWindow, const wxString &aReason)
Sets the block reason why the window/application is preventing OS shutdown.
Definition unix/app.cpp:102
bool RegisterApplicationRestart(const wxString &aCommandLine)
Registers the application for restart with the OS with the given command line string to pass as args.
Definition unix/app.cpp:77
bool MakeWriteable(const wxString &aFilePath)
Ensures that a file has write permissions.
Definition unix/io.cpp:78
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:533
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
#define SKIP_CONNECTIVITY
Definition sch_commit.h:41
#define SKIP_SET_DIRTY
Definition sch_commit.h:40
#define DELETE_REMOVED_ITEMS
Definition sch_commit.h:43
#define SKIP_UNDO
Definition sch_commit.h:38
#define SEXPR_SCHEMATIC_FILE_VERSION
Schematic file version.
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
@ GLOBAL_CLEANUP
Definition schematic.h:95
KIWAY Kiway(KFCTL_STANDALONE)
std::vector< FAB_LAYER_COLOR > dummy
MODEL3D_FORMAT_TYPE fileType(const char *aFileName)
bool show_import_issues
Stored value for "show import issues" when importing non-KiCad designs to this application.
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
Container that describes file type info.
Definition io_base.h:43
std::vector< std::string > m_FileExtensions
Filter used for file pickers if m_IsFile is true.
Definition io_base.h:47
bool m_CanRead
Whether the IO can read this file type.
Definition io_base.h:52
wxString FileFilter() const
Definition io_base.cpp:40
Implement a participant in the KIWAY alchemy.
Definition kiway.h:153
virtual void PreloadLibraries(KIWAY *aKiway)
Definition kiway.h:298
Information about a top-level schematic sheet.
SYMBOL_IMPORT_RECONCILE_RESULT ReconcileImportedSymbols(SCH_IO &aPlugin, SCHEMATIC &aSchematic, PROJECT &aProject, const wxString &aSchematicPath, const std::map< std::string, UTF8 > *aProperties, REPORTER &aReporter)
Reconcile aSchematic against the definitions aPlugin retained while loading it.
std::string path
wxLogTrace helper definitions.
@ SCH_SHEET_T
Definition typeinfo.h:171
@ SCH_BUS_BUS_ENTRY_T
Definition typeinfo.h:158
wxString formatWildcardExt(const wxString &aWildcard)
Format wildcard extension to support case sensitive file dialogs.
Definition of file extensions used in Kicad.
#define PR_CAN_ABORT