From 6ebb3baa3b9c5f75fbe3ea1079ac6888d21dc977 Mon Sep 17 00:00:00 2001 From: qu1ck Date: Sat, 14 May 2022 11:23:43 -0700 Subject: [PATCH] PCM: autoload libs from installed packages to global lib tables Fixes: https://gitlab.com/kicad/code/kicad/-/issues/9713 --- common/fp_lib_table.cpp | 101 ++++ common/lib_table_base.cpp | 12 + common/settings/kicad_settings.cpp | 9 +- eeschema/symbol_lib_table.cpp | 103 ++++ include/lib_table_base.h | 10 +- include/settings/kicad_settings.h | 9 +- kicad/pcm/dialogs/panel_pcm_settings.cpp | 8 + kicad/pcm/dialogs/panel_pcm_settings_base.cpp | 41 +- kicad/pcm/dialogs/panel_pcm_settings_base.fbp | 467 +++++++++++++++--- kicad/pcm/dialogs/panel_pcm_settings_base.h | 8 + 10 files changed, 705 insertions(+), 63 deletions(-) diff --git a/common/fp_lib_table.cpp b/common/fp_lib_table.cpp index 2668b9a210..880a8d639e 100644 --- a/common/fp_lib_table.cpp +++ b/common/fp_lib_table.cpp @@ -30,11 +30,13 @@ #include #include #include +#include #include #include #include #include +#include #include #define OPT_SEP '|' ///< options separator character @@ -478,6 +480,66 @@ const wxString FP_LIB_TABLE::GlobalPathEnvVariableName() } +class PCM_FP_LIB_TRAVERSER final : public wxDirTraverser +{ +public: + explicit PCM_FP_LIB_TRAVERSER( const wxString& aPath, FP_LIB_TABLE& aTable, + const wxString& aPrefix ) : + m_path_prefix( aPath ), + m_lib_table( aTable ), m_lib_prefix( aPrefix ) + { + wxFileName f( aPath, "" ); + m_prefix_dir_count = f.GetDirCount(); + } + + wxDirTraverseResult OnFile( const wxString& aFilePath ) override { return wxDIR_CONTINUE; } + + wxDirTraverseResult OnDir( const wxString& dirPath ) override + { + wxFileName dir = wxFileName::DirName( dirPath ); + + // consider a directory to be a lib if it's name ends with .pretty and + // it is under $KICAD6_3RD_PARTY/footprints// i.e. has nested level of at least +3 + if( dirPath.EndsWith( ".pretty" ) && dir.GetDirCount() >= m_prefix_dir_count + 3 ) + { + wxArrayString parts = dir.GetDirs(); + parts.RemoveAt( 0, m_prefix_dir_count ); + parts.Insert( "${KICAD6_3RD_PARTY}", 0 ); + + wxString libPath = wxJoin( parts, '/' ); + + if( !m_lib_table.HasLibraryWithPath( libPath ) ) + { + wxString name = parts.Last().substr( 0, parts.Last().length() - 7 ); + wxString nickname = wxString::Format( "%s%s", m_lib_prefix, name ); + + if( m_lib_table.HasLibrary( nickname ) ) + { + int increment = 1; + do + { + nickname = wxString::Format( "%s%s_%d", m_lib_prefix, name, increment ); + increment++; + } while( m_lib_table.HasLibrary( nickname ) ); + } + + m_lib_table.InsertRow( + new FP_LIB_TABLE_ROW( nickname, libPath, wxT( "KiCad" ), wxEmptyString, + _( "Added by Plugin and Content Manager" ) ) ); + } + } + + return wxDIR_CONTINUE; + } + +private: + FP_LIB_TABLE& m_lib_table; + wxString m_path_prefix; + wxString m_lib_prefix; + size_t m_prefix_dir_count; +}; + + bool FP_LIB_TABLE::LoadGlobalTable( FP_LIB_TABLE& aTable ) { bool tableExists = true; @@ -518,6 +580,45 @@ bool FP_LIB_TABLE::LoadGlobalTable( FP_LIB_TABLE& aTable ) aTable.Load( fn.GetFullPath() ); + SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager(); + KICAD_SETTINGS* settings = mgr.GetAppSettings(); + + wxString packagesPath = Pgm().GetLocalEnvVariables().at( wxT( "KICAD6_3RD_PARTY" ) ).GetValue(); + + if( settings->m_PcmLibAutoAdd ) + { + // Scan for libraries in PCM packages directory + + wxFileName d( packagesPath, "" ); + d.AppendDir( "footprints" ); + + if( d.DirExists() ) + { + PCM_FP_LIB_TRAVERSER traverser( packagesPath, aTable, settings->m_PcmLibPrefix ); + wxDir dir( d.GetPath() ); + + dir.Traverse( traverser ); + } + } + + if( settings->m_PcmLibAutoRemove ) + { + // Remove PCM libraries that no longer exist + std::vector to_remove; + + for( size_t i = 0; i < aTable.GetCount(); i++ ) + { + LIB_TABLE_ROW& row = aTable.At( i ); + wxString path = row.GetFullURI( true ); + + if( path.StartsWith( packagesPath ) && !wxDir::Exists( path ) ) + to_remove.push_back( row.GetNickName() ); + } + + for( const wxString& nickName : to_remove ) + aTable.RemoveRow( aTable.FindRow( nickName ) ); + } + return tableExists; } diff --git a/common/lib_table_base.cpp b/common/lib_table_base.cpp index cab98be390..15b0ddca85 100644 --- a/common/lib_table_base.cpp +++ b/common/lib_table_base.cpp @@ -168,6 +168,18 @@ bool LIB_TABLE::HasLibrary( const wxString& aNickname, bool aCheckEnabled ) cons } +bool LIB_TABLE::HasLibraryWithPath( const wxString& aPath ) const +{ + for( const LIB_TABLE_ROW& row : rows ) + { + if( row.GetFullURI() == aPath ) + return true; + } + + return false; +} + + wxString LIB_TABLE::GetFullURI( const wxString& aNickname, bool aExpandEnvVars ) const { const LIB_TABLE_ROW* row = findRow( aNickname, true ); diff --git a/common/settings/kicad_settings.cpp b/common/settings/kicad_settings.cpp index 25abecdd40..a9a1084407 100644 --- a/common/settings/kicad_settings.cpp +++ b/common/settings/kicad_settings.cpp @@ -80,8 +80,13 @@ KICAD_SETTINGS::KICAD_SETTINGS() : m_params.emplace_back( new PARAM( "pcm.last_download_dir", &m_PcmLastDownloadDir, "" ) ); - m_params.emplace_back( - new PARAM( "pcm.check_for_updates", &m_PcmUpdateCheck, true ) ); + m_params.emplace_back( new PARAM( "pcm.check_for_updates", &m_PcmUpdateCheck, true ) ); + + m_params.emplace_back( new PARAM( "pcm.lib_auto_add", &m_PcmLibAutoAdd, true ) ); + + m_params.emplace_back( new PARAM( "pcm.lib_auto_remove", &m_PcmLibAutoRemove, true ) ); + + m_params.emplace_back( new PARAM( "pcm.lib_prefix", &m_PcmLibPrefix, "PCM_" ) ); } diff --git a/eeschema/symbol_lib_table.cpp b/eeschema/symbol_lib_table.cpp index 0a09426903..4ebc1f3d05 100644 --- a/eeschema/symbol_lib_table.cpp +++ b/eeschema/symbol_lib_table.cpp @@ -27,11 +27,15 @@ #include #include #include +#include #include #include #include #include +#include + + #define OPT_SEP '|' ///< options separator character using namespace LIB_TABLE_T; @@ -516,6 +520,67 @@ const wxString SYMBOL_LIB_TABLE::GlobalPathEnvVariableName() } +class PCM_SYM_LIB_TRAVERSER final : public wxDirTraverser +{ +public: + explicit PCM_SYM_LIB_TRAVERSER( const wxString& aPath, SYMBOL_LIB_TABLE& aTable, + const wxString& aPrefix ) : + m_path_prefix( aPath ), + m_lib_table( aTable ), m_lib_prefix( aPrefix ) + { + wxFileName f( aPath, "" ); + m_prefix_dir_count = f.GetDirCount(); + } + + wxDirTraverseResult OnFile( const wxString& aFilePath ) override + { + wxFileName file = wxFileName::FileName( aFilePath ); + + // consider a file to be a lib if it's name ends with .kicad_sym and + // it is under $KICAD6_3RD_PARTY/symbols// i.e. has nested level of at least +2 + if( file.GetExt() == wxT( "kicad_sym" ) && file.GetDirCount() >= m_prefix_dir_count + 2 ) + { + wxArrayString parts = file.GetDirs(); + parts.RemoveAt( 0, m_prefix_dir_count ); + parts.Insert( "${KICAD6_3RD_PARTY}", 0 ); + parts.Add( file.GetFullName() ); + + wxString libPath = wxJoin( parts, '/' ); + + if( !m_lib_table.HasLibraryWithPath( libPath ) ) + { + wxString name = parts.Last().substr( 0, parts.Last().length() - 10 ); + wxString nickname = wxString::Format( "%s%s", m_lib_prefix, name ); + + if( m_lib_table.HasLibrary( nickname ) ) + { + int increment = 1; + do + { + nickname = wxString::Format( "%s%s_%d", m_lib_prefix, name, increment ); + increment++; + } while( m_lib_table.HasLibrary( nickname ) ); + } + + m_lib_table.InsertRow( + new SYMBOL_LIB_TABLE_ROW( nickname, libPath, wxT( "KiCad" ), wxEmptyString, + _( "Added by Plugin and Content Manager" ) ) ); + } + } + + return wxDIR_CONTINUE; + } + + wxDirTraverseResult OnDir( const wxString& dirPath ) override { return wxDIR_CONTINUE; } + +private: + SYMBOL_LIB_TABLE& m_lib_table; + wxString m_path_prefix; + wxString m_lib_prefix; + size_t m_prefix_dir_count; +}; + + bool SYMBOL_LIB_TABLE::LoadGlobalTable( SYMBOL_LIB_TABLE& aTable ) { bool tableExists = true; @@ -556,6 +621,44 @@ bool SYMBOL_LIB_TABLE::LoadGlobalTable( SYMBOL_LIB_TABLE& aTable ) aTable.Load( fn.GetFullPath() ); + SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager(); + KICAD_SETTINGS* settings = mgr.GetAppSettings(); + + wxString packagesPath = Pgm().GetLocalEnvVariables().at( wxT( "KICAD6_3RD_PARTY" ) ).GetValue(); + + if( settings->m_PcmLibAutoAdd ) + { + // Scan for libraries in PCM packages directory + wxFileName d( packagesPath, "" ); + d.AppendDir( "symbols" ); + + if( d.DirExists() ) + { + PCM_SYM_LIB_TRAVERSER traverser( packagesPath, aTable, settings->m_PcmLibPrefix ); + wxDir dir( d.GetPath() ); + + dir.Traverse( traverser ); + } + } + + if( settings->m_PcmLibAutoRemove ) + { + // Remove PCM libraries that no longer exist + std::vector to_remove; + + for( size_t i = 0; i < aTable.GetCount(); i++ ) + { + LIB_TABLE_ROW& row = aTable.At( i ); + wxString path = row.GetFullURI( true ); + + if( path.StartsWith( packagesPath ) && !wxFile::Exists( path ) ) + to_remove.push_back( row.GetNickName() ); + } + + for( const wxString& nickName : to_remove ) + aTable.RemoveRow( aTable.FindRow( nickName ) ); + } + return tableExists; } diff --git a/include/lib_table_base.h b/include/lib_table_base.h index bac118d256..de95c50058 100644 --- a/include/lib_table_base.h +++ b/include/lib_table_base.h @@ -420,6 +420,14 @@ public: */ bool HasLibrary( const wxString& aNickname, bool aCheckEnabled = false ) const; + /** + * Test for the existence of \a aPath in the library table. + * + * @param aCheckEnabled if true will only return true for enabled libraries + * @return true if a library \a aNickname exists in the table. + */ + bool HasLibraryWithPath( const wxString& aPath ) const; + /** * Return the logical library names, all of them that are pertinent to * a look up done on this LIB_TABLE. @@ -450,7 +458,7 @@ public: * @param aRow is the row to remove * @return true if the row was found (and removed) */ - bool RemoveRow( LIB_TABLE_ROW* aRow ) + bool RemoveRow( const LIB_TABLE_ROW* aRow ) { for( auto iter = rows.begin(); iter != rows.end(); ++iter ) { diff --git a/include/settings/kicad_settings.h b/include/settings/kicad_settings.h index 507f3d7d9e..16503f2b26 100644 --- a/include/settings/kicad_settings.h +++ b/include/settings/kicad_settings.h @@ -46,7 +46,8 @@ public: */ int m_updateCheck; - enum UPDATE_CHECK { + enum UPDATE_CHECK + { UNINITIALIZED = 0, NOT_ALLOWED = 1, ALLOWED = 2 @@ -60,6 +61,12 @@ public: // This controls background update check for PCM. // It is set according to m_updateCheck on first start. bool m_PcmUpdateCheck; + // Auto add libs to global table + bool m_PcmLibAutoAdd; + // Auto remove libs + bool m_PcmLibAutoRemove; + // Generated library nickname prefix + wxString m_PcmLibPrefix; protected: virtual std::string getLegacyFrameName() const override { return "KicadFrame"; } diff --git a/kicad/pcm/dialogs/panel_pcm_settings.cpp b/kicad/pcm/dialogs/panel_pcm_settings.cpp index bc835de229..4a7b352d30 100644 --- a/kicad/pcm/dialogs/panel_pcm_settings.cpp +++ b/kicad/pcm/dialogs/panel_pcm_settings.cpp @@ -27,9 +27,11 @@ #include #include #include +#include PANEL_PCM_SETTINGS::PANEL_PCM_SETTINGS( wxWindow* parent ) : PANEL_PCM_SETTINGS_BASE( parent ) { + m_libHelp->SetFont( KIUI::GetInfoFont( this ).Italic() ); } @@ -39,6 +41,9 @@ bool PANEL_PCM_SETTINGS::TransferDataToWindow() KICAD_SETTINGS* settings = mgr.GetAppSettings(); m_updateCheck->SetValue( settings->m_PcmUpdateCheck ); + m_libAutoAdd->SetValue( settings->m_PcmLibAutoAdd ); + m_libAutoRemove->SetValue( settings->m_PcmLibAutoRemove ); + m_libPrefix->SetValue( settings->m_PcmLibPrefix ); return true; } @@ -50,6 +55,9 @@ bool PANEL_PCM_SETTINGS::TransferDataFromWindow() KICAD_SETTINGS* settings = mgr.GetAppSettings(); settings->m_PcmUpdateCheck = m_updateCheck->GetValue(); + settings->m_PcmLibAutoAdd = m_libAutoAdd->GetValue(); + settings->m_PcmLibAutoRemove = m_libAutoRemove->GetValue(); + settings->m_PcmLibPrefix = m_libPrefix->GetValue(); return true; } diff --git a/kicad/pcm/dialogs/panel_pcm_settings_base.cpp b/kicad/pcm/dialogs/panel_pcm_settings_base.cpp index e79e2b2b7b..b8e058e648 100644 --- a/kicad/pcm/dialogs/panel_pcm_settings_base.cpp +++ b/kicad/pcm/dialogs/panel_pcm_settings_base.cpp @@ -14,9 +14,46 @@ PANEL_PCM_SETTINGS_BASE::PANEL_PCM_SETTINGS_BASE( wxWindow* parent, wxWindowID i wxBoxSizer* bSizer1; bSizer1 = new wxBoxSizer( wxVERTICAL ); - m_updateCheck = new wxCheckBox( this, wxID_ANY, _("Check for package updates on startup"), wxDefaultPosition, wxDefaultSize, 0 ); + wxStaticBoxSizer* sbSizer1; + sbSizer1 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("General") ), wxVERTICAL ); + + m_updateCheck = new wxCheckBox( sbSizer1->GetStaticBox(), wxID_ANY, _("Check for package updates on startup"), wxDefaultPosition, wxDefaultSize, 0 ); m_updateCheck->SetValue(true); - bSizer1->Add( m_updateCheck, 0, wxALL, 5 ); + sbSizer1->Add( m_updateCheck, 0, wxALL, 5 ); + + + bSizer1->Add( sbSizer1, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + + wxStaticBoxSizer* sbSizer2; + sbSizer2 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Library package handling") ), wxVERTICAL ); + + m_libAutoAdd = new wxCheckBox( sbSizer2->GetStaticBox(), wxID_ANY, _("Automatically add installed libraries to global lib table"), wxDefaultPosition, wxDefaultSize, 0 ); + m_libAutoAdd->SetValue(true); + sbSizer2->Add( m_libAutoAdd, 0, wxALL, 5 ); + + m_libAutoRemove = new wxCheckBox( sbSizer2->GetStaticBox(), wxID_ANY, _("Automatically remove uninstalled libraries"), wxDefaultPosition, wxDefaultSize, 0 ); + m_libAutoRemove->SetValue(true); + sbSizer2->Add( m_libAutoRemove, 0, wxALL, 5 ); + + wxBoxSizer* bSizer2; + bSizer2 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText1 = new wxStaticText( sbSizer2->GetStaticBox(), wxID_ANY, _("Library nickname prefix:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText1->Wrap( -1 ); + bSizer2->Add( m_staticText1, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_libPrefix = new wxTextCtrl( sbSizer2->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer2->Add( m_libPrefix, 0, wxALL, 5 ); + + + sbSizer2->Add( bSizer2, 1, wxEXPAND, 5 ); + + m_libHelp = new wxStaticText( sbSizer2->GetStaticBox(), wxID_ANY, _("After packages are (un)installed KiCad may need to be restarted to reflect changes in the global library table."), wxDefaultPosition, wxDefaultSize, 0 ); + m_libHelp->Wrap( -1 ); + sbSizer2->Add( m_libHelp, 0, wxALL, 5 ); + + + bSizer1->Add( sbSizer2, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); this->SetSizer( bSizer1 ); diff --git a/kicad/pcm/dialogs/panel_pcm_settings_base.fbp b/kicad/pcm/dialogs/panel_pcm_settings_base.fbp index 92477d5a6c..6f7d748402 100644 --- a/kicad/pcm/dialogs/panel_pcm_settings_base.fbp +++ b/kicad/pcm/dialogs/panel_pcm_settings_base.fbp @@ -59,66 +59,419 @@ none 5 - wxALL + wxEXPAND|wxRIGHT|wxLEFT 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 + wxID_ANY - Check for package updates on startup - - 0 - - - 0 + General - 1 - m_updateCheck - 1 - - - protected - 1 - - Resizable - 1 - - - ; ; forward_declare - 0 - - - wxFILTER_NONE - wxDefaultValidator - - - - + sbSizer1 + wxVERTICAL + 1 + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Check for package updates on startup + + 0 + + + 0 + + 1 + m_updateCheck + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + 5 + wxEXPAND|wxRIGHT|wxLEFT + 0 + + wxID_ANY + Library package handling + + sbSizer2 + wxVERTICAL + 1 + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Automatically add installed libraries to global lib table + + 0 + + + 0 + + 1 + m_libAutoAdd + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Automatically remove uninstalled libraries + + 0 + + + 0 + + 1 + m_libAutoRemove + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 5 + wxEXPAND + 1 + + + bSizer2 + wxHORIZONTAL + none + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Library nickname prefix: + 0 + + 0 + + + 0 + + 1 + m_staticText1 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_libPrefix + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + After packages are (un)installed KiCad may need to be restarted to reflect changes in the global library table. + 0 + + 0 + + + 0 + + 1 + m_libHelp + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + diff --git a/kicad/pcm/dialogs/panel_pcm_settings_base.h b/kicad/pcm/dialogs/panel_pcm_settings_base.h index 8499823700..52084018e3 100644 --- a/kicad/pcm/dialogs/panel_pcm_settings_base.h +++ b/kicad/pcm/dialogs/panel_pcm_settings_base.h @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #include /////////////////////////////////////////////////////////////////////////// @@ -31,6 +34,11 @@ class PANEL_PCM_SETTINGS_BASE : public wxPanel protected: wxCheckBox* m_updateCheck; + wxCheckBox* m_libAutoAdd; + wxCheckBox* m_libAutoRemove; + wxStaticText* m_staticText1; + wxTextCtrl* m_libPrefix; + wxStaticText* m_libHelp; public: