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-2018 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( AUTOSAVE_PREFIX_FILENAME + 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  try
293  {
294  g_RootSheet = pi->Load( fullFileName, &Kiway() );
295  m_CurrentSheet->clear();
296  m_CurrentSheet->push_back( g_RootSheet );
297 
298  if( !pi->GetError().IsEmpty() )
299  {
300  DisplayErrorMessage( this,
301  _( "The entire schematic could not be loaded. Errors "
302  "occurred attempting to load \nhierarchical sheet "
303  "schematics." ),
304  pi->GetError() );
305  }
306  }
307  catch( const IO_ERROR& ioe )
308  {
309  // Do not leave g_RootSheet == NULL because it is expected to be
310  // a valid sheet. Therefore create a dummy empty root sheet and screen.
311  CreateScreens();
312  Zoom_Automatique( false );
313 
314  wxString msg;
315  msg.Printf( _( "Error loading schematic file \"%s\".\n%s" ),
316  GetChars( fullFileName ), GetChars( ioe.What() ) );
317  DisplayError( this, msg );
318 
319  msg.Printf( _( "Failed to load \"%s\"" ), GetChars( fullFileName ) );
320  AppendMsgPanel( wxEmptyString, msg, CYAN );
321 
322  return false;
323  }
324 
325  // It's possible the schematic parser fixed errors due to bugs so warn the user
326  // that the schematic has been fixed (modified).
327  SCH_SHEET_LIST sheetList( g_RootSheet );
328 
329  if( sheetList.IsModified() )
330  {
331  DisplayInfoMessage( this,
332  _( "An error was found when loading the schematic that has "
333  "been automatically fixed. Please save the schematic to "
334  "repair the broken file or it may not be usable with other "
335  "versions of KiCad." ) );
336  }
337 
338  UpdateFileHistory( fullFileName );
339 
340  SCH_SCREENS schematic;
341 
342  // Convert old projects over to use symbol library table.
343  if( schematic.HasNoFullyDefinedLibIds() )
344  {
345  DIALOG_SYMBOL_REMAP dlgRemap( this );
346 
347  dlgRemap.ShowQuasiModal();
348  }
349  else
350  {
351  // Check to see whether some old library parts need to be rescued
352  // Only do this if RescueNeverShow was not set.
353  wxConfigBase *config = Kiface().KifaceSettings();
354  bool rescueNeverShow = false;
355  config->Read( RescueNeverShowEntry, &rescueNeverShow, false );
356 
357  if( !rescueNeverShow )
359  }
360 
361  schematic.UpdateSymbolLinks(); // Update all symbol library links for all sheets.
363 
364  // Ensure the schematic is fully segmented on first display
366  SchematicCleanUp( true );
367  GetScreen()->ClearUndoORRedoList( GetScreen()->m_UndoList, 1 );
368  GetScreen()->TestDanglingEnds(); // Only perform the dangling end test on root sheet.
369  GetScreen()->m_Initialized = true;
370  }
371 
373  Zoom_Automatique( false );
375  SyncView();
377 
378  return true;
379 }
380 
381 
383 {
384  wxString msg;
385  wxString fullFileName;
386  SCH_SCREEN* screen = GetScreen();
387 
388  if( !screen )
389  {
390  wxLogError( wxT( "Document not ready, cannot import" ) );
391  return false;
392  }
393 
394  // open file chooser dialog
395  wxString path = wxPathOnly( Prj().GetProjectFullName() );
396 
397  wxFileDialog dlg( this, _( "Append Schematic" ), path, wxEmptyString,
398  SchematicFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
399 
400  if( dlg.ShowModal() == wxID_CANCEL )
401  return false;
402 
403  fullFileName = dlg.GetPath();
404 
405  wxFileName fn = fullFileName;
406 
407  if( fn.IsRelative() )
408  {
409  fn.MakeAbsolute();
410  fullFileName = fn.GetFullPath();
411  }
412 
413  wxString cache_name = PART_LIBS::CacheName( fullFileName );
414 
415  if( !!cache_name )
416  {
417  PART_LIBS* libs = Prj().SchLibs();
418 
419  try
420  {
421  if( PART_LIB* lib = libs->AddLibrary( cache_name ) )
422  lib->SetCache();
423  }
424  catch( const IO_ERROR& ioe )
425  {
426  DisplayError( this, ioe.What() );
427  }
428  }
429 
430  wxLogDebug( wxT( "Importing schematic " ) + fullFileName );
431 
432  // Load the schematic into a temporary sheet.
433  SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
434  std::unique_ptr< SCH_SHEET> newSheet( new SCH_SHEET );
435 
436  newSheet->SetFileName( fullFileName );
437 
438  try
439  {
440  pi->Load( fullFileName, &Kiway(), newSheet.get() );
441 
442  if( !pi->GetError().IsEmpty() )
443  {
444  DisplayErrorMessage( this,
445  _( "The entire schematic could not be loaded. Errors "
446  "occurred attempting to load hierarchical sheet "
447  "schematics." ),
448  pi->GetError() );
449  }
450  }
451  catch( const IO_ERROR& ioe )
452  {
453  msg.Printf( _( "Error occurred loading schematic file \"%s\"." ), fullFileName );
454  DisplayErrorMessage( this, msg, ioe.What() );
455 
456  msg.Printf( _( "Failed to load schematic \"%s\"" ), fullFileName );
457  AppendMsgPanel( wxEmptyString, msg, CYAN );
458 
459  return false;
460  }
461 
462  // Make sure any new sheet changes do not cause any recursion issues.
463  SCH_SHEET_LIST hierarchy( g_RootSheet ); // This is the schematic sheet hierarchy.
464  SCH_SHEET_LIST sheetHierarchy( newSheet.get() ); // This is the hierarchy of the import.
465 
466  wxFileName destFile = screen->GetFileName();
467 
468  if( destFile.IsRelative() )
469  destFile.MakeAbsolute( Prj().GetProjectPath() );
470 
471  if( hierarchy.TestForRecursion( sheetHierarchy, destFile.GetFullPath( wxPATH_UNIX ) ) )
472  {
473  msg.Printf( _( "The sheet changes cannot be made because the destination sheet already "
474  "has the sheet \"%s\" or one of it's subsheets as a parent somewhere in "
475  "the schematic hierarchy." ),
476  destFile.GetFullPath() );
477  DisplayError( this, msg );
478  return false;
479  }
480 
481  wxArrayString names;
482 
483  // Make sure the imported schematic has been remapped to the symbol library table.
484  SCH_SCREENS newScreens( newSheet.get() ); // All screens associated with the import.
485 
486  if( newScreens.HasNoFullyDefinedLibIds() )
487  {
488  DisplayInfoMessage( this,
489  "This schematic has not been remapped to the symbol library\n"
490  "table. The project this schematic belongs to must first be\n"
491  "remapped before it can be imported into the current project." );
492  return false;
493  }
494  else
495  {
496  // If there are symbol libraries in the imported schematic that are not in the
497  // symbol library table of this project, there could be a lot of broken symbol
498  // library links. Attempt to add the missing libraries to the project symbol
499  // library table.
500  newScreens.GetLibNicknames( names );
501  wxArrayString newLibNames;
502 
503  for( const auto& name : names )
504  {
505  if( !Prj().SchSymbolLibTable()->HasLibrary( name ) )
506  newLibNames.Add( name );
507  }
508 
509  wxFileName symLibTableFn( fn.GetPath(), SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
510 
511  if( !newLibNames.IsEmpty() && symLibTableFn.Exists() && symLibTableFn.IsFileReadable() )
512  {
513  SYMBOL_LIB_TABLE table;
514 
515  try
516  {
517  table.Load( symLibTableFn.GetFullPath() );
518  }
519  catch( const IO_ERROR& ioe )
520  {
521  msg.Printf( _( "An error occurred loading the symbol library table \"%s\"." ),
522  symLibTableFn.GetFullPath() );
523  DisplayErrorMessage( NULL, msg, ioe.What() );
524  }
525 
526  if( !table.IsEmpty() )
527  {
528  for( const auto& libName : newLibNames )
529  {
530  if( !table.HasLibrary( libName ) )
531  continue;
532 
533  // Don't expand environment variable because KIPRJMOD will not be correct
534  // for a different project.
535  wxString uri = table.GetFullURI( libName, false );
536  wxFileName newLib;
537 
538  if( uri.Contains( "${KIPRJMOD}" ) )
539  {
540  newLib.SetPath( fn.GetPath() );
541  newLib.SetFullName( uri.AfterLast( '}' ) );
542  uri = newLib.GetFullPath();
543  }
544  else if( uri.Contains( "$(KIPRJMOD)" ) )
545  {
546  newLib.SetPath( fn.GetPath() );
547  newLib.SetFullName( uri.AfterLast( ')' ) );
548  uri = newLib.GetFullPath();
549  }
550  else
551  {
552  uri = table.GetFullURI( libName );
553  }
554 
555  // Add the library from the imported project to the current project
556  // symbol library table.
557  const SYMBOL_LIB_TABLE_ROW* row = table.FindRow( libName );
558 
559  wxCHECK2_MSG( row, continue, "Library '" + libName +
560  "' missing from symbol library table '" +
561  symLibTableFn.GetFullPath() + "'." );
562 
563  wxString newLibName = libName;
564  int libNameCnt = 1;
565 
566  // Rename the imported symbol library if it already exists.
567  while( Prj().SchSymbolLibTable()->HasLibrary( newLibName ) )
568  newLibName = wxString::Format( "%s%d", libName, libNameCnt );
569 
570  auto newRow = new SYMBOL_LIB_TABLE_ROW( newLibName, uri, row->GetType(),
571  row->GetOptions(), row->GetDescr() );
572  Prj().SchSymbolLibTable()->InsertRow( newRow );
573 
574  if( libName != newLibName )
575  newScreens.ChangeSymbolLibNickname( libName, newLibName );
576  }
577  }
578  }
579  }
580 
581  newScreens.ClearAnnotation();
582 
583  // Check for duplicate sheet names in the current page.
584  wxArrayString duplicateSheetNames;
585  SCH_TYPE_COLLECTOR sheets;
586 
587  sheets.Collect( screen->GetDrawItems(), SCH_COLLECTOR::SheetsOnly );
588 
589  for( int i = 0; i < sheets.GetCount(); ++i )
590  {
591  if( newSheet->GetScreen()->GetSheet( ( ( SCH_SHEET* ) sheets[i] )->GetName() ) )
592  duplicateSheetNames.Add( ( ( SCH_SHEET* ) sheets[i] )->GetName() );
593  }
594 
595  if( !duplicateSheetNames.IsEmpty() )
596  {
597  msg.Printf( "Duplicate sheet names exist on the current page. Do you want to "
598  "automatically rename the duplicate sheet names?" );
599  if( !IsOK( this, msg ) )
600  return false;
601  }
602 
603  SCH_SCREEN* newScreen = newSheet->GetScreen();
604  wxCHECK_MSG( newScreen, false, "No screen defined for imported sheet." );
605 
606  for( const auto& duplicateName : duplicateSheetNames )
607  {
608  SCH_SHEET* renamedSheet = newScreen->GetSheet( duplicateName );
609 
610  wxCHECK2_MSG( renamedSheet, continue,
611  "Sheet " + duplicateName + " not found in imported schematic." );
612 
613  timestamp_t newtimestamp = GetNewTimeStamp();
614  renamedSheet->SetTimeStamp( newtimestamp );
615  renamedSheet->SetName( wxString::Format( "Sheet%8.8lX", (unsigned long) newtimestamp ) );
616  }
617 
618  // It is finally safe to add the imported schematic.
619  screen->Append( newScreen );
620 
621  SCH_SCREENS allScreens;
622  allScreens.ReplaceDuplicateTimeStamps();
623 
624  SCH_SCREENS screens( GetCurrentSheet().Last() );
625  screens.UpdateSymbolLinks( true );
626 
627  // Clear all annotation in the imported schematic to prevent clashes with existing annotation.
628  // Must be done after updating the symbol links as we need to know about multi-unit parts.
629  // screens.ClearAnnotation();
630 
631  screens.TestDanglingEnds();
632 
634  Zoom_Automatique( false );
636 
637  SyncView();
638  HardRedraw(); // Full reinit of the current screen and the display.
639  OnModify();
640 
641  return true;
642 }
643 
644 
645 void SCH_EDIT_FRAME::OnAppendProject( wxCommandEvent& event )
646 {
647  if( GetScreen() && GetScreen()->IsModified() )
648  {
649  wxString msg = _( "This operation cannot be undone.\n\n"
650  "Do you want to save the current document before proceeding?" );
651 
652  if( IsOK( this, msg ) )
653  SaveProject();
654  }
655 
656  AppendSchematic();
657 }
658 
659 
660 void SCH_EDIT_FRAME::OnImportProject( wxCommandEvent& aEvent )
661 {
662  if( !AskToSaveChanges() )
663  return;
664 
665  // Set the project location if none is set
666  bool setProject = Prj().GetProjectFullName().IsEmpty();
667  wxString path = wxPathOnly( Prj().GetProjectFullName() );
668 
669  wxFileDialog dlg( this, _( "Import Schematic" ), path, wxEmptyString,
670  EagleSchematicFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
671 
672  if( dlg.ShowModal() == wxID_CANCEL )
673  return;
674 
675  if( setProject )
676  {
677  wxFileName projectFn( dlg.GetPath() );
678  projectFn.SetExt( ProjectFileExtension );
679  Prj().SetProjectFullName( projectFn.GetFullPath() );
680  }
681 
682  // For now there is only one import plugin
683  importFile( dlg.GetPath(), SCH_IO_MGR::SCH_EAGLE );
684 }
685 
686 
687 void SCH_EDIT_FRAME::OnSaveProject( wxCommandEvent& aEvent )
688 {
689  SaveProject();
690 }
691 
692 
694 {
695  SCH_SCREEN* screen;
696  SCH_SCREENS screenList;
697  bool success = true;
698 
699  // I want to see it in the debugger, show me the string! Can't do that with wxFileName.
700  wxString fileName = Prj().AbsolutePath( g_RootSheet->GetFileName() );
701  wxFileName fn = fileName;
702 
703  if( !fn.IsDirWritable() )
704  {
705  wxString msg = wxString::Format( _( "Directory \"%s\" is not writable." ), fn.GetPath() );
706  DisplayError( this, msg );
707  return false;
708  }
709 
710  for( screen = screenList.GetFirst(); screen; screen = screenList.GetNext() )
711  success &= SaveEEFile( screen );
712 
714 
715  UpdateTitle();
716 
717  return success;
718 }
719 
720 
722 {
723  wxFileName tmpFileName = g_RootSheet->GetFileName();
724  wxFileName fn = tmpFileName;
725  wxFileName tmp;
726  SCH_SCREENS screens;
727 
728  bool autoSaveOk = true;
729 
730  tmp.AssignDir( fn.GetPath() );
731 
732  if( !tmp.IsOk() )
733  return false;
734 
735  if( !IsWritable( tmp ) )
736  return false;
737 
738  for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
739  {
740  // Only create auto save files for the schematics that have been modified.
741  if( !screen->IsSave() )
742  continue;
743 
744  tmpFileName = fn = screen->GetFileName();
745 
746  // Auto save file name is the normal file name prefixed with AUTOSAVE_PREFIX_FILENAME.
747  fn.SetName( AUTOSAVE_PREFIX_FILENAME + fn.GetName() );
748 
749  screen->SetFileName( fn.GetFullPath() );
750 
751  if( SaveEEFile( screen, false, NO_BACKUP_FILE ) )
752  screen->SetModify();
753  else
754  autoSaveOk = false;
755 
756  screen->SetFileName( tmpFileName.GetFullPath() );
757  }
758 
759  if( autoSaveOk )
760  m_autoSaveState = false;
761 
762  return autoSaveOk;
763 }
764 
765 
766 bool SCH_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType )
767 {
768  wxString projectpath;
769  wxFileName newfilename;
770  SCH_SHEET_LIST sheetList( g_RootSheet );
771 
772  switch( (SCH_IO_MGR::SCH_FILE_T) aFileType )
773  {
774  case SCH_IO_MGR::SCH_EAGLE:
775  // We insist on caller sending us an absolute path, if it does not, we say it's a bug.
776  wxASSERT_MSG( wxFileName( aFileName ).IsAbsolute(),
777  wxT( "Import eagle schematic caller didn't send full filename" ) );
778 
779  if( !LockFile( aFileName ) )
780  {
781  wxString msg = wxString::Format( _( "Schematic file \"%s\" is already open." ),
782  aFileName );
783  DisplayError( this, msg );
784  return false;
785  }
786 
787  try
788  {
789  delete g_RootSheet;
790  g_RootSheet = nullptr;
791  SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_EAGLE ) );
792  g_RootSheet = pi->Load( aFileName, &Kiway() );
793 
794  // Eagle sheets do not use a worksheet frame by default, so set it to an empty one
796  pglayout.SetEmptyLayout();
797 
798  BASE_SCREEN::m_PageLayoutDescrFileName = "empty.kicad_wks";
799  wxFileName layoutfn( Kiway().Prj().GetProjectPath(),
801  wxFile layoutfile;
802 
803  if( layoutfile.Create( layoutfn.GetFullPath() ) )
804  {
805  layoutfile.Write( WORKSHEET_LAYOUT::EmptyLayout() );
806  layoutfile.Close();
807  }
808 
809  projectpath = Kiway().Prj().GetProjectPath();
810  newfilename.SetPath( Prj().GetProjectPath() );
811  newfilename.SetName( Prj().GetProjectName() );
812  newfilename.SetExt( SchematicFileExtension );
813 
814  m_CurrentSheet->clear();
815  m_CurrentSheet->push_back( g_RootSheet );
817 
818  g_RootSheet->SetFileName( newfilename.GetFullPath() );
819  GetScreen()->SetFileName( newfilename.GetFullPath() );
820  GetScreen()->SetModify();
821  SaveProjectSettings( false );
822 
823  UpdateFileHistory( aFileName );
824  SCH_SCREENS schematic;
825  schematic.UpdateSymbolLinks(); // Update all symbol library links for all sheets.
826 
827  // Ensure the schematic is fully segmented on first display
829  SchematicCleanUp( true );
830  GetScreen()->m_Initialized = true;
831 
832  SCH_TYPE_COLLECTOR components;
833  SCH_SCREENS allScreens;
834 
835  for( SCH_SCREEN* screen = allScreens.GetFirst(); screen; screen = allScreens.GetNext() )
836  {
837  components.Collect( screen->GetDrawItems(), SCH_COLLECTOR::ComponentsOnly );
838 
839  for( int cmpIdx = 0; cmpIdx < components.GetCount(); ++cmpIdx )
840  {
841  std::vector<wxPoint> pts;
842  SCH_COMPONENT* cmp = static_cast<SCH_COMPONENT*>( components[cmpIdx] );
843 
844  // Update footprint LIB_ID to point to the imported Eagle library
845  auto fpField = cmp->GetField( FOOTPRINT );
846 
847  if( !fpField->GetText().IsEmpty() )
848  {
849  LIB_ID fpId;
850  fpId.Parse( fpField->GetText(), LIB_ID::ID_SCH, true );
851  fpId.SetLibNickname( newfilename.GetName() );
852  fpField->SetText( fpId.Format() );
853  }
854 
855  // Add junction dots where necessary
856  cmp->GetConnectionPoints( pts );
857 
858  for( auto i = pts.begin(); i != pts.end(); ++i )
859  {
860  if( GetScreen()->IsJunctionNeeded( *i, true ) )
861  AddJunction( *i, true );
862  }
863  }
864  }
865 
866  GetScreen()->ClearUndoORRedoList( GetScreen()->m_UndoList, 1 );
867  // Only perform the dangling end test on root sheet.
869 
871  Zoom_Automatique( false );
873  SyncView();
874  UpdateTitle();
875  }
876  catch( const IO_ERROR& ioe )
877  {
878  // Do not leave g_RootSheet == NULL because it is expected to be
879  // a valid sheet. Therefore create a dummy empty root sheet and screen.
880  CreateScreens();
881  Zoom_Automatique( false );
882 
883  wxString msg;
884  msg.Printf( _( "Error loading schematic \"%s\".\n%s" ), aFileName, ioe.What() );
885  DisplayError( this, msg );
886 
887  msg.Printf( _( "Failed to load \"%s\"" ), aFileName );
888  AppendMsgPanel( wxEmptyString, msg, CYAN );
889 
890  return false;
891  }
892 
893  return true;
894 
895  default:
896  return false;
897  }
898 }
899 
900 
902 {
903  SCH_SCREENS screenList;
904 
905  // Save any currently open and modified project files.
906  for( SCH_SCREEN* screen = screenList.GetFirst(); screen; screen = screenList.GetNext() )
907  {
908  if( screen->IsModify() )
909  {
910  if( !HandleUnsavedChanges( this, _( "The current schematic has been modified. Save changes?" ),
911  [&]()->bool { return SaveProject(); } ) )
912  {
913  return false;
914  }
915  }
916  }
917 
918  return true;
919 }
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.
SCH_JUNCTION * AddJunction(const wxPoint &aPosition, bool aPutInUndoList=false)
Add a new junction at aPosition.
int GetCount() const
Function GetCount returns the number of objects in the list.
Definition: collector.h:114
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:213
bool IsModified()
Function IsModified checks the entire hierarchy for any modifications.
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).
bool OpenProjectFiles(const std::vector< wxString > &aFileSet, int aCtl=0) override
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
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:259
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
bool IsJunctionNeeded(const wxPoint &aPosition, bool aNew=false)
Test if a junction is required for the items at aPosition on the screen.
Definition: sch_screen.cpp:325
SCH_SHEET * GetSheet(const wxString &aName)
Returns a sheet object pointer that is named aName.
Definition: sch_screen.cpp:624
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
#define KICTL_CREATE
caller thinks requested project files may not exist
Definition: kiway_player.h:140
void GetConnectionPoints(std::vector< wxPoint > &aPoints) const override
Function GetConnectionPoints add all the connection points for this item to aPoints.
const wxString & GetFileName() const
Definition: sch_screen.h:131
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...
virtual SCH_SHEET * Load(const wxString &aFileName, KIWAY *aKiway, SCH_SHEET *aAppendToMe=NULL, const PROPERTIES *aProperties=NULL)
Load information from some input file format that this SCH_PLUGIN implementation knows about...
Definition: sch_plugin.cpp:50
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)
VTBL_ENTRY const wxString GetProjectFullName() const
Function GetProjectFullName returns the full path and name of the project.
Definition: project.cpp:96
const wxString & GetOptions() const
Return the options string, which may hold a password or anything else needed to instantiate the under...
virtual wxConfigBase * config()
Function config returns the wxConfigBase used in SaveSettings(), and is overloaded in KICAD_MANAGER_F...
SCH_SCREEN * LastScreen() const
Function LastScreen.
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.
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.
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.
PROJECT & Prj() const
Function Prj returns a reference to the PROJECT "associated with" this KIWAY.
SCH_SHEET_PATH * m_CurrentSheet
which sheet we are presently working on.
timestamp_t GetNewTimeStamp()
Definition: common.cpp:212
void Save_File(wxCommandEvent &event)
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library table.
SCH_SHEET * g_RootSheet
Definition: eeschema.cpp:56
SCH_FIELD * GetField(int aFieldNdx) const
Returns a field in this symbol.
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()
const std::string SchematicFileExtension
VTBL_ENTRY PROJECT & Prj() const
Function Prj returns the PROJECT associated with this KIWAY.
Definition: kiway.cpp:142
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
VTBL_ENTRY const wxString AbsolutePath(const wxString &aFileName) const
Function AbsolutePath fixes up aFileName if it is relative to the project&#39;s directory to be an absolu...
Definition: project.cpp:380
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 &#39;-cache&#39; suffix...
Definition: libarch.cpp:42
void OnSaveProject(wxCommandEvent &aEvent)
Command event handler to save the entire project and create a component library archive.
wxConfigBase * KifaceSettings() const
Definition: kiface_i.h:103
wxLogTrace helper definitions.
bool LoadProjectFile()
Loads the KiCad project file (*.pro) settings specific to Eeschema.
virtual const wxString & GetError() const
Return an error string to the caller.
Definition: sch_plugin.cpp:193
bool HasNoFullyDefinedLibIds()
Test all of the schematic symbols to see if all LIB_ID objects library nickname is not set...
int m_LastGridSizeId
Definition: draw_frame.h:107
void Load(const wxString &aFileName)
Load the library table using the path defined by aFileName aFallBackTable.
const wxString & GetDescr() const
Return the description of the library referenced by this row.
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
wxString GetFileName(void) const
Return the filename corresponding to this sheet.
Definition: sch_sheet.cpp:662
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
A collection of PART_LIB objects.
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.
wxString GetFullURI(const wxString &aLibNickname, bool aExpandEnvVars=true) const
Return the full URI of the library mapped to aLibNickname.
SCH_ITEM * GetDrawItems() const
Definition: sch_screen.h:160
Definition the SCH_COMPONENT class for Eeschema.
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:33
#define AUTOSAVE_PREFIX_FILENAME
Prefix to create filenames for schematic files or other difile when auto-saved to retrieve a crash...
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:910
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&#39;s Inspector method, which does the collection...
VTBL_ENTRY const wxString GetProjectPath() const
Function GetProjectPath returns the full path of the project.
Definition: project.cpp:102
static const KICAD_T SheetsOnly[]
A scan list for schematic sheet items only.
bool RescueSymbolLibTableProject(bool aRunningOnDemand)
UTF8 Format() const
Definition: lib_id.cpp:237
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:561
virtual void ClearUndoORRedoList(UNDO_REDO_CONTAINER &aList, int aItemCount=-1) override
Free the undo or redo list from aList element.
Definition: sch_screen.cpp:546
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
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Function DisplayInfoMessage displays an informational message box with aMessage.
Definition: confirm.cpp:277
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:245
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:295
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
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.
bool TestForRecursion(const SCH_SHEET_LIST &aSrcSheetHierarchy, const wxString &aDestFileName) const
Function TestForRecursion.
void SetTimeStamp(timestamp_t aNewTimeStamp)
Definition: base_struct.h:206