diff --git a/common/common.cpp b/common/common.cpp index 2f24e692d8..7adcd5fc9d 100644 --- a/common/common.cpp +++ b/common/common.cpp @@ -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. * diff --git a/common/trace_helpers.cpp b/common/trace_helpers.cpp index 1508267f48..8b8748e5c2 100644 --- a/common/trace_helpers.cpp +++ b/common/trace_helpers.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2018 Wayne Stambaugh - * 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 ) diff --git a/eeschema/dialogs/dialog_sch_sheet_props.cpp b/eeschema/dialogs/dialog_sch_sheet_props.cpp index fcd19f9f2e..901fa7141a 100644 --- a/eeschema/dialogs/dialog_sch_sheet_props.cpp +++ b/eeschema/dialogs/dialog_sch_sheet_props.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #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; + } } } diff --git a/eeschema/ee_collectors.cpp b/eeschema/ee_collectors.cpp index 672cb68c4a..64bad8eae4 100644 --- a/eeschema/ee_collectors.cpp +++ b/eeschema/ee_collectors.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include "sch_reference_list.h" diff --git a/eeschema/fields_grid_table.cpp b/eeschema/fields_grid_table.cpp index 11794a2c5d..35a7d77636 100644 --- a/eeschema/fields_grid_table.cpp +++ b/eeschema/fields_grid_table.cpp @@ -131,7 +131,7 @@ void FIELDS_GRID_TABLE::initGrid( DIALOG_SHIM* aDialog ) // Create a wild card using wxFileDialog syntax. wxString wildCard( _( "Schematic Files" ) ); std::vector exts; - exts.push_back( LegacySchematicFileExtension ); + exts.push_back( KiCadSchematicFileExtension ); wildCard += AddFileExtListToFilter( exts ); GRID_CELL_PATH_EDITOR* filepathEditor = diff --git a/eeschema/files-io.cpp b/eeschema/files-io.cpp index 0a8658bfe6..bef97ad806 100644 --- a/eeschema/files-io.cpp +++ b/eeschema/files-io.cpp @@ -55,22 +55,26 @@ #include -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& 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 oldSheetPaths = timeStampSheetPaths.GetPaths(); + + // The root sheet now gets a permanent UUID. + const_cast( 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( sheet->m_Uuid ).ConvertTimestampToUuid(); + + for( auto symbol : screen->Items().OfType( SCH_COMPONENT_T ) ) + const_cast( 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& 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 ) diff --git a/eeschema/netlist_exporters/netlist_exporter.cpp b/eeschema/netlist_exporters/netlist_exporter.cpp index 62c7250ac3..50cfdfb06c 100644 --- a/eeschema/netlist_exporters/netlist_exporter.cpp +++ b/eeschema/netlist_exporters/netlist_exporter.cpp @@ -34,6 +34,7 @@ #include #include #include +#include wxString NETLIST_EXPORTER::MakeCommandLine( const wxString& aFormatString, diff --git a/eeschema/netlist_object_list.cpp b/eeschema/netlist_object_list.cpp index e375c944b3..33fa31c142 100644 --- a/eeschema/netlist_object_list.cpp +++ b/eeschema/netlist_object_list.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include diff --git a/eeschema/sch_component.cpp b/eeschema/sch_component.cpp index 7e23640437..30f6b99e54 100644 --- a/eeschema/sch_component.cpp +++ b/eeschema/sch_component.cpp @@ -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(); diff --git a/eeschema/sch_component.h b/eeschema/sch_component.h index cb818eab49..5862a6d9ca 100644 --- a/eeschema/sch_component.h +++ b/eeschema/sch_component.h @@ -45,17 +45,15 @@ #include #include -#include -#include #include #include #include -#include +#include // COMPONENT_INSTANCE_REFERENCE #include #include +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 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 ...) */ diff --git a/eeschema/sch_edit_frame.h b/eeschema/sch_edit_frame.h index 0eb0e6bc45..2f22300714 100644 --- a/eeschema/sch_edit_frame.h +++ b/eeschema/sch_edit_frame.h @@ -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 ); /** diff --git a/eeschema/sch_io_mgr.h b/eeschema/sch_io_mgr.h index 9657f5cfb8..fe2edd9ff4 100644 --- a/eeschema/sch_io_mgr.h +++ b/eeschema/sch_io_mgr.h @@ -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 ); /** diff --git a/eeschema/sch_legacy_plugin.cpp b/eeschema/sch_legacy_plugin.cpp index b4c18661bb..deb059b41b 100644 --- a/eeschema/sch_legacy_plugin.cpp +++ b/eeschema/sch_legacy_plugin.cpp @@ -1817,10 +1817,10 @@ std::shared_ptr 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 save_map( cmp ); - for( auto item : aScreen->Items() ) + for( auto item : screen->Items() ) save_map.insert( item ); diff --git a/eeschema/sch_legacy_plugin.h b/eeschema/sch_legacy_plugin.h index f3e65fefff..ded4bf81a5 100644 --- a/eeschema/sch_legacy_plugin.h +++ b/eeschema/sch_legacy_plugin.h @@ -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 ); diff --git a/eeschema/sch_plugin.cpp b/eeschema/sch_plugin.cpp index 67643bedfd..8b9f96039c 100644 --- a/eeschema/sch_plugin.cpp +++ b/eeschema/sch_plugin.cpp @@ -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. diff --git a/eeschema/sch_reference_list.h b/eeschema/sch_reference_list.h index 2bf48b59c8..4611e2a469 100644 --- a/eeschema/sch_reference_list.h +++ b/eeschema/sch_reference_list.h @@ -36,9 +36,6 @@ #include -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(); } diff --git a/eeschema/sch_screen.cpp b/eeschema/sch_screen.cpp index 71277197c6..a631fad3d3 100644 --- a/eeschema/sch_screen.cpp +++ b/eeschema/sch_screen.cpp @@ -177,6 +177,14 @@ void SCH_SCREEN::DecRefCount() } +bool SCH_SCREEN::HasItems( KICAD_T aItemType ) const +{ + EE_RTREE::EE_TYPE sheets = const_cast( 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( item ) ); diff --git a/eeschema/sch_screen.h b/eeschema/sch_screen.h index cac52e3e05..b58f38344f 100644 --- a/eeschema/sch_screen.h +++ b/eeschema/sch_screen.h @@ -45,6 +45,8 @@ #include #include +#include // COMPONENT_INSTANCE_REFERENCE +#include #include #include #include @@ -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 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 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); }; diff --git a/eeschema/sch_sexpr_parser.cpp b/eeschema/sch_sexpr_parser.cpp index 5b8b330f4f..448dfdcacb 100644 --- a/eeschema/sch_sexpr_parser.cpp +++ b/eeschema/sch_sexpr_parser.cpp @@ -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& 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& { 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& { 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& } } - 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& } -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( parseSchematicSymbol() ) ); + screen->Append( static_cast( parseSchematicSymbol() ) ); break; case T_image: - aScreen->Append( static_cast( parseImage() ) ); + screen->Append( static_cast( parseImage() ) ); break; case T_sheet: - aScreen->Append( static_cast( 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( sheet ) ); break; + } case T_junction: - aScreen->Append( static_cast( parseJunction() ) ); + screen->Append( static_cast( parseJunction() ) ); break; case T_no_connect: - aScreen->Append( static_cast( parseNoConnect() ) ); + screen->Append( static_cast( parseNoConnect() ) ); break; case T_bus_entry: - aScreen->Append( static_cast( parseBusEntry() ) ); + screen->Append( static_cast( parseBusEntry() ) ); break; case T_polyline: case T_bus: case T_wire: - aScreen->Append( static_cast( parseLine() ) ); + screen->Append( static_cast( parseLine() ) ); break; case T_text: case T_label: case T_global_label: case T_hierarchical_label: - aScreen->Append( static_cast( parseSchText() ) ); + screen->Append( static_cast( 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" ); } diff --git a/eeschema/sch_sexpr_parser.h b/eeschema/sch_sexpr_parser.h index 0d65e2866b..85bef18ab4 100644 --- a/eeschema/sch_sexpr_parser.h +++ b/eeschema/sch_sexpr_parser.h @@ -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& 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 diff --git a/eeschema/sch_sexpr_plugin.cpp b/eeschema/sch_sexpr_plugin.cpp index 745d6727c2..0c39846beb 100644 --- a/eeschema/sch_sexpr_plugin.cpp +++ b/eeschema/sch_sexpr_plugin.cpp @@ -73,6 +73,7 @@ #include // for MAX_UNIT_COUNT_PER_PACKAGE definition #include #include +#include #include #include // for PropPowerSymsOnly definintion. #include @@ -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( 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( 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( 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 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( 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" ); diff --git a/eeschema/sch_sexpr_plugin.h b/eeschema/sch_sexpr_plugin.h index 09ec4e98f8..fff2681f90 100644 --- a/eeschema/sch_sexpr_plugin.h +++ b/eeschema/sch_sexpr_plugin.h @@ -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 ); diff --git a/eeschema/sch_sheet.h b/eeschema/sch_sheet.h index 5091ff97f6..de92753dc4 100644 --- a/eeschema/sch_sheet.h +++ b/eeschema/sch_sheet.h @@ -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" */ diff --git a/eeschema/sch_sheet_path.cpp b/eeschema/sch_sheet_path.cpp index b584f44497..c6c4a4c537 100644 --- a/eeschema/sch_sheet_path.cpp +++ b/eeschema/sch_sheet_path.cpp @@ -34,6 +34,8 @@ #include #include #include +#include + #include #include #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& 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 SCH_SHEET_LIST::GetPaths() const +{ + std::vector paths; + + for( auto sheetPath : *this ) + paths.emplace_back( sheetPath.Path() ); + + return paths; +} + + +void SCH_SHEET_LIST::ReplaceLegacySheetPaths( const std::vector& 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( symbol )->ReplaceInstanceSheetPath( oldSheetPath, + newSheetPath ); + } + } +} + + void SHEETLIST_ERC_ITEMS_PROVIDER::SetSeverities( int aSeverities ) { m_severities = aSeverities; diff --git a/eeschema/sch_sheet_path.h b/eeschema/sch_sheet_path.h index fd39c54171..5fa83eefc6 100644 --- a/eeschema/sch_sheet_path.h +++ b/eeschema/sch_sheet_path.h @@ -37,6 +37,17 @@ #include +/** + * 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& aSymbolInstances ); + + std::vector 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& aOldSheetPaths ); }; diff --git a/eeschema/schematic.keywords b/eeschema/schematic.keywords index 9a7b2bdff8..ad66605538 100644 --- a/eeschema/schematic.keywords +++ b/eeschema/schematic.keywords @@ -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 diff --git a/eeschema/sheet.cpp b/eeschema/sheet.cpp index e245527da7..ce1cfced8e 100644 --- a/eeschema/sheet.cpp +++ b/eeschema/sheet.cpp @@ -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( newSheet->m_Uuid ) = KIID( 0 ); + wxFileName fileName( aFileName ); if( !fileName.IsAbsolute() && !fileName.MakeAbsolute() ) diff --git a/include/common.h b/include/common.h index 9fe99c68c5..7e4529ac56 100644 --- a/include/common.h +++ b/include/common.h @@ -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; diff --git a/include/trace_helpers.h b/include/trace_helpers.h index 1f56040090..84dabbe84a 100644 --- a/include/trace_helpers.h +++ b/include/trace_helpers.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2018 Wayne Stambaugh - * 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; + ///@} /**