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, wxT( "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, wxT( "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, wxT( "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, wxT( "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, wxT( "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, wxT( "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, wxT( "Color scheme %s not modified; skipping save" ),
356  aNamespace );
357  return;
358  }
359 
360  wxASSERT( aSettings->Contains( aNamespace ) );
361 
362  wxLogTrace( traceSettings, wxT( "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  // TODO: MDI support
391  return Prj().GetProjectPath();
392 
394  return GetColorSettingsPath();
395 
396  case SETTINGS_LOC::NONE:
397  return "";
398 
399  default:
400  wxASSERT_MSG( false, wxT( "Unknown settings location!" ) );
401  }
402 
403  return "";
404 }
405 
406 
407 class MIGRATION_TRAVERSER : public wxDirTraverser
408 {
409 private:
410  wxString m_src;
411  wxString m_dest;
412  wxString m_errors;
414 
415 public:
416  MIGRATION_TRAVERSER( const wxString& aSrcDir, const wxString& aDestDir, bool aMigrateTables ) :
417  m_src( aSrcDir ),
418  m_dest( aDestDir ),
419  m_migrateTables( aMigrateTables )
420  {
421  }
422 
423  wxString GetErrors() { return m_errors; }
424 
425  wxDirTraverseResult OnFile( const wxString& aSrcFilePath ) override
426  {
427  wxFileName file( aSrcFilePath );
428 
429  if( !m_migrateTables && ( file.GetName() == wxT( "sym-lib-table" ) ||
430  file.GetName() == wxT( "fp-lib-table" ) ) )
431  {
432  return wxDIR_CONTINUE;
433  }
434 
435  // Skip migrating PCM installed packages as packages themselves are not moved
436  if( file.GetFullName() == wxT( "installed_packages.json" ) )
437  return wxDIR_CONTINUE;
438 
439  // Don't migrate hotkeys config files; we don't have a reasonable migration handler for them
440  // and so there is no way to resolve conflicts at the moment
441  if( file.GetExt() == wxT( "hotkeys" ) )
442  return wxDIR_CONTINUE;
443 
444  wxString path = file.GetPath();
445 
446  path.Replace( m_src, m_dest, false );
447  file.SetPath( path );
448 
449  wxLogTrace( traceSettings, wxT( "Copying %s to %s" ), aSrcFilePath, file.GetFullPath() );
450 
451  // For now, just copy everything
452  KiCopyFile( aSrcFilePath, file.GetFullPath(), m_errors );
453 
454  return wxDIR_CONTINUE;
455  }
456 
457  wxDirTraverseResult OnDir( const wxString& dirPath ) override
458  {
459  wxFileName dir( dirPath );
460 
461  // Whitelist of directories to migrate
462  if( dir.GetName() == "colors" ||
463  dir.GetName() == "3d" )
464  {
465 
466  wxString path = dir.GetPath();
467 
468  path.Replace( m_src, m_dest, false );
469  dir.SetPath( path );
470 
471  wxMkdir( dir.GetFullPath() );
472 
473  return wxDIR_CONTINUE;
474  }
475  else
476  {
477  return wxDIR_IGNORE;
478  }
479  }
480 };
481 
482 
484 {
485  if( m_headless )
486  {
487  wxLogTrace( traceSettings, wxT( "Settings migration not checked; running headless" ) );
488  return false;
489  }
490 
491  wxFileName path( GetUserSettingsPath(), "" );
492  wxLogTrace( traceSettings, wxT( "Using settings path %s" ), path.GetFullPath() );
493 
494  if( path.DirExists() )
495  {
496  wxFileName common = path;
497  common.SetName( "kicad_common" );
498  common.SetExt( "json" );
499 
500  if( common.Exists() )
501  {
502  wxLogTrace( traceSettings, wxT( "Path exists and has a kicad_common, continuing!" ) );
503  return true;
504  }
505  }
506 
507  // Now we have an empty path, let's figure out what to put in it
508  DIALOG_MIGRATE_SETTINGS dlg( this );
509 
510  if( dlg.ShowModal() != wxID_OK )
511  {
512  wxLogTrace( traceSettings, wxT( "Migration dialog canceled; exiting" ) );
513  return false;
514  }
515 
516  if( !path.DirExists() )
517  {
518  wxLogTrace( traceSettings, wxT( "Path didn't exist; creating it" ) );
519  path.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
520  }
521 
522  if( m_migration_source.IsEmpty() )
523  {
524  wxLogTrace( traceSettings, wxT( "No migration source given; starting with defaults" ) );
525  return true;
526  }
527 
528  wxLogTrace( traceSettings, wxT( "Migrating from path %s" ), m_migration_source );
529 
531  wxDir source_dir( m_migration_source );
532 
533  source_dir.Traverse( traverser );
534 
535  if( !traverser.GetErrors().empty() )
536  DisplayErrorMessage( nullptr, traverser.GetErrors() );
537 
538  // Remove any library configuration if we didn't choose to import
540  {
541  COMMON_SETTINGS common;
542  wxString commonPath = GetPathForSettingsFile( &common );
543  common.LoadFromFile( commonPath );
544 
545  const std::vector<wxString> libKeys = {
546  wxT( "KICAD6_SYMBOL_DIR" ),
547  wxT( "KICAD6_3DMODEL_DIR" ),
548  wxT( "KICAD6_FOOTPRINT_DIR" ),
549  wxT( "KICAD6_TEMPLATE_DIR" ), // Stores the default library table to be copied
550 
551  // Deprecated keys
552  wxT( "KICAD_PTEMPLATES" ),
553  wxT( "KISYS3DMOD" ),
554  wxT( "KISYSMOD" ),
555  wxT( "KICAD_SYMBOL_DIR" ),
556  };
557 
558  for( const wxString& key : libKeys )
559  common.m_Env.vars.erase( key );
560 
561  common.SaveToFile( commonPath );
562  }
563 
564  return true;
565 }
566 
567 
568 bool SETTINGS_MANAGER::GetPreviousVersionPaths( std::vector<wxString>* aPaths )
569 {
570  wxASSERT( aPaths );
571 
572  aPaths->clear();
573 
574  wxDir dir;
575  std::vector<wxFileName> base_paths;
576 
577  base_paths.emplace_back( wxFileName( calculateUserSettingsPath( false ), "" ) );
578 
579  // If the env override is set, also check the default paths
580  if( wxGetEnv( wxT( "KICAD_CONFIG_HOME" ), nullptr ) )
581  base_paths.emplace_back( wxFileName( calculateUserSettingsPath( false, false ), "" ) );
582 
583 #ifdef __WXGTK__
584  // When running inside FlatPak, KIPLATFORM::ENV::GetUserConfigPath() will return a sandboxed
585  // path. In case the user wants to move from non-FlatPak KiCad to FlatPak KiCad, let's add our
586  // best guess as to the non-FlatPak config path. Unfortunately FlatPak also hides the host
587  // XDG_CONFIG_HOME, so if the user customizes their config path, they will have to browse
588  // for it.
589  {
590  wxFileName wxGtkPath;
591  wxGtkPath.AssignDir( "~/.config/kicad" );
592  wxGtkPath.MakeAbsolute();
593  base_paths.emplace_back( wxGtkPath.GetPath() );
594 
595  // We also want to pick up regular flatpak if we are nightly
596  wxGtkPath.AssignDir( "~/.var/app/org.kicad.KiCad/config/kicad" );
597  wxGtkPath.MakeAbsolute();
598  base_paths.emplace_back( wxGtkPath.GetPath() );
599  }
600 #endif
601 
602  wxString subdir;
603  std::string mine = GetSettingsVersion();
604 
605  auto check_dir = [&] ( const wxString& aSubDir )
606  {
607  // Only older versions are valid for migration
608  if( compareVersions( aSubDir.ToStdString(), mine ) <= 0 )
609  {
610  wxString sub_path = dir.GetNameWithSep() + aSubDir;
611 
612  if( IsSettingsPathValid( sub_path ) )
613  {
614  aPaths->push_back( sub_path );
615  wxLogTrace( traceSettings, wxT( "GetPreviousVersionName: %s is valid" ), sub_path );
616  }
617  }
618  };
619 
620  std::set<wxString> checkedPaths;
621 
622  for( auto base_path : base_paths )
623  {
624  if( checkedPaths.count( base_path.GetFullPath() ) )
625  continue;
626 
627  checkedPaths.insert( base_path.GetFullPath() );
628 
629  if( !dir.Open( base_path.GetFullPath() ) )
630  {
631  wxLogTrace( traceSettings, wxT( "GetPreviousVersionName: could not open base path %s" ),
632  base_path.GetFullPath() );
633  continue;
634  }
635 
636  wxLogTrace( traceSettings, wxT( "GetPreviousVersionName: checking base path %s" ),
637  base_path.GetFullPath() );
638 
639  if( dir.GetFirst( &subdir, wxEmptyString, wxDIR_DIRS ) )
640  {
641  if( subdir != mine )
642  check_dir( subdir );
643 
644  while( dir.GetNext( &subdir ) )
645  {
646  if( subdir != mine )
647  check_dir( subdir );
648  }
649  }
650 
651  // If we didn't find one yet, check for legacy settings without a version directory
652  if( IsSettingsPathValid( dir.GetNameWithSep() ) )
653  {
654  wxLogTrace( traceSettings,
655  wxT( "GetPreviousVersionName: root path %s is valid" ), dir.GetName() );
656  aPaths->push_back( dir.GetName() );
657  }
658  }
659 
660  return aPaths->size() > 0;
661 }
662 
663 
664 bool SETTINGS_MANAGER::IsSettingsPathValid( const wxString& aPath )
665 {
666  wxFileName test( aPath, "kicad_common" );
667 
668  if( test.Exists() )
669  return true;
670 
671  test.SetExt( "json" );
672 
673  return test.Exists();
674 }
675 
676 
678 {
679  wxFileName path;
680 
681  path.AssignDir( GetUserSettingsPath() );
682  path.AppendDir( "colors" );
683 
684  if( !path.DirExists() )
685  {
686  if( !wxMkdir( path.GetPath() ) )
687  {
688  wxLogTrace( traceSettings,
689  wxT( "GetColorSettingsPath(): Path %s missing and could not be created!" ),
690  path.GetPath() );
691  }
692  }
693 
694  return path.GetPath();
695 }
696 
697 
699 {
700  static wxString user_settings_path;
701 
702  if( user_settings_path.empty() )
703  user_settings_path = calculateUserSettingsPath();
704 
705  return user_settings_path;
706 }
707 
708 
709 wxString SETTINGS_MANAGER::calculateUserSettingsPath( bool aIncludeVer, bool aUseEnv )
710 {
711  wxFileName cfgpath;
712 
713  // http://docs.wxwidgets.org/3.0/classwx_standard_paths.html#a7c7cf595d94d29147360d031647476b0
714 
715  wxString envstr;
716  if( aUseEnv && wxGetEnv( wxT( "KICAD_CONFIG_HOME" ), &envstr ) && !envstr.IsEmpty() )
717  {
718  // Override the assignment above with KICAD_CONFIG_HOME
719  cfgpath.AssignDir( envstr );
720  }
721  else
722  {
723  cfgpath.AssignDir( KIPLATFORM::ENV::GetUserConfigPath() );
724 
725  cfgpath.AppendDir( TO_STR( KICAD_CONFIG_DIR ) );
726  }
727 
728  if( aIncludeVer )
729  cfgpath.AppendDir( GetSettingsVersion() );
730 
731  return cfgpath.GetPath();
732 }
733 
734 
736 {
737  // CMake computes the major.minor string for us.
738  return GetMajorMinorVersion().ToStdString();
739 }
740 
741 
742 int SETTINGS_MANAGER::compareVersions( const std::string& aFirst, const std::string& aSecond )
743 {
744  int a_maj = 0;
745  int a_min = 0;
746  int b_maj = 0;
747  int b_min = 0;
748 
749  if( !extractVersion( aFirst, &a_maj, &a_min ) || !extractVersion( aSecond, &b_maj, &b_min ) )
750  {
751  wxLogTrace( traceSettings, wxT( "compareSettingsVersions: bad input (%s, %s)" ), aFirst, aSecond );
752  return -1;
753  }
754 
755  if( a_maj < b_maj )
756  {
757  return -1;
758  }
759  else if( a_maj > b_maj )
760  {
761  return 1;
762  }
763  else
764  {
765  if( a_min < b_min )
766  {
767  return -1;
768  }
769  else if( a_min > b_min )
770  {
771  return 1;
772  }
773  else
774  {
775  return 0;
776  }
777  }
778 }
779 
780 
781 bool SETTINGS_MANAGER::extractVersion( const std::string& aVersionString, int* aMajor, int* aMinor )
782 {
783  std::regex re_version( "(\\d+)\\.(\\d+)" );
784  std::smatch match;
785 
786  if( std::regex_match( aVersionString, match, re_version ) )
787  {
788  try
789  {
790  *aMajor = std::stoi( match[1].str() );
791  *aMinor = std::stoi( match[2].str() );
792  }
793  catch( ... )
794  {
795  return false;
796  }
797 
798  return true;
799  }
800 
801  return false;
802 }
803 
804 
805 bool SETTINGS_MANAGER::LoadProject( const wxString& aFullPath, bool aSetActive )
806 {
807  // Normalize path to new format even if migrating from a legacy file
808  wxFileName path( aFullPath );
809 
810  if( path.GetExt() == LegacyProjectFileExtension )
811  path.SetExt( ProjectFileExtension );
812 
813  wxString fullPath = path.GetFullPath();
814 
815  // If already loaded, we are all set. This might be called more than once over a project's
816  // lifetime in case the project is first loaded by the KiCad manager and then eeschema or
817  // pcbnew try to load it again when they are launched.
818  if( m_projects.count( fullPath ) )
819  return true;
820 
821  bool readOnly = false;
822  std::unique_ptr<wxSingleInstanceChecker> lockFile = ::LockFile( fullPath );
823 
824  if( !lockFile )
825  {
826  wxLogTrace( traceSettings, wxT( "Project %s is locked; opening read-only" ), fullPath );
827  readOnly = true;
828  }
829 
830  // No MDI yet
831  if( aSetActive && !m_projects.empty() )
832  {
833  PROJECT* oldProject = m_projects.begin()->second;
834  unloadProjectFile( oldProject, false );
835  m_projects.erase( m_projects.begin() );
836 
837  auto it = std::find_if( m_projects_list.begin(), m_projects_list.end(),
838  [&]( const std::unique_ptr<PROJECT>& ptr )
839  {
840  return ptr.get() == oldProject;
841  } );
842 
843  wxASSERT( it != m_projects_list.end() );
844  m_projects_list.erase( it );
845  }
846 
847  wxLogTrace( traceSettings, wxT( "Load project %s" ), fullPath );
848 
849  std::unique_ptr<PROJECT> project = std::make_unique<PROJECT>();
850  project->setProjectFullName( fullPath );
851 
852  bool success = loadProjectFile( *project );
853 
854  if( success )
855  {
856  project->SetReadOnly( readOnly || project->GetProjectFile().IsReadOnly() );
857 
858  if( lockFile )
859  m_project_lock.reset( lockFile.release() );
860  }
861 
862  m_projects_list.push_back( std::move( project ) );
863  m_projects[fullPath] = m_projects_list.back().get();
864 
865  wxString fn( path.GetName() );
866 
867  PROJECT_LOCAL_SETTINGS* settings = new PROJECT_LOCAL_SETTINGS( m_projects[fullPath], fn );
868 
869  if( aSetActive )
870  settings = RegisterSettings( settings );
871  else
872  settings->LoadFromFile( path.GetPath() );
873 
874  m_projects[fullPath]->setLocalSettings( settings );
875 
876  if( aSetActive && m_kiway )
878 
879  return success;
880 }
881 
882 
883 bool SETTINGS_MANAGER::UnloadProject( PROJECT* aProject, bool aSave )
884 {
885  if( !aProject || !m_projects.count( aProject->GetProjectFullName() ) )
886  return false;
887 
888  if( !unloadProjectFile( aProject, aSave ) )
889  return false;
890 
891  wxString projectPath = aProject->GetProjectFullName();
892  wxLogTrace( traceSettings, wxT( "Unload project %s" ), projectPath );
893 
894  PROJECT* toRemove = m_projects.at( projectPath );
895  auto it = std::find_if( m_projects_list.begin(), m_projects_list.end(),
896  [&]( const std::unique_ptr<PROJECT>& ptr )
897  {
898  return ptr.get() == toRemove;
899  } );
900 
901  wxASSERT( it != m_projects_list.end() );
902  m_projects_list.erase( it );
903 
904  m_projects.erase( projectPath );
905 
906  // Immediately reload a null project; this is required until the rest of the application
907  // is refactored to not assume that Prj() always works
908  if( m_projects.empty() )
909  LoadProject( "" );
910 
911  // Remove the reference in the environment to the previous project
912  wxSetEnv( PROJECT_VAR_NAME, "" );
913 
914  // Release lock on the file, in case we had one
915  m_project_lock = nullptr;
916 
917  if( m_kiway )
919 
920  return true;
921 }
922 
923 
925 {
926  // No MDI yet: First project in the list is the active project
927  wxASSERT_MSG( m_projects_list.size(), wxT( "no project in list" ) );
928  return *m_projects_list.begin()->get();
929 }
930 
931 
933 {
934  return !m_projects.empty();
935 }
936 
937 
938 PROJECT* SETTINGS_MANAGER::GetProject( const wxString& aFullPath ) const
939 {
940  if( m_projects.count( aFullPath ) )
941  return m_projects.at( aFullPath );
942 
943  return nullptr;
944 }
945 
946 
947 std::vector<wxString> SETTINGS_MANAGER::GetOpenProjects() const
948 {
949  std::vector<wxString> ret;
950 
951  for( const std::pair<const wxString, PROJECT*>& pair : m_projects )
952  ret.emplace_back( pair.first );
953 
954  return ret;
955 }
956 
957 
958 bool SETTINGS_MANAGER::SaveProject( const wxString& aFullPath, PROJECT* aProject )
959 {
960  if( !aProject )
961  aProject = &Prj();
962 
963  wxString path = aFullPath;
964 
965  if( path.empty() )
966  path = aProject->GetProjectFullName();
967 
968  // TODO: refactor for MDI
969  if( aProject->IsReadOnly() )
970  return false;
971 
972  if( !m_project_files.count( path ) )
973  return false;
974 
976  wxString projectPath = aProject->GetProjectPath();
977 
978  project->SaveToFile( projectPath );
979  aProject->GetLocalSettings().SaveToFile( projectPath );
980 
981  return true;
982 }
983 
984 
985 void SETTINGS_MANAGER::SaveProjectAs( const wxString& aFullPath, PROJECT* aProject )
986 {
987  if( !aProject )
988  aProject = &Prj();
989 
990  wxString oldName = aProject->GetProjectFullName();
991 
992  if( aFullPath.IsSameAs( oldName ) )
993  {
994  SaveProject( aFullPath, aProject );
995  return;
996  }
997 
998  // Changing this will cause UnloadProject to not save over the "old" project when loading below
999  aProject->setProjectFullName( aFullPath );
1000 
1001  wxFileName fn( aFullPath );
1002 
1003  PROJECT_FILE* project = m_project_files.at( oldName );
1004 
1005  // Ensure read-only flags are copied; this allows doing a "Save As" on a standalong board/sch
1006  // without creating project files if the checkbox is turned off
1007  project->SetReadOnly( aProject->IsReadOnly() );
1008  aProject->GetLocalSettings().SetReadOnly( aProject->IsReadOnly() );
1009 
1010  project->SetFilename( fn.GetName() );
1011  project->SaveToFile( fn.GetPath() );
1012 
1013  aProject->GetLocalSettings().SetFilename( fn.GetName() );
1014  aProject->GetLocalSettings().SaveToFile( fn.GetPath() );
1015 
1016  m_project_files[fn.GetFullPath()] = project;
1017  m_project_files.erase( oldName );
1018 
1019  m_projects[fn.GetFullPath()] = m_projects[oldName];
1020  m_projects.erase( oldName );
1021 }
1022 
1023 
1024 void SETTINGS_MANAGER::SaveProjectCopy( const wxString& aFullPath, PROJECT* aProject )
1025 {
1026  if( !aProject )
1027  aProject = &Prj();
1028 
1030  wxString oldName = project->GetFilename();
1031  wxFileName fn( aFullPath );
1032 
1033  bool readOnly = project->IsReadOnly();
1034  project->SetReadOnly( false );
1035 
1036  project->SetFilename( fn.GetName() );
1037  project->SaveToFile( fn.GetPath() );
1038  project->SetFilename( oldName );
1039 
1040  PROJECT_LOCAL_SETTINGS& localSettings = aProject->GetLocalSettings();
1041 
1042  localSettings.SetFilename( fn.GetName() );
1043  localSettings.SaveToFile( fn.GetPath() );
1044  localSettings.SetFilename( oldName );
1045 
1046  project->SetReadOnly( readOnly );
1047 }
1048 
1049 
1051 {
1052  wxFileName fullFn( aProject.GetProjectFullName() );
1053  wxString fn( fullFn.GetName() );
1054 
1055  PROJECT_FILE* file = RegisterSettings( new PROJECT_FILE( fn ), false );
1056 
1057  m_project_files[aProject.GetProjectFullName()] = file;
1058 
1059  aProject.setProjectFile( file );
1060  file->SetProject( &aProject );
1061 
1062  wxString path( fullFn.GetPath() );
1063 
1064  return file->LoadFromFile( path );
1065 }
1066 
1067 
1068 bool SETTINGS_MANAGER::unloadProjectFile( PROJECT* aProject, bool aSave )
1069 {
1070  if( !aProject )
1071  return false;
1072 
1073  wxString name = aProject->GetProjectFullName();
1074 
1075  if( !m_project_files.count( name ) )
1076  return false;
1077 
1079 
1080  auto it = std::find_if( m_settings.begin(), m_settings.end(),
1081  [&file]( const std::unique_ptr<JSON_SETTINGS>& aPtr )
1082  {
1083  return aPtr.get() == file;
1084  } );
1085 
1086  if( it != m_settings.end() )
1087  {
1088  wxString projectPath = GetPathForSettingsFile( it->get() );
1089 
1090  FlushAndRelease( &aProject->GetLocalSettings(), aSave );
1091 
1092  if( aSave )
1093  ( *it )->SaveToFile( projectPath );
1094 
1095  m_settings.erase( it );
1096  }
1097 
1098  m_project_files.erase( name );
1099 
1100  return true;
1101 }
1102 
1103 
1105 {
1107 }
1108 
1109 
1110 wxString SETTINGS_MANAGER::backupDateTimeFormat = wxT( "%Y-%m-%d_%H%M%S" );
1111 
1112 
1114 {
1115  wxDateTime timestamp = wxDateTime::Now();
1116 
1117  wxString fileName = wxString::Format( wxT( "%s-%s" ), Prj().GetProjectName(),
1118  timestamp.Format( backupDateTimeFormat ) );
1119 
1120  wxFileName target;
1121  target.SetPath( GetProjectBackupsPath() );
1122  target.SetName( fileName );
1123  target.SetExt( ArchiveFileExtension );
1124 
1125  wxDir dir( target.GetPath() );
1126 
1127  if( !target.DirExists() && !wxMkdir( target.GetPath() ) )
1128  {
1129  wxLogTrace( traceSettings, wxT( "Could not create project backup path %s" ), target.GetPath() );
1130  return false;
1131  }
1132 
1133  if( !target.IsDirWritable() )
1134  {
1135  wxLogTrace( traceSettings, wxT( "Backup directory %s is not writable" ), target.GetPath() );
1136  return false;
1137  }
1138 
1139  wxLogTrace( traceSettings, wxT( "Backing up project to %s" ), target.GetPath() );
1140 
1141  PROJECT_ARCHIVER archiver;
1142 
1143  return archiver.Archive( Prj().GetProjectPath(), target.GetFullPath(), aReporter );
1144 }
1145 
1146 
1147 class VECTOR_INSERT_TRAVERSER : public wxDirTraverser
1148 {
1149 public:
1150  VECTOR_INSERT_TRAVERSER( std::vector<wxString>& aVec,
1151  std::function<bool( const wxString& )> aCond ) :
1152  m_files( aVec ),
1153  m_condition( aCond )
1154  {
1155  }
1156 
1157  wxDirTraverseResult OnFile( const wxString& aFile ) override
1158  {
1159  if( m_condition( aFile ) )
1160  m_files.emplace_back( aFile );
1161 
1162  return wxDIR_CONTINUE;
1163  }
1164 
1165  wxDirTraverseResult OnDir( const wxString& aDirName ) override
1166  {
1167  return wxDIR_CONTINUE;
1168  }
1169 
1170 private:
1171  std::vector<wxString>& m_files;
1172 
1173  std::function<bool( const wxString& )> m_condition;
1174 };
1175 
1176 
1178 {
1180 
1181  if( !settings.enabled )
1182  return true;
1183 
1184  wxString prefix = Prj().GetProjectName() + '-';
1185 
1186  auto modTime =
1187  [&prefix]( const wxString& aFile )
1188  {
1189  wxDateTime dt;
1190  wxString fn( wxFileName( aFile ).GetName() );
1191  fn.Replace( prefix, "" );
1192  dt.ParseFormat( fn, backupDateTimeFormat );
1193  return dt;
1194  };
1195 
1196  wxFileName projectPath( Prj().GetProjectPath() );
1197 
1198  // Skip backup if project path isn't valid or writable
1199  if( !projectPath.IsOk() || !projectPath.Exists() || !projectPath.IsDirWritable() )
1200  return true;
1201 
1202  wxString backupPath = GetProjectBackupsPath();
1203 
1204  if( !wxDirExists( backupPath ) )
1205  {
1206  wxLogTrace( traceSettings, wxT( "Backup path %s doesn't exist, creating it" ), backupPath );
1207 
1208  if( !wxMkdir( backupPath ) )
1209  {
1210  wxLogTrace( traceSettings, wxT( "Could not create backups path! Skipping backup" ) );
1211  return false;
1212  }
1213  }
1214 
1215  wxDir dir( backupPath );
1216 
1217  if( !dir.IsOpened() )
1218  {
1219  wxLogTrace( traceSettings, wxT( "Could not open project backups path %s" ), dir.GetName() );
1220  return false;
1221  }
1222 
1223  std::vector<wxString> files;
1224 
1225  VECTOR_INSERT_TRAVERSER traverser( files,
1226  [&modTime]( const wxString& aFile )
1227  {
1228  return modTime( aFile ).IsValid();
1229  } );
1230 
1231  dir.Traverse( traverser, wxT( "*.zip" ) );
1232 
1233  // Sort newest-first
1234  std::sort( files.begin(), files.end(),
1235  [&]( const wxString& aFirst, const wxString& aSecond ) -> bool
1236  {
1237  wxDateTime first = modTime( aFirst );
1238  wxDateTime second = modTime( aSecond );
1239 
1240  return first.GetTicks() > second.GetTicks();
1241  } );
1242 
1243  // Do we even need to back up?
1244  if( !files.empty() )
1245  {
1246  wxDateTime lastTime = modTime( files[0] );
1247 
1248  if( lastTime.IsValid() )
1249  {
1250  wxTimeSpan delta = wxDateTime::Now() - modTime( files[0] );
1251 
1252  if( delta.IsShorterThan( wxTimeSpan::Seconds( settings.min_interval ) ) )
1253  return true;
1254  }
1255  }
1256 
1257  // Now that we know a backup is needed, apply the retention policy
1258 
1259  // Step 1: if we're over the total file limit, remove the oldest
1260  if( !files.empty() && settings.limit_total_files > 0 )
1261  {
1262  while( files.size() > static_cast<size_t>( settings.limit_total_files ) )
1263  {
1264  wxRemoveFile( files.back() );
1265  files.pop_back();
1266  }
1267  }
1268 
1269  // Step 2: Stay under the total size limit
1270  if( settings.limit_total_size > 0 )
1271  {
1272  wxULongLong totalSize = 0;
1273 
1274  for( const wxString& file : files )
1275  totalSize += wxFileName::GetSize( file );
1276 
1277  while( !files.empty() && totalSize > static_cast<wxULongLong>( settings.limit_total_size ) )
1278  {
1279  totalSize -= wxFileName::GetSize( files.back() );
1280  wxRemoveFile( files.back() );
1281  files.pop_back();
1282  }
1283  }
1284 
1285  // Step 3: Stay under the daily limit
1286  if( settings.limit_daily_files > 0 && files.size() > 1 )
1287  {
1288  wxDateTime day = modTime( files[0] );
1289  int num = 1;
1290 
1291  wxASSERT( day.IsValid() );
1292 
1293  std::vector<wxString> filesToDelete;
1294 
1295  for( size_t i = 1; i < files.size(); i++ )
1296  {
1297  wxDateTime dt = modTime( files[i] );
1298 
1299  if( dt.IsSameDate( day ) )
1300  {
1301  num++;
1302 
1303  if( num > settings.limit_daily_files )
1304  filesToDelete.emplace_back( files[i] );
1305  }
1306  else
1307  {
1308  day = dt;
1309  num = 1;
1310  }
1311  }
1312 
1313  for( const wxString& file : filesToDelete )
1314  wxRemoveFile( file );
1315  }
1316 
1317  return BackupProject( aReporter );
1318 }
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 IsReadOnly() const
Definition: project.h:124
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.
bool SaveProject(const wxString &aFullPath=wxEmptyString, PROJECT *aProject=nullptr)
Saves a loaded project.
This file is part of the common library.
std::function< void(const wxFileName &)> m_action
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)
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:587
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:361
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
void SaveProjectCopy(const wxString &aFullPath, PROJECT *aProject=nullptr)
Saves a copy of the current project under the given path.
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/)
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.
void SaveProjectAs(const wxString &aFullPath, PROJECT *aProject=nullptr)
Sets the currently loaded project path and saves it (pointers remain valid) Note that this will not m...
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.