diff --git a/resources/icons/bed/mk2_4096_bottom.png b/resources/icons/bed/mk2_4096_bottom.png new file mode 100644 index 0000000000..dcded1f7ee Binary files /dev/null and b/resources/icons/bed/mk2_4096_bottom.png differ diff --git a/resources/icons/bed/mk2_4096_top.png b/resources/icons/bed/mk2_4096_top.png new file mode 100644 index 0000000000..1fe8c67060 Binary files /dev/null and b/resources/icons/bed/mk2_4096_top.png differ diff --git a/resources/icons/bed/mk2_8192_bottom.png b/resources/icons/bed/mk2_8192_bottom.png new file mode 100644 index 0000000000..ccc83d3111 Binary files /dev/null and b/resources/icons/bed/mk2_8192_bottom.png differ diff --git a/resources/icons/bed/mk2_8192_top.png b/resources/icons/bed/mk2_8192_top.png new file mode 100644 index 0000000000..5e2ab6a500 Binary files /dev/null and b/resources/icons/bed/mk2_8192_top.png differ diff --git a/resources/icons/bed/mk3_4096_bottom.png b/resources/icons/bed/mk3_4096_bottom.png new file mode 100644 index 0000000000..4a02aaefff Binary files /dev/null and b/resources/icons/bed/mk3_4096_bottom.png differ diff --git a/resources/icons/bed/mk3_4096_top.png b/resources/icons/bed/mk3_4096_top.png new file mode 100644 index 0000000000..a2a1824dd2 Binary files /dev/null and b/resources/icons/bed/mk3_4096_top.png differ diff --git a/resources/icons/bed/mk3_8192_bottom.png b/resources/icons/bed/mk3_8192_bottom.png new file mode 100644 index 0000000000..aaa6243734 Binary files /dev/null and b/resources/icons/bed/mk3_8192_bottom.png differ diff --git a/resources/icons/bed/mk3_8192_top.png b/resources/icons/bed/mk3_8192_top.png new file mode 100644 index 0000000000..6b1e41e5c3 Binary files /dev/null and b/resources/icons/bed/mk3_8192_top.png differ diff --git a/resources/icons/bed/sl1_4096_bottom.png b/resources/icons/bed/sl1_4096_bottom.png new file mode 100644 index 0000000000..0998c0e3bf Binary files /dev/null and b/resources/icons/bed/sl1_4096_bottom.png differ diff --git a/resources/icons/bed/sl1_4096_top.png b/resources/icons/bed/sl1_4096_top.png new file mode 100644 index 0000000000..7abd8b049e Binary files /dev/null and b/resources/icons/bed/sl1_4096_top.png differ diff --git a/resources/icons/bed/sl1_8192_bottom.png b/resources/icons/bed/sl1_8192_bottom.png new file mode 100644 index 0000000000..4e38fc9204 Binary files /dev/null and b/resources/icons/bed/sl1_8192_bottom.png differ diff --git a/resources/icons/bed/sl1_8192_top.png b/resources/icons/bed/sl1_8192_top.png new file mode 100644 index 0000000000..c01d59c7b4 Binary files /dev/null and b/resources/icons/bed/sl1_8192_top.png differ diff --git a/resources/icons/printers/PrusaResearch_MK2.5.png b/resources/icons/printers/PrusaResearch_MK2.5.png index efbbc598da..d9bf3260d4 100644 Binary files a/resources/icons/printers/PrusaResearch_MK2.5.png and b/resources/icons/printers/PrusaResearch_MK2.5.png differ diff --git a/resources/icons/printers/PrusaResearch_MK2.5MMU2.png b/resources/icons/printers/PrusaResearch_MK2.5MMU2.png index eb5dccf08c..7d6e9feded 100644 Binary files a/resources/icons/printers/PrusaResearch_MK2.5MMU2.png and b/resources/icons/printers/PrusaResearch_MK2.5MMU2.png differ diff --git a/resources/icons/printers/PrusaResearch_MK2.5S.png b/resources/icons/printers/PrusaResearch_MK2.5S.png new file mode 100644 index 0000000000..d9bf3260d4 Binary files /dev/null and b/resources/icons/printers/PrusaResearch_MK2.5S.png differ diff --git a/resources/icons/printers/PrusaResearch_MK2.5SMMU2S.png b/resources/icons/printers/PrusaResearch_MK2.5SMMU2S.png new file mode 100644 index 0000000000..7d6e9feded Binary files /dev/null and b/resources/icons/printers/PrusaResearch_MK2.5SMMU2S.png differ diff --git a/resources/icons/printers/PrusaResearch_MK3.png b/resources/icons/printers/PrusaResearch_MK3.png index 5279ba01e0..5fa1a665b5 100644 Binary files a/resources/icons/printers/PrusaResearch_MK3.png and b/resources/icons/printers/PrusaResearch_MK3.png differ diff --git a/resources/icons/printers/PrusaResearch_MK3S.png b/resources/icons/printers/PrusaResearch_MK3S.png new file mode 100644 index 0000000000..5fa1a665b5 Binary files /dev/null and b/resources/icons/printers/PrusaResearch_MK3S.png differ diff --git a/resources/icons/printers/PrusaResearch_MK3SMMU2S.png b/resources/icons/printers/PrusaResearch_MK3SMMU2S.png new file mode 100644 index 0000000000..eb5dccf08c Binary files /dev/null and b/resources/icons/printers/PrusaResearch_MK3SMMU2S.png differ diff --git a/resources/icons/printers/PrusaResearch_SL1.png b/resources/icons/printers/PrusaResearch_SL1.png index 281d7a6202..3c15f95eae 100644 Binary files a/resources/icons/printers/PrusaResearch_SL1.png and b/resources/icons/printers/PrusaResearch_SL1.png differ diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index a9d3be5396..8f3423a3db 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -1333,6 +1333,8 @@ void Transformation::set_rotation(const Vec3d& rotation) void Transformation::set_rotation(Axis axis, double rotation) { rotation = angle_to_0_2PI(rotation); + if (is_approx(std::abs(rotation), 2.0 * (double)PI)) + rotation = 0.0; if (m_rotation(axis) != rotation) { diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 0dbf9dd157..4227172796 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1445,7 +1445,7 @@ int ModelVolume::extruder_id() const int extruder_id = -1; if (this->is_model_part()) { const ConfigOption *opt = this->config.option("extruder"); - if (opt == nullptr) + if ((opt == nullptr) || (opt->getInt() == 0)) opt = this->object->config.option("extruder"); extruder_id = (opt == nullptr) ? 0 : opt->getInt(); } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index bc692ca90d..6416a709ad 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1137,6 +1137,9 @@ std::string Print::validate() const // Apply the same transformations we apply to the actual meshes when slicing them. object->model_object()->instances.front()->transform_polygon(&convex_hull); // Grow convex hull with the clearance margin. + // FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2) + // which causes that the warning will be showed after arrangement with the + // appropriate object distance. Even if I set this to jtMiter the warning still shows up. convex_hull = offset(convex_hull, scale_(m_config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1)).front(); // Now we check that no instance of convex_hull intersects any of the previously checked object instances. for (const Point © : object->m_copies) { diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 3471395c29..8159eff0b5 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -1,10 +1,12 @@ #include "ConfigWizard_private.hpp" #include +#include #include #include #include #include +#include #include #include @@ -13,10 +15,15 @@ #include #include #include +#include +#include +#include +#include #include "libslic3r/Utils.hpp" #include "PresetBundle.hpp" #include "GUI.hpp" +#include "GUI_Utils.hpp" #include "slic3r/Utils/PresetUpdater.hpp" @@ -28,886 +35,1086 @@ 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) + , width(0) { - 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; + int max_row_width = 0; + int current_row_width = 0; - 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); + for (const auto &model : models) { + if (! filter(model)) { continue; } - 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); + wxBitmap bitmap(GUI::from_u8(Slic3r::var((boost::format("printers/%1%_%2%.png") % vendor.id % model.id).str())), wxBITMAP_TYPE_PNG); - auto *bitmap_widget = new wxStaticBitmap(this, wxID_ANY, bitmap); - bitmaps.push_back(bitmap_widget); + auto *title = new wxStaticText(this, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + title->SetFont(font_name); + const int wrap_width = std::max((int)MODEL_MIN_WRAP, bitmap.GetWidth()); + title->Wrap(wrap_width); - auto *variants_panel = new wxPanel(this); - auto *variants_sizer = new wxBoxSizer(wxVERTICAL); - variants_panel->SetSizer(variants_sizer); - const auto model_id = model.id; + current_row_width += wrap_width; + if (titles.size() % max_cols == max_cols - 1) { + max_row_width = std::max(max_row_width, current_row_width); + current_row_width = 0; + } - 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()); - }); - } + titles.push_back(title); - variants_panels.push_back(variants_panel); - } + auto *bitmap_widget = new wxStaticBitmap(this, wxID_ANY, bitmap); + bitmaps.push_back(bitmap_widget); - 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); } + auto *variants_panel = new wxPanel(this); + auto *variants_sizer = new wxBoxSizer(wxVERTICAL); + variants_panel->SetSizer(variants_sizer); + const auto model_id = model.id; - 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); + for (size_t i = 0; i < model.variants.size(); i++) { + const auto &variant = model.variants[i]; - SetSizer(sizer); + const auto label = model.technology == ptFFF + ? wxString::Format("%s %s %s", variant.name, _(L("mm")), _(L("nozzle"))) + : from_u8(model.name); + + 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 *cbox = new Checkbox(variants_panel, label, model_id, variant.name); + i == 0 ? cboxes.push_back(cbox) : cboxes_alt.push_back(cbox); + + 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); + } + + width = std::max(max_row_width, current_row_width); + + 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); + if (! title.IsEmpty()) { + 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_std = new wxButton(this, wxID_ANY, _(L("All standard"))); + auto *sel_all = new wxButton(this, wxID_ANY, _(L("All"))); + auto *sel_none = new wxButton(this, wxID_ANY, _(L("None"))); + sel_all_std->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true, false); }); + sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true, true); }); + sel_none->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(false); }); + title_sizer->Add(sel_all_std, 0, wxRIGHT, BTN_SPACING); + 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); + + SetSizer(sizer); } -void PrinterPicker::select_all(bool select) +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, bool alternates) { - 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); + } + } + + if (! select) { alternates = false; } + + for (const auto &cb : cboxes_alt) { + if (cb->GetValue() != alternates) { + cb->SetValue(alternates); + on_checkbox(cb, alternates); + } + } } 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); -} - -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, bool alternates) { - enable_next(printer_picker != nullptr ? printer_picker->variants_checked > 0 : false); + for (auto picker : printer_pickers) { + picker->select_all(select, alternates); + } } -PageUpdate::PageUpdate(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Automatic updates")), _(L("Updates"))), - version_check(true), - preset_update(true) +int PagePrinters::get_width() const { - const AppConfig *app_config = GUI::get_app_config(); - 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."))); - - 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(); }); + return std::accumulate(printer_pickers.begin(), printer_pickers.end(), 0, + [](int acc, const PrinterPicker *picker) { return std::max(acc, picker->get_width()); }); } -PageVendors::PageVendors(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Other Vendors")), _(L("Other Vendors"))) + +const char *PageCustom::default_profile_name = "My Settings"; + +PageCustom::PageCustom(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("Custom Printer Setup")), _(L("Custom Printer"))) { - append_text(_(L("Pick another vendor supported by Slic3r PE:"))); + cb_custom = new wxCheckBox(this, wxID_ANY, _(L("Define a custom printer profile"))); + tc_profile_name = new wxTextCtrl(this, wxID_ANY, default_profile_name); + auto *label = new wxStaticText(this, wxID_ANY, _(L("Custom profile name:"))); - auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - boldfont.SetWeight(wxFONTWEIGHT_BOLD); + tc_profile_name->Enable(false); + tc_profile_name->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent &evt) { + if (tc_profile_name->GetValue().IsEmpty()) { + if (profile_name_prev.IsEmpty()) { tc_profile_name->SetValue(default_profile_name); } + else { tc_profile_name->SetValue(profile_name_prev); } + } else { + profile_name_prev = tc_profile_name->GetValue(); + } + evt.Skip(); + }); - AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors; - wxArrayString choices_vendors; + cb_custom->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { + tc_profile_name->Enable(custom_wanted()); + wizard_p()->on_custom_setup(custom_wanted()); + }); - 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, 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); } + append(cb_custom); + append(label); + append(tc_profile_name); } -void PageVendors::on_page_set() +PageUpdate::PageUpdate(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("Automatic updates")), _(L("Updates"))) + , version_check(true) + , preset_update(true) { - on_variant_checked(); + const AppConfig *app_config = GUI::get_app_config(); + 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."))); + + 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(); }); +} + +PageVendors::PageVendors(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("Other Vendors")), _(L("Other Vendors"))) +{ + 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 standard 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); + + on_window_geometry(this, [this]() { + // Clamp the Wizard size based on screen dimensions + + 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); + + const int width_hint = p->index->GetSize().GetWidth() + p->page_fff->get_width() + 300; // XXX: magic constant, I found no better solution + if (width_hint < window_rect.width) { + window_rect.x += (window_rect.width - width_hint) / 2; + window_rect.width = width_hint; + } + + 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, false); + p->page_msla->select_all(true, false); + 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 c7fba76eec..c9ee05529f 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 2c8f23cd30..df7602adfa 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,264 @@ 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, bool alternates = false); + void select_one(size_t i, bool select); + void on_checkbox(const Checkbox *cbox, bool checked); + + int get_width() const { return width; } +private: + int width; }; 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 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, bool alternates = false); + int get_width() const; +}; + +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: + static const char* default_profile_name; + + wxCheckBox *cb_custom; + wxTextCtrl *tc_profile_name; + wxString profile_name_prev; - 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 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(); - 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; + struct Item + { + wxString label; + unsigned indent; + ConfigWizardPage *page; // nullptr page => label-only item - std::vector items; - std::vector::const_iterator item_active; + bool operator==(ConfigWizardPage *page) const { return this->page == page; } + }; - void on_paint(wxPaintEvent &evt); + int em; + int em_h; + + const wxBitmap bg; + const wxBitmap bullet_black; + const wxBitmap bullet_blue; + const wxBitmap bullet_white; + + std::vector items; + 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/Field.cpp b/src/slic3r/GUI/Field.cpp index 862f1b1075..28b0d94aad 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -145,19 +145,24 @@ void Field::get_value_by_opt_type(wxString& str) double val; // Replace the first occurence of comma in decimal number. str.Replace(",", ".", false); - if(!str.ToCDouble(&val)) - { - show_error(m_parent, _(L("Invalid numeric input."))); - set_value(double_to_string(val), true); - } - if (m_opt.min > val || val > m_opt.max) - { - show_error(m_parent, _(L("Input value is out of range"))); - if (m_opt.min > val) val = m_opt.min; - if (val > m_opt.max) val = m_opt.max; - set_value(double_to_string(val), true); - } - m_value = val; + if (str == ".") + val = 0.0; + else + { + if (!str.ToCDouble(&val)) + { + show_error(m_parent, _(L("Invalid numeric input."))); + set_value(double_to_string(val), true); + } + if (m_opt.min > val || val > m_opt.max) + { + show_error(m_parent, _(L("Input value is out of range"))); + if (m_opt.min > val) val = m_opt.min; + if (val > m_opt.max) val = m_opt.max; + set_value(double_to_string(val), true); + } + } + m_value = val; break; } case coString: case coStrings: @@ -459,7 +464,7 @@ void SpinCtrl::BUILD() { // # As a workaround, we get the new value from $event->GetString and store // # here temporarily so that we can return it from $self->get_value std::string value = e.GetString().utf8_str().data(); - if (is_matched(value, "^\\d+$")) + if (is_matched(value, "^\\-?\\d+$")) tmp_value = std::stoi(value); else tmp_value = -9999; #ifdef __WXOSX__ @@ -475,7 +480,7 @@ void SpinCtrl::BUILD() { void SpinCtrl::propagate_value() { - if (tmp_value < 0) + if (tmp_value == -9999) on_kill_focus(); else if (boost::any_cast(m_value) != tmp_value) on_change_field(); @@ -867,8 +872,8 @@ void StaticText::BUILD() if (m_opt.height >= 0) size.SetHeight(m_opt.height); if (m_opt.width >= 0) size.SetWidth(m_opt.width); - wxString legend(static_cast(m_opt.default_value)->value); - auto temp = new wxStaticText(m_parent, wxID_ANY, legend, wxDefaultPosition, size); + const wxString legend(static_cast(m_opt.default_value)->value); + auto temp = new wxStaticText(m_parent, wxID_ANY, legend, wxDefaultPosition, size, wxST_ELLIPSIZE_MIDDLE); temp->SetFont(wxGetApp().bold_font()); // // recast as a wxWindow to fit the calling convention diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6a089a4b1c..bea4bd0b47 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -141,8 +141,8 @@ bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool float inv_size_y = -1.0f / size_y; for (unsigned int i = 0; i < m_tex_coords.size(); i += 2) { - m_tex_coords[i] *= inv_size_x; - m_tex_coords[i + 1] *= inv_size_y; + m_tex_coords[i] = (m_tex_coords[i] - min_x) * inv_size_x; + m_tex_coords[i + 1] = (m_tex_coords[i + 1] - min_y) * inv_size_y; } } } @@ -595,11 +595,25 @@ void GLCanvas3D::Bed::_render_prusa(const std::string &key, float theta) const #endif // ENABLE_PRINT_BED_MODELS { std::string tex_path = resources_dir() + "/icons/bed/" + key; + + // use higher resolution images if graphic card allows + GLint max_tex_size; + ::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size); + + // temporary set to lowest resolution + max_tex_size = 2048; + + if (max_tex_size >= 8192) + tex_path += "_8192"; + else if (max_tex_size >= 4096) + tex_path += "_4096"; + #if ENABLE_PRINT_BED_MODELS std::string model_path = resources_dir() + "/models/" + key; #endif // ENABLE_PRINT_BED_MODELS #if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + // use anisotropic filter if graphic card allows GLfloat max_anisotropy = 0.0f; if (glewIsSupported("GL_EXT_texture_filter_anisotropic")) ::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy); @@ -646,11 +660,17 @@ void GLCanvas3D::Bed::_render_prusa(const std::string &key, float theta) const { filename = model_path + "_bed.stl"; if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, useVBOs)) { - Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.1 + 0.5 * m_model.get_bounding_box().size()(2)); + Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2)); if (key == "mk2") - offset.y() += 15. / 2.; + // hardcoded value to match the stl model + offset += Vec3d(0.0, 7.5, -0.03); else if (key == "mk3") - offset += Vec3d(0., (19. - 8.) / 2., 2.); + // hardcoded value to match the stl model + offset += Vec3d(0.0, 5.5, 2.43); + else if (key == "sl1") + // hardcoded value to match the stl model + offset += Vec3d(0.0, 0.0, -0.03); + m_model.center_around(offset); } @@ -5790,7 +5810,7 @@ void GLCanvas3D::set_tooltip(const std::string& tooltip) const else t->SetTip(tooltip); } - else + else if (!tooltip.empty()) // Avoid "empty" tooltips => unset of the empty tooltip leads to application crash under OSX m_canvas->SetToolTip(tooltip); } } diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 8e80d64c30..a15f500325 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 f066c82a80..4074c2afc8 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/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index fe96aaa804..38a5f33875 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -351,21 +351,10 @@ void GUI_App::persist_window_geometry(wxTopLevelWindow *window) }); window_pos_restore(window, name); -#ifdef _WIN32 - // On windows, the wxEVT_SHOW is not received if the window is created maximized - // cf. https://groups.google.com/forum/#!topic/wx-users/c7ntMt6piRI - // so we sanitize the position right away - window_pos_sanitize(window); -#else - // On other platforms on the other hand it's needed to wait before the window is actually on screen - // and some initial round of events is complete otherwise position / display index is not reported correctly. - window->Bind(wxEVT_SHOW, [=](wxShowEvent &event) { - CallAfter([=]() { - window_pos_sanitize(window); - }); - event.Skip(); + + on_window_geometry(window, [=]() { + window_pos_sanitize(window); }); -#endif } void GUI_App::load_project(wxWindow *parent, wxString& input_file) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index dc725bf88d..a00e4676bd 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -42,10 +42,9 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : // Objects(sub-objects) name def.label = L("Name"); - // def.type = coString; def.gui_type = "legend"; def.tooltip = L("Object name"); - def.full_width = true; + def.width = 200; def.default_value = new ConfigOptionString{ " " }; m_og->append_single_option_line(Option(def, "object_name")); @@ -73,14 +72,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : def.default_value = new ConfigOptionFloat(0.0); def.width = 50; - if (option_name == "Rotation") - { - def.min = -360; - def.max = 360; - } - // Add "uniform scaling" button in front of "Scale" option - else if (option_name == "Scale") { + if (option_name == "Scale") { line.near_label_widget = [this](wxWindow* parent) { auto btn = new PrusaLockButton(parent, wxID_ANY); btn->Bind(wxEVT_BUTTON, [btn, this](wxCommandEvent &event){ @@ -293,13 +286,13 @@ void ObjectManipulation::update_if_dirty() deg_rotation(i) = Geometry::rad2deg(m_new_rotation(i)); } - if (m_cache.rotation(0) != m_new_rotation(0)) + if ((m_cache.rotation(0) != m_new_rotation(0)) || (m_new_rotation(0) == 0.0)) m_og->set_value("rotation_x", double_to_string(deg_rotation(0), 2)); - if (m_cache.rotation(1) != m_new_rotation(1)) + if ((m_cache.rotation(1) != m_new_rotation(1)) || (m_new_rotation(1) == 0.0)) m_og->set_value("rotation_y", double_to_string(deg_rotation(1), 2)); - if (m_cache.rotation(2) != m_new_rotation(2)) + if ((m_cache.rotation(2) != m_new_rotation(2)) || (m_new_rotation(2) == 0.0)) m_og->set_value("rotation_z", double_to_string(deg_rotation(2), 2)); m_cache.rotation = deg_rotation; diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp index db02644590..56d6eaeb58 100644 --- a/src/slic3r/GUI/GUI_Utils.cpp +++ b/src/slic3r/GUI/GUI_Utils.cpp @@ -26,6 +26,28 @@ wxTopLevelWindow* find_toplevel_parent(wxWindow *window) return nullptr; } +void on_window_geometry(wxTopLevelWindow *tlw, std::function callback) +{ +#ifdef _WIN32 + // On windows, the wxEVT_SHOW is not received if the window is created maximized + // cf. https://groups.google.com/forum/#!topic/wx-users/c7ntMt6piRI + // OTOH the geometry is available very soon, so we can call the callback right away + callback(); +#elif defined __linux__ + tlw->Bind(wxEVT_SHOW, [=](wxShowEvent &evt) { + // On Linux, the geometry is only available after wxEVT_SHOW + CallAfter + // cf. https://groups.google.com/forum/?pli=1#!topic/wx-users/fERSXdpVwAI + tlw->CallAfter([=]() { callback(); }); + evt.Skip(); + }); +#elif defined __APPLE__ + tlw->Bind(wxEVT_SHOW, [=](wxShowEvent &evt) { + callback(); + evt.Skip(); + }); +#endif +} + CheckboxFileDialog::ExtraPanel::ExtraPanel(wxWindow *parent) : wxPanel(parent, wxID_ANY) diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index 8ca4d9383f..d84dd40f92 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -24,6 +25,8 @@ namespace GUI { wxTopLevelWindow* find_toplevel_parent(wxWindow *window); +void on_window_geometry(wxTopLevelWindow *tlw, std::function callback); + class EventGuard { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 1968dad698..f5471cdff9 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -1092,7 +1091,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) : q(q) , main_frame(main_frame) , config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({ - "bed_shape", "complete_objects", "extruder_clearance_radius", "skirts", "skirt_distance", + "bed_shape", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance", "brim_width", "variable_layer_height", "serial_port", "serial_speed", "host_type", "print_host", "printhost_apikey", "printhost_cafile", "nozzle_diameter", "single_extruder_multi_material", "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", @@ -1774,8 +1773,11 @@ void Plater::priv::arrange() // FIXME: I don't know how to obtain the minimum distance, it depends // on printer technology. I guess the following should work but it crashes. double dist = 6; //PrintConfig::min_object_distance(config); + if(printer_technology == ptFFF) { + dist = PrintConfig::min_object_distance(config); + } - auto min_obj_distance = static_cast(dist/SCALING_FACTOR); + auto min_obj_distance = coord_t(dist/SCALING_FACTOR); const auto *bed_shape_opt = config->opt("bed_shape"); diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index ee182a01ad..19626847f4 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -11,8 +11,10 @@ #include #endif /* _MSC_VER */ +#include #include #include +#include #include #include #include @@ -83,6 +85,16 @@ VendorProfile VendorProfile::from_ini(const boost::filesystem::path &path, bool return VendorProfile::from_ini(tree, path, load_all); } +static const std::unordered_map pre_family_model_map {{ + { "MK3", "MK3" }, + { "MK3MMU2", "MK3" }, + { "MK2.5", "MK2.5" }, + { "MK2.5MMU2", "MK2.5" }, + { "MK2S", "MK2" }, + { "MK2SMM", "MK2" }, + { "SL1", "SL1" }, +}}; + VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem::path &path, bool load_all) { static const std::string printer_model_key = "printer_model:"; @@ -128,11 +140,21 @@ 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"); + + const char *technology_fallback = boost::algorithm::starts_with(model.id, "SL") ? "SLA" : "FFF"; + + auto technology_field = section.second.get("technology", technology_fallback); 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", std::string()); + if (model.family.empty() && res.name == "Prusa Research") { + // If no family is specified, it can be inferred for known printers + const auto from_pre_map = pre_family_model_map.find(model.id); + if (from_pre_map != pre_family_model_map.end()) { model.family = from_pre_map->second; } + } #if 0 // Remove SLA printers from the initial alpha. if (model.technology == ptSLA) @@ -157,6 +179,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 (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 73a921cf74..074e665c9b 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; }