Eeschema: make schematic sharing truly safe across all designs.

There has been a long standing (since the beginning of the project?)
issue with sharing schematics between projects.  It has been somewhat
supported for complex hierarchies (a sheet shared multiple times in a
single design) but it has not been well supported for simple hierarchies
(the symbol references cannot be changed in the shared schematic).  This
issue has been resolved by moving all of the symbol instance sheet paths
from the symbol definitions in the all of the project files and save all
symbol path instances in the root sheet.  This ensures that orphaned
symbol instance paths do not accumulate in shared schematic files and
that designs that reuse schematic in simple hierarchies can how have
different references.  It also allows the root schematic from one project
to be uses as a sub-sheet in another project.

When legacy schematics are loaded, all sheet and symbol UUIDs are
converted from time stamps to true UUIDs.  This is done to ensure there
are no sheet path instance clashes between projects.  That being said,
there are no checks for this.  It is assumed that the probability of
UUID clashes is so low that it doesn't make sense to test for them.
This commit is contained in:
Wayne Stambaugh 2020-04-26 16:53:29 -04:00
parent 7183e9f97e
commit 169f63a6c0
29 changed files with 589 additions and 252 deletions

View File

@ -57,18 +57,6 @@ KIID::KIID() :
m_uuid( randomGenerator() ),
m_cached_timestamp( 0 )
{
#if defined(EESCHEMA)
// JEY TODO: use legacy timestamps until new EEschema file format is in
static timestamp_t oldTimeStamp;
timestamp_t newTimeStamp = time( NULL );
if( newTimeStamp <= oldTimeStamp )
newTimeStamp = oldTimeStamp + 1;
oldTimeStamp = newTimeStamp;
*this = KIID( wxString::Format( "%8.8X", newTimeStamp ) );
#endif
}
@ -178,6 +166,16 @@ wxString KIID::AsLegacyTimestampString() const
}
void KIID::ConvertTimestampToUuid()
{
if( !IsLegacyTimestamp() )
return;
m_cached_timestamp = 0;
m_uuid = randomGenerator();
}
/**
* Global variables definitions.
*

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2018 Wayne Stambaugh <stambaughw@gmail.com>
* Copyright (C) 2018-2019 KiCad Developers, see change_log.txt for contributors.
* Copyright (C) 2018-2020 KiCad Developers, see change_log.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
@ -46,7 +46,8 @@ const wxChar* const traceLocale = wxT( "KICAD_LOCALE" );
const wxChar* const traceScreen = wxT( "KICAD_SCREEN" );
const wxChar* const traceZoomScroll = wxT( "KICAD_ZOOM_SCROLL" );
const wxChar* const traceSymbolResolver = wxT( "KICAD_SYM_RESOLVE" );
const wxChar* const traceDisplayLocation = wxT( "KICAD_DISPLAY_LOCATION");
const wxChar* const traceDisplayLocation = wxT( "KICAD_DISPLAY_LOCATION" );
const wxChar* const traceSchSheetPaths = wxT( "KICAD_SCH_SHEET_PATHS" );
wxString dump( const wxArrayString& aArray )

View File

@ -35,6 +35,7 @@
#include <bitmaps.h>
#include <eeschema_settings.h>
#include <settings/color_settings.h>
#include <trace_helpers.h>
#include "panel_eeschema_color_settings.h"
DIALOG_SCH_SHEET_PROPS::DIALOG_SCH_SHEET_PROPS( SCH_EDIT_FRAME* aParent, SCH_SHEET* aSheet,
@ -244,9 +245,11 @@ bool DIALOG_SCH_SHEET_PROPS::TransferDataFromWindow()
// Ensure filepath is not empty. (In normal use will be caught by grid validators,
// but unedited data from existing files can be bad.)
// @todo What happens when there are invalid file name characters?
if( newRelativeNativeFilename.IsEmpty() )
{
wxMessageBox( _( "A sheet must have a file specified." ) );
wxMessageBox( _( "A sheet must have a valid file name." ) );
return false;
}
@ -254,9 +257,9 @@ bool DIALOG_SCH_SHEET_PROPS::TransferDataFromWindow()
// validators, but unedited data from existing files can be bad.)
wxFileName fn( newRelativeNativeFilename );
if( fn.GetExt() != "sch" )
if( fn.GetExt().CmpNoCase( KiCadSchematicFileExtension ) != 0 )
{
wxMessageBox( _( "Sheet file must have a '.sch' extension." ) );
wxMessageBox( _( "Sheet file must have a '.kicad_sch' extension." ) );
return false;
}
@ -331,10 +334,26 @@ bool DIALOG_SCH_SHEET_PROPS::TransferDataFromWindow()
bool DIALOG_SCH_SHEET_PROPS::onSheetFilenameChanged( const wxString& aNewFilename )
{
wxString msg;
// Relative file names are relative to the path of the current sheet. This allows for
// nesting of schematic files in subfolders.
wxFileName fileName( aNewFilename );
if( fileName.GetExt().IsEmpty() )
{
fileName.SetExt( KiCadSchematicFileExtension );
}
else if( fileName.GetExt().CmpNoCase( KiCadSchematicFileExtension ) != 0 )
{
msg.Printf( _( "The file \"%s\" does not appear to be a valid schematic file." ),
fileName.GetFullName() );
wxMessageDialog badSchFileDialog( this, msg, _( "Invalid Schematic File" ),
wxOK | wxCENTRE | wxICON_EXCLAMATION );
badSchFileDialog.ShowModal();
return false;
}
if( !fileName.IsAbsolute() )
{
const SCH_SCREEN* currentScreen = g_CurrentSheet->LastScreen();
@ -354,7 +373,6 @@ bool DIALOG_SCH_SHEET_PROPS::onSheetFilenameChanged( const wxString& aNewFilenam
// Inside Eeschema, filenames are stored using unix notation
newAbsoluteFilename.Replace( wxT( "\\" ), wxT( "/" ) );
wxString msg;
bool renameFile = false;
bool loadFromFile = false;
bool clearAnnotation = false;
@ -367,7 +385,8 @@ bool DIALOG_SCH_SHEET_PROPS::onSheetFilenameChanged( const wxString& aNewFilenam
if( !g_RootSheet->SearchHierarchy( newAbsoluteFilename, &useScreen ) )
{
loadFromFile = wxFileExists( newAbsoluteFilename );
wxLogDebug( "Sheet requested file \"%s\", %s",
wxLogTrace( tracePathsAndFiles, "Sheet requested file \"%s\", %s",
newAbsoluteFilename,
( loadFromFile ) ? "found" : "not found" );
}
@ -465,7 +484,7 @@ bool DIALOG_SCH_SHEET_PROPS::onSheetFilenameChanged( const wxString& aNewFilenam
if( renameFile )
{
SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
// If the the associated screen is shared by more than one sheet, do not
// change the filename of the corresponding screen here.
@ -476,11 +495,12 @@ bool DIALOG_SCH_SHEET_PROPS::onSheetFilenameChanged( const wxString& aNewFilenam
try
{
pi->Save( newAbsoluteFilename, m_sheet->GetScreen(), &Kiway() );
pi->Save( newAbsoluteFilename, m_sheet, &Kiway() );
}
catch( const IO_ERROR& ioe )
{
msg.Printf( _( "Error occurred saving schematic file \"%s\"." ), newAbsoluteFilename );
msg.Printf( _( "Error occurred saving schematic file \"%s\"." ),
newAbsoluteFilename );
DisplayErrorMessage( this, msg, ioe.What() );
msg.Printf( _( "Failed to save schematic \"%s\"" ), newAbsoluteFilename );
@ -569,17 +589,22 @@ void DIALOG_SCH_SHEET_PROPS::OnGridCellChanging( wxGridEvent& event )
success = false;
}
}
else if( event.GetRow() == SHEETFILENAME && event.GetCol() == FDC_VALUE )
else if( event.GetRow() == SHEETFILENAME && event.GetCol() == FDC_VALUE && textControl )
{
if( textControl && textControl->IsEmpty() )
if( textControl->IsEmpty() )
{
wxMessageBox( _( "A sheet must have a file specified." ) );
success = false;
}
else if( textControl && !textControl->GetValue().EndsWith( wxT( ".sch" ) ) )
else
{
wxMessageBox( _( "Sheet filename must have a '.sch' extension." ) );
success = false;
wxFileName fn = textControl->GetValue();
if( fn.GetExt().CmpNoCase( KiCadSchematicFileExtension ) != 0 )
{
wxMessageBox( _( "Sheet filename must have a '.sch' extension." ) );
success = false;
}
}
}

View File

@ -30,6 +30,7 @@
#include <sch_bus_entry.h>
#include <sch_component.h>
#include <sch_line.h>
#include <sch_screen.h>
#include <sch_sheet_path.h>
#include <transform.h>
#include "sch_reference_list.h"

View File

@ -131,7 +131,7 @@ void FIELDS_GRID_TABLE<T>::initGrid( DIALOG_SHIM* aDialog )
// Create a wild card using wxFileDialog syntax.
wxString wildCard( _( "Schematic Files" ) );
std::vector<std::string> exts;
exts.push_back( LegacySchematicFileExtension );
exts.push_back( KiCadSchematicFileExtension );
wildCard += AddFileExtListToFilter( exts );
GRID_CELL_PATH_EDITOR* filepathEditor =

View File

@ -55,22 +55,26 @@
#include <netlist.h>
bool SCH_EDIT_FRAME::SaveEEFile( SCH_SCREEN* aScreen, bool aSaveUnderNewName,
bool SCH_EDIT_FRAME::SaveEEFile( SCH_SHEET* aSheet, bool aSaveUnderNewName,
bool aCreateBackupFile )
{
wxString msg;
wxFileName schematicFileName;
bool success;
if( aScreen == NULL )
aScreen = GetScreen();
if( aSheet == NULL )
aSheet = GetCurrentSheet().Last();
SCH_SCREEN* screen = aSheet->GetScreen();
wxCHECK( screen, false );
// If no name exists in the window yet - save as new.
if( aScreen->GetFileName().IsEmpty() )
if( screen->GetFileName().IsEmpty() )
aSaveUnderNewName = true;
// Construct the name of the file to be saved
schematicFileName = Prj().AbsolutePath( aScreen->GetFileName() );
schematicFileName = Prj().AbsolutePath( screen->GetFileName() );
if( aSaveUnderNewName )
{
@ -127,7 +131,7 @@ bool SCH_EDIT_FRAME::SaveEEFile( SCH_SCREEN* aScreen, bool aSaveUnderNewName,
try
{
pi->Save( schematicFileName.GetFullPath(), aScreen, &Kiway() );
pi->Save( schematicFileName.GetFullPath(), aSheet, &Kiway() );
success = true;
}
catch( const IO_ERROR& ioe )
@ -160,14 +164,14 @@ bool SCH_EDIT_FRAME::SaveEEFile( SCH_SCREEN* aScreen, bool aSaveUnderNewName,
// Update the screen and frame info and reset the lock file.
if( aSaveUnderNewName )
{
aScreen->SetFileName( schematicFileName.GetFullPath() );
screen->SetFileName( schematicFileName.GetFullPath() );
LockFile( schematicFileName.GetFullPath() );
}
aScreen->ClrSave();
aScreen->ClrModify();
screen->ClrSave();
screen->ClrModify();
msg.Printf( _( "File %s saved" ), aScreen->GetFileName() );
msg.Printf( _( "File %s saved" ), screen->GetFileName() );
SetStatusText( msg, 0 );
}
else
@ -428,6 +432,31 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
// Update all symbol library links for all sheets.
schematic.UpdateSymbolLinks();
// Replace sheet and symbol time stamps with real UUIDs and update symbol instance
// sheet paths using the new UUID based sheet paths.
// Save the time stamp sheet paths.
SCH_SHEET_LIST timeStampSheetPaths( g_RootSheet );
std::vector<KIID_PATH> oldSheetPaths = timeStampSheetPaths.GetPaths();
// The root sheet now gets a permanent UUID.
const_cast<KIID&>( g_RootSheet->m_Uuid ).ConvertTimestampToUuid();
// Change the sheet and symbol time stamps to UUIDs.
for( SCH_SCREEN* screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )
{
for( auto sheet : screen->Items().OfType( SCH_SHEET_T ) )
const_cast<KIID&>( sheet->m_Uuid ).ConvertTimestampToUuid();
for( auto symbol : screen->Items().OfType( SCH_COMPONENT_T ) )
const_cast<KIID&>( symbol->m_Uuid ).ConvertTimestampToUuid();
}
SCH_SHEET_LIST uuidSheetPaths( g_RootSheet );
timeStampSheetPaths.ReplaceLegacySheetPaths( oldSheetPaths );
if( !cfg || cfg->m_Appearance.show_sexpr_file_convert_warning )
{
wxRichMessageDialog newFileFormatDlg(
@ -452,6 +481,9 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
{
for( SCH_SCREEN* screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )
screen->UpdateLocalLibSymbolLinks();
// Restore all of the loaded symbol instances from the root sheet screen.
sheetList.UpdateSymbolInstances( g_RootSheet->GetScreen()->m_symbolInstances );
}
g_ConnectionGraph->Reset();
@ -588,8 +620,9 @@ void SCH_EDIT_FRAME::OnImportProject( wxCommandEvent& aEvent )
bool SCH_EDIT_FRAME::SaveProject()
{
wxString msg;
SCH_SCREEN* screen;
SCH_SCREENS screenList;
SCH_SCREENS screens;
bool success = true;
bool updateFileType = false;
@ -599,13 +632,63 @@ bool SCH_EDIT_FRAME::SaveProject()
if( !fn.IsDirWritable() )
{
wxString msg = wxString::Format( _( "Directory \"%s\" is not writable." ), fn.GetPath() );
msg = wxString::Format( _( "Directory \"%s\" is not writable." ), fn.GetPath() );
DisplayError( this, msg );
return false;
}
for( screen = screenList.GetFirst(); screen; screen = screenList.GetNext() )
// Warn user on potential file overwrite. This can happen on shared sheets.
wxArrayString overwrittenFiles;
for( size_t i = 0; i < screens.GetCount(); i++ )
{
screen = screens.GetScreen( i );
wxCHECK2( screen, continue );
// Convert legacy schematics file name extensions for the new format.
wxFileName tmpFn = screen->GetFileName();
if( tmpFn.GetExt() == KiCadSchematicFileExtension )
continue;
tmpFn.SetExt( KiCadSchematicFileExtension );
if( tmpFn.FileExists() )
overwrittenFiles.Add( tmpFn.GetFullPath() );
}
if( !overwrittenFiles.IsEmpty() )
{
for( auto overwrittenFile : overwrittenFiles )
{
if( msg.IsEmpty() )
msg = overwrittenFile;
else
msg += "\n" + overwrittenFile;
}
msg = _( "The following files will be overwritten:\n\n" ) + msg;
wxRichMessageDialog dlg(
this,
_( "Saving the project to the new file format will overwrite existing files." ),
_( "Project Save Warning" ),
wxOK | wxCANCEL | wxCANCEL_DEFAULT | wxCENTER | wxICON_EXCLAMATION );
dlg.ShowDetailedText( msg );
dlg.SetOKCancelLabels( wxMessageDialog::ButtonLabel( _( "Overwrite Files" ) ),
wxMessageDialog::ButtonLabel( _( "Abort Project Save" ) ) );
if( dlg.ShowModal() == wxID_CANCEL )
return false;
}
for( size_t i = 0; i < screens.GetCount(); i++ )
{
screen = screens.GetScreen( i );
wxCHECK2( screen, continue );
// Convert legacy schematics file name extensions for the new format.
wxFileName tmpFn = screen->GetFileName();
@ -629,7 +712,7 @@ bool SCH_EDIT_FRAME::SaveProject()
screen->SetFileName( tmpFn.GetFullPath() );
}
success &= SaveEEFile( screen );
success &= SaveEEFile( screens.GetSheet( i ) );
}
if( updateFileType )
@ -678,25 +761,25 @@ bool SCH_EDIT_FRAME::doAutoSave()
if( !IsWritable( tmp ) )
return false;
for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
for( size_t i = 0; i < screens.GetCount(); i++ )
{
// Only create auto save files for the schematics that have been modified.
if( !screen->IsSave() )
if( !screens.GetScreen( i )->IsSave() )
continue;
tmpFileName = fn = screen->GetFileName();
tmpFileName = fn = screens.GetScreen( i )->GetFileName();
// Auto save file name is the normal file name prefixed with GetAutoSavePrefix().
fn.SetName( GetAutoSaveFilePrefix() + fn.GetName() );
screen->SetFileName( fn.GetFullPath() );
screens.GetScreen( i )->SetFileName( fn.GetFullPath() );
if( SaveEEFile( screen, false, NO_BACKUP_FILE ) )
screen->SetModify();
if( SaveEEFile( screens.GetSheet( i ), false, NO_BACKUP_FILE ) )
screens.GetScreen( i )->SetModify();
else
autoSaveOk = false;
screen->SetFileName( tmpFileName.GetFullPath() );
screens.GetScreen( i )->SetFileName( tmpFileName.GetFullPath() );
}
if( autoSaveOk )

View File

@ -34,6 +34,7 @@
#include <class_library.h>
#include <netlist.h>
#include <sch_reference_list.h>
#include <sch_screen.h>
wxString NETLIST_EXPORTER::MakeCommandLine( const wxString& aFormatString,

View File

@ -30,6 +30,7 @@
#include <sch_component.h>
#include <sch_line.h>
#include <sch_text.h>
#include <sch_screen.h>
#include <sch_sheet.h>
#include <algorithm>

View File

@ -383,6 +383,11 @@ void SCH_COMPONENT::AddHierarchicalReference( const KIID_PATH& aPath, const wxSt
{
if( m_instanceReferences[ii].m_Path == aPath )
{
wxLogTrace( traceSchSheetPaths,
"Removing symbol instance:\n sheet path %s\n reference %s, unit %d\n from symbol %s.",
aPath.AsString(), m_instanceReferences[ii].m_Reference,
m_instanceReferences[ii].m_Unit, m_Uuid.AsString() );
m_instanceReferences.erase( m_instanceReferences.begin() + ii );
ii--;
}
@ -392,6 +397,11 @@ void SCH_COMPONENT::AddHierarchicalReference( const KIID_PATH& aPath, const wxSt
instance.m_Path = aPath;
instance.m_Reference = aRef;
instance.m_Unit = aUnit;
wxLogTrace( traceSchSheetPaths,
"Adding symbol instance:\n sheet path %s\n reference %s, unit %d\n to symbol %s.",
aPath.AsString(), aRef, aUnit, m_Uuid.AsString() );
m_instanceReferences.push_back( instance );
}
@ -405,6 +415,10 @@ const wxString SCH_COMPONENT::GetRef( const SCH_SHEET_PATH* sheet, bool aInclude
{
if( instance.m_Path == path )
{
wxLogTrace( traceSchSheetPaths,
"Setting symbol instance:\n sheet path %s\n reference %s, unit %d\n found in symbol %s.",
path.AsString(), instance.m_Reference, instance.m_Unit, m_Uuid.AsString() );
ref = instance.m_Reference;
break;
}
@ -865,6 +879,34 @@ bool SCH_COMPONENT::AddSheetPathReferenceEntryIfMissing( const KIID_PATH& aSheet
}
bool SCH_COMPONENT::ReplaceInstanceSheetPath( const KIID_PATH& aOldSheetPath,
const KIID_PATH& aNewSheetPath )
{
auto it = std::find_if( m_instanceReferences.begin(), m_instanceReferences.end(),
[ aOldSheetPath ]( COMPONENT_INSTANCE_REFERENCE& r )->bool
{
return aOldSheetPath == r.m_Path;
}
);
if( it != m_instanceReferences.end() )
{
wxLogTrace( traceSchSheetPaths,
"Replacing sheet path %s\n with sheet path %s\n for symbol %s.",
aOldSheetPath.AsString(), aNewSheetPath.AsString(), m_Uuid.AsString() );
it->m_Path = aNewSheetPath;
return true;
}
wxLogTrace( traceSchSheetPaths,
"Could not find sheet path %s\n to replace with sheet path %s\n for symbol %s.",
aOldSheetPath.AsString(), aNewSheetPath.AsString(), m_Uuid.AsString() );
return false;
}
void SCH_COMPONENT::SetOrientation( int aOrientation )
{
TRANSFORM temp = TRANSFORM();

View File

@ -45,17 +45,15 @@
#include <wx/string.h>
#include <bitmaps.h>
#include <class_libentry.h>
#include <lib_pin.h>
#include <sch_field.h>
#include <sch_item.h>
#include <sch_pin.h>
#include <sch_screen.h>
#include <sch_sheet_path.h> // COMPONENT_INSTANCE_REFERENCE
#include <symbol_lib_table.h>
#include <transform.h>
class COMPONENT_SELECTION;
class SCH_SCREEN;
class SCH_SHEET_PATH;
class LIB_ITEM;
class LIB_PIN;
class LIB_PART;
@ -84,14 +82,6 @@ typedef std::weak_ptr<LIB_PART> PART_REF;
extern std::string toUTFTildaText( const wxString& txt );
struct COMPONENT_INSTANCE_REFERENCE
{
KIID_PATH m_Path;
wxString m_Reference;
int m_Unit;
};
/**
* Schematic symbol object.
*/
@ -363,6 +353,17 @@ public:
*/
bool AddSheetPathReferenceEntryIfMissing( const KIID_PATH& aSheetPath );
/**
* Replace \a aOldSheetPath with \a aNewSheetPath in the instance list.
*
* @param aOldSheetPath is a #KIID_PATH object of an existing path in the instance list.
* @param aNewSheetPath is a #KIID_PATH object of the path to replace the existing path.
*
* @return true if \a aOldSheetPath was found and replaced or false if \a aOldSheetPath was
* not found in the instance list.
*/
bool ReplaceInstanceSheetPath( const KIID_PATH& aOldSheetPath, const KIID_PATH& aNewSheetPath );
/**
* Clear the HIGHLIGHTED flag of all items of the component (fields, pins ...)
*/

