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-2022 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/sim_plot_frame.h>
53#include <tool/actions.h>
54#include <tool/tool_manager.h>
57#include <trace_helpers.h>
58#include <widgets/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 // Allow the schematic to be saved to new file format without making any edits.
436 OnModify();
437 }
438 else // S-expression schematic.
439 {
441 {
444 m_infoBar->ShowMessage( _( "This file was created by an older version of KiCad. "
445 "It will be converted to the new format when saved." ),
447 }
448
449 for( SCH_SCREEN* screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )
450 screen->UpdateLocalLibSymbolLinks();
451
452 // Restore all of the loaded symbol and sheet instances from the root sheet.
453 if( Schematic().RootScreen()->GetFileFormatVersionAtLoad() < 20221002 )
454 sheetList.UpdateSymbolInstances( Schematic().RootScreen()->GetSymbolInstances() );
455
456 if( Schematic().RootScreen()->GetFileFormatVersionAtLoad() < 20221110 )
457 sheetList.UpdateSheetInstances( Schematic().RootScreen()->GetSheetInstances() );
458 }
459
461
462 SetScreen( GetCurrentSheet().LastScreen() );
463
464 // Migrate conflicting bus definitions
465 // TODO(JE) This should only run once based on schematic file version
466 if( Schematic().ConnectionGraph()->GetBusesNeedingMigration().size() > 0 )
467 {
468 DIALOG_MIGRATE_BUSES dlg( this );
469 dlg.ShowQuasiModal();
471 OnModify();
472 }
473
476 }
477
478 // Load any exclusions from the project file
480
483
486
487 // re-create junctions if needed. Eeschema optimizes wires by merging
488 // colinear segments. If a schematic is saved without a valid
489 // cache library or missing installed libraries, this can cause connectivity errors
490 // unless junctions are added.
491 if( schFileType == SCH_IO_MGR::SCH_LEGACY )
493
494 SyncView();
496
498
500 UpdateTitle();
501 m_toolManager->GetTool<SCH_NAVIGATE_TOOL>()->ResetHistory();
502
503 wxFileName fn = Prj().AbsolutePath( GetScreen()->GetFileName() );
504
505 if( fn.FileExists() && !fn.IsFileWritable() )
506 {
509 m_infoBar->ShowMessage( _( "Schematic is read only." ),
511 }
512
513#ifdef PROFILE
514 openFiles.Show();
515#endif
516
517 return true;
518}
519
520
522{
523 SCH_SCREEN* screen = GetScreen();
524
525 if( !screen )
526 {
527 wxLogError( wxT( "Document not ready, cannot import" ) );
528 return false;
529 }
530
531 // open file chooser dialog
532 wxString path = wxPathOnly( Prj().GetProjectFullName() );
533
534 wxFileDialog dlg( this, _( "Insert Schematic" ), path, wxEmptyString,
535 KiCadSchematicFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
536
537 if( dlg.ShowModal() == wxID_CANCEL )
538 return false;
539
540 return AddSheetAndUpdateDisplay( dlg.GetPath() );
541}
542
543
544bool SCH_EDIT_FRAME::AddSheetAndUpdateDisplay( const wxString aFullFileName )
545{
546 if( !LoadSheetFromFile( GetCurrentSheet().Last(), &GetCurrentSheet(), aFullFileName ) )
547 return false;
548
551
552 SyncView();
553 OnModify();
554 HardRedraw(); // Full reinit of the current screen and the display.
555
557
558 return true;
559}
560
561
562void SCH_EDIT_FRAME::OnAppendProject( wxCommandEvent& event )
563{
564 if( GetScreen() && GetScreen()->IsModified() )
565 {
566 wxString msg = _( "This operation cannot be undone.\n\n"
567 "Do you want to save the current document before proceeding?" );
568
569 if( IsOK( this, msg ) )
570 SaveProject();
571 }
572
574}
575
576
577void SCH_EDIT_FRAME::OnImportProject( wxCommandEvent& aEvent )
578{
579 if( !AskToSaveChanges() )
580 return;
581
582 // Set the project location if none is set or if we are running in standalone mode
583 bool setProject = Prj().GetProjectFullName().IsEmpty() || Kiface().IsSingle();
584 wxString path = wxPathOnly( Prj().GetProjectFullName() );
585
586 std::list<std::pair<const wxString, const SCH_IO_MGR::SCH_FILE_T>> loaders;
587
588 // Import Altium schematic files.
589 loaders.emplace_back( AltiumSchematicFileWildcard(), SCH_IO_MGR::SCH_ALTIUM );
590
591 // Import CADSTAR Schematic Archive files.
592 loaders.emplace_back( CadstarSchematicArchiveFileWildcard(), SCH_IO_MGR::SCH_CADSTAR_ARCHIVE );
593
594 // Import Eagle schematic files.
595 loaders.emplace_back( EagleSchematicFileWildcard(), SCH_IO_MGR::SCH_EAGLE );
596
597 wxString fileFilters;
598 wxString allWildcards;
599
600 for( std::pair<const wxString, const SCH_IO_MGR::SCH_FILE_T>& loader : loaders )
601 {
602 if( !fileFilters.IsEmpty() )
603 fileFilters += wxChar( '|' );
604
605 fileFilters += wxGetTranslation( loader.first );
606
607 SCH_PLUGIN::SCH_PLUGIN_RELEASER plugin( SCH_IO_MGR::FindPlugin( loader.second ) );
608 wxCHECK( plugin, /*void*/ );
609 allWildcards += "*." + formatWildcardExt( plugin->GetFileExtension() ) + ";";
610 }
611
612 fileFilters = _( "All supported formats|" ) + allWildcards + "|" + fileFilters;
613
614 wxFileDialog dlg( this, _( "Import Schematic" ), path, wxEmptyString, fileFilters,
615 wxFD_OPEN | wxFD_FILE_MUST_EXIST ); // TODO
616
617 if( dlg.ShowModal() == wxID_CANCEL )
618 return;
619
620 // Don't leave dangling pointers to previously-opened document.
621 m_toolManager->GetTool<EE_SELECTION_TOOL>()->ClearSelection();
623
624 if( setProject )
625 {
626 Schematic().SetProject( nullptr );
627 GetSettingsManager()->UnloadProject( &Prj(), false );
628
629 Schematic().Reset();
630
631 wxFileName projectFn( dlg.GetPath() );
632 projectFn.SetExt( ProjectFileExtension );
633 GetSettingsManager()->LoadProject( projectFn.GetFullPath() );
634
635 Schematic().SetProject( &Prj() );
636 }
637
638 wxFileName fn = dlg.GetPath();
639
640 SCH_IO_MGR::SCH_FILE_T pluginType = SCH_IO_MGR::SCH_FILE_T::SCH_FILE_UNKNOWN;
641
642 for( std::pair<const wxString, const SCH_IO_MGR::SCH_FILE_T>& loader : loaders )
643 {
644 if( fn.GetExt().CmpNoCase( SCH_IO_MGR::GetFileExtension( loader.second ) ) == 0 )
645 {
646 pluginType = loader.second;
647 break;
648 }
649 }
650
651 if( pluginType == SCH_IO_MGR::SCH_FILE_T::SCH_FILE_UNKNOWN )
652 {
653 wxLogError( _( "Unexpected file extension: '%s'." ), fn.GetExt() );
654 return;
655 }
656
657 importFile( dlg.GetPath(), pluginType );
658
660}
661
662
663bool SCH_EDIT_FRAME::saveSchematicFile( SCH_SHEET* aSheet, const wxString& aSavePath )
664{
665 wxString msg;
666 wxFileName schematicFileName;
667 wxFileName oldFileName;
668 bool success;
669
670 SCH_SCREEN* screen = aSheet->GetScreen();
671
672 wxCHECK( screen, false );
673
674 // Construct the name of the file to be saved
675 schematicFileName = Prj().AbsolutePath( aSavePath );
676 oldFileName = schematicFileName;
677
678 // Write through symlinks, don't replace them
679 WX_FILENAME::ResolvePossibleSymlinks( schematicFileName );
680
681 if( !IsWritable( schematicFileName ) )
682 return false;
683
684 wxString tempFile = wxFileName::CreateTempFileName( "eeschema" );
685
686 // Save
687 wxLogTrace( traceAutoSave, "Saving file " + schematicFileName.GetFullPath() );
688
691
692 SCH_IO_MGR::SCH_FILE_T pluginType = SCH_IO_MGR::GuessPluginTypeFromSchPath(
693 schematicFileName.GetFullPath() );
694 SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( pluginType ) );
695
696 try
697 {
698 pi->Save( tempFile, aSheet, &Schematic() );
699 success = true;
700 }
701 catch( const IO_ERROR& ioe )
702 {
703 msg.Printf( _( "Error saving schematic file '%s'.\n%s" ),
704 schematicFileName.GetFullPath(),
705 ioe.What() );
706 DisplayError( this, msg );
707
708 msg.Printf( _( "Failed to create temporary file '%s'." ),
709 tempFile );
710 SetMsgPanel( wxEmptyString, msg );
711
712 // In case we started a file but didn't fully write it, clean up
713 wxRemoveFile( tempFile );
714
715 success = false;
716 }
717
718 if( success )
719 {
720 // Replace the original with the temporary file we just wrote
721 success = wxRenameFile( tempFile, schematicFileName.GetFullPath() );
722
723 if( !success )
724 {
725 msg.Printf( _( "Error saving schematic file '%s'.\n"
726 "Failed to rename temporary file '%s'." ),
727 schematicFileName.GetFullPath(),
728 tempFile );
729 DisplayError( this, msg );
730
731 msg.Printf( _( "Failed to rename temporary file '%s'." ),
732 tempFile );
733 SetMsgPanel( wxEmptyString, msg );
734 }
735 }
736
737 if( success )
738 {
739 // Delete auto save file.
740 wxFileName autoSaveFileName = schematicFileName;
741 autoSaveFileName.SetName( GetAutoSaveFilePrefix() + schematicFileName.GetName() );
742
743 if( autoSaveFileName.FileExists() )
744 {
745 wxLogTrace( traceAutoSave,
746 wxT( "Removing auto save file <" ) + autoSaveFileName.GetFullPath() +
747 wxT( ">" ) );
748
749 wxRemoveFile( autoSaveFileName.GetFullPath() );
750 }
751
752 screen->SetContentModified( false );
753
754 msg.Printf( _( "File '%s' saved." ), screen->GetFileName() );
755 SetStatusText( msg, 0 );
756 }
757 else
758 {
759 DisplayError( this, _( "File write operation failed." ) );
760 }
761
762 return success;
763}
764
765
766bool SCH_EDIT_FRAME::SaveProject( bool aSaveAs )
767{
768 wxString msg;
769 SCH_SCREEN* screen;
770 SCH_SCREENS screens( Schematic().Root() );
771 bool saveCopy = aSaveAs && !Kiface().IsSingle();
772 bool success = true;
773 bool updateFileType = false;
774 bool createNewProject = false;
775
776 // I want to see it in the debugger, show me the string! Can't do that with wxFileName.
777 wxString fileName = Prj().AbsolutePath( Schematic().Root().GetFileName() );
778 wxFileName fn = fileName;
779
780 // Path to save each screen to: will be the stored filename by default, but is overwritten by
781 // a Save As Copy operation.
782 std::unordered_map<SCH_SCREEN*, wxString> filenameMap;
783
784 // Handle "Save As" and saving a new project/schematic for the first time in standalone
785 if( Prj().IsNullProject() || aSaveAs )
786 {
787 // Null project should only be possible in standalone mode.
788 wxCHECK( Kiface().IsSingle() || aSaveAs, false );
789
790 wxFileName newFileName;
791 wxFileName savePath( Prj().GetProjectFullName() );
792
793 if( !savePath.IsOk() || !savePath.IsDirWritable() )
794 {
795 savePath = GetMruPath();
796
797 if( !savePath.IsOk() || !savePath.IsDirWritable() )
799 }
800
801 if( savePath.HasExt() )
802 savePath.SetExt( KiCadSchematicFileExtension );
803 else
804 savePath.SetName( wxEmptyString );
805
806 wxFileDialog dlg( this, _( "Schematic Files" ), savePath.GetPath(),
807 savePath.GetFullName(), KiCadSchematicFileWildcard(),
808 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
809
810#if wxCHECK_VERSION( 3, 1, 7 )
811 FILEDLG_HOOK_SAVE_PROJECT newProjectHook;
812 bool checkHook = false;
813#endif
814
815 if( Kiface().IsSingle() || aSaveAs )
816 {
817#if wxCHECK_VERSION( 3, 1, 7 )
818 dlg.SetCustomizeHook( newProjectHook );
819 checkHook = true;
820#else
821 // Add a "Create a project" checkbox in standalone mode and one isn't loaded
822 dlg.SetExtraControlCreator( &LEGACYFILEDLG_SAVE_PROJECT::Create );
823#endif
824 }
825
826 if( dlg.ShowModal() == wxID_CANCEL )
827 return false;
828
829 newFileName = dlg.GetPath();
830 newFileName.SetExt( KiCadSchematicFileExtension );
831
832 if( ( !newFileName.DirExists() && !newFileName.Mkdir() ) ||
833 !newFileName.IsDirWritable() )
834 {
835 msg.Printf( _( "Folder '%s' could not be created.\n\n"
836 "Make sure you have write permissions and try again." ),
837 newFileName.GetPath() );
838
839 wxMessageDialog dlgBadPath( this, msg, _( "Error" ),
840 wxOK | wxICON_EXCLAMATION | wxCENTER );
841
842 dlgBadPath.ShowModal();
843 return false;
844 }
845
846#if wxCHECK_VERSION( 3, 1, 7 )
847 if( checkHook )
848 createNewProject = newProjectHook.GetCreateNewProject();
849#else
850 if( wxWindow* ec = dlg.GetExtraControl() )
851 createNewProject = static_cast<LEGACYFILEDLG_SAVE_PROJECT*>( ec )->GetValue();
852#endif
853
854 if( !saveCopy )
855 {
856 Schematic().Root().SetFileName( newFileName.GetFullName() );
857 Schematic().RootScreen()->SetFileName( newFileName.GetFullPath() );
858 }
859 else
860 {
861 filenameMap[Schematic().RootScreen()] = newFileName.GetFullPath();
862 }
863
864 // Set the base path to all new sheets.
865 for( size_t i = 0; i < screens.GetCount(); i++ )
866 {
867 screen = screens.GetScreen( i );
868
869 wxCHECK2( screen, continue );
870
871 // The root screen file name has already been set.
872 if( screen == Schematic().RootScreen() )
873 continue;
874
875 wxFileName tmp = screen->GetFileName();
876
877 // Assume existing sheet files are being reused and do not save them to the new
878 // path. Maybe in the future, add a user option to copy schematic files to the
879 // new project path.
880 if( tmp.FileExists() )
881 continue;
882
883 if( tmp.GetPath().IsEmpty() )
884 {
885 tmp.SetPath( newFileName.GetPath() );
886 }
887 else if( tmp.GetPath() == fn.GetPath() )
888 {
889 tmp.SetPath( newFileName.GetPath() );
890 }
891 else if( tmp.GetPath().StartsWith( fn.GetPath() ) )
892 {
893 // NOTE: this hasn't been tested because the sheet properties dialog no longer
894 // allows adding a path specifier in the file name field.
895 wxString newPath = newFileName.GetPath();
896 newPath += tmp.GetPath().Right( fn.GetPath().Length() );
897 tmp.SetPath( newPath );
898 }
899
900 wxLogTrace( tracePathsAndFiles,
901 wxT( "Moving schematic from '%s' to '%s'." ),
902 screen->GetFileName(),
903 tmp.GetFullPath() );
904
905 if( !tmp.DirExists() && !tmp.Mkdir() )
906 {
907 msg.Printf( _( "Folder '%s' could not be created.\n\n"
908 "Make sure you have write permissions and try again." ),
909 newFileName.GetPath() );
910
911 wxMessageDialog dlgBadFilePath( this, msg, _( "Error" ),
912 wxOK | wxICON_EXCLAMATION | wxCENTER );
913
914 dlgBadFilePath.ShowModal();
915 return false;
916 }
917
918 if( saveCopy )
919 filenameMap[screen] = tmp.GetFullPath();
920 else
921 screen->SetFileName( tmp.GetFullPath() );
922 }
923
924 // Attempt to make sheet file name paths relative to the new root schematic path.
926
927 for( SCH_SHEET_PATH& sheet : sheets )
928 {
929 if( sheet.Last()->IsRootSheet() )
930 continue;
931
932 sheet.MakeFilePathRelativeToParentSheet();
933 }
934 }
935
936 if( filenameMap.empty() || !saveCopy )
937 {
938 for( size_t i = 0; i < screens.GetCount(); i++ )
939 filenameMap[screens.GetScreen( i )] = screens.GetScreen( i )->GetFileName();
940 }
941
942 // Warn user on potential file overwrite. This can happen on shared sheets.
943 wxArrayString overwrittenFiles;
944
945 for( size_t i = 0; i < screens.GetCount(); i++ )
946 {
947 screen = screens.GetScreen( i );
948
949 wxCHECK2( screen, continue );
950
951 // Convert legacy schematics file name extensions for the new format.
952 wxFileName tmpFn = filenameMap[screen];
953
954 if( !tmpFn.IsOk() )
955 continue;
956
957 if( tmpFn.GetExt() == KiCadSchematicFileExtension )
958 continue;
959
960 tmpFn.SetExt( KiCadSchematicFileExtension );
961
962 if( tmpFn.FileExists() )
963 overwrittenFiles.Add( tmpFn.GetFullPath() );
964 }
965
966 if( !overwrittenFiles.IsEmpty() )
967 {
968 for( const wxString& overwrittenFile : overwrittenFiles )
969 {
970 if( msg.IsEmpty() )
971 msg = overwrittenFile;
972 else
973 msg += "\n" + overwrittenFile;
974 }
975
976 wxRichMessageDialog dlg( this, _( "Saving will overwrite existing files." ),
977 _( "Save Warning" ),
978 wxOK | wxCANCEL | wxCANCEL_DEFAULT | wxCENTER |
979 wxICON_EXCLAMATION );
980 dlg.ShowDetailedText( _( "The following files will be overwritten:\n\n" ) + msg );
981 dlg.SetOKCancelLabels( wxMessageDialog::ButtonLabel( _( "Overwrite Files" ) ),
982 wxMessageDialog::ButtonLabel( _( "Abort Project Save" ) ) );
983
984 if( dlg.ShowModal() == wxID_CANCEL )
985 return false;
986 }
987
988 screens.BuildClientSheetPathList();
989
990 for( size_t i = 0; i < screens.GetCount(); i++ )
991 {
992 screen = screens.GetScreen( i );
993
994 wxCHECK2( screen, continue );
995
996 // Convert legacy schematics file name extensions for the new format.
997 wxFileName tmpFn = filenameMap[screen];
998
999 if( tmpFn.IsOk() && tmpFn.GetExt() != KiCadSchematicFileExtension )
1000 {
1001 updateFileType = true;
1002 tmpFn.SetExt( KiCadSchematicFileExtension );
1003
1004 for( EDA_ITEM* item : screen->Items().OfType( SCH_SHEET_T ) )
1005 {
1006 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
1007 wxFileName sheetFileName = sheet->GetFileName();
1008
1009 if( !sheetFileName.IsOk() || sheetFileName.GetExt() == KiCadSchematicFileExtension )
1010 continue;
1011
1012 sheetFileName.SetExt( KiCadSchematicFileExtension );
1013 sheet->SetFileName( sheetFileName.GetFullPath() );
1014 UpdateItem( sheet );
1015 }
1016
1017 filenameMap[screen] = tmpFn.GetFullPath();
1018
1019 if( !saveCopy )
1020 screen->SetFileName( tmpFn.GetFullPath() );
1021 }
1022
1023 std::vector<SCH_SHEET_PATH>& sheets = screen->GetClientSheetPaths();
1024
1025 if( sheets.size() == 1 )
1026 screen->SetVirtualPageNumber( 1 );
1027 else
1028 screen->SetVirtualPageNumber( 0 ); // multiple uses; no way to store the real sheet #
1029
1030 // This is a new schematic file so make sure it has a unique ID.
1031 if( !saveCopy && tmpFn.GetFullPath() != screen->GetFileName() )
1032 screen->AssignNewUuid();
1033
1034 success &= saveSchematicFile( screens.GetSheet( i ), tmpFn.GetFullPath() );
1035 }
1036
1037 if( success )
1038 m_autoSaveRequired = false;
1039
1040 // One or more of the modified sheets did not save correctly so update the auto save file.
1041 if( !aSaveAs && !success )
1042 success &= updateAutoSaveFile();
1043
1044 if( aSaveAs && success )
1045 LockFile( Schematic().RootScreen()->GetFileName() );
1046
1047 if( updateFileType )
1048 UpdateFileHistory( Schematic().RootScreen()->GetFileName() );
1049
1050 // Save the sheet name map to the project file
1051 std::vector<FILE_INFO_PAIR>& sheets = Prj().GetProjectFile().GetSheets();
1052 sheets.clear();
1053
1054 for( SCH_SHEET_PATH& sheetPath : Schematic().GetSheets() )
1055 {
1056 SCH_SHEET* sheet = sheetPath.Last();
1057
1058 wxCHECK2( sheet, continue );
1059
1060 // Use the schematic UUID for the root sheet.
1061 if( sheet->IsRootSheet() )
1062 {
1063 screen = sheet->GetScreen();
1064
1065 wxCHECK2( screen, continue );
1066
1067 sheets.emplace_back( std::make_pair( screen->GetUuid(), sheet->GetName() ) );
1068 }
1069 else
1070 {
1071 sheets.emplace_back( std::make_pair( sheet->m_Uuid, sheet->GetName() ) );
1072 }
1073 }
1074
1075 wxASSERT( filenameMap.count( Schematic().RootScreen() ) );
1076 wxFileName projectPath( filenameMap.at( Schematic().RootScreen() ) );
1077 projectPath.SetExt( ProjectFileExtension );
1078
1079 if( Prj().IsNullProject() || ( aSaveAs && !saveCopy ) )
1080 {
1081 Prj().SetReadOnly( !createNewProject );
1082 GetSettingsManager()->SaveProjectAs( projectPath.GetFullPath() );
1083 }
1084 else if( saveCopy && createNewProject )
1085 {
1086 GetSettingsManager()->SaveProjectCopy( projectPath.GetFullPath() );
1087 }
1088 else
1089 {
1090 RecordERCExclusions(); // ensure ERC Exclusions list is up to date
1092 }
1093
1094 if( !Kiface().IsSingle() )
1095 {
1096 WX_STRING_REPORTER backupReporter( &msg );
1097
1098 if( !GetSettingsManager()->TriggerBackupIfNeeded( backupReporter ) )
1099 SetStatusText( msg, 0 );
1100 }
1101
1102 UpdateTitle();
1103
1105 m_infoBar->Dismiss();
1106
1107 return success;
1108}
1109
1110
1112{
1113 wxFileName tmpFileName = Schematic().Root().GetFileName();
1114 wxFileName fn = tmpFileName;
1115 wxFileName tmp;
1116 SCH_SCREENS screens( Schematic().Root() );
1117
1118 // Don't run autosave if content has not been modified
1119 if( !IsContentModified() )
1120 return true;
1121
1122 bool autoSaveOk = true;
1123
1124 if( fn.GetPath().IsEmpty() )
1125 tmp.AssignDir( Prj().GetProjectPath() );
1126 else
1127 tmp.AssignDir( fn.GetPath() );
1128
1129 if( !tmp.IsOk() )
1130 return false;
1131
1132 if( !IsWritable( tmp ) )
1133 return false;
1134
1135 wxString title = GetTitle(); // Save frame title, that can be modified by the save process
1136
1137 for( size_t i = 0; i < screens.GetCount(); i++ )
1138 {
1139 // Only create auto save files for the schematics that have been modified.
1140 if( !screens.GetScreen( i )->IsContentModified() )
1141 continue;
1142
1143 tmpFileName = fn = screens.GetScreen( i )->GetFileName();
1144
1145 // Auto save file name is the normal file name prefixed with GetAutoSavePrefix().
1146 fn.SetName( GetAutoSaveFilePrefix() + fn.GetName() );
1147
1148 if( saveSchematicFile( screens.GetSheet( i ), fn.GetFullPath() ) )
1149 {
1150 // This was only an auto-save, not a real save. Reset the modified flag.
1151 screens.GetScreen( i )->SetContentModified();
1152 }
1153 else
1154 {
1155 autoSaveOk = false;
1156 }
1157 }
1158
1159 if( autoSaveOk && updateAutoSaveFile() )
1160 {
1161 m_autoSaveRequired = false;
1162 m_autoSavePending = false;
1163
1164 if( !Kiface().IsSingle()
1165 && GetSettingsManager()->GetCommonSettings()->m_Backup.backup_on_autosave )
1166 {
1168 }
1169 }
1170
1171 SetTitle( title );
1172
1173 return autoSaveOk;
1174}
1175
1176
1177bool SCH_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType )
1178{
1179 wxFileName filename( aFileName );
1180 wxFileName newfilename;
1181 SCH_SHEET_LIST sheetList = Schematic().GetSheets();
1182 SCH_IO_MGR::SCH_FILE_T fileType = (SCH_IO_MGR::SCH_FILE_T) aFileType;
1183
1184 switch( fileType )
1185 {
1186 case SCH_IO_MGR::SCH_ALTIUM:
1187 case SCH_IO_MGR::SCH_CADSTAR_ARCHIVE:
1188 case SCH_IO_MGR::SCH_EAGLE:
1189 // We insist on caller sending us an absolute path, if it does not, we say it's a bug.
1190 wxCHECK_MSG( filename.IsAbsolute(), false,
1191 wxT( "Import schematic: path is not absolute!" ) );
1192
1193 if( !LockFile( aFileName ) )
1194 {
1195 wxString msg;
1196 msg.Printf( _( "Schematic '%s' is already open." ), filename.GetFullName() );
1197
1198 if( !OverrideLock( this, msg ) )
1199 return false;
1200 }
1201
1202 try
1203 {
1204 SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( fileType ) );
1205 DIALOG_HTML_REPORTER errorReporter( this );
1206 WX_PROGRESS_REPORTER progressReporter( this, _( "Importing Schematic" ), 1 );
1207
1208 pi->SetReporter( errorReporter.m_Reporter );
1209 pi->SetProgressReporter( &progressReporter );
1210 Schematic().SetRoot( pi->Load( aFileName, &Schematic() ) );
1211
1212 if( errorReporter.m_Reporter->HasMessage() )
1213 {
1214 errorReporter.m_Reporter->Flush(); // Build HTML messages
1215 errorReporter.ShowModal();
1216 }
1217
1218 // Non-KiCad schematics do not use a drawing-sheet (or if they do, it works differently
1219 // to KiCad), so set it to an empty one
1221 drawingSheet.SetEmptyLayout();
1222
1223 BASE_SCREEN::m_DrawingSheetFileName = "empty.kicad_wks";
1224 wxFileName layoutfn( Prj().GetProjectPath(), BASE_SCREEN::m_DrawingSheetFileName );
1225 wxFFile layoutfile;
1226
1227 if( layoutfile.Open( layoutfn.GetFullPath(), "wb" ) )
1228 {
1229 layoutfile.Write( DS_DATA_MODEL::EmptyLayout() );
1230 layoutfile.Close();
1231 }
1232
1233 newfilename.SetPath( Prj().GetProjectPath() );
1234 newfilename.SetName( Prj().GetProjectName() );
1235 newfilename.SetExt( KiCadSchematicFileExtension );
1236
1237 SetScreen( GetCurrentSheet().LastScreen() );
1238
1239 Schematic().Root().SetFileName( newfilename.GetFullName() );
1240 GetScreen()->SetFileName( newfilename.GetFullPath() );
1242
1243 // Only fix junctions for CADSTAR importer for now as it may cause issues with
1244 // other importers
1245 if( fileType == SCH_IO_MGR::SCH_CADSTAR_ARCHIVE )
1246 {
1248 }
1249
1251
1252 // Only perform the dangling end test on root sheet.
1254
1256
1259 SyncView();
1260
1262 UpdateTitle();
1263 }
1264 catch( const IO_ERROR& ioe )
1265 {
1266 // Do not leave g_RootSheet == NULL because it is expected to be
1267 // a valid sheet. Therefore create a dummy empty root sheet and screen.
1268 CreateScreens();
1270
1271 wxString msg = wxString::Format( _( "Error loading schematic '%s'." ), aFileName );
1272 DisplayErrorMessage( this, msg, ioe.What() );
1273
1274 msg.Printf( _( "Failed to load '%s'." ), aFileName );
1275 SetMsgPanel( wxEmptyString, msg );
1276
1277 return false;
1278 }
1279
1280 return true;
1281
1282 default:
1283 return false;
1284 }
1285}
1286
1287
1289{
1290 SCH_SCREENS screenList( Schematic().Root() );
1291
1292 // Save any currently open and modified project files.
1293 for( SCH_SCREEN* screen = screenList.GetFirst(); screen; screen = screenList.GetNext() )
1294 {
1295 SIM_PLOT_FRAME* simFrame = (SIM_PLOT_FRAME*) Kiway().Player( FRAME_SIMULATOR, false );
1296
1297 // Simulator must be closed before loading another schematic, otherwise it may crash.
1298 // If there are any changes in the simulator the user will be prompted to save them.
1299 if( simFrame && !simFrame->Close() )
1300 return false;
1301
1302 if( screen->IsContentModified() )
1303 {
1304 if( !HandleUnsavedChanges( this, _( "The current schematic has been modified. "
1305 "Save changes?" ),
1306 [&]() -> bool
1307 {
1308 return SaveProject();
1309 } ) )
1310 {
1311 return false;
1312 }
1313 }
1314 }
1315
1316 return true;
1317}
1318
1319
1321{
1322 wxFileName tmpFn = Prj().GetProjectFullName();
1323 wxFileName autoSaveFileName( tmpFn.GetPath(), getAutoSaveFileName() );
1324
1325 wxLogTrace( traceAutoSave, "Creating auto save file %s", autoSaveFileName.GetFullPath() );
1326
1327 wxCHECK( autoSaveFileName.IsDirWritable(), false );
1328
1329 wxFileName fn;
1330 SCH_SCREENS screens( Schematic().Root() );
1331 std::vector< wxString > autoSavedFiles;
1332
1333 for( size_t i = 0; i < screens.GetCount(); i++ )
1334 {
1335 // Only create auto save files for the schematics that have been modified.
1336 if( !screens.GetScreen( i )->IsContentModified() )
1337 continue;
1338
1339 fn = screens.GetScreen( i )->GetFileName();
1340
1341 // Auto save file name is the normal file name prefixed with GetAutoSavePrefix().
1342 fn.SetName( GetAutoSaveFilePrefix() + fn.GetName() );
1343 autoSavedFiles.emplace_back( fn.GetFullPath() );
1344 }
1345
1346 wxTextFile autoSaveFile( autoSaveFileName.GetFullPath() );
1347
1348 if( autoSaveFileName.FileExists() && !wxRemoveFile( autoSaveFileName.GetFullPath() ) )
1349 {
1350 wxLogTrace( traceAutoSave, "Error removing auto save file %s",
1351 autoSaveFileName.GetFullPath() );
1352
1353 return false;
1354 }
1355
1356 // No modified sheet files to save.
1357 if( autoSavedFiles.empty() )
1358 return true;
1359
1360 if( !autoSaveFile.Create() )
1361 return false;
1362
1363 for( const wxString& fileName : autoSavedFiles )
1364 {
1365 wxLogTrace( traceAutoSave, "Adding auto save file %s to %s",
1366 fileName, autoSaveFileName.GetName() );
1367 autoSaveFile.AddLine( fileName );
1368 }
1369
1370 if( !autoSaveFile.Write() )
1371 return false;
1372
1373 wxLogTrace( traceAutoSave, "Auto save file '%s' written", autoSaveFileName.GetFullName() );
1374
1375 return true;
1376}
1377
1378
1379void SCH_EDIT_FRAME::CheckForAutoSaveFile( const wxFileName& aFileName )
1380{
1381 wxCHECK_RET( aFileName.IsOk(), wxT( "Invalid file name!" ) );
1382
1383 wxLogTrace( traceAutoSave,
1384 wxT( "Checking for auto save file " ) + aFileName.GetFullPath() );
1385
1386 if( !aFileName.FileExists() )
1387 return;
1388
1389 wxString msg = _(
1390 "Well this is potentially embarrassing!\n"
1391 "It appears that the last time you were editing one or more of the schematic files\n"
1392 "were not saved properly. Do you wish to restore the last saved edits you made?" );
1393
1394 int response = wxMessageBox( msg, Pgm().App().GetAppDisplayName(), wxYES_NO | wxICON_QUESTION,
1395 this );
1396
1397 wxTextFile autoSaveFile( aFileName.GetFullPath() );
1398
1399 if( !autoSaveFile.Open() )
1400 {
1401 msg.Printf( _( "The file '%s` could not be opened.\n"
1402 "Manual recovery of automatically saved files is required." ),
1403 aFileName.GetFullPath() );
1404
1405 wxMessageBox( msg, Pgm().App().GetAppDisplayName(), wxOK | wxICON_EXCLAMATION, this );
1406 return;
1407 }
1408
1409 if( response == wxYES )
1410 {
1411 wxArrayString unrecoveredFiles;
1412
1413 for( wxString fn = autoSaveFile.GetFirstLine(); !autoSaveFile.Eof();
1414 fn = autoSaveFile.GetNextLine() )
1415 {
1416 wxFileName recoveredFn = fn;
1417 wxString tmp = recoveredFn.GetName();
1418
1419 // Strip "_autosave-" prefix from the auto save file name.
1420 tmp.Replace( GetAutoSaveFilePrefix(), wxT( "" ), false );
1421 recoveredFn.SetName( tmp );
1422
1423 wxFileName backupFn = recoveredFn;
1424
1425 backupFn.SetExt( backupFn.GetExt() + BackupFileSuffix );
1426
1427 wxLogTrace( traceAutoSave, wxT( "Recovering auto save file:\n"
1428 " Original file: '%s'\n"
1429 " Backup file: '%s'\n"
1430 " Auto save file: '%s'" ),
1431 recoveredFn.GetFullPath(), backupFn.GetFullPath(), fn );
1432
1433 if( !wxFileExists( fn ) )
1434 {
1435 unrecoveredFiles.Add( recoveredFn.GetFullPath() );
1436 }
1437 // Attempt to back up the last schematic file before overwriting it with the auto
1438 // save file.
1439 else if( !wxCopyFile( recoveredFn.GetFullPath(), backupFn.GetFullPath() ) )
1440 {
1441 unrecoveredFiles.Add( recoveredFn.GetFullPath() );
1442 }
1443 // Attempt to replace last saved file with auto save file
1444 else if( !wxRenameFile( fn, recoveredFn.GetFullPath() ) )
1445 {
1446 unrecoveredFiles.Add( recoveredFn.GetFullPath() );
1447 }
1448 }
1449
1450 if( !unrecoveredFiles.IsEmpty() )
1451 {
1452 msg = _( "The following automatically saved file(s) could not be restored\n" );
1453
1454 for( size_t i = 0; i < unrecoveredFiles.GetCount(); i++ )
1455 msg += unrecoveredFiles[i] + wxT( "\n" );
1456
1457 msg += _( "Manual recovery will be required to restore the file(s) above." );
1458 wxMessageBox( msg, Pgm().App().GetAppDisplayName(), wxOK | wxICON_EXCLAMATION,
1459 this );
1460 }
1461 }
1462 else
1463 {
1464 wxArrayString unremovedFiles;
1465
1466 for( wxString fn = autoSaveFile.GetFirstLine(); !autoSaveFile.Eof();
1467 fn = autoSaveFile.GetNextLine() )
1468 {
1469 wxLogTrace( traceAutoSave, wxT( "Removing auto save file " ) + fn );
1470
1471 if( wxFileExists( fn ) && !wxRemoveFile( fn ) )
1472 unremovedFiles.Add( fn );
1473 }
1474
1475 if( !unremovedFiles.IsEmpty() )
1476 {
1477 msg = _( "The following automatically saved file(s) could not be removed\n" );
1478
1479 for( size_t i = 0; i < unremovedFiles.GetCount(); i++ )
1480 msg += unremovedFiles[i] + wxT( "\n" );
1481
1482 msg += _( "Manual removal will be required for the file(s) above." );
1483 wxMessageBox( msg, Pgm().App().GetAppDisplayName(), wxOK | wxICON_EXCLAMATION,
1484 this );
1485 }
1486 }
1487
1488 // Remove the auto save master file.
1489 wxLogTrace( traceAutoSave, wxT( "Removing auto save file '%s'" ), aFileName.GetFullPath() );
1490
1491 if( !wxRemoveFile( aFileName.GetFullPath() ) )
1492 {
1493 msg.Printf( _( "The automatic save master file\n"
1494 "'%s'\n"
1495 "could not be deleted." ), aFileName.GetFullPath() );
1496
1497 wxMessageDialog dlg( this, msg, Pgm().App().GetAppDisplayName(),
1498 wxOK | wxICON_EXCLAMATION | wxCENTER );
1499
1500 dlg.SetExtendedMessage(
1501 _( "This file must be manually removed or the auto save feature will be\n"
1502 "shown every time the schematic editor is launched." ) );
1503
1504 dlg.ShowModal();
1505 }
1506}
1507
1508
1510{
1511 static wxString autoSaveFileName( wxT( "#auto_saved_files#" ) );
1512
1513 return autoSaveFileName;
1514}
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
static TOOL_ACTION zoomFitScreen
Definition: actions.h:98
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
static wxString EmptyLayout()
Return a string containing the empty layout shape.
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:494
EE_TYPE OfType(KICAD_T aType) const
Definition: sch_rtree.h:238
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:93
bool IsSingle() const
Is this KIFACE running under single_top?
Definition: kiface_base.h:105
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:126
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:148
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:210
@ ELEM_SCH_SYMBOL_LIBS
Definition: project.h:207
void Reset()
Initialize this schematic to a blank one, unloading anything existing.
Definition: schematic.cpp:51
CONNECTION_GRAPH * ConnectionGraph() const override
Definition: schematic.h:130
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:104
void SetProject(PROJECT *aPrj)
Definition: schematic.cpp:76
SCH_SCREEN * RootScreen() const
Helper to retrieve the screen of the root sheet.
Definition: schematic.cpp:117
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:105
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.
void UpdateTitle()
Set the main window title bar text.
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
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 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 RecomputeIntersheetRefs(bool autoplaceUninitialized=false)
Update the schematic's page reference map for all global labels, and refresh the labels so that they ...
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:608
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:613
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:499
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
Definition: sch_screen.cpp:106
int GetFileFormatVersionAtLoad() const
Definition: sch_screen.h:129
void AssignNewUuid()
Definition: sch_screen.h:501
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
void UpdateSheetInstances(const std::vector< SCH_SHEET_INSTANCE > &aSheetInstances)
Update all of the sheet instance information using aSheetInstances.
void UpdateSymbolInstances(const std::vector< SYMBOL_INSTANCE_REFERENCE > &aSymbolInstances)
Update all of the symbol instance information using aSymbolInstances.
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.
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:55
void SetFileName(const wxString &aFilename)
Definition: sch_sheet.h:306
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition: sch_sheet.h:300
bool IsRootSheet() const
Definition: sch_sheet.cpp:194
wxString GetName() const
Definition: sch_sheet.h:101
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:104
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.
Implementing SIM_PLOT_FRAME_BASE.
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:85
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: infobar.cpp:286
@ 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: infobar.cpp:175
void AddCloseButton(const wxString &aTooltip=_("Hide this message."))
Add the default close button to the infobar on the right side.
Definition: infobar.cpp:276
MESSAGE_TYPE GetMessageType() const
Definition: infobar.h:100
void ShowMessage(const wxString &aMessage, int aFlags=wxICON_INFORMATION) override
Show the info bar with the provided message and icon.
Definition: 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:342
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:280
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:155
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
Definition: confirm.cpp:320
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:240
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:299
This file is part of the common library.
#define _(s)
@ 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
@ NO_CLEANUP
@ GLOBAL_CLEANUP
#define SEXPR_SCHEMATIC_FILE_VERSION
Schematic file version.
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
Subclass of SIM_PLOT_FRAME_BASE, which is generated by wxFormBuilder.
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:111
FormatType 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.