diff --git a/resources/icons/printers/PrusaResearch_SL1.png b/resources/icons/printers/PrusaResearch_SL1.png index 281d7a620..3c15f95ea 100644 Binary files a/resources/icons/printers/PrusaResearch_SL1.png and b/resources/icons/printers/PrusaResearch_SL1.png differ diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 3471395c2..c4f10db36 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -13,6 +14,10 @@ #include #include #include +#include +#include +#include +#include #include "libslic3r/Utils.hpp" #include "PresetBundle.hpp" @@ -28,886 +33,1064 @@ namespace GUI { struct PrinterPickerEvent : public wxEvent { - std::string vendor_id; - std::string model_id; - std::string variant_name; - bool enable; + std::string vendor_id; + std::string model_id; + std::string variant_name; + bool enable; - PrinterPickerEvent(wxEventType eventType, int winid, std::string vendor_id, std::string model_id, std::string variant_name, bool enable) : - wxEvent(winid, eventType), - vendor_id(std::move(vendor_id)), - model_id(std::move(model_id)), - variant_name(std::move(variant_name)), - enable(enable) - {} + PrinterPickerEvent(wxEventType eventType, int winid, std::string vendor_id, std::string model_id, std::string variant_name, bool enable) + : wxEvent(winid, eventType) + , vendor_id(std::move(vendor_id)) + , model_id(std::move(model_id)) + , variant_name(std::move(variant_name)) + , enable(enable) + {} - virtual wxEvent *Clone() const - { - return new PrinterPickerEvent(*this); - } + virtual wxEvent *Clone() const + { + return new PrinterPickerEvent(*this); + } }; wxDEFINE_EVENT(EVT_PRINTER_PICK, PrinterPickerEvent); -PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, const AppConfig &appconfig_vendors) : - wxPanel(parent), - vendor_id(vendor.id), - variants_checked(0) +PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors, const ModelFilter &filter) + : wxPanel(parent) + , vendor_id(vendor.id) { - const auto &models = vendor.models; + const auto &models = vendor.models; - auto *sizer = new wxBoxSizer(wxVERTICAL); + auto *sizer = new wxBoxSizer(wxVERTICAL); - auto *printer_grid = new wxFlexGridSizer(models.size(), 0, 20); - printer_grid->SetFlexibleDirection(wxVERTICAL | wxHORIZONTAL); - sizer->Add(printer_grid); + const auto font_title = GetFont().MakeBold().Scaled(1.3); + const auto font_name = GetFont().MakeBold(); + const auto font_alt_nozzle = GetFont().Scaled(0.9); - auto namefont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - namefont.SetWeight(wxFONTWEIGHT_BOLD); + // wxGrid appends widgets by rows, but we need to construct them in columns. + // These vectors are used to hold the elements so that they can be appended in the right order. + std::vector titles; + std::vector bitmaps; + std::vector variants_panels; - // wxGrid appends widgets by rows, but we need to construct them in columns. - // These vectors are used to hold the elements so that they can be appended in the right order. - std::vector titles; - std::vector bitmaps; - std::vector variants_panels; + for (const auto &model : models) { + if (! filter(model)) { continue; } - for (const auto &model : models) { - wxBitmap bitmap(GUI::from_u8(Slic3r::var((boost::format("printers/%1%_%2%.png") % vendor.id % model.id).str())), wxBITMAP_TYPE_PNG); + wxBitmap bitmap(GUI::from_u8(Slic3r::var((boost::format("printers/%1%_%2%.png") % vendor.id % model.id).str())), wxBITMAP_TYPE_PNG); - auto *title = new wxStaticText(this, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - title->SetFont(namefont); - title->Wrap(std::max((int)MODEL_MIN_WRAP, bitmap.GetWidth())); - titles.push_back(title); + auto *title = new wxStaticText(this, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + title->SetFont(font_name); + title->Wrap(std::max((int)MODEL_MIN_WRAP, bitmap.GetWidth())); + titles.push_back(title); - auto *bitmap_widget = new wxStaticBitmap(this, wxID_ANY, bitmap); - bitmaps.push_back(bitmap_widget); + auto *bitmap_widget = new wxStaticBitmap(this, wxID_ANY, bitmap); + bitmaps.push_back(bitmap_widget); - auto *variants_panel = new wxPanel(this); - auto *variants_sizer = new wxBoxSizer(wxVERTICAL); - variants_panel->SetSizer(variants_sizer); - const auto model_id = model.id; + auto *variants_panel = new wxPanel(this); + auto *variants_sizer = new wxBoxSizer(wxVERTICAL); + variants_panel->SetSizer(variants_sizer); + const auto model_id = model.id; - bool default_variant = true; // Mark the first variant as default in the GUI - for (const auto &variant : model.variants) { - const auto label = wxString::Format("%s %s %s %s", variant.name, _(L("mm")), _(L("nozzle")), - (default_variant ? "(" + _(L("default")) + ")" : wxString())); - default_variant = false; - auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name); - const size_t idx = cboxes.size(); - cboxes.push_back(cbox); - bool enabled = appconfig_vendors.get_variant("PrusaResearch", model_id, variant.name); - variants_checked += enabled; - cbox->SetValue(enabled); - variants_sizer->Add(cbox, 0, wxBOTTOM, 3); - cbox->Bind(wxEVT_CHECKBOX, [this, idx](wxCommandEvent &event) { - if (idx >= this->cboxes.size()) { return; } - this->on_checkbox(this->cboxes[idx], event.IsChecked()); - }); - } + for (size_t i = 0; i < model.variants.size(); i++) { + const auto &variant = model.variants[i]; - variants_panels.push_back(variants_panel); - } + const auto label = model.technology == ptFFF + ? wxString::Format("%s %s %s", variant.name, _(L("mm")), _(L("nozzle"))) + : from_u8(model.name); - for (auto title : titles) { printer_grid->Add(title, 0, wxBOTTOM, 3); } - for (auto bitmap : bitmaps) { printer_grid->Add(bitmap, 0, wxBOTTOM, 20); } - for (auto vp : variants_panels) { printer_grid->Add(vp); } + if (i == 1) { + auto *alt_label = new wxStaticText(variants_panel, wxID_ANY, _(L("Alternate nozzles:"))); + alt_label->SetFont(font_alt_nozzle); + variants_sizer->Add(alt_label, 0, wxBOTTOM, 3); + } - auto *all_none_sizer = new wxBoxSizer(wxHORIZONTAL); - auto *sel_all = new wxButton(this, wxID_ANY, _(L("Select all"))); - auto *sel_none = new wxButton(this, wxID_ANY, _(L("Select none"))); - sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true); }); - sel_none->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(false); }); - all_none_sizer->AddStretchSpacer(); - all_none_sizer->Add(sel_all); - all_none_sizer->Add(sel_none); - sizer->AddStretchSpacer(); - sizer->Add(all_none_sizer, 0, wxEXPAND); + auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name); + i == 0 ? cboxes.push_back(cbox) : cboxes_alt.push_back(cbox); - SetSizer(sizer); + bool enabled = appconfig_vendors.get_variant("PrusaResearch", model_id, variant.name); + cbox->SetValue(enabled); + + variants_sizer->Add(cbox, 0, wxBOTTOM, 3); + + cbox->Bind(wxEVT_CHECKBOX, [this, cbox](wxCommandEvent &event) { + on_checkbox(cbox, event.IsChecked()); + }); + } + + variants_panels.push_back(variants_panel); + } + + const size_t cols = std::min(max_cols, titles.size()); + + auto *printer_grid = new wxFlexGridSizer(cols, 0, 20); + printer_grid->SetFlexibleDirection(wxVERTICAL | wxHORIZONTAL); + + if (titles.size() > 0) { + const size_t odd_items = titles.size() % cols; + + for (size_t i = 0; i < titles.size() - odd_items; i += cols) { + for (size_t j = i; j < i + cols; j++) { printer_grid->Add(titles[j], 0, wxBOTTOM, 3); } + for (size_t j = i; j < i + cols; j++) { printer_grid->Add(bitmaps[j], 0, wxBOTTOM, 20); } + for (size_t j = i; j < i + cols; j++) { printer_grid->Add(variants_panels[j]); } + + // Add separator space + if (i > 0) { + for (size_t j = i; j < i + cols; j++) { printer_grid->Add(1, 100); } + } + } + + if (odd_items > 0) { + for (size_t i = 0; i < cols; i++) { printer_grid->Add(1, 100); } + + const size_t rem = titles.size() - odd_items; + + for (size_t i = rem; i < titles.size(); i++) { printer_grid->Add(titles[i], 0, wxBOTTOM, 3); } + for (size_t i = 0; i < cols - odd_items; i++) { printer_grid->AddSpacer(1); } + for (size_t i = rem; i < titles.size(); i++) { printer_grid->Add(bitmaps[i], 0, wxBOTTOM, 20); } + for (size_t i = 0; i < cols - odd_items; i++) { printer_grid->AddSpacer(1); } + for (size_t i = rem; i < titles.size(); i++) { printer_grid->Add(variants_panels[i]); } + } + } + + auto *title_sizer = new wxBoxSizer(wxHORIZONTAL); + auto *title_widget = new wxStaticText(this, wxID_ANY, title); + title_widget->SetFont(font_title); + title_sizer->Add(title_widget); + title_sizer->AddStretchSpacer(); + + if (titles.size() > 1) { + // It only makes sense to add the All / None buttons if there's multiple printers + + auto *sel_all = new wxButton(this, wxID_ANY, _(L("All"))); + auto *sel_none = new wxButton(this, wxID_ANY, _(L("None"))); + sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true); }); + sel_none->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(false); }); + title_sizer->Add(sel_all, 0, wxRIGHT, BTN_SPACING); + title_sizer->Add(sel_none); + } + + sizer->Add(title_sizer, 0, wxEXPAND | wxBOTTOM, BTN_SPACING); + sizer->Add(printer_grid); + + // auto *all_none_sizer = new wxBoxSizer(wxHORIZONTAL); + // auto *sel_all = new wxButton(this, wxID_ANY, _(L("Select all"))); + // auto *sel_none = new wxButton(this, wxID_ANY, _(L("Select none"))); + // sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true); }); + // sel_none->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(false); }); + // all_none_sizer->AddStretchSpacer(); + // all_none_sizer->Add(sel_all); + // all_none_sizer->Add(sel_none); + // sizer->AddStretchSpacer(); + // sizer->Add(all_none_sizer, 0, wxEXPAND); + + SetSizer(sizer); } +PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors) + : PrinterPicker(parent, vendor, std::move(title), max_cols, appconfig_vendors, [](const VendorProfile::PrinterModel&) { return true; }) +{} + void PrinterPicker::select_all(bool select) { - for (const auto &cb : cboxes) { - if (cb->GetValue() != select) { - cb->SetValue(select); - on_checkbox(cb, select); - } - } + for (const auto &cb : cboxes) { + if (cb->GetValue() != select) { + cb->SetValue(select); + on_checkbox(cb, select); + } + } + + // Alt nozzles are de-selected if this is an all-deselect, left intact otherwise + if (! select) { + for (const auto &cb : cboxes_alt) { + if (cb->GetValue()) { + cb->SetValue(false); + on_checkbox(cb, false); + } + } + } } void PrinterPicker::select_one(size_t i, bool select) { - if (i < cboxes.size() && cboxes[i]->GetValue() != select) { - cboxes[i]->SetValue(select); - on_checkbox(cboxes[i], select); - } + if (i < cboxes.size() && cboxes[i]->GetValue() != select) { + cboxes[i]->SetValue(select); + on_checkbox(cboxes[i], select); + } } void PrinterPicker::on_checkbox(const Checkbox *cbox, bool checked) { - variants_checked += checked ? 1 : -1; - PrinterPickerEvent evt(EVT_PRINTER_PICK, GetId(), vendor_id, cbox->model, cbox->variant, checked); - AddPendingEvent(evt); + PrinterPickerEvent evt(EVT_PRINTER_PICK, GetId(), vendor_id, cbox->model, cbox->variant, checked); + AddPendingEvent(evt); } // Wizard page base -ConfigWizardPage::ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname) : - wxPanel(parent->p->hscroll), - parent(parent), - shortname(std::move(shortname)), - p_prev(nullptr), - p_next(nullptr) +ConfigWizardPage::ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname, unsigned indent) + : wxPanel(parent->p->hscroll) + , parent(parent) + , shortname(std::move(shortname)) + , indent(indent) { - auto *sizer = new wxBoxSizer(wxVERTICAL); + auto *sizer = new wxBoxSizer(wxVERTICAL); - auto *text = new wxStaticText(this, wxID_ANY, std::move(title), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - auto font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - font.SetWeight(wxFONTWEIGHT_BOLD); - font.SetPointSize(14); - text->SetFont(font); - sizer->Add(text, 0, wxALIGN_LEFT, 0); - sizer->AddSpacer(10); + auto *text = new wxStaticText(this, wxID_ANY, std::move(title), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + const auto font = GetFont().MakeBold().Scaled(1.5); + text->SetFont(font); + sizer->Add(text, 0, wxALIGN_LEFT, 0); + sizer->AddSpacer(10); - content = new wxBoxSizer(wxVERTICAL); - sizer->Add(content, 1); + content = new wxBoxSizer(wxVERTICAL); + sizer->Add(content, 1); - SetSizer(sizer); + SetSizer(sizer); - this->Hide(); + this->Hide(); - Bind(wxEVT_SIZE, [this](wxSizeEvent &event) { - this->Layout(); - event.Skip(); - }); + Bind(wxEVT_SIZE, [this](wxSizeEvent &event) { + this->Layout(); + event.Skip(); + }); } ConfigWizardPage::~ConfigWizardPage() {} -ConfigWizardPage* ConfigWizardPage::chain(ConfigWizardPage *page) -{ - if (p_next != nullptr) { p_next->p_prev = nullptr; } - p_next = page; - if (page != nullptr) { - if (page->p_prev != nullptr) { page->p_prev->p_next = nullptr; } - page->p_prev = this; - } - - return page; -} - void ConfigWizardPage::append_text(wxString text) { - auto *widget = new wxStaticText(this, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - widget->Wrap(WRAP_WIDTH); - widget->SetMinSize(wxSize(WRAP_WIDTH, -1)); - append(widget); + auto *widget = new wxStaticText(this, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + widget->Wrap(WRAP_WIDTH); + widget->SetMinSize(wxSize(WRAP_WIDTH, -1)); + append(widget); } void ConfigWizardPage::append_spacer(int space) { - content->AddSpacer(space); + content->AddSpacer(space); } bool ConfigWizardPage::Show(bool show) { - if (extra_buttons() != nullptr) { extra_buttons()->Show(show); } - return wxPanel::Show(show); + if (extra_buttons() != nullptr) { extra_buttons()->Show(show); } + return wxPanel::Show(show); } -void ConfigWizardPage::enable_next(bool enable) { parent->p->enable_next(enable); } - // Wizard pages -PageWelcome::PageWelcome(ConfigWizard *parent, bool check_first_variant) : - ConfigWizardPage(parent, wxString::Format(_(L("Welcome to the Slic3r %s")), ConfigWizard::name()), _(L("Welcome"))), - printer_picker(nullptr), - others_buttons(new wxPanel(parent)), - cbox_reset(nullptr) +PageWelcome::PageWelcome(ConfigWizard *parent) + : ConfigWizardPage(parent, wxString::Format(_(L("Welcome to the Slic3r %s")), ConfigWizard::name()), _(L("Welcome"))) + , cbox_reset(nullptr) { - if (wizard_p()->run_reason == ConfigWizard::RR_DATA_EMPTY) { - wxString::Format(_(L("Run %s")), ConfigWizard::name()); - append_text(wxString::Format( - _(L("Hello, welcome to Slic3r Prusa Edition! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")), - ConfigWizard::name()) - ); - } else { - cbox_reset = new wxCheckBox(this, wxID_ANY, _(L("Remove user profiles - install from scratch (a snapshot will be taken beforehand)"))); - append(cbox_reset); - } + if (wizard_p()->run_reason == ConfigWizard::RR_DATA_EMPTY) { + wxString::Format(_(L("Run %s")), ConfigWizard::name()); + append_text(wxString::Format( + _(L("Hello, welcome to Slic3r Prusa Edition! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")), + ConfigWizard::name()) + ); + } else { + cbox_reset = new wxCheckBox(this, wxID_ANY, _(L("Remove user profiles - install from scratch (a snapshot will be taken beforehand)"))); + append(cbox_reset); + } - const auto &vendors = wizard_p()->vendors; - const auto vendor_prusa = vendors.find("PrusaResearch"); - - if (vendor_prusa != vendors.cend()) { - AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors; - - printer_picker = new PrinterPicker(this, vendor_prusa->second, appconfig_vendors); - if (check_first_variant) { - // Select the default (first) model/variant on the Prusa vendor - printer_picker->select_one(0, true); - } - printer_picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) { - appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable); - this->on_variant_checked(); - }); - - append(printer_picker); - } - - const size_t num_other_vendors = vendors.size() - (vendor_prusa != vendors.cend()); - auto *sizer = new wxBoxSizer(wxHORIZONTAL); - auto *other_vendors = new wxButton(others_buttons, wxID_ANY, _(L("Other vendors"))); - other_vendors->Enable(num_other_vendors > 0); - auto *custom_setup = new wxButton(others_buttons, wxID_ANY, _(L("Custom setup"))); - - sizer->Add(other_vendors); - sizer->AddSpacer(BTN_SPACING); - sizer->Add(custom_setup); - - other_vendors->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->wizard_p()->on_other_vendors(); }); - custom_setup->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->wizard_p()->on_custom_setup(); }); - - others_buttons->SetSizer(sizer); + Show(); } -void PageWelcome::on_page_set() + +PagePrinters::PagePrinters(ConfigWizard *parent, wxString title, wxString shortname, const VendorProfile &vendor, unsigned indent, Technology technology) + : ConfigWizardPage(parent, std::move(title), std::move(shortname), indent) { - chain(wizard_p()->page_update); - on_variant_checked(); + enum { + COL_SIZE = 200, + }; + + bool check_first_variant = wizard_p()->check_first_variant(); + + AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors; + + const auto families = vendor.families(); + for (const auto &family : families) { + const auto filter = [&](const VendorProfile::PrinterModel &model) { + return (model.technology == ptFFF && technology & T_FFF + || model.technology == ptSLA && technology & T_SLA) + && model.family == family; + }; + + if (std::find_if(vendor.models.begin(), vendor.models.end(), filter) == vendor.models.end()) { + continue; + } + + const auto picker_title = family.empty() ? wxString() : wxString::Format(_(L("%s Family")), family); + auto *picker = new PrinterPicker(this, vendor, picker_title, MAX_COLS, appconfig_vendors, filter); + + if (check_first_variant) { + // Select the default (first) model/variant on the Prusa vendor + picker->select_one(0, true); + check_first_variant = false; + } + + picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) { + appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable); + }); + + append(new wxStaticLine(this)); + + append(picker); + printer_pickers.push_back(picker); + } } -void PageWelcome::on_variant_checked() +void PagePrinters::select_all(bool select) { - enable_next(printer_picker != nullptr ? printer_picker->variants_checked > 0 : false); + for (auto picker : printer_pickers) { + picker->select_all(select); + } } -PageUpdate::PageUpdate(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Automatic updates")), _(L("Updates"))), - version_check(true), - preset_update(true) + +PageCustom::PageCustom(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("Custom Printer Setup")), _(L("Custom Printer"))) { - const AppConfig *app_config = GUI::get_app_config(); - auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - boldfont.SetWeight(wxFONTWEIGHT_BOLD); + cb_custom = new wxCheckBox(this, wxID_ANY, _(L("Define a custom printer profile"))); + tc_profile_name = new wxTextCtrl(this, wxID_ANY, "My Settings"); + auto *label = new wxStaticText(this, wxID_ANY, _(L("Custom profile name:"))); - auto *box_slic3r = new wxCheckBox(this, wxID_ANY, _(L("Check for application updates"))); - box_slic3r->SetValue(app_config->get("version_check") == "1"); - append(box_slic3r); - append_text(_(L("If enabled, Slic3r checks for new versions of Slic3r PE online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done."))); + cb_custom->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { + wizard_p()->on_custom_setup(custom_wanted()); + }); - append_spacer(VERTICAL_SPACING); - - auto *box_presets = new wxCheckBox(this, wxID_ANY, _(L("Update built-in Presets automatically"))); - box_presets->SetValue(app_config->get("preset_update") == "1"); - append(box_presets); - append_text(_(L("If enabled, Slic3r downloads updates of built-in system presets in the background. These updates are downloaded into a separate temporary location. When a new preset version becomes available it is offered at application startup."))); - const auto text_bold = _(L("Updates are never applied without user's consent and never overwrite user's customized settings.")); - auto *label_bold = new wxStaticText(this, wxID_ANY, text_bold); - label_bold->SetFont(boldfont); - label_bold->Wrap(WRAP_WIDTH); - append(label_bold); - append_text(_(L("Additionally a backup snapshot of the whole configuration is created before an update is applied."))); - - box_slic3r->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->version_check = event.IsChecked(); }); - box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); }); + append(cb_custom); + append(label); + append(tc_profile_name); } -PageVendors::PageVendors(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Other Vendors")), _(L("Other Vendors"))) +PageUpdate::PageUpdate(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("Automatic updates")), _(L("Updates"))) + , version_check(true) + , preset_update(true) { - append_text(_(L("Pick another vendor supported by Slic3r PE:"))); + const AppConfig *app_config = GUI::get_app_config(); + auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + boldfont.SetWeight(wxFONTWEIGHT_BOLD); - auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - boldfont.SetWeight(wxFONTWEIGHT_BOLD); + auto *box_slic3r = new wxCheckBox(this, wxID_ANY, _(L("Check for application updates"))); + box_slic3r->SetValue(app_config->get("version_check") == "1"); + append(box_slic3r); + append_text(_(L("If enabled, Slic3r checks for new versions of Slic3r PE online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done."))); - AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors; - wxArrayString choices_vendors; + append_spacer(VERTICAL_SPACING); - for (const auto vendor_pair : wizard_p()->vendors) { - const auto &vendor = vendor_pair.second; - if (vendor.id == "PrusaResearch") { continue; } + auto *box_presets = new wxCheckBox(this, wxID_ANY, _(L("Update built-in Presets automatically"))); + box_presets->SetValue(app_config->get("preset_update") == "1"); + append(box_presets); + append_text(_(L("If enabled, Slic3r downloads updates of built-in system presets in the background. These updates are downloaded into a separate temporary location. When a new preset version becomes available it is offered at application startup."))); + const auto text_bold = _(L("Updates are never applied without user's consent and never overwrite user's customized settings.")); + auto *label_bold = new wxStaticText(this, wxID_ANY, text_bold); + label_bold->SetFont(boldfont); + label_bold->Wrap(WRAP_WIDTH); + append(label_bold); + append_text(_(L("Additionally a backup snapshot of the whole configuration is created before an update is applied."))); - auto *picker = new PrinterPicker(this, vendor, appconfig_vendors); - picker->Hide(); - pickers.push_back(picker); - choices_vendors.Add(vendor.name); - - picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) { - appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable); - this->on_variant_checked(); - }); - } - - auto *vendor_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices_vendors); - if (choices_vendors.GetCount() > 0) { - vendor_picker->SetSelection(0); - on_vendor_pick(0); - } - - vendor_picker->Bind(wxEVT_CHOICE, [this](wxCommandEvent &evt) { - this->on_vendor_pick(evt.GetInt()); - }); - - append(vendor_picker); - for (PrinterPicker *picker : pickers) { this->append(picker); } + box_slic3r->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->version_check = event.IsChecked(); }); + box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); }); } -void PageVendors::on_page_set() +PageVendors::PageVendors(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("Other Vendors")), _(L("Other Vendors"))) { - on_variant_checked(); + append_text(_(L("Pick another vendor supported by Slic3r PE:"))); + + auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + boldfont.SetWeight(wxFONTWEIGHT_BOLD); + + AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors; + wxArrayString choices_vendors; + + for (const auto vendor_pair : wizard_p()->vendors) { + const auto &vendor = vendor_pair.second; + if (vendor.id == "PrusaResearch") { continue; } + + auto *picker = new PrinterPicker(this, vendor, "", MAX_COLS, appconfig_vendors); + picker->Hide(); + pickers.push_back(picker); + choices_vendors.Add(vendor.name); + + picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) { + appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable); + }); + } + + auto *vendor_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices_vendors); + if (choices_vendors.GetCount() > 0) { + vendor_picker->SetSelection(0); + on_vendor_pick(0); + } + + vendor_picker->Bind(wxEVT_CHOICE, [this](wxCommandEvent &evt) { + this->on_vendor_pick(evt.GetInt()); + }); + + append(vendor_picker); + for (PrinterPicker *picker : pickers) { this->append(picker); } } void PageVendors::on_vendor_pick(size_t i) { - for (PrinterPicker *picker : pickers) { picker->Hide(); } - if (i < pickers.size()) { - pickers[i]->Show(); - wizard_p()->layout_fit(); - } + for (PrinterPicker *picker : pickers) { picker->Hide(); } + if (i < pickers.size()) { + pickers[i]->Show(); + parent->Layout(); + } } -void PageVendors::on_variant_checked() +PageFirmware::PageFirmware(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("Firmware Type")), _(L("Firmware")), 1) + , gcode_opt(*print_config_def.get("gcode_flavor")) + , gcode_picker(nullptr) { - size_t variants_checked = 0; - for (const PrinterPicker *picker : pickers) { variants_checked += picker->variants_checked; } - enable_next(variants_checked > 0); -} + append_text(_(L("Choose the type of firmware used by your printer."))); + append_text(gcode_opt.tooltip); -PageFirmware::PageFirmware(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Firmware Type")), _(L("Firmware"))), - gcode_opt(*print_config_def.get("gcode_flavor")), - gcode_picker(nullptr) -{ - append_text(_(L("Choose the type of firmware used by your printer."))); - append_text(gcode_opt.tooltip); + wxArrayString choices; + choices.Alloc(gcode_opt.enum_labels.size()); + for (const auto &label : gcode_opt.enum_labels) { + choices.Add(label); + } - wxArrayString choices; - choices.Alloc(gcode_opt.enum_labels.size()); - for (const auto &label : gcode_opt.enum_labels) { - choices.Add(label); - } + gcode_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices); + const auto &enum_values = gcode_opt.enum_values; + auto needle = enum_values.cend(); + if (gcode_opt.default_value != nullptr) { + needle = std::find(enum_values.cbegin(), enum_values.cend(), gcode_opt.default_value->serialize()); + } + if (needle != enum_values.cend()) { + gcode_picker->SetSelection(needle - enum_values.cbegin()); + } else { + gcode_picker->SetSelection(0); + } - gcode_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices); - const auto &enum_values = gcode_opt.enum_values; - auto needle = enum_values.cend(); - if (gcode_opt.default_value != nullptr) { - needle = std::find(enum_values.cbegin(), enum_values.cend(), gcode_opt.default_value->serialize()); - } - if (needle != enum_values.cend()) { - gcode_picker->SetSelection(needle - enum_values.cbegin()); - } else { - gcode_picker->SetSelection(0); - } - - append(gcode_picker); + append(gcode_picker); } void PageFirmware::apply_custom_config(DynamicPrintConfig &config) { - auto sel = gcode_picker->GetSelection(); - if (sel >= 0 && sel < gcode_opt.enum_labels.size()) { - auto *opt = new ConfigOptionEnum(static_cast(sel)); - config.set_key_value("gcode_flavor", opt); - } + auto sel = gcode_picker->GetSelection(); + if (sel >= 0 && (size_t)sel < gcode_opt.enum_labels.size()) { + auto *opt = new ConfigOptionEnum(static_cast(sel)); + config.set_key_value("gcode_flavor", opt); + } } -PageBedShape::PageBedShape(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Bed Shape and Size")), _(L("Bed Shape"))), - shape_panel(new BedShapePanel(this)) +PageBedShape::PageBedShape(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("Bed Shape and Size")), _(L("Bed Shape")), 1) + , shape_panel(new BedShapePanel(this)) { - append_text(_(L("Set the shape of your printer's bed."))); + append_text(_(L("Set the shape of your printer's bed."))); - shape_panel->build_panel(wizard_p()->custom_config->option("bed_shape")); - append(shape_panel); + shape_panel->build_panel(wizard_p()->custom_config->option("bed_shape")); + append(shape_panel); } void PageBedShape::apply_custom_config(DynamicPrintConfig &config) { - const auto points(shape_panel->GetValue()); - auto *opt = new ConfigOptionPoints(points); - config.set_key_value("bed_shape", opt); + const auto points(shape_panel->GetValue()); + auto *opt = new ConfigOptionPoints(points); + config.set_key_value("bed_shape", opt); } -PageDiameters::PageDiameters(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Filament and Nozzle Diameters")), _(L("Print Diameters"))), - spin_nozzle(new wxSpinCtrlDouble(this, wxID_ANY)), - spin_filam(new wxSpinCtrlDouble(this, wxID_ANY)) +PageDiameters::PageDiameters(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("Filament and Nozzle Diameters")), _(L("Print Diameters")), 1) + , spin_nozzle(new wxSpinCtrlDouble(this, wxID_ANY)) + , spin_filam(new wxSpinCtrlDouble(this, wxID_ANY)) { - spin_nozzle->SetDigits(2); - spin_nozzle->SetIncrement(0.1); - const auto &def_nozzle = *print_config_def.get("nozzle_diameter"); - auto *default_nozzle = dynamic_cast(def_nozzle.default_value); - spin_nozzle->SetValue(default_nozzle != nullptr && default_nozzle->size() > 0 ? default_nozzle->get_at(0) : 0.5); + spin_nozzle->SetDigits(2); + spin_nozzle->SetIncrement(0.1); + const auto &def_nozzle = *print_config_def.get("nozzle_diameter"); + auto *default_nozzle = dynamic_cast(def_nozzle.default_value); + spin_nozzle->SetValue(default_nozzle != nullptr && default_nozzle->size() > 0 ? default_nozzle->get_at(0) : 0.5); - spin_filam->SetDigits(2); - spin_filam->SetIncrement(0.25); - const auto &def_filam = *print_config_def.get("filament_diameter"); - auto *default_filam = dynamic_cast(def_filam.default_value); - spin_filam->SetValue(default_filam != nullptr && default_filam->size() > 0 ? default_filam->get_at(0) : 3.0); + spin_filam->SetDigits(2); + spin_filam->SetIncrement(0.25); + const auto &def_filam = *print_config_def.get("filament_diameter"); + auto *default_filam = dynamic_cast(def_filam.default_value); + spin_filam->SetValue(default_filam != nullptr && default_filam->size() > 0 ? default_filam->get_at(0) : 3.0); - append_text(_(L("Enter the diameter of your printer's hot end nozzle."))); + append_text(_(L("Enter the diameter of your printer's hot end nozzle."))); - auto *sizer_nozzle = new wxFlexGridSizer(3, 5, 5); - auto *text_nozzle = new wxStaticText(this, wxID_ANY, _(L("Nozzle Diameter:"))); - auto *unit_nozzle = new wxStaticText(this, wxID_ANY, _(L("mm"))); - sizer_nozzle->AddGrowableCol(0, 1); - sizer_nozzle->Add(text_nozzle, 0, wxALIGN_CENTRE_VERTICAL); - sizer_nozzle->Add(spin_nozzle); - sizer_nozzle->Add(unit_nozzle, 0, wxALIGN_CENTRE_VERTICAL); - append(sizer_nozzle); + auto *sizer_nozzle = new wxFlexGridSizer(3, 5, 5); + auto *text_nozzle = new wxStaticText(this, wxID_ANY, _(L("Nozzle Diameter:"))); + auto *unit_nozzle = new wxStaticText(this, wxID_ANY, _(L("mm"))); + sizer_nozzle->AddGrowableCol(0, 1); + sizer_nozzle->Add(text_nozzle, 0, wxALIGN_CENTRE_VERTICAL); + sizer_nozzle->Add(spin_nozzle); + sizer_nozzle->Add(unit_nozzle, 0, wxALIGN_CENTRE_VERTICAL); + append(sizer_nozzle); - append_spacer(VERTICAL_SPACING); + append_spacer(VERTICAL_SPACING); - append_text(_(L("Enter the diameter of your filament."))); - append_text(_(L("Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average."))); + append_text(_(L("Enter the diameter of your filament."))); + append_text(_(L("Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average."))); - auto *sizer_filam = new wxFlexGridSizer(3, 5, 5); - auto *text_filam = new wxStaticText(this, wxID_ANY, _(L("Filament Diameter:"))); - auto *unit_filam = new wxStaticText(this, wxID_ANY, _(L("mm"))); - sizer_filam->AddGrowableCol(0, 1); - sizer_filam->Add(text_filam, 0, wxALIGN_CENTRE_VERTICAL); - sizer_filam->Add(spin_filam); - sizer_filam->Add(unit_filam, 0, wxALIGN_CENTRE_VERTICAL); - append(sizer_filam); + auto *sizer_filam = new wxFlexGridSizer(3, 5, 5); + auto *text_filam = new wxStaticText(this, wxID_ANY, _(L("Filament Diameter:"))); + auto *unit_filam = new wxStaticText(this, wxID_ANY, _(L("mm"))); + sizer_filam->AddGrowableCol(0, 1); + sizer_filam->Add(text_filam, 0, wxALIGN_CENTRE_VERTICAL); + sizer_filam->Add(spin_filam); + sizer_filam->Add(unit_filam, 0, wxALIGN_CENTRE_VERTICAL); + append(sizer_filam); } void PageDiameters::apply_custom_config(DynamicPrintConfig &config) { - auto *opt_nozzle = new ConfigOptionFloats(1, spin_nozzle->GetValue()); - config.set_key_value("nozzle_diameter", opt_nozzle); - auto *opt_filam = new ConfigOptionFloats(1, spin_filam->GetValue()); - config.set_key_value("filament_diameter", opt_filam); + auto *opt_nozzle = new ConfigOptionFloats(1, spin_nozzle->GetValue()); + config.set_key_value("nozzle_diameter", opt_nozzle); + auto *opt_filam = new ConfigOptionFloats(1, spin_filam->GetValue()); + config.set_key_value("filament_diameter", opt_filam); } -PageTemperatures::PageTemperatures(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Extruder and Bed Temperatures")), _(L("Temperatures"))), - spin_extr(new wxSpinCtrlDouble(this, wxID_ANY)), - spin_bed(new wxSpinCtrlDouble(this, wxID_ANY)) +PageTemperatures::PageTemperatures(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("Extruder and Bed Temperatures")), _(L("Temperatures")), 1) + , spin_extr(new wxSpinCtrlDouble(this, wxID_ANY)) + , spin_bed(new wxSpinCtrlDouble(this, wxID_ANY)) { - spin_extr->SetIncrement(5.0); - const auto &def_extr = *print_config_def.get("temperature"); - spin_extr->SetRange(def_extr.min, def_extr.max); - auto *default_extr = dynamic_cast(def_extr.default_value); - spin_extr->SetValue(default_extr != nullptr && default_extr->size() > 0 ? default_extr->get_at(0) : 200); + spin_extr->SetIncrement(5.0); + const auto &def_extr = *print_config_def.get("temperature"); + spin_extr->SetRange(def_extr.min, def_extr.max); + auto *default_extr = dynamic_cast(def_extr.default_value); + spin_extr->SetValue(default_extr != nullptr && default_extr->size() > 0 ? default_extr->get_at(0) : 200); - spin_bed->SetIncrement(5.0); - const auto &def_bed = *print_config_def.get("bed_temperature"); - spin_bed->SetRange(def_bed.min, def_bed.max); - auto *default_bed = dynamic_cast(def_bed.default_value); - spin_bed->SetValue(default_bed != nullptr && default_bed->size() > 0 ? default_bed->get_at(0) : 0); + spin_bed->SetIncrement(5.0); + const auto &def_bed = *print_config_def.get("bed_temperature"); + spin_bed->SetRange(def_bed.min, def_bed.max); + auto *default_bed = dynamic_cast(def_bed.default_value); + spin_bed->SetValue(default_bed != nullptr && default_bed->size() > 0 ? default_bed->get_at(0) : 0); - append_text(_(L("Enter the temperature needed for extruding your filament."))); - append_text(_(L("A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS."))); + append_text(_(L("Enter the temperature needed for extruding your filament."))); + append_text(_(L("A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS."))); - auto *sizer_extr = new wxFlexGridSizer(3, 5, 5); - auto *text_extr = new wxStaticText(this, wxID_ANY, _(L("Extrusion Temperature:"))); - auto *unit_extr = new wxStaticText(this, wxID_ANY, _(L("°C"))); - sizer_extr->AddGrowableCol(0, 1); - sizer_extr->Add(text_extr, 0, wxALIGN_CENTRE_VERTICAL); - sizer_extr->Add(spin_extr); - sizer_extr->Add(unit_extr, 0, wxALIGN_CENTRE_VERTICAL); - append(sizer_extr); + auto *sizer_extr = new wxFlexGridSizer(3, 5, 5); + auto *text_extr = new wxStaticText(this, wxID_ANY, _(L("Extrusion Temperature:"))); + auto *unit_extr = new wxStaticText(this, wxID_ANY, _(L("°C"))); + sizer_extr->AddGrowableCol(0, 1); + sizer_extr->Add(text_extr, 0, wxALIGN_CENTRE_VERTICAL); + sizer_extr->Add(spin_extr); + sizer_extr->Add(unit_extr, 0, wxALIGN_CENTRE_VERTICAL); + append(sizer_extr); - append_spacer(VERTICAL_SPACING); + append_spacer(VERTICAL_SPACING); - append_text(_(L("Enter the bed temperature needed for getting your filament to stick to your heated bed."))); - 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."))); + append_text(_(L("Enter the bed temperature needed for getting your filament to stick to your heated bed."))); + 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."))); - auto *sizer_bed = new wxFlexGridSizer(3, 5, 5); - auto *text_bed = new wxStaticText(this, wxID_ANY, _(L("Bed Temperature:"))); - auto *unit_bed = new wxStaticText(this, wxID_ANY, _(L("°C"))); - sizer_bed->AddGrowableCol(0, 1); - sizer_bed->Add(text_bed, 0, wxALIGN_CENTRE_VERTICAL); - sizer_bed->Add(spin_bed); - sizer_bed->Add(unit_bed, 0, wxALIGN_CENTRE_VERTICAL); - append(sizer_bed); + auto *sizer_bed = new wxFlexGridSizer(3, 5, 5); + auto *text_bed = new wxStaticText(this, wxID_ANY, _(L("Bed Temperature:"))); + auto *unit_bed = new wxStaticText(this, wxID_ANY, _(L("°C"))); + sizer_bed->AddGrowableCol(0, 1); + sizer_bed->Add(text_bed, 0, wxALIGN_CENTRE_VERTICAL); + sizer_bed->Add(spin_bed); + sizer_bed->Add(unit_bed, 0, wxALIGN_CENTRE_VERTICAL); + append(sizer_bed); } void PageTemperatures::apply_custom_config(DynamicPrintConfig &config) { - auto *opt_extr = new ConfigOptionInts(1, spin_extr->GetValue()); - config.set_key_value("temperature", opt_extr); - auto *opt_extr1st = new ConfigOptionInts(1, spin_extr->GetValue()); - config.set_key_value("first_layer_temperature", opt_extr1st); - auto *opt_bed = new ConfigOptionInts(1, spin_bed->GetValue()); - config.set_key_value("bed_temperature", opt_bed); - auto *opt_bed1st = new ConfigOptionInts(1, spin_bed->GetValue()); - config.set_key_value("first_layer_bed_temperature", opt_bed1st); + auto *opt_extr = new ConfigOptionInts(1, spin_extr->GetValue()); + config.set_key_value("temperature", opt_extr); + auto *opt_extr1st = new ConfigOptionInts(1, spin_extr->GetValue()); + config.set_key_value("first_layer_temperature", opt_extr1st); + auto *opt_bed = new ConfigOptionInts(1, spin_bed->GetValue()); + config.set_key_value("bed_temperature", opt_bed); + auto *opt_bed1st = new ConfigOptionInts(1, spin_bed->GetValue()); + config.set_key_value("first_layer_bed_temperature", opt_bed1st); } // Index -ConfigWizardIndex::ConfigWizardIndex(wxWindow *parent) : - wxPanel(parent), - bg(GUI::from_u8(Slic3r::var("Slic3r_192px_transparent.png")), wxBITMAP_TYPE_PNG), - bullet_black(GUI::from_u8(Slic3r::var("bullet_black.png")), wxBITMAP_TYPE_PNG), - bullet_blue(GUI::from_u8(Slic3r::var("bullet_blue.png")), wxBITMAP_TYPE_PNG), - bullet_white(GUI::from_u8(Slic3r::var("bullet_white.png")), wxBITMAP_TYPE_PNG) +ConfigWizardIndex::ConfigWizardIndex(wxWindow *parent) + : wxPanel(parent) + , bg(GUI::from_u8(Slic3r::var("Slic3r_192px_transparent.png")), wxBITMAP_TYPE_PNG) + , bullet_black(GUI::from_u8(Slic3r::var("bullet_black.png")), wxBITMAP_TYPE_PNG) + , bullet_blue(GUI::from_u8(Slic3r::var("bullet_blue.png")), wxBITMAP_TYPE_PNG) + , bullet_white(GUI::from_u8(Slic3r::var("bullet_white.png")), wxBITMAP_TYPE_PNG) + , item_active(0) + , item_hover(-1) + , last_page((size_t)-1) { - SetMinSize(bg.GetSize()); + SetMinSize(bg.GetSize()); - wxClientDC dc(this); - text_height = dc.GetCharHeight(); + const wxSize size = GetTextExtent("m"); + em = size.x; + em_h = size.y; - // Add logo bitmap. - // This could be done in on_paint() along with the index labels, but I've found it tricky - // to get the bitmap rendered well on all platforms with transparent background. - // In some cases it didn't work at all. And so wxStaticBitmap is used here instead, - // because it has all the platform quirks figured out. - auto *sizer = new wxBoxSizer(wxVERTICAL); - auto *logo = new wxStaticBitmap(this, wxID_ANY, bg); - sizer->AddStretchSpacer(); - sizer->Add(logo); - SetSizer(sizer); + // Add logo bitmap. + // This could be done in on_paint() along with the index labels, but I've found it tricky + // to get the bitmap rendered well on all platforms with transparent background. + // In some cases it didn't work at all. And so wxStaticBitmap is used here instead, + // because it has all the platform quirks figured out. + auto *sizer = new wxBoxSizer(wxVERTICAL); + auto *logo = new wxStaticBitmap(this, wxID_ANY, bg); + sizer->AddStretchSpacer(); + sizer->Add(logo); + SetSizer(sizer); - Bind(wxEVT_PAINT, &ConfigWizardIndex::on_paint, this); + Bind(wxEVT_PAINT, &ConfigWizardIndex::on_paint, this); + Bind(wxEVT_MOTION, &ConfigWizardIndex::on_mouse_move, this); + + Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent &evt) { + if (item_hover != -1) { + item_hover = -1; + Refresh(); + } + evt.Skip(); + }); + + Bind(wxEVT_LEFT_UP, [this](wxMouseEvent &evt) { + if (item_hover >= 0) { go_to(item_hover); } + }); } -void ConfigWizardIndex::load_items(ConfigWizardPage *firstpage) +wxDECLARE_EVENT(EVT_INDEX_PAGE, wxCommandEvent); + +void ConfigWizardIndex::add_page(ConfigWizardPage *page) { - items.clear(); - item_active = items.cend(); - - for (auto *page = firstpage; page != nullptr; page = page->page_next()) { - items.emplace_back(page->shortname); - } - - Refresh(); + last_page = items.size(); + items.emplace_back(Item { page->shortname, page->indent, page }); + Refresh(); } -void ConfigWizardIndex::set_active(ConfigWizardPage *page) +void ConfigWizardIndex::add_label(wxString label, unsigned indent) { - item_active = std::find(items.cbegin(), items.cend(), page->shortname); - Refresh(); + items.emplace_back(Item { std::move(label), indent, nullptr }); + Refresh(); +} + +ConfigWizardPage* ConfigWizardIndex::active_page() const +{ + if (item_active >= items.size()) { return nullptr; } + + return items[item_active].page; +} + +void ConfigWizardIndex::go_prev() +{ + // Search for a preceiding item that is a page (not a label, ie. page != nullptr) + + for (size_t i = item_active; i > 0; i--) { + if (items[i - 1].page != nullptr) { + go_to(i - 1); + return; + } + } +} + +void ConfigWizardIndex::go_next() +{ + // Search for a next item that is a page (not a label, ie. page != nullptr) + + for (size_t i = item_active + 1; i < items.size(); i++) { + if (items[i].page != nullptr) { + go_to(i); + return; + } + } +} + +void ConfigWizardIndex::go_to(size_t i) +{ + if (i < items.size() && items[i].page != nullptr) { + auto *former_active = active_page(); + if (former_active != nullptr) { former_active->Hide(); } + + item_active = i; + items[i].page->Show(); + + wxCommandEvent evt(EVT_INDEX_PAGE, GetId()); + AddPendingEvent(evt); + + Refresh(); + } +} + +void ConfigWizardIndex::go_to(ConfigWizardPage *page) +{ + if (page == nullptr) { return; } + + for (size_t i = 0; i < items.size(); i++) { + if (items[i].page == page) { go_to(i); } + } +} + +void ConfigWizardIndex::clear() +{ + auto *former_active = active_page(); + if (former_active != nullptr) { former_active->Hide(); } + + items.clear(); + item_active = 0; } void ConfigWizardIndex::on_paint(wxPaintEvent & evt) { - enum { - MARGIN = 10, - SPACING = 5, - }; + const auto size = GetClientSize(); + if (size.GetHeight() == 0 || size.GetWidth() == 0) { return; } - const auto size = GetClientSize(); - if (size.GetHeight() == 0 || size.GetWidth() == 0) { return; } + wxPaintDC dc(this); - wxPaintDC dc(this); + const auto bullet_w = bullet_black.GetSize().GetWidth(); + const auto bullet_h = bullet_black.GetSize().GetHeight(); + const int yoff_icon = bullet_h < em_h ? (em_h - bullet_h) / 2 : 0; + const int yoff_text = bullet_h > em_h ? (bullet_h - em_h) / 2 : 0; + const int yinc = item_height(); - const auto bullet_w = bullet_black.GetSize().GetWidth(); - const auto bullet_h = bullet_black.GetSize().GetHeight(); - const int yoff_icon = bullet_h < text_height ? (text_height - bullet_h) / 2 : 0; - const int yoff_text = bullet_h > text_height ? (bullet_h - text_height) / 2 : 0; - const int yinc = std::max(bullet_h, text_height) + SPACING; + unsigned y = 0; + for (size_t i = 0; i < items.size(); i++) { + const Item& item = items[i]; + unsigned x = em/2 + item.indent * em; - unsigned y = 0; - for (auto it = items.cbegin(); it != items.cend(); ++it) { - if (it < item_active) { dc.DrawBitmap(bullet_black, MARGIN, y + yoff_icon, false); } - if (it == item_active) { dc.DrawBitmap(bullet_blue, MARGIN, y + yoff_icon, false); } - if (it > item_active) { dc.DrawBitmap(bullet_white, MARGIN, y + yoff_icon, false); } - dc.DrawText(*it, MARGIN + bullet_w + SPACING, y + yoff_text); - y += yinc; - } + if (i == item_active || item_hover >= 0 && i == (size_t)item_hover) { + dc.DrawBitmap(bullet_blue, x, y + yoff_icon, false); + } + else if (i < item_active) { dc.DrawBitmap(bullet_black, x, y + yoff_icon, false); } + else if (i > item_active) { dc.DrawBitmap(bullet_white, x, y + yoff_icon, false); } + + dc.DrawText(item.label, x + bullet_w + em/2, y + yoff_text); + y += yinc; + } } +void ConfigWizardIndex::on_mouse_move(wxMouseEvent &evt) +{ + const wxClientDC dc(this); + const wxPoint pos = evt.GetLogicalPosition(dc); + + const ssize_t item_hover_new = pos.y / item_height(); + + if (item_hover_new < items.size() && item_hover_new != item_hover) { + item_hover = item_hover_new; + Refresh(); + } + + evt.Skip(); +} // priv static const std::unordered_map> legacy_preset_map {{ - { "Original Prusa i3 MK2.ini", std::make_pair("MK2S", "0.4") }, - { "Original Prusa i3 MK2 MM Single Mode.ini", std::make_pair("MK2SMM", "0.4") }, - { "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") }, - { "Original Prusa i3 MK2 MultiMaterial.ini", std::make_pair("MK2SMM", "0.4") }, - { "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") }, - { "Original Prusa i3 MK2 0.25 nozzle.ini", std::make_pair("MK2S", "0.25") }, - { "Original Prusa i3 MK2 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") }, - { "Original Prusa i3 MK3.ini", std::make_pair("MK3", "0.4") }, + { "Original Prusa i3 MK2.ini", std::make_pair("MK2S", "0.4") }, + { "Original Prusa i3 MK2 MM Single Mode.ini", std::make_pair("MK2SMM", "0.4") }, + { "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") }, + { "Original Prusa i3 MK2 MultiMaterial.ini", std::make_pair("MK2SMM", "0.4") }, + { "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") }, + { "Original Prusa i3 MK2 0.25 nozzle.ini", std::make_pair("MK2S", "0.25") }, + { "Original Prusa i3 MK2 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") }, + { "Original Prusa i3 MK3.ini", std::make_pair("MK3", "0.4") }, }}; +void ConfigWizard::priv::load_pages(bool custom_setup) +{ + const auto former_active = index->active_item(); + + index->clear(); + + index->add_page(page_welcome); + index->add_page(page_fff); + index->add_page(page_msla); + index->add_page(page_custom); + + if (custom_setup) { + index->add_page(page_firmware); + index->add_page(page_bed); + index->add_page(page_diams); + index->add_page(page_temps); + } + + index->add_page(page_update); + + index->go_to(former_active); // Will restore the active item/page if possible + + q->Layout(); +} + +bool ConfigWizard::priv::check_first_variant() const +{ + return run_reason == RR_DATA_EMPTY || run_reason == RR_DATA_LEGACY; +} + void ConfigWizard::priv::load_vendors() { - const auto vendor_dir = fs::path(Slic3r::data_dir()) / "vendor"; - const auto rsrc_vendor_dir = fs::path(resources_dir()) / "profiles"; + const auto vendor_dir = fs::path(Slic3r::data_dir()) / "vendor"; + const auto rsrc_vendor_dir = fs::path(resources_dir()) / "profiles"; - // Load vendors from the "vendors" directory in datadir + // Load vendors from the "vendors" directory in datadir for (auto &dir_entry : boost::filesystem::directory_iterator(vendor_dir)) - if (Slic3r::is_ini_file(dir_entry)) { - try { - auto vp = VendorProfile::from_ini(dir_entry.path()); - vendors[vp.id] = std::move(vp); - } - catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what(); - } - } + if (Slic3r::is_ini_file(dir_entry)) { + try { + auto vp = VendorProfile::from_ini(dir_entry.path()); + vendors[vp.id] = std::move(vp); + } + catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what(); + } + } - // Additionally load up vendors from the application resources directory, but only those not seen in the datadir + // Additionally load up vendors from the application resources directory, but only those not seen in the datadir for (auto &dir_entry : boost::filesystem::directory_iterator(rsrc_vendor_dir)) - if (Slic3r::is_ini_file(dir_entry)) { - const auto id = dir_entry.path().stem().string(); - if (vendors.find(id) == vendors.end()) { - try { - auto vp = VendorProfile::from_ini(dir_entry.path()); - vendors_rsrc[vp.id] = dir_entry.path().filename().string(); - vendors[vp.id] = std::move(vp); - } - catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what(); - } - } - } + if (Slic3r::is_ini_file(dir_entry)) { + const auto id = dir_entry.path().stem().string(); + if (vendors.find(id) == vendors.end()) { + try { + auto vp = VendorProfile::from_ini(dir_entry.path()); + vendors_rsrc[vp.id] = dir_entry.path().filename().string(); + vendors[vp.id] = std::move(vp); + } + catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what(); + } + } + } - // Load up the set of vendors / models / variants the user has had enabled up till now - const AppConfig *app_config = GUI::get_app_config(); - if (! app_config->legacy_datadir()) { - appconfig_vendors.set_vendors(*app_config); - } else { - // In case of legacy datadir, try to guess the preference based on the printer preset files that are present - const auto printer_dir = fs::path(Slic3r::data_dir()) / "printer"; - for (auto &dir_entry : boost::filesystem::directory_iterator(printer_dir)) - if (Slic3r::is_ini_file(dir_entry)) { - auto needle = legacy_preset_map.find(dir_entry.path().filename().string()); - if (needle == legacy_preset_map.end()) { continue; } + // Load up the set of vendors / models / variants the user has had enabled up till now + const AppConfig *app_config = GUI::get_app_config(); + if (! app_config->legacy_datadir()) { + appconfig_vendors.set_vendors(*app_config); + } else { + // In case of legacy datadir, try to guess the preference based on the printer preset files that are present + const auto printer_dir = fs::path(Slic3r::data_dir()) / "printer"; + for (auto &dir_entry : boost::filesystem::directory_iterator(printer_dir)) + if (Slic3r::is_ini_file(dir_entry)) { + auto needle = legacy_preset_map.find(dir_entry.path().filename().string()); + if (needle == legacy_preset_map.end()) { continue; } - const auto &model = needle->second.first; - const auto &variant = needle->second.second; - appconfig_vendors.set_variant("PrusaResearch", model, variant, true); - } - } -} - -void ConfigWizard::priv::index_refresh() -{ - index->load_items(page_welcome); + const auto &model = needle->second.first; + const auto &variant = needle->second.second; + appconfig_vendors.set_variant("PrusaResearch", model, variant, true); + } + } } void ConfigWizard::priv::add_page(ConfigWizardPage *page) { - hscroll_sizer->Add(page, 0, wxEXPAND); - - auto *extra_buttons = page->extra_buttons(); - if (extra_buttons != nullptr) { - btnsizer->Prepend(extra_buttons, 0); - } -} - -void ConfigWizard::priv::set_page(ConfigWizardPage *page) -{ - if (page == nullptr) { return; } - if (page_current != nullptr) { page_current->Hide(); } - page_current = page; - enable_next(true); - - page->on_page_set(); - index->load_items(page_welcome); - index->set_active(page); - page->Show(); - - btn_prev->Enable(page->page_prev() != nullptr); - btn_next->Show(page->page_next() != nullptr); - btn_finish->Show(page->page_next() == nullptr); - - layout_fit(); -} - -void ConfigWizard::priv::layout_fit() -{ - q->Layout(); - q->Fit(); + hscroll_sizer->Add(page, 0, wxEXPAND); } void ConfigWizard::priv::enable_next(bool enable) { - btn_next->Enable(enable); - btn_finish->Enable(enable); + btn_next->Enable(enable); + btn_finish->Enable(enable); } -void ConfigWizard::priv::on_other_vendors() +void ConfigWizard::priv::on_custom_setup(bool custom_wanted) { - page_welcome - ->chain(page_vendors) - ->chain(page_update); - set_page(page_vendors); -} - -void ConfigWizard::priv::on_custom_setup() -{ - page_welcome->chain(page_firmware); - page_temps->chain(page_update); - set_page(page_firmware); + load_pages(custom_wanted); } void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater) { - const bool is_custom_setup = page_welcome->page_next() == page_firmware; + const auto enabled_vendors = appconfig_vendors.vendors(); - if (! is_custom_setup) { - const auto enabled_vendors = appconfig_vendors.vendors(); + // Install bundles from resources if needed: + std::vector install_bundles; + for (const auto &vendor_rsrc : vendors_rsrc) { + const auto vendor = enabled_vendors.find(vendor_rsrc.first); + if (vendor == enabled_vendors.end()) { continue; } - // Install bundles from resources if needed: - std::vector install_bundles; - for (const auto &vendor_rsrc : vendors_rsrc) { - const auto vendor = enabled_vendors.find(vendor_rsrc.first); - if (vendor == enabled_vendors.end()) { continue; } + size_t size_sum = 0; + for (const auto &model : vendor->second) { size_sum += model.second.size(); } + if (size_sum == 0) { continue; } - size_t size_sum = 0; - for (const auto &model : vendor->second) { size_sum += model.second.size(); } - if (size_sum == 0) { continue; } + // This vendor needs to be installed + install_bundles.emplace_back(vendor_rsrc.second); + } - // This vendor needs to be installed - install_bundles.emplace_back(vendor_rsrc.second); - } + // Decide whether to create snapshot based on run_reason and the reset profile checkbox + bool snapshot = true; + switch (run_reason) { + case ConfigWizard::RR_DATA_EMPTY: snapshot = false; break; + case ConfigWizard::RR_DATA_LEGACY: snapshot = true; break; + case ConfigWizard::RR_DATA_INCOMPAT: snapshot = false; break; // In this case snapshot is done by PresetUpdater with the appropriate reason + case ConfigWizard::RR_USER: snapshot = page_welcome->reset_user_profile(); break; + } + if (install_bundles.size() > 0) { + // Install bundles from resources. + updater->install_bundles_rsrc(std::move(install_bundles), snapshot); + } else { + BOOST_LOG_TRIVIAL(info) << "No bundles need to be installed from resources"; + } - // Decide whether to create snapshot based on run_reason and the reset profile checkbox - bool snapshot = true; - switch (run_reason) { - case ConfigWizard::RR_DATA_EMPTY: snapshot = false; break; - case ConfigWizard::RR_DATA_LEGACY: snapshot = true; break; - case ConfigWizard::RR_DATA_INCOMPAT: snapshot = false; break; // In this case snapshot is done by PresetUpdater with the appropriate reason - case ConfigWizard::RR_USER: snapshot = page_welcome->reset_user_profile(); break; - } - if (install_bundles.size() > 0) { - // Install bundles from resources. - updater->install_bundles_rsrc(std::move(install_bundles), snapshot); - } else { - BOOST_LOG_TRIVIAL(info) << "No bundles need to be installed from resources"; - } + if (page_welcome->reset_user_profile()) { + BOOST_LOG_TRIVIAL(info) << "Resetting user profiles..."; + preset_bundle->reset(true); + } - if (page_welcome->reset_user_profile()) { - BOOST_LOG_TRIVIAL(info) << "Resetting user profiles..."; - preset_bundle->reset(true); - } + app_config->set_vendors(appconfig_vendors); + app_config->set("version_check", page_update->version_check ? "1" : "0"); + app_config->set("preset_update", page_update->preset_update ? "1" : "0"); + app_config->reset_selections(); + preset_bundle->load_presets(*app_config); - app_config->set_vendors(appconfig_vendors); - app_config->set("version_check", page_update->version_check ? "1" : "0"); - app_config->set("preset_update", page_update->preset_update ? "1" : "0"); - app_config->reset_selections(); - preset_bundle->load_presets(*app_config); - } else { - for (ConfigWizardPage *page = page_firmware; page != nullptr; page = page->page_next()) { - page->apply_custom_config(*custom_config); - } - preset_bundle->load_config("My Settings", *custom_config); - } - // Update the selections from the compatibilty. - preset_bundle->export_selections(*app_config); + + if (page_custom->custom_wanted()) { + page_firmware->apply_custom_config(*custom_config); + page_bed->apply_custom_config(*custom_config); + page_diams->apply_custom_config(*custom_config); + page_temps->apply_custom_config(*custom_config); + + const std::string profile_name = page_custom->profile_name(); + preset_bundle->load_config(profile_name, *custom_config); + } + + // Update the selections from the compatibilty. + preset_bundle->export_selections(*app_config); } // Public -ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) : - wxDialog(parent, wxID_ANY, _(name().ToStdString()), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), - p(new priv(this)) +ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) + : wxDialog(parent, wxID_ANY, _(name().ToStdString()), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) + , p(new priv(this)) { - p->run_reason = reason; + p->run_reason = reason; - p->load_vendors(); - p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({ - "gcode_flavor", "bed_shape", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature", - })); + p->load_vendors(); + p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({ + "gcode_flavor", "bed_shape", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature", + })); - p->index = new ConfigWizardIndex(this); + p->index = new ConfigWizardIndex(this); - auto *vsizer = new wxBoxSizer(wxVERTICAL); - auto *topsizer = new wxBoxSizer(wxHORIZONTAL); - auto *hline = new wxStaticLine(this); - p->btnsizer = new wxBoxSizer(wxHORIZONTAL); + auto *vsizer = new wxBoxSizer(wxVERTICAL); + auto *topsizer = new wxBoxSizer(wxHORIZONTAL); + auto *hline = new wxStaticLine(this); + p->btnsizer = new wxBoxSizer(wxHORIZONTAL); - // Initially we _do not_ SetScrollRate in order to figure out the overall width of the Wizard without scrolling. - // Later, we compare that to the size of the current screen and set minimum width based on that (see below). - p->hscroll = new wxScrolledWindow(this); - p->hscroll_sizer = new wxBoxSizer(wxHORIZONTAL); - p->hscroll->SetSizer(p->hscroll_sizer); + // Initially we _do not_ SetScrollRate in order to figure out the overall width of the Wizard without scrolling. + // Later, we compare that to the size of the current screen and set minimum width based on that (see below). + p->hscroll = new wxScrolledWindow(this); + p->hscroll_sizer = new wxBoxSizer(wxHORIZONTAL); + p->hscroll->SetSizer(p->hscroll_sizer); - topsizer->Add(p->index, 0, wxEXPAND); - topsizer->AddSpacer(INDEX_MARGIN); - topsizer->Add(p->hscroll, 1, wxEXPAND); + topsizer->Add(p->index, 0, wxEXPAND); + topsizer->AddSpacer(INDEX_MARGIN); + topsizer->Add(p->hscroll, 1, wxEXPAND); - p->btn_prev = new wxButton(this, wxID_ANY, _(L("< &Back"))); - p->btn_next = new wxButton(this, wxID_ANY, _(L("&Next >"))); - p->btn_finish = new wxButton(this, wxID_APPLY, _(L("&Finish"))); - p->btn_cancel = new wxButton(this, wxID_CANCEL); - p->btnsizer->AddStretchSpacer(); - p->btnsizer->Add(p->btn_prev, 0, wxLEFT, BTN_SPACING); - p->btnsizer->Add(p->btn_next, 0, wxLEFT, BTN_SPACING); - p->btnsizer->Add(p->btn_finish, 0, wxLEFT, BTN_SPACING); - p->btnsizer->Add(p->btn_cancel, 0, wxLEFT, BTN_SPACING); + auto *btn_sel_all = new wxButton(this, wxID_ANY, _(L("Select all printers"))); + p->btnsizer->Add(btn_sel_all); - p->add_page(p->page_welcome = new PageWelcome(this, reason == RR_DATA_EMPTY || reason == RR_DATA_LEGACY)); - p->add_page(p->page_update = new PageUpdate(this)); - p->add_page(p->page_vendors = new PageVendors(this)); - p->add_page(p->page_firmware = new PageFirmware(this)); - p->add_page(p->page_bed = new PageBedShape(this)); - p->add_page(p->page_diams = new PageDiameters(this)); - p->add_page(p->page_temps = new PageTemperatures(this)); - p->index_refresh(); + p->btn_prev = new wxButton(this, wxID_ANY, _(L("< &Back"))); + p->btn_next = new wxButton(this, wxID_ANY, _(L("&Next >"))); + p->btn_finish = new wxButton(this, wxID_APPLY, _(L("&Finish"))); + p->btn_cancel = new wxButton(this, wxID_CANCEL); + p->btnsizer->AddStretchSpacer(); + p->btnsizer->Add(p->btn_prev, 0, wxLEFT, BTN_SPACING); + p->btnsizer->Add(p->btn_next, 0, wxLEFT, BTN_SPACING); + p->btnsizer->Add(p->btn_finish, 0, wxLEFT, BTN_SPACING); + p->btnsizer->Add(p->btn_cancel, 0, wxLEFT, BTN_SPACING); - p->page_welcome->chain(p->page_update); - p->page_firmware - ->chain(p->page_bed) - ->chain(p->page_diams) - ->chain(p->page_temps); + const auto &vendors = p->vendors; + const auto vendor_prusa_it = vendors.find("PrusaResearch"); + wxCHECK_RET(vendor_prusa_it != vendors.cend(), "Vendor PrusaResearch not found"); + const VendorProfile &vendor_prusa = vendor_prusa_it->second; - vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN); - vsizer->Add(hline, 0, wxEXPAND); - vsizer->Add(p->btnsizer, 0, wxEXPAND | wxALL, DIALOG_MARGIN); + p->add_page(p->page_welcome = new PageWelcome(this)); - p->set_page(p->page_welcome); - SetSizer(vsizer); - SetSizerAndFit(vsizer); + p->page_fff = new PagePrinters(this, _(L("Prusa FFF Technology Printers")), "Prusa FFF", vendor_prusa, 0, PagePrinters::T_FFF); + p->add_page(p->page_fff); - // We can now enable scrolling on hscroll - p->hscroll->SetScrollRate(30, 30); - // Compare current ("ideal") wizard size with the size of the current screen. - // If the screen is smaller, resize wizrad to match, which will enable scrollbars. - auto wizard_size = GetSize(); - unsigned width, height; - if (GUI::get_current_screen_size(this, width, height)) { - wizard_size.SetWidth(std::min(wizard_size.GetWidth(), (int)(width - 2 * DIALOG_MARGIN))); - wizard_size.SetHeight(std::min(wizard_size.GetHeight(), (int)(height - 2 * DIALOG_MARGIN))); - SetMinSize(wizard_size); - } - Fit(); + p->page_msla = new PagePrinters(this, _(L("Prusa MSLA Technology Printers")), "Prusa MSLA", vendor_prusa, 0, PagePrinters::T_SLA); + p->add_page(p->page_msla); - p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_prev(); }); - p->btn_next->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_next(); }); - p->btn_finish->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->EndModal(wxID_OK); }); + p->add_page(p->page_custom = new PageCustom(this)); + p->add_page(p->page_update = new PageUpdate(this)); + p->add_page(p->page_vendors = new PageVendors(this)); + p->add_page(p->page_firmware = new PageFirmware(this)); + p->add_page(p->page_bed = new PageBedShape(this)); + p->add_page(p->page_diams = new PageDiameters(this)); + p->add_page(p->page_temps = new PageTemperatures(this)); + + p->load_pages(false); + + vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN); + vsizer->Add(hline, 0, wxEXPAND); + vsizer->Add(p->btnsizer, 0, wxEXPAND | wxALL, DIALOG_MARGIN); + + SetSizer(vsizer); + SetSizerAndFit(vsizer); + + // We can now enable scrolling on hscroll + p->hscroll->SetScrollRate(30, 30); + + // XXX: set size right away on windows, create util function + Bind(wxEVT_CREATE, [this](wxWindowCreateEvent &event) { + CallAfter([this]() { + // Clamp the Wizard size based on screen dimensions + // Note: Using EVT_SHOW + CallAfter because any sooner than this + // on some Linux boxes wxDisplay::GetFromWindow() returns 0 no matter what. + + const auto idx = wxDisplay::GetFromWindow(this); + wxDisplay display(idx != wxNOT_FOUND ? idx : 0u); + + const auto disp_rect = display.GetClientArea(); + wxRect window_rect( + disp_rect.x + disp_rect.width / 20, + disp_rect.y + disp_rect.height / 20, + 9*disp_rect.width / 10, + 9*disp_rect.height / 10); + + SetSize(window_rect); + }); + }); + + p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { this->p->index->go_prev(); }); + p->btn_next->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { this->p->index->go_next(); }); + p->btn_finish->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { this->EndModal(wxID_OK); }); + p->btn_finish->Hide(); + + btn_sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { + p->page_fff->select_all(true); + p->page_msla->select_all(true); + p->index->go_to(p->page_update); + }); + + p->index->Bind(EVT_INDEX_PAGE, [this](const wxCommandEvent &) { + const bool is_last = p->index->active_is_last(); + p->btn_next->Show(! is_last); + p->btn_finish->Show(is_last); + + Layout(); + }); } ConfigWizard::~ConfigWizard() {} bool ConfigWizard::run(PresetBundle *preset_bundle, const PresetUpdater *updater) { - BOOST_LOG_TRIVIAL(info) << "Running ConfigWizard, reason: " << p->run_reason; - if (ShowModal() == wxID_OK) { - auto *app_config = GUI::get_app_config(); - p->apply_config(app_config, preset_bundle, updater); - app_config->set_legacy_datadir(false); - BOOST_LOG_TRIVIAL(info) << "ConfigWizard applied"; - return true; - } else { - BOOST_LOG_TRIVIAL(info) << "ConfigWizard cancelled"; - return false; - } + BOOST_LOG_TRIVIAL(info) << "Running ConfigWizard, reason: " << p->run_reason; + if (ShowModal() == wxID_OK) { + auto *app_config = GUI::get_app_config(); + p->apply_config(app_config, preset_bundle, updater); + app_config->set_legacy_datadir(false); + BOOST_LOG_TRIVIAL(info) << "ConfigWizard applied"; + return true; + } else { + BOOST_LOG_TRIVIAL(info) << "ConfigWizard cancelled"; + return false; + } } const wxString& ConfigWizard::name(const bool from_menu/* = false*/) { - // A different naming convention is used for the Wizard on Windows vs. OSX & GTK. + // A different naming convention is used for the Wizard on Windows vs. OSX & GTK. #if WIN32 static const wxString config_wizard_name = L("Configuration Wizard"); static const wxString config_wizard_name_menu = L("Configuration &Wizard"); #else - static const wxString config_wizard_name = L("Configuration Assistant"); + static const wxString config_wizard_name = L("Configuration Assistant"); static const wxString config_wizard_name_menu = L("Configuration &Assistant"); #endif - return from_menu ? config_wizard_name_menu : config_wizard_name; + return from_menu ? config_wizard_name_menu : config_wizard_name; } } diff --git a/src/slic3r/GUI/ConfigWizard.hpp b/src/slic3r/GUI/ConfigWizard.hpp index c7fba76ee..c9ee05529 100644 --- a/src/slic3r/GUI/ConfigWizard.hpp +++ b/src/slic3r/GUI/ConfigWizard.hpp @@ -16,30 +16,30 @@ namespace GUI { class ConfigWizard: public wxDialog { public: - // Why is the Wizard run - enum RunReason { - RR_DATA_EMPTY, // No or empty datadir - RR_DATA_LEGACY, // Pre-updating datadir - RR_DATA_INCOMPAT, // Incompatible datadir - Slic3r downgrade situation - RR_USER, // User requested the Wizard from the menus - }; + // Why is the Wizard run + enum RunReason { + RR_DATA_EMPTY, // No or empty datadir + RR_DATA_LEGACY, // Pre-updating datadir + RR_DATA_INCOMPAT, // Incompatible datadir - Slic3r downgrade situation + RR_USER, // User requested the Wizard from the menus + }; - ConfigWizard(wxWindow *parent, RunReason run_reason); - ConfigWizard(ConfigWizard &&) = delete; - ConfigWizard(const ConfigWizard &) = delete; - ConfigWizard &operator=(ConfigWizard &&) = delete; - ConfigWizard &operator=(const ConfigWizard &) = delete; - ~ConfigWizard(); + ConfigWizard(wxWindow *parent, RunReason run_reason); + ConfigWizard(ConfigWizard &&) = delete; + ConfigWizard(const ConfigWizard &) = delete; + ConfigWizard &operator=(ConfigWizard &&) = delete; + ConfigWizard &operator=(const ConfigWizard &) = delete; + ~ConfigWizard(); - // Run the Wizard. Return whether it was completed. - bool run(PresetBundle *preset_bundle, const PresetUpdater *updater); + // Run the Wizard. Return whether it was completed. + bool run(PresetBundle *preset_bundle, const PresetUpdater *updater); - static const wxString& name(const bool from_menu = false); + static const wxString& name(const bool from_menu = false); private: - struct priv; - std::unique_ptr p; + struct priv; + std::unique_ptr p; - friend struct ConfigWizardPage; + friend struct ConfigWizardPage; }; diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index 2c8f23cd3..c31911429 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include "libslic3r/PrintConfig.hpp" #include "slic3r/Utils/PresetUpdater.hpp" @@ -26,211 +28,270 @@ namespace Slic3r { namespace GUI { enum { - WRAP_WIDTH = 500, - MODEL_MIN_WRAP = 150, + WRAP_WIDTH = 500, + MODEL_MIN_WRAP = 150, - DIALOG_MARGIN = 15, - INDEX_MARGIN = 40, - BTN_SPACING = 10, - INDENT_SPACING = 30, - VERTICAL_SPACING = 10, + DIALOG_MARGIN = 15, + INDEX_MARGIN = 40, + BTN_SPACING = 10, + INDENT_SPACING = 30, + VERTICAL_SPACING = 10, + + MAX_COLS = 4, + ROW_SPACING = 75, }; +typedef std::function ModelFilter; + struct PrinterPicker: wxPanel { - struct Checkbox : wxCheckBox - { - Checkbox(wxWindow *parent, const wxString &label, const std::string &model, const std::string &variant) : - wxCheckBox(parent, wxID_ANY, label), - model(model), - variant(variant) - {} + struct Checkbox : wxCheckBox + { + Checkbox(wxWindow *parent, const wxString &label, const std::string &model, const std::string &variant) : + wxCheckBox(parent, wxID_ANY, label), + model(model), + variant(variant) + {} - std::string model; - std::string variant; - }; + std::string model; + std::string variant; + }; - const std::string vendor_id; - std::vector cboxes; - unsigned variants_checked; + const std::string vendor_id; + std::vector cboxes; + std::vector cboxes_alt; - PrinterPicker(wxWindow *parent, const VendorProfile &vendor, const AppConfig &appconfig_vendors); + PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors, const ModelFilter &filter); + PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors); - void select_all(bool select); - void select_one(size_t i, bool select); - void on_checkbox(const Checkbox *cbox, bool checked); + void select_all(bool select); + void select_one(size_t i, bool select); + void on_checkbox(const Checkbox *cbox, bool checked); }; struct ConfigWizardPage: wxPanel { - ConfigWizard *parent; - const wxString shortname; - wxBoxSizer *content; + ConfigWizard *parent; + const wxString shortname; + wxBoxSizer *content; + const unsigned indent; - ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname); + ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname, unsigned indent = 0); + virtual ~ConfigWizardPage(); - virtual ~ConfigWizardPage(); + template + void append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10) + { + content->Add(thing, proportion, flag, border); + } - ConfigWizardPage* page_prev() const { return p_prev; } - ConfigWizardPage* page_next() const { return p_next; } - ConfigWizardPage* chain(ConfigWizardPage *page); + void append_text(wxString text); + void append_spacer(int space); - template - void append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10) - { - content->Add(thing, proportion, flag, border); - } + ConfigWizard::priv *wizard_p() const { return parent->p.get(); } - void append_text(wxString text); - void append_spacer(int space); - - ConfigWizard::priv *wizard_p() const { return parent->p.get(); } - - virtual bool Show(bool show = true); - virtual bool Hide() { return Show(false); } - virtual wxPanel* extra_buttons() { return nullptr; } - virtual void on_page_set() {} - virtual void apply_custom_config(DynamicPrintConfig &config) {} - - void enable_next(bool enable); -private: - ConfigWizardPage *p_prev; - ConfigWizardPage *p_next; + virtual bool Show(bool show = true); + virtual bool Hide() { return Show(false); } + virtual wxPanel* extra_buttons() { return nullptr; } // XXX + virtual void apply_custom_config(DynamicPrintConfig &config) {} }; struct PageWelcome: ConfigWizardPage { - PrinterPicker *printer_picker; - wxPanel *others_buttons; - wxCheckBox *cbox_reset; + wxCheckBox *cbox_reset; - PageWelcome(ConfigWizard *parent, bool check_first_variant); + PageWelcome(ConfigWizard *parent); - virtual wxPanel* extra_buttons() { return others_buttons; } - virtual void on_page_set(); + bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; } +}; + +struct PagePrinters: ConfigWizardPage +{ + enum Technology { + // Bitflag equivalent of PrinterTechnology + T_FFF = 0x1, + T_SLA = 0x2, + T_Any = ~0, + }; + + std::vector printer_pickers; + + PagePrinters(ConfigWizard *parent, wxString title, wxString shortname, const VendorProfile &vendor, unsigned indent, Technology technology); + + void select_all(bool select); +}; + +struct PageCustom: ConfigWizardPage +{ + PageCustom(ConfigWizard *parent); + + bool custom_wanted() const { return cb_custom->GetValue(); } + std::string profile_name() const { return into_u8(tc_profile_name->GetValue()); } + +private: + wxCheckBox *cb_custom; + wxTextCtrl *tc_profile_name; - bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; } - void on_variant_checked(); }; struct PageUpdate: ConfigWizardPage { - bool version_check; - bool preset_update; + bool version_check; + bool preset_update; - PageUpdate(ConfigWizard *parent); + PageUpdate(ConfigWizard *parent); }; struct PageVendors: ConfigWizardPage { - std::vector pickers; + std::vector pickers; - PageVendors(ConfigWizard *parent); + PageVendors(ConfigWizard *parent); - virtual void on_page_set(); - - void on_vendor_pick(size_t i); - void on_variant_checked(); + void on_vendor_pick(size_t i); }; struct PageFirmware: ConfigWizardPage { - const ConfigOptionDef &gcode_opt; - wxChoice *gcode_picker; + const ConfigOptionDef &gcode_opt; + wxChoice *gcode_picker; - PageFirmware(ConfigWizard *parent); - virtual void apply_custom_config(DynamicPrintConfig &config); + PageFirmware(ConfigWizard *parent); + virtual void apply_custom_config(DynamicPrintConfig &config); }; struct PageBedShape: ConfigWizardPage { - BedShapePanel *shape_panel; + BedShapePanel *shape_panel; - PageBedShape(ConfigWizard *parent); - virtual void apply_custom_config(DynamicPrintConfig &config); + PageBedShape(ConfigWizard *parent); + virtual void apply_custom_config(DynamicPrintConfig &config); }; struct PageDiameters: ConfigWizardPage { - wxSpinCtrlDouble *spin_nozzle; - wxSpinCtrlDouble *spin_filam; + wxSpinCtrlDouble *spin_nozzle; + wxSpinCtrlDouble *spin_filam; - PageDiameters(ConfigWizard *parent); - virtual void apply_custom_config(DynamicPrintConfig &config); + PageDiameters(ConfigWizard *parent); + virtual void apply_custom_config(DynamicPrintConfig &config); }; struct PageTemperatures: ConfigWizardPage { - wxSpinCtrlDouble *spin_extr; - wxSpinCtrlDouble *spin_bed; + wxSpinCtrlDouble *spin_extr; + wxSpinCtrlDouble *spin_bed; - PageTemperatures(ConfigWizard *parent); - virtual void apply_custom_config(DynamicPrintConfig &config); + PageTemperatures(ConfigWizard *parent); + virtual void apply_custom_config(DynamicPrintConfig &config); }; class ConfigWizardIndex: public wxPanel { public: - ConfigWizardIndex(wxWindow *parent); + ConfigWizardIndex(wxWindow *parent); - void load_items(ConfigWizardPage *firstpage); - void set_active(ConfigWizardPage *page); + void add_page(ConfigWizardPage *page); + void add_label(wxString label, unsigned indent = 0); + + size_t active_item() const { return item_active; } + ConfigWizardPage* active_page() const; + bool active_is_last() const { return item_active < items.size() && item_active == last_page; } + + void go_prev(); + void go_next(); + void go_to(size_t i); + void go_to(ConfigWizardPage *page); + + void clear(); + + // XXX + // void load_items(ConfigWizardPage *firstpage); + // void set_active(ConfigWizardPage *page); private: - const wxBitmap bg; - const wxBitmap bullet_black; - const wxBitmap bullet_blue; - const wxBitmap bullet_white; - int text_height; + // enum { + // // Units in em + // MARGIN = 1, + // SPACING = 1, + // }; - std::vector items; - std::vector::const_iterator item_active; + struct Item + { + wxString label; + unsigned indent; + ConfigWizardPage *page; // nullptr page => label-only item - void on_paint(wxPaintEvent &evt); + // bool operator==(const wxString &label) const { return this->label == label; } + bool operator==(ConfigWizardPage *page) const { return this->page == page; } + }; + + int em; + int em_h; + + const wxBitmap bg; + const wxBitmap bullet_black; + const wxBitmap bullet_blue; + const wxBitmap bullet_white; + + std::vector items; + // std::vector::const_iterator item_active; + size_t item_active; + ssize_t item_hover; + size_t last_page; + + int item_height() const { return std::max(bullet_black.GetSize().GetHeight(), em) + em; } + + void on_paint(wxPaintEvent &evt); + void on_mouse_move(wxMouseEvent &evt); }; +wxDEFINE_EVENT(EVT_INDEX_PAGE, wxCommandEvent); + struct ConfigWizard::priv { - ConfigWizard *q; - ConfigWizard::RunReason run_reason; - AppConfig appconfig_vendors; - std::unordered_map vendors; - std::unordered_map vendors_rsrc; - std::unique_ptr custom_config; + ConfigWizard *q; + ConfigWizard::RunReason run_reason; + AppConfig appconfig_vendors; + std::unordered_map vendors; + std::unordered_map vendors_rsrc; + std::unique_ptr custom_config; - wxScrolledWindow *hscroll = nullptr; - wxBoxSizer *hscroll_sizer = nullptr; - wxBoxSizer *btnsizer = nullptr; - ConfigWizardPage *page_current = nullptr; - ConfigWizardIndex *index = nullptr; - wxButton *btn_prev = nullptr; - wxButton *btn_next = nullptr; - wxButton *btn_finish = nullptr; - wxButton *btn_cancel = nullptr; + wxScrolledWindow *hscroll = nullptr; + wxBoxSizer *hscroll_sizer = nullptr; + wxBoxSizer *btnsizer = nullptr; + ConfigWizardPage *page_current = nullptr; + ConfigWizardIndex *index = nullptr; + wxButton *btn_prev = nullptr; + wxButton *btn_next = nullptr; + wxButton *btn_finish = nullptr; + wxButton *btn_cancel = nullptr; - PageWelcome *page_welcome = nullptr; - PageUpdate *page_update = nullptr; - PageVendors *page_vendors = nullptr; - PageFirmware *page_firmware = nullptr; - PageBedShape *page_bed = nullptr; - PageDiameters *page_diams = nullptr; - PageTemperatures *page_temps = nullptr; + PageWelcome *page_welcome = nullptr; + PagePrinters *page_fff = nullptr; + PagePrinters *page_msla = nullptr; + PageCustom *page_custom = nullptr; + PageUpdate *page_update = nullptr; + PageVendors *page_vendors = nullptr; // XXX: ? - priv(ConfigWizard *q) : q(q) {} + // Custom setup pages + PageFirmware *page_firmware = nullptr; + PageBedShape *page_bed = nullptr; + PageDiameters *page_diams = nullptr; + PageTemperatures *page_temps = nullptr; - void load_vendors(); - void add_page(ConfigWizardPage *page); - void index_refresh(); - void set_page(ConfigWizardPage *page); - void layout_fit(); - void go_prev() { if (page_current != nullptr) { set_page(page_current->page_prev()); } } - void go_next() { if (page_current != nullptr) { set_page(page_current->page_next()); } } - void enable_next(bool enable); + priv(ConfigWizard *q) : q(q) {} - void on_other_vendors(); - void on_custom_setup(); + void load_pages(bool custom_setup); - void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater); + bool check_first_variant() const; + void load_vendors(); + void add_page(ConfigWizardPage *page); + void enable_next(bool enable); + + void on_custom_setup(bool custom_wanted); + + void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater); }; diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 8e80d64c3..a15f50032 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -355,21 +355,6 @@ boost::filesystem::path into_path(const wxString &str) return boost::filesystem::path(str.wx_str()); } -bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height) -{ - const auto idx = wxDisplay::GetFromWindow(window); - if (idx == wxNOT_FOUND) { - return false; - } - - wxDisplay display(idx); - const auto disp_size = display.GetClientArea(); - width = disp_size.GetWidth(); - height = disp_size.GetHeight(); - - return true; -} - void about() { AboutDialog dlg; diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp index f066c82a8..4074c2afc 100644 --- a/src/slic3r/GUI/GUI.hpp +++ b/src/slic3r/GUI/GUI.hpp @@ -71,9 +71,6 @@ wxString from_path(const boost::filesystem::path &path); // boost path from wxString boost::filesystem::path into_path(const wxString &str); -// Returns the dimensions of the screen on which the main frame is displayed -bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height); - // Display an About dialog extern void about(); // Ask the destop to open the datadir using the default file explorer. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 774e61024..f5471cdff 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include #include diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index ee182a01a..dedd902ef 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -11,6 +11,7 @@ #include #endif /* _MSC_VER */ +#include #include #include #include @@ -128,11 +129,14 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem VendorProfile::PrinterModel model; model.id = section.first.substr(printer_model_key.size()); model.name = section.second.get("name", model.id); + auto technology_field = section.second.get("technology", "FFF"); if (! ConfigOptionEnum::from_string(technology_field, model.technology)) { BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Invalid printer technology field: `%2%`") % id % technology_field; model.technology = ptFFF; } + + model.family = section.second.get("family", model.id); #if 0 // Remove SLA printers from the initial alpha. if (model.technology == ptSLA) @@ -157,6 +161,20 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem return res; } +std::vector VendorProfile::families() const +{ + std::vector res; + unsigned num_familiies = 0; + + for (auto &model : models) { + if (!model.family.empty() && std::find(res.begin(), res.end(), model.family) == res.end()) { + res.push_back(model.family); + num_familiies++; + } + } + + return res; +} // Suffix to be added to a modified preset name in the combo box. static std::string g_suffix_modified = " (modified)"; diff --git a/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp index 73a921cf7..074e665c9 100644 --- a/src/slic3r/GUI/Preset.hpp +++ b/src/slic3r/GUI/Preset.hpp @@ -54,13 +54,16 @@ public: std::string id; std::string name; PrinterTechnology technology; + std::string family; std::vector variants; + PrinterVariant* variant(const std::string &name) { for (auto &v : this->variants) if (v.name == name) return &v; return nullptr; } + const PrinterVariant* variant(const std::string &name) const { return const_cast(this)->variant(name); } }; std::vector models; @@ -72,6 +75,7 @@ public: static VendorProfile from_ini(const boost::property_tree::ptree &tree, const boost::filesystem::path &path, bool load_all=true); size_t num_variants() const { size_t n = 0; for (auto &model : models) n += model.variants.size(); return n; } + std::vector families() const; bool operator< (const VendorProfile &rhs) const { return this->id < rhs.id; } bool operator==(const VendorProfile &rhs) const { return this->id == rhs.id; }