36#include <wx/aui/framemanager.h> 
   39#include <wx/fileconf.h> 
   40#include <wx/filename.h> 
   43#include <wx/stdstream.h> 
   44#include <wx/wfstream.h> 
   49    std::replace( aPath.begin(), aPath.end(), 
'.', 
'/' );
 
   50    aPath.insert( 0, 
"/" );
 
   52    nlohmann::json::json_pointer p;
 
   56        p = nlohmann::json::json_pointer( aPath );
 
   60        wxASSERT_MSG( 
false, wxT( 
"Invalid pointer path in PointerFromString!" ) );
 
 
   68                              int aSchemaVersion, 
bool aCreateIfMissing, 
bool aCreateIfDefault,
 
   83    m_internals = std::make_unique<JSON_SETTINGS_INTERNALS>();
 
   91        wxLogTrace( 
traceSettings, wxT( 
"Error: Could not create filename field for %s" ),
 
 
  148            wxLogTrace( 
traceSettings, wxT( 
"param '%s' load err" ), param->GetJsonPath().c_str() );
 
 
  161    bool migrated        = 
false;
 
  162    bool legacy_migrated = 
false;
 
  166    auto migrateFromLegacy =
 
  167            [&] ( wxFileName& aPath )
 
  171                bool backed_up = 
false;
 
  174                if( aPath.IsDirWritable() )
 
  176                    temp.AssignTempFileName( aPath.GetFullPath() );
 
  178                    if( !wxCopyFile( aPath.GetFullPath(), temp.GetFullPath() ) )
 
  181                                    wxT( 
"%s: could not create temp file for migration" ),
 
  193                wxConfigBase::DontCreateOnDemand();
 
  194                auto cfg = std::make_unique<wxFileConfig>( wxT( 
"" ), wxT( 
"" ),
 
  195                                                           aPath.GetFullPath() );
 
  203                                wxT( 
"%s: migrated; not all settings were found in legacy file" ),
 
  209                    wxLogTrace( 
traceSettings, wxT( 
"%s: migrated from legacy format" ),
 
  217                    if( !wxCopyFile( temp.GetFullPath(), aPath.GetFullPath() ) )
 
  220                                    wxT( 
"migrate; copy temp file %s to %s failed" ),
 
  222                                    aPath.GetFullPath() );
 
  225                    if( !wxRemoveFile( temp.GetFullPath() ) )
 
  228                                    wxT( 
"migrate; failed to remove temp file %s" ),
 
  229                                    temp.GetFullPath() );
 
  234                legacy_migrated = 
true;
 
  239    if( aDirectory.empty() )
 
  256            migrateFromLegacy( 
path );
 
  264                migrateFromLegacy( 
path );
 
  273        if( !
path.IsFileWritable() )
 
  278            wxFFileInputStream fp( 
path.GetFullPath(), wxT( 
"rt" ) );
 
  279            wxStdInputStream fstream( fp );
 
  283                *
