More performance enhancements.

Be more intelligent about sorting lib tree items.  (Footprint
entries, for instance, come out of an already-sorted list.)

Don't recreate menus twice when laoding Footprint Editor.

More pervasive use of WX_FILENAME to avoid expensive calls to
wxFileName::SplitPath() and string concatenation.

For POSIX kernels do all the work on the file-system side so we
don't have to keep converting back and forth between encodings.
This commit is contained in:
Jeff Young 2018-08-05 12:56:02 +01:00
parent 9e84c3fc3b
commit be1d6113d6
24 changed files with 356 additions and 321 deletions

View File

@ -534,121 +534,240 @@ bool std::less<wxPoint>::operator()( const wxPoint& aA, const wxPoint& aB ) cons
//
// A cover of wxFileName::SetFullName() which avoids expensive calls to wxFileName::SplitPath().
// A wrapper around a wxFileName which avoids expensive calls to wxFileName::SplitPath()
// and string concatenations by caching the path and filename locally and only resolving
// the wxFileName when it has to.
//
WX_FILENAME::WX_FILENAME( const wxString& aPath, const wxString& aFilename ) :
m_fn( aPath, aFilename ),
m_path( aPath ),
m_fullName( aFilename )
{ }
void WX_FILENAME::SetFullName( const wxString& aFileNameAndExtension )
{
m_fullName = aFileNameAndExtension;
}
wxString WX_FILENAME::GetName() const
{
size_t dot = m_fullName.find_last_of( wxT( '.' ) );
return m_fullName.substr( 0, dot );
}
wxString WX_FILENAME::GetFullName() const
{
return m_fullName;
}
wxString WX_FILENAME::GetPath() const
{
return m_path;
}
wxString WX_FILENAME::GetFullPath() const
{
return m_path + wxT( '/' ) + m_fullName;
}
// Write locally-cached values to the wxFileName. MUST be called before using m_fn.
void WX_FILENAME::resolve()
{
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__
resolve();
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;
}
#ifndef __WINDOWS__
//
// A version of wxDir which avoids expensive calls to wxFileName::wxFileName().
// A copy of wxMatchWild (attributed to <dalewis@cs.Buffalo.EDU>) modified to use
// POSIX-file-system-encoded inputs.
//
WX_DIR::WX_DIR( const wxString& aDirPath ) :
m_dirpath( aDirPath )
bool matchWild( const char* pat, const char* text, bool dot_special )
{
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 )
if ( !*text )
{
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 );
/* Match if both are empty. */
return !*pat;
}
*filename = dirEntryName;
const char *m = pat,
*n = text,
*ma = NULL,
*na = NULL;
int just = 0,
acount = 0,
count = 0;
return true;
if (dot_special && (*n == '.'))
{
/* Never match so that hidden Unix files
* are never found. */
return false;
}
for (;;)
{
if (*m == '*')
{
ma = ++m;
na = n;
just = 1;
acount = count;
}
else if (*m == '?')
{
m++;
if (!*n++)
return false;
}
else
{
if (*m == '\\')
{
m++;
/* Quoting "nothing" is a bad thing */
if (!*m)
return false;
}
if (!*m)
{
/*
* If we are out of both strings or we just
* saw a wildcard, then we can say we have a
* match
*/
if (!*n)
return true;
if (just)
return true;
just = 0;
goto not_matched;
}
/*
* We could check for *n == NULL at this point, but
* since it's more common to have a character there,
* check to see if they match first (m and n) and
* then if they don't match, THEN we can check for
* the NULL of n
*/
just = 0;
if (*m == *n)
{
m++;
count++;
n++;
}
else
{
not_matched:
/*
* If there are no more characters in the
* string, but we still need to find another
* character (*m != NULL), then it will be
* impossible to match it
*/
if (!*n)
return false;
if (ma)
{
m = ma;
n = ++na;
count = acount;
}
else
return false;
}
}
}
}
long long TimestampDir( const wxString& aDirPath, const wxString& aFilespec )
{
long long timestamp = 0;
#ifdef __WINDOWS__
// wxFileName construction is egregiously slow. Construct it once and just swap out
// the filename thereafter.
WX_FILENAME fn( aDirPath, wxT( "dummyName" ) );
wxDir dir( aDirPath );
wxString fullname;
if( dir.IsOpened() )
{
if( dir.GetFirst( &fullname, filespec ) )
{
do
{
fn.SetFullName( fullname );
timestamp += fn.GetTimestamp();
}
while( dir.GetNext( &fullname ) );
}
}
#else
// POSIX version. Save time by not converting between encodings -- do everything on
// the file-system side.
std::string filespec( aFilespec.fn_str() );
std::string dir_path( aDirPath.fn_str() );
DIR* dir = opendir( dir_path.c_str() );
if( dir )
{
for( dirent* dir_entry = readdir( dir ); dir_entry; dir_entry = readdir( dir ) )
{
if( !matchWild( filespec.c_str(), dir_entry->d_name, true ) )
continue;
std::string entry_path = dir_path + '/' + dir_entry->d_name;
struct stat entry_stat;
wxCRT_Lstat( entry_path.c_str(), &entry_stat );
// Timestamp the source file, not the symlink
if( S_ISLNK( entry_stat.st_mode ) ) // wxFILE_EXISTS_SYMLINK
{
char buffer[ PATH_MAX + 1 ];
ssize_t pathLen = readlink( entry_path.c_str(), buffer, PATH_MAX );
if( pathLen > 0 )
{
buffer[ pathLen ] = '\0';
entry_path = dir_path + buffer;
wxCRT_Lstat( entry_path.c_str(), &entry_stat );
}
}
if( S_ISREG( entry_stat.st_mode ) ) // wxFileExists()
timestamp += entry_stat.st_mtime * 1000;
}
}
#endif
return timestamp;
}

