1 #ifndef slic3r_ConfigWizard_private_hpp_
2 #define slic3r_ConfigWizard_private_hpp_
3 
4 #include "ConfigWizard.hpp"
5 
6 #include <vector>
7 #include <set>
8 #include <unordered_map>
9 #include <functional>
10 #include <boost/filesystem.hpp>
11 #include <boost/log/trivial.hpp>
12 
13 #include <wx/sizer.h>
14 #include <wx/panel.h>
15 #include <wx/button.h>
16 #include <wx/choice.h>
17 #include <wx/spinctrl.h>
18 #include <wx/textctrl.h>
19 #include <wx/listbox.h>
20 #include <wx/checklst.h>
21 #include <wx/radiobut.h>
22 #include <wx/html/htmlwin.h>
23 
24 #include "libslic3r/PrintConfig.hpp"
25 #include "libslic3r/PresetBundle.hpp"
26 #include "slic3r/Utils/PresetUpdater.hpp"
27 #include "BedShapeDialog.hpp"
28 #include "GUI.hpp"
29 #include "wxExtensions.hpp"
30 
31 
32 namespace fs = boost::filesystem;
33 
34 namespace Slic3r {
35 namespace GUI {
36 
37 enum {
38     WRAP_WIDTH = 500,
39     MODEL_MIN_WRAP = 150,
40 
41     DIALOG_MARGIN = 15,
42     INDEX_MARGIN = 40,
43     BTN_SPACING = 10,
44     INDENT_SPACING = 30,
45     VERTICAL_SPACING = 10,
46 
47     MAX_COLS = 4,
48     ROW_SPACING = 75,
49 };
50 
51 
52 
53 // Configuration data structures extensions needed for the wizard
54 
55 enum Technology {
56     // Bitflag equivalent of PrinterTechnology
57     T_FFF = 0x1,
58     T_SLA = 0x2,
59     T_ANY = ~0,
60 };
61 
62 struct Bundle
63 {
64 	std::unique_ptr<PresetBundle> preset_bundle;
65 	VendorProfile* vendor_profile{ nullptr };
66 	bool is_in_resources{ false };
67 	bool is_prusa_bundle{ false };
68 
69 	Bundle() = default;
70 	Bundle(Bundle&& other);
71 
72 	// Returns false if not loaded. Reason for that is logged as boost::log error.
73 	bool load(fs::path source_path, bool is_in_resources, bool is_prusa_bundle = false);
74 
vendor_idSlic3r::GUI::Bundle75 	const std::string& vendor_id() const { return vendor_profile->id; }
76 };
77 
78 struct BundleMap : std::unordered_map<std::string /* = vendor ID */, Bundle>
79 {
80 	static BundleMap load();
81 
82 	Bundle& prusa_bundle();
83 	const Bundle& prusa_bundle() const;
84 };
85 
86 struct Materials
87 {
88     Technology technology;
89     // use vector for the presets to purpose of save of presets sorting in the bundle
90     std::vector<const Preset*> presets;
91     // String is alias of material, size_t number of compatible counters
92     std::vector<std::pair<std::string, size_t>> compatibility_counter;
93     std::set<std::string> types;
94 	std::set<const Preset*> printers;
95 
MaterialsSlic3r::GUI::Materials96     Materials(Technology technology) : technology(technology) {}
97 
98     void push(const Preset *preset);
99 	void add_printer(const Preset* preset);
100     void clear();
containtsSlic3r::GUI::Materials101     bool containts(const Preset *preset) const {
102         //return std::find(presets.begin(), presets.end(), preset) != presets.end();
103 		return std::find_if(presets.begin(), presets.end(),
104 			[preset](const Preset* element) { return element == preset; }) != presets.end();
105 
106     }
107 
get_omnipresentSlic3r::GUI::Materials108 	bool get_omnipresent(const Preset* preset) {
109 		return get_printer_counter(preset) == printers.size();
110 	}
111 
get_presets_by_aliasSlic3r::GUI::Materials112     const std::vector<const Preset*> get_presets_by_alias(const std::string name) {
113         std::vector<const Preset*> ret_vec;
114         for (auto it = presets.begin(); it != presets.end(); ++it) {
115             if ((*it)->alias == name)
116                 ret_vec.push_back((*it));
117         }
118         return ret_vec;
119     }
120 
121 
122 
get_printer_counterSlic3r::GUI::Materials123 	size_t get_printer_counter(const Preset* preset) {
124 		for (auto it : compatibility_counter) {
125 			if (it.first == preset->alias)
126                 return it.second;
127         }
128 		return 0;
129 	}
130 
131     const std::string& appconfig_section() const;
132     const std::string& get_type(const Preset *preset) const;
133     const std::string& get_vendor(const Preset *preset) const;
134 
filter_presetsSlic3r::GUI::Materials135 	template<class F> void filter_presets(const Preset* printer, const std::string& type, const std::string& vendor, F cb) {
136 		for (auto preset : presets) {
137 			const Preset& prst = *(preset);
138 			const Preset& prntr = *printer;
139 		      if ((printer == nullptr || is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) &&
140 			    (type.empty() || get_type(preset) == type) &&
141 				(vendor.empty() || get_vendor(preset) == vendor)) {
142 
143 				cb(preset);
144 			}
145 		}
146 	}
147 
148     static const std::string UNKNOWN;
149     static const std::string& get_filament_type(const Preset *preset);
150     static const std::string& get_filament_vendor(const Preset *preset);
151     static const std::string& get_material_type(const Preset *preset);
152     static const std::string& get_material_vendor(const Preset *preset);
153 };
154 
155 
156 struct PrinterPickerEvent;
157 
158 // GUI elements
159 
160 typedef std::function<bool(const VendorProfile::PrinterModel&)> ModelFilter;
161 
162 struct PrinterPicker: wxPanel
163 {
164     struct Checkbox : wxCheckBox
165     {
CheckboxSlic3r::GUI::PrinterPicker::Checkbox166         Checkbox(wxWindow *parent, const wxString &label, const std::string &model, const std::string &variant) :
167             wxCheckBox(parent, wxID_ANY, label),
168             model(model),
169             variant(variant)
170         {}
171 
172         std::string model;
173         std::string variant;
174     };
175 
176     const std::string vendor_id;
177     std::vector<Checkbox*> cboxes;
178     std::vector<Checkbox*> cboxes_alt;
179 
180     PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig, const ModelFilter &filter);
181     PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig);
182 
183     void select_all(bool select, bool alternates = false);
184     void select_one(size_t i, bool select);
185     bool any_selected() const;
186     std::set<std::string> get_selected_models() const ;
187 
get_widthSlic3r::GUI::PrinterPicker188     int get_width() const { return width; }
get_button_indexesSlic3r::GUI::PrinterPicker189     const std::vector<int>& get_button_indexes() { return m_button_indexes; }
190 
191     static const std::string PRINTER_PLACEHOLDER;
192 private:
193     int width;
194     std::vector<int> m_button_indexes;
195 
196     void on_checkbox(const Checkbox *cbox, bool checked);
197 };
198 
199 struct ConfigWizardPage: wxPanel
200 {
201     ConfigWizard *parent;
202     const wxString shortname;
203     wxBoxSizer *content;
204     const unsigned indent;
205 
206     ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname, unsigned indent = 0);
207     virtual ~ConfigWizardPage();
208 
209     template<class T>
appendSlic3r::GUI::ConfigWizardPage210     T* append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10)
211     {
212         content->Add(thing, proportion, flag, border);
213         return thing;
214     }
215 
216     wxStaticText* append_text(wxString text);
217     void append_spacer(int space);
218 
wizard_pSlic3r::GUI::ConfigWizardPage219     ConfigWizard::priv *wizard_p() const { return parent->p.get(); }
220 
apply_custom_configSlic3r::GUI::ConfigWizardPage221     virtual void apply_custom_config(DynamicPrintConfig &config) {}
set_run_reasonSlic3r::GUI::ConfigWizardPage222     virtual void set_run_reason(ConfigWizard::RunReason run_reason) {}
on_activateSlic3r::GUI::ConfigWizardPage223     virtual void on_activate() {}
224 };
225 
226 struct PageWelcome: ConfigWizardPage
227 {
228     wxStaticText *welcome_text;
229     wxCheckBox *cbox_reset;
230 
231     PageWelcome(ConfigWizard *parent);
232 
reset_user_profileSlic3r::GUI::PageWelcome233     bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; }
234 
235     virtual void set_run_reason(ConfigWizard::RunReason run_reason) override;
236 };
237 
238 struct PagePrinters: ConfigWizardPage
239 {
240     std::vector<PrinterPicker *> printer_pickers;
241     Technology technology;
242     bool install;
243 
244     PagePrinters(ConfigWizard *parent,
245         wxString title,
246         wxString shortname,
247         const VendorProfile &vendor,
248         unsigned indent, Technology technology);
249 
250     void select_all(bool select, bool alternates = false);
251     int get_width() const;
252     bool any_selected() const;
253     std::set<std::string> get_selected_models();
254 
get_vendor_idSlic3r::GUI::PagePrinters255     std::string get_vendor_id() const { return printer_pickers.empty() ? "" : printer_pickers[0]->vendor_id; }
256 
257     virtual void set_run_reason(ConfigWizard::RunReason run_reason) override;
258 };
259 
260 // Here we extend wxListBox and wxCheckListBox
261 // to make the client data API much easier to use.
262 template<class T, class D> struct DataList : public T
263 {
DataListSlic3r::GUI::DataList264     DataList(wxWindow *parent) : T(parent, wxID_ANY) {}
DataListSlic3r::GUI::DataList265 	DataList(wxWindow* parent, int style) : T(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, style) {}
266 
267     // Note: We're _not_ using wxLB_SORT here because it doesn't do the right thing,
268     // eg. "ABS" is sorted before "(All)"
269 
appendSlic3r::GUI::DataList270     int append(const std::string &label, const D *data) {
271         void *ptr = reinterpret_cast<void*>(const_cast<D*>(data));
272         return this->Append(from_u8(label), ptr);
273     }
274 
appendSlic3r::GUI::DataList275     int append(const wxString &label, const D *data) {
276         void *ptr = reinterpret_cast<void*>(const_cast<D*>(data));
277         return this->Append(label, ptr);
278     }
279 
get_dataSlic3r::GUI::DataList280     const D& get_data(int n) {
281         return *reinterpret_cast<const D*>(this->GetClientData(n));
282     }
283 
findSlic3r::GUI::DataList284     int find(const D &data) {
285         for (unsigned i = 0; i < this->GetCount(); i++) {
286             if (get_data(i) == data) { return i; }
287         }
288 
289         return wxNOT_FOUND;
290     }
291 
sizeSlic3r::GUI::DataList292     int size() { return this->GetCount(); }
293 
on_mouse_moveSlic3r::GUI::DataList294     void on_mouse_move(const wxPoint& position) {
295         int item = T::HitTest(position);
296 
297         if(item == wxHitTest::wxHT_WINDOW_INSIDE)
298             BOOST_LOG_TRIVIAL(error) << "hit test wxHT_WINDOW_INSIDE";
299         else if (item == wxHitTest::wxHT_WINDOW_OUTSIDE)
300             BOOST_LOG_TRIVIAL(error) << "hit test wxHT_WINDOW_OUTSIDE";
301         else if(item == wxHitTest::wxHT_WINDOW_CORNER)
302             BOOST_LOG_TRIVIAL(error) << "hit test wxHT_WINDOW_CORNER";
303         else if (item == wxHitTest::wxHT_WINDOW_VERT_SCROLLBAR)
304             BOOST_LOG_TRIVIAL(error) << "hit test wxHT_WINDOW_VERT_SCROLLBAR";
305        else if (item == wxHitTest::wxHT_NOWHERE)
306             BOOST_LOG_TRIVIAL(error) << "hit test wxHT_NOWHERE";
307        else if (item == wxHitTest::wxHT_MAX)
308             BOOST_LOG_TRIVIAL(error) << "hit test wxHT_MAX";
309        else
310             BOOST_LOG_TRIVIAL(error) << "hit test: " << item;
311     }
312 };
313 
314 typedef DataList<wxListBox, std::string> StringList;
315 typedef DataList<wxCheckListBox, std::string> PresetList;
316 
317 struct ProfilePrintData
318 {
319     std::reference_wrapper<const std::string> name;
320     bool omnipresent;
321     bool checked;
ProfilePrintDataSlic3r::GUI::ProfilePrintData322     ProfilePrintData(const std::string& n, bool o, bool c) : name(n), omnipresent(o), checked(c) {}
323 };
324 
325 struct PageMaterials: ConfigWizardPage
326 {
327     Materials *materials;
328     StringList *list_printer, *list_type, *list_vendor;
329     PresetList *list_profile;
330     int sel_printer_count_prev, sel_printer_item_prev, sel_type_prev, sel_vendor_prev;
331     bool presets_loaded;
332 
333     wxFlexGridSizer *grid;
334     wxHtmlWindow* html_window;
335 
336     int compatible_printers_width = { 100 };
337     std::string empty_printers_label;
338     bool first_paint = { false };
339     static const std::string EMPTY;
340     int last_hovered_item = { -1 } ;
341 
342     PageMaterials(ConfigWizard *parent, Materials *materials, wxString title, wxString shortname, wxString list1name);
343 
344     void reload_presets();
345 	void update_lists(int sel1, int sel2, int sel3);
346 	void on_material_highlighted(int sel_material);
347     void on_material_hovered(int sel_material);
348     void select_material(int i);
349     void select_all(bool select);
350     void clear();
351     void set_compatible_printers_html_window(const std::vector<std::string>& printer_names, bool all_printers = false);
352     void clear_compatible_printers_label();
353 
354     void sort_list_data(StringList* list, bool add_All_item, bool material_type_ordering);
355     void sort_list_data(PresetList* list, const std::vector<ProfilePrintData>& data);
356 
357     void on_paint();
358     void on_mouse_move_on_profiles(wxMouseEvent& evt);
359     void on_mouse_enter_profiles(wxMouseEvent& evt);
360     void on_mouse_leave_profiles(wxMouseEvent& evt);
361     virtual void on_activate() override;
362 };
363 
364 struct PageCustom: ConfigWizardPage
365 {
366     PageCustom(ConfigWizard *parent);
367 
custom_wantedSlic3r::GUI::PageCustom368     bool custom_wanted() const { return cb_custom->GetValue(); }
profile_nameSlic3r::GUI::PageCustom369     std::string profile_name() const { return into_u8(tc_profile_name->GetValue()); }
370 
371 private:
372     static const char* default_profile_name;
373 
374     wxCheckBox *cb_custom;
375     wxTextCtrl *tc_profile_name;
376     wxString profile_name_prev;
377 
378 };
379 
380 struct PageUpdate: ConfigWizardPage
381 {
382     bool version_check;
383     bool preset_update;
384 
385     PageUpdate(ConfigWizard *parent);
386 };
387 
388 struct PageReloadFromDisk : ConfigWizardPage
389 {
390     bool full_pathnames;
391 
392     PageReloadFromDisk(ConfigWizard* parent);
393 };
394 
395 #if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
396 #ifdef _WIN32
397 struct PageFilesAssociation : ConfigWizardPage
398 {
399 private:
400     wxCheckBox* cb_3mf{ nullptr };
401     wxCheckBox* cb_stl{ nullptr };
402 //    wxCheckBox* cb_gcode;
403 
404 public:
405     PageFilesAssociation(ConfigWizard* parent);
406 
associate_3mfSlic3r::GUI::PageFilesAssociation407     bool associate_3mf() const { return cb_3mf->IsChecked(); }
associate_stlSlic3r::GUI::PageFilesAssociation408     bool associate_stl() const { return cb_stl->IsChecked(); }
409 //    bool associate_gcode() const { return cb_gcode->IsChecked(); }
410 };
411 #endif // _WIN32
412 #endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
413 
414 struct PageMode: ConfigWizardPage
415 {
416     wxRadioButton *radio_simple;
417     wxRadioButton *radio_advanced;
418     wxRadioButton *radio_expert;
419 
420     wxCheckBox    *check_inch;
421 
422     PageMode(ConfigWizard *parent);
423 
424     void serialize_mode(AppConfig *app_config) const;
425 
426     virtual void on_activate();
427 };
428 
429 struct PageVendors: ConfigWizardPage
430 {
431     PageVendors(ConfigWizard *parent);
432 };
433 
434 struct PageFirmware: ConfigWizardPage
435 {
436     const ConfigOptionDef &gcode_opt;
437     wxChoice *gcode_picker;
438 
439     PageFirmware(ConfigWizard *parent);
440     virtual void apply_custom_config(DynamicPrintConfig &config);
441 };
442 
443 struct PageBedShape: ConfigWizardPage
444 {
445     BedShapePanel *shape_panel;
446 
447     PageBedShape(ConfigWizard *parent);
448     virtual void apply_custom_config(DynamicPrintConfig &config);
449 };
450 
451 struct PageDiameters: ConfigWizardPage
452 {
453     wxSpinCtrlDouble *spin_nozzle;
454     wxSpinCtrlDouble *spin_filam;
455 
456     PageDiameters(ConfigWizard *parent);
457     virtual void apply_custom_config(DynamicPrintConfig &config);
458 };
459 
460 struct PageTemperatures: ConfigWizardPage
461 {
462     wxSpinCtrlDouble *spin_extr;
463     wxSpinCtrlDouble *spin_bed;
464 
465     PageTemperatures(ConfigWizard *parent);
466     virtual void apply_custom_config(DynamicPrintConfig &config);
467 };
468 
469 // hypothetically, each vendor can has printers both of technologies (FFF and SLA)
470 typedef std::map<std::string /* = vendor ID */,
471                  std::pair<PagePrinters* /* = FFF page */,
472                            PagePrinters* /* = SLA page */>> Pages3rdparty;
473 
474 
475 class ConfigWizardIndex: public wxPanel
476 {
477 public:
478     ConfigWizardIndex(wxWindow *parent);
479 
480     void add_page(ConfigWizardPage *page);
481     void add_label(wxString label, unsigned indent = 0);
482 
active_item() const483     size_t active_item() const { return item_active; }
484     ConfigWizardPage* active_page() const;
active_is_last() const485     bool active_is_last() const { return item_active < items.size() && item_active == last_page; }
486 
487     void go_prev();
488     void go_next();
489     void go_to(size_t i);
490     void go_to(const ConfigWizardPage *page);
491 
492     void clear();
493     void msw_rescale();
494 
em() const495     int em() const { return em_w; }
496 
497     static const size_t NO_ITEM = size_t(-1);
498 private:
499     struct Item
500     {
501         wxString label;
502         unsigned indent;
503         ConfigWizardPage *page;     // nullptr page => label-only item
504 
operator ==Slic3r::GUI::ConfigWizardIndex::Item505         bool operator==(ConfigWizardPage *page) const { return this->page == page; }
506     };
507 
508     int em_w;
509     int em_h;
510     ScalableBitmap bg;
511     ScalableBitmap bullet_black;
512     ScalableBitmap bullet_blue;
513     ScalableBitmap bullet_white;
514     wxStaticBitmap* logo;
515 
516     std::vector<Item> items;
517     size_t item_active;
518     ssize_t item_hover;
519     size_t last_page;
520 
item_height() const521     int item_height() const { return std::max(bullet_black.bmp().GetSize().GetHeight(), em_w) + em_w; }
522 
523     void on_paint(wxPaintEvent &evt);
524     void on_mouse_move(wxMouseEvent &evt);
525 };
526 
527 wxDEFINE_EVENT(EVT_INDEX_PAGE, wxCommandEvent);
528 
529 
530 
531 // ConfigWizard private data
532 
533 typedef std::map<std::string, std::set<std::string>> PresetAliases;
534 
535 struct ConfigWizard::priv
536 {
537     ConfigWizard *q;
538     ConfigWizard::RunReason run_reason = RR_USER;
539     AppConfig appconfig_new;      // Backing for vendor/model/variant and material selections in the GUI
540     BundleMap bundles;            // Holds all loaded config bundles, the key is the vendor names.
541                                   // Materials refers to Presets in those bundles by pointers.
542                                   // Also we update the is_visible flag in printer Presets according to the
543                                   // PrinterPickers state.
544     Materials filaments;          // Holds available filament presets and their types & vendors
545     Materials sla_materials;      // Ditto for SLA materials
546     PresetAliases aliases_fff;    // Map of aliase to preset names
547     PresetAliases aliases_sla;    // Map of aliase to preset names
548     std::unique_ptr<DynamicPrintConfig> custom_config;           // Backing for custom printer definition
549     bool any_fff_selected;        // Used to decide whether to display Filaments page
550     bool any_sla_selected;        // Used to decide whether to display SLA Materials page
551 	bool custom_printer_selected;
552 
553     wxScrolledWindow *hscroll = nullptr;
554     wxBoxSizer *hscroll_sizer = nullptr;
555     wxBoxSizer *btnsizer = nullptr;
556     ConfigWizardPage *page_current = nullptr;
557     ConfigWizardIndex *index = nullptr;
558     wxButton *btn_sel_all = nullptr;
559     wxButton *btn_prev = nullptr;
560     wxButton *btn_next = nullptr;
561     wxButton *btn_finish = nullptr;
562     wxButton *btn_cancel = nullptr;
563 
564     PageWelcome      *page_welcome = nullptr;
565     PagePrinters     *page_fff = nullptr;
566     PagePrinters     *page_msla = nullptr;
567     PageMaterials    *page_filaments = nullptr;
568     PageMaterials    *page_sla_materials = nullptr;
569     PageCustom       *page_custom = nullptr;
570     PageUpdate       *page_update = nullptr;
571     PageReloadFromDisk *page_reload_from_disk = nullptr;
572 #if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
573 #ifdef _WIN32
574     PageFilesAssociation* page_files_association = nullptr;
575 #endif // _WIN32
576 #endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
577     PageMode         *page_mode = nullptr;
578     PageVendors      *page_vendors = nullptr;
579     Pages3rdparty     pages_3rdparty;
580 
581     // Custom setup pages
582     PageFirmware     *page_firmware = nullptr;
583     PageBedShape     *page_bed = nullptr;
584     PageDiameters    *page_diams = nullptr;
585     PageTemperatures *page_temps = nullptr;
586 
587     // Pointers to all pages (regardless or whether currently part of the ConfigWizardIndex)
588     std::vector<ConfigWizardPage*> all_pages;
589 
privSlic3r::GUI::ConfigWizard::priv590     priv(ConfigWizard *q)
591         : q(q)
592         , appconfig_new(AppConfig::EAppMode::Editor)
593         , filaments(T_FFF)
594         , sla_materials(T_SLA)
595     {}
596 
597     void load_pages();
598     void init_dialog_size();
599 
600     void load_vendors();
601     void add_page(ConfigWizardPage *page);
602     void enable_next(bool enable);
603     void set_start_page(ConfigWizard::StartPage start_page);
604     void create_3rdparty_pages();
605     void set_run_reason(RunReason run_reason);
606     void update_materials(Technology technology);
607 
608     void on_custom_setup(const bool custom_wanted);
609     void on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt);
610     void select_default_materials_for_printer_model(const VendorProfile::PrinterModel &printer_model, Technology technology);
611     void select_default_materials_for_printer_models(Technology technology, const std::set<const VendorProfile::PrinterModel*> &printer_models);
612     void on_3rdparty_install(const VendorProfile *vendor, bool install);
613 
614     bool on_bnt_finish();
615     bool check_and_install_missing_materials(Technology technology, const std::string &only_for_model_id = std::string());
616     bool apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
617     // #ys_FIXME_alise
618     void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add);
619 
620     bool check_fff_selected();        // Used to decide whether to display Filaments page
621     bool check_sla_selected();        // Used to decide whether to display SLA Materials page
622 
emSlic3r::GUI::ConfigWizard::priv623     int em() const { return index->em(); }
624 };
625 
626 }
627 }
628 
629 #endif
630