Eeschema: fix saving hierarchical sheets in stand alone mode.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/8209
This commit is contained in:
Wayne Stambaugh 2021-05-27 16:01:36 -04:00
parent 9fcd847238
commit fdcc49d3ee
3 changed files with 182 additions and 9 deletions

View File

@ -68,7 +68,7 @@ bool SCH_EDIT_FRAME::SaveEEFile( SCH_SHEET* aSheet, bool aSaveUnderNewName )
wxFileName oldFileName;
bool success;
if( aSheet == NULL )
if( aSheet == nullptr )
aSheet = GetCurrentSheet().Last();
SCH_SCREEN* screen = aSheet->GetScreen();
@ -208,7 +208,7 @@ void SCH_EDIT_FRAME::Save_File( bool doSaveAs )
{
if( doSaveAs )
{
if( SaveEEFile( NULL, true ) )
if( SaveEEFile( nullptr, true ) )
{
SCH_SCREEN* screen = GetScreen();
@ -231,7 +231,7 @@ void SCH_EDIT_FRAME::Save_File( bool doSaveAs )
}
else
{
SaveEEFile( NULL );
SaveEEFile( nullptr );
}
UpdateTitle();
@ -348,18 +348,18 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
// event handler in there.
// And when a schematic file is loaded, we need these libs to initialize
// some parameters (links to PART LIB, dangling ends ...)
Prj().SetElem( PROJECT::ELEM_SCH_PART_LIBS, NULL );
Prj().SetElem( PROJECT::ELEM_SCH_PART_LIBS, nullptr );
Prj().SchLibs();
}
}
else
{
// No legacy symbol libraries including the cache are loaded with the new file format.
Prj().SetElem( PROJECT::ELEM_SCH_PART_LIBS, NULL );
Prj().SetElem( PROJECT::ELEM_SCH_PART_LIBS, nullptr );
}
// Load the symbol library table, this will be used forever more.
Prj().SetElem( PROJECT::ELEM_SYMBOL_LIB_TABLE, NULL );
Prj().SetElem( PROJECT::ELEM_SYMBOL_LIB_TABLE, nullptr );
Prj().SchSymbolLibTable();
// Load project settings after schematic has been set up with the project link, since this will
@ -776,6 +776,117 @@ bool SCH_EDIT_FRAME::SaveProject()
wxString fileName = Prj().AbsolutePath( Schematic().Root().GetFileName() );
wxFileName fn = fileName;
// If this a new schematic without a project and we are in the stand alone mode. All new
// sheets that are not loaded from an existing file will have to be saved to a new path
// along with the root sheet.
if( Prj().GetProjectFullName().IsEmpty() )
{
// This should only be possible in stand alone mode.
wxCHECK( Kiface().IsSingle(), false );
wxFileDialog dlg( this, _( "Schematic Files" ), fn.GetPath(), fn.GetFullName(),
KiCadSchematicFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
if( dlg.ShowModal() == wxID_CANCEL )
return false;
wxFileName newFileName = dlg.GetPath();
if( newFileName.GetExt().IsEmpty() )
newFileName.SetExt( KiCadSchematicFileExtension );
if( !newFileName.DirExists() && !newFileName.Mkdir() )
{
msg.Printf( _( "Cannot create folder \"%s\"." ), newFileName.GetPath() );
wxMessageDialog dlgBadPath( this, msg, _( "Error" ),
wxOK | wxICON_EXCLAMATION | wxCENTER );
dlgBadPath.ShowModal();
return false;
}
if( !newFileName.IsDirWritable() )
{
msg.Printf( _( "You do not have write permissions to folder \"%s\"." ),
newFileName.GetPath() );
wxMessageDialog dlgBadPerms( this, msg, _( "Error" ),
wxOK | wxICON_EXCLAMATION | wxCENTER );
dlgBadPerms.ShowModal();
return false;
}
Schematic().Root().SetFileName( newFileName.GetFullName() );
Schematic().RootScreen()->SetFileName( newFileName.GetFullPath() );
// Set the base path to all new sheets.
for( size_t i = 0; i < screens.GetCount(); i++ )
{
screen = screens.GetScreen( i );
wxCHECK2( screen, continue );
// The root screen file name has already been set.
if( screen == Schematic().RootScreen() )
continue;
wxFileName tmp = screen->GetFileName();
// Assume existing sheet files are being reused and do not save them to the new
// path. Maybe in the future, add a user option to copy schematic files to the
// new project path.
if( tmp.FileExists() )
continue;
if( tmp.GetPath().IsEmpty() )
{
tmp.SetPath( newFileName.GetPath() );
}
else if( tmp.GetPath() == fn.GetPath() )
{
tmp.SetPath( newFileName.GetPath() );
}
else if( tmp.GetPath().StartsWith( fn.GetPath() ) )
{
// NOTE: this hasn't been tested because the sheet properties dialog no longer
// allows adding a path specifier in the file name field.
wxString newPath = newFileName.GetPath();
newPath += tmp.GetPath().Right( fn.GetPath().Length() );
tmp.SetPath( newPath );
}
wxLogTrace( tracePathsAndFiles,
wxT( "Changing schematic file name path from '%s' to '%s'." ),
screen->GetFileName(), tmp.GetFullPath() );
if( !tmp.DirExists() && !tmp.Mkdir() )
{
msg.Printf( _( "Cannot create folder \"%s\"." ), newFileName.GetPath() );
wxMessageDialog dlgBadFilePath( this, msg, _( "Error" ),
wxOK | wxICON_EXCLAMATION | wxCENTER );
dlgBadFilePath.ShowModal();
return false;
}
screen->SetFileName( tmp.GetFullPath() );
}
// Attempt to make sheet file name paths relative to the new root schematic path.
SCH_SHEET_LIST sheets = Schematic().GetSheets();
for( SCH_SHEET_PATH& sheet : sheets )
{
if( sheet.Last()->IsRootSheet() )
continue;
sheet.MakeFilePathRelativeToParentSheet();
}
}
// Warn user on potential file overwrite. This can happen on shared sheets.
wxArrayString overwrittenFiles;

View File

@ -473,9 +473,53 @@ void SCH_SHEET_PATH::SetPageNumber( const wxString& aPageNumber )
}
void SCH_SHEET_PATH::MakeFilePathRelativeToParentSheet()
{
wxCHECK( m_sheets.size() > 1, /* void */ );
wxFileName sheetFileName = Last()->GetFileName();
// If the sheet file name is absolute, then the user requested is so don't make it relative.
if( sheetFileName.IsAbsolute() )
return;
SCH_SCREEN* screen = LastScreen();
SCH_SCREEN* parentScreen = m_sheets[ m_sheets.size() - 2 ]->GetScreen();
wxCHECK( screen && parentScreen, /* void */ );
wxFileName fileName = screen->GetFileName();
wxFileName parentFileName = parentScreen->GetFileName();
// SCH_SCREEN file names must be absolute. If they are not, someone set them incorrectly
// on load or on creation.
wxCHECK( fileName.IsAbsolute() && parentFileName.IsAbsolute(), /* void */ );
if( fileName.GetPath() == parentFileName.GetPath() )
{
Last()->SetFileName( fileName.GetFullName() );
}
else if( fileName.MakeRelativeTo( parentFileName.GetPath() ) )
{
Last()->SetFileName( fileName.GetFullPath() );
}
else
{
Last()->SetFileName( screen->GetFileName() );
}
wxLogTrace( tracePathsAndFiles,
wxT( "\n File name: '%s'"
"\n parent file name '%s',"
"\n sheet '%s' file name '%s'." ),
screen->GetFileName(), parentScreen->GetFileName(), PathHumanReadable(),
Last()->GetFileName() );
}
SCH_SHEET_LIST::SCH_SHEET_LIST( SCH_SHEET* aSheet, bool aCheckIntegrity )
{
if( aSheet != NULL )
if( aSheet != nullptr )
{
BuildSheetList( aSheet, aCheckIntegrity );
SortByPageNumbers();
@ -485,7 +529,7 @@ SCH_SHEET_LIST::SCH_SHEET_LIST( SCH_SHEET* aSheet, bool aCheckIntegrity )
void SCH_SHEET_LIST::BuildSheetList( SCH_SHEET* aSheet, bool aCheckIntegrity )
{
wxCHECK_RET( aSheet != NULL, wxT( "Cannot build sheet list from undefined sheet." ) );
wxCHECK_RET( aSheet != nullptr, wxT( "Cannot build sheet list from undefined sheet." ) );
std::vector<SCH_SHEET*> badSheets;

View File

@ -195,7 +195,7 @@ public:
const SCH_SHEET* GetSheet( unsigned aIndex ) const
{
SCH_SHEET* retv = NULL;
SCH_SHEET* retv = nullptr;
if( aIndex < size() )
retv = at( aIndex );
@ -346,6 +346,24 @@ public:
*/
bool TestForRecursion( const wxString& aSrcFileName, const wxString& aDestFileName );
/**
* Make the sheet file name relative to it's parent sheet.
*
* This should only be called when changing the parent sheet path such performing a save
* as or a new schematic without a project in stand alone mode. The sheet file name is
* only made relative if the current file name is relative. Absolute sheet file name paths
* are a user choice so do not change them.
*
* Sheet file name paths are set according to the following criteria:
* - If the sheet file name path is in the same as the parent sheet file name path, set
* the sheet file name to just the file name and extension with no path.
* - If the sheet file name path can be made relative to the parent sheet file name path,
* set the sheet file name using the relative path.
* - If the sheet file name path cannot be converted to a relative path, then fall back to
* the absolute file name path.
*/
void MakeFilePathRelativeToParentSheet();
bool operator==( const SCH_SHEET_PATH& d1 ) const;
bool operator!=( const SCH_SHEET_PATH& d1 ) const { return !( *this == d1 ) ; }