static_cast<nlohmann::json*
>( 
m_internals.get() ) =
 
  284                        nlohmann::json::parse( fstream, 
nullptr,
 
  300                    wxLogTrace( 
traceSettings, wxT( 
"%s: file version could not be read!" ),
 
  307                    wxLogTrace( 
traceSettings, wxT( 
"%s: attempting migration from version " 
  326                                wxT( 
"%s: warning: file version %d is newer than latest (%d)" ),
 
  335                wxLogTrace( 
traceSettings, wxT( 
"%s exists but can't be opened for read" ),
 
  339        catch( nlohmann::json::parse_error& error )
 
  342            wxLogTrace( 
traceSettings, wxT( 
"Json parse error reading %s: %s" ),
 
  343                        path.GetFullPath(), error.what() );
 
  344            wxLogTrace( 
traceSettings, wxT( 
"Attempting migration in case file is in legacy " 
  346            migrateFromLegacy( 
path );
 
  355        settings->LoadFromFile();
 
  357    wxLogTrace( 
traceSettings, wxT( 
"Loaded <%s> with schema %d" ),
 
  364    if( 
m_writeFile && ( legacy_migrated || migrated ) )
 
  368            wxLogTrace( 
traceSettings, wxT( 
"Warning: could not remove legacy file %s" ),
 
  369                        path.GetFullPath() );
 
 
  386        param->Store( 
this );
 
 
  402    std::map<std::string, nlohmann::json> histories;
 
  404    for( 
const std::string& candidate : { std::string( 
"system.file_history" ) } )
 
  407            histories[candidate] = 
GetJson( candidate ).value();
 
 
  425    if( aDirectory.empty() )
 
  432        wxString dir( aDirectory );
 
  439                    wxT( 
"File for %s doesn't exist and m_createIfMissing == false; not saving" ),
 
  445    if( !
path.DirExists() && !
path.Mkdir() )
 
  447        wxLogTrace( 
traceSettings, wxT( 
"Warning: could not create path %s, can't save %s" ),
 
  452    if( ( 
path.FileExists() && !
path.IsFileWritable() ) ||
 
  453        ( !
path.FileExists() && !
path.IsDirWritable() ) )
 
  455        wxLogTrace( 
traceSettings, wxT( 
"File for %s is read-only; not saving" ),
 
  460    bool modified = 
false;
 
  464        wxCHECK2( settings, 
continue );
 
  466        modified |= settings->SaveToFile();
 
  471    if( !modified && !aForce && 
path.FileExists() )
 
  473        wxLogTrace( 
traceSettings, wxT( 
"%s contents not modified, skipping save" ),
 
  480                    wxT( 
"%s contents still default and m_createIfDefault == false; not saving" ),
 
  495        if( param->ClearUnknownKeys() )
 
  499            toSave[p] = nlohmann::json( {} );
 
  507        std::stringstream buffer;
 
  508        buffer << std::setw( 2 ) << toSave << std::endl;
 
  510        wxFFileOutputStream fileStream( 
path.GetFullPath(), 
"wb" );
 
  512        if( !fileStream.IsOk()
 
  513                || !fileStream.WriteAll( buffer.str().c_str(), buffer.str().size() ) )
 
  519    catch( nlohmann::json::exception& error )
 
  521        wxLogTrace( 
traceSettings, wxT( 
"Catch error: could not save %s. Json error %s" ),
 
  527        wxLogTrace( 
traceSettings, wxT( 
"Error: could not save %s." ) );
 
 
  544    std::stringstream buffer;
 
  545    buffer << std::setw( 2 ) << *
m_internals << std::endl;
 
 
  555        wxFFileInputStream fp( aPath, wxT( 
"rt" ) );
 
  556        wxStdInputStream   fstream( fp );
 
  560            *
static_cast<nlohmann::json*
>( 
m_internals.get() ) =
 
  561                    nlohmann::json::parse( fstream, 
nullptr,
 
  570    catch( nlohmann::json::parse_error& error )
 
  572        wxLogTrace( 
traceSettings, wxT( 
"Json parse error reading %s: %s" ), aPath, error.what() );
 
 
  585    nlohmann::json::json_pointer ptr = 
m_internals->PointerFromString( aPath );
 
  591            return std::optional<nlohmann::json>{ 
m_internals->at( ptr ) };
 
  598    return std::optional<nlohmann::json>{};
 
 
  602template<
typename ValueType>
 
  605    if( std::optional<nlohmann::json> ret = 
GetJson( aPath ) )
 
  609            return ret->get<ValueType>();
 
 
  657template<
typename ValueType>
 
  660    m_internals->SetFromString( aPath, std::move( aVal ) );
 
 
  670                                                             unsigned int       aValue );
 
  672                                                                   unsigned long long aValue );
 
  674                                                            const char*        aValue );
 
  676                                                            std::string        aValue );
 
  678                                                               nlohmann::json     aValue );
 
  692                                                              wxAuiPaneInfo      aValue );
 
  698                                       std::function<
bool()> aMigrator )
 
  700    wxASSERT( aNewSchemaVersion > aOldSchemaVersion );
 
  702    m_migrators[aOldSchemaVersion] = std::make_pair( aNewSchemaVersion, aMigrator );
 
 
  708    int filever = 
m_internals->Get<
int>( 
"meta.version" );
 
  716            wxLogTrace( 
traceSettings, wxT( 
"Migrator missing for %s version %d!" ),
 
  717                        typeid( *this ).name(),
 
  722        std::pair<int, std::function<bool()>> pair = 
m_migrators.at( filever );
 
  726            wxLogTrace( 
traceSettings, wxT( 
"Migrated %s from %d to %d" ),
 
  727                        typeid( *this ).name(),
 
  730            filever = pair.first;
 
  735            wxLogTrace( 
traceSettings, wxT( 
"Migration failed for %s from %d to %d" ),
 
  736                        typeid( *this ).name(),
 
 
  749    wxLogTrace( 
traceSettings, wxT( 
"MigrateFromLegacy() not implemented for %s" ),
 
  750                typeid( *this ).name() );
 
 
  760    if( aObj.contains( ptr ) && aObj.at( ptr ).is_string() )
 
  762        aTarget = aObj.at( ptr ).get<wxString>();
 
 
  775    if( aObj.contains( ptr ) && aObj.at( ptr ).is_boolean() )
 
  777        aTarget = aObj.at( ptr ).get<
bool>();
 
 
  790    if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_integer() )
 
  792        aTarget = aObj.at( ptr ).get<
int>();
 
 
  801                                  unsigned int& aTarget )
 
  805    if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_unsigned() )
 
  807        aTarget = aObj.at( ptr ).get<
unsigned int>();
 
 
  815template<
typename ValueType>
 
  817                             const std::string& aDest )
 
  821    if( aConfig->Read( aKey, &val ) )
 
  825            ( *m_internals )[aDest] = val;
 
  829            wxASSERT_MSG( 
false, wxT( 
"Could not write value in fromLegacy!" ) );
 
 
  844                                                  const std::string& );
 
  848                                                     const std::string& );
 
  852                                                   const std::string& );
 
  856                                      const std::string& aDest )
 
  860    if( aConfig->Read( aKey, &str ) )
 
  864            ( *m_internals )[aDest] = str.ToUTF8();
 
  868            wxASSERT_MSG( 
false, wxT( 
"Could not write value in fromLegacyString!" ) );
 
 
  880    const std::string& aDest )
 
  884    if( aConfig->Read( aKey, &str ) )
 
  887        color.SetFromWxString( str );
 
  892            ( *m_internals )[aDest] = std::move( js );
 
  896            wxASSERT_MSG( 
false, wxT( 
"Could not write value in fromLegacyColor!" ) );
 
 
  922                                return aPtr == aSettings;
 
  927        wxLogTrace( 
traceSettings, wxT( 
"Flush and release %s" ), ( *it )->GetFilename() );
 
 
  940    if( std::optional<nlohmann::json> opt_json = 
GetJson( aPath ) )
 
  941        return wxString( opt_json->get<std::string>().c_str(), wxConvUTF8 );
 
 
  950    ( *m_internals )[aPath] = aVal.ToUTF8();
 
 
  954template<
