Prune orphaned schematic sheet instances.

Pasting from the clipboard when copying from another project can leave
sheet instance information that is not valid for the current project.
This change prunes all sheet instance information that is not relevant
to the current project.  It also prunes invalid paths created by pasting
sheets from different paths in the same project.
This commit is contained in:
Wayne Stambaugh 2023-12-28 12:18:54 -05:00
parent a452213546
commit 482aff3f0b
4 changed files with 99 additions and 8 deletions

View File

@ -502,6 +502,7 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
}
schematic.PruneOrphanedSymbolInstances( Prj().GetProjectName(), Schematic().GetSheets() );
schematic.PruneOrphanedSheetInstances( Prj().GetProjectName(), Schematic().GetSheets() );
Schematic().ConnectionGraph()->Reset();

View File

@ -1666,7 +1666,7 @@ void SCH_SCREEN::PruneOrphanedSymbolInstances( const wxString& aProjectName,
std::optional<SCH_SHEET_PATH> pathFound =
aValidSheetPaths.GetSheetPathByKIIDPath( instance.m_Path );
// Check for paths that do not exist in the current project and paths that do
// Check for paths that do not exist in the current project and paths that do
// not contain the current symbol.
if( !pathFound )
pathsToPrune.emplace( instance.m_Path );
@ -1684,6 +1684,51 @@ void SCH_SCREEN::PruneOrphanedSymbolInstances( const wxString& aProjectName,
}
void SCH_SCREEN::PruneOrphanedSheetInstances( const wxString& aProjectName,
const SCH_SHEET_LIST& aValidSheetPaths )
{
// The project name cannot be empty. Projects older than 7.0 did not save project names
// when saving instance data. Running this algorithm with an empty project name would
// clobber all instance data for projects other than the current one when a schematic
// file is shared across multiple projects.
wxCHECK( !aProjectName.IsEmpty(), /* void */ );
for( SCH_ITEM* item : Items().OfType( SCH_SHEET_T ) )
{
SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
wxCHECK2( sheet, continue );
std::set<KIID_PATH> pathsToPrune;
const std::vector<SCH_SHEET_INSTANCE> instances = sheet->GetInstances();
for( const SCH_SHEET_INSTANCE& instance : instances )
{
// Ignore instance paths from other projects.
if( aProjectName != instance.m_ProjectName )
continue;
std::optional<SCH_SHEET_PATH> pathFound =
aValidSheetPaths.GetSheetPathByKIIDPath( instance.m_Path );
// Check for paths that do not exist in the current project and paths that do
// not contain the current symbol.
if( !pathFound )
pathsToPrune.emplace( instance.m_Path );
else if( pathFound.value().LastScreen() != this )
pathsToPrune.emplace( pathFound.value().Path() );
}
for( const KIID_PATH& sheetPath : pathsToPrune )
{
wxLogTrace( traceSchSheetPaths, wxS( "Pruning project '%s' sheet instance %s." ),
aProjectName, sheetPath.AsString() );
sheet->RemoveInstance( sheetPath );
}
}
}
#if defined(DEBUG)
void SCH_SCREEN::Show( int nestLevel, std::ostream& os ) const
{
@ -2110,7 +2155,6 @@ void SCH_SCREEN::MigrateSimModels()
void SCH_SCREENS::PruneOrphanedSymbolInstances( const wxString& aProjectName,
const SCH_SHEET_LIST& aValidSheetPaths )
{
wxCHECK( !aProjectName.IsEmpty(), /* void */ );
@ -2118,3 +2162,13 @@ void SCH_SCREENS::PruneOrphanedSymbolInstances( const wxString& aProjectName,
for( SCH_SCREEN* screen = GetFirst(); screen; screen = GetNext() )
screen->PruneOrphanedSymbolInstances( aProjectName, aValidSheetPaths );
}
void SCH_SCREENS::PruneOrphanedSheetInstances( const wxString& aProjectName,
const SCH_SHEET_LIST& aValidSheetPaths )
{
wxCHECK( !aProjectName.IsEmpty(), /* void */ );
for( SCH_SCREEN* screen = GetFirst(); screen; screen = GetNext() )
screen->PruneOrphanedSheetInstances( aProjectName, aValidSheetPaths );
}

View File

@ -566,6 +566,21 @@ public:
void PruneOrphanedSymbolInstances( const wxString& aProjectName,
const SCH_SHEET_LIST& aValidSheetPaths );
/**
* Remove all invalid sheet instance data in this screen object for the project defined
* by \a aProjectName and the list of \a aValidSheetPaths.
*
* @warning This method will assert and exit on debug builds when \a aProjectName is empty.
*
* @note This method does not affect instance data for any other projects.
*
* @param aProjectName is the name of the current project.
* @param aValidSheetPaths is the list of valid #SCH_SHEET_PATH objects for the current
* project.
*/
void PruneOrphanedSheetInstances( const wxString& aProjectName,
const SCH_SHEET_LIST& aValidSheetPaths );
private:
friend SCH_EDIT_FRAME; // Only to populate m_symbolInstances.
friend SCH_IO_KICAD_SEXPR_PARSER; // Only to load instance information from schematic file.
@ -593,12 +608,13 @@ private:
size_t getLibSymbolNameMatches( const SCH_SYMBOL& aSymbol, std::vector<wxString>& aMatches );
/**
* Compare two #BUS_ALIAS objects by name. For sorting in the set.
*/
/**
* Compare two #BUS_ALIAS objects by name. For sorting in the set.
*/
struct BusAliasCmp
{
bool operator()( const std::shared_ptr<BUS_ALIAS>& a, const std::shared_ptr<BUS_ALIAS>& b ) const
bool operator()( const std::shared_ptr<BUS_ALIAS>& a,
const std::shared_ptr<BUS_ALIAS>& b ) const
{
return a->GetName() < b->GetName();
}
@ -823,6 +839,9 @@ public:
void PruneOrphanedSymbolInstances( const wxString& aProjectName,
const SCH_SHEET_LIST& aValidSheetPaths );
void PruneOrphanedSheetInstances( const wxString& aProjectName,
const SCH_SHEET_LIST& aValidSheetPaths );
private:
void addScreenToList( SCH_SCREEN* aScreen, SCH_SHEET* aSheet );
void buildScreenList( SCH_SHEET* aSheet);

View File

@ -1920,6 +1920,21 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
pastedSheet.SetPageNumber( pageNum );
hierarchy.push_back( pastedSheet );
// Remove all pasted sheet instance data that is not part of the current project.
std::vector<KIID_PATH> instancesToRemove;
SCH_SHEET* sheet = pastedSheet.Last();
wxCHECK2( sheet, continue );
for( const SCH_SHEET_INSTANCE& instance : sheet->GetInstances() )
{
if( instance.m_ProjectName != m_frame->Prj().GetProjectName() )
instancesToRemove.push_back( instance.m_Path );
}
for( const KIID_PATH& instancePath : instancesToRemove )
sheet->RemoveInstance( instancePath );
}
}
@ -1975,15 +1990,17 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
m_frame->GetCurrentSheet().UpdateAllScreenReferences();
prunePastedSymbolInstances();
// The copy operation creates instance paths that are not valid for the current project or
// saved as part of another project. Prune them now so they do not accumulate in the saved
// schematic file.
prunePastedSymbolInstances();
SCH_SCREENS allScreens( m_frame->Schematic().Root() );
allScreens.PruneOrphanedSymbolInstances( m_frame->Prj().GetProjectName(),
m_frame->Schematic().GetSheets() );
allScreens.PruneOrphanedSheetInstances( m_frame->Prj().GetProjectName(),
m_frame->Schematic().GetSheets() );
// Now clear the previous selection, select the pasted items, and fire up the "move" tool.
m_toolMgr->RunAction( EE_ACTIONS::clearSelection );