Created
October 30, 2016 20:05
-
-
Save hdante/b86454b6d2b3c36b257c5e8475a4cf02 to your computer and use it in GitHub Desktop.
EAGLE Plugin incomplete
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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