typename ResultType>
 
  956                                          ResultType aDefault )
 
  958    ResultType ret = std::move( aDefault );
 
  962        if( aJson.contains( aKey ) )
 
  963            ret = aJson.at( aKey ).get<ResultType>();
 
 
  975                                                        const std::string& aKey,
 
  976                                                        std::string aDefault );
 
  981                                                 const std::string& aKey, 
bool aDefault );
 
static nlohmann::json::json_pointer PointerFromString(std::string aPath)
Builds a JSON pointer based on a given string.
 
bool fromLegacyString(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy wxConfig string value to a given JSON pointer value.
 
bool fromLegacy(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy wxConfig value to a given JSON pointer value.
 
virtual wxString getFileExt() const
 
virtual std::map< std::string, nlohmann::json > GetFileHistories()
 
void Set(const std::string &aPath, ValueType aVal)
Stores a value into the JSON document Will throw an exception if ValueType isn't something that the l...
 
std::optional< nlohmann::json > GetJson(const std::string &aPath) const
Fetches a JSON object that is a subset of this JSON_SETTINGS object, using a path of the form "key1....
 
wxString m_filename
The filename (not including path) of this settings file (inicode)
 
SETTINGS_MANAGER * m_manager
A pointer to the settings manager managing this file (may be null)
 
bool Contains(const std::string &aPath) const
 
bool LoadFromRawFile(const wxString &aPath)
 
bool m_isFutureFormat
Set to true if this settings is loaded from a file with a newer schema version than is known.
 
std::vector< NESTED_SETTINGS * > m_nested_settings
Nested settings files that live inside this one, if any.
 
bool m_modified
True if the JSON data store has been written to since the last file write.
 
virtual bool LoadFromFile(const wxString &aDirectory="")
Loads the backing file from disk and then calls Load()
 
bool m_createIfDefault
Whether or not the backing store file should be created if all parameters are still at their default ...
 
bool m_writeFile
Whether or not the backing store file should be written.
 
static bool SetIfPresent(const nlohmann::json &aObj, const std::string &aPath, wxString &aTarget)
Sets the given string if the given key/path is present.
 
virtual void Load()
Updates the parameters of this object based on the current JSON document contents.
 
void ResetToDefaults()
Resets all parameters to default values.
 
bool fromLegacyColor(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy COLOR4D stored in a wxConfig string to a given JSON pointer value.
 
wxString GetFullFilename() const
 
bool m_createIfMissing
Whether or not the backing store file should be created it if doesn't exist.
 
bool Migrate()
Migrates the schema of this settings from the version in the file to the latest version.
 
std::optional< ValueType > Get(const std::string &aPath) const
Fetches a value from within the JSON document.
 
std::vector< PARAM_BASE * > m_params
The list of parameters (owned by this object)
 
virtual bool MigrateFromLegacy(wxConfigBase *aLegacyConfig)
Migrates from wxConfig to JSON-based configuration.
 
const std::string FormatAsString()
 
void registerMigration(int aOldSchemaVersion, int aNewSchemaVersion, std::function< bool(void)> aMigrator)
Registers a migration from one schema version to another.
 
nlohmann::json & At(const std::string &aPath)
Wrappers for the underlying JSON API so that most consumers don't need json.hpp All of these function...
 
JSON_SETTINGS_INTERNALS * Internals()
 
static ResultType fetchOrDefault(const nlohmann::json &aJson, const std::string &aKey, ResultType aDefault=ResultType())
Helper to retrieve a value from a JSON object (dictionary) as a certain result type.
 
void ReleaseNestedSettings(NESTED_SETTINGS *aSettings)
Saves and frees a nested settings object, if it exists within this one.
 
JSON_SETTINGS(const wxString &aFilename, SETTINGS_LOC aLocation, int aSchemaVersion)
 
bool m_deleteLegacyAfterMigration
Whether or not to delete legacy file after migration.
 
std::unique_ptr< JSON_SETTINGS_INTERNALS > m_internals
 
friend class NESTED_SETTINGS
 
SETTINGS_LOC m_location
The location of this settings file (.
 
virtual bool SaveToFile(const wxString &aDirectory="", bool aForce=false)
Calls Store() and then writes the contents of the JSON document to a file.
 
virtual wxString getLegacyFileExt() const
 
bool m_resetParamsIfMissing
Whether or not to set parameters to their default value if missing from JSON on Load()
 
std::map< int, std::pair< int, std::function< bool()> > > m_migrators
A map of starting schema version to a pair of <ending version, migrator function>
 
int m_schemaVersion
Version of this settings schema.
 
wxString m_legacy_filename
The filename of the wxConfig legacy file (if different from m_filename)
 
void AddNestedSettings(NESTED_SETTINGS *aSettings)
Transfers ownership of a given NESTED_SETTINGS to this object.
 
virtual bool Store()
Stores the current parameters into the JSON document represented by this object Note: this doesn't do...
 
wxString GetFilename() const
 
A color representation with 4 components: red, green, blue, alpha.
 
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
 
void SetParent(JSON_SETTINGS *aParent, bool aLoadFromFile=true)
 
std::vector< FAB_LAYER_COLOR > dummy
 
Common grid settings, available to every frame.