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