From 57acee0d4ecba69d5556453a7639b30675e71b91 Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Sat, 2 Jun 2012 12:07:30 -0500 Subject: [PATCH] EAGLE_PLUGIN: finish xpath error reporting mechanism for XML document traversal --- pcbnew/eagle_plugin.cpp | 383 ++++++++++++++++++++++++++++++---------- pcbnew/eagle_plugin.h | 56 +++--- 2 files changed, 316 insertions(+), 123 deletions(-) diff --git a/pcbnew/eagle_plugin.cpp b/pcbnew/eagle_plugin.cpp index 09e438366d..ace1997afd 100644 --- a/pcbnew/eagle_plugin.cpp +++ b/pcbnew/eagle_plugin.cpp @@ -48,11 +48,10 @@ our error message. Load() TODO's -*) finish xpath support *) test footprint placement on board back *) netclass info? *) verify zone fill clearances are correct -*) write BOARD::Move() and reposition to center of page? +*) write BOARD::Move() and reposition to center of page from centerBoard() */ @@ -78,6 +77,88 @@ Load() TODO's using namespace boost::property_tree; +/// segment (element) of our XPATH into the Eagle XML document tree in PTREE form. +struct TRIPLET +{ + const char* element; + const char* attribute; + const char* value; + + TRIPLET( const char* aElement, const char* aAttribute = "", const char* aValue = "" ) : + element( aElement ), + attribute( aAttribute ), + value( aValue ) + {} +}; + + +/** + * Class XPATH + * keeps track of what we are working on within a PTREE. + * Then if an exception is thrown, the place within the tree that gave us + * grief can be reported almost accurately. To minimally impact + * speed, merely assign const char* pointers during the tree walking + * expedition. The const char* pointers must be to C strings residing either in + * the data or code segment (i.e. "compiled in") or within the XML document, but + * not on the stack, since the stack is unwound during the throwing of the + * exception. The XML document will not immediately vanish since we capture + * the xpath (using function Contents()) before the XML document tree (PTREE) + * is destroyed. + */ +class XPATH +{ + std::vector p; + +public: + void push( const char* aPathSegment, const char* aAttribute="" ) + { + p.push_back( TRIPLET( aPathSegment, aAttribute ) ); + } + + void clear() { p.clear(); } + + void pop() { p.pop_back(); } + + /// modify the last path node's value + void Value( const char* aValue ) + { + p.back().value = aValue; + } + + /// modify the last path node's attribute + void Attribute( const char* aAttribute ) + { + p.back().attribute = aAttribute; + } + + /// return the contents of the XPATH as a single string + std::string Contents() + { + typedef std::vector::const_iterator CITER; + + std::string ret; + + for( CITER it = p.begin(); it != p.end(); ++it ) + { + if( it != p.begin() ) + ret += '.'; + + ret += it->element; + + if( it->attribute[0] && it->value[0] ) + { + ret += '['; + ret += it->attribute; + ret += '='; + ret += it->value; + ret += ']'; + } + } + + return ret; + } +}; + typedef EAGLE_PLUGIN::BIU BIU; typedef PTREE::const_assoc_iterator CA_ITER; @@ -91,9 +172,13 @@ typedef boost::optional opt_string; typedef boost::optional opt_int; typedef boost::optional opt_double; typedef boost::optional opt_bool; -//typedef boost::optional opt_cptree; +/** + * Function parseOptionalBool + * returns an opt_bool and sets it true or false according to the presence + * and value of an attribute within the CPTREE element. + */ static opt_bool parseOptionalBool( CPTREE& attribs, const char* aName ) { opt_bool ret; @@ -125,6 +210,8 @@ struct EROT typedef boost::optional opt_erot; +/// parse an Eagle XML "rot" field. Unfortunately the DTD seems not to explain +/// this format very well. R[S][M]. Examples: "R90", "MR180", "SR180" static EROT erot( const std::string& aRot ) { EROT rot; @@ -136,10 +223,10 @@ static EROT erot( const std::string& aRot ) + int( rot.spin ) // skip optional leading 'S' + int( rot.mirror ), // skip optional leading 'M' NULL ); - return rot; } +/// Eagle "rot" fields are optional, handle that by returning opt_erot. static opt_erot parseOptionalEROT( CPTREE& attribs ) { opt_erot ret; @@ -164,18 +251,36 @@ struct EWIRE /** * Constructor EWIRE * converts a 's xml attributes to binary without additional conversion. - * @param aResult is an EWIRE to fill in with the data converted to binary. + * This result is an EWIRE with the textual data merely converted to binary. */ EWIRE::EWIRE( CPTREE& aWire ) { CPTREE& attribs = aWire.get_child( "" ); + /* + + + */ + x1 = attribs.get( "x1" ); y1 = attribs.get( "y1" ); x2 = attribs.get( "x2" ); y2 = attribs.get( "y2" ); width = attribs.get( "width" ); layer = attribs.get( "layer" ); + + // ignoring extent, style, curve and cap } @@ -331,18 +436,18 @@ EATTR::EATTR( CPTREE& aAttribute ) /* or context -- - constant %Bool; "no" -- only in context -- - > + name %String; #REQUIRED + value %String; #IMPLIED + x %Coord; #IMPLIED + y %Coord; #IMPLIED + size %Dimension; #IMPLIED + layer %Layer; #IMPLIED + font %TextFont; #IMPLIED + ratio %Int; #IMPLIED + rot %Rotation; "R0" + display %AttributeDisplay; "value" -- only in or context -- + constant %Bool; "no" -- only in context -- + > */ name = attribs.get( "name" ); // #REQUIRED @@ -478,11 +583,8 @@ struct EPAD LONG, OFFSET, }; - opt_int shape; - opt_erot rot; - opt_bool stop; opt_bool thermals; opt_bool first; @@ -579,7 +681,7 @@ ESMD::ESMD( CPTREE& aSMD ) > */ - // the DTD says these must be present, throw exception if not found + // DTD #REQUIRED, throw exception if not found name = attribs.get( "name" ); x = attribs.get( "x" ); y = attribs.get( "y" ); @@ -622,7 +724,7 @@ EVERTEX::EVERTEX( CPTREE& aVertex ) } -// Eagle polygon, without vertices which are parsed as needed +/// Eagle polygon, without vertices which are parsed as needed struct EPOLYGON { double width; @@ -634,7 +736,6 @@ struct EPOLYGON HATCH, CUTOUT, }; - opt_int pour; opt_double isolate; opt_bool orphans; @@ -682,6 +783,7 @@ EPOLYGON::EPOLYGON( CPTREE& aPolygon ) rank = attribs.get_optional( "rank" ); } +/// Eagle hole element struct EHOLE { double x; @@ -710,6 +812,8 @@ EHOLE::EHOLE( CPTREE& aHole ) drill = attribs.get( "drill" ); } + +/// Eagle element element struct EELEMENT { std::string name; @@ -805,14 +909,16 @@ static inline std::string makeKey( const std::string& aFirst, const std::string& } -/// Make a unique time stamp, in this case from a unique tree memory location +/// Make a unique time stamp static inline unsigned long timeStamp( CPTREE& aTree ) { + // in this case from a unique tree memory location return (unsigned long)(void*) &aTree; } -EAGLE_PLUGIN::EAGLE_PLUGIN() +EAGLE_PLUGIN::EAGLE_PLUGIN() : + m_xpath( new XPATH() ) { init( NULL ); } @@ -820,6 +926,7 @@ EAGLE_PLUGIN::EAGLE_PLUGIN() EAGLE_PLUGIN::~EAGLE_PLUGIN() { + delete m_xpath; } @@ -865,41 +972,41 @@ BOARD* EAGLE_PLUGIN::Load( const wxString& aFileName, BOARD* aAppendToMe, PROPE try { - // 8 bit filename should be encoded in current locale, not necessarily utf8. + // 8 bit "filename" should be encoded according to disk filename encoding, + // (maybe this is current locale, maybe not, its a filesystem issue), + // and is not necessarily utf8. std::string filename = (const char*) aFileName.fn_str(); read_xml( filename, doc, xml_parser::trim_whitespace | xml_parser::no_comments ); - loadAllSections( doc, bool( aAppendToMe ) ); + loadAllSections( doc ); + + // should be empty, else missing m_xpath->pop() + wxASSERT( m_xpath->Contents().size() == 0 ); + } + + catch( file_parser_error fpe ) + { + // for xml_parser_error, what() has the line number in it, + // but no byte offset. That should be an adequate error message. + THROW_IO_ERROR( fpe.what() ); } // Class ptree_error is a base class for xml_parser_error & file_parser_error, // so one catch should be OK for all errors. catch( ptree_error pte ) { - // for xml_parser_error, what() has the line number in it, - // but no byte offset. That should be an adequate error message. - THROW_IO_ERROR( pte.what() ); + std::string errmsg = pte.what(); + + errmsg += " @\n"; + errmsg += m_xpath->Contents(); + + THROW_IO_ERROR( errmsg ); } // IO_ERROR exceptions are left uncaught, they pass upwards from here. - /* - if( aProperties ) - { - const wxString& pageWidth = (*aProperties)["page_width"]; - const wxString& pageHeight = (*aProperties)["page_height"]; - - if( pageWidth.size() && pageHeight.size() ) - { - EDA_RECT bbbox = m_board->GetBoundingBox(); - int w = wxAtoi( pageWidth ); - int h = wxAtoi( pageHeight ); - - m_board->Move( ); - } - } - */ + centerBoard(); deleter.release(); return m_board; @@ -909,6 +1016,8 @@ BOARD* EAGLE_PLUGIN::Load( const wxString& aFileName, BOARD* aAppendToMe, PROPE void EAGLE_PLUGIN::init( PROPERTIES* aProperties ) { m_hole_count = 0; + + m_xpath->clear(); m_pads_to_nets.clear(); m_templates.clear(); @@ -920,10 +1029,10 @@ void EAGLE_PLUGIN::init( PROPERTIES* aProperties ) } -void EAGLE_PLUGIN::loadAllSections( CPTREE& aDoc, bool aAppendToMe ) +void EAGLE_PLUGIN::loadAllSections( CPTREE& aDoc ) { CPTREE& drawing = aDoc.get_child( "eagle.drawing" ); - CPTREE& board = drawing.get_child( "board" ); + m_xpath->push( "eagle.drawing" ); { CPTREE& layers = drawing.get_child( "layers" ); @@ -931,29 +1040,32 @@ void EAGLE_PLUGIN::loadAllSections( CPTREE& aDoc, bool aAppendToMe ) } { + CPTREE& board = drawing.get_child( "board" ); + m_xpath->push( "board" ); + CPTREE& plain = board.get_child( "plain" ); loadPlain( plain ); - } - { CPTREE& signals = board.get_child( "signals" ); loadSignals( signals ); - } - { CPTREE& libs = board.get_child( "libraries" ); loadLibraries( libs ); - } - { CPTREE& elems = board.get_child( "elements" ); loadElements( elems ); + + m_xpath->pop(); // "board" } + + m_xpath->pop(); // "eagle.drawing" } void EAGLE_PLUGIN::loadLayerDefs( CPTREE& aLayers ) { + m_xpath->push( "layers.layer" ); + typedef std::vector ELAYERS; typedef ELAYERS::const_iterator EITER; @@ -981,16 +1093,21 @@ void EAGLE_PLUGIN::loadLayerDefs( CPTREE& aLayers ) // could map the colors here } + + m_xpath->pop(); } void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) { + m_xpath->push( "plain" ); + // (polygon | wire | text | circle | rectangle | frame | hole)* for( CITER gr = aGraphics.begin(); gr != aGraphics.end(); ++gr ) { if( !gr->first.compare( "wire" ) ) { + m_xpath->push( "wire" ); EWIRE w( gr->second ); DRAWSEGMENT* dseg = new DRAWSEGMENT( m_board ); @@ -1001,6 +1118,7 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) dseg->SetStart( wxPoint( kicad_x( w.x1 ), kicad_y( w.y1 ) ) ); dseg->SetEnd( wxPoint( kicad_x( w.x2 ), kicad_y( w.y2 ) ) ); dseg->SetWidth( kicad( w.width ) ); + m_xpath->pop(); } else if( !gr->first.compare( "text" ) ) @@ -1012,7 +1130,7 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) (void) breakhere; } #endif - + m_xpath->push( "text" ); ETEXT t( gr->second ); int layer = kicad_layer( t.layer ); @@ -1034,7 +1152,6 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) if( t.rot ) { - // eagles does not rotate text spun to 180 degrees unless spin is set. #if 0 if( t.rot->spin || ( t.rot->degrees != 180 && t.rot->degrees != 270 ) ) pcbtxt->SetOrientation( t.rot->degrees * 10 ); @@ -1102,10 +1219,12 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) pcbtxt->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); break; } + m_xpath->pop(); } else if( !gr->first.compare( "circle" ) ) { + m_xpath->push( "circle" ); ECIRCLE c( gr->second ); DRAWSEGMENT* dseg = new DRAWSEGMENT( m_board ); @@ -1117,12 +1236,14 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) dseg->SetStart( wxPoint( kicad_x( c.x ), kicad_y( c.y ) ) ); dseg->SetEnd( wxPoint( kicad_x( c.x + c.radius ), kicad_y( c.y ) ) ); dseg->SetWidth( kicad( c.width ) ); + m_xpath->pop(); } // This seems to be a simplified rectangular [copper] zone, cannot find any // net related info on it from the DTD. else if( !gr->first.compare( "rectangle" ) ) { + m_xpath->push( "rectangle" ); ERECT r( gr->second ); int layer = kicad_layer( r.layer ); @@ -1148,10 +1269,12 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) zone->m_Poly->SetHatch( outline_hatch, Mils2iu( zone->m_Poly->GetDefaultHatchPitchMils() ) ); } + m_xpath->pop(); } else if( !gr->first.compare( "hole" ) ) { + m_xpath->push( "hole" ); EHOLE e( gr->second ); // Fabricate a MODULE with a single PAD_HOLE_NOT_PLATED pad. @@ -1189,6 +1312,7 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) pad->SetSize( sz ); pad->SetLayerMask( ALL_CU_LAYERS /* | SOLDERMASK_LAYER_BACK | SOLDERMASK_LAYER_FRONT */ ); + m_xpath->pop(); } else if( !gr->first.compare( "frame" ) ) @@ -1197,63 +1321,83 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) } else if( !gr->first.compare( "polygon" ) ) { - // step up, be a man + // could be on a copper layer, could be on another layer. + // copper layer would be done using netCode=0 type of ZONE_CONTAINER. } } + m_xpath->pop(); } void EAGLE_PLUGIN::loadLibraries( CPTREE& aLibs ) { + m_xpath->push( "libraries.library", "name" ); + for( CITER library = aLibs.begin(); library != aLibs.end(); ++library ) { const std::string& lib_name = library->second.get( ".name" ); - // library will have node, skip that and get the packages node - CPTREE& packages = library->second.get_child( "packages" ); + m_xpath->Value( lib_name.c_str() ); - // Create a MODULE for all the eagle packages, for use later via a copy constructor - // to instantiate needed MODULES in our BOARD. Save the MODULE templates in - // a MODULE_MAP using a single lookup key consisting of libname+pkgname. - - for( CITER package = packages.begin(); package != packages.end(); ++package ) { - const std::string& pack_name = package->second.get( ".name" ); + m_xpath->push( "packages" ); -#if defined(DEBUG) - if( !pack_name.compare( "TO220H" ) ) + // library will have node, skip that and get the single packages node + CPTREE& packages = library->second.get_child( "packages" ); + + // Create a MODULE for all the eagle packages, for use later via a copy constructor + // to instantiate needed MODULES in our BOARD. Save the MODULE templates in + // a MODULE_MAP using a single lookup key consisting of libname+pkgname. + + for( CITER package = packages.begin(); package != packages.end(); ++package ) { - int breakhere = 1; - (void) breakhere; + m_xpath->push( "package", "name" ); + const std::string& pack_name = package->second.get( ".name" ); + + #if defined(DEBUG) + if( !pack_name.compare( "TO220H" ) ) + { + int breakhere = 1; + (void) breakhere; + } + #endif + m_xpath->Value( pack_name.c_str() ); + + std::string key = makeKey( lib_name, pack_name ); + + MODULE* m = makeModule( package->second, pack_name ); + + // add the templating MODULE to the MODULE template factory "m_templates" + std::pair r = m_templates.insert( key, m ); + + if( !r.second ) + { + wxString lib = FROM_UTF8( lib_name.c_str() ); + wxString pkg = FROM_UTF8( pack_name.c_str() ); + + wxString emsg = wxString::Format( + _( " name:'%s' duplicated in eagle :'%s'" ), + GetChars( pkg ), + GetChars( lib ) + ); + THROW_IO_ERROR( emsg ); + } + + m_xpath->pop(); } -#endif - std::string key = makeKey( lib_name, pack_name ); - - MODULE* m = makeModule( package->second, pack_name ); - - // add the templating MODULE to the MODULE template factory "m_templates" - std::pair r = m_templates.insert( key, m ); - - if( !r.second ) - { - wxString lib = FROM_UTF8( lib_name.c_str() ); - wxString pkg = FROM_UTF8( pack_name.c_str() ); - - wxString emsg = wxString::Format( - _( " name:'%s' duplicated in eagle :'%s'" ), - GetChars( pkg ), - GetChars( lib ) - ); - THROW_IO_ERROR( emsg ); - } + m_xpath->pop(); // "packages" } } + + m_xpath->pop(); } void EAGLE_PLUGIN::loadElements( CPTREE& aElements ) { + m_xpath->push( "elements.element", "name" ); + for( CITER it = aElements.begin(); it != aElements.end(); ++it ) { if( it->first.compare( "element" ) ) @@ -1261,6 +1405,8 @@ void EAGLE_PLUGIN::loadElements( CPTREE& aElements ) EELEMENT e( it->second ); + m_xpath->Value( e.name.c_str() ); + #if 1 && defined(DEBUG) if( !e.value.compare( "LP2985-33DBVR" ) ) { @@ -1322,12 +1468,14 @@ void EAGLE_PLUGIN::loadElements( CPTREE& aElements ) } } + m_xpath->push( "attribute", "name" ); + // VALUE and NAME can have something like our text "effects" overrides // in SWEET and new schematic. Eagle calls these XML elements "attribute". // There can be one for NAME and/or VALUE both. Features present in the // EATTR override the ones established in the package only if they are - // present here. So the logic is a bit different than in packageText() - // and in plain text. + // present here (except for rot, which if not present means angle zero). + // So the logic is a bit different than in packageText() and in plain text. for( CITER ait = it->second.begin(); ait != it->second.end(); ++ait ) { if( ait->first.compare( "attribute" ) ) @@ -1347,6 +1495,8 @@ void EAGLE_PLUGIN::loadElements( CPTREE& aElements ) continue; } + m_xpath->Value( a.name.c_str() ); + if( a.value ) { txt->SetText( FROM_UTF8( a.value->c_str() ) ); @@ -1378,7 +1528,9 @@ void EAGLE_PLUGIN::loadElements( CPTREE& aElements ) } // The "rot" in a EATTR seems to be assumed to be zero if it is not - // present, and this becomes an override to the package's text field + // present, and this zero rotation becomes an override to the + // package's text field. If they did not want zero, they specify + // what they want explicitly. EROT rot; if( a.rot ) @@ -1390,7 +1542,6 @@ void EAGLE_PLUGIN::loadElements( CPTREE& aElements ) angle -= m->GetOrientation(); // subtract module's angle txt->SetOrientation( angle ); } - else { // ETEXT::TOP_RIGHT: @@ -1400,7 +1551,11 @@ void EAGLE_PLUGIN::loadElements( CPTREE& aElements ) txt->SetMirrored( rot.mirror ); } + + m_xpath->pop(); // "attribute" } + + m_xpath->pop(); // "elements.element" } @@ -1865,6 +2020,8 @@ void EAGLE_PLUGIN::packageSMD( MODULE* aModule, CPTREE& aTree ) const void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) { + m_xpath->push( "signals.signal", "name" ); + int netCode = 1; for( CITER net = aSignals.begin(); net != aSignals.end(); ++net, ++netCode ) @@ -1872,6 +2029,8 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) const std::string& nname = net->second.get( ".name" ); wxString netName = FROM_UTF8( nname.c_str() ); + m_xpath->Value( nname.c_str() ); + m_board->AppendNet( new NETINFO_ITEM( m_board, netName, netCode ) ); // (contactref | polygon | wire | via)* @@ -1879,6 +2038,7 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) { if( !it->first.compare( "wire" ) ) { + m_xpath->push( "wire" ); EWIRE w( it->second ); int layer = kicad_layer( w.layer ); @@ -1901,10 +2061,13 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) { // put non copper wires where the sun don't shine. } + + m_xpath->pop(); } else if( !it->first.compare( "via" ) ) { + m_xpath->push( "via" ); EVIA v( it->second ); int layer_front_most = kicad_layer( v.layer_front_most ); @@ -1952,10 +2115,12 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) via->SetShape( S_CIRCLE ); // @todo should be in SEGVIA constructor } + m_xpath->pop(); } else if( !it->first.compare( "contactref" ) ) { + m_xpath->push( "contactref" ); // CPTREE& attribs = it->second.get_child( "" ); @@ -1967,10 +2132,13 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) // D(printf( "adding refname:'%s' pad:'%s' netcode:%d netname:'%s'\n", reference.c_str(), pad.c_str(), netCode, nname.c_str() );) m_pads_to_nets[ key ] = ENET( netCode, nname ); + + m_xpath->pop(); } else if( !it->first.compare( "polygon" ) ) { + m_xpath->push( "polygon" ); EPOLYGON p( it->second ); int layer = kicad_layer( p.layer ); @@ -2027,9 +2195,13 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) int rank = p.rank ? *p.rank : 0; zone->SetPriority( rank ); } + + m_xpath->pop(); // "polygon" } } } + + m_xpath->pop(); // "signals.signal" } @@ -2163,8 +2335,8 @@ int EAGLE_PLUGIN::kicad_layer( int aEagleLayer ) case 95: kiLayer = ECO1_N; break; case 96: kiLayer = ECO2_N; break; default: - D( printf( "unexpected eagle layer: %d\n", aEagleLayer );) - kiLayer = -1; break; // our eagle understanding is incomplete + D( printf( "unsupported eagle layer: %d\n", aEagleLayer );) + kiLayer = -1; break; // some layers do not map to KiCad } } @@ -2172,6 +2344,27 @@ int EAGLE_PLUGIN::kicad_layer( int aEagleLayer ) } +void EAGLE_PLUGIN::centerBoard() +{ + /* + if( m_props ) + { + const wxString& pageWidth = (*m_props)["page_width"]; + const wxString& pageHeight = (*m_props)["page_height"]; + + if( pageWidth.size() && pageHeight.size() ) + { + EDA_RECT bbbox = m_board->GetBoundingBox(); + int w = wxAtoi( pageWidth ); + int h = wxAtoi( pageHeight ); + + m_board->Move( ); + } + } + */ +} + + /* void EAGLE_PLUGIN::Save( const wxString& aFileName, BOARD* aBoard, PROPERTIES* aProperties ) { diff --git a/pcbnew/eagle_plugin.h b/pcbnew/eagle_plugin.h index fb12685d6c..25bf85a5d1 100644 --- a/pcbnew/eagle_plugin.h +++ b/pcbnew/eagle_plugin.h @@ -56,28 +56,18 @@ struct ENET {} }; -typedef std::map< std::string, ENET > NET_MAP; -typedef NET_MAP::const_iterator NET_MAP_CITER; - -/* -#include -namespace boost { - namespace property_tree - { - template < class Key, class Data, class KeyCompare = std::less > - class basic_ptree; - - typedef basic_ptree< std::string, std::string > ptree; - } -} -*/ +typedef std::map< std::string, ENET > NET_MAP; +typedef NET_MAP::const_iterator NET_MAP_CITER; typedef boost::property_tree::ptree PTREE; typedef const PTREE CPTREE; +class XPATH; + /** * Class EAGLE_PLUGIN - * works with Eagle 6.x XML board files and footprints. + * works with Eagle 6.x XML board files and footprints to implement the + * Pcbnew PLUGIN API, or a portion of it. */ class EAGLE_PLUGIN : public PLUGIN { @@ -110,38 +100,46 @@ public: //------------------------------------------------------ - typedef int BIU; + typedef int BIU; EAGLE_PLUGIN(); ~EAGLE_PLUGIN(); private: - int m_hole_count; ///< generates unique module names from eagle "hole"s. + XPATH* m_xpath; ///< keeps track of what we are working on within + ///< XML document during a Load(). - NET_MAP m_pads_to_nets; + std::string m_err_path; ///< snapshot m_xpath contentx into here on exception - MODULE_MAP m_templates; ///< is part of a MODULE factory that operates + int m_hole_count; ///< generates unique module names from eagle "hole"s. + + NET_MAP m_pads_to_nets; ///< net list + + MODULE_MAP m_templates; ///< is part of a MODULE factory that operates ///< using copy construction. ///< lookup key is libname.packagename - PROPERTIES* m_props; ///< passed via Save() or Load(), no ownership, may be NULL. - - BOARD* m_board; ///< which BOARD, no ownership here - double mm_per_biu; ///< how many mm in each BIU - double biu_per_mm; ///< how many bius in a mm + PROPERTIES* m_props; ///< passed via Save() or Load(), no ownership, may be NULL. + BOARD* m_board; ///< which BOARD is being worked on, no ownership here + double mm_per_biu; ///< how many mm in each BIU + double biu_per_mm; ///< how many bius in a mm /// initialize PLUGIN like a constructor would, and futz with fresh BOARD if needed. void init( PROPERTIES* aProperties ); + /// Convert an Eagle distance to a KiCad distance. int kicad( double d ) const; int kicad_y( double y ) const { return -kicad( y ); } int kicad_x( double x ) const { return kicad( x ); } + + /// create a font size (fontz) from an eagle font size scalar wxSize kicad_fontz( double d ) const; - + /// Convert an Eagle layer to a KiCad layer. static int kicad_layer( int aLayer ); + /// Convert a KiCad distance to an Eagle distance. double eagle( BIU d ) const { return mm_per_biu * d; } double eagle_x( BIU x ) const { return eagle( x ); } double eagle_y( BIU y ) const { return eagle( y ); } @@ -174,13 +172,16 @@ private: // all these loadXXX() throw IO_ERROR or ptree_error exceptions: - void loadAllSections( CPTREE& aDocument, bool aAppendToMe ); + void loadAllSections( CPTREE& aDocument ); void loadLayerDefs( CPTREE& aLayers ); void loadPlain( CPTREE& aPlain ); void loadSignals( CPTREE& aSignals ); void loadLibraries( CPTREE& aLibs ); void loadElements( CPTREE& aElements ); + /// move the BOARD into the center of the page + void centerBoard(); + /** * Function fmtDEG * formats an angle in a way particular to a board file format. This function @@ -203,7 +204,6 @@ private: void packageCircle( MODULE* aModule, CPTREE& aTree ) const; void packageHole( MODULE* aModule, CPTREE& aTree ) const; void packageSMD( MODULE* aModule, CPTREE& aTree ) const; - }; #endif // EAGLE_PLUGIN_H_