Remove SetRoot()

Gets rid of the SetRoot() because we don't have roots anymore so this
was confusing.  We now use SetTopLevelSheets() to add sheets to the
virtual root.  Ensure that the hierarchy is maintained on each
transition
This commit is contained in:
Seth Hillbrand
2025-12-22 17:41:59 -08:00
parent fe0c9c6d98
commit 3800c82ffa
22 changed files with 340 additions and 308 deletions
+4 -15
View File
@@ -98,23 +98,12 @@ static std::unique_ptr<SCHEMATIC> readSchematicFromFile( const std::string& aFil
manager.LoadProject( "" );
schematic->Reset();
schematic->SetProject( &manager.Prj() );
schematic->SetRoot( pi->LoadSchematicFile( aFilename, schematic.get() ) );
SCH_SHEET* rootSheet = pi->LoadSchematicFile( aFilename, schematic.get() );
// Set current sheet to the first top-level sheet, not the virtual root
std::vector<SCH_SHEET*> topLevelSheets = schematic->GetTopLevelSheets();
if( !rootSheet )
return nullptr;
if( !topLevelSheets.empty() )
{
schematic->CurrentSheet().push_back( topLevelSheets[0] );
wxLogTrace( traceSchCurrentSheet,
"Set current sheet to first top-level sheet: %s, path: %s",
topLevelSheets[0]->GetName(),
schematic->CurrentSheet().Path().AsString() );
}
else
{
wxLogWarning( "No top-level sheets found after loading schematic!" );
}
schematic->SetTopLevelSheets( { rootSheet } );
SCH_SCREENS screens( schematic->Root() );
+6 -1
View File
@@ -155,7 +155,12 @@ SCHEMATIC* EESCHEMA_HELPERS::LoadSchematic( const wxString& aFileName,
try
{
schematic->SetRoot( pi->LoadSchematicFile( schFile.GetFullPath(), schematic ) );
SCH_SHEET* rootSheet = pi->LoadSchematicFile( schFile.GetFullPath(), schematic );
if( rootSheet )
schematic->SetTopLevelSheets( { rootSheet } );
else
return nullptr;
}
catch( ... )
{
+31 -29
View File
@@ -272,8 +272,7 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
if( !topLevelSheets.empty() )
{
// New multi-root format: Load all top-level sheets
// Note: AddTopLevelSheet will create the virtual root as needed
std::vector<SCH_SHEET*> loadedSheets;
// Load each top-level sheet
for( const TOP_LEVEL_SHEET_INFO& sheetInfo : topLevelSheets )
@@ -304,9 +303,7 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
}
sheet->SetName( sheetInfo.name );
// Regular top-level sheet, add it normally
newSchematic->AddTopLevelSheet( sheet );
loadedSheets.push_back( sheet );
wxLogTrace( tracePathsAndFiles,
wxS( "Loaded top-level sheet '%s' (UUID %s) from %s" ),
@@ -316,17 +313,9 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
}
}
// Virtual root was already created by AddTopLevelSheet, no need to call SetRoot
// Set current sheet to the first top-level sheet
if( !newSchematic->GetTopLevelSheets().empty() )
if( !loadedSheets.empty() )
{
newSchematic->CurrentSheet().clear();
newSchematic->CurrentSheet().push_back( newSchematic->GetTopLevelSheets()[0] );
wxLogTrace( tracePathsAndFiles,
wxS( "Loaded multi-root schematic with %zu top-level sheets, current sheet set to '%s'" ),
newSchematic->GetTopLevelSheets().size(),
newSchematic->GetTopLevelSheets()[0]->GetName() );
newSchematic->SetTopLevelSheets( loadedSheets );
}
else
{
@@ -338,20 +327,31 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
else
{
// Legacy single-root format: Load the single root sheet
newSchematic->SetRoot( pi->LoadSchematicFile( fullFileName, newSchematic.get() ) );
SCH_SHEET* rootSheet = pi->LoadSchematicFile( fullFileName, newSchematic.get() );
// Make ${SHEETNAME} work on the root sheet until we properly support
// naming the root sheet
newSchematic->Root().SetName( _( "Root" ) );
if( rootSheet )
{
newSchematic->SetTopLevelSheets( { rootSheet } );
// Make ${SHEETNAME} work on the root sheet until we properly support
// naming the root sheet
if( SCH_SHEET* topSheet = newSchematic->GetTopLevelSheet() )
topSheet->SetName( _( "Root" ) );
wxLogTrace( tracePathsAndFiles,
wxS( "Loaded schematic with root sheet UUID %s" ),
rootSheet->m_Uuid.AsString() );
wxLogTrace( traceSchCurrentSheet,
"After loading: Current sheet path='%s', size=%zu, empty=%d",
newSchematic->CurrentSheet().Path().AsString(),
newSchematic->CurrentSheet().size(),
newSchematic->CurrentSheet().empty() ? 1 : 0 );
}
else
{
newSchematic->CreateDefaultScreens();
}
wxLogTrace( tracePathsAndFiles,
wxS( "Loaded schematic with root sheet UUID %s" ),
newSchematic->Root().m_Uuid.AsString() );
wxLogTrace( traceSchCurrentSheet,
"After loading: Current sheet path='%s', size=%zu, empty=%d",
newSchematic->CurrentSheet().Path().AsString(),
newSchematic->CurrentSheet().size(),
newSchematic->CurrentSheet().empty() ? 1 : 0 );
}
}
@@ -1458,7 +1458,7 @@ bool SCH_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType,
if( loadedSheet )
{
Schematic().SetRoot( loadedSheet );
Schematic().SetTopLevelSheets( { loadedSheet } );
if( errorReporter.m_Reporter->HasMessage() )
{
@@ -1478,7 +1478,9 @@ bool SCH_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType,
SetScreen( Schematic().RootScreen() );
Schematic().Root().SetFileName( newfilename.GetFullName() );
if( SCH_SHEET* topSheet = Schematic().GetTopLevelSheet() )
topSheet->SetFileName( newfilename.GetFullName() );
GetScreen()->SetFileName( newfilename.GetFullPath() );
GetScreen()->SetContentModified();
+2 -2
View File
@@ -465,14 +465,14 @@ SCH_SHEET* SCH_IO_ALTIUM::LoadSchematicFile( const wxString& aFileName, SCHEMATI
if( aAppendToMe )
{
wxCHECK_MSG( aSchematic->IsValid(), nullptr, "Can't append to a schematic with no root!" );
m_rootSheet = &aSchematic->Root();
m_rootSheet = aAppendToMe;
}
else
{
m_rootSheet = new SCH_SHEET( aSchematic );
m_rootSheet->SetFileName( fileName.GetFullPath() );
aSchematic->SetRoot( m_rootSheet );
aSchematic->SetTopLevelSheets( { m_rootSheet } );
SCH_SHEET_PATH sheetpath;
sheetpath.push_back( m_rootSheet );
@@ -79,13 +79,13 @@ SCH_SHEET* SCH_IO_CADSTAR_ARCHIVE::LoadSchematicFile( const wxString& aFi
if( aAppendToMe )
{
wxCHECK_MSG( aSchematic->IsValid(), nullptr, "Can't append to a schematic with no root!" );
rootSheet = &aSchematic->Root();
rootSheet = aAppendToMe;
}
else
{
rootSheet = new SCH_SHEET( aSchematic );
rootSheet->SetFileName( aFileName );
aSchematic->SetRoot( rootSheet );
aSchematic->SetTopLevelSheets( { rootSheet } );
}
if( !rootSheet->GetScreen() )
+1 -1
View File
@@ -406,7 +406,7 @@ SCH_SHEET* SCH_IO_EAGLE::LoadSchematicFile( const wxString& aFileName, SCHEMATIC
m_rootSheet = new SCH_SHEET( aSchematic );
const_cast<KIID&>( m_rootSheet->m_Uuid ) = niluuid;
m_rootSheet->SetFileName( newFilename.GetFullPath() );
aSchematic->SetRoot( m_rootSheet );
aSchematic->SetTopLevelSheets( { m_rootSheet } );
}
if( !m_rootSheet->GetScreen() )
+2 -2
View File
@@ -625,13 +625,13 @@ SCH_SHEET* SCH_IO_EASYEDA::LoadSchematicFile( const wxString& aFileName, SCHEMAT
wxCHECK_MSG( aSchematic->IsValid(), nullptr,
wxS( "Can't append to a schematic with no root!" ) );
rootSheet = &aSchematic->Root();
rootSheet = aAppendToMe;
}
else
{
rootSheet = new SCH_SHEET( aSchematic );
rootSheet->SetFileName( aFileName );
aSchematic->SetRoot( rootSheet );
aSchematic->SetTopLevelSheets( { rootSheet } );
}
if( !rootSheet->GetScreen() )
@@ -442,13 +442,13 @@ SCH_SHEET* SCH_IO_EASYEDAPRO::LoadSchematicFile( const wxString& aFileName,
wxCHECK_MSG( aSchematic->IsValid(), nullptr,
wxS( "Can't append to a schematic with no root!" ) );
rootSheet = &aSchematic->Root();
rootSheet = aAppendToMe;
}
else
{
rootSheet = new SCH_SHEET( aSchematic );
rootSheet->SetFileName( aFileName );
aSchematic->SetRoot( rootSheet );
aSchematic->SetTopLevelSheets( { rootSheet } );
}
if( !rootSheet->GetScreen() )
+2 -2
View File
@@ -50,13 +50,13 @@ SCH_SHEET* SCH_IO_LTSPICE::LoadSchematicFile( const wxString& aFileName, SCHEMAT
if( aAppendToMe )
{
wxCHECK_MSG( aSchematic->IsValid(), nullptr, "Can't append to a schematic with no root!" );
rootSheet = &aSchematic->Root();
rootSheet = aAppendToMe;
}
else
{
rootSheet = new SCH_SHEET( aSchematic );
rootSheet->SetFileName( aFileName );
aSchematic->SetRoot( rootSheet );
aSchematic->SetTopLevelSheets( { rootSheet } );
}
if( !rootSheet->GetScreen() )
+148 -146
View File
@@ -137,6 +137,8 @@ SCHEMATIC::SCHEMATIC( PROJECT* aPrj ) :
}
}
} );
Reset();
}
@@ -157,11 +159,15 @@ void SCHEMATIC::Reset()
m_rootSheet = nullptr;
m_topLevelSheets.clear();
m_hierarchy.clear();
m_connectionGraph->Reset();
m_currentSheet->clear();
m_busAliases.clear();
ensureVirtualRoot();
ensureDefaultTopLevelSheet();
}
@@ -235,127 +241,139 @@ bool SCHEMATIC::Contains( const SCH_REFERENCE& aRef ) const
}
void SCHEMATIC::SetRoot( SCH_SHEET* aRootSheet )
void SCHEMATIC::ensureVirtualRoot()
{
wxCHECK_RET( aRootSheet, wxS( "Call to SetRoot with null SCH_SHEET!" ) );
// Check if this is a virtual root (has niluuid) or a regular sheet
bool isVirtualRoot = ( aRootSheet->m_Uuid == niluuid );
wxLogTrace( traceSchSheetPaths,
"SetRoot called: sheet='%s', UUID=%s, isVirtualRoot=%d, m_topLevelSheets.size()=%zu",
aRootSheet->GetName(), aRootSheet->m_Uuid.AsString(), isVirtualRoot ? 1 : 0, m_topLevelSheets.size() );
for( size_t i = 0; i < m_topLevelSheets.size(); ++i )
if( m_rootSheet && m_rootSheet->m_Uuid == niluuid )
{
SCH_SHEET* sheet = m_topLevelSheets[i];
wxLogTrace( traceSchSheetPaths, " m_topLevelSheets[%zu]: '%s' (UUID=%s, isVirtualRoot=%d)", i,
sheet ? sheet->GetName() : wxString( "(null)" ),
sheet ? sheet->m_Uuid.AsString() : wxString( "(null)" ),
sheet && sheet->m_Uuid == niluuid ? 1 : 0 );
if( !m_rootSheet->GetScreen() )
m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
return;
}
if( isVirtualRoot )
SCH_SHEET* previousRoot = m_rootSheet;
m_rootSheet = new SCH_SHEET( this );
const_cast<KIID&>( m_rootSheet->m_Uuid ) = niluuid;
m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
if( previousRoot )
{
// aRootSheet is already the virtual root, use it directly
m_rootSheet = aRootSheet;
previousRoot->SetParent( m_rootSheet );
// Ensure virtual root has a screen to hold top-level sheets
// Only create if we have a project (screens need a valid schematic parent)
if( !m_rootSheet->GetScreen() && m_project )
{
m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
}
// Add all existing top-level sheets to the virtual root's screen
if( m_rootSheet->GetScreen() )
{
for( SCH_SHEET* sheet : m_topLevelSheets )
{
if( sheet && sheet->GetParent() != m_rootSheet )
{
sheet->SetParent( m_rootSheet );
}
m_rootSheet->GetScreen()->Append( previousRoot );
// Add to screen if not already there
if( sheet && !m_rootSheet->GetScreen()->Items().contains( sheet ) )
{
m_rootSheet->GetScreen()->Append( sheet );
}
}
}
// Current sheet should point to the first top-level sheet if available
m_currentSheet->clear();
if( !m_topLevelSheets.empty() )
{
m_currentSheet->push_back( m_topLevelSheets[0] );
wxLogTrace( traceSchCurrentSheet,
"SetRoot: Set current sheet to first top-level sheet '%s', path='%s', size=%zu",
m_topLevelSheets[0]->GetName(), m_currentSheet->Path().AsString(), m_currentSheet->size() );
}
else
{
wxLogTrace( traceSchCurrentSheet, "SetRoot: No top-level sheets, current sheet left empty" );
}
// If no top-level sheets, leave current sheet empty - it will be set when sheets are added
}
else
{
// aRootSheet is a regular sheet, create virtual root and make it a child
if( m_rootSheet == nullptr || m_rootSheet->m_Uuid != niluuid )
{
// Need to create a new virtual root
m_rootSheet = new SCH_SHEET( this );
const_cast<KIID&>( m_rootSheet->m_Uuid ) = niluuid;
// Create screen only if we have a project
if( m_project )
{
m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
}
}
else if( m_project && !m_rootSheet->GetScreen() )
{
// Virtual root exists but has no screen - create one now
m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
}
// Add this sheet as a top-level sheet
m_topLevelSheets.clear();
m_topLevelSheets.push_back( aRootSheet );
aRootSheet->SetParent( m_rootSheet );
// Clear the virtual root's screen and add only the new sheet
if( m_rootSheet->GetScreen() )
{
// Don't free the sheet being added if it already exists in the screen
m_rootSheet->GetScreen()->Items().remove( aRootSheet );
m_rootSheet->GetScreen()->Clear();
m_rootSheet->GetScreen()->Append( aRootSheet );
}
m_currentSheet->clear();
m_currentSheet->push_back( aRootSheet );
wxLogTrace( traceSchCurrentSheet, "SetRoot: Set current sheet to root sheet '%s', path='%s', size=%zu",
aRootSheet->GetName(), m_currentSheet->Path().AsString(), m_currentSheet->size() );
m_topLevelSheets.push_back( previousRoot );
}
}
if( m_project )
void SCHEMATIC::ensureDefaultTopLevelSheet()
{
ensureVirtualRoot();
if( !m_topLevelSheets.empty() )
return;
SCH_SHEET* rootSheet = new SCH_SHEET( this );
SCH_SCREEN* rootScreen = new SCH_SCREEN( this );
const_cast<KIID&>( rootSheet->m_Uuid ) = rootScreen->GetUuid();
rootSheet->SetScreen( rootScreen );
SetTopLevelSheets( { rootSheet } );
SCH_SHEET_PATH rootSheetPath;
rootSheetPath.push_back( m_rootSheet );
rootSheetPath.push_back( rootSheet );
rootSheetPath.SetPageNumber( wxT( "1" ) );
}
void SCHEMATIC::ensureCurrentSheetIsTopLevel()
{
if( m_topLevelSheets.empty() )
return;
if( m_currentSheet->empty() || !IsTopLevelSheet( m_currentSheet->at( 0 ) ) )
{
m_hierarchy = BuildSheetListSortedByPageNumbers();
m_currentSheet->clear();
m_currentSheet->push_back( m_topLevelSheets[0] );
}
}
void SCHEMATIC::rebuildHierarchyState( bool aResetConnectionGraph )
{
RefreshHierarchy();
if( aResetConnectionGraph && m_project )
m_connectionGraph->Reset();
// Build screen list from root (which now has a screen)
m_variantNames.clear();
m_variantNames.clear();
if( m_rootSheet && m_rootSheet->GetScreen() )
{
SCH_SCREENS screens( m_rootSheet );
std::set<wxString> variantNames = screens.GetVariantNames();
m_variantNames.insert( variantNames.begin(), variantNames.end() );
}
}
void SCHEMATIC::SetTopLevelSheets( const std::vector<SCH_SHEET*>& aSheets )
{
wxCHECK_RET( !aSheets.empty(), wxS( "Cannot set empty top-level sheets!" ) );
std::vector<SCH_SHEET*> validSheets;
validSheets.reserve( aSheets.size() );
for( SCH_SHEET* sheet : aSheets )
{
if( sheet )
validSheets.push_back( sheet );
}
if( validSheets.empty() )
{
ensureDefaultTopLevelSheet();
return;
}
ensureVirtualRoot();
std::set<SCH_SHEET*> desiredSheets( validSheets.begin(), validSheets.end() );
if( m_rootSheet->GetScreen() )
{
for( SCH_ITEM* item : m_rootSheet->GetScreen()->Items() )
{
SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item );
if( sheet && !desiredSheets.contains( sheet ) )
delete sheet;
}
m_rootSheet->GetScreen()->Clear( false );
}
m_currentSheet->clear();
m_topLevelSheets.clear();
for( SCH_SHEET* sheet : validSheets )
{
sheet->SetParent( m_rootSheet );
if( m_rootSheet->GetScreen() )
{
SCH_SCREENS screens( m_rootSheet );
std::set<wxString> variantNames = screens.GetVariantNames();
m_variantNames.insert( variantNames.begin(), variantNames.end() );
}
m_rootSheet->GetScreen()->Append( sheet );
m_topLevelSheets.push_back( sheet );
}
ensureCurrentSheetIsTopLevel();
rebuildHierarchyState( true );
}
@@ -380,6 +398,7 @@ SCH_SHEET_LIST SCHEMATIC::Hierarchy() const
void SCHEMATIC::RefreshHierarchy()
{
ensureDefaultTopLevelSheet();
m_hierarchy = BuildSheetListSortedByPageNumbers();
}
@@ -1891,10 +1910,7 @@ void SCHEMATIC::CreateDefaultScreens()
{
Reset();
// Create virtual root with niluuid and a screen to hold top-level sheets
SCH_SHEET* virtualRoot = new SCH_SHEET( this );
const_cast<KIID&>( virtualRoot->m_Uuid ) = niluuid;
virtualRoot->SetScreen( new SCH_SCREEN( this ) ); // Virtual root has a screen
ensureVirtualRoot();
// Create the actual first top-level sheet
SCH_SHEET* rootSheet = new SCH_SHEET( this );
@@ -1905,28 +1921,13 @@ void SCHEMATIC::CreateDefaultScreens()
rootScreen->SetFileName( "untitled.kicad_sch" ); // Set default filename to avoid conflicts
rootScreen->SetPageNumber( wxT( "1" ) );
// Set parent to virtual root
rootSheet->SetParent( virtualRoot );
// Add the top-level sheet to the virtual root's screen
virtualRoot->GetScreen()->Append( rootSheet );
// Don't leave root page number empty
SCH_SHEET_PATH rootSheetPath;
rootSheetPath.push_back( virtualRoot );
rootSheetPath.push_back( m_rootSheet );
rootSheetPath.push_back( rootSheet );
rootSheetPath.SetPageNumber( wxT( "1" ) );
// Set up the schematic structure
m_rootSheet = virtualRoot;
m_topLevelSheets.clear();
m_topLevelSheets.push_back( rootSheet );
m_currentSheet->clear();
m_currentSheet->push_back( rootSheet );
// Rehash sheetpaths in hierarchy since we changed the uuid.
RefreshHierarchy();
SetTopLevelSheets( { rootSheet } );
}
@@ -1935,29 +1936,25 @@ std::vector<SCH_SHEET*> SCHEMATIC::GetTopLevelSheets() const
return m_topLevelSheets;
}
SCH_SHEET* SCHEMATIC::GetTopLevelSheet( int aIndex ) const
{
if( aIndex < 0 )
return nullptr;
size_t index = static_cast<size_t>( aIndex );
if( index >= m_topLevelSheets.size() )
return nullptr;
return m_topLevelSheets[index];
}
void SCHEMATIC::AddTopLevelSheet( SCH_SHEET* aSheet )
{
wxCHECK_RET( aSheet, wxS( "Cannot add null sheet!" ) );
wxCHECK_RET( aSheet->GetScreen(), wxS( "Cannot add virtual root as top-level sheet!" ) );
// Make sure we have a virtual root
if( m_rootSheet == nullptr )
{
m_rootSheet = new SCH_SHEET( this );
const_cast<KIID&>( m_rootSheet->m_Uuid ) = niluuid;
// Create screen only if we have a project
if( m_project )
{
m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
}
}
// Ensure virtual root has a screen (may have been created before project was set)
if( !m_rootSheet->GetScreen() && m_project )
{
m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
}
ensureVirtualRoot();
// Set parent to virtual root
aSheet->SetParent( m_rootSheet );
@@ -1971,11 +1968,10 @@ void SCHEMATIC::AddTopLevelSheet( SCH_SHEET* aSheet )
// Add to our list
m_topLevelSheets.push_back( aSheet );
// Refresh hierarchy
RefreshHierarchy();
ensureCurrentSheetIsTopLevel();
rebuildHierarchyState( true );
}
bool SCHEMATIC::RemoveTopLevelSheet( SCH_SHEET* aSheet )
{
auto it = std::find( m_topLevelSheets.begin(), m_topLevelSheets.end(), aSheet );
@@ -1983,8 +1979,14 @@ bool SCHEMATIC::RemoveTopLevelSheet( SCH_SHEET* aSheet )
if( it == m_topLevelSheets.end() )
return false;
if( m_topLevelSheets.size() == 1 )
return false;
m_topLevelSheets.erase( it );
if( m_rootSheet && m_rootSheet->GetScreen() )
m_rootSheet->GetScreen()->Items().remove( aSheet );
// If we're removing the current sheet, switch to another one
if( !m_currentSheet->empty() && m_currentSheet->at( 0 ) == aSheet )
{
@@ -1995,7 +1997,7 @@ bool SCHEMATIC::RemoveTopLevelSheet( SCH_SHEET* aSheet )
}
}
RefreshHierarchy();
rebuildHierarchyState( true );
return true;
}
+9 -10
View File
@@ -127,16 +127,6 @@ public:
return *m_rootSheet;
}
/**
* Initialize the schematic with a new root sheet.
*
* This is typically done by calling a file loader that returns the new root sheet
* As a side-effect, takes care of some post-load initialization.
*
* @param aRootSheet is the new root sheet for this schematic.
*/
void SetRoot( SCH_SHEET* aRootSheet );
/**
* Get the list of top-level sheets.
*
@@ -144,6 +134,10 @@ public:
*/
std::vector<SCH_SHEET*> GetTopLevelSheets() const;
SCH_SHEET* GetTopLevelSheet( int aIndex = 0 ) const;
void SetTopLevelSheets( const std::vector<SCH_SHEET*>& aSheets );
/**
* Add a new top-level sheet to the schematic.
*
@@ -497,6 +491,11 @@ private:
( l->*aFunc )( std::forward<Args>( args )... );
}
void ensureVirtualRoot();
void ensureDefaultTopLevelSheet();
void ensureCurrentSheetIsTopLevel();
void rebuildHierarchyState( bool aResetConnectionGraph );
PROJECT* m_project;
/// The virtual root sheet (has no screen, contains all top-level sheets)
+8 -1
View File
@@ -67,7 +67,14 @@ void KI_TEST::SCHEMATIC_TEST_FIXTURE::LoadSchematic( const wxFileName& aFn )
m_manager.Prj().SetElem( PROJECT::ELEM::LEGACY_SYMBOL_LIBS, nullptr );
m_schematic = std::make_unique<SCHEMATIC>( &m_manager.Prj() );
m_schematic->SetRoot( m_pi->LoadSchematicFile( fn.GetFullPath(), m_schematic.get() ) );
m_schematic->Reset();
SCH_SHEET* defaultSheet = m_schematic->GetTopLevelSheet( 0 );
SCH_SHEET* root = m_pi->LoadSchematicFile( fn.GetFullPath(), m_schematic.get() );
m_schematic->AddTopLevelSheet( root );
m_schematic->RemoveTopLevelSheet( defaultSheet );
delete defaultSheet;
BOOST_REQUIRE_EQUAL( m_pi->GetError().IsEmpty(), true );
+6 -1
View File
@@ -109,9 +109,14 @@ std::unique_ptr<SCHEMATIC> LoadHierarchyFromRoot( const std::string& rootFilenam
std::unordered_map<std::string, SCH_SCREEN*> parsedScreens;
schematic->SetProject( project );
schematic->Reset();
SCH_SHEET* defaultSheet = schematic->GetTopLevelSheet( 0 );
SCH_SHEET* rootSheet = new SCH_SHEET( schematic.get() );
LoadHierarchy( schematic.get(), rootSheet, rootFilename, parsedScreens );
schematic->SetRoot( rootSheet ); // Call SetRoot AFTER loading so sheet has a screen
schematic->AddTopLevelSheet( rootSheet );
schematic->RemoveTopLevelSheet( defaultSheet );
delete defaultSheet;
return schematic;
}
@@ -41,10 +41,15 @@ struct BUS_ENTRY_CONCURRENCY_FIXTURE
{
m_mgr.LoadProject( "" );
m_schematic = std::make_unique<SCHEMATIC>( &m_mgr.Prj() );
m_schematic->Reset();
SCH_SHEET* defaultSheet = m_schematic->GetTopLevelSheet( 0 );
m_screen = new SCH_SCREEN( m_schematic.get() );
m_sheet = new SCH_SHEET( m_schematic.get() );
m_sheet->SetScreen( m_screen );
m_schematic->SetRoot( m_sheet );
m_schematic->AddTopLevelSheet( m_sheet );
m_schematic->RemoveTopLevelSheet( defaultSheet );
delete defaultSheet;
}
SETTINGS_MANAGER m_mgr;
+5 -1
View File
@@ -17,11 +17,15 @@ BOOST_AUTO_TEST_CASE( LabelDrivesCrossingWires )
manager.LoadProject( "" );
SCHEMATIC schematic( &manager.Prj() );
schematic.Reset();
SCH_SHEET* defaultSheet = schematic.GetTopLevelSheet( 0 );
SCH_SCREEN* screen = new SCH_SCREEN( nullptr );
SCH_SHEET* sheet = new SCH_SHEET( nullptr, VECTOR2I( 0, 0 ), VECTOR2I( 1000, 1000 ) );
sheet->SetScreen( screen );
schematic.SetRoot( sheet );
schematic.AddTopLevelSheet( sheet );
schematic.RemoveTopLevelSheet( defaultSheet );
delete defaultSheet;
CONNECTION_GRAPH graph;
graph.SetSchematic( &schematic );
+5 -1
View File
@@ -79,7 +79,11 @@ BOOST_AUTO_TEST_CASE( TestLoadWithLogging )
BOOST_REQUIRE( loadedSheet != nullptr );
BOOST_TEST_MESSAGE( "=== Setting root sheet" );
schematic->SetRoot( loadedSheet );
schematic->Reset();
SCH_SHEET* defaultSheet = schematic->GetTopLevelSheet( 0 );
schematic->AddTopLevelSheet( loadedSheet );
schematic->RemoveTopLevelSheet( defaultSheet );
delete defaultSheet;
BOOST_TEST_MESSAGE( "=== Building hierarchy" );
schematic->RefreshHierarchy();
@@ -31,6 +31,20 @@
#include <settings/settings_manager.h>
#include <pgm_base.h>
static SCH_SHEET* createTopLevelSheet( SCHEMATIC& aSchematic, const wxString& aName,
const wxString& aFileName )
{
SCH_SHEET* sheet = new SCH_SHEET( &aSchematic );
SCH_SCREEN* screen = new SCH_SCREEN( &aSchematic );
const_cast<KIID&>( sheet->m_Uuid ) = screen->GetUuid();
sheet->SetScreen( screen );
sheet->SetName( aName );
sheet->SetFileName( aFileName );
return sheet;
}
BOOST_AUTO_TEST_SUITE( MultiTopLevelSheets )
BOOST_AUTO_TEST_CASE( TestVirtualRootCreation )
@@ -49,50 +63,30 @@ BOOST_AUTO_TEST_CASE( TestAddTopLevelSheet )
{
SCHEMATIC schematic( nullptr );
// Create a virtual root
SCH_SHEET* virtualRoot = new SCH_SHEET();
const_cast<KIID&>( virtualRoot->m_Uuid ) = niluuid;
virtualRoot->SetScreen( nullptr );
virtualRoot->SetParent( &schematic );
schematic.SetRoot( virtualRoot );
SCH_SHEET* sheet1 = createTopLevelSheet( schematic, "Sheet1", "sheet1.kicad_sch" );
// Create and add a top-level sheet
SCH_SHEET* sheet1 = new SCH_SHEET();
sheet1->SetName( "Sheet1" );
SCH_SCREEN* screen1 = new SCH_SCREEN();
screen1->SetParent( &schematic );
sheet1->SetScreen( screen1 );
sheet1->SetParent( &schematic );
schematic.AddTopLevelSheet( sheet1 );
schematic.SetTopLevelSheets( { sheet1 } );
// Verify the sheet was added
const std::vector<SCH_SHEET*>& topSheets = schematic.GetTopLevelSheets();
BOOST_CHECK_EQUAL( topSheets.size(), 1 );
BOOST_CHECK_EQUAL( topSheets[0], sheet1 );
BOOST_CHECK_EQUAL( topSheets[0]->GetName(), "Sheet1" );
BOOST_CHECK_EQUAL( schematic.Hierarchy().size(), 1 );
}
BOOST_AUTO_TEST_CASE( TestAddMultipleTopLevelSheets )
{
SCHEMATIC schematic( nullptr );
// Create a virtual root
SCH_SHEET* virtualRoot = new SCH_SHEET();
const_cast<KIID&>( virtualRoot->m_Uuid ) = niluuid;
virtualRoot->SetScreen( nullptr );
virtualRoot->SetParent( &schematic );
schematic.SetRoot( virtualRoot );
SCH_SHEET* sheet1 = createTopLevelSheet( schematic, "Sheet1", "sheet1.kicad_sch" );
schematic.SetTopLevelSheets( { sheet1 } );
// Create and add multiple top-level sheets
for( int i = 1; i <= 3; i++ )
for( int i = 2; i <= 3; i++ )
{
SCH_SHEET* sheet = new SCH_SHEET();
sheet->SetName( wxString::Format( "Sheet%d", i ) );
SCH_SCREEN* screen = new SCH_SCREEN();
screen->SetParent( &schematic );
sheet->SetScreen( screen );
sheet->SetParent( &schematic );
SCH_SHEET* sheet = createTopLevelSheet( schematic, wxString::Format( "Sheet%d", i ),
wxString::Format( "sheet%d.kicad_sch", i ) );
schematic.AddTopLevelSheet( sheet );
}
@@ -102,34 +96,17 @@ BOOST_AUTO_TEST_CASE( TestAddMultipleTopLevelSheets )
BOOST_CHECK_EQUAL( topSheets[0]->GetName(), "Sheet1" );
BOOST_CHECK_EQUAL( topSheets[1]->GetName(), "Sheet2" );
BOOST_CHECK_EQUAL( topSheets[2]->GetName(), "Sheet3" );
BOOST_CHECK_EQUAL( schematic.Hierarchy().size(), 3 );
}
BOOST_AUTO_TEST_CASE( TestRemoveTopLevelSheet )
{
SCHEMATIC schematic( nullptr );
// Create a virtual root
SCH_SHEET* virtualRoot = new SCH_SHEET();
const_cast<KIID&>( virtualRoot->m_Uuid ) = niluuid;
virtualRoot->SetScreen( nullptr );
virtualRoot->SetParent( &schematic );
schematic.SetRoot( virtualRoot );
SCH_SHEET* sheet1 = createTopLevelSheet( schematic, "Sheet1", "sheet1.kicad_sch" );
schematic.SetTopLevelSheets( { sheet1 } );
// Create and add sheets
SCH_SHEET* sheet1 = new SCH_SHEET();
sheet1->SetName( "Sheet1" );
SCH_SCREEN* screen1 = new SCH_SCREEN();
screen1->SetParent( &schematic );
sheet1->SetScreen( screen1 );
sheet1->SetParent( &schematic );
schematic.AddTopLevelSheet( sheet1 );
SCH_SHEET* sheet2 = new SCH_SHEET();
sheet2->SetName( "Sheet2" );
SCH_SCREEN* screen2 = new SCH_SCREEN();
screen2->SetParent( &schematic );
sheet2->SetScreen( screen2 );
sheet2->SetParent( &schematic );
SCH_SHEET* sheet2 = createTopLevelSheet( schematic, "Sheet2", "sheet2.kicad_sch" );
schematic.AddTopLevelSheet( sheet2 );
// Remove first sheet
@@ -140,51 +117,62 @@ BOOST_AUTO_TEST_CASE( TestRemoveTopLevelSheet )
BOOST_CHECK_EQUAL( topSheets.size(), 1 );
BOOST_CHECK_EQUAL( topSheets[0], sheet2 );
BOOST_CHECK_EQUAL( topSheets[0]->GetName(), "Sheet2" );
BOOST_CHECK_EQUAL( schematic.Hierarchy().size(), 1 );
// Cannot remove the final remaining sheet
BOOST_CHECK( !schematic.RemoveTopLevelSheet( topSheets[0] ) );
}
BOOST_AUTO_TEST_CASE( TestBuildSheetListWithMultipleRoots )
{
SCHEMATIC schematic( nullptr );
// Create a virtual root
SCH_SHEET* virtualRoot = new SCH_SHEET();
const_cast<KIID&>( virtualRoot->m_Uuid ) = niluuid;
virtualRoot->SetScreen( nullptr );
virtualRoot->SetParent( &schematic );
schematic.SetRoot( virtualRoot );
SCH_SHEET* top1 = createTopLevelSheet( schematic, "TopSheet1", "top_sheet_1.kicad_sch" );
SCH_SHEET* top2 = createTopLevelSheet( schematic, "TopSheet2", "top_sheet_2.kicad_sch" );
// Create two top-level sheets, each with one child
for( int i = 1; i <= 2; i++ )
{
SCH_SHEET* topSheet = new SCH_SHEET();
topSheet->SetName( wxString::Format( "TopSheet%d", i ) );
topSheet->SetFileName( wxString::Format( "top_sheet_%d.kicad_sch", i ) );
SCH_SCREEN* topScreen = new SCH_SCREEN();
topScreen->SetParent( &schematic );
topSheet->SetScreen( topScreen );
topSheet->SetParent( &schematic );
SCH_SHEET* child1 = createTopLevelSheet( schematic, "ChildSheet1", "child_sheet_1.kicad_sch" );
child1->SetParent( top1 );
top1->GetScreen()->Append( child1 );
// Add a child sheet
SCH_SHEET* childSheet = new SCH_SHEET();
childSheet->SetName( wxString::Format( "ChildSheet%d", i ) );
childSheet->SetFileName( wxString::Format( "child_sheet_%d.kicad_sch", i ) );
SCH_SCREEN* childScreen = new SCH_SCREEN();
childScreen->SetParent( &schematic );
childSheet->SetScreen( childScreen );
childSheet->SetParent( &schematic );
topScreen->Append( childSheet );
SCH_SHEET* child2 = createTopLevelSheet( schematic, "ChildSheet2", "child_sheet_2.kicad_sch" );
child2->SetParent( top2 );
top2->GetScreen()->Append( child2 );
schematic.AddTopLevelSheet( topSheet );
}
schematic.SetTopLevelSheets( { top1, top2 } );
// Build sheet list
schematic.RefreshHierarchy();
SCH_SHEET_LIST sheetList = schematic.Hierarchy();
// Should have 4 sheet paths: 2 top-level + 2 children
BOOST_CHECK_EQUAL( sheetList.size(), 4 );
}
BOOST_AUTO_TEST_CASE( TestHierarchyUpdatesOnSheetOperations )
{
SCHEMATIC schematic( nullptr );
SCH_SHEET* baseSheet = createTopLevelSheet( schematic, "Base", "base.kicad_sch" );
schematic.SetTopLevelSheets( { baseSheet } );
BOOST_CHECK_EQUAL( schematic.Hierarchy().size(), 1 );
SCH_SHEET* copiedSheet = createTopLevelSheet( schematic, "Copy", "copy.kicad_sch" );
schematic.AddTopLevelSheet( copiedSheet );
BOOST_CHECK_EQUAL( schematic.Hierarchy().size(), 2 );
// Move the copied sheet ahead of the base sheet
schematic.SetTopLevelSheets( { copiedSheet, baseSheet } );
BOOST_CHECK_EQUAL( schematic.GetTopLevelSheet()->GetName(), "Copy" );
BOOST_CHECK_EQUAL( schematic.Hierarchy().size(), 2 );
// Removing one sheet should keep hierarchy valid
BOOST_CHECK( schematic.RemoveTopLevelSheet( baseSheet ) );
BOOST_CHECK_EQUAL( schematic.Hierarchy().size(), 1 );
BOOST_CHECK_EQUAL( schematic.GetTopLevelSheet()->GetName(), "Copy" );
}
BOOST_AUTO_TEST_CASE( TestTopLevelSheetInfoSerialization )
{
TOP_LEVEL_SHEET_INFO info1( KIID(), "TestSheet", "test_sheet.kicad_sch" );
@@ -43,11 +43,15 @@ struct NC_PIN_CONNECTIVITY_FIXTURE
m_settingsManager.LoadProject( "" );
m_schematic = std::make_unique<SCHEMATIC>( &m_settingsManager.Prj() );
m_schematic->Reset();
SCH_SHEET* defaultSheet = m_schematic->GetTopLevelSheet( 0 );
SCH_SHEET* root = new SCH_SHEET( m_schematic.get() );
m_schematic->SetRoot( root );
SCH_SCREEN* screen = new SCH_SCREEN( m_schematic.get() );
root->SetScreen( screen );
m_schematic->AddTopLevelSheet( root );
m_schematic->RemoveTopLevelSheet( defaultSheet );
delete defaultSheet;
}
SETTINGS_MANAGER m_settingsManager;
+13 -3
View File
@@ -130,6 +130,7 @@ BOOST_AUTO_TEST_CASE( TestSaveLoadSimpleSchematic )
// Now try to load it back
m_schematic->Reset();
SCH_SHEET* defaultSheet = m_schematic->GetTopLevelSheet( 0 );
SCH_SHEET* loadedSheet = nullptr;
BOOST_CHECK_NO_THROW( loadedSheet = io.LoadSchematicFile( fileName, m_schematic.get() ) );
BOOST_CHECK( loadedSheet != nullptr );
@@ -137,7 +138,9 @@ BOOST_AUTO_TEST_CASE( TestSaveLoadSimpleSchematic )
if( loadedSheet )
{
// Set it as root (which should create virtual root and make it a top-level sheet)
m_schematic->SetRoot( loadedSheet );
m_schematic->AddTopLevelSheet( loadedSheet );
m_schematic->RemoveTopLevelSheet( defaultSheet );
delete defaultSheet;
// Verify we can access the schematic
BOOST_CHECK( m_schematic->IsValid() );
@@ -208,13 +211,16 @@ BOOST_AUTO_TEST_CASE( TestSaveLoadHierarchicalSchematic )
// Load it back
m_schematic->Reset();
SCH_SHEET* defaultSheet = m_schematic->GetTopLevelSheet( 0 );
SCH_SHEET* loadedSheet = nullptr;
BOOST_CHECK_NO_THROW( loadedSheet = io.LoadSchematicFile( mainFileName, m_schematic.get() ) );
BOOST_CHECK( loadedSheet != nullptr );
if( loadedSheet )
{
m_schematic->SetRoot( loadedSheet );
m_schematic->AddTopLevelSheet( loadedSheet );
m_schematic->RemoveTopLevelSheet( defaultSheet );
delete defaultSheet;
// Build hierarchy
m_schematic->RefreshHierarchy();
@@ -267,7 +273,11 @@ BOOST_AUTO_TEST_CASE( TestLoadLegacyHierarchicalSchematic )
BOOST_TEST_MESSAGE( "Loaded sheet UUID: " + loadedSheet->m_Uuid.AsString().ToStdString() );
BOOST_TEST_MESSAGE( "Loaded sheet name: " + loadedSheet->GetName().ToStdString() );
m_schematic->SetRoot( loadedSheet );
m_schematic->Reset();
SCH_SHEET* defaultSheet = m_schematic->GetTopLevelSheet( 0 );
m_schematic->AddTopLevelSheet( loadedSheet );
m_schematic->RemoveTopLevelSheet( defaultSheet );
delete defaultSheet;
// Build the hierarchy
m_schematic->RefreshHierarchy();
+3 -6
View File
@@ -59,13 +59,10 @@ public:
m_manager.LoadProject( "" );
m_schematic = std::make_unique<SCHEMATIC>( &m_manager.Prj() );
m_schematic->Reset();
m_screen = new SCH_SCREEN( m_schematic.get() );
m_sheet = new SCH_SHEET( m_schematic.get() );
m_sheet->SetScreen( m_screen );
m_schematic->SetRoot( m_sheet );
m_sheet = m_schematic->GetTopLevelSheet( 0 );
m_screen = m_sheet->GetScreen();
m_parent_part = new LIB_SYMBOL( "parent_part", nullptr );
+7 -2
View File
@@ -31,6 +31,7 @@
// Code under test
#include <sch_sheet.h>
#include <sch_sheet_pin.h>
#include <sch_screen.h>
#include <schematic.h>
#include <qa_utils/uuid_test_utils.h>
@@ -98,12 +99,16 @@ BOOST_AUTO_TEST_CASE( SchematicParent )
BOOST_CHECK_EQUAL( m_sheet.IsVirtualRootSheet(), false );
BOOST_CHECK_EQUAL( m_sheet.IsTopLevelSheet(), false ); // Not yet a top-level sheet
m_schematic.SetRoot( &m_sheet );
SCH_SCREEN* screen = new SCH_SCREEN( &m_schematic );
m_sheet.SetScreen( screen );
m_schematic.AddTopLevelSheet( &m_sheet );
// After SetRoot, the sheet becomes a top-level sheet under the virtual root
// After AddTopLevelSheet, the sheet becomes a top-level sheet under the virtual root
BOOST_CHECK_EQUAL( m_sheet.IsVirtualRootSheet(), false ); // Sheet is not the virtual root
BOOST_CHECK_EQUAL( m_sheet.IsTopLevelSheet(), true ); // Sheet is now a top-level sheet
BOOST_CHECK_EQUAL( m_schematic.Root().IsVirtualRootSheet(), true ); // The root is virtual
m_schematic.RemoveTopLevelSheet( &m_sheet );
}
/**
@@ -192,12 +192,17 @@ BOOST_AUTO_TEST_CASE( DerivedSymbolEmbeddedFiles )
{
SCHEMATIC schematic( nullptr );
SCH_SHEET* rootSheet = new SCH_SHEET( &schematic );
schematic.SetRoot( rootSheet );
schematic.Reset();
SCH_SHEET* defaultSheet = schematic.GetTopLevelSheet( 0 );
SCH_SHEET* rootSheet = new SCH_SHEET( &schematic );
SCH_SCREEN* screen = new SCH_SCREEN( &schematic );
rootSheet->SetScreen( screen );
schematic.AddTopLevelSheet( rootSheet );
schematic.RemoveTopLevelSheet( defaultSheet );
delete defaultSheet;
IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
LIB_SYMBOL* loadedParent = plugin->LoadSymbol( GetLibPath(), wxS( "ParentSymbol" ) );
LIB_SYMBOL* loadedDerived = plugin->LoadSymbol( GetLibPath(), wxS( "DerivedSymbol" ) );
@@ -206,6 +211,7 @@ BOOST_AUTO_TEST_CASE( DerivedSymbolEmbeddedFiles )
SCH_SHEET_PATH rootPath;
rootPath.push_back( &schematic.Root() );
rootPath.push_back( rootSheet );
SCH_SYMBOL* schSymbol = new SCH_SYMBOL( *loadedDerived, LIB_ID( "test_lib", "DerivedSymbol" ),
&rootPath, 0 );