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