KiCad PCB EDA Suite
panel_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) 2018-2021 KiCad Developers, see AUTHORS.txt for contributors.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
25 
26 #include <advanced_config.h>
27 #include <bitmaps.h>
28 #include <dialog_shim.h>
29 #include <gal/dpi_scaling.h>
30 #include <kiface_base.h>
31 #include <pgm_base.h>
32 #include <id.h>
35 #include <widgets/stepped_slider.h>
36 #include <wx/filedlg.h>
37 
38 /*
39  * What follows is a whole lot of ugly to handle various platform GUI deficiences with respect
40  * to light/dark mode, DPI scaling, and other foibles.
41  *
42  * Ugly as it all is, it does improve our usability on various platforms.
43  */
44 
46  : PANEL_COMMON_SETTINGS_BASE( aParent ),
47  m_dialog( aDialog ),
48  m_iconScaleLabel( nullptr ),
49  m_iconScaleSlider( nullptr ),
50  m_iconScaleAuto( nullptr ),
51  m_last_scale( -1 )
52 {
53  /*
54  * Cairo canvas doesn't work on Mac, so no need for fallback anti-aliasing options
55  */
56 #ifdef __WXMAC__
57  m_antialiasingFallback->Show( false );
58  m_antialiasingFallbackLabel->Show( false );
59 #endif
60 
63 
64  /*
65  * Automatic dark mode detection works fine on Mac, so no need for the explicit options.
66  */
67 #ifdef __WXMAC__
68  m_stIconTheme->Show( false );
69  m_rbIconThemeLight->Show( false );
70  m_rbIconThemeDark->Show( false );
71  m_rbIconThemeAuto->Show( false );
72 #endif
73 
74  /*
75  * Automatic icon scaling works fine on Mac. It works mostly fine on MSW, but perhaps not
76  * uniformly enough to exclude the explicit controls there.
77  */
78 #if defined( __WXGTK__ ) || defined( __WXMSW__ )
79  // Sadly wxSlider is poorly implemented and adds its legends as sibiling windows (so that
80  // showing/hiding the control doesn't work). So we have to create it conditionally.
81  wxWindow* parent = m_sbUserInterface->GetStaticBox();
82  wxGridBagSizer* gb = m_gbUserInterface;
83 
84  m_iconScaleLabel = new wxStaticText( parent, wxID_ANY, _( "Icon scale:" ) );
85  m_iconScaleLabel->Wrap( -1 );
86  gb->Add( m_iconScaleLabel, wxGBPosition( 2, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 );
87 
88  m_iconScaleSlider = new STEPPED_SLIDER( parent, wxID_ANY, 100, 50, 275, wxDefaultPosition,
89  wxDefaultSize, wxSL_HORIZONTAL|wxSL_VALUE_LABEL );
91  gb->Add( m_iconScaleSlider, wxGBPosition( 2, 1 ), wxGBSpan( 1, 2 ), wxEXPAND|wxBOTTOM, 5 );
92 
93  m_iconScaleAuto = new wxCheckBox( parent, wxID_ANY, _( "Automatic" ) );
94  gb->Add( m_iconScaleAuto, wxGBPosition( 2, 3 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxLEFT, 15 );
95 #endif
96 
97  /*
98  * Automatic canvas scaling works fine on all supported platforms, so manual scaling is disabled
99  */
100  if( ADVANCED_CFG::GetCfg().m_AllowManualCanvasScale )
101  {
102  static constexpr int dpi_scaling_precision = 1;
103  static constexpr double dpi_scaling_increment = 0.5;
104 
107  m_canvasScaleCtrl->SetDigits( dpi_scaling_precision );
108  m_canvasScaleCtrl->SetIncrement( dpi_scaling_increment );
110 
111  m_canvasScaleCtrl->SetToolTip(
112  _( "Set the scale for the canvas."
113  "\n\n"
114  "On high-DPI displays on some platforms, KiCad cannot determine the "
115  "scaling factor. In this case you may need to set this to a value to "
116  "match your system's DPI scaling. 2.0 is a common value. "
117  "\n\n"
118  "If this does not match the system DPI scaling, the canvas will "
119  "not match the window size and cursor position." ) );
120 
121  m_canvasScaleAuto->SetToolTip(
122  _( "Use an automatic value for the canvas scale."
123  "\n\n"
124  "On some platforms, the automatic value is incorrect and should be "
125  "set manually." ) );
126  }
127  else
128  {
129  m_staticTextCanvasScale->Show( false );
130  m_canvasScaleCtrl->Show( false );
131  m_canvasScaleCtrl = nullptr;
132  m_canvasScaleAuto->Show( false );
133  }
134 
135  /*
136  * Font scaling hacks are only needed on GTK under wxWidgets 3.0.
137  */
138 #if defined( __WXGTK__ ) && !wxCHECK_VERSION( 3, 1, 0 )
139  m_fontScalingHelp->SetFont( KIUI::GetInfoFont( this ).Italic() );
140 #else
141  m_scaleFonts->Show( false );
142  m_fontScalingHelp->Show( false );
143 #endif
144 
145  if( m_iconScaleSlider )
146  {
147  m_iconScaleSlider->Connect( wxEVT_SCROLL_TOP,
148  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
149  nullptr, this );
150  m_iconScaleSlider->Connect( wxEVT_SCROLL_BOTTOM,
151  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
152  nullptr, this );
153  m_iconScaleSlider->Connect( wxEVT_SCROLL_LINEUP,
154  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
155  nullptr, this );
156  m_iconScaleSlider->Connect( wxEVT_SCROLL_LINEDOWN,
157  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
158  nullptr, this );
159  m_iconScaleSlider->Connect( wxEVT_SCROLL_PAGEUP,
160  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
161  nullptr, this );
162  m_iconScaleSlider->Connect( wxEVT_SCROLL_PAGEDOWN,
163  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
164  nullptr, this );
165  m_iconScaleSlider->Connect( wxEVT_SCROLL_THUMBTRACK,
166  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
167  nullptr, this );
168  m_iconScaleSlider->Connect( wxEVT_SCROLL_THUMBRELEASE,
169  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
170  nullptr, this );
171  m_iconScaleSlider->Connect( wxEVT_SCROLL_CHANGED,
172  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
173  nullptr, this );
174  m_iconScaleAuto->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED,
175  wxCommandEventHandler( PANEL_COMMON_SETTINGS::OnIconScaleAuto ),
176  nullptr, this );
177  }
178 
179  if( m_canvasScaleCtrl )
180  {
181  m_canvasScaleCtrl->Connect( wxEVT_COMMAND_TEXT_UPDATED,
182  wxCommandEventHandler( PANEL_COMMON_SETTINGS::OnCanvasScaleChange ),
183  nullptr, this );
184  }
185 }
186 
187 
189 {
190  if( m_iconScaleSlider )
191  {
192  m_iconScaleSlider->Disconnect( wxEVT_SCROLL_TOP,
193  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
194  nullptr, this );
195  m_iconScaleSlider->Disconnect( wxEVT_SCROLL_BOTTOM,
196  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
197  nullptr, this );
198  m_iconScaleSlider->Disconnect( wxEVT_SCROLL_LINEUP,
199  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
200  nullptr, this );
201  m_iconScaleSlider->Disconnect( wxEVT_SCROLL_LINEDOWN,
202  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
203  nullptr, this );
204  m_iconScaleSlider->Disconnect( wxEVT_SCROLL_PAGEUP,
205  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
206  nullptr, this );
207  m_iconScaleSlider->Disconnect( wxEVT_SCROLL_PAGEDOWN,
208  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
209  nullptr, this );
210  m_iconScaleSlider->Disconnect( wxEVT_SCROLL_THUMBTRACK,
211  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
212  nullptr, this );
213  m_iconScaleSlider->Disconnect( wxEVT_SCROLL_THUMBRELEASE,
214  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
215  nullptr, this );
216  m_iconScaleSlider->Disconnect( wxEVT_SCROLL_CHANGED,
217  wxScrollEventHandler( PANEL_COMMON_SETTINGS::OnScaleSlider ),
218  nullptr, this );
219  m_iconScaleAuto->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED,
220  wxCommandEventHandler( PANEL_COMMON_SETTINGS::OnIconScaleAuto ),
221  nullptr, this );
222  }
223 
224  if( m_canvasScaleCtrl )
225  {
226  m_canvasScaleCtrl->Disconnect( wxEVT_COMMAND_TEXT_UPDATED,
227  wxCommandEventHandler( PANEL_COMMON_SETTINGS::OnCanvasScaleChange ),
228  nullptr, this );
229  }
230 }
231 
232 
234 {
235  COMMON_SETTINGS* commonSettings = Pgm().GetCommonSettings();
236 
237  applySettingsToPanel( *commonSettings );
238 
239  // TODO(JE) Move these into COMMON_SETTINGS probably
240  m_textEditorPath->SetValue( Pgm().GetTextEditor( false ) );
241  m_defaultPDFViewer->SetValue( Pgm().UseSystemPdfBrowser() );
242  m_otherPDFViewer->SetValue( !Pgm().UseSystemPdfBrowser() );
243  m_PDFViewerPath->SetValue( Pgm().GetPdfBrowserName() );
244 
245  return true;
246 }
247 
248 
250 {
251  COMMON_SETTINGS* commonSettings = Pgm().GetCommonSettings();
252 
253  commonSettings->m_System.autosave_interval = m_SaveTime->GetValue() * 60;
254  commonSettings->m_System.file_history_size = m_fileHistorySize->GetValue();
255  commonSettings->m_System.clear_3d_cache_interval = m_Clear3DCacheFilesOlder->GetValue();
256 
257  commonSettings->m_Graphics.opengl_aa_mode = m_antialiasing->GetSelection();
258  commonSettings->m_Graphics.cairo_aa_mode = m_antialiasingFallback->GetSelection();
259 
260  if( m_iconScaleSlider )
261  {
262  int scale_fourths = m_iconScaleAuto->GetValue() ? -1 : m_iconScaleSlider->GetValue() / 25;
263  commonSettings->m_Appearance.icon_scale = scale_fourths;
264  }
265 
266  if( m_canvasScaleCtrl )
267  {
268  DPI_SCALING dpi( commonSettings, this );
269  dpi.SetDpiConfig( m_canvasScaleAuto->GetValue(), m_canvasScaleCtrl->GetValue() );
270  }
271 
272  if( m_rbIconThemeLight->GetValue() )
273  commonSettings->m_Appearance.icon_theme = ICON_THEME::LIGHT;
274  else if( m_rbIconThemeDark->GetValue() )
275  commonSettings->m_Appearance.icon_theme = ICON_THEME::DARK;
276  else if( m_rbIconThemeAuto->GetValue() )
277  commonSettings->m_Appearance.icon_theme = ICON_THEME::AUTO;
278 
279  commonSettings->m_Appearance.use_icons_in_menus = m_checkBoxIconsInMenus->GetValue();
280  commonSettings->m_Appearance.apply_icon_scale_to_fonts = m_scaleFonts->GetValue();
281 
282  commonSettings->m_Input.immediate_actions = !m_NonImmediateActions->GetValue();
283  commonSettings->m_Input.warp_mouse_on_move = m_warpMouseOnMove->GetValue();
284 
285  commonSettings->m_Backup.enabled = m_cbBackupEnabled->GetValue();
286  commonSettings->m_Backup.backup_on_autosave = m_cbBackupAutosave->GetValue();
287  commonSettings->m_Backup.limit_total_files = m_backupLimitTotalFiles->GetValue();
288  commonSettings->m_Backup.limit_daily_files = m_backupLimitDailyFiles->GetValue();
289  commonSettings->m_Backup.min_interval = m_backupMinInterval->GetValue() * 60;
290  commonSettings->m_Backup.limit_total_size = m_backupLimitTotalSize->GetValue() * 1024 * 1024;
291 
292  commonSettings->m_Session.remember_open_files = m_cbRememberOpenFiles->GetValue();
293 
294  Pgm().SetTextEditor( m_textEditorPath->GetValue());
295 
296  Pgm().SetPdfBrowserName( m_PDFViewerPath->GetValue() );
297  Pgm().ForceSystemPdfBrowser( m_defaultPDFViewer->GetValue() );
298  Pgm().WritePdfBrowserInfos();
299 
300  Pgm().GetSettingsManager().Save( commonSettings );
301 
302  return true;
303 }
304 
305 
307 {
308  COMMON_SETTINGS defaultSettings;
309 
310  defaultSettings.ResetToDefaults();
311 
312  applySettingsToPanel( defaultSettings );
313 
314  // TODO(JE) Move these into COMMON_SETTINGS probably
315  m_textEditorPath->SetValue( defaultSettings.m_System.text_editor );
316  m_defaultPDFViewer->SetValue( defaultSettings.m_System.use_system_pdf_viewer );
317  m_otherPDFViewer->SetValue( !defaultSettings.m_System.use_system_pdf_viewer );
318  m_PDFViewerPath->SetValue( defaultSettings.m_System.pdf_viewer_name );
319 }
320 
321 
323 {
324  int timevalue = aSettings.m_System.autosave_interval;
325  wxString msg;
326 
327  msg << timevalue / 60;
328  m_SaveTime->SetValue( msg );
329 
330  m_fileHistorySize->SetValue( aSettings.m_System.file_history_size );
331 
332  m_antialiasing->SetSelection( aSettings.m_Graphics.opengl_aa_mode );
333  m_antialiasingFallback->SetSelection( aSettings.m_Graphics.cairo_aa_mode );
334 
336 
337  if( m_iconScaleSlider )
338  {
339  int icon_scale_fourths = aSettings.m_Appearance.icon_scale;
340 
341  if( icon_scale_fourths <= 0 )
342  {
343  m_iconScaleAuto->SetValue( true );
344  m_iconScaleSlider->SetValue( 25 * KiIconScale( GetParent() ) );
345  }
346  else
347  {
348  m_iconScaleAuto->SetValue( false );
349  m_iconScaleSlider->SetValue( icon_scale_fourths * 25 );
350  }
351  }
352 
353  if( m_canvasScaleCtrl )
354  {
355  const DPI_SCALING dpi( &aSettings, this );
356  m_canvasScaleCtrl->SetValue( dpi.GetScaleFactor() );
357  m_canvasScaleAuto->SetValue( dpi.GetCanvasIsAutoScaled() );
358  }
359 
360  switch( aSettings.m_Appearance.icon_theme )
361  {
362  case ICON_THEME::LIGHT: m_rbIconThemeLight->SetValue( true ); break;
363  case ICON_THEME::DARK: m_rbIconThemeDark->SetValue( true ); break;
364  case ICON_THEME::AUTO: m_rbIconThemeAuto->SetValue( true ); break;
365  }
366 
368  m_scaleFonts->SetValue( aSettings.m_Appearance.apply_icon_scale_to_fonts );
369 
370  m_warpMouseOnMove->SetValue( aSettings.m_Input.warp_mouse_on_move );
371  m_NonImmediateActions->SetValue( !aSettings.m_Input.immediate_actions );
372 
373  m_cbRememberOpenFiles->SetValue( aSettings.m_Session.remember_open_files );
374 
375  m_cbBackupEnabled->SetValue( aSettings.m_Backup.enabled );
376  m_cbBackupAutosave->SetValue( aSettings.m_Backup.backup_on_autosave );
377  m_backupLimitTotalFiles->SetValue( aSettings.m_Backup.limit_total_files );
378  m_backupLimitDailyFiles->SetValue( aSettings.m_Backup.limit_daily_files );
379  m_backupMinInterval->SetValue( aSettings.m_Backup.min_interval / 60 );
380  m_backupLimitTotalSize->SetValue( aSettings.m_Backup.limit_total_size / ( 1024 * 1024 ) );
381 }
382 
383 
384 void PANEL_COMMON_SETTINGS::OnScaleSlider( wxScrollEvent& aEvent )
385 {
386  m_iconScaleAuto->SetValue( false );
387  aEvent.Skip();
388 }
389 
390 
391 void PANEL_COMMON_SETTINGS::OnIconScaleAuto( wxCommandEvent& aEvent )
392 {
393  if( m_iconScaleSlider )
394  {
395  if( m_iconScaleAuto->GetValue() )
396  {
397  m_last_scale = m_iconScaleAuto->GetValue();
398  m_iconScaleSlider->SetValue( 25 * KiIconScale( GetParent() ) );
399  }
400  else
401  {
402  if( m_last_scale >= 0 )
403  m_iconScaleSlider->SetValue( m_last_scale );
404  }
405  }
406 }
407 
408 
409 void PANEL_COMMON_SETTINGS::OnCanvasScaleChange( wxCommandEvent& aEvent )
410 {
411  m_canvasScaleAuto->SetValue( false );
412 }
413 
414 
415 void PANEL_COMMON_SETTINGS::OnCanvasScaleAuto( wxCommandEvent& aEvent )
416 {
417  const bool automatic = m_canvasScaleAuto->GetValue();
418 
419  if( automatic && m_canvasScaleCtrl )
420  {
421  // set the scale to the auto value, without consulting the config
422  DPI_SCALING dpi( nullptr, this );
423 
424  // update the field (no events sent)
425  m_canvasScaleCtrl->SetValue( dpi.GetScaleFactor() );
426  }
427 }
428 
429 
430 void PANEL_COMMON_SETTINGS::OnTextEditorClick( wxCommandEvent& event )
431 {
432  // Ask the user to select a new editor, but suggest the current one as the default.
433  wxString editorname = Pgm().AskUserForPreferredEditor( m_textEditorPath->GetValue() );
434 
435  // If we have a new editor name request it to be copied to m_text_editor and saved
436  // to the preferences file. If the user cancelled the dialog then the previous
437  // value will be retained.
438  if( !editorname.IsEmpty() )
439  m_textEditorPath->SetValue( editorname );
440 }
441 
442 
443 void PANEL_COMMON_SETTINGS::OnPDFViewerClick( wxCommandEvent& event )
444 {
445  wxString mask( wxT( "*" ) );
446 
447 #ifdef __WINDOWS__
448  mask += wxT( ".exe" );
449 #endif
450 
451  wxString wildcard = _( "Executable files (" ) + mask + wxT( ")|" ) + mask;
452 
453  Pgm().ReadPdfBrowserInfos();
454  wxFileName fn = Pgm().GetPdfBrowserName();
455 
456  wxFileDialog dlg( this, _( "Select Preferred PDF Viewer" ), fn.GetPath(), fn.GetFullPath(),
457  wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST );
458 
459  if( dlg.ShowModal() == wxID_CANCEL )
460  return;
461 
462  m_otherPDFViewer->SetValue( true );
463  m_PDFViewerPath->SetValue( dlg.GetPath() );
464 }
465 
466 
467 void PANEL_COMMON_SETTINGS::onUpdateUIPdfPath( wxUpdateUIEvent& event )
468 {
469  // Used by both the m_pdfViewerBtn and m_PDFViewerPath
470  event.Enable( m_otherPDFViewer->GetValue() );
471 }
void ResetToDefaults()
Resets all parameters to default values.
void ResetPanel() override
Reset the contents of this panel.
unsigned long long limit_total_size
Maximum total size of backups (bytes), 0 for unlimited.
void OnScaleSlider(wxScrollEvent &aEvent)
void OnCanvasScaleChange(wxCommandEvent &aEvent)
Event fired when the canvas scale field is modified.
int KiIconScale(wxWindow *aWindow)
Return the automatic scale factor that would be used for a given window by KiScaledBitmap and KiScale...
Definition: bitmap.cpp:122
bool enabled
Automatically back up the project when files are saved.
wxFont GetInfoFont(wxWindow *aWindow)
Definition: ui_common.cpp:144
AUTO_BACKUP m_Backup
void OnPDFViewerClick(wxCommandEvent &event) override
APPEARANCE m_Appearance
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:82
static double GetDefaultScaleFactor()
Get the "default" scaling factor to use if not other config is available.
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
Class PANEL_COMMON_SETTINGS_BASE.
double GetScaleFactor() const
Get the DPI scale from all known sources in order:
Class to handle configuration and automatic determination of the DPI scale to use for canvases.
Definition: dpi_scaling.h:36
bool TransferDataFromWindow() override
void OnTextEditorClick(wxCommandEvent &event) override
void SetDpiConfig(bool aAuto, double aValue)
Set the common DPI config in a given config object.
void applySettingsToPanel(COMMON_SETTINGS &aSettings)
static double GetMaxScaleFactor()
static double GetMinScaleFactor()
#define _(s)
void onUpdateUIPdfPath(wxUpdateUIEvent &event) override
void OnIconScaleAuto(wxCommandEvent &aEvent)
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:105
int m_last_scale
saved icon scale when Auto selected
int min_interval
Minimum time, in seconds, between subsequent backups.
see class PGM_BASE
Customized wxSlider with forced stepping.
void OnCanvasScaleAuto(wxCommandEvent &aEvent) override
Event fired when the canvas auto-scale option is changed.
bool TransferDataToWindow() override
bool backup_on_autosave
Trigger a backup on autosave.
PANEL_COMMON_SETTINGS(DIALOG_SHIM *aDialog, wxWindow *aParent)
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
int limit_daily_files
Maximum files to keep per day, 0 for unlimited.
void SetStep(int aSize)
Set the step size.
STEPPED_SLIDER * m_iconScaleSlider
int limit_total_files
Maximum number of backup archives to retain.
bool GetCanvasIsAutoScaled() const
Is the current value auto scaled, or is it user-set in the config.