1 #include "Preferences.hpp"
2 #include "OptionsGroup.hpp"
3 #include "GUI_App.hpp"
4 #include "Plater.hpp"
5 #include "I18N.hpp"
6 #include "libslic3r/AppConfig.hpp"
7 #include <wx/notebook.h>
8 
9 namespace Slic3r {
10 namespace GUI {
11 
PreferencesDialog(wxWindow * parent)12 PreferencesDialog::PreferencesDialog(wxWindow* parent) :
13     DPIDialog(parent, wxID_ANY, _L("Preferences"), wxDefaultPosition,
14               wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
15 {
16 #ifdef __WXOSX__
17     isOSX = true;
18 #endif
19 	build();
20 }
21 
create_options_tab(const wxString & title,wxNotebook * tabs)22 static std::shared_ptr<ConfigOptionsGroup>create_options_tab(const wxString& title, wxNotebook* tabs)
23 {
24 	wxPanel* tab = new wxPanel(tabs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
25 	tabs->AddPage(tab, title);
26 	tab->SetFont(wxGetApp().normal_font());
27 
28 	wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
29 	sizer->SetSizeHints(tab);
30 	tab->SetSizer(sizer);
31 
32 	std::shared_ptr<ConfigOptionsGroup> optgroup = std::make_shared<ConfigOptionsGroup>(tab);
33 	optgroup->label_width = 40;
34 	return optgroup;
35 }
36 
activate_options_tab(std::shared_ptr<ConfigOptionsGroup> optgroup)37 static void activate_options_tab(std::shared_ptr<ConfigOptionsGroup> optgroup)
38 {
39 	optgroup->activate();
40 	optgroup->update_visibility(comSimple);
41 	wxBoxSizer* sizer = static_cast<wxBoxSizer*>(static_cast<wxPanel*>(optgroup->parent())->GetSizer());
42 	sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 20);
43 }
44 
build()45 void PreferencesDialog::build()
46 {
47 	SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
48 	const wxFont& font = wxGetApp().normal_font();
49 	SetFont(font);
50 
51 	auto app_config = get_app_config();
52 
53 	wxNotebook* tabs = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME);
54 
55 	// Add "General" tab
56 	m_optgroup_general = create_options_tab(_L("General"), tabs);
57 	m_optgroup_general->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
58 		if (opt_key == "default_action_on_close_application" || opt_key == "default_action_on_select_preset")
59 			m_values[opt_key] = boost::any_cast<bool>(value) ? "none" : "discard";
60 		else
61 		    m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
62 	};
63 
64 	bool is_editor = wxGetApp().is_editor();
65 
66 	ConfigOptionDef def;
67 	Option option(def, "");
68 	if (is_editor) {
69 		def.label = L("Remember output directory");
70 		def.type = coBool;
71 		def.tooltip = L("If this is enabled, Slic3r will prompt the last output directory "
72 			"instead of the one containing the input files.");
73 		def.set_default_value(new ConfigOptionBool{ app_config->has("remember_output_path") ? app_config->get("remember_output_path") == "1" : true });
74 		option = Option(def, "remember_output_path");
75 		m_optgroup_general->append_single_option_line(option);
76 
77 		def.label = L("Auto-center parts");
78 		def.type = coBool;
79 		def.tooltip = L("If this is enabled, Slic3r will auto-center objects "
80 			"around the print bed center.");
81 		def.set_default_value(new ConfigOptionBool{ app_config->get("autocenter") == "1" });
82 		option = Option(def, "autocenter");
83 		m_optgroup_general->append_single_option_line(option);
84 
85 		def.label = L("Background processing");
86 		def.type = coBool;
87 		def.tooltip = L("If this is enabled, Slic3r will pre-process objects as soon "
88 			"as they\'re loaded in order to save time when exporting G-code.");
89 		def.set_default_value(new ConfigOptionBool{ app_config->get("background_processing") == "1" });
90 		option = Option(def, "background_processing");
91 		m_optgroup_general->append_single_option_line(option);
92 
93 		// Please keep in sync with ConfigWizard
94 		def.label = L("Check for application updates");
95 		def.type = coBool;
96 		def.tooltip = L("If enabled, PrusaSlicer will check for the new versions of itself online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done.");
97 		def.set_default_value(new ConfigOptionBool(app_config->get("version_check") == "1"));
98 		option = Option(def, "version_check");
99 		m_optgroup_general->append_single_option_line(option);
100 
101 		// Please keep in sync with ConfigWizard
102 		def.label = L("Export sources full pathnames to 3mf and amf");
103 		def.type = coBool;
104 		def.tooltip = L("If enabled, allows the Reload from disk command to automatically find and load the files when invoked.");
105 		def.set_default_value(new ConfigOptionBool(app_config->get("export_sources_full_pathnames") == "1"));
106 		option = Option(def, "export_sources_full_pathnames");
107 		m_optgroup_general->append_single_option_line(option);
108 
109 #if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
110 #ifdef _WIN32
111 		// Please keep in sync with ConfigWizard
112 		def.label = L("Associate .3mf files to PrusaSlicer");
113 		def.type = coBool;
114 		def.tooltip = L("If enabled, sets PrusaSlicer as default application to open .3mf files.");
115 		def.set_default_value(new ConfigOptionBool(app_config->get("associate_3mf") == "1"));
116 		option = Option(def, "associate_3mf");
117 		m_optgroup_general->append_single_option_line(option);
118 
119 		def.label = L("Associate .stl files to PrusaSlicer");
120 		def.type = coBool;
121 		def.tooltip = L("If enabled, sets PrusaSlicer as default application to open .stl files.");
122 		def.set_default_value(new ConfigOptionBool(app_config->get("associate_stl") == "1"));
123 		option = Option(def, "associate_stl");
124 		m_optgroup_general->append_single_option_line(option);
125 #endif // _WIN32
126 #endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
127 
128 		// Please keep in sync with ConfigWizard
129 		def.label = L("Update built-in Presets automatically");
130 		def.type = coBool;
131 		def.tooltip = L("If enabled, Slic3r downloads updates of built-in system presets in the background. These updates are downloaded into a separate temporary location. When a new preset version becomes available it is offered at application startup.");
132 		def.set_default_value(new ConfigOptionBool(app_config->get("preset_update") == "1"));
133 		option = Option(def, "preset_update");
134 		m_optgroup_general->append_single_option_line(option);
135 
136 		def.label = L("Suppress \" - default - \" presets");
137 		def.type = coBool;
138 		def.tooltip = L("Suppress \" - default - \" presets in the Print / Filament / Printer "
139 			"selections once there are any other valid presets available.");
140 		def.set_default_value(new ConfigOptionBool{ app_config->get("no_defaults") == "1" });
141 		option = Option(def, "no_defaults");
142 		m_optgroup_general->append_single_option_line(option);
143 
144 		def.label = L("Show incompatible print and filament presets");
145 		def.type = coBool;
146 		def.tooltip = L("When checked, the print and filament presets are shown in the preset editor "
147 			"even if they are marked as incompatible with the active printer");
148 		def.set_default_value(new ConfigOptionBool{ app_config->get("show_incompatible_presets") == "1" });
149 		option = Option(def, "show_incompatible_presets");
150 		m_optgroup_general->append_single_option_line(option);
151 
152 		def.label = L("Show drop project dialog");
153 		def.type = coBool;
154 		def.tooltip = L("When checked, whenever dragging and dropping a project file on the application, shows a dialog asking to select the action to take on the file to load.");
155 		def.set_default_value(new ConfigOptionBool{ app_config->get("show_drop_project_dialog") == "1" });
156 		option = Option(def, "show_drop_project_dialog");
157 		m_optgroup_general->append_single_option_line(option);
158 
159 
160 #if __APPLE__
161 		def.label = L("Allow just a single PrusaSlicer instance");
162 		def.type = coBool;
163 		def.tooltip = L("On OSX there is always only one instance of app running by default. However it is allowed to run multiple instances of same app from the command line. In such case this settings will allow only one instance.");
164 #else
165 		def.label = L("Allow just a single PrusaSlicer instance");
166 		def.type = coBool;
167 		def.tooltip = L("If this is enabled, when starting PrusaSlicer and another instance of the same PrusaSlicer is already running, that instance will be reactivated instead.");
168 #endif
169 		def.set_default_value(new ConfigOptionBool{ app_config->has("single_instance") ? app_config->get("single_instance") == "1" : false });
170 		option = Option(def, "single_instance");
171 		m_optgroup_general->append_single_option_line(option);
172 
173 		def.label = L("Ask for unsaved changes when closing application");
174 		def.type = coBool;
175 		def.tooltip = L("When closing the application, always ask for unsaved changes");
176 		def.set_default_value(new ConfigOptionBool{ app_config->get("default_action_on_close_application") == "none" });
177 		option = Option(def, "default_action_on_close_application");
178 		m_optgroup_general->append_single_option_line(option);
179 
180 		def.label = L("Ask for unsaved changes when selecting new preset");
181 		def.type = coBool;
182 		def.tooltip = L("Always ask for unsaved changes when selecting new preset");
183 		def.set_default_value(new ConfigOptionBool{ app_config->get("default_action_on_select_preset") == "none" });
184 		option = Option(def, "default_action_on_select_preset");
185 		m_optgroup_general->append_single_option_line(option);
186 	}
187 #if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
188 #ifdef _WIN32
189 	else {
190 		def.label = L("Associate .gcode files to PrusaSlicer G-code Viewer");
191 		def.type = coBool;
192 		def.tooltip = L("If enabled, sets PrusaSlicer G-code Viewer as default application to open .gcode files.");
193 		def.set_default_value(new ConfigOptionBool(app_config->get("associate_gcode") == "1"));
194 		option = Option(def, "associate_gcode");
195 		m_optgroup_general->append_single_option_line(option);
196 	}
197 #endif // _WIN32
198 #endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
199 
200 #if __APPLE__
201 	def.label = L("Use Retina resolution for the 3D scene");
202 	def.type = coBool;
203 	def.tooltip = L("If enabled, the 3D scene will be rendered in Retina resolution. "
204 	                "If you are experiencing 3D performance problems, disabling this option may help.");
205 	def.set_default_value(new ConfigOptionBool{ app_config->get("use_retina_opengl") == "1" });
206 	option = Option (def, "use_retina_opengl");
207 	m_optgroup_general->append_single_option_line(option);
208 #endif
209 
210     // Show/Hide splash screen
211 	def.label = L("Show splash screen");
212 	def.type = coBool;
213 	def.tooltip = L("Show splash screen");
214 	def.set_default_value(new ConfigOptionBool{ app_config->get("show_splash_screen") == "1" });
215 	option = Option(def, "show_splash_screen");
216 	m_optgroup_general->append_single_option_line(option);
217 
218 #if ENABLE_CTRL_M_ON_WINDOWS
219 #if defined(_WIN32) || defined(__APPLE__)
220 	def.label = L("Enable support for legacy 3DConnexion devices");
221 	def.type = coBool;
222 	def.tooltip = L("If enabled, the legacy 3DConnexion devices settings dialog is available by pressing CTRL+M");
223 	def.set_default_value(new ConfigOptionBool{ app_config->get("use_legacy_3DConnexion") == "1" });
224 	option = Option(def, "use_legacy_3DConnexion");
225 	m_optgroup_general->append_single_option_line(option);
226 #endif // _WIN32 || __APPLE__
227 #endif // ENABLE_CTRL_M_ON_WINDOWS
228 
229 	activate_options_tab(m_optgroup_general);
230 
231 	// Add "Camera" tab
232 	m_optgroup_camera = create_options_tab(_L("Camera"), tabs);
233 	m_optgroup_camera->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
234 		m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
235 	};
236 
237 	def.label = L("Use perspective camera");
238 	def.type = coBool;
239 	def.tooltip = L("If enabled, use perspective camera. If not enabled, use orthographic camera.");
240 	def.set_default_value(new ConfigOptionBool{ app_config->get("use_perspective_camera") == "1" });
241 	option = Option(def, "use_perspective_camera");
242 	m_optgroup_camera->append_single_option_line(option);
243 
244 	def.label = L("Use free camera");
245 	def.type = coBool;
246 	def.tooltip = L("If enabled, use free camera. If not enabled, use constrained camera.");
247 	def.set_default_value(new ConfigOptionBool(app_config->get("use_free_camera") == "1"));
248 	option = Option(def, "use_free_camera");
249 	m_optgroup_camera->append_single_option_line(option);
250 
251 	def.label = L("Reverse direction of zoom with mouse wheel");
252 	def.type = coBool;
253 	def.tooltip = L("If enabled, reverses the direction of zoom with mouse wheel");
254 	def.set_default_value(new ConfigOptionBool(app_config->get("reverse_mouse_wheel_zoom") == "1"));
255 	option = Option(def, "reverse_mouse_wheel_zoom");
256 	m_optgroup_camera->append_single_option_line(option);
257 
258 	activate_options_tab(m_optgroup_camera);
259 
260 	// Add "GUI" tab
261 	m_optgroup_gui = create_options_tab(_L("GUI"), tabs);
262 	m_optgroup_gui->m_on_change = [this, tabs](t_config_option_key opt_key, boost::any value) {
263         if (opt_key == "suppress_hyperlinks")
264             m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "";
265         else
266             m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
267 
268 		if (opt_key == "use_custom_toolbar_size") {
269 			m_icon_size_sizer->ShowItems(boost::any_cast<bool>(value));
270 			m_optgroup_gui->parent()->Layout();
271 			tabs->Layout();
272 			this->layout();
273 		}
274 	};
275 
276 	def.label = L("Sequential slider applied only to top layer");
277 	def.type = coBool;
278 	def.tooltip = L("If enabled, changes made using the sequential slider, in preview, apply only to gcode top layer. "
279 					"If disabled, changes made using the sequential slider, in preview, apply to the whole gcode.");
280 	def.set_default_value(new ConfigOptionBool{ app_config->get("seq_top_layer_only") == "1" });
281 	option = Option(def, "seq_top_layer_only");
282 	m_optgroup_gui->append_single_option_line(option);
283 
284 	if (is_editor) {
285 		def.label = L("Show sidebar collapse/expand button");
286 		def.type = coBool;
287 		def.tooltip = L("If enabled, the button for the collapse sidebar will be appeared in top right corner of the 3D Scene");
288 		def.set_default_value(new ConfigOptionBool{ app_config->get("show_collapse_button") == "1" });
289 		option = Option(def, "show_collapse_button");
290 		m_optgroup_gui->append_single_option_line(option);
291 
292 		def.label = L("Suppress to open hyperlink in browser");
293 		def.type = coBool;
294 		def.tooltip = L("If enabled, the descriptions of configuration parameters in settings tabs wouldn't work as hyperlinks. "
295 			"If disabled, the descriptions of configuration parameters in settings tabs will work as hyperlinks.");
296 		def.set_default_value(new ConfigOptionBool{ app_config->get("suppress_hyperlinks") == "1" });
297 		option = Option(def, "suppress_hyperlinks");
298 		m_optgroup_gui->append_single_option_line(option);
299 
300 		def.label = L("Use custom size for toolbar icons");
301 		def.type = coBool;
302 		def.tooltip = L("If enabled, you can change size of toolbar icons manually.");
303 		def.set_default_value(new ConfigOptionBool{ app_config->get("use_custom_toolbar_size") == "1" });
304 		option = Option(def, "use_custom_toolbar_size");
305 		m_optgroup_gui->append_single_option_line(option);
306 	}
307 
308 	activate_options_tab(m_optgroup_gui);
309 
310 	if (is_editor) {
311 		create_icon_size_slider();
312 		m_icon_size_sizer->ShowItems(app_config->get("use_custom_toolbar_size") == "1");
313 
314 		create_settings_mode_widget();
315 	}
316 
317 #if ENABLE_ENVIRONMENT_MAP
318 	if (is_editor) {
319 		// Add "Render" tab
320 		m_optgroup_render = create_options_tab(_L("Render"), tabs);
321 		m_optgroup_render->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
322 			m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
323 		};
324 
325 		def.label = L("Use environment map");
326 		def.type = coBool;
327 		def.tooltip = L("If enabled, renders object using the environment map.");
328 		def.set_default_value(new ConfigOptionBool{ app_config->get("use_environment_map") == "1" });
329 		option = Option(def, "use_environment_map");
330 		m_optgroup_render->append_single_option_line(option);
331 
332 		activate_options_tab(m_optgroup_render);
333 	}
334 #endif // ENABLE_ENVIRONMENT_MAP
335 
336 	auto sizer = new wxBoxSizer(wxVERTICAL);
337 	sizer->Add(tabs, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5);
338 
339 	auto buttons = CreateStdDialogButtonSizer(wxOK | wxCANCEL);
340 	wxButton* btn = static_cast<wxButton*>(FindWindowById(wxID_OK, this));
341 	btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { accept(); });
342 	sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM | wxTOP, 10);
343 
344 	SetSizer(sizer);
345 	sizer->SetSizeHints(this);
346 	this->CenterOnParent();
347 }
348 
accept()349 void PreferencesDialog::accept()
350 {
351     if (m_values.find("no_defaults") != m_values.end())
352         warning_catcher(this, wxString::Format(_L("You need to restart %s to make the changes effective."), SLIC3R_APP_NAME));
353 
354     auto app_config = get_app_config();
355 
356 	m_seq_top_layer_only_changed = false;
357 	if (auto it = m_values.find("seq_top_layer_only"); it != m_values.end())
358 		m_seq_top_layer_only_changed = app_config->get("seq_top_layer_only") != it->second;
359 
360 	m_settings_layout_changed = false;
361 	for (const std::string& key : { "old_settings_layout_mode",
362 								    "new_settings_layout_mode",
363 								    "dlg_settings_layout_mode" })
364 	{
365 	    auto it = m_values.find(key);
366 	    if (it != m_values.end() && app_config->get(key) != it->second) {
367 			m_settings_layout_changed = true;
368 			break;
369 	    }
370 	}
371 
372 	for (const std::string& key : {"default_action_on_close_application", "default_action_on_select_preset"}) {
373 	    auto it = m_values.find(key);
374 		if (it != m_values.end() && it->second != "none" && app_config->get(key) != "none")
375 			m_values.erase(it); // we shouldn't change value, if some of those parameters was selected, and then deselected
376 	}
377 
378 	for (std::map<std::string, std::string>::iterator it = m_values.begin(); it != m_values.end(); ++it)
379 		app_config->set(it->first, it->second);
380 
381 	app_config->save();
382 	EndModal(wxID_OK);
383 
384 	if (m_settings_layout_changed)
385 		;// application will be recreated after Preference dialog will be destroyed
386 	else
387 	    // Nothify the UI to update itself from the ini file.
388         wxGetApp().update_ui_from_settings();
389 }
390 
on_dpi_changed(const wxRect & suggested_rect)391 void PreferencesDialog::on_dpi_changed(const wxRect &suggested_rect)
392 {
393 	m_optgroup_general->msw_rescale();
394 	m_optgroup_camera->msw_rescale();
395 	m_optgroup_gui->msw_rescale();
396 
397     msw_buttons_rescale(this, em_unit(), { wxID_OK, wxID_CANCEL });
398 
399     layout();
400 }
401 
layout()402 void PreferencesDialog::layout()
403 {
404     const int em = em_unit();
405 
406     SetMinSize(wxSize(47 * em, 28 * em));
407     Fit();
408 
409     Refresh();
410 }
411 
create_icon_size_slider()412 void PreferencesDialog::create_icon_size_slider()
413 {
414     const auto app_config = get_app_config();
415 
416     const int em = em_unit();
417 
418     m_icon_size_sizer = new wxBoxSizer(wxHORIZONTAL);
419 
420 	wxWindow* parent = m_optgroup_gui->parent();
421 
422     if (isOSX)
423         // For correct rendering of the slider and value label under OSX
424         // we should use system default background
425         parent->SetBackgroundStyle(wxBG_STYLE_ERASE);
426 
427     auto label = new wxStaticText(parent, wxID_ANY, _L("Icon size in a respect to the default size") + " (%) :");
428 
429     m_icon_size_sizer->Add(label, 0, wxALIGN_CENTER_VERTICAL| wxRIGHT | (isOSX ? 0 : wxLEFT), em);
430 
431     const int def_val = atoi(app_config->get("custom_toolbar_size").c_str());
432 
433     long style = wxSL_HORIZONTAL;
434     if (!isOSX)
435         style |= wxSL_LABELS | wxSL_AUTOTICKS;
436 
437     auto slider = new wxSlider(parent, wxID_ANY, def_val, 30, 100,
438                                wxDefaultPosition, wxDefaultSize, style);
439 
440     slider->SetTickFreq(10);
441     slider->SetPageSize(10);
442     slider->SetToolTip(_L("Select toolbar icon size in respect to the default one."));
443 
444     m_icon_size_sizer->Add(slider, 1, wxEXPAND);
445 
446     wxStaticText* val_label{ nullptr };
447     if (isOSX) {
448         val_label = new wxStaticText(parent, wxID_ANY, wxString::Format("%d", def_val));
449         m_icon_size_sizer->Add(val_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, em);
450     }
451 
452     slider->Bind(wxEVT_SLIDER, ([this, slider, val_label](wxCommandEvent e) {
453         auto val = slider->GetValue();
454         m_values["custom_toolbar_size"] = (boost::format("%d") % val).str();
455 
456         if (val_label)
457             val_label->SetLabelText(wxString::Format("%d", val));
458     }), slider->GetId());
459 
460     for (wxWindow* win : std::vector<wxWindow*>{ slider, label, val_label }) {
461         if (!win) continue;
462         win->SetFont(wxGetApp().normal_font());
463 
464         if (isOSX) continue; // under OSX we use wxBG_STYLE_ERASE
465         win->SetBackgroundStyle(wxBG_STYLE_PAINT);
466     }
467 
468 	m_optgroup_gui->sizer->Add(m_icon_size_sizer, 0, wxEXPAND | wxALL, em);
469 }
470 
create_settings_mode_widget()471 void PreferencesDialog::create_settings_mode_widget()
472 {
473 	wxString choices[] = { _L("Old regular layout with the tab bar"),
474 						   _L("New layout, access via settings button in the top menu"),
475 						   _L("Settings in non-modal window") };
476 
477 	auto app_config = get_app_config();
478 	int selection = app_config->get("old_settings_layout_mode") == "1" ? 0 :
479 	                app_config->get("new_settings_layout_mode") == "1" ? 1 :
480 	                app_config->get("dlg_settings_layout_mode") == "1" ? 2 : 0;
481 
482 	wxWindow* parent = m_optgroup_gui->parent();
483 
484 	m_layout_mode_box = new wxRadioBox(parent, wxID_ANY, _L("Layout Options"), wxDefaultPosition, wxDefaultSize,
485 		WXSIZEOF(choices), choices, 3, wxRA_SPECIFY_ROWS);
486 	m_layout_mode_box->SetFont(wxGetApp().normal_font());
487 	m_layout_mode_box->SetSelection(selection);
488 
489 	m_layout_mode_box->Bind(wxEVT_RADIOBOX, [this](wxCommandEvent& e) {
490 		int selection = e.GetSelection();
491 		m_values["old_settings_layout_mode"] = boost::any_cast<bool>(selection == 0) ? "1" : "0";
492 		m_values["new_settings_layout_mode"] = boost::any_cast<bool>(selection == 1) ? "1" : "0";
493 		m_values["dlg_settings_layout_mode"] = boost::any_cast<bool>(selection == 2) ? "1" : "0";
494 	});
495 
496 	auto sizer = new wxBoxSizer(wxHORIZONTAL);
497 	sizer->Add(m_layout_mode_box, 1, wxALIGN_CENTER_VERTICAL);
498 	m_optgroup_gui->sizer->Add(sizer, 0, wxEXPAND);
499 }
500 
501 
502 } // GUI
503 } // Slic3r
504