diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt index a6c158d607..41220a259a 100644 --- a/eeschema/CMakeLists.txt +++ b/eeschema/CMakeLists.txt @@ -247,6 +247,7 @@ set( EESCHEMA_SRCS sch_plugins/sch_lib_plugin_cache.cpp sch_plugins/eagle/sch_eagle_plugin.cpp + sch_plugins/kicad/sch_sexpr_lib_plugin_cache.cpp sch_plugins/kicad/sch_sexpr_plugin_common.cpp sch_plugins/kicad/sch_sexpr_parser.cpp sch_plugins/kicad/sch_sexpr_plugin.cpp diff --git a/eeschema/sch_plugins/kicad/sch_sexpr_lib_plugin_cache.cpp b/eeschema/sch_plugins/kicad/sch_sexpr_lib_plugin_cache.cpp new file mode 100644 index 0000000000..706deba204 --- /dev/null +++ b/eeschema/sch_plugins/kicad/sch_sexpr_lib_plugin_cache.cpp @@ -0,0 +1,540 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 KiCad Developers, see AUTHORS.txt for contributors. + * + * @author Wayne Stambaugh + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sch_sexpr_lib_plugin_cache.h" +#include "sch_sexpr_plugin_common.h" +#include "sch_sexpr_parser.h" +#include +#include + + +SCH_SEXPR_PLUGIN_CACHE::SCH_SEXPR_PLUGIN_CACHE( const wxString& aFullPathAndFileName ) : + SCH_LIB_PLUGIN_CACHE( aFullPathAndFileName ) +{ + m_versionMajor = -1; +} + + +SCH_SEXPR_PLUGIN_CACHE::~SCH_SEXPR_PLUGIN_CACHE() +{ +} + + +void SCH_SEXPR_PLUGIN_CACHE::Load() +{ + if( !m_libFileName.FileExists() ) + { + THROW_IO_ERROR( wxString::Format( _( "Library file '%s' not found." ), + m_libFileName.GetFullPath() ) ); + } + + wxCHECK_RET( m_libFileName.IsAbsolute(), + wxString::Format( "Cannot use relative file paths in sexpr plugin to " + "open library '%s'.", m_libFileName.GetFullPath() ) ); + + // The current locale must use period as the decimal point. + wxCHECK2( wxLocale::GetInfo( wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER ) == ".", + LOCALE_IO toggle ); + + wxLogTrace( traceSchLegacyPlugin, "Loading sexpr symbol library file '%s'", + m_libFileName.GetFullPath() ); + + FILE_LINE_READER reader( m_libFileName.GetFullPath() ); + + SCH_SEXPR_PARSER parser( &reader ); + + parser.ParseLib( m_symbols ); + ++m_modHash; + + // Remember the file modification time of library file when the + // cache snapshot was made, so that in a networked environment we will + // reload the cache as needed. + m_fileModTime = GetLibModificationTime(); +} + + +void SCH_SEXPR_PLUGIN_CACHE::Save( const std::optional& aOpt ) +{ + if( !m_isModified ) + return; + + LOCALE_IO toggle; // toggles on, then off, the C locale. + + // Write through symlinks, don't replace them. + wxFileName fn = GetRealFile(); + + auto formatter = std::make_unique( fn.GetFullPath() ); + + formatter->Print( 0, "(kicad_symbol_lib (version %d) (generator kicad_symbol_editor)\n", + SEXPR_SYMBOL_LIB_FILE_VERSION ); + + for( auto parent : m_symbols ) + { + // Save the root symbol first so alias can inherit from them. + if( parent.second->IsRoot() ) + { + SaveSymbol( parent.second, *formatter.get(), 1 ); + + // Save all of the aliases associated with the current root symbol. + for( auto alias : m_symbols ) + { + if( !alias.second->IsAlias() ) + continue; + + std::shared_ptr aliasParent = alias.second->GetParent().lock(); + + if( aliasParent.get() != parent.second ) + continue; + + SaveSymbol( alias.second, *formatter.get(), 1 ); + } + } + } + + formatter->Print( 0, ")\n" ); + + formatter.reset(); + + m_fileModTime = fn.GetModificationTime(); + m_isModified = false; +} + + +void SCH_SEXPR_PLUGIN_CACHE::SaveSymbol( LIB_SYMBOL* aSymbol, OUTPUTFORMATTER& aFormatter, + int aNestLevel, const wxString& aLibName ) +{ + wxCHECK_RET( aSymbol, "Invalid LIB_SYMBOL pointer." ); + + // The current locale must use period as the decimal point. + wxCHECK2( wxLocale::GetInfo( wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER ) == ".", + LOCALE_IO toggle ); + + int nextFreeFieldId = MANDATORY_FIELDS; + std::vector fields; + std::string name = aFormatter.Quotew( aSymbol->GetLibId().GetLibItemName().wx_str() ); + std::string unitName = aSymbol->GetLibId().GetLibItemName(); + + if( !aLibName.IsEmpty() ) + { + name = aFormatter.Quotew( aLibName ); + + LIB_ID unitId; + + wxCHECK2( unitId.Parse( aLibName ) < 0, /* do nothing */ ); + + unitName = unitId.GetLibItemName(); + } + + if( aSymbol->IsRoot() ) + { + aFormatter.Print( aNestLevel, "(symbol %s", name.c_str() ); + + if( aSymbol->IsPower() ) + aFormatter.Print( 0, " (power)" ); + + // TODO: add uuid token here. + + // TODO: add anchor position token here. + + if( !aSymbol->ShowPinNumbers() ) + aFormatter.Print( 0, " (pin_numbers hide)" ); + + if( aSymbol->GetPinNameOffset() != Mils2iu( DEFAULT_PIN_NAME_OFFSET ) + || !aSymbol->ShowPinNames() ) + { + aFormatter.Print( 0, " (pin_names" ); + + if( aSymbol->GetPinNameOffset() != Mils2iu( DEFAULT_PIN_NAME_OFFSET ) ) + aFormatter.Print( 0, " (offset %s)", + FormatInternalUnits( aSymbol->GetPinNameOffset() ).c_str() ); + + if( !aSymbol->ShowPinNames() ) + aFormatter.Print( 0, " hide" ); + + aFormatter.Print( 0, ")" ); + } + + aFormatter.Print( 0, " (in_bom %s)", ( aSymbol->GetIncludeInBom() ) ? "yes" : "no" ); + aFormatter.Print( 0, " (on_board %s)", ( aSymbol->GetIncludeOnBoard() ) ? "yes" : "no" ); + + // TODO: add atomic token here. + + // TODO: add required token here." + + aFormatter.Print( 0, "\n" ); + + aSymbol->GetFields( fields ); + + for( LIB_FIELD* field : fields ) + saveField( field, aFormatter, aNestLevel + 1 ); + + nextFreeFieldId = aSymbol->GetNextAvailableFieldId(); + + // @todo At some point in the future the lock status (all units interchangeable) should + // be set deterministically. For now a custom lock property is used to preserve the + // locked flag state. + if( aSymbol->UnitsLocked() ) + { + LIB_FIELD locked( nextFreeFieldId, "ki_locked" ); + saveField( &locked, aFormatter, aNestLevel + 1 ); + nextFreeFieldId += 1; + } + + saveDcmInfoAsFields( aSymbol, aFormatter, nextFreeFieldId, aNestLevel ); + + // Save the draw items grouped by units. + std::vector units = aSymbol->GetUnitDrawItems(); + std::sort( units.begin(), units.end(), + []( const LIB_SYMBOL_UNIT& a, const LIB_SYMBOL_UNIT& b ) + { + if( a.m_unit == b.m_unit ) + return a.m_convert < b.m_convert; + + return a.m_unit < b.m_unit; + } ); + + for( auto unit : units ) + { + // Add quotes and escape chars like ") to the UTF8 unitName string + name = aFormatter.Quotes( unitName ); + name.pop_back(); // Remove last char: the quote ending the string. + + aFormatter.Print( aNestLevel + 1, "(symbol %s_%d_%d\"\n", + name.c_str(), unit.m_unit, unit.m_convert ); + + // Enforce item ordering + auto cmp = + []( const LIB_ITEM* a, const LIB_ITEM* b ) + { + return *a < *b; + }; + + std::multiset save_map( cmp ); + + for( LIB_ITEM* item : unit.m_items ) + save_map.insert( item ); + + for( LIB_ITEM* item : save_map ) + saveSymbolDrawItem( item, aFormatter, aNestLevel + 2 ); + + aFormatter.Print( aNestLevel + 1, ")\n" ); + } + } + else + { + std::shared_ptr parent = aSymbol->GetParent().lock(); + + wxASSERT( parent ); + + aFormatter.Print( aNestLevel, "(symbol %s (extends %s)\n", + name.c_str(), + aFormatter.Quotew( parent->GetName() ).c_str() ); + + aSymbol->GetFields( fields ); + + for( LIB_FIELD* field : fields ) + saveField( field, aFormatter, aNestLevel + 1 ); + + nextFreeFieldId = aSymbol->GetNextAvailableFieldId(); + + saveDcmInfoAsFields( aSymbol, aFormatter, nextFreeFieldId, aNestLevel ); + } + + aFormatter.Print( aNestLevel, ")\n" ); +} + + +void SCH_SEXPR_PLUGIN_CACHE::saveDcmInfoAsFields( LIB_SYMBOL* aSymbol, OUTPUTFORMATTER& aFormatter, + int& aNextFreeFieldId, int aNestLevel ) +{ + wxCHECK_RET( aSymbol, "Invalid LIB_SYMBOL pointer." ); + + if( !aSymbol->GetKeyWords().IsEmpty() ) + { + LIB_FIELD keywords( aNextFreeFieldId, wxString( "ki_keywords" ) ); + keywords.SetVisible( false ); + keywords.SetText( aSymbol->GetKeyWords() ); + saveField( &keywords, aFormatter, aNestLevel + 1 ); + aNextFreeFieldId += 1; + } + + if( !aSymbol->GetDescription().IsEmpty() ) + { + LIB_FIELD description( aNextFreeFieldId, wxString( "ki_description" ) ); + description.SetVisible( false ); + description.SetText( aSymbol->GetDescription() ); + saveField( &description, aFormatter, aNestLevel + 1 ); + aNextFreeFieldId += 1; + } + + wxArrayString fpFilters = aSymbol->GetFPFilters(); + + if( !fpFilters.IsEmpty() ) + { + wxString tmp; + + for( auto filter : fpFilters ) + { + // Spaces are not handled in fp filter names so escape spaces if any + wxString curr_filter = EscapeString( filter, ESCAPE_CONTEXT::CTX_NO_SPACE ); + + if( tmp.IsEmpty() ) + tmp = curr_filter; + else + tmp += " " + curr_filter; + } + + LIB_FIELD description( aNextFreeFieldId, wxString( "ki_fp_filters" ) ); + description.SetVisible( false ); + description.SetText( tmp ); + saveField( &description, aFormatter, aNestLevel + 1 ); + aNextFreeFieldId += 1; + } +} + + +void SCH_SEXPR_PLUGIN_CACHE::saveSymbolDrawItem( LIB_ITEM* aItem, OUTPUTFORMATTER& aFormatter, + int aNestLevel ) +{ + wxCHECK_RET( aItem, "Invalid LIB_ITEM pointer." ); + + switch( aItem->Type() ) + { + case LIB_SHAPE_T: + { + LIB_SHAPE* shape = static_cast( aItem ); + STROKE_PARAMS stroke; + FILL_T fillMode = shape->GetFillMode(); + bool isPrivate = shape->IsPrivate(); + + stroke.SetWidth( shape->GetWidth() ); + + COLOR4D fillColor = shape->GetFillColor(); + + switch( shape->GetShape() ) + { + case SHAPE_T::ARC: + formatArc( &aFormatter, aNestLevel, shape, isPrivate, stroke, fillMode, fillColor ); + break; + + case SHAPE_T::CIRCLE: + formatCircle( &aFormatter, aNestLevel, shape, isPrivate, stroke, fillMode, fillColor ); + break; + + case SHAPE_T::RECT: + formatRect( &aFormatter, aNestLevel, shape, isPrivate, stroke, fillMode, fillColor ); + break; + + case SHAPE_T::BEZIER: + formatBezier(&aFormatter, aNestLevel, shape, isPrivate, stroke, fillMode, fillColor ); + break; + + case SHAPE_T::POLY: + formatPoly( &aFormatter, aNestLevel, shape, isPrivate, stroke, fillMode, fillColor ); + break; + + default: + UNIMPLEMENTED_FOR( shape->SHAPE_T_asString() ); + } + + break; + } + + case LIB_PIN_T: + savePin( static_cast( aItem ), aFormatter, aNestLevel ); + break; + + case LIB_TEXT_T: + saveText( static_cast( aItem ), aFormatter, aNestLevel ); + break; + + case LIB_TEXTBOX_T: + saveTextBox( static_cast( aItem ), aFormatter, aNestLevel ); + break; + + default: + UNIMPLEMENTED_FOR( aItem->GetClass() ); + } +} + + +void SCH_SEXPR_PLUGIN_CACHE::saveField( LIB_FIELD* aField, OUTPUTFORMATTER& aFormatter, + int aNestLevel ) +{ + wxCHECK_RET( aField && aField->Type() == LIB_FIELD_T, "Invalid LIB_FIELD object." ); + + wxString fieldName = aField->GetName(); + + if( aField->GetId() >= 0 && aField->GetId() < MANDATORY_FIELDS ) + fieldName = TEMPLATE_FIELDNAME::GetDefaultFieldName( aField->GetId(), false ); + + aFormatter.Print( aNestLevel, "(property %s %s (id %d) (at %s %s %g)\n", + aFormatter.Quotew( fieldName ).c_str(), + aFormatter.Quotew( aField->GetText() ).c_str(), + aField->GetId(), + FormatInternalUnits( aField->GetPosition().x ).c_str(), + FormatInternalUnits( aField->GetPosition().y ).c_str(), + aField->GetTextAngle().AsDegrees() ); + + aField->Format( &aFormatter, aNestLevel, 0 ); + aFormatter.Print( aNestLevel, ")\n" ); +} + + +void SCH_SEXPR_PLUGIN_CACHE::savePin( LIB_PIN* aPin, OUTPUTFORMATTER& aFormatter, int aNestLevel ) +{ + wxCHECK_RET( aPin && aPin->Type() == LIB_PIN_T, "Invalid LIB_PIN object." ); + + aPin->ClearFlags( IS_CHANGED ); + + aFormatter.Print( aNestLevel, "(pin %s %s (at %s %s %s) (length %s)", + getPinElectricalTypeToken( aPin->GetType() ), + getPinShapeToken( aPin->GetShape() ), + FormatInternalUnits( aPin->GetPosition().x ).c_str(), + FormatInternalUnits( aPin->GetPosition().y ).c_str(), + FormatAngle( getPinAngle( aPin->GetOrientation() ) ).c_str(), + FormatInternalUnits( aPin->GetLength() ).c_str() ); + + if( !aPin->IsVisible() ) + aFormatter.Print( 0, " hide\n" ); + else + aFormatter.Print( 0, "\n" ); + + // This follows the EDA_TEXT effects formatting for future expansion. + aFormatter.Print( aNestLevel + 1, "(name %s (effects (font (size %s %s))))\n", + aFormatter.Quotew( aPin->GetName() ).c_str(), + FormatInternalUnits( aPin->GetNameTextSize() ).c_str(), + FormatInternalUnits( aPin->GetNameTextSize() ).c_str() ); + + aFormatter.Print( aNestLevel + 1, "(number %s (effects (font (size %s %s))))\n", + aFormatter.Quotew( aPin->GetNumber() ).c_str(), + FormatInternalUnits( aPin->GetNumberTextSize() ).c_str(), + FormatInternalUnits( aPin->GetNumberTextSize() ).c_str() ); + + + for( const std::pair& alt : aPin->GetAlternates() ) + { + aFormatter.Print( aNestLevel + 1, "(alternate %s %s %s)\n", + aFormatter.Quotew( alt.second.m_Name ).c_str(), + getPinElectricalTypeToken( alt.second.m_Type ), + getPinShapeToken( alt.second.m_Shape ) ); + } + + aFormatter.Print( aNestLevel, ")\n" ); +} + + +void SCH_SEXPR_PLUGIN_CACHE::saveText( LIB_TEXT* aText, OUTPUTFORMATTER& aFormatter, + int aNestLevel ) +{ + wxCHECK_RET( aText && aText->Type() == LIB_TEXT_T, "Invalid LIB_TEXT object." ); + + aFormatter.Print( aNestLevel, "(text%s %s (at %s %s %g)\n", + aText->IsPrivate() ? " private" : "", + aFormatter.Quotew( aText->GetText() ).c_str(), + FormatInternalUnits( aText->GetPosition().x ).c_str(), + FormatInternalUnits( aText->GetPosition().y ).c_str(), + (double) aText->GetTextAngle().AsTenthsOfADegree() ); + + aText->EDA_TEXT::Format( &aFormatter, aNestLevel, 0 ); + aFormatter.Print( aNestLevel, ")\n" ); +} + + +void SCH_SEXPR_PLUGIN_CACHE::saveTextBox( LIB_TEXTBOX* aTextBox, OUTPUTFORMATTER& aFormatter, + int aNestLevel ) +{ + wxCHECK_RET( aTextBox && aTextBox->Type() == LIB_TEXTBOX_T, "Invalid LIB_TEXTBOX object." ); + + aFormatter.Print( aNestLevel, "(text_box%s %s\n", + aTextBox->IsPrivate() ? " private" : "", + aFormatter.Quotew( aTextBox->GetText() ).c_str() ); + + aFormatter.Print( aNestLevel + 1, "(start %s %s) (end %s %s)\n", + FormatInternalUnits( aTextBox->GetStart().x ).c_str(), + FormatInternalUnits( aTextBox->GetStart().y ).c_str(), + FormatInternalUnits( aTextBox->GetEnd().x ).c_str(), + FormatInternalUnits( aTextBox->GetEnd().y ).c_str() ); + + aTextBox->GetStroke().Format( &aFormatter, aNestLevel + 1 ); + aFormatter.Print( 0, "\n" ); + + formatFill( &aFormatter, aNestLevel + 1, aTextBox->GetFillMode(), aTextBox->GetFillColor() ); + aFormatter.Print( 0, "\n" ); + + aTextBox->EDA_TEXT::Format( &aFormatter, aNestLevel, 0 ); + aFormatter.Print( aNestLevel, ")\n" ); +} + + +void SCH_SEXPR_PLUGIN_CACHE::DeleteSymbol( const wxString& aSymbolName ) +{ + LIB_SYMBOL_MAP::iterator it = m_symbols.find( aSymbolName ); + + if( it == m_symbols.end() ) + THROW_IO_ERROR( wxString::Format( _( "library %s does not contain a symbol named %s" ), + m_libFileName.GetFullName(), aSymbolName ) ); + + LIB_SYMBOL* symbol = it->second; + + if( symbol->IsRoot() ) + { + LIB_SYMBOL* rootSymbol = symbol; + + // Remove the root symbol and all its children. + m_symbols.erase( it ); + + LIB_SYMBOL_MAP::iterator it1 = m_symbols.begin(); + + while( it1 != m_symbols.end() ) + { + if( it1->second->IsAlias() + && it1->second->GetParent().lock() == rootSymbol->SharedPtr() ) + { + delete it1->second; + it1 = m_symbols.erase( it1 ); + } + else + { + it1++; + } + } + + delete rootSymbol; + } + else + { + // Just remove the alias. + m_symbols.erase( it ); + delete symbol; + } + + ++m_modHash; + m_isModified = true; +} diff --git a/eeschema/sch_plugins/kicad/sch_sexpr_lib_plugin_cache.h b/eeschema/sch_plugins/kicad/sch_sexpr_lib_plugin_cache.h new file mode 100644 index 0000000000..910229ac5d --- /dev/null +++ b/eeschema/sch_plugins/kicad/sch_sexpr_lib_plugin_cache.h @@ -0,0 +1,76 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 KiCad Developers, see AUTHORS.txt for contributors. + * + * @author Wayne Stambaugh + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef _SCH_SEXPR_LIB_PLUGIN_CACHE_ +#define _SCH_SEXPR_LIB_PLUGIN_CACHE_ + +#include "../sch_lib_plugin_cache.h" + +class FILE_LINE_READER; +class LIB_FIELD; +class LIB_ITEM; +class LIB_PIN; +class LIB_TEXT; +class LIB_TEXTBOX; +class LINE_READER; +class SCH_SEXPR_PLUGIN; + +/** + * A cache assistant for the KiCad s-expression symbol libraries. + */ +class SCH_SEXPR_PLUGIN_CACHE : public SCH_LIB_PLUGIN_CACHE +{ +public: + SCH_SEXPR_PLUGIN_CACHE( const wxString& aLibraryPath ); + virtual ~SCH_SEXPR_PLUGIN_CACHE(); + + // Most all functions in this class throw IO_ERROR exceptions. There are no + // error codes nor user interface calls from here, nor in any SCH_PLUGIN objects. + // Catch these exceptions higher up please. + + /// Save the entire library to file m_libFileName; + void Save( const std::optional& aOpt = std::nullopt ) override; + + void Load() override; + + void DeleteSymbol( const wxString& aName ) override; + + static void SaveSymbol( LIB_SYMBOL* aSymbol, OUTPUTFORMATTER& aFormatter, + int aNestLevel = 0, const wxString& aLibName = wxEmptyString ); + +private: + friend SCH_SEXPR_PLUGIN; + + static void saveSymbolDrawItem( LIB_ITEM* aItem, OUTPUTFORMATTER& aFormatter, + int aNestLevel ); + static void saveField( LIB_FIELD* aField, OUTPUTFORMATTER& aFormatter, int aNestLevel ); + static void savePin( LIB_PIN* aPin, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0 ); + static void saveText( LIB_TEXT* aText, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0 ); + static void saveTextBox( LIB_TEXTBOX* aTextBox, OUTPUTFORMATTER& aFormatter, + int aNestLevel = 0 ); + + static void saveDcmInfoAsFields( LIB_SYMBOL* aSymbol, OUTPUTFORMATTER& aFormatter, + int& aNextFreeFieldId, int aNestLevel ); + + int m_versionMajor; +}; + +#endif // _SCH_SEXPR_LIB_PLUGIN_CACHE_ diff --git a/eeschema/sch_plugins/kicad/sch_sexpr_parser.h b/eeschema/sch_plugins/kicad/sch_sexpr_parser.h index 232bcf3665..4791db2fa4 100644 --- a/eeschema/sch_plugins/kicad/sch_sexpr_parser.h +++ b/eeschema/sch_plugins/kicad/sch_sexpr_parser.h @@ -49,6 +49,7 @@ class SCH_BITMAP; class SCH_BUS_WIRE_ENTRY; class SCH_SYMBOL; class SCH_FIELD; +class SCH_ITEM; class SCH_SHAPE; class SCH_JUNCTION; class SCH_LINE; diff --git a/eeschema/sch_plugins/kicad/sch_sexpr_plugin.cpp b/eeschema/sch_plugins/kicad/sch_sexpr_plugin.cpp index 237ae7a940..13d798c2a3 100644 --- a/eeschema/sch_plugins/kicad/sch_sexpr_plugin.cpp +++ b/eeschema/sch_plugins/kicad/sch_sexpr_plugin.cpp @@ -56,6 +56,7 @@ #include #include #include +#include "sch_sexpr_lib_plugin_cache.h" #include "sch_sexpr_plugin_common.h" #include // for PropPowerSymsOnly definition. #include @@ -71,80 +72,6 @@ using namespace TSCHEMATIC_T; reader.LineNumber(), pos - reader.Line() ) -/** - * A cache assistant for the symbol library portion of the #SCH_PLUGIN API, and only for the - * #SCH_SEXPR_PLUGIN, so therefore is private to this implementation file, i.e. not placed - * into a header. - */ -class SCH_SEXPR_PLUGIN_CACHE -{ - static int m_modHash; // Keep track of the modification status of the library. - - wxString m_fileName; // Absolute path and file name. - wxFileName m_libFileName; // Absolute path and file name is required here. - wxDateTime m_fileModTime; - LIB_SYMBOL_MAP m_symbols; // Map of names of #LIB_SYMBOL pointers. - bool m_isWritable; - bool m_isModified; - int m_versionMajor; - SCH_LIB_TYPE m_libType; // Is this cache a symbol or symbol library. - - LIB_SYMBOL* removeSymbol( LIB_SYMBOL* aAlias ); - - static void saveSymbolDrawItem( LIB_ITEM* aItem, OUTPUTFORMATTER& aFormatter, - int aNestLevel ); - static void saveField( LIB_FIELD* aField, OUTPUTFORMATTER& aFormatter, int aNestLevel ); - static void savePin( LIB_PIN* aPin, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0 ); - static void saveText( LIB_TEXT* aText, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0 ); - static void saveTextBox( LIB_TEXTBOX* aTextBox, OUTPUTFORMATTER& aFormatter, - int aNestLevel = 0 ); - - static void saveDcmInfoAsFields( LIB_SYMBOL* aSymbol, OUTPUTFORMATTER& aFormatter, - int& aNextFreeFieldId, int aNestLevel ); - - friend SCH_SEXPR_PLUGIN; - -public: - SCH_SEXPR_PLUGIN_CACHE( const wxString& aLibraryPath ); - ~SCH_SEXPR_PLUGIN_CACHE(); - - int GetModifyHash() const { return m_modHash; } - - // Most all functions in this class throw IO_ERROR exceptions. There are no - // error codes nor user interface calls from here, nor in any SCH_PLUGIN objects. - // Catch these exceptions higher up please. - - /// Save the entire library to file m_libFileName; - void Save(); - - void Load(); - - void AddSymbol( const LIB_SYMBOL* aSymbol ); - - void DeleteSymbol( const wxString& aName ); - - // If m_libFileName is a symlink follow it to the real source file - wxFileName GetRealFile() const; - - wxDateTime GetLibModificationTime(); - - bool IsFile( const wxString& aFullPathAndFileName ) const; - - bool IsFileChanged() const; - - void SetModified( bool aModified = true ) { m_isModified = aModified; } - - wxString GetLogicalName() const { return m_libFileName.GetName(); } - - void SetFileName( const wxString& aFileName ) { m_libFileName = aFileName; } - - wxString GetFileName() const { return m_libFileName.GetFullPath(); } - - static void SaveSymbol( LIB_SYMBOL* aSymbol, OUTPUTFORMATTER& aFormatter, - int aNestLevel = 0, const wxString& aLibName = wxEmptyString ); -}; - - SCH_SEXPR_PLUGIN::SCH_SEXPR_PLUGIN() : m_progressReporter( nullptr ) { @@ -1255,644 +1182,6 @@ void SCH_SEXPR_PLUGIN::saveInstances( const std::vector& aSh } -int SCH_SEXPR_PLUGIN_CACHE::m_modHash = 1; // starts at 1 and goes up - - -SCH_SEXPR_PLUGIN_CACHE::SCH_SEXPR_PLUGIN_CACHE( const wxString& aFullPathAndFileName ) : - m_fileName( aFullPathAndFileName ), - m_libFileName( aFullPathAndFileName ), - m_isWritable( true ), - m_isModified( false ) -{ - m_versionMajor = -1; - m_libType = SCH_LIB_TYPE::LT_EESCHEMA; -} - - -SCH_SEXPR_PLUGIN_CACHE::~SCH_SEXPR_PLUGIN_CACHE() -{ - // When the cache is destroyed, all of the alias objects on the heap should be deleted. - for( LIB_SYMBOL_MAP::iterator it = m_symbols.begin(); it != m_symbols.end(); ++it ) - delete it->second; - - m_symbols.clear(); -} - - -// If m_libFileName is a symlink follow it to the real source file -wxFileName SCH_SEXPR_PLUGIN_CACHE::GetRealFile() const -{ - wxFileName fn( m_libFileName ); - WX_FILENAME::ResolvePossibleSymlinks( fn ); - return fn; -} - - -wxDateTime SCH_SEXPR_PLUGIN_CACHE::GetLibModificationTime() -{ - wxFileName fn = GetRealFile(); - - // update the writable flag while we have a wxFileName, in a network this - // is possibly quite dynamic anyway. - m_isWritable = fn.IsFileWritable(); - - return fn.GetModificationTime(); -} - - -bool SCH_SEXPR_PLUGIN_CACHE::IsFile( const wxString& aFullPathAndFileName ) const -{ - return m_fileName == aFullPathAndFileName; -} - - -bool SCH_SEXPR_PLUGIN_CACHE::IsFileChanged() const -{ - wxFileName fn = GetRealFile(); - - if( m_fileModTime.IsValid() && fn.IsOk() && fn.FileExists() ) - return fn.GetModificationTime() != m_fileModTime; - - return false; -} - - -LIB_SYMBOL* SCH_SEXPR_PLUGIN_CACHE::removeSymbol( LIB_SYMBOL* aSymbol ) -{ - wxCHECK_MSG( aSymbol != nullptr, nullptr, "NULL pointer cannot be removed from library." ); - - LIB_SYMBOL* firstChild = nullptr; - LIB_SYMBOL_MAP::iterator it = m_symbols.find( aSymbol->GetName() ); - - if( it == m_symbols.end() ) - return nullptr; - - // If the entry pointer doesn't match the name it is mapped to in the library, we - // have done something terribly wrong. - wxCHECK_MSG( *it->second == aSymbol, nullptr, - "Pointer mismatch while attempting to remove alias entry <" + aSymbol->GetName() + - "> from library cache <" + m_libFileName.GetName() + ">." ); - - // If the symbol is a root symbol used by other symbols find the first alias that uses - // the root symbol and make it the new root. - if( aSymbol->IsRoot() ) - { - for( auto entry : m_symbols ) - { - if( entry.second->IsAlias() - && entry.second->GetParent().lock() == aSymbol->SharedPtr() ) - { - firstChild = entry.second; - break; - } - } - - if( firstChild ) - { - for( LIB_ITEM& drawItem : aSymbol->GetDrawItems() ) - { - if( drawItem.Type() == LIB_FIELD_T ) - { - LIB_FIELD& field = static_cast( drawItem ); - - if( firstChild->FindField( field.GetCanonicalName() ) ) - continue; - } - - LIB_ITEM* newItem = (LIB_ITEM*) drawItem.Clone(); - drawItem.SetParent( firstChild ); - firstChild->AddDrawItem( newItem ); - } - - // Reparent the remaining aliases. - for( auto entry : m_symbols ) - { - if( entry.second->IsAlias() - && entry.second->GetParent().lock() == aSymbol->SharedPtr() ) - entry.second->SetParent( firstChild ); - } - } - } - - m_symbols.erase( it ); - delete aSymbol; - m_isModified = true; - ++m_modHash; - return firstChild; -} - - -void SCH_SEXPR_PLUGIN_CACHE::AddSymbol( const LIB_SYMBOL* aSymbol ) -{ - // aSymbol is cloned in SYMBOL_LIB::AddSymbol(). The cache takes ownership of aSymbol. - wxString name = aSymbol->GetName(); - LIB_SYMBOL_MAP::iterator it = m_symbols.find( name ); - - if( it != m_symbols.end() ) - { - removeSymbol( it->second ); - } - - m_symbols[ name ] = const_cast< LIB_SYMBOL* >( aSymbol ); - m_isModified = true; - ++m_modHash; -} - - -void SCH_SEXPR_PLUGIN_CACHE::Load() -{ - if( !m_libFileName.FileExists() ) - { - THROW_IO_ERROR( wxString::Format( _( "Library file '%s' not found." ), - m_libFileName.GetFullPath() ) ); - } - - wxCHECK_RET( m_libFileName.IsAbsolute(), - wxString::Format( "Cannot use relative file paths in sexpr plugin to " - "open library '%s'.", m_libFileName.GetFullPath() ) ); - - // The current locale must use period as the decimal point. - wxCHECK2( wxLocale::GetInfo( wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER ) == ".", - LOCALE_IO toggle ); - - wxLogTrace( traceSchLegacyPlugin, "Loading sexpr symbol library file '%s'", - m_libFileName.GetFullPath() ); - - FILE_LINE_READER reader( m_libFileName.GetFullPath() ); - - SCH_SEXPR_PARSER parser( &reader ); - - parser.ParseLib( m_symbols ); - ++m_modHash; - - // Remember the file modification time of library file when the - // cache snapshot was made, so that in a networked environment we will - // reload the cache as needed. - m_fileModTime = GetLibModificationTime(); -} - - -void SCH_SEXPR_PLUGIN_CACHE::Save() -{ - if( !m_isModified ) - return; - - LOCALE_IO toggle; // toggles on, then off, the C locale. - - // Write through symlinks, don't replace them. - wxFileName fn = GetRealFile(); - - auto formatter = std::make_unique( fn.GetFullPath() ); - - formatter->Print( 0, "(kicad_symbol_lib (version %d) (generator kicad_symbol_editor)\n", - SEXPR_SYMBOL_LIB_FILE_VERSION ); - - for( auto parent : m_symbols ) - { - // Save the root symbol first so alias can inherit from them. - if( parent.second->IsRoot() ) - { - SaveSymbol( parent.second, *formatter.get(), 1 ); - - // Save all of the aliases associated with the current root symbol. - for( auto alias : m_symbols ) - { - if( !alias.second->IsAlias() ) - continue; - - std::shared_ptr aliasParent = alias.second->GetParent().lock(); - - if( aliasParent.get() != parent.second ) - continue; - - SaveSymbol( alias.second, *formatter.get(), 1 ); - } - } - } - - formatter->Print( 0, ")\n" ); - - formatter.reset(); - - m_fileModTime = fn.GetModificationTime(); - m_isModified = false; -} - - -void SCH_SEXPR_PLUGIN_CACHE::SaveSymbol( LIB_SYMBOL* aSymbol, OUTPUTFORMATTER& aFormatter, - int aNestLevel, const wxString& aLibName ) -{ - wxCHECK_RET( aSymbol, "Invalid LIB_SYMBOL pointer." ); - - // The current locale must use period as the decimal point. - wxCHECK2( wxLocale::GetInfo( wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER ) == ".", - LOCALE_IO toggle ); - - int nextFreeFieldId = MANDATORY_FIELDS; - std::vector fields; - std::string name = aFormatter.Quotew( aSymbol->GetLibId().GetLibItemName().wx_str() ); - std::string unitName = aSymbol->GetLibId().GetLibItemName(); - - if( !aLibName.IsEmpty() ) - { - name = aFormatter.Quotew( aLibName ); - - LIB_ID unitId; - - wxCHECK2( unitId.Parse( aLibName ) < 0, /* do nothing */ ); - - unitName = unitId.GetLibItemName(); - } - - if( aSymbol->IsRoot() ) - { - aFormatter.Print( aNestLevel, "(symbol %s", name.c_str() ); - - if( aSymbol->IsPower() ) - aFormatter.Print( 0, " (power)" ); - - // TODO: add uuid token here. - - // TODO: add anchor position token here. - - if( !aSymbol->ShowPinNumbers() ) - aFormatter.Print( 0, " (pin_numbers hide)" ); - - if( aSymbol->GetPinNameOffset() != Mils2iu( DEFAULT_PIN_NAME_OFFSET ) - || !aSymbol->ShowPinNames() ) - { - aFormatter.Print( 0, " (pin_names" ); - - if( aSymbol->GetPinNameOffset() != Mils2iu( DEFAULT_PIN_NAME_OFFSET ) ) - aFormatter.Print( 0, " (offset %s)", - FormatInternalUnits( aSymbol->GetPinNameOffset() ).c_str() ); - - if( !aSymbol->ShowPinNames() ) - aFormatter.Print( 0, " hide" ); - - aFormatter.Print( 0, ")" ); - } - - aFormatter.Print( 0, " (in_bom %s)", ( aSymbol->GetIncludeInBom() ) ? "yes" : "no" ); - aFormatter.Print( 0, " (on_board %s)", ( aSymbol->GetIncludeOnBoard() ) ? "yes" : "no" ); - - // TODO: add atomic token here. - - // TODO: add required token here." - - aFormatter.Print( 0, "\n" ); - - aSymbol->GetFields( fields ); - - for( LIB_FIELD* field : fields ) - saveField( field, aFormatter, aNestLevel + 1 ); - - nextFreeFieldId = aSymbol->GetNextAvailableFieldId(); - - // @todo At some point in the future the lock status (all units interchangeable) should - // be set deterministically. For now a custom lock property is used to preserve the - // locked flag state. - if( aSymbol->UnitsLocked() ) - { - LIB_FIELD locked( nextFreeFieldId, "ki_locked" ); - saveField( &locked, aFormatter, aNestLevel + 1 ); - nextFreeFieldId += 1; - } - - saveDcmInfoAsFields( aSymbol, aFormatter, nextFreeFieldId, aNestLevel ); - - // Save the draw items grouped by units. - std::vector units = aSymbol->GetUnitDrawItems(); - std::sort( units.begin(), units.end(), - []( const LIB_SYMBOL_UNIT& a, const LIB_SYMBOL_UNIT& b ) - { - if( a.m_unit == b.m_unit ) - return a.m_convert < b.m_convert; - - return a.m_unit < b.m_unit; - } ); - - for( auto unit : units ) - { - // Add quotes and escape chars like ") to the UTF8 unitName string - name = aFormatter.Quotes( unitName ); - name.pop_back(); // Remove last char: the quote ending the string. - - aFormatter.Print( aNestLevel + 1, "(symbol %s_%d_%d\"\n", - name.c_str(), unit.m_unit, unit.m_convert ); - - // Enforce item ordering - auto cmp = - []( const LIB_ITEM* a, const LIB_ITEM* b ) - { - return *a < *b; - }; - - std::multiset save_map( cmp ); - - for( LIB_ITEM* item : unit.m_items ) - save_map.insert( item ); - - for( LIB_ITEM* item : save_map ) - saveSymbolDrawItem( item, aFormatter, aNestLevel + 2 ); - - aFormatter.Print( aNestLevel + 1, ")\n" ); - } - } - else - { - std::shared_ptr parent = aSymbol->GetParent().lock(); - - wxASSERT( parent ); - - aFormatter.Print( aNestLevel, "(symbol %s (extends %s)\n", - name.c_str(), - aFormatter.Quotew( parent->GetName() ).c_str() ); - - aSymbol->GetFields( fields ); - - for( LIB_FIELD* field : fields ) - saveField( field, aFormatter, aNestLevel + 1 ); - - nextFreeFieldId = aSymbol->GetNextAvailableFieldId(); - - saveDcmInfoAsFields( aSymbol, aFormatter, nextFreeFieldId, aNestLevel ); - } - - aFormatter.Print( aNestLevel, ")\n" ); -} - - -void SCH_SEXPR_PLUGIN_CACHE::saveDcmInfoAsFields( LIB_SYMBOL* aSymbol, OUTPUTFORMATTER& aFormatter, - int& aNextFreeFieldId, int aNestLevel ) -{ - wxCHECK_RET( aSymbol, "Invalid LIB_SYMBOL pointer." ); - - if( !aSymbol->GetKeyWords().IsEmpty() ) - { - LIB_FIELD keywords( aNextFreeFieldId, wxString( "ki_keywords" ) ); - keywords.SetVisible( false ); - keywords.SetText( aSymbol->GetKeyWords() ); - saveField( &keywords, aFormatter, aNestLevel + 1 ); - aNextFreeFieldId += 1; - } - - if( !aSymbol->GetDescription().IsEmpty() ) - { - LIB_FIELD description( aNextFreeFieldId, wxString( "ki_description" ) ); - description.SetVisible( false ); - description.SetText( aSymbol->GetDescription() ); - saveField( &description, aFormatter, aNestLevel + 1 ); - aNextFreeFieldId += 1; - } - - wxArrayString fpFilters = aSymbol->GetFPFilters(); - - if( !fpFilters.IsEmpty() ) - { - wxString tmp; - - for( auto filter : fpFilters ) - { - // Spaces are not handled in fp filter names so escape spaces if any - wxString curr_filter = EscapeString( filter, ESCAPE_CONTEXT::CTX_NO_SPACE ); - - if( tmp.IsEmpty() ) - tmp = curr_filter; - else - tmp += " " + curr_filter; - } - - LIB_FIELD description( aNextFreeFieldId, wxString( "ki_fp_filters" ) ); - description.SetVisible( false ); - description.SetText( tmp ); - saveField( &description, aFormatter, aNestLevel + 1 ); - aNextFreeFieldId += 1; - } -} - - -void SCH_SEXPR_PLUGIN_CACHE::saveSymbolDrawItem( LIB_ITEM* aItem, OUTPUTFORMATTER& aFormatter, - int aNestLevel ) -{ - wxCHECK_RET( aItem, "Invalid LIB_ITEM pointer." ); - - switch( aItem->Type() ) - { - case LIB_SHAPE_T: - { - LIB_SHAPE* shape = static_cast( aItem ); - STROKE_PARAMS stroke; - FILL_T fillMode = shape->GetFillMode(); - bool isPrivate = shape->IsPrivate(); - - stroke.SetWidth( shape->GetWidth() ); - - COLOR4D fillColor = shape->GetFillColor(); - - switch( shape->GetShape() ) - { - case SHAPE_T::ARC: - formatArc( &aFormatter, aNestLevel, shape, isPrivate, stroke, fillMode, fillColor ); - break; - - case SHAPE_T::CIRCLE: - formatCircle( &aFormatter, aNestLevel, shape, isPrivate, stroke, fillMode, fillColor ); - break; - - case SHAPE_T::RECT: - formatRect( &aFormatter, aNestLevel, shape, isPrivate, stroke, fillMode, fillColor ); - break; - - case SHAPE_T::BEZIER: - formatBezier(&aFormatter, aNestLevel, shape, isPrivate, stroke, fillMode, fillColor ); - break; - - case SHAPE_T::POLY: - formatPoly( &aFormatter, aNestLevel, shape, isPrivate, stroke, fillMode, fillColor ); - break; - - default: - UNIMPLEMENTED_FOR( shape->SHAPE_T_asString() ); - } - - break; - } - - case LIB_PIN_T: - savePin( static_cast( aItem ), aFormatter, aNestLevel ); - break; - - case LIB_TEXT_T: - saveText( static_cast( aItem ), aFormatter, aNestLevel ); - break; - - case LIB_TEXTBOX_T: - saveTextBox( static_cast( aItem ), aFormatter, aNestLevel ); - break; - - default: - UNIMPLEMENTED_FOR( aItem->GetClass() ); - } -} - - -void SCH_SEXPR_PLUGIN_CACHE::saveField( LIB_FIELD* aField, OUTPUTFORMATTER& aFormatter, - int aNestLevel ) -{ - wxCHECK_RET( aField && aField->Type() == LIB_FIELD_T, "Invalid LIB_FIELD object." ); - - wxString fieldName = aField->GetName(); - - if( aField->GetId() >= 0 && aField->GetId() < MANDATORY_FIELDS ) - fieldName = TEMPLATE_FIELDNAME::GetDefaultFieldName( aField->GetId(), false ); - - aFormatter.Print( aNestLevel, "(property %s %s (id %d) (at %s %s %g)\n", - aFormatter.Quotew( fieldName ).c_str(), - aFormatter.Quotew( aField->GetText() ).c_str(), - aField->GetId(), - FormatInternalUnits( aField->GetPosition().x ).c_str(), - FormatInternalUnits( aField->GetPosition().y ).c_str(), - aField->GetTextAngle().AsDegrees() ); - - aField->Format( &aFormatter, aNestLevel, 0 ); - aFormatter.Print( aNestLevel, ")\n" ); -} - - -void SCH_SEXPR_PLUGIN_CACHE::savePin( LIB_PIN* aPin, OUTPUTFORMATTER& aFormatter, int aNestLevel ) -{ - wxCHECK_RET( aPin && aPin->Type() == LIB_PIN_T, "Invalid LIB_PIN object." ); - - aPin->ClearFlags( IS_CHANGED ); - - aFormatter.Print( aNestLevel, "(pin %s %s (at %s %s %s) (length %s)", - getPinElectricalTypeToken( aPin->GetType() ), - getPinShapeToken( aPin->GetShape() ), - FormatInternalUnits( aPin->GetPosition().x ).c_str(), - FormatInternalUnits( aPin->GetPosition().y ).c_str(), - FormatAngle( getPinAngle( aPin->GetOrientation() ) ).c_str(), - FormatInternalUnits( aPin->GetLength() ).c_str() ); - - if( !aPin->IsVisible() ) - aFormatter.Print( 0, " hide\n" ); - else - aFormatter.Print( 0, "\n" ); - - // This follows the EDA_TEXT effects formatting for future expansion. - aFormatter.Print( aNestLevel + 1, "(name %s (effects (font (size %s %s))))\n", - aFormatter.Quotew( aPin->GetName() ).c_str(), - FormatInternalUnits( aPin->GetNameTextSize() ).c_str(), - FormatInternalUnits( aPin->GetNameTextSize() ).c_str() ); - - aFormatter.Print( aNestLevel + 1, "(number %s (effects (font (size %s %s))))\n", - aFormatter.Quotew( aPin->GetNumber() ).c_str(), - FormatInternalUnits( aPin->GetNumberTextSize() ).c_str(), - FormatInternalUnits( aPin->GetNumberTextSize() ).c_str() ); - - - for( const std::pair& alt : aPin->GetAlternates() ) - { - aFormatter.Print( aNestLevel + 1, "(alternate %s %s %s)\n", - aFormatter.Quotew( alt.second.m_Name ).c_str(), - getPinElectricalTypeToken( alt.second.m_Type ), - getPinShapeToken( alt.second.m_Shape ) ); - } - - aFormatter.Print( aNestLevel, ")\n" ); -} - - -void SCH_SEXPR_PLUGIN_CACHE::saveText( LIB_TEXT* aText, OUTPUTFORMATTER& aFormatter, - int aNestLevel ) -{ - wxCHECK_RET( aText && aText->Type() == LIB_TEXT_T, "Invalid LIB_TEXT object." ); - - aFormatter.Print( aNestLevel, "(text%s %s (at %s %s %g)\n", - aText->IsPrivate() ? " private" : "", - aFormatter.Quotew( aText->GetText() ).c_str(), - FormatInternalUnits( aText->GetPosition().x ).c_str(), - FormatInternalUnits( aText->GetPosition().y ).c_str(), - (double) aText->GetTextAngle().AsTenthsOfADegree() ); - - aText->EDA_TEXT::Format( &aFormatter, aNestLevel, 0 ); - aFormatter.Print( aNestLevel, ")\n" ); -} - - -void SCH_SEXPR_PLUGIN_CACHE::saveTextBox( LIB_TEXTBOX* aTextBox, OUTPUTFORMATTER& aFormatter, - int aNestLevel ) -{ - wxCHECK_RET( aTextBox && aTextBox->Type() == LIB_TEXTBOX_T, "Invalid LIB_TEXTBOX object." ); - - aFormatter.Print( aNestLevel, "(text_box%s %s\n", - aTextBox->IsPrivate() ? " private" : "", - aFormatter.Quotew( aTextBox->GetText() ).c_str() ); - - aFormatter.Print( aNestLevel + 1, "(start %s %s) (end %s %s)\n", - FormatInternalUnits( aTextBox->GetStart().x ).c_str(), - FormatInternalUnits( aTextBox->GetStart().y ).c_str(), - FormatInternalUnits( aTextBox->GetEnd().x ).c_str(), - FormatInternalUnits( aTextBox->GetEnd().y ).c_str() ); - - aTextBox->GetStroke().Format( &aFormatter, aNestLevel + 1 ); - aFormatter.Print( 0, "\n" ); - - formatFill( &aFormatter, aNestLevel + 1, aTextBox->GetFillMode(), aTextBox->GetFillColor() ); - aFormatter.Print( 0, "\n" ); - - aTextBox->EDA_TEXT::Format( &aFormatter, aNestLevel, 0 ); - aFormatter.Print( aNestLevel, ")\n" ); -} - - -void SCH_SEXPR_PLUGIN_CACHE::DeleteSymbol( const wxString& aSymbolName ) -{ - LIB_SYMBOL_MAP::iterator it = m_symbols.find( aSymbolName ); - - if( it == m_symbols.end() ) - THROW_IO_ERROR( wxString::Format( _( "library %s does not contain a symbol named %s" ), - m_libFileName.GetFullName(), aSymbolName ) ); - - LIB_SYMBOL* symbol = it->second; - - if( symbol->IsRoot() ) - { - LIB_SYMBOL* rootSymbol = symbol; - - // Remove the root symbol and all its children. - m_symbols.erase( it ); - - LIB_SYMBOL_MAP::iterator it1 = m_symbols.begin(); - - while( it1 != m_symbols.end() ) - { - if( it1->second->IsAlias() - && it1->second->GetParent().lock() == rootSymbol->SharedPtr() ) - { - delete it1->second; - it1 = m_symbols.erase( it1 ); - } - else - { - it1++; - } - } - - delete rootSymbol; - } - else - { - // Just remove the alias. - m_symbols.erase( it ); - delete symbol; - } - - ++m_modHash; - m_isModified = true; -} - - void SCH_SEXPR_PLUGIN::cacheLib( const wxString& aLibraryFileName, const PROPERTIES* aProperties ) { if( !m_cache || !m_cache->IsFile( aLibraryFileName ) || m_cache->IsFileChanged() )