Commit b0c739e7 authored by Dick Hollenbeck's avatar Dick Hollenbeck

*) Implement "Copy On Write" (COW) support in GITHUB_PLUGIN. See class header

   comment for GITHUB_PLUGIN which should flow into Doxygen output.
*) Rewrote: 
        PCB_BASE_FRAME::Save_Module_In_Library(): now uses fp-lib-table and PROPERTIES.  
        PCB_EDIT_FRAME::ArchiveModulesOnBoard(): now can archive to any writable library type.
        PCB_BASE_FRAME::SelectLibrary(): is now generic for selecting a library, not just the active library.
parent 56615d16
......@@ -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.
......
......@@ -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<MODULE> 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:
......
......@@ -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 );
//-----</PLUGIN API SUBSET, REBASED ON aNickname>---------------------------
/**
......
......@@ -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();
/**
......
......@@ -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
......
......@@ -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" );
}
......
......@@ -80,11 +80,11 @@ class EAGLE_PLUGIN : public PLUGIN
public:
//-----<PUBLIC PLUGIN API>--------------------------------------------------
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);
......
......@@ -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:
......
......@@ -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 <github_plugin.h>
#include <class_module.h>
#include <macros.h>
#include <fp_lib_table.h> // ExpandSubstitutions()
using namespace std;
static const char* PRETTY_DIR = "allow_pretty_writing_to_this_dir";
typedef boost::ptr_map<string, wxZipEntry> 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. "
"<p>The directory should have a <b>.pretty</b> file extension because the "
"Kicad plugin is used to do the saving.</p>"
"<p>The directory <b>must</b> have a <b>.pretty</b> file extension because the "
"format of the save is pretty.</p>"
)).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;
......
......@@ -25,6 +25,7 @@
#ifndef GITHUB_PLUGIN_H_
#define GITHUB_PLUGIN_H_
#include <kicad_plugin.h>
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:
<code>
http://developer.github.com
https://help.github.com/articles/creating-an-access-token-for-command-line-use
</code>
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:
<code>
https://github.com/liftoff-sr/pretty_footprints
</code>
This is typically https://github.com/user_name/repo_name
<p>
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
<b>allow_pretty_writing_to_this_dir</b> 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 <b>allow_pretty_writing_to_this_dir</b> will be path substituted with
any environment variable strings embedded in the option's value, just like the
"Library Path" is.
<p>
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.
<p>
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:
//-----<PLUGIN API>----------------------------------------------------------
// ("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 );
//-----</PLUGIN API>---------------------------------------------------------
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_
......@@ -52,16 +52,14 @@ public:
//-----<PLUGIN API>---------------------------------------------------------
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);
......
......@@ -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
......
......@@ -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 );
......
......@@ -83,20 +83,18 @@ public:
//-----<PLUGIN API>---------------------------------------------------------
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_
......@@ -64,16 +64,14 @@ public:
//-----<PLUGIN IMPLEMENTATION>----------------------------------------------
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 );
......
......@@ -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,61 @@ bool PCB_BASE_FRAME::Save_Module_In_Library( const wxString& aLibPath,
aModule->SetFPID( footprintName );
}
IO_MGR::PCB_FILE_T pluginType = IO_MGR::GuessPluginTypeFromLibPath( aLibPath );
bool module_exists = false;
#if defined(USE_FP_LIB_TABLE)
try
{
MODULE* m = m_footprintLibTable->FootprintLoad( aLibrary, footprintName );
if( m )
{
delete m;
module_exists = true;
MODULE* module_exists = NULL;
// 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.
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_exists = pi->FootprintLoad( aLibPath, footprintName );
MODULE* m = pi->FootprintLoad( aLibrary, 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 +809,9 @@ 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 );
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
......@@ -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:
......
......@@ -401,8 +401,6 @@ public:
*/
bool DeleteModuleFromCurrentLibrary();
void Select_Active_Library();
virtual EDA_COLOR_T GetGridColor( void ) const;
DECLARE_EVENT_TABLE()
......
......@@ -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();
......
......@@ -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" );
}
......
......@@ -39,18 +39,19 @@ public:
// -----<PUBLIC PLUGIN API>--------------------------------------------------
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;
// -----</PUBLIC PLUGIN API>-------------------------------------------------
PCAD_PLUGIN();
~PCAD_PLUGIN();
private:
const PROPERTIES* m_props;
BOARD* m_board;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment