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>
51 std::replace( aPath.begin(), aPath.end(),
'.',
'/' );
52 aPath.insert( 0,
"/" );
54 nlohmann::json::json_pointer p;
58 p = nlohmann::json::json_pointer( aPath );
62 wxASSERT_MSG(
false, wxT(
"Invalid pointer path in PointerFromString!" ) );
70 int aSchemaVersion,
bool aCreateIfMissing,
bool aCreateIfDefault,
72 m_filename( aFilename ),
73 m_legacy_filename(
"" ),
74 m_location( aLocation ),
75 m_createIfMissing( aCreateIfMissing ),
76 m_createIfDefault( aCreateIfDefault ),
77 m_writeFile( aWriteFile ),
78 m_deleteLegacyAfterMigration( true ),
79 m_resetParamsIfMissing( true ),
80 m_schemaVersion( aSchemaVersion ),
83 m_internals = std::make_unique<JSON_SETTINGS_INTERNALS>();
91 wxLogTrace(
traceSettings, wxT(
"Error: Could not create filename field for %s" ),
145 wxLogTrace(
traceSettings, wxT(
"param '%s' load err" ), param->GetJsonPath().c_str() );
158 bool migrated =
false;
159 bool legacy_migrated =
false;
163 auto migrateFromLegacy = [&] ( wxFileName& aPath ) {
165 bool backed_up =
false;
168 if( aPath.IsDirWritable() )
170 temp.AssignTempFileName( aPath.GetFullPath() );
172 if( !wxCopyFile( aPath.GetFullPath(), temp.GetFullPath() ) )
174 wxLogTrace(
traceSettings, wxT(
"%s: could not create temp file for migration" ),
184 wxConfigBase::DontCreateOnDemand();
185 auto cfg = std::make_unique<wxFileConfig>( wxT(
"" ), wxT(
"" ), aPath.GetFullPath() );
193 wxT(
"%s: migrated; not all settings were found in legacy file" ),
206 if( !wxCopyFile( temp.GetFullPath(), aPath.GetFullPath() ) )
209 wxT(
"migrate; copy temp file %s to %s failed" ),
210 temp.GetFullPath(), aPath.GetFullPath() );
213 if( !wxRemoveFile( temp.GetFullPath() ) )
216 wxT(
"migrate; failed to remove temp file %s" ),
217 temp.GetFullPath() );
222 legacy_migrated =
true;
227 if( aDirectory.empty() )
234 wxString dir( aDirectory );
245 migrateFromLegacy(
path );
253 migrateFromLegacy(
path );
262 if( !
path.IsFileWritable() )
267 wxFFileInputStream fp(
path.GetFullPath(), wxT(
"rt" ) );
268 wxStdInputStream fstream( fp );
272 *
static_cast<nlohmann::json*
>(
m_internals.get() ) =
273 nlohmann::json::parse( fstream,
nullptr,
289 wxLogTrace(
traceSettings, wxT(
"%s: file version could not be read!" ),
296 wxLogTrace(
traceSettings, wxT(
"%s: attempting migration from version %d to %d" ),
311 wxT(
"%s: warning: file version %d is newer than latest (%d)" ),
317 wxLogTrace(
traceSettings, wxT(
"%s exists but can't be opened for read" ),
321 catch( nlohmann::json::parse_error& error )
324 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ),
325 path.GetFullPath(), error.what() );
326 wxLogTrace(
traceSettings, wxT(
"Attempting migration in case file is in legacy format" ) );
327 migrateFromLegacy(
path );
336 settings->LoadFromFile();
341 if(
m_writeFile && ( legacy_migrated || migrated ) )
345 wxLogTrace(
traceSettings, wxT(
"Warning: could not remove legacy file %s" ),
346 path.GetFullPath() );
360 bool modified =
false;
364 modified |= !param->MatchesFile(
this );
365 param->Store(
this );
390 if( aDirectory.empty() )
397 wxString dir( aDirectory );
404 wxT(
"File for %s doesn't exist and m_createIfMissing == false; not saving" ),
410 if( !
path.DirExists() && !
path.Mkdir() )
412 wxLogTrace(
traceSettings, wxT(
"Warning: could not create path %s, can't save %s" ),
417 if( (
path.FileExists() && !
path.IsFileWritable() ) ||
418 ( !
path.FileExists() && !
path.IsDirWritable() ) )
424 bool modified =
false;
427 modified |= settings->SaveToFile();
431 if( !modified && !aForce &&
path.FileExists() )
439 wxT(
"%s contents still default and m_createIfDefault == false; not saving" ),
455 std::stringstream buffer;
456 buffer << std::setw( 2 ) << toSave << std::endl;
458 wxFFileOutputStream fileStream(
path.GetFullPath(),
"wb" );
460 if( !fileStream.IsOk()
461 || !fileStream.WriteAll( buffer.str().c_str(), buffer.str().size() ) )
467 catch( nlohmann::json::exception& error )
469 wxLogTrace(
traceSettings, wxT(
"Catch error: could not save %s. Json error %s" ),
475 wxLogTrace(
traceSettings, wxT(
"Error: could not save %s." ) );
489 std::stringstream buffer;
490 buffer << std::setw( 2 ) << *
m_internals << std::endl;
500 wxFFileInputStream fp( aPath, wxT(
"rt" ) );
501 wxStdInputStream fstream( fp );
505 *
static_cast<nlohmann::json*
>(
m_internals.get() ) =
506 nlohmann::json::parse( fstream,
nullptr,
515 catch( nlohmann::json::parse_error& error )
517 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ), aPath, error.what() );
530 nlohmann::json::json_pointer ptr =
m_internals->PointerFromString( aPath );
536 return std::optional<nlohmann::json>{
m_internals->at( ptr ) };
543 return std::optional<nlohmann::json>{};
547template<
typename ValueType>
550 if( std::optional<nlohmann::json> ret =
GetJson( aPath ) )
554 return ret->get<ValueType>();
566template std::optional<bool> JSON_SETTINGS::Get<bool>(
const std::string& aPath )
const;
567template std::optional<double> JSON_SETTINGS::Get<double>(
const std::string& aPath )
const;
568template std::optional<float> JSON_SETTINGS::Get<float>(
const std::string& aPath )
const;
569template std::optional<int> JSON_SETTINGS::Get<int>(
const std::string& aPath )
const;
570template std::optional<unsigned int> JSON_SETTINGS::Get<unsigned int>(
const std::string& aPath )
const;
571template std::optional<unsigned long long> JSON_SETTINGS::Get<unsigned long long>(
const std::string& aPath )
const;
572template std::optional<std::string> JSON_SETTINGS::Get<std::string>(
const std::string& aPath )
const;
573template std::optional<nlohmann::json> JSON_SETTINGS::Get<nlohmann::json>(
const std::string& aPath )
const;
574template std::optional<KIGFX::COLOR4D> JSON_SETTINGS::Get<KIGFX::COLOR4D>(
const std::string& aPath )
const;
575template std::optional<BOM_FIELD> JSON_SETTINGS::Get<BOM_FIELD>(
const std::string& aPath )
const;
576template std::optional<BOM_PRESET> JSON_SETTINGS::Get<BOM_PRESET>(
const std::string& aPath )
const;
577template std::optional<BOM_FMT_PRESET> JSON_SETTINGS::Get<BOM_FMT_PRESET>(
const std::string& aPath )
const;
578template std::optional<GRID> JSON_SETTINGS::Get<GRID>(
const std::string& aPath )
const;
579template std::optional<wxPoint> JSON_SETTINGS::Get<wxPoint>(
const std::string& aPath )
const;
580template std::optional<wxSize> JSON_SETTINGS::Get<wxSize>(
const std::string& aPath )
const;
581template std::optional<wxRect> JSON_SETTINGS::Get<wxRect>(
const std::string& aPath )
const;
582template std::optional<wxAuiPaneInfo> JSON_SETTINGS::Get<wxAuiPaneInfo>(
const std::string& aPath )
const;
584template<
typename ValueType>
592template void JSON_SETTINGS::Set<bool>(
const std::string& aPath,
bool aValue );
593template void JSON_SETTINGS::Set<double>(
const std::string& aPath,
double aValue );
594template void JSON_SETTINGS::Set<float>(
const std::string& aPath,
float aValue );
595template void JSON_SETTINGS::Set<int>(
const std::string& aPath,
int aValue );
596template void JSON_SETTINGS::Set<unsigned int>(
const std::string& aPath,
unsigned int aValue );
597template void JSON_SETTINGS::Set<unsigned long long>(
const std::string& aPath,
unsigned long long aValue );
598template void JSON_SETTINGS::Set<const char*>(
const std::string& aPath,
const char* aValue );
599template void JSON_SETTINGS::Set<std::string>(
const std::string& aPath, std::string aValue );
600template void JSON_SETTINGS::Set<nlohmann::json>(
const std::string& aPath, nlohmann::json aValue );
601template void JSON_SETTINGS::Set<KIGFX::COLOR4D>(
const std::string& aPath,
KIGFX::COLOR4D aValue );
602template void JSON_SETTINGS::Set<BOM_FIELD>(
const std::string& aPath,
BOM_FIELD aValue );
603template void JSON_SETTINGS::Set<BOM_PRESET>(
const std::string& aPath,
BOM_PRESET aValue );
604template void JSON_SETTINGS::Set<BOM_FMT_PRESET>(
const std::string& aPath,
BOM_FMT_PRESET aValue );
605template void JSON_SETTINGS::Set<GRID>(
const std::string& aPath,
GRID aValue );
606template void JSON_SETTINGS::Set<wxPoint>(
const std::string& aPath, wxPoint aValue );
607template void JSON_SETTINGS::Set<wxSize>(
const std::string& aPath, wxSize aValue );
608template void JSON_SETTINGS::Set<wxRect>(
const std::string& aPath, wxRect aValue );
609template void JSON_SETTINGS::Set<wxAuiPaneInfo>(
const std::string& aPath, wxAuiPaneInfo aValue );
613 std::function<
bool()> aMigrator )
615 wxASSERT( aNewSchemaVersion > aOldSchemaVersion );
617 m_migrators[aOldSchemaVersion] = std::make_pair( aNewSchemaVersion, aMigrator );
623 int filever =
m_internals->Get<
int>(
"meta.version" );
629 wxLogTrace(
traceSettings, wxT(
"Migrator missing for %s version %d!" ),
630 typeid( *this ).name(), filever );
634 std::pair<int, std::function<bool()>> pair =
m_migrators.at( filever );
638 wxLogTrace(
traceSettings, wxT(
"Migrated %s from %d to %d" ),
typeid( *this ).name(),
639 filever, pair.first );
640 filever = pair.first;
645 wxLogTrace(
traceSettings, wxT(
"Migration failed for %s from %d to %d" ),
646 typeid( *this ).name(), filever, pair.first );
658 wxT(
"MigrateFromLegacy() not implemented for %s" ),
typeid( *this ).name() );
668 if( aObj.contains( ptr ) && aObj.at( ptr ).is_string() )
670 aTarget = aObj.at( ptr ).get<wxString>();
683 if( aObj.contains( ptr ) && aObj.at( ptr ).is_boolean() )
685 aTarget = aObj.at( ptr ).get<
bool>();
698 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_integer() )
700 aTarget = aObj.at( ptr ).get<
int>();
709 unsigned int& aTarget )
713 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_unsigned() )
715 aTarget = aObj.at( ptr ).get<
unsigned int>();
723template<
typename ValueType>
725 const std::string& aDest )
729 if( aConfig->Read( aKey, &val ) )
733 ( *m_internals )[aDest] = val;
737 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacy!" ) );
750template bool JSON_SETTINGS::fromLegacy<int>( wxConfigBase*,
const std::string&,
751 const std::string& );
753template bool JSON_SETTINGS::fromLegacy<double>( wxConfigBase*,
const std::string&,
754 const std::string& );
756template bool JSON_SETTINGS::fromLegacy<bool>( wxConfigBase*,
const std::string&,
757 const std::string& );
761 const std::string& aDest )
765 if( aConfig->Read( aKey, &str ) )
769 ( *m_internals )[aDest] = str.ToUTF8();
773 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyString!" ) );
785 const std::string& aDest )
789 if( aConfig->Read( aKey, &str ) )
792 color.SetFromWxString( str );
797 ( *m_internals )[aDest] = js;
801 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyColor!" ) );
826 return aPtr == aSettings;
831 wxLogTrace(
traceSettings, wxT(
"Flush and release %s" ), ( *it )->GetFilename() );
832 ( *it )->SaveToFile();
844 if( std::optional<nlohmann::json> opt_json =
GetJson( aPath ) )
845 return wxString( opt_json->get<std::string>().c_str(), wxConvUTF8 );
851template<>
void JSON_SETTINGS::Set<wxString>(
const std::string& aPath, wxString aVal )
853 ( *m_internals )[aPath] = aVal.ToUTF8();
858void to_json( nlohmann::json& aJson,
const wxString& aString )
860 aJson = aString.ToUTF8();
864void from_json(
const nlohmann::json& aJson, wxString& aString )
866 aString = wxString( aJson.get<std::string>().c_str(), wxConvUTF8 );
870template<
typename ResultType>
872 ResultType aDefault )
874 ResultType ret = aDefault;
878 if( aJson.contains( aKey ) )
879 ret = aJson.at( aKey ).get<ResultType>();
890 const std::string& aKey, std::string 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.
virtual wxString getFileExt() const
std::optional< ValueType > Get(const std::string &aPath) const
Fetches a value from within the JSON document.
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....
bool fromLegacy(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy wxConfig value to a given JSON pointer value.
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)
std::vector< NESTED_SETTINGS * > m_nested_settings
Nested settings files that live inside this one, if any.
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::vector< PARAM_BASE * > m_params
The list of parameters (owned by this object)
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.
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()
void ReleaseNestedSettings(NESTED_SETTINGS *aSettings)
Saves and frees a nested settings object, if it exists within this one.
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...
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
virtual bool SaveToFile(const wxString &aDirectory="", bool aForce=false)
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.
NESTED_SETTINGS is a JSON_SETTINGS that lives inside a JSON_SETTINGS.
void SetParent(JSON_SETTINGS *aParent, bool aLoadFromFile=true)
const wxChar *const traceSettings
Flag to enable debug output of settings operations and management.
void from_json(const nlohmann::json &aJson, wxString &aString)
void to_json(nlohmann::json &aJson, const wxString &aString)
std::vector< FAB_LAYER_COLOR > dummy
Common grid settings, available to every frame.