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 ),
76 m_deleteLegacyAfterMigration( true ),
77 m_resetParamsIfMissing( true ),
78 m_schemaVersion( aSchemaVersion ),
81 m_internals = std::make_unique<JSON_SETTINGS_INTERNALS>();
89 wxLogTrace(
traceSettings, wxT(
"Error: Could not create filename field for %s" ),
146 wxLogTrace(
traceSettings, wxT(
"param '%s' load err" ), param->GetJsonPath().c_str() );
159 bool migrated =
false;
160 bool legacy_migrated =
false;
164 auto migrateFromLegacy =
165 [&] ( wxFileName& aPath )
169 bool backed_up =
false;
172 if( aPath.IsDirWritable() )
174 temp.AssignTempFileName( aPath.GetFullPath() );
176 if( !wxCopyFile( aPath.GetFullPath(), temp.GetFullPath() ) )
179 wxT(
"%s: could not create temp file for migration" ),
191 wxConfigBase::DontCreateOnDemand();
192 auto cfg = std::make_unique<wxFileConfig>( wxT(
"" ), wxT(
"" ),
193 aPath.GetFullPath() );
201 wxT(
"%s: migrated; not all settings were found in legacy file" ),
207 wxLogTrace(
traceSettings, wxT(
"%s: migrated from legacy format" ),
215 if( !wxCopyFile( temp.GetFullPath(), aPath.GetFullPath() ) )
218 wxT(
"migrate; copy temp file %s to %s failed" ),
220 aPath.GetFullPath() );
223 if( !wxRemoveFile( temp.GetFullPath() ) )
226 wxT(
"migrate; failed to remove temp file %s" ),
227 temp.GetFullPath() );
232 legacy_migrated =
true;
237 if( aDirectory.empty() )
244 wxString dir( aDirectory );
255 migrateFromLegacy(
path );
263 migrateFromLegacy(
path );
272 if( !
path.IsFileWritable() )
277 wxFFileInputStream fp(
path.GetFullPath(), wxT(
"rt" ) );
278 wxStdInputStream fstream( fp );
282 *
static_cast<nlohmann::json*
>(
m_internals.get() ) =
283 nlohmann::json::parse( fstream,
nullptr,
299 wxLogTrace(
traceSettings, wxT(
"%s: file version could not be read!" ),
306 wxLogTrace(
traceSettings, wxT(
"%s: attempting migration from version "
323 wxT(
"%s: warning: file version %d is newer than latest (%d)" ),
329 wxLogTrace(
traceSettings, wxT(
"%s exists but can't be opened for read" ),
333 catch( nlohmann::json::parse_error& error )
336 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ),
337 path.GetFullPath(), error.what() );
338 wxLogTrace(
traceSettings, wxT(
"Attempting migration in case file is in legacy "
340 migrateFromLegacy(
path );
349 settings->LoadFromFile();
355 if(
m_writeFile && ( legacy_migrated || migrated ) )
359 wxLogTrace(
traceSettings, wxT(
"Warning: could not remove legacy file %s" ),
360 path.GetFullPath() );
374 bool modified =
false;
378 modified |= !param->MatchesFile(
this );
379 param->Store(
this );
404 if( aDirectory.empty() )
411 wxString dir( aDirectory );
418 wxT(
"File for %s doesn't exist and m_createIfMissing == false; not saving" ),
424 if( !
path.DirExists() && !
path.Mkdir() )
426 wxLogTrace(
traceSettings, wxT(
"Warning: could not create path %s, can't save %s" ),
431 if( (
path.FileExists() && !
path.IsFileWritable() ) ||
432 ( !
path.FileExists() && !
path.IsDirWritable() ) )
434 wxLogTrace(
traceSettings, wxT(
"File for %s is read-only; not saving" ),
439 bool modified =
false;
442 modified |= settings->SaveToFile();
446 if( !modified && !aForce &&
path.FileExists() )
448 wxLogTrace(
traceSettings, wxT(
"%s contents not modified, skipping save" ),
455 wxT(
"%s contents still default and m_createIfDefault == false; not saving" ),
472 if( stringMap->ClearUnknownKeys() )
473 toSave[ stringMap->GetJsonPath() ] = nlohmann::json( {} );
481 std::stringstream buffer;
482 buffer << std::setw( 2 ) << toSave << std::endl;
484 wxFFileOutputStream fileStream(
path.GetFullPath(),
"wb" );
486 if( !fileStream.IsOk()
487 || !fileStream.WriteAll( buffer.str().c_str(), buffer.str().size() ) )
493 catch( nlohmann::json::exception& error )
495 wxLogTrace(
traceSettings, wxT(
"Catch error: could not save %s. Json error %s" ),
501 wxLogTrace(
traceSettings, wxT(
"Error: could not save %s." ) );
515 std::stringstream buffer;
516 buffer << std::setw( 2 ) << *
m_internals << std::endl;
526 wxFFileInputStream fp( aPath, wxT(
"rt" ) );
527 wxStdInputStream fstream( fp );
531 *
static_cast<nlohmann::json*
>(
m_internals.get() ) =
532 nlohmann::json::parse( fstream,
nullptr,
541 catch( nlohmann::json::parse_error& error )
543 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ), aPath, error.what() );
556 nlohmann::json::json_pointer ptr =
m_internals->PointerFromString( aPath );
562 return std::optional<nlohmann::json>{
m_internals->at( ptr ) };
569 return std::optional<nlohmann::json>{};
573template<
typename ValueType>
576 if( std::optional<nlohmann::json> ret =
GetJson( aPath ) )
580 return ret->get<ValueType>();
592template KICOMMON_API std::optional<bool> JSON_SETTINGS::Get<bool>(
const std::string& aPath )
const;
594 JSON_SETTINGS::Get<double>(
const std::string& aPath )
const;
596 JSON_SETTINGS::Get<float>(
const std::string& aPath )
const;
597template KICOMMON_API std::optional<int> JSON_SETTINGS::Get<int>(
const std::string& aPath )
const;
599 JSON_SETTINGS::Get<unsigned int>(
const std::string& aPath )
const;
601 JSON_SETTINGS::Get<unsigned long long>(
const std::string& aPath )
const;
603 JSON_SETTINGS::Get<std::string>(
const std::string& aPath )
const;
605 JSON_SETTINGS::Get<nlohmann::json>(
const std::string& aPath )
const;
607 JSON_SETTINGS::Get<KIGFX::COLOR4D>(
const std::string& aPath )
const;
609 JSON_SETTINGS::Get<BOM_FIELD>(
const std::string& aPath )
const;
611 JSON_SETTINGS::Get<BOM_PRESET>(
const std::string& aPath )
const;
613 JSON_SETTINGS::Get<BOM_FMT_PRESET>(
const std::string& aPath )
const;
614template KICOMMON_API std::optional<GRID> JSON_SETTINGS::Get<GRID>(
const std::string& aPath )
const;
616 JSON_SETTINGS::Get<wxPoint>(
const std::string& aPath )
const;
618 JSON_SETTINGS::Get<wxSize>(
const std::string& aPath )
const;
620 JSON_SETTINGS::Get<wxRect>(
const std::string& aPath )
const;
622 JSON_SETTINGS::Get<wxAuiPaneInfo>(
const std::string& aPath )
const;
624template<
typename ValueType>
627 m_internals->SetFromString( aPath, std::move( aVal ) );
632template KICOMMON_API void JSON_SETTINGS::Set<bool>(
const std::string& aPath,
bool aValue );
633template KICOMMON_API void JSON_SETTINGS::Set<double>(
const std::string& aPath,
double aValue );
634template KICOMMON_API void JSON_SETTINGS::Set<float>(
const std::string& aPath,
float aValue );
635template KICOMMON_API void JSON_SETTINGS::Set<int>(
const std::string& aPath,
int aValue );
636template KICOMMON_API void JSON_SETTINGS::Set<unsigned int>(
const std::string& aPath,
637 unsigned int aValue );
638template KICOMMON_API void JSON_SETTINGS::Set<unsigned long long>(
const std::string& aPath,
639 unsigned long long aValue );
640template KICOMMON_API void JSON_SETTINGS::Set<const char*>(
const std::string& aPath,
641 const char* aValue );
642template KICOMMON_API void JSON_SETTINGS::Set<std::string>(
const std::string& aPath,
643 std::string aValue );
644template KICOMMON_API void JSON_SETTINGS::Set<nlohmann::json>(
const std::string& aPath,
645 nlohmann::json aValue );
646template KICOMMON_API void JSON_SETTINGS::Set<KIGFX::COLOR4D>(
const std::string& aPath,
648template KICOMMON_API void JSON_SETTINGS::Set<BOM_FIELD>(
const std::string& aPath,
650template KICOMMON_API void JSON_SETTINGS::Set<BOM_PRESET>(
const std::string& aPath,
652template KICOMMON_API void JSON_SETTINGS::Set<BOM_FMT_PRESET>(
const std::string& aPath,
654template KICOMMON_API void JSON_SETTINGS::Set<GRID>(
const std::string& aPath,
GRID aValue );
655template KICOMMON_API void JSON_SETTINGS::Set<wxPoint>(
const std::string& aPath, wxPoint aValue );
656template KICOMMON_API void JSON_SETTINGS::Set<wxSize>(
const std::string& aPath, wxSize aValue );
657template KICOMMON_API void JSON_SETTINGS::Set<wxRect>(
const std::string& aPath, wxRect aValue );
658template KICOMMON_API void JSON_SETTINGS::Set<wxAuiPaneInfo>(
const std::string& aPath,
659 wxAuiPaneInfo aValue );
663 std::function<
bool()> aMigrator )
665 wxASSERT( aNewSchemaVersion > aOldSchemaVersion );
667 m_migrators[aOldSchemaVersion] = std::make_pair( aNewSchemaVersion, aMigrator );
673 int filever =
m_internals->Get<
int>(
"meta.version" );
681 wxLogTrace(
traceSettings, wxT(
"Migrator missing for %s version %d!" ),
682 typeid( *this ).name(), filever );
686 std::pair<int, std::function<bool()>> pair =
m_migrators.at( filever );
690 wxLogTrace(
traceSettings, wxT(
"Migrated %s from %d to %d" ),
typeid( *this ).name(),
691 filever, pair.first );
692 filever = pair.first;
697 wxLogTrace(
traceSettings, wxT(
"Migration failed for %s from %d to %d" ),
698 typeid( *this ).name(), filever, pair.first );
710 wxT(
"MigrateFromLegacy() not implemented for %s" ),
typeid( *this ).name() );
720 if( aObj.contains( ptr ) && aObj.at( ptr ).is_string() )
722 aTarget = aObj.at( ptr ).get<wxString>();
735 if( aObj.contains( ptr ) && aObj.at( ptr ).is_boolean() )
737 aTarget = aObj.at( ptr ).get<
bool>();
750 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_integer() )
752 aTarget = aObj.at( ptr ).get<
int>();
761 unsigned int& aTarget )
765 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_unsigned() )
767 aTarget = aObj.at( ptr ).get<
unsigned int>();
775template<
typename ValueType>
777 const std::string& aDest )
781 if( aConfig->Read( aKey, &val ) )
785 ( *m_internals )[aDest] = val;
789 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacy!" ) );
802template KICOMMON_API bool JSON_SETTINGS::fromLegacy<int>( wxConfigBase*,
const std::string&,
803 const std::string& );
805template KICOMMON_API bool JSON_SETTINGS::fromLegacy<double>( wxConfigBase*,
const std::string&,
806 const std::string& );
808template KICOMMON_API bool JSON_SETTINGS::fromLegacy<bool>( wxConfigBase*,
const std::string&,
809 const std::string& );
813 const std::string& aDest )
817 if( aConfig->Read( aKey, &str ) )
821 ( *m_internals )[aDest] = str.ToUTF8();
825 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyString!" ) );
837 const std::string& aDest )
841 if( aConfig->Read( aKey, &str ) )
844 color.SetFromWxString( str );
849 ( *m_internals )[aDest] = std::move( js );
853 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyColor!" ) );
879 return aPtr == aSettings;
884 wxLogTrace(
traceSettings, wxT(
"Flush and release %s" ), ( *it )->GetFilename() );
885 ( *it )->SaveToFile();
896 if( std::optional<nlohmann::json> opt_json =
GetJson( aPath ) )
897 return wxString( opt_json->get<std::string>().c_str(), wxConvUTF8 );
903template<>
void JSON_SETTINGS::Set<wxString>(
const std::string& aPath, wxString aVal )
905 ( *m_internals )[aPath] = aVal.ToUTF8();
909template<
typename ResultType>
911 ResultType aDefault )
913 ResultType ret = std::move( aDefault );
917 if( aJson.contains( aKey ) )
918 ret = aJson.at( aKey ).get<ResultType>();
929 const std::string& aKey, std::string aDefault );
933 const std::string& aKey,
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)
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::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)
A helper for <wxString, wxString> maps.
std::vector< FAB_LAYER_COLOR > dummy
Common grid settings, available to every frame.