From be1d6113d6a81bc414e06c5d080cc4917f5de1f1 Mon Sep 17 00:00:00 2001 From: Jeff Young <jeff@rokeby.ie> Date: Sun, 5 Aug 2018 12:56:02 +0100 Subject: [PATCH] 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. --- common/common.cpp | 297 ++++++++++++------ common/footprint_info.cpp | 23 -- common/gal/stroke_font.cpp | 6 +- common/lib_tree_model.cpp | 44 ++- common/lib_tree_model.h | 4 +- common/lib_tree_model_adapter.cpp | 6 +- common/lib_tree_model_adapter.h | 8 +- common/widgets/color_swatch.cpp | 3 +- eeschema/class_libentry.cpp | 8 +- eeschema/class_libentry.h | 6 +- eeschema/getpart.cpp | 4 +- eeschema/symbol_tree_model_adapter.cpp | 5 +- .../symbol_tree_synchronizing_adapter.cpp | 3 +- include/common.h | 52 +-- include/footprint_info.h | 31 +- include/lib_tree_item.h | 2 +- pcbnew/footprint_edit_frame.cpp | 19 +- pcbnew/fp_tree_model_adapter.cpp | 4 +- pcbnew/fp_tree_synchronizing_adapter.cpp | 11 +- pcbnew/generate_footprint_info.cpp | 4 +- pcbnew/gpcb_plugin.cpp | 56 +--- pcbnew/kicad_plugin.cpp | 78 ++--- pcbnew/load_select_footprint.cpp | 2 +- pcbnew/pcb_layer_widget.cpp | 1 - 24 files changed, 356 insertions(+), 321 deletions(-) diff --git a/common/common.cpp b/common/common.cpp index deb07d44b2..b3490b2d8f 100644 --- a/common/common.cpp +++ b/common/common.cpp @@ -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; +} + + diff --git a/common/footprint_info.cpp b/common/footprint_info.cpp index f094819a9c..2e2128dcb5 100644 --- a/common/footprint_info.cpp +++ b/common/footprint_info.cpp @@ -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(); -} diff --git a/common/gal/stroke_font.cpp b/common/gal/stroke_font.cpp index 1bda0204d8..ec7f64ea78 100644 --- a/common/gal/stroke_font.cpp +++ b/common/gal/stroke_font.cpp @@ -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 ) ); } } diff --git a/common/lib_tree_model.cpp b/common/lib_tree_model.cpp index 68fe2bb75d..5356295c1e 100644 --- a/common/lib_tree_model.cpp +++ b/common/lib_tree_model.cpp @@ -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 diff --git a/common/lib_tree_model.h b/common/lib_tree_model.h index ee9c724f24..970cc0dfa5 100644 --- a/common/lib_tree_model.h +++ b/common/lib_tree_model.h @@ -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). diff --git a/common/lib_tree_model_adapter.cpp b/common/lib_tree_model_adapter.cpp index b568538ddd..098a27dd57 100644 --- a/common/lib_tree_model_adapter.cpp +++ b/common/lib_tree_model_adapter.cpp @@ -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 ); } diff --git a/common/lib_tree_model_adapter.h b/common/lib_tree_model_adapter.h index e3253c2975..fc1b74bed8 100644 --- a/common/lib_tree_model_adapter.h +++ b/common/lib_tree_model_adapter.h @@ -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. diff --git a/common/widgets/color_swatch.cpp b/common/widgets/color_swatch.cpp index 8f8c3aeaf7..7890f13614 100644 --- a/common/widgets/color_swatch.cpp +++ b/common/widgets/color_swatch.cpp @@ -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 ); diff --git a/eeschema/class_libentry.cpp b/eeschema/class_libentry.cpp index ff23a48cfb..5ac865ca36 100644 --- a/eeschema/class_libentry.cpp +++ b/eeschema/class_libentry.cpp @@ -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; diff --git a/eeschema/class_libentry.h b/eeschema/class_libentry.h index c7238ea92e..42c0bfb7a0 100644 --- a/eeschema/class_libentry.h +++ b/eeschema/class_libentry.h @@ -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; diff --git a/eeschema/getpart.cpp b/eeschema/getpart.cpp index 96476ed1f3..09e5b9ee85 100644 --- a/eeschema/getpart.cpp +++ b/eeschema/getpart.cpp @@ -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 ); } diff --git a/eeschema/symbol_tree_model_adapter.cpp b/eeschema/symbol_tree_model_adapter.cpp index 853e4dc335..c39b53add3 100644 --- a/eeschema/symbol_tree_model_adapter.cpp +++ b/eeschema/symbol_tree_model_adapter.cpp @@ -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 ); } } diff --git a/eeschema/symbol_tree_synchronizing_adapter.cpp b/eeschema/symbol_tree_synchronizing_adapter.cpp index 9161ffb4d6..13e72674dd 100644 --- a/eeschema/symbol_tree_synchronizing_adapter.cpp +++ b/eeschema/symbol_tree_synchronizing_adapter.cpp @@ -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(); } diff --git a/include/common.h b/include/common.h index eabedc0ccb..56f1a19b94 100644 --- a/include/common.h +++ b/include/common.h @@ -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 diff --git a/include/footprint_info.h b/include/footprint_info.h index 7425a368c3..9bf70f9168 100644 --- a/include/footprint_info.h +++ b/include/footprint_info.h @@ -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. diff --git a/include/lib_tree_item.h b/include/lib_tree_item.h index 9786cf3e04..5ad5ef4bd8 100644 --- a/include/lib_tree_item.h +++ b/include/lib_tree_item.h @@ -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; } diff --git a/pcbnew/footprint_edit_frame.cpp b/pcbnew/footprint_edit_frame.cpp index cb0ab9599b..2de4d2f527 100644 --- a/pcbnew/footprint_edit_frame.cpp +++ b/pcbnew/footprint_edit_frame.cpp @@ -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(); diff --git a/pcbnew/fp_tree_model_adapter.cpp b/pcbnew/fp_tree_model_adapter.cpp index e494e674c9..e0291c62d2 100644 --- a/pcbnew/fp_tree_model_adapter.cpp +++ b/pcbnew/fp_tree_model_adapter.cpp @@ -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(); } diff --git a/pcbnew/fp_tree_synchronizing_adapter.cpp b/pcbnew/fp_tree_synchronizing_adapter.cpp index 14699a96d7..012317c3b3 100644 --- a/pcbnew/fp_tree_synchronizing_adapter.cpp +++ b/pcbnew/fp_tree_synchronizing_adapter.cpp @@ -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 ); diff --git a/pcbnew/generate_footprint_info.cpp b/pcbnew/generate_footprint_info.cpp index 90a4e1b582..d3ef286498 100644 --- a/pcbnew/generate_footprint_info.cpp +++ b/pcbnew/generate_footprint_info.cpp @@ -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(); } diff --git a/pcbnew/gpcb_plugin.cpp b/pcbnew/gpcb_plugin.cpp index 0cabd89b40..781eadc946 100644 --- a/pcbnew/gpcb_plugin.cpp +++ b/pcbnew/gpcb_plugin.cpp @@ -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 ); } diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index 13bad6dcd9..9b66fb4c6c 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -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 ); } diff --git a/pcbnew/load_select_footprint.cpp b/pcbnew/load_select_footprint.cpp index 455f79009b..ce9e481059 100644 --- a/pcbnew/load_select_footprint.cpp +++ b/pcbnew/load_select_footprint.cpp @@ -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 ); diff --git a/pcbnew/pcb_layer_widget.cpp b/pcbnew/pcb_layer_widget.cpp index 70a97cc190..826b1aa6d5 100644 --- a/pcbnew/pcb_layer_widget.cpp +++ b/pcbnew/pcb_layer_widget.cpp @@ -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();