Revert "Revert "Eeschema: fix edit sheet bug.""
This reverts commit 2d48c81d58
.
This commit is contained in:
parent
23d71de75f
commit
278127fe29
|
@ -386,14 +386,8 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
|
||||||
|
|
||||||
bool SCH_EDIT_FRAME::AppendSchematic()
|
bool SCH_EDIT_FRAME::AppendSchematic()
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
wxString msg;
|
|
||||||
wxString fullFileName;
|
wxString fullFileName;
|
||||||
wxString topLevelSheetPath;
|
|
||||||
wxFileName tmp;
|
|
||||||
SCH_SCREEN* screen = GetScreen();
|
SCH_SCREEN* screen = GetScreen();
|
||||||
bool libTableChanged = false;
|
|
||||||
|
|
||||||
|
|
||||||
if( !screen )
|
if( !screen )
|
||||||
{
|
{
|
||||||
|
@ -412,414 +406,10 @@ bool SCH_EDIT_FRAME::AppendSchematic()
|
||||||
|
|
||||||
fullFileName = dlg.GetPath();
|
fullFileName = dlg.GetPath();
|
||||||
|
|
||||||
wxFileName fn = fullFileName;
|
if( !LoadSheetFromFile( GetCurrentSheet().Last(), &GetCurrentSheet(), 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;
|
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() );
|
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();
|
screens.TestDanglingEnds();
|
||||||
|
|
||||||
GetScreen()->SetGrid( ID_POPUP_GRID_LEVEL_1000 + m_LastGridSizeId );
|
GetScreen()->SetGrid( ID_POPUP_GRID_LEVEL_1000 + m_LastGridSizeId );
|
||||||
|
|
|
@ -703,33 +703,9 @@ public:
|
||||||
bool OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl = 0 ) override;
|
bool OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl = 0 ) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import a KiCad schematic into the current page.
|
* Import a KiCad schematic into the current sheet.
|
||||||
*
|
*
|
||||||
* In order to import a schematic a lot of things have to happen to before the contents
|
* @return True if the schematic was imported properly.
|
||||||
* 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();
|
bool AppendSchematic();
|
||||||
|
|
||||||
|
@ -980,6 +956,35 @@ private:
|
||||||
void EditSchematicText( SCH_TEXT* TextStruct );
|
void EditSchematicText( SCH_TEXT* TextStruct );
|
||||||
void ChangeTextOrient( SCH_TEXT* aTextItem );
|
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.
|
* Command event handler to change a text type to another one.
|
||||||
*
|
*
|
||||||
|
@ -1102,15 +1107,54 @@ public:
|
||||||
wxPoint GetLastSheetPinPosition() const { return m_lastSheetPinPosition; }
|
wxPoint GetLastSheetPinPosition() const { return m_lastSheetPinPosition; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
void InitSheet( SCH_SHEET* aSheet, const wxString& aNewFilename );
|
||||||
* 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 );
|
|
||||||
|
|
||||||
bool validateSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy );
|
/**
|
||||||
|
* Load a the KiCad schematic file \a aFileName into the sheet \a aSheet.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
bool LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy,
|
||||||
|
const wxString& aFileName );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new SCH_SHEET_PIN object and add it to \a aSheet at the current cursor position.
|
* Create a new SCH_SHEET_PIN object and add it to \a aSheet at the current cursor position.
|
||||||
|
@ -1210,7 +1254,6 @@ public:
|
||||||
*/
|
*/
|
||||||
void OrientComponent( COMPONENT_ORIENTATION_T aOrientation = CMP_NORMAL );
|
void OrientComponent( COMPONENT_ORIENTATION_T aOrientation = CMP_NORMAL );
|
||||||
|
|
||||||
private:
|
|
||||||
void OnSelectUnit( wxCommandEvent& aEvent );
|
void OnSelectUnit( wxCommandEvent& aEvent );
|
||||||
void ConvertPart( SCH_COMPONENT* DrawComponent );
|
void ConvertPart( SCH_COMPONENT* DrawComponent );
|
||||||
|
|
||||||
|
@ -1229,7 +1272,6 @@ private:
|
||||||
void PasteListOfItems( wxDC* DC );
|
void PasteListOfItems( wxDC* DC );
|
||||||
|
|
||||||
/* Undo - redo */
|
/* Undo - redo */
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a copy of the current schematic item, and put it in the undo list.
|
* Create a copy of the current schematic item, and put it in the undo list.
|
||||||
|
|
|
@ -38,8 +38,487 @@
|
||||||
#include <sch_sheet.h>
|
#include <sch_sheet.h>
|
||||||
#include <sch_sheet_path.h>
|
#include <sch_sheet_path.h>
|
||||||
#include <sch_view.h>
|
#include <sch_view.h>
|
||||||
|
#include <symbol_lib_table.h>
|
||||||
#include <dialogs/dialog_sch_sheet_props.h>
|
#include <dialogs/dialog_sch_sheet_props.h>
|
||||||
|
#include <dialogs/dialog_sch_edit_sheet_pin.h>
|
||||||
|
#include <tool/actions.h>
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy,
|
||||||
|
@ -48,8 +527,6 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy,
|
||||||
if( aSheet == NULL || aHierarchy == NULL )
|
if( aSheet == NULL || aHierarchy == NULL )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
SCH_SHEET_LIST hierarchy( g_RootSheet ); // This is the schematic sheet hierarchy.
|
|
||||||
|
|
||||||
// Get the new texts
|
// Get the new texts
|
||||||
DIALOG_SCH_SHEET_PROPS dlg( this, aSheet );
|
DIALOG_SCH_SHEET_PROPS dlg( this, aSheet );
|
||||||
|
|
||||||
|
@ -60,8 +537,11 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy,
|
||||||
fileName.SetExt( SchematicFileExtension );
|
fileName.SetExt( SchematicFileExtension );
|
||||||
|
|
||||||
wxString msg;
|
wxString msg;
|
||||||
|
bool renameFile = false;
|
||||||
bool loadFromFile = false;
|
bool loadFromFile = false;
|
||||||
bool clearAnnotation = false;
|
bool clearAnnotation = false;
|
||||||
|
bool restoreSheet = false;
|
||||||
|
bool isExistingSheet = false;
|
||||||
SCH_SCREEN* useScreen = NULL;
|
SCH_SCREEN* useScreen = NULL;
|
||||||
|
|
||||||
// Relative file names are relative to the path of the current sheet. This allows for
|
// Relative file names are relative to the path of the current sheet. This allows for
|
||||||
|
@ -95,9 +575,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 ) );
|
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;
|
clearAnnotation = true;
|
||||||
|
|
||||||
|
@ -111,7 +591,7 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy,
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
else // New file.
|
else // New file.
|
||||||
{
|
{
|
||||||
aSheet->SetScreen( new SCH_SCREEN( &Kiway() ) );
|
aSheet->SetScreen( new SCH_SCREEN( &Kiway() ) );
|
||||||
aSheet->GetScreen()->SetModify();
|
aSheet->GetScreen()->SetModify();
|
||||||
|
@ -119,14 +599,15 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy,
|
||||||
aSheet->GetScreen()->SetFileName( newFilename );
|
aSheet->GetScreen()->SetFileName( newFilename );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // Existing sheet.
|
else // Existing sheet.
|
||||||
{
|
{
|
||||||
bool isUndoable = true;
|
bool isUndoable = true;
|
||||||
bool renameFile = false;
|
|
||||||
wxString replaceMsg;
|
wxString replaceMsg;
|
||||||
wxString newMsg;
|
wxString newMsg;
|
||||||
wxString noUndoMsg;
|
wxString noUndoMsg;
|
||||||
|
|
||||||
|
isExistingSheet = true;
|
||||||
|
|
||||||
// Changing the filename of a sheet can modify the full hierarchy structure
|
// Changing the filename of a sheet can modify the full hierarchy structure
|
||||||
// and can be not always undoable.
|
// and can be not always undoable.
|
||||||
// So prepare messages for user notifications:
|
// So prepare messages for user notifications:
|
||||||
|
@ -154,7 +635,7 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy,
|
||||||
// Sheet file name changes cannot be undone.
|
// Sheet file name changes cannot be undone.
|
||||||
isUndoable = false;
|
isUndoable = false;
|
||||||
|
|
||||||
if( useScreen || loadFromFile ) // Load from existing file.
|
if( useScreen || loadFromFile ) // Load from existing file.
|
||||||
{
|
{
|
||||||
clearAnnotation = true;
|
clearAnnotation = true;
|
||||||
|
|
||||||
|
@ -166,7 +647,7 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy,
|
||||||
if( loadFromFile )
|
if( loadFromFile )
|
||||||
aSheet->SetScreen( NULL );
|
aSheet->SetScreen( NULL );
|
||||||
}
|
}
|
||||||
else // Save to new file name.
|
else // Save to new file name.
|
||||||
{
|
{
|
||||||
if( aSheet->GetScreenCount() > 1 )
|
if( aSheet->GetScreenCount() > 1 )
|
||||||
{
|
{
|
||||||
|
@ -222,43 +703,50 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy,
|
||||||
|
|
||||||
wxFileName userFileName = dlg.GetFileName();
|
wxFileName userFileName = dlg.GetFileName();
|
||||||
userFileName.SetExt( SchematicFileExtension );
|
userFileName.SetExt( SchematicFileExtension );
|
||||||
aSheet->SetFileName( userFileName.GetFullPath( wxPATH_UNIX ) );
|
|
||||||
|
|
||||||
if( useScreen )
|
if( useScreen )
|
||||||
{
|
{
|
||||||
aSheet->SetScreen( useScreen );
|
// Create a temporary sheet for recursion testing to prevent a possible recursion error.
|
||||||
}
|
std::unique_ptr< SCH_SHEET> tmpSheet( new SCH_SHEET );
|
||||||
else if( loadFromFile )
|
tmpSheet->SetName( dlg.GetSheetName() );
|
||||||
{
|
tmpSheet->SetFileName( userFileName.GetFullPath( wxPATH_UNIX ) );
|
||||||
try
|
tmpSheet->SetScreen( useScreen );
|
||||||
{
|
|
||||||
aSheet = pi->Load( newFilename, &Kiway(), aSheet );
|
|
||||||
|
|
||||||
if( !pi->GetError().IsEmpty() )
|
// No need to check for valid library IDs if we are using an existing screen.
|
||||||
{
|
if( checkSheetForRecursion( tmpSheet.get(), aHierarchy ) )
|
||||||
DisplayErrorMessage( this,
|
|
||||||
_( "The entire schematic could not be loaded.\n"
|
|
||||||
"Errors occurred loading hierarchical sheets." ),
|
|
||||||
pi->GetError() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch( const IO_ERROR& ioe )
|
|
||||||
{
|
{
|
||||||
msg.Printf( _( "Error occurred loading schematic file \"%s\"." ), newFilename );
|
if( restoreSheet )
|
||||||
DisplayErrorMessage( this, msg, ioe.What() );
|
aHierarchy->LastScreen()->Append( aSheet );
|
||||||
|
|
||||||
msg.Printf( _( "Failed to load schematic \"%s\"" ), newFilename );
|
|
||||||
AppendMsgPanel( wxEmptyString, msg, CYAN );
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SCH_SCREEN* screen = aSheet->GetScreen();
|
// It's safe to set the sheet screen now.
|
||||||
|
aSheet->SetScreen( useScreen );
|
||||||
|
}
|
||||||
|
else if( loadFromFile )
|
||||||
|
{
|
||||||
|
if( isExistingSheet )
|
||||||
|
{
|
||||||
|
// 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 );
|
||||||
|
}
|
||||||
|
|
||||||
if( screen )
|
if( !LoadSheetFromFile( aSheet, aHierarchy, newFilename ) )
|
||||||
screen->UpdateSymbolLinks( true );
|
{
|
||||||
|
if( restoreSheet )
|
||||||
|
aHierarchy->LastScreen()->Append( aSheet );
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( restoreSheet )
|
||||||
|
aHierarchy->LastScreen()->Append( aSheet );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aSheet->SetFileName( userFileName.GetFullPath( wxPATH_UNIX ) );
|
||||||
aSheet->SetFileNameSize( dlg.GetFileNameTextSize() );
|
aSheet->SetFileNameSize( dlg.GetFileNameTextSize() );
|
||||||
aSheet->SetName( dlg.GetSheetName() );
|
aSheet->SetName( dlg.GetSheetName() );
|
||||||
aSheet->SetSheetNameSize( dlg.GetSheetNameTextSize() );
|
aSheet->SetSheetNameSize( dlg.GetSheetNameTextSize() );
|
||||||
|
@ -267,38 +755,6 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy,
|
||||||
aSheet->SetName( wxString::Format( wxT( "Sheet%8.8lX" ),
|
aSheet->SetName( wxString::Format( wxT( "Sheet%8.8lX" ),
|
||||||
(long unsigned) aSheet->GetTimeStamp() ) );
|
(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 )
|
if( aClearAnnotationNewItems )
|
||||||
*aClearAnnotationNewItems = clearAnnotation;
|
*aClearAnnotationNewItems = clearAnnotation;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue