KiCad PCB EDA Suite
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 CERN (www.cern.ch)
7 * Copyright (C) 1992-2023 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, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
27#include <symbol_library.h>
28#include <confirm.h>
29#include <connection_graph.h>
31#include <dialog_symbol_remap.h>
32#include <eeschema_settings.h>
33#include <id.h>
34#include <kiface_base.h>
35#include <kiplatform/app.h>
36#include <pgm_base.h>
37#include <profile.h>
39#include <project_rescue.h>
41#include <reporter.h>
42#include <richio.h>
43#include <sch_bus_entry.h>
44#include <sch_edit_frame.h>
46#include <sch_file_versions.h>
47#include <sch_line.h>
48#include <sch_sheet.h>
49#include <sch_sheet_path.h>
50#include <schematic.h>
52#include <sim/simulator_frame.h>
53#include <tool/actions.h>
54#include <tool/tool_manager.h>
57#include <trace_helpers.h>
58#include <widgets/wx_infobar.h>
61#include <wx/app.h>
62#include <wx/ffile.h>
63#include <wx/filedlg.h>
64#include <wx/log.h>
65#include <wx/stdpaths.h>
67#include <paths.h>
68#include <wx_filename.h> // For ::ResolvePossibleSymlinks
71
72#if wxCHECK_VERSION( 3, 1, 7 )
73#include "widgets/filedlg_hook_save_project.h"
74#else
75#include "widgets/legacyfiledlg_save_project.h"
76#endif
77
78bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
79{
80 // implement the pseudo code from KIWAY_PLAYER.h:
81 wxString msg;
82
83 EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
84
85 // This is for python:
86 if( aFileSet.size() != 1 )
87 {
88 msg.Printf( "Eeschema:%s() takes only a single filename.", __WXFUNCTION__ );
89 DisplayError( this, msg );
90 return false;
91 }
92
93 wxString fullFileName( aFileSet[0] );
94 wxFileName wx_filename( fullFileName );
95
96 // We insist on caller sending us an absolute path, if it does not, we say it's a bug.
97 wxASSERT_MSG( wx_filename.IsAbsolute(), wxT( "Path is not absolute!" ) );
98
99 if( !LockFile( fullFileName ) )
100 {
101 msg.Printf( _( "Schematic '%s' is already open." ), wx_filename.GetFullName() );
102
103 if( !OverrideLock( this, msg ) )
104 return false;
105 }
106
107 if( !AskToSaveChanges() )
108 return false;
109
110#ifdef PROFILE
111 PROF_TIMER openFiles( "OpenProjectFile" );
112#endif
113
114 wxFileName pro = fullFileName;
115 pro.SetExt( ProjectFileExtension );
116
117 bool is_new = !wxFileName::IsFileReadable( fullFileName );
118
119 // If its a non-existent schematic and caller thinks it exists
120 if( is_new && !( aCtl & KICTL_CREATE ) )
121 {
122 // notify user that fullFileName does not exist, ask if user wants to create it.
123 msg.Printf( _( "Schematic '%s' does not exist. Do you wish to create it?" ),
124 fullFileName );
125
126 if( !IsOK( this, msg ) )
127 return false;
128 }
129
130 // unload current project file before loading new
131 {
133 SetScreen( nullptr );
136 }
137
138 SetStatusText( wxEmptyString );
140
141 WX_PROGRESS_REPORTER progressReporter( this, is_new ? _( "Creating Schematic" )
142 : _( "Loading Schematic" ), 1 );
143
144 bool differentProject = pro.GetFullPath() != Prj().GetProjectFullName();
145
146 if( differentProject )
147 {
148 if( !Prj().IsNullProject() )
150
151 Schematic().SetProject( nullptr );
152 GetSettingsManager()->UnloadProject( &Prj(), false );
153
154 GetSettingsManager()->LoadProject( pro.GetFullPath() );
155
156 wxFileName legacyPro( pro );
157 legacyPro.SetExt( LegacyProjectFileExtension );
158
159 // Do not allow saving a project if one doesn't exist. This normally happens if we are
160 // standalone and opening a schematic that has been moved from its project folder.
161 if( !pro.Exists() && !legacyPro.Exists() && !( aCtl & KICTL_CREATE ) )
162 Prj().SetReadOnly();
163
165 }
166
167 SCH_IO_MGR::SCH_FILE_T schFileType = SCH_IO_MGR::GuessPluginTypeFromSchPath( fullFileName );
168
169 if( schFileType == SCH_IO_MGR::SCH_LEGACY )
170 {
171 // Don't reload the symbol libraries if we are just launching Eeschema from KiCad again.
172 // They are already saved in the kiface project object.
173 if( differentProject || !Prj().GetElem( PROJECT::ELEM_SCH_SYMBOL_LIBS ) )
174 {
175 // load the libraries here, not in SCH_SCREEN::Draw() which is a context
176 // that will not tolerate DisplayError() dialog since we're already in an
177 // event handler in there.
178 // And when a schematic file is loaded, we need these libs to initialize
179 // some parameters (links to PART LIB, dangling ends ...)
181 Prj().SchLibs();
182 }
183 }
184 else
185 {
186 // No legacy symbol libraries including the cache are loaded with the new file format.
188 }
189
190 // Load the symbol library table, this will be used forever more.
192 Prj().SchSymbolLibTable();
193
194 // Load project settings after schematic has been set up with the project link, since this will
195 // update some of the needed schematic settings such as drawing defaults
197
198 wxFileName rfn( GetCurrentFileName() );
199 rfn.MakeRelativeTo( Prj().GetProjectPath() );
200 LoadWindowState( rfn.GetFullPath() );
201
202 KIPLATFORM::APP::SetShutdownBlockReason( this, _( "Schematic file changes are unsaved" ) );
203
204 if( Kiface().IsSingle() )
205 {
207 }
208
209 if( is_new )
210 {
211 // mark new, unsaved file as modified.
213 GetScreen()->SetFileName( fullFileName );
214 }
215 else
216 {
217 wxFileName autoSaveFn = fullFileName;
218
219 autoSaveFn.SetName( getAutoSaveFileName() );
220 autoSaveFn.ClearExt();
221
222 CheckForAutoSaveFile( autoSaveFn );
223
224 SetScreen( nullptr );
225
226 SCH_PLUGIN* plugin = SCH_IO_MGR::FindPlugin( schFileType );
228
229 pi->SetProgressReporter( &progressReporter );
230
231 bool failedLoad = false;
232
233 try
234 {
235 {
236 wxBusyCursor busy;
237 Schematic().SetRoot( pi->Load( fullFileName, &Schematic() ) );
238 }
239
240 if( !pi->GetError().IsEmpty() )
241 {
242 DisplayErrorMessage( this, _( "The entire schematic could not be loaded. Errors "
243 "occurred attempting to load hierarchical sheets." ),
244 pi->GetError() );
245 }
246 }
247 catch( const FUTURE_FORMAT_ERROR& ffe )
248 {
249 msg.Printf( _( "Error loading schematic '%s'." ), fullFileName);
250 progressReporter.Hide();
251 DisplayErrorMessage( this, msg, ffe.Problem() );
252
253 failedLoad = true;
254 }
255 catch( const IO_ERROR& ioe )
256 {
257 msg.Printf( _( "Error loading schematic '%s'." ), fullFileName);
258 progressReporter.Hide();
259 DisplayErrorMessage( this, msg, ioe.What() );
260
261 failedLoad = true;
262 }
263 catch( const std::bad_alloc& )
264 {
265 msg.Printf( _( "Memory exhausted loading schematic '%s'." ), fullFileName );
266 progressReporter.Hide();
267 DisplayErrorMessage( this, msg, wxEmptyString );
268
269 failedLoad = true;
270 }
271
272 // This fixes a focus issue after the progress reporter is done on GTK. It shouldn't
273 // cause any issues on macOS and Windows. If it does, it will have to be conditionally
274 // compiled.
275 Raise();
276
277 if( failedLoad )
278 {
279 // Do not leave g_RootSheet == NULL because it is expected to be
280 // a valid sheet. Therefore create a dummy empty root sheet and screen.
283
284 msg.Printf( _( "Failed to load '%s'." ), fullFileName );
285 SetMsgPanel( wxEmptyString, msg );
286
287 return false;
288 }
289
290 // It's possible the schematic parser fixed errors due to bugs so warn the user
291 // that the schematic has been fixed (modified).
292 SCH_SHEET_LIST sheetList = Schematic().GetSheets();
293
294 if( sheetList.IsModified() )
295 {
296 DisplayInfoMessage( this,
297 _( "An error was found when loading the schematic that has "
298 "been automatically fixed. Please save the schematic to "
299 "repair the broken file or it may not be usable with other "
300 "versions of KiCad." ) );
301 }
302
303 if( sheetList.AllSheetPageNumbersEmpty() )
304 sheetList.SetInitialPageNumbers();
305
306 UpdateFileHistory( fullFileName );
307
308 SCH_SCREENS schematic( Schematic().Root() );
309
310 // LIB_ID checks and symbol rescue only apply to the legacy file formats.
311 if( schFileType == SCH_IO_MGR::SCH_LEGACY )
312 {
313 // Convert any legacy bus-bus entries to just be bus wires
314 for( SCH_SCREEN* screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )
315 {
316 std::vector<SCH_ITEM*> deleted;
317
318 for( SCH_ITEM* item : screen->Items() )
319 {
320 if( item->Type() == SCH_BUS_BUS_ENTRY_T )
321 {
322 SCH_BUS_BUS_ENTRY* entry = static_cast<SCH_BUS_BUS_ENTRY*>( item );
323 std::unique_ptr<SCH_LINE> wire = std::make_unique<SCH_LINE>();
324
325 wire->SetLayer( LAYER_BUS );
326 wire->SetStartPoint( entry->GetPosition() );
327 wire->SetEndPoint( entry->GetEnd() );
328
329 screen->Append( wire.release() );
330 deleted.push_back( item );
331 }
332 }
333
334 for( SCH_ITEM* item : deleted )
335 screen->Remove( item );
336 }
337
338
339 // Convert old projects over to use symbol library table.
340 if( schematic.HasNoFullyDefinedLibIds() )
341 {
342 DIALOG_SYMBOL_REMAP dlgRemap( this );
343
344 dlgRemap.ShowQuasiModal();
345 }
346 else
347 {
348 // Double check to ensure no legacy library list entries have been
349 // added to the project file symbol library list.
350 wxString paths;
351 wxArrayString libNames;
352
353 SYMBOL_LIBS::GetLibNamesAndPaths( &Prj(), &paths, &libNames );
354
355 if( !libNames.IsEmpty() )
356 {
357 if( eeconfig()->m_Appearance.show_illegal_symbol_lib_dialog )
358 {
359 wxRichMessageDialog invalidLibDlg(
360 this,
361 _( "Illegal entry found in project file symbol library list." ),
362 _( "Project Load Warning" ),
363 wxOK | wxCENTER | wxICON_EXCLAMATION );
364 invalidLibDlg.ShowDetailedText(
365 _( "Symbol libraries defined in the project file symbol library "
366 "list are no longer supported and will be removed.\n\n"
367 "This may cause broken symbol library links under certain "
368 "conditions." ) );
369 invalidLibDlg.ShowCheckBox( _( "Do not show this dialog again." ) );
370 invalidLibDlg.ShowModal();
372 !invalidLibDlg.IsCheckBoxChecked();
373 }
374
375 libNames.Clear();
376 paths.Clear();
377 SYMBOL_LIBS::SetLibNamesAndPaths( &Prj(), paths, libNames );
378 }
379
380 if( !cfg || !cfg->m_RescueNeverShow )
381 {
383 editor->RescueSymbolLibTableProject( false );
384 }
385 }
386
387 // Ensure there is only one legacy library loaded and that it is the cache library.
388 SYMBOL_LIBS* legacyLibs = Schematic().Prj().SchLibs();
389
390 if( legacyLibs->GetLibraryCount() == 0 )
391 {
392 wxString extMsg;
393 wxFileName cacheFn = pro;
394
395 cacheFn.SetName( cacheFn.GetName() + "-cache" );
396 cacheFn.SetExt( LegacySymbolLibFileExtension );
397
398 msg.Printf( _( "The project symbol library cache file '%s' was not found." ),
399 cacheFn.GetFullName() );
400 extMsg = _( "This can result in a broken schematic under certain conditions. "
401 "If the schematic does not have any missing symbols upon opening, "
402 "save it immediately before making any changes to prevent data "
403 "loss. If there are missing symbols, either manual recovery of "
404 "the schematic or recovery of the symbol cache library file and "
405 "reloading the schematic is required." );
406
407 wxMessageDialog dlgMissingCache( this, msg, _( "Warning" ),
408 wxOK | wxCANCEL | wxICON_EXCLAMATION | wxCENTER );
409 dlgMissingCache.SetExtendedMessage( extMsg );
410 dlgMissingCache.SetOKCancelLabels(
411 wxMessageDialog::ButtonLabel( _( "Load Without Cache File" ) ),
412 wxMessageDialog::ButtonLabel( _( "Abort" ) ) );
413
414 if( dlgMissingCache.ShowModal() == wxID_CANCEL )
415 {
416 Schematic().Reset();
418 return false;
419 }
420 }
421
422 // Update all symbol library links for all sheets.
423 schematic.UpdateSymbolLinks();
424
427 m_infoBar->ShowMessage( _( "This file was created by an older version of KiCad. "
428 "It will be converted to the new format when saved." ),
430
431 // Legacy schematic can have duplicate time stamps so fix that before converting
432 // to the s-expression format.
433 schematic.ReplaceDuplicateTimeStamps();
434
435 for( SCH_SCREEN* screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )
436 screen->FixLegacyPowerSymbolMismatches();
437
438 // Allow the schematic to be saved to new file format without making any edits.
439 OnModify();
440 }
441 else // S-expression schematic.
442 {
444 {
447 m_infoBar->ShowMessage( _( "This file was created by an older version of KiCad. "
448 "It will be converted to the new format when saved." ),
450 }
451
452 for( SCH_SCREEN* screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )
453 screen->UpdateLocalLibSymbolLinks();
454
455 // Restore all of the loaded symbol and sheet instances from the root sheet.
456 if( Schematic().RootScreen()->GetFileFormatVersionAtLoad() < 20221002 )
457 sheetList.UpdateSymbolInstanceData( Schematic().RootScreen()->GetSymbolInstances());
458
459 if( Schematic().RootScreen()->GetFileFormatVersionAtLoad() < 20221110 )
460 sheetList.UpdateSheetInstanceData( Schematic().RootScreen()->GetSheetInstances());
461
462 if( Schematic().RootScreen()->GetFileFormatVersionAtLoad() < 20230221 )
463 for( SCH_SCREEN* screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )
464 screen->FixLegacyPowerSymbolMismatches();
465
466 for( SCH_SCREEN* screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )
467 screen->MigrateSimModels();
468 }
469
471
472 SetScreen( GetCurrentSheet().LastScreen() );
473
474 // Migrate conflicting bus definitions
475 // TODO(JE) This should only run once based on schematic file version
476 if( Schematic().ConnectionGraph()->GetBusesNeedingMigration().size() > 0 )
477 {
478 DIALOG_MIGRATE_BUSES dlg( this );
479 dlg.ShowQuasiModal();
480 OnModify();
481 }
482
485 }
486
487 // Load any exclusions from the project file
489
492
495
496 // Re-create junctions if needed. Eeschema optimizes wires by merging
497 // colinear segments. If a schematic is saved without a valid
498 // cache library or missing installed libraries, this can cause connectivity errors
499 // unless junctions are added.
500 //
501 // TODO: (RFB) This really needs to be put inside the Load() function of the SCH_LEGACY_PLUGIN
502 // I can't put it right now because of the extra code that is above to convert legacy bus-bus
503 // entries to bus wires
504 if( schFileType == SCH_IO_MGR::SCH_LEGACY )
506
507 SyncView();
509
511
513 updateTitle();
514 m_toolManager->GetTool<SCH_NAVIGATE_TOOL>()->ResetHistory();
515
516 wxFileName fn = Prj().AbsolutePath( GetScreen()->GetFileName() );
517
518 if( fn.FileExists() && !fn.IsFileWritable() )
519 {
522 m_infoBar->ShowMessage( _( "Schematic is read only." ),
524 }
525
526#ifdef PROFILE
527 openFiles.Show();
528#endif
529
530 return true;
531}
532
533
535{
536 SCH_SCREEN* screen = GetScreen();
537
538 if( !screen )
539 {
540 wxLogError( wxT( "Document not ready, cannot import" ) );
541 return false;
542 }
543
544 // open file chooser dialog
545 wxString path = wxPathOnly( Prj().GetProjectFullName() );
546
547 wxFileDialog dlg( this, _( "Insert Schematic" ), path, wxEmptyString,
548 KiCadSchematicFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
549
550 if( dlg.ShowModal() == wxID_CANCEL )
551 return false;
552
553 return AddSheetAndUpdateDisplay( dlg.GetPath() );
554}
555
556
557bool SCH_EDIT_FRAME::AddSheetAndUpdateDisplay( const wxString aFullFileName )
558{
559 if( !LoadSheetFromFile( GetCurrentSheet().Last(), &GetCurrentSheet(), aFullFileName ) )
560 return false;
561
564
565 SyncView();
566 OnModify();
567 HardRedraw(); // Full reinit of the current screen and the display.
568
570
571 return true;
572}
573
574
575void SCH_EDIT_FRAME::OnAppendProject( wxCommandEvent& event )
576{
577 if( GetScreen() && GetScreen()->IsModified() )
578 {
579 wxString msg = _( "This operation cannot be undone.\n\n"
580 "Do you want to save the current document before proceeding?" );
581
582 if( IsOK( this, msg ) )
583 SaveProject();
584 }
585
587}
588
589
590void SCH_EDIT_FRAME::OnImportProject( wxCommandEvent& aEvent )
591{
592 if( !AskToSaveChanges() )
593 return;
594
595 // Set the project location if none is set or if we are running in standalone mode
596 bool setProject = Prj().GetProjectFullName().IsEmpty() || Kiface().IsSingle();
597 wxString path = wxPathOnly( Prj().GetProjectFullName() );
598
599 std::list<std::pair<const wxString, const SCH_IO_MGR::SCH_FILE_T>> loaders;
600
601 // Import Altium schematic files.
602 loaders.emplace_back( AltiumSchematicFileWildcard(), SCH_IO_MGR::SCH_ALTIUM );
603
604 // Import CADSTAR Schematic Archive files.
605 loaders.emplace_back( CadstarSchematicArchiveFileWildcard(), SCH_IO_MGR::SCH_CADSTAR_ARCHIVE );
606
607 // Import Eagle schematic files.
608 loaders.emplace_back( EagleSchematicFileWildcard(), SCH_IO_MGR::SCH_EAGLE );
609
610 wxString fileFilters;
611 wxString allWildcards;
612
613 for( std::pair<const wxString, const SCH_IO_MGR::SCH_FILE_T>& loader : loaders )
614 {
615 if( !fileFilters.IsEmpty() )
616 fileFilters += wxChar( '|' );
617
618 fileFilters += wxGetTranslation( loader.first );
619
620 SCH_PLUGIN::SCH_PLUGIN_RELEASER plugin( SCH_IO_MGR::FindPlugin( loader.second ) );
621 wxCHECK( plugin, /*void*/ );
622 allWildcards += wxS( "*." ) + formatWildcardExt( plugin->GetFileExtension() ) + wxS( ";" );
623 }
624
625 fileFilters = _( "All supported formats" ) + wxS( "|" ) + allWildcards + wxS( "|" ) +
626 fileFilters;
627
628 wxFileDialog dlg( this, _( "Import Schematic" ), path, wxEmptyString, fileFilters,
629 wxFD_OPEN | wxFD_FILE_MUST_EXIST ); // TODO
630
631 if( dlg.ShowModal() == wxID_CANCEL )
632 return;
633
634 // Don't leave dangling pointers to previously-opened document.
635 m_toolManager->GetTool<EE_SELECTION_TOOL>()->ClearSelection();
637
638 if( setProject )
639 {
640 Schematic().SetProject( nullptr );
641 GetSettingsManager()->UnloadProject( &Prj(), false );
642
643 Schematic().Reset();
644
645 wxFileName projectFn( dlg.GetPath() );
646 projectFn.SetExt( ProjectFileExtension );
647 GetSettingsManager()->LoadProject( projectFn.GetFullPath() );
648
649 Schematic().SetProject( &Prj() );
650 }
651
652 wxFileName fn = dlg.GetPath();
653
654 SCH_IO_MGR::SCH_FILE_T pluginType = SCH_IO_MGR::SCH_FILE_T::SCH_FILE_UNKNOWN;
655
656 for( std::pair<const wxString, const SCH_IO_MGR::SCH_FILE_T>& loader : loaders )
657 {
658 if( fn.GetExt().CmpNoCase( SCH_IO_MGR::GetFileExtension( loader.second ) ) == 0 )
659 {
660 pluginType = loader.second;
661 break;
662 }
663 }
664
665 if( pluginType == SCH_IO_MGR::SCH_FILE_T::SCH_FILE_UNKNOWN )
666 {
667 wxLogError( _( "Unexpected file extension: '%s'." ), fn.GetExt() );
668 return;
669 }
670
671 importFile( dlg.GetPath(), pluginType );
672
674}
675
676
677bool SCH_EDIT_FRAME::saveSchematicFile( SCH_SHEET* aSheet, const wxString& aSavePath )
678{
679 wxString msg;
680 wxFileName schematicFileName;
681 wxFileName oldFileName;
682 bool success;
683
684 SCH_SCREEN* screen = aSheet->GetScreen();
685
686 wxCHECK( screen, false );
687
688 // Cannot save to nowhere
689 wxCHECK( !aSavePath.IsEmpty(), false );
690
691 // Construct the name of the file to be saved
692 schematicFileName = Prj().AbsolutePath( aSavePath );
693 oldFileName = schematicFileName;
694
695 // Write through symlinks, don't replace them
696 WX_FILENAME::ResolvePossibleSymlinks( schematicFileName );
697
698 if( !IsWritable( schematicFileName ) )
699 return false;
700
701 wxString tempFile = wxFileName::CreateTempFileName( wxS( "eeschema" ) );
702
703 // Save
704 wxLogTrace( traceAutoSave, wxS( "Saving file " ) + schematicFileName.GetFullPath() );
705
708
709 SCH_IO_MGR::SCH_FILE_T pluginType = SCH_IO_MGR::GuessPluginTypeFromSchPath(
710 schematicFileName.GetFullPath() );
711 SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( pluginType ) );
712
713 try
714 {
715 pi->Save( tempFile, aSheet, &Schematic() );
716 success = true;
717 }
718 catch( const IO_ERROR& ioe )
719 {
720 msg.Printf( _( "Error saving schematic file '%s'.\n%s" ),
721 schematicFileName.GetFullPath(),
722 ioe.What() );
723 DisplayError( this, msg );
724
725 msg.Printf( _( "Failed to create temporary file '%s'." ),
726 tempFile );
727 SetMsgPanel( wxEmptyString, msg );
728
729 // In case we started a file but didn't fully write it, clean up
730 wxRemoveFile( tempFile );
731
732 success = false;
733 }
734
735 if( success )
736 {
737 // Replace the original with the temporary file we just wrote
738 success = wxRenameFile( tempFile, schematicFileName.GetFullPath() );
739
740 if( !success )
741 {
742 msg.Printf( _( "Error saving schematic file '%s'.\n"
743 "Failed to rename temporary file '%s'." ),
744 schematicFileName.GetFullPath(),
745 tempFile );
746 DisplayError( this, msg );
747
748 msg.Printf( _( "Failed to rename temporary file '%s'." ),
749 tempFile );
750 SetMsgPanel( wxEmptyString, msg );
751 }
752 }
753
754 if( success )
755 {
756 // Delete auto save file.
757 wxFileName autoSaveFileName = schematicFileName;
758 autoSaveFileName.SetName( GetAutoSaveFilePrefix() + schematicFileName.GetName() );
759
760 if( autoSaveFileName.FileExists() )
761 {
762 wxLogTrace( traceAutoSave,
763 wxT( "Removing auto save file <" ) + autoSaveFileName.GetFullPath() +
764 wxT( ">" ) );
765
766 wxRemoveFile( autoSaveFileName.GetFullPath() );
767 }
768
769 screen->SetContentModified( false );
770
771 msg.Printf( _( "File '%s' saved." ), screen->GetFileName() );
772 SetStatusText( msg, 0 );
773 }
774 else
775 {
776 DisplayError( this, _( "File write operation failed." ) );
777 }
778
779 return success;
780}
781
782
783bool SCH_EDIT_FRAME::SaveProject( bool aSaveAs )
784{
785 wxString msg;
786 SCH_SCREEN* screen;
787 SCH_SCREENS screens( Schematic().Root() );
788 bool saveCopy = aSaveAs && !Kiface().IsSingle();
789 bool success = true;
790 bool updateFileHistory = false;
791 bool createNewProject = false;
792
793 // I want to see it in the debugger, show me the string! Can't do that with wxFileName.
794 wxString fileName = Prj().AbsolutePath( Schematic().Root().GetFileName() );
795 wxFileName fn = fileName;
796
797 // Path to save each screen to: will be the stored filename by default, but is overwritten by
798 // a Save As Copy operation.
799 std::unordered_map<SCH_SCREEN*, wxString> filenameMap;
800
801 // Handle "Save As" and saving a new project/schematic for the first time in standalone
802 if( Prj().IsNullProject() || aSaveAs )
803 {
804 // Null project should only be possible in standalone mode.
805 wxCHECK( Kiface().IsSingle() || aSaveAs, false );
806
807 wxFileName newFileName;
808 wxFileName savePath( Prj().GetProjectFullName() );
809
810 if( !savePath.IsOk() || !savePath.IsDirWritable() )
811 {
812 savePath = GetMruPath();
813
814 if( !savePath.IsOk() || !savePath.IsDirWritable() )
816 }
817
818 if( savePath.HasExt() )
819 savePath.SetExt( KiCadSchematicFileExtension );
820 else
821 savePath.SetName( wxEmptyString );
822
823 wxFileDialog dlg( this, _( "Schematic Files" ), savePath.GetPath(),
824 savePath.GetFullName(), KiCadSchematicFileWildcard(),
825 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
826
827#if wxCHECK_VERSION( 3, 1, 7 )
828 FILEDLG_HOOK_SAVE_PROJECT newProjectHook;
829#endif
830
831 // Add a "Create a project" checkbox in standalone mode and one isn't loaded
832 if( Kiface().IsSingle() || aSaveAs )
833 {
834#if wxCHECK_VERSION( 3, 1, 7 )
835 dlg.SetCustomizeHook( newProjectHook );
836#else
837 dlg.SetExtraControlCreator( &LEGACYFILEDLG_SAVE_PROJECT::Create );
838#endif
839 }
840
841 if( dlg.ShowModal() == wxID_CANCEL )
842 return false;
843
844 newFileName = dlg.GetPath();
845 newFileName.SetExt( KiCadSchematicFileExtension );
846
847 if( ( !newFileName.DirExists() && !newFileName.Mkdir() ) ||
848 !newFileName.IsDirWritable() )
849 {
850 msg.Printf( _( "Folder '%s' could not be created.\n\n"
851 "Make sure you have write permissions and try again." ),
852 newFileName.GetPath() );
853
854 wxMessageDialog dlgBadPath( this, msg, _( "Error" ),
855 wxOK | wxICON_EXCLAMATION | wxCENTER );
856
857 dlgBadPath.ShowModal();
858 return false;
859 }
860
861#if wxCHECK_VERSION( 3, 1, 7 )
862 if( newProjectHook.IsAttachedToDialog() )
863 createNewProject = newProjectHook.GetCreateNewProject();
864#else
865 if( wxWindow* ec = dlg.GetExtraControl() )
866 createNewProject = static_cast<LEGACYFILEDLG_SAVE_PROJECT*>( ec )->GetValue();
867#endif
868
869 if( !saveCopy )
870 {
871 Schematic().Root().SetFileName( newFileName.GetFullName() );
872 Schematic().RootScreen()->SetFileName( newFileName.GetFullPath() );
873 updateFileHistory = true;
874 }
875 else
876 {
877 filenameMap[Schematic().RootScreen()] = newFileName.GetFullPath();
878 }
879
880 // Set the base path to all new sheets.
881 for( size_t i = 0; i < screens.GetCount(); i++ )
882 {
883 screen = screens.GetScreen( i );
884
885 wxCHECK2( screen, continue );
886
887 // The root screen file name has already been set.
888 if( screen == Schematic().RootScreen() )
889 continue;
890
891 wxFileName tmp = screen->GetFileName();
892
893 // Assume existing sheet files are being reused and do not save them to the new
894 // path. Maybe in the future, add a user option to copy schematic files to the
895 // new project path.
896 if( tmp.FileExists() )
897 continue;
898
899 if( tmp.GetPath().IsEmpty() )
900 {
901 tmp.SetPath( newFileName.GetPath() );
902 }
903 else if( tmp.GetPath() == fn.GetPath() )
904 {
905 tmp.SetPath( newFileName.GetPath() );
906 }
907 else if( tmp.GetPath().StartsWith( fn.GetPath() ) )
908 {
909 // NOTE: this hasn't been tested because the sheet properties dialog no longer
910 // allows adding a path specifier in the file name field.
911 wxString newPath = newFileName.GetPath();
912 newPath += tmp.GetPath().Right( fn.GetPath().Length() );
913 tmp.SetPath( newPath );
914 }
915
916 wxLogTrace( tracePathsAndFiles,
917 wxT( "Moving schematic from '%s' to '%s'." ),
918 screen->GetFileName(),
919 tmp.GetFullPath() );
920
921 if( !tmp.DirExists() && !tmp.Mkdir() )
922 {
923 msg.Printf( _( "Folder '%s' could not be created.\n\n"
924 "Make sure you have write permissions and try again." ),
925 newFileName.GetPath() );
926
927 wxMessageDialog dlgBadFilePath( this, msg, _( "Error" ),
928 wxOK | wxICON_EXCLAMATION | wxCENTER );
929
930 dlgBadFilePath.ShowModal();
931 return false;
932 }
933
934 if( saveCopy )
935 filenameMap[screen] = tmp.GetFullPath();
936 else
937 screen->SetFileName( tmp.GetFullPath() );
938 }
939
940 // Attempt to make sheet file name paths relative to the new root schematic path.
942
943 for( SCH_SHEET_PATH& sheet : sheets )
944 {
945 if( sheet.Last()->IsRootSheet() )
946 continue;
947
948 sheet.MakeFilePathRelativeToParentSheet();
949 }
950 }
951 else if( !fn.FileExists() )
952 {
953 // File doesn't exist yet; true if we just imported something
954 updateFileHistory = true;
955 }
956
957 if( filenameMap.empty() || !saveCopy )
958 {
959 for( size_t i = 0; i < screens.GetCount(); i++ )
960 filenameMap[screens.GetScreen( i )] = screens.GetScreen( i )->GetFileName();
961 }
962
963 // Warn user on potential file overwrite. This can happen on shared sheets.
964 wxArrayString overwrittenFiles;
965
966 for( size_t i = 0; i < screens.GetCount(); i++ )
967 {
968 screen = screens.GetScreen( i );
969
970 wxCHECK2( screen, continue );
971
972 // Convert legacy schematics file name extensions for the new format.
973 wxFileName tmpFn = filenameMap[screen];
974
975 if( !tmpFn.IsOk() )
976 continue;
977
978 if( tmpFn.GetExt() == KiCadSchematicFileExtension )
979 continue;
980
981 tmpFn.SetExt( KiCadSchematicFileExtension );
982
983 if( tmpFn.FileExists() )
984 overwrittenFiles.Add( tmpFn.GetFullPath() );
985 }
986
987 if( !overwrittenFiles.IsEmpty() )
988 {
989 for( const wxString& overwrittenFile : overwrittenFiles )
990 {
991 if( msg.IsEmpty() )
992 msg = overwrittenFile;
993 else
994 msg += "\n" + overwrittenFile;
995 }
996
997 wxRichMessageDialog dlg( this, _( "Saving will overwrite existing files." ),
998 _( "Save Warning" ),
999 wxOK | wxCANCEL | wxCANCEL_DEFAULT | wxCENTER |
1000 wxICON_EXCLAMATION );
1001 dlg.ShowDetailedText( _( "The following files will be overwritten:\n\n" ) + msg );
1002 dlg.SetOKCancelLabels( wxMessageDialog::ButtonLabel( _( "Overwrite Files" ) ),
1003 wxMessageDialog::ButtonLabel( _( "Abort Project Save" ) ) );
1004
1005 if( dlg.ShowModal() == wxID_CANCEL )
1006 return false;
1007 }
1008
1009 screens.BuildClientSheetPathList();
1010
1011 for( size_t i = 0; i < screens.GetCount(); i++ )
1012 {
1013 screen = screens.GetScreen( i );
1014
1015 wxCHECK2( screen, continue );
1016
1017 // Convert legacy schematics file name extensions for the new format.
1018 wxFileName tmpFn = filenameMap[screen];
1019
1020 if( tmpFn.IsOk() && tmpFn.GetExt() != KiCadSchematicFileExtension )
1021 {
1022 updateFileHistory = true;
1023 tmpFn.SetExt( KiCadSchematicFileExtension );
1024
1025 for( EDA_ITEM* item : screen->Items().OfType( SCH_SHEET_T ) )
1026 {
1027 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
1028 wxFileName sheetFileName = sheet->GetFileName();
1029
1030 if( !sheetFileName.IsOk() || sheetFileName.GetExt() == KiCadSchematicFileExtension )
1031 continue;
1032
1033 sheetFileName.SetExt( KiCadSchematicFileExtension );
1034 sheet->SetFileName( sheetFileName.GetFullPath() );
1035 UpdateItem( sheet );
1036 }
1037
1038 filenameMap[screen] = tmpFn.GetFullPath();
1039
1040 if( !saveCopy )
1041 screen->SetFileName( tmpFn.GetFullPath() );
1042 }
1043
1044 // Do not save sheet symbols with no valid filename set
1045 if( !tmpFn.IsOk() )
1046 continue;
1047
1048 std::vector<SCH_SHEET_PATH>& sheets = screen->GetClientSheetPaths();
1049
1050 if( sheets.size() == 1 )
1051 screen->SetVirtualPageNumber( 1 );
1052 else
1053 screen->SetVirtualPageNumber( 0 ); // multiple uses; no way to store the real sheet #
1054
1055 // This is a new schematic file so make sure it has a unique ID.
1056 if( !saveCopy && tmpFn.GetFullPath() != screen->GetFileName() )
1057 screen->AssignNewUuid();
1058
1059 success &= saveSchematicFile( screens.GetSheet( i ), tmpFn.GetFullPath() );
1060 }
1061
1062 if( success )
1063 m_autoSaveRequired = false;
1064
1065 // One or more of the modified sheets did not save correctly so update the auto save file.
1066 if( !aSaveAs && !success )
1067 success &= updateAutoSaveFile();
1068
1069 if( aSaveAs && success )
1070 LockFile( Schematic().RootScreen()->GetFileName() );
1071
1072 if( updateFileHistory )
1073 UpdateFileHistory( Schematic().RootScreen()->GetFileName() );
1074
1075 // Save the sheet name map to the project file
1076 std::vector<FILE_INFO_PAIR>& sheets = Prj().GetProjectFile().GetSheets();
1077 sheets.clear();
1078
1079 for( SCH_SHEET_PATH& sheetPath : Schematic().GetSheets() )
1080 {
1081 SCH_SHEET* sheet = sheetPath.Last();
1082
1083 wxCHECK2( sheet, continue );
1084
1085 // Use the schematic UUID for the root sheet.
1086 if( sheet->IsRootSheet() )
1087 {
1088 screen = sheet->GetScreen();
1089
1090 wxCHECK2( screen, continue );
1091
1092 sheets.emplace_back( std::make_pair( screen->GetUuid(), sheet->GetName() ) );
1093 }
1094 else
1095 {
1096 sheets.emplace_back( std::make_pair( sheet->m_Uuid, sheet->GetName() ) );
1097 }
1098 }
1099
1100 wxASSERT( filenameMap.count( Schematic().RootScreen() ) );
1101 wxFileName projectPath( filenameMap.at( Schematic().RootScreen() ) );
1102 projectPath.SetExt( ProjectFileExtension );
1103
1104 if( Prj().IsNullProject() || ( aSaveAs && !saveCopy ) )
1105 {
1106 Prj().SetReadOnly( !createNewProject );
1107 GetSettingsManager()->SaveProjectAs( projectPath.GetFullPath() );
1108 }
1109 else if( saveCopy && createNewProject )
1110 {
1111 GetSettingsManager()->SaveProjectCopy( projectPath.GetFullPath() );
1112 }
1113 else
1114 {
1115 RecordERCExclusions(); // ensure ERC Exclusions list is up to date
1117 }
1118
1119 if( !Kiface().IsSingle() )
1120 {
1121 WX_STRING_REPORTER backupReporter( &msg );
1122
1123 if( !GetSettingsManager()->TriggerBackupIfNeeded( backupReporter ) )
1124 SetStatusText( msg, 0 );
1125 }
1126
1127 updateTitle();
1128
1130 m_infoBar->Dismiss();
1131
1132 return success;
1133}
1134
1135
1137{
1138 wxFileName tmpFileName = Schematic().Root().GetFileName();
1139 wxFileName fn = tmpFileName;
1140 wxFileName tmp;
1141 SCH_SCREENS screens( Schematic().Root() );
1142
1143 // Don't run autosave if content has not been modified
1144 if( !IsContentModified() )
1145 return true;
1146
1147 bool autoSaveOk = true;
1148
1149 if( fn.GetPath().IsEmpty() )
1150 tmp.AssignDir( Prj().GetProjectPath() );
1151 else
1152 tmp.AssignDir( fn.GetPath() );
1153
1154 if( !tmp.IsOk() )
1155 return false;
1156
1157 if( !IsWritable( tmp ) )
1158 return false;
1159
1160 wxString title = GetTitle(); // Save frame title, that can be modified by the save process
1161
1162 for( size_t i = 0; i < screens.GetCount(); i++ )
1163 {
1164 // Only create auto save files for the schematics that have been modified.
1165 if( !screens.GetScreen( i )->IsContentModified() )
1166 continue;
1167
1168 tmpFileName = fn = screens.GetScreen( i )->GetFileName();
1169
1170 // Auto save file name is the normal file name prefixed with GetAutoSavePrefix().
1171 fn.SetName( GetAutoSaveFilePrefix() + fn.GetName() );
1172
1173 if( saveSchematicFile( screens.GetSheet( i ), fn.GetFullPath() ) )
1174 {
1175 // This was only an auto-save, not a real save. Reset the modified flag.
1176 screens.GetScreen( i )->SetContentModified();
1177 }
1178 else
1179 {
1180 autoSaveOk = false;
1181 }
1182 }
1183
1184 if( autoSaveOk && updateAutoSaveFile() )
1185 {
1186 m_autoSaveRequired = false;
1187 m_autoSavePending = false;
1188
1189 if( !Kiface().IsSingle()
1190 && GetSettingsManager()->GetCommonSettings()->m_Backup.backup_on_autosave )
1191 {
1193 }
1194 }
1195
1196 SetTitle( title );
1197
1198 return autoSaveOk;
1199}
1200
1201
1202bool SCH_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType )
1203{
1204 wxFileName filename( aFileName );
1205 wxFileName newfilename;
1206 SCH_SHEET_LIST sheetList = Schematic().GetSheets();
1207 SCH_IO_MGR::SCH_FILE_T fileType = (SCH_IO_MGR::SCH_FILE_T) aFileType;
1208
1209 switch( fileType )
1210 {
1211 case SCH_IO_MGR::SCH_ALTIUM:
1212 case SCH_IO_MGR::SCH_CADSTAR_ARCHIVE:
1213 case SCH_IO_MGR::SCH_EAGLE:
1214 // We insist on caller sending us an absolute path, if it does not, we say it's a bug.
1215 wxCHECK_MSG( filename.IsAbsolute(), false,
1216 wxT( "Import schematic: path is not absolute!" ) );
1217
1218 if( !LockFile( aFileName ) )
1219 {
1220 wxString msg;
1221 msg.Printf( _( "Schematic '%s' is already open." ), filename.GetFullName() );
1222
1223 if( !OverrideLock( this, msg ) )
1224 return false;
1225 }
1226
1227 try
1228 {
1229 SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( fileType ) );
1230 DIALOG_HTML_REPORTER errorReporter( this );
1231 WX_PROGRESS_REPORTER progressReporter( this, _( "Importing Schematic" ), 1 );
1232
1233 pi->SetReporter( errorReporter.m_Reporter );
1234 pi->SetProgressReporter( &progressReporter );
1235 Schematic().SetRoot( pi->Load( aFileName, &Schematic() ) );
1236
1237 if( errorReporter.m_Reporter->HasMessage() )
1238 {
1239 errorReporter.m_Reporter->Flush(); // Build HTML messages
1240 errorReporter.ShowModal();
1241 }
1242
1243 // Non-KiCad schematics do not use a drawing-sheet (or if they do, it works differently
1244 // to KiCad), so set it to an empty one
1246 drawingSheet.SetEmptyLayout();
1247 BASE_SCREEN::m_DrawingSheetFileName = "empty.kicad_wks";
1248
1249 newfilename.SetPath( Prj().GetProjectPath() );
1250 newfilename.SetName( Prj().GetProjectName() );
1251 newfilename.SetExt( KiCadSchematicFileExtension );
1252
1253 SetScreen( GetCurrentSheet().LastScreen() );
1254
1255 Schematic().Root().SetFileName( newfilename.GetFullName() );
1256 GetScreen()->SetFileName( newfilename.GetFullPath() );
1258
1260
1261 // Only perform the dangling end test on root sheet.
1263
1265
1268 SyncView();
1269
1271 updateTitle();
1272 }
1273 catch( const IO_ERROR& ioe )
1274 {
1275 // Do not leave g_RootSheet == NULL because it is expected to be
1276 // a valid sheet. Therefore create a dummy empty root sheet and screen.
1277 CreateScreens();
1279
1280 wxString msg = wxString::Format( _( "Error loading schematic '%s'." ), aFileName );
1281 DisplayErrorMessage( this, msg, ioe.What() );
1282
1283 msg.Printf( _( "Failed to load '%s'." ), aFileName );
1284 SetMsgPanel( wxEmptyString, msg );
1285
1286 return false;
1287 }
1288 catch( const std::exception& exc )
1289 {
1290 CreateScreens();
1292
1293 wxString msg = wxString::Format( _( "Unhandled exception occurred loading schematic "
1294 "'%s'." ), aFileName );
1295 DisplayErrorMessage( this, msg, exc.what() );
1296
1297 msg.Printf( _( "Failed to load '%s'." ), aFileName );
1298 SetMsgPanel( wxEmptyString, msg );
1299
1300 return false;
1301 }
1302
1303 return true;
1304
1305 default:
1306 return false;
1307 }
1308}
1309
1310
1312{
1313 SCH_SCREENS screenList( Schematic().Root() );
1314
1315 // Save any currently open and modified project files.
1316 for( SCH_SCREEN* screen = screenList.GetFirst(); screen; screen = screenList.GetNext() )
1317 {
1318 SIMULATOR_FRAME* simFrame = (SIMULATOR_FRAME*) Kiway().Player( FRAME_SIMULATOR, false );
1319
1320 // Simulator must be closed before loading another schematic, otherwise it may crash.
1321 // If there are any changes in the simulator the user will be prompted to save them.
1322 if( simFrame && !simFrame->Close() )
1323 return false;
1324
1325 if( screen->IsContentModified() )
1326 {
1327 if( !HandleUnsavedChanges( this, _( "The current schematic has been modified. "
1328 "Save changes?" ),
1329 [&]() -> bool
1330 {
1331 return SaveProject();
1332 } ) )
1333 {
1334 return false;
1335 }
1336 }
1337 }
1338
1339 return true;
1340}
1341
1342
1344{
1345 wxFileName tmpFn = Prj().GetProjectFullName();
1346 wxFileName autoSaveFileName( tmpFn.GetPath(), getAutoSaveFileName() );
1347
1348 wxLogTrace( traceAutoSave, "Creating auto save file %s", autoSaveFileName.GetFullPath() );
1349
1350 wxCHECK( autoSaveFileName.IsDirWritable(), false );
1351
1352 wxFileName fn;
1353 SCH_SCREENS screens( Schematic().Root() );
1354 std::vector< wxString > autoSavedFiles;
1355
1356 for( size_t i = 0; i < screens.GetCount(); i++ )
1357 {
1358 // Only create auto save files for the schematics that have been modified.
1359 if( !screens.GetScreen( i )->IsContentModified() )
1360 continue;
1361
1362 fn = screens.GetScreen( i )->GetFileName();
1363
1364 // Auto save file name is the normal file name prefixed with GetAutoSavePrefix().
1365 fn.SetName( GetAutoSaveFilePrefix() + fn.GetName() );
1366 autoSavedFiles.emplace_back( fn.GetFullPath() );
1367 }
1368
1369 wxTextFile autoSaveFile( autoSaveFileName.GetFullPath() );
1370
1371 if( autoSaveFileName.FileExists() && !wxRemoveFile( autoSaveFileName.GetFullPath() ) )
1372 {
1373 wxLogTrace( traceAutoSave, "Error removing auto save file %s",
1374 autoSaveFileName.GetFullPath() );
1375
1376 return false;
1377 }
1378
1379 // No modified sheet files to save.
1380 if( autoSavedFiles.empty() )
1381 return true;
1382
1383 if( !autoSaveFile.Create() )
1384 return false;
1385
1386 for( const wxString& fileName : autoSavedFiles )
1387 {
1388 wxLogTrace( traceAutoSave, "Adding auto save file %s to %s",
1389 fileName, autoSaveFileName.GetName() );
1390 autoSaveFile.AddLine( fileName );
1391 }
1392
1393 if( !autoSaveFile.Write() )
1394 return false;
1395
1396 wxLogTrace( traceAutoSave, "Auto save file '%s' written", autoSaveFileName.GetFullName() );
1397
1398 return true;
1399}
1400
1401
1402void SCH_EDIT_FRAME::CheckForAutoSaveFile( const wxFileName& aFileName )
1403{
1404 if( !IsGUI() )
1405 return;
1406
1407 wxCHECK_RET( aFileName.IsOk(), wxT( "Invalid file name!" ) );
1408
1409 wxLogTrace( traceAutoSave,
1410 wxT( "Checking for auto save file " ) + aFileName.GetFullPath() );
1411
1412 if( !aFileName.FileExists() )
1413 return;
1414
1415 wxString msg = _(
1416 "Well this is potentially embarrassing!\n"
1417 "It appears that the last time you were editing one or more of the schematic files\n"
1418 "were not saved properly. Do you wish to restore the last saved edits you made?" );
1419
1420 int response = wxMessageBox( msg, Pgm().App().GetAppDisplayName(), wxYES_NO | wxICON_QUESTION,
1421 this );
1422
1423 wxTextFile autoSaveFile( aFileName.GetFullPath() );
1424
1425 if( !autoSaveFile.Open() )
1426 {
1427 msg.Printf( _( "The file '%s` could not be opened.\n"
1428 "Manual recovery of automatically saved files is required." ),
1429 aFileName.GetFullPath() );
1430
1431 wxMessageBox( msg, Pgm().App().GetAppDisplayName(), wxOK | wxICON_EXCLAMATION, this );
1432 return;
1433 }
1434
1435 if( response == wxYES )
1436 {
1437 wxArrayString unrecoveredFiles;
1438
1439 for( wxString fn = autoSaveFile.GetFirstLine(); !autoSaveFile.Eof();
1440 fn = autoSaveFile.GetNextLine() )
1441 {
1442 wxFileName recoveredFn = fn;
1443 wxString tmp = recoveredFn.GetName();
1444
1445 // Strip "_autosave-" prefix from the auto save file name.
1446 tmp.Replace( GetAutoSaveFilePrefix(), wxT( "" ), false );
1447 recoveredFn.SetName( tmp );
1448
1449 wxFileName backupFn = recoveredFn;
1450
1451 backupFn.SetExt( backupFn.GetExt() + BackupFileSuffix );
1452
1453 wxLogTrace( traceAutoSave, wxT( "Recovering auto save file:\n"
1454 " Original file: '%s'\n"
1455 " Backup file: '%s'\n"
1456 " Auto save file: '%s'" ),
1457 recoveredFn.GetFullPath(), backupFn.GetFullPath(), fn );
1458
1459 if( !wxFileExists( fn ) )
1460 {
1461 unrecoveredFiles.Add( recoveredFn.GetFullPath() );
1462 }
1463 // Attempt to back up the last schematic file before overwriting it with the auto
1464 // save file.
1465 else if( !wxCopyFile( recoveredFn.GetFullPath(), backupFn.GetFullPath() ) )
1466 {
1467 unrecoveredFiles.Add( recoveredFn.GetFullPath() );
1468 }
1469 // Attempt to replace last saved file with auto save file
1470 else if( !wxRenameFile( fn, recoveredFn.GetFullPath() ) )
1471 {
1472 unrecoveredFiles.Add( recoveredFn.GetFullPath() );
1473 }
1474 }
1475
1476 if( !unrecoveredFiles.IsEmpty() )
1477 {
1478 msg = _( "The following automatically saved file(s) could not be restored\n" );
1479
1480 for( size_t i = 0; i < unrecoveredFiles.GetCount(); i++ )
1481 msg += unrecoveredFiles[i] + wxT( "\n" );
1482
1483 msg += _( "Manual recovery will be required to restore the file(s) above." );
1484 wxMessageBox( msg, Pgm().App().GetAppDisplayName(), wxOK | wxICON_EXCLAMATION,
1485 this );
1486 }
1487 }
1488 else
1489 {
1490 wxArrayString unremovedFiles;
1491
1492 for( wxString fn = autoSaveFile.GetFirstLine(); !autoSaveFile.Eof();
1493 fn = autoSaveFile.GetNextLine() )
1494 {
1495 wxLogTrace( traceAutoSave, wxT( "Removing auto save file " ) + fn );
1496
1497 if( wxFileExists( fn ) && !wxRemoveFile( fn ) )
1498 unremovedFiles.Add( fn );
1499 }
1500
1501 if( !unremovedFiles.IsEmpty() )
1502 {
1503 msg = _( "The following automatically saved file(s) could not be removed\n" );
1504
1505 for( size_t i = 0; i < unremovedFiles.GetCount(); i++ )
1506 msg += unremovedFiles[i] + wxT( "\n" );
1507
1508 msg += _( "Manual removal will be required for the file(s) above." );
1509 wxMessageBox( msg, Pgm().App().GetAppDisplayName(), wxOK | wxICON_EXCLAMATION,
1510 this );
1511 }
1512 }
1513
1514 // Remove the auto save master file.
1515 wxLogTrace( traceAutoSave, wxT( "Removing auto save file '%s'" ), aFileName.GetFullPath() );
1516
1517 if( !wxRemoveFile( aFileName.GetFullPath() ) )
1518 {
1519 msg.Printf( _( "The automatic save master file\n"
1520 "'%s'\n"
1521 "could not be deleted." ), aFileName.GetFullPath() );
1522
1523 wxMessageDialog dlg( this, msg, Pgm().App().GetAppDisplayName(),
1524 wxOK | wxICON_EXCLAMATION | wxCENTER );
1525
1526 dlg.SetExtendedMessage(
1527 _( "This file must be manually removed or the auto save feature will be\n"
1528 "shown every time the schematic editor is launched." ) );
1529
1530 dlg.ShowModal();
1531 }
1532}
1533
1534
1536{
1537 static wxString autoSaveFileName( wxT( "#auto_saved_files#" ) );
1538
1539 return autoSaveFileName;
1540}
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
static TOOL_ACTION zoomFitScreen
Definition: actions.h:99
bool IsContentModified() const
Definition: base_screen.h:60
void SetVirtualPageNumber(int aPageNumber)
Definition: base_screen.h:76
static wxString m_DrawingSheetFileName
the name of the drawing sheet file, or empty to use the default drawing sheet
Definition: base_screen.h:85
void SetContentModified(bool aModified=true)
Definition: base_screen.h:59
Class DIALOG_HTML_REPORTER.
WX_HTML_REPORT_BOX * m_Reporter
int ShowQuasiModal()
Handle the graphic items list to draw/plot the frame and title block.
Definition: ds_data_model.h:39
static DS_DATA_MODEL & GetTheInstance()
static function: returns the instance of DS_DATA_MODEL used in the application
void LoadWindowState(const wxString &aFileName)
virtual void ClearUndoRedoList()
Clear the undo and redo list using ClearUndoORRedoList()
SETTINGS_MANAGER * GetSettingsManager() const
WX_INFOBAR * m_infoBar
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)
Checks if aFileName can be written.
static wxString GetAutoSaveFilePrefix()
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.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:85
const KIID m_Uuid
Definition: eda_item.h:492
EE_TYPE OfType(KICAD_T aType) const
Definition: sch_rtree.h:238
bool GetCreateNewProject() const
Gets if this hook has attached controls to a dialog box.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:76
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
virtual const wxString Problem() const
what was the problem?
Definition: exceptions.cpp:46
APP_SETTINGS_BASE * KifaceSettings() const
Definition: kiface_base.h:95
bool IsSingle() const
Is this KIFACE running under single_top?
Definition: kiface_base.h:107
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
Definition: kiway_holder.h:53
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:394
< Helper widget to select whether a new project should be created for a file when saving
static wxWindow * Create(wxWindow *aParent)
static REPORTER & GetInstance()
Definition: reporter.cpp:117
static wxString GetDefaultUserProjectsPath()
Gets the default path we point users to create projects.
Definition: paths.cpp:139
A small class to help profiling.
Definition: profile.h:47
void Show(std::ostream &aStream=std::cerr)
Print the elapsed time (in a suitable unit) to a stream.
Definition: profile.h:103
std::vector< FILE_INFO_PAIR > & GetSheets()
Definition: project_file.h:86
virtual void SetReadOnly(bool aReadOnly=true)
Definition: project.h:127
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
Definition: project.cpp:120
virtual PROJECT_FILE & GetProjectFile() const
Definition: project.h:149
virtual void SetElem(ELEM_T aIndex, _ELEM *aElem)
Definition: project.cpp:294
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:305
@ ELEM_SYMBOL_LIB_TABLE
Definition: project.h:211
@ ELEM_SCH_SYMBOL_LIBS
Definition: project.h:208
void Reset()
Initialize this schematic to a blank one, unloading anything existing.
Definition: schematic.cpp:56
CONNECTION_GRAPH * ConnectionGraph() const override
Definition: schematic.h:132
void FixupJunctions()
Add junctions to this schematic where required.
Definition: schematic.cpp:650
SCH_SHEET_LIST GetSheets() const override
Builds and returns an updated schematic hierarchy TODO: can this be cached?
Definition: schematic.h:86
void SetRoot(SCH_SHEET *aRootSheet)
Initialize the schematic with a new root sheet.
Definition: schematic.cpp:109
void SetProject(PROJECT *aPrj)
Definition: schematic.cpp:81
SCH_SCREEN * RootScreen() const
Helper to retrieve the screen of the root sheet.
Definition: schematic.cpp:122
SCH_SHEET & Root() const
Definition: schematic.h:91
PROJECT & Prj() const override
Return a reference to the project this schematic is part of.
Definition: schematic.h:76
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
Handle actions specific to the schematic editor.
bool IsContentModified() const override
Get if the current schematic has been modified but not saved.
const wxString & getAutoSaveFileName() const
bool LoadSheetFromFile(SCH_SHEET *aSheet, SCH_SHEET_PATH *aHierarchy, const wxString &aFileName)
Load a the KiCad schematic file aFileName into the sheet aSheet.
Definition: sheet.cpp:106
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag and update other data struc...
bool OpenProjectFiles(const std::vector< wxString > &aFileSet, int aCtl=0) override
Open a project or set of files given by aFileList.
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
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.
bool doAutoSave() override
Save the schematic files that have been modified and not yet saved.
void RecordERCExclusions()
Scan existing markers and record data from any that are Excluded.
void HardRedraw() override
Rebuild the GAL and redraw the screen.
void OnAppendProject(wxCommandEvent &event)
SCH_SHEET_PATH & GetCurrentSheet() const
SCHEMATIC & Schematic() const
void updateTitle()
Set the main window title bar text.
bool saveSchematicFile(SCH_SHEET *aSheet, const wxString &aSavePath)
Save aSheet to a schematic file.
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 RecalculateConnections(SCH_CLEANUP_FLAGS aCleanupFlags)
Generate the connection data for the entire schematic hierarchy.
void SetSheetNumberAndCount()
Set the m_ScreenNumber and m_NumberOfScreens members for screens.
bool importFile(const wxString &aFileName, int aFileType)
Load the given filename but sets the path to the current project path.
bool AddSheetAndUpdateDisplay(const wxString aFullFileName)
Add a sheet file into the current sheet and updates display.
void initScreenZoom()
Initialize the zoom value of the current screen and mark the screen as zoom-initialized.
void OnImportProject(wxCommandEvent &event)
void ResolveERCExclusions()
Update markers to match recorded exclusions.
bool AppendSchematic()
Import a KiCad schematic into the current sheet.
virtual void CheckForAutoSaveFile(const wxFileName &aFileName) override
This overloaded version checks if the auto save master file "#auto_saved_files#" exists and recovers ...
void UpdateItem(EDA_ITEM *aItem, bool isAddOrDelete=false, bool aUpdateRtree=false) override
Mark an item for refresh.
void UpdateHierarchyNavigator()
Update the hierarchy navigation tree and history.
wxString GetCurrentFileName() const override
Get the full filename + path of the currently opened file in the frame.
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.
static const wxString GetFileExtension(SCH_FILE_T aFileType)
Return the schematic file extension for aFileType.
Definition: sch_io_mgr.cpp:126
static SCH_FILE_T GuessPluginTypeFromSchPath(const wxString &aSchematicPath)
Return a plugin type given a schematic using the file extension of aSchematicPath.
Definition: sch_io_mgr.cpp:174
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:147
Handle actions specific to the schematic editor.
Helper object to release a SCH_PLUGIN in the context of a potential thrown exception through its dest...
Definition: sch_io_mgr.h:535
Base class that schematic file and library loading and saving plugins should derive from.
Definition: sch_io_mgr.h:155
virtual const wxString & GetError() const
Return an error string to the caller.
Definition: sch_plugin.cpp:177
virtual void Save(const wxString &aFileName, SCH_SHEET *aSheet, SCHEMATIC *aSchematic, const STRING_UTF8_MAP *aProperties=nullptr)
Write aSchematic to a storage file in a format that this SCH_PLUGIN implementation knows about,...
Definition: sch_plugin.cpp:59
virtual SCH_SHEET * Load(const wxString &aFileName, SCHEMATIC *aSchematic, SCH_SHEET *aAppendToMe=nullptr, const STRING_UTF8_MAP *aProperties=nullptr)
Load information from some input file format that this SCH_PLUGIN implementation knows about,...
Definition: sch_plugin.cpp:51
virtual const wxString GetFileExtension() const =0
Return the file extension for the SCH_PLUGIN.
virtual void SetReporter(REPORTER *aReporter)
Set an optional reporter for warnings/errors.
Definition: sch_io_mgr.h:168
virtual void SetProgressReporter(PROGRESS_REPORTER *aReporter)
Set an optional progress reporter.
Definition: sch_io_mgr.h:173
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition: sch_screen.h:662
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 BuildClientSheetPathList()
built the list of sheet paths sharing a screen for each screen in use
size_t GetCount() const
Definition: sch_screen.h:667
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:178
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()
Gets the full RTree, usually for iterating.
Definition: sch_screen.h:109
const wxString & GetFileName() const
Definition: sch_screen.h:144
const KIID & GetUuid() const
Definition: sch_screen.h:526
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
Definition: sch_screen.cpp:110
int GetFileFormatVersionAtLoad() const
Definition: sch_screen.h:129
void AssignNewUuid()
Definition: sch_screen.h:528
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.
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:57
void SetFileName(const wxString &aFilename)
Definition: sch_sheet.h:308
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition: sch_sheet.h:302
bool IsRootSheet() const
Definition: sch_sheet.cpp:194
wxString GetName() const
Definition: sch_sheet.h:103
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:106
void SaveProjectAs(const wxString &aFullPath, PROJECT *aProject=nullptr)
Sets the currently loaded project path and saves it (pointers remain valid) Note that this will not m...
void SaveProjectCopy(const wxString &aFullPath, PROJECT *aProject=nullptr)
Saves a copy of the current project under the given path.
bool SaveProject(const wxString &aFullPath=wxEmptyString, PROJECT *aProject=nullptr)
Saves a loaded project.
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Loads a project or sets up a new project with a specified path.
bool UnloadProject(PROJECT *aProject, bool aSave=true)
Saves, unloads and unregisters the given PROJECT.
bool TriggerBackupIfNeeded(REPORTER &aReporter) const
Calls BackupProject if a new backup is needed according to the current backup policy.
The SIMULATOR_FRAME holds the main user-interface for running simulations.
A collection of SYMBOL_LIB objects.
int GetLibraryCount()
static void SetLibNamesAndPaths(PROJECT *aProject, const wxString &aPaths, const wxArrayString &aNames)
static void GetLibNamesAndPaths(PROJECT *aProject, wxString *aPaths, wxArrayString *aNames=nullptr)
TOOL_MANAGER * m_toolManager
Definition: tools_holder.h:170
@ MODEL_RELOAD
Model changes (required full reload)
Definition: tool_base.h:80
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:142
static void ResolvePossibleSymlinks(wxFileName &aFilename)
Definition: wx_filename.cpp:92
void Flush()
Build the HTML messages page.
bool HasMessage() const override
Returns true if the reporter client is non-empty.
void RemoveAllButtons()
Remove all the buttons that have been added by the user.
Definition: wx_infobar.cpp:289
@ OUTDATED_SAVE
OUTDATED_SAVE Messages that should be cleared on save.
void Dismiss() override
Dismisses the infobar and updates the containing layout and AUI manager (if one is provided).
Definition: wx_infobar.cpp:175
void AddCloseButton(const wxString &aTooltip=_("Hide this message."))
Add the default close button to the infobar on the right side.
Definition: wx_infobar.cpp:279
MESSAGE_TYPE GetMessageType() const
Definition: wx_infobar.h:100
void ShowMessage(const wxString &aMessage, int aFlags=wxICON_INFORMATION) override
Show the info bar with the provided message and icon.
Definition: wx_infobar.cpp:142
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:164
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:380
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:300
bool OverrideLock(wxWindow *aParent, const wxString &aMessage)
Display a dialog indicating the file is already open, with an option to reset the lock.
Definition: confirm.cpp:175
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
Definition: confirm.cpp:352
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:260
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:325
bool IsGUI()
Determine if the application is running with a GUI.
Definition: confirm.cpp:40
This file is part of the common library.
#define _(s)
void Reset() override
@ FRAME_SIMULATOR
Definition: frame_type.h:38
const std::string BackupFileSuffix
wxString formatWildcardExt(const wxString &aWildcard)
Format wildcard extension to support case sensitive file dialogs.
const std::string LegacyProjectFileExtension
const std::string ProjectFileExtension
const std::string KiCadSchematicFileExtension
const std::string LegacySymbolLibFileExtension
wxString KiCadSchematicFileWildcard()
wxString CadstarSchematicArchiveFileWildcard()
wxString AltiumSchematicFileWildcard()
wxString EagleSchematicFileWildcard()
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.
#define KICTL_CREATE
caller thinks requested project files may not exist.
Definition: kiway_player.h:82
@ LAYER_BUS
Definition: layer_ids.h:345
void SetShutdownBlockReason(wxWindow *aWindow, const wxString &aReason)
Sets the block reason why the window/application is preventing OS shutdown.
Definition: gtk/app.cpp:83
bool RegisterApplicationRestart(const wxString &aCommandLine)
Registers the application for restart with the OS with the given command line string to pass as args.
Definition: gtk/app.cpp:58
see class PGM_BASE
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
@ GLOBAL_CLEANUP
#define SEXPR_SCHEMATIC_FILE_VERSION
Schematic file version.
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:111
MODEL3D_FORMAT_TYPE fileType(const char *aFileName)
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
Definition: ki_exception.h:175
Definition for symbol library class.
wxLogTrace helper definitions.
@ SCH_SHEET_T
Definition: typeinfo.h:158
@ SCH_BUS_BUS_ENTRY_T
Definition: typeinfo.h:145
Definition of file extensions used in Kicad.