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