1 // FIXME: extract absolute units -> em
2 
3 #include "ConfigWizard_private.hpp"
4 
5 #include <algorithm>
6 #include <numeric>
7 #include <utility>
8 #include <unordered_map>
9 #include <stdexcept>
10 #include <boost/format.hpp>
11 #include <boost/log/trivial.hpp>
12 #include <boost/algorithm/string/predicate.hpp>
13 #include <boost/nowide/convert.hpp>
14 
15 #include <wx/settings.h>
16 #include <wx/stattext.h>
17 #include <wx/textctrl.h>
18 #include <wx/dcclient.h>
19 #include <wx/statbmp.h>
20 #include <wx/checkbox.h>
21 #include <wx/statline.h>
22 #include <wx/dataview.h>
23 #include <wx/notebook.h>
24 #include <wx/display.h>
25 #include <wx/filefn.h>
26 #include <wx/wupdlock.h>
27 #include <wx/debug.h>
28 
29 #include "libslic3r/Utils.hpp"
30 #include "libslic3r/Config.hpp"
31 #include "GUI.hpp"
32 #include "GUI_App.hpp"
33 #include "GUI_Utils.hpp"
34 #include "GUI_ObjectManipulation.hpp"
35 #include "slic3r/Config/Snapshot.hpp"
36 #include "slic3r/Utils/PresetUpdater.hpp"
37 
38 #if defined(__linux__) && defined(__WXGTK3__)
39 #define wxLinux_gtk3 true
40 #else
41 #define wxLinux_gtk3 false
42 #endif //defined(__linux__) && defined(__WXGTK3__)
43 
44 namespace Slic3r {
45 namespace GUI {
46 
47 
48 using Config::Snapshot;
49 using Config::SnapshotDB;
50 
51 
52 // Configuration data structures extensions needed for the wizard
53 
load(fs::path source_path,bool ais_in_resources,bool ais_prusa_bundle)54 bool Bundle::load(fs::path source_path, bool ais_in_resources, bool ais_prusa_bundle)
55 {
56     this->preset_bundle = std::make_unique<PresetBundle>();
57     this->is_in_resources = ais_in_resources;
58     this->is_prusa_bundle = ais_prusa_bundle;
59 
60     std::string path_string = source_path.string();
61     // Throw when parsing invalid configuration. Only valid configuration is supposed to be provided over the air.
62     auto [config_substitutions, presets_loaded] = preset_bundle->load_configbundle(
63         path_string, PresetBundle::LoadConfigBundleAttribute::LoadSystem, ForwardCompatibilitySubstitutionRule::Disable);
64     // No substitutions shall be reported when loading a system config bundle, no substitutions are allowed.
65     assert(config_substitutions.empty());
66     auto first_vendor = preset_bundle->vendors.begin();
67     if (first_vendor == preset_bundle->vendors.end()) {
68         BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: No vendor information defined, cannot install.") % path_string;
69         return false;
70     }
71     if (presets_loaded == 0) {
72         BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: No profile loaded.") % path_string;
73         return false;
74     }
75 
76     BOOST_LOG_TRIVIAL(trace) << boost::format("Vendor bundle: `%1%`: %2% profiles loaded.") % path_string % presets_loaded;
77     this->vendor_profile = &first_vendor->second;
78     return true;
79 }
80 
Bundle(Bundle && other)81 Bundle::Bundle(Bundle &&other)
82     : preset_bundle(std::move(other.preset_bundle))
83     , vendor_profile(other.vendor_profile)
84     , is_in_resources(other.is_in_resources)
85     , is_prusa_bundle(other.is_prusa_bundle)
86 {
87     other.vendor_profile = nullptr;
88 }
89 
load()90 BundleMap BundleMap::load()
91 {
92     BundleMap res;
93 
94     const auto vendor_dir = (boost::filesystem::path(Slic3r::data_dir()) / "vendor").make_preferred();
95     const auto rsrc_vendor_dir = (boost::filesystem::path(resources_dir()) / "profiles").make_preferred();
96 
97     auto prusa_bundle_path = (vendor_dir / PresetBundle::PRUSA_BUNDLE).replace_extension(".ini");
98     auto prusa_bundle_rsrc = false;
99     if (! boost::filesystem::exists(prusa_bundle_path)) {
100         prusa_bundle_path = (rsrc_vendor_dir / PresetBundle::PRUSA_BUNDLE).replace_extension(".ini");
101         prusa_bundle_rsrc = true;
102     }
103     {
104         Bundle prusa_bundle;
105         if (prusa_bundle.load(std::move(prusa_bundle_path), prusa_bundle_rsrc, true))
106             res.emplace(PresetBundle::PRUSA_BUNDLE, std::move(prusa_bundle));
107     }
108 
109     // Load the other bundles in the datadir/vendor directory
110     // and then additionally from resources/profiles.
111     bool is_in_resources = false;
112     for (auto dir : { &vendor_dir, &rsrc_vendor_dir }) {
113         for (const auto &dir_entry : boost::filesystem::directory_iterator(*dir)) {
114             if (Slic3r::is_ini_file(dir_entry)) {
115                 std::string id = dir_entry.path().stem().string();  // stem() = filename() without the trailing ".ini" part
116 
117                 // Don't load this bundle if we've already loaded it.
118                 if (res.find(id) != res.end()) { continue; }
119 
120                 Bundle bundle;
121                 if (bundle.load(dir_entry.path(), is_in_resources))
122                     res.emplace(std::move(id), std::move(bundle));
123             }
124         }
125 
126         is_in_resources = true;
127     }
128 
129     return res;
130 }
131 
prusa_bundle()132 Bundle& BundleMap::prusa_bundle()
133 {
134     auto it = find(PresetBundle::PRUSA_BUNDLE);
135     if (it == end()) {
136         throw Slic3r::RuntimeError("ConfigWizard: Internal error in BundleMap: PRUSA_BUNDLE not loaded");
137     }
138 
139     return it->second;
140 }
141 
prusa_bundle() const142 const Bundle& BundleMap::prusa_bundle() const
143 {
144     return const_cast<BundleMap*>(this)->prusa_bundle();
145 }
146 
147 
148 // Printer model picker GUI control
149 
150 struct PrinterPickerEvent : public wxEvent
151 {
152     std::string vendor_id;
153     std::string model_id;
154     std::string variant_name;
155     bool enable;
156 
PrinterPickerEventSlic3r::GUI::PrinterPickerEvent157     PrinterPickerEvent(wxEventType eventType, int winid, std::string vendor_id, std::string model_id, std::string variant_name, bool enable)
158         : wxEvent(winid, eventType)
159         , vendor_id(std::move(vendor_id))
160         , model_id(std::move(model_id))
161         , variant_name(std::move(variant_name))
162         , enable(enable)
163     {}
164 
CloneSlic3r::GUI::PrinterPickerEvent165     virtual wxEvent *Clone() const
166     {
167         return new PrinterPickerEvent(*this);
168     }
169 };
170 
171 wxDEFINE_EVENT(EVT_PRINTER_PICK, PrinterPickerEvent);
172 
173 const std::string PrinterPicker::PRINTER_PLACEHOLDER = "printer_placeholder.png";
174 
PrinterPicker(wxWindow * parent,const VendorProfile & vendor,wxString title,size_t max_cols,const AppConfig & appconfig,const ModelFilter & filter)175 PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig, const ModelFilter &filter)
176     : wxPanel(parent)
177     , vendor_id(vendor.id)
178     , width(0)
179 {
180     const auto &models = vendor.models;
181 
182     auto *sizer = new wxBoxSizer(wxVERTICAL);
183 
184     const auto font_title = GetFont().MakeBold().Scaled(1.3f);
185     const auto font_name = GetFont().MakeBold();
186     const auto font_alt_nozzle = GetFont().Scaled(0.9f);
187 
188     // wxGrid appends widgets by rows, but we need to construct them in columns.
189     // These vectors are used to hold the elements so that they can be appended in the right order.
190     std::vector<wxStaticText*> titles;
191     std::vector<wxStaticBitmap*> bitmaps;
192     std::vector<wxPanel*> variants_panels;
193 
194     int max_row_width = 0;
195     int current_row_width = 0;
196 
197     bool is_variants = false;
198 
199     for (const auto &model : models) {
200         if (! filter(model)) { continue; }
201 
202         wxBitmap bitmap;
203         int bitmap_width = 0;
204         auto load_bitmap = [](const wxString& bitmap_file, wxBitmap& bitmap, int& bitmap_width)->bool {
205             if (wxFileExists(bitmap_file)) {
206                 bitmap.LoadFile(bitmap_file, wxBITMAP_TYPE_PNG);
207                 bitmap_width = bitmap.GetWidth();
208                 return true;
209             }
210             return false;
211         };
212         if (!load_bitmap(GUI::from_u8(Slic3r::data_dir() + "/vendor/" + vendor.id + "/" + model.id + "_thumbnail.png"), bitmap, bitmap_width)) {
213             if (!load_bitmap(GUI::from_u8(Slic3r::resources_dir() + "/profiles/" + vendor.id + "/" + model.id + "_thumbnail.png"), bitmap, bitmap_width)) {
214                 BOOST_LOG_TRIVIAL(warning) << boost::format("Can't find bitmap file `%1%` for vendor `%2%`, printer `%3%`, using placeholder icon instead")
215                     % (Slic3r::resources_dir() + "/profiles/" + vendor.id + "/" + model.id + "_thumbnail.png")
216                     % vendor.id
217                     % model.id;
218                 load_bitmap(Slic3r::var(PRINTER_PLACEHOLDER), bitmap, bitmap_width);
219             }
220         }
221         auto *title = new wxStaticText(this, wxID_ANY, from_u8(model.name), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
222         title->SetFont(font_name);
223         const int wrap_width = std::max((int)MODEL_MIN_WRAP, bitmap_width);
224         title->Wrap(wrap_width);
225 
226         current_row_width += wrap_width;
227         if (titles.size() % max_cols == max_cols - 1) {
228             max_row_width = std::max(max_row_width, current_row_width);
229             current_row_width = 0;
230         }
231 
232         titles.push_back(title);
233 
234         auto *bitmap_widget = new wxStaticBitmap(this, wxID_ANY, bitmap);
235         bitmaps.push_back(bitmap_widget);
236 
237         auto *variants_panel = new wxPanel(this);
238         auto *variants_sizer = new wxBoxSizer(wxVERTICAL);
239         variants_panel->SetSizer(variants_sizer);
240         const auto model_id = model.id;
241 
242         for (size_t i = 0; i < model.variants.size(); i++) {
243             const auto &variant = model.variants[i];
244 
245             const auto label = model.technology == ptFFF
246                 ? from_u8((boost::format("%1% %2% %3%") % variant.name % _utf8(L("mm")) % _utf8(L("nozzle"))).str())
247                 : from_u8(model.name);
248 
249             if (i == 1) {
250                 auto *alt_label = new wxStaticText(variants_panel, wxID_ANY, _L("Alternate nozzles:"));
251                 alt_label->SetFont(font_alt_nozzle);
252                 variants_sizer->Add(alt_label, 0, wxBOTTOM, 3);
253                 is_variants = true;
254             }
255 
256             auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name);
257             i == 0 ? cboxes.push_back(cbox) : cboxes_alt.push_back(cbox);
258 
259             const bool enabled = appconfig.get_variant(vendor.id, model_id, variant.name);
260             cbox->SetValue(enabled);
261 
262             variants_sizer->Add(cbox, 0, wxBOTTOM, 3);
263 
264             cbox->Bind(wxEVT_CHECKBOX, [this, cbox](wxCommandEvent &event) {
265                 on_checkbox(cbox, event.IsChecked());
266             });
267         }
268 
269         variants_panels.push_back(variants_panel);
270     }
271 
272     width = std::max(max_row_width, current_row_width);
273 
274     const size_t cols = std::min(max_cols, titles.size());
275 
276     auto *printer_grid = new wxFlexGridSizer(cols, 0, 20);
277     printer_grid->SetFlexibleDirection(wxVERTICAL | wxHORIZONTAL);
278 
279     if (titles.size() > 0) {
280         const size_t odd_items = titles.size() % cols;
281 
282         for (size_t i = 0; i < titles.size() - odd_items; i += cols) {
283             for (size_t j = i; j < i + cols; j++) { printer_grid->Add(bitmaps[j], 0, wxBOTTOM, 20); }
284             for (size_t j = i; j < i + cols; j++) { printer_grid->Add(titles[j], 0, wxBOTTOM, 3); }
285             for (size_t j = i; j < i + cols; j++) { printer_grid->Add(variants_panels[j]); }
286 
287             // Add separator space to multiliners
288             if (titles.size() > cols) {
289                 for (size_t j = i; j < i + cols; j++) { printer_grid->Add(1, 30); }
290             }
291         }
292         if (odd_items > 0) {
293             const size_t rem = titles.size() - odd_items;
294 
295             for (size_t i = rem; i < titles.size(); i++) { printer_grid->Add(bitmaps[i], 0, wxBOTTOM, 20); }
296             for (size_t i = 0; i < cols - odd_items; i++) { printer_grid->AddSpacer(1); }
297             for (size_t i = rem; i < titles.size(); i++) { printer_grid->Add(titles[i], 0, wxBOTTOM, 3); }
298             for (size_t i = 0; i < cols - odd_items; i++) { printer_grid->AddSpacer(1); }
299             for (size_t i = rem; i < titles.size(); i++) { printer_grid->Add(variants_panels[i]); }
300         }
301     }
302 
303     auto *title_sizer = new wxBoxSizer(wxHORIZONTAL);
304     if (! title.IsEmpty()) {
305         auto *title_widget = new wxStaticText(this, wxID_ANY, title);
306         title_widget->SetFont(font_title);
307         title_sizer->Add(title_widget);
308     }
309     title_sizer->AddStretchSpacer();
310 
311     if (/*titles.size() > 1*/is_variants) {
312         // It only makes sense to add the All / None buttons if there's multiple printers
313 
314         auto *sel_all_std = new wxButton(this, wxID_ANY, titles.size() > 1 ? _L("All standard") : _L("Standard"));
315         auto *sel_all = new wxButton(this, wxID_ANY, _L("All"));
316         auto *sel_none = new wxButton(this, wxID_ANY, _L("None"));
317         sel_all_std->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true, false); });
318         sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true, true); });
319         sel_none->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(false); });
320         title_sizer->Add(sel_all_std, 0, wxRIGHT, BTN_SPACING);
321         title_sizer->Add(sel_all, 0, wxRIGHT, BTN_SPACING);
322         title_sizer->Add(sel_none);
323 
324         // fill button indexes used later for buttons rescaling
325         m_button_indexes = { sel_all_std->GetId(), sel_all->GetId(), sel_none->GetId() };
326     }
327 
328     sizer->Add(title_sizer, 0, wxEXPAND | wxBOTTOM, BTN_SPACING);
329     sizer->Add(printer_grid);
330 
331     SetSizer(sizer);
332 }
333 
PrinterPicker(wxWindow * parent,const VendorProfile & vendor,wxString title,size_t max_cols,const AppConfig & appconfig)334 PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig)
335     : PrinterPicker(parent, vendor, std::move(title), max_cols, appconfig, [](const VendorProfile::PrinterModel&) { return true; })
336 {}
337 
select_all(bool select,bool alternates)338 void PrinterPicker::select_all(bool select, bool alternates)
339 {
340     for (const auto &cb : cboxes) {
341         if (cb->GetValue() != select) {
342             cb->SetValue(select);
343             on_checkbox(cb, select);
344         }
345     }
346 
347     if (! select) { alternates = false; }
348 
349     for (const auto &cb : cboxes_alt) {
350         if (cb->GetValue() != alternates) {
351             cb->SetValue(alternates);
352             on_checkbox(cb, alternates);
353         }
354     }
355 }
356 
select_one(size_t i,bool select)357 void PrinterPicker::select_one(size_t i, bool select)
358 {
359     if (i < cboxes.size() && cboxes[i]->GetValue() != select) {
360         cboxes[i]->SetValue(select);
361         on_checkbox(cboxes[i], select);
362     }
363 }
364 
any_selected() const365 bool PrinterPicker::any_selected() const
366 {
367     for (const auto &cb : cboxes) {
368         if (cb->GetValue()) { return true; }
369     }
370 
371     for (const auto &cb : cboxes_alt) {
372         if (cb->GetValue()) { return true; }
373     }
374 
375     return false;
376 }
377 
get_selected_models() const378 std::set<std::string> PrinterPicker::get_selected_models() const
379 {
380     std::set<std::string> ret_set;
381 
382     for (const auto& cb : cboxes)
383         if (cb->GetValue())
384             ret_set.emplace(cb->model);
385 
386     for (const auto& cb : cboxes_alt)
387         if (cb->GetValue())
388             ret_set.emplace(cb->model);
389 
390     return ret_set;
391 }
392 
on_checkbox(const Checkbox * cbox,bool checked)393 void PrinterPicker::on_checkbox(const Checkbox *cbox, bool checked)
394 {
395     PrinterPickerEvent evt(EVT_PRINTER_PICK, GetId(), vendor_id, cbox->model, cbox->variant, checked);
396     AddPendingEvent(evt);
397 }
398 
399 
400 // Wizard page base
401 
ConfigWizardPage(ConfigWizard * parent,wxString title,wxString shortname,unsigned indent)402 ConfigWizardPage::ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname, unsigned indent)
403     : wxPanel(parent->p->hscroll)
404     , parent(parent)
405     , shortname(std::move(shortname))
406     , indent(indent)
407 {
408     auto *sizer = new wxBoxSizer(wxVERTICAL);
409 
410     auto *text = new wxStaticText(this, wxID_ANY, std::move(title), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
411     const auto font = GetFont().MakeBold().Scaled(1.5);
412     text->SetFont(font);
413     sizer->Add(text, 0, wxALIGN_LEFT, 0);
414     sizer->AddSpacer(10);
415 
416     content = new wxBoxSizer(wxVERTICAL);
417     sizer->Add(content, 1, wxEXPAND);
418 
419     SetSizer(sizer);
420 
421     // There is strange layout on Linux with GTK3,
422     // see https://github.com/prusa3d/PrusaSlicer/issues/5103 and https://github.com/prusa3d/PrusaSlicer/issues/4861
423     // So, non-active pages will be hidden later, on wxEVT_SHOW, after completed Layout() for all pages
424     if (!wxLinux_gtk3)
425         this->Hide();
426 
427     Bind(wxEVT_SIZE, [this](wxSizeEvent &event) {
428         this->Layout();
429         event.Skip();
430     });
431 }
432 
~ConfigWizardPage()433 ConfigWizardPage::~ConfigWizardPage() {}
434 
append_text(wxString text)435 wxStaticText* ConfigWizardPage::append_text(wxString text)
436 {
437     auto *widget = new wxStaticText(this, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
438     widget->Wrap(WRAP_WIDTH);
439     widget->SetMinSize(wxSize(WRAP_WIDTH, -1));
440     append(widget);
441     return widget;
442 }
443 
append_spacer(int space)444 void ConfigWizardPage::append_spacer(int space)
445 {
446     // FIXME: scaling
447     content->AddSpacer(space);
448 }
449 
450 
451 // Wizard pages
452 
PageWelcome(ConfigWizard * parent)453 PageWelcome::PageWelcome(ConfigWizard *parent)
454     : ConfigWizardPage(parent, from_u8((boost::format(
455 #ifdef __APPLE__
456             _utf8(L("Welcome to the %s Configuration Assistant"))
457 #else
458             _utf8(L("Welcome to the %s Configuration Wizard"))
459 #endif
460             ) % SLIC3R_APP_NAME).str()), _L("Welcome"))
461     , welcome_text(append_text(from_u8((boost::format(
462         _utf8(L("Hello, welcome to %s! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")))
463         % SLIC3R_APP_NAME
464         % _utf8(ConfigWizard::name())).str())
465     ))
466     , cbox_reset(append(
467         new wxCheckBox(this, wxID_ANY, _L("Remove user profiles (a snapshot will be taken beforehand)"))
468     ))
469 {
470     welcome_text->Hide();
471     cbox_reset->Hide();
472 }
473 
set_run_reason(ConfigWizard::RunReason run_reason)474 void PageWelcome::set_run_reason(ConfigWizard::RunReason run_reason)
475 {
476     const bool data_empty = run_reason == ConfigWizard::RR_DATA_EMPTY;
477     welcome_text->Show(data_empty);
478     cbox_reset->Show(!data_empty);
479 }
480 
481 
PagePrinters(ConfigWizard * parent,wxString title,wxString shortname,const VendorProfile & vendor,unsigned indent,Technology technology)482 PagePrinters::PagePrinters(ConfigWizard *parent,
483     wxString title,
484     wxString shortname,
485     const VendorProfile &vendor,
486     unsigned indent,
487     Technology technology)
488     : ConfigWizardPage(parent, std::move(title), std::move(shortname), indent)
489     , technology(technology)
490     , install(false)   // only used for 3rd party vendors
491 {
492     enum {
493         COL_SIZE = 200,
494     };
495 
496     AppConfig *appconfig = &this->wizard_p()->appconfig_new;
497 
498     const auto families = vendor.families();
499     for (const auto &family : families) {
500         const auto filter = [&](const VendorProfile::PrinterModel &model) {
501             return ((model.technology == ptFFF && technology & T_FFF)
502                     || (model.technology == ptSLA && technology & T_SLA))
503                 && model.family == family;
504         };
505 
506         if (std::find_if(vendor.models.begin(), vendor.models.end(), filter) == vendor.models.end()) {
507             continue;
508         }
509 
510         const auto picker_title = family.empty() ? wxString() : from_u8((boost::format(_utf8(L("%s Family"))) % family).str());
511         auto *picker = new PrinterPicker(this, vendor, picker_title, MAX_COLS, *appconfig, filter);
512 
513         picker->Bind(EVT_PRINTER_PICK, [this, appconfig](const PrinterPickerEvent &evt) {
514             appconfig->set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable);
515             wizard_p()->on_printer_pick(this, evt);
516         });
517 
518         append(new wxStaticLine(this));
519 
520         append(picker);
521         printer_pickers.push_back(picker);
522     }
523 }
524 
select_all(bool select,bool alternates)525 void PagePrinters::select_all(bool select, bool alternates)
526 {
527     for (auto picker : printer_pickers) {
528         picker->select_all(select, alternates);
529     }
530 }
531 
get_width() const532 int PagePrinters::get_width() const
533 {
534     return std::accumulate(printer_pickers.begin(), printer_pickers.end(), 0,
535         [](int acc, const PrinterPicker *picker) { return std::max(acc, picker->get_width()); });
536 }
537 
any_selected() const538 bool PagePrinters::any_selected() const
539 {
540     for (const auto *picker : printer_pickers) {
541         if (picker->any_selected()) { return true; }
542     }
543 
544     return false;
545 }
546 
get_selected_models()547 std::set<std::string> PagePrinters::get_selected_models()
548 {
549     std::set<std::string> ret_set;
550 
551     for (const auto *picker : printer_pickers)
552     {
553         std::set<std::string> tmp_models = picker->get_selected_models();
554         ret_set.insert(tmp_models.begin(), tmp_models.end());
555     }
556 
557     return ret_set;
558 }
559 
set_run_reason(ConfigWizard::RunReason run_reason)560 void PagePrinters::set_run_reason(ConfigWizard::RunReason run_reason)
561 {
562     if (technology == T_FFF
563         && (run_reason == ConfigWizard::RR_DATA_EMPTY || run_reason == ConfigWizard::RR_DATA_LEGACY)
564         && printer_pickers.size() > 0
565         && printer_pickers[0]->vendor_id == PresetBundle::PRUSA_BUNDLE) {
566         printer_pickers[0]->select_one(0, true);
567     }
568 }
569 
570 
571 const std::string PageMaterials::EMPTY;
572 
PageMaterials(ConfigWizard * parent,Materials * materials,wxString title,wxString shortname,wxString list1name)573 PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxString title, wxString shortname, wxString list1name)
574     : ConfigWizardPage(parent, std::move(title), std::move(shortname))
575     , materials(materials)
576 	, list_printer(new  StringList(this, wxLB_MULTIPLE))
577     , list_type(new StringList(this))
578     , list_vendor(new StringList(this))
579     , list_profile(new PresetList(this))
580 {
581     append_spacer(VERTICAL_SPACING);
582 
583     const int em = parent->em_unit();
584     const int list_h = 30*em;
585 
586 
587 	list_printer->SetMinSize(wxSize(23*em, list_h));
588     list_type->SetMinSize(wxSize(8*em, list_h));
589     list_vendor->SetMinSize(wxSize(13*em, list_h));
590     list_profile->SetMinSize(wxSize(23*em, list_h));
591 
592 
593 
594     grid = new wxFlexGridSizer(4, em/2, em);
595     grid->AddGrowableCol(3, 1);
596     grid->AddGrowableRow(1, 1);
597 
598 	grid->Add(new wxStaticText(this, wxID_ANY, _L("Printer:")));
599     grid->Add(new wxStaticText(this, wxID_ANY, list1name));
600     grid->Add(new wxStaticText(this, wxID_ANY, _L("Vendor:")));
601     grid->Add(new wxStaticText(this, wxID_ANY, _L("Profile:")));
602 
603 	grid->Add(list_printer, 0, wxEXPAND);
604     grid->Add(list_type, 0, wxEXPAND);
605     grid->Add(list_vendor, 0, wxEXPAND);
606     grid->Add(list_profile, 1, wxEXPAND);
607 
608     auto *btn_sizer = new wxBoxSizer(wxHORIZONTAL);
609     auto *sel_all = new wxButton(this, wxID_ANY, _L("All"));
610     auto *sel_none = new wxButton(this, wxID_ANY, _L("None"));
611     btn_sizer->Add(sel_all, 0, wxRIGHT, em / 2);
612     btn_sizer->Add(sel_none);
613 
614 
615     grid->Add(new wxBoxSizer(wxHORIZONTAL));
616     grid->Add(new wxBoxSizer(wxHORIZONTAL));
617     grid->Add(new wxBoxSizer(wxHORIZONTAL));
618     grid->Add(btn_sizer, 0, wxALIGN_RIGHT);
619 
620     append(grid, 1, wxEXPAND);
621 
622     append_spacer(VERTICAL_SPACING);
623 
624     html_window = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition,
625         wxSize(60 * em, 20 * em), wxHW_SCROLLBAR_AUTO);
626     append(html_window, 0, wxEXPAND);
627 
628 	list_printer->Bind(wxEVT_LISTBOX, [this](wxCommandEvent& evt) {
629 		update_lists(evt.GetInt(), list_type->GetSelection(), list_vendor->GetSelection());
630 		});
631     list_type->Bind(wxEVT_LISTBOX, [this](wxCommandEvent &) {
632         update_lists(list_printer->GetSelection(), list_type->GetSelection(), list_vendor->GetSelection());
633     });
634     list_vendor->Bind(wxEVT_LISTBOX, [this](wxCommandEvent &) {
635         update_lists(list_printer->GetSelection(), list_type->GetSelection(), list_vendor->GetSelection());
636     });
637 
638     list_profile->Bind(wxEVT_CHECKLISTBOX, [this](wxCommandEvent &evt) { select_material(evt.GetInt()); });
639     list_profile->Bind(wxEVT_LISTBOX, [this](wxCommandEvent& evt) { on_material_highlighted(evt.GetInt()); });
640 
641     sel_all->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { select_all(true); });
642     sel_none->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { select_all(false); });
643     /*
644     Bind(wxEVT_PAINT, [this](wxPaintEvent& evt) {on_paint();});
645 
646     list_profile->Bind(wxEVT_MOTION, [this](wxMouseEvent& evt) { on_mouse_move_on_profiles(evt); });
647     list_profile->Bind(wxEVT_ENTER_WINDOW, [this](wxMouseEvent& evt) { on_mouse_enter_profiles(evt); });
648     list_profile->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& evt) { on_mouse_leave_profiles(evt); });
649     */
650     reload_presets();
651     set_compatible_printers_html_window(std::vector<std::string>(), false);
652 }
on_paint()653 void PageMaterials::on_paint()
654 {
655 }
on_mouse_move_on_profiles(wxMouseEvent & evt)656 void PageMaterials::on_mouse_move_on_profiles(wxMouseEvent& evt)
657 {
658     const wxClientDC dc(list_profile);
659     const wxPoint pos = evt.GetLogicalPosition(dc);
660     int item = list_profile->HitTest(pos);
661     on_material_hovered(item);
662 }
on_mouse_enter_profiles(wxMouseEvent & evt)663 void PageMaterials::on_mouse_enter_profiles(wxMouseEvent& evt)
664 {}
on_mouse_leave_profiles(wxMouseEvent & evt)665 void PageMaterials::on_mouse_leave_profiles(wxMouseEvent& evt)
666 {
667     on_material_hovered(-1);
668 }
reload_presets()669 void PageMaterials::reload_presets()
670 {
671     clear();
672 
673 	list_printer->append(_L("(All)"), &EMPTY);
674     //list_printer->SetLabelMarkup("<b>bald</b>");
675 	for (const Preset* printer : materials->printers) {
676 		list_printer->append(printer->name, &printer->name);
677 	}
678     sort_list_data(list_printer, true, false);
679     if (list_printer->GetCount() > 0) {
680         list_printer->SetSelection(0);
681 		sel_printer_count_prev = wxNOT_FOUND;
682         sel_printer_item_prev = wxNOT_FOUND;
683         sel_type_prev = wxNOT_FOUND;
684         sel_vendor_prev = wxNOT_FOUND;
685         update_lists(0, 0, 0);
686     }
687 
688     presets_loaded = true;
689 }
690 
set_compatible_printers_html_window(const std::vector<std::string> & printer_names,bool all_printers)691 void PageMaterials::set_compatible_printers_html_window(const std::vector<std::string>& printer_names, bool all_printers)
692 {
693     const auto bgr_clr =
694 #if defined(__APPLE__)
695         wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
696 #else
697         wxSystemSettings::GetColour(wxSYS_COLOUR_MENU);
698 #endif
699     const auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
700     const auto text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
701     const auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
702     wxString first_line = _L("Filaments marked with <b>*</b> are <b>not</b> compatible with some installed printers.");
703     wxString text;
704     if (all_printers) {
705         wxString second_line = _L("All installed printers are compatible with the selected filament.");
706         text = wxString::Format(
707             "<html>"
708             "<style>"
709             "table{border-spacing: 1px;}"
710             "</style>"
711             "<body bgcolor= %s>"
712             "<font color=%s>"
713             "<font size=\"3\">"
714             "%s<br /><br />%s"
715             "</font>"
716             "</font>"
717             "</body>"
718             "</html>"
719             , bgr_clr_str
720             , text_clr_str
721             , first_line
722             , second_line
723             );
724     } else {
725         wxString second_line = _L("Only the following installed printers are compatible with the selected filament:");
726         text = wxString::Format(
727             "<html>"
728             "<style>"
729             "table{border-spacing: 1px;}"
730             "</style>"
731             "<body bgcolor= %s>"
732             "<font color=%s>"
733             "<font size=\"3\">"
734             "%s<br /><br />%s"
735             "<table>"
736             "<tr>"
737             , bgr_clr_str
738             , text_clr_str
739             , first_line
740             , second_line);
741         for (int i = 0; i < printer_names.size(); ++i)
742         {
743             text += wxString::Format("<td>%s</td>", boost::nowide::widen(printer_names[i]));
744             if (i % 3 == 2) {
745                 text += wxString::Format(
746                     "</tr>"
747                     "<tr>");
748             }
749         }
750         text += wxString::Format(
751             "</tr>"
752             "</table>"
753             "</font>"
754             "</font>"
755             "</body>"
756             "</html>"
757         );
758     }
759 
760     wxFont font = get_default_font_for_dpi(this, get_dpi_for_window(this));
761     const int fs = font.GetPointSize();
762     int size[] = { fs,fs,fs,fs,fs,fs,fs };
763     html_window->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
764     html_window->SetPage(text);
765 }
766 
clear_compatible_printers_label()767 void PageMaterials::clear_compatible_printers_label()
768 {
769     set_compatible_printers_html_window(std::vector<std::string>(), false);
770 }
771 
on_material_hovered(int sel_material)772 void PageMaterials::on_material_hovered(int sel_material)
773 {
774 
775 }
776 
on_material_highlighted(int sel_material)777 void PageMaterials::on_material_highlighted(int sel_material)
778 {
779     if (sel_material == last_hovered_item)
780         return;
781     if (sel_material == -1) {
782         clear_compatible_printers_label();
783         return;
784     }
785     last_hovered_item = sel_material;
786     std::vector<std::string> tabs;
787     tabs.push_back(std::string());
788     tabs.push_back(std::string());
789     tabs.push_back(std::string());
790     //selected material string
791     std::string material_name = list_profile->get_data(sel_material);
792     // get material preset
793     const std::vector<const Preset*> matching_materials = materials->get_presets_by_alias(material_name);
794     if (matching_materials.empty())
795     {
796         clear_compatible_printers_label();
797         return;
798     }
799     //find matching printers
800     std::vector<std::string> names;
801     for (const Preset* printer : materials->printers) {
802         for (const Preset* material : matching_materials) {
803             if (is_compatible_with_printer(PresetWithVendorProfile(*material, material->vendor), PresetWithVendorProfile(*printer, printer->vendor))) {
804                 names.push_back(printer->name);
805                 break;
806             }
807         }
808     }
809     set_compatible_printers_html_window(names, names.size() == materials->printers.size());
810 }
811 
update_lists(int sel_printer,int sel_type,int sel_vendor)812 void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
813 {
814 	wxWindowUpdateLocker freeze_guard(this);
815 	(void)freeze_guard;
816 
817 	wxArrayInt sel_printers;
818 	int sel_printers_count = list_printer->GetSelections(sel_printers);
819 
820 	if (sel_printers_count != sel_printer_count_prev || (sel_printers_count == 1 && sel_printer_item_prev != sel_printer && sel_printer != -1)) {
821         // Refresh type list
822 		list_type->Clear();
823 		list_type->append(_L("(All)"), &EMPTY);
824 		if (sel_printers_count > 0) {
825             // If all is selected with other printers
826             // unselect "all" or all printers depending on last value
827             if (sel_printers[0] == 0 && sel_printers_count > 1) {
828                 if (sel_printer == 0) {
829                     list_printer->SetSelection(wxNOT_FOUND);
830                     list_printer->SetSelection(0);
831                 } else {
832                     list_printer->SetSelection(0, false);
833                     sel_printers_count = list_printer->GetSelections(sel_printers);
834                 }
835             }
836 			if (sel_printers[0] != 0) {
837 				for (size_t i = 0; i < sel_printers_count; i++) {
838 					const std::string& printer_name = list_printer->get_data(sel_printers[i]);
839 					const Preset* printer = nullptr;
840 					for (const Preset* it : materials->printers) {
841 						if (it->name == printer_name) {
842 							printer = it;
843 							break;
844 						}
845 					}
846 					materials->filter_presets(printer, EMPTY, EMPTY, [this](const Preset* p) {
847 						const std::string& type = this->materials->get_type(p);
848 						if (list_type->find(type) == wxNOT_FOUND) {
849 							list_type->append(type, &type);
850 						}
851 						});
852 				}
853 			} else {
854                 //clear selection except "ALL"
855                 list_printer->SetSelection(wxNOT_FOUND);
856                 list_printer->SetSelection(0);
857                 sel_printers_count = list_printer->GetSelections(sel_printers);
858 
859 				materials->filter_presets(nullptr, EMPTY, EMPTY, [this](const Preset* p) {
860 					const std::string& type = this->materials->get_type(p);
861 					if (list_type->find(type) == wxNOT_FOUND) {
862 						list_type->append(type, &type);
863 					}
864 					});
865 			}
866             sort_list_data(list_type, true, true);
867 		}
868 
869 		sel_printer_count_prev = sel_printers_count;
870         sel_printer_item_prev = sel_printer;
871 		sel_type = 0;
872 		sel_type_prev = wxNOT_FOUND;
873 		list_type->SetSelection(sel_type);
874 		list_profile->Clear();
875 	}
876 
877 	if (sel_type != sel_type_prev) {
878 		// Refresh vendor list
879 
880 		// XXX: The vendor list is created with quadratic complexity here,
881 		// but the number of vendors is going to be very small this shouldn't be a problem.
882 
883 		list_vendor->Clear();
884 		list_vendor->append(_L("(All)"), &EMPTY);
885 		if (sel_printers_count != 0 && sel_type != wxNOT_FOUND) {
886 			const std::string& type = list_type->get_data(sel_type);
887 			// find printer preset
888 			for (size_t i = 0; i < sel_printers_count; i++) {
889 				const std::string& printer_name = list_printer->get_data(sel_printers[i]);
890 				const Preset* printer = nullptr;
891 				for (const Preset* it : materials->printers) {
892 					if (it->name == printer_name) {
893 						printer = it;
894 						break;
895 					}
896 				}
897 				materials->filter_presets(printer, type, EMPTY, [this](const Preset* p) {
898 					const std::string& vendor = this->materials->get_vendor(p);
899 					if (list_vendor->find(vendor) == wxNOT_FOUND) {
900 						list_vendor->append(vendor, &vendor);
901 					}
902 					});
903 			}
904             sort_list_data(list_vendor, true, false);
905 		}
906 
907 		sel_type_prev = sel_type;
908 		sel_vendor = 0;
909 		sel_vendor_prev = wxNOT_FOUND;
910 		list_vendor->SetSelection(sel_vendor);
911 		list_profile->Clear();
912 	}
913 
914 	if (sel_vendor != sel_vendor_prev) {
915 		// Refresh material list
916 
917 		list_profile->Clear();
918         clear_compatible_printers_label();
919 		if (sel_printers_count != 0 && sel_type != wxNOT_FOUND && sel_vendor != wxNOT_FOUND) {
920 			const std::string& type = list_type->get_data(sel_type);
921 			const std::string& vendor = list_vendor->get_data(sel_vendor);
922 			// finst printer preset
923             std::vector<ProfilePrintData> to_list;
924 			for (size_t i = 0; i < sel_printers_count; i++) {
925 				const std::string& printer_name = list_printer->get_data(sel_printers[i]);
926 				const Preset* printer = nullptr;
927 				for (const Preset* it : materials->printers) {
928 					if (it->name == printer_name) {
929 						printer = it;
930 						break;
931 					}
932 				}
933 
934 				materials->filter_presets(printer, type, vendor, [this, &to_list](const Preset* p) {
935 					bool was_checked = false;
936 					//size_t printer_counter = materials->get_printer_counter(p);
937 					int cur_i = list_profile->find(p->alias);
938                     bool emplace_to_to_list = false;
939 					if (cur_i == wxNOT_FOUND) {
940 						cur_i = list_profile->append(p->alias + (materials->get_omnipresent(p) ? "" : " *"), &p->alias);
941                         emplace_to_to_list = true;
942                     } else
943 						was_checked = list_profile->IsChecked(cur_i);
944 
945 					const std::string& section = materials->appconfig_section();
946 
947 					const bool checked = wizard_p()->appconfig_new.has(section, p->name);
948 					list_profile->Check(cur_i, checked || was_checked);
949                     if (emplace_to_to_list)
950                         to_list.emplace_back(p->alias, materials->get_omnipresent(p), checked || was_checked);
951 
952 					/* Update preset selection in config.
953 					 * If one preset from aliases bundle is selected,
954 					 * than mark all presets with this aliases as selected
955 					 * */
956 					if (checked && !was_checked)
957 						wizard_p()->update_presets_in_config(section, p->alias, true);
958 					else if (!checked && was_checked)
959 						wizard_p()->appconfig_new.set(section, p->name, "1");
960 					});
961 			}
962             sort_list_data(list_profile, to_list);
963 		}
964 
965 		sel_vendor_prev = sel_vendor;
966 	}
967 }
968 
sort_list_data(StringList * list,bool add_All_item,bool material_type_ordering)969 void PageMaterials::sort_list_data(StringList* list, bool add_All_item, bool material_type_ordering)
970 {
971 // get data from list
972 // sort data
973 // first should be <all>
974 // then prusa profiles
975 // then the rest
976 // in alphabetical order
977 
978     std::vector<std::reference_wrapper<const std::string>> prusa_profiles;
979     std::vector<std::reference_wrapper<const std::string>> other_profiles;
980     for (int i = 0 ; i < list->size(); ++i) {
981         const std::string& data = list->get_data(i);
982         if (data == EMPTY) // do not sort <all> item
983             continue;
984         if (!material_type_ordering && data.find("Prusa") != std::string::npos)
985             prusa_profiles.push_back(data);
986         else
987             other_profiles.push_back(data);
988     }
989     if(material_type_ordering) {
990 
991         const ConfigOptionDef* def = print_config_def.get("filament_type");
992         std::vector<std::string>enum_values = def->enum_values;
993         int end_of_sorted = 0;
994         for (size_t vals = 0; vals < enum_values.size(); vals++) {
995             for (size_t profs = end_of_sorted; profs < other_profiles.size(); profs++)
996             {
997                 // find instead compare because PET vs PETG
998                 if (other_profiles[profs].get().find(enum_values[vals]) != std::string::npos) {
999                     //swap
1000                     if(profs != end_of_sorted) {
1001                         std::reference_wrapper<const std::string> aux = other_profiles[end_of_sorted];
1002                         other_profiles[end_of_sorted] = other_profiles[profs];
1003                         other_profiles[profs] = aux;
1004                     }
1005                     end_of_sorted++;
1006                     break;
1007                 }
1008             }
1009         }
1010     } else {
1011         std::sort(prusa_profiles.begin(), prusa_profiles.end(), [](std::reference_wrapper<const std::string> a, std::reference_wrapper<const std::string> b) {
1012             return a.get() < b.get();
1013             });
1014         std::sort(other_profiles.begin(), other_profiles.end(), [](std::reference_wrapper<const std::string> a, std::reference_wrapper<const std::string> b) {
1015             return a.get() < b.get();
1016             });
1017     }
1018 
1019     list->Clear();
1020     if (add_All_item)
1021         list->append(_L("(All)"), &EMPTY);
1022     for (const auto& item : prusa_profiles)
1023         list->append(item, &const_cast<std::string&>(item.get()));
1024     for (const auto& item : other_profiles)
1025         list->append(item, &const_cast<std::string&>(item.get()));
1026 }
1027 
sort_list_data(PresetList * list,const std::vector<ProfilePrintData> & data)1028 void PageMaterials::sort_list_data(PresetList* list, const std::vector<ProfilePrintData>& data)
1029 {
1030     // sort data
1031     // then prusa profiles
1032     // then the rest
1033     // in alphabetical order
1034     std::vector<ProfilePrintData> prusa_profiles;
1035     std::vector<ProfilePrintData> other_profiles;
1036     //for (int i = 0; i < data.size(); ++i) {
1037     for (const auto& item : data) {
1038         const std::string& name = item.name;
1039         if (name.find("Prusa") != std::string::npos)
1040             prusa_profiles.emplace_back(item);
1041         else
1042             other_profiles.emplace_back(item);
1043     }
1044     std::sort(prusa_profiles.begin(), prusa_profiles.end(), [](ProfilePrintData a, ProfilePrintData b) {
1045         return a.name.get() < b.name.get();
1046         });
1047     std::sort(other_profiles.begin(), other_profiles.end(), [](ProfilePrintData a, ProfilePrintData b) {
1048         return a.name.get() < b.name.get();
1049         });
1050     list->Clear();
1051     //for (const auto& item : prusa_profiles)
1052     for (int i = 0; i < prusa_profiles.size(); ++i) {
1053         list->append(std::string(prusa_profiles[i].name) + (prusa_profiles[i].omnipresent ? "" : " *"), &const_cast<std::string&>(prusa_profiles[i].name.get()));
1054         list->Check(i, prusa_profiles[i].checked);
1055     }
1056     //for (const auto& item : other_profiles)
1057     for (int i = 0; i < other_profiles.size(); ++i) {
1058         list->append(std::string(other_profiles[i].name) + (other_profiles[i].omnipresent ? "" : " *"), &const_cast<std::string&>(other_profiles[i].name.get()));
1059         list->Check(i + prusa_profiles.size(), other_profiles[i].checked);
1060     }
1061 }
1062 
select_material(int i)1063 void PageMaterials::select_material(int i)
1064 {
1065     const bool checked = list_profile->IsChecked(i);
1066 
1067     const std::string& alias_key = list_profile->get_data(i);
1068     wizard_p()->update_presets_in_config(materials->appconfig_section(), alias_key, checked);
1069 }
1070 
select_all(bool select)1071 void PageMaterials::select_all(bool select)
1072 {
1073     wxWindowUpdateLocker freeze_guard(this);
1074     (void)freeze_guard;
1075 
1076     for (unsigned i = 0; i < list_profile->GetCount(); i++) {
1077         const bool current = list_profile->IsChecked(i);
1078         if (current != select) {
1079             list_profile->Check(i, select);
1080             select_material(i);
1081         }
1082     }
1083 }
1084 
clear()1085 void PageMaterials::clear()
1086 {
1087 	list_printer->Clear();
1088     list_type->Clear();
1089     list_vendor->Clear();
1090     list_profile->Clear();
1091 	sel_printer_count_prev = wxNOT_FOUND;
1092     sel_printer_item_prev = wxNOT_FOUND;
1093     sel_type_prev = wxNOT_FOUND;
1094     sel_vendor_prev = wxNOT_FOUND;
1095     presets_loaded = false;
1096 }
1097 
on_activate()1098 void PageMaterials::on_activate()
1099 {
1100     if (! presets_loaded) {
1101         wizard_p()->update_materials(materials->technology);
1102         reload_presets();
1103     }
1104     first_paint = true;
1105 }
1106 
1107 
1108 const char *PageCustom::default_profile_name = "My Settings";
1109 
PageCustom(ConfigWizard * parent)1110 PageCustom::PageCustom(ConfigWizard *parent)
1111     : ConfigWizardPage(parent, _L("Custom Printer Setup"), _L("Custom Printer"))
1112 {
1113     cb_custom = new wxCheckBox(this, wxID_ANY, _L("Define a custom printer profile"));
1114     tc_profile_name = new wxTextCtrl(this, wxID_ANY, default_profile_name);
1115     auto *label = new wxStaticText(this, wxID_ANY, _L("Custom profile name:"));
1116 
1117     tc_profile_name->Enable(false);
1118     tc_profile_name->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent &evt) {
1119         if (tc_profile_name->GetValue().IsEmpty()) {
1120             if (profile_name_prev.IsEmpty()) { tc_profile_name->SetValue(default_profile_name); }
1121             else { tc_profile_name->SetValue(profile_name_prev); }
1122         } else {
1123             profile_name_prev = tc_profile_name->GetValue();
1124         }
1125         evt.Skip();
1126     });
1127 
1128     cb_custom->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) {
1129         tc_profile_name->Enable(custom_wanted());
1130         wizard_p()->on_custom_setup(custom_wanted());
1131 
1132     });
1133 
1134     append(cb_custom);
1135     append(label);
1136     append(tc_profile_name);
1137 }
1138 
PageUpdate(ConfigWizard * parent)1139 PageUpdate::PageUpdate(ConfigWizard *parent)
1140     : ConfigWizardPage(parent, _L("Automatic updates"), _L("Updates"))
1141     , version_check(true)
1142     , preset_update(true)
1143 {
1144     const AppConfig *app_config = wxGetApp().app_config;
1145     auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
1146     boldfont.SetWeight(wxFONTWEIGHT_BOLD);
1147 
1148     auto *box_slic3r = new wxCheckBox(this, wxID_ANY, _L("Check for application updates"));
1149     box_slic3r->SetValue(app_config->get("version_check") == "1");
1150     append(box_slic3r);
1151     append_text(wxString::Format(_L(
1152         "If enabled, %s checks for new application versions online. When a new version becomes available, "
1153          "a notification is displayed at the next application startup (never during program usage). "
1154          "This is only a notification mechanisms, no automatic installation is done."), SLIC3R_APP_NAME));
1155 
1156     append_spacer(VERTICAL_SPACING);
1157 
1158     auto *box_presets = new wxCheckBox(this, wxID_ANY, _L("Update built-in Presets automatically"));
1159     box_presets->SetValue(app_config->get("preset_update") == "1");
1160     append(box_presets);
1161     append_text(wxString::Format(_L(
1162         "If enabled, %s downloads updates of built-in system presets in the background."
1163         "These updates are downloaded into a separate temporary location."
1164         "When a new preset version becomes available it is offered at application startup."), SLIC3R_APP_NAME));
1165     const auto text_bold = _L("Updates are never applied without user's consent and never overwrite user's customized settings.");
1166     auto *label_bold = new wxStaticText(this, wxID_ANY, text_bold);
1167     label_bold->SetFont(boldfont);
1168     label_bold->Wrap(WRAP_WIDTH);
1169     append(label_bold);
1170     append_text(_L("Additionally a backup snapshot of the whole configuration is created before an update is applied."));
1171 
1172     box_slic3r->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->version_check = event.IsChecked(); });
1173     box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); });
1174 }
1175 
PageReloadFromDisk(ConfigWizard * parent)1176 PageReloadFromDisk::PageReloadFromDisk(ConfigWizard* parent)
1177     : ConfigWizardPage(parent, _L("Reload from disk"), _L("Reload from disk"))
1178     , full_pathnames(false)
1179 {
1180     auto* box_pathnames = new wxCheckBox(this, wxID_ANY, _L("Export full pathnames of models and parts sources into 3mf and amf files"));
1181     box_pathnames->SetValue(wxGetApp().app_config->get("export_sources_full_pathnames") == "1");
1182     append(box_pathnames);
1183     append_text(_L(
1184         "If enabled, allows the Reload from disk command to automatically find and load the files when invoked.\n"
1185         "If not enabled, the Reload from disk command will ask to select each file using an open file dialog."
1186     ));
1187 
1188     box_pathnames->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) { this->full_pathnames = event.IsChecked(); });
1189 }
1190 
1191 #if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
1192 #ifdef _WIN32
PageFilesAssociation(ConfigWizard * parent)1193 PageFilesAssociation::PageFilesAssociation(ConfigWizard* parent)
1194     : ConfigWizardPage(parent, _L("Files association"), _L("Files association"))
1195 {
1196     cb_3mf = new wxCheckBox(this, wxID_ANY, _L("Associate .3mf files to PrusaSlicer"));
1197     cb_stl = new wxCheckBox(this, wxID_ANY, _L("Associate .stl files to PrusaSlicer"));
1198 //    cb_gcode = new wxCheckBox(this, wxID_ANY, _L("Associate .gcode files to PrusaSlicer G-code Viewer"));
1199 
1200     append(cb_3mf);
1201     append(cb_stl);
1202 //    append(cb_gcode);
1203 }
1204 #endif // _WIN32
1205 #endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
1206 
PageMode(ConfigWizard * parent)1207 PageMode::PageMode(ConfigWizard *parent)
1208     : ConfigWizardPage(parent, _L("View mode"), _L("View mode"))
1209 {
1210     append_text(_L("PrusaSlicer's user interfaces comes in three variants:\nSimple, Advanced, and Expert.\n"
1211         "The Simple mode shows only the most frequently used settings relevant for regular 3D printing. "
1212         "The other two offer progressively more sophisticated fine-tuning, "
1213         "they are suitable for advanced and expert users, respectively."));
1214 
1215     radio_simple = new wxRadioButton(this, wxID_ANY, _L("Simple mode"));
1216     radio_advanced = new wxRadioButton(this, wxID_ANY, _L("Advanced mode"));
1217     radio_expert = new wxRadioButton(this, wxID_ANY, _L("Expert mode"));
1218 
1219     append(radio_simple);
1220     append(radio_advanced);
1221     append(radio_expert);
1222 
1223     append_text("\n" + _L("The size of the object can be specified in inches"));
1224     check_inch = new wxCheckBox(this, wxID_ANY, _L("Use inches"));
1225     append(check_inch);
1226 }
1227 
on_activate()1228 void PageMode::on_activate()
1229 {
1230     std::string mode { "simple" };
1231     wxGetApp().app_config->get("", "view_mode", mode);
1232 
1233     if (mode == "advanced") { radio_advanced->SetValue(true); }
1234     else if (mode == "expert") { radio_expert->SetValue(true); }
1235     else { radio_simple->SetValue(true); }
1236 
1237     check_inch->SetValue(wxGetApp().app_config->get("use_inches") == "1");
1238 }
1239 
serialize_mode(AppConfig * app_config) const1240 void PageMode::serialize_mode(AppConfig *app_config) const
1241 {
1242     std::string mode = "";
1243 
1244     if (radio_simple->GetValue()) { mode = "simple"; }
1245     if (radio_advanced->GetValue()) { mode = "advanced"; }
1246     if (radio_expert->GetValue()) { mode = "expert"; }
1247 
1248     // If "Mode" page wasn't selected (no one radiobutton is checked),
1249     // we shouldn't to update a view_mode value in app_config
1250     if (mode.empty())
1251         return;
1252 
1253     app_config->set("view_mode", mode);
1254     app_config->set("use_inches", check_inch->GetValue() ? "1" : "0");
1255 }
1256 
PageVendors(ConfigWizard * parent)1257 PageVendors::PageVendors(ConfigWizard *parent)
1258     : ConfigWizardPage(parent, _L("Other Vendors"), _L("Other Vendors"))
1259 {
1260     const AppConfig &appconfig = this->wizard_p()->appconfig_new;
1261 
1262     append_text(wxString::Format(_L("Pick another vendor supported by %s"), SLIC3R_APP_NAME) + ":");
1263 
1264     auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
1265     boldfont.SetWeight(wxFONTWEIGHT_BOLD);
1266 
1267     for (const auto &pair : wizard_p()->bundles) {
1268         const VendorProfile *vendor = pair.second.vendor_profile;
1269         if (vendor->id == PresetBundle::PRUSA_BUNDLE) { continue; }
1270 
1271         auto *cbox = new wxCheckBox(this, wxID_ANY, vendor->name);
1272         cbox->Bind(wxEVT_CHECKBOX, [=](wxCommandEvent &event) {
1273             wizard_p()->on_3rdparty_install(vendor, cbox->IsChecked());
1274         });
1275 
1276         const auto &vendors = appconfig.vendors();
1277         const bool enabled = vendors.find(pair.first) != vendors.end();
1278         if (enabled) {
1279             cbox->SetValue(true);
1280 
1281             auto pages = wizard_p()->pages_3rdparty.find(vendor->id);
1282             wxCHECK_RET(pages != wizard_p()->pages_3rdparty.end(), "Internal error: 3rd party vendor printers page not created");
1283 
1284             for (PagePrinters* page : { pages->second.first, pages->second.second })
1285                 if (page) page->install = true;
1286         }
1287 
1288         append(cbox);
1289     }
1290 }
1291 
PageFirmware(ConfigWizard * parent)1292 PageFirmware::PageFirmware(ConfigWizard *parent)
1293     : ConfigWizardPage(parent, _L("Firmware Type"), _L("Firmware"), 1)
1294     , gcode_opt(*print_config_def.get("gcode_flavor"))
1295     , gcode_picker(nullptr)
1296 {
1297     append_text(_L("Choose the type of firmware used by your printer."));
1298     append_text(_(gcode_opt.tooltip));
1299 
1300     wxArrayString choices;
1301     choices.Alloc(gcode_opt.enum_labels.size());
1302     for (const auto &label : gcode_opt.enum_labels) {
1303         choices.Add(label);
1304     }
1305 
1306     gcode_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices);
1307     const auto &enum_values = gcode_opt.enum_values;
1308     auto needle = enum_values.cend();
1309     if (gcode_opt.default_value) {
1310         needle = std::find(enum_values.cbegin(), enum_values.cend(), gcode_opt.default_value->serialize());
1311     }
1312     if (needle != enum_values.cend()) {
1313         gcode_picker->SetSelection(needle - enum_values.cbegin());
1314     } else {
1315         gcode_picker->SetSelection(0);
1316     }
1317 
1318     append(gcode_picker);
1319 }
1320 
apply_custom_config(DynamicPrintConfig & config)1321 void PageFirmware::apply_custom_config(DynamicPrintConfig &config)
1322 {
1323     auto sel = gcode_picker->GetSelection();
1324     if (sel >= 0 && (size_t)sel < gcode_opt.enum_labels.size()) {
1325         auto *opt = new ConfigOptionEnum<GCodeFlavor>(static_cast<GCodeFlavor>(sel));
1326         config.set_key_value("gcode_flavor", opt);
1327     }
1328 }
1329 
PageBedShape(ConfigWizard * parent)1330 PageBedShape::PageBedShape(ConfigWizard *parent)
1331     : ConfigWizardPage(parent, _L("Bed Shape and Size"), _L("Bed Shape"), 1)
1332     , shape_panel(new BedShapePanel(this))
1333 {
1334     append_text(_L("Set the shape of your printer's bed."));
1335 
1336     shape_panel->build_panel(*wizard_p()->custom_config->option<ConfigOptionPoints>("bed_shape"),
1337         *wizard_p()->custom_config->option<ConfigOptionString>("bed_custom_texture"),
1338         *wizard_p()->custom_config->option<ConfigOptionString>("bed_custom_model"));
1339 
1340     append(shape_panel);
1341 }
1342 
apply_custom_config(DynamicPrintConfig & config)1343 void PageBedShape::apply_custom_config(DynamicPrintConfig &config)
1344 {
1345     const std::vector<Vec2d>& points = shape_panel->get_shape();
1346     const std::string& custom_texture = shape_panel->get_custom_texture();
1347     const std::string& custom_model = shape_panel->get_custom_model();
1348     config.set_key_value("bed_shape", new ConfigOptionPoints(points));
1349     config.set_key_value("bed_custom_texture", new ConfigOptionString(custom_texture));
1350     config.set_key_value("bed_custom_model", new ConfigOptionString(custom_model));
1351 }
1352 
PageDiameters(ConfigWizard * parent)1353 PageDiameters::PageDiameters(ConfigWizard *parent)
1354     : ConfigWizardPage(parent, _L("Filament and Nozzle Diameters"), _L("Print Diameters"), 1)
1355     , spin_nozzle(new wxSpinCtrlDouble(this, wxID_ANY))
1356     , spin_filam(new wxSpinCtrlDouble(this, wxID_ANY))
1357 {
1358     spin_nozzle->SetDigits(2);
1359     spin_nozzle->SetIncrement(0.1);
1360     auto *default_nozzle = print_config_def.get("nozzle_diameter")->get_default_value<ConfigOptionFloats>();
1361     spin_nozzle->SetValue(default_nozzle != nullptr && default_nozzle->size() > 0 ? default_nozzle->get_at(0) : 0.5);
1362 
1363     spin_filam->SetDigits(2);
1364     spin_filam->SetIncrement(0.25);
1365     auto *default_filam = print_config_def.get("filament_diameter")->get_default_value<ConfigOptionFloats>();
1366     spin_filam->SetValue(default_filam != nullptr && default_filam->size() > 0 ? default_filam->get_at(0) : 3.0);
1367 
1368     append_text(_L("Enter the diameter of your printer's hot end nozzle."));
1369 
1370     auto *sizer_nozzle = new wxFlexGridSizer(3, 5, 5);
1371     auto *text_nozzle = new wxStaticText(this, wxID_ANY, _L("Nozzle Diameter:"));
1372     auto *unit_nozzle = new wxStaticText(this, wxID_ANY, _L("mm"));
1373     sizer_nozzle->AddGrowableCol(0, 1);
1374     sizer_nozzle->Add(text_nozzle, 0, wxALIGN_CENTRE_VERTICAL);
1375     sizer_nozzle->Add(spin_nozzle);
1376     sizer_nozzle->Add(unit_nozzle, 0, wxALIGN_CENTRE_VERTICAL);
1377     append(sizer_nozzle);
1378 
1379     append_spacer(VERTICAL_SPACING);
1380 
1381     append_text(_L("Enter the diameter of your filament."));
1382     append_text(_L("Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average."));
1383 
1384     auto *sizer_filam = new wxFlexGridSizer(3, 5, 5);
1385     auto *text_filam = new wxStaticText(this, wxID_ANY, _L("Filament Diameter:"));
1386     auto *unit_filam = new wxStaticText(this, wxID_ANY, _L("mm"));
1387     sizer_filam->AddGrowableCol(0, 1);
1388     sizer_filam->Add(text_filam, 0, wxALIGN_CENTRE_VERTICAL);
1389     sizer_filam->Add(spin_filam);
1390     sizer_filam->Add(unit_filam, 0, wxALIGN_CENTRE_VERTICAL);
1391     append(sizer_filam);
1392 }
1393 
apply_custom_config(DynamicPrintConfig & config)1394 void PageDiameters::apply_custom_config(DynamicPrintConfig &config)
1395 {
1396     auto *opt_nozzle = new ConfigOptionFloats(1, spin_nozzle->GetValue());
1397     config.set_key_value("nozzle_diameter", opt_nozzle);
1398     auto *opt_filam = new ConfigOptionFloats(1, spin_filam->GetValue());
1399     config.set_key_value("filament_diameter", opt_filam);
1400 
1401     auto set_extrusion_width = [&config, opt_nozzle](const char *key, double dmr) {
1402         char buf[64];
1403         sprintf(buf, "%.2lf", dmr * opt_nozzle->values.front() / 0.4);
1404         config.set_key_value(key, new ConfigOptionFloatOrPercent(atof(buf), false));
1405     };
1406 
1407     set_extrusion_width("support_material_extrusion_width",   0.35);
1408     set_extrusion_width("top_infill_extrusion_width",		  0.40);
1409     set_extrusion_width("first_layer_extrusion_width",		  0.42);
1410 
1411     set_extrusion_width("extrusion_width",					  0.45);
1412     set_extrusion_width("perimeter_extrusion_width",		  0.45);
1413     set_extrusion_width("external_perimeter_extrusion_width", 0.45);
1414     set_extrusion_width("infill_extrusion_width",			  0.45);
1415     set_extrusion_width("solid_infill_extrusion_width",       0.45);
1416 }
1417 
PageTemperatures(ConfigWizard * parent)1418 PageTemperatures::PageTemperatures(ConfigWizard *parent)
1419     : ConfigWizardPage(parent, _L("Nozzle and Bed Temperatures"), _L("Temperatures"), 1)
1420     , spin_extr(new wxSpinCtrlDouble(this, wxID_ANY))
1421     , spin_bed(new wxSpinCtrlDouble(this, wxID_ANY))
1422 {
1423     spin_extr->SetIncrement(5.0);
1424     const auto &def_extr = *print_config_def.get("temperature");
1425     spin_extr->SetRange(def_extr.min, def_extr.max);
1426     auto *default_extr = def_extr.get_default_value<ConfigOptionInts>();
1427     spin_extr->SetValue(default_extr != nullptr && default_extr->size() > 0 ? default_extr->get_at(0) : 200);
1428 
1429     spin_bed->SetIncrement(5.0);
1430     const auto &def_bed = *print_config_def.get("bed_temperature");
1431     spin_bed->SetRange(def_bed.min, def_bed.max);
1432     auto *default_bed = def_bed.get_default_value<ConfigOptionInts>();
1433     spin_bed->SetValue(default_bed != nullptr && default_bed->size() > 0 ? default_bed->get_at(0) : 0);
1434 
1435     append_text(_L("Enter the temperature needed for extruding your filament."));
1436     append_text(_L("A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS."));
1437 
1438     auto *sizer_extr = new wxFlexGridSizer(3, 5, 5);
1439     auto *text_extr = new wxStaticText(this, wxID_ANY, _L("Extrusion Temperature:"));
1440     auto *unit_extr = new wxStaticText(this, wxID_ANY, _L("°C"));
1441     sizer_extr->AddGrowableCol(0, 1);
1442     sizer_extr->Add(text_extr, 0, wxALIGN_CENTRE_VERTICAL);
1443     sizer_extr->Add(spin_extr);
1444     sizer_extr->Add(unit_extr, 0, wxALIGN_CENTRE_VERTICAL);
1445     append(sizer_extr);
1446 
1447     append_spacer(VERTICAL_SPACING);
1448 
1449     append_text(_L("Enter the bed temperature needed for getting your filament to stick to your heated bed."));
1450     append_text(_L("A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have no heated bed."));
1451 
1452     auto *sizer_bed = new wxFlexGridSizer(3, 5, 5);
1453     auto *text_bed = new wxStaticText(this, wxID_ANY, _L("Bed Temperature:"));
1454     auto *unit_bed = new wxStaticText(this, wxID_ANY, _L("°C"));
1455     sizer_bed->AddGrowableCol(0, 1);
1456     sizer_bed->Add(text_bed, 0, wxALIGN_CENTRE_VERTICAL);
1457     sizer_bed->Add(spin_bed);
1458     sizer_bed->Add(unit_bed, 0, wxALIGN_CENTRE_VERTICAL);
1459     append(sizer_bed);
1460 }
1461 
apply_custom_config(DynamicPrintConfig & config)1462 void PageTemperatures::apply_custom_config(DynamicPrintConfig &config)
1463 {
1464     auto *opt_extr = new ConfigOptionInts(1, spin_extr->GetValue());
1465     config.set_key_value("temperature", opt_extr);
1466     auto *opt_extr1st = new ConfigOptionInts(1, spin_extr->GetValue());
1467     config.set_key_value("first_layer_temperature", opt_extr1st);
1468     auto *opt_bed = new ConfigOptionInts(1, spin_bed->GetValue());
1469     config.set_key_value("bed_temperature", opt_bed);
1470     auto *opt_bed1st = new ConfigOptionInts(1, spin_bed->GetValue());
1471     config.set_key_value("first_layer_bed_temperature", opt_bed1st);
1472 }
1473 
1474 
1475 // Index
1476 
ConfigWizardIndex(wxWindow * parent)1477 ConfigWizardIndex::ConfigWizardIndex(wxWindow *parent)
1478     : wxPanel(parent)
1479     , bg(ScalableBitmap(parent, "PrusaSlicer_192px_transparent.png", 192))
1480     , bullet_black(ScalableBitmap(parent, "bullet_black.png"))
1481     , bullet_blue(ScalableBitmap(parent, "bullet_blue.png"))
1482     , bullet_white(ScalableBitmap(parent, "bullet_white.png"))
1483     , item_active(NO_ITEM)
1484     , item_hover(NO_ITEM)
1485     , last_page((size_t)-1)
1486 {
1487     SetMinSize(bg.bmp().GetSize());
1488 
1489     const wxSize size = GetTextExtent("m");
1490     em_w = size.x;
1491     em_h = size.y;
1492 
1493     // Add logo bitmap.
1494     // This could be done in on_paint() along with the index labels, but I've found it tricky
1495     // to get the bitmap rendered well on all platforms with transparent background.
1496     // In some cases it didn't work at all. And so wxStaticBitmap is used here instead,
1497     // because it has all the platform quirks figured out.
1498     auto *sizer = new wxBoxSizer(wxVERTICAL);
1499     logo = new wxStaticBitmap(this, wxID_ANY, bg.bmp());
1500     sizer->AddStretchSpacer();
1501     sizer->Add(logo);
1502     SetSizer(sizer);
1503 
1504     Bind(wxEVT_PAINT, &ConfigWizardIndex::on_paint, this);
1505     Bind(wxEVT_MOTION, &ConfigWizardIndex::on_mouse_move, this);
1506 
1507     Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent &evt) {
1508         if (item_hover != -1) {
1509             item_hover = -1;
1510             Refresh();
1511         }
1512         evt.Skip();
1513     });
1514 
1515     Bind(wxEVT_LEFT_UP, [this](wxMouseEvent &evt) {
1516         if (item_hover >= 0) { go_to(item_hover); }
1517     });
1518 }
1519 
1520 wxDECLARE_EVENT(EVT_INDEX_PAGE, wxCommandEvent);
1521 
add_page(ConfigWizardPage * page)1522 void ConfigWizardIndex::add_page(ConfigWizardPage *page)
1523 {
1524     last_page = items.size();
1525     items.emplace_back(Item { page->shortname, page->indent, page });
1526     Refresh();
1527 }
1528 
add_label(wxString label,unsigned indent)1529 void ConfigWizardIndex::add_label(wxString label, unsigned indent)
1530 {
1531     items.emplace_back(Item { std::move(label), indent, nullptr });
1532     Refresh();
1533 }
1534 
active_page() const1535 ConfigWizardPage* ConfigWizardIndex::active_page() const
1536 {
1537     if (item_active >= items.size()) { return nullptr; }
1538 
1539     return items[item_active].page;
1540 }
1541 
go_prev()1542 void ConfigWizardIndex::go_prev()
1543 {
1544     // Search for a preceiding item that is a page (not a label, ie. page != nullptr)
1545 
1546     if (item_active == NO_ITEM) { return; }
1547 
1548     for (size_t i = item_active; i > 0; i--) {
1549         if (items[i - 1].page != nullptr) {
1550             go_to(i - 1);
1551             return;
1552         }
1553     }
1554 }
1555 
go_next()1556 void ConfigWizardIndex::go_next()
1557 {
1558     // Search for a next item that is a page (not a label, ie. page != nullptr)
1559 
1560     if (item_active == NO_ITEM) { return; }
1561 
1562     for (size_t i = item_active + 1; i < items.size(); i++) {
1563         if (items[i].page != nullptr) {
1564             go_to(i);
1565             return;
1566         }
1567     }
1568 }
1569 
1570 // This one actually performs the go-to op
go_to(size_t i)1571 void ConfigWizardIndex::go_to(size_t i)
1572 {
1573     if (i != item_active
1574         && i < items.size()
1575         && items[i].page != nullptr) {
1576         auto *new_active = items[i].page;
1577         auto *former_active = active_page();
1578         if (former_active != nullptr) {
1579             former_active->Hide();
1580         }
1581 
1582         item_active = i;
1583         new_active->Show();
1584 
1585         wxCommandEvent evt(EVT_INDEX_PAGE, GetId());
1586         AddPendingEvent(evt);
1587 
1588         Refresh();
1589 
1590         new_active->on_activate();
1591     }
1592 }
1593 
go_to(const ConfigWizardPage * page)1594 void ConfigWizardIndex::go_to(const ConfigWizardPage *page)
1595 {
1596     if (page == nullptr) { return; }
1597 
1598     for (size_t i = 0; i < items.size(); i++) {
1599         if (items[i].page == page) {
1600             go_to(i);
1601             return;
1602         }
1603     }
1604 }
1605 
clear()1606 void ConfigWizardIndex::clear()
1607 {
1608     auto *former_active = active_page();
1609     if (former_active != nullptr) { former_active->Hide(); }
1610 
1611     items.clear();
1612     item_active = NO_ITEM;
1613 }
1614 
on_paint(wxPaintEvent & evt)1615 void ConfigWizardIndex::on_paint(wxPaintEvent & evt)
1616 {
1617     const auto size = GetClientSize();
1618     if (size.GetHeight() == 0 || size.GetWidth() == 0) { return; }
1619 
1620     wxPaintDC dc(this);
1621 
1622     const auto bullet_w = bullet_black.bmp().GetSize().GetWidth();
1623     const auto bullet_h = bullet_black.bmp().GetSize().GetHeight();
1624     const int yoff_icon = bullet_h < em_h ? (em_h - bullet_h) / 2 : 0;
1625     const int yoff_text = bullet_h > em_h ? (bullet_h - em_h) / 2 : 0;
1626     const int yinc = item_height();
1627 
1628     int index_width = 0;
1629 
1630     unsigned y = 0;
1631     for (size_t i = 0; i < items.size(); i++) {
1632         const Item& item = items[i];
1633         unsigned x = em_w/2 + item.indent * em_w;
1634 
1635         if (i == item_active || (item_hover >= 0 && i == (size_t)item_hover)) {
1636             dc.DrawBitmap(bullet_blue.bmp(), x, y + yoff_icon, false);
1637         }
1638         else if (i < item_active)  { dc.DrawBitmap(bullet_black.bmp(), x, y + yoff_icon, false); }
1639         else if (i > item_active)  { dc.DrawBitmap(bullet_white.bmp(), x, y + yoff_icon, false); }
1640 
1641         x += + bullet_w + em_w/2;
1642         const auto text_size = dc.GetTextExtent(item.label);
1643         dc.DrawText(item.label, x, y + yoff_text);
1644 
1645         y += yinc;
1646         index_width = std::max(index_width, (int)x + text_size.x);
1647     }
1648 
1649     if (GetMinSize().x < index_width) {
1650         CallAfter([this, index_width]() {
1651             SetMinSize(wxSize(index_width, GetMinSize().y));
1652             Refresh();
1653         });
1654     }
1655 }
1656 
on_mouse_move(wxMouseEvent & evt)1657 void ConfigWizardIndex::on_mouse_move(wxMouseEvent &evt)
1658 {
1659     const wxClientDC dc(this);
1660     const wxPoint pos = evt.GetLogicalPosition(dc);
1661 
1662     const ssize_t item_hover_new = pos.y / item_height();
1663 
1664     if (item_hover_new < ssize_t(items.size()) && item_hover_new != item_hover) {
1665         item_hover = item_hover_new;
1666         Refresh();
1667     }
1668 
1669     evt.Skip();
1670 }
1671 
msw_rescale()1672 void ConfigWizardIndex::msw_rescale()
1673 {
1674     const wxSize size = GetTextExtent("m");
1675     em_w = size.x;
1676     em_h = size.y;
1677 
1678     bg.msw_rescale();
1679     SetMinSize(bg.bmp().GetSize());
1680     logo->SetBitmap(bg.bmp());
1681 
1682     bullet_black.msw_rescale();
1683     bullet_blue.msw_rescale();
1684     bullet_white.msw_rescale();
1685     Refresh();
1686 }
1687 
1688 
1689 // Materials
1690 
1691 const std::string Materials::UNKNOWN = "(Unknown)";
1692 
push(const Preset * preset)1693 void Materials::push(const Preset *preset)
1694 {
1695     presets.emplace_back(preset);
1696     types.insert(technology & T_FFF
1697         ? Materials::get_filament_type(preset)
1698         : Materials::get_material_type(preset));
1699 }
1700 
add_printer(const Preset * preset)1701 void  Materials::add_printer(const Preset* preset)
1702 {
1703 	printers.insert(preset);
1704 }
1705 
clear()1706 void Materials::clear()
1707 {
1708     presets.clear();
1709     types.clear();
1710 	printers.clear();
1711     compatibility_counter.clear();
1712 }
1713 
appconfig_section() const1714 const std::string& Materials::appconfig_section() const
1715 {
1716     return (technology & T_FFF) ? AppConfig::SECTION_FILAMENTS : AppConfig::SECTION_MATERIALS;
1717 }
1718 
get_type(const Preset * preset) const1719 const std::string& Materials::get_type(const Preset *preset) const
1720 {
1721     return (technology & T_FFF) ? get_filament_type(preset) : get_material_type(preset);
1722 }
1723 
get_vendor(const Preset * preset) const1724 const std::string& Materials::get_vendor(const Preset *preset) const
1725 {
1726     return (technology & T_FFF) ? get_filament_vendor(preset) : get_material_vendor(preset);
1727 }
1728 
get_filament_type(const Preset * preset)1729 const std::string& Materials::get_filament_type(const Preset *preset)
1730 {
1731     const auto *opt = preset->config.opt<ConfigOptionStrings>("filament_type");
1732     if (opt != nullptr && opt->values.size() > 0) {
1733         return opt->values[0];
1734     } else {
1735         return UNKNOWN;
1736     }
1737 }
1738 
get_filament_vendor(const Preset * preset)1739 const std::string& Materials::get_filament_vendor(const Preset *preset)
1740 {
1741     const auto *opt = preset->config.opt<ConfigOptionString>("filament_vendor");
1742     return opt != nullptr ? opt->value : UNKNOWN;
1743 }
1744 
get_material_type(const Preset * preset)1745 const std::string& Materials::get_material_type(const Preset *preset)
1746 {
1747     const auto *opt = preset->config.opt<ConfigOptionString>("material_type");
1748     if (opt != nullptr) {
1749         return opt->value;
1750     } else {
1751         return UNKNOWN;
1752     }
1753 }
1754 
get_material_vendor(const Preset * preset)1755 const std::string& Materials::get_material_vendor(const Preset *preset)
1756 {
1757     const auto *opt = preset->config.opt<ConfigOptionString>("material_vendor");
1758     return opt != nullptr ? opt->value : UNKNOWN;
1759 }
1760 
1761 // priv
1762 
1763 static const std::unordered_map<std::string, std::pair<std::string, std::string>> legacy_preset_map {{
1764     { "Original Prusa i3 MK2.ini",                           std::make_pair("MK2S", "0.4") },
1765     { "Original Prusa i3 MK2 MM Single Mode.ini",            std::make_pair("MK2SMM", "0.4") },
1766     { "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") },
1767     { "Original Prusa i3 MK2 MultiMaterial.ini",             std::make_pair("MK2SMM", "0.4") },
1768     { "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini",  std::make_pair("MK2SMM", "0.6") },
1769     { "Original Prusa i3 MK2 0.25 nozzle.ini",               std::make_pair("MK2S", "0.25") },
1770     { "Original Prusa i3 MK2 0.6 nozzle.ini",                std::make_pair("MK2S", "0.6") },
1771     { "Original Prusa i3 MK3.ini",                           std::make_pair("MK3",  "0.4") },
1772 }};
1773 
load_pages()1774 void ConfigWizard::priv::load_pages()
1775 {
1776     wxWindowUpdateLocker freeze_guard(q);
1777     (void)freeze_guard;
1778 
1779     const ConfigWizardPage *former_active = index->active_page();
1780 
1781     index->clear();
1782 
1783     index->add_page(page_welcome);
1784 
1785     // Printers
1786     index->add_page(page_fff);
1787     index->add_page(page_msla);
1788     index->add_page(page_vendors);
1789     for (const auto &pages : pages_3rdparty) {
1790         for ( PagePrinters* page : { pages.second.first, pages.second.second })
1791             if (page && page->install)
1792                 index->add_page(page);
1793     }
1794 
1795     index->add_page(page_custom);
1796     if (page_custom->custom_wanted()) {
1797         index->add_page(page_firmware);
1798         index->add_page(page_bed);
1799         index->add_page(page_diams);
1800         index->add_page(page_temps);
1801     }
1802 
1803     // Filaments & Materials
1804     if (any_fff_selected) { index->add_page(page_filaments); }
1805     if (any_sla_selected) { index->add_page(page_sla_materials); }
1806 
1807     // there should to be selected at least one printer
1808     btn_finish->Enable(any_fff_selected || any_sla_selected || custom_printer_selected);
1809 
1810     index->add_page(page_update);
1811     index->add_page(page_reload_from_disk);
1812 #if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
1813 #ifdef _WIN32
1814     index->add_page(page_files_association);
1815 #endif // _WIN32
1816 #endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
1817     index->add_page(page_mode);
1818 
1819     index->go_to(former_active);   // Will restore the active item/page if possible
1820 
1821     q->Layout();
1822 }
1823 
init_dialog_size()1824 void ConfigWizard::priv::init_dialog_size()
1825 {
1826     // Clamp the Wizard size based on screen dimensions
1827 
1828     const auto idx = wxDisplay::GetFromWindow(q);
1829     wxDisplay display(idx != wxNOT_FOUND ? idx : 0u);
1830 
1831     const auto disp_rect = display.GetClientArea();
1832     wxRect window_rect(
1833         disp_rect.x + disp_rect.width / 20,
1834         disp_rect.y + disp_rect.height / 20,
1835         9*disp_rect.width / 10,
1836         9*disp_rect.height / 10);
1837 
1838     const int width_hint = index->GetSize().GetWidth() + page_fff->get_width() + 30 * em();    // XXX: magic constant, I found no better solution
1839     if (width_hint < window_rect.width) {
1840         window_rect.x += (window_rect.width - width_hint) / 2;
1841         window_rect.width = width_hint;
1842     }
1843 
1844     q->SetSize(window_rect);
1845 }
1846 
load_vendors()1847 void ConfigWizard::priv::load_vendors()
1848 {
1849     bundles = BundleMap::load();
1850 
1851     // Load up the set of vendors / models / variants the user has had enabled up till now
1852     AppConfig *app_config = wxGetApp().app_config;
1853     if (! app_config->legacy_datadir()) {
1854         appconfig_new.set_vendors(*app_config);
1855     } else {
1856         // In case of legacy datadir, try to guess the preference based on the printer preset files that are present
1857         const auto printer_dir = fs::path(Slic3r::data_dir()) / "printer";
1858         for (auto &dir_entry : boost::filesystem::directory_iterator(printer_dir))
1859             if (Slic3r::is_ini_file(dir_entry)) {
1860                 auto needle = legacy_preset_map.find(dir_entry.path().filename().string());
1861                 if (needle == legacy_preset_map.end()) { continue; }
1862 
1863                 const auto &model = needle->second.first;
1864                 const auto &variant = needle->second.second;
1865                 appconfig_new.set_variant("PrusaResearch", model, variant, true);
1866             }
1867     }
1868 
1869     // Initialize the is_visible flag in printer Presets
1870     for (auto &pair : bundles) {
1871         pair.second.preset_bundle->load_installed_printers(appconfig_new);
1872     }
1873 
1874     // Copy installed filaments and SLA material names from app_config to appconfig_new
1875     // while resolving current names of profiles, which were renamed in the meantime.
1876     for (PrinterTechnology technology : { ptFFF, ptSLA }) {
1877     	const std::string &section_name = (technology == ptFFF) ? AppConfig::SECTION_FILAMENTS : AppConfig::SECTION_MATERIALS;
1878 		std::map<std::string, std::string> section_new;
1879 		if (app_config->has_section(section_name)) {
1880 			const std::map<std::string, std::string> &section_old = app_config->get_section(section_name);
1881 			for (const std::pair<std::string, std::string> &material_name_and_installed : section_old)
1882 				if (material_name_and_installed.second == "1") {
1883 					// Material is installed. Resolve it in bundles.
1884                     size_t num_found = 0;
1885 					const std::string &material_name = material_name_and_installed.first;
1886 				    for (auto &bundle : bundles) {
1887 				    	const PresetCollection &materials = bundle.second.preset_bundle->materials(technology);
1888 				    	const Preset           *preset    = materials.find_preset(material_name);
1889 				    	if (preset == nullptr) {
1890 				    		// Not found. Maybe the material preset is there, bu it was was renamed?
1891 							const std::string *new_name = materials.get_preset_name_renamed(material_name);
1892 							if (new_name != nullptr)
1893 								preset = materials.find_preset(*new_name);
1894 				    	}
1895                         if (preset != nullptr) {
1896                             // Materal preset was found, mark it as installed.
1897                             section_new[preset->name] = "1";
1898                             ++ num_found;
1899                         }
1900 				    }
1901                     if (num_found == 0)
1902             	        BOOST_LOG_TRIVIAL(error) << boost::format("Profile %1% was not found in installed vendor Preset Bundles.") % material_name;
1903                     else if (num_found > 1)
1904             	        BOOST_LOG_TRIVIAL(error) << boost::format("Profile %1% was found in %2% vendor Preset Bundles.") % material_name % num_found;
1905                 }
1906 		}
1907         appconfig_new.set_section(section_name, section_new);
1908     };
1909 }
1910 
add_page(ConfigWizardPage * page)1911 void ConfigWizard::priv::add_page(ConfigWizardPage *page)
1912 {
1913     const int proportion = (page->shortname == _L("Filaments")) || (page->shortname == _L("SLA Materials")) ? 1 : 0;
1914     hscroll_sizer->Add(page, proportion, wxEXPAND);
1915     all_pages.push_back(page);
1916 }
1917 
enable_next(bool enable)1918 void ConfigWizard::priv::enable_next(bool enable)
1919 {
1920     btn_next->Enable(enable);
1921     btn_finish->Enable(enable);
1922 }
1923 
set_start_page(ConfigWizard::StartPage start_page)1924 void ConfigWizard::priv::set_start_page(ConfigWizard::StartPage start_page)
1925 {
1926     switch (start_page) {
1927         case ConfigWizard::SP_PRINTERS:
1928             index->go_to(page_fff);
1929             btn_next->SetFocus();
1930             break;
1931         case ConfigWizard::SP_FILAMENTS:
1932             index->go_to(page_filaments);
1933             btn_finish->SetFocus();
1934             break;
1935         case ConfigWizard::SP_MATERIALS:
1936             index->go_to(page_sla_materials);
1937             btn_finish->SetFocus();
1938             break;
1939         default:
1940             index->go_to(page_welcome);
1941             btn_next->SetFocus();
1942             break;
1943     }
1944 }
1945 
create_3rdparty_pages()1946 void ConfigWizard::priv::create_3rdparty_pages()
1947 {
1948     for (const auto &pair : bundles) {
1949         const VendorProfile *vendor = pair.second.vendor_profile;
1950         if (vendor->id == PresetBundle::PRUSA_BUNDLE) { continue; }
1951 
1952         bool is_fff_technology = false;
1953         bool is_sla_technology = false;
1954 
1955         for (auto& model: vendor->models)
1956         {
1957             if (!is_fff_technology && model.technology == ptFFF)
1958                  is_fff_technology = true;
1959             if (!is_sla_technology && model.technology == ptSLA)
1960                  is_sla_technology = true;
1961         }
1962 
1963         PagePrinters* pageFFF = nullptr;
1964         PagePrinters* pageSLA = nullptr;
1965 
1966         if (is_fff_technology) {
1967             pageFFF = new PagePrinters(q, vendor->name + " " +_L("FFF Technology Printers"), vendor->name+" FFF", *vendor, 1, T_FFF);
1968             add_page(pageFFF);
1969         }
1970 
1971         if (is_sla_technology) {
1972             pageSLA = new PagePrinters(q, vendor->name + " " + _L("SLA Technology Printers"), vendor->name+" MSLA", *vendor, 1, T_SLA);
1973             add_page(pageSLA);
1974         }
1975 
1976         pages_3rdparty.insert({vendor->id, {pageFFF, pageSLA}});
1977     }
1978 }
1979 
set_run_reason(RunReason run_reason)1980 void ConfigWizard::priv::set_run_reason(RunReason run_reason)
1981 {
1982     this->run_reason = run_reason;
1983     for (auto &page : all_pages) {
1984         page->set_run_reason(run_reason);
1985     }
1986 }
1987 
update_materials(Technology technology)1988 void ConfigWizard::priv::update_materials(Technology technology)
1989 {
1990     if (any_fff_selected && (technology & T_FFF)) {
1991         filaments.clear();
1992         aliases_fff.clear();
1993         // Iterate filaments in all bundles
1994         for (const auto &pair : bundles) {
1995             for (const auto &filament : pair.second.preset_bundle->filaments) {
1996                 // Check if filament is already added
1997                 if (filaments.containts(&filament))
1998 					continue;
1999                 // Iterate printers in all bundles
2000                 for (const auto &printer : pair.second.preset_bundle->printers) {
2001 					if (!printer.is_visible || printer.printer_technology() != ptFFF)
2002 						continue;
2003                     // Filter out inapplicable printers
2004 					if (is_compatible_with_printer(PresetWithVendorProfile(filament, filament.vendor), PresetWithVendorProfile(printer, printer.vendor))) {
2005 						if (!filaments.containts(&filament)) {
2006 							filaments.push(&filament);
2007 							if (!filament.alias.empty())
2008 								aliases_fff[filament.alias].insert(filament.name);
2009 						}
2010 						filaments.add_printer(&printer);
2011                     }
2012 				}
2013 
2014             }
2015         }
2016         // count compatible printers
2017         for (const auto& preset : filaments.presets) {
2018 
2019             const auto filter = [preset](const std::pair<std::string, size_t> element) {
2020                 return preset->alias == element.first;
2021             };
2022             if (std::find_if(filaments.compatibility_counter.begin(), filaments.compatibility_counter.end(), filter) != filaments.compatibility_counter.end()) {
2023                 continue;
2024             }
2025             std::vector<size_t> idx_with_same_alias;
2026             for (size_t i = 0; i < filaments.presets.size(); ++i) {
2027                 if (preset->alias == filaments.presets[i]->alias)
2028                     idx_with_same_alias.push_back(i);
2029             }
2030             size_t counter = 0;
2031             for (const auto& printer : filaments.printers) {
2032                 if (!(*printer).is_visible || (*printer).printer_technology() != ptFFF)
2033                     continue;
2034                 bool compatible = false;
2035                 // Test otrher materials with same alias
2036                 for (size_t i = 0; i < idx_with_same_alias.size() && !compatible; ++i) {
2037                     const Preset& prst = *(filaments.presets[idx_with_same_alias[i]]);
2038                     const Preset& prntr = *printer;
2039                     if (is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) {
2040                         compatible = true;
2041                         break;
2042                     }
2043                 }
2044                 if (compatible)
2045                     counter++;
2046             }
2047             filaments.compatibility_counter.emplace_back(preset->alias, counter);
2048         }
2049     }
2050 
2051     if (any_sla_selected && (technology & T_SLA)) {
2052         sla_materials.clear();
2053         aliases_sla.clear();
2054 
2055         // Iterate SLA materials in all bundles
2056         for (const auto &pair : bundles) {
2057             for (const auto &material : pair.second.preset_bundle->sla_materials) {
2058                 // Check if material is already added
2059                 if (sla_materials.containts(&material))
2060                 	continue;
2061                 // Iterate printers in all bundles
2062 				// For now, we only allow the profiles to be compatible with another profiles inside the same bundle.
2063                 for (const auto& printer : pair.second.preset_bundle->printers) {
2064                     if(!printer.is_visible || printer.printer_technology() != ptSLA)
2065                         continue;
2066                     // Filter out inapplicable printers
2067                     if (is_compatible_with_printer(PresetWithVendorProfile(material, nullptr), PresetWithVendorProfile(printer, nullptr))) {
2068                         // Check if material is already added
2069                         if(!sla_materials.containts(&material)) {
2070                             sla_materials.push(&material);
2071                             if (!material.alias.empty())
2072                                 aliases_sla[material.alias].insert(material.name);
2073                         }
2074                         sla_materials.add_printer(&printer);
2075                     }
2076                 }
2077             }
2078         }
2079         // count compatible printers
2080         for (const auto& preset : sla_materials.presets) {
2081 
2082             const auto filter = [preset](const std::pair<std::string, size_t> element) {
2083                 return preset->alias == element.first;
2084             };
2085             if (std::find_if(sla_materials.compatibility_counter.begin(), sla_materials.compatibility_counter.end(), filter) != sla_materials.compatibility_counter.end()) {
2086                 continue;
2087             }
2088             std::vector<size_t> idx_with_same_alias;
2089             for (size_t i = 0; i < sla_materials.presets.size(); ++i) {
2090                 if(preset->alias == sla_materials.presets[i]->alias)
2091                     idx_with_same_alias.push_back(i);
2092             }
2093             size_t counter = 0;
2094             for (const auto& printer : sla_materials.printers) {
2095                 if (!(*printer).is_visible || (*printer).printer_technology() != ptSLA)
2096                     continue;
2097                 bool compatible = false;
2098                 // Test otrher materials with same alias
2099                 for (size_t i = 0; i < idx_with_same_alias.size() && !compatible; ++i) {
2100                     const Preset& prst = *(sla_materials.presets[idx_with_same_alias[i]]);
2101                     const Preset& prntr = *printer;
2102                     if (is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) {
2103                         compatible = true;
2104                         break;
2105                     }
2106                 }
2107                 if (compatible)
2108                     counter++;
2109             }
2110             sla_materials.compatibility_counter.emplace_back(preset->alias, counter);
2111         }
2112     }
2113 }
2114 
on_custom_setup(const bool custom_wanted)2115 void ConfigWizard::priv::on_custom_setup(const bool custom_wanted)
2116 {
2117 	custom_printer_selected = custom_wanted;
2118     load_pages();
2119 }
2120 
on_printer_pick(PagePrinters * page,const PrinterPickerEvent & evt)2121 void ConfigWizard::priv::on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt)
2122 {
2123     if (check_sla_selected() != any_sla_selected ||
2124         check_fff_selected() != any_fff_selected) {
2125         any_fff_selected = check_fff_selected();
2126         any_sla_selected = check_sla_selected();
2127 
2128         load_pages();
2129     }
2130 
2131     // Update the is_visible flag on relevant printer profiles
2132     for (auto &pair : bundles) {
2133         if (pair.first != evt.vendor_id) { continue; }
2134 
2135         for (auto &preset : pair.second.preset_bundle->printers) {
2136             if (preset.config.opt_string("printer_model") == evt.model_id
2137                 && preset.config.opt_string("printer_variant") == evt.variant_name) {
2138                 preset.is_visible = evt.enable;
2139             }
2140         }
2141 
2142         // When a printer model is picked, but there is no material installed compatible with this printer model,
2143         // install default materials for selected printer model silently.
2144 		check_and_install_missing_materials(page->technology, evt.model_id);
2145     }
2146 
2147     if (page->technology & T_FFF) {
2148         page_filaments->clear();
2149     } else if (page->technology & T_SLA) {
2150         page_sla_materials->clear();
2151     }
2152 }
2153 
select_default_materials_for_printer_model(const VendorProfile::PrinterModel & printer_model,Technology technology)2154 void ConfigWizard::priv::select_default_materials_for_printer_model(const VendorProfile::PrinterModel &printer_model, Technology technology)
2155 {
2156     PageMaterials* page_materials = technology & T_FFF ? page_filaments : page_sla_materials;
2157     for (const std::string& material : printer_model.default_materials)
2158         appconfig_new.set(page_materials->materials->appconfig_section(), material, "1");
2159 }
2160 
select_default_materials_for_printer_models(Technology technology,const std::set<const VendorProfile::PrinterModel * > & printer_models)2161 void ConfigWizard::priv::select_default_materials_for_printer_models(Technology technology, const std::set<const VendorProfile::PrinterModel*> &printer_models)
2162 {
2163     PageMaterials     *page_materials    = technology & T_FFF ? page_filaments : page_sla_materials;
2164     const std::string &appconfig_section = page_materials->materials->appconfig_section();
2165 
2166     auto select_default_materials_for_printer_page = [this, appconfig_section, printer_models](PagePrinters *page_printers, Technology technology)
2167     {
2168         const std::string vendor_id = page_printers->get_vendor_id();
2169         for (auto& pair : bundles)
2170             if (pair.first == vendor_id)
2171             	for (const VendorProfile::PrinterModel *printer_model : printer_models)
2172     		        for (const std::string &material : printer_model->default_materials)
2173 			            appconfig_new.set(appconfig_section, material, "1");
2174     };
2175 
2176     PagePrinters* page_printers = technology & T_FFF ? page_fff : page_msla;
2177     select_default_materials_for_printer_page(page_printers, technology);
2178 
2179     for (const auto& printer : pages_3rdparty)
2180     {
2181         page_printers = technology & T_FFF ? printer.second.first : printer.second.second;
2182         if (page_printers)
2183             select_default_materials_for_printer_page(page_printers, technology);
2184     }
2185 
2186     update_materials(technology);
2187     ((technology & T_FFF) ? page_filaments : page_sla_materials)->reload_presets();
2188 }
2189 
on_3rdparty_install(const VendorProfile * vendor,bool install)2190 void ConfigWizard::priv::on_3rdparty_install(const VendorProfile *vendor, bool install)
2191 {
2192     auto it = pages_3rdparty.find(vendor->id);
2193     wxCHECK_RET(it != pages_3rdparty.end(), "Internal error: GUI page not found for 3rd party vendor profile");
2194 
2195     for (PagePrinters* page : { it->second.first, it->second.second })
2196         if (page) {
2197             if (page->install && !install)
2198                 page->select_all(false);
2199             page->install = install;
2200             // if some 3rd vendor is selected, select first printer for them
2201             if (install)
2202                 page->printer_pickers[0]->select_one(0, true);
2203             page->Layout();
2204         }
2205 
2206     load_pages();
2207 }
2208 
on_bnt_finish()2209 bool ConfigWizard::priv::on_bnt_finish()
2210 {
2211     /* When Filaments or Sla Materials pages are activated,
2212      * materials for this pages are automaticaly updated and presets are reloaded.
2213      *
2214      * But, if _Finish_ button was clicked without activation of those pages
2215      * (for example, just some printers were added/deleted),
2216      * than last changes wouldn't be updated for filaments/materials.
2217      * SO, do that before close of Wizard
2218      */
2219     update_materials(T_ANY);
2220     if (any_fff_selected)
2221         page_filaments->reload_presets();
2222     if (any_sla_selected)
2223         page_sla_materials->reload_presets();
2224 
2225 	// theres no need to check that filament is selected if we have only custom printer
2226     if (custom_printer_selected && !any_fff_selected && !any_sla_selected) return true;
2227     // check, that there is selected at least one filament/material
2228     return check_and_install_missing_materials(T_ANY);
2229 }
2230 
2231 // This allmighty method verifies, whether there is at least a single compatible filament or SLA material installed
2232 // for each Printer preset of each Printer Model installed.
2233 //
2234 // In case only_for_model_id is set, then the test is done for that particular printer model only, and the default materials are installed silently.
2235 // Otherwise the user is quieried whether to install the missing default materials or not.
2236 //
2237 // Return true if the tested Printer Models already had materials installed.
2238 // Return false if there were some Printer Models with missing materials, independent from whether the defaults were installed for these
2239 // respective Printer Models or not.
check_and_install_missing_materials(Technology technology,const std::string & only_for_model_id)2240 bool ConfigWizard::priv::check_and_install_missing_materials(Technology technology, const std::string &only_for_model_id)
2241 {
2242 	// Walk over all installed Printer presets and verify whether there is a filament or SLA material profile installed at the same PresetBundle,
2243 	// which is compatible with it.
2244     const auto printer_models_missing_materials = [this, only_for_model_id](PrinterTechnology technology, const std::string &section)
2245     {
2246 		const std::map<std::string, std::string> &appconfig_presets = appconfig_new.has_section(section) ? appconfig_new.get_section(section) : std::map<std::string, std::string>();
2247     	std::set<const VendorProfile::PrinterModel*> printer_models_without_material;
2248         for (const auto &pair : bundles) {
2249         	const PresetCollection &materials = pair.second.preset_bundle->materials(technology);
2250         	for (const auto &printer : pair.second.preset_bundle->printers) {
2251                 if (printer.is_visible && printer.printer_technology() == technology) {
2252 	            	const VendorProfile::PrinterModel *printer_model = PresetUtils::system_printer_model(printer);
2253 	            	assert(printer_model != nullptr);
2254 	            	if ((only_for_model_id.empty() || only_for_model_id == printer_model->id) &&
2255 	            		printer_models_without_material.find(printer_model) == printer_models_without_material.end()) {
2256                     	bool has_material = false;
2257 			            for (const std::pair<std::string, std::string> &preset : appconfig_presets) {
2258 			            	if (preset.second == "1") {
2259 			            		const Preset *material = materials.find_preset(preset.first, false);
2260 			            		if (material != nullptr && is_compatible_with_printer(PresetWithVendorProfile(*material, nullptr), PresetWithVendorProfile(printer, nullptr))) {
2261 				                	has_material = true;
2262 				                    break;
2263 				                }
2264 			                }
2265 			            }
2266 			            if (! has_material)
2267 			            	printer_models_without_material.insert(printer_model);
2268 			        }
2269                 }
2270             }
2271         }
2272         assert(printer_models_without_material.empty() || only_for_model_id.empty() || only_for_model_id == (*printer_models_without_material.begin())->id);
2273         return printer_models_without_material;
2274     };
2275 
2276     const auto ask_and_select_default_materials = [this](const wxString &message, const std::set<const VendorProfile::PrinterModel*> &printer_models, Technology technology)
2277     {
2278         wxMessageDialog msg(q, message, _L("Notice"), wxYES_NO);
2279         if (msg.ShowModal() == wxID_YES)
2280             select_default_materials_for_printer_models(technology, printer_models);
2281     };
2282 
2283     const auto printer_model_list = [](const std::set<const VendorProfile::PrinterModel*> &printer_models) -> wxString {
2284     	wxString out;
2285     	for (const VendorProfile::PrinterModel *printer_model : printer_models) {
2286             wxString name = from_u8(printer_model->name);
2287     		out += "\t\t";
2288     		out += name;
2289     		out += "\n";
2290     	}
2291     	return out;
2292     };
2293 
2294     if (any_fff_selected && (technology & T_FFF)) {
2295     	std::set<const VendorProfile::PrinterModel*> printer_models_without_material = printer_models_missing_materials(ptFFF, AppConfig::SECTION_FILAMENTS);
2296     	if (! printer_models_without_material.empty()) {
2297 			if (only_for_model_id.empty())
2298 				ask_and_select_default_materials(
2299 					_L("The following FFF printer models have no filament selected:") +
2300 					"\n\n\t" +
2301 					printer_model_list(printer_models_without_material) +
2302 					"\n\n\t" +
2303 					_L("Do you want to select default filaments for these FFF printer models?"),
2304 					printer_models_without_material,
2305 					T_FFF);
2306 			else
2307 				select_default_materials_for_printer_model(**printer_models_without_material.begin(), T_FFF);
2308 			return false;
2309 		}
2310     }
2311 
2312     if (any_sla_selected && (technology & T_SLA)) {
2313     	std::set<const VendorProfile::PrinterModel*> printer_models_without_material = printer_models_missing_materials(ptSLA, AppConfig::SECTION_MATERIALS);
2314     	if (! printer_models_without_material.empty()) {
2315 	        if (only_for_model_id.empty())
2316 	            ask_and_select_default_materials(
2317 					_L("The following SLA printer models have no materials selected:") +
2318 	            	"\n\n\t" +
2319 				   	printer_model_list(printer_models_without_material) +
2320 					"\n\n\t" +
2321 					_L("Do you want to select default SLA materials for these printer models?"),
2322 					printer_models_without_material,
2323 	            	T_SLA);
2324 	        else
2325 				select_default_materials_for_printer_model(**printer_models_without_material.begin(), T_SLA);
2326 	        return false;
2327 	    }
2328     }
2329 
2330     return true;
2331 }
2332 
apply_config(AppConfig * app_config,PresetBundle * preset_bundle,const PresetUpdater * updater)2333 bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater)
2334 {
2335     const auto enabled_vendors = appconfig_new.vendors();
2336 
2337     // Install bundles from resources if needed:
2338     std::vector<std::string> install_bundles;
2339     for (const auto &pair : bundles) {
2340         if (! pair.second.is_in_resources) { continue; }
2341 
2342         if (pair.second.is_prusa_bundle) {
2343             // Always install Prusa bundle, because it has a lot of filaments/materials
2344             // likely to be referenced by other profiles.
2345             install_bundles.emplace_back(pair.first);
2346             continue;
2347         }
2348 
2349         const auto vendor = enabled_vendors.find(pair.first);
2350         if (vendor == enabled_vendors.end()) { continue; }
2351 
2352         size_t size_sum = 0;
2353         for (const auto &model : vendor->second) { size_sum += model.second.size(); }
2354 
2355         if (size_sum > 0) {
2356             // This vendor needs to be installed
2357             install_bundles.emplace_back(pair.first);
2358         }
2359     }
2360 
2361     // Decide whether to create snapshot based on run_reason and the reset profile checkbox
2362     bool snapshot = true;
2363     Snapshot::Reason snapshot_reason = Snapshot::SNAPSHOT_UPGRADE;
2364     switch (run_reason) {
2365         case ConfigWizard::RR_DATA_EMPTY:
2366             snapshot = false;
2367             break;
2368         case ConfigWizard::RR_DATA_LEGACY:
2369             snapshot = true;
2370             break;
2371         case ConfigWizard::RR_DATA_INCOMPAT:
2372             // In this case snapshot has already been taken by
2373             // PresetUpdater with the appropriate reason
2374             snapshot = false;
2375             break;
2376         case ConfigWizard::RR_USER:
2377             snapshot = page_welcome->reset_user_profile();
2378             snapshot_reason = Snapshot::SNAPSHOT_USER;
2379             break;
2380     }
2381 
2382     if (snapshot && ! take_config_snapshot_cancel_on_error(*app_config, snapshot_reason, "", _u8L("Continue with applying configuration changes?")))
2383         return false;
2384 
2385     if (install_bundles.size() > 0) {
2386         // Install bundles from resources.
2387         // Don't create snapshot - we've already done that above if applicable.
2388         if (! updater->install_bundles_rsrc(std::move(install_bundles), false))
2389             return false;
2390     } else {
2391         BOOST_LOG_TRIVIAL(info) << "No bundles need to be installed from resources";
2392     }
2393 
2394     if (page_welcome->reset_user_profile()) {
2395         BOOST_LOG_TRIVIAL(info) << "Resetting user profiles...";
2396         preset_bundle->reset(true);
2397     }
2398 
2399     app_config->set_vendors(appconfig_new);
2400     if (appconfig_new.has_section(AppConfig::SECTION_FILAMENTS)) {
2401         app_config->set_section(AppConfig::SECTION_FILAMENTS, appconfig_new.get_section(AppConfig::SECTION_FILAMENTS));
2402     }
2403     if (appconfig_new.has_section(AppConfig::SECTION_MATERIALS)) {
2404         app_config->set_section(AppConfig::SECTION_MATERIALS, appconfig_new.get_section(AppConfig::SECTION_MATERIALS));
2405     }
2406     app_config->set("version_check", page_update->version_check ? "1" : "0");
2407     app_config->set("preset_update", page_update->preset_update ? "1" : "0");
2408     app_config->set("export_sources_full_pathnames", page_reload_from_disk->full_pathnames ? "1" : "0");
2409 
2410 #if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
2411 #ifdef _WIN32
2412     app_config->set("associate_3mf", page_files_association->associate_3mf() ? "1" : "0");
2413     app_config->set("associate_stl", page_files_association->associate_stl() ? "1" : "0");
2414 //    app_config->set("associate_gcode", page_files_association->associate_gcode() ? "1" : "0");
2415 
2416     if (wxGetApp().is_editor()) {
2417         if (page_files_association->associate_3mf())
2418             wxGetApp().associate_3mf_files();
2419         if (page_files_association->associate_stl())
2420             wxGetApp().associate_stl_files();
2421     }
2422 //    else {
2423 //        if (page_files_association->associate_gcode())
2424 //            wxGetApp().associate_gcode_files();
2425 //    }
2426 
2427 #endif // _WIN32
2428 #endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
2429 
2430     page_mode->serialize_mode(app_config);
2431 
2432     std::string preferred_model;
2433 
2434     // Figure out the default pre-selected printer based on the selections in the pickers.
2435     // The default is the first selected printer model (one with at least 1 variant selected).
2436     // The default is only applied by load_presets() if the user doesn't have a (visible) printer
2437     // selected already.
2438     // Prusa printers are considered first, then 3rd party.
2439     const auto config_prusa = enabled_vendors.find("PrusaResearch");
2440     if (config_prusa != enabled_vendors.end()) {
2441         for (const auto &model : bundles.prusa_bundle().vendor_profile->models) {
2442             const auto model_it = config_prusa->second.find(model.id);
2443             if (model_it != config_prusa->second.end() && model_it->second.size() > 0) {
2444                 preferred_model = model.id;
2445                 break;
2446             }
2447         }
2448     }
2449     if (preferred_model.empty()) {
2450         for (const auto &bundle : bundles) {
2451             if (bundle.second.is_prusa_bundle) { continue; }
2452 
2453             const auto config = enabled_vendors.find(bundle.first);
2454 			if (config == enabled_vendors.end()) { continue; }
2455             for (const auto &model : bundle.second.vendor_profile->models) {
2456                 const auto model_it = config->second.find(model.id);
2457                 if (model_it != config->second.end() && model_it->second.size() > 0) {
2458                     preferred_model = model.id;
2459                     break;
2460                 }
2461             }
2462         }
2463     }
2464 
2465     // Reloading the configs after some modifications were done to PrusaSlicer.ini.
2466     // Just perform the substitutions silently, as the substitutions were already presented to the user on application start-up
2467     // and the Wizard shall not create any new values that would require substitution.
2468     // Throw on substitutions in system profiles, as the system profiles provided over the air should be compatible with this PrusaSlicer version.
2469     preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem, preferred_model);
2470 
2471     if (page_custom->custom_wanted()) {
2472         page_firmware->apply_custom_config(*custom_config);
2473         page_bed->apply_custom_config(*custom_config);
2474         page_diams->apply_custom_config(*custom_config);
2475         page_temps->apply_custom_config(*custom_config);
2476 
2477         const std::string profile_name = page_custom->profile_name();
2478         preset_bundle->load_config_from_wizard(profile_name, *custom_config);
2479     }
2480 
2481     // Update the selections from the compatibilty.
2482     preset_bundle->export_selections(*app_config);
2483 
2484     return true;
2485 }
2486 
update_presets_in_config(const std::string & section,const std::string & alias_key,bool add)2487 void ConfigWizard::priv::update_presets_in_config(const std::string& section, const std::string& alias_key, bool add)
2488 {
2489     const PresetAliases& aliases = section == AppConfig::SECTION_FILAMENTS ? aliases_fff : aliases_sla;
2490 
2491     auto update = [this, add](const std::string& s, const std::string& key) {
2492     	assert(! s.empty());
2493         if (add)
2494             appconfig_new.set(s, key, "1");
2495         else
2496             appconfig_new.erase(s, key);
2497     };
2498 
2499     // add or delete presets had a same alias
2500     auto it = aliases.find(alias_key);
2501     if (it != aliases.end())
2502         for (const std::string& name : it->second)
2503             update(section, name);
2504 }
2505 
check_fff_selected()2506 bool ConfigWizard::priv::check_fff_selected()
2507 {
2508     bool ret = page_fff->any_selected();
2509     for (const auto& printer: pages_3rdparty)
2510         if (printer.second.first)               // FFF page
2511             ret |= printer.second.first->any_selected();
2512     return ret;
2513 }
2514 
check_sla_selected()2515 bool ConfigWizard::priv::check_sla_selected()
2516 {
2517     bool ret = page_msla->any_selected();
2518     for (const auto& printer: pages_3rdparty)
2519         if (printer.second.second)               // SLA page
2520             ret |= printer.second.second->any_selected();
2521     return ret;
2522 }
2523 
2524 
2525 // Public
2526 
ConfigWizard(wxWindow * parent)2527 ConfigWizard::ConfigWizard(wxWindow *parent)
2528     : DPIDialog(parent, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _(name()), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
2529     , p(new priv(this))
2530 {
2531     this->SetFont(wxGetApp().normal_font());
2532 
2533     p->load_vendors();
2534     p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({
2535         "gcode_flavor", "bed_shape", "bed_custom_texture", "bed_custom_model", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature",
2536     }));
2537 
2538     p->index = new ConfigWizardIndex(this);
2539 
2540     auto *vsizer = new wxBoxSizer(wxVERTICAL);
2541     auto *topsizer = new wxBoxSizer(wxHORIZONTAL);
2542     auto *hline = new wxStaticLine(this);
2543     p->btnsizer = new wxBoxSizer(wxHORIZONTAL);
2544 
2545     // Initially we _do not_ SetScrollRate in order to figure out the overall width of the Wizard  without scrolling.
2546     // Later, we compare that to the size of the current screen and set minimum width based on that (see below).
2547     p->hscroll = new wxScrolledWindow(this);
2548     p->hscroll_sizer = new wxBoxSizer(wxHORIZONTAL);
2549     p->hscroll->SetSizer(p->hscroll_sizer);
2550 
2551     topsizer->Add(p->index, 0, wxEXPAND);
2552     topsizer->AddSpacer(INDEX_MARGIN);
2553     topsizer->Add(p->hscroll, 1, wxEXPAND);
2554 
2555     p->btn_sel_all = new wxButton(this, wxID_ANY, _L("Select all standard printers"));
2556     p->btnsizer->Add(p->btn_sel_all);
2557 
2558     p->btn_prev = new wxButton(this, wxID_ANY, _L("< &Back"));
2559     p->btn_next = new wxButton(this, wxID_ANY, _L("&Next >"));
2560     p->btn_finish = new wxButton(this, wxID_APPLY, _L("&Finish"));
2561     p->btn_cancel = new wxButton(this, wxID_CANCEL, _L("Cancel"));   // Note: The label needs to be present, otherwise we get accelerator bugs on Mac
2562     p->btnsizer->AddStretchSpacer();
2563     p->btnsizer->Add(p->btn_prev, 0, wxLEFT, BTN_SPACING);
2564     p->btnsizer->Add(p->btn_next, 0, wxLEFT, BTN_SPACING);
2565     p->btnsizer->Add(p->btn_finish, 0, wxLEFT, BTN_SPACING);
2566     p->btnsizer->Add(p->btn_cancel, 0, wxLEFT, BTN_SPACING);
2567 
2568     const auto prusa_it = p->bundles.find("PrusaResearch");
2569     wxCHECK_RET(prusa_it != p->bundles.cend(), "Vendor PrusaResearch not found");
2570     const VendorProfile *vendor_prusa = prusa_it->second.vendor_profile;
2571 
2572     p->add_page(p->page_welcome = new PageWelcome(this));
2573 
2574     p->page_fff = new PagePrinters(this, _L("Prusa FFF Technology Printers"), "Prusa FFF", *vendor_prusa, 0, T_FFF);
2575     p->add_page(p->page_fff);
2576 
2577     p->page_msla = new PagePrinters(this, _L("Prusa MSLA Technology Printers"), "Prusa MSLA", *vendor_prusa, 0, T_SLA);
2578     p->add_page(p->page_msla);
2579 
2580 	// Pages for 3rd party vendors
2581 	p->create_3rdparty_pages();   // Needs to be done _before_ creating PageVendors
2582 	p->add_page(p->page_vendors = new PageVendors(this));
2583 	p->add_page(p->page_custom = new PageCustom(this));
2584 	p->custom_printer_selected = p->page_custom->custom_wanted();
2585 
2586     p->any_sla_selected = p->check_sla_selected();
2587     p->any_fff_selected = p->check_fff_selected();
2588 
2589     p->update_materials(T_ANY);
2590 
2591     p->add_page(p->page_filaments = new PageMaterials(this, &p->filaments,
2592         _L("Filament Profiles Selection"), _L("Filaments"), _L("Type:") ));
2593     p->add_page(p->page_sla_materials = new PageMaterials(this, &p->sla_materials,
2594         _L("SLA Material Profiles Selection") + " ", _L("SLA Materials"), _L("Type:") ));
2595 
2596 
2597     p->add_page(p->page_update   = new PageUpdate(this));
2598     p->add_page(p->page_reload_from_disk = new PageReloadFromDisk(this));
2599 #if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
2600 #ifdef _WIN32
2601     p->add_page(p->page_files_association = new PageFilesAssociation(this));
2602 #endif // _WIN32
2603 #endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
2604     p->add_page(p->page_mode     = new PageMode(this));
2605     p->add_page(p->page_firmware = new PageFirmware(this));
2606     p->add_page(p->page_bed      = new PageBedShape(this));
2607     p->add_page(p->page_diams    = new PageDiameters(this));
2608     p->add_page(p->page_temps    = new PageTemperatures(this));
2609 
2610     p->load_pages();
2611     p->index->go_to(size_t{0});
2612 
2613     vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN);
2614     vsizer->Add(hline, 0, wxEXPAND);
2615     vsizer->Add(p->btnsizer, 0, wxEXPAND | wxALL, DIALOG_MARGIN);
2616 
2617     SetSizer(vsizer);
2618     SetSizerAndFit(vsizer);
2619 
2620     // We can now enable scrolling on hscroll
2621     p->hscroll->SetScrollRate(30, 30);
2622 
2623     on_window_geometry(this, [this]() {
2624         p->init_dialog_size();
2625     });
2626 
2627     p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { this->p->index->go_prev(); });
2628 
2629     p->btn_next->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &)
2630     {
2631         // check, that there is selected at least one filament/material
2632         ConfigWizardPage* active_page = this->p->index->active_page();
2633         if (// Leaving the filaments or SLA materials page and
2634         	(active_page == p->page_filaments || active_page == p->page_sla_materials) &&
2635         	// some Printer models had no filament or SLA material selected.
2636         	! p->check_and_install_missing_materials(dynamic_cast<PageMaterials*>(active_page)->materials->technology))
2637         	// In that case don't leave the page and the function above queried the user whether to install default materials.
2638             return;
2639         this->p->index->go_next();
2640     });
2641 
2642     p->btn_finish->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &)
2643     {
2644         if (p->on_bnt_finish())
2645             this->EndModal(wxID_OK);
2646     });
2647 
2648     p->btn_sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) {
2649         p->any_sla_selected = true;
2650         p->load_pages();
2651         p->page_fff->select_all(true, false);
2652         p->page_msla->select_all(true, false);
2653         p->index->go_to(p->page_mode);
2654     });
2655 
2656     p->index->Bind(EVT_INDEX_PAGE, [this](const wxCommandEvent &) {
2657         const bool is_last = p->index->active_is_last();
2658         p->btn_next->Show(! is_last);
2659         if (is_last)
2660             p->btn_finish->SetFocus();
2661 
2662         Layout();
2663     });
2664 
2665     if (wxLinux_gtk3)
2666         this->Bind(wxEVT_SHOW, [this, vsizer](const wxShowEvent& e) {
2667             ConfigWizardPage* active_page = p->index->active_page();
2668             if (!active_page)
2669                 return;
2670             for (auto page : p->all_pages)
2671                 if (page != active_page)
2672                     page->Hide();
2673             // update best size for the dialog after hiding of the non-active pages
2674             vsizer->SetSizeHints(this);
2675             // set initial dialog size
2676             p->init_dialog_size();
2677         });
2678 }
2679 
~ConfigWizard()2680 ConfigWizard::~ConfigWizard() {}
2681 
run(RunReason reason,StartPage start_page)2682 bool ConfigWizard::run(RunReason reason, StartPage start_page)
2683 {
2684     BOOST_LOG_TRIVIAL(info) << boost::format("Running ConfigWizard, reason: %1%, start_page: %2%") % reason % start_page;
2685 
2686     GUI_App &app = wxGetApp();
2687 
2688     p->set_run_reason(reason);
2689     p->set_start_page(start_page);
2690 
2691     if (ShowModal() == wxID_OK) {
2692         if (! p->apply_config(app.app_config, app.preset_bundle, app.preset_updater))
2693             return false;
2694         app.app_config->set_legacy_datadir(false);
2695         app.update_mode();
2696         app.obj_manipul()->update_ui_from_settings();
2697         BOOST_LOG_TRIVIAL(info) << "ConfigWizard applied";
2698         return true;
2699     } else {
2700         BOOST_LOG_TRIVIAL(info) << "ConfigWizard cancelled";
2701         return false;
2702     }
2703 }
2704 
name(const bool from_menu)2705 const wxString& ConfigWizard::name(const bool from_menu/* = false*/)
2706 {
2707     // A different naming convention is used for the Wizard on Windows & GTK vs. OSX.
2708     // Note: Don't call _() macro here.
2709     //       This function just return the current name according to the OS.
2710     //       Translation is implemented inside GUI_App::add_config_menu()
2711 #if __APPLE__
2712     static const wxString config_wizard_name =  L("Configuration Assistant");
2713     static const wxString config_wizard_name_menu = L("Configuration &Assistant");
2714 #else
2715     static const wxString config_wizard_name = L("Configuration Wizard");
2716     static const wxString config_wizard_name_menu = L("Configuration &Wizard");
2717 #endif
2718     return from_menu ? config_wizard_name_menu : config_wizard_name;
2719 }
2720 
on_dpi_changed(const wxRect & suggested_rect)2721 void ConfigWizard::on_dpi_changed(const wxRect &suggested_rect)
2722 {
2723     p->index->msw_rescale();
2724 
2725     const int em = em_unit();
2726 
2727     msw_buttons_rescale(this, em, { wxID_APPLY,
2728                                     wxID_CANCEL,
2729                                     p->btn_sel_all->GetId(),
2730                                     p->btn_next->GetId(),
2731                                     p->btn_prev->GetId() });
2732 
2733     for (auto printer_picker: p->page_fff->printer_pickers)
2734         msw_buttons_rescale(this, em, printer_picker->get_button_indexes());
2735 
2736     p->init_dialog_size();
2737 
2738     Refresh();
2739 }
2740 
2741 }
2742 }
2743