270a6830b0
- Improve text layout / responsiveness - Fix missing stock tables warning not showing up in the right cases Fixes https://gitlab.com/kicad/code/kicad/-/issues/22923
357 lines
14 KiB
C++
357 lines
14 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
|
|
* @author Jon Evans <jon@craftyjon.com>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation, either version 3 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifndef LIBRARY_MANAGER_H
|
|
#define LIBRARY_MANAGER_H
|
|
|
|
#include <future>
|
|
#include <memory>
|
|
#include <shared_mutex>
|
|
|
|
#include <kicommon.h>
|
|
#include <libraries/library_table.h>
|
|
#include <io/io_base.h>
|
|
|
|
|
|
class LIBRARY_MANAGER;
|
|
class PROJECT;
|
|
|
|
|
|
/// Status of a library load managed by a library adapter
|
|
enum class LOAD_STATUS
|
|
{
|
|
INVALID,
|
|
LOADING,
|
|
LOADED,
|
|
LOAD_ERROR
|
|
};
|
|
|
|
/// The overall status of a loaded or loading library
|
|
struct KICOMMON_API LIB_STATUS
|
|
{
|
|
LOAD_STATUS load_status = LOAD_STATUS::INVALID;
|
|
std::optional<LIBRARY_ERROR> error;
|
|
};
|
|
|
|
|
|
/// Storage for an actual loaded library (including library content owned by the plugin)
|
|
struct KICOMMON_API LIB_DATA
|
|
{
|
|
std::unique_ptr<IO_BASE> plugin;
|
|
const LIBRARY_TABLE_ROW* row = nullptr;
|
|
LIB_STATUS status;
|
|
|
|
int modify_hash = -1;
|
|
std::vector<wxString> available_fields_cache;
|
|
};
|
|
|
|
|
|
/**
|
|
* The interface used by the classes that actually can load IO plugins for the
|
|
* different parts of KiCad and return concrete types (symbols, footprints, etc)
|
|
*/
|
|
class KICOMMON_API LIBRARY_MANAGER_ADAPTER
|
|
{
|
|
public:
|
|
/**
|
|
* Constructs a type-specific adapter into the library manager. The code for these
|
|
* generally resides in app-specific libraries (eeschema/pcbnew for example) rather than
|
|
* being in kicommon.
|
|
*
|
|
* @param aManager should usually be Pgm().GetLibraryManager() except in QA tests
|
|
*/
|
|
LIBRARY_MANAGER_ADAPTER( LIBRARY_MANAGER& aManager );
|
|
|
|
virtual ~LIBRARY_MANAGER_ADAPTER();
|
|
|
|
LIBRARY_MANAGER& Manager() const;
|
|
|
|
/// The type of library table this adapter works with
|
|
virtual LIBRARY_TABLE_TYPE Type() const = 0;
|
|
|
|
/// Retrieves the global library table for this adapter type
|
|
LIBRARY_TABLE* GlobalTable() const;
|
|
|
|
/// Retrieves the project library table for this adapter type, or nullopt if one doesn't exist
|
|
std::optional<LIBRARY_TABLE*> ProjectTable() const;
|
|
|
|
std::optional<wxString> FindLibraryByURI( const wxString& aURI ) const;
|
|
|
|
/// Returns a list of library nicknames that are available (skips any that failed to load)
|
|
std::vector<wxString> GetLibraryNames() const;
|
|
|
|
/**
|
|
* Test for the existence of \a aNickname in the library tables.
|
|
*
|
|
* @param aCheckEnabled if true will only return true for enabled libraries
|
|
* @return true if a library \a aNickname exists in the loaded tables.
|
|
*/
|
|
bool HasLibrary( const wxString& aNickname, bool aCheckEnabled = false ) const;
|
|
|
|
/// Deletes the given library from disk if it exists; returns true if deleted
|
|
bool DeleteLibrary( const wxString& aNickname );
|
|
|
|
std::optional<wxString> GetLibraryDescription( const wxString& aNickname ) const;
|
|
|
|
/// Like LIBRARY_MANAGER::Rows but filtered to the LIBRARY_TABLE_TYPE of this adapter
|
|
std::vector<LIBRARY_TABLE_ROW*> Rows( LIBRARY_TABLE_SCOPE aScope = LIBRARY_TABLE_SCOPE::BOTH,
|
|
bool aIncludeInvalid = false ) const;
|
|
|
|
/// Like LIBRARY_MANAGER::GetRow but filtered to the LIBRARY_TABLE_TYPE of this adapter
|
|
std::optional<LIBRARY_TABLE_ROW*> GetRow( const wxString& aNickname,
|
|
LIBRARY_TABLE_SCOPE aScope = LIBRARY_TABLE_SCOPE::BOTH ) const;
|
|
|
|
/// Like LIBRARY_MANAGER::FindRowByURI but filtered to the LIBRARY_TABLE_TYPE of this adapter
|
|
std::optional<LIBRARY_TABLE_ROW*> FindRowByURI( const wxString& aUri,
|
|
LIBRARY_TABLE_SCOPE aScope = LIBRARY_TABLE_SCOPE::BOTH ) const;
|
|
|
|
/// Notify the adapter that the active project has changed
|
|
virtual void ProjectChanged();
|
|
|
|
/// Notify the adapter that the global library tables have changed
|
|
void GlobalTablesChanged( std::initializer_list<LIBRARY_TABLE_TYPE> aChangedTables = {} );
|
|
|
|
void CheckTableRow( LIBRARY_TABLE_ROW& aRow );
|
|
|
|
/// Loads all available libraries for this adapter type in the background
|
|
void AsyncLoad();
|
|
|
|
virtual std::optional<LIB_STATUS> LoadOne( LIB_DATA* aLib ) = 0;
|
|
|
|
/// Returns async load progress between 0.0 and 1.0, or nullopt if load is not in progress
|
|
std::optional<float> AsyncLoadProgress() const;
|
|
|
|
void BlockUntilLoaded();
|
|
|
|
/// Aborts any async load in progress; blocks until fully done aborting.
|
|
/// This is the public interface to allow LIBRARY_MANAGER to abort loads before project changes.
|
|
void AbortAsyncLoad();
|
|
|
|
bool IsLibraryLoaded( const wxString& aNickname );
|
|
|
|
/// Returns the status of a loaded library, or nullopt if the library hasn't been loaded (yet)
|
|
std::optional<LIB_STATUS> GetLibraryStatus( const wxString& aNickname ) const;
|
|
|
|
/// Returns a list of all library nicknames and their status (even if they failed to load)
|
|
std::vector<std::pair<wxString, LIB_STATUS>> GetLibraryStatuses() const;
|
|
|
|
/// Returns all library load errors as newline-separated strings for display
|
|
wxString GetLibraryLoadErrors() const;
|
|
|
|
void ReloadLibraryEntry( const wxString& aNickname,
|
|
LIBRARY_TABLE_SCOPE aScope = LIBRARY_TABLE_SCOPE::BOTH );
|
|
|
|
/// Return true if the given nickname exists and is not a read-only library
|
|
virtual bool IsWritable( const wxString& aNickname ) const;
|
|
|
|
/// Creates the library (i.e. saves to disk) for the given row if it exists
|
|
bool CreateLibrary( const wxString& aNickname );
|
|
|
|
virtual bool SupportsConfigurationDialog( const wxString& aNickname ) const { return false; }
|
|
|
|
virtual void ShowConfigurationDialog( const wxString& aNickname, wxWindow* aParent ) const {};
|
|
|
|
virtual std::optional<LIBRARY_ERROR> LibraryError( const wxString& aNickname ) const;
|
|
|
|
protected:
|
|
virtual std::map<wxString, LIB_DATA>& globalLibs() = 0;
|
|
virtual std::map<wxString, LIB_DATA>& globalLibs() const = 0;
|
|
virtual std::shared_mutex& globalLibsMutex() = 0;
|
|
virtual std::shared_mutex& globalLibsMutex() const = 0;
|
|
|
|
/// Override in derived class to perform library-specific enumeration.
|
|
/// @param aUri is the pre-resolved library URI (must be resolved on the main thread
|
|
/// since URI expansion accesses PROJECT data that is not thread-safe).
|
|
virtual void enumerateLibrary( LIB_DATA* aLib, const wxString& aUri ) = 0;
|
|
|
|
static wxString getUri( const LIBRARY_TABLE_ROW* aRow );
|
|
|
|
std::optional<const LIB_DATA*> fetchIfLoaded( const wxString& aNickname ) const;
|
|
|
|
std::optional<LIB_DATA*> fetchIfLoaded( const wxString& aNickname );
|
|
|
|
/// Fetches a loaded library, triggering a load of that library if it isn't loaded yet
|
|
LIBRARY_RESULT<LIB_DATA*> loadIfNeeded( const wxString& aNickname );
|
|
|
|
LIBRARY_RESULT<LIB_DATA*> loadFromScope( const wxString& aNickname,
|
|
LIBRARY_TABLE_SCOPE aScope,
|
|
std::map<wxString, LIB_DATA>& aTarget,
|
|
std::shared_mutex& aMutex );
|
|
|
|
/// Aborts any async load in progress; blocks until fully done aborting
|
|
void abortLoad();
|
|
|
|
/// Creates a concrete plugin for the given row
|
|
virtual LIBRARY_RESULT<IO_BASE*> createPlugin( const LIBRARY_TABLE_ROW* row ) = 0;
|
|
|
|
virtual IO_BASE* plugin( const LIB_DATA* aRow ) = 0;
|
|
|
|
LIBRARY_MANAGER& m_manager;
|
|
|
|
// The actual library content is held in an associated IO plugin
|
|
std::map<wxString, LIB_DATA> m_libraries;
|
|
|
|
mutable std::shared_mutex m_librariesMutex;
|
|
|
|
std::atomic_bool m_abort;
|
|
std::vector<std::future<void>> m_futures;
|
|
|
|
std::atomic<size_t> m_loadCount{ 0 };
|
|
std::atomic<size_t> m_loadTotal{ 0 };
|
|
std::mutex m_loadMutex;
|
|
};
|
|
|
|
|
|
class KICOMMON_API LIBRARY_MANAGER
|
|
{
|
|
public:
|
|
LIBRARY_MANAGER();
|
|
|
|
~LIBRARY_MANAGER();
|
|
|
|
LIBRARY_MANAGER( const LIBRARY_MANAGER& ) = delete;
|
|
LIBRARY_MANAGER& operator=( const LIBRARY_MANAGER& ) = delete;
|
|
|
|
static wxString DefaultGlobalTablePath( LIBRARY_TABLE_TYPE aType );
|
|
|
|
static wxString StockTablePath( LIBRARY_TABLE_TYPE aType );
|
|
|
|
static bool IsTableValid( const wxString& aPath );
|
|
|
|
/// @return true if all required global tables are present on disk and valid
|
|
static bool GlobalTablesValid();
|
|
|
|
/// @return a list of global tables that are not valid (or an empty list if GlobalTablesValid() returns true)
|
|
static std::vector<LIBRARY_TABLE_TYPE> InvalidGlobalTables();
|
|
|
|
static bool CreateGlobalTable( LIBRARY_TABLE_TYPE aType, bool aPopulateDefaultLibraries );
|
|
|
|
/// (Re)loads the global library tables in the given list, or all tables if no list is given
|
|
void LoadGlobalTables( std::initializer_list<LIBRARY_TABLE_TYPE> aTablesToLoad = {} );
|
|
|
|
/// (Re)loads the project library tables in the given list, or all tables if no list is given
|
|
void LoadProjectTables( std::initializer_list<LIBRARY_TABLE_TYPE> aTablesToLoad = {} );
|
|
|
|
void ReloadTables( LIBRARY_TABLE_SCOPE aScope, std::initializer_list<LIBRARY_TABLE_TYPE> aTablesToLoad = {} );
|
|
|
|
/// Notify all adapters that the project has changed
|
|
void ProjectChanged();
|
|
|
|
/// Abort any async library loading operations in progress. This should be called before
|
|
/// modifying the project list to prevent race conditions with background threads that
|
|
/// access Prj().
|
|
void AbortAsyncLoads();
|
|
|
|
void RegisterAdapter( LIBRARY_TABLE_TYPE aType,
|
|
std::unique_ptr<LIBRARY_MANAGER_ADAPTER>&& aAdapter );
|
|
|
|
bool RemoveAdapter( LIBRARY_TABLE_TYPE aType, LIBRARY_MANAGER_ADAPTER* aAdapter );
|
|
|
|
std::optional<LIBRARY_MANAGER_ADAPTER*> Adapter( LIBRARY_TABLE_TYPE aType ) const;
|
|
|
|
/**
|
|
* Retrieves a given table; creating a new empty project table if a valid project is
|
|
* loaded and the given table type doesn't exist in the project.
|
|
* @param aType determines which type of table to return
|
|
* @param aScope determines whether to return a global or project table
|
|
* @return the given table if it exists (which should be the case unless a project table is
|
|
* requested and there is no valid project loaded)
|
|
*/
|
|
std::optional<LIBRARY_TABLE*> Table( LIBRARY_TABLE_TYPE aType,
|
|
LIBRARY_TABLE_SCOPE aScope );
|
|
|
|
/**
|
|
* Returns a flattened list of libraries of the given type
|
|
* @param aType determines which type of libraries to return (symbol, footprint, ...)
|
|
* @param aIncludeInvalid will include the nicknames of libraries even if they could not be
|
|
* loaded for some reason (file not found, etc)
|
|
* @return a list of library nicknames (the first part of a LIB_ID)
|
|
*/
|
|
std::vector<LIBRARY_TABLE_ROW*> Rows( LIBRARY_TABLE_TYPE aType,
|
|
LIBRARY_TABLE_SCOPE aScope = LIBRARY_TABLE_SCOPE::BOTH,
|
|
bool aIncludeInvalid = false ) const;
|
|
|
|
/**
|
|
*
|
|
* @param aType determines which type of libraries to return (symbol, footprint, ...)
|
|
* @param aScope determines whether to search project, global, or both library tables
|
|
* @param aNickname is the library nickname to retrieve
|
|
* @return the row, or a nullopt if it does not exist
|
|
*/
|
|
std::optional<LIBRARY_TABLE_ROW*> GetRow( LIBRARY_TABLE_TYPE aType, const wxString &aNickname,
|
|
LIBRARY_TABLE_SCOPE aScope = LIBRARY_TABLE_SCOPE::BOTH );
|
|
|
|
std::optional<LIBRARY_TABLE_ROW*> FindRowByURI( LIBRARY_TABLE_TYPE aType, const wxString &aUri,
|
|
LIBRARY_TABLE_SCOPE aScope = LIBRARY_TABLE_SCOPE::BOTH ) const;
|
|
|
|
void ReloadLibraryEntry( LIBRARY_TABLE_TYPE aType, const wxString& aNickname,
|
|
LIBRARY_TABLE_SCOPE aScope = LIBRARY_TABLE_SCOPE::BOTH );
|
|
|
|
void LoadProjectTables( const wxString& aProjectPath,
|
|
std::initializer_list<LIBRARY_TABLE_TYPE> aTablesToLoad = {} );
|
|
|
|
/**
|
|
* Return the full location specifying URI for the LIB, either in original UI form or
|
|
* in environment variable expanded form.
|
|
*
|
|
* @param aType determines which tables will be searched for the library
|
|
* @param aNickname is the library to look up
|
|
* @param aSubstituted Tells if caller wanted the substituted form, else not.
|
|
* @return the URI for the given library, or nullopt if the nickname is not a valid library
|
|
*/
|
|
std::optional<wxString> GetFullURI( LIBRARY_TABLE_TYPE aType, const wxString& aNickname,
|
|
bool aSubstituted = false );
|
|
|
|
static wxString GetFullURI( const LIBRARY_TABLE_ROW* aRow, bool aSubstituted = false );
|
|
|
|
static wxString ExpandURI( const wxString& aShortURI, const PROJECT& aProject );
|
|
|
|
static bool UrisAreEquivalent( const wxString& aURI1, const wxString& aURI2 );
|
|
|
|
private:
|
|
void loadTables( const wxString& aTablePath, LIBRARY_TABLE_SCOPE aScope,
|
|
std::vector<LIBRARY_TABLE_TYPE> aTablesToLoad = {} );
|
|
|
|
void loadNestedTables( LIBRARY_TABLE& aTable );
|
|
|
|
static wxString tableFileName( LIBRARY_TABLE_TYPE aType );
|
|
|
|
void createEmptyTable( LIBRARY_TABLE_TYPE aType, LIBRARY_TABLE_SCOPE aScope );
|
|
|
|
std::map<LIBRARY_TABLE_TYPE, std::unique_ptr<LIBRARY_TABLE>> m_tables;
|
|
|
|
/// Map of full URI to table object for tables that are referenced by global or project tables
|
|
std::map<wxString, std::unique_ptr<LIBRARY_TABLE>> m_childTables;
|
|
|
|
// TODO: support multiple projects
|
|
std::map<LIBRARY_TABLE_TYPE, std::unique_ptr<LIBRARY_TABLE>> m_projectTables;
|
|
|
|
std::map<LIBRARY_TABLE_TYPE, std::unique_ptr<LIBRARY_MANAGER_ADAPTER>> m_adapters;
|
|
|
|
mutable std::mutex m_adaptersMutex;
|
|
|
|
typedef std::tuple<LIBRARY_TABLE_TYPE, LIBRARY_TABLE_SCOPE, wxString> ROW_CACHE_KEY;
|
|
|
|
std::map<ROW_CACHE_KEY, LIBRARY_TABLE_ROW*> m_rowCache;
|
|
mutable std::mutex m_rowCacheMutex;
|
|
};
|
|
|
|
#endif //LIBRARY_MANAGER_H
|