KiCad PCB EDA Suite
Loading...
Searching...
No Matches
common_settings.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
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <set>
22#include <fstream>
23#include <sstream>
24
26#include <env_vars.h>
27#include <paths.h>
28#include <search_stack.h>
34#include <settings/parameters.h>
35#include <systemdirsappend.h>
36#include <trace_helpers.h>
37#include <wx/config.h>
38#include <wx/log.h>
39#include <wx/regex.h>
40#include <wx/tokenzr.h>
41#include <wx/window.h>
42
43
45const wxRegEx versionedEnvVarRegex( wxS( "KICAD[0-9]+_[A-Z0-9_]+(_DIR)?" ) );
46
48const int commonSchemaVersion = 6;
49
51
55 m_Backup(),
56 m_Env(),
57 m_Input(),
59 m_Graphics(),
60 m_Session(),
61 m_System(),
64 m_Api(),
66{
67 /*
68 * Automatic dark mode detection works fine on Mac.
69 */
70#if defined( __WXGTK__ ) || defined( __WXMSW__ )
71 m_params.emplace_back( new PARAM_ENUM<ICON_THEME>( "appearance.icon_theme",
73#else
74 m_Appearance.icon_theme = ICON_THEME::AUTO;
75#endif
76
77#if defined( __WXMSW__ )
78 m_params.emplace_back( new PARAM_ENUM<APP_THEME>( "appearance.app_theme", &m_Appearance.app_theme,
80#else
81 m_Appearance.app_theme = APP_THEME::AUTO;
82#endif
83
84 /*
85 * Automatic canvas scaling works fine on all supported platforms, so it's no longer exposed as
86 * a configuration option.
87 */
88 m_Appearance.canvas_scale = 0.0;
89
90 /*
91 * Menu icons are off by default on OSX and on for all other platforms.
92 */
93#ifdef __WXMAC__
94 m_params.emplace_back( new PARAM<bool>( "appearance.use_icons_in_menus",
95 &m_Appearance.use_icons_in_menus, false ) );
96#else
97 m_params.emplace_back( new PARAM<bool>( "appearance.use_icons_in_menus",
98 &m_Appearance.use_icons_in_menus, true ) );
99#endif
100
101 /*
102 * Font scaling hacks are only needed on GTK under wxWidgets 3.0.
103 */
104 m_Appearance.apply_icon_scale_to_fonts = false;
105
106 m_params.emplace_back( new PARAM<bool>( "appearance.show_scrollbars",
107 &m_Appearance.show_scrollbars, false ) );
108
109 m_params.emplace_back( new PARAM<double>( "appearance.hicontrast_dimming_factor",
110 &m_Appearance.hicontrast_dimming_factor, 0.8f ) );
111
112 m_params.emplace_back( new PARAM<int>( "appearance.text_editor_zoom",
113 &m_Appearance.text_editor_zoom, 0 ) );
114
115 m_params.emplace_back( new PARAM<int>( "appearance.toolbar_icon_size",
116 &m_Appearance.toolbar_icon_size, 24, 16, 64 ) );
117
118 m_params.emplace_back( new PARAM<bool>( "appearance.grid_striping",
119 &m_Appearance.grid_striping, false ) );
120
121 m_params.emplace_back( new PARAM<bool>( "appearance.use_custom_cursors",
122 &m_Appearance.use_custom_cursors, true ) );
123
124 m_Appearance.zoom_correction_factor = 1.0;
125 m_params.emplace_back( new PARAM<double>( "appearance.zoom_correction_factor",
126 &m_Appearance.zoom_correction_factor, 1.0, 0.1, 10.0 ) );
127
128 m_params.emplace_back( new PARAM<bool>( "auto_backup.enabled", &m_Backup.enabled, true ) );
129
130 m_params.emplace_back( new PARAM_ENUM<BACKUP_FORMAT>( "auto_backup.format", &m_Backup.format,
132
133 m_params.emplace_back( new PARAM_ENUM<BACKUP_LOCATION>( "auto_backup.location",
136
137 m_params.emplace_back( new PARAM<unsigned long long>( "auto_backup.limit_total_size",
138 &m_Backup.limit_total_size, 104857600 ) );
139
140 auto envVarsParam = m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "environment.vars",
141 [&]() -> nlohmann::json
142 {
143 nlohmann::json ret = {};
144
145 for( const std::pair<wxString, ENV_VAR_ITEM> entry : m_Env.vars )
146 {
147 const ENV_VAR_ITEM& var = entry.second;
148
149 wxASSERT( entry.first == var.GetKey() );
150
151 // Default values are never persisted
152 if( var.IsDefault() )
153 {
154 wxLogTrace( traceEnvVars,
155 wxS( "COMMON_SETTINGS: Env var %s skipping save (default)" ),
156 var.GetKey() );
157 continue;
158 }
159
160 wxString value = var.GetValue();
161
162 value.Trim( true ).Trim( false ); // Trim from both sides
163
164 // Vars that existed in JSON are persisted, but if they were overridden
165 // externally, we persist the old value (i.e. the one that was loaded from JSON)
166 if( var.GetDefinedExternally() )
167 {
168 if( var.GetDefinedInSettings() )
169 {
170 wxLogTrace( traceEnvVars,
171 wxS( "COMMON_SETTINGS: Env var %s was overridden "
172 "externally, saving previously-loaded value %s" ),
173 var.GetKey(), var.GetSettingsValue() );
174 value = var.GetSettingsValue();
175 }
176 else
177 {
178 wxLogTrace( traceEnvVars,
179 wxS( "COMMON_SETTINGS: Env var %s skipping save "
180 "(external)" ),
181 var.GetKey() );
182 continue;
183 }
184 }
185
186 wxLogTrace( traceEnvVars,
187 wxS( "COMMON_SETTINGS: Saving env var %s = %s" ),
188 var.GetKey(), value);
189
190 std::string key( var.GetKey().Trim( true ).Trim( false ).ToUTF8() );
191 ret[ std::move( key ) ] = value;
192 }
193
194 return ret;
195 },
196 [&]( const nlohmann::json& aJson )
197 {
198 if( !aJson.is_object() )
199 return;
200
201 for( const auto& entry : aJson.items() )
202 {
203 wxString key = wxString( entry.key().c_str(), wxConvUTF8 ).Trim( true ).Trim( false );
204 wxString val = entry.value().get<wxString>().Trim( true ).Trim( false );
205
206 if( m_Env.vars.count( key ) )
207 {
208 if( m_Env.vars[key].GetDefinedExternally() )
209 {
210 wxLogTrace( traceEnvVars,
211 wxS( "COMMON_SETTINGS: %s is defined externally" ),
212 key );
213 m_Env.vars[key].SetDefinedInSettings();
214 m_Env.vars[key].SetSettingsValue( val );
215 continue;
216 }
217 else
218 {
219 wxLogTrace( traceEnvVars,
220 wxS( "COMMON_SETTINGS: Updating %s: %s -> %s"),
221 key, m_Env.vars[key].GetValue(), val );
222 m_Env.vars[key].SetValue( val );
223 }
224 }
225 else
226 {
227 wxLogTrace( traceEnvVars,
228 wxS( "COMMON_SETTINGS: Loaded new var: %s = %s" ),
229 key, val );
230 m_Env.vars[key] = ENV_VAR_ITEM( key, val );
231 }
232
233 m_Env.vars[key].SetDefinedInSettings();
234 m_Env.vars[key].SetSettingsValue( val );
235 }
236 },
237 {} ) );
238 envVarsParam->SetClearUnknownKeys();
239
240 m_params.emplace_back( new PARAM<bool>( "input.focus_follow_sch_pcb",
241 &m_Input.focus_follow_sch_pcb, false ) );
242
243 m_params.emplace_back( new PARAM<bool>( "input.auto_pan", &m_Input.auto_pan, false ) );
244
245 m_params.emplace_back( new PARAM<int>( "input.auto_pan_acceleration",
246 &m_Input.auto_pan_acceleration, 5 ) );
247
248 m_params.emplace_back( new PARAM<bool>( "input.center_on_zoom",
249 &m_Input.center_on_zoom, true ) );
250
251 m_params.emplace_back( new PARAM<bool>( "input.immediate_actions",
252 &m_Input.immediate_actions, true ) );
253
254 m_params.emplace_back( new PARAM<bool>( "input.warp_mouse_on_move",
255 &m_Input.warp_mouse_on_move, true ) );
256
257 m_params.emplace_back( new PARAM<bool>( "input.horizontal_pan",
258 &m_Input.horizontal_pan, false ) );
259
260 m_params.emplace_back( new PARAM<bool>( "input.hotkey_feedback",
261 &m_Input.hotkey_feedback, true ) );
262
263 m_params.emplace_back( new PARAM<bool>( "input.zoom_acceleration",
264 &m_Input.zoom_acceleration, false ) );
265
266#ifdef __WXMAC__
267 int default_zoom_speed = 5;
268#else
269 int default_zoom_speed = 1;
270#endif
271
272 m_params.emplace_back( new PARAM<int>( "input.zoom_speed",
273 &m_Input.zoom_speed, default_zoom_speed ) );
274
275 m_params.emplace_back( new PARAM<bool>( "input.zoom_speed_auto",
276 &m_Input.zoom_speed_auto, true ) );
277
278 m_params.emplace_back( new PARAM<int>( "input.scroll_modifier_zoom",
279 &m_Input.scroll_modifier_zoom, 0 ) );
280
281 m_params.emplace_back( new PARAM<int>( "input.scroll_modifier_pan_h",
282 &m_Input.scroll_modifier_pan_h, WXK_CONTROL ) );
283
284 m_params.emplace_back( new PARAM<int>( "input.scroll_modifier_pan_v",
285 &m_Input.scroll_modifier_pan_v, WXK_SHIFT ) );
286
287 m_params.emplace_back( new PARAM<int>( "input.motion_pan_modifier",
288 &m_Input.motion_pan_modifier, 0 ) );
289
290 m_params.emplace_back( new PARAM<bool>( "input.reverse_scroll_zoom",
291 &m_Input.reverse_scroll_zoom, false ) );
292
293 m_params.emplace_back( new PARAM<bool>( "input.reverse_scroll_pan_h",
294 &m_Input.reverse_scroll_pan_h, false ) );
295
296 m_params.emplace_back( new PARAM_ENUM<MOUSE_DRAG_ACTION>( "input.mouse_left",
299
300 m_params.emplace_back( new PARAM_ENUM<MOUSE_DRAG_ACTION>( "input.mouse_middle",
303
304 m_params.emplace_back( new PARAM_ENUM<MOUSE_DRAG_ACTION>( "input.mouse_right",
307
308 m_params.emplace_back( new PARAM<int>( "spacemouse.rotate_speed",
309 &m_SpaceMouse.rotate_speed, 5, 1, 10 ) );
310
311 m_params.emplace_back( new PARAM<int>( "spacemouse.pan_speed",
312 &m_SpaceMouse.pan_speed, 5, 1, 10 ) );
313
314 m_params.emplace_back( new PARAM<bool>( "spacemouse.reverse_rotate",
315 &m_SpaceMouse.reverse_rotate, false ) );
316
317 m_params.emplace_back( new PARAM<bool>( "spacemouse.reverse_pan_x",
318 &m_SpaceMouse.reverse_pan_x, false ) );
319
320 m_params.emplace_back( new PARAM<bool>( "spacemouse.reverse_pan_y",
321 &m_SpaceMouse.reverse_pan_y, false ) );
322
323 m_params.emplace_back( new PARAM<bool>( "spacemouse.reverse_zoom",
324 &m_SpaceMouse.reverse_zoom, false ) );
325
326 m_params.emplace_back( new PARAM<int>( "graphics.canvas_type",
327 &m_Graphics.canvas_type, EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL ) );
328
329 m_params.emplace_back( new PARAM<int>( "graphics.antialiasing_mode",
330 &m_Graphics.aa_mode, 2, 0, 2 ) );
331
332 m_params.emplace_back( new PARAM<bool>( "system.local_history_enabled",
333 &m_System.local_history_enabled, true ) );
334 m_params.emplace_back( new PARAM<int>( "system.local_history_debounce",
335 &m_System.local_history_debounce, 5, 0, 100000 ) );
336
337#ifdef __WXMAC__
338 m_params.emplace_back( new PARAM<wxString>( "system.text_editor",
339 &m_System.text_editor, wxS( "/usr/bin/open -e" ) ) );
340#else
341 m_params.emplace_back( new PARAM<wxString>( "system.text_editor",
342 &m_System.text_editor, wxS( "" ) ) );
343#endif
344
345#if defined( __WINDOWS__ )
346 m_params.emplace_back( new PARAM<wxString>( "system.file_explorer",
347 &m_System.file_explorer, wxS( "explorer.exe /n,/select,%F" ) ) );
348#else
349 m_params.emplace_back( new PARAM<wxString>( "system.file_explorer",
350 &m_System.file_explorer, wxS( "" ) ) );
351#endif
352
353 m_params.emplace_back( new PARAM<int>( "system.file_history_size",
354 &m_System.file_history_size, 9 ) );
355
356 m_params.emplace_back( new PARAM<wxString>( "system.language",
357 &m_System.language, wxS( "Default" ) ) );
358
359 m_params.emplace_back( new PARAM<wxString>( "system.pdf_viewer_name",
360 &m_System.pdf_viewer_name, wxS( "" ) ) );
361
362 m_params.emplace_back( new PARAM<bool>( "system.use_system_pdf_viewer",
363 &m_System.use_system_pdf_viewer, true ) );
364
365 m_params.emplace_back( new PARAM<wxString>( "system.working_dir",
366 &m_System.working_dir, wxS( "" ) ) );
367
368 m_params.emplace_back( new PARAM<int>( "system.clear_3d_cache_interval",
369 &m_System.clear_3d_cache_interval, 30 ) );
370
371 m_params.emplace_back( new PARAM<bool>( "do_not_show_again.zone_fill_warning",
372 &m_DoNotShowAgain.zone_fill_warning, false ) );
373
374 m_params.emplace_back( new PARAM<bool>( "do_not_show_again.env_var_overwrite_warning",
375 &m_DoNotShowAgain.env_var_overwrite_warning, false ) );
376
377 m_params.emplace_back( new PARAM<bool>( "do_not_show_again.scaled_3d_models_warning",
378 &m_DoNotShowAgain.scaled_3d_models_warning, false ) );
379
380 m_params.emplace_back( new PARAM<bool>( "do_not_show_again.data_collection_prompt",
381 &m_DoNotShowAgain.data_collection_prompt, false ) );
382
383 m_params.emplace_back( new PARAM<bool>( "do_not_show_again.update_check_prompt",
384 &m_DoNotShowAgain.update_check_prompt, false ) );
385
386 m_params.emplace_back( new PARAM<bool>( "do_not_show_again.migrate_wrl_prompt",
387 &m_DoNotShowAgain.migrate_wrl_prompt, false ) );
388
389 m_params.emplace_back( new PARAM<bool>( "embed_file_defaults.datasheet", &m_EmbedFileDefaults.datasheet, true ) );
390
391 m_params.emplace_back(
392 new PARAM<bool>( "embed_file_defaults.drawing_sheet", &m_EmbedFileDefaults.drawing_sheet, true ) );
393
394 m_params.emplace_back( new PARAM<bool>( "embed_file_defaults.model_3d", &m_EmbedFileDefaults.model_3d, false ) );
395
396 m_params.emplace_back( new PARAM<bool>( "embed_file_defaults.sim_model", &m_EmbedFileDefaults.sim_model, false ) );
397
398 m_params.emplace_back( new PARAM_LIST<wxString>( "system.extra_3d_search_dirs",
399 &m_Extra3DSearchDirs, {} ) );
400
401 m_params.emplace_back( new PARAM<bool>( "session.remember_open_files",
402 &m_Session.remember_open_files, false ) );
403
404 m_params.emplace_back( new PARAM_LIST<wxString>( "session.pinned_symbol_libs",
405 &m_Session.pinned_symbol_libs, {} ) );
406
407 m_params.emplace_back( new PARAM_LIST<wxString>( "session.pinned_fp_libs",
408 &m_Session.pinned_fp_libs, {} ) );
409
410 m_params.emplace_back( new PARAM_LIST<wxString>( "session.pinned_design_block_libs",
411 &m_Session.pinned_design_block_libs, {} ) );
412
413 m_params.emplace_back( new PARAM<int>( "package_manager.sash_pos",
414 &m_PackageManager.sash_pos, 380 ) );
415
416 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "git.repositories",
417 [&]() -> nlohmann::json
418 {
419 nlohmann::json ret = {};
420
421 for( const GIT_REPOSITORY& repo : m_Git.repositories )
422 {
423 nlohmann::json repoJson = {};
424
425 repoJson["name"] = repo.name;
426 repoJson["path"] = repo.path;
427 repoJson["authType"] = repo.authType;
428 repoJson["username"] = repo.username;
429 repoJson["ssh_path"] = repo.ssh_path;
430 repoJson["active"] = repo.active;
431
432 ret.push_back( repoJson );
433 }
434
435 return ret;
436 },
437 [&]( const nlohmann::json& aJson )
438 {
439 if( !aJson.is_array() )
440 return;
441
442 m_Git.repositories.clear();
443
444 for( const auto& repoJson : aJson )
445 {
446 GIT_REPOSITORY repo;
447
448 repo.name = repoJson["name"].get<wxString>();
449 repo.path = repoJson["path"].get<wxString>();
450 repo.authType = repoJson["authType"].get<wxString>();
451 repo.username = repoJson["username"].get<wxString>();
452 repo.ssh_path = repoJson["ssh_path"].get<wxString>();
453 repo.active = repoJson["active"].get<bool>();
454 repo.checkValid = true;
455
456 m_Git.repositories.push_back( repo );
457 }
458 },
459 {} ) );
460
461 m_params.emplace_back( new PARAM<wxString>( "git.authorName",
462 &m_Git.authorName, wxS( "" ) ) );
463
464 m_params.emplace_back( new PARAM<wxString>( "git.authorEmail",
465 &m_Git.authorEmail, wxS( "" ) ) );
466
467 m_params.emplace_back( new PARAM<bool>( "git.useDefaultAuthor",
468 &m_Git.useDefaultAuthor, true ) );
469
470 m_params.emplace_back( new PARAM<bool>( "git.enableGit",
471 &m_Git.enableGit, true ) );
472
473 m_params.emplace_back( new PARAM<int>( "git.updatInterval",
474 &m_Git.updatInterval, 5 ) );
475
476 m_params.emplace_back( new PARAM<wxString>( "api.interpreter_path",
477 &m_Api.python_interpreter, wxS( "" ) ) );
478
479 m_params.emplace_back( new PARAM<bool>( "api.enable_server",
480 &m_Api.enable_server, false ) );
481
482 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "dialog.controls",
483 [&]() -> nlohmann::json
484 {
485 nlohmann::json ret = nlohmann::json::object();
486
487 for( const auto& dlg : m_csInternals->m_dialogControlValues )
488 ret[ dlg.first ] = dlg.second;
489
490 return ret;
491 },
492 [&]( const nlohmann::json& aVal )
493 {
494 m_csInternals->m_dialogControlValues.clear();
495
496 if( !aVal.is_object() )
497 return;
498
499 for( auto& [dlgKey, dlgVal] : aVal.items() )
500 {
501 if( !dlgVal.is_object() )
502 continue;
503
504 for( auto& [ctrlKey, ctrlVal] : dlgVal.items() )
505 m_csInternals->m_dialogControlValues[ dlgKey ][ ctrlKey ] = ctrlVal;
506 }
507 },
508 nlohmann::json::object() ) );
509
510
511 registerMigration( 0, 1, std::bind( &COMMON_SETTINGS::migrateSchema0to1, this ) );
512 registerMigration( 1, 2, std::bind( &COMMON_SETTINGS::migrateSchema1to2, this ) );
513 registerMigration( 2, 3, std::bind( &COMMON_SETTINGS::migrateSchema2to3, this ) );
514 registerMigration( 3, 4, std::bind( &COMMON_SETTINGS::migrateSchema3to4, this ) );
515 registerMigration( 4, 5, std::bind( &COMMON_SETTINGS::migrateSchema4to5, this ) );
516 registerMigration( 5, 6, std::bind( &COMMON_SETTINGS::migrateSchema5to6, this ) );
517}
518
519
521{
527
528 nlohmann::json::json_pointer mwp_pointer( "/input/mousewheel_pan"_json_pointer );
529
530 bool mwp = false;
531
532 try
533 {
534 mwp = m_internals->at( mwp_pointer );
535 m_internals->At( "input" ).erase( "mousewheel_pan" );
536 }
537 catch( ... )
538 {
539 wxLogTrace( traceSettings,
540 wxT( "COMMON_SETTINGS::Migrate 0->1: mousewheel_pan not found" ) );
541 }
542
543 if( mwp )
544 {
545 ( *m_internals )[nlohmann::json::json_pointer( "/input/horizontal_pan" )] = true;
546 ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_h" )] = WXK_SHIFT;
547 ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_v" )] = 0;
548 ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_zoom" )] = WXK_CONTROL;
549 }
550 else
551 {
552 ( *m_internals )[nlohmann::json::json_pointer( "/input/horizontal_pan" )] = false;
553 ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_h" )] = WXK_CONTROL;
554 ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_v" )] = WXK_SHIFT;
555 ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_zoom" )] = 0;
556 }
557
558 return true;
559}
560
561
563{
564 nlohmann::json::json_pointer v1_pointer( "/input/prefer_select_to_drag"_json_pointer );
565
566 bool prefer_selection = false;
567
568 try
569 {
570 prefer_selection = m_internals->at( v1_pointer );
571 m_internals->at( nlohmann::json::json_pointer( "/input"_json_pointer ) )
572 .erase( "prefer_select_to_drag" );
573 }
574 catch( ... )
575 {
576 wxLogTrace( traceSettings,
577 wxT( "COMMON_SETTINGS::Migrate 1->2: prefer_select_to_drag not found" ) );
578 }
579
580 if( prefer_selection )
581 ( *m_internals )[nlohmann::json::json_pointer( "/input/mouse_left" )] = MOUSE_DRAG_ACTION::SELECT;
582 else
583 ( *m_internals )[nlohmann::json::json_pointer( "/input/mouse_left" )] = MOUSE_DRAG_ACTION::DRAG_ANY;
584
585 return true;
586}
587
588
590{
591 wxFileName cfgpath;
592 cfgpath.AssignDir( PATHS::GetUserSettingsPath() );
593 cfgpath.AppendDir( wxT( "3d" ) );
594 cfgpath.SetFullName( wxS( "3Dresolver.cfg" ) );
595 cfgpath.MakeAbsolute();
596
597 std::vector<LEGACY_3D_SEARCH_PATH> legacyPaths;
598 readLegacy3DResolverCfg( cfgpath.GetFullPath(), legacyPaths );
599
600 // env variables have a limited allowed character set for names
601 wxRegEx nonValidCharsRegex( wxS( "[^A-Z0-9_]+" ), wxRE_ADVANCED );
602
603 for( const LEGACY_3D_SEARCH_PATH& path : legacyPaths )
604 {
605 wxString key = path.m_Alias;
606 const wxString& val = path.m_Pathvar;
607
608 // The 3d alias config didn't use the same naming restrictions as real env variables
609 // We need to sanitize them
610
611 // upper case only
612 key.MakeUpper();
613
614 // logically swap - with _
615 key.Replace( wxS( "-" ), wxS( "_" ) );
616
617 // remove any other chars
618 nonValidCharsRegex.Replace( &key, wxEmptyString );
619
620 if( !m_Env.vars.count( key ) )
621 {
622 wxLogTrace( traceEnvVars, wxS( "COMMON_SETTINGS: Loaded new var: %s = %s" ), key, val );
623 m_Env.vars[key] = ENV_VAR_ITEM( key, val );
624 }
625 }
626
627 if( cfgpath.FileExists() )
628 {
629 wxRemoveFile( cfgpath.GetFullPath() );
630 }
631
632 return true;
633}
634
635
637{
638 // >= 10 = add 1
639 try
640 {
641 // Update netclass panel shown columns for eeschema
642 const nlohmann::json::json_pointer v3_pointer_eeschema( "/netclass_panel/eeschema_shown_columns"_json_pointer );
643 wxString eeSchemaColumnList_old = m_internals->at( v3_pointer_eeschema );
644
645 wxStringTokenizer eeSchemaShownTokens( eeSchemaColumnList_old, " \t\r\n" );
646 wxString eeSchemaColumnList_new;
647
648 while( eeSchemaShownTokens.HasMoreTokens() )
649 {
650 long colNumber;
651 eeSchemaShownTokens.GetNextToken().ToLong( &colNumber );
652
653 if( colNumber >= 10 )
654 ++colNumber;
655
656 eeSchemaColumnList_new += wxString::Format( wxT( "%ld " ), colNumber );
657 }
658
659 eeSchemaColumnList_new.Trim( true );
660 eeSchemaColumnList_new.Trim( false );
661
662 m_internals->at( v3_pointer_eeschema ) = eeSchemaColumnList_new.ToUTF8();
663
664 // Update netclass panel shown columns for pcbnew
665 const nlohmann::json::json_pointer v3_pointer_pcbnew( "/netclass_panel/pcbnew_shown_columns"_json_pointer );
666 wxString pcbnewColumnList_old = m_internals->at( v3_pointer_pcbnew );
667
668 wxStringTokenizer pcbnewShownTokens( pcbnewColumnList_old, " \t\r\n" );
669 wxString pcbnewColumnList_new;
670
671 while( pcbnewShownTokens.HasMoreTokens() )
672 {
673 long colNumber;
674 pcbnewShownTokens.GetNextToken().ToLong( &colNumber );
675
676 if( colNumber >= 10 )
677 ++colNumber;
678
679 pcbnewColumnList_new += wxString::Format( wxT( "%ld " ), colNumber );
680 }
681
682 pcbnewColumnList_new.Trim( true );
683 pcbnewColumnList_new.Trim( false );
684
685 m_internals->at( v3_pointer_pcbnew ) = pcbnewColumnList_new.ToUTF8();
686 }
687 catch( ... )
688 {
689 wxLogTrace( traceSettings, wxT( "COMMON_SETTINGS::Migrate 3->4: /netclass_panel/shown_columns not found" ) );
690 }
691
692 return true;
693}
694
695
697{
698 try
699 {
700 nlohmann::json& controls = m_internals->At( "dialog" ).at( "controls" );
701
702 for( auto& [dlgKey, dlgVal] : controls.items() )
703 {
704 if( !dlgVal.is_object() )
705 continue;
706
707 auto geoIt = dlgVal.find( "__geometry" );
708
709 if( geoIt == dlgVal.end() || !geoIt->is_object() )
710 continue;
711
712 nlohmann::json& geom = *geoIt;
713
714 // Legacy values were stored in logical pixels. Convert to DIP using the
715 // primary display's scale factor (best approximation without window context).
716 int w = geom.value( "w", 0 );
717 int h = geom.value( "h", 0 );
718
719 wxSize dipSize = wxWindow::ToDIP( wxSize( w, h ), nullptr );
720 geom[ "w" ] = dipSize.x;
721 geom[ "h" ] = dipSize.y;
722
723 geom.erase( "dip" );
724 }
725 }
726 catch( ... )
727 {
728 wxLogTrace( traceSettings,
729 wxT( "COMMON_SETTINGS::Migrate 4->5: dialog.controls not found" ) );
730 }
731
732 return true;
733}
734
735
737{
738 // Schema 6 introduces auto_backup.format and auto_backup.location. Pre-schema-6
739 // installs unconditionally produced timestamped zip archives whenever a save was
740 // eligible for backup, so the new INCREMENTAL default would silently disable archive
741 // creation for users who already had auto_backup.enabled set. Write ZIP into the
742 // upgraded config to preserve their backup behavior; new installs skip the migration
743 // path entirely and keep the new default. The location default (PROJECT_DIR) already
744 // matches the legacy on-disk layout, so we leave it absent.
745 try
746 {
747 if( !Contains( "auto_backup.format" ) )
748 Set<int>( "auto_backup.format", static_cast<int>( BACKUP_FORMAT::ZIP ) );
749 }
750 catch( ... )
751 {
752 wxLogTrace( traceSettings,
753 wxT( "COMMON_SETTINGS::Migrate 5->6: failed to set auto_backup.format" ) );
754 }
755
756 return true;
757}
758
759
760bool COMMON_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg )
761{
762 bool ret = true;
763
764 ret &= fromLegacy<double>( aCfg, "CanvasScale", "appearance.canvas_scale" );
765 ret &= fromLegacy<int>( aCfg, "IconScale", "appearance.icon_scale" );
766 ret &= fromLegacy<bool>( aCfg, "UseIconsInMenus", "appearance.use_icons_in_menus" );
767 ret &= fromLegacy<bool>( aCfg, "ShowEnvVarWarningDialog", "environment.show_warning_dialog" );
768
769 auto load_env_vars =
770 [&]()
771 {
772 wxString key, value;
773 long index = 0;
774 nlohmann::json::json_pointer ptr = m_internals->PointerFromString( "environment.vars" );
775
776 aCfg->SetPath( "EnvironmentVariables" );
777 ( *m_internals )[ptr] = nlohmann::json( {} );
778
779 while( aCfg->GetNextEntry( key, index ) )
780 {
781 if( versionedEnvVarRegex.Matches( key ) )
782 {
783 wxLogTrace( traceSettings,
784 wxT( "Migrate Env: %s is blacklisted; skipping." ), key );
785 continue;
786 }
787
788 value = aCfg->Read( key, wxEmptyString );
789
790 if( !value.IsEmpty() )
791 {
792 ptr.push_back( key.ToStdString() );
793
794 wxLogTrace( traceSettings, wxT( "Migrate Env: %s=%s" ),
795 ptr.to_string(), value );
796 ( *m_internals )[ptr] = value.ToUTF8();
797
798 ptr.pop_back();
799 }
800 }
801
802 aCfg->SetPath( ".." );
803 };
804
805 load_env_vars();
806
807 bool mousewheel_pan = false;
808
809 if( aCfg->Read( "MousewheelPAN", &mousewheel_pan ) && mousewheel_pan )
810 {
811 Set( "input.horizontal_pan", true );
812 Set( "input.scroll_modifier_pan_h", static_cast<int>( WXK_SHIFT ) );
813 Set( "input.scroll_modifier_pan_v", 0 );
814 Set( "input.scroll_modifier_zoom", static_cast<int>( WXK_CONTROL ) );
815 }
816
817 ret &= fromLegacy<bool>( aCfg, "AutoPAN", "input.auto_pan" );
818 ret &= fromLegacy<bool>( aCfg, "ImmediateActions", "input.immediate_actions" );
819 ret &= fromLegacy<bool>( aCfg, "PreferSelectionToDragging", "input.prefer_select_to_drag" );
820 ret &= fromLegacy<bool>( aCfg, "MoveWarpsCursor", "input.warp_mouse_on_move" );
821 ret &= fromLegacy<bool>( aCfg, "ZoomNoCenter", "input.center_on_zoom" );
822
823 // This was stored inverted in legacy config
824 if( std::optional<bool> value = Get<bool>( "input.center_on_zoom" ) )
825 Set( "input.center_on_zoom", !( *value ) );
826
827 ret &= fromLegacy<int>( aCfg, "OpenGLAntialiasingMode", "graphics.opengl_antialiasing_mode" );
828 ret &= fromLegacy<int>( aCfg, "CairoAntialiasingMode", "graphics.cairo_antialiasing_mode" );
829
830 ret &= fromLegacy<int>( aCfg, "AutoSaveInterval", "system.local_history_debounce" );
831 ret &= fromLegacyString( aCfg, "Editor", "system.editor_name" );
832 ret &= fromLegacy<int>( aCfg, "FileHistorySize", "system.file_history_size" );
833 ret &= fromLegacyString( aCfg, "LanguageID", "system.language" );
834 ret &= fromLegacyString( aCfg, "PdfBrowserName", "system.pdf_viewer_name" );
835 ret &= fromLegacy<bool>( aCfg, "UseSystemBrowser", "system.use_system_pdf_viewer" );
836 ret &= fromLegacyString( aCfg, "WorkingDir", "system.working_dir" );
837
838 return ret;
839}
840
841
843{
844 auto addVar =
845 [&]( const wxString& aKey, const wxString& aDefault )
846 {
847 m_Env.vars[aKey] = ENV_VAR_ITEM( aKey, aDefault, aDefault );
848
849 wxString envValue;
850
851 if( wxGetEnv( aKey, &envValue ) == true && !envValue.IsEmpty() )
852 {
853 m_Env.vars[aKey].SetValue( envValue );
854 m_Env.vars[aKey].SetDefinedExternally();
855 wxLogTrace( traceEnvVars,
856 wxS( "InitializeEnvironment: Entry %s defined externally as %s" ), aKey,
857 envValue );
858 }
859 else
860 {
861 wxLogTrace( traceEnvVars, wxS( "InitializeEnvironment: Setting entry %s to "
862 "default %s" ),
863 aKey, aDefault );
864 }
865 };
866
867 addVar( ENV_VAR::GetVersionedEnvVarName( wxS( "FOOTPRINT_DIR" ) ), PATHS::GetStockFootprintsPath() );
868 addVar( ENV_VAR::GetVersionedEnvVarName( wxS( "3DMODEL_DIR" ) ), PATHS::GetStock3dmodelsPath() );
869 addVar( ENV_VAR::GetVersionedEnvVarName( wxS( "TEMPLATE_DIR" ) ), PATHS::GetStockTemplatesPath() );
870 addVar( wxT( "KICAD_USER_TEMPLATE_DIR" ), PATHS::GetUserTemplatesPath() );
871 addVar( ENV_VAR::GetVersionedEnvVarName( wxS( "3RD_PARTY" ) ), PATHS::GetDefault3rdPartyPath() );
872 addVar( ENV_VAR::GetVersionedEnvVarName( wxS( "SYMBOL_DIR" ) ), PATHS::GetStockSymbolsPath() );
873 addVar( ENV_VAR::GetVersionedEnvVarName( wxS( "DESIGN_BLOCK_DIR" ) ), PATHS::GetStockDesignBlocksPath() );
874}
875
876
878 std::vector<LEGACY_3D_SEARCH_PATH>& aSearchPaths )
879{
880 wxFileName cfgpath( path );
881
882 // This should be the same as wxWidgets 3.0 wxPATH_NORM_ALL which is deprecated in 3.1.
883 // There are known issues with environment variable expansion so maybe we should be using
884 // our own ExpandEnvVarSubstitutions() here instead.
885 cfgpath.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS );
886 wxString cfgname = cfgpath.GetFullPath();
887
888 std::ifstream cfgFile;
889 std::string cfgLine;
890
891 if( !wxFileName::Exists( cfgname ) )
892 {
893 std::ostringstream ostr;
894 ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
895 wxString errmsg = "no 3D configuration file";
896 ostr << " * " << errmsg.ToUTF8() << " '";
897 ostr << cfgname.ToUTF8() << "'";
898 wxLogTrace( traceSettings, "%s\n", ostr.str().c_str() );
899 return false;
900 }
901
902 cfgFile.open( cfgname.ToUTF8() );
903
904 if( !cfgFile.is_open() )
905 {
906 std::ostringstream ostr;
907 ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
908 wxString errmsg = wxS( "Could not open configuration file" );
909 ostr << " * " << errmsg.ToUTF8() << " '" << cfgname.ToUTF8() << "'";
910 wxLogTrace( traceSettings, wxS( "%s\n" ), ostr.str().c_str() );
911 return false;
912 }
913
914 int lineno = 0;
916 size_t idx;
917 int vnum = 0; // version number
918
919 while( cfgFile.good() )
920 {
921 cfgLine.clear();
922 std::getline( cfgFile, cfgLine );
923 ++lineno;
924
925 if( cfgLine.empty() )
926 {
927 if( cfgFile.eof() )
928 break;
929
930 continue;
931 }
932
933 if( 1 == lineno && cfgLine.compare( 0, 2, "#V" ) == 0 )
934 {
935 // extract the version number and parse accordingly
936 if( cfgLine.size() > 2 )
937 {
938 std::istringstream istr;
939 istr.str( cfgLine.substr( 2 ) );
940 istr >> vnum;
941 }
942
943 continue;
944 }
945
946 idx = 0;
947
948 if( !getLegacy3DHollerith( cfgLine, idx, al.m_Alias ) )
949 continue;
950
951 // Don't add KICADn_3DMODEL_DIR, one of its legacy equivalents, or KIPRJMOD from a
952 // config file. They're system variables which are defined at runtime.
953 wxString versionedPath = wxString::Format( wxS( "${%s}" ),
954 ENV_VAR::GetVersionedEnvVarName( wxS( "3DMODEL_DIR" ) ) );
955
956 if( al.m_Alias == versionedPath || al.m_Alias == wxS( "${KIPRJMOD}" )
957 || al.m_Alias == wxS( "$(KIPRJMOD)" ) || al.m_Alias == wxS( "${KISYS3DMOD}" )
958 || al.m_Alias == wxS( "$(KISYS3DMOD)" ) )
959 {
960 continue;
961 }
962
963 if( !getLegacy3DHollerith( cfgLine, idx, al.m_Pathvar ) )
964 continue;
965
966 if( !getLegacy3DHollerith( cfgLine, idx, al.m_Description ) )
967 continue;
968
969 aSearchPaths.push_back( al );
970 }
971
972 cfgFile.close();
973
974 return true;
975}
976
977
978bool COMMON_SETTINGS::getLegacy3DHollerith( const std::string& aString, size_t& aIndex,
979 wxString& aResult )
980{
981 aResult.clear();
982
983 if( aIndex >= aString.size() )
984 {
985 std::ostringstream ostr;
986 ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
987 wxString errmsg = wxS( "bad Hollerith string on line" );
988 ostr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'";
989 wxLogTrace( traceSettings, wxS( "%s\n" ), ostr.str().c_str() );
990
991 return false;
992 }
993
994 size_t i2 = aString.find( '"', aIndex );
995
996 if( std::string::npos == i2 )
997 {
998 std::ostringstream ostr;
999 ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
1000 wxString errmsg = wxS( "missing opening quote mark in config file" );
1001 ostr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'";
1002 wxLogTrace( traceSettings, wxS( "%s\n" ), ostr.str().c_str() );
1003
1004 return false;
1005 }
1006
1007 ++i2;
1008
1009 if( i2 >= aString.size() )
1010 {
1011 std::ostringstream ostr;
1012 ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
1013 wxString errmsg = wxS( "invalid entry (unexpected end of line)" );
1014 ostr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'";
1015 wxLogTrace( traceSettings, wxS( "%s\n" ), ostr.str().c_str() );
1016
1017 return false;
1018 }
1019
1020 std::string tnum;
1021
1022 while( aString[i2] >= '0' && aString[i2] <= '9' )
1023 tnum.append( 1, aString[i2++] );
1024
1025 if( tnum.empty() || aString[i2++] != ':' )
1026 {
1027 std::ostringstream ostr;
1028 ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
1029 wxString errmsg = wxS( "bad Hollerith string on line" );
1030 ostr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'";
1031 wxLogTrace( traceSettings, wxS( "%s\n" ), ostr.str().c_str() );
1032
1033 return false;
1034 }
1035
1036 std::istringstream istr;
1037 istr.str( tnum );
1038 size_t nchars;
1039 istr >> nchars;
1040
1041 if( ( i2 + nchars ) >= aString.size() )
1042 {
1043 std::ostringstream ostr;
1044 ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
1045 wxString errmsg = wxS( "invalid entry (unexpected end of line)" );
1046 ostr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'";
1047 wxLogTrace( traceSettings, wxS( "%s\n" ), ostr.str().c_str() );
1048
1049 return false;
1050 }
1051
1052 if( nchars > 0 )
1053 {
1054 aResult = wxString::FromUTF8( aString.substr( i2, nchars ).c_str() );
1055 i2 += nchars;
1056 }
1057
1058 if( i2 >= aString.size() || aString[i2] != '"' )
1059 {
1060 std::ostringstream ostr;
1061 ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
1062 wxString errmsg = wxS( "missing closing quote mark in config file" );
1063 ostr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'";
1064 wxLogTrace( traceSettings, wxS( "%s\n" ), ostr.str().c_str() );
1065
1066 return false;
1067 }
1068
1069 aIndex = i2 + 1;
1070 return true;
1071}
int index
SPACEMOUSE m_SpaceMouse
std::unique_ptr< COMMON_SETTINGS_INTERNALS > m_csInternals
APPEARANCE m_Appearance
virtual ~COMMON_SETTINGS()
static bool getLegacy3DHollerith(const std::string &aString, size_t &aIndex, wxString &aResult)
bool readLegacy3DResolverCfg(const wxString &aPath, std::vector< LEGACY_3D_SEARCH_PATH > &aSearchPaths)
PACKAGE_MANAGER m_PackageManager
void InitializeEnvironment()
Creates the built-in environment variables and sets their default values.
AUTO_BACKUP m_Backup
DO_NOT_SHOW_AGAIN m_DoNotShowAgain
virtual bool MigrateFromLegacy(wxConfigBase *aLegacyConfig) override
Migrates from wxConfig to JSON-based configuration.
@ GAL_TYPE_OPENGL
OpenGL implementation.
KiCad uses environment variables internally for determining the base paths for libraries,...
bool fromLegacyString(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy wxConfig string value to a given JSON pointer value.
bool fromLegacy(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy wxConfig value to a given JSON pointer value.
void Set(const std::string &aPath, ValueType aVal)
Stores a value into the JSON document Will throw an exception if ValueType isn't something that the l...
bool Contains(const std::string &aPath) const
std::optional< ValueType > Get(const std::string &aPath) const
Fetches a value from within the JSON document.
std::vector< PARAM_BASE * > m_params
The list of parameters (owned by this object)
JSON_SETTINGS(const wxString &aFilename, SETTINGS_LOC aLocation, int aSchemaVersion)
std::unique_ptr< JSON_SETTINGS_INTERNALS > m_internals
Stores an enum as an integer.
Definition parameters.h:230
Like a normal param, but with custom getter and setter functions.
Definition parameters.h:297
static wxString GetStockSymbolsPath()
Gets the stock (install) symbols path.
Definition paths.cpp:300
static wxString GetUserTemplatesPath()
Gets the user path for custom templates.
Definition paths.cpp:71
static wxString GetDefault3rdPartyPath()
Gets the default path for PCM packages.
Definition paths.cpp:126
static wxString GetStock3dmodelsPath()
Gets the stock (install) 3dmodels path.
Definition paths.cpp:333
static wxString GetStockTemplatesPath()
Gets the stock (install) templates path.
Definition paths.cpp:355
static wxString GetUserSettingsPath()
Return the user configuration path used to store KiCad's configuration files.
Definition paths.cpp:634
static wxString GetStockDesignBlocksPath()
Gets the stock (install) footprints path.
Definition paths.cpp:322
static wxString GetStockFootprintsPath()
Gets the stock (install) footprints path.
Definition paths.cpp:311
const int commonSchemaVersion
! Update the schema version whenever a migration is required
const wxRegEx versionedEnvVarRegex(wxS("KICAD[0-9]+_[A-Z0-9_]+(_DIR)?"))
! The following environment variables will never be migrated from a previous version
@ ZIP
Zip archive snapshots; autosave uses recovery files.
@ INCREMENTAL
Git-based local history (default)
@ USER_DIR
Under the KiCad user data directory.
@ PROJECT_DIR
Inside the project directory (default)
Functions related to environment variables, including help functions.
const wxChar *const traceEnvVars
Flag to enable debug output of environment variable operations.
SETTINGS_LOC
@ USER
The main config directory (e.g. ~/.config/kicad/)
#define traceSettings
KICOMMON_API wxString GetVersionedEnvVarName(const wxString &aBaseName)
Construct a versioned environment variable based on this KiCad major version.
Definition env_vars.cpp:78
STL namespace.
System directories search utilities.
std::string path
wxLogTrace helper definitions.
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().
Definition wx_filename.h:35