Fix crash on save after pasting symbols in schematic editor.

This was caused by pasting zero length instance paths which are not valid.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/16300
This commit is contained in:
Wayne Stambaugh 2023-12-11 08:29:10 -05:00
parent 7554e629a8
commit 484491aaea
3 changed files with 70 additions and 20 deletions

View File

@ -1387,21 +1387,19 @@ void SCH_EDITOR_CONTROL::updatePastedSymbol( SCH_SYMBOL* aSymbol, SCH_SCREEN* aP
const KIID_PATH& aClipPath,
bool aForceKeepAnnotations )
{
wxCHECK( aSymbol && aPasteScreen, /* void */ );
wxCHECK( m_frame && aSymbol && aPasteScreen, /* void */ );
KIID_PATH clipItemPath = aClipPath;
SCH_SYMBOL_INSTANCE instance;
KIID_PATH pasteLookupPath = aClipPath;
wxString reference, value, footprint;
int unit;
m_pastedSymbols.insert( aSymbol );
clipItemPath.push_back( aSymbol->m_Uuid );
// The pasted symbol look up paths include the symbol UUID.
pasteLookupPath.push_back( aSymbol->m_Uuid );
if( m_clipboardSymbolInstances.count( clipItemPath ) > 0 )
if( m_clipboardSymbolInstances.count( pasteLookupPath ) > 0 )
{
SCH_SYMBOL_INSTANCE instance = m_clipboardSymbolInstances.at( clipItemPath );
unit = instance.m_Unit;
reference = instance.m_Reference;
instance = m_clipboardSymbolInstances.at( pasteLookupPath );
}
else
{
@ -1412,20 +1410,20 @@ void SCH_EDITOR_CONTROL::updatePastedSymbol( SCH_SYMBOL* aSymbol, SCH_SCREEN* aP
// Pasted from notepad or an older instance of eeschema. Use the values in the fields
// instead.
reference = aSymbol->GetField( REFERENCE_FIELD )->GetText();
value = aSymbol->GetField( VALUE_FIELD )->GetText();
footprint = aSymbol->GetField( FOOTPRINT_FIELD )->GetText();
unit = aSymbol->GetUnit();
instance.m_Reference = aSymbol->GetField( REFERENCE_FIELD )->GetText();
instance.m_Unit = aSymbol->GetUnit();
}
if( aForceKeepAnnotations && !reference.IsEmpty() )
aSymbol->SetRef( &aPastePath, reference );
else
instance.m_Path = aPastePath.Path() + aClipPath;
instance.m_ProjectName = m_frame->Prj().GetProjectName();
aSymbol->AddHierarchicalReference( instance );
if( !aForceKeepAnnotations )
aSymbol->ClearAnnotation( &aPastePath, false );
// We might clear annotations but always leave the original unit number from the paste.
aSymbol->SetUnitSelection( &aPastePath, unit );
aSymbol->SetUnit( unit );
aSymbol->SetUnit( instance.m_Unit );
}
@ -1531,6 +1529,29 @@ void SCH_EDITOR_CONTROL::setPastedSymbolInstances( SCH_SCREENS& aScreenList )
}
void SCH_EDITOR_CONTROL::prunePastedSymbolInstances()
{
wxCHECK( m_frame, /* void */ );
for( SCH_SYMBOL* symbol : m_pastedSymbols )
{
wxCHECK2( symbol, continue );
std::vector<KIID_PATH> instancePathsToRemove;
for( const SCH_SYMBOL_INSTANCE& instance : symbol->GetInstanceReferences() )
{
if( ( instance.m_ProjectName != m_frame->Prj().GetProjectName() )
|| instance.m_Path.empty() )
instancePathsToRemove.emplace_back( instance.m_Path );
}
for( const KIID_PATH& path : instancePathsToRemove )
symbol->RemoveInstance( path );
}
}
int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
{
wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( wxWindow::FindFocus() );
@ -1581,6 +1602,7 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
SCH_SCREENS tempScreens( tempSheet );
m_pastedSymbols.clear();
m_clipboardSheetInstances.clear();
m_clipboardSymbolInstances.clear();
@ -1934,6 +1956,8 @@ 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.

View File

@ -194,6 +194,16 @@ private:
void setPastedSheetInstances( const SCH_SHEET* aPastedSheet );
void setPastedSymbolInstances( SCH_SCREENS& aScreenList );
/**
* Remove all pasted symbol instances that do not belong to the current project.
*
* @warning This should **only** be called when cleaning up after a paste. Otherwise it
* could clobber symbol instances for schematics shared across projects. Use
* SCH_SCREENS::PruneOrphanedSymbolInstances() to clean up invalid instance for
* the current project.
*/
void prunePastedSymbolInstances();
/**
* Read the footprint info from each line in the stuff file by reference designator.
*
@ -238,6 +248,8 @@ private:
// A map of KIID_PATH --> sheet instances for the clipboard contents.
std::map<KIID_PATH, SCH_SHEET_INSTANCE> m_clipboardSheetInstances;
std::set<SCH_SYMBOL*> m_pastedSymbols;
};

View File

@ -3,7 +3,7 @@
*
* Copyright (C) 2020 Ian McInerney <ian.s.mcinerney@ieee.org>
* Copyright (C) 2007-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.TXT for contributors.
* Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -191,6 +191,20 @@ public:
return false;
}
KIID_PATH& operator+=( const KIID_PATH& aRhs )
{
for( const KIID& kiid : aRhs )
emplace_back( kiid );
return *this;
}
friend KIID_PATH operator+( KIID_PATH aLhs, const KIID_PATH& aRhs )
{
aLhs += aRhs;
return aLhs;
}
};
/**