|
|
/*
* This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 Jon Evans <jon@craftyjon.com> * Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors. * * 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 _SETTINGS_MANAGER_H
#define _SETTINGS_MANAGER_H
#include <algorithm>
#include <mutex>
#include <typeinfo>
#include <core/wx_stl_compat.h> // for wxString hash
#include <settings/color_settings.h>
class COLOR_SETTINGS; class COMMON_SETTINGS; class KIWAY; class PROJECT; class PROJECT_FILE; class REPORTER; class wxSingleInstanceChecker; class wxFileName; class LOCKFILE;
/// Project settings path will be <projectname> + this
#define PROJECT_BACKUPS_DIR_SUFFIX wxT( "-backups" )
class KICOMMON_API SETTINGS_MANAGER { public: SETTINGS_MANAGER( bool aHeadless = false );
~SETTINGS_MANAGER();
/**
* @return true if settings load was successful */ bool IsOK() { return m_ok; }
/**
* Associate this setting manager with the given Kiway. * * @param aKiway is the kiway this settings manager should use */ void SetKiway( KIWAY* aKiway ) { m_kiway = aKiway; }
/**
* Takes ownership of the pointer passed in * @param aSettings is a settings object to register * @return a handle to the owned pointer */ template<typename T> T* RegisterSettings( T* aSettings, bool aLoadNow = true ) { return static_cast<T*>( registerSettings( aSettings, aLoadNow ) ); }
void Load();
void Load( JSON_SETTINGS* aSettings );
void Save();
void Save( JSON_SETTINGS* aSettings );
/**
* If the given settings object is registered, save it to disk and unregister it * @param aSettings is the object to release */ void FlushAndRelease( JSON_SETTINGS* aSettings, bool aSave = true );
/**
* Returns a handle to the a given settings by type * If the settings have already been loaded, returns the existing pointer. * If the settings have not been loaded, creates a new object owned by the * settings manager and returns a pointer to it. * * @tparam T is a type derived from APP_SETTINGS_BASE * @param aLoadNow is true to load the registered file from disk immediately * @return a pointer to a loaded settings object */ template<typename T> T* GetAppSettings() { T* ret = nullptr; size_t typeHash = typeid( T ).hash_code();
if( m_app_settings_cache.count( typeHash ) ) ret = static_cast<T*>( m_app_settings_cache.at( typeHash ) );
if( ret ) return ret;
auto it = std::find_if( m_settings.begin(), m_settings.end(), []( const std::unique_ptr<JSON_SETTINGS>& aSettings ) { return dynamic_cast<T*>( aSettings.get() ); } );
if( it != m_settings.end() ) { ret = dynamic_cast<T*>( it->get() ); } else { throw std::runtime_error( "Tried to GetAppSettings before registering" ); }
m_app_settings_cache[typeHash] = ret;
return ret; }
template<typename T> T* GetAppSettings( const wxString& aFilename ) { #ifndef __WXMAC__
return GetAppSettings<T>(); #else
T* ret = nullptr; size_t typeHash = typeid( T ).hash_code();
if( m_app_settings_cache.count( typeHash ) ) ret = static_cast<T*>( m_app_settings_cache.at( typeHash ) );
if( ret ) return ret;
auto it = std::find_if( m_settings.begin(), m_settings.end(), [&]( const std::unique_ptr<JSON_SETTINGS>& aSettings ) { return aSettings->GetFilename() == aFilename; } );
if( it != m_settings.end() ) { ret = static_cast<T*>( it->get() ); } else { throw std::runtime_error( "Tried to GetAppSettings before registering" ); }
m_app_settings_cache[typeHash] = ret;
return ret; #endif
}
/**
* Retrieves a color settings object that applications can read colors from. * If the given settings file cannot be found, returns the default settings. * * @param aName is the name of the color scheme to load * @return a loaded COLOR_SETTINGS object */ COLOR_SETTINGS* GetColorSettings( const wxString& aName = "user" );
std::vector<COLOR_SETTINGS*> GetColorSettingsList() { std::vector<COLOR_SETTINGS*> ret;
for( const std::pair<const wxString, COLOR_SETTINGS*>& entry : m_color_settings ) ret.push_back( entry.second );
std::sort( ret.begin(), ret.end(), []( COLOR_SETTINGS* a, COLOR_SETTINGS* b ) { return a->GetName() < b->GetName(); } );
return ret; }
/**
* Safely saves a COLOR_SETTINGS to disk, preserving any changes outside the given namespace. * * A color settings namespace is one of the top-level JSON objects like "board", etc. * This will perform a read-modify-write * * @param aSettings is a pointer to a valid COLOR_SETTINGS object managed by SETTINGS_MANAGER * @param aNamespace is the namespace of settings to save */ void SaveColorSettings( COLOR_SETTINGS* aSettings, const std::string& aNamespace = "" );
/**
* Registers a new color settings object with the given filename * @param aFilename is the location to store the new settings object * @return a pointer to the new object */ COLOR_SETTINGS* AddNewColorSettings( const wxString& aFilename );
/**
* Returns a color theme for storing colors migrated from legacy (5.x and earlier) settings, * creating the theme if necessary. This theme will be called "user.json" / "User". * @return the color settings to be used for migrating legacy settings */ COLOR_SETTINGS* GetMigratedColorSettings();
/**
* Retrieves the common settings shared by all applications * @return a pointer to a loaded COMMON_SETTINGS */ COMMON_SETTINGS* GetCommonSettings() const { return m_common_settings; }
/**
* Returns the path a given settings file should be loaded from / stored to. * @param aSettings is the settings object * @return a path based on aSettings->m_location */ wxString GetPathForSettingsFile( JSON_SETTINGS* aSettings );
/**
* Handles the initialization of the user settings directory and migration from previous * KiCad versions as needed. * * This method will check for the existence of the user settings path for this KiCad version. * If it exists, settings load will proceed normally using that path. * * If that directory is empty or does not exist, the migration wizard will be launched, which * will give users the option to migrate settings from a previous KiCad version (if one is * found), manually specify a directory to migrate fromm, or start with default settings. * * @return true if migration was successful or not necessary, false otherwise */ bool MigrateIfNeeded();
/**
* Helper for DIALOG_MIGRATE_SETTINGS to specify a source for migration * @param aSource is a directory containing settings files to migrate from (can be empty) */ void SetMigrationSource( const wxString& aSource ) { m_migration_source = aSource; }
void SetMigrateLibraryTables( bool aMigrate = true ) { m_migrateLibraryTables = aMigrate; }
/**
* Retrieves the name of the most recent previous KiCad version that can be found in the * user settings directory. For legacy versions (5.x, and 5.99 builds before this code was * written), this will return "5.x" * * @param aName is filled with the name of the previous version, if one exists * @return true if a previous version to migrate from exists */ bool GetPreviousVersionPaths( std::vector<wxString>* aName = nullptr );
/**
* Re-scans the color themes directory, reloading any changes it finds. */ void ReloadColorSettings();
/**
* Loads a project or sets up a new project with a specified path * @param aFullPath is the full path to the project * @param aSetActive if true will set the loaded project as the active project * @return true if the PROJECT_FILE was successfully loaded from disk */ bool LoadProject( const wxString& aFullPath, bool aSetActive = true );
/**
* Saves, unloads and unregisters the given PROJECT * @param aProject is the project object to unload * @param aSave if true will save the project before unloading * @return true if the PROJECT file was successfully saved */ bool UnloadProject( PROJECT* aProject, bool aSave = true );
/**
* Helper for checking if we have a project open * TODO: This should be deprecated along with Prj() once we support multiple projects fully * @return true if a call to Prj() will succeed */ bool IsProjectOpen() const;
/**
* Helper for checking if we have a project open that is not a dummy project * @return true if a call to Prj() will succeed and the project is not a dummy project */ bool IsProjectOpenNotDummy() const;
/**
* A helper while we are not MDI-capable -- return the one and only project * @return the loaded project */ PROJECT& Prj() const;
/**
* Retrieves a loaded project by name * @param aFullPath is the full path including name and extension to the project file * @return a pointer to the project if loaded, or nullptr */ PROJECT* GetProject( const wxString& aFullPath ) const;
/**
* @return a list of open projects */ std::vector<wxString> GetOpenProjects() const;
/**
* Saves a loaded project. * @param aFullPath is the project name to save. If empty, will save the first loaded project. * @param aProject is the project to save, or nullptr to save the active project (Prj() return) * @return true if save was successful */ bool SaveProject( const wxString& aFullPath = wxEmptyString, PROJECT* aProject = nullptr );
/**
* Sets the currently loaded project path and saves it (pointers remain valid) * Note that this will not modify the read-only state of the project, so it will have no effect * if the project is marked as read-only! * @param aFullPath is the full filename to set for the project * @param aProject is the project to save, or nullptr to save the active project (Prj() return) */ void SaveProjectAs( const wxString& aFullPath, PROJECT* aProject = nullptr );
/**
* Saves a copy of the current project under the given path. Will save the copy even if the * current project is marked as read-only. * @param aFullPath is the full filename to set for the project * @param aProject is the project to save, or nullptr to save the active project (Prj() return) */ void SaveProjectCopy( const wxString& aFullPath, PROJECT* aProject = nullptr );
/**
* @return the full path to where project backups should be stored */ wxString GetProjectBackupsPath() const;
/**
* Creates a backup archive of the current project * @param aReporter is used for progress reporting * @param aTarget is the full path to the backup file. If empty, will generate and return the * full path to the backup file. * @return true if everything succeeded */ bool BackupProject( REPORTER& aReporter, wxFileName& aTarget ) const;
/**
* Calls BackupProject if a new backup is needed according to the current backup policy. * @param aReporter is used for progress reporting * @return if everything succeeded */ bool TriggerBackupIfNeeded( REPORTER& aReporter ) const;
/**
* Checks if a given path is probably a valid KiCad configuration directory. * Actually it just checks if a file called "kicad_common" exists, because that's probably * good enough for now. * * @param aPath is the path to check * @return true if the path contains KiCad settings */ static bool IsSettingsPathValid( const wxString& aPath );
/**
* Returns the path where color scheme files are stored; creating it if missing * (normally ./colors/ under the user settings path) */ static wxString GetColorSettingsPath();
/**
* Parses the current KiCad build version and extracts the major and minor revision to use * as the name of the settings directory for this KiCad version. * * @return a string such as "5.1" */ static std::string GetSettingsVersion();
/**
* A proxy for PATHS::GetUserSettingsPath rather than fighting swig */ static wxString GetUserSettingsPath(); private: JSON_SETTINGS* registerSettings( JSON_SETTINGS* aSettings, bool aLoadNow = true );
/**
* Compares two settings versions, like "5.99" and "6.0" * @return -1 if aFirst is older than aSecond, 1 if aFirst is newer than aSecond, 0 otherwise */ static int compareVersions( const std::string& aFirst, const std::string& aSecond );
/**
* Extracts the numeric version from a given settings string * @param aVersionString is the string to split at the "." * @param aMajor will store the first part * @param aMinor will store the second part * @return true if extraction succeeded */ static bool extractVersion( const std::string& aVersionString, int* aMajor = nullptr, int* aMinor = nullptr );
/**
* Attempts to load a color theme by name (the color theme directory and .json ext are assumed) * @param aName is the filename of the color theme (without the extension or path) * @return the loaded settings, or nullptr if load failed */ COLOR_SETTINGS* loadColorSettingsByName( const wxString& aName );
COLOR_SETTINGS* registerColorSettings( const wxString& aFilename, bool aAbsolutePath = false );
void loadAllColorSettings();
/**
* Registers a PROJECT_FILE and attempts to load it from disk * @param aProject is the project object to load the file for * @return true if the PROJECT_FILE was successfully loaded */ bool loadProjectFile( PROJECT& aProject );
/**
* Optionally saves, and then unloads and unregisters the given PROJECT_FILE * @param aProject is the project object to unload the file for * @param aSave if true will save the project file before unloading * @return true if the PROJECT file was successfully saved */ bool unloadProjectFile( PROJECT* aProject, bool aSave );
// Helper to create built-in colors and register them
void registerBuiltinColorSettings();
private:
/// True if running outside a UI context
bool m_headless;
/// The kiway this settings manager interacts with
KIWAY* m_kiway;
std::vector<std::unique_ptr<JSON_SETTINGS>> m_settings;
std::unordered_map<wxString, COLOR_SETTINGS*> m_color_settings;
/// Cache for app settings
std::unordered_map<size_t, JSON_SETTINGS*> m_app_settings_cache;
// Convenience shortcut
COMMON_SETTINGS* m_common_settings;
wxString m_migration_source;
/// If true, the symbol and footprint library tables will be migrated from the previous version
bool m_migrateLibraryTables;
/// True if settings loaded successfully at construction
bool m_ok;
/// Loaded projects (ownership here)
std::vector<std::unique_ptr<PROJECT>> m_projects_list;
/// Loaded projects, mapped according to project full name
std::map<wxString, PROJECT*> m_projects;
/// Loaded project files, mapped according to project full name
std::map<wxString, PROJECT_FILE*> m_project_files;
/// Lock for loaded project (expand to multiple once we support MDI)
std::unique_ptr<LOCKFILE> m_project_lock;
static wxString backupDateTimeFormat; };
#endif
|