KiCad PCB EDA Suite
json_settings.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2020 Jon Evans <jon@craftyjon.com>
5  * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation, either version 3 of the License, or (at your
10  * option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <algorithm>
22 #include <fstream>
23 #include <iomanip>
24 #include <utility>
25 #include <sstream>
26 
27 #include <locale_io.h>
28 #include <gal/color4d.h>
29 #include <settings/json_settings.h>
31 #include <settings/parameters.h>
32 #include <wx/config.h>
33 #include <wx/debug.h>
34 #include <wx/fileconf.h>
35 #include <wx/filename.h>
36 #include <wx/wfstream.h>
37 
38 
39 const wxChar* const traceSettings = wxT( "KICAD_SETTINGS" );
40 
41 JSON_SETTINGS::JSON_SETTINGS( const wxString& aFilename, SETTINGS_LOC aLocation,
42  int aSchemaVersion, bool aCreateIfMissing, bool aCreateIfDefault,
43  bool aWriteFile ) :
44  nlohmann::json(),
45  m_filename( aFilename ),
46  m_legacy_filename( "" ),
47  m_location( aLocation ),
48  m_createIfMissing( aCreateIfMissing ),
49  m_createIfDefault( aCreateIfDefault ),
50  m_writeFile( aWriteFile ),
51  m_deleteLegacyAfterMigration( true ),
52  m_resetParamsIfMissing( true ),
53  m_schemaVersion( aSchemaVersion ),
54  m_manager( nullptr )
55 {
56  try
57  {
58  ( *this )[PointerFromString( "meta.filename" )] = GetFullFilename();
59  }
60  catch( ... )
61  {
62  wxLogTrace( traceSettings, "Error: Could not create filename field for %s",
63  GetFullFilename() );
64  }
65 
66 
67  m_params.emplace_back(
68  new PARAM<int>( "meta.version", &m_schemaVersion, m_schemaVersion, true ) );
69 }
70 
71 
73 {
74  for( auto param: m_params )
75  delete param;
76 
77  m_params.clear();
78 }
79 
80 
82 {
83  return wxString( m_filename + "." + getFileExt() );
84 }
85 
86 
88 {
89  for( auto param : m_params )
90  {
91  try
92  {
93  param->Load( this, m_resetParamsIfMissing );
94  }
95  catch( ... )
96  {
97  // Skip unreadable parameters in file
98  wxLogTrace( traceSettings, "param '%s' load err", param->GetJsonPath().c_str() );
99  }
100  }
101 }
102 
103 
104 bool JSON_SETTINGS::LoadFromFile( const wxString& aDirectory )
105 {
106  // First, load all params to default values
107  clear();
108  Load();
109 
110  bool success = true;
111  bool migrated = false;
112  bool legacy_migrated = false;
113 
114  LOCALE_IO locale;
115 
116  auto migrateFromLegacy = [&] ( wxFileName& aPath ) {
117  // Backup and restore during migration so that the original can be mutated if convenient
118  bool backed_up = false;
119  wxFileName temp;
120 
121  if( aPath.IsDirWritable() )
122  {
123  temp.AssignTempFileName( aPath.GetFullPath() );
124 
125  if( !wxCopyFile( aPath.GetFullPath(), temp.GetFullPath() ) )
126  {
127  wxLogTrace( traceSettings, "%s: could not create temp file for migration",
128  GetFullFilename() );
129  }
130  else
131  backed_up = true;
132  }
133 
134  wxConfigBase::DontCreateOnDemand();
135  auto cfg = std::make_unique<wxFileConfig>( wxT( "" ), wxT( "" ), aPath.GetFullPath() );
136 
137  // If migrate fails or is not implemented, fall back to built-in defaults that were
138  // already loaded above
139  if( !MigrateFromLegacy( cfg.get() ) )
140  {
141  wxLogTrace( traceSettings,
142  "%s: migrated; not all settings were found in legacy file",
143  GetFullFilename() );
144  }
145  else
146  {
147  wxLogTrace( traceSettings, "%s: migrated from legacy format", GetFullFilename() );
148  }
149 
150  if( backed_up )
151  {
152  cfg.reset();
153 
154  if( !wxCopyFile( temp.GetFullPath(), aPath.GetFullPath() ) )
155  {
156  wxLogTrace( traceSettings,
157  "migrate; copy temp file %s to %s failed",
158  temp.GetFullPath(), aPath.GetFullPath() );
159  }
160 
161  if( !wxRemoveFile( temp.GetFullPath() ) )
162  {
163  wxLogTrace( traceSettings,
164  "migrate; failed to remove temp file %s",
165  temp.GetFullPath() );
166  }
167  }
168 
169  // Either way, we want to clean up the old file afterwards
170  legacy_migrated = true;
171  };
172 
173  wxFileName path;
174 
175  if( aDirectory.empty() )
176  {
177  path.Assign( m_filename );
178  path.SetExt( getFileExt() );
179  }
180  else
181  {
182  wxString dir( aDirectory );
183  path.Assign( dir, m_filename, getFileExt() );
184  }
185 
186  if( !path.Exists() )
187  {
188  // Case 1: legacy migration, no .json extension yet
189  path.SetExt( getLegacyFileExt() );
190 
191  if( path.Exists() )
192  {
193  migrateFromLegacy( path );
194  }
195  // Case 2: legacy filename is different from new one
196  else if( !m_legacy_filename.empty() )
197  {
198  path.SetName( m_legacy_filename );
199 
200  if( path.Exists() )
201  migrateFromLegacy( path );
202  }
203  else
204  {
205  success = false;
206  }
207  }
208  else
209  {
210  if( !path.IsFileWritable() )
211  m_writeFile = false;
212 
213  try
214  {
215  FILE* fp = wxFopen( path.GetFullPath(), wxT( "rt" ) );
216 
217  if( fp )
218  {
219  *static_cast<nlohmann::json*>( this ) = nlohmann::json::parse( fp, nullptr,
220  /* allow_exceptions = */ true,
221  /* ignore_comments = */ true );
222 
223  // If parse succeeds, check if schema migration is required
224  int filever = -1;
225 
226  try
227  {
228  filever = at( PointerFromString( "meta.version" ) ).get<int>();
229  }
230  catch( ... )
231  {
232  wxLogTrace( traceSettings, "%s: file version could not be read!",
233  GetFullFilename() );
234  success = false;
235  }
236 
237  if( filever >= 0 && filever < m_schemaVersion )
238  {
239  wxLogTrace( traceSettings, "%s: attempting migration from version %d to %d",
240  GetFullFilename(), filever, m_schemaVersion );
241 
242  if( Migrate() )
243  {
244  migrated = true;
245  }
246  else
247  {
248  wxLogTrace( traceSettings, "%s: migration failed!", GetFullFilename() );
249  }
250  }
251  else if( filever > m_schemaVersion )
252  {
253  wxLogTrace( traceSettings,
254  "%s: warning: file version %d is newer than latest (%d)",
255  GetFullFilename(), filever, m_schemaVersion );
256  }
257  }
258  else
259  {
260  wxLogTrace( traceSettings, "%s exists but can't be opened for read",
261  GetFullFilename() );
262  }
263  }
264  catch( nlohmann::json::parse_error& error )
265  {
266  wxLogTrace( traceSettings, "Json parse error reading %s: %s",
267  path.GetFullPath(), error.what() );
268  wxLogTrace( traceSettings, "Attempting migration in case file is in legacy format" );
269  migrateFromLegacy( path );
270  }
271  }
272 
273  // Now that we have new data in the JSON structure, load the params again
274  Load();
275 
276  // And finally load any nested settings
277  for( auto settings : m_nested_settings )
278  settings->LoadFromFile();
279 
280  wxLogTrace( traceSettings, "Loaded <%s> with schema %d", GetFullFilename(), m_schemaVersion );
281 
282  // If we migrated, clean up the legacy file (with no extension)
283  if( legacy_migrated || migrated )
284  {
285  if( legacy_migrated && m_deleteLegacyAfterMigration && !wxRemoveFile( path.GetFullPath() ) )
286  {
287  wxLogTrace( traceSettings, "Warning: could not remove legacy file %s",
288  path.GetFullPath() );
289  }
290 
291  // And write-out immediately so that we don't lose data if the program later crashes.
292  SaveToFile( aDirectory, true );
293  }
294 
295  return success;
296 }
297 
298 
300 {
301  bool modified = false;
302 
303  for( auto param : m_params )
304  {
305  modified |= !param->MatchesFile( this );
306  param->Store( this );
307  }
308 
309  return modified;
310 }
311 
312 
314 {
315  for( auto param : m_params )
316  param->SetDefault();
317 }
318 
319 
320 bool JSON_SETTINGS::SaveToFile( const wxString& aDirectory, bool aForce )
321 {
322  if( !m_writeFile )
323  return false;
324 
325  // Default PROJECT won't have a filename set
326  if( m_filename.IsEmpty() )
327  return false;
328 
329  wxFileName path;
330 
331  if( aDirectory.empty() )
332  {
333  path.Assign( m_filename );
334  path.SetExt( getFileExt() );
335  }
336  else
337  {
338  wxString dir( aDirectory );
339  path.Assign( dir, m_filename, getFileExt() );
340  }
341 
342  if( !m_createIfMissing && !path.FileExists() )
343  {
344  wxLogTrace( traceSettings,
345  "File for %s doesn't exist and m_createIfMissing == false; not saving",
346  GetFullFilename() );
347  return false;
348  }
349 
350  // Ensure the path exists, and create it if not.
351  if( !path.DirExists() && !path.Mkdir() )
352  {
353  wxLogTrace( traceSettings, "Warning: could not create path %s, can't save %s",
354  path.GetPath(), GetFullFilename() );
355  return false;
356  }
357 
358  if( ( path.FileExists() && !path.IsFileWritable() ) ||
359  ( !path.FileExists() && !path.IsDirWritable() ) )
360  {
361  wxLogTrace( traceSettings, "File for %s is read-only; not saving", GetFullFilename() );
362  return false;
363  }
364 
365  bool modified = false;
366 
367  for( auto settings : m_nested_settings )
368  modified |= settings->SaveToFile();
369 
370  modified |= Store();
371 
372  if( !modified && !aForce && path.FileExists() )
373  {
374  wxLogTrace( traceSettings, "%s contents not modified, skipping save", GetFullFilename() );
375  return false;
376  }
377  else if( !modified && !aForce && !m_createIfDefault )
378  {
379  wxLogTrace( traceSettings,
380  "%s contents still default and m_createIfDefault == false; not saving",
381  GetFullFilename() );
382  return false;
383  }
384 
385  wxLogTrace( traceSettings, "Saving %s", GetFullFilename() );
386 
388  bool success = true;
389 
390  try
391  {
392  std::stringstream buffer;
393  buffer << std::setw( 2 ) << *this << std::endl;
394 
395  wxFFileOutputStream fileStream( path.GetFullPath(), "wb" );
396 
397  if( !fileStream.IsOk()
398  || !fileStream.WriteAll( buffer.str().c_str(), buffer.str().size() ) )
399  {
400  wxLogTrace( traceSettings, "Warning: could not save %s", GetFullFilename() );
401  success = false;
402  }
403  }
404  catch( nlohmann::json::exception& error )
405  {
406  wxLogTrace( traceSettings, "Catch error: could not save %s. Json error %s",
407  GetFullFilename(), error.what() );
408  success = false;
409  }
410  catch( ... )
411  {
412  wxLogTrace( traceSettings, "Error: could not save %s." );
413  success = false;
414  }
415 
416  return success;
417 }
418 
419 
420 OPT<nlohmann::json> JSON_SETTINGS::GetJson( const std::string& aPath ) const
421 {
422  nlohmann::json::json_pointer ptr = PointerFromString( aPath );
423 
424  if( this->contains( ptr ) )
425  {
426  try
427  {
428  return OPT<nlohmann::json>{ this->at( ptr ) };
429  }
430  catch( ... )
431  {
432  }
433  }
434 
435  return OPT<nlohmann::json>{};
436 }
437 
438 
439 void JSON_SETTINGS::registerMigration( int aOldSchemaVersion, int aNewSchemaVersion,
440  std::function<bool()> aMigrator )
441 {
442  wxASSERT( aNewSchemaVersion > aOldSchemaVersion );
443  wxASSERT( aNewSchemaVersion <= m_schemaVersion );
444  m_migrators[aOldSchemaVersion] = std::make_pair( aNewSchemaVersion, aMigrator );
445 }
446 
447 
449 {
450  int filever = at( PointerFromString( "meta.version" ) ).get<int>();
451 
452  while( filever < m_schemaVersion )
453  {
454  if( !m_migrators.count( filever ) )
455  {
456  wxLogTrace( traceSettings, "Migrator missing for %s version %d!",
457  typeid( *this ).name(), filever );
458  return false;
459  }
460 
461  std::pair<int, std::function<bool()>> pair = m_migrators.at( filever );
462 
463  if( pair.second() )
464  {
465  wxLogTrace( traceSettings, "Migrated %s from %d to %d", typeid( *this ).name(),
466  filever, pair.first );
467  filever = pair.first;
468  ( *this )[PointerFromString( "meta.version" )] = filever;
469  }
470  else
471  {
472  wxLogTrace( traceSettings, "Migration failed for %s from %d to %d",
473  typeid( *this ).name(), filever, pair.first );
474  return false;
475  }
476  }
477 
478  return true;
479 }
480 
481 
482 bool JSON_SETTINGS::MigrateFromLegacy( wxConfigBase* aLegacyConfig )
483 {
484  wxLogTrace( traceSettings,
485  "MigrateFromLegacy() not implemented for %s", typeid( *this ).name() );
486  return false;
487 }
488 
489 
490 nlohmann::json::json_pointer JSON_SETTINGS::PointerFromString( std::string aPath )
491 {
492  std::replace( aPath.begin(), aPath.end(), '.', '/' );
493  aPath.insert( 0, "/" );
494 
495  nlohmann::json::json_pointer p;
496 
497  try
498  {
499  p = nlohmann::json::json_pointer( aPath );
500  }
501  catch( ... )
502  {
503  wxASSERT_MSG( false, wxT( "Invalid pointer path in PointerFromString!" ) );
504  }
505 
506  return p;
507 }
508 
509 
510 bool JSON_SETTINGS::SetIfPresent( const nlohmann::json& aObj, const std::string& aPath,
511  wxString& aTarget )
512 {
513  nlohmann::json::json_pointer ptr = PointerFromString( aPath );
514 
515  if( aObj.contains( ptr ) && aObj.at( ptr ).is_string() )
516  {
517  aTarget = aObj.at( ptr ).get<wxString>();
518  return true;
519  }
520 
521  return false;
522 }
523 
524 
525 bool JSON_SETTINGS::SetIfPresent( const nlohmann::json& aObj, const std::string& aPath,
526  bool& aTarget )
527 {
528  nlohmann::json::json_pointer ptr = PointerFromString( aPath );
529 
530  if( aObj.contains( ptr ) && aObj.at( ptr ).is_boolean() )
531  {
532  aTarget = aObj.at( ptr ).get<bool>();
533  return true;
534  }
535 
536  return false;
537 }
538 
539 
540 bool JSON_SETTINGS::SetIfPresent( const nlohmann::json& aObj, const std::string& aPath,
541  int& aTarget )
542 {
543  nlohmann::json::json_pointer ptr = PointerFromString( aPath );
544 
545  if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_integer() )
546  {
547  aTarget = aObj.at( ptr ).get<int>();
548  return true;
549  }
550 
551  return false;
552 }
553 
554 
555 bool JSON_SETTINGS::SetIfPresent( const nlohmann::json& aObj, const std::string& aPath,
556  unsigned int& aTarget )
557 {
558  nlohmann::json::json_pointer ptr = PointerFromString( aPath );
559 
560  if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_unsigned() )
561  {
562  aTarget = aObj.at( ptr ).get<unsigned int>();
563  return true;
564  }
565 
566  return false;
567 }
568 
569 
570 template<typename ValueType>
571 bool JSON_SETTINGS::fromLegacy( wxConfigBase* aConfig, const std::string& aKey,
572  const std::string& aDest )
573 {
574  ValueType val;
575 
576  if( aConfig->Read( aKey, &val ) )
577  {
578  try
579  {
580  ( *this )[PointerFromString( aDest )] = val;
581  }
582  catch( ... )
583  {
584  wxASSERT_MSG( false, wxT( "Could not write value in fromLegacy!" ) );
585  return false;
586  }
587 
588  return true;
589  }
590 
591  return false;
592 }
593 
594 
595 // Explicitly declare these because we only support a few types anyway, and it means we can keep
596 // wxConfig detail out of the header file
597 template bool JSON_SETTINGS::fromLegacy<int>( wxConfigBase*, const std::string&,
598  const std::string& );
599 
600 template bool JSON_SETTINGS::fromLegacy<double>( wxConfigBase*, const std::string&,
601  const std::string& );
602 
603 template bool JSON_SETTINGS::fromLegacy<bool>( wxConfigBase*, const std::string&,
604  const std::string& );
605 
606 
607 bool JSON_SETTINGS::fromLegacyString( wxConfigBase* aConfig, const std::string& aKey,
608  const std::string& aDest )
609 {
610  wxString str;
611 
612  if( aConfig->Read( aKey, &str ) )
613  {
614  try
615  {
616  ( *this )[PointerFromString( aDest )] = str.ToUTF8();
617  }
618  catch( ... )
619  {
620  wxASSERT_MSG( false, wxT( "Could not write value in fromLegacyString!" ) );
621  return false;
622  }
623 
624  return true;
625  }
626 
627  return false;
628 }
629 
630 
631 bool JSON_SETTINGS::fromLegacyColor( wxConfigBase* aConfig, const std::string& aKey,
632  const std::string& aDest )
633 {
634  wxString str;
635 
636  if( aConfig->Read( aKey, &str ) )
637  {
639  color.SetFromWxString( str );
640 
641  try
642  {
643  nlohmann::json js = nlohmann::json::array( { color.r, color.g, color.b, color.a } );
644  ( *this )[PointerFromString( aDest )] = js;
645  }
646  catch( ... )
647  {
648  wxASSERT_MSG( false, wxT( "Could not write value in fromLegacyColor!" ) );
649  return false;
650  }
651 
652  return true;
653  }
654 
655  return false;
656 }
657 
658 
660 {
661  wxLogTrace( traceSettings, "AddNestedSettings %s", aSettings->GetFilename() );
662  m_nested_settings.push_back( aSettings );
663 }
664 
665 
667 {
668  if( !aSettings )
669  return;
670 
671  auto it = std::find_if( m_nested_settings.begin(), m_nested_settings.end(),
672  [&aSettings]( const JSON_SETTINGS* aPtr ) {
673  return aPtr == aSettings;
674  } );
675 
676  if( it != m_nested_settings.end() )
677  {
678  wxLogTrace( traceSettings, "Flush and release %s", ( *it )->GetFilename() );
679  ( *it )->SaveToFile();
680  m_nested_settings.erase( it );
681  }
682 
683  aSettings->SetParent( nullptr );
684 }
685 
686 
687 // Specializations to allow conversion between wxString and std::string via JSON_SETTINGS API
688 
689 template<> OPT<wxString> JSON_SETTINGS::Get( const std::string& aPath ) const
690 {
691  if( OPT<nlohmann::json> opt_json = GetJson( aPath ) )
692  return wxString( opt_json->get<std::string>().c_str(), wxConvUTF8 );
693 
694  return NULLOPT;
695 }
696 
697 
698 template<> void JSON_SETTINGS::Set<wxString>( const std::string& aPath, wxString aVal )
699 {
700  ( *this )[PointerFromString( std::move( aPath ) ) ] = aVal.ToUTF8();
701 }
702 
703 // Specializations to allow directly reading/writing wxStrings from JSON
704 
705 void to_json( nlohmann::json& aJson, const wxString& aString )
706 {
707  aJson = aString.ToUTF8();
708 }
709 
710 
711 void from_json( const nlohmann::json& aJson, wxString& aString )
712 {
713  aString = wxString( aJson.get<std::string>().c_str(), wxConvUTF8 );
714 }
void ResetToDefaults()
Resets all parameters to default values.
virtual bool Store()
Stores the current parameters into the JSON document represented by this object Note: this doesn't do...
std::vector< PARAM_BASE * > m_params
The list of parameters (owned by this object)
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
bool m_createIfMissing
Whether or not the backing store file should be created it if doesn't exist.
wxString GetFilename() const
Definition: json_settings.h:64
int color
Definition: DXF_plotter.cpp:60
virtual wxString getLegacyFileExt() const
bool parse(std::istream &aStream, bool aVerbose)
Parse a PCB or footprint file from the given input stream.
SETTINGS_LOC
Definition: json_settings.h:44
virtual bool LoadFromFile(const wxString &aDirectory="")
Loads the backing file from disk and then calls Load()
OPT< 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 GetFullFilename() const
nlohmann::json json
Definition: gerbview.cpp:40
void from_json(const nlohmann::json &aJson, wxString &aString)
void AddNestedSettings(NESTED_SETTINGS *aSettings)
Transfers ownership of a given NESTED_SETTINGS to this object.
bool m_deleteLegacyAfterMigration
Whether or not to delete legacy file after migration.
OPT< ValueType > Get(const std::string &aPath) const
Fetches a value from within the JSON document.
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>
wxString m_filename
The filename (not including path) of this settings file (inicode)
bool Migrate()
Migrates the schema of this settings from the version in the file to the latest version.
NESTED_SETTINGS is a JSON_SETTINGS that lives inside a JSON_SETTINGS.
virtual bool SaveToFile(const wxString &aDirectory="", bool aForce=false)
wxString m_legacy_filename
The filename of the wxConfig legacy file (if different from m_filename)
const auto NULLOPT
Definition: optional.h:9
void to_json(nlohmann::json &aJson, const wxString &aString)
static bool SetIfPresent(const nlohmann::json &aObj, const std::string &aPath, wxString &aTarget)
Sets the given string if the given key/path is present.
std::vector< NESTED_SETTINGS * > m_nested_settings
Nested settings files that live inside this one, if any.
JSON_SETTINGS(const wxString &aFilename, SETTINGS_LOC aLocation, int aSchemaVersion)
Definition: json_settings.h:56
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition: kicad_algo.h:81
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 m_resetParamsIfMissing
Whether or not to set parameters to their default value if missing from JSON on Load()
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.
void registerMigration(int aOldSchemaVersion, int aNewSchemaVersion, std::function< bool(void)> aMigrator)
Registers a migration from one schema version to another.
static LIB_PART * dummy()
Used to draw a dummy shape when a LIB_PART is not found in library.
boost::optional< T > OPT
Definition: optional.h:7
bool fromLegacy(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy wxConfig value to a given JSON pointer value.
virtual ~JSON_SETTINGS()
const wxChar *const traceSettings
Flag to enable debug output of settings operations and management.
bool m_createIfDefault
Whether or not the backing store file should be created if all parameters are still at their default ...
void ReleaseNestedSettings(NESTED_SETTINGS *aSettings)
Saves and frees a nested settings object, if it exists within this one.
virtual wxString getFileExt() const
static nlohmann::json::json_pointer PointerFromString(std::string aPath)
Builds a JSON pointer based on a given string.
virtual bool MigrateFromLegacy(wxConfigBase *aLegacyConfig)
Migrates from wxConfig to JSON-based configuration.
int m_schemaVersion
Version of this settings schema.
virtual void Load()
Updates the parameters of this object based on the current JSON document contents.
bool m_writeFile
Whether or not the backing store file should be written.
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:98
void SetParent(JSON_SETTINGS *aParent)