From 2d48c81d581f1f268806cce0a33f040dfd69c229 Mon Sep 17 00:00:00 2001 From: Wayne Stambaugh Date: Sun, 4 Aug 2019 12:33:55 -0400 Subject: [PATCH] Revert "Eeschema: fix edit sheet bug." This reverts commit 6051dfae30ffa29fc920ae4d0741fd821c69bec2. --- eeschema/files-io.cpp | 412 ++++++++++++++++++++++++++- eeschema/sch_edit_frame.h | 110 +++---- eeschema/sheet.cpp | 582 +++++--------------------------------- 3 files changed, 508 insertions(+), 596 deletions(-) diff --git a/eeschema/files-io.cpp b/eeschema/files-io.cpp index 9c7821c40f..2289d7ce01 100644 --- a/eeschema/files-io.cpp +++ b/eeschema/files-io.cpp @@ -386,8 +386,14 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in bool SCH_EDIT_FRAME::AppendSchematic() { + int i; + wxString msg; wxString fullFileName; + wxString topLevelSheetPath; + wxFileName tmp; SCH_SCREEN* screen = GetScreen(); + bool libTableChanged = false; + if( !screen ) { @@ -406,10 +412,414 @@ bool SCH_EDIT_FRAME::AppendSchematic() fullFileName = dlg.GetPath(); - if( !LoadSheetFromFile( GetCurrentSheet().Last(), &GetCurrentSheet(), fullFileName ) ) + wxFileName fn = fullFileName; + + if( fn.IsRelative() ) + { + fn.MakeAbsolute(); + fullFileName = fn.GetFullPath(); + } + + // Load the schematic into a temporary sheet. + SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) ); + std::unique_ptr< SCH_SHEET> newSheet( new SCH_SHEET ); + + newSheet->SetFileName( fullFileName ); + + try + { + pi->Load( fullFileName, &Kiway(), newSheet.get() ); + + if( !pi->GetError().IsEmpty() ) + { + DisplayErrorMessage( this, + _( "The entire schematic could not be loaded. Errors " + "occurred attempting to load hierarchical sheet " + "schematics." ), + pi->GetError() ); + } + } + catch( const IO_ERROR& ioe ) + { + msg.Printf( _( "Error occurred loading schematic file \"%s\"." ), fullFileName ); + DisplayErrorMessage( this, msg, ioe.What() ); + + msg.Printf( _( "Failed to load schematic \"%s\"" ), fullFileName ); + AppendMsgPanel( wxEmptyString, msg, CYAN ); + return false; + } + + tmp = fn; + + // If the appended schematic is in a different folder from the current project and + // it contains hierarchical sheets, the hierarchical sheet paths need to be updated. + if( fn.GetPath( wxPATH_GET_SEPARATOR ) != Prj().GetProjectPath() && newSheet->CountSheets() ) + { + // Give the user the option to choose relative path if possible. + if( tmp.MakeRelativeTo( Prj().GetProjectPath() ) ) + { + wxMessageDialog msgDlg1( + this, + "Do you want to use a relative path to the appended " + "schematic?", "Select Path Type", + wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_QUESTION | wxCENTER ); + msgDlg1.SetYesNoLabels( wxMessageDialog::ButtonLabel( "Use Relative Path" ), + wxMessageDialog::ButtonLabel( "Use Absolute Path" ) ); + int rsp = msgDlg1.ShowModal(); + + if( rsp == wxID_CANCEL ) + { + return false; + } + else if( rsp == wxID_NO ) + { + topLevelSheetPath = fn.GetPathWithSep(); + + if( wxFileName::GetPathSeparator() == '\\' ) + topLevelSheetPath.Replace( "\\", "/" ); + } + else + { + topLevelSheetPath = tmp.GetPathWithSep( wxPATH_UNIX ); + } + } + else + { + topLevelSheetPath = tmp.GetPathWithSep(); + + if( wxFileName::GetPathSeparator() == '\\' ) + topLevelSheetPath.Replace( "\\", "/" ); + } + } + + // Make sure any new sheet changes do not cause any recursion issues. + SCH_SHEET_LIST hierarchy( g_RootSheet ); // This is the schematic sheet hierarchy. + SCH_SHEET_LIST sheetHierarchy( newSheet.get() ); // This is the hierarchy of the import. + + wxFileName destFile = screen->GetFileName(); + + if( destFile.IsRelative() ) + destFile.MakeAbsolute( Prj().GetProjectPath() ); + + if( hierarchy.TestForRecursion( sheetHierarchy, destFile.GetFullPath( wxPATH_UNIX ) ) ) + { + msg.Printf( _( "The sheet changes cannot be made because the destination sheet already " + "has the sheet \"%s\" or one of it's subsheets as a parent somewhere in " + "the schematic hierarchy." ), + destFile.GetFullPath() ); + DisplayError( this, msg ); + return false; + } + + wxArrayString names; + + // Make sure the imported schematic has been remapped to the symbol library table. + SCH_SCREENS newScreens( newSheet.get() ); // All screens associated with the import. + + if( newScreens.HasNoFullyDefinedLibIds() ) + { + DisplayInfoMessage( this, + "This schematic has not been remapped to the symbol library\n" + "table. The project this schematic belongs to must first be\n" + "remapped before it can be imported into the current project." ); + return false; + } + + wxArrayString newLibNames; + SCH_SCREENS prjScreens( g_RootSheet ); + + newScreens.GetLibNicknames( names ); + + wxMessageDialog::ButtonLabel okButtonLabel( _( "Continue Append" ) ); + wxMessageDialog::ButtonLabel cancelButtonLabel( _( "Cancel Append" ) ); + + if( fn.GetPath( wxPATH_GET_SEPARATOR ) == Prj().GetProjectPath() + && !prjScreens.HasSchematic( fullFileName ) ) + { + // A schematic in the current project path that isn't part of the current project. + // It's possible the user copied this schematic from another project so the library + // links may not be avaible. Even this is check is no guarantee that all symbol + // library links are valid but it's better than nothing. + for( const auto& name : names ) + { + if( !Prj().SchSymbolLibTable()->HasLibrary( name ) ) + newLibNames.Add( name ); + } + + if( !newLibNames.IsEmpty() ) + { + msg = _( "There are library names in the appended schematic that are missing " + "from the project library table. This may result in broken symbol " + "library links for the appended schematic. Do you wish to continue?" ); + wxMessageDialog msgDlg1( this, msg, _( "Continue Append Schematic" ), + wxOK | wxCANCEL | wxCANCEL_DEFAULT | + wxCENTER | wxICON_QUESTION ); + msgDlg1.SetOKCancelLabels( okButtonLabel, cancelButtonLabel ); + + if( msgDlg1.ShowModal() == wxID_CANCEL ) + return false; + } + } + else if( fn.GetPath( wxPATH_GET_SEPARATOR ) != Prj().GetProjectPath() ) + { + // A schematic loaded from a path other than the current project path. + + // If there are symbol libraries in the imported schematic that are not in the + // symbol library table of this project, there could be a lot of broken symbol + // library links. Attempt to add the missing libraries to the project symbol + // library table. + wxArrayString duplicateLibNames; + + for( const auto& name : names ) + { + if( !Prj().SchSymbolLibTable()->HasLibrary( name ) ) + newLibNames.Add( name ); + else + duplicateLibNames.Add( name ); + } + + SYMBOL_LIB_TABLE table; + wxFileName symLibTableFn( fn.GetPath(), SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() ); + + // If there are any new or duplicate libraries, check to see if it's possible that + // there could be any missing libraries that would cause broken symbol library links. + if( !newLibNames.IsEmpty() || !duplicateLibNames.IsEmpty() ) + { + if( !symLibTableFn.Exists() || !symLibTableFn.IsFileReadable() ) + { + msg.Printf( _( "The project library table \"%s\" does not exist or cannot " + "be read. This may result in broken symbol links for the " + "appended schematic. Do you wish to continue?" ), + fn.GetFullPath() ); + wxMessageDialog msgDlg2( this, msg, _( "Continue Append Schematic" ), + wxOK | wxCANCEL | wxCANCEL_DEFAULT | + wxCENTER | wxICON_QUESTION ); + msgDlg2.SetOKCancelLabels( okButtonLabel, cancelButtonLabel ); + + if( msgDlg2.ShowModal() == wxID_CANCEL ) + return false; + } + else + { + try + { + table.Load( symLibTableFn.GetFullPath() ); + } + catch( const IO_ERROR& ioe ) + { + msg.Printf( _( "An error occurred loading the symbol library table \"%s\"." ), + symLibTableFn.GetFullPath() ); + DisplayErrorMessage( NULL, msg, ioe.What() ); + return false; + } + } + } + + // Check to see if any of the symbol libraries found in the appended schematic do + // not exist in the current project are missing from the appended project symbol + // library table. + if( !newLibNames.IsEmpty() ) + { + bool missingLibNames = table.IsEmpty(); + + if( !missingLibNames ) + { + for( const auto& newLibName : newLibNames ) + { + if( !table.HasLibrary( newLibName ) ) + { + missingLibNames = true; + break; + } + } + } + + if( missingLibNames ) + { + msg = _( "There are library names in the appended schematic that are missing " + "from the appended schematic project library table. This may result " + "in broken symbol library links for the appended schematic. " + "Do you wish to continue?" ); + wxMessageDialog msgDlg3( this, msg, _( "Continue Append Schematic" ), + wxOK | wxCANCEL | wxCANCEL_DEFAULT | + wxCENTER | wxICON_QUESTION ); + msgDlg3.SetOKCancelLabels( okButtonLabel, cancelButtonLabel ); + + if( msgDlg3.ShowModal() == wxID_CANCEL ) + return false; + } + } + + // The library name already exists in the current project. Check to see if the + // duplicate name is the same library in the current project. If it's not, it's + // most likely that the symbol library links will be broken. + if( !duplicateLibNames.IsEmpty() && !table.IsEmpty() ) + { + bool libNameConflict = false; + + for( const auto& duplicateLibName : duplicateLibNames ) + { + const SYMBOL_LIB_TABLE_ROW* thisRow = nullptr; + const SYMBOL_LIB_TABLE_ROW* otherRow = nullptr; + + if( Prj().SchSymbolLibTable()->HasLibrary( duplicateLibName ) ) + thisRow = Prj().SchSymbolLibTable()->FindRow( duplicateLibName ); + + if( table.HasLibrary( duplicateLibName ) ) + otherRow = table.FindRow( duplicateLibName ); + + // It's in the global library table so there is no conflict. + if( thisRow && !otherRow ) + continue; + + if( !thisRow || !otherRow ) + continue; + + wxFileName otherUriFileName; + wxString thisURI = thisRow->GetFullURI( true ); + wxString otherURI = otherRow->GetFullURI( false); + + if( otherURI.Contains( "${KIPRJMOD}" ) || otherURI.Contains( "$(KIPRJMOD)" ) ) + { + // Cannot use relative paths here, "${KIPRJMOD}../path-to-cache-lib" does + // not expand to a valid symbol library path. + otherUriFileName.SetPath( fn.GetPath() ); + otherUriFileName.SetFullName( otherURI.AfterLast( '}' ) ); + otherURI = otherUriFileName.GetFullPath(); + } + + if( thisURI != otherURI ) + { + libNameConflict = true; + break; + } + } + + if( libNameConflict ) + { + msg = _( "A duplicate library name that references a different library exists " + "in the current library table. This conflict cannot be resolved and " + "may result in broken symbol library links for the appended schematic. " + "Do you wish to continue?" ); + wxMessageDialog msgDlg4( this, msg, _( "Continue Append Schematic" ), + wxOK | wxCANCEL | wxCANCEL_DEFAULT | + wxCENTER | wxICON_QUESTION ); + msgDlg4.SetOKCancelLabels( okButtonLabel, cancelButtonLabel ); + + if( msgDlg4.ShowModal() == wxID_CANCEL ) + return false; + } + } + + // All (most?) of the possible broken symbol library link cases are covered. Map the + // new appended schematic project symbol library table entries to the current project + // symbol library table. + if( !newLibNames.IsEmpty() && !table.IsEmpty() ) + { + for( const auto& libName : newLibNames ) + { + if( !table.HasLibrary( libName ) + || Prj().SchSymbolLibTable()->HasLibrary( libName ) ) + continue; + + // Don't expand environment variable because KIPRJMOD will not be correct + // for a different project. + wxString uri = table.GetFullURI( libName, false ); + wxFileName newLib; + + if( uri.Contains( "${KIPRJMOD}" ) || uri.Contains( "$(KIPRJMOD)" ) ) + { + // Cannot use relative paths here, "${KIPRJMOD}../path-to-cache-lib" does + // not expand to a valid symbol library path. + newLib.SetPath( fn.GetPath() ); + newLib.SetFullName( uri.AfterLast( '}' ) ); + uri = newLib.GetFullPath(); + } + else + { + uri = table.GetFullURI( libName ); + } + + // Add the library from the imported project to the current project + // symbol library table. + const SYMBOL_LIB_TABLE_ROW* row = table.FindRow( libName ); + + auto newRow = new SYMBOL_LIB_TABLE_ROW( libName, uri, row->GetType(), + row->GetOptions(), row->GetDescr() ); + + Prj().SchSymbolLibTable()->InsertRow( newRow ); + libTableChanged = true; + } + } + } + + newScreens.ClearAnnotation(); + + // Check for duplicate sheet names in the current page. + wxArrayString duplicateSheetNames; + SCH_TYPE_COLLECTOR sheets; + + sheets.Collect( screen->GetDrawItems(), SCH_COLLECTOR::SheetsOnly ); + + for( i = 0; i < sheets.GetCount(); ++i ) + { + if( newSheet->GetScreen()->GetSheet( ( ( SCH_SHEET* ) sheets[i] )->GetName() ) ) + duplicateSheetNames.Add( ( ( SCH_SHEET* ) sheets[i] )->GetName() ); + } + + if( !duplicateSheetNames.IsEmpty() ) + { + msg.Printf( "Duplicate sheet names exist on the current page. Do you want to " + "automatically rename the duplicate sheet names?" ); + + if( !IsOK( this, msg ) ) + return false; + } + + SCH_SCREEN* newScreen = newSheet->GetScreen(); + wxCHECK_MSG( newScreen, false, "No screen defined for imported sheet." ); + + for( const auto& duplicateName : duplicateSheetNames ) + { + SCH_SHEET* renamedSheet = newScreen->GetSheet( duplicateName ); + + wxCHECK2_MSG( renamedSheet, continue, + "Sheet " + duplicateName + " not found in imported schematic." ); + + timestamp_t newtimestamp = GetNewTimeStamp(); + renamedSheet->SetTimeStamp( newtimestamp ); + renamedSheet->SetName( wxString::Format( "Sheet%8.8lX", (unsigned long) newtimestamp ) ); + } + + SCH_TYPE_COLLECTOR newTopLevelSheets; + + newTopLevelSheets.Collect( newSheet->GetScreen()->GetDrawItems(), SCH_COLLECTOR::SheetsOnly ); + + for( i = 0; i < newTopLevelSheets.GetCount(); ++i ) + { + SCH_SHEET* tmpSheet = dynamic_cast< SCH_SHEET* >( newTopLevelSheets[i] ); + wxCHECK2( tmpSheet != nullptr, continue ); + tmpSheet->SetFileName( topLevelSheetPath + tmpSheet->GetFileName() ); + } + + if( libTableChanged ) + Prj().SchSymbolLibTable()->Save( Prj().GetProjectPath() + + SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() ); + + // It is finally safe to add the imported schematic. + screen->Append( newScreen ); + + SCH_SCREENS allScreens; + allScreens.ReplaceDuplicateTimeStamps(); SCH_SCREENS screens( GetCurrentSheet().Last() ); + screens.UpdateSymbolLinks( true ); + + // Clear all annotation in the imported schematic to prevent clashes with existing annotation. + // Must be done after updating the symbol links as we need to know about multi-unit parts. + // screens.ClearAnnotation(); + screens.TestDanglingEnds(); GetScreen()->SetGrid( ID_POPUP_GRID_LEVEL_1000 + m_LastGridSizeId ); diff --git a/eeschema/sch_edit_frame.h b/eeschema/sch_edit_frame.h index 51799e3796..20154c8807 100644 --- a/eeschema/sch_edit_frame.h +++ b/eeschema/sch_edit_frame.h @@ -703,9 +703,33 @@ public: bool OpenProjectFiles( const std::vector& aFileSet, int aCtl = 0 ) override; /** - * Import a KiCad schematic into the current sheet. + * Import a KiCad schematic into the current page. * - * @return True if the schematic was imported properly. + * In order to import a schematic a lot of things have to happen to before the contents + * of the imported schematic can be appended to the current page. The following list + * describes this process: + * + * - Load the schematic into a temporary SCH_SHEET object. + * - Make sure the imported schematic does not cause any hierarchy recursion issues. + * - Verify the imported schematic uses fully qualified #LIB_ID objects (symbol library table). + * - Check to see if any symbol libraries need to be added to the current project's symbol + * library table. This includes: + * - Check if the symbol library already exists in the project or global symbol library + * table. + * - Convert symbol library URLS that use the ${KIPRJMOD} environment variable to absolute + * paths. ${KIPRJMOD} will not be the same for this project. + * - Check for duplicate symbol library nicknames and change the new symbol library nickname + * to prevent library name clashes. + * - Update all schematic symbol LIB_ID object library nicknames when the library nickname + * was changed to prevent clashes. + * - Check for duplicate sheet names which is illegal and automatically rename any duplicate + * sheets in the imported schematic. + * - Clear all of the annotation in the imported schematic to prevent clashes. + * - Append the objects from the temporary sheet to the current page. + * - Replace any duplicate time stamps. + * - Refresh the symbol library links. + * + * @return True if the project was imported properly. */ bool AppendSchematic(); @@ -956,35 +980,6 @@ private: void EditSchematicText( SCH_TEXT* TextStruct ); void ChangeTextOrient( SCH_TEXT* aTextItem ); - /** - * Verify that \a aSheet will not cause a recursion error in \a aHierarchy. - * - * @param aSheet is the #SCH_SHEET object to test. - * @param aHierarchy is the #SCH_SHEET_PATH where \a aSheet is going to reside. - * - * @return true if \a aSheet will cause a resursion error in \a aHierarchy. - */ - bool checkSheetForRecursion( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy ); - - /** - * Verify that the symbol library links \a aSheet and all of it's child sheets have - * been remapped to the symbol library table. - * - * @param aSheet is the #SCH_SHEET object to test. - * - * @return true if \a aSheet and it's child sheets have not been remapped. - */ - bool checkForNoFullyDefinedLibIds( SCH_SHEET* aSheet ); - - /** - * Load the given filename but sets the path to the current project path. - * - * @param full filepath of file to be imported. - * @param aFileType SCH_FILE_T value for file type - */ - bool importFile( const wxString& aFileName, int aFileType ); - -public: /** * Command event handler to change a text type to another one. * @@ -1107,54 +1102,15 @@ public: wxPoint GetLastSheetPinPosition() const { return m_lastSheetPinPosition; } private: - void InitSheet( SCH_SHEET* aSheet, const wxString& aNewFilename ); - /** - * Load a the KiCad schematic file \a aFileName into the sheet \a aSheet. + * Load the given filename but sets the path to the current project path. * - * If \a aSheet does not have a valid #SCH_SCREEN object, the schematic is loaded into - * \a Sheet. Otherwise, it is appended to the current #SCH_SCREEN object. - * - * In order to import a schematic a lot of things have to happen to before the contents - * of the imported schematic can be appended to the current page. The following list - * describes this process: - * - * - Load the schematic into a temporary #SCH_SHEET object. - * - Make sure the imported schematic does not cause any hierarchy recursion issues. - * - Verify the imported schematic uses fully qualified #LIB_ID objects (symbol library table). - * - Check all of the possible combinations that could cause broken symbol library links - * and give the user the option to cancel the append process. The following conditions - * are check but they still do not guarantee that there will not be any broken symbol - * library links: - * - The source schematic is in the current project path and contains symbol library - * nicknames not found in the project symbol library table. This can happen if the - * schematic is copied to the current project path from another project. - * - The source schematic is in a different path and there are symbol library link nicknames - * that do not exist in either the current symbol library table or the source project - * symbol library table if it exists in the source path. - * - The source schematic is in a different path and contains duplicate symbol library - * nicknames that point to different libraries. - * - Check to see if any symbol libraries need to be added to the current project's symbol - * library table. This includes: - * - Check if the symbol library already exists in the project or global symbol library - * table. - * - Convert symbol library URLS that use the ${KIPRJMOD} environment variable to absolute - * paths. ${KIPRJMOD} will not be the same for this project. - * - Clear all of the annotation in the imported schematic to prevent clashes. - * - Append the objects from the temporary sheet to the current page. - * - Replace any duplicate time stamps. - * - Refresh the symbol library links. - * - * @param aSheet is the sheet to either append or load the schematic. - * @param aHierarchy is the current position in the schematic hierarchy used to test for - * possible file recursion issues. - * @param aFileName is the file name to load. The file name is expected to have an absolute - * path. - * - * @return True if the schematic was imported properly. + * @param full filepath of file to be imported. + * @param aFileType SCH_FILE_T value for file type */ - bool LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy, - const wxString& aFileName ); + bool importFile( const wxString& aFileName, int aFileType ); + + bool validateSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy ); /** * Create a new SCH_SHEET_PIN object and add it to \a aSheet at the current cursor position. @@ -1254,6 +1210,7 @@ public: */ void OrientComponent( COMPONENT_ORIENTATION_T aOrientation = CMP_NORMAL ); +private: void OnSelectUnit( wxCommandEvent& aEvent ); void ConvertPart( SCH_COMPONENT* DrawComponent ); @@ -1272,6 +1229,7 @@ public: void PasteListOfItems( wxDC* DC ); /* Undo - redo */ +public: /** * Create a copy of the current schematic item, and put it in the undo list. diff --git a/eeschema/sheet.cpp b/eeschema/sheet.cpp index 12a6d5589b..8681fd523d 100644 --- a/eeschema/sheet.cpp +++ b/eeschema/sheet.cpp @@ -38,487 +38,8 @@ #include #include #include -#include + #include -#include -#include - - -bool SCH_EDIT_FRAME::checkSheetForRecursion( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy ) -{ - wxASSERT( aSheet && aHierarchy ); - - wxString msg; - SCH_SHEET_LIST hierarchy( g_RootSheet ); // This is the full schematic sheet hierarchy. - SCH_SHEET_LIST sheetHierarchy( aSheet ); // This is the hierarchy of the loaded file. - - wxFileName destFile = aHierarchy->LastScreen()->GetFileName(); - - // SCH_SCREEN object file paths are expected to be absolute. If this assert fires, - // something is seriously broken. - wxASSERT( destFile.IsAbsolute() ); - - if( hierarchy.TestForRecursion( sheetHierarchy, destFile.GetFullPath( wxPATH_UNIX ) ) ) - { - msg.Printf( _( "The sheet changes cannot be made because the destination sheet already " - "has the sheet \"%s\" or one of it's subsheets as a parent somewhere in " - "the schematic hierarchy." ), - destFile.GetFullPath() ); - DisplayError( this, msg ); - return true; - } - - return false; -} - - -bool SCH_EDIT_FRAME::checkForNoFullyDefinedLibIds( SCH_SHEET* aSheet ) -{ - wxASSERT( aSheet && aSheet->GetScreen() ); - - wxString msg; - SCH_SCREENS newScreens( aSheet ); - - if( newScreens.HasNoFullyDefinedLibIds() ) - { - msg.Printf( _( "The schematic \"%s\" has not had it's symbol library links remapped " - "to the symbol library table. The project this schematic belongs to " - "must first be remapped before it can be imported into the current " - "project." ), aSheet->GetScreen()->GetFileName() ); - DisplayInfoMessage( this, msg ); - return true; - } - - return false; -} - - -void SCH_EDIT_FRAME::InitSheet( SCH_SHEET* aSheet, const wxString& aNewFilename ) -{ - aSheet->SetScreen( new SCH_SCREEN( &Kiway() ) ); - aSheet->GetScreen()->SetModify(); - aSheet->GetScreen()->SetMaxUndoItems( m_UndoRedoCountMax ); - aSheet->GetScreen()->SetFileName( aNewFilename ); -} - - -bool SCH_EDIT_FRAME::LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy, - const wxString& aFileName ) -{ - wxASSERT( aSheet && aHierarchy ); - - int i; - wxString msg; - wxString topLevelSheetPath; - wxFileName tmp; - wxFileName currentSheetFileName; - bool libTableChanged = false; - SCH_SCREEN* currentScreen = aHierarchy->LastScreen(); - SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) ); - std::unique_ptr< SCH_SHEET> newSheet( new SCH_SHEET ); - - wxFileName fileName( aFileName ); - - if( !fileName.IsAbsolute() ) - { - wxCHECK_MSG( fileName.MakeAbsolute(), false, - wxString::Format( "Cannot make file name \"%s\" path absolute.", - aFileName ) ); - } - - wxString fullFilename = fileName.GetFullPath(); - - try - { - if( aSheet->GetScreen() != nullptr ) - { - newSheet.reset( pi->Load( fullFilename, &Kiway() ) ); - } - else - { - newSheet->SetFileName( fullFilename ); - pi->Load( fullFilename, &Kiway(), newSheet.get() ); - } - - if( !pi->GetError().IsEmpty() ) - { - msg = _( "The entire schematic could not be loaded. Errors occurred attempting " - "to load hierarchical sheet schematics." ); - - wxMessageDialog msgDlg1( this, msg, _( "Schematic Load Error" ), - wxOK | wxCANCEL | wxCANCEL_DEFAULT | - wxCENTER | wxICON_QUESTION ); - msgDlg1.SetOKLabel( wxMessageDialog::ButtonLabel( _( "Use partial schematic" ) ) ); - msgDlg1.SetExtendedMessage( pi->GetError() ); - - if( msgDlg1.ShowModal() == wxID_CANCEL ) - return false; - } - } - catch( const IO_ERROR& ioe ) - { - msg.Printf( _( "Error occurred loading schematic file \"%s\"." ), fullFilename ); - DisplayErrorMessage( this, msg, ioe.What() ); - - msg.Printf( _( "Failed to load schematic \"%s\"" ), fullFilename ); - AppendMsgPanel( wxEmptyString, msg, CYAN ); - - return false; - } - - tmp = fileName; - - // If the loaded schematic is in a different folder from the current project and - // it contains hierarchical sheets, the hierarchical sheet paths need to be updated. - if( fileName.GetPath( wxPATH_GET_SEPARATOR ) != Prj().GetProjectPath() - && newSheet->CountSheets() ) - { - // Give the user the option to choose relative path if possible. - if( tmp.MakeRelativeTo( Prj().GetProjectPath() ) ) - { - wxMessageDialog msgDlg2( - this, - "Do you want to use a relative path to the loaded " - "schematic?", "Select Path Type", - wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_QUESTION | wxCENTER ); - msgDlg2.SetYesNoLabels( wxMessageDialog::ButtonLabel( "Use Relative Path" ), - wxMessageDialog::ButtonLabel( "Use Absolute Path" ) ); - int rsp = msgDlg2.ShowModal(); - - if( rsp == wxID_CANCEL ) - { - return false; - } - else if( rsp == wxID_NO ) - { - topLevelSheetPath = fileName.GetPathWithSep(); - - if( wxFileName::GetPathSeparator() == '\\' ) - topLevelSheetPath.Replace( "\\", "/" ); - } - else - { - topLevelSheetPath = tmp.GetPathWithSep( wxPATH_UNIX ); - } - } - else - { - topLevelSheetPath = tmp.GetPathWithSep(); - - if( wxFileName::GetPathSeparator() == '\\' ) - topLevelSheetPath.Replace( "\\", "/" ); - } - } - - // Make sure any new sheet changes do not cause any recursion issues. - SCH_SHEET_LIST hierarchy( g_RootSheet ); // This is the schematic sheet hierarchy. - SCH_SHEET_LIST sheetHierarchy( newSheet.get() ); // This is the hierarchy of the loaded file. - - if( checkSheetForRecursion( newSheet.get(), aHierarchy ) - || checkForNoFullyDefinedLibIds( newSheet.get() ) ) - return false; - - // Make a valiant attempt to warn the user of all possible scenarios where there could - // be broken symbol library links. - wxArrayString names; - wxArrayString newLibNames; - SCH_SCREENS newScreens( newSheet.get() ); // All screens associated with the import. - SCH_SCREENS prjScreens( g_RootSheet ); - - newScreens.GetLibNicknames( names ); - - wxMessageDialog::ButtonLabel okButtonLabel( _( "Continue Load" ) ); - wxMessageDialog::ButtonLabel cancelButtonLabel( _( "Cancel Load" ) ); - - if( fileName.GetPath( wxPATH_GET_SEPARATOR ) == Prj().GetProjectPath() - && !prjScreens.HasSchematic( fullFilename ) ) - { - // A schematic in the current project path that isn't part of the current project. - // It's possible the user copied this schematic from another project so the library - // links may not be avaible. Even this is check is no guarantee that all symbol - // library links are valid but it's better than nothing. - for( const auto& name : names ) - { - if( !Prj().SchSymbolLibTable()->HasLibrary( name ) ) - newLibNames.Add( name ); - } - - if( !newLibNames.IsEmpty() ) - { - msg = _( "There are library names in the loaded schematic that are missing " - "from the project library table. This may result in broken symbol " - "library links for the loaded schematic. Do you wish to continue?" ); - wxMessageDialog msgDlg3( this, msg, _( "Continue Load Schematic" ), - wxOK | wxCANCEL | wxCANCEL_DEFAULT | - wxCENTER | wxICON_QUESTION ); - msgDlg3.SetOKCancelLabels( okButtonLabel, cancelButtonLabel ); - - if( msgDlg3.ShowModal() == wxID_CANCEL ) - return false; - } - } - else if( fileName.GetPath( wxPATH_GET_SEPARATOR ) != Prj().GetProjectPath() ) - { - // A schematic loaded from a path other than the current project path. - - // If there are symbol libraries in the imported schematic that are not in the - // symbol library table of this project, there could be a lot of broken symbol - // library links. Attempt to add the missing libraries to the project symbol - // library table. - wxArrayString duplicateLibNames; - - for( const auto& name : names ) - { - if( !Prj().SchSymbolLibTable()->HasLibrary( name ) ) - newLibNames.Add( name ); - else - duplicateLibNames.Add( name ); - } - - SYMBOL_LIB_TABLE table; - wxFileName symLibTableFn( fileName.GetPath(), - SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() ); - - // If there are any new or duplicate libraries, check to see if it's possible that - // there could be any missing libraries that would cause broken symbol library links. - if( !newLibNames.IsEmpty() || !duplicateLibNames.IsEmpty() ) - { - if( !symLibTableFn.Exists() || !symLibTableFn.IsFileReadable() ) - { - msg.Printf( _( "The project library table \"%s\" does not exist or cannot " - "be read. This may result in broken symbol links for the " - "schematic. Do you wish to continue?" ), - fileName.GetFullPath() ); - wxMessageDialog msgDlg4( this, msg, _( "Continue Load Schematic" ), - wxOK | wxCANCEL | wxCANCEL_DEFAULT | - wxCENTER | wxICON_QUESTION ); - msgDlg4.SetOKCancelLabels( okButtonLabel, cancelButtonLabel ); - - if( msgDlg4.ShowModal() == wxID_CANCEL ) - return false; - } - else - { - try - { - table.Load( symLibTableFn.GetFullPath() ); - } - catch( const IO_ERROR& ioe ) - { - msg.Printf( _( "An error occurred loading the symbol library table " - "\"%s\"." ), - symLibTableFn.GetFullPath() ); - DisplayErrorMessage( NULL, msg, ioe.What() ); - return false; - } - } - } - - // Check to see if any of the symbol libraries found in the appended schematic do - // not exist in the current project are missing from the appended project symbol - // library table. - if( !newLibNames.IsEmpty() ) - { - bool missingLibNames = table.IsEmpty(); - - if( !missingLibNames ) - { - for( const auto& newLibName : newLibNames ) - { - if( !table.HasLibrary( newLibName ) ) - { - missingLibNames = true; - break; - } - } - } - - if( missingLibNames ) - { - msg = _( "There are library names in the loaded schematic that are missing " - "from the loaded schematic project library table. This may result " - "in broken symbol library links for the schematic. " - "Do you wish to continue?" ); - wxMessageDialog msgDlg5( this, msg, _( "Continue Load Schematic" ), - wxOK | wxCANCEL | wxCANCEL_DEFAULT | - wxCENTER | wxICON_QUESTION ); - msgDlg5.SetOKCancelLabels( okButtonLabel, cancelButtonLabel ); - - if( msgDlg5.ShowModal() == wxID_CANCEL ) - return false; - } - } - - // The library name already exists in the current project. Check to see if the - // duplicate name is the same library in the current project. If it's not, it's - // most likely that the symbol library links will be broken. - if( !duplicateLibNames.IsEmpty() && !table.IsEmpty() ) - { - bool libNameConflict = false; - - for( const auto& duplicateLibName : duplicateLibNames ) - { - const SYMBOL_LIB_TABLE_ROW* thisRow = nullptr; - const SYMBOL_LIB_TABLE_ROW* otherRow = nullptr; - - if( Prj().SchSymbolLibTable()->HasLibrary( duplicateLibName ) ) - thisRow = Prj().SchSymbolLibTable()->FindRow( duplicateLibName ); - - if( table.HasLibrary( duplicateLibName ) ) - otherRow = table.FindRow( duplicateLibName ); - - // It's in the global library table so there is no conflict. - if( thisRow && !otherRow ) - continue; - - if( !thisRow || !otherRow ) - continue; - - wxFileName otherUriFileName; - wxString thisURI = thisRow->GetFullURI( true ); - wxString otherURI = otherRow->GetFullURI( false); - - if( otherURI.Contains( "${KIPRJMOD}" ) || otherURI.Contains( "$(KIPRJMOD)" ) ) - { - // Cannot use relative paths here, "${KIPRJMOD}../path-to-cache-lib" does - // not expand to a valid symbol library path. - otherUriFileName.SetPath( fileName.GetPath() ); - otherUriFileName.SetFullName( otherURI.AfterLast( '}' ) ); - otherURI = otherUriFileName.GetFullPath(); - } - - if( thisURI != otherURI ) - { - libNameConflict = true; - break; - } - } - - if( libNameConflict ) - { - msg = _( "A duplicate library name that references a different library exists " - "in the current library table. This conflict cannot be resolved and " - "may result in broken symbol library links for the schematic. " - "Do you wish to continue?" ); - wxMessageDialog msgDlg6( this, msg, _( "Continue Load Schematic" ), - wxOK | wxCANCEL | wxCANCEL_DEFAULT | - wxCENTER | wxICON_QUESTION ); - msgDlg6.SetOKCancelLabels( okButtonLabel, cancelButtonLabel ); - - if( msgDlg6.ShowModal() == wxID_CANCEL ) - return false; - } - } - - // All (most?) of the possible broken symbol library link cases are covered. Map the - // new appended schematic project symbol library table entries to the current project - // symbol library table. - if( !newLibNames.IsEmpty() && !table.IsEmpty() ) - { - for( const auto& libName : newLibNames ) - { - if( !table.HasLibrary( libName ) - || Prj().SchSymbolLibTable()->HasLibrary( libName ) ) - continue; - - // Don't expand environment variable because KIPRJMOD will not be correct - // for a different project. - wxString uri = table.GetFullURI( libName, false ); - wxFileName newLib; - - if( uri.Contains( "${KIPRJMOD}" ) || uri.Contains( "$(KIPRJMOD)" ) ) - { - // Cannot use relative paths here, "${KIPRJMOD}../path-to-cache-lib" does - // not expand to a valid symbol library path. - newLib.SetPath( fileName.GetPath() ); - newLib.SetFullName( uri.AfterLast( '}' ) ); - uri = newLib.GetFullPath(); - } - else - { - uri = table.GetFullURI( libName ); - } - - // Add the library from the imported project to the current project - // symbol library table. - const SYMBOL_LIB_TABLE_ROW* row = table.FindRow( libName ); - - auto newRow = new SYMBOL_LIB_TABLE_ROW( libName, uri, row->GetType(), - row->GetOptions(), row->GetDescr() ); - - Prj().SchSymbolLibTable()->InsertRow( newRow ); - libTableChanged = true; - } - } - } - - // Check for duplicate sheet names in the current page. - wxArrayString duplicateSheetNames; - SCH_TYPE_COLLECTOR sheets; - - sheets.Collect( currentScreen->GetDrawItems(), SCH_COLLECTOR::SheetsOnly ); - - for( i = 0; i < sheets.GetCount(); ++i ) - { - if( newSheet->GetScreen()->GetSheet( ( ( SCH_SHEET* ) sheets[i] )->GetName() ) ) - duplicateSheetNames.Add( ( ( SCH_SHEET* ) sheets[i] )->GetName() ); - } - - if( !duplicateSheetNames.IsEmpty() ) - { - msg.Printf( "Duplicate sheet names exist on the current page. Do you want to " - "automatically rename the duplicate sheet names?" ); - - if( !IsOK( this, msg ) ) - return false; - } - - // Rename all duplicate sheet names. - SCH_SCREEN* newScreen = newSheet->GetScreen(); - wxCHECK_MSG( newScreen, false, "No screen defined for sheet." ); - - for( const auto& duplicateName : duplicateSheetNames ) - { - SCH_SHEET* renamedSheet = newScreen->GetSheet( duplicateName ); - - wxCHECK2_MSG( renamedSheet, continue, - "Sheet " + duplicateName + " not found in imported schematic." ); - - timestamp_t newtimestamp = GetNewTimeStamp(); - renamedSheet->SetTimeStamp( newtimestamp ); - renamedSheet->SetName( wxString::Format( "Sheet%8.8lX", (unsigned long) newtimestamp ) ); - } - - // Set all sheets loaded into the correct sheet file paths. - SCH_TYPE_COLLECTOR newTopLevelSheets; - - newTopLevelSheets.Collect( newSheet->GetScreen()->GetDrawItems(), SCH_COLLECTOR::SheetsOnly ); - - for( i = 0; i < newTopLevelSheets.GetCount(); ++i ) - { - SCH_SHEET* tmpSheet = dynamic_cast< SCH_SHEET* >( newTopLevelSheets[i] ); - wxCHECK2( tmpSheet != nullptr, continue ); - tmpSheet->SetFileName( topLevelSheetPath + tmpSheet->GetFileName() ); - } - - if( libTableChanged ) - Prj().SchSymbolLibTable()->Save( Prj().GetProjectPath() + - SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() ); - - // It is finally safe to add or append the imported schematic. - if( aSheet->GetScreen() == nullptr ) - aSheet->SetScreen( newScreen ); - else - aSheet->GetScreen()->Append( newScreen ); - - SCH_SCREENS allScreens; - allScreens.ReplaceDuplicateTimeStamps(); - - SCH_SCREENS screens( aSheet ); - screens.UpdateSymbolLinks( true ); - - return true; -} bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy, @@ -527,6 +48,8 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy, if( aSheet == NULL || aHierarchy == NULL ) return false; + SCH_SHEET_LIST hierarchy( g_RootSheet ); // This is the schematic sheet hierarchy. + // Get the new texts DIALOG_SCH_SHEET_PROPS dlg( this, aSheet ); @@ -537,11 +60,8 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy, fileName.SetExt( SchematicFileExtension ); wxString msg; - bool renameFile = false; bool loadFromFile = false; bool clearAnnotation = false; - bool restoreSheet = false; - bool isExistingSheet = false; SCH_SCREEN* useScreen = NULL; // Relative file names are relative to the path of the current sheet. This allows for @@ -575,9 +95,9 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy, SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) ); - if( aSheet->GetScreen() == NULL ) // New sheet. + if( aSheet->GetScreen() == NULL ) // New sheet. { - if( useScreen || loadFromFile ) // Load from existing file. + if( useScreen || loadFromFile ) // Load from existing file. { clearAnnotation = true; @@ -591,7 +111,7 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy, return false; } - else // New file. + else // New file. { aSheet->SetScreen( new SCH_SCREEN( &Kiway() ) ); aSheet->GetScreen()->SetModify(); @@ -599,15 +119,14 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy, aSheet->GetScreen()->SetFileName( newFilename ); } } - else // Existing sheet. + else // Existing sheet. { bool isUndoable = true; + bool renameFile = false; wxString replaceMsg; wxString newMsg; wxString noUndoMsg; - isExistingSheet = true; - // Changing the filename of a sheet can modify the full hierarchy structure // and can be not always undoable. // So prepare messages for user notifications: @@ -635,7 +154,7 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy, // Sheet file name changes cannot be undone. isUndoable = false; - if( useScreen || loadFromFile ) // Load from existing file. + if( useScreen || loadFromFile ) // Load from existing file. { clearAnnotation = true; @@ -647,7 +166,7 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy, if( loadFromFile ) aSheet->SetScreen( NULL ); } - else // Save to new file name. + else // Save to new file name. { if( aSheet->GetScreenCount() > 1 ) { @@ -703,50 +222,43 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy, wxFileName userFileName = dlg.GetFileName(); userFileName.SetExt( SchematicFileExtension ); + aSheet->SetFileName( userFileName.GetFullPath( wxPATH_UNIX ) ); if( useScreen ) { - // Create a temporary sheet for recursion testing to prevent a possible recursion error. - std::unique_ptr< SCH_SHEET> tmpSheet( new SCH_SHEET ); - tmpSheet->SetName( dlg.GetSheetName() ); - tmpSheet->SetFileName( userFileName.GetFullPath( wxPATH_UNIX ) ); - tmpSheet->SetScreen( useScreen ); - - // No need to check for valid library IDs if we are using an existing screen. - if( checkSheetForRecursion( tmpSheet.get(), aHierarchy ) ) - { - if( restoreSheet ) - aHierarchy->LastScreen()->Append( aSheet ); - - return false; - } - - // It's safe to set the sheet screen now. aSheet->SetScreen( useScreen ); } else if( loadFromFile ) { - if( isExistingSheet ) + try { - // Temporarily remove the sheet from the current schematic page so that recursion - // and symbol library link tests can be performed with the modified sheet settings. - restoreSheet = true; - aHierarchy->LastScreen()->Remove( aSheet ); - } + aSheet = pi->Load( newFilename, &Kiway(), aSheet ); - if( !LoadSheetFromFile( aSheet, aHierarchy, newFilename ) ) + if( !pi->GetError().IsEmpty() ) + { + DisplayErrorMessage( this, + _( "The entire schematic could not be loaded.\n" + "Errors occurred loading hierarchical sheets." ), + pi->GetError() ); + } + } + catch( const IO_ERROR& ioe ) { - if( restoreSheet ) - aHierarchy->LastScreen()->Append( aSheet ); + msg.Printf( _( "Error occurred loading schematic file \"%s\"." ), newFilename ); + DisplayErrorMessage( this, msg, ioe.What() ); + + msg.Printf( _( "Failed to load schematic \"%s\"" ), newFilename ); + AppendMsgPanel( wxEmptyString, msg, CYAN ); return false; } - if( restoreSheet ) - aHierarchy->LastScreen()->Append( aSheet ); + SCH_SCREEN* screen = aSheet->GetScreen(); + + if( screen ) + screen->UpdateSymbolLinks( true ); } - aSheet->SetFileName( userFileName.GetFullPath( wxPATH_UNIX ) ); aSheet->SetFileNameSize( dlg.GetFileNameTextSize() ); aSheet->SetName( dlg.GetSheetName() ); aSheet->SetSheetNameSize( dlg.GetSheetNameTextSize() ); @@ -755,6 +267,38 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy, aSheet->SetName( wxString::Format( wxT( "Sheet%8.8lX" ), (long unsigned) aSheet->GetTimeStamp() ) ); + // Make sure the sheet changes do not cause any recursion. + SCH_SHEET_LIST sheetHierarchy( aSheet ); + + // Make sure files have fully qualified path and file name. + wxFileName destFn = aHierarchy->Last()->GetFileName(); + + if( destFn.IsRelative() ) + destFn.MakeAbsolute( Prj().GetProjectPath() ); + + if( hierarchy.TestForRecursion( sheetHierarchy, destFn.GetFullPath( wxPATH_UNIX ) ) ) + { + msg.Printf( _( "The sheet changes cannot be made because the destination sheet already " + "has the sheet \"%s\" or one of it's subsheets as a parent somewhere in " + "the schematic hierarchy." ), + newFilename ); + DisplayError( this, msg ); + return false; + } + + // Check to make sure the symbols have been remapped to the symbol library table. + SCH_SCREENS newScreens( aSheet ); + + if( newScreens.HasNoFullyDefinedLibIds() ) + { + msg.Printf(_( "The schematic \"%s\" has not been remapped to the symbol\nlibrary table. " + " The project this schematic belongs to must first be remapped\nbefore it " + "can be imported into the current project." ), fileName.GetFullName() ); + + DisplayInfoMessage( this, msg ); + return false; + } + if( aClearAnnotationNewItems ) *aClearAnnotationNewItems = clearAnnotation;