View File

@ -23,12 +23,6 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file footprint_info.cpp
*/
/*
* Functions to read footprint libraries and fill m_footprints by available footprints names
* and their documentation (comments and keywords)
@ -156,7 +150,6 @@ FOOTPRINT_LIST* FOOTPRINT_LIST::GetInstance( KIWAY& aKiway )
FOOTPRINT_ASYNC_LOADER::FOOTPRINT_ASYNC_LOADER() : m_list( nullptr )
{
m_started = false;
m_total_libs = 0;
}
@ -177,8 +170,6 @@ void FOOTPRINT_ASYNC_LOADER::SetList( FOOTPRINT_LIST* aList )
void FOOTPRINT_ASYNC_LOADER::Start(
FP_LIB_TABLE* aTable, wxString const* aNickname, unsigned aNThreads )
{
m_started = true;
// Capture the FP_LIB_TABLE into m_last_table. Formatting it as a string instead of storing the
// raw data avoids having to pull in the FP-specific parts.
STRING_FORMATTER sof;
@ -210,17 +201,3 @@ void FOOTPRINT_ASYNC_LOADER::Abort()
m_list = nullptr;
}
}
void FOOTPRINT_ASYNC_LOADER::SetCompletionCallback( std::function<void()> aCallback )
{
m_completion_cb = std::move(aCallback);
}
bool FOOTPRINT_ASYNC_LOADER::IsSameTable( FP_LIB_TABLE* aOther )
{
STRING_FORMATTER sof;
aOther->Format( &sof, 0 );
return m_last_table == sof.GetString();
}

View File

@ -142,15 +142,15 @@ BOX2D STROKE_FONT::computeBoundingBox( const GLYPH& aGLYPH, const VECTOR2D& aGLY
std::deque<VECTOR2D> boundingPoints;
boundingPoints.push_back( VECTOR2D( aGLYPHBoundingX.x, 0 ) );
boundingPoints.push_back( VECTOR2D( aGLYPHBoundingX.y, 0 ) );
boundingPoints.emplace_back( VECTOR2D( aGLYPHBoundingX.x, 0 ) );
boundingPoints.emplace_back( VECTOR2D( aGLYPHBoundingX.y, 0 ) );
for( GLYPH::const_iterator pointListIt = aGLYPH.begin(); pointListIt != aGLYPH.end(); ++pointListIt )
{
for( std::deque<VECTOR2D>::const_iterator pointIt = pointListIt->begin();
pointIt != pointListIt->end(); ++pointIt )
{
boundingPoints.push_back( VECTOR2D( aGLYPHBoundingX.x, pointIt->y ) );
boundingPoints.emplace_back( VECTOR2D( aGLYPHBoundingX.x, pointIt->y ) );
}
}

View File

@ -26,7 +26,7 @@
#include <make_unique.h>
#include <utility>
#include <pgm_base.h>
#include <kicad_string.h>
// Each node gets this lowest score initially, without any matches applied.
// Matches will then increase this score depending on match quality. This way,
@ -59,19 +59,29 @@ void LIB_TREE_NODE::ResetScore()
}
void LIB_TREE_NODE::AssignIntrinsicRanks()
void LIB_TREE_NODE::AssignIntrinsicRanks( bool presorted )
{
std::vector<LIB_TREE_NODE*> sort_buf;
for( auto const& node: Children )
sort_buf.push_back( &*node );
if( presorted )
{
int max = Children.size() - 1;
std::sort( sort_buf.begin(), sort_buf.end(),
[]( LIB_TREE_NODE* a, LIB_TREE_NODE* b ) -> bool
{ return a->MatchName > b->MatchName; } );
for( int i = 0; i <= max; ++i )
Children[i]->IntrinsicRank = max - i;
}
else
{
for( auto const& node: Children )
sort_buf.push_back( &*node );
for( int i = 0; i < (int) sort_buf.size(); ++i )
sort_buf[i]->IntrinsicRank = i;
std::sort( sort_buf.begin(), sort_buf.end(),
[]( LIB_TREE_NODE* a, LIB_TREE_NODE* b ) -> bool
{ return StrNumCmp( a->Name, b->Name, INT_MAX, true ) > 0; } );
for( int i = 0; i < (int) sort_buf.size(); ++i )
sort_buf[i]->IntrinsicRank = i;
}
}
@ -108,7 +118,7 @@ LIB_TREE_NODE::LIB_TREE_NODE()
Type( INVALID ),
IntrinsicRank( 0 ),
Score( kLowestDefaultScore ),
SearchTextNormalized( false ),
Normalized( false ),
Unit( 0 ),
IsRoot( false )
{}
@ -146,14 +156,15 @@ LIB_TREE_NODE_LIB_ID::LIB_TREE_NODE_LIB_ID( LIB_TREE_NODE* aParent, LIB_TREE_ITE
Type = LIBID;
Parent = aParent;
LibId = aItem->GetLibId();
LibId.SetLibNickname( aItem->GetLibNickname() );
LibId.SetLibItemName( aItem->GetName () );
Name = aItem->GetName();
Desc = aItem->GetDescription();
MatchName = aItem->GetName().Lower();
MatchName = aItem->GetName();
SearchText = aItem->GetSearchText();
SearchTextNormalized = false;
Normalized = false;
IsRoot = aItem->IsRoot();
@ -182,7 +193,7 @@ void LIB_TREE_NODE_LIB_ID::Update( LIB_TREE_ITEM* aItem )
Desc = aItem->GetDescription();
SearchText = aItem->GetSearchText();
SearchTextNormalized = false;
Normalized = false;
IsRoot = aItem->IsRoot();
Children.clear();
@ -197,10 +208,11 @@ void LIB_TREE_NODE_LIB_ID::UpdateScore( EDA_COMBINED_MATCHER& aMatcher )
if( Score <= 0 )
return; // Leaf nodes without scores are out of the game.
if( !SearchTextNormalized )
if( !Normalized )
{
MatchName = MatchName.Lower();
SearchText = SearchText.Lower();
SearchTextNormalized = true;
Normalized = true;
}
// Keywords and description we only count if the match string is at

View File

@ -98,7 +98,7 @@ public:
wxString Desc; ///< Description to be displayed
wxString MatchName; ///< Normalized name for matching
wxString SearchText; ///< Descriptive text to search
bool SearchTextNormalized; ///< Support for lazy normalization.
bool Normalized; ///< Support for lazy normalization.
LIB_ID LibId; ///< LIB_ID determined by the parent library nickname and alias name.
@ -122,7 +122,7 @@ public:
* Store intrinsic ranks on all children of this node. See IntrinsicRank
* member doc for more information.
*/
void AssignIntrinsicRanks();
void AssignIntrinsicRanks( bool presorted = false );
/**
* Sort child nodes quickly and recursively (IntrinsicRanks must have been set).

View File

@ -107,15 +107,15 @@ void LIB_TREE_MODEL_ADAPTER::SetPreselectNode( LIB_ID const& aLibId, int aUnit )
void LIB_TREE_MODEL_ADAPTER::DoAddLibrary( wxString const& aNodeName, wxString const& aDesc,
std::vector<LIB_TREE_ITEM*> const& aItemList )
std::vector<LIB_TREE_ITEM*> const& aItemList,
bool presorted )
{
auto& lib_node = m_tree.AddLib( aNodeName, aDesc );
for( auto item: aItemList )
lib_node.AddItem( item );
lib_node.AssignIntrinsicRanks();
m_tree.AssignIntrinsicRanks();
lib_node.AssignIntrinsicRanks( presorted );
}

View File

@ -151,7 +151,13 @@ public:
* @param aItemList list of components
*/
void DoAddLibrary( wxString const& aNodeName, wxString const& aDesc,
std::vector<LIB_TREE_ITEM*> const& aItemList );
std::vector<LIB_TREE_ITEM*> const& aItemList, bool presorted );
/**
* Sort the tree and assign ranks after adding libraries.
*/
void AssignIntrinsicRanks() { m_tree.AssignIntrinsicRanks(); }
/**
* Set the search string provided by the user.

View File

@ -70,7 +70,8 @@ wxBitmap COLOR_SWATCH::MakeBitmap( COLOR4D aColor, COLOR4D aBackground, wxSize a
static std::unique_ptr<wxStaticBitmap> makeColorSwatch( wxWindow* aParent, COLOR4D aColor,
COLOR4D aBackground, int aID )
{
wxSize size = aParent->ConvertDialogToPixels( SWATCH_SIZE_DU );
static wxSize size = aParent->ConvertDialogToPixels( SWATCH_SIZE_DU );
wxBitmap bitmap = COLOR_SWATCH::MakeBitmap( aColor, aBackground, size );
auto ret = std::make_unique<wxStaticBitmap>( aParent, aID, bitmap );

View File

@ -145,7 +145,11 @@ wxString LIB_ALIAS::GetUnitReference( int aUnit )
wxString LIB_ALIAS::GetSearchText()
{
wxString text = GetKeyWords() + wxT( " " ) + GetDescription();
// Matches are scored by offset from front of string, so inclusion of this spacer
// discounts matches found after it.
static const wxString discount( wxT( " " ) );
wxString text = GetKeyWords() + discount + GetDescription();
// If a footprint is defined for the part, add it to the serach string
if( shared )
@ -153,7 +157,7 @@ wxString LIB_ALIAS::GetSearchText()
wxString footprint = shared->GetFootprintField().GetText();
if( !footprint.IsEmpty() )
text += wxT( " " ) + footprint;
text += discount + footprint;
}
return text;

View File

@ -123,21 +123,21 @@ public:
description = aDescription;
}
wxString GetDescription() override { return description; }
const wxString& GetDescription() override { return description; }
void SetKeyWords( const wxString& aKeyWords )
{
keyWords = aKeyWords;
}
wxString GetKeyWords() const { return keyWords; }
const wxString& GetKeyWords() const { return keyWords; }
void SetDocFileName( const wxString& aDocFileName )
{
docFileName = aDocFileName;
}
wxString GetDocFileName() const { return docFileName; }
const wxString& GetDocFileName() const { return docFileName; }
wxString GetSearchText() override;

View File

@ -133,6 +133,8 @@ SCH_BASE_FRAME::COMPONENT_SELECTION SCH_BASE_FRAME::SelectComponentFromLibTree(
}
}
adapter->AssignIntrinsicRanks();
if( aFilter->GetFilterPowerParts() )
adapter->SetFilter( SYMBOL_TREE_MODEL_ADAPTER::CMP_FILTER_POWER );
@ -150,7 +152,7 @@ SCH_BASE_FRAME::COMPONENT_SELECTION SCH_BASE_FRAME::SelectComponentFromLibTree(
history_list.push_back( alias );
}
adapter->DoAddLibrary( "-- " + _( "Recently Used" ) + " --", wxEmptyString, history_list );
adapter->DoAddLibrary( "-- " + _( "Recently Used" ) + " --", wxEmptyString, history_list, true );
adapter->SetPreselectNode( aHistoryList[0].LibId, aHistoryList[0].Unit );
}

View File

@ -76,6 +76,8 @@ void SYMBOL_TREE_MODEL_ADAPTER::AddLibraries( const std::vector<wxString>& aNick
ii++;
}
m_tree.AssignIntrinsicRanks();
if( prg )
{
prg->Destroy();
@ -105,8 +107,7 @@ void SYMBOL_TREE_MODEL_ADAPTER::AddLibrary( wxString const& aLibNickname )
if( alias_list.size() > 0 )
{
comp_list.assign( alias_list.begin(), alias_list.end() );
DoAddLibrary( aLibNickname, m_libs->GetDescription( aLibNickname ), comp_list );
m_tree.AssignIntrinsicRanks();
DoAddLibrary( aLibNickname, m_libs->GetDescription( aLibNickname ), comp_list, false );
}
}

View File

@ -102,9 +102,10 @@ void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::Sync( bool aForce, std::function<void(in
auto& lib_node = m_tree.AddLib( libName, library->GetDescr() );
updateLibrary( lib_node );
m_tree.AssignIntrinsicRanks();
}
}
m_tree.AssignIntrinsicRanks();
}

View File

@ -383,65 +383,35 @@ namespace std
/**
* A wrapper around a wxFileName which is much more performant with a (very) limited API.
* A wrapper around a wxFileName which is much more performant with a subset of the API.
*/
class WX_FILENAME
{
public:
WX_FILENAME( const wxString& aPath, const wxString& aFilename ) :
m_fn( aPath, aFilename ),
m_path( aPath ),
m_fullName( aFilename )
{ }
WX_FILENAME( const wxString& aPath, const wxString& 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 );
wxString GetName() const;
wxString GetFullName() const;
wxString GetPath() const;
wxString GetFullPath() const;
// Avoid multiple calls to stat() on POSIX kernels.
long long GetTimestamp();
operator wxFileName() const { return m_fn; }
private:
// Write cached values to the wrapped wxFileName. MUST be called before using m_fn.
void resolve();
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();
long long TimestampDir( const wxString& aDirPath, const wxString& aFilespec );
// 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

View File

@ -92,13 +92,13 @@ public:
return LIB_ID( m_nickname, m_fpname );
}
wxString GetDescription() override
const wxString& GetDescription() override
{
ensure_loaded();
return m_doc;
}
wxString GetKeywords()
const wxString& GetKeywords()
{
ensure_loaded();
return m_keywords;
@ -106,7 +106,11 @@ public:
wxString GetSearchText() override
{
return GetKeywords() + wxT( " " ) + GetDescription();
// Matches are scored by offset from front of string, so inclusion of this spacer
// discounts matches found after it.
static const wxString discount( wxT( " " ) );
return GetKeywords() + discount + GetDescription();
}
unsigned GetPadCount()
@ -154,8 +158,8 @@ protected:
wxString m_nickname; ///< library as known in FP_LIB_TABLE
wxString m_fpname; ///< Module name.
int m_num; ///< Order number in the display list.
int m_pad_count; ///< Number of pads
int m_unique_pad_count; ///< Number of unique pads
unsigned m_pad_count; ///< Number of pads
unsigned m_unique_pad_count; ///< Number of unique pads
wxString m_doc; ///< Footprint description.
wxString m_keywords; ///< Footprint keywords.
};
@ -313,10 +317,8 @@ class APIEXPORT FOOTPRINT_ASYNC_LOADER
friend class FOOTPRINT_LIST_IMPL;
FOOTPRINT_LIST* m_list;
std::function<void()> m_completion_cb;
std::string m_last_table;
bool m_started; ///< True if Start() has been called - does not reset
int m_total_libs;
public:
@ -364,21 +366,6 @@ public:
*/
void Abort();
/**
* Set a callback to receive notice when loading is complete.
*
* Callback MUST be threadsafe, and must be set before calling Start
* if you want to use it (it is safe not to set it at all).
*/
void SetCompletionCallback( std::function<void()> aCallback );
/**
* Return true if the given table is the same as the last table loaded.
* Useful for checking if the table has been modified and needs to be
* reloaded.
*/
bool IsSameTable( FP_LIB_TABLE* aOther );
/**
* Default number of worker threads. Determined empirically (by dickelbeck):
* More than 6 is not significantly faster, less than 6 is likely slower.

View File

@ -44,7 +44,7 @@ public:
virtual const wxString& GetName() const = 0;
virtual wxString GetLibNickname() const = 0;
virtual wxString GetDescription() { return wxEmptyString; }
virtual const wxString& GetDescription() = 0;
virtual wxString GetSearchText() { return wxEmptyString; }

View File

@ -270,12 +270,19 @@ FOOTPRINT_EDIT_FRAME::FOOTPRINT_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent,
initLibraryTree();
m_treePane = new FOOTPRINT_TREE_PANE( this );
ReCreateMenuBar();
// ReCreateMenuBar(); // UseGalCanvas() will do this for us.
ReCreateHToolbar();
ReCreateAuxiliaryToolbar();
ReCreateVToolbar();
ReCreateOptToolbar();
m_Layers->ReFill();
m_Layers->ReFillRender();
GetScreen()->m_Active_Layer = F_SilkS;
m_Layers->SelectLayer( F_SilkS );
m_Layers->OnLayerSelected();
if( m_canvas )
m_canvas->SetEnableBlockCommands( true );
@ -334,16 +341,6 @@ FOOTPRINT_EDIT_FRAME::FOOTPRINT_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent,
GetGalCanvas()->GetGAL()->SetAxesEnabled( true );
UseGalCanvas( aBackend != EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE );
if( m_auimgr.GetPane( "m_LayersManagerToolBar" ).IsShown() )
{
m_Layers->ReFill();
m_Layers->ReFillRender();
GetScreen()->m_Active_Layer = F_SilkS;
m_Layers->SelectLayer( F_SilkS );
m_Layers->OnLayerSelected();
}
m_auimgr.Update();
updateTitle();

View File

@ -49,8 +49,10 @@ void FP_TREE_MODEL_ADAPTER::AddLibraries()
{
const FP_LIB_TABLE_ROW* library = m_libs->FindRow( libName );
DoAddLibrary( libName, library->GetDescr(), getFootprints( libName ) );
DoAddLibrary( libName, library->GetDescr(), getFootprints( libName ), true );
}
m_tree.AssignIntrinsicRanks();
}

View File

@ -69,16 +69,21 @@ void FP_TREE_SYNCHRONIZING_ADAPTER::Sync()
}
// Look for new libraries
size_t count = m_libMap.size();
for( const auto& libName : m_libs->GetLogicalLibs() )
{
if( m_libMap.count( libName ) == 0 )
{
const FP_LIB_TABLE_ROW* library = m_libs->FindRow( libName );
DoAddLibrary( libName, library->GetDescr(), getFootprints( libName ) );
DoAddLibrary( libName, library->GetDescr(), getFootprints( libName ), true );
m_libMap.insert( libName );
}
}
if( m_libMap.size() > count )
m_tree.AssignIntrinsicRanks();
}
@ -92,7 +97,7 @@ void FP_TREE_SYNCHRONIZING_ADAPTER::updateLibrary( LIB_TREE_NODE_LIB& aLibNode )
{
std::vector<LIB_TREE_ITEM*> footprints = getFootprints( aLibNode.Name );
// remove the common part from the aliases list
// remove the common part from the footprints list
for( auto nodeIt = aLibNode.Children.begin(); nodeIt != aLibNode.Children.end(); )
{
// Since the list is sorted we can use a binary search to speed up searches within
@ -119,7 +124,7 @@ void FP_TREE_SYNCHRONIZING_ADAPTER::updateLibrary( LIB_TREE_NODE_LIB& aLibNode )
}
}
// now the aliases list contains only new aliases that need to be added to the tree
// now the footprint list contains only new aliases that need to be added to the tree
for( auto footprint : footprints )
aLibNode.AddItem( footprint );

View File

@ -133,9 +133,9 @@ public:
};
wxString GenerateFootprintInfo( FP_LIB_TABLE* aSymLibTable, LIB_ID const& aLibId )
wxString GenerateFootprintInfo( FP_LIB_TABLE* aFpLibTable, LIB_ID const& aLibId )
{
FOOTPRINT_INFO_GENERATOR gen( aSymLibTable, aLibId );
FOOTPRINT_INFO_GENERATOR gen( aFpLibTable, aLibId );
gen.GenerateHtml();
return gen.GetHtml();
}

View File

@ -108,20 +108,19 @@ static inline long parseInt( const wxString& aValue, double aScalar )
*/
class GPCB_FPL_CACHE_ITEM
{
wxFileName m_file_name; ///< The the full file name and path of the footprint to cache.
WX_FILENAME m_filename; ///< The the full file name and path of the footprint to cache.
std::unique_ptr<MODULE> m_module;
public:
GPCB_FPL_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName );
GPCB_FPL_CACHE_ITEM( MODULE* aModule, const WX_FILENAME& aFileName );
wxString GetName() const { return m_file_name.GetDirs().Last(); }
wxFileName GetFileName() const { return m_file_name; }
MODULE* GetModule() const { return m_module.get(); }
WX_FILENAME GetFileName() const { return m_filename; }
MODULE* GetModule() const { return m_module.get(); }
};
GPCB_FPL_CACHE_ITEM::GPCB_FPL_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName ) :
m_file_name( aFileName ),
GPCB_FPL_CACHE_ITEM::GPCB_FPL_CACHE_ITEM( MODULE* aModule, const WX_FILENAME& aFileName ) :
m_filename( aFileName ),
m_module( aModule )
{
}
@ -225,7 +224,7 @@ void GPCB_FPL_CACHE::Load()
// Note: like our .pretty footprint libraries, the gpcb footprint libraries are folders,
// and the footprints are the .fp files inside this folder.
WX_DIR dir( m_lib_path.GetPath() );
wxDir dir( m_lib_path.GetPath() );
if( !dir.IsOpened() )
{
@ -233,21 +232,21 @@ void GPCB_FPL_CACHE::Load()
m_lib_path.GetPath().GetData() ) );
}
wxString fpFileName;
wxString wildcard = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
wxString fullName;
wxString fileSpec = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
// wxFileName construction is egregiously slow. Construct it once and just swap out
// the filename.
WX_FILENAME fn( m_lib_path.GetPath(), wxT( "dummy." ) + GedaPcbFootprintLibFileExtension );
// the filename thereafter.
WX_FILENAME fn( m_lib_path.GetPath(), wxT( "dummyName" ) );
if( !dir.GetFirst( &fpFileName, wildcard ) )
if( !dir.GetFirst( &fullName, fileSpec ) )
return;
wxString cacheErrorMsg;
do
{
fn.SetFullName( fpFileName );
fn.SetFullName( fullName );
// Queue I/O errors so only files that fail to parse don't get loaded.
try
@ -260,7 +259,7 @@ void GPCB_FPL_CACHE::Load()
// The footprint name is the file name without the extension.
footprint->SetFPID( LIB_ID( wxEmptyString, fn.GetName() ) );
m_modules.insert( name, new GPCB_FPL_CACHE_ITEM( footprint, fn.GetName() ) );
m_modules.insert( name, new GPCB_FPL_CACHE_ITEM( footprint, fn ) );
}
catch( const IO_ERROR& ioe )
{
@ -269,7 +268,7 @@ void GPCB_FPL_CACHE::Load()
cacheErrorMsg += ioe.What();
}
} while( dir.GetNext( &fpFileName ) );
} while( dir.GetNext( &fullName ) );
if( !cacheErrorMsg.IsEmpty() )
THROW_IO_ERROR( cacheErrorMsg );
@ -306,30 +305,9 @@ bool GPCB_FPL_CACHE::IsModified()
long long GPCB_FPL_CACHE::GetTimestamp( const wxString& aLibPath )
{
long long files_timestamp = 0;
wxString fileSpec = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
// wxFileName construction is egregiously slow. Construct it once and just swap out
// the filename.
WX_FILENAME fn( aLibPath, wxT( "dummy." ) + GedaPcbFootprintLibFileExtension );
WX_DIR dir( aLibPath );
if( dir.IsOpened() )
{
wxString fpFileName;
wxString wildcard = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
if( dir.GetFirst( &fpFileName, wildcard ) )
{
do
{
fn.SetFullName( fpFileName );
files_timestamp += fn.GetTimestamp();
} while( dir.GetNext( &fpFileName ) );
}
}
return files_timestamp;
return TimestampDir( aLibPath, fileSpec );
}

