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-2019 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 
31 #include <fctsys.h>
32 #include <sch_draw_panel.h>
33 #include <confirm.h>
34 #include <gestfich.h>
35 #include <sch_edit_frame.h>
36 #include <pgm_base.h>
37 #include <kiface_i.h>
38 #include <richio.h>
39 #include <trace_helpers.h>
40 
41 #include <eeschema_id.h>
42 #include <class_library.h>
43 #include <lib_edit_frame.h>
44 #include <sch_sheet.h>
45 #include <sch_sheet_path.h>
46 #include <sch_component.h>
48 #include <project_rescue.h>
49 #include <eeschema_config.h>
50 #include <sch_legacy_plugin.h>
51 #include <sch_eagle_plugin.h>
52 #include <symbol_lib_table.h>
53 #include <dialog_symbol_remap.h>
55 
56 
57 bool SCH_EDIT_FRAME::SaveEEFile( SCH_SCREEN* aScreen, bool aSaveUnderNewName,
58  bool aCreateBackupFile )
59 {
60  wxString msg;
61  wxFileName schematicFileName;
62  bool success;
63 
64  if( aScreen == NULL )
65  aScreen = GetScreen();
66 
67  // If no name exists in the window yet - save as new.
68  if( aScreen->GetFileName().IsEmpty() )
69  aSaveUnderNewName = true;
70 
71  // Construct the name of the file to be saved
72  schematicFileName = Prj().AbsolutePath( aScreen->GetFileName() );
73 
74  if( aSaveUnderNewName )
75  {
76  wxFileDialog dlg( this, _( "Schematic Files" ), wxPathOnly( Prj().GetProjectFullName() ),
77  schematicFileName.GetFullName(), SchematicFileWildcard(),
78  wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
79 
80  if( dlg.ShowModal() == wxID_CANCEL )
81  return false;
82 
83  schematicFileName = dlg.GetPath();
84 
85  if( schematicFileName.GetExt() != SchematicFileExtension )
86  schematicFileName.SetExt( SchematicFileExtension );
87  }
88 
89  if( !IsWritable( schematicFileName ) )
90  return false;
91 
92  // Create backup if requested
93  if( aCreateBackupFile && schematicFileName.FileExists() )
94  {
95  wxFileName backupFileName = schematicFileName;
96 
97  // Rename the old file to a '.bak' one:
98  backupFileName.SetExt( SchematicBackupFileExtension );
99 
100  if( backupFileName.FileExists() )
101  wxRemoveFile( backupFileName.GetFullPath() );
102 
103  if( !wxRenameFile( schematicFileName.GetFullPath(), backupFileName.GetFullPath() ) )
104  {
105  msg.Printf( _( "Could not save backup of file \"%s\"" ),
106  GetChars( schematicFileName.GetFullPath() ) );
107  DisplayError( this, msg );
108  }
109  }
110 
111  // Save
112  wxLogTrace( traceAutoSave,
113  wxT( "Saving file <" ) + schematicFileName.GetFullPath() + wxT( ">" ) );
114 
115  SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
116 
117  try
118  {
119  pi->Save( schematicFileName.GetFullPath(), aScreen, &Kiway() );
120  success = true;
121  }
122  catch( const IO_ERROR& ioe )
123  {
124  msg.Printf( _( "Error saving schematic file \"%s\".\n%s" ),
125  GetChars( schematicFileName.GetFullPath() ), GetChars( ioe.What() ) );
126  DisplayError( this, msg );
127 
128  msg.Printf( _( "Failed to save \"%s\"" ), GetChars( schematicFileName.GetFullPath() ) );
129  AppendMsgPanel( wxEmptyString, msg, CYAN );
130 
131  success = false;
132  }
133 
134  if( success )
135  {
136  // Delete auto save file.
137  wxFileName autoSaveFileName = schematicFileName;
138  autoSaveFileName.SetName( GetAutoSaveFilePrefix() + schematicFileName.GetName() );
139 
140  if( autoSaveFileName.FileExists() )
141  {
142  wxLogTrace( traceAutoSave,
143  wxT( "Removing auto save file <" ) + autoSaveFileName.GetFullPath() +
144  wxT( ">" ) );
145 
146  wxRemoveFile( autoSaveFileName.GetFullPath() );
147  }
148 
149  // Update the screen and frame info and reset the lock file.
150  if( aSaveUnderNewName )
151  {
152  aScreen->SetFileName( schematicFileName.GetFullPath() );
153  LockFile( schematicFileName.GetFullPath() );
154  }
155 
156  aScreen->ClrSave();
157  aScreen->ClrModify();
158 
159  msg.Printf( _( "File %s saved" ), GetChars( aScreen->GetFileName() ) );
160  SetStatusText( msg, 0 );
161  }
162  else
163  {
164  DisplayError( this, _( "File write operation failed." ) );
165  }
166 
167  return success;
168 }
169 
170 
171 void SCH_EDIT_FRAME::Save_File( wxCommandEvent& event )
172 {
173  int id = event.GetId();
174 
175  switch( id )
176  {
177  case ID_UPDATE_ONE_SHEET:
178  SaveEEFile( NULL );
179  break;
180 
182  if( SaveEEFile( NULL, true ) )
183  {
185  }
186  break;
187  }
188 
189  UpdateTitle();
190 }
191 
192 
193 bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
194 {
195  // implement the pseudo code from KIWAY_PLAYER.h:
196 
197  // This is for python:
198  if( aFileSet.size() != 1 )
199  {
200  UTF8 msg = StrPrintf( "Eeschema:%s() takes only a single filename.", __func__ );
201  DisplayError( this, msg );
202  return false;
203  }
204 
205  wxString fullFileName( aFileSet[0] );
206 
207  // We insist on caller sending us an absolute path, if it does not, we say it's a bug.
208  wxASSERT_MSG( wxFileName( fullFileName ).IsAbsolute(), wxT( "Path is not absolute!" ) );
209 
210  if( !LockFile( fullFileName ) )
211  {
212  wxString msg = wxString::Format( _( "Schematic file \"%s\" is already open." ),
213  fullFileName );
214  DisplayError( this, msg );
215  return false;
216  }
217 
218  if( !AskToSaveChanges() )
219  return false;
220 
221  wxFileName pro = fullFileName;
222  pro.SetExt( ProjectFileExtension );
223 
224  bool is_new = !wxFileName::IsFileReadable( fullFileName );
225 
226  // If its a non-existent schematic and caller thinks it exists
227  if( is_new && !( aCtl & KICTL_CREATE ) )
228  {
229  // notify user that fullFileName does not exist, ask if user wants to create it.
230  wxString ask = wxString::Format( _( "Schematic \"%s\" does not exist. Do you wish to create it?" ),
231  fullFileName );
232  if( !IsOK( this, ask ) )
233  return false;
234  }
235 
236  // unload current project file before loading new
237  {
238  SetScreen( nullptr );
239  delete g_RootSheet;
240  g_RootSheet = NULL;
241 
242  CreateScreens();
243  }
244 
245  GetScreen()->SetFileName( fullFileName );
246  g_RootSheet->SetFileName( fullFileName );
247 
248  SetStatusText( wxEmptyString );
249  ClearMsgPanel();
250 
251  // PROJECT::SetProjectFullName() is an impactful function. It should only be
252  // called under carefully considered circumstances.
253 
254  // The calling code should know not to ask me here to change projects unless
255  // it knows what consequences that will have on other KIFACEs running and using
256  // this same PROJECT. It can be very harmful if that calling code is stupid.
257 
258  // Don't reload the symbol libraries if we are just launching Eeschema from KiCad again.
259  // They are already saved in the kiface project object.
260  if( pro.GetFullPath() != Prj().GetProjectFullName()
262  {
263  Prj().SetProjectFullName( pro.GetFullPath() );
264 
265  // load the libraries here, not in SCH_SCREEN::Draw() which is a context
266  // that will not tolerate DisplayError() dialog since we're already in an
267  // event handler in there.
268  // And when a schematic file is loaded, we need these libs to initialize
269  // some parameters (links to PART LIB, dangling ends ...)
271  Prj().SchLibs();
272  }
273 
274  LoadProjectFile();
275 
276  // Load the symbol library table, this will be used forever more.
278  Prj().SchSymbolLibTable();
279 
280  if( is_new )
281  {
282  // mark new, unsaved file as modified.
283  GetScreen()->SetModify();
284  }
285  else
286  {
287  SetScreen( nullptr );
288  delete g_RootSheet; // Delete the current project.
289  g_RootSheet = NULL; // Force CreateScreens() to build new empty project on load failure.
290  SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
291 
292  // This will rename the file if there is an autosave and the user want to recover
293  CheckForAutoSaveFile( fullFileName );
294 
295  try
296  {
297  g_RootSheet = pi->Load( fullFileName, &Kiway() );
298  m_CurrentSheet->clear();
299  m_CurrentSheet->push_back( g_RootSheet );
300 
301  if( !pi->GetError().IsEmpty() )
302  {
303  DisplayErrorMessage( this,
304  _( "The entire schematic could not be loaded. Errors "
305  "occurred attempting to load \nhierarchical sheet "
306  "schematics." ),
307  pi->GetError() );
308  }
309  }
310  catch( const IO_ERROR& ioe )
311  {
312  // Do not leave g_RootSheet == NULL because it is expected to be
313  // a valid sheet. Therefore create a dummy empty root sheet and screen.
314  CreateScreens();
315  Zoom_Automatique( false );
316 
317  wxString msg;
318  msg.Printf( _( "Error loading schematic file \"%s\".\n%s" ),
319  GetChars( fullFileName ), GetChars( ioe.What() ) );
320  DisplayError( this, msg );
321 
322  msg.Printf( _( "Failed to load \"%s\"" ), GetChars( fullFileName ) );
323  AppendMsgPanel( wxEmptyString, msg, CYAN );
324 
325  return false;
326  }
327 
328  // It's possible the schematic parser fixed errors due to bugs so warn the user
329  // that the schematic has been fixed (modified).
330  SCH_SHEET_LIST sheetList( g_RootSheet );
331 
332  if( sheetList.IsModified() )
333  {
334  DisplayInfoMessage( this,
335  _( "An error was found when loading the schematic that has "
336  "been automatically fixed. Please save the schematic to "
337  "repair the broken file or it may not be usable with other "
338  "versions of KiCad." ) );
339  }
340 
341  UpdateFileHistory( fullFileName );
342 
343  SCH_SCREENS schematic;
344 
345  // Convert old projects over to use symbol library table.
346  if( schematic.HasNoFullyDefinedLibIds() )
347  {
348  DIALOG_SYMBOL_REMAP dlgRemap( this );
349 
350  dlgRemap.ShowQuasiModal();
351  }
352  else
353  {
354  // Check to see whether some old library parts need to be rescued
355  // Only do this if RescueNeverShow was not set.
356  wxConfigBase *config = Kiface().KifaceSettings();
357  bool rescueNeverShow = false;
358  config->Read( RescueNeverShowEntry, &rescueNeverShow, false );
359 
360  if( !rescueNeverShow )
362  }
363 
364  schematic.UpdateSymbolLinks(); // Update all symbol library links for all sheets.
366 
367  // Ensure the schematic is fully segmented on first display
369  SchematicCleanUp( true );
370  GetScreen()->ClearUndoORRedoList( GetScreen()->m_UndoList, 1 );
371  GetScreen()->TestDanglingEnds(); // Only perform the dangling end test on root sheet.
372  GetScreen()->m_Initialized = true;
373  }
374 
376  Zoom_Automatique( false );
378  SyncView();
380 
381  UpdateTitle();
382 
383  return true;
384 }
385 
386 
388 {
389  wxString msg;
390  wxString fullFileName;
391  SCH_SCREEN* screen = GetScreen();
392 
393  if( !screen )
394  {
395  wxLogError( wxT( "Document not ready, cannot import" ) );
396  return false;
397  }
398 
399  // open file chooser dialog
400  wxString path = wxPathOnly( Prj().GetProjectFullName() );
401 
402  wxFileDialog dlg( this, _( "Append Schematic" ), path, wxEmptyString,
403  SchematicFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
404 
405  if( dlg.ShowModal() == wxID_CANCEL )
406  return false;
407 
408  fullFileName = dlg.GetPath();
409 
410  wxFileName fn = fullFileName;
411 
412  if( fn.IsRelative() )
413  {
414  fn.MakeAbsolute();
415  fullFileName = fn.GetFullPath();
416  }
417 
418  wxString cache_name = PART_LIBS::CacheName( fullFileName );
419 
420  if( !!cache_name )
421  {
422  PART_LIBS* libs = Prj().SchLibs();
423 
424  try
425  {
426  if( PART_LIB* lib = libs->AddLibrary( cache_name ) )
427  lib->SetCache();
428  }
429  catch( const IO_ERROR& ioe )
430  {
431  DisplayError( this, ioe.What() );
432  }
433  }
434 
435  wxLogDebug( wxT( "Importing schematic " ) + fullFileName );
436 
437  // Load the schematic into a temporary sheet.
438  SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
439  std::unique_ptr< SCH_SHEET> newSheet( new SCH_SHEET );
440 
441  newSheet->SetFileName( fullFileName );
442 
443  try
444  {
445  pi->Load( fullFileName, &Kiway(), newSheet.get() );
446 
447  if( !pi->GetError().IsEmpty() )
448  {
449  DisplayErrorMessage( this,
450  _( "The entire schematic could not be loaded. Errors "
451  "occurred attempting to load hierarchical sheet "
452  "schematics." ),
453  pi->GetError() );
454  }
455  }
456  catch( const IO_ERROR& ioe )
457  {
458  msg.Printf( _( "Error occurred loading schematic file \"%s\"." ), fullFileName );
459  DisplayErrorMessage( this, msg, ioe.What() );
460 
461  msg.Printf( _( "Failed to load schematic \"%s\"" ), fullFileName );
462  AppendMsgPanel( wxEmptyString, msg, CYAN );
463 
464  return false;
465  }
466 
467  // Make sure any new sheet changes do not cause any recursion issues.
468  SCH_SHEET_LIST hierarchy( g_RootSheet ); // This is the schematic sheet hierarchy.
469  SCH_SHEET_LIST sheetHierarchy( newSheet.get() ); // This is the hierarchy of the import.
470 
471  wxFileName destFile = screen->GetFileName();
472 
473  if( destFile.IsRelative() )
474  destFile.MakeAbsolute( Prj().GetProjectPath() );
475 
476  if( hierarchy.TestForRecursion( sheetHierarchy, destFile.GetFullPath( wxPATH_UNIX ) ) )
477  {
478  msg.Printf( _( "The sheet changes cannot be made because the destination sheet already "
479  "has the sheet \"%s\" or one of it's subsheets as a parent somewhere in "
480  "the schematic hierarchy." ),
481  destFile.GetFullPath() );
482  DisplayError( this, msg );
483  return false;
484  }
485 
486  wxArrayString names;
487 
488  // Make sure the imported schematic has been remapped to the symbol library table.
489  SCH_SCREENS newScreens( newSheet.get() ); // All screens associated with the import.
490 
491  if( newScreens.HasNoFullyDefinedLibIds() )
492  {
493  DisplayInfoMessage( this,
494  "This schematic has not been remapped to the symbol library\n"
495  "table. The project this schematic belongs to must first be\n"
496  "remapped before it can be imported into the current project." );
497  return false;
498  }
499  else
500  {
501  // If there are symbol libraries in the imported schematic that are not in the
502  // symbol library table of this project, there could be a lot of broken symbol
503  // library links. Attempt to add the missing libraries to the project symbol
504  // library table.
505  newScreens.GetLibNicknames( names );
506  wxArrayString newLibNames;
507 
508  for( const auto& name : names )
509  {
510  if( !Prj().SchSymbolLibTable()->HasLibrary( name ) )
511  newLibNames.Add( name );
512  }
513 
514  wxFileName symLibTableFn( fn.GetPath(), SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
515 
516  if( !newLibNames.IsEmpty() && symLibTableFn.Exists() && symLibTableFn.IsFileReadable() )
517  {
518  SYMBOL_LIB_TABLE table;
519 
520  try
521  {
522  table.Load( symLibTableFn.GetFullPath() );
523  }
524  catch( const IO_ERROR& ioe )
525  {
526  msg.Printf( _( "An error occurred loading the symbol library table \"%s\"." ),
527  symLibTableFn.GetFullPath() );
528  DisplayErrorMessage( NULL, msg, ioe.What() );
529  }
530 
531  if( !table.IsEmpty() )
532  {
533  for( const auto& libName : newLibNames )
534  {
535  if( !table.HasLibrary( libName ) )
536  continue;
537 
538  // Don't expand environment variable because KIPRJMOD will not be correct
539  // for a different project.
540  wxString uri = table.GetFullURI( libName, false );
541  wxFileName newLib;
542 
543  if( uri.Contains( "${KIPRJMOD}" ) )
544  {
545  newLib.SetPath( fn.GetPath() );
546  newLib.SetFullName( uri.AfterLast( '}' ) );
547  uri = newLib.GetFullPath();
548  }
549  else if( uri.Contains( "$(KIPRJMOD)" ) )
550  {
551  newLib.SetPath( fn.GetPath() );
552  newLib.SetFullName( uri.AfterLast( ')' ) );
553  uri = newLib.GetFullPath();
554  }
555  else
556  {
557  uri = table.GetFullURI( libName );
558  }
559 
560  // Add the library from the imported project to the current project
561  // symbol library table.
562  const SYMBOL_LIB_TABLE_ROW* row = table.FindRow( libName );
563 
564  wxCHECK2_MSG( row, continue, "Library '" + libName +
565  "' missing from symbol library table '" +
566  symLibTableFn.GetFullPath() + "'." );
567 
568  wxString newLibName = libName;
569  int libNameCnt = 1;
570 
571  // Rename the imported symbol library if it already exists.
572  while( Prj().SchSymbolLibTable()->HasLibrary( newLibName ) )
573  newLibName = wxString::Format( "%s%d", libName, libNameCnt );
574 
575  auto newRow = new SYMBOL_LIB_TABLE_ROW( newLibName, uri, row->GetType(),
576  row->GetOptions(), row->GetDescr() );
577  Prj().SchSymbolLibTable()->InsertRow( newRow );
578 
579  if( libName != newLibName )
580  newScreens.ChangeSymbolLibNickname( libName, newLibName );
581  }
582  }
583  }
584  }
585 
586  newScreens.ClearAnnotation();
587 
588  // Check for duplicate sheet names in the current page.
589  wxArrayString duplicateSheetNames;
590  SCH_TYPE_COLLECTOR sheets;
591 
592  sheets.Collect( screen->GetDrawItems(), SCH_COLLECTOR::SheetsOnly );
593 
594  for( int i = 0; i < sheets.GetCount(); ++i )
595  {
596  if( newSheet->GetScreen()->GetSheet( ( ( SCH_SHEET* ) sheets[i] )->GetName() ) )
597  duplicateSheetNames.Add( ( ( SCH_SHEET* ) sheets[i] )->GetName() );
598  }
599 
600  if( !duplicateSheetNames.IsEmpty() )
601  {
602  msg.Printf( "Duplicate sheet names exist on the current page. Do you want to "
603  "automatically rename the duplicate sheet names?" );
604  if( !IsOK( this, msg ) )
605  return false;
606  }
607 
608  SCH_SCREEN* newScreen = newSheet->GetScreen();
609  wxCHECK_MSG( newScreen, false, "No screen defined for imported sheet." );
610 
611  for( const auto& duplicateName : duplicateSheetNames )
612  {
613  SCH_SHEET* renamedSheet = newScreen->GetSheet( duplicateName );
614 
615  wxCHECK2_MSG( renamedSheet, continue,
616  "Sheet " + duplicateName + " not found in imported schematic." );
617 
618  timestamp_t newtimestamp = GetNewTimeStamp();
619  renamedSheet->SetTimeStamp( newtimestamp );
620  renamedSheet->SetName( wxString::Format( "Sheet%8.8lX", (unsigned long) newtimestamp ) );
621  }
622 
623  // It is finally safe to add the imported schematic.
624  screen->Append( newScreen );
625 
626  SCH_SCREENS allScreens;
627  allScreens.ReplaceDuplicateTimeStamps();
628 
629  SCH_SCREENS screens( GetCurrentSheet().Last() );
630  screens.UpdateSymbolLinks( true );
631 
632  // Clear all annotation in the imported schematic to prevent clashes with existing annotation.
633  // Must be done after updating the symbol links as we need to know about multi-unit parts.
634  // screens.ClearAnnotation();
635 
636  screens.TestDanglingEnds();
637 
639  Zoom_Automatique( false );
641 
642  SyncView();
643  HardRedraw(); // Full reinit of the current screen and the display.
644  OnModify();
645 
646  return true;
647 }
648 
649 
650 void SCH_EDIT_FRAME::OnAppendProject( wxCommandEvent& event )
651 {
652  if( GetScreen() && GetScreen()->IsModified() )
653  {
654  wxString msg = _( "This operation cannot be undone.\n\n"
655  "Do you want to save the current document before proceeding?" );
656 
657  if( IsOK( this, msg ) )
658  SaveProject();
659  }
660 
661  AppendSchematic();
662 }
663 
664 
665 void SCH_EDIT_FRAME::OnImportProject( wxCommandEvent& aEvent )
666 {
667  if( !AskToSaveChanges() )
668  return;
669 
670  // Set the project location if none is set
671  bool setProject = Prj().GetProjectFullName().IsEmpty();
672  wxString path = wxPathOnly( Prj().GetProjectFullName() );
673 
674  wxFileDialog dlg( this, _( "Import Schematic" ), path, wxEmptyString,
675  EagleSchematicFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
676 
677  if( dlg.ShowModal() == wxID_CANCEL )
678  return;
679 
680  if( setProject )
681  {
682  wxFileName projectFn( dlg.GetPath() );
683  projectFn.SetExt( ProjectFileExtension );
684  Prj().SetProjectFullName( projectFn.GetFullPath() );
685  }
686 
687  // For now there is only one import plugin
688  importFile( dlg.GetPath(), SCH_IO_MGR::SCH_EAGLE );
689 }
690 
691 
692 void SCH_EDIT_FRAME::OnSaveProject( wxCommandEvent& aEvent )
693 {
694  SaveProject();
695 }
696 
697 
699 {
700  SCH_SCREEN* screen;
701  SCH_SCREENS screenList;
702  bool success = true;
703 
704  // I want to see it in the debugger, show me the string! Can't do that with wxFileName.
705  wxString fileName = Prj().AbsolutePath( g_RootSheet->GetFileName() );
706  wxFileName fn = fileName;
707 
708  if( !fn.IsDirWritable() )
709  {
710  wxString msg = wxString::Format( _( "Directory \"%s\" is not writable." ), fn.GetPath() );
711  DisplayError( this, msg );
712  return false;
713  }
714 
715  for( screen = screenList.GetFirst(); screen; screen = screenList.GetNext() )
716  success &= SaveEEFile( screen );
717 
719 
720  UpdateTitle();
721 
722  return success;
723 }
724 
725 
727 {
728  wxFileName tmpFileName = g_RootSheet->GetFileName();
729  wxFileName fn = tmpFileName;
730  wxFileName tmp;
731  SCH_SCREENS screens;
732 
733  bool autoSaveOk = true;
734 
735  tmp.AssignDir( fn.GetPath() );
736 
737  if( !tmp.IsOk() )
738  return false;
739 
740  if( !IsWritable( tmp ) )
741  return false;
742 
743  for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
744  {
745  // Only create auto save files for the schematics that have been modified.
746  if( !screen->IsSave() )
747  continue;
748 
749  tmpFileName = fn = screen->GetFileName();
750 
751  // Auto save file name is the normal file name prefixed with GetAutoSavePrefix().
752  fn.SetName( GetAutoSaveFilePrefix() + fn.GetName() );
753 
754  screen->SetFileName( fn.GetFullPath() );
755 
756  if( SaveEEFile( screen, false, NO_BACKUP_FILE ) )
757  screen->SetModify();
758  else
759  autoSaveOk = false;
760 
761  screen->SetFileName( tmpFileName.GetFullPath() );
762  }
763 
764  if( autoSaveOk )
765  m_autoSaveState = false;
766 
767  return autoSaveOk;
768 }
769 
770 
771 bool SCH_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType )
772 {
773  wxString projectpath;
774  wxFileName newfilename;
775  SCH_SHEET_LIST sheetList( g_RootSheet );
776 
777  switch( (SCH_IO_MGR::SCH_FILE_T) aFileType )
778  {
779  case SCH_IO_MGR::SCH_EAGLE:
780  // We insist on caller sending us an absolute path, if it does not, we say it's a bug.
781  wxASSERT_MSG( wxFileName( aFileName ).IsAbsolute(),
782  wxT( "Import eagle schematic caller didn't send full filename" ) );
783 
784  if( !LockFile( aFileName ) )
785  {
786  wxString msg = wxString::Format( _( "Schematic file \"%s\" is already open." ),
787  aFileName );
788  DisplayError( this, msg );
789  return false;
790  }
791 
792  try
793  {
794  delete g_RootSheet;
795  g_RootSheet = nullptr;
796  SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_EAGLE ) );
797  g_RootSheet = pi->Load( aFileName, &Kiway() );
798 
799  // Eagle sheets do not use a worksheet frame by default, so set it to an empty one
801  pglayout.SetEmptyLayout();
802 
803  BASE_SCREEN::m_PageLayoutDescrFileName = "empty.kicad_wks";
804  wxFileName layoutfn( Kiway().Prj().GetProjectPath(),
806  wxFile layoutfile;
807 
808  if( layoutfile.Create( layoutfn.GetFullPath() ) )
809  {
810  layoutfile.Write( WORKSHEET_LAYOUT::EmptyLayout() );
811  layoutfile.Close();
812  }
813 
814  projectpath = Kiway().Prj().GetProjectPath();
815  newfilename.SetPath( Prj().GetProjectPath() );
816  newfilename.SetName( Prj().GetProjectName() );
817  newfilename.SetExt( SchematicFileExtension );
818 
819  m_CurrentSheet->clear();
820  m_CurrentSheet->push_back( g_RootSheet );
822 
823  g_RootSheet->SetFileName( newfilename.GetFullPath() );
824  GetScreen()->SetFileName( newfilename.GetFullPath() );
825  GetScreen()->SetModify();
826  SaveProjectSettings( false );
827 
828  UpdateFileHistory( aFileName );
829  SCH_SCREENS schematic;
830  schematic.UpdateSymbolLinks(); // Update all symbol library links for all sheets.
831 
832  // Ensure the schematic is fully segmented on first display
834  SchematicCleanUp( true );
835  GetScreen()->m_Initialized = true;
836 
837  SCH_TYPE_COLLECTOR components;
838  SCH_SCREENS allScreens;
839 
840  for( SCH_SCREEN* screen = allScreens.GetFirst(); screen; screen = allScreens.GetNext() )
841  {
842  components.Collect( screen->GetDrawItems(), SCH_COLLECTOR::ComponentsOnly );
843 
844  for( int cmpIdx = 0; cmpIdx < components.GetCount(); ++cmpIdx )
845  {
846  std::vector<wxPoint> pts;
847  SCH_COMPONENT* cmp = static_cast<SCH_COMPONENT*>( components[cmpIdx] );
848 
849  // Update footprint LIB_ID to point to the imported Eagle library
850  auto fpField = cmp->GetField( FOOTPRINT );
851 
852  if( !fpField->GetText().IsEmpty() )
853  {
854  LIB_ID fpId;
855  fpId.Parse( fpField->GetText(), LIB_ID::ID_SCH, true );
856  fpId.SetLibNickname( newfilename.GetName() );
857  fpField->SetText( fpId.Format() );
858  }
859  }
860  }
861 
862  GetScreen()->ClearUndoORRedoList( GetScreen()->m_UndoList, 1 );
863  // Only perform the dangling end test on root sheet.
865 
867  Zoom_Automatique( false );
869  SyncView();
870  UpdateTitle();
871  }
872  catch( const IO_ERROR& ioe )
873  {
874  // Do not leave g_RootSheet == NULL because it is expected to be
875  // a valid sheet. Therefore create a dummy empty root sheet and screen.
876  CreateScreens();
877  Zoom_Automatique( false );
878 
879  wxString msg;
880  msg.Printf( _( "Error loading schematic \"%s\".\n%s" ), aFileName, ioe.What() );
881  DisplayError( this, msg );
882 
883  msg.Printf( _( "Failed to load \"%s\"" ), aFileName );
884  AppendMsgPanel( wxEmptyString, msg, CYAN );
885 
886  return false;
887  }
888 
889  return true;
890 
891  default:
892  return false;
893  }
894 }
895 
896 
898 {
899  SCH_SCREENS screenList;
900 
901  // Save any currently open and modified project files.
902  for( SCH_SCREEN* screen = screenList.GetFirst(); screen; screen = screenList.GetNext() )
903  {
904  if( screen->IsModify() )
905  {
906  if( !HandleUnsavedChanges( this, _( "The current schematic has been modified. Save changes?" ),
907  [&]()->bool { return SaveProject(); } ) )
908  {
909  return false;
910  }
911  }
912  }
913 
914  return true;
915 }
const std::string SchematicBackupFileExtension
Definition of the SCH_SHEET class for Eeschema.
Class UTF8 is an 8 bit string that is assuredly encoded in UTF8, and supplies special conversion supp...
Definition: utf8.h:73
Class SCH_SHEET_LIST.
bool m_autoSaveState
Flag to indicate the last auto save state.
static const wxString & GetSymbolLibTableFileName()
bool HandleUnsavedChanges(wxWindow *aParent, const wxString &aMessage, const std::function< bool()> &aSaveFunction)
Function HandleUnsavedChanges displays a dialog with Save, Cancel and Discard Changes buttons.
Definition: confirm.cpp:211
bool IsModified()
Function IsModified checks the entire hierarchy for any modifications.
const wxString & GetFileName() const
Definition: sch_screen.h:131
SYMBOL_LIB_TABLE_ROW * FindRow(const wxString &aNickName)
Return an SYMBOL_LIB_TABLE_ROW if aNickName is found in this table or in any chained fallBack table f...
bool SchematicCleanUp(bool aAppend=false)
Performs routine schematic cleaning including breaking wire and buses and deleting identical objects ...
virtual void Zoom_Automatique(bool aWarpPointer) override
Redraw the screen with best zoom level and the best centering that shows all the page or the board.
SCH_SCREEN * GetNext()
Hold a record identifying a symbol library accessed by the appropriate symbol library SCH_PLUGIN obje...
Class TYPE_COLLECTOR merely gathers up all SCH_ITEMs of a given set of KICAD_T type(s).
KIWAY & Kiway() const
Function Kiway returns a reference to the KIWAY that this object has an opportunity to participate in...
Definition: kiway_player.h:60
bool OpenProjectFiles(const std::vector< wxString > &aFileSet, int aCtl=0) override
const wxString & GetOptions() const
Return the options string, which may hold a password or anything else needed to instantiate the under...
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library table.
wxString SchematicFileWildcard()
int StrPrintf(std::string *aResult, const char *aFormat,...)
Function StrPrintf is like sprintf() but the output is appended to a std::string instead of to a char...
Definition: richio.cpp:74
long timestamp_t
timestamp_t is our type to represent unique IDs for all kinds of elements; historically simply the ti...
Definition: common.h:53
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Function DisplayErrorMessage displays an error message with aMessage.
Definition: confirm.cpp:257
This file is part of the common library TODO brief description.
const wxChar *const traceAutoSave
Flag to enable auto save feature debug tracing.
This file is part of the common library.
void SetFileName(const wxString &aFilename)
Definition: sch_sheet.h:467
SCH_SHEET * GetSheet(const wxString &aName)
Returns a sheet object pointer that is named aName.
Definition: sch_screen.cpp:626
void OnModify()
Must be called after a schematic change in order to set the "modify" flag of the current screen* and ...
const std::string ProjectFileExtension
virtual void SetScreen(BASE_SCREEN *aScreen) override
VTBL_ENTRY PROJECT & Prj() const
Function Prj returns the PROJECT associated with this KIWAY.
Definition: kiway.cpp:142
#define KICTL_CREATE
caller thinks requested project files may not exist
Definition: kiway_player.h:140
bool AskToSaveChanges()
Checks if any of the screens has unsaved changes and asks the user whether to save or drop them.
bool BreakSegmentsOnJunctions(bool aApped=false)
Tests all junctions and bus entries in the schematic for intersections with wires and buses and break...
wxConfigBase * KifaceSettings() const
Definition: kiface_i.h:103
const wxChar RescueNeverShowEntry[]
void UpdateTitle()
Set the main window title bar text.
static const KICAD_T ComponentsOnly[]
A scan list for schematic component items only.
void OnAppendProject(wxCommandEvent &event)
virtual wxConfigBase * config()
Function config returns the wxConfigBase used in SaveSettings(), and is overloaded in KICAD_MANAGER_F...
VTBL_ENTRY _ELEM * GetElem(ELEM_T aIndex)
Typically wrapped somewhere else in a more meaningful function wrapper.
Definition: project.cpp:205
bool IsWritable(const wxFileName &aFileName)
Function IsWritable 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
#define NO_BACKUP_FILE
static const wxString CacheName(const wxString &aFullProjectFilename)
Return the name of the cache library after potentially fixing it from an older naming scheme.
bool importFile(const wxString &aFileName, int aFileType)
Load the given filename but sets the path to the current project path.
VTBL_ENTRY const wxString GetProjectPath() const
Function GetProjectPath returns the full path of the project.
Definition: project.cpp:102
Field Name Module PCB, i.e. "16DIP300".
void UpdateFileHistory(const wxString &FullFileName, wxFileHistory *aFileHistory=NULL)
Function UpdateFileHistory Updates the list of recently opened files.
static wxString EmptyLayout()
Returns a string containing the empty layout shape.
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
Definition: kicad.cpp:52
static WORKSHEET_LAYOUT & GetTheInstance()
static function: returns the instance of WORKSHEET_LAYOUT used in the application
void SaveProjectSettings(bool aAskForSave) override
Save changes to the project settings to the project (.pro) file.
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:385
const wxString & GetDescr() const
Return the description of the library referenced by this row.
void OnImportProject(wxCommandEvent &event)
VTBL_ENTRY void SetElem(ELEM_T aIndex, _ELEM *aElem)
Definition: project.cpp:217
static wxString m_PageLayoutDescrFileName
the name of the page layout descr file, or emty to used the default pagelayout
Definition: base_screen.h:180
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
int GetCount() const
Function GetCount returns the number of objects in the list.
Definition: collector.h:114
SCH_SHEET_PATH * m_CurrentSheet
which sheet we are presently working on.
timestamp_t GetNewTimeStamp()
Definition: common.cpp:216
void Save_File(wxCommandEvent &event)
SCH_SHEET * g_RootSheet
Definition: eeschema.cpp:56
void CheckForAutoSaveFile(const wxFileName &aFileName)
Function CheckForAutoSaveFile checks if an auto save file exists for aFileName and takes the appropri...
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:33
void SetName(const wxString &aName)
Definition: sch_sheet.h:269
void HardRedraw() override
Rebuild the GAL and redraw the screen.
void SyncView()
Mark all items for refresh.
bool m_Initialized
Definition: base_screen.h:207
int ShowQuasiModal()
PROJECT & Prj() const
Function Prj returns a reference to the PROJECT "associated with" this KIWAY.
wxString GetFullURI(const wxString &aLibNickname, bool aExpandEnvVars=true) const
Return the full URI of the library mapped to aLibNickname.
const std::string SchematicFileExtension
const wxString GetType() const override
Return the type of symbol library table represented by this row.
SCH_SHEET_PATH & GetCurrentSheet()
PART_LIB * AddLibrary(const wxString &aFileName)
Allocate and adds a part library to the library list.
Definition: colors.h:59
Definition of file extensions used in Kicad.
bool AppendSchematic()
Import a KiCad schematic into the current page.
bool SaveEEFile(SCH_SCREEN *aScreen, bool aSaveUnderNewName=false, bool aCreateBackupFile=CREATE_BACKUP_FILE)
Save aScreen to a schematic file.
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
void OnSaveProject(wxCommandEvent &aEvent)
Command event handler to save the entire project and create a component library archive.
wxLogTrace helper definitions.
bool LoadProjectFile()
Loads the KiCad project file (*.pro) settings specific to Eeschema.
bool HasNoFullyDefinedLibIds()
Test all of the schematic symbols to see if all LIB_ID objects library nickname is not set.
VTBL_ENTRY const wxString GetProjectFullName() const
Function GetProjectFullName returns the full path and name of the project.
Definition: project.cpp:96
int m_LastGridSizeId
Definition: draw_frame.h:110
void Load(const wxString &aFileName)
Load the library table using the path defined by aFileName aFallBackTable.
VTBL_ENTRY void SetProjectFullName(const wxString &aFullPathAndName)
Function SetProjectFullName sets the: 1) full directory, 2) basename, and 3) extension of the project...
Definition: project.cpp:64
UTF8 Format() const
Definition: lib_id.cpp:237
WORKSHEET_LAYOUT handles the graphic items list to draw/plot the title block and other items (page re...
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:209
virtual bool doAutoSave() override
Save the schematic files that have been modified and not yet saved.
int SetLibNickname(const UTF8 &aNickname)
Override the logical library name portion of the LIB_ID to aNickname.
Definition: lib_id.cpp:193
wxString GetFileName(void) const
Return the filename corresponding to this sheet.
Definition: sch_sheet.cpp:662
A collection of PART_LIB objects.
static wxString GetAutoSaveFilePrefix()
Function GetAutoSaveFilePrefix.
Helper object to release a SCH_PLUGIN in the context of a potential thrown exception through its dest...
Definition: sch_io_mgr.h:523
void AppendMsgPanel(const wxString &textUpper, const wxString &textLower, COLOR4D color, int pad=6)
Append a message to the message panel.
Definition the SCH_COMPONENT class for Eeschema.
static const wxChar * GetChars(const wxString &s)
Function GetChars returns a wxChar* to the actual wxChar* data within a wxString, and is helpful for ...
Definition: macros.h:92
see class PGM_BASE
void UpdateSymbolLinks(bool aForce=false)
Initialize or reinitialize the weak reference to the LIB_PART for each SCH_COMPONENT found in the ful...
void TestDanglingEnds()
const char * name
Definition: DXF_plotter.cpp:61
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:205
void Append(SCH_ITEM *aItem)
Definition: sch_screen.h:162
void SetSheetNumberAndCount()
Set the m_ScreenNumber and m_NumberOfScreens members for screens.
wxString EagleSchematicFileWildcard()
bool TestDanglingEnds()
Test all of the connectable objects in the schematic for unused connection points.
Definition: sch_screen.cpp:912
void SetEmptyLayout()
Fills the list with an empty layout shape.
size_t i
Definition: json11.cpp:597
void ClrModify()
Definition: base_screen.h:325
Class SCH_COMPONENT describes a real schematic component.
Definition: sch_component.h:70
void Collect(SCH_ITEM *aBoard, const KICAD_T aScanList[])
Function Collect scans a BOARD_ITEM using this class's Inspector method, which does the collection.
static const KICAD_T SheetsOnly[]
A scan list for schematic sheet items only.
bool RescueSymbolLibTableProject(bool aRunningOnDemand)
Definition of class LIB_EDIT_FRAME.
int ReplaceDuplicateTimeStamps()
Test all sheet and component objects in the schematic for duplicate time stamps and replaces them as ...
classes and function to generate graphics to plt or draw titles blocks and frame references
SCH_SCREEN * GetFirst()
void ClearDrawingState()
Clear the state flags of all the items in the screen.
Definition: sch_screen.cpp:563
virtual void ClearUndoORRedoList(UNDO_REDO_CONTAINER &aList, int aItemCount=-1) override
Free the undo or redo list from aList element.
Definition: sch_screen.cpp:548
void SetFileName(const wxString &aFileName)
Definition: sch_screen.h:129
Definition for part library class.
void SetModify()
Definition: base_screen.h:324
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
int SetGrid(const wxRealPoint &size)
set the current grid size m_Grid.
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
bool TestForRecursion(const SCH_SHEET_LIST &aSrcSheetHierarchy, const wxString &aDestFileName) const
Function TestForRecursion.
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Function DisplayInfoMessage displays an informational message box with aMessage.
Definition: confirm.cpp:275
Object used to load, save, search, and otherwise manipulate symbol library files.
void DisplayError(wxWindow *parent, const wxString &text, int displaytime)
Function DisplayError displays an error or warning message box with aMessage.
Definition: confirm.cpp:243
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)
Function IsOK displays a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:293
void ClrSave()
Definition: base_screen.h:327
void ClearMsgPanel(void)
Clear all messages from the message panel.
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition: sch_screen.h:523
SCH_SCREEN * LastScreen() const
Function LastScreen.
bool IsEmpty(bool aIncludeFallback=true)
Return true if the table is empty.
bool LockFile(const wxString &aFileName)
Mark a schematic file as being in use.
void SetTimeStamp(timestamp_t aNewTimeStamp)
Definition: base_struct.h:206
SCH_ITEM * GetDrawItems() const
Definition: sch_screen.h:160