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