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