37#include <wx/aui/framemanager.h>
40#include <wx/fileconf.h>
41#include <wx/filename.h>
44#include <wx/stdstream.h>
45#include <wx/wfstream.h>
50 std::replace( aPath.begin(), aPath.end(),
'.',
'/' );
51 aPath.insert( 0,
"/" );
53 nlohmann::json::json_pointer p;
57 p = nlohmann::json::json_pointer( aPath );
61 wxASSERT_MSG(
false, wxT(
"Invalid pointer path in PointerFromString!" ) );
69 int aSchemaVersion,
bool aCreateIfMissing,
bool aCreateIfDefault,
85 m_internals = std::make_unique<JSON_SETTINGS_INTERNALS>();
93 wxLogTrace(
traceSettings, wxT(
"Error: Could not create filename field for %s" ),
150 wxLogTrace(
traceSettings, wxT(
"param '%s' load err" ), param->GetJsonPath().c_str() );
163 bool migrated =
false;
164 bool legacy_migrated =
false;
168 auto migrateFromLegacy =
169 [&] ( wxFileName& aPath )
173 bool backed_up =
false;
176 if( aPath.IsDirWritable() )
178 temp.AssignTempFileName( aPath.GetFullPath() );
180 if( !wxCopyFile( aPath.GetFullPath(), temp.GetFullPath() ) )
183 wxT(
"%s: could not create temp file for migration" ),
195 wxConfigBase::DontCreateOnDemand();
196 auto cfg = std::make_unique<wxFileConfig>( wxT(
"" ), wxT(
"" ),
197 aPath.GetFullPath() );
205 wxT(
"%s: migrated; not all settings were found in legacy file" ),
211 wxLogTrace(
traceSettings, wxT(
"%s: migrated from legacy format" ),
219 if( !wxCopyFile( temp.GetFullPath(), aPath.GetFullPath() ) )
222 wxT(
"migrate; copy temp file %s to %s failed" ),
224 aPath.GetFullPath() );
227 if( !wxRemoveFile( temp.GetFullPath() ) )
230 wxT(
"migrate; failed to remove temp file %s" ),
231 temp.GetFullPath() );
236 legacy_migrated =
true;
241 if( aDirectory.empty() )
258 migrateFromLegacy(
path );
266 migrateFromLegacy(
path );
275 if( !
path.IsFileWritable() )
280 wxFFileInputStream fp(
path.GetFullPath(), wxT(
"rt" ) );
281 wxStdInputStream fstream( fp );
285 *
static_cast<nlohmann::json*
>(
m_internals.get() ) =
286 nlohmann::json::parse( fstream,
nullptr,
302 wxLogTrace(
traceSettings, wxT(
"%s: file version could not be read!" ),
309 wxLogTrace(
traceSettings, wxT(
"%s: attempting migration from version "
328 wxT(
"%s: warning: file version %d is newer than latest (%d)" ),
337 wxLogTrace(
traceSettings, wxT(
"%s exists but can't be opened for read" ),
341 catch( nlohmann::json::parse_error& error )
344 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ),
345 path.GetFullPath(), error.what() );
346 wxLogTrace(
traceSettings, wxT(
"Attempting migration in case file is in legacy "
348 migrateFromLegacy(
path );
357 settings->LoadFromFile();
359 wxLogTrace(
traceSettings, wxT(
"Loaded <%s> with schema %d" ),
372 if(
m_writeFile && ( legacy_migrated || migrated ) )
376 bool savedMigrated =
SaveToFile( aDirectory,
true );
378 if( savedMigrated && legacy_migrated && !wxRemoveFile(
path.GetFullPath() ) )
380 wxLogTrace(
traceSettings, wxT(
"Warning: could not remove legacy file %s" ),
381 path.GetFullPath() );
383 else if( !savedMigrated )
386 wxT(
"Migrated save of %s failed; leaving legacy file intact" ),
403 param->Store(
this );
405 catch(
const std::exception& e )
407 wxLogTrace(
traceSettings, wxT(
"param '%s' store err: %s" ),
408 param->GetJsonPath().c_str(), e.what() );
425 std::map<std::string, nlohmann::json> histories;
427 for(
const std::string& candidate : { std::string(
"system.file_history" ) } )
430 histories[candidate] =
GetJson( candidate ).value();
448 if( aDirectory.empty() )
455 wxString dir( aDirectory );
462 wxT(
"File for %s doesn't exist and m_createIfMissing == false; not saving" ),
468 if( !
path.DirExists() && !
path.Mkdir() )
470 wxLogTrace(
traceSettings, wxT(
"Warning: could not create path %s, can't save %s" ),
475 if( (
path.FileExists() && !
path.IsFileWritable() ) ||
476 ( !
path.FileExists() && !
path.IsDirWritable() ) )
478 wxLogTrace(
traceSettings, wxT(
"File for %s is read-only; not saving" ),
483 bool modified =
false;
487 wxCHECK2( settings,
continue );
489 modified |= settings->SaveToFile();
494 if( !modified && !aForce &&
path.FileExists() )
496 wxLogTrace(
traceSettings, wxT(
"%s contents not modified, skipping save" ),
503 wxT(
"%s contents still default and m_createIfDefault == false; not saving" ),
518 if( param->ClearUnknownKeys() )
522 toSave[p] = nlohmann::json( {} );
530 std::stringstream buffer;
531 buffer << std::setw( 2 ) << toSave << std::endl;
533 std::string payload = buffer.str();
544 catch( nlohmann::json::exception& error )
546 wxLogTrace(
traceSettings, wxT(
"Catch error: could not save %s. Json error %s" ),
552 wxLogTrace(
traceSettings, wxT(
"Error: could not save %s." ) );
572 std::stringstream buffer;
573 buffer << std::setw( 2 ) << *
m_internals << std::endl;
583 wxFFileInputStream fp( aPath, wxT(
"rt" ) );
584 wxStdInputStream fstream( fp );
588 *
static_cast<nlohmann::json*
>(
m_internals.get() ) =
589 nlohmann::json::parse( fstream,
nullptr,
598 catch( nlohmann::json::parse_error& error )
600 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ), aPath, error.what() );
613 nlohmann::json::json_pointer ptr =
m_internals->PointerFromString( aPath );
619 return std::optional<nlohmann::json>{
m_internals->at( ptr ) };
626 return std::optional<nlohmann::json>{};
630template<
typename ValueType>
633 if( std::optional<nlohmann::json> ret =
GetJson( aPath ) )
637 return ret->get<ValueType>();
685template<
typename ValueType>
688 m_internals->SetFromString( aPath, std::move( aVal ) );
698 unsigned int aValue );
700 unsigned long long aValue );
702 const char* aValue );
704 std::string aValue );
706 nlohmann::json aValue );
720 wxAuiPaneInfo aValue );
726 std::function<
bool()> aMigrator )
728 wxASSERT( aNewSchemaVersion > aOldSchemaVersion );
730 m_migrators[aOldSchemaVersion] = std::make_pair( aNewSchemaVersion, aMigrator );
736 int filever =
m_internals->Get<
int>(
"meta.version" );
744 wxLogTrace(
traceSettings, wxT(
"Migrator missing for %s version %d!" ),
745 typeid( *this ).name(),
750 std::pair<int, std::function<bool()>> pair =
m_migrators.at( filever );
754 wxLogTrace(
traceSettings, wxT(
"Migrated %s from %d to %d" ),
755 typeid( *this ).name(),
758 filever = pair.first;
763 wxLogTrace(
traceSettings, wxT(
"Migration failed for %s from %d to %d" ),
764 typeid( *this ).name(),
777 wxLogTrace(
traceSettings, wxT(
"MigrateFromLegacy() not implemented for %s" ),
778 typeid( *this ).name() );
788 if( aObj.contains( ptr ) && aObj.at( ptr ).is_string() )
790 aTarget = aObj.at( ptr ).get<wxString>();
803 if( aObj.contains( ptr ) && aObj.at( ptr ).is_boolean() )
805 aTarget = aObj.at( ptr ).get<
bool>();
818 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_integer() )
820 aTarget = aObj.at( ptr ).get<
int>();
829 unsigned int& aTarget )
833 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_unsigned() )
835 aTarget = aObj.at( ptr ).get<
unsigned int>();
843template<
typename ValueType>
845 const std::string& aDest )
849 if( aConfig->Read( aKey, &val ) )
853 ( *m_internals )[aDest] = val;
857 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacy!" ) );
872 const std::string& );
876 const std::string& );
880 const std::string& );
884 const std::string& aDest )
888 if( aConfig->Read( aKey, &str ) )
892 ( *m_internals )[aDest] = str.ToUTF8();
896 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyString!" ) );
908 const std::string& aDest )
912 if( aConfig->Read( aKey, &str ) )
919 nlohmann::json js = nlohmann::json::array( { color.
r, color.
g, color.
b, color.
a } );
920 ( *m_internals )[aDest] = std::move( js );
924 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyColor!" ) );
950 return aPtr == aSettings;
955 wxLogTrace(
traceSettings, wxT(
"Flush and release %s" ), ( *it )->GetFilename() );
968 if( std::optional<nlohmann::json> opt_json =
GetJson( aPath ) )
969 return wxString( opt_json->get<std::string>().c_str(), wxConvUTF8 );
978 ( *m_internals )[aPath] = aVal.ToUTF8();
982template<
typename ResultType>
984 ResultType aDefault )
986 ResultType ret = std::move( aDefault );
990 if( aJson.contains( aKey ) )
991 ret = aJson.at( aKey ).get<ResultType>();
1003 const std::string& aKey,
1004 std::string aDefault );
1009 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 m_fileSynced
True once the store has been synchronized with an on-disk file (loaded or saved)
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.
bool SetFromWxString(const wxString &aColorString)
Set color values by parsing a string using wxColour::Set().
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.