diff --git a/eeschema/files-io.cpp b/eeschema/files-io.cpp index 90c9ca9dad..2289d7ce01 100644 --- a/eeschema/files-io.cpp +++ b/eeschema/files-io.cpp @@ -386,9 +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 ) { @@ -415,25 +420,6 @@ bool SCH_EDIT_FRAME::AppendSchematic() fullFileName = fn.GetFullPath(); } - wxString cache_name = PART_LIBS::CacheName( fullFileName ); - - if( !!cache_name ) - { - PART_LIBS* libs = Prj().SchLibs(); - - try - { - if( PART_LIB* lib = libs->AddLibrary( cache_name ) ) - lib->SetCache(); - } - catch( const IO_ERROR& ioe ) - { - DisplayError( this, ioe.What() ); - } - } - - wxLogDebug( wxT( "Importing schematic " ) + fullFileName ); - // 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 ); @@ -464,6 +450,49 @@ bool SCH_EDIT_FRAME::AppendSchematic() 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. @@ -496,89 +525,231 @@ bool SCH_EDIT_FRAME::AppendSchematic() "remapped before it can be imported into the current project." ); return false; } - else - { - // 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. - newScreens.GetLibNicknames( names ); - wxArrayString newLibNames; + 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( !newLibNames.IsEmpty() && symLibTableFn.Exists() && symLibTableFn.IsFileReadable() ) + // 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() ) { - SYMBOL_LIB_TABLE table; + 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 ); - try - { - table.Load( symLibTableFn.GetFullPath() ); + if( msgDlg2.ShowModal() == wxID_CANCEL ) + return false; } - catch( const IO_ERROR& ioe ) + else { - msg.Printf( _( "An error occurred loading the symbol library table \"%s\"." ), - symLibTableFn.GetFullPath() ); - DisplayErrorMessage( NULL, msg, ioe.What() ); - } - - if( !table.IsEmpty() ) - { - for( const auto& libName : newLibNames ) + try { - if( !table.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}" ) ) - { - newLib.SetPath( fn.GetPath() ); - newLib.SetFullName( uri.AfterLast( '}' ) ); - uri = newLib.GetFullPath(); - } - else if( uri.Contains( "$(KIPRJMOD)" ) ) - { - 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 ); - - wxCHECK2_MSG( row, continue, "Library '" + libName + - "' missing from symbol library table '" + - symLibTableFn.GetFullPath() + "'." ); - - wxString newLibName = libName; - int libNameCnt = 1; - - // Rename the imported symbol library if it already exists. - while( Prj().SchSymbolLibTable()->HasLibrary( newLibName ) ) - newLibName = wxString::Format( "%s%d", libName, libNameCnt ); - - auto newRow = new SYMBOL_LIB_TABLE_ROW( newLibName, uri, row->GetType(), - row->GetOptions(), row->GetDescr() ); - Prj().SchSymbolLibTable()->InsertRow( newRow ); - - if( libName != newLibName ) - newScreens.ChangeSymbolLibNickname( libName, newLibName ); + 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; } } } @@ -591,7 +762,7 @@ bool SCH_EDIT_FRAME::AppendSchematic() sheets.Collect( screen->GetDrawItems(), SCH_COLLECTOR::SheetsOnly ); - for( int i = 0; i < sheets.GetCount(); ++i ) + for( i = 0; i < sheets.GetCount(); ++i ) { if( newSheet->GetScreen()->GetSheet( ( ( SCH_SHEET* ) sheets[i] )->GetName() ) ) duplicateSheetNames.Add( ( ( SCH_SHEET* ) sheets[i] )->GetName() ); @@ -601,6 +772,7 @@ bool SCH_EDIT_FRAME::AppendSchematic() { 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; } @@ -620,6 +792,21 @@ bool SCH_EDIT_FRAME::AppendSchematic() 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 ); diff --git a/eeschema/sch_screen.cpp b/eeschema/sch_screen.cpp index 5a913a6b1f..be83a20463 100644 --- a/eeschema/sch_screen.cpp +++ b/eeschema/sch_screen.cpp @@ -1607,6 +1607,18 @@ int SCH_SCREENS::ChangeSymbolLibNickname( const wxString& aFrom, const wxString& } +bool SCH_SCREENS::HasSchematic( const wxString& aSchematicFileName ) +{ + for( const SCH_SCREEN* screen = GetFirst(); screen; screen = GetNext() ) + { + if( screen->GetFileName() == aSchematicFileName ) + return true; + } + + return false; +} + + void SCH_SCREENS::BuildClientSheetPathList() { SCH_SHEET_LIST sheetList( g_RootSheet ); diff --git a/eeschema/sch_screen.h b/eeschema/sch_screen.h index b8c64ea18e..e065bd84ac 100644 --- a/eeschema/sch_screen.h +++ b/eeschema/sch_screen.h @@ -622,6 +622,17 @@ public: */ int ChangeSymbolLibNickname( const wxString& aFrom, const wxString& aTo ); + /** + * Check if one of the schematics in the list of screens is \a aSchematicFileName. + * + * Schematic file names in SCH_SCREEN object are stored with the absolute path to + * the schematic file. + * + * @param aSchematicFileName is the schematic file name to search. + * @return true if the a schematic matching the file name has been found. + */ + bool HasSchematic( const wxString& aSchematicFileName ); + /** * built the list of sheet paths sharing a screen for each screen in use */