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>
32#include <gestfich.h>
34#include <kiplatform/io.h>
35#include <kiway.h>
36#include <lockfile.h>
37#include <macros.h>
38#include <pgm_base.h>
39#include <paths.h>
40
41#include <algorithm>
42#include <project.h>
52#include <env_vars.h>
54
55
57 m_kiway( nullptr ),
58 m_common_settings( nullptr ),
60{
61 wxFileName path( PATHS::GetUserSettingsPath(), wxS( "" ) );
62 wxLogTrace( traceSettings, wxT( "Using settings path %s" ), path.GetFullPath() );
63
64 if( !path.DirExists() )
65 {
66 wxLogTrace( traceSettings, wxT( "Path didn't exist; creating it" ) );
67 path.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
68 }
69
71 {
72 // This will be picked up by the first-run wizard later in application start,
73 // but we allow it for now because many things rely on being able to access the
74 // settings manager. For now, default settings in memory will be used.
75 wxLogTrace( traceSettings, wxT( "Note: no valid settings directory on disk" ) );
76 }
77
78 m_ok = true;
79
80 // create the common settings shared by all applications. Not loaded immediately
82
83 // Create the built-in color settings
84 // Here to allow the Python API to access the built-in colors
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
290std::vector<COLOR_SETTINGS*> SETTINGS_MANAGER::GetColorSettingsList()
291{
292 std::vector<COLOR_SETTINGS*> ret;
293
294 for( const std::pair<const wxString, COLOR_SETTINGS*>& entry : m_color_settings )
295 ret.push_back( entry.second );
296
297 std::sort( ret.begin(), ret.end(), []( COLOR_SETTINGS* a, COLOR_SETTINGS* b )
298 { return a->GetName() < b->GetName(); } );
299
300 return ret;
301}
302
303
305{
306 wxLogTrace( traceSettings, wxT( "Attempting to load color theme %s" ), aName );
307
308 wxFileName fn( GetColorSettingsPath(), aName, wxS( "json" ) );
309
310 if( !fn.IsOk() || !fn.Exists() )
311 {
312 wxLogTrace( traceSettings, wxT( "Theme file %s.json not found, falling back to user" ),
313 aName );
314 return nullptr;
315 }
316
317 COLOR_SETTINGS* settings = RegisterSettings( new COLOR_SETTINGS( aName ) );
318
319 if( settings->GetFilename() != aName.ToStdString() )
320 {
321 wxLogTrace( traceSettings, wxT( "Warning: stored filename is actually %s, " ),
322 settings->GetFilename() );
323 }
324
325 m_color_settings[aName] = settings;
326
327 return settings;
328}
329
330
331class JSON_DIR_TRAVERSER : public wxDirTraverser
332{
333private:
334 std::function<void( const wxFileName& )> m_action;
335
336public:
337 explicit JSON_DIR_TRAVERSER( std::function<void( const wxFileName& )> aAction )
338 : m_action( std::move( aAction ) )
339 {
340 }
341
342 wxDirTraverseResult OnFile( const wxString& aFilePath ) override
343 {
344 wxFileName file( aFilePath );
345
346 if( file.GetExt() == wxS( "json" ) )
347 m_action( file );
348
349 return wxDIR_CONTINUE;
350 }
351
352 wxDirTraverseResult OnDir( const wxString& dirPath ) override
353 {
354 return wxDIR_CONTINUE;
355 }
356};
357
358
359COLOR_SETTINGS* SETTINGS_MANAGER::registerColorSettings( const wxString& aName, bool aAbsolutePath )
360{
361 if( !m_color_settings.count( aName ) )
362 {
363 COLOR_SETTINGS* colorSettings = RegisterSettings( new COLOR_SETTINGS( aName,
364 aAbsolutePath ) );
365 m_color_settings[aName] = colorSettings;
366 }
367
368 return m_color_settings.at( aName );
369}
370
371
373{
374 if( aName.EndsWith( wxT( ".json" ) ) )
375 return registerColorSettings( aName.BeforeLast( '.' ) );
376 else
377 return registerColorSettings( aName );
378}
379
380
382{
383 if( !m_color_settings.count( "user" ) )
384 {
385 COLOR_SETTINGS* settings = registerColorSettings( wxT( "user" ) );
386 settings->SetName( wxT( "User" ) );
387 Save( settings );
388 }
389
390 return m_color_settings.at( "user" );
391}
392
393
395{
397 m_color_settings[settings->GetFilename()] = RegisterSettings( settings, false );
398}
399
400
402{
403 // Create the built-in color settings
405
406 wxFileName third_party_path;
407 const ENV_VAR_MAP& env = Pgm().GetLocalEnvVariables();
408 auto it = env.find( ENV_VAR::GetVersionedEnvVarName( wxS( "3RD_PARTY" ) ) );
409
410 if( it != env.end() && !it->second.GetValue().IsEmpty() )
411 third_party_path.SetPath( it->second.GetValue() );
412 else
413 third_party_path.SetPath( PATHS::GetDefault3rdPartyPath() );
414
415 third_party_path.AppendDir( wxS( "colors" ) );
416
417 // PCM-managed themes
418 wxDir third_party_colors_dir( third_party_path.GetFullPath() );
419
420 // System-installed themes
421 wxDir system_colors_dir( PATHS::GetStockDataPath( false ) + "/colors" );
422
423 // User-created themes
424 wxDir colors_dir( GetColorSettingsPath() );
425
426 // Search for and load any other settings
427 JSON_DIR_TRAVERSER loader( [&]( const wxFileName& aFilename )
428 {
429 registerColorSettings( aFilename.GetName() );
430 } );
431
432 JSON_DIR_TRAVERSER readOnlyLoader(
433 [&]( const wxFileName& aFilename )
434 {
435 COLOR_SETTINGS* settings = registerColorSettings( aFilename.GetFullPath(), true );
436 settings->SetReadOnly( true );
437 } );
438
439 if( system_colors_dir.IsOpened() )
440 system_colors_dir.Traverse( readOnlyLoader );
441
442 if( third_party_colors_dir.IsOpened() )
443 third_party_colors_dir.Traverse( readOnlyLoader );
444
445 if( colors_dir.IsOpened() )
446 colors_dir.Traverse( loader );
447}
448
449
455
456
457void SETTINGS_MANAGER::SaveColorSettings( COLOR_SETTINGS* aSettings, const std::string& aNamespace )
458{
459 // The passed settings should already be managed
460 wxASSERT( std::find_if( m_color_settings.begin(), m_color_settings.end(),
461 [aSettings] ( const std::pair<wxString, COLOR_SETTINGS*>& el )
462 {
463 return el.second->GetFilename() == aSettings->GetFilename();
464 }
465 ) != m_color_settings.end() );
466
467 if( aSettings->IsReadOnly() )
468 return;
469
470 if( !aSettings->Store() )
471 {
472 wxLogTrace( traceSettings, wxT( "Color scheme %s not modified; skipping save" ),
473 aNamespace );
474 return;
475 }
476
477 wxASSERT( aSettings->Contains( aNamespace ) );
478
479 wxLogTrace( traceSettings, wxT( "Saving color scheme %s, preserving %s" ),
480 aSettings->GetFilename(),
481 aNamespace );
482
483 std::optional<nlohmann::json> backup = aSettings->GetJson( aNamespace );
484 wxString path = GetColorSettingsPath();
485
486 aSettings->LoadFromFile( path );
487
488 if( backup )
489 ( *aSettings->Internals() )[aNamespace].update( *backup );
490
491 aSettings->Load();
492
493 aSettings->SaveToFile( path, true );
494}
495
496
498{
499 wxASSERT( aSettings );
500
501 switch( aSettings->GetLocation() )
502 {
505
507 // TODO: MDI support
508 return Prj().GetProjectPath();
509
511 return GetColorSettingsPath();
512
514 return GetToolbarSettingsPath();
515
517 return "";
518
519 default:
520 wxASSERT_MSG( false, wxT( "Unknown settings location!" ) );
521 }
522
523 return "";
524}
525
526
527class MIGRATION_TRAVERSER : public wxDirTraverser
528{
529private:
530 wxString m_src;
531 wxString m_dest;
532 wxString m_errors;
534
535public:
536 MIGRATION_TRAVERSER( const wxString& aSrcDir, const wxString& aDestDir, bool aMigrateTables ) :
537 m_src( aSrcDir ),
538 m_dest( aDestDir ),
539 m_migrateTables( aMigrateTables )
540 {
541 }
542
543 wxString GetErrors() { return m_errors; }
544
545 wxDirTraverseResult OnFile( const wxString& aSrcFilePath ) override
546 {
547 wxFileName file( aSrcFilePath );
548
549 if( !m_migrateTables && ( file.GetName() == FILEEXT::SymbolLibraryTableFileName ||
550 file.GetName() == FILEEXT::FootprintLibraryTableFileName ) )
551 {
552 return wxDIR_CONTINUE;
553 }
554
555 // Skip migrating PCM installed packages as packages themselves are not moved
556 if( file.GetFullName() == wxT( "installed_packages.json" ) )
557 return wxDIR_CONTINUE;
558
559 // Don't migrate hotkeys config files; we don't have a reasonable migration handler for them
560 // and so there is no way to resolve conflicts at the moment
561 if( file.GetExt() == wxT( "hotkeys" ) )
562 return wxDIR_CONTINUE;
563
564 wxString path = file.GetPath();
565
566 path.Replace( m_src, m_dest, false );
567 file.SetPath( path );
568
569 wxLogTrace( traceSettings, wxT( "Copying %s to %s" ), aSrcFilePath, file.GetFullPath() );
570
571 // For now, just copy everything
572 KiCopyFile( aSrcFilePath, file.GetFullPath(), m_errors );
573
574 return wxDIR_CONTINUE;
575 }
576
577 wxDirTraverseResult OnDir( const wxString& dirPath ) override
578 {
579 wxFileName dir( dirPath );
580
581 // Whitelist of directories to migrate
582 if( dir.GetName() == wxS( "colors" ) ||
583 dir.GetName() == wxS( "3d" ) )
584 {
585
586 wxString path = dir.GetPath();
587
588 path.Replace( m_src, m_dest, false );
589 dir.SetPath( path );
590
591 if( !wxDirExists( dir.GetPath() ) )
592 wxMkdir( dir.GetPath() );
593
594 return wxDIR_CONTINUE;
595 }
596 else
597 {
598 return wxDIR_IGNORE;
599 }
600 }
601};
602
603
605{
606 wxFileName path( PATHS::GetUserSettingsPath(), wxS( "" ) );
607
608 if( path.DirExists() )
609 {
610 wxFileName common = path;
611 common.SetName( wxS( "kicad_common" ) );
612 common.SetExt( wxS( "json" ) );
613
614 if( common.Exists() )
615 {
616 wxLogTrace( traceSettings, wxT( "Path exists and has a kicad_common, continuing!" ) );
617 return true;
618 }
619 }
620
621 return false;
622}
623
624
625bool SETTINGS_MANAGER::MigrateFromPreviousVersion( const wxString& aSourcePath )
626{
627 wxFileName path( PATHS::GetUserSettingsPath(), wxS( "" ) );
628
629 if( aSourcePath.IsEmpty() )
630 return false;
631
632 wxLogTrace( traceSettings, wxT( "Migrating from path %s" ), aSourcePath );
633
634 // TODO(JE) library tables - move library table migration out of here probably
635 MIGRATION_TRAVERSER traverser( aSourcePath, path.GetFullPath(), m_migrateLibraryTables );
636 wxDir source_dir( aSourcePath );
637
638 source_dir.Traverse( traverser );
639
640 if( !traverser.GetErrors().empty() )
641 DisplayErrorMessage( nullptr, traverser.GetErrors() );
642
643 // Remove any library configuration if we didn't choose to import
645 {
646 COMMON_SETTINGS common;
647 wxString commonPath = GetPathForSettingsFile( &common );
648 common.LoadFromFile( commonPath );
649
650 const std::vector<wxString> libKeys = {
651 wxT( "KICAD6_SYMBOL_DIR" ),
652 wxT( "KICAD6_3DMODEL_DIR" ),
653 wxT( "KICAD6_FOOTPRINT_DIR" ),
654 wxT( "KICAD6_TEMPLATE_DIR" ), // Stores the default library table to be copied
655 wxT( "KICAD7_SYMBOL_DIR" ),
656 wxT( "KICAD7_3DMODEL_DIR" ),
657 wxT( "KICAD7_FOOTPRINT_DIR" ),
658 wxT( "KICAD7_TEMPLATE_DIR" ),
659 wxT( "KICAD8_SYMBOL_DIR" ),
660 wxT( "KICAD8_3DMODEL_DIR" ),
661 wxT( "KICAD8_FOOTPRINT_DIR" ),
662 wxT( "KICAD8_TEMPLATE_DIR" ),
663
664 // Deprecated keys
665 wxT( "KICAD_PTEMPLATES" ),
666 wxT( "KISYS3DMOD" ),
667 wxT( "KISYSMOD" ),
668 wxT( "KICAD_SYMBOL_DIR" ),
669 };
670
671 for( const wxString& key : libKeys )
672 common.m_Env.vars.erase( key );
673
674 common.SaveToFile( commonPath );
675 }
676
677 return true;
678}
679
680
681bool SETTINGS_MANAGER::GetPreviousVersionPaths( std::vector<wxString>* aPaths )
682{
683 wxASSERT( aPaths );
684
685 aPaths->clear();
686
687 wxDir dir;
688 std::vector<wxFileName> base_paths;
689
690 base_paths.emplace_back( wxFileName( PATHS::CalculateUserSettingsPath( false ), wxS( "" ) ) );
691
692 // If the env override is set, also check the default paths
693 if( wxGetEnv( wxT( "KICAD_CONFIG_HOME" ), nullptr ) )
694 base_paths.emplace_back( wxFileName( PATHS::CalculateUserSettingsPath( false, false ),
695 wxS( "" ) ) );
696
697#ifdef __WXGTK__
698 // When running inside FlatPak, KIPLATFORM::ENV::GetUserConfigPath() will return a sandboxed
699 // path. In case the user wants to move from non-FlatPak KiCad to FlatPak KiCad, let's add our
700 // best guess as to the non-FlatPak config path. Unfortunately FlatPak also hides the host
701 // XDG_CONFIG_HOME, so if the user customizes their config path, they will have to browse
702 // for it.
703 {
704 wxFileName wxGtkPath;
705 wxGtkPath.AssignDir( wxS( "~/.config/kicad" ) );
706 wxGtkPath.MakeAbsolute();
707 base_paths.emplace_back( wxGtkPath );
708
709 // We also want to pick up regular flatpak if we are nightly
710 wxGtkPath.AssignDir( wxS( "~/.var/app/org.kicad.KiCad/config/kicad" ) );
711 wxGtkPath.MakeAbsolute();
712 base_paths.emplace_back( wxGtkPath );
713 }
714#endif
715
716 wxString subdir;
717 std::string mine = GetSettingsVersion();
718
719 auto check_dir =
720 [&] ( const wxString& aSubDir )
721 {
722 // Only older versions are valid for migration
723 if( compareVersions( aSubDir.ToStdString(), mine ) <= 0 )
724 {
725 wxString sub_path = dir.GetNameWithSep() + aSubDir;
726
727 if( IsSettingsPathValid( sub_path ) )
728 {
729 aPaths->push_back( sub_path );
730 wxLogTrace( traceSettings, wxT( "GetPreviousVersionName: %s is valid" ), sub_path );
731 }
732 }
733 };
734
735 std::set<wxString> checkedPaths;
736
737 for( const wxFileName& base_path : base_paths )
738 {
739 if( checkedPaths.count( base_path.GetFullPath() ) )
740 continue;
741
742 checkedPaths.insert( base_path.GetFullPath() );
743
744 if( !dir.Open( base_path.GetFullPath() ) )
745 {
746 wxLogTrace( traceSettings, wxT( "GetPreviousVersionName: could not open base path %s" ),
747 base_path.GetFullPath() );
748 continue;
749 }
750
751 wxLogTrace( traceSettings, wxT( "GetPreviousVersionName: checking base path %s" ),
752 base_path.GetFullPath() );
753
754 if( dir.GetFirst( &subdir, wxEmptyString, wxDIR_DIRS ) )
755 {
756 if( subdir != mine )
757 check_dir( subdir );
758
759 while( dir.GetNext( &subdir ) )
760 {
761 if( subdir != mine )
762 check_dir( subdir );
763 }
764 }
765
766 // If we didn't find one yet, check for legacy settings without a version directory
767 if( IsSettingsPathValid( dir.GetNameWithSep() ) )
768 {
769 wxLogTrace( traceSettings,
770 wxT( "GetPreviousVersionName: root path %s is valid" ), dir.GetName() );
771 aPaths->push_back( dir.GetName() );
772 }
773 }
774
775 std::erase_if( *aPaths,
776 []( const wxString& aPath ) -> bool
777 {
778 wxFileName fulldir = wxFileName::DirName( aPath );
779 const wxArrayString& dirs = fulldir.GetDirs();
780
781 if( dirs.empty() || !fulldir.IsDirReadable() )
782 return true;
783
784 std::string ver = dirs.back().ToStdString();
785
786 if( !extractVersion( ver ) )
787 return true;
788
789 return false;
790 } );
791
792 std::sort( aPaths->begin(), aPaths->end(),
793 [&]( const wxString& a, const wxString& b ) -> bool
794 {
795 wxFileName aPath = wxFileName::DirName( a );
796 wxFileName bPath = wxFileName::DirName( b );
797
798 const wxArrayString& aDirs = aPath.GetDirs();
799 const wxArrayString& bDirs = bPath.GetDirs();
800
801 if( aDirs.empty() )
802 return false;
803
804 if( bDirs.empty() )
805 return true;
806
807 std::string verA = aDirs.back().ToStdString();
808 std::string verB = bDirs.back().ToStdString();
809
810 if( !extractVersion( verA ) )
811 return false;
812
813 if( !extractVersion( verB ) )
814 return true;
815
816 return compareVersions( verA, verB ) > 0;
817 } );
818
819 return aPaths->size() > 0;
820}
821
822
823bool SETTINGS_MANAGER::IsSettingsPathValid( const wxString& aPath )
824{
825 wxFileName test( aPath, wxS( "kicad_common" ) );
826
827 if( test.Exists() )
828 return true;
829
830 test.SetExt( "json" );
831
832 return test.Exists();
833}
834
835
837{
838 wxFileName path;
839
840 path.AssignDir( PATHS::GetUserSettingsPath() );
841 path.AppendDir( wxS( "colors" ) );
842
843 if( !path.DirExists() )
844 {
845 if( !wxMkdir( path.GetPath() ) )
846 {
847 wxLogTrace( traceSettings,
848 wxT( "GetColorSettingsPath(): Path %s missing and could not be created!" ),
849 path.GetPath() );
850 }
851 }
852
853 return path.GetPath();
854}
855
856
858{
859 wxFileName path;
860
861 path.AssignDir( PATHS::GetUserSettingsPath() );
862 path.AppendDir( wxS( "toolbars" ) );
863
864 if( !path.DirExists() )
865 {
866 if( !wxMkdir( path.GetPath() ) )
867 {
868 wxLogTrace( traceSettings,
869 wxT( "GetToolbarSettingsPath(): Path %s missing and could not be created!" ),
870 path.GetPath() );
871 }
872 }
873
874 return path.GetPath();
875}
876
877
879{
880 // CMake computes the major.minor string for us.
881 return GetMajorMinorVersion().ToStdString();
882}
883
884
885int SETTINGS_MANAGER::compareVersions( const std::string& aFirst, const std::string& aSecond )
886{
887 int a_maj = 0;
888 int a_min = 0;
889 int b_maj = 0;
890 int b_min = 0;
891
892 if( !extractVersion( aFirst, &a_maj, &a_min ) || !extractVersion( aSecond, &b_maj, &b_min ) )
893 {
894 wxLogTrace( traceSettings, wxT( "compareSettingsVersions: bad input (%s, %s)" ),
895 aFirst, aSecond );
896 return -1;
897 }
898
899 if( a_maj < b_maj )
900 {
901 return -1;
902 }
903 else if( a_maj > b_maj )
904 {
905 return 1;
906 }
907 else
908 {
909 if( a_min < b_min )
910 {
911 return -1;
912 }
913 else if( a_min > b_min )
914 {
915 return 1;
916 }
917 else
918 {
919 return 0;
920 }
921 }
922}
923
924
925bool SETTINGS_MANAGER::extractVersion( const std::string& aVersionString, int* aMajor, int* aMinor )
926{
927 std::regex re_version( "(\\d+)\\.(\\d+)" );
928 std::smatch match;
929
930 if( std::regex_match( aVersionString, match, re_version ) )
931 {
932 try
933 {
934 int major = std::stoi( match[1].str() );
935 int minor = std::stoi( match[2].str() );
936
937 if( aMajor )
938 *aMajor = major;
939
940 if( aMinor )
941 *aMinor = minor;
942 }
943 catch( ... )
944 {
945 return false;
946 }
947
948 return true;
949 }
950
951 return false;
952}
953
954
955bool SETTINGS_MANAGER::LoadProject( const wxString& aFullPath, bool aSetActive )
956{
957 // Normalize path to current project extension. Users may open legacy .pro files,
958 // or the OS may hand us a .kicad_sch/.kicad_pcb via file association or drag-and-drop.
959 wxFileName path( aFullPath );
960
961 if( path.HasName() && path.GetExt() != FILEEXT::ProjectFileExtension )
963
964 wxString fullPath = path.GetFullPath();
965
966 // If already loaded, we are all set. This might be called more than once over a project's
967 // lifetime in case the project is first loaded by the KiCad manager and then Eeschema or
968 // Pcbnew try to load it again when they are launched.
969 if( m_projects.count( fullPath ) )
970 return true;
971
972 LOCKFILE lockFile( fullPath );
973
974 if( !lockFile.Valid() )
975 {
976 wxLogTrace( traceSettings, wxT( "Project %s is locked; opening read-only" ), fullPath );
977 }
978
979 // No MDI yet
980 if( aSetActive && !m_projects.empty() )
981 {
982 // Cancel any in-progress library preloads and wait for them to finish before
983 // modifying m_projects_list. Background preload threads access Prj() which becomes
984 // invalid when the project list is modified.
985 if( m_kiway )
986 {
987 if( KIFACE* pcbFace = m_kiway->KiFACE( KIWAY::FACE_PCB, false ) )
988 pcbFace->CancelPreload( true );
989 }
990
991 // Abort any async library loads before modifying m_projects_list to prevent race
992 // conditions where background threads try to access Prj() while the list is empty.
993 if( PgmOrNull() )
995
996 PROJECT* oldProject = m_projects.begin()->second;
997 unloadProjectFile( oldProject, false );
998 m_projects.erase( m_projects.begin() );
999
1000 auto it = std::find_if( m_projects_list.begin(), m_projects_list.end(),
1001 [&]( const std::unique_ptr<PROJECT>& ptr )
1002 {
1003 return ptr.get() == oldProject;
1004 } );
1005
1006 wxASSERT( it != m_projects_list.end() );
1007 m_projects_list.erase( it );
1008 }
1009
1010 wxLogTrace( traceSettings, wxT( "Load project %s" ), fullPath );
1011
1012 std::unique_ptr<PROJECT> project = std::make_unique<PROJECT>();
1013 project->setProjectFullName( fullPath );
1014
1015 if( aSetActive )
1016 {
1017 // until multiple projects are in play, set an environment variable for the
1018 // the project pointer.
1019 wxFileName projectPath( fullPath );
1020 wxSetEnv( PROJECT_VAR_NAME, projectPath.GetPath() );
1021
1022 // set the cwd but don't impact kicad-cli
1023 if( !projectPath.GetPath().IsEmpty() && wxTheApp && wxTheApp->IsGUI() )
1024 wxSetWorkingDirectory( projectPath.GetPath() );
1025
1026 // Anchor text_eval VCS lookups to the project directory. The GUI relies on cwd,
1027 // which is deliberately left untouched for kicad-cli; an explicit context is
1028 // required so repo-scoped queries resolve correctly from either entry point.
1029 // Force an absolute path because libgit2 resolves relative paths against the
1030 // process cwd, which is what we are working around. Clear the context for an
1031 // empty/null project load so VCS queries fall back to cwd rather than locking
1032 // onto whatever directory happens to be current.
1033 if( projectPath.GetPath().IsEmpty() )
1034 {
1035 TEXT_EVAL_VCS::SetContextPath( wxString() );
1036 }
1037 else
1038 {
1039 wxFileName vcsContext( projectPath );
1040 vcsContext.MakeAbsolute();
1041 TEXT_EVAL_VCS::SetContextPath( vcsContext.GetPath() );
1042 }
1043 }
1044
1045 bool success = loadProjectFile( *project );
1046
1047 if( success )
1048 {
1049 project->SetReadOnly( !lockFile.Valid() || project->GetProjectFile().IsReadOnly() );
1050
1051 if( lockFile && aSetActive )
1052 project->SetProjectLock( new LOCKFILE( std::move( lockFile ) ) );
1053 }
1054
1055 m_projects_list.push_back( std::move( project ) );
1056 m_projects[fullPath] = m_projects_list.back().get();
1057
1058 wxString fn( path.GetName() );
1059
1060 PROJECT_LOCAL_SETTINGS* settings = new PROJECT_LOCAL_SETTINGS( m_projects[fullPath], fn );
1061
1062 if( aSetActive )
1063 settings = RegisterSettings( settings );
1064 else
1065 settings->LoadFromFile( path.GetPath() );
1066
1067 m_projects[fullPath]->setLocalSettings( settings );
1068
1069 // If not running from SWIG; notify the library manager of the new project
1070 // TODO(JE) this maybe could be handled through kiway (below) in the future
1071 if( aSetActive && PgmOrNull() )
1073
1074 if( aSetActive && m_kiway )
1075 m_kiway->ProjectChanged();
1076
1077 return success;
1078}
1079
1080
1081bool SETTINGS_MANAGER::UnloadProject( PROJECT* aProject, bool aSave )
1082{
1083 if( !aProject || !m_projects.count( aProject->GetProjectFullName() ) )
1084 return false;
1085
1086 wxString projectPath = aProject->GetProjectFullName();
1087 wxLogTrace( traceSettings, wxT( "Unload project %s" ), projectPath );
1088
1089 PROJECT* toRemove = m_projects.at( projectPath );
1090 bool wasActiveProject = m_projects_list.begin()->get() == toRemove;
1091
1092 // Cancel any in-progress library preloads and wait for them to finish before
1093 // modifying m_projects_list. Background preload threads access Prj() which becomes
1094 // invalid when the project list is modified.
1095 if( wasActiveProject && m_kiway )
1096 {
1097 if( KIFACE* pcbFace = m_kiway->KiFACE( KIWAY::FACE_PCB, false ) )
1098 pcbFace->CancelPreload( true );
1099 }
1100
1101 // Abort any async library loads before modifying m_projects_list to prevent race
1102 // conditions where background threads try to access Prj() while the list is empty.
1103 if( wasActiveProject && PgmOrNull() )
1105
1106 if( !unloadProjectFile( aProject, aSave ) )
1107 return false;
1108
1109 auto it = std::find_if( m_projects_list.begin(), m_projects_list.end(),
1110 [&]( const std::unique_ptr<PROJECT>& ptr )
1111 {
1112 return ptr.get() == toRemove;
1113 } );
1114
1115 wxASSERT( it != m_projects_list.end() );
1116 m_projects_list.erase( it );
1117
1118 m_projects.erase( projectPath );
1119
1120 if( wasActiveProject )
1121 {
1122 // Immediately reload a null project; this is required until the rest of the application
1123 // is refactored to not assume that Prj() always works
1124 if( m_projects_list.empty() )
1125 LoadProject( "" );
1126
1127 // Remove the reference in the environment to the previous project
1128 wxSetEnv( PROJECT_VAR_NAME, wxS( "" ) );
1129
1130 // Drop the VCS context so lingering text_eval queries don't probe a stale project dir.
1131 TEXT_EVAL_VCS::SetContextPath( wxString() );
1132
1133#ifdef _WIN32
1134 // On Windows, processes hold a handle to their current working directory, preventing
1135 // it from being deleted. Reset to the user settings path to release the project
1136 // directory. This mirrors the wxSetWorkingDirectory call in LoadProject.
1137 if( wxTheApp && wxTheApp->IsGUI() )
1138 wxSetWorkingDirectory( PATHS::GetUserSettingsPath() );
1139#endif
1140
1141 if( m_kiway )
1142 m_kiway->ProjectChanged();
1143 }
1144
1145 return true;
1146}
1147
1148
1150{
1151 // No MDI yet: First project in the list is the active project
1152 if( m_projects_list.empty() )
1153 {
1154 wxLogTrace( traceSettings, wxT( "Prj() called with no project loaded" ) );
1155
1156 static PROJECT s_emptyProject;
1157 return s_emptyProject;
1158 }
1159
1160 return *m_projects_list.begin()->get();
1161}
1162
1163
1165{
1166 return !m_projects.empty();
1167}
1168
1169
1171{
1172 return m_projects.size() > 1 || ( m_projects.size() == 1
1173 && !m_projects.begin()->second->GetProjectFullName().IsEmpty() );
1174}
1175
1176
1177PROJECT* SETTINGS_MANAGER::GetProject( const wxString& aFullPath ) const
1178{
1179 if( m_projects.count( aFullPath ) )
1180 return m_projects.at( aFullPath );
1181
1182 return nullptr;
1183}
1184
1185
1186std::vector<wxString> SETTINGS_MANAGER::GetOpenProjects() const
1187{
1188 std::vector<wxString> ret;
1189
1190 for( const std::pair<const wxString, PROJECT*>& pair : m_projects )
1191 {
1192 // Don't save empty projects (these are the default project settings)
1193 if( !pair.first.IsEmpty() )
1194 ret.emplace_back( pair.first );
1195 }
1196
1197 return ret;
1198}
1199
1200
1201bool SETTINGS_MANAGER::SaveProject( const wxString& aFullPath, PROJECT* aProject )
1202{
1203 if( !aProject )
1204 aProject = &Prj();
1205
1206 wxString path = aFullPath;
1207
1208 if( path.empty() )
1209 path = aProject->GetProjectFullName();
1210
1211 // TODO: refactor for MDI
1212 if( aProject->IsReadOnly() )
1213 return false;
1214
1215 if( !m_project_files.count( path ) )
1216 return false;
1217
1219 wxString projectPath = aProject->GetProjectPath();
1220
1221 project->SaveToFile( projectPath );
1222 aProject->GetLocalSettings().SaveToFile( projectPath );
1223
1224 return true;
1225}
1226
1227
1228void SETTINGS_MANAGER::SaveProjectAs( const wxString& aFullPath, PROJECT* aProject )
1229{
1230 if( !aProject )
1231 aProject = &Prj();
1232
1233 wxString oldName = aProject->GetProjectFullName();
1234
1235 if( aFullPath.IsSameAs( oldName ) )
1236 {
1237 SaveProject( aFullPath, aProject );
1238 return;
1239 }
1240
1241 // Changing this will cause UnloadProject to not save over the "old" project when loading below
1242 aProject->setProjectFullName( aFullPath );
1243
1244 wxFileName fn( aFullPath );
1245
1246 PROJECT_FILE* project = m_project_files.at( oldName );
1247
1248 // Ensure read-only flags are copied; this allows doing a "Save As" on a standalone board/sch
1249 // without creating project files if the checkbox is turned off
1250 project->SetReadOnly( aProject->IsReadOnly() );
1251 aProject->GetLocalSettings().SetReadOnly( aProject->IsReadOnly() );
1252
1253 project->SetFilename( fn.GetName() );
1254 project->SaveToFile( fn.GetPath() );
1255
1256 aProject->GetLocalSettings().SetFilename( fn.GetName() );
1257 aProject->GetLocalSettings().SaveToFile( fn.GetPath() );
1258
1259 m_project_files[fn.GetFullPath()] = project;
1260 m_project_files.erase( oldName );
1261
1262 m_projects[fn.GetFullPath()] = m_projects[oldName];
1263 m_projects.erase( oldName );
1264}
1265
1266
1267void SETTINGS_MANAGER::SaveProjectCopy( const wxString& aFullPath, PROJECT* aProject )
1268{
1269 if( !aProject )
1270 aProject = &Prj();
1271
1273 wxString oldName = project->GetFilename();
1274 wxFileName fn( aFullPath );
1275
1276 bool readOnly = project->IsReadOnly();
1277 project->SetReadOnly( false );
1278
1279 project->SetFilename( fn.GetName() );
1280 project->SaveToFile( fn.GetPath() );
1281 project->SetFilename( oldName );
1282
1283 PROJECT_LOCAL_SETTINGS& localSettings = aProject->GetLocalSettings();
1284
1285 localSettings.SetFilename( fn.GetName() );
1286 localSettings.SaveToFile( fn.GetPath() );
1287 localSettings.SetFilename( oldName );
1288
1289 project->SetReadOnly( readOnly );
1290}
1291
1292
1294{
1295 wxFileName fullFn( aProject.GetProjectFullName() );
1296 wxString fn( fullFn.GetName() );
1297
1298 PROJECT_FILE* file = RegisterSettings( new PROJECT_FILE( fn ), false );
1299
1300 m_project_files[aProject.GetProjectFullName()] = file;
1301
1302 aProject.setProjectFile( file );
1303 file->SetProject( &aProject );
1304
1305 wxString path( fullFn.GetPath() );
1306
1307 return file->LoadFromFile( path );
1308}
1309
1310
1312{
1313 if( !aProject )
1314 return false;
1315
1316 wxString name = aProject->GetProjectFullName();
1317
1318 if( !m_project_files.count( name ) )
1319 return false;
1320
1322
1323 if( !file->ShouldAutoSave() )
1324 aSave = false;
1325
1326 auto it = std::find_if( m_settings.begin(), m_settings.end(),
1327 [&file]( const std::unique_ptr<JSON_SETTINGS>& aPtr )
1328 {
1329 return aPtr.get() == file;
1330 } );
1331
1332 if( it != m_settings.end() )
1333 {
1334 wxString projectPath = GetPathForSettingsFile( it->get() );
1335
1336 bool saveLocalSettings = aSave && aProject->GetLocalSettings().ShouldAutoSave();
1337
1338 FlushAndRelease( &aProject->GetLocalSettings(), saveLocalSettings );
1339
1340 if( aSave )
1341 ( *it )->SaveToFile( projectPath );
1342
1343 m_settings.erase( it );
1344 }
1345
1346 m_project_files.erase( name );
1347
1348 return true;
1349}
1350
1351
1356
1357
1358wxString SETTINGS_MANAGER::backupDateTimeFormat = wxT( "%Y-%m-%d_%H%M%S" );
1359
1360
1361bool SETTINGS_MANAGER::BackupProject( REPORTER& aReporter, wxFileName& aTarget ) const
1362{
1363 wxDateTime timestamp = wxDateTime::Now();
1364
1365 wxString fileName = wxString::Format( wxT( "%s-%s" ), Prj().GetProjectName(),
1366 timestamp.Format( backupDateTimeFormat ) );
1367
1368 if( !aTarget.IsOk() )
1369 {
1370 aTarget.SetPath( GetProjectBackupsPath() );
1371 aTarget.SetName( fileName );
1372 aTarget.SetExt( FILEEXT::ArchiveFileExtension );
1373 }
1374
1375 if( !aTarget.DirExists() && !wxMkdir( aTarget.GetPath() ) )
1376 {
1377 wxLogTrace( traceSettings, wxT( "Could not create project backup path %s" ),
1378 aTarget.GetPath() );
1379 return false;
1380 }
1381
1382 if( !aTarget.IsDirWritable() )
1383 {
1384 wxLogTrace( traceSettings, wxT( "Backup directory %s is not writable" ),
1385 aTarget.GetPath() );
1386 return false;
1387 }
1388
1389 wxLogTrace( traceSettings, wxT( "Backing up project to %s" ), aTarget.GetPath() );
1390
1391 return PROJECT_ARCHIVER::Archive( Prj().GetProjectPath(), aTarget.GetFullPath(), aReporter );
1392}
1393
1394
1395class VECTOR_INSERT_TRAVERSER : public wxDirTraverser
1396{
1397public:
1398 VECTOR_INSERT_TRAVERSER( std::vector<wxString>& aVec,
1399 std::function<bool( const wxString& )> aCond ) :
1400 m_files( aVec ),
1401 m_condition( std::move( aCond ) )
1402 {
1403 }
1404
1405 wxDirTraverseResult OnFile( const wxString& aFile ) override
1406 {
1407 if( m_condition( aFile ) )
1408 m_files.emplace_back( aFile );
1409
1410 return wxDIR_CONTINUE;
1411 }
1412
1413 wxDirTraverseResult OnDir( const wxString& aDirName ) override
1414 {
1415 return wxDIR_CONTINUE;
1416 }
1417
1418private:
1419 std::vector<wxString>& m_files;
1420
1421 std::function<bool( const wxString& )> m_condition;
1422};
1423
1424
1426{
1428
1429 if( !settings.enabled )
1430 return true;
1431
1432 wxString prefix = Prj().GetProjectName() + '-';
1433
1434 auto modTime =
1435 [&prefix]( const wxString& aFile )
1436 {
1437 wxDateTime dt;
1438 wxString fn( wxFileName( aFile ).GetName() );
1439 fn.Replace( prefix, wxS( "" ) );
1440 dt.ParseFormat( fn, backupDateTimeFormat );
1441 return dt;
1442 };
1443
1444 wxFileName projectPath( Prj().GetProjectPath(), wxEmptyString, wxEmptyString );
1445
1446 // Skip backup if project path isn't valid or writable
1447 if( !projectPath.IsOk() || !projectPath.Exists() || !projectPath.IsDirWritable() )
1448 return true;
1449
1450 wxString backupPath = GetProjectBackupsPath();
1451
1452 if( !wxDirExists( backupPath ) )
1453 {
1454 wxLogTrace( traceSettings, wxT( "Backup path %s doesn't exist, creating it" ), backupPath );
1455
1456 if( !wxMkdir( backupPath ) )
1457 {
1458 wxLogTrace( traceSettings, wxT( "Could not create backups path! Skipping backup" ) );
1459 return false;
1460 }
1461 }
1462
1463 wxDir dir( backupPath );
1464
1465 if( !dir.IsOpened() )
1466 {
1467 wxLogTrace( traceSettings, wxT( "Could not open project backups path %s" ), dir.GetName() );
1468 return false;
1469 }
1470
1471 std::vector<wxString> files;
1472
1473 VECTOR_INSERT_TRAVERSER traverser( files,
1474 [&modTime]( const wxString& aFile )
1475 {
1476 return modTime( aFile ).IsValid();
1477 } );
1478
1479 dir.Traverse( traverser, wxT( "*.zip" ) );
1480
1481 // Sort newest-first
1482 std::sort( files.begin(), files.end(),
1483 [&]( const wxString& aFirst, const wxString& aSecond ) -> bool
1484 {
1485 wxDateTime first = modTime( aFirst );
1486 wxDateTime second = modTime( aSecond );
1487
1488 return first.GetTicks() > second.GetTicks();
1489 } );
1490
1491 // Do we even need to back up?
1492 if( !files.empty() )
1493 {
1494 wxDateTime lastTime = modTime( files[0] );
1495
1496 if( lastTime.IsValid() )
1497 {
1498 wxTimeSpan delta = wxDateTime::Now() - modTime( files[0] );
1499
1500 if( delta.IsShorterThan( wxTimeSpan::Seconds( settings.min_interval ) ) )
1501 return true;
1502 }
1503 }
1504
1505 // Backup
1506 wxFileName target;
1507 bool backupSuccessful = BackupProject( aReporter, target );
1508
1509 if( !backupSuccessful )
1510 return false;
1511
1512 // Update the file list
1513 files.insert( files.begin(), target.GetFullPath() );
1514
1515 // Are there any changes since the last backup?
1516 if( files.size() >= 2
1517 && PROJECT_ARCHIVER::AreZipArchivesIdentical( files[0], files[1], aReporter ) )
1518 {
1519 wxRemoveFile( files[0] );
1520 return true;
1521 }
1522
1523 // Now that we know a backup is needed, apply the retention policy
1524
1525 // Step 1: if we're over the total file limit, remove the oldest
1526 if( !files.empty() && settings.limit_total_files > 0 )
1527 {
1528 while( files.size() > static_cast<size_t>( settings.limit_total_files ) )
1529 {
1530 wxRemoveFile( files.back() );
1531 files.pop_back();
1532 }
1533 }
1534
1535 // Step 2: Stay under the total size limit
1536 if( settings.limit_total_size > 0 )
1537 {
1538 wxULongLong totalSize = 0;
1539
1540 for( const wxString& file : files )
1541 totalSize += wxFileName::GetSize( file );
1542
1543 while( !files.empty() && totalSize > static_cast<wxULongLong>( settings.limit_total_size ) )
1544 {
1545 totalSize -= wxFileName::GetSize( files.back() );
1546 wxRemoveFile( files.back() );
1547 files.pop_back();
1548 }
1549 }
1550
1551 // Step 3: Stay under the daily limit
1552 if( settings.limit_daily_files > 0 && files.size() > 1 )
1553 {
1554 wxDateTime day = modTime( files[0] );
1555 int num = 1;
1556
1557 wxASSERT( day.IsValid() );
1558
1559 std::vector<wxString> filesToDelete;
1560
1561 for( size_t i = 1; i < files.size(); i++ )
1562 {
1563 wxDateTime dt = modTime( files[i] );
1564
1565 if( dt.IsSameDate( day ) )
1566 {
1567 num++;
1568
1569 if( num > settings.limit_daily_files )
1570 filesToDelete.emplace_back( files[i] );
1571 }
1572 else
1573 {
1574 day = dt;
1575 num = 1;
1576 }
1577 }
1578
1579 for( const wxString& file : filesToDelete )
1580 wxRemoveFile( file );
1581 }
1582
1583 return true;
1584}
1585
1586
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
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
@ FACE_PCB
pcbnew DSO
Definition kiway.h:323
void AbortAsyncLoads()
Abort any async library loading operations in progress.
void ProjectChanged()
Notify all adapters that the project has changed.
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:645
static wxString GetDefault3rdPartyPath()
Gets the default path for PCM packages.
Definition paths.cpp:126
static wxString GetStockDataPath(bool aRespectRunFromBuildDir=true)
Gets the stock (install) data path, which is the base path for things like scripting,...
Definition paths.cpp:233
static wxString GetUserSettingsPath()
Return the user configuration path used to store KiCad's configuration files.
Definition paths.cpp:634
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition pgm_base.cpp:787
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:132
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)
bool LoadFromFile(const wxString &aDirectory="") override
Loads the backing file from disk and then calls Load()
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:66
virtual void setProjectFile(PROJECT_FILE *aFile)
Set the backing store file for this project.
Definition project.h:342
virtual bool IsReadOnly() const
Definition project.h:162
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
Definition project.cpp:181
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:187
virtual const wxString GetProjectName() const
Return the short name of the project.
Definition project.cpp:199
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:151
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:75
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.
void SaveColorSettings(COLOR_SETTINGS *aSettings, const std::string &aNamespace="")
Safely save a COLOR_SETTINGS to disk, preserving any changes outside the given namespace.
bool MigrateFromPreviousVersion(const wxString &aSourcePath)
Handle migration of the settings from previous KiCad versions.
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)
COLOR_SETTINGS * GetColorSettings(const wxString &aName)
Retrieve a color settings object that applications can read colors from.
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.
bool SettingsDirectoryValid() const
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).
std::vector< COLOR_SETTINGS * > GetColorSettingsList()
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:221
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:294
static const std::string SymbolLibraryTableFileName
static const std::string ProjectFileExtension
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
void SetContextPath(const wxString &aPath)
Set the filesystem path used as the repository-discovery starting point for repo-scoped VCS queries (...
STL namespace.
PGM_BASE & Pgm()
The global program "get" accessor.
PGM_BASE * PgmOrNull()
Return a reference that can be nullptr when running a shared lib from a script, not from a kicad app.
see class PGM_BASE
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition project.h:41
#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.
Implement a participant in the KIWAY alchemy.
Definition kiway.h:156
std::string path
int delta
Definition of file extensions used in Kicad.