diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 10375a9dc..89a8e7974 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -86,8 +86,9 @@ sub OnInit { Slic3r::GUI::set_wxapp($self); $self->{app_config} = Slic3r::GUI::AppConfig->new; - $self->{preset_bundle} = Slic3r::GUI::PresetBundle->new; Slic3r::GUI::set_app_config($self->{app_config}); + $self->{preset_bundle} = Slic3r::GUI::PresetBundle->new; + Slic3r::GUI::set_preset_bundle($self->{preset_bundle}); # just checking for existence of Slic3r::data_dir is not enough: it may be an empty directory # supplied as argument to --datadir; in that case we should still run the wizard @@ -104,7 +105,11 @@ sub OnInit { $self->{preset_updater} = Slic3r::PresetUpdater->new($VERSION_ONLINE_EVENT); Slic3r::GUI::set_preset_updater($self->{preset_updater}); - eval { $self->{preset_updater}->config_update(); }; + eval { + if (! $self->{preset_updater}->config_update()) { + exit 0; + } + }; if ($@) { warn $@ . "\n"; fatal_error(undef, $@); @@ -120,8 +125,6 @@ sub OnInit { show_error(undef, $@); } - Slic3r::GUI::set_preset_bundle($self->{preset_bundle}); - # application frame print STDERR "Creating main frame...\n"; Wx::Image::FindHandlerType(wxBITMAP_TYPE_PNG) || Wx::Image::AddHandler(Wx::PNGHandler->new); @@ -144,8 +147,10 @@ sub OnInit { # On OSX the UI was not initialized correctly if the wizard was called # before the UI was up and running. $self->CallAfter(sub { - Slic3r::GUI::config_wizard_startup($app_conf_exists); - $self->{preset_updater}->slic3r_update_notify(); + if (! Slic3r::GUI::config_wizard_startup($app_conf_exists)) { + # Only notify if there was not wizard so as not to bother too much ... + $self->{preset_updater}->slic3r_update_notify(); + } $self->{preset_updater}->sync($self->{preset_bundle}); }); diff --git a/resources/icons/Slic3r_192px_grayscale.png b/resources/icons/Slic3r_192px_grayscale.png new file mode 100644 index 000000000..910f94187 Binary files /dev/null and b/resources/icons/Slic3r_192px_grayscale.png differ diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 7798333fb..2f03daaa7 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -220,6 +220,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/Utils/ASCIIFolding.hpp ${LIBDIR}/slic3r/GUI/ConfigWizard.cpp ${LIBDIR}/slic3r/GUI/ConfigWizard.hpp + ${LIBDIR}/slic3r/GUI/UpdateDialogs.cpp + ${LIBDIR}/slic3r/GUI/UpdateDialogs.hpp ${LIBDIR}/slic3r/Utils/Http.cpp ${LIBDIR}/slic3r/Utils/Http.hpp ${LIBDIR}/slic3r/Utils/OctoPrint.cpp diff --git a/xs/src/slic3r/GUI/ConfigWizard.cpp b/xs/src/slic3r/GUI/ConfigWizard.cpp index a1ee5a748..84f401ba1 100644 --- a/xs/src/slic3r/GUI/ConfigWizard.cpp +++ b/xs/src/slic3r/GUI/ConfigWizard.cpp @@ -207,7 +207,7 @@ PageWelcome::PageWelcome(ConfigWizard *parent) : others_buttons(new wxPanel(parent)), cbox_reset(nullptr) { - if (wizard_p()->flag_startup && wizard_p()->flag_empty_datadir) { + if (wizard_p()->run_reason == ConfigWizard::RR_DATA_EMPTY) { wxString::Format(_(L("Run %s")), ConfigWizard::name()); append_text(wxString::Format( _(L("Hello, welcome to Slic3r Prusa Edition! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")), @@ -709,7 +709,7 @@ void ConfigWizard::priv::on_custom_setup() set_page(page_firmware); } -void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, PresetUpdater *updater) +void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater) { const bool is_custom_setup = page_welcome->page_next() == page_firmware; @@ -730,8 +730,14 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese install_bundles.emplace_back(vendor_rsrc.second); } - // If the datadir was empty don't take a snapshot (it would just be an empty snapshot) - const bool snapshot = !flag_empty_datadir || page_welcome->reset_user_profile(); + // Decide whether to create snapshot based on run_reason and the reset profile checkbox + bool snapshot = true; + switch (run_reason) { + case ConfigWizard::RR_DATA_EMPTY: snapshot = false; break; + case ConfigWizard::RR_DATA_LEGACY: snapshot = true; break; + case ConfigWizard::RR_DATA_INCOMPAT: snapshot = false; break; // In this case snapshot is done by PresetUpdater with the appropriate reason + case ConfigWizard::RR_USER: snapshot = page_welcome->reset_user_profile(); break; + } if (install_bundles.size() > 0) { // Install bundles from resources. updater->install_bundles_rsrc(std::move(install_bundles), snapshot); @@ -744,8 +750,7 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese 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 (flag_startup) - app_config->reset_selections(); + app_config->reset_selections(); // ^ TODO: replace with appropriate printer selection preset_bundle->load_presets(*app_config); } else { @@ -760,12 +765,11 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese // Public -ConfigWizard::ConfigWizard(wxWindow *parent, bool startup, bool empty_datadir) : +ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) : wxDialog(parent, wxID_ANY, name(), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), p(new priv(this)) { - p->flag_startup = startup; - p->flag_empty_datadir = empty_datadir; + p->run_reason = reason; p->load_vendors(); p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({ @@ -822,10 +826,13 @@ ConfigWizard::ConfigWizard(wxWindow *parent, bool startup, bool empty_datadir) : ConfigWizard::~ConfigWizard() {} -void ConfigWizard::run(PresetBundle *preset_bundle, PresetUpdater *updater) +bool ConfigWizard::run(PresetBundle *preset_bundle, const PresetUpdater *updater) { if (ShowModal() == wxID_OK) { p->apply_config(GUI::get_app_config(), preset_bundle, updater); + return true; + } else { + return false; } } diff --git a/xs/src/slic3r/GUI/ConfigWizard.hpp b/xs/src/slic3r/GUI/ConfigWizard.hpp index eeb64c57f..73fce7cd2 100644 --- a/xs/src/slic3r/GUI/ConfigWizard.hpp +++ b/xs/src/slic3r/GUI/ConfigWizard.hpp @@ -16,14 +16,23 @@ namespace GUI { class ConfigWizard: public wxDialog { public: - ConfigWizard(wxWindow *parent, bool startup, bool empty_datadir); + // Why is the Wizard run + enum RunReason { + RR_DATA_EMPTY, // No or empty datadir + RR_DATA_LEGACY, // Pre-updating datadir + RR_DATA_INCOMPAT, // Incompatible datadir - Slic3r downgrade situation + RR_USER, // User requested the Wizard from the menus + }; + + ConfigWizard(wxWindow *parent, RunReason run_reason); ConfigWizard(ConfigWizard &&) = delete; ConfigWizard(const ConfigWizard &) = delete; ConfigWizard &operator=(ConfigWizard &&) = delete; ConfigWizard &operator=(const ConfigWizard &) = delete; ~ConfigWizard(); - void run(PresetBundle *preset_bundle, PresetUpdater *updater); + // Run the Wizard. Return whether it was completed. + bool run(PresetBundle *preset_bundle, const PresetUpdater *updater); static const wxString& name(); private: diff --git a/xs/src/slic3r/GUI/ConfigWizard_private.hpp b/xs/src/slic3r/GUI/ConfigWizard_private.hpp index cdab2eb3c..474394bc3 100644 --- a/xs/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/xs/src/slic3r/GUI/ConfigWizard_private.hpp @@ -190,8 +190,7 @@ private: struct ConfigWizard::priv { ConfigWizard *q; - bool flag_startup; - bool flag_empty_datadir; + ConfigWizard::RunReason run_reason; AppConfig appconfig_vendors; std::unordered_map vendors; std::unordered_map vendors_rsrc; @@ -228,7 +227,7 @@ struct ConfigWizard::priv void on_other_vendors(); void on_custom_setup(); - void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, PresetUpdater *updater); + void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater); }; diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 88c3f421b..bd74e118e 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -390,7 +390,7 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l 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(false, false); + config_wizard(ConfigWizard::RR_USER); break; case ConfigMenuTakeSnapshot: // Take a configuration snapshot. @@ -469,10 +469,11 @@ bool check_unsaved_changes() return dialog->ShowModal() == wxID_YES; } -void config_wizard_startup(bool app_config_exists) +bool config_wizard_startup(bool app_config_exists) { if (! app_config_exists || g_PresetBundle->has_defauls_only()) { - config_wizard(true, true); + config_wizard(ConfigWizard::RR_DATA_EMPTY); + return true; } else if (g_AppConfig->legacy_datadir()) { // Looks like user has legacy pre-vendorbundle data directory, // explain what this is and run the wizard @@ -496,20 +497,19 @@ void config_wizard_startup(bool app_config_exists) dlg.SetExtendedMessage(ext_msg); const auto res = dlg.ShowModal(); - config_wizard(true, false); + config_wizard(ConfigWizard::RR_DATA_LEGACY); + return true; } + return false; } -void config_wizard(bool startup, bool empty_datadir) +void config_wizard(int reason) { - 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; - ConfigWizard wizard(g_wxMainFrame, startup, empty_datadir); + ConfigWizard wizard(nullptr, static_cast(reason)); wizard.run(g_PresetBundle, g_PresetUpdater); // Load the currently selected preset into the GUI, update the preset selection box. @@ -686,6 +686,11 @@ wxApp* get_app(){ return g_wxApp; } +PresetBundle* get_preset_bundle() +{ + return g_PresetBundle; +} + const wxColour& get_modified_label_clr() { return g_color_label_modified; } diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 92a6e6ebb..a8bbdccc7 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -82,6 +82,7 @@ void set_preset_updater(PresetUpdater *updater); AppConfig* get_app_config(); wxApp* get_app(); +PresetBundle* get_preset_bundle(); const wxColour& get_modified_label_clr(); const wxColour& get_sys_label_clr(); @@ -93,11 +94,13 @@ extern void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int // 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); +// Checks if configuration wizard needs to run, calls config_wizard if so. +// Returns whether the Wizard ran. +extern bool config_wizard_startup(bool app_config_exists); // Opens the configuration wizard, returns true if wizard is finished & accepted. -extern void config_wizard(bool startup, bool empty_datadir); +// The run_reason argument is actually ConfigWizard::RunReason, but int is used here because of Perl. +extern void config_wizard(int run_reason); // Create "Preferences" dialog after selecting menu "Preferences" in Perl part extern void open_preferences_dialog(int event_preferences); diff --git a/xs/src/slic3r/GUI/UpdateDialogs.cpp b/xs/src/slic3r/GUI/UpdateDialogs.cpp new file mode 100644 index 000000000..076554e23 --- /dev/null +++ b/xs/src/slic3r/GUI/UpdateDialogs.cpp @@ -0,0 +1,203 @@ +#include "UpdateDialogs.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libslic3r/libslic3r.h" +#include "libslic3r/Utils.hpp" +#include "GUI.hpp" + +namespace Slic3r { +namespace GUI { + + +enum { + CONTENT_WIDTH = 400, + BORDER = 30, + VERT_SPACING = 15, + HORIZ_SPACING = 5, +}; + + +MsgDialog::MsgDialog(const wxString &title, const wxString &headline, wxWindowID button_id) : + MsgDialog(title, headline, wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG), button_id) +{} + +MsgDialog::MsgDialog(const wxString &title, const wxString &headline, wxBitmap bitmap, wxWindowID button_id) : + wxDialog(nullptr, wxID_ANY, title), + boldfont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)), + content_sizer(new wxBoxSizer(wxVERTICAL)), + btn_sizer(new wxBoxSizer(wxHORIZONTAL)) +{ + boldfont.SetWeight(wxFONTWEIGHT_BOLD); + + auto *topsizer = new wxBoxSizer(wxHORIZONTAL); + auto *rightsizer = new wxBoxSizer(wxVERTICAL); + + auto *headtext = new wxStaticText(this, wxID_ANY, headline); + headtext->SetFont(boldfont); + headtext->Wrap(CONTENT_WIDTH); + rightsizer->Add(headtext); + rightsizer->AddSpacer(VERT_SPACING); + + rightsizer->Add(content_sizer); + + if (button_id != wxID_NONE) { + auto *button = new wxButton(this, button_id); + button->SetFocus(); + btn_sizer->Add(button); + } + + rightsizer->Add(btn_sizer, 0, wxALIGN_CENTRE_HORIZONTAL); + + auto *logo = new wxStaticBitmap(this, wxID_ANY, std::move(bitmap)); + + topsizer->Add(logo, 0, wxALL, BORDER); + topsizer->Add(rightsizer, 0, wxALL, BORDER); + + SetSizerAndFit(topsizer); +} + +MsgDialog::~MsgDialog() {} + + +// MsgUpdateSlic3r + +MsgUpdateSlic3r::MsgUpdateSlic3r(const Semver &ver_current, const Semver &ver_online) : + MsgDialog(_(L("Update available")), _(L("New version of Slic3r PE is available"))), + ver_current(ver_current), + ver_online(ver_online) +{ + const auto url = wxString::Format("https://github.com/prusa3d/Slic3r/releases/tag/version_%s", ver_online.to_string()); + auto *link = new wxHyperlinkCtrl(this, wxID_ANY, url, url); + + auto *text = new wxStaticText(this, wxID_ANY, _(L("To download, follow the link below."))); + const auto link_width = link->GetSize().GetWidth(); + text->Wrap(CONTENT_WIDTH > link_width ? CONTENT_WIDTH : link_width); + content_sizer->Add(text); + content_sizer->AddSpacer(VERT_SPACING); + + auto *versions = new wxFlexGridSizer(2, 0, VERT_SPACING); + versions->Add(new wxStaticText(this, wxID_ANY, _(L("Current version:")))); + versions->Add(new wxStaticText(this, wxID_ANY, ver_current.to_string())); + versions->Add(new wxStaticText(this, wxID_ANY, _(L("New version:")))); + versions->Add(new wxStaticText(this, wxID_ANY, ver_online.to_string())); + content_sizer->Add(versions); + content_sizer->AddSpacer(VERT_SPACING); + + content_sizer->Add(link); + content_sizer->AddSpacer(2*VERT_SPACING); + + cbox = new wxCheckBox(this, wxID_ANY, _(L("Don't notify about new releases any more"))); + content_sizer->Add(cbox); + content_sizer->AddSpacer(VERT_SPACING); + + Fit(); +} + +MsgUpdateSlic3r::~MsgUpdateSlic3r() {} + +bool MsgUpdateSlic3r::disable_version_check() const +{ + return cbox->GetValue(); +} + + +// MsgUpdateConfig + +MsgUpdateConfig::MsgUpdateConfig(const std::unordered_map &updates) : + MsgDialog(_(L("Configuration update")), _(L("Configuration update is available")), wxID_NONE) +{ + auto *text = new wxStaticText(this, wxID_ANY, _(L( + "Would you like to install it?\n\n" + "Note that a full configuration snapshot will be created first. It can then be restored at any time " + "should there be a problem with the new version.\n\n" + "Updated configuration bundles:" + ))); + text->Wrap(CONTENT_WIDTH); + content_sizer->Add(text); + content_sizer->AddSpacer(VERT_SPACING); + + auto *versions = new wxFlexGridSizer(2, 0, VERT_SPACING); + for (const auto &update : updates) { + auto *text_vendor = new wxStaticText(this, wxID_ANY, update.first); + text_vendor->SetFont(boldfont); + versions->Add(text_vendor); + versions->Add(new wxStaticText(this, wxID_ANY, update.second)); + } + + content_sizer->Add(versions); + content_sizer->AddSpacer(2*VERT_SPACING); + + auto *btn_cancel = new wxButton(this, wxID_CANCEL); + btn_sizer->Add(btn_cancel); + btn_sizer->AddSpacer(HORIZ_SPACING); + auto *btn_ok = new wxButton(this, wxID_YES); + btn_sizer->Add(btn_ok); + btn_ok->SetFocus(); + + Fit(); +} + +MsgUpdateConfig::~MsgUpdateConfig() {} + + +// MsgDataIncompatible + +MsgDataIncompatible::MsgDataIncompatible(const std::unordered_map &incompats) : + MsgDialog(_(L("Slic3r incompatibility")), _(L("Slic3r configuration is incompatible")), wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png"))), wxID_NONE) +{ + auto *text = new wxStaticText(this, wxID_ANY, _(L( + "This version of Slic3r PE is not compatible with currently installed configuration bundles.\n" + "This probably happened as a result of running an older Slic3r PE after using a newer one.\n\n" + "TODO: more instrs\n" + ))); + text->Wrap(CONTENT_WIDTH); + content_sizer->Add(text); + + auto *text2 = new wxStaticText(this, wxID_ANY, wxString::Format(_(L("This Slic3r PE version: %s")), SLIC3R_VERSION)); + text2->Wrap(CONTENT_WIDTH); + content_sizer->Add(text2); + content_sizer->AddSpacer(VERT_SPACING); + + auto *text3 = new wxStaticText(this, wxID_ANY, _(L("Incompatible bundles:"))); + text3->Wrap(CONTENT_WIDTH); + content_sizer->Add(text3); + content_sizer->AddSpacer(VERT_SPACING); + + auto *versions = new wxFlexGridSizer(2, 0, VERT_SPACING); + for (const auto &incompat : incompats) { + auto *text_vendor = new wxStaticText(this, wxID_ANY, incompat.first); + text_vendor->SetFont(boldfont); + versions->Add(text_vendor); + versions->Add(new wxStaticText(this, wxID_ANY, incompat.second)); + } + + content_sizer->Add(versions); + content_sizer->AddSpacer(2*VERT_SPACING); + + auto *btn_exit = new wxButton(this, wxID_EXIT, _(L("Exit Slic3r"))); + btn_sizer->Add(btn_exit); + btn_sizer->AddSpacer(HORIZ_SPACING); + auto *btn_reconf = new wxButton(this, wxID_REPLACE, _(L("Re-configure"))); + btn_sizer->Add(btn_reconf); + btn_exit->SetFocus(); + + auto exiter = [this](const wxCommandEvent& evt) { this->EndModal(evt.GetId()); }; + btn_exit->Bind(wxEVT_BUTTON, exiter); + btn_reconf->Bind(wxEVT_BUTTON, exiter); + + Fit(); +} + +MsgDataIncompatible::~MsgDataIncompatible() {} + + +} +} diff --git a/xs/src/slic3r/GUI/UpdateDialogs.hpp b/xs/src/slic3r/GUI/UpdateDialogs.hpp new file mode 100644 index 000000000..ae949b8dd --- /dev/null +++ b/xs/src/slic3r/GUI/UpdateDialogs.hpp @@ -0,0 +1,92 @@ +#ifndef slic3r_UpdateDialogs_hpp_ +#define slic3r_UpdateDialogs_hpp_ + +#include +#include + +#include +#include +#include + +#include "slic3r/Utils/Semver.hpp" + +class wxBoxSizer; +class wxCheckBox; + +namespace Slic3r { + +namespace GUI { + + +// A message / query dialog with a bitmap on the left and any content on the right +// with buttons underneath. +struct MsgDialog : wxDialog +{ + MsgDialog(MsgDialog &&) = delete; + MsgDialog(const MsgDialog &) = delete; + MsgDialog &operator=(MsgDialog &&) = delete; + MsgDialog &operator=(const MsgDialog &) = delete; + virtual ~MsgDialog(); + +protected: + // button_id is an id of a button that can be added by default, use wxID_NONE to disable + MsgDialog(const wxString &title, const wxString &headline, wxWindowID button_id = wxID_OK); + MsgDialog(const wxString &title, const wxString &headline, wxBitmap bitmap, wxWindowID button_id = wxID_OK); + + wxFont boldfont; + wxBoxSizer *content_sizer; + wxBoxSizer *btn_sizer; +}; + +// A confirmation dialog listing configuration updates +class MsgUpdateSlic3r : public MsgDialog +{ +public: + MsgUpdateSlic3r(const Semver &ver_current, const Semver &ver_online); + MsgUpdateSlic3r(MsgUpdateSlic3r &&) = delete; + MsgUpdateSlic3r(const MsgUpdateSlic3r &) = delete; + MsgUpdateSlic3r &operator=(MsgUpdateSlic3r &&) = delete; + MsgUpdateSlic3r &operator=(const MsgUpdateSlic3r &) = delete; + virtual ~MsgUpdateSlic3r(); + + // Tells whether the user checked the "don't bother me again" checkbox + bool disable_version_check() const; + +private: + const Semver &ver_current; + const Semver &ver_online; + wxCheckBox *cbox; +}; + + +// Confirmation dialog informing about configuration update. Lists updated bundles & their versions. +class MsgUpdateConfig : public MsgDialog +{ +public: + // updates is a map of "vendor name" -> "version (comment)" + MsgUpdateConfig(const std::unordered_map &updates); + MsgUpdateConfig(MsgUpdateConfig &&) = delete; + MsgUpdateConfig(const MsgUpdateConfig &) = delete; + MsgUpdateConfig &operator=(MsgUpdateConfig &&) = delete; + MsgUpdateConfig &operator=(const MsgUpdateConfig &) = delete; + ~MsgUpdateConfig(); +}; + +// Informs about currently installed bundles not being compatible with the running Slic3r. Asks about action. +class MsgDataIncompatible : public MsgDialog +{ +public: + // incompats is a map of "vendor name" -> "version restrictions" + MsgDataIncompatible(const std::unordered_map &incompats); + MsgDataIncompatible(MsgDataIncompatible &&) = delete; + MsgDataIncompatible(const MsgDataIncompatible &) = delete; + MsgDataIncompatible &operator=(MsgDataIncompatible &&) = delete; + MsgDataIncompatible &operator=(const MsgDataIncompatible &) = delete; + ~MsgDataIncompatible(); +}; + + +} +} + +#endif diff --git a/xs/src/slic3r/Utils/PresetUpdater.cpp b/xs/src/slic3r/Utils/PresetUpdater.cpp index 8dd974537..ec152df63 100644 --- a/xs/src/slic3r/Utils/PresetUpdater.cpp +++ b/xs/src/slic3r/Utils/PresetUpdater.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include #include @@ -14,18 +14,13 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/PresetBundle.hpp" +#include "slic3r/GUI/UpdateDialogs.hpp" +#include "slic3r/GUI/ConfigWizard.hpp" #include "slic3r/Utils/Http.hpp" #include "slic3r/Config/Version.hpp" #include "slic3r/Config/Snapshot.hpp" @@ -48,90 +43,49 @@ static const char *INDEX_FILENAME = "index.idx"; static const char *TMP_EXTENSION = ".download"; -// A confirmation dialog listing configuration updates -struct UpdateNotification : wxDialog -{ - // If this dialog gets any more complex, it should probably be factored out... - - enum { - CONTENT_WIDTH = 400, - BORDER = 30, - SPACING = 15, - }; - - wxCheckBox *cbox; - - UpdateNotification(const Semver &ver_current, const Semver &ver_online) : wxDialog(nullptr, wxID_ANY, _(L("Update available"))) - { - auto *topsizer = new wxBoxSizer(wxHORIZONTAL); - auto *sizer = new wxBoxSizer(wxVERTICAL); - - const auto url = wxString::Format("https://github.com/prusa3d/Slic3r/releases/tag/version_%s", ver_online.to_string()); - auto *link = new wxHyperlinkCtrl(this, wxID_ANY, url, url); - - auto *text = new wxStaticText(this, wxID_ANY, - _(L("New version of Slic3r PE is available. To download, follow the link below."))); - const auto link_width = link->GetSize().GetWidth(); - text->Wrap(CONTENT_WIDTH > link_width ? CONTENT_WIDTH : link_width); - sizer->Add(text); - sizer->AddSpacer(SPACING); - - auto *versions = new wxFlexGridSizer(2, 0, SPACING); - versions->Add(new wxStaticText(this, wxID_ANY, _(L("Current version:")))); - versions->Add(new wxStaticText(this, wxID_ANY, ver_current.to_string())); - versions->Add(new wxStaticText(this, wxID_ANY, _(L("New version:")))); - versions->Add(new wxStaticText(this, wxID_ANY, ver_online.to_string())); - sizer->Add(versions); - sizer->AddSpacer(SPACING); - - sizer->Add(link); - sizer->AddSpacer(2*SPACING); - - cbox = new wxCheckBox(this, wxID_ANY, _(L("Don't notify about new releases any more"))); - sizer->Add(cbox); - sizer->AddSpacer(SPACING); - - auto *ok = new wxButton(this, wxID_OK); - ok->SetFocus(); - sizer->Add(ok, 0, wxALIGN_CENTRE_HORIZONTAL); - - auto *logo = new wxStaticBitmap(this, wxID_ANY, wxBitmap(GUI::from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG)); - - topsizer->Add(logo, 0, wxALL, BORDER); - topsizer->Add(sizer, 0, wxALL, BORDER); - - SetSizerAndFit(topsizer); - } - - bool disable_version_check() const { return cbox->GetValue(); } -}; - struct Update { fs::path source; fs::path target; Version version; - Update(fs::path &&source, const fs::path &target, const Version &version) : - source(source), + Update(fs::path &&source, fs::path &&target, const Version &version) : + source(std::move(source)), target(std::move(target)), version(version) {} - Update(fs::path &&source, fs::path &&target) : - source(source), - target(std::move(target)) - {} + std::string name() const { return source.stem().string(); } - std::string name() { return source.stem().string(); } - - friend std::ostream& operator<<(std::ostream& os , const Update &update) { - os << "Update(" << update.source.string() << " -> " << update.target.string() << ')'; + friend std::ostream& operator<<(std::ostream& os , const Update &self) { + os << "Update(" << self.source.string() << " -> " << self.target.string() << ')'; return os; } }; -typedef std::vector Updates; +struct Incompat +{ + fs::path bundle; + Version version; + + Incompat(fs::path &&bundle, const Version &version) : + bundle(std::move(bundle)), + version(version) + {} + + std::string name() const { return bundle.stem().string(); } + + friend std::ostream& operator<<(std::ostream& os , const Incompat &self) { + os << "Incompat(" << self.bundle.string() << ')'; + return os; + } +}; + +struct Updates +{ + std::vector incompats; + std::vector updates; +}; struct PresetUpdater::priv @@ -206,7 +160,7 @@ bool PresetUpdater::priv::get_file(const std::string &url, const fs::path &targe BOOST_LOG_TRIVIAL(error) << boost::format("Error getting: `%1%`: HTTP %2%, %3%") % url % http_status - % body; + % error; }) .on_complete([&](std::string body, unsigned http_status) { fs::fstream file(tmp_path, std::ios::out | std::ios::binary | std::ios::trunc); @@ -248,7 +202,7 @@ void PresetUpdater::priv::sync_version() const BOOST_LOG_TRIVIAL(error) << boost::format("Error getting: `%1%`: HTTP %2%, %3%") % version_check_url % http_status - % body; + % error; }) .on_complete([&](std::string body, unsigned http_status) { boost::trim(body); @@ -354,7 +308,7 @@ Updates PresetUpdater::priv::get_config_updates() const BOOST_LOG_TRIVIAL(info) << "Checking for cached configuration updates..."; for (const auto idx : index_db) { - const auto bundle_path = vendor_path / (idx.vendor() + ".ini"); + auto bundle_path = vendor_path / (idx.vendor() + ".ini"); if (! fs::exists(bundle_path)) { BOOST_LOG_TRIVIAL(info) << "Bundle not present for index, skipping: " << idx.vendor(); @@ -377,14 +331,12 @@ Updates PresetUpdater::priv::get_config_updates() const BOOST_LOG_TRIVIAL(debug) << boost::format("Vendor: %1%, version installed: %2%, version cached: %3%") % vp.name - % recommended->config_version.to_string() - % ver_current->config_version.to_string(); + % ver_current->config_version.to_string() + % recommended->config_version.to_string(); if (! ver_current->is_current_slic3r_supported()) { BOOST_LOG_TRIVIAL(warning) << "Current Slic3r incompatible with installed bundle: " << bundle_path.string(); - - // TODO: Downgrade situation - + updates.incompats.emplace_back(std::move(bundle_path), *ver_current); } else if (recommended->config_version > ver_current->config_version) { // Config bundle update situation @@ -406,7 +358,7 @@ Updates PresetUpdater::priv::get_config_updates() const const auto cached_vp = VendorProfile::from_ini(path_in_cache, false); if (cached_vp.config_version == recommended->config_version) { - updates.emplace_back(std::move(path_in_cache), bundle_path, *recommended); + updates.updates.emplace_back(std::move(path_in_cache), std::move(bundle_path), *recommended); } } } @@ -416,28 +368,43 @@ Updates PresetUpdater::priv::get_config_updates() const void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) const { - BOOST_LOG_TRIVIAL(info) << boost::format("Performing %1% updates") % updates.size(); + if (updates.incompats.size() > 0) { + if (snapshot) { + BOOST_LOG_TRIVIAL(info) << "Taking a snapshot..."; + SnapshotDB::singleton().take_snapshot(*GUI::get_app_config(), Snapshot::SNAPSHOT_DOWNGRADE); + } - if (snapshot) { - BOOST_LOG_TRIVIAL(info) << "Taking a snapshot..."; - SnapshotDB::singleton().take_snapshot(*GUI::get_app_config(), Snapshot::SNAPSHOT_UPGRADE); + BOOST_LOG_TRIVIAL(info) << boost::format("Deleting %1% incompatible bundles") % updates.incompats.size(); + + for (const auto &incompat : updates.incompats) { + BOOST_LOG_TRIVIAL(info) << '\t' << incompat; + fs::remove(incompat.bundle); + } } + else if (updates.updates.size() > 0) { + if (snapshot) { + BOOST_LOG_TRIVIAL(info) << "Taking a snapshot..."; + SnapshotDB::singleton().take_snapshot(*GUI::get_app_config(), Snapshot::SNAPSHOT_UPGRADE); + } - for (const auto &update : updates) { - BOOST_LOG_TRIVIAL(info) << '\t' << update; + BOOST_LOG_TRIVIAL(info) << boost::format("Performing %1% updates") % updates.updates.size(); - fs::copy_file(update.source, update.target, fs::copy_option::overwrite_if_exists); + for (const auto &update : updates.updates) { + BOOST_LOG_TRIVIAL(info) << '\t' << update; - PresetBundle bundle; - bundle.load_configbundle(update.target.string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM); + fs::copy_file(update.source, update.target, fs::copy_option::overwrite_if_exists); - auto preset_remover = [](const Preset &preset) { - fs::remove(preset.file); - }; + PresetBundle bundle; + bundle.load_configbundle(update.target.string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM); - for (const auto &preset : bundle.prints) { preset_remover(preset); } - for (const auto &preset : bundle.filaments) { preset_remover(preset); } - for (const auto &preset : bundle.printers) { preset_remover(preset); } + auto preset_remover = [](const Preset &preset) { + fs::remove(preset.file); + }; + + for (const auto &preset : bundle.prints) { preset_remover(preset); } + for (const auto &preset : bundle.filaments) { preset_remover(preset); } + for (const auto &preset : bundle.printers) { preset_remover(preset); } + } } } @@ -497,7 +464,7 @@ void PresetUpdater::slic3r_update_notify() if (ver_online) { // Only display the notification if the version available online is newer AND if we haven't seen it before if (*ver_online > *ver_slic3r && (! ver_online_seen || *ver_online_seen < *ver_online)) { - UpdateNotification notification(*ver_slic3r, *ver_online); + GUI::MsgUpdateSlic3r notification(*ver_slic3r, *ver_online); notification.ShowModal(); if (notification.disable_version_check()) { app_config->set("version_check", "0"); @@ -508,32 +475,55 @@ void PresetUpdater::slic3r_update_notify() } } -void PresetUpdater::config_update() const +bool PresetUpdater::config_update() const { - if (! p->enabled_config_update) { return; } + if (! p->enabled_config_update) { return true; } auto updates = p->get_config_updates(); - if (updates.size() > 0) { - BOOST_LOG_TRIVIAL(info) << boost::format("Update of %1% bundles available. Asking for confirmation ...") % updates.size(); + if (updates.incompats.size() > 0) { + BOOST_LOG_TRIVIAL(info) << boost::format("%1% bundles incompatible. Asking for action...") % updates.incompats.size(); - const auto msg = _(L("Configuration update is available. Would you like to install it?")); - - auto ext_msg = _(L( - "Note that a full configuration snapshot will be created first. It can then be restored at any time " - "should there be a problem with the new version.\n\n" - "Updated configuration bundles:\n" - )); - - for (const auto &update : updates) { - ext_msg += update.target.stem().string() + " " + update.version.config_version.to_string(); - if (! update.version.comment.empty()) { - ext_msg += std::string(" (") + update.version.comment + ")"; - } - ext_msg += "\n"; + std::unordered_map incompats_map; + for (const auto &incompat : updates.incompats) { + auto vendor = incompat.name(); + auto restrictions = wxString::Format(_(L("requires min. %s and max. %s")), + incompat.version.min_slic3r_version.to_string(), + incompat.version.max_slic3r_version.to_string() + ); + incompats_map.emplace(std::move(vendor), std::move(restrictions)); } - wxMessageDialog dlg(NULL, msg, _(L("Configuration update")), wxYES_NO|wxCENTRE); - dlg.SetExtendedMessage(ext_msg); + GUI::MsgDataIncompatible dlg(std::move(incompats_map)); + const auto res = dlg.ShowModal(); + if (res == wxID_REPLACE) { + BOOST_LOG_TRIVIAL(info) << "User wants to re-configure..."; + p->perform_updates(std::move(updates)); + GUI::ConfigWizard wizard(nullptr, GUI::ConfigWizard::RR_DATA_INCOMPAT); + if (wizard.run(GUI::get_preset_bundle(), this)) { + p->had_config_update = true; + } else { + return false; + } + } else { + BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye..."; + return false; + } + } + else if (updates.updates.size() > 0) { + BOOST_LOG_TRIVIAL(info) << boost::format("Update of %1% bundles available. Asking for confirmation ...") % updates.updates.size(); + + std::unordered_map updates_map; + for (const auto &update : updates.updates) { + auto vendor = update.name(); + auto ver_str = update.version.config_version.to_string(); + if (! update.version.comment.empty()) { + ver_str += std::string(" (") + update.version.comment + ")"; + } + updates_map.emplace(std::move(vendor), std::move(ver_str)); + } + + GUI::MsgUpdateConfig dlg(std::move(updates_map)); + const auto res = dlg.ShowModal(); if (res == wxID_YES) { BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update"; @@ -546,9 +536,11 @@ void PresetUpdater::config_update() const } else { BOOST_LOG_TRIVIAL(info) << "No configuration updates available."; } + + return true; } -void PresetUpdater::install_bundles_rsrc(std::vector bundles, bool snapshot) +void PresetUpdater::install_bundles_rsrc(std::vector bundles, bool snapshot) const { Updates updates; @@ -557,7 +549,7 @@ void PresetUpdater::install_bundles_rsrc(std::vector bundles, bool for (const auto &bundle : bundles) { auto path_in_rsrc = p->rsrc_path / bundle; auto path_in_vendors = p->vendor_path / bundle; - updates.emplace_back(std::move(path_in_rsrc), std::move(path_in_vendors)); + updates.updates.emplace_back(std::move(path_in_rsrc), std::move(path_in_vendors), Version()); } p->perform_updates(std::move(updates), snapshot); diff --git a/xs/src/slic3r/Utils/PresetUpdater.hpp b/xs/src/slic3r/Utils/PresetUpdater.hpp index 287f20652..6a53cca81 100644 --- a/xs/src/slic3r/Utils/PresetUpdater.hpp +++ b/xs/src/slic3r/Utils/PresetUpdater.hpp @@ -27,10 +27,11 @@ public: void slic3r_update_notify(); // If updating is enabled, check if updates are available in cache, if so, ask about installation. - void config_update() const; + // A false return value implies Slic3r should exit due to incompatibility of configuration. + bool config_update() const; // "Update" a list of bundles from resources (behaves like an online update). - void install_bundles_rsrc(std::vector bundles, bool snapshot = true); + void install_bundles_rsrc(std::vector bundles, bool snapshot = true) const; private: struct priv; std::unique_ptr p; diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index 0d9f0b62e..ca90c54f2 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -60,10 +60,10 @@ void set_app_config(AppConfig *app_config) bool check_unsaved_changes() %code%{ RETVAL=Slic3r::GUI::check_unsaved_changes(); %}; -void config_wizard_startup(int app_config_exists) +bool config_wizard_startup(int app_config_exists) %code%{ try { - Slic3r::GUI::config_wizard_startup(app_config_exists != 0); + RETVAL=Slic3r::GUI::config_wizard_startup(app_config_exists != 0); } catch (std::exception& e) { croak("%s\n", e.what()); } diff --git a/xs/xsp/Utils_PresetUpdater.xsp b/xs/xsp/Utils_PresetUpdater.xsp index 53c3aa985..dc874acab 100644 --- a/xs/xsp/Utils_PresetUpdater.xsp +++ b/xs/xsp/Utils_PresetUpdater.xsp @@ -9,5 +9,5 @@ PresetUpdater(int version_online_event); void sync(PresetBundle* preset_bundle); void slic3r_update_notify(); - void config_update(); + bool config_update(); };