Fix eeschema copy/paste: save and load sheet and symbol instances
Save sheet and symbol instance information to the clipboard on copy Load sheet and symbol instance information from the clipboard on paste and renumber page numbers after loading. Correctly handle pasting in a multiple hierarchy by ensuring symbol and sheet instances are updated for all instances of the destination sheet. Fixes https://gitlab.com/kicad/code/kicad/-/issues/8207
This commit is contained in:
parent
91fd28c4be
commit
5822cd85c4
|
@ -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-2020 KiCad Developers, see CHANGELOG.TXT for contributors.
|
||||
* Copyright (C) 1992-2021 KiCad Developers, see CHANGELOG.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
|
||||
|
@ -253,6 +253,27 @@ KIID_PATH::KIID_PATH( const wxString& aString )
|
|||
}
|
||||
|
||||
|
||||
bool KIID_PATH::MakeRelativeTo( const KIID_PATH& aPath )
|
||||
{
|
||||
KIID_PATH copy = *this;
|
||||
clear();
|
||||
|
||||
if( aPath.size() > copy.size() )
|
||||
return false; // this path is not contained within aPath
|
||||
|
||||
for( size_t i = 0; i < aPath.size(); ++i )
|
||||
{
|
||||
if( copy.at( i ).AsString() != aPath.at( i ).AsString() )
|
||||
return false; // this path is not contained within aPath
|
||||
}
|
||||
|
||||
for( size_t i = aPath.size(); i < copy.size(); ++i )
|
||||
push_back( copy.at( i ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
wxString KIID_PATH::AsString() const
|
||||
{
|
||||
wxString path;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2019 KiCad Developers, see CHANGELOG.TXT for contributors.
|
||||
* Copyright (C) 2019-2021 KiCad Developers, see CHANGELOG.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
|
||||
|
@ -51,6 +51,26 @@ wxString GetRefDesUnannotated( const wxString& aSource )
|
|||
}
|
||||
|
||||
|
||||
int GetRefDesNumber( const wxString& aRefDes )
|
||||
{
|
||||
int retval = -1; // negative to indicate not found
|
||||
size_t firstnum = aRefDes.find_first_of( "0123456789" );
|
||||
|
||||
if( firstnum != wxString::npos )
|
||||
{
|
||||
wxString candidateValue = aRefDes.Mid( firstnum );
|
||||
long result;
|
||||
|
||||
if( !candidateValue.ToLong( &result ) )
|
||||
retval = -1;
|
||||
else
|
||||
retval = static_cast<int>( result );
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
int RefDesStringCompare( const wxString& aFirst, const wxString& aSecond )
|
||||
{
|
||||
// Compare unescaped text
|
||||
|
|
|
@ -2162,16 +2162,10 @@ void SCH_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyableOnly,
|
|||
break;
|
||||
|
||||
case T_sheet_instances:
|
||||
if( aIsCopyableOnly )
|
||||
Unexpected( T_sheet_instances );
|
||||
|
||||
parseSchSheetInstances( aSheet, screen );
|
||||
break;
|
||||
|
||||
case T_symbol_instances:
|
||||
if( aIsCopyableOnly )
|
||||
Unexpected( T_symbol_instances );
|
||||
|
||||
parseSchSymbolInstances( screen );
|
||||
break;
|
||||
|
||||
|
|
|
@ -717,10 +717,11 @@ void SCH_SEXPR_PLUGIN::Format( SCH_SHEET* aSheet )
|
|||
}
|
||||
|
||||
|
||||
void SCH_SEXPR_PLUGIN::Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSheetPath,
|
||||
void SCH_SEXPR_PLUGIN::Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSelectionPath,
|
||||
SCH_SHEET_LIST* aFullSheetHierarchy,
|
||||
OUTPUTFORMATTER* aFormatter )
|
||||
{
|
||||
wxCHECK( aSelection && aFormatter, /* void */ );
|
||||
wxCHECK( aSelection && aSelectionPath && aFullSheetHierarchy && aFormatter, /* void */ );
|
||||
|
||||
LOCALE_IO toggle;
|
||||
|
||||
|
@ -765,6 +766,12 @@ void SCH_SEXPR_PLUGIN::Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSheetP
|
|||
m_out->Print( 0, ")\n\n" );
|
||||
}
|
||||
|
||||
// Store the selected sheets instance information
|
||||
SCH_SHEET_LIST selectedSheets;
|
||||
selectedSheets.push_back( *aSelectionPath ); // Include the "root" of the selection
|
||||
|
||||
SCH_REFERENCE_LIST selectedSymbols;
|
||||
|
||||
for( i = 0; i < aSelection->GetSize(); ++i )
|
||||
{
|
||||
item = (SCH_ITEM*) aSelection->GetItem( i );
|
||||
|
@ -772,7 +779,10 @@ void SCH_SEXPR_PLUGIN::Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSheetP
|
|||
switch( item->Type() )
|
||||
{
|
||||
case SCH_COMPONENT_T:
|
||||
saveSymbol( static_cast<SCH_COMPONENT*>( item ), aSheetPath, 0 );
|
||||
saveSymbol( static_cast<SCH_COMPONENT*>( item ), aSelectionPath, 0 );
|
||||
|
||||
aSelectionPath->AppendSymbol( selectedSymbols, static_cast<SCH_COMPONENT*>( item ),
|
||||
true, true );
|
||||
break;
|
||||
|
||||
case SCH_BITMAP_T:
|
||||
|
@ -781,6 +791,16 @@ void SCH_SEXPR_PLUGIN::Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSheetP
|
|||
|
||||
case SCH_SHEET_T:
|
||||
saveSheet( static_cast< SCH_SHEET* >( item ), 0 );
|
||||
|
||||
{
|
||||
SCH_SHEET_PATH subSheetPath = *aSelectionPath;
|
||||
subSheetPath.push_back( static_cast<SCH_SHEET*>( item ) );
|
||||
|
||||
aFullSheetHierarchy->GetSheetsWithinPath( selectedSheets, subSheetPath );
|
||||
aFullSheetHierarchy->GetSymbolsWithinPath( selectedSymbols, subSheetPath, true,
|
||||
true );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SCH_JUNCTION_T:
|
||||
|
@ -811,6 +831,30 @@ void SCH_SEXPR_PLUGIN::Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSheetP
|
|||
wxASSERT( "Unexpected schematic object type in SCH_SEXPR_PLUGIN::Format()" );
|
||||
}
|
||||
}
|
||||
|
||||
// Make all instance information relative to the selection path
|
||||
KIID_PATH selectionPath = aSelectionPath->PathWithoutRootUuid();
|
||||
|
||||
selectedSheets.SortByPageNumbers();
|
||||
std::vector<SCH_SHEET_INSTANCE> sheetinstances = selectedSheets.GetSheetInstances();
|
||||
|
||||
for( SCH_SHEET_INSTANCE& sheetInstance : sheetinstances )
|
||||
{
|
||||
wxASSERT_MSG( sheetInstance.m_Path.MakeRelativeTo( selectionPath ),
|
||||
"Sheet is not inside the selection path?" );
|
||||
}
|
||||
|
||||
|
||||
selectedSymbols.SortByReferenceOnly();
|
||||
std::vector<SYMBOL_INSTANCE_REFERENCE> symbolInstances = selectedSymbols.GetSymbolInstances();
|
||||
|
||||
for( SYMBOL_INSTANCE_REFERENCE& symbolInstance : symbolInstances )
|
||||
{
|
||||
wxASSERT_MSG( symbolInstance.m_Path.MakeRelativeTo( selectionPath ),
|
||||
"Symbol is not inside the selection path?" );
|
||||
}
|
||||
|
||||
saveInstances( sheetinstances, symbolInstances, 0 );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -99,8 +99,8 @@ public:
|
|||
|
||||
void Format( SCH_SHEET* aSheet );
|
||||
|
||||
void Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSheetPath,
|
||||
OUTPUTFORMATTER* aFormatter );
|
||||
void Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSelectionPath,
|
||||
SCH_SHEET_LIST* aFullSheetHierarchy, OUTPUTFORMATTER* aFormatter );
|
||||
|
||||
void EnumerateSymbolLib( wxArrayString& aSymbolNameList,
|
||||
const wxString& aLibraryPath,
|
||||
|
|
|
@ -177,6 +177,21 @@ int SCH_SHEET_PATH::ComparePageNumAndName( const SCH_SHEET_PATH& aSheetPathToTes
|
|||
}
|
||||
|
||||
|
||||
bool SCH_SHEET_PATH::IsContainedWithin( const SCH_SHEET_PATH& aSheetPathToTest ) const
|
||||
{
|
||||
if( aSheetPathToTest.size() > size() )
|
||||
return false;
|
||||
|
||||
for( size_t i = 0; i < aSheetPathToTest.size(); ++i )
|
||||
{
|
||||
if( at( i )->m_Uuid != aSheetPathToTest.at( i )->m_Uuid )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
SCH_SHEET* SCH_SHEET_PATH::Last() const
|
||||
{
|
||||
if( !empty() )
|
||||
|
@ -551,7 +566,7 @@ bool SCH_SHEET_LIST::PageNumberExists( const wxString& aPageNumber ) const
|
|||
{
|
||||
for( const SCH_SHEET_PATH& sheet : *this )
|
||||
{
|
||||
if( sheet.Last()->GetPageNumber( sheet ) == aPageNumber )
|
||||
if( sheet.GetPageNumber() == aPageNumber )
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -720,6 +735,30 @@ void SCH_SHEET_LIST::GetSymbols( SCH_REFERENCE_LIST& aReferences, bool aIncludeP
|
|||
}
|
||||
|
||||
|
||||
void SCH_SHEET_LIST::GetSymbolsWithinPath( SCH_REFERENCE_LIST& aReferences,
|
||||
const SCH_SHEET_PATH& aSheetPath,
|
||||
bool aIncludePowerSymbols,
|
||||
bool aForceIncludeOrphanSymbols ) const
|
||||
{
|
||||
for( const SCH_SHEET_PATH& sheet : *this )
|
||||
{
|
||||
if( sheet.IsContainedWithin( aSheetPath ) )
|
||||
sheet.GetSymbols( aReferences, aIncludePowerSymbols, aForceIncludeOrphanSymbols );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SCH_SHEET_LIST::GetSheetsWithinPath( SCH_SHEET_PATHS& aSheets,
|
||||
const SCH_SHEET_PATH& aSheetPath ) const
|
||||
{
|
||||
for( const SCH_SHEET_PATH& sheet : *this )
|
||||
{
|
||||
if( sheet.IsContainedWithin( aSheetPath ) )
|
||||
aSheets.push_back( sheet );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SCH_SHEET_LIST::GetMultiUnitSymbols( SCH_MULTI_UNIT_REFERENCE_MAP &aRefList,
|
||||
bool aIncludePowerSymbols ) const
|
||||
{
|
||||
|
@ -792,6 +831,20 @@ SCH_SHEET_PATH* SCH_SHEET_LIST::FindSheetForScreen( const SCH_SCREEN* aScreen )
|
|||
}
|
||||
|
||||
|
||||
SCH_SHEET_LIST SCH_SHEET_LIST::FindAllSheetsForScreen( const SCH_SCREEN* aScreen ) const
|
||||
{
|
||||
SCH_SHEET_LIST retval;
|
||||
|
||||
for( const SCH_SHEET_PATH& sheetpath : *this )
|
||||
{
|
||||
if( sheetpath.LastScreen() == aScreen )
|
||||
retval.push_back( sheetpath );
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
void SCH_SHEET_LIST::UpdateSymbolInstances(
|
||||
const std::vector<SYMBOL_INSTANCE_REFERENCE>& aSymbolInstances )
|
||||
{
|
||||
|
|
|
@ -223,6 +223,14 @@ public:
|
|||
*/
|
||||
int ComparePageNumAndName( const SCH_SHEET_PATH& aSheetPathToTest ) const;
|
||||
|
||||
/**
|
||||
* Check if this path is contained inside aSheetPathToTest.
|
||||
*
|
||||
* @param aSheetPathToTest is the sheet path to compare against.
|
||||
* @return true if this path is contained inside or equal to aSheetPathToTest.
|
||||
*/
|
||||
bool IsContainedWithin( const SCH_SHEET_PATH& aSheetPathToTest ) const;
|
||||
|
||||
/**
|
||||
* Return a pointer to the last #SCH_SHEET of the list.
|
||||
*
|
||||
|
@ -432,6 +440,30 @@ public:
|
|||
void GetSymbols( SCH_REFERENCE_LIST& aReferences, bool aIncludePowerSymbols = true,
|
||||
bool aForceIncludeOrphanSymbols = false ) const;
|
||||
|
||||
/**
|
||||
* Add a #SCH_REFERENCE object to \a aReferences for each symbol in the list of sheets that are
|
||||
* contained within \a aSheetPath as well as recursively downwards inside aSheetPath.
|
||||
*
|
||||
* @param aReferences List of references to populate.
|
||||
* @param aSheetPath Path to return symbols from
|
||||
* @param aIncludePowerSymbols Set to false to only get normal symbols.
|
||||
* @param aForceIncludeOrphanSymbols Set to true to include symbols having no symbol found
|
||||
* in lib. The normal option is false, and set to true
|
||||
* only to build the full list of symbols.
|
||||
*/
|
||||
void GetSymbolsWithinPath( SCH_REFERENCE_LIST& aReferences, const SCH_SHEET_PATH& aSheetPath,
|
||||
bool aIncludePowerSymbols = true,
|
||||
bool aForceIncludeOrphanSymbols = false ) const;
|
||||
|
||||
/**
|
||||
* Add a #SCH_SHEET_PATH object to \a aSheets for each sheet in the list that are
|
||||
* contained within \a aSheetPath as well as recursively downwards inside aSheetPath.
|
||||
*
|
||||
* @param aReferences List of sheets to populate.
|
||||
* @param aSheetPath Path to return sheets from
|
||||
*/
|
||||
void GetSheetsWithinPath( SCH_SHEET_PATHS& aSheets, const SCH_SHEET_PATH& aSheetPath ) const;
|
||||
|
||||
/**
|
||||
* Add a #SCH_REFERENCE_LIST object to \a aRefList for each same-reference set of
|
||||
* multi-unit parts in the list of sheets. The map key for each element will be the
|
||||
|
@ -460,6 +492,11 @@ public:
|
|||
*/
|
||||
SCH_SHEET_PATH* FindSheetForScreen( const SCH_SCREEN* aScreen );
|
||||
|
||||
/**
|
||||
* Return a #SCH_SHEET_LIST with a copy of all the #SCH_SHEET_PATH using a particular screen.
|
||||
*/
|
||||
SCH_SHEET_LIST FindAllSheetsForScreen( const SCH_SCREEN* aScreen ) const;
|
||||
|
||||
/**
|
||||
* Build the list of sheets and their sheet path from \a aSheet.
|
||||
*
|
||||
|
|
|
@ -380,6 +380,7 @@ void SCH_EDIT_FRAME::RollbackSchematicFromUndo()
|
|||
delete undo;
|
||||
|
||||
SetSheetNumberAndCount();
|
||||
UpdateHierarchyNavigator();
|
||||
|
||||
TestDanglingEnds();
|
||||
}
|
||||
|
|
|
@ -1243,14 +1243,12 @@ bool SCH_EDITOR_CONTROL::doCopy()
|
|||
}
|
||||
}
|
||||
|
||||
m_supplementaryClipboardInstances.Clear();
|
||||
schematic.GetSheets().GetSymbols( m_supplementaryClipboardInstances, true, true );
|
||||
m_supplementaryClipboardPath = m_frame->GetCurrentSheet().Path();
|
||||
|
||||
STRING_FORMATTER formatter;
|
||||
SCH_SEXPR_PLUGIN plugin;
|
||||
SCH_SHEET_LIST hiearchy = schematic.GetSheets();
|
||||
SCH_SHEET_PATH selPath = m_frame->GetCurrentSheet();
|
||||
|
||||
plugin.Format( &selection, &m_frame->GetCurrentSheet(), &formatter );
|
||||
plugin.Format( &selection, &selPath, &hiearchy, &formatter );
|
||||
|
||||
return m_toolMgr->SaveClipboard( formatter.GetString() );
|
||||
}
|
||||
|
@ -1302,60 +1300,118 @@ int SCH_EDITOR_CONTROL::Copy( const TOOL_EVENT& aEvent )
|
|||
}
|
||||
|
||||
|
||||
void SCH_EDITOR_CONTROL::updatePastedInstances( const SCH_SHEET_PATH& aPastePath,
|
||||
const KIID_PATH& aClipPath, SCH_SHEET* aSheet,
|
||||
bool aForceKeepAnnotations )
|
||||
void SCH_EDITOR_CONTROL::updatePastedSymbol( SCH_COMPONENT* aSymbol, SCH_SCREEN* aPasteScreen,
|
||||
const SCH_SHEET_PATH& aPastePath,
|
||||
const KIID_PATH& aClipPath,
|
||||
bool aForceKeepAnnotations )
|
||||
{
|
||||
KIID_PATH clipItemPath = aClipPath;
|
||||
clipItemPath.push_back( aSymbol->m_Uuid );
|
||||
|
||||
wxString reference, value, footprint;
|
||||
int unit;
|
||||
|
||||
if( m_clipboardSymbolInstances.count( clipItemPath ) > 0 )
|
||||
{
|
||||
SYMBOL_INSTANCE_REFERENCE instance = m_clipboardSymbolInstances.at( clipItemPath );
|
||||
|
||||
unit = instance.m_Unit;
|
||||
reference = instance.m_Reference;
|
||||
value = instance.m_Value;
|
||||
footprint = instance.m_Footprint;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
|
||||
if( aForceKeepAnnotations && !reference.IsEmpty() )
|
||||
{
|
||||
aSymbol->SetRef( &aPastePath, reference );
|
||||
aSymbol->SetValue( &aPastePath, value );
|
||||
aSymbol->SetFootprint( &aPastePath, footprint );
|
||||
}
|
||||
else
|
||||
{
|
||||
aSymbol->ClearAnnotation( &aPastePath );
|
||||
}
|
||||
|
||||
// We might clear annotations but always leave the original unit number from the paste
|
||||
aSymbol->SetUnitSelection( &aPastePath, unit );
|
||||
aSymbol->SetUnit( unit );
|
||||
}
|
||||
|
||||
|
||||
SCH_SHEET_PATH SCH_EDITOR_CONTROL::updatePastedSheet( const SCH_SHEET_PATH& aPastePath,
|
||||
const KIID_PATH& aClipPath, SCH_SHEET* aSheet,
|
||||
bool aForceKeepAnnotations,
|
||||
SCH_SHEET_LIST* aPastedSheetsSoFar,
|
||||
SCH_REFERENCE_LIST* aPastedSymbolsSoFar )
|
||||
{
|
||||
SCH_SHEET_PATH sheetPath = aPastePath;
|
||||
sheetPath.push_back( aSheet );
|
||||
|
||||
aSheet->AddInstance( sheetPath.Path() );
|
||||
|
||||
wxString pageNum;
|
||||
|
||||
if( m_clipboardSheetInstances.count( aClipPath ) > 0 )
|
||||
pageNum = m_clipboardSheetInstances.at( aClipPath ).m_PageNumber;
|
||||
else
|
||||
pageNum = wxString::Format( "%d", static_cast<int>( aPastedSheetsSoFar->size() ) );
|
||||
|
||||
aSheet->SetPageNumber( sheetPath, pageNum );
|
||||
aPastedSheetsSoFar->push_back( sheetPath );
|
||||
|
||||
if( aSheet->GetScreen() == nullptr )
|
||||
return sheetPath; // We can only really set the page number but not load any items
|
||||
|
||||
for( SCH_ITEM* item : aSheet->GetScreen()->Items() )
|
||||
{
|
||||
if( item->Type() == SCH_COMPONENT_T )
|
||||
{
|
||||
SCH_COMPONENT* symbol = static_cast<SCH_COMPONENT*>( item );
|
||||
|
||||
KIID_PATH clipItemPath = aClipPath;
|
||||
clipItemPath.push_back( symbol->m_Uuid );
|
||||
|
||||
// SCH_REFERENCE_LIST doesn't include the root sheet in the path
|
||||
clipItemPath.erase( clipItemPath.begin() );
|
||||
|
||||
int ii = m_supplementaryClipboardInstances.FindRefByPath( clipItemPath.AsString() );
|
||||
|
||||
if( ii >= 0 )
|
||||
{
|
||||
SCH_REFERENCE instance = m_supplementaryClipboardInstances[ ii ];
|
||||
|
||||
symbol->SetUnitSelection( &aPastePath, instance.GetUnit() );
|
||||
symbol->SetUnit( instance.GetUnit() );
|
||||
|
||||
if( aForceKeepAnnotations )
|
||||
{
|
||||
symbol->SetRef( &aPastePath, instance.GetRef() );
|
||||
symbol->SetValue( &aPastePath, instance.GetValue() );
|
||||
symbol->SetFootprint( &aPastePath, instance.GetFootprint() );
|
||||
}
|
||||
else
|
||||
{
|
||||
symbol->ClearAnnotation( &aPastePath );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
symbol->ClearAnnotation( &aPastePath );
|
||||
}
|
||||
updatePastedSymbol( symbol, aSheet->GetScreen(), sheetPath, aClipPath,
|
||||
aForceKeepAnnotations );
|
||||
}
|
||||
else if( item->Type() == SCH_SHEET_T )
|
||||
{
|
||||
SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
|
||||
SCH_SHEET_PATH pastePath = aPastePath;
|
||||
pastePath.push_back( sheet );
|
||||
SCH_SHEET* subsheet = static_cast<SCH_SHEET*>( item );
|
||||
|
||||
KIID_PATH clipPath = aClipPath;
|
||||
clipPath.push_back( sheet->m_Uuid );
|
||||
KIID_PATH newClipPath = aClipPath;
|
||||
newClipPath.push_back( subsheet->m_Uuid );
|
||||
|
||||
sheet->AddInstance( pastePath.Path() );
|
||||
updatePastedInstances( pastePath, clipPath, sheet, aForceKeepAnnotations );
|
||||
updatePastedSheet( sheetPath, newClipPath, subsheet, aForceKeepAnnotations,
|
||||
aPastedSheetsSoFar, aPastedSymbolsSoFar );
|
||||
|
||||
SCH_SHEET_PATH subSheetPath = sheetPath;
|
||||
subSheetPath.push_back( subsheet );
|
||||
|
||||
subSheetPath.GetSymbols( *aPastedSymbolsSoFar );
|
||||
}
|
||||
}
|
||||
|
||||
return sheetPath;
|
||||
}
|
||||
|
||||
|
||||
void SCH_EDITOR_CONTROL::setClipboardInstances( const SCH_SCREEN* aPastedScreen )
|
||||
{
|
||||
m_clipboardSheetInstances.clear();
|
||||
|
||||
for( const SCH_SHEET_INSTANCE sheet : aPastedScreen->GetSheetInstances() )
|
||||
m_clipboardSheetInstances[sheet.m_Path] = sheet;
|
||||
|
||||
m_clipboardSymbolInstances.clear();
|
||||
|
||||
for( const SYMBOL_INSTANCE_REFERENCE symbol : aPastedScreen->GetSymbolInstances() )
|
||||
m_clipboardSymbolInstances[symbol.m_Path] = symbol;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1396,6 +1452,9 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
|
|||
|
||||
bool forceKeepAnnotations = false;
|
||||
|
||||
// Save loaded screen instances to m_clipboardSheetInstances
|
||||
setClipboardInstances( paste_screen );
|
||||
|
||||
if( aEvent.IsAction( &ACTIONS::pasteSpecial ) )
|
||||
{
|
||||
DIALOG_PASTE_SPECIAL dlg( m_frame, &forceKeepAnnotations );
|
||||
|
@ -1416,10 +1475,35 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
|
|||
if( destFn.IsRelative() )
|
||||
destFn.MakeAbsolute( m_frame->Prj().GetProjectPath() );
|
||||
|
||||
// List of paths in the hierarchy that refer to the destination sheet of the paste
|
||||
SCH_SHEET_LIST pasteInstances = hierarchy.FindAllSheetsForScreen( pasteRoot.LastScreen() );
|
||||
pasteInstances.SortByPageNumbers();
|
||||
|
||||
// Build a list of screens from the current design (to avoid loading sheets that already exist)
|
||||
std::map<wxString, SCH_SCREEN*> loadedScreens;
|
||||
|
||||
for( const SCH_SHEET_PATH& item : hierarchy )
|
||||
{
|
||||
if( item.LastScreen() )
|
||||
loadedScreens[item.Last()->GetFileName()] = item.LastScreen();
|
||||
}
|
||||
|
||||
// Build symbol list for reannotation of duplicates
|
||||
SCH_SHEET_LIST sheets = m_frame->Schematic().GetSheets();
|
||||
SCH_REFERENCE_LIST existingRefs;
|
||||
sheets.GetSymbols( existingRefs );
|
||||
existingRefs.SortByReferenceOnly();
|
||||
|
||||
// Keep track of pasted sheets and symbols for the different
|
||||
// paths to the hiearchy
|
||||
std::map<SCH_SHEET_PATH, SCH_REFERENCE_LIST> pastedSymbols;
|
||||
std::map<SCH_SHEET_PATH, SCH_SHEET_LIST> pastedSheets;
|
||||
|
||||
for( SCH_ITEM* item : paste_screen->Items() )
|
||||
{
|
||||
loadedItems.push_back( item );
|
||||
|
||||
//@todo: we might want to sort the sheets by page number before adding to loadedItems
|
||||
if( item->Type() == SCH_SHEET_T )
|
||||
{
|
||||
SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
|
||||
|
@ -1448,7 +1532,7 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
|
|||
for( unsigned i = 0; i < loadedItems.size(); ++i )
|
||||
{
|
||||
EDA_ITEM* item = loadedItems[i];
|
||||
KIID_PATH clipPath = m_supplementaryClipboardPath;
|
||||
KIID_PATH clipPath( wxT("/") ); // clipboard is at root
|
||||
|
||||
if( item->Type() == SCH_COMPONENT_T )
|
||||
{
|
||||
|
@ -1463,29 +1547,51 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
|
|||
wxCHECK2( currentScreen, continue );
|
||||
|
||||
auto it = currentScreen->GetLibSymbols().find( symbol->GetSchSymbolLibraryName() );
|
||||
auto end = currentScreen->GetLibSymbols().end();
|
||||
|
||||
if( it != currentScreen->GetLibSymbols().end() )
|
||||
symbol->SetLibSymbol( new LIB_PART( *it->second ) );
|
||||
|
||||
if( !forceKeepAnnotations )
|
||||
if( it == end )
|
||||
{
|
||||
// clear the annotation, but preserve the selected unit
|
||||
int unit = symbol->GetUnit();
|
||||
symbol->ClearAnnotation( nullptr );
|
||||
symbol->SetUnit( unit );
|
||||
// If can't find library definition in the design, use the pasted library
|
||||
it = paste_screen->GetLibSymbols().find( symbol->GetSchSymbolLibraryName() );
|
||||
end = paste_screen->GetLibSymbols().end();
|
||||
}
|
||||
|
||||
LIB_PART* libPart = nullptr;
|
||||
|
||||
if( it != end )
|
||||
{
|
||||
libPart = new LIB_PART( *it->second );
|
||||
symbol->SetLibSymbol( libPart );
|
||||
}
|
||||
|
||||
for( SCH_SHEET_PATH& instance : pasteInstances )
|
||||
{
|
||||
updatePastedSymbol( symbol, paste_screen, instance, clipPath,
|
||||
forceKeepAnnotations );
|
||||
}
|
||||
|
||||
// Assign a new KIID
|
||||
const_cast<KIID&>( item->m_Uuid ) = KIID();
|
||||
|
||||
// Make sure pins get a new UUID
|
||||
for( SCH_PIN* pin : symbol->GetPins() )
|
||||
const_cast<KIID&>( pin->m_Uuid ) = KIID();
|
||||
}
|
||||
|
||||
if( item->Type() == SCH_SHEET_T )
|
||||
for( SCH_SHEET_PATH& instance : pasteInstances )
|
||||
{
|
||||
// Ignore pseudo-symbols (e.g. power symbols) and symbols from a non-existant library
|
||||
if( libPart && symbol->GetRef( &instance )[0] != wxT( '#' ) )
|
||||
{
|
||||
SCH_REFERENCE schReference( symbol, libPart, instance );
|
||||
schReference.SetSheetNumber( instance.GetVirtualPageNumber() );
|
||||
pastedSymbols[instance].AddItem( schReference );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( item->Type() == SCH_SHEET_T )
|
||||
{
|
||||
SCH_SHEET* sheet = (SCH_SHEET*) item;
|
||||
SCH_FIELD& nameField = sheet->GetFields()[SHEETNAME];
|
||||
wxFileName fn = sheet->GetFileName();
|
||||
SCH_SCREEN* existingScreen = nullptr;
|
||||
wxString baseName = nameField.GetText();
|
||||
wxString candidateName = baseName;
|
||||
wxString number;
|
||||
|
@ -1496,20 +1602,20 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
|
|||
baseName.RemoveLast();
|
||||
}
|
||||
|
||||
//@todo: it might be better to just iterate through the sheet names
|
||||
// in this screen instead of the whole hiearchy.
|
||||
int uniquifier = std::max( 0, wxAtoi( number ) ) + 1;
|
||||
|
||||
// Ensure we have latest hierarchy, as we may have added a sheet in the previous
|
||||
// iteration
|
||||
hierarchy = m_frame->Schematic().GetSheets();
|
||||
|
||||
while( hierarchy.NameExists( candidateName ) )
|
||||
candidateName = wxString::Format( wxT( "%s%d" ), baseName, uniquifier++ );
|
||||
|
||||
nameField.SetText( candidateName );
|
||||
|
||||
wxFileName fn = sheet->GetFileName();
|
||||
SCH_SCREEN* existingScreen = nullptr;
|
||||
|
||||
sheet->SetParent( pasteRoot.Last() );
|
||||
sheet->SetScreen( nullptr );
|
||||
sheetsPasted = true;
|
||||
|
||||
if( !fn.IsAbsolute() )
|
||||
{
|
||||
|
@ -1517,10 +1623,14 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
|
|||
fn.Normalize( wxPATH_NORM_ALL, currentSheetFileName.GetPath() );
|
||||
}
|
||||
|
||||
// Try to find the screen for the pasted sheet by several means
|
||||
if( !m_frame->Schematic().Root().SearchHierarchy( fn.GetFullPath( wxPATH_UNIX ),
|
||||
&existingScreen ) )
|
||||
{
|
||||
searchSupplementaryClipboard( sheet->GetFileName(), &existingScreen );
|
||||
if( loadedScreens.count( sheet->GetFileName() ) > 0 )
|
||||
existingScreen = loadedScreens.at( sheet->GetFileName() );
|
||||
else
|
||||
searchSupplementaryClipboard( sheet->GetFileName(), &existingScreen );
|
||||
}
|
||||
|
||||
if( existingScreen )
|
||||
|
@ -1533,34 +1643,34 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
|
|||
m_frame->InitSheet( sheet, sheet->GetFileName() );
|
||||
}
|
||||
|
||||
sheetsPasted = true;
|
||||
|
||||
// Push it to the clipboard path while it still has its old KIID
|
||||
clipPath.push_back( sheet->m_Uuid );
|
||||
}
|
||||
|
||||
// Everything gets a new KIID
|
||||
const_cast<KIID&>( item->m_Uuid ) = KIID();
|
||||
|
||||
// Once we have our new KIID we can update all pasted instances. This will either
|
||||
// reset the annotations or copy "kept" annotations from the supplementary clipboard.
|
||||
if( item->Type() == SCH_SHEET_T )
|
||||
{
|
||||
SCH_SHEET* sheet = (SCH_SHEET*) item;
|
||||
SCH_SHEET_PATH pastePath = pasteRoot;
|
||||
pastePath.push_back( sheet );
|
||||
|
||||
int page = 1;
|
||||
wxString pageNum = wxString::Format( "%d", page );
|
||||
|
||||
while( hierarchy.PageNumberExists( pageNum ) )
|
||||
pageNum = wxString::Format( "%d", ++page );
|
||||
|
||||
sheet->AddInstance( pastePath.Path() );
|
||||
sheet->SetPageNumber( pastePath, pageNum );
|
||||
updatePastedInstances( pastePath, clipPath, sheet, forceKeepAnnotations );
|
||||
// Assign a new KIID to the pasted sheet
|
||||
const_cast<KIID&>( sheet->m_Uuid ) = KIID();
|
||||
|
||||
// Make sure pins get a new UUID
|
||||
for( SCH_SHEET_PIN* pin : sheet->GetPins() )
|
||||
const_cast<KIID&>( pin->m_Uuid ) = KIID();
|
||||
|
||||
// Once we have our new KIID we can update all pasted instances. This will either
|
||||
// reset the annotations or copy "kept" annotations from the supplementary clipboard.
|
||||
for( SCH_SHEET_PATH& instance : pasteInstances )
|
||||
{
|
||||
SCH_SHEET_PATH sheetPath = updatePastedSheet( instance, clipPath, sheet,
|
||||
forceKeepAnnotations,
|
||||
&pastedSheets[instance],
|
||||
&pastedSymbols[instance] );
|
||||
|
||||
sheetPath.GetSymbols( pastedSymbols[instance] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Everything gets a new KIID
|
||||
const_cast<KIID&>( item->m_Uuid ) = KIID();
|
||||
}
|
||||
|
||||
item->SetFlags( IS_NEW | IS_PASTED | IS_MOVED );
|
||||
|
@ -1573,8 +1683,29 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
|
|||
getView()->Hide( item, true );
|
||||
}
|
||||
|
||||
pasteInstances.SortByPageNumbers();
|
||||
m_frame->GetCurrentSheet().UpdateAllScreenReferences();
|
||||
|
||||
if( sheetsPasted )
|
||||
{
|
||||
// Update page numbers: Find next free numeric page number
|
||||
for( SCH_SHEET_PATH& instance : pasteInstances )
|
||||
{
|
||||
pastedSheets[instance].SortByPageNumbers();
|
||||
|
||||
for( SCH_SHEET_PATH& pastedSheet : pastedSheets[instance] )
|
||||
{
|
||||
int page = 1;
|
||||
wxString pageNum = wxString::Format( "%d", page );
|
||||
|
||||
while( hierarchy.PageNumberExists( pageNum ) )
|
||||
pageNum = wxString::Format( "%d", ++page );
|
||||
|
||||
pastedSheet.SetPageNumber( pageNum );
|
||||
hierarchy.push_back( pastedSheet );
|
||||
}
|
||||
}
|
||||
|
||||
m_frame->SetSheetNumberAndCount();
|
||||
m_frame->UpdateHierarchyNavigator();
|
||||
}
|
||||
|
|
|
@ -161,8 +161,16 @@ private:
|
|||
|
||||
void doCrossProbeSchToPcb( const TOOL_EVENT& aEvent, bool aForce );
|
||||
|
||||
void updatePastedInstances( const SCH_SHEET_PATH& aPastePath, const KIID_PATH& aClipPath,
|
||||
SCH_SHEET* aSheet, bool aForceKeepAnnotations );
|
||||
void updatePastedSymbol( SCH_COMPONENT* aSymbol, SCH_SCREEN* aPasteScreen,
|
||||
const SCH_SHEET_PATH& aPastePath, const KIID_PATH& aClipPath,
|
||||
bool aForceKeepAnnotations );
|
||||
|
||||
SCH_SHEET_PATH updatePastedSheet( const SCH_SHEET_PATH& aPastePath, const KIID_PATH& aClipPath,
|
||||
SCH_SHEET* aSheet, bool aForceKeepAnnotations,
|
||||
SCH_SHEET_LIST* aPastedSheetsSoFar,
|
||||
SCH_REFERENCE_LIST* aPastedSymbolsSoFar );
|
||||
|
||||
void setClipboardInstances( const SCH_SCREEN* aPastedScreen );
|
||||
|
||||
/**
|
||||
* Read the footprint info from each line in the stuff file by reference designator.
|
||||
|
@ -209,8 +217,12 @@ private:
|
|||
// A map of sheet filename --> screens for the clipboard contents. We use these to hook up
|
||||
// cut/paste operations for unsaved sheet content.
|
||||
std::map<wxString, SCH_SCREEN*> m_supplementaryClipboard;
|
||||
SCH_REFERENCE_LIST m_supplementaryClipboardInstances;
|
||||
KIID_PATH m_supplementaryClipboardPath;
|
||||
|
||||
// A map of KIID_PATH --> symbol instances for the clipboard contents.
|
||||
std::map<KIID_PATH, SYMBOL_INSTANCE_REFERENCE> m_clipboardSymbolInstances;
|
||||
|
||||
// A map of KIID_PATH --> sheet instances for the clipboard contents.
|
||||
std::map<KIID_PATH, SCH_SHEET_INSTANCE> m_clipboardSheetInstances;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -113,6 +113,7 @@ public:
|
|||
|
||||
KIID_PATH( const wxString& aString );
|
||||
|
||||
bool MakeRelativeTo( const KIID_PATH& aPath );
|
||||
|
||||
wxString AsString() const;
|
||||
|
||||
|
|
|
@ -54,6 +54,16 @@ wxString GetRefDesPrefix( const wxString& aRefDes );
|
|||
*/
|
||||
wxString GetRefDesUnannotated( const wxString& aRefDes );
|
||||
|
||||
/**
|
||||
* Get the numeric suffix from a refdes - e.g.
|
||||
* R1 -> 1
|
||||
* IC34 -> 34
|
||||
* R? -> -1
|
||||
* @param aRefDes full refdes
|
||||
* @return the suffix, or -1 if nothing found
|
||||
*/
|
||||
int GetRefDesNumber( const wxString& aRefDes );
|
||||
|
||||
/**
|
||||
* Acts just like the strcmp function but treats numbers within the string text
|
||||
* correctly for sorting. eg. A10 > A2
|
||||
|
|
Loading…
Reference in New Issue