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