← Back to team overview

kicad-developers team mailing list archive

[PATCH] Add live footprint filtering in modview window

 

In the footprint viewer window it is often hard to find the exact footprint
you want across multiple libraries

The attached patch set adds a search filter input to the toolbar.

Entering a filter string in this box updates the list of available
libraries and footprints.

Features:

1. Wildcard searching with * and ?
2. Include library name in the filter with the : character
3. Not case sensitive

To accomplish this, all the libraries have to be loaded first, rather than
individually when the library is selected.

Because of this change, I have also added a "reload footprint libraries"
button to the toolbar.

This is a feature I have wanted for a while, I think it saves a lot of
effort when searching for footprints.

Cheers,
Oliver
From 6bfa2abd7bf5b360cd5b98d6451a0f5b36dcc550 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/5] 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 b4c25ff..250bb67 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

From f36083d9f4fe6774070fca25dcfb91c2907bdced 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/5] 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 155ad4b4473de419b6a8449065faae8723ae4fcd 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/5] 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 2a62a307ffca1491ed52b2c10d15d82c5bbc3d83 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/5] 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 250bb67..488b67a 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 4666685181b07573cde8c0b8fbb5a31de0212f68 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/5] 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


Follow ups