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 // Cancel any in-progress library preloads and wait for them to finish before
966 // modifying m_projects_list. Background preload threads access Prj() which becomes
967 // invalid when the project list is modified.
968 if( m_kiway )
969 {
970 if( KIFACE* pcbFace = m_kiway->KiFACE( KIWAY::FACE_PCB, false ) )
971 pcbFace->CancelPreload( true );
972 }
973
974 // Abort any async library loads before modifying m_projects_list to prevent race
975 // conditions where background threads try to access Prj() while the list is empty.
976 if( PgmOrNull() )
978
979 PROJECT* oldProject = m_projects.begin()->second;
980 unloadProjectFile( oldProject, false );
981 m_projects.erase( m_projects.begin() );
982
983 auto it = std::find_if( m_projects_list.begin(), m_projects_list.end(),
984 [&]( const std::unique_ptr<PROJECT>& ptr )
985 {
986 return ptr.get() == oldProject;
987 } );
988
989 wxASSERT( it != m_projects_list.end() );
990 m_projects_list.erase( it );
991 }
992
993 wxLogTrace( traceSettings, wxT( "Load project %s" ), fullPath );
994
995 std::unique_ptr<PROJECT> project = std::make_unique<PROJECT>();
996 project->setProjectFullName( fullPath );
997
998 if( aSetActive )
999 {
1000 // until multiple projects are in play, set an environment variable for the
1001 // the project pointer.
1002 wxFileName projectPath( fullPath );
1003 wxSetEnv( PROJECT_VAR_NAME, projectPath.GetPath() );
1004
1005 // set the cwd but don't impact kicad-cli
1006 if( !projectPath.GetPath().IsEmpty() && wxTheApp && wxTheApp->IsGUI() )
1007 wxSetWorkingDirectory( projectPath.GetPath() );
1008 }
1009
1010 bool success = loadProjectFile( *project );
1011
1012 if( success )
1013 {
1014 project->SetReadOnly( !lockFile.Valid() || project->GetProjectFile().IsReadOnly() );
1015
1016 if( lockFile && aSetActive )
1017 project->SetProjectLock( new LOCKFILE( std::move( lockFile ) ) );
1018 }
1019
1020 m_projects_list.push_back( std::move( project ) );
1021 m_projects[fullPath] = m_projects_list.back().get();
1022
1023 wxString fn( path.GetName() );
1024
1025 PROJECT_LOCAL_SETTINGS* settings = new PROJECT_LOCAL_SETTINGS( m_projects[fullPath], fn );
1026
1027 if( aSetActive )
1028 settings = RegisterSettings( settings );
1029 else
1030 settings->LoadFromFile( path.GetPath() );
1031
1032 m_projects[fullPath]->setLocalSettings( settings );
1033
1034 // If not running from SWIG; notify the library manager of the new project
1035 // TODO(JE) this maybe could be handled through kiway (below) in the future
1036 if( aSetActive && PgmOrNull() )
1038
1039 if( aSetActive && m_kiway )
1040 m_kiway->ProjectChanged();
1041
1042 return success;
1043}
1044
1045
1046bool SETTINGS_MANAGER::UnloadProject( PROJECT* aProject, bool aSave )
1047{
1048 if( !aProject || !m_projects.count( aProject->GetProjectFullName() ) )
1049 return false;
1050
1051 if( !unloadProjectFile( aProject, aSave ) )
1052 return false;
1053
1054 wxString projectPath = aProject->GetProjectFullName();
1055 wxLogTrace( traceSettings, wxT( "Unload project %s" ), projectPath );
1056
1057 PROJECT* toRemove = m_projects.at( projectPath );
1058 bool wasActiveProject = m_projects_list.begin()->get() == toRemove;
1059
1060 // Cancel any in-progress library preloads and wait for them to finish before
1061 // modifying m_projects_list. Background preload threads access Prj() which becomes
1062 // invalid when the project list is modified.
1063 if( wasActiveProject && m_kiway )
1064 {
1065 if( KIFACE* pcbFace = m_kiway->KiFACE( KIWAY::FACE_PCB, false ) )
1066 pcbFace->CancelPreload( true );
1067 }
1068
1069 // Abort any async library loads before modifying m_projects_list to prevent race
1070 // conditions where background threads try to access Prj() while the list is empty.
1071 if( wasActiveProject && PgmOrNull() )
1073
1074 auto it = std::find_if( m_projects_list.begin(), m_projects_list.end(),
1075 [&]( const std::unique_ptr<PROJECT>& ptr )
1076 {
1077 return ptr.get() == toRemove;
1078 } );
1079
1080 wxASSERT( it != m_projects_list.end() );
1081 m_projects_list.erase( it );
1082
1083 m_projects.erase( projectPath );
1084
1085 if( wasActiveProject )
1086 {
1087 // Immediately reload a null project; this is required until the rest of the application
1088 // is refactored to not assume that Prj() always works
1089 if( m_projects_list.empty() )
1090 LoadProject( "" );
1091
1092 // Remove the reference in the environment to the previous project
1093 wxSetEnv( PROJECT_VAR_NAME, wxS( "" ) );
1094
1095#ifdef _WIN32
1096 // On Windows, processes hold a handle to their current working directory, preventing
1097 // it from being deleted. Reset to the user settings path to release the project
1098 // directory. This mirrors the wxSetWorkingDirectory call in LoadProject.
1099 if( wxTheApp && wxTheApp->IsGUI() )
1100 wxSetWorkingDirectory( PATHS::GetUserSettingsPath() );
1101#endif
1102
1103 if( m_kiway )
1104 m_kiway->ProjectChanged();
1105 }
1106
1107 return true;
1108}
1109
1110
1112{
1113 // No MDI yet: First project in the list is the active project
1114 wxASSERT_MSG( m_projects_list.size(), wxT( "no project in list" ) );
1115 return *m_projects_list.begin()->get();
1116}
1117
1118
1120{
1121 return !m_projects.empty();
1122}
1123
1124
1126{
1127 return m_projects.size() > 1 || ( m_projects.size() == 1
1128 && !m_projects.begin()->second->GetProjectFullName().IsEmpty() );
1129}
1130
1131
1132PROJECT* SETTINGS_MANAGER::GetProject( const wxString& aFullPath ) const
1133{
1134 if( m_projects.count( aFullPath ) )
1135 return m_projects.at( aFullPath );
1136
1137 return nullptr;
1138}
1139
1140
1141std::vector<wxString> SETTINGS_MANAGER::GetOpenProjects() const
1142{
1143 std::vector<wxString> ret;
1144
1145 for( const std::pair<const wxString, PROJECT*>& pair : m_projects )
1146 {
1147 // Don't save empty projects (these are the default project settings)
1148 if( !pair.first.IsEmpty() )
1149 ret.emplace_back( pair.first );
1150 }
1151
1152 return ret;
1153}
1154
1155
1156bool SETTINGS_MANAGER::SaveProject( const wxString& aFullPath, PROJECT* aProject )
1157{
1158 if( !aProject )
1159 aProject = &Prj();
1160
1161 wxString path = aFullPath;
1162
1163 if( path.empty() )
1164 path = aProject->GetProjectFullName();
1165
1166 // TODO: refactor for MDI
1167 if( aProject->IsReadOnly() )
1168 return false;
1169
1170 if( !m_project_files.count( path ) )
1171 return false;
1172
1174 wxString projectPath = aProject->GetProjectPath();
1175
1176 project->SaveToFile( projectPath );
1177 aProject->GetLocalSettings().SaveToFile( projectPath );
1178
1179 return true;
1180}
1181
1182
1183void SETTINGS_MANAGER::SaveProjectAs( const wxString& aFullPath, PROJECT* aProject )
1184{
1185 if( !aProject )
1186 aProject = &Prj();
1187
1188 wxString oldName = aProject->GetProjectFullName();
1189
1190 if( aFullPath.IsSameAs( oldName ) )
1191 {
1192 SaveProject( aFullPath, aProject );
1193 return;
1194 }
1195
1196 // Changing this will cause UnloadProject to not save over the "old" project when loading below
1197 aProject->setProjectFullName( aFullPath );
1198
1199 wxFileName fn( aFullPath );
1200
1201 PROJECT_FILE* project = m_project_files.at( oldName );
1202
1203 // Ensure read-only flags are copied; this allows doing a "Save As" on a standalone board/sch
1204 // without creating project files if the checkbox is turned off
1205 project->SetReadOnly( aProject->IsReadOnly() );
1206 aProject->GetLocalSettings().SetReadOnly( aProject->IsReadOnly() );
1207
1208 project->SetFilename( fn.GetName() );
1209 project->SaveToFile( fn.GetPath() );
1210
1211 aProject->GetLocalSettings().SetFilename( fn.GetName() );
1212 aProject->GetLocalSettings().SaveToFile( fn.GetPath() );
1213
1214 m_project_files[fn.GetFullPath()] = project;
1215 m_project_files.erase( oldName );
1216
1217 m_projects[fn.GetFullPath()] = m_projects[oldName];
1218 m_projects.erase( oldName );
1219}
1220
1221
1222void SETTINGS_MANAGER::SaveProjectCopy( const wxString& aFullPath, PROJECT* aProject )
1223{
1224 if( !aProject )
1225 aProject = &Prj();
1226
1228 wxString oldName = project->GetFilename();
1229 wxFileName fn( aFullPath );
1230
1231 bool readOnly = project->IsReadOnly();
1232 project->SetReadOnly( false );
1233
1234 project->SetFilename( fn.GetName() );
1235 project->SaveToFile( fn.GetPath() );
1236 project->SetFilename( oldName );
1237
1238 PROJECT_LOCAL_SETTINGS& localSettings = aProject->GetLocalSettings();
1239
1240 localSettings.SetFilename( fn.GetName() );
1241 localSettings.SaveToFile( fn.GetPath() );
1242 localSettings.SetFilename( oldName );
1243
1244 project->SetReadOnly( readOnly );
1245}
1246
1247
1249{
1250 wxFileName fullFn( aProject.GetProjectFullName() );
1251 wxString fn( fullFn.GetName() );
1252
1253 PROJECT_FILE* file = RegisterSettings( new PROJECT_FILE( fn ), false );
1254
1255 m_project_files[aProject.GetProjectFullName()] = file;
1256
1257 aProject.setProjectFile( file );
1258 file->SetProject( &aProject );
1259
1260 wxString path( fullFn.GetPath() );
1261
1262 return file->LoadFromFile( path );
1263}
1264
1265
1267{
1268 if( !aProject )
1269 return false;
1270
1271 wxString name = aProject->GetProjectFullName();
1272
1273 if( !m_project_files.count( name ) )
1274 return false;
1275
1277
1278 if( !file->ShouldAutoSave() )
1279 aSave = false;
1280
1281 auto it = std::find_if( m_settings.begin(), m_settings.end(),
1282 [&file]( const std::unique_ptr<JSON_SETTINGS>& aPtr )
1283 {
1284 return aPtr.get() == file;
1285 } );
1286
1287 if( it != m_settings.end() )
1288 {
1289 wxString projectPath = GetPathForSettingsFile( it->get() );
1290
1291 bool saveLocalSettings = aSave && aProject->GetLocalSettings().ShouldAutoSave();
1292
1293 FlushAndRelease( &aProject->GetLocalSettings(), saveLocalSettings );
1294
1295 if( aSave )
1296 ( *it )->SaveToFile( projectPath );
1297
1298 m_settings.erase( it );
1299 }
1300
1301 m_project_files.erase( name );
1302
1303 return true;
1304}
1305
1306
1311
1312
1313wxString SETTINGS_MANAGER::backupDateTimeFormat = wxT( "%Y-%m-%d_%H%M%S" );
1314
1315
1316bool SETTINGS_MANAGER::BackupProject( REPORTER& aReporter, wxFileName& aTarget ) const
1317{
1318 wxDateTime timestamp = wxDateTime::Now();
1319
1320 wxString fileName = wxString::Format( wxT( "%s-%s" ), Prj().GetProjectName(),
1321 timestamp.Format( backupDateTimeFormat ) );
1322
1323 if( !aTarget.IsOk() )
1324 {
1325 aTarget.SetPath( GetProjectBackupsPath() );
1326 aTarget.SetName( fileName );
1327 aTarget.SetExt( FILEEXT::ArchiveFileExtension );
1328 }
1329
1330 if( !aTarget.DirExists() && !wxMkdir( aTarget.GetPath() ) )
1331 {
1332 wxLogTrace( traceSettings, wxT( "Could not create project backup path %s" ),
1333 aTarget.GetPath() );
1334 return false;
1335 }
1336
1337 if( !aTarget.IsDirWritable() )
1338 {
1339 wxLogTrace( traceSettings, wxT( "Backup directory %s is not writable" ),
1340 aTarget.GetPath() );
1341 return false;
1342 }
1343
1344 wxLogTrace( traceSettings, wxT( "Backing up project to %s" ), aTarget.GetPath() );
1345
1346 return PROJECT_ARCHIVER::Archive( Prj().GetProjectPath(), aTarget.GetFullPath(), aReporter );
1347}
1348
1349
1350class VECTOR_INSERT_TRAVERSER : public wxDirTraverser
1351{
1352public:
1353 VECTOR_INSERT_TRAVERSER( std::vector<wxString>& aVec,
1354 std::function<bool( const wxString& )> aCond ) :
1355 m_files( aVec ),
1356 m_condition( std::move( aCond ) )
1357 {
1358 }
1359
1360 wxDirTraverseResult OnFile( const wxString& aFile ) override
1361 {
1362 if( m_condition( aFile ) )
1363 m_files.emplace_back( aFile );
1364
1365 return wxDIR_CONTINUE;
1366 }
1367
1368 wxDirTraverseResult OnDir( const wxString& aDirName ) override
1369 {
1370 return wxDIR_CONTINUE;
1371 }
1372
1373private:
1374 std::vector<wxString>& m_files;
1375
1376 std::function<bool( const wxString& )> m_condition;
1377};
1378
1379
1381{
1383
1384 if( !settings.enabled )
1385 return true;
1386
1387 wxString prefix = Prj().GetProjectName() + '-';
1388
1389 auto modTime =
1390 [&prefix]( const wxString& aFile )
1391 {
1392 wxDateTime dt;
1393 wxString fn( wxFileName( aFile ).GetName() );
1394 fn.Replace( prefix, wxS( "" ) );
1395 dt.ParseFormat( fn, backupDateTimeFormat );
1396 return dt;
1397 };
1398
1399 wxFileName projectPath( Prj().GetProjectPath(), wxEmptyString, wxEmptyString );
1400
1401 // Skip backup if project path isn't valid or writable
1402 if( !projectPath.IsOk() || !projectPath.Exists() || !projectPath.IsDirWritable() )
1403 return true;
1404
1405 wxString backupPath = GetProjectBackupsPath();
1406
1407 if( !wxDirExists( backupPath ) )
1408 {
1409 wxLogTrace( traceSettings, wxT( "Backup path %s doesn't exist, creating it" ), backupPath );
1410
1411 if( !wxMkdir( backupPath ) )
1412 {
1413 wxLogTrace( traceSettings, wxT( "Could not create backups path! Skipping backup" ) );
1414 return false;
1415 }
1416 }
1417
1418 wxDir dir( backupPath );
1419
1420 if( !dir.IsOpened() )
1421 {
1422 wxLogTrace( traceSettings, wxT( "Could not open project backups path %s" ), dir.GetName() );
1423 return false;
1424 }
1425
1426 std::vector<wxString> files;
1427
1428 VECTOR_INSERT_TRAVERSER traverser( files,
1429 [&modTime]( const wxString& aFile )
1430 {
1431 return modTime( aFile ).IsValid();
1432 } );
1433
1434 dir.Traverse( traverser, wxT( "*.zip" ) );
1435
1436 // Sort newest-first
1437 std::sort( files.begin(), files.end(),
1438 [&]( const wxString& aFirst, const wxString& aSecond ) -> bool
1439 {
1440 wxDateTime first = modTime( aFirst );
1441 wxDateTime second = modTime( aSecond );
1442
1443 return first.GetTicks() > second.GetTicks();
1444 } );
1445
1446 // Do we even need to back up?
1447 if( !files.empty() )
1448 {
1449 wxDateTime lastTime = modTime( files[0] );
1450
1451 if( lastTime.IsValid() )
1452 {
1453 wxTimeSpan delta = wxDateTime::Now() - modTime( files[0] );
1454
1455 if( delta.IsShorterThan( wxTimeSpan::Seconds( settings.min_interval ) ) )
1456 return true;
1457 }
1458 }
1459
1460 // Backup
1461 wxFileName target;
1462 bool backupSuccessful = BackupProject( aReporter, target );
1463
1464 if( !backupSuccessful )
1465 return false;
1466
1467 // Update the file list
1468 files.insert( files.begin(), target.GetFullPath() );
1469
1470 // Are there any changes since the last backup?
1471 if( files.size() >= 2
1472 && PROJECT_ARCHIVER::AreZipArchivesIdentical( files[0], files[1], aReporter ) )
1473 {
1474 wxRemoveFile( files[0] );
1475 return true;
1476 }
1477
1478 // Now that we know a backup is needed, apply the retention policy
1479
1480 // Step 1: if we're over the total file limit, remove the oldest
1481 if( !files.empty() && settings.limit_total_files > 0 )
1482 {
1483 while( files.size() > static_cast<size_t>( settings.limit_total_files ) )
1484 {
1485 wxRemoveFile( files.back() );
1486 files.pop_back();
1487 }
1488 }
1489
1490 // Step 2: Stay under the total size limit
1491 if( settings.limit_total_size > 0 )
1492 {
1493 wxULongLong totalSize = 0;
1494
1495 for( const wxString& file : files )
1496 totalSize += wxFileName::GetSize( file );
1497
1498 while( !files.empty() && totalSize > static_cast<wxULongLong>( settings.limit_total_size ) )
1499 {
1500 totalSize -= wxFileName::GetSize( files.back() );
1501 wxRemoveFile( files.back() );
1502 files.pop_back();
1503 }
1504 }
1505
1506 // Step 3: Stay under the daily limit
1507 if( settings.limit_daily_files > 0 && files.size() > 1 )
1508 {
1509 wxDateTime day = modTime( files[0] );
1510 int num = 1;
1511
1512 wxASSERT( day.IsValid() );
1513
1514 std::vector<wxString> filesToDelete;
1515
1516 for( size_t i = 1; i < files.size(); i++ )
1517 {
1518 wxDateTime dt = modTime( files[i] );
1519
1520 if( dt.IsSameDate( day ) )
1521 {
1522 num++;
1523
1524 if( num > settings.limit_daily_files )
1525 filesToDelete.emplace_back( files[i] );
1526 }
1527 else
1528 {
1529 day = dt;
1530 num = 1;
1531 }
1532 }
1533
1534 for( const wxString& file : filesToDelete )
1535 wxRemoveFile( file );
1536 }
1537
1538 return true;
1539}
1540
1541
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:302
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:635
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:624
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition pgm_base.cpp:781
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:134
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:65
virtual void setProjectFile(PROJECT_FILE *aFile)
Set the backing store file for this project.
Definition project.h:340
virtual bool IsReadOnly() const
Definition project.h:161
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
Definition project.cpp:162
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:168
virtual const wxString GetProjectName() const
Return the short name of the project.
Definition project.cpp:180
virtual PROJECT_LOCAL_SETTINGS & GetLocalSettings() const
Definition project.h:209
virtual void setProjectFullName(const wxString &aFullPathAndName)
Set the full directory, basename, and extension of the project.
Definition project.cpp:138
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:293
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.
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:155
std::string path
int delta
Definition of file extensions used in Kicad.