From 8092d98ea382b0c21013e046b941884c96ab1821 Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Tue, 1 Jan 2013 22:05:48 -0600 Subject: [PATCH] 1) Implement EAGLE_PLUGIN::FootprintLoad(), FootprintEnumerate(), and IsFootprintWritable(). 2) Add PCAD_PLUGIN to _pcbnew.so for scripting, fixing a linking bug. --- pcbnew/CMakeLists.txt | 10 +- pcbnew/eagle_plugin.cpp | 224 +++++++++++++++++++++++++-------------- pcbnew/eagle_plugin.h | 62 +++++------ pcbnew/legacy_plugin.cpp | 34 +++--- 4 files changed, 201 insertions(+), 129 deletions(-) diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 0f989aa8fd..1afecf8e36 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -322,12 +322,14 @@ if (KICAD_SCRIPTING_MODULES) 3d-viewer pcbcommon common - bitmaps + pcad2kicadpcb polygon + bitmaps ${wxWidgets_LIBRARIES} ${OPENGL_LIBRARIES} ${GDI_PLUS_LIBRARIES} - ${PYTHON_LIBRARIES}) + ${PYTHON_LIBRARIES} + ) endif (KICAD_SCRIPTING_MODULES) @@ -414,9 +416,9 @@ target_link_libraries(pcbnew 3d-viewer pcbcommon common - bitmaps - polygon pcad2kicadpcb + polygon + bitmaps ${wxWidgets_LIBRARIES} ${OPENGL_LIBRARIES} ${GDI_PLUS_LIBRARIES} diff --git a/pcbnew/eagle_plugin.cpp b/pcbnew/eagle_plugin.cpp index 1a11a6eeaf..8cd93d8be3 100644 --- a/pcbnew/eagle_plugin.cpp +++ b/pcbnew/eagle_plugin.cpp @@ -67,6 +67,7 @@ Load() TODO's #include #include #include +#include #include #include @@ -1053,6 +1054,7 @@ static inline unsigned long timeStamp( CPTREE& aTree ) EAGLE_PLUGIN::EAGLE_PLUGIN() : m_rules( new ERULES() ), m_xpath( new XPATH() ) +// m_mod_time( wxDateTime::Now() ) { init( NULL ); } @@ -1158,7 +1160,8 @@ void EAGLE_PLUGIN::init( PROPERTIES* aProperties ) m_xpath->clear(); m_pads_to_nets.clear(); - m_templates.clear(); + + // m_templates.clear(); this is the FOOTPRINT cache too m_board = NULL; m_props = aProperties; @@ -1494,6 +1497,58 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) } +void EAGLE_PLUGIN::loadLibrary( CPTREE& aLib, const std::string* aLibName ) +{ + m_xpath->push( "packages" ); + + // library will have node, skip that and get the single packages node + CPTREE& packages = aLib.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 ) + { + m_xpath->push( "package", "name" ); + const std::string& pack_name = package->second.get( ".name" ); + +#if defined(DEBUG) + if( pack_name == "TO220H" ) + { + int breakhere = 1; + (void) breakhere; + } +#endif + m_xpath->Value( pack_name.c_str() ); + + std::string key = aLibName ? makeKey( *aLibName, pack_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 = aLibName ? FROM_UTF8( aLibName->c_str() ) : m_lib_path; + 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(); + } + + m_xpath->pop(); // "packages" +} + + void EAGLE_PLUGIN::loadLibraries( CPTREE& aLibs ) { m_xpath->push( "libraries.library", "name" ); @@ -1504,55 +1559,7 @@ void EAGLE_PLUGIN::loadLibraries( CPTREE& aLibs ) m_xpath->Value( lib_name.c_str() ); - { - m_xpath->push( "packages" ); - - // 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 ) - { - m_xpath->push( "package", "name" ); - const std::string& pack_name = package->second.get( ".name" ); - - #if defined(DEBUG) - if( pack_name == "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(); - } - - m_xpath->pop(); // "packages" - } + loadLibrary( library->second, &lib_name ); } m_xpath->pop(); @@ -1573,7 +1580,7 @@ void EAGLE_PLUGIN::loadElements( CPTREE& aElements ) EELEMENT e( it->second ); - // use "NULL-ness" as an indiation of presence of the attribute: + // use "NULL-ness" as an indication of presence of the attribute: EATTR* nameAttr = 0; EATTR* valueAttr = 0; @@ -2591,56 +2598,119 @@ void EAGLE_PLUGIN::centerBoard() } -/* -void EAGLE_PLUGIN::Save( const wxString& aFileName, BOARD* aBoard, PROPERTIES* aProperties ) +wxDateTime EAGLE_PLUGIN::getModificationTime( const wxString& aPath ) { - // Eagle lovers apply here. + wxFileName fn( aPath ); + + /* + // update the writable flag while we have a wxFileName, in a network this + // is possibly quite dynamic anyway. + m_writable = fn.IsFileWritable(); + */ + + return fn.GetModificationTime(); } -int EAGLE_PLUGIN::biuSprintf( char* buf, BIU aValue ) const +void EAGLE_PLUGIN::cacheLib( const wxString& aLibPath ) { - double engUnits = mm_per_biu * aValue; - int len; - - if( engUnits != 0.0 && fabs( engUnits ) <= 0.0001 ) + try { - // printf( "f: " ); - len = sprintf( buf, "%.10f", engUnits ); + wxDateTime modtime; - while( --len > 0 && buf[len] == '0' ) - buf[len] = '\0'; + if( aLibPath != m_lib_path || + m_mod_time != ( modtime = getModificationTime( aLibPath ) ) ) + { + //D(printf("Loading '%s'\n", TO_UTF8( aLibPath ) );) - ++len; + PTREE doc; + LOCALE_IO toggle; // toggles on, then off, the C locale. + + m_templates.clear(); + + // Set this before completion of loading, since we rely on it for + // text of an exception. Delay setting m_mod_time until after successful load + // however. + m_lib_path = aLibPath; + + // 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*) aLibPath.char_str( wxConvFile ); + + read_xml( filename, doc, xml_parser::trim_whitespace | xml_parser::no_comments ); + + CPTREE& library = doc.get_child( "eagle.drawing.library" ); + + m_xpath->push( "eagle.drawing.library" ); + + loadLibrary( library, NULL ); + + m_xpath->pop(); + + m_mod_time = modtime; + } } - else + catch( file_parser_error fpe ) { - // printf( "g: " ); - len = sprintf( buf, "%.10g", engUnits ); + // 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() ); } - return len; -} + // 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 ) + { + std::string errmsg = pte.what(); -std::string EAGLE_PLUGIN::fmtBIU( BIU aValue ) const -{ - char temp[50]; + errmsg += " @\n"; + errmsg += m_xpath->Contents(); - int len = biuSprintf( temp, aValue ); - - return std::string( temp, len ); + THROW_IO_ERROR( errmsg ); + } } wxArrayString EAGLE_PLUGIN::FootprintEnumerate( const wxString& aLibraryPath, PROPERTIES* aProperties ) { - return wxArrayString(); + init( aProperties ); + + cacheLib( aLibraryPath ); + + wxArrayString ret; + + for( MODULE_CITER it = m_templates.begin(); it != m_templates.end(); ++it ) + ret.Add( FROM_UTF8( it->first.c_str() ) ); + + return ret; } MODULE* EAGLE_PLUGIN::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, PROPERTIES* aProperties ) { - return NULL; + init( aProperties ); + + cacheLib( aLibraryPath ); + + std::string key = TO_UTF8( aFootprintName ); + + MODULE_CITER mi = m_templates.find( key ); + + if( mi == m_templates.end() ) + return NULL; + + // copy constructor to clone the template + MODULE* ret = new MODULE( *mi->second ); + + return ret; +} + + +/* +void EAGLE_PLUGIN::Save( const wxString& aFileName, BOARD* aBoard, PROPERTIES* aProperties ) +{ + // Eagle lovers apply here. } diff --git a/pcbnew/eagle_plugin.h b/pcbnew/eagle_plugin.h index 816b2bb1ba..e28ad5db07 100644 --- a/pcbnew/eagle_plugin.h +++ b/pcbnew/eagle_plugin.h @@ -85,13 +85,18 @@ public: const wxString& GetFileExtension() const; -/* - void Save( const wxString& aFileName, BOARD* aBoard, PROPERTIES* aProperties = NULL ); - wxArrayString FootprintEnumerate( const wxString& aLibraryPath, PROPERTIES* aProperties = NULL); MODULE* FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, PROPERTIES* aProperties = NULL ); + bool IsFootprintLibWritable( const wxString& aLibraryPath ) + { + return false; // until someone writes others like FootprintSave(), etc. + } + +/* + void Save( const wxString& aFileName, BOARD* aBoard, PROPERTIES* aProperties = NULL ); + void FootprintSave( const wxString& aLibraryPath, const MODULE* aFootprint, PROPERTIES* aProperties = NULL ); void FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName ); @@ -100,7 +105,6 @@ public: bool FootprintLibDelete( const wxString& aLibraryPath, PROPERTIES* aProperties = NULL ); - bool IsFootprintLibWritable( const wxString& aLibraryPath ); */ //------------------------------------------------------ @@ -116,21 +120,23 @@ private: XPATH* m_xpath; ///< keeps track of what we are working on within ///< XML document during a Load(). - std::string m_err_path; ///< snapshot m_xpath contentx into here on exception - 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 + ///< lookup key is either libname.packagename or simply + ///< packagename if FootprintLoad() or FootprintEnumberate() 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 + wxString m_lib_path; + wxDateTime m_mod_time; + /// initialize PLUGIN like a constructor would, and futz with fresh BOARD if needed. void init( PROPERTIES* aProperties ); @@ -150,31 +156,11 @@ private: double eagle_x( BIU x ) const { return eagle( x ); } double eagle_y( BIU y ) const { return eagle( y ); } + /// This PLUGIN only caches one footprint library, this determines which one. + void cacheLib( const wxString& aLibraryPath ); -#if 0 - /// encapsulate the BIU formatting tricks in one place. - int biuSprintf( char* buf, BIU aValue ) const; - - /** - * Function fmtBIU - * converts a BIU to engineering units by scaling and formatting to ASCII. - * This function is the complement of biuParse(). One has to know what the - * other is doing. - */ - std::string fmtBIU( BIU aValue ) const; - - std::string fmtBIUPair( BIU first, BIU second ) const; - - std::string fmtBIUPoint( const wxPoint& aPoint ) const - { - return fmtBIUPair( aPoint.x, aPoint.y ); - } - - std::string fmtBIUSize( const wxSize& aSize ) const - { - return fmtBIUPair( aSize.x, aSize.y ); - } -#endif + /// get a file's or dir's modification time. + static wxDateTime getModificationTime( const wxString& aPath ); // all these loadXXX() throw IO_ERROR or ptree_error exceptions: @@ -183,6 +169,20 @@ private: void loadLayerDefs( CPTREE& aLayers ); void loadPlain( CPTREE& aPlain ); void loadSignals( CPTREE& aSignals ); + + /** + * Function loadLibrary + * loads the Eagle "library" XML element, which can occur either under + * a "libraries" element (if a *.brd file) or under a "drawing" element if a + * *.lbr file. + * @param aLib is the portion of the loaded XML document tree that is the "library" + * element. + * @param aLibName is a pointer to the library name or NULL. If NULL this means + * we are loading a *.lbr not a *.brd file and the key used in m_templates is to exclude + * the library name. + */ + void loadLibrary( CPTREE& aLib, const std::string* aLibName ); + void loadLibraries( CPTREE& aLibs ); void loadElements( CPTREE& aElements ); diff --git a/pcbnew/legacy_plugin.cpp b/pcbnew/legacy_plugin.cpp index 25401a23e2..b1f1d95b8b 100644 --- a/pcbnew/legacy_plugin.cpp +++ b/pcbnew/legacy_plugin.cpp @@ -3866,7 +3866,7 @@ typedef MODULE_MAP::const_iterator MODULE_CITER; struct FPL_CACHE { LEGACY_PLUGIN* m_owner; // my owner, I need its LEGACY_PLUGIN::LoadMODULE() - wxString m_lib_name; + wxString m_lib_path; wxDateTime m_mod_time; MODULE_MAP m_modules; // map or tuple of footprint_name vs. MODULE* bool m_writable; @@ -3877,7 +3877,7 @@ struct FPL_CACHE // error codes nor user interface calls from here, nor in any PLUGIN. // Catch these exceptions higher up please. - /// save the entire legacy library to m_lib_name; + /// save the entire legacy library to m_lib_path; void Save(); void SaveHeader( FILE* aFile ); @@ -3905,7 +3905,7 @@ struct FPL_CACHE FPL_CACHE::FPL_CACHE( LEGACY_PLUGIN* aOwner, const wxString& aLibraryPath ) : m_owner( aOwner ), - m_lib_name( aLibraryPath ), + m_lib_path( aLibraryPath ), m_writable( true ) { } @@ -3913,7 +3913,7 @@ FPL_CACHE::FPL_CACHE( LEGACY_PLUGIN* aOwner, const wxString& aLibraryPath ) : wxDateTime FPL_CACHE::GetLibModificationTime() { - wxFileName fn( m_lib_name ); + wxFileName fn( m_lib_path ); // update the writable flag while we have a wxFileName, in a network this // is possibly quite dynamic anyway. @@ -3925,7 +3925,7 @@ wxDateTime FPL_CACHE::GetLibModificationTime() void FPL_CACHE::Load() { - FILE_LINE_READER reader( m_lib_name ); + FILE_LINE_READER reader( m_lib_path ); ReadAndVerifyHeader( &reader ); SkipIndex( &reader ); @@ -3970,7 +3970,7 @@ void FPL_CACHE::ReadAndVerifyHeader( LINE_READER* aReader ) L_bad_library: THROW_IO_ERROR( wxString::Format( _( "File '%s' is empty or is not a legacy library" ), - m_lib_name.GetData() ) ); + m_lib_path.GetData() ) ); } @@ -4082,7 +4082,7 @@ void FPL_CACHE::Save() if( !m_writable ) { THROW_IO_ERROR( wxString::Format( - _( "Legacy library file '%s' is read only" ), m_lib_name.GetData() ) ); + _( "Legacy library file '%s' is read only" ), m_lib_path.GetData() ) ); } wxString tempFileName; @@ -4090,20 +4090,20 @@ void FPL_CACHE::Save() // a block {} scope to fire wxFFile wxf()'s destructor { // CreateTempFileName works better with an absolute path - wxFileName abs_lib_name( m_lib_name ); + wxFileName abs_lib_name( m_lib_path ); abs_lib_name.MakeAbsolute(); tempFileName = wxFileName::CreateTempFileName( abs_lib_name.GetFullPath() ); - wxLogDebug( wxT( "tempFileName:'%s' m_lib_name:'%s'\n" ), - TO_UTF8( tempFileName ), TO_UTF8( m_lib_name ) ); + wxLogDebug( wxT( "tempFileName:'%s' m_lib_path:'%s'\n" ), + TO_UTF8( tempFileName ), TO_UTF8( m_lib_path ) ); FILE* fp = wxFopen( tempFileName, wxT( "w" ) ); if( !fp ) { THROW_IO_ERROR( wxString::Format( _( "Unable to open or create legacy library file '%s'" ), - m_lib_name.GetData() ) ); + m_lib_path.GetData() ) ); } // wxf now owns fp, will close on exception or exit from @@ -4117,20 +4117,20 @@ void FPL_CACHE::Save() } // fp is now closed here, and that seems proper before trying to rename - // the temporary file to m_lib_name. + // the temporary file to m_lib_path. - wxRemove( m_lib_name ); // it is not an error if this does not exist + wxRemove( m_lib_path ); // it is not an error if this does not exist // Even on linux you can see an _intermittent_ error when calling wxRename(), // and it is fully inexplicable. See if this dodges the error. wxMilliSleep( 250L ); - if( wxRename( tempFileName, m_lib_name ) ) + if( wxRename( tempFileName, m_lib_path ) ) { THROW_IO_ERROR( wxString::Format( _( "Unable to rename tempfile '%s' to library file '%s'" ), tempFileName.GetData(), - m_lib_name.GetData() ) ); + m_lib_path.GetData() ) ); } } @@ -4173,7 +4173,7 @@ void FPL_CACHE::SaveModules( FILE* aFile ) void LEGACY_PLUGIN::cacheLib( const wxString& aLibraryPath ) { - if( !m_cache || m_cache->m_lib_name != aLibraryPath || + if( !m_cache || m_cache->m_lib_path != aLibraryPath || // somebody else on a network touched the library: m_cache->m_mod_time != m_cache->GetLibModificationTime() ) { @@ -4342,7 +4342,7 @@ bool LEGACY_PLUGIN::FootprintLibDelete( const wxString& aLibraryPath, PROPERTIES aLibraryPath.GetData() ) ); } - if( m_cache && m_cache->m_lib_name == aLibraryPath ) + if( m_cache && m_cache->m_lib_path == aLibraryPath ) { delete m_cache; m_cache = 0;