View File

@ -85,24 +85,21 @@ void filterNetClass( const BOARD& aBoard, NETCLASS& aNetClass )
*/
class FP_CACHE_ITEM
{
wxFileName m_file_name; ///< The the full file name and path of the footprint to cache.
WX_FILENAME m_filename;
std::unique_ptr<MODULE> m_module;
public:
FP_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName );
FP_CACHE_ITEM( MODULE* aModule, const WX_FILENAME& aFileName );
const wxString& GetName() const { return m_file_name.GetDirs().Last(); }
const wxFileName& GetFileName() const { return m_file_name; }
const MODULE* GetModule() const { return m_module.get(); }
const WX_FILENAME& GetFileName() const { return m_filename; }
const MODULE* GetModule() const { return m_module.get(); }
};
FP_CACHE_ITEM::FP_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName ) :
FP_CACHE_ITEM::FP_CACHE_ITEM( MODULE* aModule, const WX_FILENAME& aFileName ) :
m_filename( aFileName ),
m_module( aModule )
{
m_file_name = aFileName;
}
{ }
typedef boost::ptr_map< wxString, FP_CACHE_ITEM > MODULE_MAP;
@ -207,11 +204,11 @@ void FP_CACHE::Save( MODULE* aModule )
if( aModule && aModule != it->second->GetModule() )
continue;
wxFileName fn = it->second->GetFileName();
WX_FILENAME fn = it->second->GetFileName();
wxString tempFileName =
#ifdef USE_TMP_FILE
fn.CreateTempFileName( fn.GetPath() );
wxFileName::CreateTempFileName( fn.GetPath() );
#else
fn.GetFullPath();
#endif
@ -244,7 +241,7 @@ void FP_CACHE::Save( MODULE* aModule )
THROW_IO_ERROR( msg );
}
#endif
m_cache_timestamp += fn.GetModificationTime().GetValue().GetValue();
m_cache_timestamp += fn.GetTimestamp();
}
m_cache_timestamp += m_lib_path.GetModificationTime().GetValue().GetValue();
@ -260,7 +257,7 @@ void FP_CACHE::Load()
m_cache_dirty = false;
m_cache_timestamp = 0;
WX_DIR dir( m_lib_raw_path );
wxDir dir( m_lib_raw_path );
if( !dir.IsOpened() )
{
@ -269,20 +266,20 @@ void FP_CACHE::Load()
THROW_IO_ERROR( msg );
}
wxString fpFileName;
wxString wildcard = wxT( "*." ) + KiCadFootprintFileExtension;
wxString fullName;
wxString fileSpec = wxT( "*." ) + KiCadFootprintFileExtension;
// wxFileName construction is egregiously slow. Construct it once and just swap out
// the filename.
WX_FILENAME fn( m_lib_raw_path, wxT( "dummy." ) + KiCadFootprintFileExtension );
// the filename thereafter.
WX_FILENAME fn( m_lib_raw_path, wxT( "dummyName" ) );
if( dir.GetFirst( &fpFileName, wildcard ) )
if( dir.GetFirst( &fullName, fileSpec ) )
{
wxString cacheError;
do
{
fn.SetFullName( fpFileName );
fn.SetFullName( fullName );
// Queue I/O errors so only files that fail to parse don't get loaded.
try
@ -292,8 +289,6 @@ void FP_CACHE::Load()
m_owner->m_parser->SetLineReader( &reader );
MODULE* footprint = (MODULE*) m_owner->m_parser->Parse();
// The footprint name is the file name without the extension.
wxString fpName = fn.GetName();
footprint->SetFPID( LIB_ID( wxEmptyString, fpName ) );
@ -308,7 +303,7 @@ void FP_CACHE::Load()
cacheError += ioe.What();
}
} while( dir.GetNext( &fpFileName ) );
} while( dir.GetNext( &fullName ) );
if( !cacheError.IsEmpty() )
THROW_IO_ERROR( cacheError );
@ -351,30 +346,9 @@ bool FP_CACHE::IsModified()
long long FP_CACHE::GetTimestamp( const wxString& aLibPath )
{
long long files_timestamp = 0;
wxString fileSpec = wxT( "*." ) + KiCadFootprintFileExtension;
// wxFileName construction is egregiously slow. Construct it once and just
// swap out the filename for each file.
WX_FILENAME fn( aLibPath, wxT( "dummy." ) + KiCadFootprintFileExtension );
WX_DIR dir( aLibPath );
if( dir.IsOpened() )
{
wxString fpFileName;
wxString wildcard = wxT( "*." ) + KiCadFootprintFileExtension;
if( dir.GetFirst( &fpFileName, wildcard ) )
{
do
{
fn.SetFullName( fpFileName );
files_timestamp += fn.GetTimestamp();
} while( dir.GetNext( &fpFileName ) );
}
}
return files_timestamp;
return TimestampDir( aLibPath, fileSpec );
}
@ -2120,14 +2094,15 @@ void PCB_IO::FootprintSave( const wxString& aLibraryPath, const MODULE* aFootpri
fn.GetFullPath() ) );
}
wxString fullPath = fn.GetFullPath();
wxString fullName = fn.GetFullName();
MODULE_CITER it = mods.find( footprintName );
if( it != mods.end() )
{
wxLogTrace( traceKicadPcbPlugin, wxT( "Removing footprint library file '%s'." ),
fn.GetFullPath().GetData() );
wxLogTrace( traceKicadPcbPlugin, wxT( "Removing footprint file '%s'." ), fullPath );
mods.erase( footprintName );
wxRemoveFile( fn.GetFullPath() );
wxRemoveFile( fullPath );
}
// I need my own copy for the cache
@ -2142,9 +2117,8 @@ void PCB_IO::FootprintSave( const wxString& aLibraryPath, const MODULE* aFootpri
if( module->GetLayer() != F_Cu )
module->Flip( module->GetPosition() );
wxLogTrace( traceKicadPcbPlugin, wxT( "Creating s-expression footprint file: %s." ),
fn.GetFullPath().GetData() );
mods.insert( footprintName, new FP_CACHE_ITEM( module, fn ) );
wxLogTrace( traceKicadPcbPlugin, wxT( "Creating s-expr footprint file '%s'." ), fullPath );
mods.insert( footprintName, new FP_CACHE_ITEM( module, WX_FILENAME( fullPath, fullName ) ) );
m_cache->Save( module );
}

View File

@ -207,7 +207,7 @@ MODULE* PCB_BASE_FRAME::SelectFootprintFromLibTree( bool aAllowBrowser )
for( auto const& item : s_ModuleHistoryList )
historyInfos.push_back( GFootprintList.GetModuleInfo( item ) );
adapter->DoAddLibrary( "-- " + _( "Recently Used" ) + " --", wxEmptyString, historyInfos );
adapter->DoAddLibrary( "-- " + _( "Recently Used" ) + " --", wxEmptyString, historyInfos, true );
if( !historyInfos.empty() )
adapter->SetPreselectNode( historyInfos[0]->GetLibId(), 0 );

View File

@ -113,7 +113,6 @@ PCB_LAYER_WIDGET::PCB_LAYER_WIDGET( PCB_BASE_FRAME* aParent, wxWindow* aFocusOwn
{
m_alwaysShowActiveCopperLayer = false;
m_fp_editor_mode = aFpEditorMode;
ReFillRender();
// Update default tabs labels
SetLayersManagerTabsText();