View File

@ -592,10 +592,10 @@ public:
bool AppendSchematic();
/**
* Save \a aScreen to a schematic file.
* Save \a aSheet to a schematic file.
*
* @param aScreen A pointer to the SCH_SCREEN object to save. A NULL pointer saves
* the current screen.
* @param aSheet A pointer to the #SCH_SHEET object to save. A NULL pointer saves
* the current screen only.
* @param aSaveUnderNewName Controls how the file is to be saved;: using previous name
* or under a new name .
* @param aCreateBackupFile Creates a back of the file associated with \a aScreen
@ -604,9 +604,9 @@ public:
* #NO_BACKUP_FILE are defined for improved code readability.
* @return True if the file has been saved.
*/
bool SaveEEFile( SCH_SCREEN* aScreen,
bool aSaveUnderNewName = false,
bool aCreateBackupFile = CREATE_BACKUP_FILE );
bool SaveEEFile( SCH_SHEET* aSheet,
bool aSaveUnderNewName = false,
bool aCreateBackupFile = CREATE_BACKUP_FILE );
/**

View File

@ -218,7 +218,7 @@ public:
*
* @param aFileName is the name of a file to save to on disk.
*
* @param aSchematic is the class #SCH_SCREEN in memory document tree from which to extract
* @param aSchematic is the class #SCH_SHEET in memory document tree from which to extract
* information when writing to \a aFileName. The caller continues to
* own the SCHEMATIC, and the plugin should refrain from modifying the
* SCHEMATIC if possible.
@ -230,11 +230,13 @@ public:
* save the file, because it can take any number of additional named
* tuning arguments that the plugin is known to support. The caller
* continues to own this object (plugin may not delete it), and plugins
* should expect it to be optionally NULL.
* should expect it to be optionally NULL. Set the
* #PropSaveCurrentSheetOnly property to only save the current sheet.
* Otherwise, all hierarchial sheets are saved.
*
* @throw IO_ERROR if there is a problem saving or exporting.
*/
virtual void Save( const wxString& aFileName, SCH_SCREEN* aSchematic, KIWAY* aKiway,
virtual void Save( const wxString& aFileName, SCH_SHEET* aSchematic, KIWAY* aKiway,
const PROPERTIES* aProperties = NULL );
/**

View File

@ -1817,10 +1817,10 @@ std::shared_ptr<BUS_ALIAS> SCH_LEGACY_PLUGIN::loadBusAlias( LINE_READER& aReader
}
void SCH_LEGACY_PLUGIN::Save( const wxString& aFileName, SCH_SCREEN* aScreen, KIWAY* aKiway,
void SCH_LEGACY_PLUGIN::Save( const wxString& aFileName, SCH_SHEET* aSheet, KIWAY* aKiway,
const PROPERTIES* aProperties )
{
wxCHECK_RET( aScreen != NULL, "NULL SCH_SCREEN object." );
wxCHECK_RET( aSheet != NULL, "NULL SCH_SHEET object." );
wxCHECK_RET( !aFileName.IsEmpty(), "No schematic file name defined." );
LOCALE_IO toggle; // toggles on, then off, the C locale, to write floating point values.
@ -1837,15 +1837,19 @@ void SCH_LEGACY_PLUGIN::Save( const wxString& aFileName, SCH_SCREEN* aScreen, KI
m_out = &formatter; // no ownership
Format( aScreen );
Format( aSheet );
}
void SCH_LEGACY_PLUGIN::Format( SCH_SCREEN* aScreen )
void SCH_LEGACY_PLUGIN::Format( SCH_SHEET* aSheet )
{
wxCHECK_RET( aScreen != NULL, "NULL SCH_SCREEN* object." );
wxCHECK_RET( aSheet != NULL, "NULL SCH_SHEET* object." );
wxCHECK_RET( m_kiway != NULL, "NULL KIWAY* object." );
SCH_SCREEN* screen = aSheet->GetScreen();
wxCHECK( screen, /* void */ );
// Write the header
m_out->Print( 0, "%s %s %d\n", "EESchema", SCHEMATIC_HEAD_STRING, EESCHEMA_VERSION );
@ -1858,15 +1862,15 @@ void SCH_LEGACY_PLUGIN::Format( SCH_SCREEN* aScreen )
* simple hierarchy and flat hierarchy. Used also to search the root
* sheet ( ScreenNumber = 1 ) within the files
*/
const TITLE_BLOCK& tb = aScreen->GetTitleBlock();
const PAGE_INFO& page = aScreen->GetPageSettings();
const TITLE_BLOCK& tb = screen->GetTitleBlock();
const PAGE_INFO& page = screen->GetPageSettings();
m_out->Print( 0, "$Descr %s %d %d%s\n", TO_UTF8( page.GetType() ),
page.GetWidthMils(),
page.GetHeightMils(),
!page.IsCustom() && page.IsPortrait() ? " portrait" : "" );
m_out->Print( 0, "encoding utf-8\n" );
m_out->Print( 0, "Sheet %d %d\n", aScreen->m_ScreenNumber, aScreen->m_NumberOfScreens );
m_out->Print( 0, "Sheet %d %d\n", screen->m_ScreenNumber, screen->m_NumberOfScreens );
m_out->Print( 0, "Title %s\n", EscapedUTF8( tb.GetTitle() ).c_str() );
m_out->Print( 0, "Date %s\n", EscapedUTF8( tb.GetDate() ).c_str() );
m_out->Print( 0, "Rev %s\n", EscapedUTF8( tb.GetRevision() ).c_str() );
@ -1882,7 +1886,7 @@ void SCH_LEGACY_PLUGIN::Format( SCH_SCREEN* aScreen )
m_out->Print( 0, "Comment9 %s\n", EscapedUTF8( tb.GetComment( 8 ) ).c_str() );
m_out->Print( 0, "$EndDescr\n" );
for( const auto& alias : aScreen->GetBusAliases() )
for( const auto& alias : screen->GetBusAliases() )
{
saveBusAlias( alias );
}
@ -1891,7 +1895,7 @@ void SCH_LEGACY_PLUGIN::Format( SCH_SCREEN* aScreen )
auto cmp = []( const SCH_ITEM* a, const SCH_ITEM* b ) { return *a < *b; };
std::multiset<SCH_ITEM*, decltype( cmp )> save_map( cmp );
for( auto item : aScreen->Items() )
for( auto item : screen->Items() )
save_map.insert( item );

