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