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 ),
82 m_internals = std::make_unique<JSON_SETTINGS_INTERNALS>();
90 wxLogTrace(
traceSettings, wxT(
"Error: Could not create filename field for %s" ),
147 wxLogTrace(
traceSettings, wxT(
"param '%s' load err" ), param->GetJsonPath().c_str() );
160 bool migrated =
false;
161 bool legacy_migrated =
false;
165 auto migrateFromLegacy =
166 [&] ( wxFileName& aPath )
170 bool backed_up =
false;
173 if( aPath.IsDirWritable() )
175 temp.AssignTempFileName( aPath.GetFullPath() );
177 if( !wxCopyFile( aPath.GetFullPath(), temp.GetFullPath() ) )
180 wxT(
"%s: could not create temp file for migration" ),
192 wxConfigBase::DontCreateOnDemand();
193 auto cfg = std::make_unique<wxFileConfig>( wxT(
"" ), wxT(
"" ),
194 aPath.GetFullPath() );
202 wxT(
"%s: migrated; not all settings were found in legacy file" ),
208 wxLogTrace(
traceSettings, wxT(
"%s: migrated from legacy format" ),
216 if( !wxCopyFile( temp.GetFullPath(), aPath.GetFullPath() ) )
219 wxT(
"migrate; copy temp file %s to %s failed" ),
221 aPath.GetFullPath() );
224 if( !wxRemoveFile( temp.GetFullPath() ) )
227 wxT(
"migrate; failed to remove temp file %s" ),
228 temp.GetFullPath() );
233 legacy_migrated =
true;
238 if( aDirectory.empty() )
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 "
325 wxT(
"%s: warning: file version %d is newer than latest (%d)" ),
333 wxLogTrace(
traceSettings, wxT(
"%s exists but can't be opened for read" ),
337 catch( nlohmann::json::parse_error& error )
340 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ),
341 path.GetFullPath(), error.what() );
342 wxLogTrace(
traceSettings, wxT(
"Attempting migration in case file is in legacy "
344 migrateFromLegacy(
path );
353 settings->LoadFromFile();
355 wxLogTrace(
traceSettings, wxT(
"Loaded <%s> with schema %d" ),
362 if(
m_writeFile && ( legacy_migrated || migrated ) )
366 wxLogTrace(
traceSettings, wxT(
"Warning: could not remove legacy file %s" ),
367 path.GetFullPath() );
384 param->Store(
this );
409 if( aDirectory.empty() )
416 wxString dir( aDirectory );
423 wxT(
"File for %s doesn't exist and m_createIfMissing == false; not saving" ),
429 if( !
path.DirExists() && !
path.Mkdir() )
431 wxLogTrace(
traceSettings, wxT(
"Warning: could not create path %s, can't save %s" ),
436 if( (
path.FileExists() && !
path.IsFileWritable() ) ||
437 ( !
path.FileExists() && !
path.IsDirWritable() ) )
439 wxLogTrace(
traceSettings, wxT(
"File for %s is read-only; not saving" ),
444 bool modified =
false;
447 modified |= settings->SaveToFile();
451 if( !modified && !aForce &&
path.FileExists() )
453 wxLogTrace(
traceSettings, wxT(
"%s contents not modified, skipping save" ),
460 wxT(
"%s contents still default and m_createIfDefault == false; not saving" ),
475 if( param->ClearUnknownKeys() )
477 nlohmann::json_pointer p
480 toSave[p] = nlohmann::json( {} );
488 std::stringstream buffer;
489 buffer << std::setw( 2 ) << toSave << std::endl;
491 wxFFileOutputStream fileStream(
path.GetFullPath(),
"wb" );
493 if( !fileStream.IsOk()
494 || !fileStream.WriteAll( buffer.str().c_str(), buffer.str().size() ) )
500 catch( nlohmann::json::exception& error )
502 wxLogTrace(
traceSettings, wxT(
"Catch error: could not save %s. Json error %s" ),
508 wxLogTrace(
traceSettings, wxT(
"Error: could not save %s." ) );
525 std::stringstream buffer;
526 buffer << std::setw( 2 ) << *
m_internals << std::endl;
536 wxFFileInputStream fp( aPath, wxT(
"rt" ) );
537 wxStdInputStream fstream( fp );
541 *
static_cast<nlohmann::json*
>(
m_internals.get() ) =
542 nlohmann::json::parse( fstream,
nullptr,
551 catch( nlohmann::json::parse_error& error )
553 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ), aPath, error.what() );
566 nlohmann::json::json_pointer ptr =
m_internals->PointerFromString( aPath );
572 return std::optional<nlohmann::json>{
m_internals->at( ptr ) };
579 return std::optional<nlohmann::json>{};
583template<
typename ValueType>
586 if( std::optional<nlohmann::json> ret =
GetJson( aPath ) )
590 return ret->get<ValueType>();
602template KICOMMON_API std::optional<bool> JSON_SETTINGS::Get<bool>(
const std::string& aPath )
const;
604 JSON_SETTINGS::Get<double>(
const std::string& aPath )
const;
606 JSON_SETTINGS::Get<float>(
const std::string& aPath )
const;
607template KICOMMON_API std::optional<int> JSON_SETTINGS::Get<int>(
const std::string& aPath )
const;
609 JSON_SETTINGS::Get<unsigned int>(
const std::string& aPath )
const;
611 JSON_SETTINGS::Get<unsigned long long>(
const std::string& aPath )
const;
613 JSON_SETTINGS::Get<std::string>(
const std::string& aPath )
const;
615 JSON_SETTINGS::Get<nlohmann::json>(
const std::string& aPath )
const;
617 JSON_SETTINGS::Get<KIGFX::COLOR4D>(
const std::string& aPath )
const;
619 JSON_SETTINGS::Get<BOM_FIELD>(
const std::string& aPath )
const;
621 JSON_SETTINGS::Get<BOM_PRESET>(
const std::string& aPath )
const;
623 JSON_SETTINGS::Get<BOM_FMT_PRESET>(
const std::string& aPath )
const;
624template KICOMMON_API std::optional<GRID> JSON_SETTINGS::Get<GRID>(
const std::string& aPath )
const;
626 JSON_SETTINGS::Get<wxPoint>(
const std::string& aPath )
const;
628 JSON_SETTINGS::Get<wxSize>(
const std::string& aPath )
const;
630 JSON_SETTINGS::Get<wxRect>(
const std::string& aPath )
const;
632 JSON_SETTINGS::Get<wxAuiPaneInfo>(
const std::string& aPath )
const;
634template<
typename ValueType>
637 m_internals->SetFromString( aPath, std::move( aVal ) );
642template KICOMMON_API void JSON_SETTINGS::Set<bool>(
const std::string& aPath,
bool aValue );
643template KICOMMON_API void JSON_SETTINGS::Set<double>(
const std::string& aPath,
double aValue );
644template KICOMMON_API void JSON_SETTINGS::Set<float>(
const std::string& aPath,
float aValue );
645template KICOMMON_API void JSON_SETTINGS::Set<int>(
const std::string& aPath,
int aValue );
646template KICOMMON_API void JSON_SETTINGS::Set<unsigned int>(
const std::string& aPath,
647 unsigned int aValue );
648template KICOMMON_API void JSON_SETTINGS::Set<unsigned long long>(
const std::string& aPath,
649 unsigned long long aValue );
650template KICOMMON_API void JSON_SETTINGS::Set<const char*>(
const std::string& aPath,
651 const char* aValue );
652template KICOMMON_API void JSON_SETTINGS::Set<std::string>(
const std::string& aPath,
653 std::string aValue );
654template KICOMMON_API void JSON_SETTINGS::Set<nlohmann::json>(
const std::string& aPath,
655 nlohmann::json aValue );
656template KICOMMON_API void JSON_SETTINGS::Set<KIGFX::COLOR4D>(
const std::string& aPath,
658template KICOMMON_API void JSON_SETTINGS::Set<BOM_FIELD>(
const std::string& aPath,
660template KICOMMON_API void JSON_SETTINGS::Set<BOM_PRESET>(
const std::string& aPath,
662template KICOMMON_API void JSON_SETTINGS::Set<BOM_FMT_PRESET>(
const std::string& aPath,
664template KICOMMON_API void JSON_SETTINGS::Set<GRID>(
const std::string& aPath,
GRID aValue );
665template KICOMMON_API void JSON_SETTINGS::Set<wxPoint>(
const std::string& aPath, wxPoint aValue );
666template KICOMMON_API void JSON_SETTINGS::Set<wxSize>(
const std::string& aPath, wxSize aValue );
667template KICOMMON_API void JSON_SETTINGS::Set<wxRect>(
const std::string& aPath, wxRect aValue );
668template KICOMMON_API void JSON_SETTINGS::Set<wxAuiPaneInfo>(
const std::string& aPath,
669 wxAuiPaneInfo aValue );
673 std::function<
bool()> aMigrator )
675 wxASSERT( aNewSchemaVersion > aOldSchemaVersion );
677 m_migrators[aOldSchemaVersion] = std::make_pair( aNewSchemaVersion, aMigrator );
683 int filever =
m_internals->Get<
int>(
"meta.version" );
691 wxLogTrace(
traceSettings, wxT(
"Migrator missing for %s version %d!" ),
692 typeid( *this ).name(),
697 std::pair<int, std::function<bool()>> pair =
m_migrators.at( filever );
701 wxLogTrace(
traceSettings, wxT(
"Migrated %s from %d to %d" ),
702 typeid( *this ).name(),
705 filever = pair.first;
710 wxLogTrace(
traceSettings, wxT(
"Migration failed for %s from %d to %d" ),
711 typeid( *this ).name(),
724 wxLogTrace(
traceSettings, wxT(
"MigrateFromLegacy() not implemented for %s" ),
725 typeid( *this ).name() );
735 if( aObj.contains( ptr ) && aObj.at( ptr ).is_string() )
737 aTarget = aObj.at( ptr ).get<wxString>();
750 if( aObj.contains( ptr ) && aObj.at( ptr ).is_boolean() )
752 aTarget = aObj.at( ptr ).get<
bool>();
765 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_integer() )
767 aTarget = aObj.at( ptr ).get<
int>();
776 unsigned int& aTarget )
780 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_unsigned() )
782 aTarget = aObj.at( ptr ).get<
unsigned int>();
790template<
typename ValueType>
792 const std::string& aDest )
796 if( aConfig->Read( aKey, &val ) )
800 ( *m_internals )[aDest] = val;
804 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacy!" ) );
818KICOMMON_API bool JSON_SETTINGS::fromLegacy<int>( wxConfigBase*,
const std::string&,
819 const std::string& );
822KICOMMON_API bool JSON_SETTINGS::fromLegacy<double>( wxConfigBase*,
const std::string&,
823 const std::string& );
826KICOMMON_API bool JSON_SETTINGS::fromLegacy<bool>( wxConfigBase*,
const std::string&,
827 const std::string& );
831 const std::string& aDest )
835 if( aConfig->Read( aKey, &str ) )
839 ( *m_internals )[aDest] = str.ToUTF8();
843 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyString!" ) );
855 const std::string& aDest )
859 if( aConfig->Read( aKey, &str ) )
862 color.SetFromWxString( str );
867 ( *m_internals )[aDest] = std::move( js );
871 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyColor!" ) );
897 return aPtr == aSettings;
902 wxLogTrace(
traceSettings, wxT(
"Flush and release %s" ), ( *it )->GetFilename() );
915 if( std::optional<nlohmann::json> opt_json =
GetJson( aPath ) )
916 return wxString( opt_json->get<std::string>().c_str(), wxConvUTF8 );
923void JSON_SETTINGS::Set<wxString>(
const std::string& aPath, wxString aVal )
925 ( *m_internals )[aPath] = aVal.ToUTF8();
929template<
typename ResultType>
931 ResultType aDefault )
933 ResultType ret = std::move( aDefault );
937 if( aJson.contains( aKey ) )
938 ret = aJson.at( aKey ).get<ResultType>();
950 const std::string& aKey,
951 std::string aDefault );
956 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)
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.