View File

@ -104,10 +104,10 @@ public:
void LoadContent( LINE_READER& aReader, SCH_SCREEN* aScreen,
int version = EESCHEMA_VERSION );
void Save( const wxString& aFileName, SCH_SCREEN* aScreen, KIWAY* aKiway,
void Save( const wxString& aFileName, SCH_SHEET* aScreen, KIWAY* aKiway,
const PROPERTIES* aProperties = nullptr ) override;
void Format( SCH_SCREEN* aScreen );
void Format( SCH_SHEET* aSheet );
void Format( SELECTION* aSelection, OUTPUTFORMATTER* aFormatter );

View File

@ -55,7 +55,7 @@ SCH_SHEET* SCH_PLUGIN::Load( const wxString& aFileName, KIWAY* aKiway, SCH_SHEET
}
void SCH_PLUGIN::Save( const wxString& aFileName, SCH_SCREEN* aSchematic, KIWAY* aKiway,
void SCH_PLUGIN::Save( const wxString& aFileName, SCH_SHEET* aSheet, KIWAY* aKiway,
const PROPERTIES* aProperties )
{
// not pure virtual so that plugins only have to implement subset of the SCH_PLUGIN interface.

View File

@ -36,9 +36,6 @@
#include <map>
class SCH_REFERENCE;
class SCH_REFERENCE_LIST;
/**
* SCH_REFERENCE
* is used as a helper to define a component's reference designator in a schematic. This
@ -68,7 +65,6 @@ class SCH_REFERENCE
friend class SCH_REFERENCE_LIST;
public:
SCH_REFERENCE() :
@ -97,6 +93,8 @@ public:
int GetUnit() const { return m_Unit; }
void SetUnit( int aUnit ) { m_Unit = aUnit; }
void SetSheetNumber( int aSheetNumber ) { m_SheetNum = aSheetNumber; }
const wxString GetPath() const
@ -132,10 +130,12 @@ public:
{
return m_Ref;
}
void SetRefStr( const std::string& aReference )
{
m_Ref = aReference;
}
const char* GetRefStr() const
{
return m_Ref.c_str();
@ -229,7 +229,7 @@ public:
* Function GetCount
* @return the number of items in the list
*/
unsigned GetCount()
unsigned GetCount() const
{
return flatList.size();
}

View File

@ -177,6 +177,14 @@ void SCH_SCREEN::DecRefCount()
}
bool SCH_SCREEN::HasItems( KICAD_T aItemType ) const
{
EE_RTREE::EE_TYPE sheets = const_cast<EE_RTREE&>( m_rtree ).OfType( aItemType );
return sheets.begin() != sheets.end();
}
void SCH_SCREEN::Append( SCH_ITEM* aItem )
{
if( aItem->Type() != SCH_SHEET_PIN_T && aItem->Type() != SCH_FIELD_T )
@ -1267,7 +1275,16 @@ SCH_SCREEN* SCH_SCREENS::GetScreen( unsigned int aIndex ) const
}
void SCH_SCREENS::addScreenToList( SCH_SCREEN* aScreen )
SCH_SHEET* SCH_SCREENS::GetSheet( unsigned int aIndex ) const
{
if( aIndex < m_sheets.size() )
return m_sheets[ aIndex ];
return NULL;
}
void SCH_SCREENS::addScreenToList( SCH_SCREEN* aScreen, SCH_SHEET* aSheet )
{
if( aScreen == NULL )
return;
@ -1279,6 +1296,7 @@ void SCH_SCREENS::addScreenToList( SCH_SCREEN* aScreen )
}
m_screens.push_back( aScreen );
m_sheets.push_back( aSheet );
}
@ -1288,7 +1306,7 @@ void SCH_SCREENS::buildScreenList( SCH_SHEET* aSheet )
{
SCH_SCREEN* screen = aSheet->GetScreen();
addScreenToList( screen );
addScreenToList( screen, aSheet );
for( SCH_ITEM* item : screen->Items().OfType( SCH_SHEET_T ) )
buildScreenList( static_cast<SCH_SHEET*>( item ) );

View File

@ -45,6 +45,8 @@
#include <title_block.h>
#include <lib_id.h>
#include <sch_component.h> // COMPONENT_INSTANCE_REFERENCE
#include <sch_reference_list.h>
#include <sch_rtree.h>
#include <sch_sheet.h>
#include <sch_sheet_path.h>
@ -58,7 +60,11 @@ class SCH_LINE;
class SCH_TEXT;
class PLOTTER;
class REPORTER;
class SCH_EDIT_FRAME;
class SCH_SHEET;
class SCH_SHEET_LIST;
class SCH_SEXPR_PARSER;
class SCH_SEXPR_PLUGIN;
enum SCH_LINE_TEST_T
{
@ -120,6 +126,26 @@ private:
/// Library symbols required for this schematic.
std::map<wxString, LIB_PART*> m_libSymbols;
/**
* The list of symbol instances loaded from the schematic file.
*
* This list is only used to as temporary storage when the schematic file is loaded.
* If the screen is the root sheet, then this information is used to update the
* #SCH_COMPONENT instance reference and unit information after the entire schematic
* is loaded and is never used again. If this screen is not the root sheet, then the
* schematic file is the root sheet of another project and this information is saved
* unchanged back to the schematic file.
*
* @warning Under no circumstances is this information to be modified or used after the
* schematic file is loaded. It is read only and it is only written to non-root
* schematic files.
*/
std::vector<COMPONENT_INSTANCE_REFERENCE> m_symbolInstances;
friend SCH_EDIT_FRAME; // Only to populate m_symbolInstances.
friend SCH_SEXPR_PARSER; // Only to load instance information from schematic file.
friend SCH_SEXPR_PLUGIN; // Only to save the loaded instance information to schematic file.
void clearLibSymbols();
public:
@ -146,6 +172,10 @@ public:
return m_rtree.empty();
}
bool HasItems( KICAD_T aItemType ) const;
bool HasSheets() const { return HasItems( SCH_SHEET_T ); }
static inline bool ClassOf( const EDA_ITEM* aItem )
{
return aItem && SCH_SCREEN_T == aItem->Type();
@ -530,15 +560,17 @@ class SCH_SCREENS
{
private:
std::vector< SCH_SCREEN* > m_screens;
std::vector< SCH_SHEET* > m_sheets;
unsigned int m_index;
public:
SCH_SCREENS( SCH_SHEET* aSheet = NULL );
~SCH_SCREENS();
int GetCount() const { return m_screens.size(); }
size_t GetCount() const { return m_screens.size(); }
SCH_SCREEN* GetFirst();
SCH_SCREEN* GetNext();
SCH_SCREEN* GetScreen( unsigned int aIndex ) const;
SCH_SHEET* GetSheet( unsigned int aIndex ) const;
/**
* Clear the annotation for all components in the hierarchy.
@ -656,7 +688,7 @@ public:
bool CanCauseCaseSensitivityIssue( const wxString& aSchematicFileName ) const;
private:
void addScreenToList( SCH_SCREEN* aScreen );
void addScreenToList( SCH_SCREEN* aScreen, SCH_SHEET* aSheet );
void buildScreenList( SCH_SHEET* aSheet);
};

View File

@ -443,13 +443,18 @@ void SCH_SEXPR_PARSER::parseStroke( STROKE_PARAMS& aStroke )
}
case T_color:
aStroke.m_Color =
COLOR4D( parseInt( "red" ) / 255.0,
parseInt( "green" ) / 255.0,
parseInt( "blue" ) / 255.0,
parseDouble( "alpha" ) );
{
COLOR4D color;
color.r = parseInt( "red" ) / 255.0;
color.g = parseInt( "green" ) / 255.0;
color.b = parseInt( "blue" ) / 255.0;
color.a = Clamp( parseDouble( "alpha" ), 0.0, 1.0 );
aStroke.m_Color = color;
NeedRIGHT();
break;
}
default:
Expecting( "width, type, or color" );
@ -496,14 +501,17 @@ void SCH_SEXPR_PARSER::parseFill( FILL_PARAMS& aFill )
}
case T_color:
aFill.m_Color =
COLOR4D( parseInt( "red" ) / 255.0,
parseInt( "green" ) / 255.0,
parseInt( "blue" ) / 255.0,
parseDouble( "alpha" ) );
{
COLOR4D color;
color.r = parseInt( "red" ) / 255.0;
color.g = parseInt( "green" ) / 255.0;
color.b = parseInt( "blue" ) / 255.0;
color.a = Clamp( parseDouble( "alpha" ), 0.0, 1.0 );
aFill.m_Color = color;
NeedRIGHT();
break;
}
default:
Expecting( "type or color" );
@ -1791,11 +1799,12 @@ SCH_SHEET_PIN* SCH_SEXPR_PARSER::parseSchSheetPin( SCH_SHEET* aSheet )
}
void SCH_SEXPR_PARSER::parseSchSymbolInstances( std::unique_ptr<SCH_COMPONENT>& aSymbol )
void SCH_SEXPR_PARSER::parseSchSymbolInstances( SCH_SCREEN* aScreen )
{
wxCHECK_RET( CurTok() == T_instances,
wxCHECK_RET( CurTok() == T_symbol_instances,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
wxT( " as a instances token." ) );
wxCHECK( aScreen, /* void */ );
T token;
@ -1812,9 +1821,9 @@ void SCH_SEXPR_PARSER::parseSchSymbolInstances( std::unique_ptr<SCH_COMPONENT>&
{
NeedSYMBOL();
int unit = 1;
wxString reference;
KIID_PATH path( FromUTF8() );
COMPONENT_INSTANCE_REFERENCE instance;
instance.m_Path = KIID_PATH( FromUTF8() );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
@ -1827,12 +1836,12 @@ void SCH_SEXPR_PARSER::parseSchSymbolInstances( std::unique_ptr<SCH_COMPONENT>&
{
case T_reference:
NeedSYMBOL();
reference = FromUTF8();
instance.m_Reference = FromUTF8();
NeedRIGHT();
break;
case T_unit:
unit = parseInt( "symbol unit" );
instance.m_Unit = parseInt( "symbol unit" );
NeedRIGHT();
break;
@ -1841,8 +1850,7 @@ void SCH_SEXPR_PARSER::parseSchSymbolInstances( std::unique_ptr<SCH_COMPONENT>&
}
}
aSymbol->AddHierarchicalReference( path, reference, unit );
aSymbol->GetField( REFERENCE )->SetText( reference );
aScreen->m_symbolInstances.emplace_back( instance );
break;
}
@ -1853,9 +1861,13 @@ void SCH_SEXPR_PARSER::parseSchSymbolInstances( std::unique_ptr<SCH_COMPONENT>&
}
void SCH_SEXPR_PARSER::ParseSchematic( SCH_SCREEN* aScreen )
void SCH_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet )
{
wxCHECK_RET( aScreen != nullptr, "" );
wxCHECK( aSheet != nullptr, /* void */ );
SCH_SCREEN* screen = aSheet->GetScreen();
wxCHECK( screen != nullptr, /* void */ );
T token;
@ -1880,7 +1892,7 @@ void SCH_SEXPR_PARSER::ParseSchematic( SCH_SCREEN* aScreen )
{
PAGE_INFO pageInfo;
parsePAGE_INFO( pageInfo );
aScreen->SetPageSettings( pageInfo );
screen->SetPageSettings( pageInfo );
break;
}
@ -1888,7 +1900,7 @@ void SCH_SEXPR_PARSER::ParseSchematic( SCH_SCREEN* aScreen )
{
TITLE_BLOCK tb;
parseTITLE_BLOCK( tb );
aScreen->SetTitleBlock( tb );
screen->SetTitleBlock( tb );
break;
}
@ -1907,7 +1919,7 @@ void SCH_SEXPR_PARSER::ParseSchematic( SCH_SCREEN* aScreen )
switch( token )
{
case T_symbol:
aScreen->AddLibSymbol( ParseSymbol( symbolLibMap, true ) );
screen->AddLibSymbol( ParseSymbol( symbolLibMap, true ) );
break;
default:
@ -1919,49 +1931,63 @@ void SCH_SEXPR_PARSER::ParseSchematic( SCH_SCREEN* aScreen )
}
case T_symbol:
aScreen->Append( static_cast<SCH_ITEM*>( parseSchematicSymbol() ) );
screen->Append( static_cast<SCH_ITEM*>( parseSchematicSymbol() ) );
break;
case T_image:
aScreen->Append( static_cast<SCH_ITEM*>( parseImage() ) );
screen->Append( static_cast<SCH_ITEM*>( parseImage() ) );
break;
case T_sheet:
aScreen->Append( static_cast<SCH_ITEM*>( parseSheet() ) );
{
SCH_SHEET* sheet = parseSheet();
// Set the parent to aSheet. This effectively creates a method to find
// the root sheet from any sheet so a pointer to the root sheet does not
// need to be stored globally. Note: this is not the same as a hierarchy.
// Complex hierarchies can have multiple copies of a sheet. This only
// provides a simple tree to find the root sheet.
sheet->SetParent( aSheet );
screen->Append( static_cast<SCH_ITEM*>( sheet ) );
break;
}
case T_junction:
aScreen->Append( static_cast<SCH_ITEM*>( parseJunction() ) );
screen->Append( static_cast<SCH_ITEM*>( parseJunction() ) );
break;
case T_no_connect:
aScreen->Append( static_cast<SCH_ITEM*>( parseNoConnect() ) );
screen->Append( static_cast<SCH_ITEM*>( parseNoConnect() ) );
break;
case T_bus_entry:
aScreen->Append( static_cast<SCH_ITEM*>( parseBusEntry() ) );
screen->Append( static_cast<SCH_ITEM*>( parseBusEntry() ) );
break;
case T_polyline:
case T_bus:
case T_wire:
aScreen->Append( static_cast<SCH_ITEM*>( parseLine() ) );
screen->Append( static_cast<SCH_ITEM*>( parseLine() ) );
break;
case T_text:
case T_label:
case T_global_label:
case T_hierarchical_label:
aScreen->Append( static_cast<SCH_ITEM*>( parseSchText() ) );
screen->Append( static_cast<SCH_ITEM*>( parseSchText() ) );
break;
case T_symbol_instances:
parseSchSymbolInstances( screen );
break;
default:
Expecting( "symbol, bitmap, sheet, junction, no_connect, bus_entry, line"
"bus, text, label, global_label, or hierarchical_label" );
Expecting( "symbol, bitmap, sheet, junction, no_connect, bus_entry, line, bus"
"text, label, global_label, hierarchical_label, or symbol_instances" );
}
}
aScreen->UpdateLocalLibSymbolLinks();
screen->UpdateLocalLibSymbolLinks();
}
@ -2094,10 +2120,6 @@ SCH_COMPONENT* SCH_SEXPR_PARSER::parseSchematicSymbol()
break;
}
case T_instances:
parseSchSymbolInstances( symbol );
break;
default:
Expecting( "lib_id, lib_name, at, mirror, uuid, property, or instances" );
}

