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