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 <stambaughw@gmail.com>
6  * Copyright (C) 2013 CERN (www.cern.ch)
7  * Copyright (C) 1992-2020 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 <advanced_config.h>
28 #include <class_library.h>
29 #include <confirm.h>
30 #include <connection_graph.h>
31 #include <dialog_migrate_buses.h>
32 #include <dialog_symbol_remap.h>
33 #include <eeschema_settings.h>
34 #include <gestfich.h>
35 #include <id.h>
36 #include <kiface_i.h>
37 #include <kiplatform/app.h>
38 #include <pgm_base.h>
39 #include <profile.h>
40 #include <project/project_file.h>
41 #include <project_rescue.h>
42 #include <reporter.h>
43 #include <richio.h>
44 #include <sch_edit_frame.h>
46 #include <sch_file_versions.h>
47 #include <sch_sheet.h>
48 #include <sch_sheet_path.h>
49 #include <schematic.h>
51 #include <tool/actions.h>
52 #include <tool/tool_manager.h>
54 #include <trace_helpers.h>
55 #include <widgets/infobar.h>
58 #include <wx/ffile.h>
60 #include <paths.h>
61 
62 bool SCH_EDIT_FRAME::SaveEEFile( SCH_SHEET* aSheet, bool aSaveUnderNewName )
63 {
64  wxString msg;
65  wxFileName schematicFileName;
66  bool success;
67 
68  if( aSheet == NULL )
69  aSheet = GetCurrentSheet().Last();
70 
71  SCH_SCREEN* screen = aSheet->GetScreen();
72 
73  wxCHECK( screen, false );
74 
75  // If no name exists in the window yet - save as new.
76  if( screen->GetFileName().IsEmpty() )
77  aSaveUnderNewName = true;
78 
79  // Construct the name of the file to be saved
80  schematicFileName = Prj().AbsolutePath( screen->GetFileName() );
81 
82  if( aSaveUnderNewName )
83  {
84  wxFileName savePath( Prj().GetProjectFullName() );
85 
86  if( !savePath.IsOk() || !savePath.IsDirWritable() )
87  {
88  savePath = GetMruPath();
89 
90  if( !savePath.IsOk() || !savePath.IsDirWritable() )
92  }
93 
94  wxFileDialog dlg( this, _( "Schematic Files" ), savePath.GetPath(),
95  schematicFileName.GetFullName(), KiCadSchematicFileWildcard(),
96  wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
97 
98  if( dlg.ShowModal() == wxID_CANCEL )
99  return false;
100 
101  schematicFileName = dlg.GetPath();
102 
103  if( schematicFileName.GetExt().IsEmpty() )
104  schematicFileName.SetExt( KiCadSchematicFileExtension );
105  }
106 
107  if( !IsWritable( schematicFileName ) )
108  return false;
109 
110  wxFileName tempFile( schematicFileName );
111  tempFile.SetName( wxT( "." ) + tempFile.GetName() );
112  tempFile.SetExt( tempFile.GetExt() + wxT( "$" ) );
113 
114  // Save
115  wxLogTrace( traceAutoSave,
116  wxT( "Saving file <" ) + schematicFileName.GetFullPath() + wxT( ">" ) );
117 
118  SCH_IO_MGR::SCH_FILE_T pluginType = SCH_IO_MGR::GuessPluginTypeFromSchPath(
119  schematicFileName.GetFullPath() );
120  SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( pluginType ) );
121 
122  try
123  {
124  pi->Save( tempFile.GetFullPath(), aSheet, &Schematic() );
125  success = true;
126  }
127  catch( const IO_ERROR& ioe )
128  {
129  msg.Printf( _( "Error saving schematic file \"%s\".\n%s" ),
130  schematicFileName.GetFullPath(), ioe.What() );
131  DisplayError( this, msg );
132 
133  msg.Printf( _( "Failed to create temporary file \"%s\"" ), tempFile.GetFullPath() );
134  SetMsgPanel( wxEmptyString, msg );
135 
136  // In case we started a file but didn't fully write it, clean up
137  wxRemoveFile( tempFile.GetFullPath() );
138 
139  success = false;
140  }
141 
142  if( success )
143  {
144  // Replace the original with the temporary file we just wrote
145  success = wxRenameFile( tempFile.GetFullPath(), schematicFileName.GetFullPath() );
146 
147  if( !success )
148  {
149  msg.Printf( _( "Error saving schematic file \"%s\".\n"
150  "Failed to rename temporary file %s" ),
151  schematicFileName.GetFullPath(), tempFile.GetFullPath() );
152  DisplayError( this, msg );
153 
154  msg.Printf( _( "Failed to rename temporary file \"%s\"" ), tempFile.GetFullPath() );
155  SetMsgPanel( wxEmptyString, msg );
156  }
157  }
158 
159  if( success )
160  {
161  // Delete auto save file.
162  wxFileName autoSaveFileName = schematicFileName;
163  autoSaveFileName.SetName( GetAutoSaveFilePrefix() + schematicFileName.GetName() );
164 
165  if( autoSaveFileName.FileExists() )
166  {
167  wxLogTrace( traceAutoSave,
168  wxT( "Removing auto save file <" ) + autoSaveFileName.GetFullPath() +
169  wxT( ">" ) );
170 
171  wxRemoveFile( autoSaveFileName.GetFullPath() );
172  }
173 
174  // Update the screen and frame info and reset the lock file.
175  if( aSaveUnderNewName )
176  {
177  screen->SetFileName( schematicFileName.GetFullPath() );
178  aSheet->SetFileName( schematicFileName.GetFullPath() );
179  LockFile( schematicFileName.GetFullPath() );
180  }
181 
182  screen->ClrSave();
183  screen->ClrModify();
184  UpdateTitle();
185 
186  msg.Printf( _( "File \"%s\" saved." ), screen->GetFileName() );
187  SetStatusText( msg, 0 );
188  }
189  else
190  {
191  DisplayError( this, _( "File write operation failed." ) );
192  }
193 
194  return success;
195 }
196 
197 
198 void SCH_EDIT_FRAME::Save_File( bool doSaveAs )
199 {
200  if( doSaveAs )
201  {
202  if( SaveEEFile( NULL, true ) )
203  {
204  SCH_SCREEN* screen = GetScreen();
205 
206  wxCHECK( screen, /* void */ );
207 
208  wxFileName fn = screen->GetFileName();
209 
210  if( fn.GetExt() == LegacySchematicFileExtension )
212 
213  // If we are saving under a new name, and don't have a real project yet, create one
214  fn.SetExt( ProjectFileExtension );
215 
216  if( fn.IsDirWritable() && !fn.FileExists() )
217  {
218  Prj().SetReadOnly( false );
219  GetSettingsManager()->SaveProjectAs( fn.GetFullPath() );
220  }
221  }
222  }
223  else
224  {
225  SaveEEFile( NULL );
226  }
227 
228  UpdateTitle();
229 }
230 
231 
232 bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
233 {
234  // implement the pseudo code from KIWAY_PLAYER.h:
235  wxString msg;
236 
237  auto cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
238 
239  // This is for python:
240  if( aFileSet.size() != 1 )
241  {
242  msg.Printf( "Eeschema:%s() takes only a single filename.", __WXFUNCTION__ );
243  DisplayError( this, msg );
244  return false;
245  }
246 
247  wxString fullFileName( aFileSet[0] );
248 
249  // We insist on caller sending us an absolute path, if it does not, we say it's a bug.
250  wxASSERT_MSG( wxFileName( fullFileName ).IsAbsolute(), wxT( "Path is not absolute!" ) );
251 
252  if( !LockFile( fullFileName ) )
253  {
254  msg.Printf( _( "Schematic file \"%s\" is already open." ), fullFileName );
255  DisplayError( this, msg );
256  return false;
257  }
258 
259  if( !AskToSaveChanges() )
260  return false;
261 
262  PROF_COUNTER openFiles( "OpenProjectFile" );
263 
264  wxFileName pro = fullFileName;
265  pro.SetExt( ProjectFileExtension );
266 
267  bool is_new = !wxFileName::IsFileReadable( fullFileName );
268 
269  // If its a non-existent schematic and caller thinks it exists
270  if( is_new && !( aCtl & KICTL_CREATE ) )
271  {
272  // notify user that fullFileName does not exist, ask if user wants to create it.
273  msg.Printf( _( "Schematic \"%s\" does not exist. Do you wish to create it?" ),
274  fullFileName );
275 
276  if( !IsOK( this, msg ) )
277  return false;
278  }
279 
280  // Loading a complex project and build data can be time
281  // consumming, so display a busy cursor
282  wxBusyCursor dummy;
283 
284  // unload current project file before loading new
285  {
286  SetScreen( nullptr );
288  CreateScreens();
289  }
290 
291  SetStatusText( wxEmptyString );
292  m_infoBar->Dismiss();
293 
294  SCH_IO_MGR::SCH_FILE_T schFileType = SCH_IO_MGR::GuessPluginTypeFromSchPath( fullFileName );
295 
296  // PROJECT::SetProjectFullName() is an impactful function. It should only be
297  // called under carefully considered circumstances.
298 
299  // The calling code should know not to ask me here to change projects unless
300  // it knows what consequences that will have on other KIFACEs running and using
301  // this same PROJECT. It can be very harmful if that calling code is stupid.
302 
303  // NOTE: The calling code should never call this in hosted (non-standalone) mode with a
304  // different project than what has been loaded by the manager frame. This will crash.
305 
306  bool differentProject = pro.GetFullPath() != Prj().GetProjectFullName();
307 
308  if( differentProject )
309  {
310  if( !Prj().IsNullProject() )
312 
313  Schematic().SetProject( nullptr );
314  GetSettingsManager()->UnloadProject( &Prj(), false );
315 
316  GetSettingsManager()->LoadProject( pro.GetFullPath() );
317 
318  wxFileName legacyPro( pro );
319  legacyPro.SetExt( LegacyProjectFileExtension );
320 
321  // Do not allow saving a project if one doesn't exist. This normally happens if we are
322  // standalone and opening a schematic that has been moved from its project folder.
323  if( !pro.Exists() && !legacyPro.Exists() && !( aCtl & KICTL_CREATE ) )
324  Prj().SetReadOnly();
325 
326  CreateScreens();
327  }
328 
329  if( schFileType == SCH_IO_MGR::SCH_LEGACY )
330  {
331  // Don't reload the symbol libraries if we are just launching Eeschema from KiCad again.
332  // They are already saved in the kiface project object.
333  if( differentProject || !Prj().GetElem( PROJECT::ELEM_SCH_PART_LIBS ) )
334  {
335  // load the libraries here, not in SCH_SCREEN::Draw() which is a context
336  // that will not tolerate DisplayError() dialog since we're already in an
337  // event handler in there.
338  // And when a schematic file is loaded, we need these libs to initialize
339  // some parameters (links to PART LIB, dangling ends ...)
341  Prj().SchLibs();
342  }
343  }
344  else
345  {
346  // No legacy symbol libraries including the cache are loaded with the new file format.
348  }
349 
350  // Load the symbol library table, this will be used forever more.
352  Prj().SchSymbolLibTable();
353 
354  // Load project settings after schematic has been set up with the project link, since this will
355  // update some of the needed schematic settings such as drawing defaults
357 
358  wxFileName rfn( GetCurrentFileName() );
359  rfn.MakeRelativeTo( Prj().GetProjectPath() );
360  LoadWindowState( rfn.GetFullPath() );
361 
362  KIPLATFORM::APP::SetShutdownBlockReason( this, _( "Schematic file changes are unsaved" ) );
363 
364  if( Kiface().IsSingle() )
365  {
367  }
368 
369  if( is_new )
370  {
371  // mark new, unsaved file as modified.
372  GetScreen()->SetModify();
373  GetScreen()->SetFileName( fullFileName );
374  }
375  else
376  {
377  // This will rename the file if there is an autosave and the user want to recover.
378  CheckForAutoSaveFile( fullFileName );
379 
380  SetScreen( nullptr );
381 
382  SCH_PLUGIN* plugin = SCH_IO_MGR::FindPlugin( schFileType );
383  SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( plugin );
384 
385  try
386  {
387  Schematic().SetRoot( pi->Load( fullFileName, &Schematic() ) );
388 
389  if( !pi->GetError().IsEmpty() )
390  {
391  DisplayErrorMessage( this,
392  _( "The entire schematic could not be loaded. Errors "
393  "occurred attempting to load \nhierarchical sheet "
394  "schematics." ),
395  pi->GetError() );
396  }
397  }
398  catch( const IO_ERROR& ioe )
399  {
400  // Do not leave g_RootSheet == NULL because it is expected to be
401  // a valid sheet. Therefore create a dummy empty root sheet and screen.
402  CreateScreens();
404 
405  msg.Printf( _( "Error loading schematic file \"%s\".\n%s" ),
406  fullFileName, ioe.What() );
407  DisplayError( this, msg );
408 
409  msg.Printf( _( "Failed to load \"%s\"" ), fullFileName );
410  SetMsgPanel( wxEmptyString, msg );
411 
412  return false;
413  }
414 
415  // It's possible the schematic parser fixed errors due to bugs so warn the user
416  // that the schematic has been fixed (modified).
417  SCH_SHEET_LIST sheetList = Schematic().GetSheets();
418 
419  if( sheetList.IsModified() )
420  {
421  DisplayInfoMessage( this,
422  _( "An error was found when loading the schematic that has "
423  "been automatically fixed. Please save the schematic to "
424  "repair the broken file or it may not be usable with other "
425  "versions of KiCad." ) );
426  }
427 
428  if( sheetList.AllSheetPageNumbersEmpty() )
429  sheetList.SetInitialPageNumbers();
430 
431  UpdateFileHistory( fullFileName );
432 
433  SCH_SCREENS schematic( Schematic().Root() );
434 
435  // LIB_ID checks and symbol rescue only apply to the legacy file formats.
436  if( schFileType == SCH_IO_MGR::SCH_LEGACY )
437  {
438  // Convert old projects over to use symbol library table.
439  if( schematic.HasNoFullyDefinedLibIds() )
440  {
441  DIALOG_SYMBOL_REMAP dlgRemap( this );
442 
443  dlgRemap.ShowQuasiModal();
444  }
445  else
446  {
447  // Double check to ensure no legacy library list entries have been
448  // added to the projec file symbol library list.
449  wxString paths;
450  wxArrayString libNames;
451 
452  PART_LIBS::LibNamesAndPaths( &Prj(), false, &paths, &libNames );
453 
454  if( !libNames.IsEmpty() )
455  {
457  {
458  wxRichMessageDialog invalidLibDlg(
459  this,
460  _( "Illegal entry found in project file symbol library list." ),
461  _( "Project Load Warning" ),
462  wxOK | wxCENTER | wxICON_EXCLAMATION );
463  invalidLibDlg.ShowDetailedText(
464  _( "Symbol libraries defined in the project file symbol library "
465  "list are no longer supported and will be removed.\n\nThis may "
466  "cause broken symbol library links under certain conditions." ) );
467  invalidLibDlg.ShowCheckBox( _( "Do not show this dialog again." ) );
468  invalidLibDlg.ShowModal();
470  !invalidLibDlg.IsCheckBoxChecked();
471  }
472 
473  libNames.Clear();
474  paths.Clear();
475  PART_LIBS::LibNamesAndPaths( &Prj(), true, &paths, &libNames );
476  }
477 
478  if( !cfg || !cfg->m_RescueNeverShow )
479  {
481  editor->RescueSymbolLibTableProject( false );
482  }
483  }
484 
485  // Update all symbol library links for all sheets.
486  schematic.UpdateSymbolLinks();
487 
490  m_infoBar->ShowMessage( _( "This file was created by an older version of KiCad. "
491  "It will be converted to the new format when saved." ),
492  wxICON_WARNING, WX_INFOBAR::MESSAGE_TYPE::OUTDATED_SAVE );
493 
494  // Legacy schematic can have duplicate time stamps so fix that before converting
495  // to the s-expression format.
496  schematic.ReplaceDuplicateTimeStamps();
497 
498  // Allow the schematic to be saved to new file format without making any edits.
499  OnModify();
500  }
501  else // S-expression schematic.
502  {
504  {
507  m_infoBar->ShowMessage( _( "This file was created by an older version of KiCad. "
508  "It will be converted to the new format when saved." ),
509  wxICON_WARNING, WX_INFOBAR::MESSAGE_TYPE::OUTDATED_SAVE );
510  }
511 
512  for( SCH_SCREEN* screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )
513  screen->UpdateLocalLibSymbolLinks();
514 
515  // Restore all of the loaded symbol and sheet instances from the root sheet.
516  sheetList.UpdateSymbolInstances( Schematic().RootScreen()->GetSymbolInstances() );
517  sheetList.UpdateSheetInstances( Schematic().RootScreen()->GetSheetInstances() );
518  }
519 
521 
522  SetScreen( GetCurrentSheet().LastScreen() );
523 
524  // Migrate conflicting bus definitions
525  // TODO(JE) This should only run once based on schematic file version
526  if( Schematic().ConnectionGraph()->GetBusesNeedingMigration().size() > 0 )
527  {
528  DIALOG_MIGRATE_BUSES dlg( this );
529  dlg.ShowQuasiModal();
531  OnModify();
532  }
533 
536  }
537 
538  // Load any exclusions from the project file
540 
541  initScreenZoom();
543 
545 
546  // re-create junctions if needed. Eeschema optimizes wires by merging
547  // colinear segments. If a schematic is saved without a valid
548  // cache library or missing installed libraries, this can cause connectivity errors
549  // unless junctions are added.
550  FixupJunctions();
551 
552  SyncView();
554 
555  UpdateTitle();
556 
557  wxFileName fn = Prj().AbsolutePath( GetScreen()->GetFileName() );
558 
559  if( fn.FileExists() && !fn.IsFileWritable() )
560  {
563  m_infoBar->ShowMessage( _( "Schematic file is read only." ), wxICON_WARNING );
564  }
565 
566 #ifdef PROFILE
567  openFiles.Show();
568 #endif
569 
570  return true;
571 }
572 
573 
575 {
576  wxString fullFileName;
577  SCH_SCREEN* screen = GetScreen();
578 
579  if( !screen )
580  {
581  wxLogError( wxT( "Document not ready, cannot import" ) );
582  return false;
583  }
584 
585  // open file chooser dialog
586  wxString path = wxPathOnly( Prj().GetProjectFullName() );
587 
588  wxFileDialog dlg( this, _( "Append Schematic" ), path, wxEmptyString,
589  KiCadSchematicFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
590 
591  if( dlg.ShowModal() == wxID_CANCEL )
592  return false;
593 
594  fullFileName = dlg.GetPath();
595 
596  if( !LoadSheetFromFile( GetCurrentSheet().Last(), &GetCurrentSheet(), fullFileName ) )
597  return false;
598 
599  initScreenZoom();
601 
602  SyncView();
603  OnModify();
604  HardRedraw(); // Full reinit of the current screen and the display.
605 
606  return true;
607 }
608 
609 
610 void SCH_EDIT_FRAME::OnAppendProject( wxCommandEvent& event )
611 {
612  if( GetScreen() && GetScreen()->IsModified() )
613  {
614  wxString msg = _( "This operation cannot be undone.\n\n"
615  "Do you want to save the current document before proceeding?" );
616 
617  if( IsOK( this, msg ) )
618  SaveProject();
619  }
620 
621  AppendSchematic();
622 }
623 
624 
625 void SCH_EDIT_FRAME::OnImportProject( wxCommandEvent& aEvent )
626 {
627  if( !AskToSaveChanges() )
628  return;
629 
630  // Set the project location if none is set
631  bool setProject = Prj().GetProjectFullName().IsEmpty();
632  wxString path = wxPathOnly( Prj().GetProjectFullName() );
633 
634  // clang-format off
635  std::list<std::pair<const wxString, const SCH_IO_MGR::SCH_FILE_T>> loaders;
636 
637  if( ADVANCED_CFG::GetCfg().m_PluginAltiumSch )
638  loaders.emplace_back( AltiumSchematicFileWildcard(), SCH_IO_MGR::SCH_ALTIUM ); // Import Altium schematic files
639 
640  loaders.emplace_back( CadstarSchematicArchiveFileWildcard(), SCH_IO_MGR::SCH_CADSTAR_ARCHIVE ); //Import CADSTAR Schematic Archive files
641  loaders.emplace_back( EagleSchematicFileWildcard(), SCH_IO_MGR::SCH_EAGLE ); // Import Eagle schematic files
642  // clang-format on
643 
644  wxString fileFilters;
645  wxString allWildcards;
646 
647  for( auto& loader : loaders )
648  {
649  if( !fileFilters.IsEmpty() )
650  fileFilters += wxChar( '|' );
651 
652  fileFilters += wxGetTranslation( loader.first );
653 
654  SCH_PLUGIN::SCH_PLUGIN_RELEASER plugin( SCH_IO_MGR::FindPlugin( loader.second ) );
655  wxCHECK( plugin, /*void*/ );
656  allWildcards += "*." + formatWildcardExt( plugin->GetFileExtension() ) + ";";
657  }
658 
659  fileFilters = _( "All supported formats|" ) + allWildcards + "|" + fileFilters;
660 
661  wxFileDialog dlg( this, _( "Import Schematic" ), path, wxEmptyString, fileFilters,
662  wxFD_OPEN | wxFD_FILE_MUST_EXIST ); // TODO
663 
664  if( dlg.ShowModal() == wxID_CANCEL )
665  return;
666 
667  if( setProject )
668  {
669  Schematic().SetProject( nullptr );
670  GetSettingsManager()->UnloadProject( &Prj(), false );
671 
672  Schematic().Reset();
673 
674  wxFileName projectFn( dlg.GetPath() );
675  projectFn.SetExt( ProjectFileExtension );
676  GetSettingsManager()->LoadProject( projectFn.GetFullPath() );
677 
678  Schematic().SetProject( &Prj() );
679  }
680 
681  wxFileName fn = dlg.GetPath();
682 
683  SCH_IO_MGR::SCH_FILE_T pluginType = SCH_IO_MGR::SCH_FILE_T::SCH_FILE_UNKNOWN;
684 
685  for( auto& loader : loaders )
686  {
687  if( fn.GetExt().CmpNoCase( SCH_IO_MGR::GetFileExtension( loader.second ) ) == 0 )
688  {
689  pluginType = loader.second;
690  break;
691  }
692  }
693 
694  if( pluginType == SCH_IO_MGR::SCH_FILE_T::SCH_FILE_UNKNOWN )
695  {
696  wxLogError( wxString::Format( "unexpected file extension: %s", fn.GetExt() ) );
697  return;
698  }
699 
700  importFile( dlg.GetPath(), pluginType );
701 }
702 
703 
705 {
706  wxString msg;
707  SCH_SCREEN* screen;
708  SCH_SCREENS screens( Schematic().Root() );
709  bool success = true;
710  bool updateFileType = false;
711 
712  // I want to see it in the debugger, show me the string! Can't do that with wxFileName.
713  wxString fileName = Prj().AbsolutePath( Schematic().Root().GetFileName() );
714  wxFileName fn = fileName;
715 
716  // Warn user on potential file overwrite. This can happen on shared sheets.
717  wxArrayString overwrittenFiles;
718 
719  for( size_t i = 0; i < screens.GetCount(); i++ )
720  {
721  screen = screens.GetScreen( i );
722 
723  wxCHECK2( screen, continue );
724 
725  // Convert legacy schematics file name extensions for the new format.
726  wxFileName tmpFn = screen->GetFileName();
727 
728  if( !tmpFn.IsOk() )
729  continue;
730 
731  if( tmpFn.GetExt() == KiCadSchematicFileExtension )
732  continue;
733 
734  tmpFn.SetExt( KiCadSchematicFileExtension );
735 
736  if( tmpFn.FileExists() )
737  overwrittenFiles.Add( tmpFn.GetFullPath() );
738  }
739 
740  if( !overwrittenFiles.IsEmpty() )
741  {
742  for( const wxString& overwrittenFile : overwrittenFiles )
743  {
744  if( msg.IsEmpty() )
745  msg = overwrittenFile;
746  else
747  msg += "\n" + overwrittenFile;
748  }
749 
750  wxRichMessageDialog dlg( this, _( "Saving will overwrite existing files." ),
751  _( "Save Warning" ),
752  wxOK | wxCANCEL | wxCANCEL_DEFAULT | wxCENTER | wxICON_EXCLAMATION );
753  dlg.ShowDetailedText( _( "The following files will be overwritten:\n\n" ) + msg );
754  dlg.SetOKCancelLabels( wxMessageDialog::ButtonLabel( _( "Overwrite Files" ) ),
755  wxMessageDialog::ButtonLabel( _( "Abort Project Save" ) ) );
756 
757  if( dlg.ShowModal() == wxID_CANCEL )
758  return false;
759  }
760 
761  screens.BuildClientSheetPathList();
762 
763  for( size_t i = 0; i < screens.GetCount(); i++ )
764  {
765  screen = screens.GetScreen( i );
766 
767  wxCHECK2( screen, continue );
768 
769  // Convert legacy schematics file name extensions for the new format.
770  wxFileName tmpFn = screen->GetFileName();
771 
772  if( tmpFn.IsOk() && tmpFn.GetExt() != KiCadSchematicFileExtension )
773  {
774  updateFileType = true;
775  tmpFn.SetExt( KiCadSchematicFileExtension );
776 
777  for( auto item : screen->Items().OfType( SCH_SHEET_T ) )
778  {
779  SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
780  wxFileName sheetFileName = sheet->GetFileName();
781 
782  if( !sheetFileName.IsOk() || sheetFileName.GetExt() == KiCadSchematicFileExtension )
783  continue;
784 
785  sheetFileName.SetExt( KiCadSchematicFileExtension );
786  sheet->SetFileName( sheetFileName.GetFullPath() );
787  UpdateItem( sheet );
788  }
789 
790  screen->SetFileName( tmpFn.GetFullPath() );
791  }
792 
793  std::vector<SCH_SHEET_PATH>& sheets = screen->GetClientSheetPaths();
794 
795  if( sheets.size() == 1 )
796  screen->SetVirtualPageNumber( 1 );
797  else
798  screen->SetVirtualPageNumber( 0 ); // multiple uses; no way to store the real sheet #
799 
800  success &= SaveEEFile( screens.GetSheet( i ) );
801  }
802 
803  if( updateFileType )
804  UpdateFileHistory( Schematic().RootScreen()->GetFileName() );
805 
806  // Save the sheet name map to the project file
807  std::vector<FILE_INFO_PAIR>& sheets = Prj().GetProjectFile().GetSheets();
808  sheets.clear();
809 
810  for( SCH_SHEET_PATH& sheetPath : Schematic().GetSheets() )
811  {
812  SCH_SHEET* sheet = sheetPath.Last();
813  sheets.emplace_back( std::make_pair( sheet->m_Uuid, sheet->GetName() ) );
814  }
815 
816  if( !Prj().IsNullProject() )
817  Pgm().GetSettingsManager().SaveProject();
818 
819  if( !Kiface().IsSingle() )
820  {
821  WX_STRING_REPORTER backupReporter( &msg );
822 
823  if( !GetSettingsManager()->TriggerBackupIfNeeded( backupReporter ) )
824  SetStatusText( msg, 0 );
825  }
826 
827  UpdateTitle();
828 
830 
831  return success;
832 }
833 
834 
836 {
837  wxFileName tmpFileName = Schematic().Root().GetFileName();
838  wxFileName fn = tmpFileName;
839  wxFileName tmp;
840  SCH_SCREENS screens( Schematic().Root() );
841 
842  bool autoSaveOk = true;
843 
844  if( fn.GetPath().IsEmpty() )
845  tmp.AssignDir( Prj().GetProjectPath() );
846  else
847  tmp.AssignDir( fn.GetPath() );
848 
849  if( !tmp.IsOk() )
850  return false;
851 
852  if( !IsWritable( tmp ) )
853  return false;
854 
855  for( size_t i = 0; i < screens.GetCount(); i++ )
856  {
857  // Only create auto save files for the schematics that have been modified.
858  if( !screens.GetScreen( i )->IsSave() )
859  continue;
860 
861  tmpFileName = fn = screens.GetScreen( i )->GetFileName();
862 
863  // Auto save file name is the normal file name prefixed with GetAutoSavePrefix().
864  fn.SetName( GetAutoSaveFilePrefix() + fn.GetName() );
865 
866  screens.GetScreen( i )->SetFileName( fn.GetFullPath() );
867 
868  if( SaveEEFile( screens.GetSheet( i ), false ) )
869  screens.GetScreen( i )->SetModify();
870  else
871  autoSaveOk = false;
872 
873  screens.GetScreen( i )->SetFileName( tmpFileName.GetFullPath() );
874  }
875 
876  if( autoSaveOk )
877  {
878  m_autoSaveState = false;
879 
880  if( !Kiface().IsSingle() &&
882  {
884  }
885  }
886 
887  return autoSaveOk;
888 }
889 
890 
891 bool SCH_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType )
892 {
893  wxFileName newfilename;
894  SCH_SHEET_LIST sheetList = Schematic().GetSheets();
895 
896  switch( (SCH_IO_MGR::SCH_FILE_T) aFileType )
897  {
898  case SCH_IO_MGR::SCH_ALTIUM:
899  case SCH_IO_MGR::SCH_CADSTAR_ARCHIVE:
900  case SCH_IO_MGR::SCH_EAGLE:
901  // We insist on caller sending us an absolute path, if it does not, we say it's a bug.
902  wxASSERT_MSG( wxFileName( aFileName ).IsAbsolute(),
903  wxT( "Import schematic caller didn't send full filename" ) );
904 
905  if( !LockFile( aFileName ) )
906  {
907  wxString msg = wxString::Format( _( "Schematic file \"%s\" is already open." ),
908  aFileName );
909  DisplayError( this, msg );
910  return false;
911  }
912 
913  try
914  {
916  SCH_IO_MGR::FindPlugin( (SCH_IO_MGR::SCH_FILE_T) aFileType ) );
917  Schematic().SetRoot( pi->Load( aFileName, &Schematic() ) );
918 
919  // Eagle sheets do not use a drawing-sheet frame by default, so set it to an empty one
921  drawingSheet.SetEmptyLayout();
922 
923  BASE_SCREEN::m_PageLayoutDescrFileName = "empty.kicad_wks";
924  wxFileName layoutfn( Prj().GetProjectPath(), BASE_SCREEN::m_PageLayoutDescrFileName );
925  wxFFile layoutfile;
926 
927  if( layoutfile.Open( layoutfn.GetFullPath(), "wb" ) )
928  {
929  layoutfile.Write( DS_DATA_MODEL::EmptyLayout() );
930  layoutfile.Close();
931  }
932 
933  newfilename.SetPath( Prj().GetProjectPath() );
934  newfilename.SetName( Prj().GetProjectName() );
935  newfilename.SetExt( KiCadSchematicFileExtension );
936 
937  SetScreen( GetCurrentSheet().LastScreen() );
938 
939  Schematic().Root().SetFileName( newfilename.GetFullPath() );
940  GetScreen()->SetFileName( newfilename.GetFullPath() );
941  GetScreen()->SetModify();
942 
944 
945  UpdateFileHistory( aFileName );
946 
947  // Only perform the dangling end test on root sheet.
949 
951 
952  initScreenZoom();
954  SyncView();
955  UpdateTitle();
956  }
957  catch( const IO_ERROR& ioe )
958  {
959  // Do not leave g_RootSheet == NULL because it is expected to be
960  // a valid sheet. Therefore create a dummy empty root sheet and screen.
961  CreateScreens();
963 
964  wxString msg;
965  msg.Printf( _( "Error loading schematic \"%s\".\n%s" ), aFileName, ioe.What() );
966  DisplayError( this, msg );
967 
968  msg.Printf( _( "Failed to load \"%s\"" ), aFileName );
969  SetMsgPanel( wxEmptyString, msg );
970 
971  return false;
972  }
973 
974  return true;
975 
976  default:
977  return false;
978  }
979 }
980 
981 
983 {
984  SCH_SCREENS screenList( Schematic().Root() );
985 
986  // Save any currently open and modified project files.
987  for( SCH_SCREEN* screen = screenList.GetFirst(); screen; screen = screenList.GetNext() )
988  {
989  if( screen->IsModify() )
990  {
991  if( !HandleUnsavedChanges( this, _( "The current schematic has been modified. "
992  "Save changes?" ),
993  [&]()->bool { return SaveProject(); } ) )
994  {
995  return false;
996  }
997  }
998  }
999 
1000  return true;
1001 }
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:252
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
EE_TYPE OfType(KICAD_T aType) const
Definition: sch_rtree.h:219
void ShowMessage(const wxString &aMessage, int aFlags=wxICON_INFORMATION) override
Show the info bar with the provided message and icon.
Definition: infobar.cpp:124
void Save_File(bool doSaveAs=false)
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:207
bool IsModified()
Check the entire hierarchy for any modifications.
const wxString & GetFileName() const
Definition: sch_screen.h:192
Handle the graphic items list to draw/plot the frame and title block.
Definition: ds_data_model.h:38
SCH_SCREEN * GetNext()
void UpdateItem(EDA_ITEM *aItem, bool isAddOrDelete=false)
Mark an item for refresh.
bool OpenProjectFiles(const std::vector< wxString > &aFileSet, int aCtl=0) override
Open a project or set of files given by aFileList.
static void LibNamesAndPaths(PROJECT *aProject, bool doSave, wxString *aPaths, wxArrayString *aNames=NULL)
Save or load the names of the currently configured part libraries (without paths).
void SetVirtualPageNumber(int aPageNumber)
Definition: base_screen.h:80
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:265
This file is part of the common library TODO brief description.
SETTINGS_MANAGER * GetSettingsManager() const
const wxChar *const traceAutoSave
Flag to enable auto save feature debug tracing.
void RecalculateConnections(SCH_CLEANUP_FLAGS aCleanupFlags)
Generates the connection data for the entire schematic hierarchy.
void SaveProjectSettings() override
Save changes to the project settings to the project (.pro) file.
Model changes (required full reload)
Definition: tool_base.h:81
CONNECTION_GRAPH * ConnectionGraph() const override
Definition: schematic.h:151
SCH_SHEET * Last() const
Return a pointer to the last SCH_SHEET of the list.
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)
const std::string ProjectFileExtension
void SetScreen(BASE_SCREEN *aScreen) override
virtual PROJECT_FILE & GetProjectFile() const
Definition: project.h:145
#define KICTL_CREATE
caller thinks requested project files may not exist.
Definition: kiway_player.h:79
bool AskToSaveChanges()
Checks 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:185
AUTO_BACKUP m_Backup
void ResolveERCExclusions()
Update markers to match recorded exclusions.
static TOOL_ACTION zoomFitScreen
Definition: actions.h:99
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:141
void OnAppendProject(wxCommandEvent &event)
static LIB_PART * dummy()
Used to draw a dummy shape when a LIB_PART is not found in library.
Definition: sch_symbol.cpp:69
bool IsWritable(const wxFileName &aFileName)
Checks if aFileName can be written.
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:285
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
A small class to help profiling.
Definition: profile.h:45
bool importFile(const wxString &aFileName, int aFileType)
Load the given filename but sets the path to the current project path.
bool SaveEEFile(SCH_SHEET *aSheet, bool aSaveUnderNewName=false)
Save aSheet to a schematic file.
void LoadWindowState(const wxString &aFileName)
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.
void OnImportProject(wxCommandEvent &event)
virtual void SetElem(ELEM_T aIndex, _ELEM *aElem)
static wxString m_PageLayoutDescrFileName
the name of the page layout descr file, or emty to used the default pagelayout
Definition: base_screen.h:89
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:51
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
void SetRoot(SCH_SHEET *aRootSheet)
Initializes the schematic with a new root sheet.
Definition: schematic.cpp:100
bool RescueSymbolLibTableProject(bool aRunningOnDemand)
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:187
wxString GetMruPath() const
wxString CadstarSchematicArchiveFileWildcard()
void Dismiss() override
Dismisses the infobar and updates the containing layout and AUI manager (if one is provided).
Definition: infobar.cpp:166
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:153
void SetFileName(wxString aFilename)
Definition: sch_sheet.h:505
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:101
void CheckForAutoSaveFile(const wxFileName &aFileName)
Check if an auto save file exists for aFileName and takes the appropriate action depending on the use...
wxString GetName() const
Definition: sch_sheet.h:283
#define NULL
bool IsSingle() const
Is this KIFACE_I running under single_top?
Definition: kiface_i.h:104
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:29
void SetMsgPanel(const std::vector< MSG_PANEL_ITEM > &aList)
Clear the message panel and populates it with the contents of aList.
void HardRedraw() override
Rebuild the GAL and redraw the screen.
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:139
int ShowQuasiModal()
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
void initScreenZoom()
Initialize the zoom value of the current screen and mark the screen as zoom-initialized.
COMMON_SETTINGS * GetCommonSettings() const
Retrieves the common settings shared by all applications.
SCHEMATIC & Schematic() const
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.
bool CreateArchiveLibraryCacheFile(bool aUseCurrentSheetFilename=false)
Create a symbol library file with the name of the root document plus the '-cache' suffix,...
Definition: libarch.cpp:42
wxString formatWildcardExt(const wxString &aWildcard)
Format wildcard extension to support case sensitive file dialogs.
wxLogTrace helper definitions.
size_t GetCount() const
Definition: sch_screen.h:547
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...
void TestDanglingEnds(const SCH_SHEET_PATH *aPath=nullptr, std::function< void(SCH_ITEM *)> *aChangedHandler=nullptr)
Test all of the connectable objects in the schematic for unused connection points.
Definition: sch_screen.cpp:995
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition: sch_sheet.h:499
const std::string LegacyProjectFileExtension
void SetProject(PROJECT *aPrj)
Definition: schematic.cpp:73
virtual bool IsNullProject() const
Check if this project is a null project (i.e.
void UpdateSymbolInstances(const std::vector< SYMBOL_INSTANCE_REFERENCE > &aSymbolInstances)
Update all of the symbol instance information using aSymbolInstances.
const std::string LegacySchematicFileExtension
void RemoveAllButtons()
Remove all the buttons that have been added by the user.
Definition: infobar.cpp:277
bool LoadProjectSettings()
Loads the KiCad project file (*.pro) settings specific to Eeschema.
A wrapper for reporting to a wxString object.
Definition: reporter.h:159
void DismissOutdatedSave()
Dismisses the infobar for outdated save warnings and updates the containing layout and AUI manager (i...
Definition: infobar.cpp:157
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:219
bool doAutoSave() override
Save the schematic files that have been modified and not yet saved.
const KIID m_Uuid
Definition: eda_item.h:524
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
static wxString GetAutoSaveFilePrefix()
Helper object to release a SCH_PLUGIN in the context of a potential thrown exception through its dest...
Definition: sch_io_mgr.h:470
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Loads a project or sets up a new project with a specified path.
static wxString GetDefaultUserProjectsPath()
Gets the default path we point users to create projects.
Definition: paths.cpp:129
void Reset()
Initializes this schematic to a blank one, unloading anything existing.
Definition: schematic.cpp:50
SCH_SHEET_LIST GetSheets() const override
Builds and returns an updated schematic hierarchy TODO: can this be cached?
Definition: schematic.h:111
std::vector< FILE_INFO_PAIR > & GetSheets()
Definition: project_file.h:82
bool AllSheetPageNumbersEmpty() const
Check all of the sheet instance for empty page numbers.
SCH_SHEET & Root() const
Definition: schematic.h:116
virtual void SetReadOnly(bool aReadOnly=true)
Definition: project.h:126
TOOL_MANAGER * m_toolManager
Definition: tools_holder.h:157
see class PGM_BASE
bool IsSave() const
Definition: base_screen.h:64
void SetSheetNumberAndCount()
Set the m_ScreenNumber and m_NumberOfScreens members for screens.
wxString EagleSchematicFileWildcard()
WX_INFOBAR * m_infoBar
#define _(s)
Definition: 3d_actions.cpp:33
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()
Definition: sch_screen.h:162
bool SaveProject(const wxString &aFullPath=wxEmptyString)
Saves a loaded project.
SCH_SHEET * GetSheet(unsigned int aIndex) const
const std::string KiCadSchematicFileExtension
void ClrModify()
Definition: base_screen.h:60
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.
bool backup_on_autosave
Trigger a backup on autosave.
SCH_SHEET_PATH & GetCurrentSheet() const
int ReplaceDuplicateTimeStamps()
Test all sheet and component objects in the schematic for duplicate time stamps and replaces them as ...
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
std::vector< SCH_SHEET_PATH > & GetClientSheetPaths()
Definition: sch_screen.h:212
SCH_SCREEN * GetFirst()
void Show(std::ostream &aStream=std::cerr)
Print the elapsed time (in a suitable unit) to a stream.
Definition: profile.h:102
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:848
void SetFileName(const wxString &aFileName)
Definition: sch_screen.h:190
static REPORTER & GetInstance()
Definition: reporter.cpp:105
Definition for part library class.
#define SEXPR_SCHEMATIC_FILE_VERSION
Symbol library file version.
void SetModify()
Definition: base_screen.h:59
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
void UpdateSymbolLinks(REPORTER *aReporter=nullptr)
Initialize the LIB_PART reference for each SCH_COMPONENT 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:280
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:75
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:296
virtual void ClearUndoRedoList()
Clear the undo and redo list using ClearUndoORRedoList()
wxString AltiumSchematicFileWildcard()
void ClrSave()
Definition: base_screen.h:62
void SetInitialPageNumbers()
Set initial sheet page numbers.
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition: sch_screen.h:536
bool LockFile(const wxString &aFileName)
Mark a schematic file as being in use.
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:26
void AddCloseButton(const wxString &aTooltip=_("Hide this message."))
Add the default close button to the infobar on the right side.
Definition: infobar.cpp:267