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