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