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 <jon@craftyjon.com>
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  // This only effect the first time KiCad is run. The user's setting will be used for all
59  // subsequent runs.
60  // Menu icons are off by default on OSX and on for all other platforms.
61  // Use automatic canvas scaling on OSX, but not on the other platforms (their detection
62  // isn't as good).
63 #if defined( __WXMAC__ )
64  bool defaultUseIconsInMenus = false;
65  double canvasScale = 0.0;
66 #else
67  bool defaultUseIconsInMenus = true;
68  double canvasScale = 1.0;
69 #endif
70 
71  m_params.emplace_back( new PARAM<double>( "appearance.canvas_scale",
72  &m_Appearance.canvas_scale, canvasScale ) );
73 
74  m_params.emplace_back( new PARAM<int>( "appearance.icon_scale",
75  &m_Appearance.icon_scale, 0 ) );
76 
77  m_params.emplace_back( new PARAM_ENUM<ICON_THEME>( "appearance.icon_theme",
79 
80  m_params.emplace_back( new PARAM<bool>( "appearance.use_icons_in_menus",
81  &m_Appearance.use_icons_in_menus, defaultUseIconsInMenus ) );
82 
83  m_params.emplace_back( new PARAM<bool>( "auto_backup.enabled", &m_Backup.enabled, true ) );
84 
85  m_params.emplace_back( new PARAM<bool>( "auto_backup.backup_on_autosave",
86  &m_Backup.backup_on_autosave, false ) );
87 
88  m_params.emplace_back( new PARAM<int>( "auto_backup.limit_total_files",
89  &m_Backup.limit_total_files, 25 ) );
90 
91  m_params.emplace_back( new PARAM<unsigned long long>( "auto_backup.limit_total_size",
92  &m_Backup.limit_total_size, 104857600 ) );
93 
94  m_params.emplace_back( new PARAM<int>( "auto_backup.limit_daily_files",
96 
97  m_params.emplace_back( new PARAM<int>( "auto_backup.min_interval",
98  &m_Backup.min_interval, 300 ) );
99 
100  m_params.emplace_back( new PARAM<bool>( "environment.show_warning_dialog",
101  &m_Env.show_warning_dialog, false ) );
102 
103  m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "environment.vars",
104  [&]() -> nlohmann::json
105  {
106  nlohmann::json ret = {};
107 
108  for( const std::pair<wxString, ENV_VAR_ITEM> entry : m_Env.vars )
109  {
110  const ENV_VAR_ITEM& var = entry.second;
111 
112  wxASSERT( entry.first == var.GetKey() );
113 
114  // Default values are never persisted
115  if( var.IsDefault() )
116  {
117  wxLogTrace( traceEnvVars,
118  "COMMON_SETTINGS: Env var %s skipping save (default)",
119  var.GetKey() );
120  continue;
121  }
122 
123  wxString value = var.GetValue();
124 
125  // Vars that existed in JSON are persisted, but if they were overridden
126  // externally, we persist the old value (i.e. the one that was loaded from JSON)
127  if( var.GetDefinedExternally() )
128  {
129  if( var.GetDefinedInSettings() )
130  {
131  wxLogTrace( traceEnvVars,
132  "COMMON_SETTINGS: Env var %s was overridden externally, "
133  "saving previously-loaded value %s",
134  var.GetKey(), var.GetSettingsValue() );
135  value = var.GetSettingsValue();
136  }
137  else
138  {
139  wxLogTrace( traceEnvVars,
140  "COMMON_SETTINGS: Env var %s skipping save (external)",
141  var.GetKey() );
142  continue;
143  }
144  }
145 
146  wxLogTrace( traceEnvVars,
147  "COMMON_SETTINGS: Saving env var %s = %s",
148  var.GetKey(), value);
149 
150  std::string key( var.GetKey().ToUTF8() );
151  ret[key] = value;
152  }
153 
154  return ret;
155  },
156  [&]( const nlohmann::json& aJson )
157  {
158  if( !aJson.is_object() )
159  return;
160 
161  for( const auto& entry : aJson.items() )
162  {
163  wxString key = wxString( entry.key().c_str(), wxConvUTF8 );
164  wxString val = entry.value().get<wxString>();
165 
166  if( m_Env.vars.count( key ) )
167  {
168  if( m_Env.vars[key].GetDefinedExternally() )
169  {
170  wxLogTrace( traceEnvVars, "COMMON_SETTINGS: %s is defined externally",
171  key );
172  m_Env.vars[key].SetDefinedInSettings();
173  m_Env.vars[key].SetSettingsValue( val );
174  continue;
175  }
176  else
177  {
178  wxLogTrace( traceEnvVars, "COMMON_SETTINGS: Updating %s: %s -> %s",
179  key, m_Env.vars[key].GetValue(), val );
180  m_Env.vars[key].SetValue( val );
181  }
182  }
183  else
184  {
185  wxLogTrace( traceEnvVars, "COMMON_SETTINGS: Loaded new var: %s = %s",
186  key, val );
187  m_Env.vars[key] = ENV_VAR_ITEM( key, val );
188  }
189 
190  m_Env.vars[key].SetDefinedInSettings();
191  m_Env.vars[key].SetSettingsValue( val );
192  }
193  },
194  {} ) );
195 
196  m_params.emplace_back( new PARAM<bool>( "input.auto_pan", &m_Input.auto_pan, false ) );
197 
198  m_params.emplace_back( new PARAM<int>( "input.auto_pan_acceleration",
199  &m_Input.auto_pan_acceleration, 5 ) );
200 
201  m_params.emplace_back( new PARAM<bool>( "input.center_on_zoom",
202  &m_Input.center_on_zoom, true ) );
203 
204  m_params.emplace_back( new PARAM<bool>( "input.immediate_actions",
205  &m_Input.immediate_actions, true ) );
206 
207  m_params.emplace_back( new PARAM<bool>( "input.warp_mouse_on_move",
208  &m_Input.warp_mouse_on_move, true ) );
209 
210  m_params.emplace_back( new PARAM<bool>( "input.horizontal_pan",
211  &m_Input.horizontal_pan, false ) );
212 
213  m_params.emplace_back( new PARAM<bool>( "input.zoom_acceleration",
214  &m_Input.zoom_acceleration, false ) );
215 
216 #ifdef __WXMAC__
217  int default_zoom_speed = 5;
218 #else
219  int default_zoom_speed = 1;
220 #endif
221 
222  m_params.emplace_back( new PARAM<int>( "input.zoom_speed",
223  &m_Input.zoom_speed, default_zoom_speed ) );
224 
225  m_params.emplace_back( new PARAM<bool>( "input.zoom_speed_auto",
226  &m_Input.zoom_speed_auto, true ) );
227 
228  m_params.emplace_back( new PARAM<int>( "input.scroll_modifier_zoom",
229  &m_Input.scroll_modifier_zoom, 0 ) );
230 
231  m_params.emplace_back( new PARAM<int>( "input.scroll_modifier_pan_h",
232  &m_Input.scroll_modifier_pan_h, WXK_CONTROL ) );
233 
234  m_params.emplace_back( new PARAM<int>( "input.scroll_modifier_pan_v",
235  &m_Input.scroll_modifier_pan_v, WXK_SHIFT ) );
236 
237  m_params.emplace_back( new PARAM_ENUM<MOUSE_DRAG_ACTION>( "input.mouse_left",
240 
241  m_params.emplace_back( new PARAM_ENUM<MOUSE_DRAG_ACTION>( "input.mouse_middle",
242  &m_Input.drag_middle, MOUSE_DRAG_ACTION::PAN, MOUSE_DRAG_ACTION::SELECT,
244 
245  m_params.emplace_back( new PARAM_ENUM<MOUSE_DRAG_ACTION>( "input.mouse_right",
248 
249  m_params.emplace_back( new PARAM<int>( "graphics.opengl_antialiasing_mode",
250  &m_Graphics.opengl_aa_mode, 0, 0, 2 ) );
251 
252  m_params.emplace_back( new PARAM<int>( "graphics.cairo_antialiasing_mode",
253  &m_Graphics.cairo_aa_mode, 0, 0, 2 ) );
254 
255  m_params.emplace_back( new PARAM<int>( "system.autosave_interval",
256  &m_System.autosave_interval, 600 ) );
257 
258  m_params.emplace_back( new PARAM<wxString>( "system.editor_name",
259  &m_System.editor_name, "" ) );
260 
261  m_params.emplace_back( new PARAM<int>( "system.file_history_size",
262  &m_System.file_history_size, 9 ) );
263 
264  m_params.emplace_back( new PARAM<wxString>( "system.language",
265  &m_System.language, "Default" ) );
266 
267  m_params.emplace_back( new PARAM<wxString>( "system.pdf_viewer_name",
268  &m_System.pdf_viewer_name, "" ) );
269 
270  m_params.emplace_back( new PARAM<bool>( "system.use_system_pdf_viewer",
271  &m_System.use_system_pdf_viewer, true ) );
272 
273  m_params.emplace_back( new PARAM<wxString>( "system.working_dir",
274  &m_System.working_dir, "" ) );
275 
276  m_params.emplace_back( new PARAM<int>( "system.clear_3d_cache_interval",
277  &m_System.clear_3d_cache_interval, 30 ) );
278 
279  m_params.emplace_back( new PARAM<bool>( "session.remember_open_files",
280  &m_Session.remember_open_files, false ) );
281 
282  m_params.emplace_back( new PARAM<int>( "netclass_panel.sash_pos",
283  &m_NetclassPanel.sash_pos, 160 ) );
284 
285  registerMigration( 0, 1, std::bind( &COMMON_SETTINGS::migrateSchema0to1, this ) );
286  registerMigration( 1, 2, std::bind( &COMMON_SETTINGS::migrateSchema1to2, this ) );
287 }
288 
289 
291 {
298  nlohmann::json::json_pointer mwp_pointer( "/input/mousewheel_pan"_json_pointer );
299 
300  bool mwp = false;
301 
302  try
303  {
304  mwp = m_internals->at( mwp_pointer );
305  m_internals->At( "input" ).erase( "mousewheel_pan" );
306  }
307  catch( ... )
308  {
309  wxLogTrace( traceSettings, "COMMON_SETTINGS::Migrate 0->1: mousewheel_pan not found" );
310  }
311 
312  if( mwp )
313  {
314  ( *m_internals )[nlohmann::json::json_pointer( "/input/horizontal_pan" )] = true;
315 
316  ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_h" )] = WXK_SHIFT;
317  ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_v" )] = 0;
318  ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_zoom" )] = WXK_CONTROL;
319  }
320  else
321  {
322  ( *m_internals )[nlohmann::json::json_pointer( "/input/horizontal_pan" )] = false;
323 
324  ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_h" )] = WXK_CONTROL;
325  ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_v" )] = WXK_SHIFT;
326  ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_zoom" )] = 0;
327  }
328 
329  return true;
330 }
331 
332 
334 {
335  nlohmann::json::json_pointer v1_pointer( "/input/prefer_select_to_drag"_json_pointer );
336 
337  bool prefer_selection = false;
338 
339  try
340  {
341  prefer_selection = m_internals->at( v1_pointer );
342  m_internals->at( nlohmann::json::json_pointer( "/input"_json_pointer ) ).erase( "prefer_select_to_drag" );
343  }
344  catch( ... )
345  {
346  wxLogTrace( traceSettings, "COMMON_SETTINGS::Migrate 1->2: prefer_select_to_drag not found" );
347  }
348 
349  if( prefer_selection )
350  ( *m_internals )[nlohmann::json::json_pointer( "/input/mouse_left" )] = MOUSE_DRAG_ACTION::SELECT;
351  else
352  ( *m_internals )[nlohmann::json::json_pointer( "/input/mouse_left" )] = MOUSE_DRAG_ACTION::DRAG_ANY;
353 
354  return true;
355 }
356 
357 
358 bool COMMON_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg )
359 {
360  bool ret = true;
361 
362  ret &= fromLegacy<double>( aCfg, "CanvasScale", "appearance.canvas_scale" );
363  ret &= fromLegacy<int>( aCfg, "IconScale", "appearance.icon_scale" );
364  ret &= fromLegacy<bool>( aCfg, "UseIconsInMenus", "appearance.use_icons_in_menus" );
365 
366 // Force OSX to automatically scale the canvas. Before v6, the user setting wasn't used on OSX
367 // and was set to 1.0. In v6, the setting is now used by OSX and should default to automatic
368 // scaling.
369 #ifdef __WXMAC__
370  Set( "appearance.canvas_scale", 0.0 );
371 #endif
372 
373  ret &= fromLegacy<bool>( aCfg, "ShowEnvVarWarningDialog", "environment.show_warning_dialog" );
374 
375  auto load_env_vars = [&] () {
376  wxString key, value;
377  long index = 0;
378  nlohmann::json::json_pointer ptr = m_internals->PointerFromString( "environment.vars" );
379 
380  aCfg->SetPath( "EnvironmentVariables" );
381  ( *m_internals )[ptr] = nlohmann::json( {} );
382 
383  while( aCfg->GetNextEntry( key, index ) )
384  {
385  if( envVarBlacklist.count( key ) )
386  {
387  wxLogTrace( traceSettings, "Migrate Env: %s is blacklisted; skipping.", key );
388  continue;
389  }
390 
391  value = aCfg->Read( key, wxEmptyString );
392 
393  if( !value.IsEmpty() )
394  {
395  ptr.push_back( key.ToStdString() );
396 
397  wxLogTrace( traceSettings, "Migrate Env: %s=%s", ptr.to_string(), value );
398  ( *m_internals )[ptr] = value.ToUTF8();
399 
400  ptr.pop_back();
401  }
402  }
403 
404  aCfg->SetPath( ".." );
405  };
406 
407  load_env_vars();
408 
409  bool mousewheel_pan = false;
410 
411  if( aCfg->Read( "MousewheelPAN", &mousewheel_pan ) && mousewheel_pan )
412  {
413  Set( "input.horizontal_pan", true );
414  Set( "input.scroll_modifier_pan_h", static_cast<int>( WXK_SHIFT ) );
415  Set( "input.scroll_modifier_pan_v", 0 );
416  Set( "input.scroll_modifier_zoom", static_cast<int>( WXK_CONTROL ) );
417  }
418 
419  ret &= fromLegacy<bool>( aCfg, "AutoPAN", "input.auto_pan" );
420  ret &= fromLegacy<bool>( aCfg, "ImmediateActions", "input.immediate_actions" );
421  ret &= fromLegacy<bool>( aCfg, "PreferSelectionToDragging", "input.prefer_select_to_drag" );
422  ret &= fromLegacy<bool>( aCfg, "MoveWarpsCursor", "input.warp_mouse_on_move" );
423  ret &= fromLegacy<bool>( aCfg, "ZoomNoCenter", "input.center_on_zoom" );
424 
425  // This was stored inverted in legacy config
426  if( OPT<bool> value = Get<bool>( "input.center_on_zoom" ) )
427  Set( "input.center_on_zoom", !( *value ) );
428 
429  ret &= fromLegacy<int>( aCfg, "OpenGLAntialiasingMode", "graphics.opengl_antialiasing_mode" );
430  ret &= fromLegacy<int>( aCfg, "CairoAntialiasingMode", "graphics.cairo_antialiasing_mode" );
431 
432  ret &= fromLegacy<int>( aCfg, "AutoSaveInterval", "system.autosave_interval" );
433  ret &= fromLegacyString( aCfg, "Editor", "system.editor_name" );
434  ret &= fromLegacy<int>( aCfg, "FileHistorySize", "system.file_history_size" );
435  ret &= fromLegacyString( aCfg, "LanguageID", "system.language" );
436  ret &= fromLegacyString( aCfg, "PdfBrowserName", "system.pdf_viewer_name" );
437  ret &= fromLegacy<bool>( aCfg, "UseSystemBrowser", "system.use_system_pdf_viewer" );
438  ret &= fromLegacyString( aCfg, "WorkingDir", "system.working_dir" );
439 
440  return ret;
441 }
442 
443 
445 {
446  auto addVar =
447  [&]( const wxString& aKey, const wxString& aDefault )
448  {
449  m_Env.vars[aKey] = ENV_VAR_ITEM( aKey, aDefault, aDefault );
450 
451  wxString envValue;
452 
453  if( wxGetEnv( aKey, &envValue ) == true && !envValue.IsEmpty() )
454  {
455  m_Env.vars[aKey].SetValue( envValue );
456  m_Env.vars[aKey].SetDefinedExternally();
457  wxLogTrace( traceEnvVars,
458  "InitializeEnvironment: Entry %s defined externally as %s", aKey,
459  envValue );
460  }
461  else
462  {
463  wxLogTrace( traceEnvVars, "InitializeEnvironment: Setting entry %s to default %s",
464  aKey, aDefault );
465  }
466  };
467 
468  wxFileName basePath( PATHS::GetStockEDALibraryPath(), wxEmptyString );
469 
470  wxFileName path( basePath );
471  path.AppendDir( wxT( "modules" ) );
472  addVar( wxT( "KICAD6_FOOTPRINT_DIR" ), path.GetFullPath() );
473 
474  path = basePath;
475  path.AppendDir( wxT( "3dmodels" ) );
476  addVar( wxT( "KICAD6_3DMODEL_DIR" ), path.GetFullPath() );
477 
478  // We don't have just one default template path, so use this logic that originally was in
479  // PGM_BASE::InitPgm to determine the best default template path
480  {
481  // Attempt to find the best default template path.
482  SEARCH_STACK bases;
483  SEARCH_STACK templatePaths;
484 
485  SystemDirsAppend( &bases );
486 
487  for( unsigned i = 0; i < bases.GetCount(); ++i )
488  {
489  wxFileName fn( bases[i], wxEmptyString );
490 
491  // Add KiCad template file path to search path list.
492  fn.AppendDir( "template" );
493 
494  // Only add path if exists and can be read by the user.
495  if( fn.DirExists() && fn.IsDirReadable() )
496  {
497  wxLogTrace( tracePathsAndFiles, "Checking template path '%s' exists",
498  fn.GetPath() );
499  templatePaths.AddPaths( fn.GetPath() );
500  }
501  }
502 
503  if( templatePaths.IsEmpty() )
504  {
505  path = basePath;
506  path.AppendDir( "template" );
507  }
508  else
509  {
510  // Take the first one. There may be more but this will likely be the best option.
511  path.AssignDir( templatePaths[0] );
512  }
513 
514  addVar( wxT( "KICAD6_TEMPLATE_DIR" ), path.GetFullPath() );
515  }
516 
517  addVar( wxT( "KICAD_USER_TEMPLATE_DIR" ), PATHS::GetUserTemplatesPath() );
518 
519  path = basePath;
520  path.AppendDir( wxT( "library" ) );
521  addVar( wxT( "KICAD6_SYMBOL_DIR" ), path.GetFullPath() );
522 }
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.
const wxChar *const tracePathsAndFiles
Flag to enable path and file name debug output.
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:226
virtual bool MigrateFromLegacy(wxConfigBase *aLegacyConfig) override
Migrates from wxConfig to JSON-based configuration.
Look for files in a number of paths.
Definition: search_stack.h:41
nlohmann::json json
Definition: gerbview.cpp:41
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
void SystemDirsAppend(SEARCH_STACK *aSearchStack)
Append system places to aSearchStack in a platform specific way and pertinent to KiCad programs.
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:177
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.
void AddPaths(const wxString &aPaths, int aIndex=-1)
Insert or append path(s).
int limit_total_files
Maximum number of backup archives to retain.