diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a6dae6c63..5bc075996f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,11 +21,14 @@ set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules) # option(USE_PCBNEW_SEXPR_FILE_FORMAT - "Use s-expression Pcbnew file format support (default OFF)." ) + "Use Pcbnew s-expression file format support (default OFF)." ) option(USE_PCBNEW_NANOMETRES "Use nanometers for Pcbnew internal units instead of deci-mils (default OFF).") +option(USE_PCBNEW_SEXPR_FOOTPRINT_LIBS + "Use Pcbnew s-expression footprint library format (default OFF).") + # Russian GOST patch option(wxUSE_UNICODE "enable/disable building unicode (default OFF)") option(KICAD_GOST "enable/disable building using GOST notation for multiple gates per package (default OFF)") @@ -100,15 +103,20 @@ endif( USE_PCBNEW_SEXPR_FILE_FORMAT AND NOT USE_PCBNEW_NANOMETRES ) #================================================ if(CMAKE_COMPILER_IS_GNUCXX) + # Added -Wno-narrowing on 10/7/12 to prevent a huge number of warnings when + # compiling with GCC 4.7. This appears to be caused by and int to unsigned + # conversion in the Boost polygon library. At some point in the future when + # Boost is updated to the next version, -Wno-narrowing should be removed to + # see if the problem has been resolved. Wayne. if(WIN32) # under Windows/mingw, -fPIC option is enabled by default # Set default flags for Release build. - set(CMAKE_C_FLAGS_RELEASE "-Wall -O2 -DNDEBUG") - set(CMAKE_CXX_FLAGS_RELEASE "-Wall -O2 -DNDEBUG") + set(CMAKE_C_FLAGS_RELEASE "-Wall -Wno-narrowing -O2 -DNDEBUG") + set(CMAKE_CXX_FLAGS_RELEASE "-Wall -Wno-narrowing -O2 -DNDEBUG") set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-s") # Set default flags for Debug build. - set(CMAKE_C_FLAGS_DEBUG "-Wall -g3 -ggdb3 -DDEBUG") - set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g3 -ggdb3 -DDEBUG") + set(CMAKE_C_FLAGS_DEBUG "-Wall -Wno-narrowing -g3 -ggdb3 -DDEBUG") + set(CMAKE_CXX_FLAGS_DEBUG "-Wall -Wno-narrowing -g3 -ggdb3 -DDEBUG") else(WIN32) # Set default flags for Release build. set(CMAKE_C_FLAGS_RELEASE "-Wall -O2 -DNDEBUG -fPIC") @@ -116,8 +124,8 @@ if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-s") # Set default flags for Debug build. - set(CMAKE_C_FLAGS_DEBUG "-Wall -g3 -ggdb3 -DDEBUG -fPIC") - set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g3 -ggdb3 -DDEBUG -fPIC") + set(CMAKE_C_FLAGS_DEBUG "-Wall -Wno-narrowing -g3 -ggdb3 -DDEBUG -fPIC") + set(CMAKE_CXX_FLAGS_DEBUG "-Wall -Wno-narrowing -g3 -ggdb3 -DDEBUG -fPIC") endif(WIN32) endif(CMAKE_COMPILER_IS_GNUCXX) diff --git a/CMakeModules/config.h.cmake b/CMakeModules/config.h.cmake index ff8454b84d..f9cad7c542 100644 --- a/CMakeModules/config.h.cmake +++ b/CMakeModules/config.h.cmake @@ -55,8 +55,10 @@ #cmakedefine USE_IMAGES_IN_MENUS 1 +/// Definitions to enable the s-expression file formats and nanometer units. #cmakedefine USE_PCBNEW_NANOMETRES #cmakedefine USE_PCBNEW_SEXPR_FILE_FORMAT +#cmakedefine USE_PCBNEW_SEXPR_FOOTPRINT_LIBS /// The legacy file format revision of the *.brd file created by this build #if defined(USE_PCBNEW_NANOMETRES) diff --git a/common/basicframe.cpp b/common/basicframe.cpp index 6b4d9ec111..0babb48879 100644 --- a/common/basicframe.cpp +++ b/common/basicframe.cpp @@ -499,6 +499,27 @@ void EDA_BASE_FRAME::CopyVersionInfoToClipboard( wxCommandEvent& event ) tmp << wxT( "Options: " ); + tmp << wxT( " USE_PCBNEW_SEXPR_FILE_FORMAT=" ); +#ifdef USE_PCBNEW_SEXPR_FILE_FORMAT + tmp << wxT( "ON\n" ); +#else + tmp << wxT( "OFF\n" ); +#endif + + tmp << wxT( " USE_PCBNEW_NANOMETRES=" ); +#ifdef USE_PCBNEW_NANOMETRES + tmp << wxT( "ON\n" ); +#else + tmp << wxT( "OFF\n" ); +#endif + + tmp << wxT( " USE_PCBNEW_SEXPR_FOOTPRINT_LIBS=" ); +#ifdef USE_PCBNEW_SEXPR_FOOTPRINT_LIBS + tmp << wxT( "ON\n" ); +#else + tmp << wxT( "OFF\n" ); +#endif + tmp << wxT( " KICAD_GOST=" ); #ifdef KICAD_GOST tmp << wxT( "ON\n" ); diff --git a/common/richio.cpp b/common/richio.cpp index 301970fd29..5c1b0c29d0 100644 --- a/common/richio.cpp +++ b/common/richio.cpp @@ -185,6 +185,44 @@ unsigned STRING_LINE_READER::ReadLine() throw( IO_ERROR ) } +INPUTSTREAM_LINE_READER::INPUTSTREAM_LINE_READER( wxInputStream* aStream ) : + LINE_READER( LINE_READER_LINE_DEFAULT_MAX ), + m_stream( aStream ) +{ +} + + +unsigned INPUTSTREAM_LINE_READER::ReadLine() throw( IO_ERROR ) +{ + length = 0; + line[0] = 0; + + while( !m_stream->Eof() ) + { + if( length >= maxLineLength ) + THROW_IO_ERROR( _( "Maximum line length exceeded" ) ); + + if( length + 1 > capacity ) + expandCapacity( capacity * 2 ); + + line[ length ] = m_stream->GetC(); + length++; + + if( line[ length - 1 ] == '\n' ) + break; + } + + line[ length ] = 0; + length -= 1; + + // lineNum is incremented even if there was no line read, because this + // leads to better error reporting when we hit an end of file. + ++lineNum; + + return length; +} + + //--------------------------------------------------------- // factor out a common GetQuoteChar diff --git a/common/wildcards_and_files_ext.cpp b/common/wildcards_and_files_ext.cpp index c3a74a8ec3..6a770046b1 100644 --- a/common/wildcards_and_files_ext.cpp +++ b/common/wildcards_and_files_ext.cpp @@ -51,6 +51,7 @@ const wxString DrillFileExtension( wxT( "drl" ) ); const wxString SVGFileExtension( wxT( "svg" ) ); const wxString ReportFileExtension( wxT( "rpt" ) ); const wxString FootprintPlaceFileExtension( wxT( "pos" ) ); +const wxString FootprintFileExtension( wxT( "kicad_mod" ) ); // These strings are wildcards for file selection dialogs. // Because thes are static, one should explicitely call wxGetTranslation @@ -64,7 +65,8 @@ const wxString GerberFileWildcard( _( "Gerber files (*.pho)|*.pho" ) ); const wxString LegacyPcbFileWildcard( _( "KiCad printed circuit board files (*.brd)|*.brd" ) ); const wxString EaglePcbFileWildcard( _( "Eagle ver. 6.x XML PCB files (*.brd)|*.brd" ) ); const wxString PcbFileWildcard( _( "KiCad s-expr printed circuit board files (*.kicad_pcb)|*.kicad_pcb" ) ); -const wxString FootprintLibFileWildcard( _( "KiCad footprint library file (*.mod)|*.mod" ) ); +const wxString FootprintLibFileWildcard( _( "KiCad footprint s-expre library file (*.kicad_mod)|*.kicad_mod" ) ); +const wxString LegacyFootprintLibFileWildcard( _( "KiCad footprint library file (*.mod)|*.mod" ) ); const wxString PdfFileWildcard( _( "Portable document format files (*.pdf)|*.pdf" ) ); const wxString MacrosFileWildcard( _( "KiCad recorded macros (*.mcr)|*.mcr" ) ); const wxString AllFilesWildcard( _( "All files (*)|*" ) ); diff --git a/include/richio.h b/include/richio.h index 206433ad00..8b6f63f5ea 100644 --- a/include/richio.h +++ b/include/richio.h @@ -384,6 +384,28 @@ public: }; +/** + * Class INPUTSTREAM_LINE_READER + * is a LINE_READER that reads from a wxInputStream object. + */ +class INPUTSTREAM_LINE_READER : public LINE_READER +{ +protected: + wxInputStream* m_stream; //< The input stream to read. No ownership of this pointer. + +public: + + /** + * Constructor WXINPUTSTREAM_LINE_READER + * + * @param aStream A pointer to a wxInputStream object to read. + */ + INPUTSTREAM_LINE_READER( wxInputStream* aStream ); + + unsigned ReadLine() throw( IO_ERROR ); // see LINE_READER::ReadLine() description +}; + + /** * Class OUTPUTFORMATTER * is an important interface (abstract) class used to output UTF8 text in diff --git a/include/wildcards_and_files_ext.h b/include/wildcards_and_files_ext.h index 448de6555c..512ae02503 100644 --- a/include/wildcards_and_files_ext.h +++ b/include/wildcards_and_files_ext.h @@ -60,6 +60,7 @@ extern const wxString DrillFileExtension; extern const wxString SVGFileExtension; extern const wxString ReportFileExtension; extern const wxString FootprintPlaceFileExtension; +extern const wxString FootprintFileExtension; /// Proper wxFileDialog wild card definitions. extern const wxString SchematicSymbolFileWildcard; @@ -82,6 +83,7 @@ extern const wxString ReportFileWildcard; extern const wxString FootprintPlaceFileWildcard; extern const wxString VrmlFileWildcard; extern const wxString DocModulesFileName; +extern const wxString LegacyFootprintLibFileWildcard; extern const wxString FootprintLibFileWildcard; #endif // INCLUDE_WILDCARDS_AND_FILES_EXT_H_ diff --git a/include/wxBasePcbFrame.h b/include/wxBasePcbFrame.h index 270956a419..8903f6f904 100644 --- a/include/wxBasePcbFrame.h +++ b/include/wxBasePcbFrame.h @@ -365,28 +365,29 @@ public: * (usually: true in Schematic editor, false in Module editor) */ void GlobalChange_PadSettings( D_PAD* aPad, - bool aSameFootprints, - bool aPadShapeFilter, - bool aPadOrientFilter, - bool aPadLayerFilter, - bool aRedraw, bool aSaveForUndo ); + bool aSameFootprints, + bool aPadShapeFilter, + bool aPadOrientFilter, + bool aPadLayerFilter, + bool aRedraw, + bool aSaveForUndo ); // loading footprints /** * Function loadFootprintFromLibrary * loads @a aFootprintName from @a aLibraryPath. - * If found add the module is also added to the BOARD, just for good measure. * - * @param aLibraryPath - the full filename or the short name of the library to read. + * @param aLibraryPath - the full filename or the short name of the library to read. * if it is a short name, the file is searched in all library valid paths - * @param aFootprintName is the footprint to load - * @param aDisplayError = true to display an error message if any. + * @param aFootprintName is the footprint to load + * @param aDisplayError = true to display an error message if any. + * @param aAddToBoard Set to true to add the footprint to the board if found. * - * @return MODULE* - new module, or NULL + * @return MODULE* - new module, or NULL */ - MODULE* loadFootprintFromLibrary( const wxString& aLibraryPath, - const wxString& aFootprintName, bool aDisplayError ); + MODULE* loadFootprintFromLibrary( const wxString& aLibraryPath, const wxString& aFootprintName, + bool aDisplayError, bool aAddToBoard = true ); /** * Function loadFootprintFromLibraries diff --git a/pcbnew/class_board_item.cpp b/pcbnew/class_board_item.cpp index be9891554f..5a73cf1abd 100644 --- a/pcbnew/class_board_item.cpp +++ b/pcbnew/class_board_item.cpp @@ -82,10 +82,8 @@ wxString BOARD_ITEM::GetLayerName() const if( board != NULL ) return board->GetLayerName( m_Layer ).Trim(); - wxFAIL_MSG( wxT( "No board found for board item type " ) + GetClass() ); - layerName = _( "** undefined layer **" ); - - return layerName; + // If no parent, return the default layer for the object. + return BOARD::GetDefaultLayerName( m_Layer, true ); } diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index 1d1c3dab6e..55ea65407f 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -29,6 +29,7 @@ #include // LEGACY_BOARD_FILE_VERSION #include #include <3d_struct.h> +#include #include #include @@ -44,11 +45,258 @@ #include #include +#include +#include #include +#include +#include + +using namespace std; + #define FMTIU BOARD_ITEM::FormatInternalUnits +/** + * Class FP_CACHE_ITEM + * is helper class for creating a footprint library cache. + * + * The new footprint library design is a file path of individual module files + * that contain a single module per file. This class is a helper only for the + * footprint portion of the PLUGIN API, and only for the #PCB_IO plugin. It is + * private to this implementation file so it is not placed into a header. + */ +class FP_CACHE_ITEM +{ + wxFileName m_file_name; /// The the full file name and path of the footprint to cache. + bool m_writable; /// Writability status of the footprint file. + wxDateTime m_mod_time; /// The last file modified time stamp. + auto_ptr< MODULE > m_module; + +public: + FP_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName ); + + wxString GetName() const { return m_file_name.GetDirs().Last(); } + wxFileName GetFileName() const { return m_file_name; } + bool IsModified() const; + MODULE* GetModule() const { return m_module.get(); } + void UpdateModificationTime() { m_mod_time = m_file_name.GetModificationTime(); } +}; + + +FP_CACHE_ITEM::FP_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName ) +{ + m_file_name = aFileName; + auto_ptr< MODULE > tmp( aModule ); + m_module = tmp; + m_mod_time.Now(); +} + + +bool FP_CACHE_ITEM::IsModified() const +{ + return m_file_name.GetModificationTime() != m_mod_time; +} + + +typedef boost::ptr_map< std::string, FP_CACHE_ITEM > MODULE_MAP; +typedef MODULE_MAP::iterator MODULE_ITER; +typedef MODULE_MAP::const_iterator MODULE_CITER; + + +class FP_CACHE +{ + PCB_IO* m_owner; /// Plugin object that owns the cache. + wxFileName m_lib_path; /// The path of the library. + wxDateTime m_mod_time; /// Footprint library path modified time stamp. + MODULE_MAP m_modules; /// Map of footprint file name per MODULE*. + +public: + FP_CACHE( PCB_IO* aOwner, const wxString& aLibraryPath ); + + wxString GetPath() const { return m_lib_path.GetPath(); } + wxDateTime GetLastModificationTime() const { return m_mod_time; } + bool IsWritable() const { return m_lib_path.IsOk() && m_lib_path.IsDirWritable(); } + MODULE_MAP& GetModules() { return m_modules; } + + // Most all functions in this class throw IO_ERROR exceptions. There are no + // 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; + void Save(); + + void Load(); + + void Remove( const wxString& aFootprintName ); + + wxDateTime GetLibModificationTime(); + + bool IsModified(); +}; + + +FP_CACHE::FP_CACHE( PCB_IO* aOwner, const wxString& aLibraryPath ) +{ + m_owner = aOwner; + m_lib_path.SetPath( aLibraryPath ); +} + + +wxDateTime FP_CACHE::GetLibModificationTime() +{ + return m_lib_path.GetModificationTime(); +} + + +void FP_CACHE::Save() +{ + if( !m_lib_path.DirExists() && !m_lib_path.Mkdir() ) + { + THROW_IO_ERROR( wxString::Format( _( "Cannot create footprint library path '%s'." ), + m_lib_path.GetPath().GetData() ) ); + } + + if( !m_lib_path.IsDirWritable() ) + { + THROW_IO_ERROR( wxString::Format( _( "Footprint library path '%s' is read only" ), + GetChars( m_lib_path.GetPath() ) ) ); + } + + for( MODULE_ITER it = m_modules.begin(); it != m_modules.end(); ++it ) + { + wxFileName fn = it->second->GetFileName(); + + if( fn.FileExists() && !it->second->IsModified() ) + continue; + + wxString tempFileName = fn.CreateTempFileName( fn.GetPath() ); + + // Allow file output stream to go out of scope to close the file stream before + // renaming the file. + { + wxLogDebug( wxT( "Creating temporary library file %s" ), GetChars( tempFileName ) ); + + wxFFileOutputStream os( tempFileName ); + + if( !os.IsOk() ) + { + THROW_IO_ERROR( wxString::Format( _( "cannot save footprint library file '%s'" ), + tempFileName.GetData() ) ); + } + + STREAM_OUTPUTFORMATTER formatter( os ); + + m_owner->SetOutputFormatter( &formatter ); + m_owner->Format( (BOARD_ITEM*) it->second->GetModule() ); + } + + wxRemove( fn.GetFullPath() ); // it is not an error if this does not exist + + if( wxRename( tempFileName, fn.GetFullPath() ) ) + { + THROW_IO_ERROR( wxString::Format( _( "cannot rename temporary file '%s' to footprint library file '%s'" ), + tempFileName.GetData(), + fn.GetFullPath().GetData() ) ); + } + + it->second->UpdateModificationTime(); + m_mod_time = GetLibModificationTime(); + } +} + + +void FP_CACHE::Load() +{ + wxDir dir( m_lib_path.GetPath() ); + + if( !dir.IsOpened() ) + { + THROW_IO_ERROR( wxString::Format( _( "footprint library path '%s' does not exist" ), + m_lib_path.GetPath().GetData() ) ); + } + + wxString fpFileName; + wxString wildcard = wxT( "*." ) + FootprintFileExtension; + + if( !dir.GetFirst( &fpFileName, wildcard, wxDIR_FILES ) ) + return; + + do + { + FILE* fp = wxFopen( fpFileName, wxT( "r" ) ); + + if( !fp ) + { + THROW_IO_ERROR( wxString::Format( _( "cannot open footprint library file '%s'" ), + fpFileName.GetData() ) ); + } + + // reader now owns fp, will close on exception or return + PCB_PARSER parser( new FILE_LINE_READER( fp, fpFileName ) ); + + std::string name = TO_UTF8( fpFileName ); + + m_modules.insert( name, new FP_CACHE_ITEM( (MODULE*) parser.Parse(), fpFileName ) ); + + } while( dir.GetNext( &fpFileName ) ); + + // 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_mod_time = GetLibModificationTime(); +} + + +void FP_CACHE::Remove( const wxString& aFootprintName ) +{ + + std::string footprintName = TO_UTF8( aFootprintName ); + + MODULE_CITER it = m_modules.find( footprintName ); + + if( it == m_modules.end() ) + { + THROW_IO_ERROR( wxString::Format( _( "library '%s' has no footprint '%s' to delete" ), + m_lib_path.GetPath().GetData(), + aFootprintName.GetData() ) ); + } + + // Remove the module from the cache and delete the module file from the library. + wxString fullPath = it->second->GetFileName().GetFullPath(); + m_modules.erase( footprintName ); + wxRemoveFile( fullPath ); +} + + +bool FP_CACHE::IsModified() +{ + if( !m_lib_path.DirExists() ) + return true; + + for( MODULE_ITER it = m_modules.begin(); it != m_modules.end(); ++it ) + { + wxFileName fn = it->second->GetFileName(); + + if( !fn.FileExists() ) + { + wxLogDebug( wxT( "Footprint cache file '%s' does not exist." ), + fn.GetFullPath().GetData() ); + return true; + } + + if( it->second->IsModified() ) + { + wxLogDebug( wxT( "Footprint cache file '%s' has been modified." ), + fn.GetFullPath().GetData() ); + return true; + } + } + + return false; +} + + void PCB_IO::Save( const wxString& aFileName, BOARD* aBoard, PROPERTIES* aProperties ) { LOCALE_IO toggle; // toggles on, then off, the C locale. @@ -779,13 +1027,23 @@ void PCB_IO::format( D_PAD* aPad, int aNestLevel ) const m_out->Print( aNestLevel+1, "(layers" ); - unsigned layerMask = aPad->GetLayerMask() & m_board->GetEnabledLayers(); + unsigned layerMask = aPad->GetLayerMask(); + + if( m_board ) + layerMask &= m_board->GetEnabledLayers(); for( int layer = 0; layerMask; ++layer, layerMask >>= 1 ) { if( layerMask & 1 ) { - m_out->Print( 0, " %s", m_out->Quotew( m_board->GetLayerName( layer ) ).c_str() ); + wxString layerName; + + if( m_board ) + layerName = m_board->GetLayerName( layer ); + else + layerName = BOARD::GetDefaultLayerName( layer, true ); + + m_out->Print( 0, " %s", m_out->Quotew(layerName ).c_str() ); } } @@ -1185,12 +1443,19 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const } -PCB_IO::PCB_IO() +PCB_IO::PCB_IO() : + m_cache( 0 ) { m_out = &m_sf; } +PCB_IO::~PCB_IO() +{ + delete m_cache; +} + + BOARD* PCB_IO::Load( const wxString& aFileName, BOARD* aAppendToMe, PROPERTIES* aProperties ) { wxFFile file( aFileName, wxT("r") ); @@ -1210,5 +1475,252 @@ BOARD* PCB_IO::Load( const wxString& aFileName, BOARD* aAppendToMe, PROPERTIES* // Give the filename to the board if it's new if( !aAppendToMe ) board->SetFileName( aFileName ); + return board; } + + +void PCB_IO::init( PROPERTIES* aProperties ) +{ + m_board = NULL; + m_props = aProperties; +} + + +void PCB_IO::cacheLib( const wxString& aLibraryPath ) +{ + if( !m_cache || m_cache->GetPath() != aLibraryPath || m_cache->IsModified() ) + { + // a spectacular episode in memory management: + delete m_cache; + m_cache = new FP_CACHE( this, aLibraryPath ); + m_cache->Load(); + } +} + + +wxArrayString PCB_IO::FootprintEnumerate( const wxString& aLibraryPath, PROPERTIES* aProperties ) +{ + LOCALE_IO toggle; // toggles on, then off, the C locale. + + init( aProperties ); + + cacheLib( aLibraryPath ); + + const MODULE_MAP& mods = m_cache->GetModules(); + + wxArrayString ret; + + for( MODULE_CITER it = mods.begin(); it != mods.end(); ++it ) + { + ret.Add( FROM_UTF8( it->first.c_str() ) ); + } + + return ret; +} + + +MODULE* PCB_IO::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, + PROPERTIES* aProperties ) +{ + LOCALE_IO toggle; // toggles on, then off, the C locale. + + init( aProperties ); + + cacheLib( aLibraryPath ); + + const MODULE_MAP& mods = m_cache->GetModules(); + + MODULE_CITER it = mods.find( TO_UTF8( aFootprintName ) ); + + if( it == mods.end() ) + { + return NULL; + } + + // copy constructor to clone the already loaded MODULE + return new MODULE( *it->second->GetModule() ); +} + + +void PCB_IO::FootprintSave( const wxString& aLibraryPath, const MODULE* aFootprint, + PROPERTIES* aProperties ) +{ + LOCALE_IO toggle; // toggles on, then off, the C locale. + + init( aProperties ); + + cacheLib( aLibraryPath ); + + if( !m_cache->IsWritable() ) + { + THROW_IO_ERROR( wxString::Format( _( "Library '%s' is read only" ), + aLibraryPath.GetData() ) ); + } + + std::string footprintName = TO_UTF8( aFootprint->GetLibRef() ); + + MODULE_MAP& mods = m_cache->GetModules(); + + // Quietly overwrite module and delete module file from path for any by same name. + wxFileName fn( aLibraryPath, aFootprint->GetLibRef(), FootprintFileExtension ); + + if( !fn.IsOk() ) + { + THROW_IO_ERROR( wxString::Format( _( "Footprint file name '%s' is not valid." ), + GetChars( fn.GetFullPath() ) ) ); + } + + if( fn.FileExists() && !fn.IsFileWritable() ) + { + THROW_IO_ERROR( wxString::Format( _( "user does not have write permission to delete file '%s' " ), + GetChars( fn.GetFullPath() ) ) ); + } + + MODULE_CITER it = mods.find( footprintName ); + + if( it != mods.end() ) + { + wxLogDebug( wxT( "Removing footprint library file '%s'." ), fn.GetFullPath().GetData() ); + mods.erase( footprintName ); + wxRemoveFile( fn.GetFullPath() ); + } + + // I need my own copy for the cache + MODULE* module = new MODULE( *aFootprint ); + + // and it's time stamp must be 0, it should have no parent, orientation should + // be zero, and it should be on the front layer. + module->SetTimeStamp( 0 ); + module->SetParent( 0 ); + module->SetOrientation( 0 ); + + if( module->GetLayer() != LAYER_N_FRONT ) + module->Flip( module->GetPosition() ); + + wxLogDebug( wxT( "Creating s-expression footprint file: %s." ), fn.GetFullPath().GetData() ); + mods.insert( footprintName, new FP_CACHE_ITEM( module, fn ) ); + m_cache->Save(); +} + + +void PCB_IO::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName ) +{ + LOCALE_IO toggle; // toggles on, then off, the C locale. + + init( NULL ); + + cacheLib( aLibraryPath ); + + if( !m_cache->IsWritable() ) + { + THROW_IO_ERROR( wxString::Format( _( "Library '%s' is read only" ), + aLibraryPath.GetData() ) ); + } + + m_cache->Remove( aFootprintName ); +} + + +void PCB_IO::FootprintLibCreate( const wxString& aLibraryPath, PROPERTIES* aProperties ) +{ + if( wxDir::Exists( aLibraryPath ) ) + { + THROW_IO_ERROR( wxString::Format( _( "cannot overwrite library path '%s'" ), + aLibraryPath.GetData() ) ); + } + + LOCALE_IO toggle; + + init( aProperties ); + + delete m_cache; + m_cache = new FP_CACHE( this, aLibraryPath ); + m_cache->Save(); +} + + +void PCB_IO::FootprintLibDelete( const wxString& aLibraryPath, PROPERTIES* aProperties ) +{ + wxFileName fn; + fn.SetPath( aLibraryPath ); + + // Return if there is no library path to delete. + if( !fn.DirExists() ) + return; + + if( !fn.IsDirWritable() ) + { + THROW_IO_ERROR( wxString::Format( _( "user does not have permission to delete directory '%s'" ), + aLibraryPath.GetData() ) ); + } + + wxDir dir( aLibraryPath ); + + if( dir.HasSubDirs() ) + { + THROW_IO_ERROR( wxString::Format( _( "library directory '%s' has unexpected sub-directories" ), + aLibraryPath.GetData() ) ); + } + + // All the footprint files must be deleted before the directory can be deleted. + if( dir.HasFiles() ) + { + unsigned i; + wxFileName tmp; + wxArrayString files; + + wxDir::GetAllFiles( aLibraryPath, &files ); + + for( i = 0; i < files.GetCount(); i++ ) + { + tmp = files[i]; + + if( tmp.GetExt() != FootprintFileExtension ) + { + THROW_IO_ERROR( wxString::Format( _( "unexpected file '%s' has found in library path '%'" ), + files[i].GetData(), aLibraryPath.GetData() ) ); + } + } + + for( i = 0; i < files.GetCount(); i++ ) + { + wxRemoveFile( files[i] ); + } + } + + wxLogDebug( wxT( "Removing footprint library '%s'" ), aLibraryPath.GetData() ); + + // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog + // we don't want that. we want bare metal portability with no UI here. + if( !wxRmdir( aLibraryPath ) ) + { + THROW_IO_ERROR( wxString::Format( _( "footprint library '%s' cannot be deleted" ), + aLibraryPath.GetData() ) ); + } + + // For some reason removing a directory in Windows is not immediately updated. This delay + // prevents an error when attempting to immediately recreate the same directory when over + // writing an existing library. +#ifdef __WINDOWS__ + wxMilliSleep( 250L ); +#endif + + if( m_cache && m_cache->GetPath() == aLibraryPath ) + { + delete m_cache; + m_cache = NULL; + } +} + + +bool PCB_IO::IsFootprintLibWritable( const wxString& aLibraryPath ) +{ + LOCALE_IO toggle; + + init( NULL ); + + cacheLib( aLibraryPath ); + + return m_cache->IsWritable(); +} diff --git a/pcbnew/kicad_plugin.h b/pcbnew/kicad_plugin.h index 98fb908a2e..23f92c7d92 100644 --- a/pcbnew/kicad_plugin.h +++ b/pcbnew/kicad_plugin.h @@ -29,6 +29,7 @@ class BOARD; class BOARD_ITEM; +class FP_CACHE; /** Current s-expression file format version. 2 was the last legacy format version. */ @@ -68,10 +69,28 @@ public: BOARD* Load( const wxString& aFileName, BOARD* aAppendToMe, PROPERTIES* aProperties = NULL ); + wxArrayString FootprintEnumerate( const wxString& aLibraryPath, PROPERTIES* aProperties = NULL); + + MODULE* FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, + PROPERTIES* aProperties = NULL ); + + void FootprintSave( const wxString& aLibraryPath, const MODULE* aFootprint, + PROPERTIES* aProperties = NULL ); + + void FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName ); + + void FootprintLibCreate( const wxString& aLibraryPath, PROPERTIES* aProperties = NULL); + + void FootprintLibDelete( const wxString& aLibraryPath, PROPERTIES* aProperties = NULL ); + + bool IsFootprintLibWritable( const wxString& aLibraryPath ); + //------------------------------------------------------------- PCB_IO(); + ~PCB_IO(); + /** * Function Format * outputs \a aItem to \a aFormatter in s-expression format. @@ -92,17 +111,19 @@ public: return ret; } + void SetOutputFormatter( OUTPUTFORMATTER* aFormatter ) { m_out = aFormatter; } protected: wxString m_error; ///< for throwing exceptions BOARD* m_board; ///< which BOARD, no ownership here PROPERTIES* m_props; ///< passed via Save() or Load(), no ownership, may be NULL. + FP_CACHE* m_cache; ///< Footprint library cache. LINE_READER* m_reader; ///< no ownership here. wxString m_filename; ///< for saves only, name is in m_reader for loads - int m_loading_format_version; ///< which BOARD_FORMAT_VERSION am I Load()ing? + int m_loading_format_version; ///< which #BOARD_FORMAT_VERSION should be Load()ed? STRING_FORMATTER m_sf; OUTPUTFORMATTER* m_out; ///< output any Format()s to this, no ownership @@ -144,6 +165,11 @@ private: throw( IO_ERROR ); void formatLayer( const BOARD_ITEM* aItem ) const; + + /// we only cache one footprint library for now, this determines which one. + void cacheLib( const wxString& aLibraryPath ); + + void init( PROPERTIES* aProperties ); }; #endif // KICAD_PLUGIN_H_ diff --git a/pcbnew/legacy_plugin.cpp b/pcbnew/legacy_plugin.cpp index c78da51898..3b833df926 100644 --- a/pcbnew/legacy_plugin.cpp +++ b/pcbnew/legacy_plugin.cpp @@ -4387,4 +4387,3 @@ LEGACY_PLUGIN::~LEGACY_PLUGIN() { delete m_cache; } - diff --git a/pcbnew/librairi.cpp b/pcbnew/librairi.cpp index 8631d4708a..f6ee71dc5d 100644 --- a/pcbnew/librairi.cpp +++ b/pcbnew/librairi.cpp @@ -163,7 +163,7 @@ void FOOTPRINT_EDIT_FRAME::Export_Module( MODULE* aModule, bool aCreateSysLib ) config->Read( EXPORT_IMPORT_LASTPATH_KEY, &path ); title = aCreateSysLib ? _( "Create New Library" ) : _( "Export Module" ); - wildcard = aCreateSysLib ? FootprintLibFileWildcard : ModExportFileWildcard; + wildcard = aCreateSysLib ? LegacyFootprintLibFileWildcard : ModExportFileWildcard; fn.SetPath( path ); @@ -272,7 +272,7 @@ void PCB_EDIT_FRAME::ArchiveModulesOnBoard( const wxString& aLibName, bool aNewM { wxFileDialog dlg( this, _( "Library" ), path, wxEmptyString, - wxGetTranslation( FootprintLibFileWildcard ), + wxGetTranslation( LegacyFootprintLibFileWildcard ), wxFD_SAVE ); if( dlg.ShowModal() == wxID_CANCEL ) @@ -562,4 +562,3 @@ int FOOTPRINT_EDIT_FRAME::CreateLibrary( const wxString& aLibName ) return 1; // remember how many times we succeeded } - diff --git a/pcbnew/loadcmp.cpp b/pcbnew/loadcmp.cpp index 124bbcc7ba..7e03dabea2 100644 --- a/pcbnew/loadcmp.cpp +++ b/pcbnew/loadcmp.cpp @@ -284,7 +284,9 @@ MODULE* PCB_BASE_FRAME::GetModuleLibrary( const wxString& aLibraryPath, * if it is a short name, the file is searched in all library valid paths */ MODULE* PCB_BASE_FRAME::loadFootprintFromLibrary( const wxString& aLibraryPath, - const wxString& aFootprintName, bool aDisplayError ) + const wxString& aFootprintName, + bool aDisplayError, + bool aAddToBoard ) { try { @@ -309,7 +311,9 @@ MODULE* PCB_BASE_FRAME::loadFootprintFromLibrary( const wxString& aLibraryPath, return NULL; } - GetBoard()->Add( footprint, ADD_APPEND ); + if( aAddToBoard ) + GetBoard()->Add( footprint, ADD_APPEND ); + SetStatusText( wxEmptyString ); return footprint; } @@ -509,3 +513,86 @@ MODULE* FOOTPRINT_EDIT_FRAME::Select_1_Module_From_BOARD( BOARD* aPcb ) return module; } + + + + +void FOOTPRINT_EDIT_FRAME::OnSaveLibraryAs( wxCommandEvent& aEvent ) +{ + wxFileName fn; + wxString msg, path, title; + FOOTPRINT_LIST fpInfoList; + + title = _( "Save Footprint Library As" ); + + fn = wxFileName( wxEmptyString, GetCurrentLib(), FootprintLibFileExtension ); + path = wxGetApp().FindLibraryPath( fn ); + fn.SetPath( path ); + + wxDirDialog dlg( this, msg, fn.GetPath() ); + + if( dlg.ShowModal() == wxID_CANCEL ) + return; + + fn.SetPath( dlg.GetPath() ); + fn.AppendDir( GetCurrentLib() ); + + path = fn.GetPath(); + + try + { + // @todo : hard code this as IO_MGR::KICAD plugin, what would be the reason to "export" + // any other single footprint type, with clipboard support coming? + // Use IO_MGR::LEGACY for now, until the IO_MGR::KICAD plugin is ready. + PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::KICAD ) ); + + try + { + // try to delete the library whether it exists or not, quietly. + pi->FootprintLibDelete( fn.GetPath() ); + } + catch( IO_ERROR ioe ) + { + // Ignore this, it will often happen and is not an error because + // the library may not exist. If library was in a read only directory, + // it will still exist as we get to the FootprintLibCreate() below. + } + + pi->FootprintLibCreate( fn.GetPath() ); + + wxArrayString libNameList; + + wxLogDebug( wxT( "Loading legacy footprint library '%s'." ), m_CurrentLib.GetData() ); + + wxFileName libFileName = m_CurrentLib; + + libFileName.SetExt( FootprintLibFileExtension ); + + wxString libPath = wxGetApp().FindLibraryPath( libFileName ); + + libNameList.Add( m_CurrentLib ); + fpInfoList.ReadFootprintFiles( libNameList ); + + for( unsigned i = 0; i < fpInfoList.GetCount(); i++ ) + { + MODULE* module = loadFootprintFromLibrary( libFileName.GetFullPath(), + fpInfoList.GetItem( i ).m_Module, + true, false ); + + wxLogDebug( wxT( "Saving footprint %s as s-expression to path %s" ), + module->GetLibRef().GetData(), path.GetData() ); + + pi->FootprintSave( path, module ); + } + } + catch( IO_ERROR ioe ) + { + DisplayError( this, ioe.errorText ); + return; + } + + msg.Printf( _( "Footprint library type '%s' saved to <%s> as s-expression" ), + GetCurrentLib().GetData(), path.GetData() ); + + DisplayInfoMessage( this, msg ); +} diff --git a/pcbnew/menubar_modedit.cpp b/pcbnew/menubar_modedit.cpp index 92fdb4a3d4..fc1ae61d87 100644 --- a/pcbnew/menubar_modedit.cpp +++ b/pcbnew/menubar_modedit.cpp @@ -76,19 +76,19 @@ void FOOTPRINT_EDIT_FRAME::ReCreateMenuBar() // from File AddMenuItem( openSubmenu, ID_MODEDIT_IMPORT_PART, - _( "Load from File (&Import)" ), + _( "Import Module from File (&Import)" ), _( "Import a footprint from an existing file" ), KiBitmap( import_module_xpm ) ); // from Library AddMenuItem( openSubmenu, ID_MODEDIT_LOAD_MODULE, - _( "Load from Li&brary" ), + _( "Load Module from Current Li&brary" ), _( "Open a footprint module from a Library" ), KiBitmap( module_xpm ) ); // from current Board AddMenuItem( openSubmenu, ID_MODEDIT_LOAD_MODULE_FROM_BOARD, - _( "Load from &Current Board" ), + _( "Load Module from &Current Board" ), _( "Load a footprint module from the current loaded board" ), KiBitmap( load_module_board_xpm ) ); @@ -99,6 +99,13 @@ void FOOTPRINT_EDIT_FRAME::ReCreateMenuBar() KiBitmap( open_document_xpm ) ); fileMenu->AppendSeparator(); +#ifdef USE_PCBNEW_SEXPR_FOOTPRINT_LIBS + // Save the currently loaded legacy library as an s-expression library. + AddMenuItem( fileMenu, ID_MODEDIT_SAVE_LIBRARY_AS, _( "Save Library In S-Expression Format" ), + _( "Save currently loaded legacy library as an s-expression library." ), + wxNullBitmap ); +#endif + // Save module text = AddHotkeyName( _( "&Save Module in Active Library" ), g_Module_Editor_Hokeys_Descr, HK_SAVE_MODULE ); @@ -108,7 +115,7 @@ void FOOTPRINT_EDIT_FRAME::ReCreateMenuBar() // Save module in new lib AddMenuItem( fileMenu, ID_MODEDIT_CREATE_NEW_LIB_AND_SAVE_CURRENT_PART, - _( "S&ave Module in a New Lib" ), + _( "S&ave Module in a New Library" ), _( "Create new library and save current module" ), KiBitmap( new_library_xpm ) ); diff --git a/pcbnew/module_editor_frame.h b/pcbnew/module_editor_frame.h index 5637671074..a764291941 100644 --- a/pcbnew/module_editor_frame.h +++ b/pcbnew/module_editor_frame.h @@ -85,6 +85,15 @@ public: void ToolOnRightClick( wxCommandEvent& event ); void OnSelectOptionToolbar( wxCommandEvent& event ); + /** + * Function OnSaveLibraryAs + * saves the current library to a new name and/or library type. + * + * @note Saving as a new library type requires the plug-in to support saving libraris. + * @see PLUGIN::FootprintSave and PLUGIN::FootprintLibCreate + */ + void OnSaveLibraryAs( wxCommandEvent& aEvent ); + /** * Function OnHotKey * handle hot key events. diff --git a/pcbnew/moduleframe.cpp b/pcbnew/moduleframe.cpp index c6b01d5969..e45848a5af 100644 --- a/pcbnew/moduleframe.cpp +++ b/pcbnew/moduleframe.cpp @@ -67,6 +67,11 @@ BEGIN_EVENT_TABLE( FOOTPRINT_EDIT_FRAME, PCB_BASE_FRAME ) EVT_COMBOBOX( ID_ON_GRID_SELECT, FOOTPRINT_EDIT_FRAME::OnSelectGrid ) EVT_TOOL( ID_MODEDIT_SELECT_CURRENT_LIB, FOOTPRINT_EDIT_FRAME::Process_Special_Functions ) + +#ifdef USE_PCBNEW_SEXPR_FOOTPRINT_LIBS + EVT_TOOL( ID_MODEDIT_SAVE_LIBRARY_AS, FOOTPRINT_EDIT_FRAME::OnSaveLibraryAs ) +#endif + EVT_TOOL( ID_MODEDIT_SAVE_LIBMODULE, FOOTPRINT_EDIT_FRAME::Process_Special_Functions ) EVT_TOOL( ID_OPEN_MODULE_VIEWER, FOOTPRINT_EDIT_FRAME::Process_Special_Functions ) @@ -109,9 +114,12 @@ BEGIN_EVENT_TABLE( FOOTPRINT_EDIT_FRAME, PCB_BASE_FRAME ) FOOTPRINT_EDIT_FRAME::Process_Special_Functions ) EVT_MENU( ID_POPUP_MODEDIT_EDIT_BODY_ITEM, FOOTPRINT_EDIT_FRAME::Process_Special_Functions ) - EVT_MENU( ID_POPUP_MODEDIT_EDIT_WIDTH_ALL_EDGE, FOOTPRINT_EDIT_FRAME::Process_Special_Functions ) - EVT_MENU( ID_POPUP_MODEDIT_EDIT_LAYER_CURRENT_EDGE, FOOTPRINT_EDIT_FRAME::Process_Special_Functions ) - EVT_MENU( ID_POPUP_MODEDIT_EDIT_LAYER_ALL_EDGE, FOOTPRINT_EDIT_FRAME::Process_Special_Functions ) + EVT_MENU( ID_POPUP_MODEDIT_EDIT_WIDTH_ALL_EDGE, + FOOTPRINT_EDIT_FRAME::Process_Special_Functions ) + EVT_MENU( ID_POPUP_MODEDIT_EDIT_LAYER_CURRENT_EDGE, + FOOTPRINT_EDIT_FRAME::Process_Special_Functions ) + EVT_MENU( ID_POPUP_MODEDIT_EDIT_LAYER_ALL_EDGE, + FOOTPRINT_EDIT_FRAME::Process_Special_Functions ) EVT_MENU( ID_POPUP_MODEDIT_ENTER_EDGE_WIDTH, FOOTPRINT_EDIT_FRAME::Process_Special_Functions ) // Module transformations @@ -126,6 +134,7 @@ BEGIN_EVENT_TABLE( FOOTPRINT_EDIT_FRAME, PCB_BASE_FRAME ) EVT_MENU( ID_MENU_PCB_SHOW_3D_FRAME, FOOTPRINT_EDIT_FRAME::Show3D_Frame ) EVT_UPDATE_UI( ID_MODEDIT_DELETE_PART, FOOTPRINT_EDIT_FRAME::OnUpdateLibSelected ) + EVT_UPDATE_UI( ID_MODEDIT_SAVE_LIBRARY_AS, FOOTPRINT_EDIT_FRAME::OnUpdateLibSelected ) EVT_UPDATE_UI( ID_MODEDIT_EXPORT_PART, FOOTPRINT_EDIT_FRAME::OnUpdateModuleSelected ) EVT_UPDATE_UI( ID_MODEDIT_CREATE_NEW_LIB_AND_SAVE_CURRENT_PART, FOOTPRINT_EDIT_FRAME::OnUpdateModuleSelected ) diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp index 68ca82169d..ccc1965d75 100644 --- a/pcbnew/pcb_parser.cpp +++ b/pcbnew/pcb_parser.cpp @@ -33,6 +33,7 @@ #include #include <3d_struct.h> #include + #include #include #include @@ -43,9 +44,9 @@ #include #include #include +#include #include #include - #include using namespace std; @@ -318,6 +319,10 @@ BOARD_ITEM* PCB_PARSER::Parse() throw( IO_ERROR, PARSE_ERROR ) item = (BOARD_ITEM*) parseBOARD(); break; + case T_module: + item = (BOARD_ITEM*) parseMODULE(); + break; + default: wxString err; err.Printf( _( "unknown token \"%s\"" ), GetChars( FromUTF8() ) ); diff --git a/pcbnew/pcb_parser.h b/pcbnew/pcb_parser.h index cbbbe7ecd9..e9ad61f4eb 100644 --- a/pcbnew/pcb_parser.h +++ b/pcbnew/pcb_parser.h @@ -47,8 +47,10 @@ class TEXTE_MODULE; class TEXTE_PCB; class MODULE; class PCB_TARGET; +class PROPERTIES; class S3D_MASTER; class ZONE_CONTAINER; +class FPL_CACHE; WX_DECLARE_STRING_HASH_MAP( int, LAYER_HASH_MAP ); @@ -61,8 +63,8 @@ WX_DECLARE_STRING_HASH_MAP( int, LAYER_HASH_MAP ); */ class PCB_PARSER : public PCB_LEXER { - BOARD* m_board; - LAYER_HASH_MAP m_layerMap; //< Map layer name to it's index saved in BOARD::m_Layer. + BOARD* m_board; + LAYER_HASH_MAP m_layerMap; //< Map layer name to it's index saved in BOARD::m_Layer. void parseHeader() throw( IO_ERROR, PARSE_ERROR ); void parseGeneralSection() throw( IO_ERROR, PARSE_ERROR ); diff --git a/pcbnew/pcbnew_id.h b/pcbnew/pcbnew_id.h index 0a0ecc9bde..c78a27462a 100644 --- a/pcbnew/pcbnew_id.h +++ b/pcbnew/pcbnew_id.h @@ -301,6 +301,7 @@ enum pcbnew_ids ID_MODEDIT_CHECK, ID_MODEDIT_SELECT_CURRENT_LIB, ID_MODEDIT_SAVE_LIBMODULE, + ID_MODEDIT_SAVE_LIBRARY_AS, ID_MODEDIT_DELETE_PART, ID_MODEDIT_NEW_MODULE, ID_MODEDIT_NEW_MODULE_FROM_WIZARD, diff --git a/pcbnew/tool_modedit.cpp b/pcbnew/tool_modedit.cpp index afd11ddb98..490c6fca29 100644 --- a/pcbnew/tool_modedit.cpp +++ b/pcbnew/tool_modedit.cpp @@ -77,12 +77,12 @@ void FOOTPRINT_EDIT_FRAME::ReCreateHToolbar() m_mainToolBar->AddSeparator(); m_mainToolBar->AddTool( ID_MODEDIT_NEW_MODULE, wxEmptyString, KiBitmap( new_footprint_xpm ), _( "New module" ) ); - + m_mainToolBar->AddTool( ID_MODEDIT_NEW_MODULE_FROM_WIZARD, wxEmptyString, KiBitmap( module_wizard_xpm ), _( "New module from footprint wizard" ) ); - - + + m_mainToolBar->AddTool( ID_MODEDIT_LOAD_MODULE, wxEmptyString, KiBitmap( load_module_lib_xpm ),