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,
70 m_filename( aFilename ),
71 m_legacy_filename(
"" ),
72 m_location( aLocation ),
73 m_createIfMissing( aCreateIfMissing ),
74 m_createIfDefault( aCreateIfDefault ),
75 m_writeFile( aWriteFile ),
77 m_deleteLegacyAfterMigration( true ),
78 m_resetParamsIfMissing( true ),
79 m_schemaVersion( aSchemaVersion ),
80 m_isFutureFormat( false ),
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 );
411 if( aDirectory.empty() )
418 wxString dir( aDirectory );
425 wxT(
"File for %s doesn't exist and m_createIfMissing == false; not saving" ),
431 if( !
path.DirExists() && !
path.Mkdir() )
433 wxLogTrace(
traceSettings, wxT(
"Warning: could not create path %s, can't save %s" ),
438 if( (
path.FileExists() && !
path.IsFileWritable() ) ||
439 ( !
path.FileExists() && !
path.IsDirWritable() ) )
441 wxLogTrace(
traceSettings, wxT(
"File for %s is read-only; not saving" ),
446 bool modified =
false;
450 wxCHECK2( settings,
continue );
452 modified |= settings->SaveToFile();
457 if( !modified && !aForce &&
path.FileExists() )
459 wxLogTrace(
traceSettings, wxT(
"%s contents not modified, skipping save" ),
466 wxT(
"%s contents still default and m_createIfDefault == false; not saving" ),
481 if( param->ClearUnknownKeys() )
483 nlohmann::json_pointer p
486 toSave[p] = nlohmann::json( {} );
494 std::stringstream buffer;
495 buffer << std::setw( 2 ) << toSave << std::endl;
497 wxFFileOutputStream fileStream(
path.GetFullPath(),
"wb" );
499 if( !fileStream.IsOk()
500 || !fileStream.WriteAll( buffer.str().c_str(), buffer.str().size() ) )
506 catch( nlohmann::json::exception& error )
508 wxLogTrace(
traceSettings, wxT(
"Catch error: could not save %s. Json error %s" ),
514 wxLogTrace(
traceSettings, wxT(
"Error: could not save %s." ) );
531 std::stringstream buffer;
532 buffer << std::setw( 2 ) << *
m_internals << std::endl;
542 wxFFileInputStream fp( aPath, wxT(
"rt" ) );
543 wxStdInputStream fstream( fp );
547 *
static_cast<nlohmann::json*
>(
m_internals.get() ) =
548 nlohmann::json::parse( fstream,
nullptr,
557 catch( nlohmann::json::parse_error& error )
559 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ), aPath, error.what() );
572 nlohmann::json::json_pointer ptr =
m_internals->PointerFromString( aPath );
578 return std::optional<nlohmann::json>{
m_internals->at( ptr ) };
585 return std::optional<nlohmann::json>{};
589template<
typename ValueType>
592 if( std::optional<nlohmann::json> ret =
GetJson( aPath ) )
596 return ret->get<ValueType>();
609 JSON_SETTINGS::Get<bool>(
const std::string& aPath )
const;
611 JSON_SETTINGS::Get<double>(
const std::string& aPath )
const;
613 JSON_SETTINGS::Get<float>(
const std::string& aPath )
const;
614template KICOMMON_API std::optional<int> JSON_SETTINGS::Get<int>(
const std::string& aPath )
const;
616 JSON_SETTINGS::Get<unsigned int>(
const std::string& aPath )
const;
618 JSON_SETTINGS::Get<unsigned long long>(
const std::string& aPath )
const;
620 JSON_SETTINGS::Get<std::string>(
const std::string& aPath )
const;
622 JSON_SETTINGS::Get<nlohmann::json>(
const std::string& aPath )
const;
624 JSON_SETTINGS::Get<KIGFX::COLOR4D>(
const std::string& aPath )
const;
626 JSON_SETTINGS::Get<BOM_FIELD>(
const std::string& aPath )
const;
628 JSON_SETTINGS::Get<BOM_PRESET>(
const std::string& aPath )
const;
630 JSON_SETTINGS::Get<BOM_FMT_PRESET>(
const std::string& aPath )
const;
632 JSON_SETTINGS::Get<GRID>(
const std::string& aPath )
const;
634 JSON_SETTINGS::Get<wxPoint>(
const std::string& aPath )
const;
636 JSON_SETTINGS::Get<wxSize>(
const std::string& aPath )
const;
638 JSON_SETTINGS::Get<wxRect>(
const std::string& aPath )
const;
640 JSON_SETTINGS::Get<wxAuiPaneInfo>(
const std::string& aPath )
const;
642template<
typename ValueType>
645 m_internals->SetFromString( aPath, std::move( aVal ) );
650template KICOMMON_API void JSON_SETTINGS::Set<bool>(
const std::string& aPath,
bool aValue );
651template KICOMMON_API void JSON_SETTINGS::Set<double>(
const std::string& aPath,
double aValue );
652template KICOMMON_API void JSON_SETTINGS::Set<float>(
const std::string& aPath,
float aValue );
653template KICOMMON_API void JSON_SETTINGS::Set<int>(
const std::string& aPath,
int aValue );
654template KICOMMON_API void JSON_SETTINGS::Set<unsigned int>(
const std::string& aPath,
655 unsigned int aValue );
656template KICOMMON_API void JSON_SETTINGS::Set<unsigned long long>(
const std::string& aPath,
657 unsigned long long aValue );
658template KICOMMON_API void JSON_SETTINGS::Set<const char*>(
const std::string& aPath,
659 const char* aValue );
660template KICOMMON_API void JSON_SETTINGS::Set<std::string>(
const std::string& aPath,
661 std::string aValue );
662template KICOMMON_API void JSON_SETTINGS::Set<nlohmann::json>(
const std::string& aPath,
663 nlohmann::json aValue );
664template KICOMMON_API void JSON_SETTINGS::Set<KIGFX::COLOR4D>(
const std::string& aPath,
666template KICOMMON_API void JSON_SETTINGS::Set<BOM_FIELD>(
const std::string& aPath,
668template KICOMMON_API void JSON_SETTINGS::Set<BOM_PRESET>(
const std::string& aPath,
670template KICOMMON_API void JSON_SETTINGS::Set<BOM_FMT_PRESET>(
const std::string& aPath,
672template KICOMMON_API void JSON_SETTINGS::Set<GRID>(
const std::string& aPath,
GRID aValue );
673template KICOMMON_API void JSON_SETTINGS::Set<wxPoint>(
const std::string& aPath, wxPoint aValue );
674template KICOMMON_API void JSON_SETTINGS::Set<wxSize>(
const std::string& aPath, wxSize aValue );
675template KICOMMON_API void JSON_SETTINGS::Set<wxRect>(
const std::string& aPath, wxRect aValue );
676template KICOMMON_API void JSON_SETTINGS::Set<wxAuiPaneInfo>(
const std::string& aPath,
677 wxAuiPaneInfo aValue );
681 std::function<
bool()> aMigrator )
683 wxASSERT( aNewSchemaVersion > aOldSchemaVersion );
685 m_migrators[aOldSchemaVersion] = std::make_pair( aNewSchemaVersion, aMigrator );
691 int filever =
m_internals->Get<
int>(
"meta.version" );
699 wxLogTrace(
traceSettings, wxT(
"Migrator missing for %s version %d!" ),
700 typeid( *this ).name(),
705 std::pair<int, std::function<bool()>> pair =
m_migrators.at( filever );
709 wxLogTrace(
traceSettings, wxT(
"Migrated %s from %d to %d" ),
710 typeid( *this ).name(),
713 filever = pair.first;
718 wxLogTrace(
traceSettings, wxT(
"Migration failed for %s from %d to %d" ),
719 typeid( *this ).name(),
732 wxLogTrace(
traceSettings, wxT(
"MigrateFromLegacy() not implemented for %s" ),
733 typeid( *this ).name() );
743 if( aObj.contains( ptr ) && aObj.at( ptr ).is_string() )
745 aTarget = aObj.at( ptr ).get<wxString>();
758 if( aObj.contains( ptr ) && aObj.at( ptr ).is_boolean() )
760 aTarget = aObj.at( ptr ).get<
bool>();
773 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_integer() )
775 aTarget = aObj.at( ptr ).get<
int>();
784 unsigned int& aTarget )
788 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_unsigned() )
790 aTarget = aObj.at( ptr ).get<
unsigned int>();
798template<
typename ValueType>
800 const std::string& aDest )
804 if( aConfig->Read( aKey, &val ) )
808 ( *m_internals )[aDest] = val;
812 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacy!" ) );
826KICOMMON_API bool JSON_SETTINGS::fromLegacy<int>( wxConfigBase*,
const std::string&,
827 const std::string& );
830KICOMMON_API bool JSON_SETTINGS::fromLegacy<double>( wxConfigBase*,
const std::string&,
831 const std::string& );
834KICOMMON_API bool JSON_SETTINGS::fromLegacy<bool>( wxConfigBase*,
const std::string&,
835 const std::string& );
839 const std::string& aDest )
843 if( aConfig->Read( aKey, &str ) )
847 ( *m_internals )[aDest] = str.ToUTF8();
851 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyString!" ) );
863 const std::string& aDest )
867 if( aConfig->Read( aKey, &str ) )
870 color.SetFromWxString( str );
875 ( *m_internals )[aDest] = std::move( js );
879 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyColor!" ) );
905 return aPtr == aSettings;
910 wxLogTrace(
traceSettings, wxT(
"Flush and release %s" ), ( *it )->GetFilename() );
923 if( std::optional<nlohmann::json> opt_json =
GetJson( aPath ) )
924 return wxString( opt_json->get<std::string>().c_str(), wxConvUTF8 );
931void JSON_SETTINGS::Set<wxString>(
const std::string& aPath, wxString aVal )
933 ( *m_internals )[aPath] = aVal.ToUTF8();
937template<
typename ResultType>
939 ResultType aDefault )
941 ResultType ret = std::move( aDefault );
945 if( aJson.contains( aKey ) )
946 ret = aJson.at( aKey ).get<ResultType>();
958 const std::string& aKey,
959 std::string aDefault );
964 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
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
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.
NESTED_SETTINGS is a JSON_SETTINGS that lives inside a JSON_SETTINGS.
void SetParent(JSON_SETTINGS *aParent, bool aLoadFromFile=true)
std::vector< FAB_LAYER_COLOR > dummy
Common grid settings, available to every frame.