View File

@ -203,7 +203,7 @@ class SCH_SEXPR_PARSER : public SCHEMATIC_LEXER
void parsePAGE_INFO( PAGE_INFO& aPageInfo );
void parseTITLE_BLOCK( TITLE_BLOCK& aTitleBlock );
void parseSchSymbolInstances( std::unique_ptr<SCH_COMPONENT>& aSymbol );
void parseSchSymbolInstances( SCH_SCREEN* aScreen );
SCH_SHEET_PIN* parseSchSheetPin( SCH_SHEET* aSheet );
SCH_FIELD* parseSchField( SCH_COMPONENT* aParentSymbol );
@ -226,13 +226,13 @@ public:
LIB_ITEM* ParseDrawItem();
/**
* Parse a single schematic file into \a aScreen.
* Parse a single schematic file into \a aSheet.
*
* @note This does not load any sub-sheets or decent complex sheet hierarchies.
*
* @param aScreen The #SCH_SCREEN object to store the parsed schematic file.
* @param aSheet The #SCH_SHEET object to store the parsed schematic file.
*/
void ParseSchematic( SCH_SCREEN* aScreen );
void ParseSchematic( SCH_SHEET* aSheet );
/**
* Return whether a version number, if any was parsed, was too recent

View File

@ -73,6 +73,7 @@
#include <eeschema_id.h> // for MAX_UNIT_COUNT_PER_PACKAGE definition
#include <sch_file_versions.h>
#include <schematic_lexer.h>
#include <sch_reference_list.h>
#include <sch_sexpr_parser.h>
#include <symbol_lib_table.h> // for PropPowerSymsOnly definintion.
#include <confirm.h>
@ -299,11 +300,11 @@ static void formatStroke( OUTPUTFORMATTER* aFormatter, int aNestLevel, int aWidt
aFormatter->Print( 0, " (type %s)", TO_UTF8( getLineStyleToken( aStyle ) ) );
if( !( aColor == COLOR4D::UNSPECIFIED ) )
aFormatter->Print( 0, " (color %d %d %d %0.4f)",
aFormatter->Print( 0, " (color %d %d %d %s)",
KiROUND( aColor.r * 255.0 ),
KiROUND( aColor.g * 255.0 ),
KiROUND( aColor.b * 255.0 ),
aColor.a );
Double2Str( aColor.a ).c_str() );
aFormatter->Print( 0, ")" );
}
@ -523,23 +524,7 @@ void SCH_SEXPR_PLUGIN::loadHierarchy( SCH_SHEET* aSheet )
try
{
loadFile( fileName.GetFullPath(), aSheet->GetScreen() );
for( auto aItem : aSheet->GetScreen()->Items().OfType( SCH_SHEET_T ) )
{
assert( aItem->Type() == SCH_SHEET_T );
auto sheet = static_cast<SCH_SHEET*>( aItem );
// Set the parent to aSheet. This effectively creates a method to find
// the root sheet from any sheet so a pointer to the root sheet does not
// need to be stored globally. Note: this is not the same as a hierarchy.
// Complex hierarchies can have multiple copies of a sheet. This only
// provides a simple tree to find the root sheet.
sheet->SetParent( aSheet );
// Recursion starts here.
loadHierarchy( sheet );
}
loadFile( fileName.GetFullPath(), aSheet );
}
catch( const IO_ERROR& ioe )
{
@ -553,6 +538,17 @@ void SCH_SEXPR_PLUGIN::loadHierarchy( SCH_SHEET* aSheet )
m_error += ioe.What();
}
// This was moved out of the try{} block so that any sheets definitionsthat
// the plugin fully parsed before the exception was raised will be loaded.
for( auto aItem : aSheet->GetScreen()->Items().OfType( SCH_SHEET_T ) )
{
wxCHECK2( aItem->Type() == SCH_SHEET_T, /* do nothing */ );
auto sheet = static_cast<SCH_SHEET*>( aItem );
// Recursion starts here.
loadHierarchy( sheet );
}
}
m_currentPath.pop();
@ -561,20 +557,20 @@ void SCH_SEXPR_PLUGIN::loadHierarchy( SCH_SHEET* aSheet )
}
void SCH_SEXPR_PLUGIN::loadFile( const wxString& aFileName, SCH_SCREEN* aScreen )
void SCH_SEXPR_PLUGIN::loadFile( const wxString& aFileName, SCH_SHEET* aSheet )
{
FILE_LINE_READER reader( aFileName );
SCH_SEXPR_PARSER parser( &reader );
parser.ParseSchematic( aScreen );
parser.ParseSchematic( aSheet );
}
void SCH_SEXPR_PLUGIN::Save( const wxString& aFileName, SCH_SCREEN* aScreen, KIWAY* aKiway,
void SCH_SEXPR_PLUGIN::Save( const wxString& aFileName, SCH_SHEET* aSheet, KIWAY* aKiway,
const PROPERTIES* aProperties )
{
wxCHECK_RET( aScreen != NULL, "NULL SCH_SCREEN object." );
wxCHECK_RET( aSheet != NULL, "NULL SCH_SHEET object." );
wxCHECK_RET( !aFileName.IsEmpty(), "No schematic file name defined." );
LOCALE_IO toggle; // toggles on, then off, the C locale, to write floating point values.
@ -591,34 +587,44 @@ void SCH_SEXPR_PLUGIN::Save( const wxString& aFileName, SCH_SCREEN* aScreen, KIW
m_out = &formatter; // no ownership
Format( aScreen );
Format( aSheet );
}
void SCH_SEXPR_PLUGIN::Format( SCH_SCREEN* aScreen )
void SCH_SEXPR_PLUGIN::Format( SCH_SHEET* aSheet )
{
wxCHECK_RET( aScreen != NULL, "NULL SCH_SCREEN* object." );
wxCHECK_RET( aSheet != NULL, "NULL SCH_SHEET* object." );
wxCHECK_RET( m_kiway != NULL, "NULL KIWAY* object." );
SCH_SCREEN* screen = aSheet->GetScreen();
wxCHECK( screen, /* void */ );
m_out->Print( 0, "(kicad_sch (version %d) (host eeschema %s)\n\n",
SEXPR_SCHEMATIC_FILE_VERSION,
m_out->Quotew( GetBuildVersion() ).c_str() );
aScreen->GetPageSettings().Format( m_out, 1, 0 );
// Root sheet must have a permanent UUID.
// if( aSheet->IsRootSheet() && aSheet->m_Uuid.IsLegacyTimestamp() )
// const_cast<KIID&>( aSheet->m_Uuid ).ConvertTimestampToUuid();
// m_out->Print( 1, "(uuid %s)\n\n", m_out->Quotew( aSheet->m_Uuid.AsString() ).c_str() );
screen->GetPageSettings().Format( m_out, 1, 0 );
m_out->Print( 0, "\n" );
aScreen->GetTitleBlock().Format( m_out, 1, 0 );
screen->GetTitleBlock().Format( m_out, 1, 0 );
// Save cache library.
m_out->Print( 1, "(lib_symbols\n" );
for( auto libSymbol : aScreen->GetLibSymbols() )
for( auto libSymbol : screen->GetLibSymbols() )
SCH_SEXPR_PLUGIN_CACHE::SaveSymbol( libSymbol.second, *m_out, 2, libSymbol.first );
m_out->Print( 1, ")\n\n" );
// @todo save schematic instance information (page #).
for( const auto& alias : aScreen->GetBusAliases() )
for( const auto& alias : screen->GetBusAliases() )
{
saveBusAlias( alias, 1 );
}
@ -627,7 +633,7 @@ void SCH_SEXPR_PLUGIN::Format( SCH_SCREEN* aScreen )
auto cmp = []( const SCH_ITEM* a, const SCH_ITEM* b ) { return *a < *b; };
std::multiset<SCH_ITEM*, decltype( cmp )> save_map( cmp );
for( auto item : aScreen->Items() )
for( auto item : screen->Items() )
save_map.insert( item );
KICAD_T itemType = TYPE_NOT_INIT;
@ -703,6 +709,48 @@ void SCH_SEXPR_PLUGIN::Format( SCH_SCREEN* aScreen )
}
}
// If this is the root sheet, save all of the sheet paths.
if( aSheet->IsRootSheet() )
{
m_out->Print( 0, "\n" );
m_out->Print( 1, "(symbol_instances\n" );
SCH_SHEET_LIST sheetPaths( aSheet );
for( auto sheetPath : sheetPaths )
{
SCH_REFERENCE_LIST instances;
sheetPath.GetComponents( instances, true, true );
instances.SortByReferenceOnly();
for( size_t i = 0; i < instances.GetCount(); i++ )
{
m_out->Print( 2, "(path %s (reference %s) (unit %d))\n",
m_out->Quotew( instances[i].GetPath() ).c_str(),
m_out->Quotew( instances[i].GetRef() ).c_str(),
instances[i].GetUnit() );
}
}
m_out->Print( 1, ")\n" ); // Close instances token.
}
else if( screen->m_symbolInstances.size() )
{
m_out->Print( 0, "\n" );
m_out->Print( 1, "(symbol_instances\n" );
for( auto instance : screen->m_symbolInstances )
{
m_out->Print( 2, "(path %s (reference %s) (unit %d))\n",
m_out->Quotew( instance.m_Path.AsString() ).c_str(),
m_out->Quotew( instance.m_Reference ).c_str(),
instance.m_Unit );
}
m_out->Print( 1, ")\n" ); // Close instances token.
}
m_out->Print( 0, ")\n" );
}
@ -834,46 +882,6 @@ void SCH_SEXPR_PLUGIN::saveSymbol( SCH_COMPONENT* aSymbol, int aNestLevel )
saveField( &field, aNestLevel + 1 );
}
// @todo Save sheet UUID at top level of schematic file. This will require saving from
// the SCH_SHEET object instead of the SCH_SCREEN object.
// KIID projectId;
// wxString projectName = "unknown";
// SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( aSymbol->GetParent() );
// if( sheet )
// {
// SCH_SHEET* rootSheet = sheet->GetRootSheet();
// wxASSERT( rootSheet );
// projectName = rootSheet->GetName();
// projectId = rootSheet->m_Uuid;
// }
// For simple hierarchies, the reference is defined by the reference property (field).
if( aSymbol->GetInstanceReferences().size() > 1 )
{
m_out->Print( aNestLevel + 1, "(instances\n" );
// @todo Group project level instances.
for( const auto instance : aSymbol->GetInstanceReferences() )
{
wxString path = "/";
// Skip root sheet
for( int i = 1; i < (int) instance.m_Path.size(); ++i )
path += instance.m_Path[i].AsString() + "/";
m_out->Print( aNestLevel + 2, "(path %s (reference %s) (unit %d))\n",
m_out->Quotew( path ).c_str(),
m_out->Quotew( instance.m_Reference ).c_str(),
instance.m_Unit );
}
m_out->Print( aNestLevel + 1, ")\n" );
}
m_out->Print( aNestLevel, ")\n" );
}
@ -962,7 +970,7 @@ void SCH_SEXPR_PLUGIN::saveSheet( SCH_SHEET* aSheet, int aNestLevel )
{
wxCHECK_RET( aSheet != nullptr && m_out != nullptr, "" );
m_out->Print( aNestLevel, "(sheet (at %s %s) (size %s %s)",
m_out->Print( aNestLevel, "(sheet (at %s %s) (size %s %s)\n",
FormatInternalUnits( aSheet->GetPosition().x ).c_str(),
FormatInternalUnits( aSheet->GetPosition().y ).c_str(),
FormatInternalUnits( aSheet->GetSize().GetWidth() ).c_str(),
@ -970,21 +978,21 @@ void SCH_SEXPR_PLUGIN::saveSheet( SCH_SHEET* aSheet, int aNestLevel )
if( !aSheet->UsesDefaultStroke() )
{
m_out->Print( 0, " " );
formatStroke( m_out, 0, aSheet->GetBorderWidth(), PLOT_DASH_TYPE::SOLID,
formatStroke( m_out, aNestLevel + 1, aSheet->GetBorderWidth(), PLOT_DASH_TYPE::SOLID,
aSheet->GetBorderColor() );
m_out->Print( 0, "\n" );
}
if( !( aSheet->GetBackgroundColor() == COLOR4D::UNSPECIFIED ) )
{
m_out->Print( 0, " (fill (color %d %d %d %0.4f))",
m_out->Print( aNestLevel + 1, "(fill (color %d %d %d %0.4f))",
KiROUND( aSheet->GetBackgroundColor().r * 255.0 ),
KiROUND( aSheet->GetBackgroundColor().g * 255.0 ),
KiROUND( aSheet->GetBackgroundColor().b * 255.0 ),
aSheet->GetBackgroundColor().a );
m_out->Print( 0, "\n" );
}
m_out->Print( 0, "\n" );
m_out->Print( aNestLevel + 1, "(uuid %s)", TO_UTF8( aSheet->m_Uuid.AsString() ) );
m_out->Print( 0, "\n" );

View File

@ -90,10 +90,10 @@ public:
void LoadContent( LINE_READER& aReader, SCH_SCREEN* aScreen,
int version = EESCHEMA_VERSION );
void Save( const wxString& aFileName, SCH_SCREEN* aScreen, KIWAY* aKiway,
void Save( const wxString& aFileName, SCH_SHEET* aSheet, KIWAY* aKiway,
const PROPERTIES* aProperties = nullptr ) override;
void Format( SCH_SCREEN* aScreen );
void Format( SCH_SHEET* aSheet );
void Format( SELECTION* aSelection, OUTPUTFORMATTER* aFormatter );
@ -126,7 +126,7 @@ public:
private:
void loadHierarchy( SCH_SHEET* aSheet );
void loadFile( const wxString& aFileName, SCH_SCREEN* aScreen );
void loadFile( const wxString& aFileName, SCH_SHEET* aSheet );
void saveSymbol( SCH_COMPONENT* aComponent, int aNestLevel );
void saveField( SCH_FIELD* aField, int aNestLevel );

View File

@ -261,7 +261,7 @@ public:
* and false for items moved with no reference to anchor.
*
* Usually return true for small items (labels, junctions) and false for
* items which can be large (hierarchical sheets, compoments)
* items which can be large (hierarchical sheets, components)
*
* @return false for a hierarchical sheet
*/
@ -318,6 +318,11 @@ public:
*/
SCH_SHEET* GetRootSheet();
/**
* @return true if this sheet is the root sheet.
*/
bool IsRootSheet() { return GetRootSheet() == this; }
/**
* Set the #SCH_SCREEN associated with this sheet to \a aScreen.
*
@ -485,7 +490,7 @@ public:
bool LocatePathOfScreen( SCH_SCREEN* aScreen, SCH_SHEET_PATH* aList );
/**
* Count the number of sheets found in "this" sheet includeing all of the subsheets.
* Count the number of sheets found in "this" sheet including all of the subsheets.
*
* @return the full count of sheets+subsheets contained by "this"
*/

View File

@ -34,6 +34,8 @@
#include <sch_component.h>
#include <sch_sheet.h>
#include <template_fieldnames.h>
#include <trace_helpers.h>
#include <boost/functional/hash.hpp>
#include <wx/filename.h>
#include "erc_item.h"
@ -331,9 +333,6 @@ bool SCH_SHEET_PATH::TestForRecursion( const wxString& aSrcFileName, const wxStr
}
/********************************************************************/
/* Class SCH_SHEET_LIST to handle the list of Sheets in a hierarchy */
/********************************************************************/
SCH_SHEET_LIST::SCH_SHEET_LIST( SCH_SHEET* aSheet )
{
m_isRootSheet = false;
@ -636,28 +635,6 @@ bool SCH_SHEET_LIST::SetComponentFootprint( const wxString& aReference,
}
bool SCH_SHEET_LIST::IsComplexHierarchy() const
{
wxString fileName;
for( unsigned i = 0; i < size(); i++ )
{
fileName = at( i ).Last()->GetFileName();
for( unsigned j = 0; j < size(); j++ )
{
if( i == j )
continue;
if( fileName == at( j ).Last()->GetFileName() )
return true;
}
}
return false;
}
bool SCH_SHEET_LIST::TestForRecursion( const SCH_SHEET_LIST& aSrcSheetHierarchy,
const wxString& aDestFileName )
{
@ -701,6 +678,79 @@ SCH_SHEET_PATH* SCH_SHEET_LIST::FindSheetForScreen( SCH_SCREEN* aScreen )
}
void SCH_SHEET_LIST::UpdateSymbolInstances(
std::vector<COMPONENT_INSTANCE_REFERENCE>& aSymbolInstances )
{
wxCHECK( m_isRootSheet, /* void */ ); // Only performed for the entire schematic.
SCH_REFERENCE_LIST symbolInstances;
GetComponents( symbolInstances, true, true );
for( size_t i = 0; i < symbolInstances.GetCount(); i++ )
{
// The instance paths are stored in the file sans root path so the comparison
// should not include the root path.
wxString path = symbolInstances[i].GetPath();
auto it = std::find_if( aSymbolInstances.begin(), aSymbolInstances.end(),
[ path ]( COMPONENT_INSTANCE_REFERENCE& r )->bool
{
return path == r.m_Path.AsString();
}
);
if( it == aSymbolInstances.end() )
{
wxLogTrace( traceSchSheetPaths, "No symbol instance found for path \"%s\"", path );
continue;
}
SCH_COMPONENT* symbol = symbolInstances[i].GetComp();
wxCHECK2( symbol, continue );
// Symbol instance paths are stored and looked up in memory with the root path so use
// the full path here.
symbol->AddHierarchicalReference( symbolInstances[i].GetSheetPath().Path(),
it->m_Reference, it->m_Unit );
symbol->GetField( REFERENCE )->SetText( it->m_Reference );
}
}
std::vector<KIID_PATH> SCH_SHEET_LIST::GetPaths() const
{
std::vector<KIID_PATH> paths;
for( auto sheetPath : *this )
paths.emplace_back( sheetPath.Path() );
return paths;
}
void SCH_SHEET_LIST::ReplaceLegacySheetPaths( const std::vector<KIID_PATH>& aOldSheetPaths )
{
wxCHECK( size() == aOldSheetPaths.size(), /* void */ );
for( size_t i = 0; i < size(); i++ )
{
const KIID_PATH oldSheetPath = aOldSheetPaths.at( i );
const KIID_PATH newSheetPath = at( i ).Path();
SCH_SCREEN* screen = at(i).LastScreen();
wxCHECK( screen, /* void */ );
for( auto symbol : screen->Items().OfType( SCH_COMPONENT_T ) )
{
static_cast<SCH_COMPONENT*>( symbol )->ReplaceInstanceSheetPath( oldSheetPath,
newSheetPath );
}
}
}
void SHEETLIST_ERC_ITEMS_PROVIDER::SetSeverities( int aSeverities )
{
m_severities = aSeverities;

View File

@ -37,6 +37,17 @@
#include <map>
/**
* A simple container for schematic symbol instance infromation.
*/
struct COMPONENT_INSTANCE_REFERENCE
{
KIID_PATH m_Path;
wxString m_Reference;
int m_Unit;
};
/** Info about complex hierarchies handling:
* A hierarchical schematic uses sheets (hierarchical sheets) included in a
* given sheet. Each sheet corresponds to a schematic drawing handled by a
@ -405,15 +416,6 @@ public:
bool SetComponentFootprint( const wxString& aReference, const wxString& aFootPrint,
bool aSetVisible );
/**
* Function IsComplexHierarchy
* searches all of the sheets for duplicate files names which indicates a complex
* hierarchy.
*
* @return true if the #SCH_SHEET_LIST is a complex hierarchy.
*/
bool IsComplexHierarchy() const;
/**
* Function TestForRecursion
*
@ -446,6 +448,29 @@ public:
void BuildSheetList( SCH_SHEET* aSheet );
bool NameExists( const wxString& aSheetName );
/**
* Update all of the symbol instance information using \a aSymbolInstances.
*
* @param aSymbolInstances is the symbol path information loaded from the root schematic.
*/
void UpdateSymbolInstances( std::vector<COMPONENT_INSTANCE_REFERENCE>& aSymbolInstances );
std::vector<KIID_PATH> GetPaths() const;
/**
* Update all of the symbol sheet paths to the sheet paths defined in \a aOldSheetPaths.
*
* @note The list of old sheet paths must be the exact same size and order as the existing
* sheet paths. This should not be an issue if no new sheets where added between the
* creation of this sheet list and \a aOldSheetPaths. This should only be called
* when updating legacy schematics to the new schematic file format. Once this
* happens, the schematic cannot be save to the legacy file format because the
* time stamp part of UUIDs are no longer guaranteed to be unique.
*
* @param aOldSheetPaths is the #SHEET_PATH_LIST to update from.
*/
void ReplaceLegacySheetPaths( const std::vector<KIID_PATH>& aOldSheetPaths );
};

View File

@ -39,7 +39,6 @@ id
image
input
input_low
instances
inverted
inverted_clock
italic
@ -99,6 +98,7 @@ solid
start
stroke
symbol
symbol_instances
text
thickness
title

View File

@ -115,6 +115,10 @@ bool SCH_EDIT_FRAME::LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHier
SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( schFileType ) );
std::unique_ptr< SCH_SHEET> newSheet( new SCH_SHEET );
// This will cause the sheet UUID to be set to the loaded schematic UUID. This is required
// to ensure all of the sheet paths in any subsheets are correctly generated.
const_cast<KIID&>( newSheet->m_Uuid ) = KIID( 0 );
wxFileName fileName( aFileName );
if( !fileName.IsAbsolute() && !fileName.MakeAbsolute() )

View File

@ -83,6 +83,13 @@ public:
wxString AsString() const;
wxString AsLegacyTimestampString() const;
/**
* Change an existing time stamp based UUID into a true UUID.
*
* If this is not a time stamp based UUID, then no change is made.
*/
void ConvertTimestampToUuid();
bool operator==( KIID const& rhs) const
{
return m_uuid == rhs.m_uuid;

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2018 Wayne Stambaugh <stambaughw@gmail.com>
* Copyright (C) 2018-2019 KiCad Developers, see change_log.txt for contributors.
* Copyright (C) 2018-2020 KiCad Developers, see change_log.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
@ -167,6 +167,13 @@ extern const wxChar* const traceZoomScroll;
*/
extern const wxChar* const traceSymbolResolver;
/**
* Flag to enable debug output of schematic symbol sheet path manipulation code.
*
* Use "KICAD_SCH_SHEET_PATHS" to enable.
*/
extern const wxChar* const traceSchSheetPaths;
///@}
/**