From 829c23643719c269a3d731ef31c7cb5965c946a5 Mon Sep 17 00:00:00 2001
From: Jeff Young <jeff@rokeby.ie>
Date: Thu, 25 Jan 2018 23:49:04 +0000
Subject: [PATCH] Symbol library load performance enhancements.

1) Edit Symbol gets similar fix as Place Symbol
2) progress dialog updating reduced to once every 50ms
3) SearchText gets lazy normalization
4) TypeNames get lazy translation
5) default fieldNames get translated a single time per language change

These fixes reduce first-load-time of both Edit Symbol and Place Symbol
by about 2/3, and second-load-time of Edit Symbol to near-instantaneous.
---
 eeschema/cmp_tree_model.cpp      | 24 +++++++++++++++++++++---
 eeschema/cmp_tree_model.h        |  2 ++
 eeschema/lib_arc.cpp             |  1 -
 eeschema/lib_arc.h               |  5 +++++
 eeschema/lib_bezier.cpp          |  1 -
 eeschema/lib_bezier.h            |  4 ++++
 eeschema/lib_circle.cpp          |  1 -
 eeschema/lib_circle.h            |  4 ++++
 eeschema/lib_draw_item.cpp       |  3 +--
 eeschema/lib_draw_item.h         |  8 +++++---
 eeschema/lib_field.cpp           |  2 --
 eeschema/lib_field.h             |  5 +++++
 eeschema/lib_manager.cpp         | 13 +++++++------
 eeschema/lib_manager_adapter.cpp | 18 ++++++++++++++++--
 eeschema/lib_pin.cpp             |  1 -
 eeschema/lib_pin.h               |  5 +++++
 eeschema/lib_polyline.cpp        |  1 -
 eeschema/lib_polyline.h          |  4 ++++
 eeschema/lib_rectangle.cpp       |  1 -
 eeschema/lib_rectangle.h         |  5 +++++
 eeschema/lib_text.cpp            |  1 -
 eeschema/lib_text.h              |  5 +++++
 eeschema/template_fieldnames.cpp | 31 ++++++++++++++++++++++++++-----
 23 files changed, 115 insertions(+), 30 deletions(-)

diff --git a/eeschema/cmp_tree_model.cpp b/eeschema/cmp_tree_model.cpp
index 605e44b823..a8308fbd43 100644
--- a/eeschema/cmp_tree_model.cpp
+++ b/eeschema/cmp_tree_model.cpp
@@ -25,6 +25,7 @@
 #include <eda_pattern_match.h>
 #include <make_unique.h>
 #include <utility>
+#include <pgm_base.h>
 
 
 // Each node gets this lowest score initially, without any matches applied.
@@ -114,13 +115,24 @@ CMP_TREE_NODE::CMP_TREE_NODE()
 
 CMP_TREE_NODE_UNIT::CMP_TREE_NODE_UNIT( CMP_TREE_NODE* aParent, int aUnit )
 {
+    static void* locale = nullptr;
+    static wxString namePrefix;
+
+    // Fetching translations can take a surprising amount of time when loading libraries,
+    // so only do it when necessary.
+    if( Pgm().GetLocale() != locale )
+    {
+        namePrefix = _( "Unit" );
+        locale = Pgm().GetLocale();
+    }
+
     Parent = aParent;
     Type = UNIT;
 
     Unit = aUnit;
     LibId = aParent->LibId;
 
-    Name = _( "Unit" ) + " " + LIB_PART::SubReference( aUnit, false );
+    Name = namePrefix + " " + LIB_PART::SubReference( aUnit, false );
     Desc = wxEmptyString;
     MatchName = wxEmptyString;
 
@@ -158,7 +170,7 @@ void CMP_TREE_NODE_LIB_ID::Update( LIB_ALIAS* aAlias )
     // Search text spaces out keywords and description to penalize description
     // matches - earlier matches are worth more.
     MatchName   = aAlias->GetName().Lower();
-    SearchText  = (aAlias->GetKeyWords() + "        " + Desc).Lower();
+    SearchText  = (aAlias->GetKeyWords() + "        " + Desc);
 
     // Extract default footprint text
     LIB_PART* part = aAlias->GetPart();
@@ -177,7 +189,7 @@ void CMP_TREE_NODE_LIB_ID::Update( LIB_ALIAS* aAlias )
     if( !footprint.IsEmpty() )
     {
         SearchText += "        ";
-        SearchText += footprint.Lower();
+        SearchText += footprint;
     }
 
     Children.clear();
@@ -195,6 +207,12 @@ void CMP_TREE_NODE_LIB_ID::UpdateScore( EDA_COMBINED_MATCHER& aMatcher )
     if( Score <= 0 )
         return; // Leaf nodes without scores are out of the game.
 
+    if( !SearchTextNormalized )
+    {
+        SearchText = SearchText.Lower();
+        SearchTextNormalized = true;
+    }
+
     // Keywords and description we only count if the match string is at
     // least two characters long. That avoids spurious, low quality
     // matches. Most abbreviations are at three characters long.
diff --git a/eeschema/cmp_tree_model.h b/eeschema/cmp_tree_model.h
index e9e04cb7e5..30788e13d0 100644
--- a/eeschema/cmp_tree_model.h
+++ b/eeschema/cmp_tree_model.h
@@ -100,6 +100,8 @@ 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.
+
 
     LIB_ID      LibId;       ///< LIB_ID determined by the parent library nickname and alias name.
     int         Unit;        ///< Actual unit, or zero
diff --git a/eeschema/lib_arc.cpp b/eeschema/lib_arc.cpp
index 0b5531e6db..88d8edf97a 100644
--- a/eeschema/lib_arc.cpp
+++ b/eeschema/lib_arc.cpp
@@ -92,7 +92,6 @@ LIB_ARC::LIB_ARC( LIB_PART*      aParent ) : LIB_ITEM( LIB_ARC_T, aParent )
     m_Width         = 0;
     m_Fill          = NO_FILL;
     m_isFillable    = true;
-    m_typeName      = _( "Arc" );
     m_editState     = 0;
     m_lastEditState = 0;
     m_editCenterDistance = 0.0;
diff --git a/eeschema/lib_arc.h b/eeschema/lib_arc.h
index 9bd050b98b..886d464d15 100644
--- a/eeschema/lib_arc.h
+++ b/eeschema/lib_arc.h
@@ -91,6 +91,11 @@ public:
         return wxT( "LIB_ARC" );
     }
 
+    wxString GetTypeName() override
+    {
+        return _( "Arc" );
+    }
+
     bool HitTest( const wxPoint& aPosition ) const override;
 
     bool HitTest( const wxPoint& aPosition, int aThreshold, const TRANSFORM& aTransform ) const override;
diff --git a/eeschema/lib_bezier.cpp b/eeschema/lib_bezier.cpp
index 2039049e76..916b610483 100644
--- a/eeschema/lib_bezier.cpp
+++ b/eeschema/lib_bezier.cpp
@@ -47,7 +47,6 @@ LIB_BEZIER::LIB_BEZIER( LIB_PART* aParent ) :
     m_Fill       = NO_FILL;
     m_Width      = 0;
     m_isFillable = true;
-    m_typeName   = _( "Bezier" );
 }
 
 
diff --git a/eeschema/lib_bezier.h b/eeschema/lib_bezier.h
index 7e649d59de..919a7f37f1 100644
--- a/eeschema/lib_bezier.h
+++ b/eeschema/lib_bezier.h
@@ -58,6 +58,10 @@ public:
         return wxT( "LIB_BEZIER" );
     }
 
+    wxString GetTypeName() override
+    {
+        return _( "Bezier" );
+    }
 
     void AddPoint( const wxPoint& aPoint ) { m_BezierPoints.push_back( aPoint ); }
 
diff --git a/eeschema/lib_circle.cpp b/eeschema/lib_circle.cpp
index 2ba1570083..e2f1bd31eb 100644
--- a/eeschema/lib_circle.cpp
+++ b/eeschema/lib_circle.cpp
@@ -50,7 +50,6 @@ LIB_CIRCLE::LIB_CIRCLE( LIB_PART*      aParent ) :
     m_Width      = 0;
     m_Fill       = NO_FILL;
     m_isFillable = true;
-    m_typeName   = _( "Circle" );
 }
 
 
diff --git a/eeschema/lib_circle.h b/eeschema/lib_circle.h
index 41c02794ed..1a2740285d 100644
--- a/eeschema/lib_circle.h
+++ b/eeschema/lib_circle.h
@@ -56,6 +56,10 @@ public:
         return wxT( "LIB_CIRCLE" );
     }
 
+    wxString GetTypeName() override
+    {
+        return _( "Circle" );
+    }
 
     bool HitTest( const wxPoint& aPosition ) const override;
 
diff --git a/eeschema/lib_draw_item.cpp b/eeschema/lib_draw_item.cpp
index c9e16b0e06..efd9986af4 100644
--- a/eeschema/lib_draw_item.cpp
+++ b/eeschema/lib_draw_item.cpp
@@ -52,7 +52,6 @@ LIB_ITEM::LIB_ITEM( KICAD_T        aType,
     m_Convert           = aConvert;
     m_Fill              = aFillType;
     m_Parent            = (EDA_ITEM*) aComponent;
-    m_typeName          = _( "Undefined" );
     m_isFillable        = false;
     m_eraseLastDrawItem = false;
 }
@@ -62,7 +61,7 @@ void LIB_ITEM::GetMsgPanelInfo( MSG_PANEL_ITEMS& aList )
 {
     wxString msg;
 
-    aList.push_back( MSG_PANEL_ITEM( _( "Type" ), m_typeName, CYAN ) );
+    aList.push_back( MSG_PANEL_ITEM( _( "Type" ), GetTypeName(), CYAN ) );
 
     if( m_Unit == 0 )
         msg = _( "All" );
diff --git a/eeschema/lib_draw_item.h b/eeschema/lib_draw_item.h
index 650c4f8a50..28549177ba 100644
--- a/eeschema/lib_draw_item.h
+++ b/eeschema/lib_draw_item.h
@@ -128,8 +128,6 @@ protected:
      */
     FILL_T   m_Fill;
 
-    wxString m_typeName;          ///< Name of object displayed in the message panel.
-
     wxPoint  m_initialPos;        ///< Temporary position when moving an existing item.
     wxPoint  m_initialCursorPos;  ///< Initial cursor position at the beginning of a move.
 
@@ -148,7 +146,11 @@ public:
 
     virtual ~LIB_ITEM() { }
 
-    wxString GetTypeName() { return m_typeName; }
+    /**
+     * Provide a user-consumable name of the object type.  Perform localization when
+     * called so that run-time language selection works.
+     */
+    virtual wxString GetTypeName() = 0;
 
     /**
      * Begin an editing a component library draw item in \a aEditMode at \a aPosition.
diff --git a/eeschema/lib_field.cpp b/eeschema/lib_field.cpp
index d8fef90fb2..1260fbb7f3 100644
--- a/eeschema/lib_field.cpp
+++ b/eeschema/lib_field.cpp
@@ -84,8 +84,6 @@ void LIB_FIELD::Init( int id )
     SetTextWidth( GetDefaultTextSize() );
     SetTextHeight( GetDefaultTextSize() );
 
-    m_typeName = _( "Field" );
-
     SetTextAngle( TEXT_ANGLE_HORIZ );    // constructor already did this.
 
     m_rotate = false;
diff --git a/eeschema/lib_field.h b/eeschema/lib_field.h
index e64af1e067..f622729ed1 100644
--- a/eeschema/lib_field.h
+++ b/eeschema/lib_field.h
@@ -100,6 +100,11 @@ public:
         return wxT( "LIB_FIELD" );
     }
 
+    wxString GetTypeName() override
+    {
+        return _( "Field" );
+    }
+
     /**
      * Object constructor initialization helper.
      */
diff --git a/eeschema/lib_manager.cpp b/eeschema/lib_manager.cpp
index e9ec9a6c75..fc82ed2f42 100644
--- a/eeschema/lib_manager.cpp
+++ b/eeschema/lib_manager.cpp
@@ -288,14 +288,15 @@ std::list<LIB_ALIAS*> LIB_MANAGER::GetAliases( const wxString& aLibrary ) const
     }
     else
     {
-        wxArrayString symbols;
+        std::vector<LIB_ALIAS*> aliases;
 
-        try {
-            symTable()->EnumerateSymbolLib( aLibrary, symbols );
+        try
+        {
+            symTable()->LoadSymbolLib( aliases, aLibrary );
+        }
+        catch( IO_ERROR& ) {}
 
-            for( const auto& symbol : symbols )
-                ret.push_back( symTable()->LoadSymbol( aLibrary, symbol ) );
-        } catch( IO_ERROR& e ) {}
+        std::copy( aliases.begin(), aliases.end(), std::back_inserter( ret ) );
     }
 
     return ret;
diff --git a/eeschema/lib_manager_adapter.cpp b/eeschema/lib_manager_adapter.cpp
index 2a01db656f..0f753bb6cc 100644
--- a/eeschema/lib_manager_adapter.cpp
+++ b/eeschema/lib_manager_adapter.cpp
@@ -54,8 +54,12 @@ bool LIB_MANAGER_ADAPTER::IsContainer( const wxDataViewItem& aItem ) const
 }
 
 
+#define PROGRESS_INTERVAL_MILLIS 50
+
 void LIB_MANAGER_ADAPTER::Sync( bool aForce, std::function<void(int, int, const wxString&)> aProgressCallback )
 {
+    wxLongLong nextUpdate = wxGetUTCTimeMillis() + PROGRESS_INTERVAL_MILLIS;
+
     int libMgrHash = m_libMgr->GetHash();
 
     if( !aForce && m_lastSyncHash == libMgrHash )
@@ -68,7 +72,12 @@ void LIB_MANAGER_ADAPTER::Sync( bool aForce, std::function<void(int, int, const
     for( auto it = m_tree.Children.begin(); it != m_tree.Children.end(); /* iteration inside */ )
     {
         const wxString& name = it->get()->Name;
-        aProgressCallback( i++, max, name );
+
+        if( wxGetUTCTimeMillis() > nextUpdate )
+        {
+            aProgressCallback( i++, max, name );
+            nextUpdate = wxGetUTCTimeMillis() + PROGRESS_INTERVAL_MILLIS;
+        }
 
         if( !m_libMgr->LibraryExists( name ) )
         {
@@ -88,7 +97,12 @@ void LIB_MANAGER_ADAPTER::Sync( bool aForce, std::function<void(int, int, const
     {
         if( m_libHashes.count( libName ) == 0 )
         {
-            aProgressCallback( i++, max, libName );
+            if( wxGetUTCTimeMillis() > nextUpdate )
+            {
+                aProgressCallback( i++, max, libName );
+                nextUpdate = wxGetUTCTimeMillis() + PROGRESS_INTERVAL_MILLIS;
+            }
+
             auto& lib_node = m_tree.AddLib( libName );
             updateLibrary( lib_node );
             m_tree.AssignIntrinsicRanks();
diff --git a/eeschema/lib_pin.cpp b/eeschema/lib_pin.cpp
index 8af8d385d6..52ef34c826 100644
--- a/eeschema/lib_pin.cpp
+++ b/eeschema/lib_pin.cpp
@@ -154,7 +154,6 @@ LIB_PIN::LIB_PIN( LIB_PART*      aParent ) :
     m_numTextSize = LIB_EDIT_FRAME::GetPinNumDefaultSize();
     m_nameTextSize = LIB_EDIT_FRAME::GetPinNameDefaultSize();
     m_width = 0;
-    m_typeName = _( "Pin" );
 }
 
 
diff --git a/eeschema/lib_pin.h b/eeschema/lib_pin.h
index d3dfda010a..79605da585 100644
--- a/eeschema/lib_pin.h
+++ b/eeschema/lib_pin.h
@@ -110,6 +110,11 @@ public:
         return wxT( "LIB_PIN" );
     }
 
+    wxString GetTypeName() override
+    {
+        return _( "Pin" );
+    }
+
 #if defined(DEBUG)
     void Show( int nestLevel, std::ostream& os ) const override;
 #endif
diff --git a/eeschema/lib_polyline.cpp b/eeschema/lib_polyline.cpp
index 28b61daa18..761688a54d 100644
--- a/eeschema/lib_polyline.cpp
+++ b/eeschema/lib_polyline.cpp
@@ -48,7 +48,6 @@ LIB_POLYLINE::LIB_POLYLINE( LIB_PART*      aParent ) :
     m_Fill  = NO_FILL;
     m_Width = 0;
     m_isFillable = true;
-    m_typeName   = _( "PolyLine" );
     m_ModifyIndex = 0;
 }
 
diff --git a/eeschema/lib_polyline.h b/eeschema/lib_polyline.h
index f7ae53950d..2a6bba10be 100644
--- a/eeschema/lib_polyline.h
+++ b/eeschema/lib_polyline.h
@@ -57,6 +57,10 @@ public:
         return wxT( "LIB_POLYLINE" );
     }
 
+    wxString GetTypeName() override
+    {
+        return _( "PolyLine" );
+    }
 
     void AddPoint( const wxPoint& aPoint );
 
diff --git a/eeschema/lib_rectangle.cpp b/eeschema/lib_rectangle.cpp
index 1be9cfa561..e1a92fbd29 100644
--- a/eeschema/lib_rectangle.cpp
+++ b/eeschema/lib_rectangle.cpp
@@ -48,7 +48,6 @@ LIB_RECTANGLE::LIB_RECTANGLE( LIB_PART*      aParent ) :
     m_Width                = 0;
     m_Fill                 = NO_FILL;
     m_isFillable           = true;
-    m_typeName             = _( "Rectangle" );
     m_isHeightLocked       = false;
     m_isWidthLocked        = false;
     m_isStartPointSelected = false;
diff --git a/eeschema/lib_rectangle.h b/eeschema/lib_rectangle.h
index 3909c41027..5c85b7931b 100644
--- a/eeschema/lib_rectangle.h
+++ b/eeschema/lib_rectangle.h
@@ -59,6 +59,11 @@ public:
         return wxT( "LIB_RECTANGLE" );
     }
 
+    wxString GetTypeName() override
+    {
+        return _( "Rectangle" );
+    }
+
     void SetEndPosition( const wxPoint& aPosition ) { m_End = aPosition; }
 
     bool HitTest( const wxPoint& aPosition ) const override;
diff --git a/eeschema/lib_text.cpp b/eeschema/lib_text.cpp
index 671f21e188..ba29349124 100644
--- a/eeschema/lib_text.cpp
+++ b/eeschema/lib_text.cpp
@@ -48,7 +48,6 @@ LIB_TEXT::LIB_TEXT( LIB_PART * aParent ) :
     EDA_TEXT()
 {
     SetTextSize( wxSize( 50, 50 ) );
-    m_typeName   = _( "Text" );
     m_rotate     = false;
     m_updateText = false;
 }
diff --git a/eeschema/lib_text.h b/eeschema/lib_text.h
index 42096ab041..bad432168d 100644
--- a/eeschema/lib_text.h
+++ b/eeschema/lib_text.h
@@ -65,6 +65,11 @@ public:
         return wxT( "LIB_TEXT" );
     }
 
+    wxString GetTypeName() override
+    {
+        return _( "Text" );
+    }
+
     /**
      * Sets the text item string to \a aText.
      *
diff --git a/eeschema/template_fieldnames.cpp b/eeschema/template_fieldnames.cpp
index 01fbaec352..1505fd7100 100644
--- a/eeschema/template_fieldnames.cpp
+++ b/eeschema/template_fieldnames.cpp
@@ -26,33 +26,54 @@
 #include <dsnlexer.h>
 #include <fctsys.h>
 #include <macros.h>
+#include <pgm_base.h>
 
 using namespace TFIELD_T;
 
+
 const wxString TEMPLATE_FIELDNAME::GetDefaultFieldName( int aFieldNdx )
 {
+    static void* locale = nullptr;
+    static wxString referenceDefault;
+    static wxString valueDefault;
+    static wxString footprintDefault;
+    static wxString datasheetDefault;
+    static wxString fieldDefault;
+
+    // Fetching translations can take a surprising amount of time when loading libraries,
+    // so only do it when necessary.
+    if( Pgm().GetLocale() != locale )
+    {
+        referenceDefault = _( "Reference" );
+        valueDefault     = _( "Value" );
+        footprintDefault = _( "Footprint" );
+        datasheetDefault = _( "Datasheet" );
+        fieldDefault     = _( "Field" );
+        locale = Pgm().GetLocale();
+    }
+
     // Fixed values for the first few default fields used by EESCHEMA
     // (mandatory fields)
     switch( aFieldNdx )
     {
     case  REFERENCE:
-        return _( "Reference" );   // The component reference, R1, C1, etc.
+        return referenceDefault;   // The component reference, R1, C1, etc.
 
     case  VALUE:
-        return _( "Value" );       // The component value + name
+        return valueDefault;       // The component value + name
 
     case  FOOTPRINT:
-        return _( "Footprint" );   // The footprint for use with Pcbnew
+        return footprintDefault;   // The footprint for use with Pcbnew
 
     case  DATASHEET:
-        return _( "Datasheet" );   // Link to a datasheet for component
+        return datasheetDefault;   // Link to a datasheet for component
 
     default:
         break;
     }
 
     // Other fields are use fields, give a default name:
-    wxString fieldName = _( "Field" );
+    wxString fieldName = fieldDefault;
     fieldName << aFieldNdx;
     return fieldName;
 }