diff --git a/TODO.txt b/TODO.txt index 8e8aa2df07..7a534122bb 100644 --- a/TODO.txt +++ b/TODO.txt @@ -62,10 +62,6 @@ PCBNew Dick's Final TODO List: ====================== -*) Rewrite - PCB_BASE_FRAME::Save_Module_In_Library - PCB_EDIT_FRAME::ArchiveModulesOnBoard - to use FP_LIB_TABLE mechanisms. *) Apply Fabrizio and Alexander's linux desktop patches after unifying them. *) Get licensing cleaned up. *) Re-arrange the repo architecture. diff --git a/common/fp_lib_table.cpp b/common/fp_lib_table.cpp index 61166e4016..f6e8cc64d0 100644 --- a/common/fp_lib_table.cpp +++ b/common/fp_lib_table.cpp @@ -179,11 +179,27 @@ MODULE* FP_LIB_TABLE::FootprintLoad( const wxString& aNickname, const wxString& } -void FP_LIB_TABLE::FootprintSave( const wxString& aNickname, const MODULE* aFootprint ) +FP_LIB_TABLE::SAVE_T FP_LIB_TABLE::FootprintSave( const wxString& aNickname, const MODULE* aFootprint, bool aOverwrite ) { const ROW* row = FindRow( aNickname ); wxASSERT( (PLUGIN*) row->plugin ); - return row->plugin->FootprintSave( row->GetFullURI( true ), aFootprint, row->GetProperties() ); + + if( !aOverwrite ) + { + // Try loading the footprint to see if it already exists, caller wants overwrite + // protection, which is atypical, not the default. + + wxString fpname = FROM_UTF8( aFootprint->GetFPID().GetFootprintName().c_str() ); + + std::auto_ptr m( row->plugin->FootprintLoad( row->GetFullURI( true ), fpname, row->GetProperties() ) ); + + if( m.get() ) + return SAVE_SKIPPED; + } + + row->plugin->FootprintSave( row->GetFullURI( true ), aFootprint, row->GetProperties() ); + + return SAVE_OK; } @@ -203,6 +219,22 @@ bool FP_LIB_TABLE::IsFootprintLibWritable( const wxString& aNickname ) } +void FP_LIB_TABLE::FootprintLibDelete( const wxString& aNickname ) +{ + const ROW* row = FindRow( aNickname ); + wxASSERT( (PLUGIN*) row->plugin ); + row->plugin->FootprintLibDelete( row->GetFullURI( true ), row->GetProperties() ); +} + + +void FP_LIB_TABLE::FootprintLibCreate( const wxString& aNickname ) +{ + const ROW* row = FindRow( aNickname ); + wxASSERT( (PLUGIN*) row->plugin ); + row->plugin->FootprintLibCreate( row->GetFullURI( true ), row->GetProperties() ); +} + + const wxString FP_LIB_TABLE::GetDescription( const wxString& aNickname ) { // use "no exception" form of find row: diff --git a/include/fp_lib_table.h b/include/fp_lib_table.h index 4b7b3d9a43..5a754320b6 100644 --- a/include/fp_lib_table.h +++ b/include/fp_lib_table.h @@ -394,6 +394,16 @@ public: */ MODULE* FootprintLoad( const wxString& aNickname, const wxString& aFootprintName ); + /** + * Enum SAVE_T + * is the set of return values from FootprintSave() below. + */ + enum SAVE_T + { + SAVE_OK, + SAVE_SKIPPED, + }; + /** * Function FootprintSave * will write @a aFootprint to an existing library given by @a aNickname. @@ -405,9 +415,14 @@ public: * @param aFootprint is what to store in the library. The caller continues * to own the footprint after this call. * + * @param aOverwrite when true means overwrite any existing footprint by the + * same name, else if false means skip the write and return SAVE_SKIPPED. + * + * @return SAVE_T - SAVE_OK or SAVE_SKIPPED. If error saving, then IO_ERROR is thrown. + * * @throw IO_ERROR if there is a problem saving. */ - void FootprintSave( const wxString& aNickname, const MODULE* aFootprint ); + SAVE_T FootprintSave( const wxString& aNickname, const MODULE* aFootprint, bool aOverwrite = true ); /** * Function FootprintDelete @@ -431,6 +446,10 @@ public: */ bool IsFootprintLibWritable( const wxString& aNickname ); + void FootprintLibDelete( const wxString& aNickname ); + + void FootprintLibCreate( const wxString& aNickname ); + //-------------------------------- /** diff --git a/include/wxBasePcbFrame.h b/include/wxBasePcbFrame.h index f89a4ed383..ff0db2f2c2 100644 --- a/include/wxBasePcbFrame.h +++ b/include/wxBasePcbFrame.h @@ -295,7 +295,7 @@ public: * @param aLibName = name of the library to use * @param aModule = the given footprint * @param aOverwrite = true to overwrite an existing footprint, false to - * abort an existing footprint is found + * abort if an existing footprint with same name is found * @param aDisplayDialog = true to display a dialog to enter or confirm the * footprint name * @return : true if OK, false if abort @@ -305,6 +305,16 @@ public: bool aOverwrite, bool aDisplayDialog ); + /** + * Function SelectLibrary + * puts up a dialog and allows the user to pick a library, for unspecified use. + * + * @param aNicknameExisting is the current choice to highlight + * + * @return wxString - the library or wxEmptyString on abort. + */ + wxString SelectLibrary( const wxString& aNicknameExisting ); + MODULE* GetModuleByName(); /** diff --git a/include/wxPcbStruct.h b/include/wxPcbStruct.h index 17be2ccdfd..df4420d61f 100644 --- a/include/wxPcbStruct.h +++ b/include/wxPcbStruct.h @@ -119,7 +119,7 @@ protected: void setupTools(); void destroyTools(); - void onGenericCommand( wxCommandEvent& aEvent ); + void onGenericCommand( wxCommandEvent& aEvent ); // we'll use lower case function names for private member functions. void createPopUpMenuForZones( ZONE_CONTAINER* edge_zone, wxMenu* aPopMenu ); @@ -906,12 +906,11 @@ public: /** * Function ArchiveModulesOnBoard * Save modules in a library: - * @param aLibName: the full filename of the library to create or modify * @param aNewModulesOnly: * true : save modules not already existing in this lib * false: save all modules */ - void ArchiveModulesOnBoard( const wxString& aLibName, bool aNewModulesOnly ); + void ArchiveModulesOnBoard( bool aNewModulesOnly ); /** * Function RecreateBOMFileFromBoard diff --git a/pcbnew/eagle_plugin.cpp b/pcbnew/eagle_plugin.cpp index ac13113884..152bd7962f 100644 --- a/pcbnew/eagle_plugin.cpp +++ b/pcbnew/eagle_plugin.cpp @@ -1106,17 +1106,15 @@ EAGLE_PLUGIN::~EAGLE_PLUGIN() } -const wxString& EAGLE_PLUGIN::PluginName() const +const wxString EAGLE_PLUGIN::PluginName() const { - static const wxString name = wxT( "Eagle" ); - return name; + return wxT( "Eagle" ); } -const wxString& EAGLE_PLUGIN::GetFileExtension() const +const wxString EAGLE_PLUGIN::GetFileExtension() const { - static const wxString extension = wxT( "brd" ); - return extension; + return wxT( "brd" ); } diff --git a/pcbnew/eagle_plugin.h b/pcbnew/eagle_plugin.h index 6fa626af29..1dde4a94e5 100644 --- a/pcbnew/eagle_plugin.h +++ b/pcbnew/eagle_plugin.h @@ -80,11 +80,11 @@ class EAGLE_PLUGIN : public PLUGIN public: //------------------------------------------------------- - const wxString& PluginName() const; + const wxString PluginName() const; BOARD* Load( const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties = NULL ); - const wxString& GetFileExtension() const; + const wxString GetFileExtension() const; wxArrayString FootprintEnumerate( const wxString& aLibraryPath, const PROPERTIES* aProperties = NULL); diff --git a/pcbnew/edit.cpp b/pcbnew/edit.cpp index 9ca03faa0d..cf90c3fad9 100644 --- a/pcbnew/edit.cpp +++ b/pcbnew/edit.cpp @@ -1177,11 +1177,11 @@ void PCB_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) break; case ID_MENU_ARCHIVE_NEW_MODULES: - ArchiveModulesOnBoard( wxEmptyString, true ); + ArchiveModulesOnBoard( true ); break; case ID_MENU_ARCHIVE_ALL_MODULES: - ArchiveModulesOnBoard( wxEmptyString, false ); + ArchiveModulesOnBoard( false ); break; case ID_GEN_IMPORT_DXF_FILE: diff --git a/pcbnew/github/github_plugin.cpp b/pcbnew/github/github_plugin.cpp index dd521f5de1..14f3e21f9e 100644 --- a/pcbnew/github/github_plugin.cpp +++ b/pcbnew/github/github_plugin.cpp @@ -24,22 +24,6 @@ /* - This is a pcbnew PLUGIN which supports some of the PLUGIN::Footprint*() functions - in the PLUGIN interface, and could do so by utilizing the version 3 github.com - API documented here: - - http://developer.github.com - https://help.github.com/articles/creating-an-access-token-for-command-line-use - - but it does not. Rather it simply reads in a zip file of the repo and unzips it - from RAM as needed. Therefore the PLUGIN is read only for accessing - remote pretty libraries. If you want to support writing to the repo, then you - could use the above API. - -@todo: - Derive this PLUGIN from KICAD_PLUGIN so we can use its FootprintSave(). - Support local footprints if they are present in an optional directory. - Possibly cache the zip file locally. Use HTTP's "have changed" or whatever it is called. */ @@ -76,9 +60,14 @@ #include #include #include +#include // ExpandSubstitutions() using namespace std; + +static const char* PRETTY_DIR = "allow_pretty_writing_to_this_dir"; + + typedef boost::ptr_map MODULE_MAP; typedef MODULE_MAP::iterator MODULE_ITER; typedef MODULE_MAP::const_iterator MODULE_CITER; @@ -95,28 +84,27 @@ struct GH_CACHE : public MODULE_MAP GITHUB_PLUGIN::GITHUB_PLUGIN() : - m_cache( 0 ) + PCB_IO(), + m_gh_cache( 0 ) { } GITHUB_PLUGIN::~GITHUB_PLUGIN() { - delete m_cache; + delete m_gh_cache; } -const wxString& GITHUB_PLUGIN::PluginName() const +const wxString GITHUB_PLUGIN::PluginName() const { - static wxString name( wxT( "Github" ) ); - return name; + return wxT( "Github" ); } -const wxString& GITHUB_PLUGIN::GetFileExtension() const +const wxString GITHUB_PLUGIN::GetFileExtension() const { - static wxString empty_ext; - return empty_ext; + return wxEmptyString; } @@ -124,12 +112,14 @@ wxArrayString GITHUB_PLUGIN::FootprintEnumerate( const wxString& aLibraryPath, const PROPERTIES* aProperties ) { //D(printf("%s: this:%p aLibraryPath:'%s'\n", __func__, this, TO_UTF8(aLibraryPath) );) - - cacheLib( aLibraryPath ); + cacheLib( aLibraryPath, aProperties ); wxArrayString ret; - for( MODULE_ITER it = m_cache->begin(); it!=m_cache->end(); ++it ) + if( m_pretty_dir.size() ) + ret = PCB_IO::FootprintEnumerate( m_pretty_dir ); + + for( MODULE_ITER it = m_gh_cache->begin(); it!=m_gh_cache->end(); ++it ) { ret.Add( FROM_UTF8( it->first.c_str() ) ); } @@ -143,13 +133,24 @@ MODULE* GITHUB_PLUGIN::FootprintLoad( const wxString& aLibraryPath, { // D(printf("%s: this:%p aLibraryPath:'%s'\n", __func__, this, TO_UTF8(aLibraryPath) );) - cacheLib( aLibraryPath ); + // clear or set to valid the variable m_pretty_dir + cacheLib( aLibraryPath, aProperties ); + + if( m_pretty_dir.size() ) + { + // API has FootprintLoad() *not* throwing an exception if footprint not found. + MODULE* local = PCB_IO::FootprintLoad( m_pretty_dir, aFootprintName, aProperties ); + + if( local ) + return local; + } + string fp_name = TO_UTF8( aFootprintName ); - MODULE_CITER it = m_cache->find( fp_name ); + MODULE_CITER it = m_gh_cache->find( fp_name ); - if( it != m_cache->end() ) // fp_name is present + if( it != m_gh_cache->end() ) // fp_name is present { wxMemoryInputStream mis( &m_zip_image[0], m_zip_image.size() ); @@ -181,7 +182,109 @@ MODULE* GITHUB_PLUGIN::FootprintLoad( const wxString& aLibraryPath, bool GITHUB_PLUGIN::IsFootprintLibWritable( const wxString& aLibraryPath ) { - return false; + if( m_pretty_dir.size() ) + return PCB_IO::IsFootprintLibWritable( m_pretty_dir ); + else + return false; +} + + +void GITHUB_PLUGIN::FootprintSave( const wxString& aLibraryPath, + const MODULE* aFootprint, const PROPERTIES* aProperties ) +{ + // set m_pretty_dir to either empty or something in aProperties + cacheLib( aLibraryPath, aProperties ); + + if( GITHUB_PLUGIN::IsFootprintLibWritable( aLibraryPath ) ) + { + PCB_IO::FootprintSave( m_pretty_dir, aFootprint, aProperties ); + } + else + { + // This typically will not happen if the caller first properly calls + // IsFootprintLibWritable() to determine if calling FootprintSave() is + // even legal, so I spend no time on internationalization here: + + string msg = StrPrintf( "Github library\n'%s'\nis only writable if you set option '%s' in Library Tables dialog.", + (const char*) TO_UTF8( aLibraryPath ), PRETTY_DIR ); + + THROW_IO_ERROR( msg ); + } +} + + +void GITHUB_PLUGIN::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName, + const PROPERTIES* aProperties ) +{ + // set m_pretty_dir to either empty or something in aProperties + cacheLib( aLibraryPath, aProperties ); + + if( GITHUB_PLUGIN::IsFootprintLibWritable( aLibraryPath ) ) + { + // Does the PCB_IO base class have this footprint? + // We cannot write to github. + + wxArrayString pretties = PCB_IO::FootprintEnumerate( m_pretty_dir, aProperties ); + + if( pretties.Index( aFootprintName ) != wxNOT_FOUND ) + { + PCB_IO::FootprintDelete( m_pretty_dir, aFootprintName, aProperties ); + } + else + { + wxString msg = wxString::Format( + _( "Footprint\n'%s'\nis not in the writable portion of this Github library\n'%s'" ), + GetChars( aFootprintName ), + GetChars( aLibraryPath ) + ); + + THROW_IO_ERROR( msg ); + } + } + else + { + // This typically will not happen if the caller first properly calls + // IsFootprintLibWritable() to determine if calling FootprintSave() is + // even legal, so I spend no time on internationalization here: + + string msg = StrPrintf( "Github library\n'%s'\nis only writable if you set option '%s' in Library Tables dialog.", + (const char*) TO_UTF8( aLibraryPath ), PRETTY_DIR ); + + THROW_IO_ERROR( msg ); + } +} + + +void GITHUB_PLUGIN::FootprintLibCreate( const wxString& aLibraryPath, const PROPERTIES* aProperties ) +{ + // set m_pretty_dir to either empty or something in aProperties + cacheLib( aLibraryPath, aProperties ); + + if( m_pretty_dir.size() ) + { + PCB_IO::FootprintLibCreate( m_pretty_dir, aProperties ); + } + else + { + // THROW_IO_ERROR() @todo + } +} + + +bool GITHUB_PLUGIN::FootprintLibDelete( const wxString& aLibraryPath, const PROPERTIES* aProperties ) +{ + // set m_pretty_dir to either empty or something in aProperties + cacheLib( aLibraryPath, aProperties ); + + if( m_pretty_dir.size() ) + { + return PCB_IO::FootprintLibDelete( m_pretty_dir, aProperties ); + } + else + { + // THROW_IO_ERROR() @todo + return false; + } } @@ -190,32 +293,74 @@ void GITHUB_PLUGIN::FootprintLibOptions( PROPERTIES* aListToAppendTo ) const // inherit options supported by all PLUGINs. PLUGIN::FootprintLibOptions( aListToAppendTo ); - (*aListToAppendTo)["allow_pretty_writing_to_this_dir"] = wxString( _( + (*aListToAppendTo)[ PRETTY_DIR ] = wxString( _( "Set this property to a directory where footprints are to be written as pretty " "footprints when saving to this library. Anything saved will take precedence over " "footprints by the same name in the github repo. These saved footprints can then " "be sent to the library maintainer as updates. " - "

The directory should have a .pretty file extension because the " - "Kicad plugin is used to do the saving.

" + "

The directory must have a .pretty file extension because the " + "format of the save is pretty.

" )).utf8_str(); + /* (*aListToAppendTo)["cache_github_zip_in_this_dir"] = wxString( _( "Set this property to a directory where the github *.zip file will be cached. " "This should speed up subsequent visits to this library." )).utf8_str(); + */ } -void GITHUB_PLUGIN::cacheLib( const wxString& aLibraryPath ) throw( IO_ERROR ) +void GITHUB_PLUGIN::cacheLib( const wxString& aLibraryPath, const PROPERTIES* aProperties ) throw( IO_ERROR ) { - if( !m_cache || m_lib_path != aLibraryPath ) + // This is edge triggered based on a change in 'aLibraryPath', + // usually it does nothing. When the edge fires, m_pretty_dir is set + // to either: + // 1) empty or + // 2) a verified and validated, writable, *.pretty directory. + + if( !m_gh_cache || m_lib_path != aLibraryPath ) { + delete m_gh_cache; + m_gh_cache = 0; + + m_pretty_dir.clear(); + + if( aProperties ) + { + string pretty_dir; + + if( aProperties->Value( PRETTY_DIR, &pretty_dir ) ) + { + wxString wx_pretty_dir = FROM_UTF8( pretty_dir.c_str() ); + + wx_pretty_dir = FP_LIB_TABLE::ExpandSubstitutions( wx_pretty_dir ); + + wxFileName wx_pretty_fn = wx_pretty_dir; + + if( !wx_pretty_fn.IsOk() || + !wx_pretty_fn.IsDirWritable() || + wx_pretty_fn.GetExt() != wxT( "pretty" ) + ) + { + wxString msg = wxString::Format( + _( "option '%s' for Github library '%s' must point to a writable directory ending with '.pretty'." ), + GetChars( FROM_UTF8( PRETTY_DIR ) ), + GetChars( aLibraryPath ) + ); + + THROW_IO_ERROR( msg ); + } + + m_pretty_dir = wx_pretty_dir; + } + } + // operator==( wxString, wxChar* ) does not exist, construct wxString once here. const wxString kicad_mod( wxT( "kicad_mod" ) ); //D(printf("%s: this:%p m_lib_path:'%s' aLibraryPath:'%s'\n", __func__, this, TO_UTF8( m_lib_path), TO_UTF8(aLibraryPath) );) - delete m_cache; - m_cache = new GH_CACHE(); + m_gh_cache = new GH_CACHE(); // INIT_LOGGER( "/tmp", "test.log" ); remote_get_zip( aLibraryPath ); @@ -238,7 +383,7 @@ void GITHUB_PLUGIN::cacheLib( const wxString& aLibraryPath ) throw( IO_ERROR ) { string fp_name = TO_UTF8( fn.GetName() ); // omit extension & path - m_cache->insert( fp_name, entry ); + m_gh_cache->insert( fp_name, entry ); } else delete entry; diff --git a/pcbnew/github/github_plugin.h b/pcbnew/github/github_plugin.h index 12d4629ecd..f1396c5cbf 100644 --- a/pcbnew/github/github_plugin.h +++ b/pcbnew/github/github_plugin.h @@ -25,6 +25,7 @@ #ifndef GITHUB_PLUGIN_H_ #define GITHUB_PLUGIN_H_ +#include struct GH_CACHE; @@ -32,38 +33,98 @@ struct GH_CACHE; /** * Class GITHUB_PLUGIN * implements a portion of pcbnew PLUGIN to provide read only access to a github - * repo consisting of pretty footprints + * repo consisting of pretty footprints. It could have used version 3 of the + github.com API documented here: + + http://developer.github.com + https://help.github.com/articles/creating-an-access-token-for-command-line-use + + but it does not. Rather it simply reads in a zip file of the repo and unzips it + from RAM as needed. Therefore the PLUGIN is read only for accessing + remote pretty libraries. The "Library Path" in the fp-lib-table should be set + to the full https:// URL. For example: + + https://github.com/liftoff-sr/pretty_footprints + + + This is typically https://github.com/user_name/repo_name + +

+ This PLUGIN also supports "Copy On Write", a.k.a "COW". So a library defined + in the fp-lib-table will take an optional option called + allow_pretty_writing_to_this_dir which is essentially the lib_path for + a local Kicad (pretty) library which is combined to make up the Github library. + If the option is missing, then the Github library is read only. If it is present, + then any writes will go to the local *.pretty directory. Any reads will always + give precedence to the local footprints. So once you have written to the local + directory, no github updates will travel down on any footprints for which you've + written locally. Always keep a separate local *.pretty directory for each Github + library, never combine them you will likely create a mess. You must manually + create the local directory in advance, and the directory name must end with ".pretty". + The option allow_pretty_writing_to_this_dir will be path substituted with + any environment variable strings embedded in the option's value, just like the + "Library Path" is. +

+ What's the point of COW? It is to turbo charge the sharing of footprints. If you + periodically email your COW pretty footprints to the Github repo maintainer, + you can help update the Github copy. The idea should be to keep the COW file + set as small as possible. After you've received confirmation that your changes + have been committed up at github.com, you can safely delete your COW file(s) + and those from github.com will flow down. +

+ Note that if you use the module editor to delete a footprint and it is present + in the COW local dir, it will get deleted from there. However, it may not + be deleted from the library as a whole if the footprint of the same name also + existed in the github repo. In this case deleting the local copy will simply + unmask the one at the github repo. Remember, it is masked out if there is + a local COW copy, since the local copy always takes precedence. + * * @author Dick Hollenbeck * @date Original date: 10-Sep-2013 */ -class GITHUB_PLUGIN : public PLUGIN +class GITHUB_PLUGIN : public PCB_IO { public: //--------------------------------------------------------------- - // ("read-only" subset) + const wxString PluginName() const; - const wxString& PluginName() const; + const wxString GetFileExtension() const; - const wxString& GetFileExtension() const; - - wxArrayString FootprintEnumerate( const wxString& aLibraryPath, const PROPERTIES* aProperties ); + wxArrayString FootprintEnumerate( const wxString& aLibraryPath, + const PROPERTIES* aProperties = NULL ); MODULE* FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, const PROPERTIES* aProperties ); + void FootprintSave( const wxString& aLibraryPath, const MODULE* aFootprint, + const PROPERTIES* aProperties = NULL ); + + void FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName, + const PROPERTIES* aProperties = NULL ); + bool IsFootprintLibWritable( const wxString& aLibraryPath ); void FootprintLibOptions( PROPERTIES* aListToAppendTo ) const; + // Since I derive from PCB_IO, I have to implement this, else I'd inherit his, which is bad since + // my lib_path is not his. Note: it is impossible to create a Github library, but can the C.O.W. portion. + void FootprintLibCreate( const wxString& aLibraryPath, const PROPERTIES* aProperties ); + + // Since I derive from PCB_IO, I have to implement this, else I'd inherit his, which is bad since + // my lib_path is not his. Note: it is impossible to delete a Github library, but can the C.O.W portion. + bool FootprintLibDelete( const wxString& aLibraryPath, const PROPERTIES* aProperties ); + //-------------------------------------------------------------- GITHUB_PLUGIN(); // constructor, if any, must be zero arg ~GITHUB_PLUGIN(); -private: +protected: - void cacheLib( const wxString& aLibraryPath ) throw( IO_ERROR ); + void init( const PROPERTIES* aProperties ); + + void cacheLib( const wxString& aLibraryPath, const PROPERTIES* aProperties ) throw( IO_ERROR ); /** * Function repoURL_zipURL @@ -84,7 +145,9 @@ private: wxString m_lib_path; ///< from aLibraryPath, something like https://github.com/liftoff-sr/pretty_footprints std::string m_zip_image; ///< byte image of the zip file in its entirety. - GH_CACHE* m_cache; + GH_CACHE* m_gh_cache; + wxString m_pretty_dir; }; + #endif // GITHUB_PLUGIN_H_ diff --git a/pcbnew/gpcb_plugin.h b/pcbnew/gpcb_plugin.h index 3e38d67e36..b1fb4a9c72 100644 --- a/pcbnew/gpcb_plugin.h +++ b/pcbnew/gpcb_plugin.h @@ -52,16 +52,14 @@ public: //-------------------------------------------------------------- - const wxString& PluginName() const + const wxString PluginName() const { - static const wxString name = wxT( "Geda PCB" ); - return name; + return wxT( "Geda PCB" ); } - const wxString& GetFileExtension() const + const wxString GetFileExtension() const { - static const wxString extension = wxT( "fp" ); - return extension; + return wxT( "fp" ); } wxArrayString FootprintEnumerate( const wxString& aLibraryPath, const PROPERTIES* aProperties = NULL); diff --git a/pcbnew/io_mgr.h b/pcbnew/io_mgr.h index bd5b92a61d..e1983ee8ee 100644 --- a/pcbnew/io_mgr.h +++ b/pcbnew/io_mgr.h @@ -220,13 +220,13 @@ public: * Function PluginName * returns a brief hard coded name for this PLUGIN. */ - virtual const wxString& PluginName() const = 0; + virtual const wxString PluginName() const = 0; /** * Function GetFileExtension * returns the file extension for the PLUGIN. */ - virtual const wxString& GetFileExtension() const = 0; + virtual const wxString GetFileExtension() const = 0; /** * Function Load diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index 23092f07e5..7529f4d9d7 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -85,7 +85,10 @@ public: wxString GetName() const { return m_file_name.GetDirs().Last(); } wxFileName GetFileName() const { return m_file_name; } + + /// Tell if the disk content or the lib_path has changed. bool IsModified() const; + MODULE* GetModule() const { return m_module.get(); } void UpdateModificationTime() { m_mod_time = m_file_name.GetModificationTime(); } }; @@ -339,6 +342,7 @@ bool FP_CACHE::IsModified( const wxString& aLibPath, const wxString& aFootprintN for( MODULE_CITER it = m_modules.begin(); it != m_modules.end(); ++it ) { wxFileName fn = m_lib_path; + fn.SetName( it->second->GetFileName().GetName() ); fn.SetExt( KiCadFootprintFileExtension ); @@ -1804,7 +1808,7 @@ void PCB_IO::FootprintDelete( const wxString& aLibraryPath, const wxString& aFoo { LOCALE_IO toggle; // toggles on, then off, the C locale. - init( NULL ); + init( aProperties ); cacheLib( aLibraryPath ); diff --git a/pcbnew/kicad_plugin.h b/pcbnew/kicad_plugin.h index 515e1b1a71..b761c3b6fd 100644 --- a/pcbnew/kicad_plugin.h +++ b/pcbnew/kicad_plugin.h @@ -83,20 +83,18 @@ public: //-------------------------------------------------------------- - const wxString& PluginName() const + const wxString PluginName() const { - static const wxString name = wxT( "KiCad" ); - return name; + return wxT( "KiCad" ); } - const wxString& GetFileExtension() const + const wxString GetFileExtension() const { // Would have used wildcards_and_files_ext.cpp's KiCadPcbFileExtension, // but to be pure, a plugin should not assume that it will always be linked // with the core of the pcbnew code. (Might someday be a DLL/DSO.) Besides, // file extension policy should be controlled by the plugin. - static const wxString extension = wxT( "kicad_pcb" ); - return extension; + return wxT( "kicad_pcb" ); } void Save( const wxString& aFileName, BOARD* aBoard, @@ -172,6 +170,10 @@ protected: int m_ctl; PCB_PARSER* m_parser; + /// we only cache one footprint library, this determines which one. + void cacheLib( const wxString& aLibraryPath, const wxString& aFootprintName = wxEmptyString ); + + void init( const PROPERTIES* aProperties ); private: void format( BOARD* aBoard, int aNestLevel = 0 ) const @@ -211,11 +213,6 @@ private: void formatLayers( LAYER_MSK aLayerMask, int aNestLevel = 0 ) const throw( IO_ERROR ); - - /// we only cache one footprint library for now, this determines which one. - void cacheLib( const wxString& aLibraryPath, const wxString& aFootprintName = wxEmptyString ); - - void init( const PROPERTIES* aProperties ); }; #endif // KICAD_PLUGIN_H_ diff --git a/pcbnew/legacy_plugin.h b/pcbnew/legacy_plugin.h index b17aecb79d..c5eba40083 100644 --- a/pcbnew/legacy_plugin.h +++ b/pcbnew/legacy_plugin.h @@ -64,16 +64,14 @@ public: //--------------------------------------------------- - const wxString& PluginName() const + const wxString PluginName() const { - static const wxString name = wxT( "KiCad-Legacy" ); - return name; + return wxT( "KiCad-Legacy" ); } - const wxString& GetFileExtension() const + const wxString GetFileExtension() const { - static const wxString extension = wxT( "brd" ); - return extension; + return wxT( "brd" ); } BOARD* Load( const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties = NULL ); diff --git a/pcbnew/librairi.cpp b/pcbnew/librairi.cpp index 2db0b816a2..e23ecb0af1 100644 --- a/pcbnew/librairi.cpp +++ b/pcbnew/librairi.cpp @@ -53,29 +53,29 @@ // unique, "file local" translations: -#define FMT_OK_OVERWRITE _( "Library <%s> exists, OK to replace ?" ) +#define FMT_OK_OVERWRITE _( "Library '%s' exists, OK to replace ?" ) #define FMT_CREATE_LIB _( "Create New Library" ) -#define FMT_OK_DELETE _( "OK to delete module %s in library <%s>" ) +#define FMT_OK_DELETE _( "OK to delete module %s in library '%s'" ) #define FMT_IMPORT_MODULE _( "Import Footprint Module" ) -#define FMT_FILE_NOT_FOUND _( "File <%s> not found" ) +#define FMT_FILE_NOT_FOUND _( "File '%s' not found" ) #define FMT_NOT_MODULE _( "Not a module file" ) -#define FMT_MOD_NOT_FOUND _( "Unable to find or load footprint %s from lib path <%s>" ) -#define FMT_BAD_PATH _( "Unable to find or load footprint from path <%s>" ) -#define FMT_BAD_PATHS _( "The footprint library <%s> could not be found in any of the search paths." ) -#define FMT_LIB_READ_ONLY _( "Library <%s> is read only, not writable" ) +#define FMT_MOD_NOT_FOUND _( "Unable to find or load footprint %s from lib path '%s'" ) +#define FMT_BAD_PATH _( "Unable to find or load footprint from path '%s'" ) +#define FMT_BAD_PATHS _( "The footprint library '%s' could not be found in any of the search paths." ) +#define FMT_LIB_READ_ONLY _( "Library '%s' is read only, not writable" ) #define FMT_EXPORT_MODULE _( "Export Module" ) #define FMT_SAVE_MODULE _( "Save Module" ) #define FMT_MOD_REF _( "Module Reference:" ) -#define FMT_EXPORTED _( "Module exported to file <%s>" ) -#define FMT_MOD_DELETED _( "Module %s deleted from library <%s>" ) +#define FMT_EXPORTED _( "Module exported to file '%s'" ) +#define FMT_MOD_DELETED _( "Module %s deleted from library '%s'" ) #define FMT_MOD_CREATE _( "Module Creation" ) #define FMT_NO_MODULES _( "No modules to archive!" ) #define FMT_LIBRARY _( "Library" ) // window title -#define FMT_MOD_EXISTS _( "Module %s already exists in library <%s>" ) +#define FMT_MOD_EXISTS _( "Module %s already exists in library '%s'" ) #define FMT_NO_REF_ABORTED _( "No reference, aborted" ) -#define FMT_SELECT_LIB _( "Select Active Library" ) +#define FMT_SELECT_LIB _( "Select Library" ) static const wxString ModExportFileWildcard( _( "KiCad foot print export files (*.emp)|*.emp" ) ); @@ -463,6 +463,53 @@ wxString FOOTPRINT_EDIT_FRAME::CreateNewLibrary() bool FOOTPRINT_EDIT_FRAME::DeleteModuleFromCurrentLibrary() { +#if defined(USE_FP_LIB_TABLE) + + wxString nickname = getLibNickName(); + + if( !m_footprintLibTable->IsFootprintLibWritable( nickname ) ) + { + wxString msg = wxString::Format( + _( "Library '%s' is read only" ), + GetChars( nickname ) + ); + + DisplayError( this, msg ); + return false; + } + + wxString fpid_txt = PCB_BASE_FRAME::SelectFootprint( this, nickname, + wxEmptyString, wxEmptyString, m_footprintLibTable ); + + if( !fpid_txt ) + return false; + + FPID fpid( fpid_txt ); + wxString fpname = FROM_UTF8( fpid.GetFootprintName().c_str() ); + + // Confirmation + wxString msg = wxString::Format( FMT_OK_DELETE, fpname.GetData(), nickname.GetData() ); + + if( !IsOK( this, msg ) ) + return false; + + try + { + m_footprintLibTable->FootprintDelete( nickname, fpname ); + } + catch( IO_ERROR ioe ) + { + DisplayError( this, ioe.errorText ); + return false; + } + + msg.Printf( FMT_MOD_DELETED, fpname.GetData(), nickname.GetData() ); + + SetStatusText( msg ); + + return true; + +#else PCB_EDIT_FRAME* parent = (PCB_EDIT_FRAME*) GetParent(); wxString libPath = getLibPath(); wxString footprintName = PCB_BASE_FRAME::SelectFootprint( this, libPath, @@ -497,17 +544,73 @@ bool FOOTPRINT_EDIT_FRAME::DeleteModuleFromCurrentLibrary() SetStatusText( msg ); return true; + +#endif } -/* Save modules in a library: - * param aNewModulesOnly: - * true : save modules not already existing in this lib - * false: save all modules - */ -void PCB_EDIT_FRAME::ArchiveModulesOnBoard( const wxString& aLibName, bool aNewModulesOnly ) +#if defined(USE_FP_LIB_TABLE) +void PCB_EDIT_FRAME::ArchiveModulesOnBoard( bool aNewModulesOnly ) { - wxString fileName = aLibName; + if( GetBoard()->m_Modules == NULL ) + { + DisplayInfoMessage( this, FMT_NO_MODULES ); + return; + } + + wxString last_nickname = wxGetApp().ReturnLastVisitedLibraryPath(); + + wxString nickname = SelectLibrary( last_nickname ); + + if( !nickname ) + return; + + wxGetApp().SaveLastVisitedLibraryPath( nickname ); + + if( !aNewModulesOnly ) + { + wxString msg = wxString::Format( FMT_OK_OVERWRITE, GetChars( nickname ) ); + + if( !IsOK( this, msg ) ) + return; + } + + m_canvas->SetAbortRequest( false ); + + try + { + // Delete old library if we're replacing it entirely. + if( !aNewModulesOnly ) + { + m_footprintLibTable->FootprintLibDelete( nickname ); + m_footprintLibTable->FootprintLibCreate( nickname ); + + for( MODULE* m = GetBoard()->m_Modules; m; m = m->Next() ) + { + m_footprintLibTable->FootprintSave( nickname, m, true ); + } + } + else + { + for( MODULE* m = GetBoard()->m_Modules; m; m = m->Next() ) + { + m_footprintLibTable->FootprintSave( nickname, m, false ); + + // Check for request to stop backup (ESCAPE key actuated) + if( m_canvas->GetAbortRequest() ) + break; + } + } + } + catch( IO_ERROR ioe ) + { + DisplayError( this, ioe.errorText ); + } +} +#else +void PCB_EDIT_FRAME::ArchiveModulesOnBoard( bool aNewModulesOnly ) +{ + wxString fileName; wxString path; if( GetBoard()->m_Modules == NULL ) @@ -518,7 +621,6 @@ void PCB_EDIT_FRAME::ArchiveModulesOnBoard( const wxString& aLibName, bool aNewM path = wxGetApp().ReturnLastVisitedLibraryPath(); - if( !aLibName ) { wxFileDialog dlg( this, FMT_LIBRARY, path, wxEmptyString, @@ -532,7 +634,9 @@ void PCB_EDIT_FRAME::ArchiveModulesOnBoard( const wxString& aLibName, bool aNewM } wxFileName fn( fileName ); + wxGetApp().SaveLastVisitedLibraryPath( fn.GetPath() ); + bool lib_exists = wxFileExists( fileName ); if( !aNewModulesOnly && lib_exists ) @@ -584,12 +688,12 @@ void PCB_EDIT_FRAME::ArchiveModulesOnBoard( const wxString& aLibName, bool aNewM catch( IO_ERROR ioe ) { DisplayError( this, ioe.errorText ); - return; } } +#endif -bool PCB_BASE_FRAME::Save_Module_In_Library( const wxString& aLibPath, +bool PCB_BASE_FRAME::Save_Module_In_Library( const wxString& aLibrary, MODULE* aModule, bool aOverwrite, bool aDisplayDialog ) @@ -618,10 +722,10 @@ bool PCB_BASE_FRAME::Save_Module_In_Library( const wxString& aLibPath, if( ! MODULE::IsLibNameValid( footprintName ) ) { - wxString msg; - msg.Printf( _("Error:\none of invalid chars '%s' found\nin '%s'" ), - MODULE::ReturnStringLibNameInvalidChars( true ), - GetChars( footprintName ) ); + wxString msg = wxString::Format( + _("Error:\none of invalid chars '%s' found\nin '%s'" ), + MODULE::ReturnStringLibNameInvalidChars( true ), + GetChars( footprintName ) ); DisplayError( NULL, msg ); return false; @@ -637,25 +741,24 @@ bool PCB_BASE_FRAME::Save_Module_In_Library( const wxString& aLibPath, aModule->SetFPID( footprintName ); } - IO_MGR::PCB_FILE_T pluginType = IO_MGR::GuessPluginTypeFromLibPath( aLibPath ); - - MODULE* module_exists = NULL; + bool module_exists = false; +#if defined(USE_FP_LIB_TABLE) try { - PLUGIN::RELEASER pi( IO_MGR::PluginFind( pluginType ) ); + MODULE* m = m_footprintLibTable->FootprintLoad( aLibrary, footprintName ); - module_exists = pi->FootprintLoad( aLibPath, footprintName ); - - if( module_exists ) + if( m ) { - delete module_exists; + delete m; + + module_exists = true; // an existing footprint is found in current lib if( aDisplayDialog ) { wxString msg = wxString::Format( FMT_MOD_EXISTS, - footprintName.GetData(), aLibPath.GetData() ); + footprintName.GetData(), aLibrary.GetData() ); SetStatusText( msg ); } @@ -669,7 +772,46 @@ bool PCB_BASE_FRAME::Save_Module_In_Library( const wxString& aLibPath, // this always overwrites any existing footprint, but should yell on its // own if the library or footprint is not writable. - pi->FootprintSave( aLibPath, aModule ); + m_footprintLibTable->FootprintSave( aLibrary, aModule ); + +#else + + + IO_MGR::PCB_FILE_T pluginType = IO_MGR::GuessPluginTypeFromLibPath( aLibrary ); + + try + { + PLUGIN::RELEASER pi( IO_MGR::PluginFind( pluginType ) ); + + MODULE* m = pi->FootprintLoad( aLibrary, footprintName ); + + if( m ) + { + delete m; + + module_exists = true; + + // an existing footprint is found in current lib + if( aDisplayDialog ) + { + wxString msg = wxString::Format( FMT_MOD_EXISTS, + footprintName.GetData(), aLibrary.GetData() ); + + SetStatusText( msg ); + } + + if( !aOverwrite ) + { + // Do not save the given footprint: an old one exists + return true; + } + } + + // this always overwrites any existing footprint, but should yell on its + // own if the library or footprint is not writable. + pi->FootprintSave( aLibrary, aModule ); +#endif + } catch( IO_ERROR ioe ) { @@ -680,10 +822,10 @@ bool PCB_BASE_FRAME::Save_Module_In_Library( const wxString& aLibPath, if( aDisplayDialog ) { wxString fmt = module_exists ? - _( "Component [%s] replaced in <%s>" ) : - _( "Component [%s] added in <%s>" ); + _( "Component [%s] replaced in '%s'" ) : + _( "Component [%s] added in '%s'" ); - wxString msg = wxString::Format( fmt, footprintName.GetData(), aLibPath.GetData() ); + wxString msg = wxString::Format( fmt, footprintName.GetData(), aLibrary.GetData() ); SetStatusText( msg ); } @@ -750,10 +892,10 @@ MODULE* PCB_BASE_FRAME::Create_1_Module( const wxString& aModuleName ) #if !defined( USE_FP_LIB_TABLE ) -void FOOTPRINT_EDIT_FRAME::Select_Active_Library() +wxString PCB_BASE_FRAME::SelectLibrary( const wxString& aNicknameExisting ) { if( g_LibraryNames.GetCount() == 0 ) - return; + return wxEmptyString; wxArrayString headers; headers.Add( _( "Library" ) ); @@ -768,37 +910,31 @@ void FOOTPRINT_EDIT_FRAME::Select_Active_Library() itemsToDisplay.push_back( item ); } - EDA_LIST_DIALOG dlg( this, FMT_SELECT_LIB, headers, itemsToDisplay, getLibNickName() ); + EDA_LIST_DIALOG dlg( this, FMT_SELECT_LIB, headers, itemsToDisplay, aNicknameExisting ); if( dlg.ShowModal() != wxID_OK ) - return; + return wxEmptyString; wxFileName fileName = wxFileName( wxEmptyString, dlg.GetTextSelection(), LegacyFootprintLibPathExtension ); fileName = wxGetApp().FindLibraryPath( fileName ); - if( fileName.IsOk() && fileName.FileExists() ) - { - setLibNickName( fileName.GetName() ); - setLibPath( fileName.GetFullPath() ); - } - else + if( !fileName.IsOk() || !fileName.FileExists() ) { wxString msg = wxString::Format( FMT_BAD_PATHS, GetChars( dlg.GetTextSelection() ) ); DisplayError( this, msg ); - setLibNickName( wxEmptyString ); - setLibPath( wxEmptyString ); + return wxEmptyString; } - updateTitle(); + return fileName.GetFullPath(); } #else -void FOOTPRINT_EDIT_FRAME::Select_Active_Library() +wxString PCB_BASE_FRAME::SelectLibrary( const wxString& aNicknameExisting ) { wxArrayString headers; @@ -818,18 +954,16 @@ void FOOTPRINT_EDIT_FRAME::Select_Active_Library() itemsToDisplay.push_back( item ); } - EDA_LIST_DIALOG dlg( this, FMT_SELECT_LIB, headers, itemsToDisplay, getLibNickName() ); + EDA_LIST_DIALOG dlg( this, FMT_SELECT_LIB, headers, itemsToDisplay, aNicknameExisting ); if( dlg.ShowModal() != wxID_OK ) - return; + return wxEmptyString; wxString nickname = dlg.GetTextSelection(); - setLibNickName( nickname ); + wxLogDebug( wxT( "Chose footprint library '%s'." ), GetChars( nickname ) ); - wxLogDebug( wxT( "Chose footprint library <%s>." ), GetChars( nickname ) ); - - updateTitle(); + return nickname; } #endif diff --git a/pcbnew/modedit.cpp b/pcbnew/modedit.cpp index 37fc32f77f..1822915d8d 100644 --- a/pcbnew/modedit.cpp +++ b/pcbnew/modedit.cpp @@ -249,7 +249,22 @@ void FOOTPRINT_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) break; case ID_MODEDIT_SELECT_CURRENT_LIB: - Select_Active_Library(); + { + wxString library = SelectLibrary( getLibNickName() ); + + if( library.size() ) + { +#if defined(USE_FP_LIB_TABLE) + setLibNickName( library ); +#else + wxFileName fileName( library ); + + setLibNickName( fileName.GetName() ); + setLibPath( fileName.GetFullPath() ); +#endif + updateTitle(); + } + } break; case ID_OPEN_MODULE_VIEWER: @@ -349,11 +364,19 @@ void FOOTPRINT_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) break; case ID_MODEDIT_SAVE_LIBMODULE: +#if defined(USE_FP_LIB_TABLE) + if( GetBoard()->m_Modules && getLibNickName().size() ) + { + Save_Module_In_Library( getLibNickName(), GetBoard()->m_Modules, true, true ); + GetScreen()->ClrModify(); + } +#else if( GetBoard()->m_Modules && getLibPath() != wxEmptyString ) { Save_Module_In_Library( getLibPath(), GetBoard()->m_Modules, true, true ); GetScreen()->ClrModify(); } +#endif break; case ID_MODEDIT_INSERT_MODULE_IN_BOARD: diff --git a/pcbnew/module_editor_frame.h b/pcbnew/module_editor_frame.h index 4d50de7b49..01e9a304ca 100644 --- a/pcbnew/module_editor_frame.h +++ b/pcbnew/module_editor_frame.h @@ -401,8 +401,6 @@ public: */ bool DeleteModuleFromCurrentLibrary(); - void Select_Active_Library(); - virtual EDA_COLOR_T GetGridColor( void ) const; DECLARE_EVENT_TABLE() diff --git a/pcbnew/moduleframe.cpp b/pcbnew/moduleframe.cpp index 50dd1e9d4c..31cb657ea5 100644 --- a/pcbnew/moduleframe.cpp +++ b/pcbnew/moduleframe.cpp @@ -363,9 +363,15 @@ void FOOTPRINT_EDIT_FRAME::OnCloseWindow( wxCloseEvent& Event ) case wxID_YES: // code from FOOTPRINT_EDIT_FRAME::Process_Special_Functions, // at case ID_MODEDIT_SAVE_LIBMODULE +#if defined(USE_FP_LIB_TABLE) + if( GetBoard()->m_Modules && getLibNickName().size() ) + { + if( Save_Module_In_Library( getLibNickName(), GetBoard()->m_Modules, true, true ) ) +#else if( GetBoard()->m_Modules && getLibPath() != wxEmptyString ) { if( Save_Module_In_Library( getLibPath(), GetBoard()->m_Modules, true, true ) ) +#endif { // save was correct GetScreen()->ClrModify(); diff --git a/pcbnew/pcad2kicadpcb_plugin/pcad_plugin.cpp b/pcbnew/pcad2kicadpcb_plugin/pcad_plugin.cpp index c605f42a26..0c7c75c974 100644 --- a/pcbnew/pcad2kicadpcb_plugin/pcad_plugin.cpp +++ b/pcbnew/pcad2kicadpcb_plugin/pcad_plugin.cpp @@ -55,19 +55,15 @@ PCAD_PLUGIN::~PCAD_PLUGIN() } -const wxString& PCAD_PLUGIN::PluginName() const +const wxString PCAD_PLUGIN::PluginName() const { - static const wxString name = wxT( "P-Cad" ); - - return name; + return wxT( "P-Cad" ); } -const wxString& PCAD_PLUGIN::GetFileExtension() const +const wxString PCAD_PLUGIN::GetFileExtension() const { - static const wxString extension = wxT( "pcb" ); - - return extension; + return wxT( "pcb" ); } diff --git a/pcbnew/pcad2kicadpcb_plugin/pcad_plugin.h b/pcbnew/pcad2kicadpcb_plugin/pcad_plugin.h index 55949829ff..45ecbd92b4 100644 --- a/pcbnew/pcad2kicadpcb_plugin/pcad_plugin.h +++ b/pcbnew/pcad2kicadpcb_plugin/pcad_plugin.h @@ -39,18 +39,19 @@ public: // ------------------------------------------------------- - const wxString& PluginName() const; + const wxString PluginName() const; BOARD* Load( const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties = NULL ); - const wxString& GetFileExtension() const; + const wxString GetFileExtension() const; // ------------------------------------------------------ PCAD_PLUGIN(); ~PCAD_PLUGIN(); + private: const PROPERTIES* m_props; BOARD* m_board;