KiCad PCB EDA Suite
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 (C) 2020-2021 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 
21 #include <set>
22 
23 #include <paths.h>
24 #include <search_stack.h>
27 #include <settings/parameters.h>
28 #include <systemdirsappend.h>
29 #include <trace_helpers.h>
30 #include <wx/config.h>
31 #include <wx/log.h>
32 
33 
35 const std::set<wxString> envVarBlacklist =
36  {
37  wxT( "KICAD6_SYMBOL_DIR" ),
38  wxT( "KICAD6_FOOTPRINT_DIR" ),
39  wxT( "KICAD6_TEMPLATES_DIR" ),
40  wxT( "KICAD6_3DMODEL_DIR" )
41  };
42 
43 
45 const int commonSchemaVersion = 2;
46 
49  m_Appearance(),
50  m_Backup(),
51  m_Env(),
52  m_Input(),
53  m_Graphics(),
54  m_Session(),
55  m_System(),
56  m_NetclassPanel()
57 {
58  /*
59  * Automatic dark mode detection works fine on Mac.
60  */
61 #if defined( __WXGTK__ ) || defined( __WXMSW__ )
62  m_params.emplace_back( new PARAM_ENUM<ICON_THEME>( "appearance.icon_theme",
64 #else
66 #endif
67 
68  /*
69  * Automatic icon scaling works fine on Mac. It works mostly fine on MSW, but perhaps not
70  * uniformly enough to exclude the explicit controls there.
71  */
72 #if defined( __WXGTK__ ) || defined( __WXMSW__ )
73  m_params.emplace_back( new PARAM<int>( "appearance.icon_scale",
74  &m_Appearance.icon_scale, 0 ) );
75 #else
77 #endif
78 
79  /*
80  * Automatic canvas scaling works fine on all supported platforms, so it's no longer exposed as
81  * a configuration option.
82  */
84 
85  /*
86  * Menu icons are off by default on OSX and on for all other platforms.
87  */
88 #ifdef __WXMAC__
89  m_params.emplace_back( new PARAM<bool>( "appearance.use_icons_in_menus",
90  &m_Appearance.use_icons_in_menus, false ) );
91 #else
92  m_params.emplace_back( new PARAM<bool>( "appearance.use_icons_in_menus",
94 #endif
95 
96  /*
97  * Font scaling hacks are only needed on GTK under wxWidgets 3.0.
98  */
99 #if defined( __WXGTK__ ) && !wxCHECK_VERSION( 3, 1, 0 )
100  m_params.emplace_back( new PARAM<bool>( "appearance.apply_icon_scale_to_fonts",
102 #else
104 #endif
105 
106  m_params.emplace_back( new PARAM<int>( "appearance.text_editor_zoom",
108 
109  m_params.emplace_back( new PARAM<bool>( "auto_backup.enabled", &m_Backup.enabled, true ) );
110 
111  m_params.emplace_back( new PARAM<bool>( "auto_backup.backup_on_autosave",
112  &m_Backup.backup_on_autosave, false ) );
113 
114  m_params.emplace_back( new PARAM<int>( "auto_backup.limit_total_files",
115  &m_Backup.limit_total_files, 25 ) );
116 
117  m_params.emplace_back( new PARAM<unsigned long long>( "auto_backup.limit_total_size",
118  &m_Backup.limit_total_size, 104857600 ) );
119 
120  m_params.emplace_back( new PARAM<int>( "auto_backup.limit_daily_files",
121  &m_Backup.limit_daily_files, 5 ) );
122 
123  m_params.emplace_back( new PARAM<int>( "auto_backup.min_interval",
124  &m_Backup.min_interval, 300 ) );
125 
126  m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "environment.vars",
127  [&]() -> nlohmann::json
128  {
129  nlohmann::json ret = {};
130 
131  for( const std::pair<wxString, ENV_VAR_ITEM> entry : m_Env.vars )
132  {
133  const ENV_VAR_ITEM& var = entry.second;
134 
135  wxASSERT( entry.first == var.GetKey() );
136 
137  // Default values are never persisted
138  if( var.IsDefault() )
139  {
140  wxLogTrace( traceEnvVars,
141  "COMMON_SETTINGS: Env var %s skipping save (default)",
142  var.GetKey() );
143  continue;
144  }
145 
146  wxString value = var.GetValue();
147 
148  // Vars that existed in JSON are persisted, but if they were overridden
149  // externally, we persist the old value (i.e. the one that was loaded from JSON)
150  if( var.GetDefinedExternally() )
151  {
152  if( var.GetDefinedInSettings() )
153  {
154  wxLogTrace( traceEnvVars,
155  "COMMON_SETTINGS: Env var %s was overridden externally, "
156  "saving previously-loaded value %s",
157  var.GetKey(), var.GetSettingsValue() );
158  value = var.GetSettingsValue();
159  }
160  else
161  {
162  wxLogTrace( traceEnvVars,
163  "COMMON_SETTINGS: Env var %s skipping save (external)",
164  var.GetKey() );
165  continue;
166  }
167  }
168 
169  wxLogTrace( traceEnvVars,
170  "COMMON_SETTINGS: Saving env var %s = %s",
171  var.GetKey(), value);
172 
173  std::string key( var.GetKey().ToUTF8() );
174  ret[key] = value;
175  }
176 
177  return ret;
178  },
179  [&]( const nlohmann::json& aJson )
180  {
181  if( !aJson.is_object() )
182  return;
183 
184  for( const auto& entry : aJson.items() )
185  {
186  wxString key = wxString( entry.key().c_str(), wxConvUTF8 );
187  wxString val = entry.value().get<wxString>();
188 
189  if( m_Env.vars.count( key ) )
190  {
191  if( m_Env.vars[key].GetDefinedExternally() )
192  {
193  wxLogTrace( traceEnvVars, "COMMON_SETTINGS: %s is defined externally",
194  key );
195  m_Env.vars[key].SetDefinedInSettings();
196  m_Env.vars[key].SetSettingsValue( val );
197  continue;
198  }
199  else
200  {
201  wxLogTrace( traceEnvVars, "COMMON_SETTINGS: Updating %s: %s -> %s",
202  key, m_Env.vars[key].GetValue(), val );
203  m_Env.vars[key].SetValue( val );
204  }
205  }
206  else
207  {
208  wxLogTrace( traceEnvVars, "COMMON_SETTINGS: Loaded new var: %s = %s",
209  key, val );
210  m_Env.vars[key] = ENV_VAR_ITEM( key, val );
211  }
212 
213  m_Env.vars[key].SetDefinedInSettings();
214  m_Env.vars[key].SetSettingsValue( val );
215  }
216  },
217  {} ) );
218 
219  m_params.emplace_back( new PARAM<bool>( "input.auto_pan", &m_Input.auto_pan, false ) );
220 
221  m_params.emplace_back( new PARAM<int>( "input.auto_pan_acceleration",
222  &m_Input.auto_pan_acceleration, 5 ) );
223 
224  m_params.emplace_back( new PARAM<bool>( "input.center_on_zoom",
225  &m_Input.center_on_zoom, true ) );
226 
227  m_params.emplace_back( new PARAM<bool>( "input.immediate_actions",
228  &m_Input.immediate_actions, true ) );
229 
230  m_params.emplace_back( new PARAM<bool>( "input.warp_mouse_on_move",
231  &m_Input.warp_mouse_on_move, true ) );
232 
233  m_params.emplace_back( new PARAM<bool>( "input.horizontal_pan",
234  &m_Input.horizontal_pan, false ) );
235 
236  m_params.emplace_back( new PARAM<bool>( "input.zoom_acceleration",
237  &m_Input.zoom_acceleration, false ) );
238 
239 #ifdef __WXMAC__
240  int default_zoom_speed = 5;
241 #else
242  int default_zoom_speed = 1;
243 #endif
244 
245  m_params.emplace_back( new PARAM<int>( "input.zoom_speed",
246  &m_Input.zoom_speed, default_zoom_speed ) );
247 
248  m_params.emplace_back( new PARAM<bool>( "input.zoom_speed_auto",
249  &m_Input.zoom_speed_auto, true ) );
250 
251  m_params.emplace_back( new PARAM<int>( "input.scroll_modifier_zoom",
252  &m_Input.scroll_modifier_zoom, 0 ) );
253 
254  m_params.emplace_back( new PARAM<int>( "input.scroll_modifier_pan_h",
255  &m_Input.scroll_modifier_pan_h, WXK_CONTROL ) );
256 
257  m_params.emplace_back( new PARAM<int>( "input.scroll_modifier_pan_v",
258  &m_Input.scroll_modifier_pan_v, WXK_SHIFT ) );
259 
260  m_params.emplace_back( new PARAM_ENUM<MOUSE_DRAG_ACTION>( "input.mouse_left",
263 
264  m_params.emplace_back( new PARAM_ENUM<MOUSE_DRAG_ACTION>( "input.mouse_middle",
265  &m_Input.drag_middle, MOUSE_DRAG_ACTION::PAN, MOUSE_DRAG_ACTION::SELECT,
267 
268  m_params.emplace_back( new PARAM_ENUM<MOUSE_DRAG_ACTION>( "input.mouse_right",
271 
272  m_params.emplace_back( new PARAM<int>( "graphics.opengl_antialiasing_mode",
273  &m_Graphics.opengl_aa_mode, 0, 0, 2 ) );
274 
275  m_params.emplace_back( new PARAM<int>( "graphics.cairo_antialiasing_mode",
276  &m_Graphics.cairo_aa_mode, 0, 0, 2 ) );
277 
278  m_params.emplace_back( new PARAM<int>( "system.autosave_interval",
279  &m_System.autosave_interval, 600 ) );
280 
281 #ifdef __WXMAC__
282  m_params.emplace_back( new PARAM<wxString>( "system.text_editor",
283  &m_System.text_editor, "/usr/bin/open -e" ) );
284 #else
285  m_params.emplace_back( new PARAM<wxString>( "system.text_editor",
286  &m_System.text_editor, "" ) );
287 #endif
288 
289  m_params.emplace_back( new PARAM<int>( "system.file_history_size",
290  &m_System.file_history_size, 9 ) );
291 
292  m_params.emplace_back( new PARAM<wxString>( "system.language",
293  &m_System.language, "Default" ) );
294 
295  m_params.emplace_back( new PARAM<wxString>( "system.pdf_viewer_name",
296  &m_System.pdf_viewer_name, "" ) );
297 
298  m_params.emplace_back( new PARAM<bool>( "system.use_system_pdf_viewer",
299  &m_System.use_system_pdf_viewer, true ) );
300 
301  m_params.emplace_back( new PARAM<wxString>( "system.working_dir",
302  &m_System.working_dir, "" ) );
303 
304  m_params.emplace_back( new PARAM<int>( "system.clear_3d_cache_interval",
305  &m_System.clear_3d_cache_interval, 30 ) );
306 
307  m_params.emplace_back( new PARAM<bool>( "do_not_show_again.zone_fill_warning",
308  &m_DoNotShowAgain.zone_fill_warning, false ) );
309 
310  m_params.emplace_back( new PARAM<bool>( "do_not_show_again.env_var_overwrite_warning",
311  &m_DoNotShowAgain.env_var_overwrite_warning, false ) );
312 
313  m_params.emplace_back( new PARAM<bool>( "do_not_show_again.scaled_3d_models_warning",
314  &m_DoNotShowAgain.scaled_3d_models_warning, false ) );
315 
316  m_params.emplace_back( new PARAM<bool>( "session.remember_open_files",
317  &m_Session.remember_open_files, false ) );
318 
319  m_params.emplace_back( new PARAM<int>( "netclass_panel.sash_pos",
320  &m_NetclassPanel.sash_pos, 160 ) );
321 
322  m_params.emplace_back( new PARAM<int>( "package_manager.sash_pos",
323  &m_PackageManager.sash_pos, 380 ) );
324 
325  registerMigration( 0, 1, std::bind( &COMMON_SETTINGS::migrateSchema0to1, this ) );
326  registerMigration( 1, 2, std::bind( &COMMON_SETTINGS::migrateSchema1to2, this ) );
327 }
328 
329 
331 {
338  nlohmann::json::json_pointer mwp_pointer( "/input/mousewheel_pan"_json_pointer );
339 
340  bool mwp = false;
341 
342  try
343  {
344  mwp = m_internals->at( mwp_pointer );
345  m_internals->At( "input" ).erase( "mousewheel_pan" );
346  }
347  catch( ... )
348  {
349  wxLogTrace( traceSettings, wxT( "COMMON_SETTINGS::Migrate 0->1: mousewheel_pan not found" ) );
350  }
351 
352  if( mwp )
353  {
354  ( *m_internals )[nlohmann::json::json_pointer( "/input/horizontal_pan" )] = true;
355 
356  ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_h" )] = WXK_SHIFT;
357  ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_v" )] = 0;
358  ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_zoom" )] = WXK_CONTROL;
359  }
360  else
361  {
362  ( *m_internals )[nlohmann::json::json_pointer( "/input/horizontal_pan" )] = false;
363 
364  ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_h" )] = WXK_CONTROL;
365  ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_v" )] = WXK_SHIFT;
366  ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_zoom" )] = 0;
367  }
368 
369  return true;
370 }
371 
372 
374 {
375  nlohmann::json::json_pointer v1_pointer( "/input/prefer_select_to_drag"_json_pointer );
376 
377  bool prefer_selection = false;
378 
379  try
380  {
381  prefer_selection = m_internals->at( v1_pointer );
382  m_internals->at( nlohmann::json::json_pointer( "/input"_json_pointer ) ).erase( "prefer_select_to_drag" );
383  }
384  catch( ... )
385  {
386  wxLogTrace( traceSettings, wxT( "COMMON_SETTINGS::Migrate 1->2: prefer_select_to_drag not found" ) );
387  }
388 
389  if( prefer_selection )
390  ( *m_internals )[nlohmann::json::json_pointer( "/input/mouse_left" )] = MOUSE_DRAG_ACTION::SELECT;
391  else
392  ( *m_internals )[nlohmann::json::json_pointer( "/input/mouse_left" )] = MOUSE_DRAG_ACTION::DRAG_ANY;
393 
394  return true;
395 }
396 
397 
398 bool COMMON_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg )
399 {
400  bool ret = true;
401 
402  ret &= fromLegacy<double>( aCfg, "CanvasScale", "appearance.canvas_scale" );
403  ret &= fromLegacy<int>( aCfg, "IconScale", "appearance.icon_scale" );
404  ret &= fromLegacy<bool>( aCfg, "UseIconsInMenus", "appearance.use_icons_in_menus" );
405  ret &= fromLegacy<bool>( aCfg, "ShowEnvVarWarningDialog", "environment.show_warning_dialog" );
406 
407  auto load_env_vars =
408  [&]()
409  {
410  wxString key, value;
411  long index = 0;
412  nlohmann::json::json_pointer ptr = m_internals->PointerFromString( "environment.vars" );
413 
414  aCfg->SetPath( "EnvironmentVariables" );
415  ( *m_internals )[ptr] = nlohmann::json( {} );
416 
417  while( aCfg->GetNextEntry( key, index ) )
418  {
419  if( envVarBlacklist.count( key ) )
420  {
421  wxLogTrace( traceSettings, wxT( "Migrate Env: %s is blacklisted; skipping." ), key );
422  continue;
423  }
424 
425  value = aCfg->Read( key, wxEmptyString );
426 
427  if( !value.IsEmpty() )
428  {
429  ptr.push_back( key.ToStdString() );
430 
431  wxLogTrace( traceSettings, wxT( "Migrate Env: %s=%s" ), ptr.to_string(), value );
432  ( *m_internals )[ptr] = value.ToUTF8();
433 
434  ptr.pop_back();
435  }
436  }
437 
438  aCfg->SetPath( ".." );
439  };
440 
441  load_env_vars();
442 
443  bool mousewheel_pan = false;
444 
445  if( aCfg->Read( "MousewheelPAN", &mousewheel_pan ) && mousewheel_pan )
446  {
447  Set( "input.horizontal_pan", true );
448  Set( "input.scroll_modifier_pan_h", static_cast<int>( WXK_SHIFT ) );
449  Set( "input.scroll_modifier_pan_v", 0 );
450  Set( "input.scroll_modifier_zoom", static_cast<int>( WXK_CONTROL ) );
451  }
452 
453  ret &= fromLegacy<bool>( aCfg, "AutoPAN", "input.auto_pan" );
454  ret &= fromLegacy<bool>( aCfg, "ImmediateActions", "input.immediate_actions" );
455  ret &= fromLegacy<bool>( aCfg, "PreferSelectionToDragging", "input.prefer_select_to_drag" );
456  ret &= fromLegacy<bool>( aCfg, "MoveWarpsCursor", "input.warp_mouse_on_move" );
457  ret &= fromLegacy<bool>( aCfg, "ZoomNoCenter", "input.center_on_zoom" );
458 
459  // This was stored inverted in legacy config
460  if( OPT<bool> value = Get<bool>( "input.center_on_zoom" ) )
461  Set( "input.center_on_zoom", !( *value ) );
462 
463  ret &= fromLegacy<int>( aCfg, "OpenGLAntialiasingMode", "graphics.opengl_antialiasing_mode" );
464  ret &= fromLegacy<int>( aCfg, "CairoAntialiasingMode", "graphics.cairo_antialiasing_mode" );
465 
466  ret &= fromLegacy<int>( aCfg, "AutoSaveInterval", "system.autosave_interval" );
467  ret &= fromLegacyString( aCfg, "Editor", "system.editor_name" );
468  ret &= fromLegacy<int>( aCfg, "FileHistorySize", "system.file_history_size" );
469  ret &= fromLegacyString( aCfg, "LanguageID", "system.language" );
470  ret &= fromLegacyString( aCfg, "PdfBrowserName", "system.pdf_viewer_name" );
471  ret &= fromLegacy<bool>( aCfg, "UseSystemBrowser", "system.use_system_pdf_viewer" );
472  ret &= fromLegacyString( aCfg, "WorkingDir", "system.working_dir" );
473 
474  return ret;
475 }
476 
477 
479 {
480  auto addVar =
481  [&]( const wxString& aKey, const wxString& aDefault )
482  {
483  m_Env.vars[aKey] = ENV_VAR_ITEM( aKey, aDefault, aDefault );
484 
485  wxString envValue;
486 
487  if( wxGetEnv( aKey, &envValue ) == true && !envValue.IsEmpty() )
488  {
489  m_Env.vars[aKey].SetValue( envValue );
490  m_Env.vars[aKey].SetDefinedExternally();
491  wxLogTrace( traceEnvVars,
492  "InitializeEnvironment: Entry %s defined externally as %s", aKey,
493  envValue );
494  }
495  else
496  {
497  wxLogTrace( traceEnvVars, "InitializeEnvironment: Setting entry %s to default %s",
498  aKey, aDefault );
499  }
500  };
501 
502  wxFileName basePath( PATHS::GetStockEDALibraryPath(), wxEmptyString );
503 
504  wxFileName path( basePath );
505  path.AppendDir( wxT( "footprints" ) );
506  addVar( wxT( "KICAD6_FOOTPRINT_DIR" ), path.GetFullPath() );
507 
508  path = basePath;
509  path.AppendDir( wxT( "3dmodels" ) );
510  addVar( wxT( "KICAD6_3DMODEL_DIR" ), path.GetFullPath() );
511 
512  addVar( wxT( "KICAD6_TEMPLATE_DIR" ), PATHS::GetStockTemplatesPath() );
513 
514  addVar( wxT( "KICAD_USER_TEMPLATE_DIR" ), PATHS::GetUserTemplatesPath() );
515 
516  addVar( wxT( "KICAD6_3RD_PARTY" ), PATHS::GetDefault3rdPartyPath() );
517 
518  path = basePath;
519  path.AppendDir( wxT( "symbols" ) );
520  addVar( wxT( "KICAD6_SYMBOL_DIR" ), path.GetFullPath() );
521 }
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...
std::vector< PARAM_BASE * > m_params
The list of parameters (owned by this object)
unsigned long long limit_total_size
Maximum total size of backups (bytes), 0 for unlimited.
KiCad uses environment variables internally for determining the base paths for libraries,...
const int commonSchemaVersion
! Update the schema version whenever a migration is required
bool enabled
Automatically back up the project when files are saved.
SETTINGS_LOC
Definition: json_settings.h:46
AUTO_BACKUP m_Backup
System directories search utilities.
APPEARANCE m_Appearance
Stores an enum as an integer.
Definition: parameters.h:215
virtual bool MigrateFromLegacy(wxConfigBase *aLegacyConfig) override
Migrates from wxConfig to JSON-based configuration.
nlohmann::json json
Definition: gerbview.cpp:41
static wxString GetStockTemplatesPath()
Gets the stock (install) templates path.
Definition: paths.cpp:242
void InitializeEnvironment()
Creates the built-in environment variables and sets their default values.
std::unique_ptr< JSON_SETTINGS_INTERNALS > m_internals
static wxString GetUserTemplatesPath()
Gets the user path for custom templates.
Definition: paths.cpp:86
wxLogTrace helper definitions.
ENVIRONMENT m_Env
const wxChar *const traceEnvVars
Flag to enable debug output of environment variable operations.
int min_interval
Minimum time, in seconds, between subsequent backups.
bool fromLegacyString(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy wxConfig string value to a given JSON pointer value.
const std::set< wxString > envVarBlacklist
! The following environment variables will never be migrated from a previous version
The main config directory (e.g. ~/.config/kicad/)
static wxString GetStockEDALibraryPath()
Gets the stock (install) EDA library data path, which is the base path for templates,...
Definition: paths.cpp:186
boost::optional< T > OPT
Definition: optional.h:7
bool backup_on_autosave
Trigger a backup on autosave.
int limit_daily_files
Maximum files to keep per day, 0 for unlimited.
const wxChar *const traceSettings
Flag to enable debug output of settings operations and management.
static wxString GetDefault3rdPartyPath()
Gets the default path for PCM packages.
Definition: paths.cpp:129
int limit_total_files
Maximum number of backup archives to retain.