diff options
Diffstat (limited to 'xs')
26 files changed, 1368 insertions, 38 deletions
diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index e01f11b26..1e777bf67 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -214,12 +214,16 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/Config/Version.hpp ${LIBDIR}/slic3r/Utils/ASCIIFolding.cpp ${LIBDIR}/slic3r/Utils/ASCIIFolding.hpp + ${LIBDIR}/slic3r/GUI/ConfigWizard.cpp + ${LIBDIR}/slic3r/GUI/ConfigWizard.hpp ${LIBDIR}/slic3r/Utils/Http.cpp ${LIBDIR}/slic3r/Utils/Http.hpp ${LIBDIR}/slic3r/Utils/OctoPrint.cpp ${LIBDIR}/slic3r/Utils/OctoPrint.hpp ${LIBDIR}/slic3r/Utils/Bonjour.cpp ${LIBDIR}/slic3r/Utils/Bonjour.hpp + ${LIBDIR}/slic3r/Utils/PresetUpdater.cpp + ${LIBDIR}/slic3r/Utils/PresetUpdater.hpp ${LIBDIR}/slic3r/Utils/Time.cpp ${LIBDIR}/slic3r/Utils/Time.hpp ) @@ -367,6 +371,7 @@ set(XS_XSP_FILES ${XSP_DIR}/SurfaceCollection.xsp ${XSP_DIR}/TriangleMesh.xsp ${XSP_DIR}/Utils_OctoPrint.xsp + ${XSP_DIR}/Utils_PresetUpdater.xsp ${XSP_DIR}/XS.xsp ) foreach (file ${XS_XSP_FILES}) diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 6eb307c5c..06db9efef 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -659,6 +659,7 @@ public: ConfigOptionPoints() : ConfigOptionVector<Pointf>() {} explicit ConfigOptionPoints(size_t n, const Pointf &value) : ConfigOptionVector<Pointf>(n, value) {} explicit ConfigOptionPoints(std::initializer_list<Pointf> il) : ConfigOptionVector<Pointf>(std::move(il)) {} + explicit ConfigOptionPoints(const std::vector<Pointf> &values) : ConfigOptionVector<Pointf>(values) {} static ConfigOptionType static_type() { return coPoints; } ConfigOptionType type() const override { return static_type(); } diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 657e5a452..5795f044b 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1620,7 +1620,7 @@ PrintConfigDef::PrintConfigDef() "temperature control commands in the output."); def->cli = "temperature=i@"; def->full_label = L("Temperature"); - def->max = 0; + def->min = 0; def->max = max_temp; def->default_value = new ConfigOptionInts { 200 }; diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index 5727d6c89..f900137d3 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -3,6 +3,8 @@ #include <locale> +#include "libslic3r.h" + namespace Slic3r { extern void set_logging_level(unsigned int level); diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 733757e25..703d5ff66 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -119,7 +119,7 @@ static std::string g_data_dir; void set_data_dir(const std::string &dir) { - g_data_dir = dir; + g_data_dir = dir + "-alpha"; // FIXME: Resolve backcompat problems } const std::string& data_dir() diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index d7c9a590a..9706ced2c 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -64,6 +64,7 @@ REGISTER_CLASS(PresetCollection, "GUI::PresetCollection"); REGISTER_CLASS(PresetBundle, "GUI::PresetBundle"); REGISTER_CLASS(PresetHints, "GUI::PresetHints"); REGISTER_CLASS(TabIface, "GUI::Tab"); +REGISTER_CLASS(PresetUpdater, "PresetUpdater"); REGISTER_CLASS(OctoPrint, "OctoPrint"); SV* ConfigBase__as_hash(ConfigBase* THIS) diff --git a/xs/src/slic3r/GUI/2DBed.cpp b/xs/src/slic3r/GUI/2DBed.cpp index c5d68400d..6d788cf34 100644 --- a/xs/src/slic3r/GUI/2DBed.cpp +++ b/xs/src/slic3r/GUI/2DBed.cpp @@ -1,4 +1,4 @@ -#include "2DBed.hpp"; +#include "2DBed.hpp" #include <wx/dcbuffer.h> #include "BoundingBox.hpp" @@ -66,7 +66,7 @@ void Bed_2D::repaint() shift.y - (cbb.max.y - GetSize().GetHeight())); // draw bed fill - dc.SetBrush(*new wxBrush(*new wxColour(255, 255, 255), wxSOLID)); + dc.SetBrush(wxBrush(wxColour(255, 255, 255), wxSOLID)); wxPointList pt_list; for (auto pt: m_bed_shape) { @@ -87,7 +87,7 @@ void Bed_2D::repaint() } polylines = intersection_pl(polylines, bed_polygon); - dc.SetPen(*new wxPen(*new wxColour(230, 230, 230), 1, wxSOLID)); + dc.SetPen(wxPen(wxColour(230, 230, 230), 1, wxSOLID)); for (auto pl : polylines) { for (size_t i = 0; i < pl.points.size()-1; i++){ @@ -98,8 +98,8 @@ void Bed_2D::repaint() } // draw bed contour - dc.SetPen(*new wxPen(*new wxColour(0, 0, 0), 1, wxSOLID)); - dc.SetBrush(*new wxBrush(*new wxColour(0, 0, 0), wxTRANSPARENT)); + dc.SetPen(wxPen(wxColour(0, 0, 0), 1, wxSOLID)); + dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxTRANSPARENT)); dc.DrawPolygon(&pt_list, 0, 0); auto origin_px = to_pixels(Pointf(0, 0)); @@ -108,7 +108,7 @@ void Bed_2D::repaint() auto axes_len = 50; auto arrow_len = 6; auto arrow_angle = Geometry::deg2rad(45.0); - dc.SetPen(*new wxPen(*new wxColour(255, 0, 0), 2, wxSOLID)); // red + dc.SetPen(wxPen(wxColour(255, 0, 0), 2, wxSOLID)); // red auto x_end = Pointf(origin_px.x + axes_len, origin_px.y); dc.DrawLine(wxPoint(origin_px.x, origin_px.y), wxPoint(x_end.x, x_end.y)); for (auto angle : { -arrow_angle, arrow_angle }){ @@ -118,7 +118,7 @@ void Bed_2D::repaint() dc.DrawLine(wxPoint(x_end.x, x_end.y), wxPoint(end.x, end.y)); } - dc.SetPen(*new wxPen(*new wxColour(0, 255, 0), 2, wxSOLID)); // green + dc.SetPen(wxPen(wxColour(0, 255, 0), 2, wxSOLID)); // green auto y_end = Pointf(origin_px.x, origin_px.y - axes_len); dc.DrawLine(wxPoint(origin_px.x, origin_px.y), wxPoint(y_end.x, y_end.y)); for (auto angle : { -arrow_angle, arrow_angle }) { @@ -129,19 +129,23 @@ void Bed_2D::repaint() } // draw origin - dc.SetPen(*new wxPen(*new wxColour(0, 0, 0), 1, wxSOLID)); - dc.SetBrush(*new wxBrush(*new wxColour(0, 0, 0), wxSOLID)); + dc.SetPen(wxPen(wxColour(0, 0, 0), 1, wxSOLID)); + dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxSOLID)); dc.DrawCircle(origin_px.x, origin_px.y, 3); - dc.SetTextForeground(*new wxColour(0, 0, 0)); - dc.SetFont(*new wxFont(10, wxDEFAULT, wxNORMAL, wxNORMAL)); - dc.DrawText("(0,0)", origin_px.x + 1, origin_px.y + 2); + static const auto origin_label = wxString("(0,0)"); + dc.SetTextForeground(wxColour(0, 0, 0)); + dc.SetFont(wxFont(10, wxDEFAULT, wxNORMAL, wxNORMAL)); + auto extent = dc.GetTextExtent(origin_label); + const auto origin_label_x = origin_px.x <= cw / 2 ? origin_px.x + 1 : origin_px.x - 1 - extent.GetWidth(); + const auto origin_label_y = origin_px.y <= ch / 2 ? origin_px.y + 1 : origin_px.y - 1 - extent.GetHeight(); + dc.DrawText(origin_label, origin_label_x, origin_label_y); // draw current position if (m_pos!= Pointf(0, 0)) { auto pos_px = to_pixels(m_pos); - dc.SetPen(*new wxPen(*new wxColour(200, 0, 0), 2, wxSOLID)); - dc.SetBrush(*new wxBrush(*new wxColour(200, 0, 0), wxTRANSPARENT)); + dc.SetPen(wxPen(wxColour(200, 0, 0), 2, wxSOLID)); + dc.SetBrush(wxBrush(wxColour(200, 0, 0), wxTRANSPARENT)); dc.DrawCircle(pos_px.x, pos_px.y, 5); dc.DrawLine(pos_px.x - 15, pos_px.y, pos_px.x + 15, pos_px.y); diff --git a/xs/src/slic3r/GUI/AppConfig.cpp b/xs/src/slic3r/GUI/AppConfig.cpp index e32b645b4..9e5ce5f1b 100644 --- a/xs/src/slic3r/GUI/AppConfig.cpp +++ b/xs/src/slic3r/GUI/AppConfig.cpp @@ -1,5 +1,3 @@ -#include <GL/glew.h> - #include "../../libslic3r/libslic3r.h" #include "../../libslic3r/Utils.hpp" #include "AppConfig.hpp" @@ -9,15 +7,20 @@ #include <string.h> #include <utility> #include <assert.h> +#include <vector> #include <boost/filesystem.hpp> #include <boost/nowide/cenv.hpp> #include <boost/nowide/fstream.hpp> #include <boost/property_tree/ini_parser.hpp> #include <boost/property_tree/ptree.hpp> +#include <boost/algorithm/string/predicate.hpp> namespace Slic3r { +static const std::string VENDOR_PREFIX = "vendor:"; +static const std::string MODEL_PREFIX = "model:"; + void AppConfig::reset() { m_storage.clear(); @@ -42,9 +45,11 @@ void AppConfig::set_defaults() set("no_defaults", "1"); if (get("show_incompatible_presets").empty()) set("show_incompatible_presets", "0"); - // Version check is enabled by default in the config, but it is not implemented yet. + // Version check is enabled by default in the config, but it is not implemented yet. // XXX if (get("version_check").empty()) set("version_check", "1"); + if (get("preset_update").empty()) + set("preset_update", "1"); // Use OpenGL 1.1 even if OpenGL 2.0 is available. This is mainly to support some buggy Intel HD Graphics drivers. // https://github.com/prusa3d/Slic3r/issues/233 if (get("use_legacy_opengl").empty()) @@ -67,6 +72,19 @@ void AppConfig::load() if (! data.empty()) // If there is a non-empty data, then it must be a top-level (without a section) config entry. m_storage[""][section.first] = data; + } else if (boost::starts_with(section.first, VENDOR_PREFIX)) { + // This is a vendor section listing enabled model / variants + const auto vendor_name = section.first.substr(VENDOR_PREFIX.size()); + auto &vendor = m_vendors[vendor_name]; + for (const auto &kvp : section.second) { + if (! boost::starts_with(kvp.first, MODEL_PREFIX)) { continue; } + const auto model_name = kvp.first.substr(MODEL_PREFIX.size()); + std::vector<std::string> variants; + if (! unescape_strings_cstyle(kvp.second.data(), variants)) { continue; } + for (const auto &variant : variants) { + vendor[model_name].insert(variant); + } + } } else { // This must be a section name. Read the entries of a section. std::map<std::string, std::string> &storage = m_storage[section.first]; @@ -96,10 +114,57 @@ void AppConfig::save() for (const std::pair<std::string, std::string> &kvp : category.second) c << kvp.first << " = " << kvp.second << std::endl; } + // Write vendor sections + for (const auto &vendor : m_vendors) { + size_t size_sum = 0; + for (const auto &model : vendor.second) { size_sum += model.second.size(); } + if (size_sum == 0) { continue; } + + c << std::endl << "[" << VENDOR_PREFIX << vendor.first << "]" << std::endl; + + for (const auto &model : vendor.second) { + if (model.second.size() == 0) { continue; } + const std::vector<std::string> variants(model.second.begin(), model.second.end()); + const auto escaped = escape_strings_cstyle(variants); + c << MODEL_PREFIX << model.first << " = " << escaped << std::endl; + } + } c.close(); m_dirty = false; } +bool AppConfig::get_variant(const std::string &vendor, const std::string &model, const std::string &variant) const +{ + const auto it_v = m_vendors.find(vendor); + if (it_v == m_vendors.end()) { return false; } + const auto it_m = it_v->second.find(model); + return it_m == it_v->second.end() ? false : it_m->second.find(variant) != it_m->second.end(); +} + +void AppConfig::set_variant(const std::string &vendor, const std::string &model, const std::string &variant, bool enable) +{ + if (enable) { + if (get_variant(vendor, model, variant)) { return; } + m_vendors[vendor][model].insert(variant); + } else { + auto it_v = m_vendors.find(vendor); + if (it_v == m_vendors.end()) { return; } + auto it_m = it_v->second.find(model); + if (it_m == it_v->second.end()) { return; } + auto it_var = it_m->second.find(variant); + if (it_var == it_m->second.end()) { return; } + it_m->second.erase(it_var); + } + // If we got here, there was an update + m_dirty = true; +} + +void AppConfig::set_vendors(const AppConfig &from) +{ + m_vendors = from.m_vendors; + m_dirty = true; +} + std::string AppConfig::get_last_dir() const { const auto it = m_storage.find("recent"); @@ -156,6 +221,16 @@ void AppConfig::reset_selections() } } +bool AppConfig::version_check_enabled() const +{ + return get("version_check") == "1"; +} + +bool AppConfig::slic3r_update_avail() const +{ + return version_check_enabled() && get("version_online") != SLIC3R_VERSION; +} + std::string AppConfig::config_path() { return (boost::filesystem::path(Slic3r::data_dir()) / "slic3r.ini").make_preferred().string(); diff --git a/xs/src/slic3r/GUI/AppConfig.hpp b/xs/src/slic3r/GUI/AppConfig.hpp index 9b1d5a712..7aac95fd6 100644 --- a/xs/src/slic3r/GUI/AppConfig.hpp +++ b/xs/src/slic3r/GUI/AppConfig.hpp @@ -1,9 +1,12 @@ #ifndef slic3r_AppConfig_hpp_ #define slic3r_AppConfig_hpp_ +#include <set> #include <map> #include <string> +#include "libslic3r/Config.hpp" + namespace Slic3r { class AppConfig @@ -65,6 +68,10 @@ public: void clear_section(const std::string §ion) { m_storage[section].clear(); } + bool get_variant(const std::string &vendor, const std::string &model, const std::string &variant) const; + void set_variant(const std::string &vendor, const std::string &model, const std::string &variant, bool enable); + void set_vendors(const AppConfig &from); + // return recent/skein_directory or recent/config_directory or empty string. std::string get_last_dir() const; void update_config_dir(const std::string &dir); @@ -78,6 +85,10 @@ public: // the first non-default preset when called. void reset_selections(); + // Whether the Slic3r version available online differs from this one + bool version_check_enabled() const; + bool slic3r_update_avail() const; + // Get the default config path from Slic3r::data_dir(). static std::string config_path(); @@ -87,6 +98,8 @@ public: private: // Map of section, name -> value std::map<std::string, std::map<std::string, std::string>> m_storage; + // Map of enabled vendors / models / variants + std::map<std::string, std::map<std::string, std::set<std::string>>> m_vendors; // Has any value been modified since the config.ini has been last saved or loaded? bool m_dirty; }; diff --git a/xs/src/slic3r/GUI/ConfigWizard.cpp b/xs/src/slic3r/GUI/ConfigWizard.cpp new file mode 100644 index 000000000..f13448c37 --- /dev/null +++ b/xs/src/slic3r/GUI/ConfigWizard.cpp @@ -0,0 +1,720 @@ +#include "ConfigWizard_private.hpp" + +#include <algorithm> +#include <utility> +#include <boost/filesystem.hpp> + +#include <wx/settings.h> +#include <wx/stattext.h> +#include <wx/textctrl.h> +#include <wx/dcclient.h> +#include <wx/statbmp.h> +#include <wx/checkbox.h> +#include <wx/statline.h> + +#include "libslic3r/Utils.hpp" +#include "PresetBundle.hpp" +#include "GUI.hpp" + +namespace fs = boost::filesystem; + +namespace Slic3r { +namespace GUI { + + +// Printer model picker GUI control + +struct PrinterPickerEvent : public wxEvent +{ + 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) + {} + + 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), + variants_checked(0) +{ + const auto vendor_id = vendor.id; + const auto &models = vendor.models; + + auto *printer_grid = new wxFlexGridSizer(models.size(), 0, 20); + printer_grid->SetFlexibleDirection(wxVERTICAL); + SetSizer(printer_grid); + + auto namefont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + namefont.SetWeight(wxFONTWEIGHT_BOLD); + + for (auto model = models.cbegin(); model != models.cend(); ++model) { + auto *panel = new wxPanel(this); + auto *sizer = new wxBoxSizer(wxVERTICAL); + panel->SetSizer(sizer); + + auto *title = new wxStaticText(panel, wxID_ANY, model->name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + title->SetFont(namefont); + sizer->Add(title, 0, wxBOTTOM, 3); + + auto bitmap_file = wxString::Format("printers/%s_%s.png", vendor.id, model->id); + wxBitmap bitmap(GUI::from_u8(Slic3r::var(bitmap_file.ToStdString())), wxBITMAP_TYPE_PNG); + auto *bitmap_widget = new wxStaticBitmap(panel, wxID_ANY, bitmap); + sizer->Add(bitmap_widget, 0, wxBOTTOM, 3); + + sizer->AddSpacer(20); + + const auto model_id = model->id; + + for (const auto &variant : model->variants) { + const auto variant_name = variant.name; + auto *cbox = new wxCheckBox(panel, wxID_ANY, wxString::Format("%s %s %s", variant.name, _(L("mm")), _(L("nozzle")))); + bool enabled = appconfig_vendors.get_variant("PrusaResearch", model_id, variant_name); + variants_checked += enabled; + cbox->SetValue(enabled); + sizer->Add(cbox, 0, wxBOTTOM, 3); + cbox->Bind(wxEVT_CHECKBOX, [=](wxCommandEvent &event) { + this->variants_checked += event.IsChecked() ? 1 : -1; + PrinterPickerEvent evt(EVT_PRINTER_PICK, this->GetId(), std::move(vendor_id), std::move(model_id), std::move(variant_name), event.IsChecked()); + this->AddPendingEvent(evt); + }); + } + + printer_grid->Add(panel); + } + +} + + +// Wizard page base + +ConfigWizardPage::ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname) : + wxPanel(parent), + parent(parent), + shortname(std::move(shortname)), + p_prev(nullptr), + p_next(nullptr) +{ + 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); + + content = new wxBoxSizer(wxVERTICAL); + sizer->Add(content, 1); + + SetSizer(sizer); + + this->Hide(); + + 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(CONTENT_WIDTH); + widget->SetMinSize(wxSize(CONTENT_WIDTH, -1)); + append(widget); +} + +void ConfigWizardPage::append_spacer(int 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) : + ConfigWizardPage(parent, _(L("Welcome to the Slic3r Configuration assistant")), _(L("Welcome"))), + printer_picker(nullptr), + others_buttons(new wxPanel(parent)) +{ + append_text(_(L("Hello, welcome to Slic3r Prusa Edition! TODO: This text."))); + + const PresetBundle &bundle = wizard_p()->bundle_vendors; + const auto &vendors = bundle.vendors; + const auto vendor_prusa = std::find(vendors.cbegin(), vendors.cend(), VendorProfile("PrusaResearch")); + + if (vendor_prusa != vendors.cend()) { + const auto &models = vendor_prusa->models; + + AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors; + + printer_picker = new PrinterPicker(this, *vendor_prusa, appconfig_vendors); + 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); +} + +void PageWelcome::on_page_set() +{ + chain(wizard_p()->page_update); + on_variant_checked(); +} + +void PageWelcome::on_variant_checked() +{ + enable_next(printer_picker != nullptr ? printer_picker->variants_checked > 0 : false); +} + +PageUpdate::PageUpdate(ConfigWizard *parent) : + ConfigWizardPage(parent, _(L("Automatic updates")), _(L("Updates"))), + version_check(true), + preset_update(true) +{ + const AppConfig *app_config = GUI::get_app_config(); + + append_text(_(L("TODO: text"))); + auto *box_slic3r = new wxCheckBox(this, wxID_ANY, _(L("Check for Slic3r updates"))); + box_slic3r->SetValue(app_config->get("version_check") == "1"); + append(box_slic3r); + + append_text(_(L("TODO: text"))); + 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); + + 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:"))); + + const PresetBundle &bundle = wizard_p()->bundle_vendors; + 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 : bundle.vendors) { + 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); } +} + +void PageVendors::on_page_set() +{ + on_variant_checked(); +} + +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(); + } +} + +void PageVendors::on_variant_checked() +{ + size_t variants_checked = 0; + for (const PrinterPicker *picker : pickers) { variants_checked += picker->variants_checked; } + enable_next(variants_checked > 0); +} + +PageFirmware::PageFirmware(ConfigWizard *parent) : + ConfigWizardPage(parent, _(L("Firmware Type")), _(L("Firmware"))), + gcode_opt(print_config_def.options["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); + } + + 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); +} + +void PageFirmware::apply_custom_config(DynamicPrintConfig &config) +{ + ConfigOptionEnum<GCodeFlavor> opt; + + auto sel = gcode_picker->GetSelection(); + if (sel != wxNOT_FOUND && opt.deserialize(gcode_picker->GetString(sel).ToStdString())) { + 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)) +{ + append_text(_(L("Set the shape of your printer's bed."))); + + shape_panel->build_panel(wizard_p()->custom_config->option<ConfigOptionPoints>("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); +} + +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)) +{ + spin_nozzle->SetDigits(2); + spin_nozzle->SetIncrement(0.1); + const auto &def_nozzle = print_config_def.options["nozzle_diameter"]; + auto *default_nozzle = dynamic_cast<const ConfigOptionFloats*>(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.options["filament_diameter"]; + auto *default_filam = dynamic_cast<const ConfigOptionFloats*>(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."))); + + 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_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); +} + +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); +} + +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)) +{ + spin_extr->SetIncrement(5.0); + const auto &def_extr = print_config_def.options["temperature"]; + spin_extr->SetRange(def_extr.min, def_extr.max); + auto *default_extr = dynamic_cast<const ConfigOptionInts*>(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.options["bed_temperature"]; + spin_bed->SetRange(def_bed.min, def_bed.max); + auto *default_bed = dynamic_cast<const ConfigOptionInts*>(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."))); + + 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_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); +} + +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); +} + + +// 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) +{ + SetMinSize(bg.GetSize()); + + wxClientDC dc(this); + text_height = dc.GetCharHeight(); + + // 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); +} + +void ConfigWizardIndex::load_items(ConfigWizardPage *firstpage) +{ + items.clear(); + item_active = items.cend(); + + for (auto *page = firstpage; page != nullptr; page = page->page_next()) { + items.emplace_back(page->shortname); + } + + Refresh(); +} + +void ConfigWizardIndex::set_active(ConfigWizardPage *page) +{ + item_active = std::find(items.cbegin(), items.cend(), page->shortname); + Refresh(); +} + +void ConfigWizardIndex::on_paint(wxPaintEvent & evt) +{ + enum { + MARGIN = 10, + SPACING = 5, + }; + + const auto size = GetClientSize(); + if (size.GetHeight() == 0 || size.GetWidth() == 0) { return; } + + 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 < 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 (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; + } +} + + + +// priv + +void ConfigWizard::priv::load_vendors() +{ + const auto vendor_dir = fs::path(Slic3r::data_dir()) / "vendor"; + for (fs::directory_iterator it(vendor_dir); it != fs::directory_iterator(); ++it) { + if (it->path().extension() == ".ini") { + bundle_vendors.load_configbundle(it->path().string(), PresetBundle::LOAD_CFGBUNDLE_VENDOR_ONLY); + } + } + + appconfig_vendors.set_vendors(*GUI::get_app_config()); +} + +void ConfigWizard::priv::index_refresh() +{ + index->load_items(page_welcome); +} + +void ConfigWizard::priv::add_page(ConfigWizardPage *page) +{ + topsizer->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(); +} + +void ConfigWizard::priv::enable_next(bool enable) +{ + btn_next->Enable(enable); + btn_finish->Enable(enable); +} + +void ConfigWizard::priv::on_other_vendors() +{ + 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); +} + +void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle) +{ + const bool is_custom_setup = page_welcome->page_next() == page_firmware; + + if (! is_custom_setup) { + 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(); // XXX: only on "fresh start"? + 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); + } +} + +// Public + +ConfigWizard::ConfigWizard(wxWindow *parent) : + wxDialog(parent, wxID_ANY, _(L("Configuration Assistant")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), + p(new priv(this)) +{ + 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); + + auto *vsizer = new wxBoxSizer(wxVERTICAL); + p->topsizer = new wxBoxSizer(wxHORIZONTAL); + auto *hline = new wxStaticLine(this); + p->btnsizer = new wxBoxSizer(wxHORIZONTAL); + + p->topsizer->Add(p->index, 0, wxEXPAND); + p->topsizer->AddSpacer(INDEX_MARGIN); + + p->btn_prev = new wxButton(this, wxID_BACKWARD); + p->btn_next = new wxButton(this, wxID_FORWARD); + 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->add_page(p->page_welcome = new PageWelcome(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->index_refresh(); + + p->page_welcome->chain(p->page_update); + p->page_firmware + ->chain(p->page_bed) + ->chain(p->page_diams) + ->chain(p->page_temps); + + vsizer->Add(p->topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN); + vsizer->Add(hline, 0, wxEXPAND); + vsizer->Add(p->btnsizer, 0, wxEXPAND | wxALL, DIALOG_MARGIN); + + p->set_page(p->page_welcome); + SetSizerAndFit(vsizer); + SetMinSize(GetSize()); + + 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); }); +} + +ConfigWizard::~ConfigWizard() {} + +bool ConfigWizard::run(wxWindow *parent, PresetBundle *preset_bundle) +{ + const auto profiles_dir = fs::path(resources_dir()) / "profiles"; + for (fs::directory_iterator it(profiles_dir); it != fs::directory_iterator(); ++it) { + if (it->path().extension() == ".ini") { + PresetBundle::install_vendor_configbundle(it->path()); + } + } + + ConfigWizard wizard(parent); + if (wizard.ShowModal() == wxID_OK) { + wizard.p->apply_config(GUI::get_app_config(), preset_bundle); + return true; + } else { + return false; + } +} + + +} +} diff --git a/xs/src/slic3r/GUI/ConfigWizard.hpp b/xs/src/slic3r/GUI/ConfigWizard.hpp new file mode 100644 index 000000000..40ecf09a1 --- /dev/null +++ b/xs/src/slic3r/GUI/ConfigWizard.hpp @@ -0,0 +1,38 @@ +#ifndef slic3r_ConfigWizard_hpp_ +#define slic3r_ConfigWizard_hpp_ + +#include <memory> + +#include <wx/dialog.h> + +namespace Slic3r { + +class PresetBundle; + +namespace GUI { + + +class ConfigWizard: public wxDialog +{ +public: + ConfigWizard(wxWindow *parent); + ConfigWizard(ConfigWizard &&) = delete; + ConfigWizard(const ConfigWizard &) = delete; + ConfigWizard &operator=(ConfigWizard &&) = delete; + ConfigWizard &operator=(const ConfigWizard &) = delete; + ~ConfigWizard(); + + static bool run(wxWindow *parent, PresetBundle *preset_bundle); +private: + struct priv; + std::unique_ptr<priv> p; + + friend class ConfigWizardPage; +}; + + + +} +} + +#endif diff --git a/xs/src/slic3r/GUI/ConfigWizard_private.hpp b/xs/src/slic3r/GUI/ConfigWizard_private.hpp new file mode 100644 index 000000000..652328aaa --- /dev/null +++ b/xs/src/slic3r/GUI/ConfigWizard_private.hpp @@ -0,0 +1,213 @@ +#ifndef slic3r_ConfigWizard_private_hpp_ +#define slic3r_ConfigWizard_private_hpp_ + +#include "ConfigWizard.hpp" + +#include <vector> + +#include <wx/sizer.h> +#include <wx/panel.h> +#include <wx/button.h> +#include <wx/choice.h> +#include <wx/spinctrl.h> + +#include "libslic3r/PrintConfig.hpp" +#include "AppConfig.hpp" +#include "PresetBundle.hpp" +#include "BedShapeDialog.hpp" + + +namespace Slic3r { +namespace GUI { + + +enum { + CONTENT_WIDTH = 500, + + DIALOG_MARGIN = 15, + INDEX_MARGIN = 40, + BTN_SPACING = 10, + INDENT_SPACING = 30, + VERTICAL_SPACING = 10, +}; + +struct PrinterPicker: wxPanel +{ + unsigned variants_checked; + + PrinterPicker(wxWindow *parent, const VendorProfile &vendor, const AppConfig &appconfig_vendors); +}; + +struct ConfigWizardPage: wxPanel +{ + ConfigWizard *parent; + const wxString shortname; + wxBoxSizer *content; + + ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname); + + virtual ~ConfigWizardPage(); + + ConfigWizardPage* page_prev() const { return p_prev; } + ConfigWizardPage* page_next() const { return p_next; } + ConfigWizardPage* chain(ConfigWizardPage *page); + + template<class T> + void append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10) + { + content->Add(thing, proportion, flag, border); + } + + 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; +}; + +struct PageWelcome: ConfigWizardPage +{ + PrinterPicker *printer_picker; + wxPanel *others_buttons; + + PageWelcome(ConfigWizard *parent); + + virtual wxPanel* extra_buttons() { return others_buttons; } + virtual void on_page_set(); + + void on_variant_checked(); +}; + +struct PageUpdate: ConfigWizardPage +{ + bool version_check; + bool preset_update; + + PageUpdate(ConfigWizard *parent); +}; + +struct PageVendors: ConfigWizardPage +{ + std::vector<PrinterPicker*> pickers; + + PageVendors(ConfigWizard *parent); + + virtual void on_page_set(); + + void on_vendor_pick(size_t i); + void on_variant_checked(); +}; + +struct PageFirmware: ConfigWizardPage +{ + const ConfigOptionDef &gcode_opt; + wxChoice *gcode_picker; + + PageFirmware(ConfigWizard *parent); + virtual void apply_custom_config(DynamicPrintConfig &config); +}; + +struct PageBedShape: ConfigWizardPage +{ + BedShapePanel *shape_panel; + + PageBedShape(ConfigWizard *parent); + virtual void apply_custom_config(DynamicPrintConfig &config); +}; + +struct PageDiameters: ConfigWizardPage +{ + wxSpinCtrlDouble *spin_nozzle; + wxSpinCtrlDouble *spin_filam; + + PageDiameters(ConfigWizard *parent); + virtual void apply_custom_config(DynamicPrintConfig &config); +}; + +struct PageTemperatures: ConfigWizardPage +{ + wxSpinCtrlDouble *spin_extr; + wxSpinCtrlDouble *spin_bed; + + PageTemperatures(ConfigWizard *parent); + virtual void apply_custom_config(DynamicPrintConfig &config); +}; + + +class ConfigWizardIndex: public wxPanel +{ +public: + ConfigWizardIndex(wxWindow *parent); + + 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; + + std::vector<wxString> items; + std::vector<wxString>::const_iterator item_active; + + void on_paint(wxPaintEvent &evt); +}; + +struct ConfigWizard::priv +{ + ConfigWizard *q; + AppConfig appconfig_vendors; + PresetBundle bundle_vendors; + std::unique_ptr<DynamicPrintConfig> custom_config; + + wxBoxSizer *topsizer = 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; + + priv(ConfigWizard *q) : q(q) {} + + 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); + + void on_other_vendors(); + void on_custom_setup(); + + void apply_config(AppConfig *app_config, PresetBundle *preset_bundle); +}; + + + +} +} + +#endif diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 0a163bf28..dcb21f644 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -46,6 +46,7 @@ #include "AppConfig.hpp" #include "ConfigSnapshotDialog.hpp" #include "Utils.hpp" +#include "ConfigWizard.hpp" #include "Preferences.hpp" #include "PresetBundle.hpp" @@ -397,6 +398,21 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l menu->Append(local_menu, _(L("&Configuration"))); } +bool open_config_wizard(PresetBundle *preset_bundle) +{ + if (g_wxMainFrame == nullptr) { + throw std::runtime_error("Main frame not set"); + } + + return ConfigWizard::run(g_wxMainFrame, preset_bundle); +} + +void open_preferences_dialog(int event_preferences) +{ + auto dlg = new PreferencesDialog(g_wxMainFrame, event_preferences); + dlg->ShowModal(); +} + void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed) { add_created_tab(new TabPrint (g_wxTabPanel, no_controller)); diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 0cbdf8729..938eed498 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -84,6 +84,12 @@ wxColour* get_sys_label_clr(); void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_language_change); +// Opens the first-time configuration wizard, returns true if wizard is finished & accepted. +bool open_config_wizard(PresetBundle *preset_bundle); + +// Create "Preferences" dialog after selecting menu "Preferences" in Perl part +void open_preferences_dialog(int event_preferences); + // Create a new preset tab (print, filament and printer), void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed); TabIface* get_preset_tab_iface(char *name); diff --git a/xs/src/slic3r/GUI/Preferences.cpp b/xs/src/slic3r/GUI/Preferences.cpp index 0009b17ec..6731cd394 100644 --- a/xs/src/slic3r/GUI/Preferences.cpp +++ b/xs/src/slic3r/GUI/Preferences.cpp @@ -14,6 +14,7 @@ void PreferencesDialog::build() m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0"; }; + // TODO // $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( // opt_id = > 'version_check', // type = > 'bool', diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index c437f8b41..66836074e 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -2,6 +2,7 @@ #include <cassert> #include "Preset.hpp" +#include "AppConfig.hpp" #include <fstream> #include <boost/filesystem.hpp> @@ -175,6 +176,15 @@ bool Preset::update_compatible_with_printer(const Preset &active_printer, const return this->is_compatible = is_compatible_with_printer(active_printer, extra_config); } +void Preset::set_visible_from_appconfig(const AppConfig &app_config) +{ + if (vendor == nullptr) { return; } + const std::string &model = config.opt_string("printer_model"); + const std::string &variant = config.opt_string("printer_variant"); + if (model.empty() || variant.empty()) { return; } + is_visible = app_config.get_variant(vendor->id, model, variant); +} + const std::vector<std::string>& Preset::print_options() { static std::vector<std::string> s_opts { diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp index 3634c5dd9..4f734b85e 100644 --- a/xs/src/slic3r/GUI/Preset.hpp +++ b/xs/src/slic3r/GUI/Preset.hpp @@ -13,6 +13,8 @@ class wxItemContainer; namespace Slic3r { +class AppConfig; + enum ConfigFileType { CONFIG_FILE_TYPE_UNKNOWN, @@ -35,14 +37,12 @@ public: PrinterVariant() {} PrinterVariant(const std::string &name) : name(name) {} std::string name; - bool enabled = true; }; struct PrinterModel { PrinterModel() {} - PrinterModel(const std::string &name) : name(name) {} + std::string id; std::string name; - bool enabled = true; std::vector<PrinterVariant> variants; PrinterVariant* variant(const std::string &name) { for (auto &v : this->variants) @@ -51,11 +51,10 @@ public: return nullptr; } const PrinterVariant* variant(const std::string &name) const { return const_cast<PrinterModel*>(this)->variant(name); } - - bool operator< (const PrinterModel &rhs) const { return this->name < rhs.name; } - bool operator==(const PrinterModel &rhs) const { return this->name == rhs.name; } }; - std::set<PrinterModel> models; + std::vector<PrinterModel> models; + + VendorProfile(std::string id) : id(std::move(id)) {} size_t num_variants() const { size_t n = 0; for (auto &model : models) n += model.variants.size(); return n; } @@ -86,7 +85,8 @@ public: bool is_external = false; // System preset is read-only. bool is_system = false; - // Preset is visible, if it is compatible with the active Printer. + // Preset is visible, if it is associated with a printer model / variant that is enabled in the AppConfig + // or if it has no printer model / variant association. // Also the "default" preset is only visible, if it is the only preset in the list. bool is_visible = true; // Has this preset been modified? @@ -132,6 +132,9 @@ public: // Mark this preset as compatible if it is compatible with active_printer. bool update_compatible_with_printer(const Preset &active_printer, const DynamicPrintConfig *extra_config); + // Set is_visible according to application config + void set_visible_from_appconfig(const AppConfig &app_config); + // Resize the extruder specific fields, initialize them with the content of the 1st extruder. void set_num_extruders(unsigned int n) { set_num_extruders(this->config, n); } @@ -163,6 +166,13 @@ public: PresetCollection(Preset::Type type, const std::vector<std::string> &keys); ~PresetCollection(); + typedef std::deque<Preset>::iterator Iterator; + typedef std::deque<Preset>::const_iterator ConstIterator; + Iterator begin() { return m_presets.begin() + 1; } + ConstIterator begin() const { return m_presets.begin() + 1; } + Iterator end() { return m_presets.end(); } + ConstIterator end() const { return m_presets.end(); } + void reset(bool delete_files); Preset::Type type() const { return m_type; } diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index 7131bf771..bd9e35ca8 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -4,6 +4,7 @@ #include "PresetBundle.hpp" #include "BitmapCache.hpp" +#include <algorithm> #include <fstream> #include <boost/filesystem.hpp> #include <boost/algorithm/clamp.hpp> @@ -104,6 +105,7 @@ void PresetBundle::setup_directories() std::initializer_list<boost::filesystem::path> paths = { data_dir, data_dir / "vendor", + data_dir / "cache", // TODO: rename as vendor-cache? (Check usage elsewhere!) #ifdef SLIC3R_PROFILE_USE_PRESETS_SUBDIR // Store the print/filament/printer presets into a "presets" directory. data_dir / "presets", @@ -198,7 +200,9 @@ static inline std::string remove_ini_suffix(const std::string &name) // If the "vendor" section is missing, enable all models and variants of the particular vendor. void PresetBundle::load_installed_printers(const AppConfig &config) { - // m_storage + for (auto &preset : printers) { + preset.set_visible_from_appconfig(config); + } } // Load selections (current print, current filaments, current printer) from config.ini @@ -218,6 +222,10 @@ void PresetBundle::load_selections(const AppConfig &config) break; this->set_filament_preset(i, remove_ini_suffix(config.get("presets", name))); } + + // Update visibility of presets based on application vendor / model / variant configuration. + this->load_installed_printers(config); + // Update visibility of presets based on their compatibility with the active printer. // Always try to select a compatible print and filament preset to the current printer preset, // as the application may have been closed with an active "external" preset, which does not @@ -667,7 +675,7 @@ static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree) static void load_vendor_profile(const boost::property_tree::ptree &tree, VendorProfile &vendor_profile) { const std::string printer_model_key = "printer_model:"; - for (auto §ion : tree) + for (auto §ion : tree) { if (section.first == "vendor") { // Load the names of the active presets. for (auto &kvp : section.second) { @@ -682,7 +690,8 @@ static void load_vendor_profile(const boost::property_tree::ptree &tree, VendorP } } else if (boost::starts_with(section.first, printer_model_key)) { VendorProfile::PrinterModel model; - model.name = section.first.substr(printer_model_key.size()); + model.id = section.first.substr(printer_model_key.size()); + model.name = section.second.get<std::string>("name", model.id); section.second.get<std::string>("variants", ""); std::vector<std::string> variants; if (Slic3r::unescape_strings_cstyle(section.second.get<std::string>("variants", ""), variants)) { @@ -693,9 +702,10 @@ static void load_vendor_profile(const boost::property_tree::ptree &tree, VendorP } else { // Log error? } - if (! model.name.empty() && ! model.variants.empty()) - vendor_profile.models.insert(model); + if (! model.id.empty() && ! model.variants.empty()) + vendor_profile.models.push_back(std::move(model)); } + } } // Load a config bundle file, into presets and store the loaded presets into separate files @@ -703,6 +713,11 @@ static void load_vendor_profile(const boost::property_tree::ptree &tree, VendorP void PresetBundle::install_vendor_configbundle(const std::string &src_path0) { boost::filesystem::path src_path(src_path0); + install_vendor_configbundle(src_path); +} + +void PresetBundle::install_vendor_configbundle(const boost::filesystem::path &src_path) +{ boost::filesystem::copy_file(src_path, (boost::filesystem::path(data_dir()) / "vendor" / src_path.filename()).make_preferred(), boost::filesystem::copy_option::overwrite_if_exists); } @@ -719,12 +734,11 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla pt::ptree tree; boost::nowide::ifstream ifs(path); pt::read_ini(ifs, tree); - // Flatten the config bundle by applying the inheritance rules. Internal profiles (with names starting with '*') are removed. - flatten_configbundle_hierarchy(tree); const VendorProfile *vendor_profile = nullptr; - if (flags & LOAD_CFGBNDLE_SYSTEM) { - VendorProfile vp; + if (flags & (LOAD_CFGBNDLE_SYSTEM | LOAD_CFGBUNDLE_VENDOR_ONLY)) { + boost::filesystem::path fspath(path); + VendorProfile vp(fspath.stem().string()); load_vendor_profile(tree, vp); if (vp.name.empty()) throw std::runtime_error(std::string("Vendor Config Bundle is not valid: Missing vendor name key.")); @@ -732,6 +746,13 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla return 0; vendor_profile = &(*this->vendors.insert(vp).first); } + + if (flags & LOAD_CFGBUNDLE_VENDOR_ONLY) { + return 0; + } + + // 1.5) Flatten the config bundle by applying the inheritance rules. Internal profiles (with names starting with '*') are removed. + flatten_configbundle_hierarchy(tree); // 2) Parse the property_tree, extract the active preset names and the profiles, save them into local config files. std::vector<std::string> loaded_prints; @@ -803,7 +824,9 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla section.first << "\" defines no printer variant, it will be ignored."; continue; } - auto it_model = vendor_profile->models.find(VendorProfile::PrinterModel(printer_model)); + auto it_model = std::find_if(vendor_profile->models.cbegin(), vendor_profile->models.cend(), + [&](const VendorProfile::PrinterModel &m) { return m.id == printer_model; } + ); if (it_model == vendor_profile->models.end()) { BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" << section.first << "\" defines invalid printer model \"" << printer_model << "\", it will be ignored."; diff --git a/xs/src/slic3r/GUI/PresetBundle.hpp b/xs/src/slic3r/GUI/PresetBundle.hpp index f481ba374..4189e6c46 100644 --- a/xs/src/slic3r/GUI/PresetBundle.hpp +++ b/xs/src/slic3r/GUI/PresetBundle.hpp @@ -5,6 +5,7 @@ #include "Preset.hpp" #include <set> +#include <boost/filesystem/path.hpp> namespace Slic3r { @@ -81,12 +82,14 @@ public: LOAD_CFGBNDLE_RESET_USER_PROFILE = 2, // Load a system config bundle. LOAD_CFGBNDLE_SYSTEM = 4, + LOAD_CFGBUNDLE_VENDOR_ONLY = 8, }; // Load the config bundle, store it to the user profile directory by default. size_t load_configbundle(const std::string &path, unsigned int flags = LOAD_CFGBNDLE_SAVE); // Install the Vendor specific config bundle into user's directory. void install_vendor_configbundle(const std::string &src_path); + static void install_vendor_configbundle(const boost::filesystem::path &src_path); // Export a config bundle file containing all the presets and the names of the active presets. void export_configbundle(const std::string &path); // , const DynamicPrintConfig &settings); diff --git a/xs/src/slic3r/Utils/PresetUpdater.cpp b/xs/src/slic3r/Utils/PresetUpdater.cpp new file mode 100644 index 000000000..040d326b5 --- /dev/null +++ b/xs/src/slic3r/Utils/PresetUpdater.cpp @@ -0,0 +1,137 @@ +#include "PresetUpdater.hpp" + +#include <iostream> // XXX +#include <thread> +#include <boost/algorithm/string.hpp> +#include <boost/filesystem/path.hpp> +#include <boost/filesystem/fstream.hpp> + +#include <wx/app.h> +#include <wx/event.h> + +#include "libslic3r/Utils.hpp" +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/PresetBundle.hpp" +#include "slic3r/Utils/Http.hpp" +#include "slic3r/Utils/Semver.hpp" + +namespace fs = boost::filesystem; + + +namespace Slic3r { + + +// TODO: proper URL +static const std::string SLIC3R_VERSION_URL = "https://gist.githubusercontent.com/vojtechkral/4d8fd4a3b8699a01ec892c264178461c/raw/e9187c3e15ceaf1a90f29b7c43cf3ccc746140f0/slic3rPE.version"; +enum { + SLIC3R_VERSION_BODY_MAX = 256, +}; + +struct PresetUpdater::priv +{ + int version_online_event; + bool version_check; + bool preset_update; + + fs::path cache_path; + bool cancel; + std::thread thread; + + priv(int event); + + void download(const std::set<VendorProfile> vendors) const; +}; + +PresetUpdater::priv::priv(int event) : + version_online_event(event), + version_check(false), + preset_update(false), + cache_path(fs::path(Slic3r::data_dir()) / "cache"), + cancel(false) +{} + +void PresetUpdater::priv::download(const std::set<VendorProfile> vendors) const +{ + std::cerr << "PresetUpdater::priv::download()" << std::endl; + + if (!version_check) { return; } + + // Download current Slic3r version + Http::get(SLIC3R_VERSION_URL) + .size_limit(SLIC3R_VERSION_BODY_MAX) + .on_progress([this](Http::Progress, bool &cancel) { + cancel = this->cancel; + }) + .on_complete([&](std::string body, unsigned http_status) { + boost::trim(body); + std::cerr << "Got version: " << http_status << ", body: \"" << body << '"' << std::endl; + wxCommandEvent* evt = new wxCommandEvent(version_online_event); + evt->SetString(body); + GUI::get_app()->QueueEvent(evt); + }) + .perform_sync(); + + if (!preset_update) { return; } + + // Donwload vendor preset bundles + std::cerr << "Bundle vendors: " << vendors.size() << std::endl; + for (const auto &vendor : vendors) { + std::cerr << "vendor: " << vendor.name << std::endl; + std::cerr << " URL: " << vendor.config_update_url << std::endl; + + if (cancel) { return; } + + // TODO: Proper caching + + auto target_path = cache_path / vendor.id; + target_path += ".ini"; + std::cerr << "target_path: " << target_path << std::endl; + + Http::get(vendor.config_update_url) + .on_progress([this](Http::Progress, bool &cancel) { + cancel = this->cancel; + }) + .on_complete([&](std::string body, unsigned http_status) { + std::cerr << "Got ini: " << http_status << ", body: " << body.size() << std::endl; + fs::fstream file(target_path, std::ios::out | std::ios::binary | std::ios::trunc); + file.write(body.c_str(), body.size()); + }) + .perform_sync(); + } +} + +PresetUpdater::PresetUpdater(int version_online_event, AppConfig *app_config) : + p(new priv(version_online_event)) +{ + p->preset_update = app_config->get("preset_update") == "1"; + // preset_update implies version_check: + p->version_check = p->preset_update || app_config->get("version_check") == "1"; +} + + +// Public + +PresetUpdater::~PresetUpdater() +{ + if (p && p->thread.joinable()) { + p->cancel = true; + p->thread.join(); + } +} + +void PresetUpdater::download(PresetBundle *preset_bundle) +{ + std::cerr << "PresetUpdater::download()" << std::endl; + + // Copy the whole vendors data for use in the background thread + // Unfortunatelly as of C++11, it needs to be copied again + // into the closure (but perhaps the compiler can elide this). + std::set<VendorProfile> vendors = preset_bundle->vendors; + + p->thread = std::move(std::thread([this, vendors]() { + this->p->download(std::move(vendors)); + })); +} + + +} diff --git a/xs/src/slic3r/Utils/PresetUpdater.hpp b/xs/src/slic3r/Utils/PresetUpdater.hpp new file mode 100644 index 000000000..aafe9569b --- /dev/null +++ b/xs/src/slic3r/Utils/PresetUpdater.hpp @@ -0,0 +1,30 @@ +#ifndef slic3r_PresetUpdate_hpp_ +#define slic3r_PresetUpdate_hpp_ + +#include <memory> + +namespace Slic3r { + + +class AppConfig; +class PresetBundle; + +class PresetUpdater +{ +public: + PresetUpdater(int version_online_event, AppConfig *app_config); + PresetUpdater(PresetUpdater &&) = delete; + PresetUpdater(const PresetUpdater &) = delete; + PresetUpdater &operator=(PresetUpdater &&) = delete; + PresetUpdater &operator=(const PresetUpdater &) = delete; + ~PresetUpdater(); + + void download(PresetBundle *preset_bundle); +private: + struct priv; + std::unique_ptr<priv> p; +}; + + +} +#endif diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index c8d76adba..8dba13caf 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -57,6 +57,12 @@ int combochecklist_get_flags(SV *ui) void set_app_config(AppConfig *app_config) %code%{ Slic3r::GUI::set_app_config(app_config); %}; +bool open_config_wizard(PresetBundle *preset_bundle) + %code%{ RETVAL=Slic3r::GUI::open_config_wizard(preset_bundle); %}; + +void open_preferences_dialog(int preferences_event) + %code%{ Slic3r::GUI::open_preferences_dialog(preferences_event); %}; + void set_preset_bundle(PresetBundle *preset_bundle) %code%{ Slic3r::GUI::set_preset_bundle(preset_bundle); %}; diff --git a/xs/xsp/GUI_AppConfig.xsp b/xs/xsp/GUI_AppConfig.xsp index 08a88883d..de0e5a22b 100644 --- a/xs/xsp/GUI_AppConfig.xsp +++ b/xs/xsp/GUI_AppConfig.xsp @@ -43,4 +43,5 @@ void update_last_output_dir(char *dir); void reset_selections(); + bool slic3r_update_avail() const; }; diff --git a/xs/xsp/GUI_Preset.xsp b/xs/xsp/GUI_Preset.xsp index 84efdde53..1187a1cf5 100644 --- a/xs/xsp/GUI_Preset.xsp +++ b/xs/xsp/GUI_Preset.xsp @@ -147,7 +147,7 @@ PresetCollection::arrayref() void install_vendor_configbundle(const char *path) %code%{ try { - THIS->install_vendor_configbundle(path); + THIS->install_vendor_configbundle(std::string(path)); } catch (std::exception& e) { croak("Installing a vendor config bundle %s failed:\n%s\n", path, e.what()); } diff --git a/xs/xsp/Utils_PresetUpdater.xsp b/xs/xsp/Utils_PresetUpdater.xsp new file mode 100644 index 000000000..666379f02 --- /dev/null +++ b/xs/xsp/Utils_PresetUpdater.xsp @@ -0,0 +1,11 @@ +%module{Slic3r::XS}; + +%{ +#include <xsinit.h> +#include "slic3r/Utils/PresetUpdater.hpp" +%} + +%name{Slic3r::PresetUpdater} class PresetUpdater { + PresetUpdater(int version_online_event, AppConfig *app_config); + void download(PresetBundle* preset_bundle); +}; diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 87a8d8d86..c1ca58827 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -236,6 +236,10 @@ Ref<PresetHints> O_OBJECT_SLIC3R_T TabIface* O_OBJECT_SLIC3R Ref<TabIface> O_OBJECT_SLIC3R_T +PresetUpdater* O_OBJECT_SLIC3R +Ref<PresetUpdater> O_OBJECT_SLIC3R_T +Clone<PresetUpdater> O_OBJECT_SLIC3R_T + OctoPrint* O_OBJECT_SLIC3R Ref<OctoPrint> O_OBJECT_SLIC3R_T Clone<OctoPrint> O_OBJECT_SLIC3R_T |