Skip to content

Instantly share code, notes, and snippets.

@hdante
Created October 30, 2016 20:05
Show Gist options
  • Save hdante/b86454b6d2b3c36b257c5e8475a4cf02 to your computer and use it in GitHub Desktop.
Save hdante/b86454b6d2b3c36b257c5e8475a4cf02 to your computer and use it in GitHub Desktop.
EAGLE Plugin incomplete
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2016 CERN
* Copyright (C) 2016 KiCad Developers, see change_log.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 <ctype.h>
#include <algorithm>
#include <wx/mstream.h>
#include <wx/filename.h>
#include <wx/tokenzr.h>
#include <drawtxt.h>
#include <kiway.h>
#include <kicad_string.h>
#include <richio.h>
#include <core/typeinfo.h>
#include <general.h>
#include <lib_field.h>
#include <sch_bus_entry.h>
#include <sch_marker.h>
#include <sch_junction.h>
#include <sch_line.h>
#include <sch_no_connect.h>
#include <sch_component.h>
#include <sch_text.h>
#include <sch_sheet.h>
#include <sch_bitmap.h>
#include <sch_legacy_plugin.h>
#include <template_fieldnames.h>
#include <class_sch_screen.h>
#include <class_libentry.h>
#include <class_library.h>
#include <lib_arc.h>
#include <lib_bezier.h>
#include <lib_circle.h>
#include <lib_pin.h>
#include <lib_polyline.h>
#include <lib_rectangle.h>
#include <lib_text.h>
#define SCH_PARSE_ERROR( text, reader, pos ) \
THROW_PARSE_ERROR( text, reader.GetSource(), reader.Line(), \
reader.LineNumber(), pos - reader.Line() )
// Token delimiters.
const char* delims = " \t\r\n";
const wxChar traceSchLegacyPlugin[] = wxT( "KI_SCH_EAGLE_PLUGIN" );
using std::vector;
using std::string;
static double toPixels(const string& cm)
{
return atof(cm.c_str())*72.0/2.54;
}
struct Wire {
double x1, y1, x2, y2;
};
struct Text {
double x, y, size;
string text;
};
struct Rectangle {
double x1, y1, x2, y2;
};
struct Pin {
string name;
double x, y, size;
unsigned number;
};
class Symbol {
public:
Symbol() : pinCount(0) { }
Symbol( const string &name_ ) : Symbol(), name(name_) { }
unsigned pinCount;
string name;
vector<Wire> wires;
vector<Text> texts;
vector<Rectangle> rectangles;
vector<Pin> pins;
};
struct State {
State( LIB_ALIAS_MAP& aliases_ ) : value(SKIP), aliases(aliases_) { }
enum { SKIP, SYMBOL } value;
Symbol symbol;
string data;
LIB_ALIAS_MAP &aliases;
};
static void onParseStart( string& element, map<string, string>& attributes, string& data, State& state )
{
switch (state.value)
{
case SKIP:
if( element == "symbol" )
{
state.value = SYMBOL;
state.symbol = Symbol( attributes["name"] );
}
break;
case SYMBOL:
if( element == "wire" )
{
auto x1 = toPixels( attributes["x1"] );
auto y1 = toPixels( attributes["y1"] );
auto x2 = toPixels( attributes["x2"] );
auto y2 = toPixels( attributes["y2"] );
state.symbol.wires.push_back( { x1, y1, x2, y2 } );
}
else if( element == "text" )
{
auto x = toPixels( attributes["x"] );
auto y = toPixels( attributes["y"] );
auto size = toPixels( attributes["size"] );
state.symbol.texts.push_back( { x, y, size, data } );
}
else if( element == "rectangle" )
{
auto x1 = toPixels( attributes["x1"] );
auto y1 = toPixels( attributes["y1"] );
auto x2 = toPixels( attributes["x2"] );
auto y2 = toPixels( attributes["y2"] );
state.symbol.rectangles.push_back( { x1, y1, x2, y2 } );
}
else if( element == "pin" )
{
auto name = attributes["name"];
auto x = toPixels( attributes["x"] );
auto y = toPixels( attributes["y"] );
auto size = toPixels( attributes["size"] );
state.symbol.pins.push_back( { name, x, y, size, state.symbol.pinCount++ } );
}
break;
}
state.current = element;
}
static void onParseData(string &data, State &state)
{
if (state.value == SYMBOL)
state.data += data;
}
static void onParseEnd(string &element, State &state)
{
if (state.value == SYMBOL && element == "text") {
state.symbol.texts[state.symbol.texts.size()-1] = state.data;
}
else if (element == "symbol") {
loadPart(state.aliases, state.symbol);
state.value = SKIP;
}
}
static void loadPart( LIB_ALIAS_MAP& aliases, Symbol& symbol)
{
std::unique_ptr< LIB_PART > part( new LIB_PART( wxEmptyString ) );
part->SetPinNameOffset( 0 );
part->SetShowPinNumbers( true );
part->SetShowPinNames( true );
part->SetUnitCount( 1 );
LIB_FIELD& value = part->GetValueField();
value.SetText( symbol.name );
part->m_aliases.push_back( new LIB_ALIAS( symbol.name, part.get() ) );
aliases[ part->GetName() ] = part->GetAlias( name );
LIB_FIELD& reference = part->GetReferenceField();
reference.SetText( "EAGLE" );
part->LockUnits( false );
part->SetNormal();
}
SCH_EAGLE_PLUGIN::SCH_EAGLE_PLUGIN()
{
init( NULL );
}
void SCH_EAGLE_PLUGIN::init( KIWAY* aKiway, const PROPERTIES* aProperties )
{
m_version = 0;
m_props = aProperties;
m_kiway = aKiway;
m_cache = NULL;
m_out = NULL;
}
/**
* Class SCH_EAGLE_PLUGIN_CACHE
* is a cache assistant for the part library portion of the #SCH_PLUGIN API, and only for the
* #SCH_EAGLE_PLUGIN, so therefore is private to this implementation file, i.e. not placed
* into a header.
*/
class SCH_EAGLE_PLUGIN_CACHE
{
wxFileName m_libFileName; // Absolute path and file name is required here.
wxDateTime m_fileModTime;
LIB_ALIAS_MAP m_aliases; // Map of names of LIB_ALIAS pointers.
bool m_isWritable;
bool m_isModified;
int m_modHash; // Keep track of the modification status of the library.
int m_versionMajor;
int m_versionMinor;
LIB_PART* loadPart( FILE_LINE_READER& aReader );
void loadField( std::unique_ptr< LIB_PART >& aPart, FILE_LINE_READER& aReader );
void loadDrawEntries( std::unique_ptr< LIB_PART >& aPart,
FILE_LINE_READER& aReader );
LIB_ARC* loadArc( std::unique_ptr< LIB_PART >& aPart, FILE_LINE_READER& aReader );
LIB_CIRCLE* loadCircle( std::unique_ptr< LIB_PART >& aPart, FILE_LINE_READER& aReader );
LIB_TEXT* loadText( std::unique_ptr< LIB_PART >& aPart, FILE_LINE_READER& aReader );
LIB_RECTANGLE* loadRectangle( std::unique_ptr< LIB_PART >& aPart, FILE_LINE_READER& aReader );
LIB_PIN* loadPin( std::unique_ptr< LIB_PART >& aPart, FILE_LINE_READER& aReader );
LIB_POLYLINE* loadPolyLine( std::unique_ptr< LIB_PART >& aPart, FILE_LINE_READER& aReader );
LIB_BEZIER* loadBezier( std::unique_ptr< LIB_PART >& aPart, FILE_LINE_READER& aReader );
friend SCH_EAGLE_PLUGIN;
public:
SCH_EAGLE_PLUGIN_CACHE( const wxString& aLibraryPath );
~SCH_EAGLE_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.
void Load();
wxDateTime GetLibModificationTime();
bool IsFile( const wxString& aFullPathAndFileName ) const;
bool IsFileChanged() const;
wxString GetLogicalName() const { return m_libFileName.GetName(); }
};
SCH_EAGLE_PLUGIN_CACHE::SCH_EAGLE_PLUGIN_CACHE( const wxString& aFullPathAndFileName ) :
m_libFileName( aFullPathAndFileName ),
m_isWritable( true ),
m_isModified( false ),
m_modHash( 1 )
{
m_versionMajor = -1;
m_versionMinor = -1;
}
SCH_EAGLE_PLUGIN_CACHE::~SCH_EAGLE_PLUGIN_CACHE()
{
// When the cache is destroyed, all of the alias objects on the heap should be deleted.
for( LIB_ALIAS_MAP::iterator it = m_aliases.begin(); it != m_aliases.end(); ++it )
{
wxLogTrace( traceSchLegacyPlugin, wxT( "Removing alias %s from library %s." ),
GetChars( it->second->GetName() ), GetChars( GetLogicalName() ) );
LIB_PART* part = it->second->GetPart();
LIB_ALIAS* alias = it->second;
delete alias;
// When the last alias of a part is destroyed, the part is no longer required and it
// too is destroyed.
if( part && part->GetAliasCount() == 0 )
delete part;
}
m_aliases.clear();
}
wxDateTime SCH_EAGLE_PLUGIN_CACHE::GetLibModificationTime()
{
// update the writable flag while we have a wxFileName, in a network this
// is possibly quite dynamic anyway.
m_isWritable = m_libFileName.IsFileWritable();
return m_libFileName.GetModificationTime();
}
bool SCH_EAGLE_PLUGIN_CACHE::IsFile( const wxString& aFullPathAndFileName ) const
{
return m_libFileName == aFullPathAndFileName;
}
bool SCH_EAGLE_PLUGIN_CACHE::IsFileChanged() const
{
return m_libFileName.GetModificationTime() != m_fileModTime;
}
void SCH_EAGLE_PLUGIN_CACHE::Load()
{
State state(m_aliases);
XML_Parser parser = XML_ParserCreate( "UTF-8" );
XML_SetElementHandler(parser, XmlHandlerStart, XmlHandlerEnd);
XML_SetCharacterDataHandler(parser, XmlHandlerData);
XML_SetUserData(parser, &state);
ifstream eagle(m_libFileName.GetFullPath());
while( eagle.good() )
{
char buffer[10000];
eagle.read(buffer, sizeof(buffer));
XML_Parse(parser, buffer, eagle.gcount(), eagle.gcount() == 0);
}
++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();
}
LIB_POLYLINE* SCH_EAGLE_PLUGIN_CACHE::loadWire( std::unique_ptr< LIB_PART >& aPart,
Wire& wire )
{
std::unique_ptr< LIB_POLYLINE > polyLine( new LIB_POLYLINE( aPart.get() ) );
polyLine->SetUnit(0);
polyLine->SetConvert(1);
polyLine->SetWidth(0);
polyLine->AddPoint( { wire.x1, wire.y1 } );
polyLine->AddPoint( { wire.x2, wire.y2 } );
polyLine->SetFillMode( NO_FILL );
return polyLine.release();
}
LIB_PART* SCH_EAGLE_PLUGIN_CACHE::loadPart( XML_READER& aReader )
{
const char* line = aReader.Line();
wxCHECK( strCompare( "DEF", line, &line ), NULL );
// Read DEF line:
char yes_no = 0;
std::unique_ptr< LIB_PART > part( new LIB_PART( wxEmptyString ) );
wxString name, prefix;
parseUnquotedString( name, aReader, line, &line ); // Part name.
parseUnquotedString( prefix, aReader, line, &line ); // Prefix name
parseInt( aReader, line, &line ); // NumOfPins, unused.
part->SetPinNameOffset( parseInt( aReader, line, &line ) ); // Pin name offset.
yes_no = parseChar( aReader, line, &line ); // Show pin numbers.
if( !( yes_no == 'Y' || yes_no == 'N') )
SCH_PARSE_ERROR( "expected Y or N", aReader, line );
part->SetShowPinNumbers( ( yes_no == 'N' ) ? false : true );
yes_no = parseChar( aReader, line, &line ); // Show pin numbers.
if( !( yes_no == 'Y' || yes_no == 'N') )
SCH_PARSE_ERROR( "expected Y or N", aReader, line );
part->SetShowPinNames( ( yes_no == 'N' ) ? false : true ); // Show pin names.
part->SetUnitCount( parseInt( aReader, line, &line ) ); // Number of units.
// Ensure m_unitCount is >= 1. Could be read as 0 in old libraries.
if( part->GetUnitCount() < 1 )
part->SetUnitCount( 1 );
// Copy part name and prefix.
LIB_FIELD& value = part->GetValueField();
// The root alias is added to the alias list by SetName() which is called by SetText().
if( name.IsEmpty() )
{
part->m_name = "~";
value.SetText( "~" );
}
else if( name[0] != '~' )
{
part->m_name = name;
value.SetText( name );
}
else
{
name = name.Right( name.Length() - 1 );
part->m_name = name;
value.SetText( name );
value.SetVisible( false );
}
// Add the root alias to the alias list.
part->m_aliases.push_back( new LIB_ALIAS( name, part.get() ) );
m_aliases[ part->GetName() ] = part->GetAlias( name );
LIB_FIELD& reference = part->GetReferenceField();
if( prefix == "~" )
{
reference.Empty();
reference.SetVisible( false );
}
else
{
reference.SetText( prefix );
}
// In version 2.0 and earlier, this parameter was a '0' which was just a place holder.
// The was no concept of interchangeable multiple unit symbols.
if( LIB_VERSION( m_versionMajor, m_versionMinor ) <= LIB_VERSION( 2, 0 ) )
{
// Nothing needs to be set since the default setting for symbols with multiple
// units were never interchangeable. Just parse the 0 an move on.
parseInt( aReader, line, &line );
}
else
{
char locked = parseChar( aReader, line, &line );
if( locked == 'L' )
part->LockUnits( true );
else if( locked == 'F' )
part->LockUnits( false );
else
SCH_PARSE_ERROR( "expected L or F", aReader, line );
}
// There is the optional power component flag.
if( *line )
{
char power = parseChar( aReader, line, &line );
if( power == 'P' )
part->SetPower();
else if( power == 'N' )
part->SetNormal();
else
SCH_PARSE_ERROR( "expected P or N", aReader, line );
}
line = aReader.ReadLine();
// Read lines until "ENDDEF" is found.
while( line )
{
if( *line == '#' ) // Comment
continue;
else if( strCompare( "Ti", line, &line ) ) // Modification date is ignored.
continue;
else if( *line == 'F' ) // Fields
loadField( part, aReader );
else if( strCompare( "DRAW", line, &line ) ) // Drawing objects.
loadDrawEntries( part, aReader );
else if( strCompare( "ENDDEF", line, &line ) ) // End of part description
return part.release();
line = aReader.ReadLine();
}
SCH_PARSE_ERROR( "missing ENDDEF", aReader, line );
}
void SCH_EAGLE_PLUGIN_CACHE::loadField( std::unique_ptr< LIB_PART >& aPart,
FILE_LINE_READER& aReader )
{
const char* line = aReader.Line();
wxCHECK_RET( *line == 'F', "Invalid field line" );
int id;
if( sscanf( line + 1, "%d", &id ) != 1 || id < 0 )
SCH_PARSE_ERROR( _( "invalid field ID" ), aReader, line + 1 );
std::unique_ptr< LIB_FIELD > field( new LIB_FIELD( aPart.get(), id ) );
// Skip to the first double quote.
while( *line != '"' && *line != 0 )
line++;
if( *line == 0 )
SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, line );
wxString text;
parseQuotedString( text, aReader, line, &line, true );
// Doctor the *.lib file field which has a "~" in blank fields. New saves will
// not save like this.
if( text.size() == 1 && text[0] == '~' )
text.clear();
field->m_Text = text;
wxPoint pos;
pos.x = parseInt( aReader, line, &line );
pos.y = parseInt( aReader, line, &line );
field->SetPosition( pos );
wxSize textSize;
textSize.x = textSize.y = parseInt( aReader, line, &line );
field->SetSize( textSize );
char textOrient = parseChar( aReader, line, &line );
if( textOrient == 'H' )
field->SetOrientation( TEXT_ORIENT_HORIZ );
else if( textOrient == 'V' )
field->SetOrientation( TEXT_ORIENT_VERT );
else
SCH_PARSE_ERROR( _( "invalid field text orientation parameter" ), aReader, line );
char textVisible = parseChar( aReader, line, &line );
if( textVisible == 'V' )
field->SetVisible( true );
else if ( textVisible == 'I' )
field->SetVisible( false );
else
SCH_PARSE_ERROR( _( "invalid field text visibility parameter" ), aReader, line );
// It may be technically correct to use the library version to determine if the field text
// attributes are present. If anyone knows if that is valid and what version that would be,
// please change this to test the library version rather than an EOL or the quoted string
// of the field name.
if( *line != 0 && *line != '"' )
{
char textHJustify = parseChar( aReader, line, &line );
if( textHJustify == 'C' )
field->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
else if( textHJustify == 'L' )
field->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
else if( textHJustify == 'R' )
field->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
else
SCH_PARSE_ERROR( _( "invalid field text horizontal justification parameter" ),
aReader, line );
wxString attributes;
parseUnquotedString( attributes, aReader, line, &line );
if( !(attributes.size() == 3 || attributes.size() == 1 ) )
SCH_PARSE_ERROR( _( "invalid field text attributes size" ),
aReader, line );
if( attributes[0] == 'C' )
field->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
else if( attributes[0] == 'B' )
field->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
else if( attributes[0] == 'T' )
field->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
else
SCH_PARSE_ERROR( _( "invalid field text vertical justification parameter" ),
aReader, line );
if( attributes.size() == 3 )
{
if( attributes[1] == 'I' ) // Italic
field->SetItalic( true );
else if( attributes[1] != 'N' ) // No italics is default, check for error.
SCH_PARSE_ERROR( _( "invalid field text italic parameter" ), aReader, line );
if ( attributes[2] == 'B' ) // Bold
field->SetBold( true );
else if( attributes[2] != 'N' ) // No bold is default, check for error.
SCH_PARSE_ERROR( _( "invalid field text bold parameter" ), aReader, line );
}
}
// Fields in RAM must always have names.
if( id < MANDATORY_FIELDS )
{
// Fields in RAM must always have names, because we are trying to get
// less dependent on field ids and more dependent on names.
// Plus assumptions are made in the field editors.
field->m_name = TEMPLATE_FIELDNAME::GetDefaultFieldName( id );
LIB_FIELD* fixedField = aPart->GetField( field->GetId() );
// this will fire only if somebody broke a constructor or editor.
// MANDATORY_FIELDS are always present in ram resident components, no
// exceptions, and they always have their names set, even fixed fields.
wxASSERT( fixedField );
*fixedField = *field;
if( field->GetId() == VALUE )
aPart->m_name = field->GetText();
}
else
{
wxString name;
parseQuotedString( name, aReader, line, &line, true ); // Optional.
if( !name.IsEmpty() )
field->m_name = name;
aPart->AddDrawItem( field.release() ); // LIB_FIELD* is now owned by the LIB_PART.
}
}
void SCH_EAGLE_PLUGIN_CACHE::loadDrawEntries( std::unique_ptr< LIB_PART >& aPart,
FILE_LINE_READER& aReader )
{
const char* line = aReader.Line();
wxCHECK_RET( strCompare( "DRAW", line, &line ), "Invalid DRAW section" );
line = aReader.ReadLine();
while( line )
{
if( strCompare( "ENDDRAW", line, &line ) )
return;
switch( line[0] )
{
case 'A': // Arc
aPart->AddDrawItem( loadArc( aPart, aReader ) );
break;
case 'C': // Circle
aPart->AddDrawItem( loadCircle( aPart, aReader ) );
break;
case 'T': // Text
aPart->AddDrawItem( loadText( aPart, aReader ) );
break;
case 'S': // Square
aPart->AddDrawItem( loadRectangle( aPart, aReader ) );
break;
case 'X': // Pin Description
aPart->AddDrawItem( loadPin( aPart, aReader ) );
break;
case 'P': // Polyline
aPart->AddDrawItem( loadPolyLine( aPart, aReader ) );
break;
case 'B': // Bezier Curves
aPart->AddDrawItem( loadBezier( aPart, aReader ) );
break;
case '#': // Comment
case '\n': // Empty line
case '\r':
case 0:
break;
default:
SCH_PARSE_ERROR( _( "undefined DRAW entry" ), aReader, line );
}
line = aReader.ReadLine();
}
SCH_PARSE_ERROR( _( "file ended prematurely loading component draw element" ), aReader, line );
}
LIB_ARC* SCH_EAGLE_PLUGIN_CACHE::loadArc( std::unique_ptr< LIB_PART >& aPart,
FILE_LINE_READER& aReader )
{
const char* line = aReader.Line();
wxCHECK_MSG( strCompare( "A", line, &line ), NULL, "Invalid LIB_ARC definition" );
std::unique_ptr< LIB_ARC > arc( new LIB_ARC( aPart.get() ) );
wxPoint center;
center.x = parseInt( aReader, line, &line );
center.y = parseInt( aReader, line, &line );
arc->SetPosition( center );
arc->SetRadius( parseInt( aReader, line, &line ) );
int angle1 = parseInt( aReader, line, &line );
int angle2 = parseInt( aReader, line, &line );
NORMALIZE_ANGLE_POS( angle1 );
NORMALIZE_ANGLE_POS( angle2 );
arc->SetFirstRadiusAngle( angle1 );
arc->SetSecondRadiusAngle( angle2 );
arc->SetUnit( parseInt( aReader, line, &line ) );
arc->SetConvert( parseInt( aReader, line, &line ) );
arc->SetWidth( parseInt( aReader, line, &line ) );
// Actual Coordinates of arc ends are read from file
if( *line != 0 )
{
arc->SetFillMode( parseFillMode( aReader, line, &line ) );
wxPoint arcStart, arcEnd;
arcStart.x = parseInt( aReader, line, &line );
arcStart.y = parseInt( aReader, line, &line );
arcEnd.x = parseInt( aReader, line, &line );
arcEnd.y = parseInt( aReader, line, &line );
arc->SetStart( arcStart );
arc->SetEnd( arcEnd );
}
else
{
// Actual Coordinates of arc ends are not read from file
// (old library), calculate them
wxPoint arcStart( arc->GetRadius(), 0 );
wxPoint arcEnd( arc->GetRadius(), 0 );
RotatePoint( &arcStart.x, &arcStart.y, -angle1 );
arcStart += arc->GetPosition();
arc->SetStart( arcStart );
RotatePoint( &arcEnd.x, &arcEnd.y, -angle2 );
arcEnd += arc->GetPosition();
arc->SetEnd( arcEnd );
}
return arc.release();
}
LIB_CIRCLE* SCH_EAGLE_PLUGIN_CACHE::loadCircle( std::unique_ptr< LIB_PART >& aPart,
FILE_LINE_READER& aReader )
{
const char* line = aReader.Line();
wxCHECK_MSG( strCompare( "C", line, &line ), NULL, "Invalid LIB_CIRCLE definition" );
std::unique_ptr< LIB_CIRCLE > circle( new LIB_CIRCLE( aPart.get() ) );
wxPoint center;
center.x = parseInt( aReader, line, &line );
center.y = parseInt( aReader, line, &line );
circle->SetPosition( center );
circle->SetRadius( parseInt( aReader, line, &line ) );
circle->SetUnit( parseInt( aReader, line, &line ) );
circle->SetConvert( parseInt( aReader, line, &line ) );
circle->SetWidth( parseInt( aReader, line, &line ) );
if( *line != 0 )
circle->SetFillMode( parseFillMode( aReader, line, &line ) );
return circle.release();
}
LIB_TEXT* SCH_EAGLE_PLUGIN_CACHE::loadText( std::unique_ptr< LIB_PART >& aPart,
FILE_LINE_READER& aReader )
{
const char* line = aReader.Line();
wxCHECK_MSG( strCompare( "T", line, &line ), NULL, "Invalid LIB_TEXT definition" );
std::unique_ptr< LIB_TEXT > text( new LIB_TEXT( aPart.get() ) );
text->SetOrientation( (double) parseInt( aReader, line, &line ) );
wxPoint center;
center.x = parseInt( aReader, line, &line );
center.y = parseInt( aReader, line, &line );
text->SetPosition( center );
wxSize size;
size.x = size.y = parseInt( aReader, line, &line );
text->SetSize( size );
text->SetAttributes( parseInt( aReader, line, &line ) );
text->SetUnit( parseInt( aReader, line, &line ) );
text->SetConvert( parseInt( aReader, line, &line ) );
wxString str;
// If quoted string loading fails, load as not quoted string.
if( *line == '"' )
parseQuotedString( str, aReader, line, &line );
else
parseUnquotedString( str, aReader, line, &line );
if( !str.IsEmpty() )
{
// convert two apostrophes back to double quote
str.Replace( "''", "\"" );
str.Replace( wxT( "~" ), wxT( " " ) );
}
text->SetText( str );
// Here things are murky and not well defined. At some point it appears the format
// was changed to add text properties. However rather than add the token to the end of
// the text definition, it was added after the string and no mention if the file
// verion was bumped or not so this code make break on very old component libraries.
//
// Update: apparently even in the latest version this can be different so added a test
// for end of line before checking for the text properties.
if( LIB_VERSION( m_versionMajor, m_versionMinor ) > LIB_VERSION( 2, 0 ) && !is_eol( *line ) )
{
if( strCompare( "Italic", line, &line ) )
text->SetItalic( true );
else if( !strCompare( "Normal", line, &line ) )
SCH_PARSE_ERROR( _( "invalid text stype, expected 'Normal' or 'Italic'" ),
aReader, line );
if( parseInt( aReader, line, &line ) > 0 )
text->SetBold( true );
switch( parseChar( aReader, line, &line ) )
{
case 'L':
text->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
break;
case 'C':
text->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
break;
case 'R':
text->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
break;
default:
SCH_PARSE_ERROR( _( "invalid horizontal text justication parameter, expected L, C, or R" ),
aReader, line );
}
switch( parseChar( aReader, line, &line ) )
{
case 'T':
text->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
break;
case 'C':
text->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
break;
case 'B':
text->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
break;
default:
SCH_PARSE_ERROR( _( "invalid vertical text justication parameter, expected T, C, or B" ),
aReader, line );
}
}
return text.release();
}
LIB_RECTANGLE* SCH_EAGLE_PLUGIN_CACHE::loadRectangle( std::unique_ptr< LIB_PART >& aPart,
FILE_LINE_READER& aReader )
{
const char* line = aReader.Line();
wxCHECK_MSG( strCompare( "S", line, &line ), NULL, "Invalid LIB_RECTANGLE definition" );
std::unique_ptr< LIB_RECTANGLE > rectangle( new LIB_RECTANGLE( aPart.get() ) );
wxPoint pos;
pos.x = parseInt( aReader, line, &line );
pos.y = parseInt( aReader, line, &line );
rectangle->SetPosition( pos );
wxPoint end;
end.x = parseInt( aReader, line, &line );
end.y = parseInt( aReader, line, &line );
rectangle->SetEnd( end );
rectangle->SetUnit( parseInt( aReader, line, &line ) );
rectangle->SetConvert( parseInt( aReader, line, &line ) );
rectangle->SetWidth( parseInt( aReader, line, &line ) );
if( *line != 0 )
rectangle->SetFillMode( parseFillMode( aReader, line, &line ) );
return rectangle.release();
}
LIB_PIN* SCH_EAGLE_PLUGIN_CACHE::loadPin( std::unique_ptr< LIB_PART >& aPart,
FILE_LINE_READER& aReader )
{
const char* line = aReader.Line();
wxCHECK_MSG( strCompare( "X", line, &line ), NULL, "Invalid LIB_PIN definition" );
std::unique_ptr< LIB_PIN > pin( new LIB_PIN( aPart.get() ) );
wxString name, number;
parseUnquotedString( name, aReader, line, &line );
parseUnquotedString( number, aReader, line, &line );
pin->SetName( name );
pin->SetPinNumFromString( number );
wxPoint pos;
pos.x = parseInt( aReader, line, &line );
pos.y = parseInt( aReader, line, &line );
pin->SetPosition( pos );
pin->SetLength( parseInt( aReader, line, &line ) );
pin->SetOrientation( parseChar( aReader, line, &line ) );
pin->SetNumberTextSize( parseInt( aReader, line, &line ) );
pin->SetNameTextSize( parseInt( aReader, line, &line ) );
pin->SetUnit( parseInt( aReader, line, &line ) );
pin->SetConvert( parseInt( aReader, line, &line ) );
char type = parseChar( aReader, line, &line );
wxString attributes;
// Optional
parseUnquotedString( attributes, aReader, line, &line, true );
switch( type )
{
case 'I':
pin->SetType( PIN_INPUT );
break;
case 'O':
pin->SetType( PIN_OUTPUT );
break;
case 'B':
pin->SetType( PIN_BIDI );
break;
case 'T':
pin->SetType( PIN_TRISTATE );
break;
case 'P':
pin->SetType( PIN_PASSIVE );
break;
case 'U':
pin->SetType( PIN_UNSPECIFIED );
break;
case 'W':
pin->SetType( PIN_POWER_IN );
break;
case 'w':
pin->SetType( PIN_POWER_OUT );
break;
case 'C':
pin->SetType( PIN_OPENCOLLECTOR );
break;
case 'E':
pin->SetType( PIN_OPENEMITTER );
break;
case 'N':
pin->SetType( PIN_NC );
break;
default:
SCH_PARSE_ERROR( _( "unknown pin type" ), aReader, line );
}
if( !attributes.IsEmpty() ) /* Special Symbol defined */
{
enum
{
INVERTED = 1 << 0,
CLOCK = 1 << 1,
LOWLEVEL_IN = 1 << 2,
LOWLEVEL_OUT = 1 << 3,
FALLING_EDGE = 1 << 4,
NONLOGIC = 1 << 5
};
int flags = 0;
for( int j = attributes.size(); j > 0; )
{
switch( attributes[--j].GetValue() )
{
case '~':
break;
case 'N':
pin->SetVisible( false );
break;
case 'I':
flags |= INVERTED;
break;
case 'C':
flags |= CLOCK;
break;
case 'L':
flags |= LOWLEVEL_IN;
break;
case 'V':
flags |= LOWLEVEL_OUT;
break;
case 'F':
flags |= FALLING_EDGE;
break;
case 'X':
flags |= NONLOGIC;
break;
default:
SCH_PARSE_ERROR( _( "unknown pin attribute" ), aReader, line );
}
}
switch( flags )
{
case 0:
pin->SetShape( PINSHAPE_LINE );
break;
case INVERTED:
pin->SetShape( PINSHAPE_INVERTED );
break;
case CLOCK:
pin->SetShape( PINSHAPE_CLOCK );
break;
case INVERTED | CLOCK:
pin->SetShape( PINSHAPE_INVERTED_CLOCK );
break;
case LOWLEVEL_IN:
pin->SetShape( PINSHAPE_INPUT_LOW );
break;
case LOWLEVEL_IN | CLOCK:
pin->SetShape( PINSHAPE_CLOCK_LOW );
break;
case LOWLEVEL_OUT:
pin->SetShape( PINSHAPE_OUTPUT_LOW );
break;
case FALLING_EDGE:
pin->SetShape( PINSHAPE_FALLING_EDGE_CLOCK );
break;
case NONLOGIC:
pin->SetShape( PINSHAPE_NONLOGIC );
break;
default:
SCH_PARSE_ERROR( _( "pin attributes do not define a valid pin shape" ), aReader, line );
}
}
return pin.release();
}
LIB_POLYLINE* SCH_EAGLE_PLUGIN_CACHE::loadPolyLine( std::unique_ptr< LIB_PART >& aPart,
FILE_LINE_READER& aReader )
{
const char* line = aReader.Line();
wxCHECK_MSG( strCompare( "P", line, &line ), NULL, "Invalid LIB_POLYLINE definition" );
std::unique_ptr< LIB_POLYLINE > polyLine( new LIB_POLYLINE( aPart.get() ) );
int points = parseInt( aReader, line, &line );
polyLine->SetUnit( parseInt( aReader, line, &line ) );
polyLine->SetConvert( parseInt( aReader, line, &line ) );
polyLine->SetWidth( parseInt( aReader, line, &line ) );
wxPoint pt;
for( int i = 0; i < points; i++ )
{
pt.x = parseInt( aReader, line, &line );
pt.y = parseInt( aReader, line, &line );
polyLine->AddPoint( pt );
}
if( *line != 0 )
polyLine->SetFillMode( parseFillMode( aReader, line, &line ) );
return polyLine.release();
}
LIB_BEZIER* SCH_EAGLE_PLUGIN_CACHE::loadBezier( std::unique_ptr< LIB_PART >& aPart,
FILE_LINE_READER& aReader )
{
const char* line = aReader.Line();
wxCHECK_MSG( strCompare( "B", line, &line ), NULL, "Invalid LIB_BEZIER definition" );
std::unique_ptr< LIB_BEZIER > bezier( new LIB_BEZIER( aPart.get() ) );
int points = parseInt( aReader, line, &line );
bezier->SetUnit( parseInt( aReader, line, &line ) );
bezier->SetConvert( parseInt( aReader, line, &line ) );
bezier->SetWidth( parseInt( aReader, line, &line ) );
wxPoint pt;
for( int i = 0; i < points; i++ )
{
pt.x = parseInt( aReader, line, &line );
pt.y = parseInt( aReader, line, &line );
bezier->AddPoint( pt );
}
if( *line != 0 )
bezier->SetFillMode( parseFillMode( aReader, line, &line ) );
return bezier.release();
}
void SCH_EAGLE_PLUGIN::cacheLib( const wxString& aLibraryFileName )
{
if( !m_cache || !m_cache->IsFile( aLibraryFileName ) || m_cache->IsFileChanged() )
{
// a spectacular episode in memory management:
delete m_cache;
m_cache = new SCH_EAGLE_PLUGIN_CACHE( aLibraryFileName );
m_cache->Load();
}
}
int SCH_EAGLE_PLUGIN::GetModifyHash() const
{
if( m_cache )
return m_cache->GetModifyHash();
// If the cache hasn't been loaded, it hasn't been modified.
return 0;
}
void SCH_EAGLE_PLUGIN::EnumerateSymbolLib( wxArrayString& aAliasNameList,
const wxString& aLibraryPath,
const PROPERTIES* aProperties )
{
LOCALE_IO toggle; // toggles on, then off, the C locale.
init( NULL, aProperties );
cacheLib( aLibraryPath );
const LIB_ALIAS_MAP& aliases = m_cache->m_aliases;
for( LIB_ALIAS_MAP::const_iterator it = aliases.begin(); it != aliases.end(); ++it )
aAliasNameList.Add( FROM_UTF8( it->first.c_str() ) );
}
LIB_ALIAS* SCH_EAGLE_PLUGIN::LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
const PROPERTIES* aProperties )
{
LOCALE_IO toggle; // toggles on, then off, the C locale.
m_props = aProperties;
cacheLib( aLibraryPath );
LIB_ALIAS_MAP::const_iterator it = m_cache->m_aliases.find( TO_UTF8( aAliasName ) );
if( it == m_cache->m_aliases.end() )
return NULL;
return it->second;
}
#ifndef _SCH_EAGLE_PLUGIN_H_
#define _SCH_EAGLE_PLUGIN_H_
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2016 CERN
* Copyright (C) 2016 KiCad Developers, see change_log.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 <sch_io_mgr.h>
class KIWAY;
class LINE_READER;
class SCH_SCREEN;
class SCH_SHEET;
class SCH_BITMAP;
class SCH_JUNCTION;
class SCH_NO_CONNECT;
class SCH_LINE;
class SCH_BUS_ENTRY_BASE;
class SCH_TEXT;
class SCH_COMPONENT;
class SCH_FIELD;
class PROPERTIES;
class SCH_EAGLE_PLUGIN_CACHE;
class LIB_PART;
class PART_LIB;
class LIB_ALIAS;
/**
* Class SCH_EAGLE_PLUGIN
*
* As with all SCH_PLUGINs there is no UI dependencies i.e. windowing calls allowed.
*/
class SCH_EAGLE_PLUGIN : public SCH_PLUGIN
{
public:
SCH_EAGLE_PLUGIN();
virtual ~SCH_EAGLE_PLUGIN() {}
const wxString GetName() const override
{
return wxT( "EAGLE File Format" );
}
const wxString GetFileExtension() const override
{
return wxT( "lbr" );
}
int GetModifyHash() const override;
void EnumerateSymbolLib( wxArrayString& aAliasNameList,
const wxString& aLibraryPath,
const PROPERTIES* aProperties = NULL ) override;
LIB_ALIAS* LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
const PROPERTIES* aProperties = NULL ) override;
private:
void cacheLib( const wxString& aLibraryFileName );
protected:
int m_version; ///< Version of file being loaded.
wxString m_error; ///< For throwing exceptions
wxString m_path; ///< Root project path for loading child sheets.
const PROPERTIES* m_props; ///< Passed via Save() or Load(), no ownership, may be NULL.
KIWAY* m_kiway; ///< Required for path to legacy component libraries.
FILE_OUTPUTFORMATTER* m_out; ///< The output formatter for saving SCH_SCREEN objects.
SCH_EAGLE_PLUGIN_CACHE* m_cache;
/// initialize PLUGIN like a constructor would.
void init( KIWAY* aKiway, const PROPERTIES* aProperties = NULL );
};
#endif // _SCH_EAGLE_PLUGIN_H_
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment