mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-23 16:51:21 -06:00
Merge remote-tracking branch 'origin/updating' into cpp_ui_optimization
This commit is contained in:
commit
3b0eb6b786
62 changed files with 4998 additions and 967 deletions
|
|
@ -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);
|
||||
|
|
|
|||
125
xs/src/slic3r/GUI/AboutDialog.cpp
Normal file
125
xs/src/slic3r/GUI/AboutDialog.cpp
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
#include "AboutDialog.hpp"
|
||||
|
||||
#include "../../libslic3r/Utils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
AboutDialogLogo::AboutDialogLogo(wxWindow* parent)
|
||||
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
|
||||
{
|
||||
this->SetBackgroundColour(*wxWHITE);
|
||||
this->logo = wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG);
|
||||
this->SetMinSize(this->logo.GetSize());
|
||||
|
||||
this->Bind(wxEVT_PAINT, &AboutDialogLogo::onRepaint, this);
|
||||
}
|
||||
|
||||
void AboutDialogLogo::onRepaint(wxEvent &event)
|
||||
{
|
||||
wxPaintDC dc(this);
|
||||
dc.SetBackgroundMode(wxTRANSPARENT);
|
||||
|
||||
wxSize size = this->GetSize();
|
||||
int logo_w = this->logo.GetWidth();
|
||||
int logo_h = this->logo.GetHeight();
|
||||
dc.DrawBitmap(this->logo, (size.GetWidth() - logo_w)/2, (size.GetHeight() - logo_h)/2, true);
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
AboutDialog::AboutDialog()
|
||||
: wxDialog(NULL, wxID_ANY, _(L("About Slic3r")), wxDefaultPosition, wxSize(600, 340), wxCAPTION)
|
||||
{
|
||||
this->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
wxBoxSizer* hsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
this->SetSizer(hsizer);
|
||||
|
||||
// logo
|
||||
AboutDialogLogo* logo = new AboutDialogLogo(this);
|
||||
hsizer->Add(logo, 0, wxEXPAND | wxLEFT | wxRIGHT, 30);
|
||||
|
||||
wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
hsizer->Add(vsizer, 1, wxEXPAND, 0);
|
||||
|
||||
// title
|
||||
{
|
||||
wxStaticText* title = new wxStaticText(this, wxID_ANY, "Slic3r Prusa Edition", wxDefaultPosition, wxDefaultSize);
|
||||
wxFont title_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
title_font.SetWeight(wxFONTWEIGHT_BOLD);
|
||||
title_font.SetFamily(wxFONTFAMILY_ROMAN);
|
||||
title_font.SetPointSize(24);
|
||||
title->SetFont(title_font);
|
||||
vsizer->Add(title, 0, wxALIGN_LEFT | wxTOP, 30);
|
||||
}
|
||||
|
||||
// version
|
||||
{
|
||||
auto version_string = _(L("Version ")) + std::string(SLIC3R_VERSION);
|
||||
wxStaticText* version = new wxStaticText(this, wxID_ANY, version_string.c_str(), wxDefaultPosition, wxDefaultSize);
|
||||
wxFont version_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
#ifdef __WXMSW__
|
||||
version_font.SetPointSize(9);
|
||||
#else
|
||||
version_font.SetPointSize(11);
|
||||
#endif
|
||||
version->SetFont(version_font);
|
||||
vsizer->Add(version, 0, wxALIGN_LEFT | wxBOTTOM, 10);
|
||||
}
|
||||
|
||||
// text
|
||||
wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_NEVER);
|
||||
{
|
||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
#ifdef __WXMSW__
|
||||
int size[] = {8,8,8,8,8,8,8};
|
||||
#else
|
||||
int size[] = {11,11,11,11,11,11,11};
|
||||
#endif
|
||||
html->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
|
||||
html->SetBorders(2);
|
||||
const char* text =
|
||||
"<html>"
|
||||
"<body bgcolor=\"#ffffff\" link=\"#808080\">"
|
||||
"<font color=\"#808080\">"
|
||||
"Copyright © 2016-2018 Prusa Research. <br />"
|
||||
"Copyright © 2011-2017 Alessandro Ranellucci. <br />"
|
||||
"<a href=\"http://slic3r.org/\">Slic3r</a> is licensed under the "
|
||||
"<a href=\"http://www.gnu.org/licenses/agpl-3.0.html\">GNU Affero General Public License, version 3</a>."
|
||||
"<br /><br /><br />"
|
||||
"Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others. "
|
||||
"Manual by Gary Hodgson. Inspired by the RepRap community. <br />"
|
||||
"Slic3r logo designed by Corey Daniels, <a href=\"http://www.famfamfam.com/lab/icons/silk/\">Silk Icon Set</a> designed by Mark James. "
|
||||
"</font>"
|
||||
"</body>"
|
||||
"</html>";
|
||||
html->SetPage(text);
|
||||
vsizer->Add(html, 1, wxEXPAND | wxALIGN_LEFT | wxRIGHT | wxBOTTOM, 20);
|
||||
html->Bind(wxEVT_HTML_LINK_CLICKED, &AboutDialog::onLinkClicked, this);
|
||||
}
|
||||
|
||||
wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCLOSE);
|
||||
this->SetEscapeId(wxID_CLOSE);
|
||||
this->Bind(wxEVT_BUTTON, &AboutDialog::onCloseDialog, this, wxID_CLOSE);
|
||||
vsizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3);
|
||||
|
||||
this->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this);
|
||||
logo->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this);
|
||||
html->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this);
|
||||
}
|
||||
|
||||
void AboutDialog::onLinkClicked(wxHtmlLinkEvent &event)
|
||||
{
|
||||
wxLaunchDefaultBrowser(event.GetLinkInfo().GetHref());
|
||||
event.Skip(false);
|
||||
}
|
||||
|
||||
void AboutDialog::onCloseDialog(wxEvent &)
|
||||
{
|
||||
this->EndModal(wxID_CLOSE);
|
||||
this->Close();
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
36
xs/src/slic3r/GUI/AboutDialog.hpp
Normal file
36
xs/src/slic3r/GUI/AboutDialog.hpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef slic3r_GUI_AboutDialog_hpp_
|
||||
#define slic3r_GUI_AboutDialog_hpp_
|
||||
|
||||
#include "GUI.hpp"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/intl.h>
|
||||
#include <wx/html/htmlwin.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class AboutDialogLogo : public wxPanel
|
||||
{
|
||||
public:
|
||||
AboutDialogLogo(wxWindow* parent);
|
||||
|
||||
private:
|
||||
wxBitmap logo;
|
||||
void onRepaint(wxEvent &event);
|
||||
};
|
||||
|
||||
class AboutDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
AboutDialog();
|
||||
|
||||
private:
|
||||
void onLinkClicked(wxHtmlLinkEvent &event);
|
||||
void onCloseDialog(wxEvent &);
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
#include <GL/glew.h>
|
||||
|
||||
#include "../../libslic3r/libslic3r.h"
|
||||
#include "../../libslic3r/Utils.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
|
|
@ -9,15 +7,21 @@
|
|||
#include <string.h>
|
||||
#include <utility>
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
#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 +46,15 @@ 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.
|
||||
|
||||
if (get("version_check").empty())
|
||||
set("version_check", "1");
|
||||
// TODO: proper URL
|
||||
if (get("version_check_url").empty())
|
||||
set("version_check_url", "https://gist.githubusercontent.com/vojtechkral/4d8fd4a3b8699a01ec892c264178461c/raw/e9187c3e15ceaf1a90f29b7c43cf3ccc746140f0/slic3rPE.version");
|
||||
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 +77,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];
|
||||
|
|
@ -75,6 +98,10 @@ void AppConfig::load()
|
|||
}
|
||||
}
|
||||
|
||||
// Figure out if datadir has legacy presets
|
||||
auto ini_ver = Semver::parse(get("version"));
|
||||
m_legacy_datadir = ini_ver ? *ini_ver < Semver(1, 40, 0) : true;
|
||||
|
||||
// Override missing or keys with their defaults.
|
||||
this->set_defaults();
|
||||
m_dirty = false;
|
||||
|
|
@ -96,10 +123,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 +230,33 @@ void AppConfig::reset_selections()
|
|||
}
|
||||
}
|
||||
|
||||
bool AppConfig::version_check_enabled() const
|
||||
{
|
||||
return get("version_check") == "1";
|
||||
}
|
||||
|
||||
bool AppConfig::slic3r_update_avail() const
|
||||
{
|
||||
// FIXME: Update with Semver
|
||||
// TODO: probably need to move semver to libslic3r
|
||||
return version_check_enabled() && get("version_online") != SLIC3R_VERSION;
|
||||
}
|
||||
|
||||
Semver AppConfig::get_slic3r_version() const
|
||||
{
|
||||
auto res = Semver::parse(get("version"));
|
||||
if (! res) {
|
||||
throw std::runtime_error(std::string("Could not parse Slic3r version string in application config."));
|
||||
} else {
|
||||
return *res;
|
||||
}
|
||||
}
|
||||
|
||||
void AppConfig::set_slic3r_version(const Semver &version)
|
||||
{
|
||||
set("version", version.to_string());
|
||||
}
|
||||
|
||||
std::string AppConfig::config_path()
|
||||
{
|
||||
return (boost::filesystem::path(Slic3r::data_dir()) / "slic3r.ini").make_preferred().string();
|
||||
|
|
|
|||
|
|
@ -1,15 +1,19 @@
|
|||
#ifndef slic3r_AppConfig_hpp_
|
||||
#define slic3r_AppConfig_hpp_
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "slic3r/Utils/Semver.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class AppConfig
|
||||
{
|
||||
public:
|
||||
AppConfig() : m_dirty(false) { this->reset(); }
|
||||
AppConfig() : m_dirty(false), m_legacy_datadir(false) { this->reset(); }
|
||||
|
||||
// Clear and reset to defaults.
|
||||
void reset();
|
||||
|
|
@ -65,6 +69,14 @@ public:
|
|||
void clear_section(const std::string §ion)
|
||||
{ m_storage[section].clear(); }
|
||||
|
||||
typedef std::map<std::string, std::map<std::string, std::set<std::string>>> VendorMap;
|
||||
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);
|
||||
void set_vendors(const VendorMap &vendors) { m_vendors = vendors; m_dirty = true; }
|
||||
void set_vendors(VendorMap &&vendors) { m_vendors = std::move(vendors); m_dirty = true; }
|
||||
const VendorMap& vendors() const { return m_vendors; }
|
||||
|
||||
// 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,8 +90,16 @@ 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;
|
||||
Semver get_slic3r_version() const;
|
||||
void set_slic3r_version(const Semver &version);
|
||||
|
||||
// Get the default config path from Slic3r::data_dir().
|
||||
static std::string config_path();
|
||||
|
||||
bool legacy_datadir() const { return m_legacy_datadir; }
|
||||
|
||||
// Does the config file exist?
|
||||
static bool exists();
|
||||
|
|
@ -87,8 +107,12 @@ 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
|
||||
VendorMap m_vendors;
|
||||
// Has any value been modified since the config.ini has been last saved or loaded?
|
||||
bool m_dirty;
|
||||
// Whether the existing version is before system profiles & configuration updating
|
||||
bool m_legacy_datadir;
|
||||
};
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
|
|
|||
139
xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp
Normal file
139
xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
#include "ConfigSnapshotDialog.hpp"
|
||||
|
||||
#include "../Config/Snapshot.hpp"
|
||||
#include "../Utils/Time.hpp"
|
||||
|
||||
#include "../../libslic3r/Utils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
static std::string format_reason(const Config::Snapshot::Reason reason)
|
||||
{
|
||||
switch (reason) {
|
||||
case Config::Snapshot::SNAPSHOT_UPGRADE:
|
||||
return std::string(_(L("Upgrade")));
|
||||
case Config::Snapshot::SNAPSHOT_DOWNGRADE:
|
||||
return std::string(_(L("Downgrade")));
|
||||
case Config::Snapshot::SNAPSHOT_USER:
|
||||
return std::string(_(L("User")));
|
||||
case Config::Snapshot::SNAPSHOT_UNKNOWN:
|
||||
default:
|
||||
return std::string(_(L("Unknown")));
|
||||
}
|
||||
}
|
||||
|
||||
static std::string generate_html_row(const Config::Snapshot &snapshot, bool row_even)
|
||||
{
|
||||
// Start by declaring a row with an alternating background color.
|
||||
std::string text = "<tr bgcolor=\"";
|
||||
text += row_even ? "#FFFFFF" : "#C0C0C0";
|
||||
text += "\">";
|
||||
text += "<td>";
|
||||
// Format the row header.
|
||||
text += std::string("<font size=\"5\"><b>") + Utils::format_local_date_time(snapshot.time_captured) + ": " + format_reason(snapshot.reason);
|
||||
if (! snapshot.comment.empty())
|
||||
text += " (" + snapshot.comment + ")";
|
||||
text += "</b></font><br>";
|
||||
// End of row header.
|
||||
// text += _(L("ID:")) + " " + snapshot.id + "<br>";
|
||||
// text += _(L("time captured:")) + " " + Utils::format_local_date_time(snapshot.time_captured) + "<br>";
|
||||
text += _(L("slic3r version:")) + " " + snapshot.slic3r_version_captured.to_string() + "<br>";
|
||||
// text += "reason: " + snapshot.reason + "<br>";
|
||||
text += "print: " + snapshot.print + "<br>";
|
||||
text += "filaments: " + snapshot.filaments.front() + "<br>";
|
||||
text += "printer: " + snapshot.printer + "<br>";
|
||||
|
||||
for (const Config::Snapshot::VendorConfig &vc : snapshot.vendor_configs) {
|
||||
text += "vendor: " + vc.name + ", ver: " + vc.version.to_string() + ", min slic3r ver: " + vc.min_slic3r_version.to_string();
|
||||
if (vc.max_slic3r_version != Semver::inf())
|
||||
text += ", max slic3r ver: " + vc.max_slic3r_version.to_string();
|
||||
text += "<br>";
|
||||
for (const std::pair<std::string, std::set<std::string>> &model : vc.models_variants_installed) {
|
||||
text += "model: " + model.first + ", variants: ";
|
||||
for (const std::string &variant : model.second) {
|
||||
if (&variant != &*model.second.begin())
|
||||
text += ", ";
|
||||
text += variant;
|
||||
}
|
||||
text += "<br>";
|
||||
}
|
||||
}
|
||||
|
||||
text += "<p align=\"right\"><a href=\"" + snapshot.id + "\">Activate</a></p>";
|
||||
text += "</td>";
|
||||
text += "</tr>";
|
||||
return text;
|
||||
}
|
||||
|
||||
static std::string generate_html_page(const Config::SnapshotDB &snapshot_db)
|
||||
{
|
||||
std::string text =
|
||||
"<html>"
|
||||
"<body bgcolor=\"#ffffff\" cellspacing=\"2\" cellpadding=\"0\" border=\"0\" link=\"#800000\">"
|
||||
"<font color=\"#000000\">";
|
||||
text += "<table style=\"width:100%\">";
|
||||
for (size_t i_row = 0; i_row < snapshot_db.snapshots().size(); ++ i_row) {
|
||||
const Config::Snapshot &snapshot = snapshot_db.snapshots()[snapshot_db.snapshots().size() - i_row - 1];
|
||||
text += generate_html_row(snapshot, i_row & 1);
|
||||
}
|
||||
text +=
|
||||
"</table>"
|
||||
"</font>"
|
||||
"</body>"
|
||||
"</html>";
|
||||
return text;
|
||||
}
|
||||
|
||||
ConfigSnapshotDialog::ConfigSnapshotDialog(const Config::SnapshotDB &snapshot_db)
|
||||
: wxDialog(NULL, wxID_ANY, _(L("Configuration Snapshots")), wxDefaultPosition, wxSize(600, 500), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX)
|
||||
{
|
||||
this->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
this->SetSizer(vsizer);
|
||||
|
||||
// text
|
||||
wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO);
|
||||
{
|
||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
#ifdef __WXMSW__
|
||||
int size[] = {8,8,8,8,11,11,11};
|
||||
#else
|
||||
int size[] = {11,11,11,11,14,14,14};
|
||||
#endif
|
||||
html->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
|
||||
html->SetBorders(2);
|
||||
std::string text = generate_html_page(snapshot_db);
|
||||
html->SetPage(text.c_str());
|
||||
vsizer->Add(html, 1, wxEXPAND | wxALIGN_LEFT | wxRIGHT | wxBOTTOM, 0);
|
||||
html->Bind(wxEVT_HTML_LINK_CLICKED, &ConfigSnapshotDialog::onLinkClicked, this);
|
||||
}
|
||||
|
||||
wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCLOSE);
|
||||
this->SetEscapeId(wxID_CLOSE);
|
||||
this->Bind(wxEVT_BUTTON, &ConfigSnapshotDialog::onCloseDialog, this, wxID_CLOSE);
|
||||
vsizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3);
|
||||
|
||||
/*
|
||||
this->Bind(wxEVT_LEFT_DOWN, &ConfigSnapshotDialog::onCloseDialog, this);
|
||||
logo->Bind(wxEVT_LEFT_DOWN, &ConfigSnapshotDialog::onCloseDialog, this);
|
||||
html->Bind(wxEVT_LEFT_DOWN, &ConfigSnapshotDialog::onCloseDialog, this);
|
||||
*/
|
||||
}
|
||||
|
||||
void ConfigSnapshotDialog::onLinkClicked(wxHtmlLinkEvent &event)
|
||||
{
|
||||
m_snapshot_to_activate = event.GetLinkInfo().GetHref();
|
||||
this->EndModal(wxID_CLOSE);
|
||||
this->Close();
|
||||
}
|
||||
|
||||
void ConfigSnapshotDialog::onCloseDialog(wxEvent &)
|
||||
{
|
||||
this->EndModal(wxID_CLOSE);
|
||||
this->Close();
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
34
xs/src/slic3r/GUI/ConfigSnapshotDialog.hpp
Normal file
34
xs/src/slic3r/GUI/ConfigSnapshotDialog.hpp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef slic3r_GUI_ConfigSnapshotDialog_hpp_
|
||||
#define slic3r_GUI_ConfigSnapshotDialog_hpp_
|
||||
|
||||
#include "GUI.hpp"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/intl.h>
|
||||
#include <wx/html/htmlwin.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
namespace Config {
|
||||
class SnapshotDB;
|
||||
}
|
||||
|
||||
class ConfigSnapshotDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
ConfigSnapshotDialog(const Config::SnapshotDB &snapshot_db);
|
||||
|
||||
const std::string& snapshot_to_activate() const { return m_snapshot_to_activate; }
|
||||
|
||||
private:
|
||||
void onLinkClicked(wxHtmlLinkEvent &event);
|
||||
void onCloseDialog(wxEvent &);
|
||||
|
||||
std::string m_snapshot_to_activate;
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_GUI_ConfigSnapshotDialog_hpp_ */
|
||||
808
xs/src/slic3r/GUI/ConfigWizard.cpp
Normal file
808
xs/src/slic3r/GUI/ConfigWizard.cpp
Normal file
|
|
@ -0,0 +1,808 @@
|
|||
#include "ConfigWizard_private.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <unordered_map>
|
||||
|
||||
#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"
|
||||
#include "slic3r/Utils/PresetUpdater.hpp"
|
||||
|
||||
// TODO: Wizard vs Assistant
|
||||
|
||||
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),
|
||||
vendor_id(vendor.id),
|
||||
variants_checked(0)
|
||||
{
|
||||
const auto &models = vendor.models;
|
||||
|
||||
auto *sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto *printer_grid = new wxFlexGridSizer(models.size(), 0, 20);
|
||||
printer_grid->SetFlexibleDirection(wxVERTICAL);
|
||||
sizer->Add(printer_grid);
|
||||
|
||||
auto namefont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
namefont.SetWeight(wxFONTWEIGHT_BOLD);
|
||||
|
||||
for (const auto &model : models) {
|
||||
auto *panel = new wxPanel(this);
|
||||
auto *col_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
panel->SetSizer(col_sizer);
|
||||
|
||||
auto *title = new wxStaticText(panel, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||
title->SetFont(namefont);
|
||||
col_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);
|
||||
col_sizer->Add(bitmap_widget, 0, wxBOTTOM, 3);
|
||||
|
||||
col_sizer->AddSpacer(20);
|
||||
|
||||
const auto model_id = model.id;
|
||||
|
||||
for (const auto &variant : model.variants) {
|
||||
const auto label = wxString::Format("%s %s %s", variant.name, _(L("mm")), _(L("nozzle")));
|
||||
auto *cbox = new Checkbox(panel, label, model_id, variant.name);
|
||||
const size_t idx = cboxes.size();
|
||||
cboxes.push_back(cbox);
|
||||
bool enabled = appconfig_vendors.get_variant("PrusaResearch", model_id, variant.name);
|
||||
variants_checked += enabled;
|
||||
cbox->SetValue(enabled);
|
||||
col_sizer->Add(cbox, 0, wxBOTTOM, 3);
|
||||
cbox->Bind(wxEVT_CHECKBOX, [this, idx](wxCommandEvent &event) {
|
||||
if (idx >= this->cboxes.size()) { return; }
|
||||
this->on_checkbox(this->cboxes[idx], event.IsChecked());
|
||||
});
|
||||
}
|
||||
|
||||
printer_grid->Add(panel);
|
||||
}
|
||||
|
||||
auto *all_none_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto *sel_all = new wxButton(this, wxID_ANY, _(L("Select all")));
|
||||
auto *sel_none = new wxButton(this, wxID_ANY, _(L("Select none")));
|
||||
sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true); });
|
||||
sel_none->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(false); });
|
||||
all_none_sizer->AddStretchSpacer();
|
||||
all_none_sizer->Add(sel_all);
|
||||
all_none_sizer->Add(sel_none);
|
||||
sizer->AddStretchSpacer();
|
||||
sizer->Add(all_none_sizer, 0, wxEXPAND);
|
||||
|
||||
SetSizer(sizer);
|
||||
}
|
||||
|
||||
void PrinterPicker::select_all(bool select)
|
||||
{
|
||||
for (const auto &cb : cboxes) {
|
||||
if (cb->GetValue() != select) {
|
||||
cb->SetValue(select);
|
||||
on_checkbox(cb, select);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrinterPicker::on_checkbox(const Checkbox *cbox, bool checked)
|
||||
{
|
||||
variants_checked += checked ? 1 : -1;
|
||||
PrinterPickerEvent evt(EVT_PRINTER_PICK, GetId(), vendor_id, cbox->model, cbox->variant, checked);
|
||||
AddPendingEvent(evt);
|
||||
}
|
||||
|
||||
|
||||
// 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 auto &vendors = wizard_p()->vendors;
|
||||
const auto vendor_prusa = vendors.find("PrusaResearch");
|
||||
|
||||
if (vendor_prusa != vendors.cend()) {
|
||||
AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors;
|
||||
|
||||
printer_picker = new PrinterPicker(this, vendor_prusa->second, appconfig_vendors);
|
||||
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;
|
||||
// const auto &vendors = wizard_p()->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 : vendors) {
|
||||
for (const auto vendor_pair : wizard_p()->vendors) {
|
||||
const auto &vendor = vendor_pair.second;
|
||||
if (vendor.id == "PrusaResearch") { continue; }
|
||||
|
||||
auto *picker = new PrinterPicker(this, vendor, 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
|
||||
|
||||
static const std::unordered_map<std::string, std::pair<std::string, std::string>> legacy_preset_map {{
|
||||
{ "Original Prusa i3 MK2.ini", std::make_pair("MK2S", "0.4") },
|
||||
{ "Original Prusa i3 MK2 MM Single Mode.ini", std::make_pair("MK2S", "0.4") },
|
||||
{ "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") },
|
||||
{ "Original Prusa i3 MK2 MultiMaterial.ini", std::make_pair("MK2S", "0.4") },
|
||||
{ "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") },
|
||||
{ "Original Prusa i3 MK2 0.25 nozzle.ini", std::make_pair("MK2S", "0.25") },
|
||||
{ "Original Prusa i3 MK2 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") },
|
||||
{ "Original Prusa i3 MK3.ini", std::make_pair("MK3", "0.4") },
|
||||
}};
|
||||
|
||||
void ConfigWizard::priv::load_vendors()
|
||||
{
|
||||
const auto vendor_dir = fs::path(Slic3r::data_dir()) / "vendor";
|
||||
const auto rsrc_vendor_dir = fs::path(resources_dir()) / "profiles";
|
||||
|
||||
// Load vendors from the "vendors" directory in datadir
|
||||
for (fs::directory_iterator it(vendor_dir); it != fs::directory_iterator(); ++it) {
|
||||
if (it->path().extension() == ".ini") {
|
||||
auto vp = VendorProfile::from_ini(it->path());
|
||||
vendors[vp.id] = std::move(vp);
|
||||
}
|
||||
}
|
||||
|
||||
// Additionally load up vendors from the application resources directory, but only those not seen in the datadir
|
||||
for (fs::directory_iterator it(rsrc_vendor_dir); it != fs::directory_iterator(); ++it) {
|
||||
if (it->path().extension() == ".ini") {
|
||||
const auto id = it->path().stem().string();
|
||||
if (vendors.find(id) == vendors.end()) {
|
||||
auto vp = VendorProfile::from_ini(it->path());
|
||||
vendors_rsrc[vp.id] = it->path().filename().string();
|
||||
vendors[vp.id] = std::move(vp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load up the set of vendors / models / variants the user has had enabled up till now
|
||||
const AppConfig *app_config = GUI::get_app_config();
|
||||
if (! app_config->legacy_datadir()) {
|
||||
appconfig_vendors.set_vendors(*app_config);
|
||||
} else {
|
||||
// In case of legacy datadir, try to guess the preference based on the printer preset files that are present
|
||||
const auto printer_dir = fs::path(Slic3r::data_dir()) / "printer";
|
||||
for (fs::directory_iterator it(printer_dir); it != fs::directory_iterator(); ++it) {
|
||||
auto needle = legacy_preset_map.find(it->path().filename().string());
|
||||
if (needle == legacy_preset_map.end()) { continue; }
|
||||
|
||||
const auto &model = needle->second.first;
|
||||
const auto &variant = needle->second.second;
|
||||
appconfig_vendors.set_variant("PrusaResearch", model, variant, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigWizard::priv::index_refresh()
|
||||
{
|
||||
index->load_items(page_welcome);
|
||||
}
|
||||
|
||||
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, PresetUpdater *updater)
|
||||
{
|
||||
const bool is_custom_setup = page_welcome->page_next() == page_firmware;
|
||||
|
||||
if (! is_custom_setup) {
|
||||
const auto enabled_vendors = appconfig_vendors.vendors();
|
||||
|
||||
// Install bundles from resources if needed:
|
||||
std::vector<std::string> install_bundles;
|
||||
for (const auto &vendor_rsrc : vendors_rsrc) {
|
||||
const auto vendor = enabled_vendors.find(vendor_rsrc.first);
|
||||
if (vendor == enabled_vendors.end()) { continue; }
|
||||
|
||||
size_t size_sum = 0;
|
||||
for (const auto &model : vendor->second) { size_sum += model.second.size(); }
|
||||
if (size_sum == 0) { continue; }
|
||||
|
||||
// This vendor needs to be installed
|
||||
install_bundles.emplace_back(vendor_rsrc.second);
|
||||
}
|
||||
if (install_bundles.size() > 0) {
|
||||
updater->install_bundles_rsrc(app_config, std::move(install_bundles));
|
||||
}
|
||||
|
||||
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");
|
||||
if (fresh_start)
|
||||
app_config->reset_selections();
|
||||
// ^ TODO: replace with appropriate printer selection
|
||||
preset_bundle->load_presets(*app_config);
|
||||
} else {
|
||||
for (ConfigWizardPage *page = page_firmware; page != nullptr; page = page->page_next()) {
|
||||
page->apply_custom_config(*custom_config);
|
||||
}
|
||||
preset_bundle->load_config("My Settings", *custom_config);
|
||||
}
|
||||
// Update the selections from the compatibilty.
|
||||
preset_bundle->export_selections(*app_config);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
ConfigWizard::ConfigWizard(wxWindow *parent, bool fresh_start) :
|
||||
wxDialog(parent, wxID_ANY, _(L("Configuration Assistant")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
|
||||
p(new priv(this))
|
||||
{
|
||||
p->fresh_start = fresh_start;
|
||||
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() {}
|
||||
|
||||
void ConfigWizard::run(PresetBundle *preset_bundle, PresetUpdater *updater)
|
||||
{
|
||||
if (ShowModal() == wxID_OK) {
|
||||
p->apply_config(GUI::get_app_config(), preset_bundle, updater);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
39
xs/src/slic3r/GUI/ConfigWizard.hpp
Normal file
39
xs/src/slic3r/GUI/ConfigWizard.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef slic3r_ConfigWizard_hpp_
|
||||
#define slic3r_ConfigWizard_hpp_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <wx/dialog.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class PresetBundle;
|
||||
class PresetUpdater;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
|
||||
class ConfigWizard: public wxDialog
|
||||
{
|
||||
public:
|
||||
ConfigWizard(wxWindow *parent, bool fresh_start);
|
||||
ConfigWizard(ConfigWizard &&) = delete;
|
||||
ConfigWizard(const ConfigWizard &) = delete;
|
||||
ConfigWizard &operator=(ConfigWizard &&) = delete;
|
||||
ConfigWizard &operator=(const ConfigWizard &) = delete;
|
||||
~ConfigWizard();
|
||||
|
||||
void run(PresetBundle *preset_bundle, PresetUpdater *updater);
|
||||
private:
|
||||
struct priv;
|
||||
std::unique_ptr<priv> p;
|
||||
|
||||
friend class ConfigWizardPage;
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
236
xs/src/slic3r/GUI/ConfigWizard_private.hpp
Normal file
236
xs/src/slic3r/GUI/ConfigWizard_private.hpp
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
#ifndef slic3r_ConfigWizard_private_hpp_
|
||||
#define slic3r_ConfigWizard_private_hpp_
|
||||
|
||||
#include "ConfigWizard.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#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 "slic3r/Utils/PresetUpdater.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
#include "Preset.hpp"
|
||||
#include "BedShapeDialog.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
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
|
||||
{
|
||||
struct Checkbox : wxCheckBox
|
||||
{
|
||||
Checkbox(wxWindow *parent, const wxString &label, const std::string &model, const std::string &variant) :
|
||||
wxCheckBox(parent, wxID_ANY, label),
|
||||
model(model),
|
||||
variant(variant)
|
||||
{}
|
||||
|
||||
std::string model;
|
||||
std::string variant;
|
||||
};
|
||||
|
||||
const std::string vendor_id;
|
||||
std::vector<Checkbox*> cboxes;
|
||||
unsigned variants_checked;
|
||||
|
||||
PrinterPicker(wxWindow *parent, const VendorProfile &vendor, const AppConfig &appconfig_vendors);
|
||||
|
||||
void select_all(bool select);
|
||||
void on_checkbox(const Checkbox *cbox, bool checked);
|
||||
};
|
||||
|
||||
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;
|
||||
bool fresh_start;
|
||||
AppConfig appconfig_vendors; // TODO: use order-preserving container
|
||||
std::unordered_map<std::string, VendorProfile> vendors; // TODO: just set?
|
||||
std::unordered_map<std::string, std::string> vendors_rsrc;
|
||||
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, PresetUpdater *updater);
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -39,17 +39,24 @@
|
|||
#include <wx/sizer.h>
|
||||
#include <wx/combo.h>
|
||||
#include <wx/window.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/settings.h>
|
||||
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
#include "Tab.hpp"
|
||||
#include "TabIface.hpp"
|
||||
#include "AboutDialog.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
#include "ConfigSnapshotDialog.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "ConfigWizard.hpp"
|
||||
#include "Preferences.hpp"
|
||||
#include "PresetBundle.hpp"
|
||||
|
||||
#include "../Utils/PresetUpdater.hpp"
|
||||
#include "../Config/Snapshot.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
#if __APPLE__
|
||||
|
|
@ -177,6 +184,7 @@ wxFrame *g_wxMainFrame = nullptr;
|
|||
wxNotebook *g_wxTabPanel = nullptr;
|
||||
AppConfig *g_AppConfig = nullptr;
|
||||
PresetBundle *g_PresetBundle= nullptr;
|
||||
PresetUpdater *g_PresetUpdater = nullptr;
|
||||
wxColour g_color_label_modified;
|
||||
wxColour g_color_label_sys;
|
||||
|
||||
|
|
@ -226,6 +234,11 @@ void set_preset_bundle(PresetBundle *preset_bundle)
|
|||
g_PresetBundle = preset_bundle;
|
||||
}
|
||||
|
||||
void set_preset_updater(PresetUpdater *updater)
|
||||
{
|
||||
g_PresetUpdater = updater;
|
||||
}
|
||||
|
||||
std::vector<Tab *>& get_tabs_list()
|
||||
{
|
||||
return g_tabs_list;
|
||||
|
|
@ -349,26 +362,158 @@ void get_installed_languages(wxArrayString & names,
|
|||
}
|
||||
}
|
||||
|
||||
void add_debug_menu(wxMenuBar *menu, int event_language_change)
|
||||
enum ConfigMenuIDs {
|
||||
ConfigMenuWizard,
|
||||
ConfigMenuSnapshots,
|
||||
ConfigMenuTakeSnapshot,
|
||||
ConfigMenuUpdate,
|
||||
ConfigMenuPreferences,
|
||||
ConfigMenuLanguage,
|
||||
ConfigMenuCnt,
|
||||
};
|
||||
|
||||
void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_language_change)
|
||||
{
|
||||
//#if 0
|
||||
auto local_menu = new wxMenu();
|
||||
local_menu->Append(wxWindow::NewControlId(1), _(L("Change Application Language")));
|
||||
local_menu->Bind(wxEVT_MENU, [event_language_change](wxEvent&){
|
||||
wxArrayString names;
|
||||
wxArrayLong identifiers;
|
||||
get_installed_languages(names, identifiers);
|
||||
if (select_language(names, identifiers)){
|
||||
save_language();
|
||||
show_info(g_wxTabPanel, _(L("Application will be restarted")), _(L("Attention!")));
|
||||
if (event_language_change > 0) {
|
||||
wxCommandEvent event(event_language_change);
|
||||
g_wxApp->ProcessEvent(event);
|
||||
wxWindowID config_id_base = wxWindow::NewControlId((int)ConfigMenuCnt);
|
||||
|
||||
// A different naming convention is used for the Wizard on Windows vs. OSX & GTK.
|
||||
#if WIN32
|
||||
auto config_wizard_menu = _(L("Configuration Wizard"));
|
||||
auto config_wizard_tooltip = _(L("Run configuration wizard"));
|
||||
#else
|
||||
auto config_wizard_menu = _(L("Configuration Assistant"));
|
||||
auto config_wizard_tooltip = _(L("Run configuration Assistant"));
|
||||
#endif
|
||||
// Cmd+, is standard on OS X - what about other operating systems?
|
||||
local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_menu + "\u2026", config_wizard_tooltip);
|
||||
local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("Configuration Snapshots\u2026")), _(L("Inspect / activate configuration snapshots")));
|
||||
local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _(L("Take Configuration Snapshot")), _(L("Capture a configuration snapshot")));
|
||||
local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates")));
|
||||
local_menu->AppendSeparator();
|
||||
local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("Preferences\u2026\tCtrl+,")), _(L("Application preferences")));
|
||||
local_menu->AppendSeparator();
|
||||
local_menu->Append(config_id_base + ConfigMenuLanguage, _(L("Change Application Language")));
|
||||
local_menu->Bind(wxEVT_MENU, [config_id_base, event_language_change, event_preferences_changed](wxEvent &event){
|
||||
switch (event.GetId() - config_id_base) {
|
||||
case ConfigMenuWizard:
|
||||
config_wizard(0);
|
||||
break;
|
||||
case ConfigMenuTakeSnapshot:
|
||||
// Take a configuration snapshot.
|
||||
if (check_unsaved_changes()) {
|
||||
wxTextEntryDialog dlg(nullptr, _(L("Taking configuration snapshot")), _(L("Snapshot name")));
|
||||
if (dlg.ShowModal() == wxID_OK)
|
||||
Slic3r::GUI::Config::SnapshotDB::singleton().take_snapshot(
|
||||
*g_AppConfig, Slic3r::GUI::Config::Snapshot::SNAPSHOT_USER, dlg.GetValue().ToUTF8().data());
|
||||
}
|
||||
break;
|
||||
case ConfigMenuSnapshots:
|
||||
if (check_unsaved_changes()) {
|
||||
ConfigSnapshotDialog dlg(Slic3r::GUI::Config::SnapshotDB::singleton());
|
||||
dlg.ShowModal();
|
||||
if (! dlg.snapshot_to_activate().empty()) {
|
||||
Config::SnapshotDB::singleton().restore_snapshot(dlg.snapshot_to_activate(), *g_AppConfig);
|
||||
g_PresetBundle->load_presets(*g_AppConfig);
|
||||
// Load the currently selected preset into the GUI, update the preset selection box.
|
||||
for (Tab *tab : g_tabs_list)
|
||||
tab->load_current_preset();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ConfigMenuPreferences:
|
||||
{
|
||||
PreferencesDialog dlg(g_wxMainFrame, event_preferences_changed);
|
||||
dlg.ShowModal();
|
||||
break;
|
||||
}
|
||||
case ConfigMenuLanguage:
|
||||
{
|
||||
wxArrayString names;
|
||||
wxArrayLong identifiers;
|
||||
get_installed_languages(names, identifiers);
|
||||
if (select_language(names, identifiers)) {
|
||||
save_language();
|
||||
show_info(g_wxTabPanel, _(L("Application will be restarted")), _(L("Attention!")));
|
||||
if (event_language_change > 0) {
|
||||
wxCommandEvent event(event_language_change);
|
||||
g_wxApp->ProcessEvent(event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
menu->Append(local_menu, _(L("&Localization")));
|
||||
//#endif
|
||||
menu->Append(local_menu, _(L("&Configuration")));
|
||||
}
|
||||
|
||||
// This is called when closing the application, when loading a config file or when starting the config wizard
|
||||
// to notify the user whether he is aware that some preset changes will be lost.
|
||||
bool check_unsaved_changes()
|
||||
{
|
||||
std::string dirty;
|
||||
for (Tab *tab : g_tabs_list)
|
||||
if (tab->current_preset_is_dirty())
|
||||
if (dirty.empty())
|
||||
dirty = tab->name();
|
||||
else
|
||||
dirty += std::string(", ") + tab->name();
|
||||
if (dirty.empty())
|
||||
// No changes, the application may close or reload presets.
|
||||
return true;
|
||||
// Ask the user.
|
||||
auto dialog = new wxMessageDialog(g_wxMainFrame,
|
||||
_(L("You have unsaved changes ")) + dirty + _(L(". Discard changes and continue anyway?")),
|
||||
_(L("Unsaved Presets")),
|
||||
wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT);
|
||||
return dialog->ShowModal() == wxID_YES;
|
||||
}
|
||||
|
||||
void config_wizard_startup(bool app_config_exists)
|
||||
{
|
||||
if (! app_config_exists || g_PresetBundle->has_defauls_only()) {
|
||||
config_wizard(true);
|
||||
} else if (g_AppConfig->legacy_datadir()) {
|
||||
// Looks like user has legacy pre-vendorbundle data directory,
|
||||
// explain what this is and run the wizard
|
||||
|
||||
const auto msg = _(L("Configuration update"));
|
||||
const auto ext_msg = _(L(
|
||||
"Slic3r PE now uses an updated configuration structure.\n\n"
|
||||
|
||||
"So called 'System presets' have been introduced, which hold the built-in default settings for various "
|
||||
"printers. These System presets cannot be modified, instead, users now may create their"
|
||||
"own presets inheriting settings from one of the System presets.\n"
|
||||
"An inheriting preset may either inherit a particular value from its parent or override it with a customized value.\n\n"
|
||||
|
||||
// TODO: Assistant vs Wizard
|
||||
"Please proceed with the Configuration wizard that follows to set up the new presets "
|
||||
"and to choose whether to enable automatic preset updates."
|
||||
));
|
||||
wxMessageDialog dlg(NULL, msg, _(L("Configuration update")), wxOK|wxCENTRE);
|
||||
dlg.SetExtendedMessage(ext_msg);
|
||||
const auto res = dlg.ShowModal();
|
||||
|
||||
config_wizard(true);
|
||||
}
|
||||
}
|
||||
|
||||
void config_wizard(bool fresh_start) // TODO: fresh_start useful ?
|
||||
{
|
||||
if (g_wxMainFrame == nullptr)
|
||||
throw std::runtime_error("Main frame not set");
|
||||
|
||||
// Exit wizard if there are unsaved changes and the user cancels the action.
|
||||
if (! check_unsaved_changes())
|
||||
return;
|
||||
|
||||
// TODO: Offer "reset user profile" ???
|
||||
ConfigWizard wizard(g_wxMainFrame, fresh_start);
|
||||
wizard.run(g_PresetBundle, g_PresetUpdater);
|
||||
|
||||
// Load the currently selected preset into the GUI, update the preset selection box.
|
||||
for (Tab *tab : g_tabs_list)
|
||||
tab->load_current_preset();
|
||||
}
|
||||
|
||||
void open_preferences_dialog(int event_preferences)
|
||||
|
|
@ -810,6 +955,11 @@ int get_export_option(wxFileDialog* dlg)
|
|||
|
||||
}
|
||||
|
||||
void about()
|
||||
{
|
||||
AboutDialog dlg;
|
||||
dlg.ShowModal();
|
||||
dlg.Destroy();
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ namespace Slic3r {
|
|||
class PresetBundle;
|
||||
class PresetCollection;
|
||||
class AppConfig;
|
||||
class PresetUpdater;
|
||||
class DynamicPrintConfig;
|
||||
class TabIface;
|
||||
|
||||
|
|
@ -77,6 +78,7 @@ void set_main_frame(wxFrame *main_frame);
|
|||
void set_tab_panel(wxNotebook *tab_panel);
|
||||
void set_app_config(AppConfig *app_config);
|
||||
void set_preset_bundle(PresetBundle *preset_bundle);
|
||||
void set_preset_updater(PresetUpdater *updater);
|
||||
|
||||
AppConfig* get_app_config();
|
||||
wxApp* get_app();
|
||||
|
|
@ -85,10 +87,20 @@ const wxColour& get_modified_label_clr();
|
|||
const wxColour& get_sys_label_clr();
|
||||
unsigned get_colour_approx_luma(const wxColour &colour);
|
||||
|
||||
void add_debug_menu(wxMenuBar *menu, int event_language_change);
|
||||
extern void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_language_change);
|
||||
|
||||
// This is called when closing the application, when loading a config file or when starting the config wizard
|
||||
// to notify the user whether he is aware that some preset changes will be lost.
|
||||
extern bool check_unsaved_changes();
|
||||
|
||||
// Checks if configuration wizard needs to run, calls config_wizard if so
|
||||
extern void config_wizard_startup(bool app_config_exists);
|
||||
|
||||
// Opens the configuration wizard, returns true if wizard is finished & accepted.
|
||||
extern void config_wizard(bool fresh_start);
|
||||
|
||||
// Create "Preferences" dialog after selecting menu "Preferences" in Perl part
|
||||
void open_preferences_dialog(int event_preferences);
|
||||
extern 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);
|
||||
|
|
@ -138,7 +150,11 @@ wxButton* get_wiping_dialog_button();
|
|||
|
||||
void add_export_option(wxFileDialog* dlg, const std::string& format);
|
||||
int get_export_option(wxFileDialog* dlg);
|
||||
}
|
||||
}
|
||||
|
||||
// Display an About dialog
|
||||
void about();
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -2,9 +2,13 @@
|
|||
#include <cassert>
|
||||
|
||||
#include "Preset.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <boost/nowide/cenv.hpp>
|
||||
|
|
@ -23,14 +27,16 @@
|
|||
#include "../../libslic3r/Utils.hpp"
|
||||
#include "../../libslic3r/PlaceholderParser.hpp"
|
||||
|
||||
using boost::property_tree::ptree;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
ConfigFileType guess_config_file_type(const boost::property_tree::ptree &tree)
|
||||
ConfigFileType guess_config_file_type(const ptree &tree)
|
||||
{
|
||||
size_t app_config = 0;
|
||||
size_t bundle = 0;
|
||||
size_t config = 0;
|
||||
for (const boost::property_tree::ptree::value_type &v : tree) {
|
||||
for (const ptree::value_type &v : tree) {
|
||||
if (v.second.empty()) {
|
||||
if (v.first == "background_processing" ||
|
||||
v.first == "last_output_path" ||
|
||||
|
|
@ -58,6 +64,79 @@ ConfigFileType guess_config_file_type(const boost::property_tree::ptree &tree)
|
|||
(bundle > config) ? CONFIG_FILE_TYPE_CONFIG_BUNDLE : CONFIG_FILE_TYPE_CONFIG;
|
||||
}
|
||||
|
||||
|
||||
VendorProfile VendorProfile::from_ini(const boost::filesystem::path &path, bool load_all)
|
||||
{
|
||||
ptree tree;
|
||||
boost::filesystem::ifstream ifs(path);
|
||||
boost::property_tree::read_ini(ifs, tree);
|
||||
return VendorProfile::from_ini(tree, path, load_all);
|
||||
}
|
||||
|
||||
VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem::path &path, bool load_all)
|
||||
{
|
||||
static const std::string printer_model_key = "printer_model:";
|
||||
const std::string id = path.stem().string();
|
||||
|
||||
if (! boost::filesystem::exists(path)) {
|
||||
throw std::runtime_error((boost::format("Cannot load Vendor Config Bundle `%1%`: File not found: `%2%`.") % id % path).str());
|
||||
}
|
||||
|
||||
VendorProfile res(id);
|
||||
|
||||
auto get_or_throw = [&](const ptree &tree, const std::string &key) -> ptree::const_assoc_iterator
|
||||
{
|
||||
auto res = tree.find(key);
|
||||
if (res == tree.not_found()) {
|
||||
throw std::runtime_error((boost::format("Vendor Config Bundle `%1%` is not valid: Missing secion or key: `%2%`.") % id % key).str());
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
const auto &vendor_section = get_or_throw(tree, "vendor")->second;
|
||||
res.name = get_or_throw(vendor_section, "name")->second.data();
|
||||
|
||||
auto config_version_str = get_or_throw(vendor_section, "config_version")->second.data();
|
||||
auto config_version = Semver::parse(config_version_str);
|
||||
if (! config_version) {
|
||||
throw std::runtime_error((boost::format("Vendor Config Bundle `%1%` is not valid: Cannot parse config_version: `%2%`.") % id % config_version_str).str());
|
||||
} else {
|
||||
res.config_version = std::move(*config_version);
|
||||
}
|
||||
|
||||
auto config_update_url = vendor_section.find("config_update_url");
|
||||
if (config_update_url != vendor_section.not_found()) {
|
||||
res.config_update_url = config_update_url->second.data();
|
||||
}
|
||||
|
||||
if (! load_all) {
|
||||
return res;
|
||||
}
|
||||
|
||||
for (auto §ion : tree) {
|
||||
if (boost::starts_with(section.first, printer_model_key)) {
|
||||
VendorProfile::PrinterModel model;
|
||||
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)) {
|
||||
for (const std::string &variant_name : variants) {
|
||||
if (model.variant(variant_name) == nullptr)
|
||||
model.variants.emplace_back(VendorProfile::PrinterVariant(variant_name));
|
||||
}
|
||||
} else {
|
||||
// Log error? // XXX
|
||||
}
|
||||
if (! model.id.empty() && ! model.variants.empty())
|
||||
res.models.push_back(std::move(model));
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// Suffix to be added to a modified preset name in the combo box.
|
||||
static std::string g_suffix_modified = " (modified)";
|
||||
const std::string& Preset::suffix_modified()
|
||||
|
|
@ -175,6 +254,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 {
|
||||
|
|
@ -453,18 +541,6 @@ size_t PresetCollection::first_visible_idx() const
|
|||
return idx;
|
||||
}
|
||||
|
||||
// Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible.
|
||||
size_t PresetCollection::first_compatible_idx() const
|
||||
{
|
||||
size_t idx = m_default_suppressed ? 1 : 0;
|
||||
for (; idx < this->m_presets.size(); ++ idx)
|
||||
if (m_presets[idx].is_compatible)
|
||||
break;
|
||||
if (idx == this->m_presets.size())
|
||||
idx = 0;
|
||||
return idx;
|
||||
}
|
||||
|
||||
void PresetCollection::set_default_suppressed(bool default_suppressed)
|
||||
{
|
||||
if (m_default_suppressed != default_suppressed) {
|
||||
|
|
@ -473,7 +549,7 @@ void PresetCollection::set_default_suppressed(bool default_suppressed)
|
|||
}
|
||||
}
|
||||
|
||||
void PresetCollection::update_compatible_with_printer(const Preset &active_printer, bool select_other_if_incompatible)
|
||||
size_t PresetCollection::update_compatible_with_printer_internal(const Preset &active_printer, bool unselect_if_incompatible)
|
||||
{
|
||||
DynamicPrintConfig config;
|
||||
config.set_key_value("printer_preset", new ConfigOptionString(active_printer.name));
|
||||
|
|
@ -484,14 +560,12 @@ void PresetCollection::update_compatible_with_printer(const Preset &active_print
|
|||
Preset &preset_selected = m_presets[idx_preset];
|
||||
Preset &preset_edited = selected ? m_edited_preset : preset_selected;
|
||||
if (! preset_edited.update_compatible_with_printer(active_printer, &config) &&
|
||||
selected && select_other_if_incompatible)
|
||||
selected && unselect_if_incompatible)
|
||||
m_idx_selected = (size_t)-1;
|
||||
if (selected)
|
||||
preset_selected.is_compatible = preset_edited.is_compatible;
|
||||
}
|
||||
if (m_idx_selected == (size_t)-1)
|
||||
// Find some other compatible preset, or the "-- default --" preset.
|
||||
this->select_preset(first_compatible_idx());
|
||||
return m_idx_selected;
|
||||
}
|
||||
|
||||
// Save the preset under a new name. If the name is different from the old one,
|
||||
|
|
@ -677,8 +751,8 @@ bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, b
|
|||
// 1) Try to find the preset by its name.
|
||||
auto it = this->find_preset_internal(name);
|
||||
size_t idx = 0;
|
||||
if (it != m_presets.end() && it->name == name)
|
||||
// Preset found by its name.
|
||||
if (it != m_presets.end() && it->name == name && it->is_visible)
|
||||
// Preset found by its name and it is visible.
|
||||
idx = it - m_presets.begin();
|
||||
else {
|
||||
// Find the first visible preset.
|
||||
|
|
@ -699,6 +773,23 @@ bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, b
|
|||
return false;
|
||||
}
|
||||
|
||||
bool PresetCollection::select_preset_by_name_strict(const std::string &name)
|
||||
{
|
||||
// 1) Try to find the preset by its name.
|
||||
auto it = this->find_preset_internal(name);
|
||||
size_t idx = (size_t)-1;
|
||||
if (it != m_presets.end() && it->name == name && it->is_visible)
|
||||
// Preset found by its name.
|
||||
idx = it - m_presets.begin();
|
||||
// 2) Select the new preset.
|
||||
if (idx != (size_t)-1) {
|
||||
this->select_preset(idx);
|
||||
return true;
|
||||
}
|
||||
m_idx_selected = idx;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string PresetCollection::name() const
|
||||
{
|
||||
switch (this->type()) {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,12 @@
|
|||
|
||||
#include <deque>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/property_tree/ptree_fwd.hpp>
|
||||
|
||||
#include "../../libslic3r/libslic3r.h"
|
||||
#include "../../libslic3r/PrintConfig.hpp"
|
||||
#include "slic3r/Utils/Semver.hpp"
|
||||
|
||||
class wxBitmap;
|
||||
class wxChoice;
|
||||
|
|
@ -13,6 +17,9 @@ class wxItemContainer;
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
class AppConfig;
|
||||
class PresetBundle;
|
||||
|
||||
enum ConfigFileType
|
||||
{
|
||||
CONFIG_FILE_TYPE_UNKNOWN,
|
||||
|
|
@ -28,21 +35,19 @@ class VendorProfile
|
|||
public:
|
||||
std::string name;
|
||||
std::string id;
|
||||
std::string config_version;
|
||||
Semver config_version;
|
||||
std::string config_update_url;
|
||||
|
||||
struct PrinterVariant {
|
||||
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 +56,14 @@ 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() {}
|
||||
VendorProfile(std::string id) : id(std::move(id)) {}
|
||||
|
||||
static VendorProfile from_ini(const boost::filesystem::path &path, bool load_all=true);
|
||||
static VendorProfile from_ini(const boost::property_tree::ptree &tree, const boost::filesystem::path &path, bool load_all=true);
|
||||
|
||||
size_t num_variants() const { size_t n = 0; for (auto &model : models) n += model.variants.size(); return n; }
|
||||
|
||||
|
|
@ -86,7 +94,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 +141,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 +175,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; }
|
||||
|
|
@ -234,19 +253,49 @@ public:
|
|||
{ return const_cast<PresetCollection*>(this)->find_preset(name, first_visible_if_not_found); }
|
||||
|
||||
size_t first_visible_idx() const;
|
||||
size_t first_compatible_idx() const;
|
||||
// Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible.
|
||||
// If one of the prefered_alternates is compatible, select it.
|
||||
template<typename PreferedCondition>
|
||||
size_t first_compatible_idx(PreferedCondition prefered_condition) const
|
||||
{
|
||||
size_t i = m_default_suppressed ? 1 : 0;
|
||||
size_t n = this->m_presets.size();
|
||||
size_t i_compatible = n;
|
||||
for (; i < n; ++ i)
|
||||
if (m_presets[i].is_compatible) {
|
||||
if (prefered_condition(m_presets[i].name))
|
||||
return i;
|
||||
if (i_compatible == n)
|
||||
// Store the first compatible profile into i_compatible.
|
||||
i_compatible = i;
|
||||
}
|
||||
return (i_compatible == n) ? 0 : i_compatible;
|
||||
}
|
||||
// Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible.
|
||||
size_t first_compatible_idx() const { return this->first_compatible_idx([](const std::string&){return true;}); }
|
||||
|
||||
// Return index of the first visible preset. Certainly at least the '- default -' preset shall be visible.
|
||||
// Return the first visible preset. Certainly at least the '- default -' preset shall be visible.
|
||||
Preset& first_visible() { return this->preset(this->first_visible_idx()); }
|
||||
const Preset& first_visible() const { return this->preset(this->first_visible_idx()); }
|
||||
Preset& first_compatible() { return this->preset(this->first_compatible_idx()); }
|
||||
template<typename PreferedCondition>
|
||||
Preset& first_compatible(PreferedCondition prefered_condition) { return this->preset(this->first_compatible_idx(prefered_condition)); }
|
||||
const Preset& first_compatible() const { return this->preset(this->first_compatible_idx()); }
|
||||
|
||||
// Return number of presets including the "- default -" preset.
|
||||
size_t size() const { return this->m_presets.size(); }
|
||||
|
||||
// For Print / Filament presets, disable those, which are not compatible with the printer.
|
||||
void update_compatible_with_printer(const Preset &active_printer, bool select_other_if_incompatible);
|
||||
template<typename PreferedCondition>
|
||||
void update_compatible_with_printer(const Preset &active_printer, bool select_other_if_incompatible, PreferedCondition prefered_condition)
|
||||
{
|
||||
if (this->update_compatible_with_printer_internal(active_printer, select_other_if_incompatible) == (size_t)-1)
|
||||
// Find some other compatible preset, or the "-- default --" preset.
|
||||
this->select_preset(this->first_compatible_idx(prefered_condition));
|
||||
}
|
||||
void update_compatible_with_printer(const Preset &active_printer, bool select_other_if_incompatible)
|
||||
{ this->update_compatible_with_printer(active_printer, select_other_if_incompatible, [](const std::string&){return true;}); }
|
||||
|
||||
size_t num_visible() const { return std::count_if(m_presets.begin(), m_presets.end(), [](const Preset &preset){return preset.is_visible;}); }
|
||||
|
||||
|
|
@ -282,6 +331,11 @@ public:
|
|||
// Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
|
||||
std::string path_from_name(const std::string &new_name) const;
|
||||
|
||||
protected:
|
||||
// Select a preset, if it exists. If it does not exist, select an invalid (-1) index.
|
||||
// This is a temporary state, which shall be fixed immediately by the following step.
|
||||
bool select_preset_by_name_strict(const std::string &name);
|
||||
|
||||
private:
|
||||
PresetCollection();
|
||||
PresetCollection(const PresetCollection &other);
|
||||
|
|
@ -299,6 +353,8 @@ private:
|
|||
std::deque<Preset>::const_iterator find_preset_internal(const std::string &name) const
|
||||
{ return const_cast<PresetCollection*>(this)->find_preset_internal(name); }
|
||||
|
||||
size_t update_compatible_with_printer_internal(const Preset &active_printer, bool unselect_if_incompatible);
|
||||
|
||||
static std::vector<std::string> dirty_options(const Preset *edited, const Preset *reference);
|
||||
|
||||
// Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER.
|
||||
|
|
@ -324,6 +380,9 @@ private:
|
|||
wxBitmap *m_bitmap_main_frame;
|
||||
// Path to the directory to store the config files into.
|
||||
std::string m_dir_path;
|
||||
|
||||
// to access select_preset_by_name_strict()
|
||||
friend class PresetBundle;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "PresetBundle.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/algorithm/clamp.hpp>
|
||||
|
|
@ -111,6 +112,7 @@ void PresetBundle::setup_directories()
|
|||
std::initializer_list<boost::filesystem::path> paths = {
|
||||
data_dir,
|
||||
data_dir / "vendor",
|
||||
data_dir / "cache",
|
||||
#ifdef SLIC3R_PROFILE_USE_PRESETS_SUBDIR
|
||||
// Store the print/filament/printer presets into a "presets" directory.
|
||||
data_dir / "presets",
|
||||
|
|
@ -205,26 +207,44 @@ 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
|
||||
// This is done just once on application start up.
|
||||
void PresetBundle::load_selections(const AppConfig &config)
|
||||
{
|
||||
prints.select_preset_by_name(remove_ini_suffix(config.get("presets", "print")), true);
|
||||
filaments.select_preset_by_name(remove_ini_suffix(config.get("presets", "filament")), true);
|
||||
printers.select_preset_by_name(remove_ini_suffix(config.get("presets", "printer")), true);
|
||||
// Update visibility of presets based on application vendor / model / variant configuration.
|
||||
this->load_installed_printers(config);
|
||||
|
||||
// Parse the initial print / filament / printer profile names.
|
||||
std::string initial_print_profile_name = remove_ini_suffix(config.get("presets", "print"));
|
||||
std::vector<std::string> initial_filament_profile_names;
|
||||
std::string initial_printer_profile_name = remove_ini_suffix(config.get("presets", "printer"));
|
||||
|
||||
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(printers.get_selected_preset().config.option("nozzle_diameter"));
|
||||
size_t num_extruders = nozzle_diameter->values.size();
|
||||
this->set_filament_preset(0, filaments.get_selected_preset().name);
|
||||
initial_filament_profile_names.emplace_back(remove_ini_suffix(config.get("presets", "filament")));
|
||||
this->set_filament_preset(0, initial_filament_profile_names.back());
|
||||
for (unsigned int i = 1; i < (unsigned int)num_extruders; ++ i) {
|
||||
char name[64];
|
||||
sprintf(name, "filament_%d", i);
|
||||
if (! config.has("presets", name))
|
||||
break;
|
||||
this->set_filament_preset(i, remove_ini_suffix(config.get("presets", name)));
|
||||
initial_filament_profile_names.emplace_back(remove_ini_suffix(config.get("presets", name)));
|
||||
this->set_filament_preset(i, initial_filament_profile_names.back());
|
||||
}
|
||||
|
||||
// Activate print / filament / printer profiles from the config.
|
||||
// If the printer profile enumerated by the config are not visible, select an alternate preset.
|
||||
// Do not select alternate profiles for the print / filament profiles as those presets
|
||||
// will be selected by the following call of this->update_compatible_with_printer(true).
|
||||
prints.select_preset_by_name_strict(initial_print_profile_name);
|
||||
filaments.select_preset_by_name_strict(initial_filament_profile_names.front());
|
||||
printers.select_preset_by_name(initial_printer_profile_name, true);
|
||||
|
||||
// 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
|
||||
|
|
@ -675,45 +695,16 @@ static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree)
|
|||
flatten_configbundle_hierarchy(tree, "printer");
|
||||
}
|
||||
|
||||
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)
|
||||
if (section.first == "vendor") {
|
||||
// Load the names of the active presets.
|
||||
for (auto &kvp : section.second) {
|
||||
if (kvp.first == "name")
|
||||
vendor_profile.name = kvp.second.data();
|
||||
else if (kvp.first == "id")
|
||||
vendor_profile.id = kvp.second.data();
|
||||
else if (kvp.first == "config_version")
|
||||
vendor_profile.config_version = kvp.second.data();
|
||||
else if (kvp.first == "config_update_url")
|
||||
vendor_profile.config_update_url = kvp.second.data();
|
||||
}
|
||||
} else if (boost::starts_with(section.first, printer_model_key)) {
|
||||
VendorProfile::PrinterModel model;
|
||||
model.name = section.first.substr(printer_model_key.size());
|
||||
section.second.get<std::string>("variants", "");
|
||||
std::vector<std::string> variants;
|
||||
if (Slic3r::unescape_strings_cstyle(section.second.get<std::string>("variants", ""), variants)) {
|
||||
for (const std::string &variant_name : variants) {
|
||||
if (model.variant(variant_name) == nullptr)
|
||||
model.variants.emplace_back(VendorProfile::PrinterVariant(variant_name));
|
||||
}
|
||||
} else {
|
||||
// Log error?
|
||||
}
|
||||
if (! model.name.empty() && ! model.variants.empty())
|
||||
vendor_profile.models.insert(model);
|
||||
}
|
||||
}
|
||||
|
||||
// Load a config bundle file, into presets and store the loaded presets into separate files
|
||||
// of the local configuration directory.
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -730,19 +721,21 @@ 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;
|
||||
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."));
|
||||
if (flags & (LOAD_CFGBNDLE_SYSTEM | LOAD_CFGBUNDLE_VENDOR_ONLY)) {
|
||||
auto vp = VendorProfile::from_ini(tree, path);
|
||||
if (vp.num_variants() == 0)
|
||||
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;
|
||||
|
|
@ -814,7 +807,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.";
|
||||
|
|
@ -916,14 +911,35 @@ void PresetBundle::update_multi_material_filament_presets()
|
|||
|
||||
void PresetBundle::update_compatible_with_printer(bool select_other_if_incompatible)
|
||||
{
|
||||
this->prints.update_compatible_with_printer(this->printers.get_edited_preset(), select_other_if_incompatible);
|
||||
this->filaments.update_compatible_with_printer(this->printers.get_edited_preset(), select_other_if_incompatible);
|
||||
const Preset &printer_preset = this->printers.get_edited_preset();
|
||||
const std::string &prefered_print_profile = printer_preset.config.opt_string("default_print_profile");
|
||||
const std::vector<std::string> &prefered_filament_profiles = printer_preset.config.option<ConfigOptionStrings>("default_filament_profile")->values;
|
||||
prefered_print_profile.empty() ?
|
||||
this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible) :
|
||||
this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible,
|
||||
[&prefered_print_profile](const std::string& profile_name){ return profile_name == prefered_print_profile; });
|
||||
prefered_filament_profiles.empty() ?
|
||||
this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible) :
|
||||
this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible,
|
||||
[&prefered_filament_profiles](const std::string& profile_name)
|
||||
{ return std::find(prefered_filament_profiles.begin(), prefered_filament_profiles.end(), profile_name) != prefered_filament_profiles.end(); });
|
||||
if (select_other_if_incompatible) {
|
||||
// Verify validity of the current filament presets.
|
||||
for (std::string &filament_name : this->filament_presets) {
|
||||
Preset *preset = this->filaments.find_preset(filament_name, false);
|
||||
if (preset == nullptr || ! preset->is_compatible)
|
||||
filament_name = this->filaments.first_compatible().name;
|
||||
this->filament_presets.front() = this->filaments.get_edited_preset().name;
|
||||
for (size_t idx = 1; idx < this->filament_presets.size(); ++ idx) {
|
||||
std::string &filament_name = this->filament_presets[idx];
|
||||
Preset *preset = this->filaments.find_preset(filament_name, false);
|
||||
if (preset == nullptr || ! preset->is_compatible) {
|
||||
// Pick a compatible profile. If there are prefered_filament_profiles, use them.
|
||||
if (prefered_filament_profiles.empty())
|
||||
filament_name = this->filaments.first_compatible().name;
|
||||
else {
|
||||
const std::string &preferred = (idx < prefered_filament_profiles.size()) ?
|
||||
prefered_filament_profiles[idx] : prefered_filament_profiles.front();
|
||||
filament_name = this->filaments.first_compatible(
|
||||
[&preferred](const std::string& profile_name){ return profile_name == preferred; }).name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "Preset.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
@ -86,12 +87,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);
|
||||
|
|
|
|||
|
|
@ -644,7 +644,8 @@ void Tab::load_key_value(const std::string& opt_key, const boost::any& value)
|
|||
change_opt_value(*m_config, opt_key, value);
|
||||
// Mark the print & filament enabled if they are compatible with the currently selected preset.
|
||||
if (opt_key.compare("compatible_printers") == 0) {
|
||||
m_preset_bundle->update_compatible_with_printer(0);
|
||||
// Don't select another profile if this profile happens to become incompatible.
|
||||
m_preset_bundle->update_compatible_with_printer(false);
|
||||
}
|
||||
m_presets->update_dirty_ui(m_presets_choice);
|
||||
on_presets_changed();
|
||||
|
|
@ -2203,9 +2204,9 @@ wxSizer* Tab::compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox
|
|||
presets.Add(preset.name);
|
||||
}
|
||||
|
||||
auto dlg = new wxMultiChoiceDialog(parent,
|
||||
_(L("Select the printers this profile is compatible with.")),
|
||||
_(L("Compatible printers")), presets);
|
||||
wxMultiChoiceDialog dlg(parent,
|
||||
_(L("Select the printers this profile is compatible with.")),
|
||||
_(L("Compatible printers")), presets);
|
||||
// # Collect and set indices of printers marked as compatible.
|
||||
wxArrayInt selections;
|
||||
auto *compatible_printers = dynamic_cast<const ConfigOptionStrings*>(m_config->option("compatible_printers"));
|
||||
|
|
@ -2217,12 +2218,12 @@ wxSizer* Tab::compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox
|
|||
selections.Add(idx);
|
||||
break;
|
||||
}
|
||||
dlg->SetSelections(selections);
|
||||
dlg.SetSelections(selections);
|
||||
std::vector<std::string> value;
|
||||
// Show the dialog.
|
||||
if (dlg->ShowModal() == wxID_OK) {
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
selections.Clear();
|
||||
selections = dlg->GetSelections();
|
||||
selections = dlg.GetSelections();
|
||||
for (auto idx : selections)
|
||||
value.push_back(presets[idx].ToStdString());
|
||||
if (value.empty()) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue