← Back to team overview

kicad-developers team mailing list archive

Re: [PATCH] Add live footprint filtering in modview window

 

Wayne,

Patch set now goes to 0009 (all patches re-attached to this email).

Most recent patch adds the following:

- Busy cursor while loading each library (otherwise it feels like KiCad is
just sleepy)
- Further nullptr checks, trying to fix Seth's error
- Removed a tooltip that erroneously suggested that regex was supported.

Cheers

On Wed, Nov 1, 2017 at 11:21 AM, Wayne Stambaugh <stambaughw@xxxxxxxxx>
wrote:

> On 10/31/2017 5:01 PM, Oliver Walters wrote:
> > How should I proceed here then?
> >
> > I would like to see the various libraries being "cached" in the
> > background, but this is increasing the scope of the work by a large
> factor.
> >
> > One thing I have noticed:
> >
> > In eeschema when you launch the component viewer, it (on first run) maps
> > and caches all the footprint libraries. This can take AGES (especially
> > on Windows). However on subsequent launches of the component viewer it
> > appears instantly. It appears to be keeping a static map of the
> > footprint library data.
> >
> > a) Would this be an acceptable approach for the footprint viewer window
>
> Sure.  Code reuse is a good thing.  I'm pretty sure the threaded
> footprint library code is split out from the component chooser so it
> should be reusable.
>
> > b) What happens when the library data changes externally? Does component
> > viewer need to be reloaded?
>
> No, only the library that changed gets reloaded the next time it's
> accessed.  It is not automatic.  I thought about using wxFileWatcher but
> that could be a lot of overhead for little net gain.  See the pcb plugin
> cache() functions.
>
> > c) Can we globally perform this caching in a background thread when
> > KiCad launches? This will hide the large pauses (up to a minute under
> > Windows) from the user...
>
> Yes, this should be done as a project element so that it can be accessed
> from all of the main windows.  Please keep in mind, this could be a lot
> of work and given that we are nearing a stable 5 release feature freeze,
> so if it's not by then it will not make it into the stable 5 release.
>
> >
> > Oliver
> >
> > On Tue, Oct 31, 2017 at 11:32 PM, Wayne Stambaugh <stambaughw@xxxxxxxxx
> > <mailto:stambaughw@xxxxxxxxx>> wrote:
> >
> >     On 10/31/2017 1:25 AM, Oliver Walters wrote:
> >     > Hmm, I had thought that there was a way to load only the *names* of
> >     > footprints, rather than individually parsing each footprint file.
> It
> >     > appears that this is not the case. Any suggestions on how the speed
> >     > could be improved? Currently I'm reading out all the footprint
> names in
> >     > each footprint library and only storing the names (wxString)
> rather than
> >     > the MODULE* objects. However, I still have to parse the entire
> library
> >     > on load.
> >     >
> >     > Ideally, I think it would be good to just read in the names, and
> then
> >     > load and display individual MODULE objects on demand.. Is this
> possible?
> >
> >     This is possible (although not implemented) for library types (kicad,
> >     geda) that use one file per footprint.  You could just read the file
> >     names from the folder and load the files as required.  If you want to
> >     search any other properties of the footprint, then you will have to
> load
> >     all of the footprints anyway.  I don't know if this would be worth
> the
> >     effort.
> >
> >     For library types that contain multiple footprints per file (legacy,
> >     Eagle), this wouldn't make much sense.  Parsing the entire file just
> to
> >     pick out the footprint names probably isn't going to save you very
> much
> >     time.
> >
> >     >
> >     > On Tue, Oct 31, 2017 at 10:40 AM, Wayne Stambaugh <
> stambaughw@xxxxxxxxx <mailto:stambaughw@xxxxxxxxx>
> >     > <mailto:stambaughw@xxxxxxxxx <mailto:stambaughw@xxxxxxxxx>>>
> wrote:
> >     >
> >     >     On 10/30/2017 5:23 PM, Oliver Walters wrote:
> >     >     > Thanks for the suggestions on fixing the text. I have that
> sorted.
> >     >     >
> >     >     > I will look into different ways of caching footprint data so
> it is quicker.
> >     >     >
> >     >     > Wayne, I didn't know about FOOTPRINT_FILTER I will switch to
> using that
> >     >     > instead (and provide regex search).
> >     >
> >     >     Thanks Oliver!
> >     >
> >     >     >
> >     >     > On 31 Oct 2017 06:55, "Seth Hillbrand" <
> seth.hillbrand@xxxxxxxxx <mailto:seth.hillbrand@xxxxxxxxx>
> >     <mailto:seth.hillbrand@xxxxxxxxx <mailto:seth.hillbrand@xxxxxxxxx>>
> >     >     > <mailto:seth.hillbrand@xxxxxxxxx <mailto:
> seth.hillbrand@xxxxxxxxx>
> >     <mailto:seth.hillbrand@xxxxxxxxx
> >     <mailto:seth.hillbrand@xxxxxxxxx>>>> wrote:
> >     >     >
> >     >     >     On Mon, Oct 30, 2017 at 11:42 AM, Wayne Stambaugh
> >     >     >     <stambaughw@xxxxxxxxx <mailto:stambaughw@xxxxxxxxx>
> >     <mailto:stambaughw@xxxxxxxxx <mailto:stambaughw@xxxxxxxxx>>
> >     >     <mailto:stambaughw@xxxxxxxxx <mailto:stambaughw@xxxxxxxxx>
> >     <mailto:stambaughw@xxxxxxxxx <mailto:stambaughw@xxxxxxxxx>>>>wrote:
> >     >     >
> >     >     >         On 10/30/2017 1:16 PM, Seth Hillbrand wrote:
> >     >     >         > Oliver, this is neat and very helpful.
> >     >     >         >
> >     >     >         > The greyed-out thing is a wx2.8 bug.  You can work
> >     >     around it by setting
> >     >     >         > the foreground color when updating the filter like
> >     this:
> >     >     >         >
> >     >     >         >  void FOOTPRINT_VIEWER_FRAME::OnFilterUpdated(
> >     >     wxCommandEvent& event )
> >     >     >         >  {
> >     >     >         > +    // Workaround wx2.8 bug showing greyed color
> >     >     >         > +    if( m_searchBox->GetValue() !=
> >     >     m_searchBox->GetDescriptiveText() )
> >     >     >         > +        m_searchBox->SetForegroundColour(
> >     >     >         > m_searchBox->GetDefaultAttributes().colFg );
> >     >     >         > +
> >     >     >         >      // Filter is non case sensitive
> >     >     >         >      wxString filter =
> >     m_searchBox->GetValue().Lower();
> >     >     >         >
> >     >     >         > The searchbox handles resetting it to grey on
> idle()
> >     >     when the text is empty.
> >     >     >
> >     >     >         Don't you mean wx 3.0?  CMake should not even
> >     generate the
> >     >     build
> >     >     >         configuration files without wx 3.0 or greater.
> >     >     >
> >     >     >
> >     >     >     Hmm... This was an issue back in 2.8 that appears to be
> only
> >     >     partly
> >     >     >     fixed.  The workaround I suggest above is functional
> >     but, for
> >     >     this,
> >     >     >     we can also execute a cleaner fix by setting the
> descriptive
> >     >     text in
> >     >     >     the declaration:
> >     >     >
> >     >     >     @@ -67,9 +67,10 @@ void
> >     FOOTPRINT_VIEWER_FRAME::ReCreateHToolbar()
> >     >     >                                      KiBitmap( module_xpm ),
> >     >     >                                      _( "Select footprint to
> >     >     browse" ) );
> >     >     >
> >     >     >     -        m_searchBox = new wxSearchCtrl( m_mainToolBar,
> >     >     >     ID_MODVIEW_SEARCH_TEXT );
> >     >     >     +        m_searchBox = new wxSearchCtrl( m_mainToolBar,
> >     >     >     ID_MODVIEW_SEARCH_TEXT,
> >     >     >     +                _( "Enter filter string" ) );
> >     >     >              m_searchBox->SetMinSize( wxSize( 250, 30 ) );
> >     >     >     -        m_searchBox->SetDescriptiveText( _( "Enter
> filter
> >     >     string" ) );
> >     >     >
> >     >     >
> >     >     >
> >     >     >     _______________________________________________
> >     >     >     Mailing list: https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >     <https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>>
> >     >     >     <https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >     <https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>>>
> >     >     >     Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>
> >     >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx
> >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>>
> >     >     >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx
> >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>
> >     >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx
> >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>>>
> >     >     >     Unsubscribe : https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >     <https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>>
> >     >     >     <https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >     <https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>>>
> >     >     >     More help   : https://help.launchpad.net/ListHelp
> >     <https://help.launchpad.net/ListHelp>
> >     >     <https://help.launchpad.net/ListHelp
> >     <https://help.launchpad.net/ListHelp>>
> >     >     >     <https://help.launchpad.net/ListHelp
> >     <https://help.launchpad.net/ListHelp>
> >     >     <https://help.launchpad.net/ListHelp
> >     <https://help.launchpad.net/ListHelp>>>
> >     >     >
> >     >     >
> >     >     >
> >     >     > _______________________________________________
> >     >     > Mailing list: https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >     <https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>>
> >     >     > Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>
> >     >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx
> >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>>
> >     >     > Unsubscribe : https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >     <https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>>
> >     >     > More help   : https://help.launchpad.net/ListHelp
> >     <https://help.launchpad.net/ListHelp>
> >     >     <https://help.launchpad.net/ListHelp
> >     <https://help.launchpad.net/ListHelp>>
> >     >     >
> >     >
> >     >
> >     >     _______________________________________________
> >     >     Mailing list: https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >     <https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>>
> >     >     Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>
> >     >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx
> >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>>
> >     >     Unsubscribe : https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >     <https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>>
> >     >     More help   : https://help.launchpad.net/ListHelp
> >     <https://help.launchpad.net/ListHelp>
> >     >     <https://help.launchpad.net/ListHelp
> >     <https://help.launchpad.net/ListHelp>>
> >     >
> >     >
> >
> >
>
>
From ca7867bceb7d086e83df4bfc415754a7347a3bde Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Fri, 17 Nov 2017 08:23:15 +1100
Subject: [PATCH 9/9] Tweaks to footprint filtering

- Further nullptr checking
- Added busy cursor while loading library
- Removed 'regular expression' tooltip
---
 pcbnew/modview_frame.cpp | 22 +++++++++++++++++-----
 pcbnew/tool_modview.cpp  |  1 -
 2 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/pcbnew/modview_frame.cpp b/pcbnew/modview_frame.cpp
index 283a119..ceac8e6 100644
--- a/pcbnew/modview_frame.cpp
+++ b/pcbnew/modview_frame.cpp
@@ -29,6 +29,7 @@
 
 #include <wx/sizer.h>
 #include <wx/progdlg.h>
+#include <wx/utils.h>
 
 #include <fctsys.h>
 #include <pgm_base.h>
@@ -487,7 +488,10 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateLibraryList()
     // Get list of loaded footprint libraries
     m_libraryNicknames = Prj().PcbFootprintLibs()->GetLogicalLibs();
 
-    m_fpList = FOOTPRINT_LIST::GetInstance( Kiway() );
+    if( m_fpList == nullptr )
+    {
+        m_fpList = FOOTPRINT_LIST::GetInstance( Kiway() );
+    }
 
     for( auto nickname : m_libraryNicknames )
     {
@@ -526,11 +530,22 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateFootprintList()
         return;
     }
 
+    if( !m_fpList )
+    {
+        return;
+    }
+
     const wxString nickname = getCurNickname();
 
     auto lib_table = Prj().PcbFootprintLibs();
 
-    if( !m_fpList || !m_fpList->ReadFootprintFiles( lib_table, &nickname ) )
+    wxBeginBusyCursor();
+
+    bool result = m_fpList->ReadFootprintFiles( lib_table, &nickname );
+
+    wxEndBusyCursor();
+
+    if( !result )
     {
         setCurFootprintName( wxEmptyString );
         return;
@@ -762,9 +777,6 @@ void FOOTPRINT_VIEWER_FRAME::OnActivate( wxActivateEvent& event )
             return;
     }
 
-    // If we are here, the library list has changed, rebuild it
-    // (This is now handled by the "Reload Footprint Libraries" button)
-    // ReCreateLibraryList();
     UpdateTitle();
 }
 
diff --git a/pcbnew/tool_modview.cpp b/pcbnew/tool_modview.cpp
index cab0683..ab949af 100644
--- a/pcbnew/tool_modview.cpp
+++ b/pcbnew/tool_modview.cpp
@@ -73,7 +73,6 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateHToolbar()
         m_searchBox->SetToolTip( _( "Filter footprint list\n"
                                     "- Search is not case sensitive\n"
                                     "- Wildcard search (?*) is supported\n"
-                                    "- Regular expression search is supported\n"
                                     "- Use : separator to include library name in search" ) );
 
         m_mainToolBar->AddControl( m_searchBox );
-- 
2.7.4

From a14a71e2170054a4037aa87296e68d58861141a1 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Fri, 17 Nov 2017 00:00:11 +1100
Subject: [PATCH 8/9] Reimplmented library filtering

- Filtering with the ':' character includes the library name in the filter
- Catching a nullptr
---
 pcbnew/modview_frame.cpp | 62 +++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 54 insertions(+), 8 deletions(-)

diff --git a/pcbnew/modview_frame.cpp b/pcbnew/modview_frame.cpp
index 3a8452e..283a119 100644
--- a/pcbnew/modview_frame.cpp
+++ b/pcbnew/modview_frame.cpp
@@ -373,19 +373,65 @@ void FOOTPRINT_VIEWER_FRAME::FilterLibs()
         return;
     }
 
-    m_footprintList->Freeze();
-    m_footprintList->Clear();
+    wxString libFilter;
 
-    m_footprintFilter.ClearFilters();
+    /*
+     * If the filter includes the ':' character,
+     * that means that the library nickname should be also filtered!
+     */
+    if( m_filterText.Contains( ":" ) )
+    {
+        wxArrayString split = wxSplit( m_filterText, ':' );
+
+        if( split.Count() > 0 )
+        {
+            libFilter = split.Item( 0 );
+        }
+    }
 
-    m_footprintFilter.FilterByPattern( m_filterText );
+    m_libList->Freeze();
+    m_libList->Clear();
 
-    for( auto& it : m_footprintFilter )
+    for( auto lib : m_libraryNicknames )
     {
-        m_footprintList->Append( it.GetFootprintName() );
+        // Only show libraries that match the library filter (if there is one)
+        if( libFilter.IsEmpty() || lib.Lower().Matches( libFilter ) )
+        {
+            m_libList->Append( lib );
+        }
     }
 
-    m_footprintList->Thaw();
+    m_libList->Thaw();
+
+    // If a previously selected library was filtered out, deselect
+    int index = m_libList->FindString( getCurNickname() );
+
+    if( index != wxNOT_FOUND )
+    {
+        m_libList->SetSelection( index, true );
+    }
+    else
+    {
+        setCurNickname( wxEmptyString );
+        setCurFootprintName( wxEmptyString );
+    }
+
+    if( !getCurNickname().IsEmpty() && m_fpList != nullptr )
+    {
+        m_footprintList->Freeze();
+        m_footprintList->Clear();
+
+        m_footprintFilter.ClearFilters();
+
+        m_footprintFilter.FilterByPattern( m_filterText );
+
+        for( auto& it : m_footprintFilter )
+        {
+            m_footprintList->Append( it.GetFootprintName() );
+        }
+
+        m_footprintList->Thaw();
+    }
 }
 
 
@@ -474,7 +520,7 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateLibraryList()
 
 void FOOTPRINT_VIEWER_FRAME::ReCreateFootprintList()
 {
-    if( !getCurNickname() )
+    if( getCurNickname().IsEmpty() )
     {
         setCurFootprintName( wxEmptyString );
         return;
-- 
2.7.4

From 2467d9cf21bd3aec7484ef8a0d65ab4205b9a0fc Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Thu, 16 Nov 2017 23:23:47 +1100
Subject: [PATCH 7/9] Switch to use FOOTPRINT_FILTER

- Use existing filter class
- Libraries are loaded as selected
- Filter now works only on selected library
---
 pcbnew/modview_frame.cpp | 208 ++++++++++++-----------------------------------
 pcbnew/modview_frame.h   |  18 ++--
 2 files changed, 57 insertions(+), 169 deletions(-)

diff --git a/pcbnew/modview_frame.cpp b/pcbnew/modview_frame.cpp
index 12167da..3a8452e 100644
--- a/pcbnew/modview_frame.cpp
+++ b/pcbnew/modview_frame.cpp
@@ -111,9 +111,6 @@ BEGIN_EVENT_TABLE( FOOTPRINT_VIEWER_FRAME, EDA_DRAW_FRAME )
 
     EVT_SEARCHCTRL_CANCEL_BTN( ID_MODVIEW_SEARCH_TEXT, FOOTPRINT_VIEWER_FRAME::OnFilterCleared )
 
-    EVT_TOOL( ID_MODVIEW_FILTER_BY_LIBRARY, FOOTPRINT_VIEWER_FRAME::OnToggleFilterByLibrary )
-
-
     // listbox events
     EVT_LISTBOX( ID_MODVIEW_LIB_LIST, FOOTPRINT_VIEWER_FRAME::ClickOnLibList )
     EVT_LISTBOX( ID_MODVIEW_FOOTPRINT_LIST, FOOTPRINT_VIEWER_FRAME::ClickOnFootprintList )
@@ -347,56 +344,22 @@ void FOOTPRINT_VIEWER_FRAME::OnFilterCleared( wxCommandEvent& event )
 
 void FOOTPRINT_VIEWER_FRAME::ClearFilter()
 {
-    m_libFilter = wxEmptyString;
-    m_fpFilter = wxEmptyString;
-
     // SetFocus must be called first to set the text color to black
     m_searchBox->SetFocus();
 
     m_searchBox->Clear();
     m_searchBox->SetDescriptiveText( _( "Enter filter string" ) );
 
+    m_filterText.Clear();
+
+    // Update filter
     FilterLibs();
 }
 
 
 void FOOTPRINT_VIEWER_FRAME::OnFilterUpdated( wxCommandEvent& event )
 {
-    // Filter is non case sensitive
-    wxString filter = m_searchBox->GetValue().Lower();
-
-    wxArrayString splitFilter = wxSplit( filter, ':' );
-
-    // Include footprint library name in filter?
-    if( splitFilter.size() == 2 )
-    {
-        m_libFilter = splitFilter[0];
-        m_fpFilter = splitFilter[1];
-    }
-    else
-    {
-        m_libFilter = wxEmptyString;
-        m_fpFilter = filter;
-    }
-
-    // Enforce leading and trailing asterisk character
-    // This is required for wxString::Matches to work
-
-    if( !m_fpFilter.IsEmpty() )
-    {
-        if( !m_fpFilter.StartsWith( "*" ) )
-            m_fpFilter.Prepend( "*" );
-        if( !m_fpFilter.EndsWith( "*" ) )
-            m_fpFilter.Append( "*" );
-    }
-
-    if( !m_libFilter.IsEmpty() )
-    {
-        if( !m_libFilter.StartsWith( "*" ) )
-            m_libFilter.Prepend( "*" );
-        if( !m_libFilter.EndsWith( "*" ) )
-            m_libFilter.Append( "*" );
-    }
+    m_filterText = m_searchBox->GetValue().Lower();
 
     // Update the selected libraries
     FilterLibs();
@@ -405,60 +368,24 @@ void FOOTPRINT_VIEWER_FRAME::OnFilterUpdated( wxCommandEvent& event )
 
 void FOOTPRINT_VIEWER_FRAME::FilterLibs()
 {
-
-    wxString fpName;
-    wxString libName;
-
-    // Update the list of libraries
-
-    m_libList->Freeze();
-    m_libList->Clear();
-
-    for( auto it = m_footprintMap.begin(); it != m_footprintMap.end(); ++it )
+    if( !m_fpList )
     {
-        libName = it->first;
-
-        // Skip this library if it does not match the specified library filter
-        if( !m_libFilter.IsEmpty() && !libName.Lower().Matches( m_libFilter ) )
-        {
-            continue;
-        }
-
-        auto& fpNames = it->second;
-
-        // Test if the library contains any footprints that match the footprint filter
+        return;
+    }
 
-        bool anyMatch = false;
+    m_footprintList->Freeze();
+    m_footprintList->Clear();
 
-        for( auto& fp: fpNames )
-        {
-            fpName = fp.Lower();
+    m_footprintFilter.ClearFilters();
 
-            if( m_fpFilter.IsEmpty() || fpName.Lower().Matches( m_fpFilter ) )
-            {
-                anyMatch = true;
-                break;
-            }
-        }
+    m_footprintFilter.FilterByPattern( m_filterText );
 
-        if( anyMatch )
-        {
-            m_libList->Append( libName );
-        }
+    for( auto& it : m_footprintFilter )
+    {
+        m_footprintList->Append( it.GetFootprintName() );
     }
 
-    m_libList->Thaw();
-
-    // Update listed footprints
-    ReCreateFootprintList();
-}
-
-
-void FOOTPRINT_VIEWER_FRAME::OnToggleFilterByLibrary( wxCommandEvent& event )
-{
-    //m_filterByLibrary = m_mainToolBar->GetToolToggled( ID_MODVIEW_FILTER_BY_LIBRARY );
-    //TODO
-    //printf( "Filter? %d\n", (int) m_filterByLibrary );
+    m_footprintList->Thaw();
 }
 
 
@@ -508,72 +435,20 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateLibraryList()
 {
     ClearFilter();
 
-    m_footprintMap.clear();
-
+    m_libList->Freeze();
     m_libList->Clear();
 
     // Get list of loaded footprint libraries
-    std::vector< wxString > nicknames = Prj().PcbFootprintLibs()->GetLogicalLibs();
-
-    wxProgressDialog progress( _( "Loading footprint libraries" ),
-                               _( "Reading library data" ),
-                               nicknames.size(),
-                               NULL,
-                               wxPD_APP_MODAL );
+    m_libraryNicknames = Prj().PcbFootprintLibs()->GetLogicalLibs();
 
-    progress.Show();
+    m_fpList = FOOTPRINT_LIST::GetInstance( Kiway() );
 
-    auto lib_table = Prj().PcbFootprintLibs();
-    auto fp_list( FOOTPRINT_LIST::GetInstance( Kiway() ) );
-
-    //TODO - Make this process threaded using 'FOOTPRINT_ASYNC_LOADER' ?
-
-    wxString errors;
-
-    for( unsigned ii = 0; ii < nicknames.size(); ii++ )
+    for( auto nickname : m_libraryNicknames )
     {
-        wxString libName = nicknames[ii];
-        m_libList->Append( libName );
-
-        progress.Update( ii, _( "Loading " + libName ) );
-
-        fp_list->ReadFootprintFiles( lib_table, !libName ? NULL : &libName );
-
-        if( fp_list->GetErrorCount() )
-        {
-            errors += "<p><b>Library: " + libName + "</b></p>";
-
-            while( auto error = fp_list->PopError() )
-            {
-                wxString tmp = error->Problem();
-
-                tmp.Replace( "\n", "<br>" );
-
-                errors += "<p>" + tmp + "</p><br>";
-            }
-        }
-
-        std::vector<wxString> fpNames;
-
-        for( auto& footprint : fp_list->GetList() )
-        {
-            fpNames.push_back( footprint->GetFootprintName() );
-        }
-
-        m_footprintMap[ libName ] = fpNames;
+        m_libList->Append( nickname );
     }
 
-    // Display errors (if there were any)
-    if( !errors.IsEmpty() )
-    {
-        HTML_MESSAGE_BOX dlg( this, _( "Footprint errors" ) );
-
-        dlg.MessageSet( _( "Errors were encountered loading footprints" ) );
-
-        dlg.AddHTML_Text( errors );
-
-        dlg.ShowModal();
-    }
+    m_libList->Thaw();
 
     // Search for a previous selection:
     int index =  m_libList->FindString( getCurNickname() );
@@ -590,8 +465,6 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateLibraryList()
         setCurFootprintName( wxEmptyString );
     }
 
-    progress.Destroy();
-
     ReCreateFootprintList();
     ReCreateHToolbar();
 
@@ -601,28 +474,49 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateLibraryList()
 
 void FOOTPRINT_VIEWER_FRAME::ReCreateFootprintList()
 {
-    m_footprintList->Clear();
-
     if( !getCurNickname() )
     {
         setCurFootprintName( wxEmptyString );
         return;
     }
 
-    std::vector<wxString> fpNames = m_footprintMap[ getCurNickname() ];
+    const wxString nickname = getCurNickname();
 
-    m_footprintList->Freeze();
-    m_footprintList->Clear();
+    auto lib_table = Prj().PcbFootprintLibs();
+
+    if( !m_fpList || !m_fpList->ReadFootprintFiles( lib_table, &nickname ) )
+    {
+        setCurFootprintName( wxEmptyString );
+        return;
+    }
 
-    for( auto& fpName : fpNames )
+    // Display error information if any footprints could not be parsed due to errors
+
+    if( m_fpList->GetErrorCount() )
     {
-        if( m_fpFilter.IsEmpty() || fpName.Lower().Matches( m_fpFilter ) )
+        wxString errors;
+
+        while( auto error = m_fpList->PopError() )
         {
-            m_footprintList->Append( fpName );
+            wxString tmp = error->Problem();
+            tmp.Replace( "\n", "<br>" );
+
+            errors += tmp;
         }
+
+        HTML_MESSAGE_BOX dlg( this, _( "Footprint errors" ) );
+
+        dlg.MessageSet( wxString::Format( _( "Errors were encountered loading library '%s'" ), nickname ) );
+        dlg.AddHTML_Text( errors );
+
+        dlg.ShowModal();
     }
 
-    m_footprintList->Thaw();
+    // Point the footprint filter to the correct footprint list
+    m_footprintFilter.SetList( *m_fpList );
+
+    // Update filter
+    FilterLibs();
 
     int index = m_footprintList->FindString( getCurFootprintName() );
 
diff --git a/pcbnew/modview_frame.h b/pcbnew/modview_frame.h
index 08ff908..790ae03 100644
--- a/pcbnew/modview_frame.h
+++ b/pcbnew/modview_frame.h
@@ -32,6 +32,8 @@
 
 #include <wx/gdicmn.h>
 #include <wx/srchctrl.h>
+#include <footprint_filter.h>
+#include <footprint_info.h>
 
 class wxSashLayoutWindow;
 class wxListBox;
@@ -65,19 +67,13 @@ public:
 
 private:
 
-    /*
-     * Map of all footprint files
-     * libName:fpNames
-     */
-    std::map< wxString, std::vector<wxString> > m_footprintMap;
+    FOOTPRINT_FILTER m_footprintFilter;                  // Filter for footprint names
+    std::unique_ptr<FOOTPRINT_LIST> m_fpList = nullptr;  // List of footprints within each library
+    std::vector<wxString> m_libraryNicknames;            // List of library nicknames gleaned from FP_LIB_TABLE
+    wxString m_filterText;                               // Text used for filtering footprints
 
     wxSearchCtrl*             m_searchBox;
 
-    wxString    m_libFilter;    // Library name filter string
-    wxString    m_fpFilter;     // Footprint name filter string
-
-    bool        m_filterByLibrary = true;   // Filter footprints by currently selected library
-
     wxListBox*              m_libList;          // The list of library names
     wxListBox*              m_footprintList;    // The list of footprint names
 
@@ -120,8 +116,6 @@ private:
 
     void ClearFilter();
 
-    void OnToggleFilterByLibrary( wxCommandEvent& event );
-
     /**
      * Function RedrawActiveWindow
      * Display the current selected component.
-- 
2.7.4

From b5971916191a02ce722fb2dd071cf726390e4c06 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Tue, 31 Oct 2017 08:07:48 +1100
Subject: [PATCH 6/9] Fixed greyed-out text bug

- Setting the filter text on creation fixed the issue
---
 pcbnew/modview_frame.cpp | 3 +++
 pcbnew/tool_modview.cpp  | 3 ++-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/pcbnew/modview_frame.cpp b/pcbnew/modview_frame.cpp
index 0da8b59..12167da 100644
--- a/pcbnew/modview_frame.cpp
+++ b/pcbnew/modview_frame.cpp
@@ -350,6 +350,9 @@ void FOOTPRINT_VIEWER_FRAME::ClearFilter()
     m_libFilter = wxEmptyString;
     m_fpFilter = wxEmptyString;
 
+    // SetFocus must be called first to set the text color to black
+    m_searchBox->SetFocus();
+
     m_searchBox->Clear();
     m_searchBox->SetDescriptiveText( _( "Enter filter string" ) );
 
diff --git a/pcbnew/tool_modview.cpp b/pcbnew/tool_modview.cpp
index 358c601..cab0683 100644
--- a/pcbnew/tool_modview.cpp
+++ b/pcbnew/tool_modview.cpp
@@ -67,12 +67,13 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateHToolbar()
                                 KiBitmap( module_xpm ),
                                 _( "Select footprint to browse" ) );
 
-        m_searchBox = new wxSearchCtrl( m_mainToolBar, ID_MODVIEW_SEARCH_TEXT );
+        m_searchBox = new wxSearchCtrl( m_mainToolBar, ID_MODVIEW_SEARCH_TEXT, _( "Enter filter string" ) );
         m_searchBox->SetMinSize( wxSize( 250, 30 ) );
         m_searchBox->SetDescriptiveText( _( "Enter filter string" ) );
         m_searchBox->SetToolTip( _( "Filter footprint list\n"
                                     "- Search is not case sensitive\n"
                                     "- Wildcard search (?*) is supported\n"
+                                    "- Regular expression search is supported\n"
                                     "- Use : separator to include library name in search" ) );
 
         m_mainToolBar->AddControl( m_searchBox );
-- 
2.7.4

From c77afcb88623fd9e69c7661365008db2ee188ba1 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Sun, 29 Oct 2017 23:17:12 +1100
Subject: [PATCH 5/9] Added error display

- Displays single list of ALL footprint errors (from all libs)
---
 pcbnew/modview_frame.cpp | 33 +++++++++++++++++++++++----------
 pcbnew/modview_frame.h   |  1 -
 2 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/pcbnew/modview_frame.cpp b/pcbnew/modview_frame.cpp
index 2906da2..0da8b59 100644
--- a/pcbnew/modview_frame.cpp
+++ b/pcbnew/modview_frame.cpp
@@ -43,6 +43,7 @@
 #include <lib_id.h>
 #include <confirm.h>
 #include <bitmaps.h>
+#include <html_messagebox.h>
 
 #include <class_board.h>
 #include <class_module.h>
@@ -524,6 +525,8 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateLibraryList()
 
     //TODO - Make this process threaded using 'FOOTPRINT_ASYNC_LOADER' ?
 
+    wxString errors;
+
     for( unsigned ii = 0; ii < nicknames.size(); ii++ )
     {
         wxString libName = nicknames[ii];
@@ -535,19 +538,16 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateLibraryList()
 
         if( fp_list->GetErrorCount() )
         {
-            // TODO - Display errors
+            errors += "<p><b>Library: " + libName + "</b></p>";
 
-            /*
-            if( fp_info_list->GetErrorCount() )
+            while( auto error = fp_list->PopError() )
             {
-                fp_info_list->DisplayErrors( this );
+                wxString tmp = error->Problem();
+
+                tmp.Replace( "\n", "<br>" );
 
-                // For footprint libraries that support one footprint per file, there may have been
-                // valid footprints read so show the footprints that loaded properly.
-                if( fp_info_list->GetList().size() == 0 )
-                    return;
+                errors += "<p>" + tmp + "</p><br>";
             }
-            */
         }
 
         std::vector<wxString> fpNames;
@@ -560,6 +560,18 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateLibraryList()
         m_footprintMap[ libName ] = fpNames;
     }
 
+    // Display errors (if there were any)
+    if( !errors.IsEmpty() )
+    {
+        HTML_MESSAGE_BOX dlg( this, _( "Footprint errors" ) );
+
+        dlg.MessageSet( _( "Errors were encountered loading footprints" ) );
+
+        dlg.AddHTML_Text( errors );
+
+        dlg.ShowModal();
+    }
+
     // Search for a previous selection:
     int index =  m_libList->FindString( getCurNickname() );
 
@@ -808,7 +820,8 @@ void FOOTPRINT_VIEWER_FRAME::OnActivate( wxActivateEvent& event )
     }
 
     // If we are here, the library list has changed, rebuild it
-    ReCreateLibraryList();
+    // (This is now handled by the "Reload Footprint Libraries" button)
+    // ReCreateLibraryList();
     UpdateTitle();
 }
 
diff --git a/pcbnew/modview_frame.h b/pcbnew/modview_frame.h
index 9c9a0f5..08ff908 100644
--- a/pcbnew/modview_frame.h
+++ b/pcbnew/modview_frame.h
@@ -169,7 +169,6 @@ private:
 
     void ReloadAllLibraries( wxCommandEvent& event )
     {
-        OnFilterCleared( event );
         ReCreateLibraryList();
     }
 
-- 
2.7.4

From f0d6b058479cc5e408f78e3189056af08715b3c9 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Sun, 29 Oct 2017 21:26:51 +1100
Subject: [PATCH 4/9] Replaced wxTextCtrl with wxSearchCtrl

- Also added option to display ALL results (even if not in selected library)
Replaced wxTextCtrl with wxSearchCtrl

- Added button to refresh entire library set
- Moved filter input to main tool bar
---
 pcbnew/modview_frame.cpp | 77 ++++++++++++++++++++++++------------------------
 pcbnew/modview_frame.h   | 33 +++++++++++++++------
 pcbnew/pcbnew_id.h       |  1 +
 pcbnew/tool_modview.cpp  | 16 ++++++++++
 4 files changed, 79 insertions(+), 48 deletions(-)

diff --git a/pcbnew/modview_frame.cpp b/pcbnew/modview_frame.cpp
index 8b7abdc..2906da2 100644
--- a/pcbnew/modview_frame.cpp
+++ b/pcbnew/modview_frame.cpp
@@ -89,6 +89,8 @@ BEGIN_EVENT_TABLE( FOOTPRINT_VIEWER_FRAME, EDA_DRAW_FRAME )
     EVT_MENU( wxID_ABOUT, EDA_BASE_FRAME::GetKicadAbout )
 
     // Toolbar events
+    EVT_TOOL( ID_MODVIEW_RELOAD_LIBS,
+              FOOTPRINT_VIEWER_FRAME::ReloadAllLibraries )
     EVT_TOOL( ID_MODVIEW_SELECT_LIB,
               FOOTPRINT_VIEWER_FRAME::SelectCurrentLibrary )
     EVT_TOOL( ID_MODVIEW_SELECT_PART,
@@ -104,6 +106,13 @@ BEGIN_EVENT_TABLE( FOOTPRINT_VIEWER_FRAME, EDA_DRAW_FRAME )
     // Search events
     EVT_TEXT( ID_MODVIEW_SEARCH_TEXT, FOOTPRINT_VIEWER_FRAME::OnFilterUpdated )
 
+    EVT_SEARCHCTRL_SEARCH_BTN( ID_MODVIEW_SEARCH_TEXT, FOOTPRINT_VIEWER_FRAME::OnFilterUpdated )
+
+    EVT_SEARCHCTRL_CANCEL_BTN( ID_MODVIEW_SEARCH_TEXT, FOOTPRINT_VIEWER_FRAME::OnFilterCleared )
+
+    EVT_TOOL( ID_MODVIEW_FILTER_BY_LIBRARY, FOOTPRINT_VIEWER_FRAME::OnToggleFilterByLibrary )
+
+
     // listbox events
     EVT_LISTBOX( ID_MODVIEW_LIB_LIST, FOOTPRINT_VIEWER_FRAME::ClickOnLibList )
     EVT_LISTBOX( ID_MODVIEW_FOOTPRINT_LIST, FOOTPRINT_VIEWER_FRAME::ClickOnFootprintList )
@@ -262,40 +271,9 @@ FOOTPRINT_VIEWER_FRAME::FOOTPRINT_VIEWER_FRAME( KIWAY* aKiway, wxWindow* aParent
     EDA_PANEINFO toolbar;
     toolbar.HorizontalToolbarPane();
 
-    if( !m_auxiliaryToolBar )
-    {
-        m_auxiliaryToolBar = new wxAuiToolBar( this, ID_AUX_TOOLBAR, wxDefaultPosition, wxDefaultSize,
-                                               KICAD_AUI_TB_STYLE | wxAUI_TB_HORZ_LAYOUT | wxAUI_TB_OVERFLOW );
-
-        m_auxiliaryToolBar->AddTool( ID_MODVIEW_FILTER_BY_LIBRARY,
-                              KiBitmap( module_library_list_xpm ),
-                              wxNullBitmap,
-                              true, NULL,
-                              _( "Filter footprint list by selected library" ),
-                              wxEmptyString );
-
-        m_auxiliaryToolBar->AddSeparator();
-
-        m_searchBox = new wxTextCtrl( m_auxiliaryToolBar, ID_MODVIEW_SEARCH_TEXT );
-        m_searchBox->SetMinSize( wxSize( 250, -1 ) );
-        m_searchBox->SetHint( _( "Enter filter text" ) );
-        m_searchBox->SetToolTip( _( "Filter footprint list\n"
-                                    "- Search is not case sensitive\n"
-                                    "- Wildcard search (?*) is supported\n"
-                                    "- Use : separator to include library name in search" ) );
-
-        m_auxiliaryToolBar->AddControl( m_searchBox );
-
-        m_auxiliaryToolBar->Realize();
-    }
-
     // Manage main toolbar, top pane
     m_auimgr.AddPane( m_mainToolBar, toolbarPaneInfo );
 
-    m_auimgr.AddPane( m_auxiliaryToolBar,
-                      wxAuiPaneInfo( toolbar ).Name( "m_searchToolBar" ).ToolbarPane()
-                      .Top().Row( 2 ).Layer( 1 ).MinSize( minsize ) );
-
     // Manage the list of libraries, left pane.
     m_auimgr.AddPane( m_libList,
                       info.Name( "m_libList" )
@@ -330,11 +308,6 @@ FOOTPRINT_VIEWER_FRAME::FOOTPRINT_VIEWER_FRAME( KIWAY* aKiway, wxWindow* aParent
         m_auimgr.GetPane( m_mainToolBar ).BestSize( tbsize );
     }
 
-    // Ensure that toolbars are visible
-    // It seems that auiMgr::LoadPerspective() sometimes hides these
-    m_mainToolBar->Show();
-    m_auxiliaryToolBar->Show();
-
     // after changing something to the aui manager,
     // call Update()() to reflect the changes
     m_auimgr.Update();
@@ -365,6 +338,24 @@ FOOTPRINT_VIEWER_FRAME::~FOOTPRINT_VIEWER_FRAME()
 }
 
 
+void FOOTPRINT_VIEWER_FRAME::OnFilterCleared( wxCommandEvent& event )
+{
+    ClearFilter();
+}
+
+
+void FOOTPRINT_VIEWER_FRAME::ClearFilter()
+{
+    m_libFilter = wxEmptyString;
+    m_fpFilter = wxEmptyString;
+
+    m_searchBox->Clear();
+    m_searchBox->SetDescriptiveText( _( "Enter filter string" ) );
+
+    FilterLibs();
+}
+
+
 void FOOTPRINT_VIEWER_FRAME::OnFilterUpdated( wxCommandEvent& event )
 {
     // Filter is non case sensitive
@@ -459,6 +450,14 @@ void FOOTPRINT_VIEWER_FRAME::FilterLibs()
 }
 
 
+void FOOTPRINT_VIEWER_FRAME::OnToggleFilterByLibrary( wxCommandEvent& event )
+{
+    //m_filterByLibrary = m_mainToolBar->GetToolToggled( ID_MODVIEW_FILTER_BY_LIBRARY );
+    //TODO
+    //printf( "Filter? %d\n", (int) m_filterByLibrary );
+}
+
+
 void FOOTPRINT_VIEWER_FRAME::OnCloseWindow( wxCloseEvent& Event )
 {
     DBG(printf( "%s:\n", __func__ );)
@@ -503,6 +502,8 @@ void FOOTPRINT_VIEWER_FRAME::OnSetRelativeOffset( wxCommandEvent& event )
 
 void FOOTPRINT_VIEWER_FRAME::ReCreateLibraryList()
 {
+    ClearFilter();
+
     m_footprintMap.clear();
 
     m_libList->Clear();
@@ -593,9 +594,7 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateFootprintList()
         return;
     }
 
-    wxString libName = getCurNickname();
-
-    auto fpNames = m_footprintMap[ libName ];
+    std::vector<wxString> fpNames = m_footprintMap[ getCurNickname() ];
 
     m_footprintList->Freeze();
     m_footprintList->Clear();
diff --git a/pcbnew/modview_frame.h b/pcbnew/modview_frame.h
index a2f0714..9c9a0f5 100644
--- a/pcbnew/modview_frame.h
+++ b/pcbnew/modview_frame.h
@@ -31,7 +31,7 @@
 
 
 #include <wx/gdicmn.h>
-#include <wx/textctrl.h>
+#include <wx/srchctrl.h>
 
 class wxSashLayoutWindow;
 class wxListBox;
@@ -71,11 +71,13 @@ private:
      */
     std::map< wxString, std::vector<wxString> > m_footprintMap;
 
-    wxTextCtrl*             m_searchBox;
+    wxSearchCtrl*             m_searchBox;
 
     wxString    m_libFilter;    // Library name filter string
     wxString    m_fpFilter;     // Footprint name filter string
 
+    bool        m_filterByLibrary = true;   // Filter footprints by currently selected library
+
     wxListBox*              m_libList;          // The list of library names
     wxListBox*              m_footprintList;    // The list of footprint names
 
@@ -97,21 +99,28 @@ private:
     void UpdateTitle();
 
     /**
-     * Function SetFilterText
+     * Function OnFilterUpdated
      * Update the search string for footprint filtering
+     * Called when the filter text is changed
      */
     void OnFilterUpdated( wxCommandEvent& event );
 
     /**
-     *
+     * Function OnFilterCleared
+     * Clear (reset) the filter text
+     * Called when the search box "cancel" button is pressed
+     */
+    void OnFilterCleared( wxCommandEvent& event );
+
+    /**
+     * Function FilterLibs
+     * Updates the visible libraries and footprints based on search filter
      */
     void FilterLibs();
 
-    void ClearFilter()
-    {
-        m_searchBox->SetValue( wxEmptyString );
-        m_searchBox->SetHint( _( "Enter filter text" ) );
-    }
+    void ClearFilter();
+
+    void OnToggleFilterByLibrary( wxCommandEvent& event );
 
     /**
      * Function RedrawActiveWindow
@@ -158,6 +167,12 @@ private:
      */
     virtual void OnActivate( wxActivateEvent& event ) override;
 
+    void ReloadAllLibraries( wxCommandEvent& event )
+    {
+        OnFilterCleared( event );
+        ReCreateLibraryList();
+    }
+
     void SelectCurrentLibrary( wxCommandEvent& event );
 
     /**
diff --git a/pcbnew/pcbnew_id.h b/pcbnew/pcbnew_id.h
index 2209d66..9f5ed98 100644
--- a/pcbnew/pcbnew_id.h
+++ b/pcbnew/pcbnew_id.h
@@ -383,6 +383,7 @@ enum pcbnew_ids
     ID_MODVIEW_FOOTPRINT_WINDOW,
     ID_MODVIEW_LIB_LIST,
     ID_MODVIEW_FOOTPRINT_LIST,
+    ID_MODVIEW_RELOAD_LIBS,
     ID_MODVIEW_SELECT_LIB,
     ID_MODVIEW_SELECT_PART,
     ID_MODVIEW_PREVIOUS,
diff --git a/pcbnew/tool_modview.cpp b/pcbnew/tool_modview.cpp
index 0b2aba7..358c601 100644
--- a/pcbnew/tool_modview.cpp
+++ b/pcbnew/tool_modview.cpp
@@ -53,6 +53,12 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateHToolbar()
                                             | wxAUI_TB_OVERFLOW );
 
         // Set up toolbar
+        m_mainToolBar->AddTool( ID_MODVIEW_RELOAD_LIBS, wxEmptyString,
+                                KiBitmap( reload_xpm ),
+                                _( "Reload footprint libraries" ) );
+
+        m_mainToolBar->AddSeparator();
+
         m_mainToolBar->AddTool( ID_MODVIEW_SELECT_LIB, wxEmptyString,
                                 KiBitmap( library_xpm ),
                                 _( "Select library to browse" ) );
@@ -61,6 +67,16 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateHToolbar()
                                 KiBitmap( module_xpm ),
                                 _( "Select footprint to browse" ) );
 
+        m_searchBox = new wxSearchCtrl( m_mainToolBar, ID_MODVIEW_SEARCH_TEXT );
+        m_searchBox->SetMinSize( wxSize( 250, 30 ) );
+        m_searchBox->SetDescriptiveText( _( "Enter filter string" ) );
+        m_searchBox->SetToolTip( _( "Filter footprint list\n"
+                                    "- Search is not case sensitive\n"
+                                    "- Wildcard search (?*) is supported\n"
+                                    "- Use : separator to include library name in search" ) );
+
+        m_mainToolBar->AddControl( m_searchBox );
+
         m_mainToolBar->AddSeparator();
         m_mainToolBar->AddTool( ID_MODVIEW_PREVIOUS, wxEmptyString,
                                 KiBitmap( lib_previous_xpm ),
-- 
2.7.4

From 79aa3b78a59584aca43c978f3f8e32e310082603 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Sat, 28 Oct 2017 10:59:01 +1100
Subject: [PATCH 3/9] Footprint filtering is now working

---
 pcbnew/modview_frame.cpp | 141 ++++++++++++++++++++++++++++++-----------------
 pcbnew/modview_frame.h   |  10 +++-
 2 files changed, 100 insertions(+), 51 deletions(-)

diff --git a/pcbnew/modview_frame.cpp b/pcbnew/modview_frame.cpp
index b991f79..8b7abdc 100644
--- a/pcbnew/modview_frame.cpp
+++ b/pcbnew/modview_frame.cpp
@@ -102,7 +102,7 @@ BEGIN_EVENT_TABLE( FOOTPRINT_VIEWER_FRAME, EDA_DRAW_FRAME )
     EVT_TOOL( ID_MODVIEW_SHOW_3D_VIEW, FOOTPRINT_VIEWER_FRAME::Show3D_Frame )
 
     // Search events
-    EVT_TEXT( ID_MODVIEW_SEARCH_TEXT, FOOTPRINT_VIEWER_FRAME::UpdateFilter )
+    EVT_TEXT( ID_MODVIEW_SEARCH_TEXT, FOOTPRINT_VIEWER_FRAME::OnFilterUpdated )
 
     // listbox events
     EVT_LISTBOX( ID_MODVIEW_LIB_LIST, FOOTPRINT_VIEWER_FRAME::ClickOnLibList )
@@ -331,6 +331,7 @@ FOOTPRINT_VIEWER_FRAME::FOOTPRINT_VIEWER_FRAME( KIWAY* aKiway, wxWindow* aParent
     }
 
     // Ensure that toolbars are visible
+    // It seems that auiMgr::LoadPerspective() sometimes hides these
     m_mainToolBar->Show();
     m_auxiliaryToolBar->Show();
 
@@ -364,77 +365,97 @@ FOOTPRINT_VIEWER_FRAME::~FOOTPRINT_VIEWER_FRAME()
 }
 
 
-void FOOTPRINT_VIEWER_FRAME::UpdateFilter( wxCommandEvent& event )
+void FOOTPRINT_VIEWER_FRAME::OnFilterUpdated( wxCommandEvent& event )
 {
     // Filter is non case sensitive
     wxString filter = m_searchBox->GetValue().Lower();
 
     wxArrayString splitFilter = wxSplit( filter, ':' );
 
-    wxString libFilter;
-    wxString fpFilter;
-
-    bool incLibName = splitFilter.size() == 2;
-
     // Include footprint library name in filter?
-    if( incLibName )
+    if( splitFilter.size() == 2 )
     {
-        printf( "Including library name in filter\n");
-        libFilter = splitFilter[0];
-        fpFilter = splitFilter[1];
+        m_libFilter = splitFilter[0];
+        m_fpFilter = splitFilter[1];
     }
     else
     {
-        fpFilter = filter;
+        m_libFilter = wxEmptyString;
+        m_fpFilter = filter;
     }
 
-    // Enfore leading and trailing asterii
+    // Enforce leading and trailing asterisk character
     // This is required for wxString::Matches to work
 
-    if( !fpFilter.IsEmpty() )
+    if( !m_fpFilter.IsEmpty() )
     {
-        if( !fpFilter.StartsWith( "*" ) )
-            fpFilter.Prepend( "*" );
-        if( !fpFilter.EndsWith( "*" ) )
-            fpFilter.Append( "*" );
+        if( !m_fpFilter.StartsWith( "*" ) )
+            m_fpFilter.Prepend( "*" );
+        if( !m_fpFilter.EndsWith( "*" ) )
+            m_fpFilter.Append( "*" );
     }
 
-    if( !libFilter.IsEmpty() )
+    if( !m_libFilter.IsEmpty() )
     {
-        if( !libFilter.StartsWith( "*" ) )
-            libFilter.Prepend( "*" );
-        if( !libFilter.EndsWith( "*" ) )
-            libFilter.Append( "*" );
+        if( !m_libFilter.StartsWith( "*" ) )
+            m_libFilter.Prepend( "*" );
+        if( !m_libFilter.EndsWith( "*" ) )
+            m_libFilter.Append( "*" );
     }
 
+    // Update the selected libraries
+    FilterLibs();
+}
+
+
+void FOOTPRINT_VIEWER_FRAME::FilterLibs()
+{
+
     wxString fpName;
     wxString libName;
 
+    // Update the list of libraries
+
+    m_libList->Freeze();
+    m_libList->Clear();
+
     for( auto it = m_footprintMap.begin(); it != m_footprintMap.end(); ++it )
     {
-        libName = it->first.Lower();
+        libName = it->first;
 
-        if( incLibName )
+        // Skip this library if it does not match the specified library filter
+        if( !m_libFilter.IsEmpty() && !libName.Lower().Matches( m_libFilter ) )
         {
-            // Skip this library if it does not match the specified library filter
-            if( !libFilter.IsEmpty() && !libName.Lower().Matches( libFilter ) )
-            {
-                continue;
-            }
+            continue;
         }
 
         auto& fpNames = it->second;
 
+        // Test if the library contains any footprints that match the footprint filter
+
+        bool anyMatch = false;
+
         for( auto& fp: fpNames )
         {
             fpName = fp.Lower();
 
-            if( fpFilter.IsEmpty() || fpName.Lower().Matches( fpFilter ) )
+            if( m_fpFilter.IsEmpty() || fpName.Lower().Matches( m_fpFilter ) )
             {
-                std::cout << libName << ":" << fpName << std::endl;
+                anyMatch = true;
+                break;
             }
         }
+
+        if( anyMatch )
+        {
+            m_libList->Append( libName );
+        }
     }
+
+    m_libList->Thaw();
+
+    // Update listed footprints
+    ReCreateFootprintList();
 }
 
 
@@ -500,7 +521,8 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateLibraryList()
     auto lib_table = Prj().PcbFootprintLibs();
     auto fp_list( FOOTPRINT_LIST::GetInstance( Kiway() ) );
 
-    //TODO - Make this process threaded using 'FOOTPRINT_ASYNC_LOADER'
+    //TODO - Make this process threaded using 'FOOTPRINT_ASYNC_LOADER' ?
+
     for( unsigned ii = 0; ii < nicknames.size(); ii++ )
     {
         wxString libName = nicknames[ii];
@@ -510,7 +532,22 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateLibraryList()
 
         fp_list->ReadFootprintFiles( lib_table, !libName ? NULL : &libName );
 
-        // TODO - Display errors
+        if( fp_list->GetErrorCount() )
+        {
+            // TODO - Display errors
+
+            /*
+            if( fp_info_list->GetErrorCount() )
+            {
+                fp_info_list->DisplayErrors( this );
+
+                // For footprint libraries that support one footprint per file, there may have been
+                // valid footprints read so show the footprints that loaded properly.
+                if( fp_info_list->GetList().size() == 0 )
+                    return;
+            }
+            */
+        }
 
         std::vector<wxString> fpNames;
 
@@ -556,33 +593,33 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateFootprintList()
         return;
     }
 
-    auto fp_info_list( FOOTPRINT_LIST::GetInstance( Kiway() ) );
+    wxString libName = getCurNickname();
 
-    wxString nickname = getCurNickname();
+    auto fpNames = m_footprintMap[ libName ];
 
-    fp_info_list->ReadFootprintFiles( Prj().PcbFootprintLibs(), !nickname ? NULL : &nickname );
+    m_footprintList->Freeze();
+    m_footprintList->Clear();
 
-    if( fp_info_list->GetErrorCount() )
+    for( auto& fpName : fpNames )
     {
-        fp_info_list->DisplayErrors( this );
-
-        // For footprint libraries that support one footprint per file, there may have been
-        // valid footprints read so show the footprints that loaded properly.
-        if( fp_info_list->GetList().size() == 0 )
-            return;
+        if( m_fpFilter.IsEmpty() || fpName.Lower().Matches( m_fpFilter ) )
+        {
+            m_footprintList->Append( fpName );
+        }
     }
 
-    for( auto& footprint : fp_info_list->GetList() )
-    {
-        m_footprintList->Append( footprint->GetFootprintName() );
-    }
+    m_footprintList->Thaw();
 
     int index = m_footprintList->FindString( getCurFootprintName() );
 
     if( index == wxNOT_FOUND )
+    {
         setCurFootprintName( wxEmptyString );
+    }
     else
+    {
         m_footprintList->SetSelection( index, true );
+    }
 }
 
 
@@ -590,8 +627,10 @@ void FOOTPRINT_VIEWER_FRAME::ClickOnLibList( wxCommandEvent& event )
 {
     int ii = m_libList->GetSelection();
 
-    if( ii < 0 )
+    if( ii < 0 || ii >= (int) m_libList->GetCount() )
+    {
         return;
+    }
 
     wxString name = m_libList->GetString( ii );
 
@@ -613,8 +652,10 @@ void FOOTPRINT_VIEWER_FRAME::ClickOnFootprintList( wxCommandEvent& event )
 
     int ii = m_footprintList->GetSelection();
 
-    if( ii < 0 )
+    if( ii < 0 || ii >= (int) m_footprintList->GetCount() )
+    {
         return;
+    }
 
     wxString name = m_footprintList->GetString( ii );
 
diff --git a/pcbnew/modview_frame.h b/pcbnew/modview_frame.h
index d0f65f9..a2f0714 100644
--- a/pcbnew/modview_frame.h
+++ b/pcbnew/modview_frame.h
@@ -73,6 +73,9 @@ private:
 
     wxTextCtrl*             m_searchBox;
 
+    wxString    m_libFilter;    // Library name filter string
+    wxString    m_fpFilter;     // Footprint name filter string
+
     wxListBox*              m_libList;          // The list of library names
     wxListBox*              m_footprintList;    // The list of footprint names
 
@@ -97,7 +100,12 @@ private:
      * Function SetFilterText
      * Update the search string for footprint filtering
      */
-    void UpdateFilter( wxCommandEvent& event );
+    void OnFilterUpdated( wxCommandEvent& event );
+
+    /**
+     *
+     */
+    void FilterLibs();
 
     void ClearFilter()
     {
-- 
2.7.4

From b955843e6d2d1d7e029009df2cac1de792bc3458 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Sat, 28 Oct 2017 01:13:59 +1100
Subject: [PATCH 2/9] Implement filtering for footprints

- Filter by library:name pair
- Works! Pretty quick too.
---
 pcbnew/modview_frame.cpp | 108 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 104 insertions(+), 4 deletions(-)

diff --git a/pcbnew/modview_frame.cpp b/pcbnew/modview_frame.cpp
index 4d9527f..b991f79 100644
--- a/pcbnew/modview_frame.cpp
+++ b/pcbnew/modview_frame.cpp
@@ -28,6 +28,7 @@
  */
 
 #include <wx/sizer.h>
+#include <wx/progdlg.h>
 
 #include <fctsys.h>
 #include <pgm_base.h>
@@ -329,6 +330,10 @@ FOOTPRINT_VIEWER_FRAME::FOOTPRINT_VIEWER_FRAME( KIWAY* aKiway, wxWindow* aParent
         m_auimgr.GetPane( m_mainToolBar ).BestSize( tbsize );
     }
 
+    // Ensure that toolbars are visible
+    m_mainToolBar->Show();
+    m_auxiliaryToolBar->Show();
+
     // after changing something to the aui manager,
     // call Update()() to reflect the changes
     m_auimgr.Update();
@@ -361,9 +366,75 @@ FOOTPRINT_VIEWER_FRAME::~FOOTPRINT_VIEWER_FRAME()
 
 void FOOTPRINT_VIEWER_FRAME::UpdateFilter( wxCommandEvent& event )
 {
-    wxString filter = m_searchBox->GetValue();
+    // Filter is non case sensitive
+    wxString filter = m_searchBox->GetValue().Lower();
+
+    wxArrayString splitFilter = wxSplit( filter, ':' );
+
+    wxString libFilter;
+    wxString fpFilter;
+
+    bool incLibName = splitFilter.size() == 2;
+
+    // Include footprint library name in filter?
+    if( incLibName )
+    {
+        printf( "Including library name in filter\n");
+        libFilter = splitFilter[0];
+        fpFilter = splitFilter[1];
+    }
+    else
+    {
+        fpFilter = filter;
+    }
+
+    // Enfore leading and trailing asterii
+    // This is required for wxString::Matches to work
+
+    if( !fpFilter.IsEmpty() )
+    {
+        if( !fpFilter.StartsWith( "*" ) )
+            fpFilter.Prepend( "*" );
+        if( !fpFilter.EndsWith( "*" ) )
+            fpFilter.Append( "*" );
+    }
+
+    if( !libFilter.IsEmpty() )
+    {
+        if( !libFilter.StartsWith( "*" ) )
+            libFilter.Prepend( "*" );
+        if( !libFilter.EndsWith( "*" ) )
+            libFilter.Append( "*" );
+    }
+
+    wxString fpName;
+    wxString libName;
+
+    for( auto it = m_footprintMap.begin(); it != m_footprintMap.end(); ++it )
+    {
+        libName = it->first.Lower();
 
-    //TODO
+        if( incLibName )
+        {
+            // Skip this library if it does not match the specified library filter
+            if( !libFilter.IsEmpty() && !libName.Lower().Matches( libFilter ) )
+            {
+                continue;
+            }
+        }
+
+        auto& fpNames = it->second;
+
+        for( auto& fp: fpNames )
+        {
+            fpName = fp.Lower();
+
+            if( fpFilter.IsEmpty() || fpName.Lower().Matches( fpFilter ) )
+            {
+                std::cout << libName << ":" << fpName << std::endl;
+            }
+        }
+    }
 }
 
 
@@ -415,13 +486,40 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateLibraryList()
 
     m_libList->Clear();
 
+    // Get list of loaded footprint libraries
     std::vector< wxString > nicknames = Prj().PcbFootprintLibs()->GetLogicalLibs();
 
+    wxProgressDialog progress( _( "Loading footprint libraries" ),
+                               _( "Reading library data" ),
+                               nicknames.size(),
+                               NULL,
+                               wxPD_APP_MODAL );
+
+    progress.Show();
+
+    auto lib_table = Prj().PcbFootprintLibs();
+    auto fp_list( FOOTPRINT_LIST::GetInstance( Kiway() ) );
+
+    //TODO - Make this process threaded using 'FOOTPRINT_ASYNC_LOADER'
     for( unsigned ii = 0; ii < nicknames.size(); ii++ )
     {
-        m_libList->Append( nicknames[ii] );
+        wxString libName = nicknames[ii];
+        m_libList->Append( libName );
+
+        progress.Update( ii, _( "Loading " + libName ) );
+
+        fp_list->ReadFootprintFiles( lib_table, !libName ? NULL : &libName );
+
+        // TODO - Display errors
+
+        std::vector<wxString> fpNames;
 
-        m_footprintMap[ nicknames[ii] ] = std::vector<wxString>();
+        for( auto& footprint : fp_list->GetList() )
+        {
+            fpNames.push_back( footprint->GetFootprintName() );
+        }
+
+        m_footprintMap[ libName ] = fpNames;
     }
 
     // Search for a previous selection:
@@ -439,6 +537,8 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateLibraryList()
         setCurFootprintName( wxEmptyString );
     }
 
+    progress.Destroy();
+
     ReCreateFootprintList();
     ReCreateHToolbar();
 
-- 
2.7.4

From 4269aa6f612b4818959c7d51f2e143d1c85d158c Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Fri, 27 Oct 2017 14:38:30 +1100
Subject: [PATCH 1/9] Added footprint search box

- No functionality yet
- Playing wih wxAUI settings
---
 pcbnew/modview_frame.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++++++--
 pcbnew/modview_frame.h   | 25 +++++++++++++++++++--
 pcbnew/pcbnew_id.h       |  3 +++
 3 files changed, 81 insertions(+), 4 deletions(-)

diff --git a/pcbnew/modview_frame.cpp b/pcbnew/modview_frame.cpp
index 3d9d598..4d9527f 100644
--- a/pcbnew/modview_frame.cpp
+++ b/pcbnew/modview_frame.cpp
@@ -27,6 +27,8 @@
  * @file modview_frame.cpp
  */
 
+#include <wx/sizer.h>
+
 #include <fctsys.h>
 #include <pgm_base.h>
 #include <kiway.h>
@@ -98,6 +100,9 @@ BEGIN_EVENT_TABLE( FOOTPRINT_VIEWER_FRAME, EDA_DRAW_FRAME )
               FOOTPRINT_VIEWER_FRAME::ExportSelectedFootprint )
     EVT_TOOL( ID_MODVIEW_SHOW_3D_VIEW, FOOTPRINT_VIEWER_FRAME::Show3D_Frame )
 
+    // Search events
+    EVT_TEXT( ID_MODVIEW_SEARCH_TEXT, FOOTPRINT_VIEWER_FRAME::UpdateFilter )
+
     // listbox events
     EVT_LISTBOX( ID_MODVIEW_LIB_LIST, FOOTPRINT_VIEWER_FRAME::ClickOnLibList )
     EVT_LISTBOX( ID_MODVIEW_FOOTPRINT_LIST, FOOTPRINT_VIEWER_FRAME::ClickOnFootprintList )
@@ -166,6 +171,7 @@ FOOTPRINT_VIEWER_FRAME::FOOTPRINT_VIEWER_FRAME( KIWAY* aKiway, wxWindow* aParent
     m_footprintList = new wxListBox( this, ID_MODVIEW_FOOTPRINT_LIST,
             wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_HSCROLL );
 
+
     SetBoard( new BOARD() );
     // In viewer, the default net clearance is not known (it depends on the actual board).
     // So we do not show the default clearance, by setting it to 0
@@ -252,17 +258,51 @@ FOOTPRINT_VIEWER_FRAME::FOOTPRINT_VIEWER_FRAME( KIWAY* aKiway, wxWindow* aParent
     EDA_PANEINFO mesg;
     mesg.MessageToolbarPane();
 
+    EDA_PANEINFO toolbar;
+    toolbar.HorizontalToolbarPane();
+
+    if( !m_auxiliaryToolBar )
+    {
+        m_auxiliaryToolBar = new wxAuiToolBar( this, ID_AUX_TOOLBAR, wxDefaultPosition, wxDefaultSize,
+                                               KICAD_AUI_TB_STYLE | wxAUI_TB_HORZ_LAYOUT | wxAUI_TB_OVERFLOW );
+
+        m_auxiliaryToolBar->AddTool( ID_MODVIEW_FILTER_BY_LIBRARY,
+                              KiBitmap( module_library_list_xpm ),
+                              wxNullBitmap,
+                              true, NULL,
+                              _( "Filter footprint list by selected library" ),
+                              wxEmptyString );
+
+        m_auxiliaryToolBar->AddSeparator();
+
+        m_searchBox = new wxTextCtrl( m_auxiliaryToolBar, ID_MODVIEW_SEARCH_TEXT );
+        m_searchBox->SetMinSize( wxSize( 250, -1 ) );
+        m_searchBox->SetHint( _( "Enter filter text" ) );
+        m_searchBox->SetToolTip( _( "Filter footprint list\n"
+                                    "- Search is not case sensitive\n"
+                                    "- Wildcard search (?*) is supported\n"
+                                    "- Use : separator to include library name in search" ) );
+
+        m_auxiliaryToolBar->AddControl( m_searchBox );
+
+        m_auxiliaryToolBar->Realize();
+    }
+
     // Manage main toolbar, top pane
     m_auimgr.AddPane( m_mainToolBar, toolbarPaneInfo );
 
+    m_auimgr.AddPane( m_auxiliaryToolBar,
+                      wxAuiPaneInfo( toolbar ).Name( "m_searchToolBar" ).ToolbarPane()
+                      .Top().Row( 2 ).Layer( 1 ).MinSize( minsize ) );
+
     // Manage the list of libraries, left pane.
     m_auimgr.AddPane( m_libList,
-                      wxAuiPaneInfo( info ).Name( "m_libList" )
+                      info.Name( "m_libList" )
                       .Left().Row( 1 ).MinSize( minsize ) );
 
     // Manage the list of footprints, center pane.
     m_auimgr.AddPane( m_footprintList,
-                      wxAuiPaneInfo( info ).Name( "m_footprintList" )
+                      info.Name( "m_footprintList" )
                       .Left().Row( 2 ).MinSize( minsize ) );
 
     // Manage the draw panel, right pane.
@@ -319,6 +359,13 @@ FOOTPRINT_VIEWER_FRAME::~FOOTPRINT_VIEWER_FRAME()
 }
 
 
+void FOOTPRINT_VIEWER_FRAME::UpdateFilter( wxCommandEvent& event )
+{
+    wxString filter = m_searchBox->GetValue();
+
+    //TODO
+}
+
 
 void FOOTPRINT_VIEWER_FRAME::OnCloseWindow( wxCloseEvent& Event )
 {
@@ -364,13 +411,19 @@ void FOOTPRINT_VIEWER_FRAME::OnSetRelativeOffset( wxCommandEvent& event )
 
 void FOOTPRINT_VIEWER_FRAME::ReCreateLibraryList()
 {
+    m_footprintMap.clear();
+
     m_libList->Clear();
 
     std::vector< wxString > nicknames = Prj().PcbFootprintLibs()->GetLogicalLibs();
 
     for( unsigned ii = 0; ii < nicknames.size(); ii++ )
+    {
         m_libList->Append( nicknames[ii] );
 
+        m_footprintMap[ nicknames[ii] ] = std::vector<wxString>();
+    }
+
     // Search for a previous selection:
     int index =  m_libList->FindString( getCurNickname() );
 
diff --git a/pcbnew/modview_frame.h b/pcbnew/modview_frame.h
index bb6abcb..d0f65f9 100644
--- a/pcbnew/modview_frame.h
+++ b/pcbnew/modview_frame.h
@@ -31,6 +31,7 @@
 
 
 #include <wx/gdicmn.h>
+#include <wx/textctrl.h>
 
 class wxSashLayoutWindow;
 class wxListBox;
@@ -64,8 +65,16 @@ public:
 
 private:
 
-    wxListBox*          m_libList;               // The list of libs names
-    wxListBox*          m_footprintList;         // The list of footprint names
+    /*
+     * Map of all footprint files
+     * libName:fpNames
+     */
+    std::map< wxString, std::vector<wxString> > m_footprintMap;
+
+    wxTextCtrl*             m_searchBox;
+
+    wxListBox*              m_libList;          // The list of library names
+    wxListBox*              m_footprintList;    // The list of footprint names
 
     const wxString      getCurNickname();
     void                setCurNickname( const wxString& aNickname );
@@ -85,6 +94,18 @@ private:
     void UpdateTitle();
 
     /**
+     * Function SetFilterText
+     * Update the search string for footprint filtering
+     */
+    void UpdateFilter( wxCommandEvent& event );
+
+    void ClearFilter()
+    {
+        m_searchBox->SetValue( wxEmptyString );
+        m_searchBox->SetHint( _( "Enter filter text" ) );
+    }
+
+    /**
      * Function RedrawActiveWindow
      * Display the current selected component.
      * If the component is an alias, the ROOT component is displayed
diff --git a/pcbnew/pcbnew_id.h b/pcbnew/pcbnew_id.h
index 8c1bf27..2209d66 100644
--- a/pcbnew/pcbnew_id.h
+++ b/pcbnew/pcbnew_id.h
@@ -389,6 +389,9 @@ enum pcbnew_ids
     ID_MODVIEW_NEXT,
     ID_MODVIEW_SHOW_3D_VIEW,
     ID_MODVIEW_FOOTPRINT_EXPORT_TO_BOARD,
+    ID_MODVIEW_SEARCH_TEXT,
+    ID_MODVIEW_FILTER_BY_LIBRARY,
+
     ID_FOOTPRINT_WIZARD_WINDOW,
     ID_FOOTPRINT_WIZARD_PAGES,
     ID_FOOTPRINT_WIZARD_PARAMETERS,
-- 
2.7.4


Follow ups

References