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