Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'xs/src/slic3r/GUI')
-rw-r--r--xs/src/slic3r/GUI/2DBed.cpp32
-rw-r--r--xs/src/slic3r/GUI/AppConfig.cpp81
-rw-r--r--xs/src/slic3r/GUI/AppConfig.hpp13
-rw-r--r--xs/src/slic3r/GUI/ConfigWizard.cpp720
-rw-r--r--xs/src/slic3r/GUI/ConfigWizard.hpp38
-rw-r--r--xs/src/slic3r/GUI/ConfigWizard_private.hpp213
-rw-r--r--xs/src/slic3r/GUI/GUI.cpp16
-rw-r--r--xs/src/slic3r/GUI/GUI.hpp6
-rw-r--r--xs/src/slic3r/GUI/Preferences.cpp1
-rw-r--r--xs/src/slic3r/GUI/Preset.cpp10
-rw-r--r--xs/src/slic3r/GUI/Preset.hpp26
-rw-r--r--xs/src/slic3r/GUI/PresetBundle.cpp43
-rw-r--r--xs/src/slic3r/GUI/PresetBundle.hpp3
13 files changed, 1167 insertions, 35 deletions
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 &section)
{ 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 &section : tree)
+ for (auto &section : 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);