Recursively add KiCad and GEDA libraries inside a folder

CHANGED: KiCad and GEDA libraries can now be added recursively by selecting a parent folder.
This commit is contained in:
Mark Roszko 2020-01-21 10:39:07 +00:00 committed by Ian McInerney
parent a91ca785e5
commit 10a862c458
1 changed files with 131 additions and 19 deletions

View File

@ -61,16 +61,21 @@
#include <pcbnew_id.h> // For ID_PCBNEW_END_LIST
// clang-format off
/**
* Container that describes file type info for the add a library options
*/
struct supportedFileType
{
wxString m_Description; ///< Description shown in the file picker dialog
wxString m_FileFilter; ///< In case of folders it stands for extensions of files stored inside
bool m_IsFile; ///< Whether the library is a folder or a file
wxString m_Description; ///< Description shown in the file picker dialog
wxString m_FileFilter; ///< Filter used for file pickers if m_IsFile is true
wxString m_FolderSearchExtension; ///< In case of folders it stands for extensions of files stored inside
bool m_IsFile; ///< Whether the library is a folder or a file
IO_MGR::PCB_FILE_T m_Plugin;
};
/*
* Event IDs for the menu items in the split button menu
/**
* Event IDs for the menu items in the split button menu for add a library
*/
enum {
ID_PANEL_FPLIB_ADD_KICADMOD = ID_PCBNEW_END_LIST,
@ -79,18 +84,99 @@ enum {
ID_PANEL_FPLIB_ADD_GEDA,
};
/*
* Map of event id as key to the file type struct
*/
static const std::map<int, supportedFileType> fileTypes =
/**
* Map with event id as the key to supported file types that will be listed for the add a library option.
*
*/
static const std::map<int, supportedFileType>& fileTypes()
{
{ ID_PANEL_FPLIB_ADD_KICADMOD, { "KiCad (folder with .kicad_mod files)", "", false, IO_MGR::KICAD_SEXP } },
{ ID_PANEL_FPLIB_ADD_EAGLE6, { "Eagle 6.x (*.lbr)", EagleFootprintLibPathWildcard(), true, IO_MGR::EAGLE } },
{ ID_PANEL_FPLIB_ADD_KICADLEGACY, { "KiCad legacy (*.mod)", LegacyFootprintLibPathWildcard(), true, IO_MGR::LEGACY } },
{ ID_PANEL_FPLIB_ADD_GEDA, { "Geda (folder with *.fp files)", "", false, IO_MGR::GEDA_PCB } },
};
/*
* This is wrapped inside a function to prevent a static initialization order fiasco with the file extension
* variables. Once C++20 is allowed in KiCad code, those file extensions can be made constexpr and this can
* be removed from a function call and placed in the file normally.
*/
static const std::map<int, supportedFileType> fileTypes =
{
{ ID_PANEL_FPLIB_ADD_KICADMOD, { "KiCad (folder with .kicad_mod files)", "", KiCadFootprintFileExtension, false, IO_MGR::KICAD_SEXP } },
{ ID_PANEL_FPLIB_ADD_EAGLE6, { "Eagle 6.x (*.lbr)", EagleFootprintLibPathWildcard(), "", true, IO_MGR::EAGLE } },
{ ID_PANEL_FPLIB_ADD_KICADLEGACY, { "KiCad legacy (*.mod)", LegacyFootprintLibPathWildcard(), "", true, IO_MGR::LEGACY } },
{ ID_PANEL_FPLIB_ADD_GEDA, { "Geda (folder with *.fp files)", "", GedaPcbFootprintLibFileExtension, false, IO_MGR::GEDA_PCB } },
};
return fileTypes;
}
// clang-format on
/**
* Traverser implementation that looks to find any and all "folder" libraries by looking for files
* with a specific extension inside folders
*/
class LIBRARY_TRAVERSER : public wxDirTraverser
{
public:
LIBRARY_TRAVERSER( wxString aSearchExtension ) : m_searchExtension( aSearchExtension )
{
}
virtual wxDirTraverseResult OnFile( const wxString& aFileName )
{
wxFileName file( aFileName );
if( m_searchExtension.IsSameAs( file.GetExt(), false ) )
{
m_foundDirs.insert( { m_currentDir, 1 } );
}
return wxDIR_CONTINUE;
}
virtual wxDirTraverseResult OnOpenError( const wxString& aOpenErrorName )
{
m_failedDirs.insert( { aOpenErrorName, 1 } );
return wxDIR_IGNORE;
}
bool HasDirectoryOpenFailures()
{
return m_failedDirs.size() > 0;
}
virtual wxDirTraverseResult OnDir( const wxString& aDirName )
{
m_currentDir = aDirName;
return wxDIR_CONTINUE;
}
void GetPaths( wxArrayString& aPathArray )
{
for( auto foundDirsPair : m_foundDirs )
{
aPathArray.Add( foundDirsPair.first );
}
}
void GetFailedPaths( wxArrayString& aPathArray )
{
for( auto failedDirsPair : m_failedDirs )
{
aPathArray.Add( failedDirsPair.first );
}
}
private:
wxString m_searchExtension;
wxString m_currentDir;
std::unordered_map<wxString, int> m_foundDirs;
std::unordered_map<wxString, int> m_failedDirs;
};
/**
* This class builds a wxGridTableBase by wrapping an #FP_LIB_TABLE object.
*/
@ -353,7 +439,7 @@ PANEL_FP_LIB_TABLE::PANEL_FP_LIB_TABLE( DIALOG_EDIT_LIBRARY_TABLES* aParent,
// Populate the browse library options
wxMenu* browseMenu = m_browseButton->GetSplitButtonMenu();
for( auto& fileType : fileTypes )
for( auto& fileType : fileTypes() )
{
browseMenu->Append( fileType.first, fileType.second.m_Description );
@ -618,14 +704,14 @@ void PANEL_FP_LIB_TABLE::browseLibrariesHandler( wxCommandEvent& event )
if( event.GetEventType() == wxEVT_BUTTON )
{
// Let's default to adding a kicad module for just the module
fileTypeIt = fileTypes.find( ID_PANEL_FPLIB_ADD_KICADMOD );
fileTypeIt = fileTypes().find( ID_PANEL_FPLIB_ADD_KICADMOD );
}
else
{
fileTypeIt = fileTypes.find( event.GetId() );
fileTypeIt = fileTypes().find( event.GetId() );
}
if( fileTypeIt == fileTypes.end() )
if( fileTypeIt == fileTypes().end() )
{
wxLogWarning( "File type selection event received but could not find the file type in the table" );
return;
@ -666,7 +752,33 @@ void PANEL_FP_LIB_TABLE::browseLibrariesHandler( wxCommandEvent& event )
if( result == wxID_CANCEL )
return;
files.Add( dlg.GetPath() );
// is there a file extension configured to hunt out their containing folders?
if( fileType.m_FolderSearchExtension != "" )
{
wxDir rootDir( dlg.GetPath() );
LIBRARY_TRAVERSER traverser( fileType.m_FolderSearchExtension );
rootDir.Traverse( traverser );
traverser.GetPaths( files );
if( traverser.HasDirectoryOpenFailures() )
{
wxArrayString failedDirs;
traverser.GetPaths( failedDirs );
wxString detailedMsg = _( "The following directories could not be opened: \n" );
for( auto& path : failedDirs )
detailedMsg << path << "\n";
DisplayErrorMessage( this, _( "Failed to open directories to look for libraries" ),
detailedMsg );
}
}
else
{
files.Add( dlg.GetPath() );
}
m_lastBrowseDir = dlg.GetPath();
}