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