Performance enhancements for footprint info list.
Cache the footprint info on disk (in the project). Move timestamp-generation (and checking) to the filesystem so the above will be bullet-proof. Rewrite some wxWidgets classes for performance (see common.h).
This commit is contained in:
parent
5e8e2570ba
commit
f3f814e622
|
@ -529,3 +529,123 @@ bool std::less<wxPoint>::operator()( const wxPoint& aA, const wxPoint& aB ) cons
|
||||||
return aA.x < aB.x;
|
return aA.x < aB.x;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// A cover of wxFileName::SetFullName() which avoids expensive calls to wxFileName::SplitPath().
|
||||||
|
//
|
||||||
|
void WX_FILENAME::SetFullName( const wxString& aFileNameAndExtension )
|
||||||
|
{
|
||||||
|
m_fullName = aFileNameAndExtension;
|
||||||
|
|
||||||
|
size_t dot = m_fullName.find_last_of( wxT( '.' ) );
|
||||||
|
m_fn.SetName( m_fullName.substr( 0, dot ) );
|
||||||
|
m_fn.SetExt( m_fullName.substr( dot + 1 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// An alernative to wxFileName::GetModificationTime() which avoids multiple calls to stat() on
|
||||||
|
// POSIX kernels.
|
||||||
|
//
|
||||||
|
long long WX_FILENAME::GetTimestamp()
|
||||||
|
{
|
||||||
|
#ifdef __WINDOWS__
|
||||||
|
if( m_fn.FileExists() )
|
||||||
|
return m_fn.GetModificationTime().GetValue().GetValue();
|
||||||
|
#else
|
||||||
|
// By stat-ing the file ourselves we save wxWidgets from doing it three times:
|
||||||
|
// Exists( wxFILE_EXISTS_SYMLINK ), FileExists(), and finally GetModificationTime()
|
||||||
|
struct stat fn_stat;
|
||||||
|
wxLstat( GetFullPath(), &fn_stat );
|
||||||
|
|
||||||
|
// Timestamp the source file, not the symlink
|
||||||
|
if( S_ISLNK( fn_stat.st_mode ) ) // wxFILE_EXISTS_SYMLINK
|
||||||
|
{
|
||||||
|
char buffer[ PATH_MAX + 1 ];
|
||||||
|
ssize_t pathLen = readlink( TO_UTF8( GetFullPath() ), buffer, PATH_MAX );
|
||||||
|
|
||||||
|
if( pathLen > 0 )
|
||||||
|
{
|
||||||
|
buffer[ pathLen ] = '\0';
|
||||||
|
wxString srcPath = m_path + wxT( '/' ) + wxString::FromUTF8( buffer );
|
||||||
|
wxLstat( srcPath, &fn_stat );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( S_ISREG( fn_stat.st_mode ) ) // wxFileExists()
|
||||||
|
return fn_stat.st_mtime * 1000;
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// A version of wxDir which avoids expensive calls to wxFileName::wxFileName().
|
||||||
|
//
|
||||||
|
WX_DIR::WX_DIR( const wxString& aDirPath ) :
|
||||||
|
m_dirpath( aDirPath )
|
||||||
|
{
|
||||||
|
m_dir = NULL;
|
||||||
|
|
||||||
|
// throw away the trailing slashes
|
||||||
|
size_t n = m_dirpath.length();
|
||||||
|
|
||||||
|
while ( n > 0 && m_dirpath[--n] == '/' )
|
||||||
|
;
|
||||||
|
|
||||||
|
m_dirpath.Truncate(n + 1);
|
||||||
|
|
||||||
|
m_dir = opendir( m_dirpath.fn_str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool WX_DIR::IsOpened() const
|
||||||
|
{
|
||||||
|
return m_dir != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WX_DIR::~WX_DIR()
|
||||||
|
{
|
||||||
|
if ( m_dir )
|
||||||
|
closedir( m_dir );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool WX_DIR::GetFirst( wxString *filename, const wxString& filespec )
|
||||||
|
{
|
||||||
|
m_filespec = filespec;
|
||||||
|
|
||||||
|
rewinddir( m_dir );
|
||||||
|
return GetNext( filename );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool WX_DIR::GetNext(wxString *filename) const
|
||||||
|
{
|
||||||
|
dirent *dirEntry = NULL;
|
||||||
|
wxString dirEntryName;
|
||||||
|
bool matches = false;
|
||||||
|
|
||||||
|
while ( !matches )
|
||||||
|
{
|
||||||
|
dirEntry = readdir( m_dir );
|
||||||
|
|
||||||
|
if ( !dirEntry )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#if wxUSE_UNICODE
|
||||||
|
dirEntryName = wxString( dirEntry->d_name, *wxConvFileName );
|
||||||
|
#else
|
||||||
|
dirEntryName = dirEntry->d_name;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
matches = wxMatchWild( m_filespec, dirEntryName );
|
||||||
|
}
|
||||||
|
|
||||||
|
*filename = dirEntryName;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -381,4 +381,67 @@ namespace std
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper around a wxFileName which is much more performant with a (very) limited API.
|
||||||
|
*/
|
||||||
|
class WX_FILENAME
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WX_FILENAME( const wxString& aPath, const wxString& aFilename ) :
|
||||||
|
m_fn( aPath, aFilename ),
|
||||||
|
m_path( aPath ),
|
||||||
|
m_fullName( aFilename )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// Avoid wxFileName's expensive path concatenation.
|
||||||
|
wxString GetFullPath() { return m_path + wxT( '/' ) + m_fullName; }
|
||||||
|
|
||||||
|
wxString GetName() { return m_fn.GetName(); }
|
||||||
|
|
||||||
|
// Avoid wxFileName's expensive calls to wxFileName::SplitPath().
|
||||||
|
void SetFullName( const wxString& aFileNameAndExtension );
|
||||||
|
|
||||||
|
// Avoid multiple calls to stat() on POSIX kernels.
|
||||||
|
long long GetTimestamp();
|
||||||
|
|
||||||
|
operator wxFileName() const { return m_fn; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
wxFileName m_fn;
|
||||||
|
wxString m_path;
|
||||||
|
wxString m_fullName;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __WINDOWS__
|
||||||
|
#define WX_DIR wxDir
|
||||||
|
#else
|
||||||
|
// For POSIX kernels we implement our own version which avoids expensive calls to
|
||||||
|
// wxFileName::wxFileName().
|
||||||
|
class WX_DIR
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WX_DIR( const wxString& dir );
|
||||||
|
~WX_DIR();
|
||||||
|
|
||||||
|
// returns true if the directory was successfully opened
|
||||||
|
bool IsOpened() const;
|
||||||
|
|
||||||
|
bool GetFirst( wxString *filename, const wxString& filespec );
|
||||||
|
|
||||||
|
// get next file in the enumeration started with GetFirst()
|
||||||
|
bool GetNext( wxString *filename ) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DIR* m_dir;
|
||||||
|
|
||||||
|
wxString m_dirpath;
|
||||||
|
wxString m_filespec;
|
||||||
|
|
||||||
|
wxDECLARE_NO_COPY_CLASS( WX_DIR );
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // INCLUDE__COMMON_H_
|
#endif // INCLUDE__COMMON_H_
|
||||||
|
|
|
@ -145,7 +145,7 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
/// lazily load stuff not filled in by constructor. This may throw IO_ERRORS.
|
/// lazily load stuff not filled in by constructor. This may throw IO_ERRORS.
|
||||||
virtual void load() = 0;
|
virtual void load() { };
|
||||||
|
|
||||||
FOOTPRINT_LIST* m_owner; ///< provides access to FP_LIB_TABLE
|
FOOTPRINT_LIST* m_owner; ///< provides access to FP_LIB_TABLE
|
||||||
|
|
||||||
|
@ -193,9 +193,6 @@ protected:
|
||||||
FPILIST m_list;
|
FPILIST m_list;
|
||||||
ERRLIST m_errors; ///< some can be PARSE_ERRORs also
|
ERRLIST m_errors; ///< some can be PARSE_ERRORs also
|
||||||
|
|
||||||
MUTEX m_list_lock;
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FOOTPRINT_LIST() : m_lib_table( 0 )
|
FOOTPRINT_LIST() : m_lib_table( 0 )
|
||||||
{
|
{
|
||||||
|
@ -205,6 +202,9 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void WriteCacheToFile( wxTextFile* aFile ) { };
|
||||||
|
virtual void ReadCacheFromFile( wxTextFile* aFile ) { };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the number of items stored in list
|
* @return the number of items stored in list
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -179,7 +179,6 @@ void FOOTPRINT_LIST_IMPL::StartWorkers( FP_LIB_TABLE* aTable, wxString const* aN
|
||||||
{
|
{
|
||||||
m_loader = aLoader;
|
m_loader = aLoader;
|
||||||
m_lib_table = aTable;
|
m_lib_table = aTable;
|
||||||
m_library = aNickname;
|
|
||||||
|
|
||||||
// Clear data before reading files
|
// Clear data before reading files
|
||||||
m_count_finished.store( 0 );
|
m_count_finished.store( 0 );
|
||||||
|
@ -326,7 +325,6 @@ bool FOOTPRINT_LIST_IMPL::JoinWorkers()
|
||||||
|
|
||||||
FOOTPRINT_LIST_IMPL::FOOTPRINT_LIST_IMPL() :
|
FOOTPRINT_LIST_IMPL::FOOTPRINT_LIST_IMPL() :
|
||||||
m_loader( nullptr ),
|
m_loader( nullptr ),
|
||||||
m_library( nullptr ),
|
|
||||||
m_count_finished( 0 ),
|
m_count_finished( 0 ),
|
||||||
m_list_timestamp( 0 ),
|
m_list_timestamp( 0 ),
|
||||||
m_progress_reporter( nullptr ),
|
m_progress_reporter( nullptr ),
|
||||||
|
@ -339,3 +337,72 @@ FOOTPRINT_LIST_IMPL::~FOOTPRINT_LIST_IMPL()
|
||||||
{
|
{
|
||||||
StopWorkers();
|
StopWorkers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FOOTPRINT_LIST_IMPL::WriteCacheToFile( wxTextFile* aCacheFile )
|
||||||
|
{
|
||||||
|
if( aCacheFile->Exists() )
|
||||||
|
{
|
||||||
|
aCacheFile->Open();
|
||||||
|
aCacheFile->Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
aCacheFile->Create();
|
||||||
|
}
|
||||||
|
|
||||||
|
aCacheFile->AddLine( wxString::Format( "%lld", m_list_timestamp ) );
|
||||||
|
|
||||||
|
for( auto& fpinfo : m_list )
|
||||||
|
{
|
||||||
|
aCacheFile->AddLine( fpinfo->GetLibNickname() );
|
||||||
|
aCacheFile->AddLine( fpinfo->GetName() );
|
||||||
|
aCacheFile->AddLine( fpinfo->GetDescription() );
|
||||||
|
aCacheFile->AddLine( fpinfo->GetKeywords() );
|
||||||
|
aCacheFile->AddLine( wxString::Format( "%d", fpinfo->GetOrderNum() ) );
|
||||||
|
aCacheFile->AddLine( wxString::Format( "%u", fpinfo->GetPadCount() ) );
|
||||||
|
aCacheFile->AddLine( wxString::Format( "%u", fpinfo->GetUniquePadCount() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
aCacheFile->Write();
|
||||||
|
aCacheFile->Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FOOTPRINT_LIST_IMPL::ReadCacheFromFile( wxTextFile* aCacheFile )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_list.clear();
|
||||||
|
|
||||||
|
if( aCacheFile->Exists() )
|
||||||
|
aCacheFile->Open();
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
aCacheFile->GetFirstLine().ToLongLong( &m_list_timestamp );
|
||||||
|
|
||||||
|
while( aCacheFile->GetCurrentLine() + 6 < aCacheFile->GetLineCount() )
|
||||||
|
{
|
||||||
|
wxString libNickname = aCacheFile->GetNextLine();
|
||||||
|
wxString name = aCacheFile->GetNextLine();
|
||||||
|
wxString description = aCacheFile->GetNextLine();
|
||||||
|
wxString keywords = aCacheFile->GetNextLine();
|
||||||
|
int orderNum = wxAtoi( aCacheFile->GetNextLine() );
|
||||||
|
unsigned int padCount = (unsigned) wxAtoi( aCacheFile->GetNextLine() );
|
||||||
|
unsigned int uniquePadCount = (unsigned) wxAtoi( aCacheFile->GetNextLine() );
|
||||||
|
|
||||||
|
auto* fpinfo = new FOOTPRINT_INFO_IMPL( libNickname, name, description, keywords,
|
||||||
|
orderNum, padCount, uniquePadCount );
|
||||||
|
m_list.emplace_back( std::unique_ptr<FOOTPRINT_INFO>( fpinfo ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch( ... )
|
||||||
|
{
|
||||||
|
// whatever went wrong, invalidate the cache
|
||||||
|
m_list_timestamp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( aCacheFile->IsOpened() )
|
||||||
|
aCacheFile->Close();
|
||||||
|
}
|
||||||
|
|
|
@ -37,21 +37,42 @@ public:
|
||||||
FOOTPRINT_INFO_IMPL( FOOTPRINT_LIST* aOwner, const wxString& aNickname,
|
FOOTPRINT_INFO_IMPL( FOOTPRINT_LIST* aOwner, const wxString& aNickname,
|
||||||
const wxString& aFootprintName )
|
const wxString& aFootprintName )
|
||||||
{
|
{
|
||||||
m_owner = aOwner;
|
|
||||||
m_loaded = false;
|
|
||||||
m_nickname = aNickname;
|
m_nickname = aNickname;
|
||||||
m_fpname = aFootprintName;
|
m_fpname = aFootprintName;
|
||||||
m_num = 0;
|
m_num = 0;
|
||||||
m_pad_count = 0;
|
m_pad_count = 0;
|
||||||
m_unique_pad_count = 0;
|
m_unique_pad_count = 0;
|
||||||
|
|
||||||
|
m_owner = aOwner;
|
||||||
|
m_loaded = false;
|
||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A constructor for cached items
|
||||||
|
FOOTPRINT_INFO_IMPL( const wxString& aNickname, const wxString& aFootprintName,
|
||||||
|
const wxString& aDescription, const wxString& aKeywords,
|
||||||
|
int aOrderNum, unsigned int aPadCount, unsigned int aUniquePadCount )
|
||||||
|
{
|
||||||
|
m_nickname = aNickname;
|
||||||
|
m_fpname = aFootprintName;
|
||||||
|
m_num = aOrderNum;
|
||||||
|
m_pad_count = aPadCount;
|
||||||
|
m_unique_pad_count = aUniquePadCount;
|
||||||
|
m_doc = aDescription;
|
||||||
|
m_keywords = aKeywords;
|
||||||
|
|
||||||
|
m_owner = nullptr;
|
||||||
|
m_loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// A dummy constructor for use as a target in a binary search
|
// A dummy constructor for use as a target in a binary search
|
||||||
FOOTPRINT_INFO_IMPL( const wxString& aFootprintName )
|
FOOTPRINT_INFO_IMPL( const wxString& aFootprintName )
|
||||||
{
|
{
|
||||||
m_fpname = aFootprintName;
|
m_fpname = aFootprintName;
|
||||||
|
|
||||||
|
m_owner = nullptr;
|
||||||
|
m_loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -62,7 +83,6 @@ protected:
|
||||||
class FOOTPRINT_LIST_IMPL : public FOOTPRINT_LIST
|
class FOOTPRINT_LIST_IMPL : public FOOTPRINT_LIST
|
||||||
{
|
{
|
||||||
FOOTPRINT_ASYNC_LOADER* m_loader;
|
FOOTPRINT_ASYNC_LOADER* m_loader;
|
||||||
const wxString* m_library;
|
|
||||||
std::vector<std::thread> m_threads;
|
std::vector<std::thread> m_threads;
|
||||||
SYNC_QUEUE<wxString> m_queue_in;
|
SYNC_QUEUE<wxString> m_queue_in;
|
||||||
SYNC_QUEUE<wxString> m_queue_out;
|
SYNC_QUEUE<wxString> m_queue_out;
|
||||||
|
@ -96,6 +116,9 @@ public:
|
||||||
FOOTPRINT_LIST_IMPL();
|
FOOTPRINT_LIST_IMPL();
|
||||||
virtual ~FOOTPRINT_LIST_IMPL();
|
virtual ~FOOTPRINT_LIST_IMPL();
|
||||||
|
|
||||||
|
void WriteCacheToFile( wxTextFile* aFile ) override;
|
||||||
|
void ReadCacheFromFile( wxTextFile* aFile ) override;
|
||||||
|
|
||||||
bool ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname = nullptr,
|
bool ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname = nullptr,
|
||||||
PROGRESS_REPORTER* aProgressReporter = nullptr ) override;
|
PROGRESS_REPORTER* aProgressReporter = nullptr ) override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -109,7 +109,6 @@ static inline long parseInt( const wxString& aValue, double aScalar )
|
||||||
class GPCB_FPL_CACHE_ITEM
|
class GPCB_FPL_CACHE_ITEM
|
||||||
{
|
{
|
||||||
wxFileName m_file_name; ///< The the full file name and path of the footprint to cache.
|
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.
|
|
||||||
std::unique_ptr<MODULE> m_module;
|
std::unique_ptr<MODULE> m_module;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -122,10 +121,9 @@ public:
|
||||||
|
|
||||||
|
|
||||||
GPCB_FPL_CACHE_ITEM::GPCB_FPL_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName ) :
|
GPCB_FPL_CACHE_ITEM::GPCB_FPL_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName ) :
|
||||||
|
m_file_name( aFileName ),
|
||||||
m_module( aModule )
|
m_module( aModule )
|
||||||
{
|
{
|
||||||
m_file_name = aFileName;
|
|
||||||
m_writable = true; // temporary init
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -200,27 +198,13 @@ public:
|
||||||
* parent directory).
|
* parent directory).
|
||||||
* Timestamps should not be considered ordered. They either match or they don't.
|
* Timestamps should not be considered ordered. They either match or they don't.
|
||||||
*/
|
*/
|
||||||
long long GetTimestamp();
|
static long long GetTimestamp( const wxString& aLibPath );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function IsModified
|
* Function IsModified
|
||||||
* Return true if the cache is not up-to-date.
|
* Return true if the cache is not up-to-date.
|
||||||
*/
|
*/
|
||||||
bool IsModified();
|
bool IsModified();
|
||||||
|
|
||||||
/**
|
|
||||||
* Function IsPath
|
|
||||||
* checks if \a aPath is the same as the current cache path.
|
|
||||||
*
|
|
||||||
* This tests paths by converting \a aPath using the native separators. Internally
|
|
||||||
* #FP_CACHE stores the current path using native separators. This prevents path
|
|
||||||
* miscompares on Windows due to the fact that paths can be stored with / instead of \\
|
|
||||||
* in the footprint library table.
|
|
||||||
*
|
|
||||||
* @param aPath is the library path to test against.
|
|
||||||
* @return true if \a aPath is the same as the cache path.
|
|
||||||
*/
|
|
||||||
bool IsPath( const wxString& aPath ) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -235,36 +219,35 @@ GPCB_FPL_CACHE::GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPat
|
||||||
|
|
||||||
void GPCB_FPL_CACHE::Load()
|
void GPCB_FPL_CACHE::Load()
|
||||||
{
|
{
|
||||||
|
m_cache_dirty = false;
|
||||||
|
m_cache_timestamp = 0;
|
||||||
|
|
||||||
// Note: like our .pretty footprint libraries, the gpcb footprint libraries are folders,
|
// Note: like our .pretty footprint libraries, the gpcb footprint libraries are folders,
|
||||||
// and the footprints are the .fp files inside this folder.
|
// and the footprints are the .fp files inside this folder.
|
||||||
|
|
||||||
wxDir dir( m_lib_path.GetPath() );
|
WX_DIR dir( m_lib_path.GetPath() );
|
||||||
|
|
||||||
if( !dir.IsOpened() )
|
if( !dir.IsOpened() )
|
||||||
{
|
{
|
||||||
m_cache_timestamp = 0;
|
|
||||||
m_cache_dirty = false;
|
|
||||||
|
|
||||||
THROW_IO_ERROR( wxString::Format( _( "footprint library path \"%s\" does not exist" ),
|
THROW_IO_ERROR( wxString::Format( _( "footprint library path \"%s\" does not exist" ),
|
||||||
m_lib_path.GetPath().GetData() ) );
|
m_lib_path.GetPath().GetData() ) );
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
m_cache_timestamp = m_lib_path.GetModificationTime().GetValue().GetValue();
|
|
||||||
m_cache_dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
wxString fpFileName;
|
wxString fpFileName;
|
||||||
wxString wildcard = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
|
wxString wildcard = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
|
||||||
|
|
||||||
if( !dir.GetFirst( &fpFileName, wildcard, wxDIR_FILES ) )
|
// wxFileName construction is egregiously slow. Construct it once and just swap out
|
||||||
|
// the filename.
|
||||||
|
WX_FILENAME fn( m_lib_path.GetPath(), wxT( "dummy." ) + GedaPcbFootprintLibFileExtension );
|
||||||
|
|
||||||
|
if( !dir.GetFirst( &fpFileName, wildcard ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wxString cacheErrorMsg;
|
wxString cacheErrorMsg;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
wxFileName fn( m_lib_path.GetPath(), fpFileName );
|
fn.SetFullName( fpFileName );
|
||||||
|
|
||||||
// Queue I/O errors so only files that fail to parse don't get loaded.
|
// Queue I/O errors so only files that fail to parse don't get loaded.
|
||||||
try
|
try
|
||||||
|
@ -313,50 +296,39 @@ void GPCB_FPL_CACHE::Remove( const wxString& aFootprintName )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool GPCB_FPL_CACHE::IsPath( const wxString& aPath ) const
|
|
||||||
{
|
|
||||||
// Converts path separators to native path separators
|
|
||||||
wxFileName newPath;
|
|
||||||
newPath.AssignDir( aPath );
|
|
||||||
|
|
||||||
return m_lib_path == newPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool GPCB_FPL_CACHE::IsModified()
|
bool GPCB_FPL_CACHE::IsModified()
|
||||||
{
|
{
|
||||||
if( m_cache_dirty )
|
m_cache_dirty = m_cache_dirty || GetTimestamp( m_lib_path.GetFullPath() ) != m_cache_timestamp;
|
||||||
return true;
|
|
||||||
else
|
return m_cache_dirty;
|
||||||
return GetTimestamp() != m_cache_timestamp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
long long GPCB_FPL_CACHE::GetTimestamp()
|
long long GPCB_FPL_CACHE::GetTimestamp( const wxString& aLibPath )
|
||||||
{
|
{
|
||||||
// Avoid expensive GetModificationTime checks if we already know we're dirty
|
|
||||||
if( m_cache_dirty )
|
|
||||||
return wxDateTime::Now().GetValue().GetValue();
|
|
||||||
|
|
||||||
long long files_timestamp = 0;
|
long long files_timestamp = 0;
|
||||||
|
|
||||||
if( m_lib_path.DirExists() )
|
// wxFileName construction is egregiously slow. Construct it once and just swap out
|
||||||
{
|
// the filename.
|
||||||
files_timestamp = m_lib_path.GetModificationTime().GetValue().GetValue();
|
WX_FILENAME fn( aLibPath, wxT( "dummy." ) + GedaPcbFootprintLibFileExtension );
|
||||||
|
|
||||||
for( MODULE_CITER it = m_modules.begin(); it != m_modules.end(); ++it )
|
WX_DIR dir( aLibPath );
|
||||||
|
|
||||||
|
if( dir.IsOpened() )
|
||||||
{
|
{
|
||||||
wxFileName moduleFile = it->second->GetFileName();
|
wxString fpFileName;
|
||||||
if( moduleFile.FileExists() )
|
wxString wildcard = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
|
||||||
files_timestamp += moduleFile.GetModificationTime().GetValue().GetValue();
|
|
||||||
|
if( dir.GetFirst( &fpFileName, wildcard ) )
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
fn.SetFullName( fpFileName );
|
||||||
|
files_timestamp += fn.GetTimestamp();
|
||||||
|
} while( dir.GetNext( &fpFileName ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the new timestamp doesn't match the cache timestamp, then save ourselves the
|
|
||||||
// expensive calls next time
|
|
||||||
if( m_cache_timestamp != files_timestamp )
|
|
||||||
m_cache_dirty = true;
|
|
||||||
|
|
||||||
return files_timestamp;
|
return files_timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1092,11 +1064,7 @@ bool GPCB_PLUGIN::FootprintLibDelete( const wxString& aLibraryPath, const PROPER
|
||||||
|
|
||||||
long long GPCB_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const
|
long long GPCB_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const
|
||||||
{
|
{
|
||||||
// If we have no cache, return a number which won't match any stored timestamps
|
return GPCB_FPL_CACHE::GetTimestamp( aLibraryPath );
|
||||||
if( !m_cache || !m_cache->IsPath( aLibraryPath ) )
|
|
||||||
return wxDateTime::Now().GetValue().GetValue();
|
|
||||||
|
|
||||||
return m_cache->GetTimestamp();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -152,7 +152,7 @@ public:
|
||||||
* parent directory).
|
* parent directory).
|
||||||
* Timestamps should not be considered ordered. They either match or they don't.
|
* Timestamps should not be considered ordered. They either match or they don't.
|
||||||
*/
|
*/
|
||||||
long long GetTimestamp();
|
static long long GetTimestamp( const wxString& aLibPath );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function IsModified
|
* Function IsModified
|
||||||
|
@ -257,51 +257,49 @@ void FP_CACHE::Save( MODULE* aModule )
|
||||||
|
|
||||||
void FP_CACHE::Load()
|
void FP_CACHE::Load()
|
||||||
{
|
{
|
||||||
wxDir dir( m_lib_raw_path );
|
m_cache_dirty = false;
|
||||||
|
m_cache_timestamp = 0;
|
||||||
|
|
||||||
|
WX_DIR dir( m_lib_raw_path );
|
||||||
|
|
||||||
if( !dir.IsOpened() )
|
if( !dir.IsOpened() )
|
||||||
{
|
{
|
||||||
m_cache_timestamp = 0;
|
|
||||||
m_cache_dirty = false;
|
|
||||||
|
|
||||||
wxString msg = wxString::Format( _( "Footprint library path \"%s\" does not exist" ),
|
wxString msg = wxString::Format( _( "Footprint library path \"%s\" does not exist" ),
|
||||||
m_lib_raw_path );
|
m_lib_raw_path );
|
||||||
THROW_IO_ERROR( msg );
|
THROW_IO_ERROR( msg );
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
m_cache_timestamp = m_lib_path.GetModificationTime().GetValue().GetValue();
|
|
||||||
m_cache_dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
wxString fpFileName;
|
wxString fpFileName;
|
||||||
wxString wildcard = wxT( "*." ) + KiCadFootprintFileExtension;
|
wxString wildcard = wxT( "*." ) + KiCadFootprintFileExtension;
|
||||||
|
|
||||||
if( dir.GetFirst( &fpFileName, wildcard, wxDIR_FILES ) )
|
// wxFileName construction is egregiously slow. Construct it once and just swap out
|
||||||
|
// the filename.
|
||||||
|
WX_FILENAME fn( m_lib_raw_path, wxT( "dummy." ) + KiCadFootprintFileExtension );
|
||||||
|
|
||||||
|
if( dir.GetFirst( &fpFileName, wildcard ) )
|
||||||
{
|
{
|
||||||
wxString cacheError;
|
wxString cacheError;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
// prepend the libpath into fullPath
|
fn.SetFullName( fpFileName );
|
||||||
wxFileName fullPath( m_lib_raw_path, fpFileName );
|
|
||||||
|
|
||||||
// Queue I/O errors so only files that fail to parse don't get loaded.
|
// Queue I/O errors so only files that fail to parse don't get loaded.
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FILE_LINE_READER reader( fullPath.GetFullPath() );
|
FILE_LINE_READER reader( fn.GetFullPath() );
|
||||||
|
|
||||||
m_owner->m_parser->SetLineReader( &reader );
|
m_owner->m_parser->SetLineReader( &reader );
|
||||||
|
|
||||||
MODULE* footprint = (MODULE*) m_owner->m_parser->Parse();
|
MODULE* footprint = (MODULE*) m_owner->m_parser->Parse();
|
||||||
|
|
||||||
// The footprint name is the file name without the extension.
|
// The footprint name is the file name without the extension.
|
||||||
wxString fpName = fullPath.GetName();
|
wxString fpName = fn.GetName();
|
||||||
|
|
||||||
footprint->SetFPID( LIB_ID( wxEmptyString, fpName ) );
|
footprint->SetFPID( LIB_ID( wxEmptyString, fpName ) );
|
||||||
m_modules.insert( fpName, new FP_CACHE_ITEM( footprint, fullPath ) );
|
m_modules.insert( fpName, new FP_CACHE_ITEM( footprint, fn ) );
|
||||||
|
|
||||||
m_cache_timestamp += fullPath.GetModificationTime().GetValue().GetValue();
|
m_cache_timestamp += fn.GetTimestamp();
|
||||||
}
|
}
|
||||||
catch( const IO_ERROR& ioe )
|
catch( const IO_ERROR& ioe )
|
||||||
{
|
{
|
||||||
|
@ -345,64 +343,37 @@ bool FP_CACHE::IsPath( const wxString& aPath ) const
|
||||||
|
|
||||||
bool FP_CACHE::IsModified()
|
bool FP_CACHE::IsModified()
|
||||||
{
|
{
|
||||||
if( m_cache_dirty )
|
m_cache_dirty = m_cache_dirty || GetTimestamp( m_lib_path.GetFullPath() ) != m_cache_timestamp;
|
||||||
return true;
|
|
||||||
else
|
return m_cache_dirty;
|
||||||
return GetTimestamp() != m_cache_timestamp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
long long FP_CACHE::GetTimestamp()
|
long long FP_CACHE::GetTimestamp( const wxString& aLibPath )
|
||||||
{
|
{
|
||||||
// Avoid expensive GetModificationTime checks if we already know we're dirty
|
|
||||||
if( m_cache_dirty )
|
|
||||||
return wxDateTime::Now().GetValue().GetValue();
|
|
||||||
|
|
||||||
long long files_timestamp = 0;
|
long long files_timestamp = 0;
|
||||||
|
|
||||||
if( m_lib_path.DirExists() )
|
// wxFileName construction is egregiously slow. Construct it once and just
|
||||||
{
|
// swap out the filename for each file.
|
||||||
files_timestamp = m_lib_path.GetModificationTime().GetValue().GetValue();
|
WX_FILENAME fn( aLibPath, wxT( "dummy." ) + KiCadFootprintFileExtension );
|
||||||
|
|
||||||
for( MODULE_CITER it = m_modules.begin(); it != m_modules.end(); ++it )
|
WX_DIR dir( aLibPath );
|
||||||
{
|
|
||||||
const wxFileName& fn = it->second->GetFileName();
|
|
||||||
#ifdef __WINDOWS__
|
|
||||||
if( fn.FileExists() )
|
|
||||||
files_timestamp += fn.GetModificationTime().GetValue().GetValue();
|
|
||||||
#else
|
|
||||||
// By stat-ing the file ourselves we save wxWidgets from doing it three times:
|
|
||||||
// fn.Exists( wxFILE_EXISTS_SYMLINK ), fn.FileExists() & fn.GetModificationTime()
|
|
||||||
struct stat fn_stat;
|
|
||||||
wxLstat( fn.GetFullPath(), &fn_stat );
|
|
||||||
|
|
||||||
// Timestamp the source file, not the symlink
|
if( dir.IsOpened() )
|
||||||
if( S_ISLNK( fn_stat.st_mode ) ) // wxFILE_EXISTS_SYMLINK
|
|
||||||
{
|
{
|
||||||
char buffer[ PATH_MAX + 1 ];
|
wxString fpFileName;
|
||||||
ssize_t pathLen = readlink( TO_UTF8( fn.GetFullPath() ), buffer, PATH_MAX );
|
wxString wildcard = wxT( "*." ) + KiCadFootprintFileExtension;
|
||||||
|
|
||||||
if( pathLen > 0 )
|
if( dir.GetFirst( &fpFileName, wildcard ) )
|
||||||
{
|
{
|
||||||
buffer[ pathLen ] = '\0';
|
do
|
||||||
wxFileName srcFn;
|
{
|
||||||
srcFn.Assign( fn.GetPath() + wxT( "/" ) + wxString::FromUTF8( buffer ) );
|
fn.SetFullName( fpFileName );
|
||||||
srcFn.Normalize();
|
files_timestamp += fn.GetTimestamp();
|
||||||
wxLstat( srcFn.GetFullPath(), &fn_stat );
|
} while( dir.GetNext( &fpFileName ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( S_ISREG( fn_stat.st_mode ) ) // wxFileExists()
|
|
||||||
files_timestamp += fn_stat.st_mtime * 1000;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the new timestamp doesn't match the cache timestamp, then save ourselves the
|
|
||||||
// expensive calls next time
|
|
||||||
if( m_cache_timestamp != files_timestamp )
|
|
||||||
m_cache_dirty = true;
|
|
||||||
|
|
||||||
return files_timestamp;
|
return files_timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2200,11 +2171,7 @@ void PCB_IO::FootprintDelete( const wxString& aLibraryPath, const wxString& aFoo
|
||||||
|
|
||||||
long long PCB_IO::GetLibraryTimestamp( const wxString& aLibraryPath ) const
|
long long PCB_IO::GetLibraryTimestamp( const wxString& aLibraryPath ) const
|
||||||
{
|
{
|
||||||
// If we have no cache, return a number which won't match any stored timestamps
|
return FP_CACHE::GetTimestamp( aLibraryPath );
|
||||||
if( !m_cache || !m_cache->IsPath( aLibraryPath ) )
|
|
||||||
return wxDateTime::Now().GetValue().GetValue();
|
|
||||||
|
|
||||||
return m_cache->GetTimestamp();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3164,30 +3164,20 @@ struct LP_CACHE
|
||||||
{
|
{
|
||||||
LEGACY_PLUGIN* m_owner; // my owner, I need its LEGACY_PLUGIN::loadMODULE()
|
LEGACY_PLUGIN* m_owner; // my owner, I need its LEGACY_PLUGIN::loadMODULE()
|
||||||
wxString m_lib_path;
|
wxString m_lib_path;
|
||||||
wxDateTime m_mod_time;
|
|
||||||
MODULE_MAP m_modules; // map or tuple of footprint_name vs. MODULE*
|
MODULE_MAP m_modules; // map or tuple of footprint_name vs. MODULE*
|
||||||
bool m_writable;
|
bool m_writable;
|
||||||
|
|
||||||
|
bool m_cache_dirty; // Stored separately because it's expensive to check
|
||||||
|
// m_cache_timestamp against all the files.
|
||||||
|
long long m_cache_timestamp; // A hash of the timestamps for all the footprint
|
||||||
|
// files.
|
||||||
|
|
||||||
LP_CACHE( LEGACY_PLUGIN* aOwner, const wxString& aLibraryPath );
|
LP_CACHE( LEGACY_PLUGIN* aOwner, const wxString& aLibraryPath );
|
||||||
|
|
||||||
// Most all functions in this class throw IO_ERROR exceptions. There are no
|
// 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.
|
// error codes nor user interface calls from here, nor in any PLUGIN.
|
||||||
// Catch these exceptions higher up please.
|
// Catch these exceptions higher up please.
|
||||||
|
|
||||||
/// save the entire legacy library to m_lib_path;
|
|
||||||
void Save();
|
|
||||||
|
|
||||||
void SaveHeader( FILE* aFile );
|
|
||||||
|
|
||||||
void SaveIndex( FILE* aFile );
|
|
||||||
|
|
||||||
void SaveModules( FILE* aFile );
|
|
||||||
|
|
||||||
void SaveEndOfFile( FILE* aFile )
|
|
||||||
{
|
|
||||||
fprintf( aFile, "$EndLIBRARY\n" );
|
|
||||||
}
|
|
||||||
|
|
||||||
void Load();
|
void Load();
|
||||||
|
|
||||||
void ReadAndVerifyHeader( LINE_READER* aReader );
|
void ReadAndVerifyHeader( LINE_READER* aReader );
|
||||||
|
@ -3196,32 +3186,39 @@ struct LP_CACHE
|
||||||
|
|
||||||
void LoadModules( LINE_READER* aReader );
|
void LoadModules( LINE_READER* aReader );
|
||||||
|
|
||||||
wxDateTime GetLibModificationTime();
|
bool IsModified();
|
||||||
|
static long long GetTimestamp( const wxString& aLibPath );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
LP_CACHE::LP_CACHE( LEGACY_PLUGIN* aOwner, const wxString& aLibraryPath ) :
|
LP_CACHE::LP_CACHE( LEGACY_PLUGIN* aOwner, const wxString& aLibraryPath ) :
|
||||||
m_owner( aOwner ),
|
m_owner( aOwner ),
|
||||||
m_lib_path( aLibraryPath ),
|
m_lib_path( aLibraryPath ),
|
||||||
m_writable( true )
|
m_writable( true ),
|
||||||
|
m_cache_timestamp( 0 ),
|
||||||
|
m_cache_dirty( true )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
wxDateTime LP_CACHE::GetLibModificationTime()
|
bool LP_CACHE::IsModified()
|
||||||
{
|
{
|
||||||
wxFileName fn( m_lib_path );
|
m_cache_dirty = m_cache_dirty || GetTimestamp( m_lib_path ) != m_cache_timestamp;
|
||||||
|
|
||||||
// update the writable flag while we have a wxFileName, in a network this
|
return m_cache_dirty;
|
||||||
// is possibly quite dynamic anyway.
|
}
|
||||||
m_writable = fn.IsFileWritable();
|
|
||||||
|
|
||||||
return fn.GetModificationTime();
|
|
||||||
|
long long LP_CACHE::GetTimestamp( const wxString& aLibPath )
|
||||||
|
{
|
||||||
|
return wxFileName( aLibPath ).GetModificationTime().GetValue().GetValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LP_CACHE::Load()
|
void LP_CACHE::Load()
|
||||||
{
|
{
|
||||||
|
m_cache_dirty = false;
|
||||||
|
|
||||||
FILE_LINE_READER reader( m_lib_path );
|
FILE_LINE_READER reader( m_lib_path );
|
||||||
|
|
||||||
ReadAndVerifyHeader( &reader );
|
ReadAndVerifyHeader( &reader );
|
||||||
|
@ -3231,7 +3228,7 @@ void LP_CACHE::Load()
|
||||||
// Remember the file modification time of library file when the
|
// Remember the file modification time of library file when the
|
||||||
// cache snapshot was made, so that in a networked environment we will
|
// cache snapshot was made, so that in a networked environment we will
|
||||||
// reload the cache as needed.
|
// reload the cache as needed.
|
||||||
m_mod_time = GetLibModificationTime();
|
m_cache_timestamp = GetTimestamp( m_lib_path );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3388,19 +3385,13 @@ void LP_CACHE::LoadModules( LINE_READER* aReader )
|
||||||
|
|
||||||
long long LEGACY_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const
|
long long LEGACY_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const
|
||||||
{
|
{
|
||||||
// If we have no cache, return a number which won't match any stored timestamps
|
return LP_CACHE::GetTimestamp( aLibraryPath );
|
||||||
if( !m_cache || m_cache->m_lib_path != aLibraryPath )
|
|
||||||
return wxDateTime::Now().GetValue().GetValue();
|
|
||||||
|
|
||||||
return m_cache->GetLibModificationTime().GetValue().GetValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LEGACY_PLUGIN::cacheLib( const wxString& aLibraryPath )
|
void LEGACY_PLUGIN::cacheLib( const wxString& aLibraryPath )
|
||||||
{
|
{
|
||||||
if( !m_cache || m_cache->m_lib_path != aLibraryPath ||
|
if( !m_cache || m_cache->m_lib_path != aLibraryPath || m_cache->IsModified() )
|
||||||
// somebody else on a network touched the library:
|
|
||||||
m_cache->m_mod_time != m_cache->GetLibModificationTime() )
|
|
||||||
{
|
{
|
||||||
// a spectacular episode in memory management:
|
// a spectacular episode in memory management:
|
||||||
delete m_cache;
|
delete m_cache;
|
||||||
|
|
|
@ -192,7 +192,7 @@ MODULE* PCB_BASE_FRAME::SelectFootprintFromLibTree( bool aAllowBrowser )
|
||||||
|
|
||||||
static wxString lastComponentName;
|
static wxString lastComponentName;
|
||||||
|
|
||||||
WX_PROGRESS_REPORTER progressReporter( this, _( "Loading Footprint Libraries" ), 2 );
|
WX_PROGRESS_REPORTER progressReporter( this, _( "Loading Footprint Libraries" ), 3 );
|
||||||
GFootprintList.ReadFootprintFiles( fpTable, nullptr, &progressReporter );
|
GFootprintList.ReadFootprintFiles( fpTable, nullptr, &progressReporter );
|
||||||
progressReporter.Show( false );
|
progressReporter.Show( false );
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,31 @@
|
||||||
#include <gal/graphics_abstraction_layer.h>
|
#include <gal/graphics_abstraction_layer.h>
|
||||||
#include <class_board.h>
|
#include <class_board.h>
|
||||||
#include <view/view.h>
|
#include <view/view.h>
|
||||||
|
#include "footprint_info_impl.h"
|
||||||
|
#include <project.h>
|
||||||
|
|
||||||
|
PCB_BASE_EDIT_FRAME::PCB_BASE_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent,
|
||||||
|
FRAME_T aFrameType, const wxString& aTitle,
|
||||||
|
const wxPoint& aPos, const wxSize& aSize, long aStyle,
|
||||||
|
const wxString& aFrameName ) :
|
||||||
|
PCB_BASE_FRAME( aKiway, aParent, aFrameType, aTitle, aPos, aSize, aStyle, aFrameName ),
|
||||||
|
m_rotationAngle( 900 ), m_undoRedoBlocked( false )
|
||||||
|
{
|
||||||
|
static bool oneShot = true;
|
||||||
|
|
||||||
|
if( oneShot )
|
||||||
|
{
|
||||||
|
wxTextFile footprintInfoCache( Prj().GetProjectPath() + "fp-info-cache" );
|
||||||
|
GFootprintList.ReadCacheFromFile( &footprintInfoCache );
|
||||||
|
oneShot = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PCB_BASE_EDIT_FRAME::~PCB_BASE_EDIT_FRAME()
|
||||||
|
{
|
||||||
|
wxTextFile footprintInfoCache( Prj().GetProjectPath() + "fp-info-cache" );
|
||||||
|
GFootprintList.WriteCacheToFile( &footprintInfoCache );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void PCB_BASE_EDIT_FRAME::SetRotationAngle( int aRotationAngle )
|
void PCB_BASE_EDIT_FRAME::SetRotationAngle( int aRotationAngle )
|
||||||
|
|
|
@ -37,12 +37,9 @@ class PCB_BASE_EDIT_FRAME : public PCB_BASE_FRAME
|
||||||
public:
|
public:
|
||||||
PCB_BASE_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent, FRAME_T aFrameType,
|
PCB_BASE_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent, FRAME_T aFrameType,
|
||||||
const wxString& aTitle, const wxPoint& aPos, const wxSize& aSize,
|
const wxString& aTitle, const wxPoint& aPos, const wxSize& aSize,
|
||||||
long aStyle, const wxString& aFrameName ) :
|
long aStyle, const wxString& aFrameName );
|
||||||
PCB_BASE_FRAME( aKiway, aParent, aFrameType, aTitle, aPos, aSize, aStyle, aFrameName ),
|
|
||||||
m_rotationAngle( 900 ), m_undoRedoBlocked( false )
|
|
||||||
{}
|
|
||||||
|
|
||||||
virtual ~PCB_BASE_EDIT_FRAME() {};
|
virtual ~PCB_BASE_EDIT_FRAME();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function GetModel()
|
* Function GetModel()
|
||||||
|
|
Loading…
Reference in New Issue