KiCad PCB EDA Suite
settings_manager.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 <[email protected]>
5  * Copyright (C) 2021 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 "settings/json_settings.h"
22 #include <regex>
23 #include <wx/debug.h>
24 #include <wx/dir.h>
25 #include <wx/filename.h>
26 #include <wx/snglinst.h>
27 #include <wx/stdpaths.h>
28 #include <wx/utils.h>
29 
30 #include <build_version.h>
31 #include <confirm.h>
33 #include <gestfich.h>
34 #include <kiplatform/environment.h>
35 #include <kiway.h>
36 #include <lockfile.h>
37 #include <macros.h>
38 #include <pgm_base.h>
39 #include <paths.h>
40 #include <project.h>
42 #include <project/project_file.h>
49 
50 
52  m_headless( aHeadless ),
53  m_kiway( nullptr ),
54  m_common_settings( nullptr ),
55  m_migration_source(),
56  m_migrateLibraryTables( true )
57 {
59 
60  // Check if the settings directory already exists, and if not, perform a migration if possible
61  if( !MigrateIfNeeded() )
62  {
63  m_ok = false;
64  return;
65  }
66 
67  m_ok = true;
68 
69  // create the common settings shared by all applications. Not loaded immediately
71 }
72 
74 {
75  m_settings.clear();
76  m_color_settings.clear();
77  m_projects.clear();
78 }
79 
80 
82 {
83  std::unique_ptr<JSON_SETTINGS> ptr( aSettings );
84 
85  ptr->SetManager( this );
86 
87  wxLogTrace( traceSettings, "Registered new settings object <%s>", ptr->GetFullFilename() );
88 
89  if( aLoadNow )
90  ptr->LoadFromFile( GetPathForSettingsFile( ptr.get() ) );
91 
92  m_settings.push_back( std::move( ptr ) );
93  return m_settings.back().get();
94 }
95 
96 
98 {
99  // TODO(JE) We should check for dirty settings here and write them if so, because
100  // Load() could be called late in the application lifecycle
101 
102  for( auto&& settings : m_settings )
103  settings->LoadFromFile( GetPathForSettingsFile( settings.get() ) );
104 }
105 
106 
108 {
109  auto it = std::find_if( m_settings.begin(), m_settings.end(),
110  [&aSettings]( const std::unique_ptr<JSON_SETTINGS>& aPtr )
111  {
112  return aPtr.get() == aSettings;
113  } );
114 
115  if( it != m_settings.end() )
116  ( *it )->LoadFromFile( GetPathForSettingsFile( it->get() ) );
117 }
118 
119 
121 {
122  for( auto&& settings : m_settings )
123  {
124  // Never automatically save color settings, caller should use SaveColorSettings
125  if( dynamic_cast<COLOR_SETTINGS*>( settings.get() ) )
126  continue;
127 
128  settings->SaveToFile( GetPathForSettingsFile( settings.get() ) );
129  }
130 }
131 
132 
134 {
135  auto it = std::find_if( m_settings.begin(), m_settings.end(),
136  [&aSettings]( const std::unique_ptr<JSON_SETTINGS>& aPtr )
137  {
138  return aPtr.get() == aSettings;
139  } );
140 
141  if( it != m_settings.end() )
142  {
143  wxLogTrace( traceSettings, "Saving %s", ( *it )->GetFullFilename() );
144  ( *it )->SaveToFile( GetPathForSettingsFile( it->get() ) );
145  }
146 }
147 
148 
149 void SETTINGS_MANAGER::FlushAndRelease( JSON_SETTINGS* aSettings, bool aSave )
150 {
151  auto it = std::find_if( m_settings.begin(), m_settings.end(),
152  [&aSettings]( const std::unique_ptr<JSON_SETTINGS>& aPtr )
153  {
154  return aPtr.get() == aSettings;
155  } );
156 
157  if( it != m_settings.end() )
158  {
159  wxLogTrace( traceSettings, "Flush and release %s", ( *it )->GetFullFilename() );
160 
161  if( aSave )
162  ( *it )->SaveToFile( GetPathForSettingsFile( it->get() ) );
163 
164  size_t typeHash = typeid( *it->get() ).hash_code();
165 
166  if( m_app_settings_cache.count( typeHash ) )
167  m_app_settings_cache.erase( typeHash );
168 
169  m_settings.erase( it );
170  }
171 }
172 
173 
175 {
176  if( m_color_settings.count( aName ) )
177  return m_color_settings.at( aName );
178 
179  if( !aName.empty() )
180  {
181  COLOR_SETTINGS* ret = loadColorSettingsByName( aName );
182 
183  if( !ret )
184  {
185  ret = registerColorSettings( aName );
186  *ret = *m_color_settings.at( "_builtin_default" );
187  ret->SetFilename( wxT( "user" ) );
188  ret->SetReadOnly( false );
189  }
190 
191  return ret;
192  }
193 
194  // This had better work
195  return m_color_settings.at( "_builtin_default" );
196 }
197 
198 
200 {
201  wxLogTrace( traceSettings, "Attempting to load color theme %s", aName );
202 
203  wxFileName fn( GetColorSettingsPath(), aName, "json" );
204 
205  if( !fn.IsOk() || !fn.Exists() )
206  {
207  wxLogTrace( traceSettings, "Theme file %s.json not found, falling back to user", aName );
208  return nullptr;
209  }
210 
211  COLOR_SETTINGS* settings = RegisterSettings( new COLOR_SETTINGS( aName ) );
212 
213  if( settings->GetFilename() != aName.ToStdString() )
214  {
215  wxLogTrace( traceSettings, "Warning: stored filename is actually %s, ",
216  settings->GetFilename() );
217  }
218 
219  m_color_settings[aName] = settings;
220 
221  return settings;
222 }
223 
224 
225 class JSON_DIR_TRAVERSER : public wxDirTraverser
226 {
227 private:
228  std::function<void( const wxFileName& )> m_action;
229 
230 public:
231  explicit JSON_DIR_TRAVERSER( std::function<void( const wxFileName& )> aAction )
232  : m_action( std::move( aAction ) )
233  {
234  }
235 
236  wxDirTraverseResult OnFile( const wxString& aFilePath ) override
237  {
238  wxFileName file( aFilePath );
239 
240  if( file.GetExt() == "json" )
241  m_action( file );
242 
243  return wxDIR_CONTINUE;
244  }
245 
246  wxDirTraverseResult OnDir( const wxString& dirPath ) override
247  {
248  return wxDIR_CONTINUE;
249  }
250 };
251 
252 
253 COLOR_SETTINGS* SETTINGS_MANAGER::registerColorSettings( const wxString& aName, bool aAbsolutePath )
254 {
255  if( !m_color_settings.count( aName ) )
256  {
257  COLOR_SETTINGS* colorSettings = RegisterSettings( new COLOR_SETTINGS( aName,
258  aAbsolutePath ) );
259  m_color_settings[aName] = colorSettings;
260  }
261 
262  return m_color_settings.at( aName );
263 }
264 
265 
267 {
268  if( aName.EndsWith( wxT( ".json" ) ) )
269  return registerColorSettings( aName.BeforeLast( '.' ) );
270  else
271  return registerColorSettings( aName );
272 }
273 
274 
276 {
277  if( !m_color_settings.count( "user" ) )
278  {
279  COLOR_SETTINGS* settings = registerColorSettings( wxT( "user" ) );
280  settings->SetName( wxT( "User" ) );
281  Save( settings );
282  }
283 
284  return m_color_settings.at( "user" );
285 }
286 
287 
289 {
290  // Create the built-in color settings
292  m_color_settings[settings->GetFilename()] = RegisterSettings( settings, false );
293 
294  wxFileName third_party_path;
295  const ENV_VAR_MAP& env = Pgm().GetLocalEnvVariables();
296  auto it = env.find( "KICAD6_3RD_PARTY" );
297 
298  if( it != env.end() && !it->second.GetValue().IsEmpty() )
299  third_party_path.SetPath( it->second.GetValue() );
300  else
301  third_party_path.SetPath( PATHS::GetDefault3rdPartyPath() );
302 
303  third_party_path.AppendDir( "colors" );
304 
305  wxDir third_party_colors_dir( third_party_path.GetFullPath() );
306  wxString color_settings_path = GetColorSettingsPath();
307 
308  // Search for and load any other settings
309  JSON_DIR_TRAVERSER loader( [&]( const wxFileName& aFilename )
310  {
311  registerColorSettings( aFilename.GetName() );
312  } );
313 
314  JSON_DIR_TRAVERSER thirdPartyLoader(
315  [&]( const wxFileName& aFilename )
316  {
317  COLOR_SETTINGS* settings = registerColorSettings( aFilename.GetFullPath(), true );
318  settings->SetReadOnly( true );
319  } );
320 
321  wxDir colors_dir( color_settings_path );
322 
323  if( colors_dir.IsOpened() )
324  {
325  if( third_party_colors_dir.IsOpened() )
326  third_party_colors_dir.Traverse( thirdPartyLoader );
327 
328  colors_dir.Traverse( loader );
329  }
330 }
331 
332 
334 {
335  m_color_settings.clear();
337 }
338 
339 
340 void SETTINGS_MANAGER::SaveColorSettings( COLOR_SETTINGS* aSettings, const std::string& aNamespace )
341 {
342  // The passed settings should already be managed
343  wxASSERT( std::find_if( m_color_settings.begin(), m_color_settings.end(),
344  [aSettings] ( const std::pair<wxString, COLOR_SETTINGS*>& el )
345  {
346  return el.second->GetFilename() == aSettings->GetFilename();
347  }
348  ) != m_color_settings.end() );
349 
350  if( aSettings->IsReadOnly() )
351  return;
352 
353  if( !aSettings->Store() )
354  {
355  wxLogTrace( traceSettings, "Color scheme %s not modified; skipping save",
356  aNamespace );
357  return;
358  }
359 
360  wxASSERT( aSettings->Contains( aNamespace ) );
361 
362  wxLogTrace( traceSettings, "Saving color scheme %s, preserving %s",
363  aSettings->GetFilename(),
364  aNamespace );
365 
366  OPT<nlohmann::json> backup = aSettings->GetJson( aNamespace );
367  wxString path = GetColorSettingsPath();
368 
369  aSettings->LoadFromFile( path );
370 
371  if( backup )
372  ( *aSettings->Internals() )[aNamespace].update( *backup );
373 
374  aSettings->Load();
375 
376  aSettings->SaveToFile( path, true );
377 }
378 
379 
381 {
382  wxASSERT( aSettings );
383 
384  switch( aSettings->GetLocation() )
385  {
386  case SETTINGS_LOC::USER:
387  return GetUserSettingsPath();
388 
390  return Prj().GetProjectPath();
391 
393  return GetColorSettingsPath();
394 
395  case SETTINGS_LOC::NONE:
396  return "";
397 
398  default:
399  wxASSERT_MSG( false, "Unknown settings location!" );
400  }
401 
402  return "";
403 }
404 
405 
406 class MIGRATION_TRAVERSER : public wxDirTraverser
407 {
408 private:
409  wxString m_src;
410  wxString m_dest;
411  wxString m_errors;
413 
414 public:
415  MIGRATION_TRAVERSER( const wxString& aSrcDir, const wxString& aDestDir, bool aMigrateTables ) :
416  m_src( aSrcDir ),
417  m_dest( aDestDir ),
418  m_migrateTables( aMigrateTables )
419  {
420  }
421 
422  wxString GetErrors() { return m_errors; }
423 
424  wxDirTraverseResult OnFile( const wxString& aSrcFilePath ) override
425  {
426  wxFileName file( aSrcFilePath );
427 
428  if( !m_migrateTables && ( file.GetName() == wxT( "sym-lib-table" ) ||
429  file.GetName() == wxT( "fp-lib-table" ) ) )
430  {
431  return wxDIR_CONTINUE;
432  }
433 
434  // Skip migrating PCM installed packages as packages themselves are not moved
435  if( file.GetFullName() == wxT( "installed_packages.json" ) )
436  return wxDIR_CONTINUE;
437 
438  // Don't migrate hotkeys config files; we don't have a reasonable migration handler for them
439  // and so there is no way to resolve conflicts at the moment
440  if( file.GetExt() == wxT( "hotkeys" ) )
441  return wxDIR_CONTINUE;
442 
443  wxString path = file.GetPath();
444 
445  path.Replace( m_src, m_dest, false );
446  file.SetPath( path );
447 
448  wxLogTrace( traceSettings, "Copying %s to %s", aSrcFilePath, file.GetFullPath() );
449 
450  // For now, just copy everything
451  KiCopyFile( aSrcFilePath, file.GetFullPath(), m_errors );
452 
453  return wxDIR_CONTINUE;
454  }
455 
456  wxDirTraverseResult OnDir( const wxString& dirPath ) override
457  {
458  wxFileName dir( dirPath );
459 
460  // Whitelist of directories to migrate
461  if( dir.GetName() == "colors" ||
462  dir.GetName() == "3d" )
463  {
464 
465  wxString path = dir.GetPath();
466 
467  path.Replace( m_src, m_dest, false );
468  dir.SetPath( path );
469 
470  wxMkdir( dir.GetFullPath() );
471 
472  return wxDIR_CONTINUE;
473  }
474  else
475  {
476  return wxDIR_IGNORE;
477  }
478  }
479 };
480 
481 
483 {
484  if( m_headless )
485  {
486  wxLogTrace( traceSettings, "Settings migration not checked; running headless" );
487  return false;
488  }
489 
490  wxFileName path( GetUserSettingsPath(), "" );
491  wxLogTrace( traceSettings, "Using settings path %s", path.GetFullPath() );
492 
493  if( path.DirExists() )
494  {
495  wxFileName common = path;
496  common.SetName( "kicad_common" );
497  common.SetExt( "json" );
498 
499  if( common.Exists() )
500  {
501  wxLogTrace( traceSettings, "Path exists and has a kicad_common, continuing!" );
502  return true;
503  }
504  }
505 
506  // Now we have an empty path, let's figure out what to put in it
507  DIALOG_MIGRATE_SETTINGS dlg( this );
508 
509  if( dlg.ShowModal() != wxID_OK )
510  {
511  wxLogTrace( traceSettings, "Migration dialog canceled; exiting" );
512  return false;
513  }
514 
515  if( !path.DirExists() )
516  {
517  wxLogTrace( traceSettings, "Path didn't exist; creating it" );
518  path.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
519  }
520 
521  if( m_migration_source.IsEmpty() )
522  {
523  wxLogTrace( traceSettings, "No migration source given; starting with defaults" );
524  return true;
525  }
526 
527  wxLogTrace( traceSettings, "Migrating from path %s", m_migration_source );
528 
530  wxDir source_dir( m_migration_source );
531 
532  source_dir.Traverse( traverser );
533 
534  if( !traverser.GetErrors().empty() )
535  DisplayErrorMessage( nullptr, traverser.GetErrors() );
536 
537  // Remove any library configuration if we didn't choose to import
539  {
540  COMMON_SETTINGS common;
541  wxString commonPath = GetPathForSettingsFile( &common );
542  common.LoadFromFile( commonPath );
543 
544  const std::vector<wxString> libKeys = {
545  wxT( "KICAD6_SYMBOL_DIR" ),
546  wxT( "KICAD6_3DMODEL_DIR" ),
547  wxT( "KICAD6_FOOTPRINT_DIR" ),
548  wxT( "KICAD6_TEMPLATE_DIR" ), // Stores the default library table to be copied
549 
550  // Deprecated keys
551  wxT( "KICAD_PTEMPLATES" ),
552  wxT( "KISYS3DMOD" ),
553  wxT( "KISYSMOD" ),
554  wxT( "KICAD_SYMBOL_DIR" ),
555  };
556 
557  for( const wxString& key : libKeys )
558  common.m_Env.vars.erase( key );
559 
560  common.SaveToFile( commonPath );
561  }
562 
563  return true;
564 }
565 
566 
567 bool SETTINGS_MANAGER::GetPreviousVersionPaths( std::vector<wxString>* aPaths )
568 {
569  wxASSERT( aPaths );
570 
571  aPaths->clear();
572 
573  wxDir dir;
574  std::vector<wxFileName> base_paths;
575 
576  base_paths.emplace_back( wxFileName( calculateUserSettingsPath( false ), "" ) );
577 
578  // If the env override is set, also check the default paths
579  if( wxGetEnv( wxT( "KICAD_CONFIG_HOME" ), nullptr ) )
580  base_paths.emplace_back( wxFileName( calculateUserSettingsPath( false, false ), "" ) );
581 
582 #ifdef __WXGTK__
583  // When running inside FlatPak, KIPLATFORM::ENV::GetUserConfigPath() will return a sandboxed
584  // path. In case the user wants to move from non-FlatPak KiCad to FlatPak KiCad, let's add our
585  // best guess as to the non-FlatPak config path. Unfortunately FlatPak also hides the host
586  // XDG_CONFIG_HOME, so if the user customizes their config path, they will have to browse
587  // for it.
588  {
589  wxFileName wxGtkPath;
590  wxGtkPath.AssignDir( "~/.config/kicad" );
591  wxGtkPath.MakeAbsolute();
592  base_paths.emplace_back( wxGtkPath.GetPath() );
593 
594  // We also want to pick up regular flatpak if we are nightly
595  wxGtkPath.AssignDir( "~/.var/app/org.kicad.KiCad/config/kicad" );
596  wxGtkPath.MakeAbsolute();
597  base_paths.emplace_back( wxGtkPath.GetPath() );
598  }
599 #endif
600 
601  wxString subdir;
602  std::string mine = GetSettingsVersion();
603 
604  auto check_dir = [&] ( const wxString& aSubDir )
605  {
606  // Only older versions are valid for migration
607  if( compareVersions( aSubDir.ToStdString(), mine ) <= 0 )
608  {
609  wxString sub_path = dir.GetNameWithSep() + aSubDir;
610 
611  if( IsSettingsPathValid( sub_path ) )
612  {
613  aPaths->push_back( sub_path );
614  wxLogTrace( traceSettings, "GetPreviousVersionName: %s is valid", sub_path );
615  }
616  }
617  };
618 
619  std::set<wxString> checkedPaths;
620 
621  for( auto base_path : base_paths )
622  {
623  if( checkedPaths.count( base_path.GetFullPath() ) )
624  continue;
625 
626  checkedPaths.insert( base_path.GetFullPath() );
627 
628  if( !dir.Open( base_path.GetFullPath() ) )
629  {
630  wxLogTrace( traceSettings, "GetPreviousVersionName: could not open base path %s",
631  base_path.GetFullPath() );
632  continue;
633  }
634 
635  wxLogTrace( traceSettings, "GetPreviousVersionName: checking base path %s",
636  base_path.GetFullPath() );
637 
638  if( dir.GetFirst( &subdir, wxEmptyString, wxDIR_DIRS ) )
639  {
640  if( subdir != mine )
641  check_dir( subdir );
642 
643  while( dir.GetNext( &subdir ) )
644  {
645  if( subdir != mine )
646  check_dir( subdir );
647  }
648  }
649 
650  // If we didn't find one yet, check for legacy settings without a version directory
651  if( IsSettingsPathValid( dir.GetNameWithSep() ) )
652  {
653  wxLogTrace( traceSettings,
654  "GetPreviousVersionName: root path %s is valid", dir.GetName() );
655  aPaths->push_back( dir.GetName() );
656  }
657  }
658 
659  return aPaths->size() > 0;
660 }
661 
662 
663 bool SETTINGS_MANAGER::IsSettingsPathValid( const wxString& aPath )
664 {
665  wxFileName test( aPath, "kicad_common" );
666 
667  if( test.Exists() )
668  return true;
669 
670  test.SetExt( "json" );
671 
672  return test.Exists();
673 }
674 
675 
677 {
678  wxFileName path;
679 
680  path.AssignDir( GetUserSettingsPath() );
681  path.AppendDir( "colors" );
682 
683  if( !path.DirExists() )
684  {
685  if( !wxMkdir( path.GetPath() ) )
686  {
687  wxLogTrace( traceSettings,
688  "GetColorSettingsPath(): Path %s missing and could not be created!",
689  path.GetPath() );
690  }
691  }
692 
693  return path.GetPath();
694 }
695 
696 
698 {
699  static wxString user_settings_path;
700 
701  if( user_settings_path.empty() )
702  user_settings_path = calculateUserSettingsPath();
703 
704  return user_settings_path;
705 }
706 
707 
708 wxString SETTINGS_MANAGER::calculateUserSettingsPath( bool aIncludeVer, bool aUseEnv )
709 {
710  wxFileName cfgpath;
711 
712  // http://docs.wxwidgets.org/3.0/classwx_standard_paths.html#a7c7cf595d94d29147360d031647476b0
713 
714  wxString envstr;
715  if( aUseEnv && wxGetEnv( wxT( "KICAD_CONFIG_HOME" ), &envstr ) && !envstr.IsEmpty() )
716  {
717  // Override the assignment above with KICAD_CONFIG_HOME
718  cfgpath.AssignDir( envstr );
719  }
720  else
721  {
722  cfgpath.AssignDir( KIPLATFORM::ENV::GetUserConfigPath() );
723 
724  cfgpath.AppendDir( TO_STR( KICAD_CONFIG_DIR ) );
725  }
726 
727  if( aIncludeVer )
728  cfgpath.AppendDir( GetSettingsVersion() );
729 
730  return cfgpath.GetPath();
731 }
732 
733 
735 {
736  // CMake computes the major.minor string for us.
737  return GetMajorMinorVersion().ToStdString();
738 }
739 
740 
741 int SETTINGS_MANAGER::compareVersions( const std::string& aFirst, const std::string& aSecond )
742 {
743  int a_maj = 0;
744  int a_min = 0;
745  int b_maj = 0;
746  int b_min = 0;
747 
748  if( !extractVersion( aFirst, &a_maj, &a_min ) || !extractVersion( aSecond, &b_maj, &b_min ) )
749  {
750  wxLogTrace( traceSettings, "compareSettingsVersions: bad input (%s, %s)", aFirst, aSecond );
751  return -1;
752  }
753 
754  if( a_maj < b_maj )
755  {
756  return -1;
757  }
758  else if( a_maj > b_maj )
759  {
760  return 1;
761  }
762  else
763  {
764  if( a_min < b_min )
765  {
766  return -1;
767  }
768  else if( a_min > b_min )
769  {
770  return 1;
771  }
772  else
773  {
774  return 0;
775  }
776  }
777 }
778 
779 
780 bool SETTINGS_MANAGER::extractVersion( const std::string& aVersionString, int* aMajor, int* aMinor )
781 {
782  std::regex re_version( "(\\d+)\\.(\\d+)" );
783  std::smatch match;
784 
785  if( std::regex_match( aVersionString, match, re_version ) )
786  {
787  try
788  {
789  *aMajor = std::stoi( match[1].str() );
790  *aMinor = std::stoi( match[2].str() );
791  }
792  catch( ... )
793  {
794  return false;
795  }
796 
797  return true;
798  }
799 
800  return false;
801 }
802 
803 
804 bool SETTINGS_MANAGER::LoadProject( const wxString& aFullPath, bool aSetActive )
805 {
806  // Normalize path to new format even if migrating from a legacy file
807  wxFileName path( aFullPath );
808 
809  if( path.GetExt() == LegacyProjectFileExtension )
810  path.SetExt( ProjectFileExtension );
811 
812  wxString fullPath = path.GetFullPath();
813 
814  // If already loaded, we are all set. This might be called more than once over a project's
815  // lifetime in case the project is first loaded by the KiCad manager and then eeschema or
816  // pcbnew try to load it again when they are launched.
817  if( m_projects.count( fullPath ) )
818  return true;
819 
820  bool readOnly = false;
821  std::unique_ptr<wxSingleInstanceChecker> lockFile = ::LockFile( fullPath );
822 
823  if( !lockFile )
824  {
825  wxLogTrace( traceSettings, "Project %s is locked; opening read-only", fullPath );
826  readOnly = true;
827  }
828 
829  // No MDI yet
830  if( aSetActive && !m_projects.empty() )
831  {
832  PROJECT* oldProject = m_projects.begin()->second;
833  unloadProjectFile( oldProject, false );
834  m_projects.erase( m_projects.begin() );
835 
836  auto it = std::find_if( m_projects_list.begin(), m_projects_list.end(),
837  [&]( const std::unique_ptr<PROJECT>& ptr )
838  {
839  return ptr.get() == oldProject;
840  } );
841 
842  wxASSERT( it != m_projects_list.end() );
843  m_projects_list.erase( it );
844  }
845 
846  wxLogTrace( traceSettings, "Load project %s", fullPath );
847 
848  std::unique_ptr<PROJECT> project = std::make_unique<PROJECT>();
849  project->setProjectFullName( fullPath );
850 
851  bool success = loadProjectFile( *project );
852 
853  if( success )
854  {
855  project->SetReadOnly( readOnly || project->GetProjectFile().IsReadOnly() );
856 
857  if( lockFile )
858  m_project_lock.reset( lockFile.release() );
859  }
860 
861  m_projects_list.push_back( std::move( project ) );
862  m_projects[fullPath] = m_projects_list.back().get();
863 
864  wxString fn( path.GetName() );
865 
866  PROJECT_LOCAL_SETTINGS* settings = new PROJECT_LOCAL_SETTINGS( m_projects[fullPath], fn );
867 
868  if( aSetActive )
869  settings = RegisterSettings( settings );
870  else
871  settings->LoadFromFile( path.GetPath() );
872 
873  m_projects[fullPath]->setLocalSettings( settings );
874 
875  if( aSetActive && m_kiway )
877 
878  return success;
879 }
880 
881 
882 bool SETTINGS_MANAGER::UnloadProject( PROJECT* aProject, bool aSave )
883 {
884  if( !aProject || !m_projects.count( aProject->GetProjectFullName() ) )
885  return false;
886 
887  if( !unloadProjectFile( aProject, aSave ) )
888  return false;
889 
890  wxString projectPath = aProject->GetProjectFullName();
891  wxLogTrace( traceSettings, "Unload project %s", projectPath );
892 
893  PROJECT* toRemove = m_projects.at( projectPath );
894  auto it = std::find_if( m_projects_list.begin(), m_projects_list.end(),
895  [&]( const std::unique_ptr<PROJECT>& ptr )
896  {
897  return ptr.get() == toRemove;
898  } );
899 
900  wxASSERT( it != m_projects_list.end() );
901  m_projects_list.erase( it );
902 
903  m_projects.erase( projectPath );
904 
905  // Immediately reload a null project; this is required until the rest of the application
906  // is refactored to not assume that Prj() always works
907  if( m_projects.empty() )
908  LoadProject( "" );
909 
910  // Remove the reference in the environment to the previous project
911  wxSetEnv( PROJECT_VAR_NAME, "" );
912 
913  // Release lock on the file, in case we had one
914  m_project_lock = nullptr;
915 
916  if( m_kiway )
918 
919  return true;
920 }
921 
922 
924 {
925  // No MDI yet: First project in the list is the active project
926  wxASSERT_MSG( m_projects_list.size(), "no project in list" );
927  return *m_projects_list.begin()->get();
928 }
929 
930 
932 {
933  return !m_projects.empty();
934 }
935 
936 
937 PROJECT* SETTINGS_MANAGER::GetProject( const wxString& aFullPath ) const
938 {
939  if( m_projects.count( aFullPath ) )
940  return m_projects.at( aFullPath );
941 
942  return nullptr;
943 }
944 
945 
946 std::vector<wxString> SETTINGS_MANAGER::GetOpenProjects() const
947 {
948  std::vector<wxString> ret;
949 
950  for( const std::pair<const wxString, PROJECT*>& pair : m_projects )
951  ret.emplace_back( pair.first );
952 
953  return ret;
954 }
955 
956 
957 bool SETTINGS_MANAGER::SaveProject( const wxString& aFullPath )
958 {
959  wxString path = aFullPath;
960 
961  if( path.empty() )
963 
964  // TODO: refactor for MDI
965  if( Prj().IsReadOnly() )
966  return false;
967 
968  if( !m_project_files.count( path ) )
969  return false;
970 
972  wxString projectPath = GetPathForSettingsFile( project );
973 
974  project->SaveToFile( projectPath );
975  Prj().GetLocalSettings().SaveToFile( projectPath );
976 
977  return true;
978 }
979 
980 
981 void SETTINGS_MANAGER::SaveProjectAs( const wxString& aFullPath )
982 {
983  wxString oldName = Prj().GetProjectFullName();
984 
985  if( aFullPath.IsSameAs( oldName ) )
986  {
987  SaveProject( aFullPath );
988  return;
989  }
990 
991  // Changing this will cause UnloadProject to not save over the "old" project when loading below
992  Prj().setProjectFullName( aFullPath );
993 
994  wxFileName fn( aFullPath );
995 
996  PROJECT_FILE* project = m_project_files.at( oldName );
997 
998  // Ensure read-only flags are copied; this allows doing a "Save As" on a standalong board/sch
999  // without creating project files if the checkbox is turned off
1000  project->SetReadOnly( Prj().IsReadOnly() );
1001  Prj().GetLocalSettings().SetReadOnly( Prj().IsReadOnly() );
1002 
1003  project->SetFilename( fn.GetName() );
1004  project->SaveToFile( fn.GetPath() );
1005 
1006  Prj().GetLocalSettings().SetFilename( fn.GetName() );
1007  Prj().GetLocalSettings().SaveToFile( fn.GetPath() );
1008 
1009  m_project_files[fn.GetFullPath()] = project;
1010  m_project_files.erase( oldName );
1011 
1012  m_projects[fn.GetFullPath()] = m_projects[oldName];
1013  m_projects.erase( oldName );
1014 }
1015 
1016 
1017 void SETTINGS_MANAGER::SaveProjectCopy( const wxString& aFullPath )
1018 {
1019  PROJECT_FILE* project = m_project_files.at( Prj().GetProjectFullName() );
1020  wxString oldName = project->GetFilename();
1021  wxFileName fn( aFullPath );
1022 
1023  bool readOnly = project->IsReadOnly();
1024  project->SetReadOnly( false );
1025 
1026  project->SetFilename( fn.GetName() );
1027  project->SaveToFile( fn.GetPath() );
1028  project->SetFilename( oldName );
1029 
1030  Prj().GetLocalSettings().SetFilename( fn.GetName() );
1031  Prj().GetLocalSettings().SaveToFile( fn.GetPath() );
1032  Prj().GetLocalSettings().SetFilename( oldName );
1033 
1034  project->SetReadOnly( readOnly );
1035 }
1036 
1037 
1039 {
1040  wxFileName fullFn( aProject.GetProjectFullName() );
1041  wxString fn( fullFn.GetName() );
1042 
1043  PROJECT_FILE* file = RegisterSettings( new PROJECT_FILE( fn ), false );
1044 
1045  m_project_files[aProject.GetProjectFullName()] = file;
1046 
1047  aProject.setProjectFile( file );
1048  file->SetProject( &aProject );
1049 
1050  wxString path( fullFn.GetPath() );
1051 
1052  return file->LoadFromFile( path );
1053 }
1054 
1055 
1056 bool SETTINGS_MANAGER::unloadProjectFile( PROJECT* aProject, bool aSave )
1057 {
1058  if( !aProject )
1059  return false;
1060 
1061  wxString name = aProject->GetProjectFullName();
1062 
1063  if( !m_project_files.count( name ) )
1064  return false;
1065 
1067 
1068  auto it = std::find_if( m_settings.begin(), m_settings.end(),
1069  [&file]( const std::unique_ptr<JSON_SETTINGS>& aPtr )
1070  {
1071  return aPtr.get() == file;
1072  } );
1073 
1074  if( it != m_settings.end() )
1075  {
1076  wxString projectPath = GetPathForSettingsFile( it->get() );
1077 
1078  FlushAndRelease( &aProject->GetLocalSettings(), aSave );
1079 
1080  if( aSave )
1081  ( *it )->SaveToFile( projectPath );
1082 
1083  m_settings.erase( it );
1084  }
1085 
1086  m_project_files.erase( name );
1087 
1088  return true;
1089 }
1090 
1091 
1093 {
1095 }
1096 
1097 
1098 wxString SETTINGS_MANAGER::backupDateTimeFormat = wxT( "%Y-%m-%d_%H%M%S" );
1099 
1100 
1102 {
1103  wxDateTime timestamp = wxDateTime::Now();
1104 
1105  wxString fileName = wxString::Format( wxT( "%s-%s" ), Prj().GetProjectName(),
1106  timestamp.Format( backupDateTimeFormat ) );
1107 
1108  wxFileName target;
1109  target.SetPath( GetProjectBackupsPath() );
1110  target.SetName( fileName );
1111  target.SetExt( ArchiveFileExtension );
1112 
1113  wxDir dir( target.GetPath() );
1114 
1115  if( !target.DirExists() && !wxMkdir( target.GetPath() ) )
1116  {
1117  wxLogTrace( traceSettings, "Could not create project backup path %s", target.GetPath() );
1118  return false;
1119  }
1120 
1121  if( !target.IsDirWritable() )
1122  {
1123  wxLogTrace( traceSettings, "Backup directory %s is not writable", target.GetPath() );
1124  return false;
1125  }
1126 
1127  wxLogTrace( traceSettings, "Backing up project to %s", target.GetPath() );
1128 
1129  PROJECT_ARCHIVER archiver;
1130 
1131  return archiver.Archive( Prj().GetProjectPath(), target.GetFullPath(), aReporter );
1132 }
1133 
1134 
1135 class VECTOR_INSERT_TRAVERSER : public wxDirTraverser
1136 {
1137 public:
1138  VECTOR_INSERT_TRAVERSER( std::vector<wxString>& aVec,
1139  std::function<bool( const wxString& )> aCond ) :
1140  m_files( aVec ),
1141  m_condition( aCond )
1142  {
1143  }
1144 
1145  wxDirTraverseResult OnFile( const wxString& aFile ) override
1146  {
1147  if( m_condition( aFile ) )
1148  m_files.emplace_back( aFile );
1149 
1150  return wxDIR_CONTINUE;
1151  }
1152 
1153  wxDirTraverseResult OnDir( const wxString& aDirName ) override
1154  {
1155  return wxDIR_CONTINUE;
1156  }
1157 
1158 private:
1159  std::vector<wxString>& m_files;
1160 
1161  std::function<bool( const wxString& )> m_condition;
1162 };
1163 
1164 
1166 {
1168 
1169  if( !settings.enabled )
1170  return true;
1171 
1172  wxString prefix = Prj().GetProjectName() + '-';
1173 
1174  auto modTime =
1175  [&prefix]( const wxString& aFile )
1176  {
1177  wxDateTime dt;
1178  wxString fn( wxFileName( aFile ).GetName() );
1179  fn.Replace( prefix, "" );
1180  dt.ParseFormat( fn, backupDateTimeFormat );
1181  return dt;
1182  };
1183 
1184  wxFileName projectPath( Prj().GetProjectPath() );
1185 
1186  // Skip backup if project path isn't valid or writable
1187  if( !projectPath.IsOk() || !projectPath.Exists() || !projectPath.IsDirWritable() )
1188  return true;
1189 
1190  wxString backupPath = GetProjectBackupsPath();
1191 
1192  if( !wxDirExists( backupPath ) )
1193  {
1194  wxLogTrace( traceSettings, "Backup path %s doesn't exist, creating it", backupPath );
1195 
1196  if( !wxMkdir( backupPath ) )
1197  {
1198  wxLogTrace( traceSettings, "Could not create backups path! Skipping backup" );
1199  return false;
1200  }
1201  }
1202 
1203  wxDir dir( backupPath );
1204 
1205  if( !dir.IsOpened() )
1206  {
1207  wxLogTrace( traceSettings, "Could not open project backups path %s", dir.GetName() );
1208  return false;
1209  }
1210 
1211  std::vector<wxString> files;
1212 
1213  VECTOR_INSERT_TRAVERSER traverser( files,
1214  [&modTime]( const wxString& aFile )
1215  {
1216  return modTime( aFile ).IsValid();
1217  } );
1218 
1219  dir.Traverse( traverser, wxT( "*.zip" ) );
1220 
1221  // Sort newest-first
1222  std::sort( files.begin(), files.end(),
1223  [&]( const wxString& aFirst, const wxString& aSecond ) -> bool
1224  {
1225  wxDateTime first = modTime( aFirst );
1226  wxDateTime second = modTime( aSecond );
1227 
1228  return first.GetTicks() > second.GetTicks();
1229  } );
1230 
1231  // Do we even need to back up?
1232  if( !files.empty() )
1233  {
1234  wxDateTime lastTime = modTime( files[0] );
1235 
1236  if( lastTime.IsValid() )
1237  {
1238  wxTimeSpan delta = wxDateTime::Now() - modTime( files[0] );
1239 
1240  if( delta.IsShorterThan( wxTimeSpan::Seconds( settings.min_interval ) ) )
1241  return true;
1242  }
1243  }
1244 
1245  // Now that we know a backup is needed, apply the retention policy
1246 
1247  // Step 1: if we're over the total file limit, remove the oldest
1248  if( !files.empty() && settings.limit_total_files > 0 )
1249  {
1250  while( files.size() > static_cast<size_t>( settings.limit_total_files ) )
1251  {
1252  wxRemoveFile( files.back() );
1253  files.pop_back();
1254  }
1255  }
1256 
1257  // Step 2: Stay under the total size limit
1258  if( settings.limit_total_size > 0 )
1259  {
1260  wxULongLong totalSize = 0;
1261 
1262  for( const wxString& file : files )
1263  totalSize += wxFileName::GetSize( file );
1264 
1265  while( !files.empty() && totalSize > static_cast<wxULongLong>( settings.limit_total_size ) )
1266  {
1267  totalSize -= wxFileName::GetSize( files.back() );
1268  wxRemoveFile( files.back() );
1269  files.pop_back();
1270  }
1271  }
1272 
1273  // Step 3: Stay under the daily limit
1274  if( settings.limit_daily_files > 0 && files.size() > 1 )
1275  {
1276  wxDateTime day = modTime( files[0] );
1277  int num = 1;
1278 
1279  wxASSERT( day.IsValid() );
1280 
1281  std::vector<wxString> filesToDelete;
1282 
1283  for( size_t i = 1; i < files.size(); i++ )
1284  {
1285  wxDateTime dt = modTime( files[i] );
1286 
1287  if( dt.IsSameDate( day ) )
1288  {
1289  num++;
1290 
1291  if( num > settings.limit_daily_files )
1292  filesToDelete.emplace_back( files[i] );
1293  }
1294  else
1295  {
1296  day = dt;
1297  num = 1;
1298  }
1299  }
1300 
1301  for( const wxString& file : filesToDelete )
1302  wxRemoveFile( file );
1303  }
1304 
1305  return BackupProject( aReporter );
1306 }
static wxString backupDateTimeFormat
bool Archive(const wxString &aSrcDir, const wxString &aDestFile, REPORTER &aReporter, bool aVerbose=true, bool aIncludeExtraFiles=false)
Creates an archive of the project.
virtual bool Store()
Stores the current parameters into the JSON document represented by this object Note: this doesn't do...
JSON_SETTINGS_INTERNALS * Internals()
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
Container for project specific data.
Definition: project.h:62
unsigned long long limit_total_size
Maximum total size of backups (bytes), 0 for unlimited.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:292
#define TO_STR(x)
Definition: macros.h:105
std::unique_ptr< wxSingleInstanceChecker > m_project_lock
Lock for loaded project (expand to multiple once we support MDI)
T * RegisterSettings(T *aSettings, bool aLoadNow=true)
Takes ownership of the pointer passed in.
This file is part of the common library.
std::function< void(const wxFileName &)> m_action
void SaveProjectAs(const wxString &aFullPath)
Sets the currently loaded project path and saves it (pointers remain valid) Note that this will not m...
static bool IsSettingsPathValid(const wxString &aPath)
Checks if a given path is probably a valid KiCad configuration directory.
const std::string ProjectFileExtension
KIWAY * m_kiway
The kiway this settings manager interacts with.
void SetReadOnly(bool aReadOnly)
Definition: json_settings.h:84
wxString GetFilename() const
Definition: json_settings.h:72
bool m_migrateLibraryTables
If true, the symbol and footprint library tables will be migrated from the previous version.
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition: project.h:38
bool enabled
Automatically back up the project when files are saved.
AUTO_BACKUP m_Backup
virtual bool LoadFromFile(const wxString &aDirectory="")
Loads the backing file from disk and then calls Load()
The project local settings are things that are attached to a particular project, but also might be pa...
bool SaveToFile(const wxString &aDirectory="", bool aForce=false) override
wxDirTraverseResult OnFile(const wxString &aSrcFilePath) override
void KiCopyFile(const wxString &aSrcPath, const wxString &aDestPath, wxString &aErrors)
Definition: gestfich.cpp:214
std::unordered_map< size_t, JSON_SETTINGS * > m_app_settings_cache
Cache for app settings.
Definition: bitmap.cpp:64
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....
VECTOR_INSERT_TRAVERSER(std::vector< wxString > &aVec, std::function< bool(const wxString &)> aCond)
std::map< wxString, PROJECT_FILE * > m_project_files
Loaded project files, mapped according to project full name.
bool MigrateIfNeeded()
Handles the initialization of the user settings directory and migration from previous KiCad versions ...
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:70
COLOR_SETTINGS * AddNewColorSettings(const wxString &aFilename)
Registers a new color settings object with the given filename.
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
static int compareVersions(const std::string &aFirst, const std::string &aSecond)
Compares two settings versions, like "5.99" and "6.0".
bool BackupProject(REPORTER &aReporter) const
Creates a backup archive of the current project.
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:122
The backing store for a PROJECT, in JSON format.
Definition: project_file.h:64
wxString GetMajorMinorVersion()
Get only the major and minor version in a string major.minor.
This file contains miscellaneous commonly used macros and functions.
virtual PROJECT_LOCAL_SETTINGS & GetLocalSettings() const
Definition: project.h:151
std::vector< wxString > & m_files
The color scheme directory (e.g. ~/.config/kicad/colors/)
wxString m_migration_source
bool m_ok
True if settings loaded successfully at construction.
COLOR_SETTINGS * registerColorSettings(const wxString &aFilename, bool aAbsolutePath=false)
bool unloadProjectFile(PROJECT *aProject, bool aSave)
Optionally saves, and then unloads and unregisters the given PROJECT_FILE.
virtual bool SaveToFile(const wxString &aDirectory="", bool aForce=false)
void SaveProjectCopy(const wxString &aFullPath)
Saves a copy of the current project under the given path.
bool IsProjectOpen() const
Helper for checking if we have a project open TODO: This should be deprecated along with Prj() once w...
wxDirTraverseResult OnDir(const wxString &aDirName) override
The settings directory inside a project folder.
wxDirTraverseResult OnDir(const wxString &dirPath) override
bool TriggerBackupIfNeeded(REPORTER &aReporter) const
Calls BackupProject if a new backup is needed according to the current backup policy.
SETTINGS_LOC GetLocation() const
Definition: json_settings.h:79
std::function< bool(const wxString &)> m_condition
bool GetPreviousVersionPaths(std::vector< wxString > *aName=nullptr)
Retrieves the name of the most recent previous KiCad version that can be found in the user settings d...
COMMON_SETTINGS * GetCommonSettings() const
Retrieves the common settings shared by all applications.
virtual void ProjectChanged()
Calls ProjectChanged() on all KIWAY_PLAYERs.
Definition: kiway.cpp:577
std::unique_ptr< wxSingleInstanceChecker > LockFile(const wxString &aFileName)
Test to see if aFileName can be locked (is not already locked) and only then returns a wxSingleInstan...
Definition: lockfile.cpp:34
Definition of file extensions used in Kicad.
static std::vector< COLOR_SETTINGS * > CreateBuiltinColorSettings()
Constructs and returns a list of color settings objects based on the built-in color themes.
bool m_headless
True if running outside a UI context.
wxDirTraverseResult OnFile(const wxString &aFile) override
static void EnsureUserPathsExist()
Ensures/creates user default paths.
Definition: paths.cpp:341
void SetName(const wxString &aName)
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
Definition: project.cpp:116
ENVIRONMENT m_Env
const std::string LegacyProjectFileExtension
No directory prepended, full path in filename (used for PROJECT_FILE)
std::map< wxString, PROJECT * > m_projects
Loaded projects, mapped according to project full name.
JSON_DIR_TRAVERSER(std::function< void(const wxFileName &)> aAction)
wxDirTraverseResult OnFile(const wxString &aFilePath) override
SETTINGS_MANAGER(bool aHeadless=false)
virtual void setProjectFullName(const wxString &aFullPathAndName)
Set the full directory, basename, and extension of the project.
Definition: project.cpp:84
static wxString GetColorSettingsPath()
Returns the path where color scheme files are stored; creating it if missing (normally .
wxString GetProjectBackupsPath() const
bool loadProjectFile(PROJECT &aProject)
Registers a PROJECT_FILE and attempts to load it from disk.
static wxString GetUserSettingsPath()
Return the user configuration path used to store KiCad's configuration files.
std::unordered_map< wxString, COLOR_SETTINGS * > m_color_settings
int min_interval
Minimum time, in seconds, between subsequent backups.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Loads a project or sets up a new project with a specified path.
COLOR_SETTINGS * loadColorSettingsByName(const wxString &aName)
Attempts to load a color theme by name (the color theme directory and .json ext are assumed)
COLOR_SETTINGS * GetColorSettings(const wxString &aName="user")
Retrieves a color settings object that applications can read colors from.
void ReloadColorSettings()
Re-scans the color themes directory, reloading any changes it finds.
std::vector< std::unique_ptr< JSON_SETTINGS > > m_settings
see class PGM_BASE
std::map< wxString, ENV_VAR_ITEM > ENV_VAR_MAP
const char * name
Definition: DXF_plotter.cpp:56
std::vector< wxString > GetOpenProjects() const
static wxString calculateUserSettingsPath(bool aIncludeVer=true, bool aUseEnv=true)
Determines the base path for user settings files.
#define PROJECT_BACKUPS_DIR_SUFFIX
Project settings path will be <projectname> + this.
bool UnloadProject(PROJECT *aProject, bool aSave=true)
Saves, unloads and unregisters the given PROJECT.
The main config directory (e.g. ~/.config/kicad/)
bool SaveProject(const wxString &aFullPath=wxEmptyString)
Saves a loaded project.
wxDirTraverseResult OnDir(const wxString &dirPath) override
void SaveColorSettings(COLOR_SETTINGS *aSettings, const std::string &aNamespace="")
Safely saves a COLOR_SETTINGS to disk, preserving any changes outside the given namespace.
bool IsReadOnly() const
Definition: json_settings.h:83
wxString GetUserConfigPath()
Retrieves the operating system specific path for a user's configuration store.
boost::optional< T > OPT
Definition: optional.h:7
static std::string GetSettingsVersion()
Parses the current KiCad build version and extracts the major and minor revision to use as the name o...
JSON_SETTINGS * registerSettings(JSON_SETTINGS *aSettings, bool aLoadNow=true)
Color settings are a bit different than most of the settings objects in that there can be more than o...
constexpr int delta
virtual const wxString GetProjectName() const
Return the short name of the project.
Definition: project.cpp:128
bool Contains(const std::string &aPath) const
int limit_daily_files
Maximum files to keep per day, 0 for unlimited.
const wxChar *const traceSettings
Flag to enable debug output of settings operations and management.
COMMON_SETTINGS * m_common_settings
void SetFilename(const wxString &aFilename)
Definition: json_settings.h:76
virtual void setProjectFile(PROJECT_FILE *aFile)
Set the backing store file for this project.
Definition: project.h:320
static wxString GetDefault3rdPartyPath()
Gets the default path for PCM packages.
Definition: paths.cpp:129
COLOR_SETTINGS * GetMigratedColorSettings()
Returns a color theme for storing colors migrated from legacy (5.x and earlier) settings,...
MIGRATION_TRAVERSER(const wxString &aSrcDir, const wxString &aDestDir, bool aMigrateTables)
void FlushAndRelease(JSON_SETTINGS *aSettings, bool aSave=true)
If the given settings object is registered, save it to disk and unregister it.
std::vector< std::unique_ptr< PROJECT > > m_projects_list
Loaded projects (ownership here)
virtual void Load()
Updates the parameters of this object based on the current JSON document contents.
PROJECT * GetProject(const wxString &aFullPath) const
Retrieves a loaded project by name.
int limit_total_files
Maximum number of backup archives to retain.
static bool extractVersion(const std::string &aVersionString, int *aMajor, int *aMinor)
Extracts the numeric version from a given settings string.
wxString GetPathForSettingsFile(JSON_SETTINGS *aSettings)
Returns the path a given settings file should be loaded from / stored to.
const std::string ArchiveFileExtension
File locking utilities.