Factor out common s-expression library cache source code.
This commit is contained in:
parent
7181f0b40b
commit
3f8f90db9f
|
@ -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
|
||||
|
|
|
@ -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 <stambaughw@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <lib_field.h>
|
||||
#include <lib_shape.h>
|
||||
#include <lib_symbol.h>
|
||||
#include <lib_text.h>
|
||||
#include <lib_textbox.h>
|
||||
#include <locale_io.h>
|
||||
#include <macros.h>
|
||||
#include <richio.h>
|
||||
#include "sch_sexpr_lib_plugin_cache.h"
|
||||
#include "sch_sexpr_plugin_common.h"
|
||||
#include "sch_sexpr_parser.h"
|
||||
#include <string_utils.h>
|
||||
#include <trace_helpers.h>
|
||||
|
||||
|
||||
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<bool>& 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<FILE_OUTPUTFORMATTER>( 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<LIB_SYMBOL> 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<LIB_FIELD*> 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<LIB_SYMBOL_UNIT> 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<LIB_ITEM*, decltype( cmp )> 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<LIB_SYMBOL> 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<LIB_SHAPE*>( 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<LIB_PIN*>( aItem ), aFormatter, aNestLevel );
|
||||
break;
|
||||
|
||||
case LIB_TEXT_T:
|
||||
saveText( static_cast<LIB_TEXT*>( aItem ), aFormatter, aNestLevel );
|
||||
break;
|
||||
|
||||
case LIB_TEXTBOX_T:
|
||||
saveTextBox( static_cast<LIB_TEXTBOX*>( 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<const wxString, LIB_PIN::ALT>& 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;
|
||||
}
|
|
@ -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 <stambaughw@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<bool>& 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_
|
|
@ -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;
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
#include <sch_file_versions.h>
|
||||
#include <schematic_lexer.h>
|
||||
#include <sch_plugins/kicad/sch_sexpr_parser.h>
|
||||
#include "sch_sexpr_lib_plugin_cache.h"
|
||||
#include "sch_sexpr_plugin_common.h"
|
||||
#include <symbol_lib_table.h> // for PropPowerSymsOnly definition.
|
||||
#include <ee_selection.h>
|
||||
|
@ -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<SCH_SHEET_INSTANCE>& 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<LIB_FIELD&>( 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<FILE_OUTPUTFORMATTER>( 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<LIB_SYMBOL> 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<LIB_FIELD*> 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<LIB_SYMBOL_UNIT> 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<LIB_ITEM*, decltype( cmp )> 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<LIB_SYMBOL> 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<LIB_SHAPE*>( 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<LIB_PIN*>( aItem ), aFormatter, aNestLevel );
|
||||
break;
|
||||
|
||||
case LIB_TEXT_T:
|
||||
saveText( static_cast<LIB_TEXT*>( aItem ), aFormatter, aNestLevel );
|
||||
break;
|
||||
|
||||
case LIB_TEXTBOX_T:
|
||||
saveTextBox( static_cast<LIB_TEXTBOX*>( 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<const wxString, LIB_PIN::ALT>& 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() )
|
||||
|
|
Loading…
Reference in New Issue