From 65b9ef6636dbee66aeced9b4d0c7f6288e532bfd Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 4 Feb 2020 15:24:35 +0100 Subject: configuration updater forced update dialog check for updates button check address when downloading bundles --- src/slic3r/Config/Version.cpp | 4 ++ src/slic3r/Config/Version.hpp | 1 + src/slic3r/GUI/GUI_App.cpp | 66 +++++++++++++++++----------- src/slic3r/GUI/GUI_App.hpp | 1 + src/slic3r/GUI/Plater.cpp | 7 +++ src/slic3r/GUI/Plater.hpp | 2 + src/slic3r/GUI/UpdateDialogs.cpp | 90 +++++++++++++++++++++++++++++++++++++- src/slic3r/GUI/UpdateDialogs.hpp | 38 ++++++++++++++++ src/slic3r/Utils/PresetUpdater.cpp | 87 ++++++++++++++++++++++++++++++------ 9 files changed, 257 insertions(+), 39 deletions(-) diff --git a/src/slic3r/Config/Version.cpp b/src/slic3r/Config/Version.cpp index 2104a6eea..2d036e9f3 100644 --- a/src/slic3r/Config/Version.cpp +++ b/src/slic3r/Config/Version.cpp @@ -66,6 +66,10 @@ bool Version::is_current_slic3r_supported() const return this->is_slic3r_supported(Slic3r::SEMVER); } +bool Version::is_current_slic3r_downgrade() const +{ + return Slic3r::SEMVER < min_slic3r_version; +} #if 0 //TODO: This test should be moved to a unit test, once we have C++ unit tests in place. static int version_test() diff --git a/src/slic3r/Config/Version.hpp b/src/slic3r/Config/Version.hpp index e5f1a2e21..881b856ff 100644 --- a/src/slic3r/Config/Version.hpp +++ b/src/slic3r/Config/Version.hpp @@ -29,6 +29,7 @@ struct Version bool is_slic3r_supported(const Semver &slicer_version) const; bool is_current_slic3r_supported() const; + bool is_current_slic3r_downgrade() const; }; // Index of vendor specific config bundle versions and Slic3r compatibilities. diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index c6f5cfb47..3a097d9ab 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -278,34 +278,23 @@ bool GUI_App::on_init_inner() RemovableDriveManager::get_instance().update(wxGetLocalTime(), true); #endif - // Preset updating & Configwizard are done after the above initializations, - // and after MainFrame is created & shown. - // The extra CallAfter() is needed because of Mac, where this is the only way - // to popup a modal dialog on start without screwing combo boxes. - // This is ugly but I honestly found no better way to do it. - // Neither wxShowEvent nor wxWindowCreateEvent work reliably. + // Preset updating & Configwizard are done after the above initializations, + // and after MainFrame is created & shown. + // The extra CallAfter() is needed because of Mac, where this is the only way + // to popup a modal dialog on start without screwing combo boxes. + // This is ugly but I honestly found no better way to do it. + // Neither wxShowEvent nor wxWindowCreateEvent work reliably. + static bool once = true; if (once) { once = false; + check_updates(false); - PresetUpdater::UpdateResult updater_result; - try { - updater_result = preset_updater->config_update(app_config->orig_version()); - if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) { - mainframe->Close(); - } else if (updater_result == PresetUpdater::R_INCOMPAT_CONFIGURED) { - app_conf_exists = true; - } - } catch (const std::exception &ex) { - show_error(nullptr, from_u8(ex.what())); - } - - CallAfter([this] { - config_wizard_startup(); - preset_updater->slic3r_update_notify(); - preset_updater->sync(preset_bundle); - }); - + CallAfter([this] { + config_wizard_startup(); + preset_updater->slic3r_update_notify(); + preset_updater->sync(preset_bundle); + }); } }); @@ -810,7 +799,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu) local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip); local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("&Configuration Snapshots")) + dots, _(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->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")) + dots + #ifdef __APPLE__ @@ -841,6 +830,9 @@ void GUI_App::add_config_menu(wxMenuBar *menu) case ConfigMenuWizard: run_wizard(ConfigWizard::RR_USER); break; + case ConfigMenuUpdate: + check_updates(true); + break; case ConfigMenuTakeSnapshot: // Take a configuration snapshot. if (check_unsaved_changes()) { @@ -1230,6 +1222,30 @@ bool GUI_App::config_wizard_startup() return false; } +void GUI_App::check_updates(const bool verbose) +{ + + PresetUpdater::UpdateResult updater_result; + try { + updater_result = preset_updater->config_update(app_config->orig_version()); + if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) { + mainframe->Close(); + } + else if (updater_result == PresetUpdater::R_INCOMPAT_CONFIGURED) { + app_conf_exists = true; + } + else if(verbose && updater_result == PresetUpdater::R_NOOP) + { + MsgNoUpdates dlg; + dlg.ShowModal(); + } + } + catch (const std::exception & ex) { + show_error(nullptr, from_u8(ex.what())); + } + + +} // static method accepting a wxWindow object as first parameter // void warning_catcher{ // my($self, $message_dialog) = @_; diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 250d8122a..10b09b1da 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -200,6 +200,7 @@ private: bool select_language(); bool config_wizard_startup(); + void check_updates(const bool verbose); #ifdef __WXMSW__ void associate_3mf_files(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7f615b7f6..1b69decfc 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5375,6 +5375,13 @@ void Plater::on_config_change(const DynamicPrintConfig &config) this->p->schedule_background_process(); } +void Plater::set_bed_shape() const +{ + p->set_bed_shape(p->config->option("bed_shape")->values, + p->config->option("bed_custom_texture")->value, + p->config->option("bed_custom_model")->value); +} + void Plater::force_filament_colors_update() { bool update_scheduled = false; diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 568727abf..e9d71b299 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -284,6 +284,8 @@ public: const Mouse3DController& get_mouse3d_controller() const; Mouse3DController& get_mouse3d_controller(); + void set_bed_shape() const; + // ROII wrapper for suppressing the Undo / Redo snapshot to be taken. class SuppressSnapshots { diff --git a/src/slic3r/GUI/UpdateDialogs.cpp b/src/slic3r/GUI/UpdateDialogs.cpp index d5c69be0b..b152ed135 100644 --- a/src/slic3r/GUI/UpdateDialogs.cpp +++ b/src/slic3r/GUI/UpdateDialogs.cpp @@ -142,6 +142,71 @@ MsgUpdateConfig::MsgUpdateConfig(const std::vector &updates) : MsgUpdateConfig::~MsgUpdateConfig() {} +//MsgUpdateForced + +MsgUpdateForced::MsgUpdateForced(const std::vector& updates) : + MsgDialog(nullptr, wxString::Format(_(L("%s incompatibility")), SLIC3R_APP_NAME), _(L("Configuration update is necessary to install")), wxID_NONE) +{ + auto* text = new wxStaticText(this, wxID_ANY, wxString::Format(_(L( + "%s will now start updates. Otherwise it won't be able to start.\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:" + )), SLIC3R_APP_NAME)); + + logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 192)); + + text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); + content_sizer->Add(text); + content_sizer->AddSpacer(VERT_SPACING); + + const auto lang_code = wxGetApp().current_language_code_safe().ToStdString(); + + auto* versions = new wxBoxSizer(wxVERTICAL); + for (const auto& update : updates) { + auto* flex = new wxFlexGridSizer(2, 0, VERT_SPACING); + + auto* text_vendor = new wxStaticText(this, wxID_ANY, update.vendor); + text_vendor->SetFont(boldfont); + flex->Add(text_vendor); + flex->Add(new wxStaticText(this, wxID_ANY, update.version.to_string())); + + if (!update.comment.empty()) { + flex->Add(new wxStaticText(this, wxID_ANY, _(L("Comment:"))), 0, wxALIGN_RIGHT); + auto* update_comment = new wxStaticText(this, wxID_ANY, from_u8(update.comment)); + update_comment->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); + flex->Add(update_comment); + } + + versions->Add(flex); + + if (!update.changelog_url.empty() && update.version.prerelease() == nullptr) { + auto* line = new wxBoxSizer(wxHORIZONTAL); + auto changelog_url = (boost::format(update.changelog_url) % lang_code).str(); + line->AddSpacer(3 * VERT_SPACING); + line->Add(new wxHyperlinkCtrl(this, wxID_ANY, _(L("Open changelog page")), changelog_url)); + versions->Add(line); + } + } + + content_sizer->Add(versions); + content_sizer->AddSpacer(2 * VERT_SPACING); + + auto* btn_exit = new wxButton(this, wxID_EXIT, wxString::Format(_(L("Exit %s")), SLIC3R_APP_NAME)); + btn_sizer->Add(btn_exit); + btn_sizer->AddSpacer(HORIZ_SPACING); + auto* btn_ok = new wxButton(this, wxID_OK); + btn_sizer->Add(btn_ok); + btn_ok->SetFocus(); + + auto exiter = [this](const wxCommandEvent& evt) { this->EndModal(evt.GetId()); }; + btn_exit->Bind(wxEVT_BUTTON, exiter); + btn_ok->Bind(wxEVT_BUTTON, exiter); + + Fit(); +} + +MsgUpdateForced::~MsgUpdateForced() {} // MsgDataIncompatible @@ -157,7 +222,7 @@ MsgDataIncompatible::MsgDataIncompatible(const std::unordered_mapWrap(CONTENT_WIDTH * wxGetApp().em_unit()); content_sizer->Add(text); @@ -236,5 +301,28 @@ MsgDataLegacy::MsgDataLegacy() : MsgDataLegacy::~MsgDataLegacy() {} +// MsgNoUpdate + +MsgNoUpdates::MsgNoUpdates() : + MsgDialog(nullptr, _(L("Configuration updates")), _(L("No updates aviable"))) +{ + + auto* text = new wxStaticText(this, wxID_ANY, wxString::Format( + _(L( + "%s has no configuration updates aviable." + )), + SLIC3R_APP_NAME, ConfigWizard::name() + )); + text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); + content_sizer->Add(text); + content_sizer->AddSpacer(VERT_SPACING); + + logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 192)); + + Fit(); +} + +MsgNoUpdates::~MsgNoUpdates() {} + } } diff --git a/src/slic3r/GUI/UpdateDialogs.hpp b/src/slic3r/GUI/UpdateDialogs.hpp index a916e0145..6d355065a 100644 --- a/src/slic3r/GUI/UpdateDialogs.hpp +++ b/src/slic3r/GUI/UpdateDialogs.hpp @@ -62,6 +62,33 @@ public: ~MsgUpdateConfig(); }; +// Informs about currently installed bundles not being compatible with the running Slic3r. Asks about action. +class MsgUpdateForced : public MsgDialog +{ +public: + struct Update + { + std::string vendor; + Semver version; + std::string comment; + std::string changelog_url; + + Update(std::string vendor, Semver version, std::string comment, std::string changelog_url) + : vendor(std::move(vendor)) + , version(std::move(version)) + , comment(std::move(comment)) + , changelog_url(std::move(changelog_url)) + {} + }; + + MsgUpdateForced(const std::vector& updates); + MsgUpdateForced(MsgUpdateForced&&) = delete; + MsgUpdateForced(const MsgUpdateForced&) = delete; + MsgUpdateForced& operator=(MsgUpdateForced&&) = delete; + MsgUpdateForced& operator=(const MsgUpdateForced&) = delete; + ~MsgUpdateForced(); +}; + // Informs about currently installed bundles not being compatible with the running Slic3r. Asks about action. class MsgDataIncompatible : public MsgDialog { @@ -87,6 +114,17 @@ public: ~MsgDataLegacy(); }; +// Informs about absence of bundles requiring update. +class MsgNoUpdates : public MsgDialog +{ +public: + MsgNoUpdates(); + MsgNoUpdates(MsgNoUpdates&&) = delete; + MsgNoUpdates(const MsgNoUpdates&) = delete; + MsgNoUpdates& operator=(MsgNoUpdates&&) = delete; + MsgNoUpdates& operator=(const MsgNoUpdates&) = delete; + ~MsgNoUpdates(); +}; } } diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 55e3a5a73..ecdc12b50 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -73,13 +73,16 @@ struct Update std::string vendor; std::string changelog_url; + bool forced_update; + Update() {} - Update(fs::path &&source, fs::path &&target, const Version &version, std::string vendor, std::string changelog_url) + Update(fs::path &&source, fs::path &&target, const Version &version, std::string vendor, std::string changelog_url, bool forced = false) : source(std::move(source)) , target(std::move(target)) , version(version) , vendor(std::move(vendor)) , changelog_url(std::move(changelog_url)) + , forced_update(forced) {} void install() const @@ -297,6 +300,12 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors) const auto idx_url = vendor.config_update_url + "/" + INDEX_FILENAME; const std::string idx_path = (cache_path / (vendor.id + ".idx")).string(); const std::string idx_path_temp = idx_path + "-update"; + //check if idx_url is leading to our site + if(idx_url.substr(0, 54) != "http://files.prusa3d.com/wp-content/uploads/repository") + { + BOOST_LOG_TRIVIAL(warning) << "unsafe url path for vendor: " << vendor.name; + continue; + } if (!get_file(idx_url, idx_path_temp)) { continue; } if (cancel) { return; } @@ -418,11 +427,16 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version continue; } - if (ver_current_found && !ver_current->is_current_slic3r_supported()) { - // "Reconfigure" situation. - BOOST_LOG_TRIVIAL(warning) << "Current Slic3r incompatible with installed bundle: " << bundle_path.string(); - updates.incompats.emplace_back(std::move(bundle_path), *ver_current, vp.name); - continue; + bool current_not_supported = false; //if slcr is incompatible but situation is not downgrade, we do forced updated and this bool is information to do it + + if (ver_current_found && !ver_current->is_current_slic3r_supported()){ + if(ver_current->is_current_slic3r_downgrade()) { + // "Reconfigure" situation. + BOOST_LOG_TRIVIAL(warning) << "Current Slic3r incompatible with installed bundle: " << bundle_path.string(); + updates.incompats.emplace_back(std::move(bundle_path), *ver_current, vp.name); + continue; + } + current_not_supported = true; } if (recommended->config_version < vp.config_version) { @@ -462,7 +476,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version if (new_vp.config_version == recommended->config_version) { // The config bundle from the cache directory matches the recommended version of the index from the cache directory. // This is the newest known recommended config. Use it. - new_update = Update(std::move(path_in_cache), std::move(bundle_path), *recommended, vp.name, vp.changelog_url); + new_update = Update(std::move(path_in_cache), std::move(bundle_path), *recommended, vp.name, vp.changelog_url, current_not_supported); // and install the config index from the cache into vendor's directory. bundle_path_idx_to_install = idx.path(); found = true; @@ -492,7 +506,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version } recommended = rsrc_idx.recommended(); if (recommended != rsrc_idx.end() && recommended->config_version == rsrc_vp.config_version && recommended->config_version > vp.config_version) { - new_update = Update(std::move(path_in_rsrc), std::move(bundle_path), *recommended, vp.name, vp.changelog_url); + new_update = Update(std::move(path_in_rsrc), std::move(bundle_path), *recommended, vp.name, vp.changelog_url, current_not_supported); bundle_path_idx_to_install = path_idx_in_rsrc; found = true; } else { @@ -513,11 +527,11 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version // Find a recommended config bundle version for the slic3r version last executed. This makes sure that a config bundle update will not be missed // when upgrading an application. On the other side, the user will be bugged every time he will switch between slic3r versions. const auto existing_recommended = existing_idx.recommended(old_slic3r_version); - if (existing_recommended != existing_idx.end() && recommended->config_version == existing_recommended->config_version) { + /*if (existing_recommended != existing_idx.end() && recommended->config_version == existing_recommended->config_version) { // The user has already seen (and presumably rejected) this update BOOST_LOG_TRIVIAL(info) << boost::format("Downloaded index for `%1%` is the same as installed one, not offering an update.") % idx.vendor(); continue; - } + }*/ } catch (const std::exception &err) { BOOST_LOG_TRIVIAL(error) << boost::format("Cannot load the installed index at `%1%`: %2%") % bundle_path_idx % err.what(); } @@ -554,14 +568,17 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons BOOST_LOG_TRIVIAL(info) << "Taking a snapshot..."; SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_DOWNGRADE); } - + BOOST_LOG_TRIVIAL(info) << boost::format("Deleting %1% incompatible bundles") % updates.incompats.size(); for (auto &incompat : updates.incompats) { BOOST_LOG_TRIVIAL(info) << '\t' << incompat; incompat.remove(); } + + } else if (updates.updates.size() > 0) { + if (snapshot) { BOOST_LOG_TRIVIAL(info) << "Taking a snapshot..."; SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_UPGRADE); @@ -688,6 +705,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3 ); } else if (min_slic3r != Semver::zero()) { restrictions = wxString::Format(_(L("requires min. %s")), min_slic3r.to_string()); + BOOST_LOG_TRIVIAL(debug) << "Bundle is not downgrade, user will now have to do whole wizard. This should not happen."; } else { restrictions = wxString::Format(_(L("requires max. %s")), max_slic3r.to_string()); } @@ -704,16 +722,59 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3 // (snapshot is taken beforehand) p->perform_updates(std::move(updates)); - if (! GUI::wxGetApp().run_wizard(GUI::ConfigWizard::RR_DATA_INCOMPAT)) { + if (!GUI::wxGetApp().run_wizard(GUI::ConfigWizard::RR_DATA_INCOMPAT)) { return R_INCOMPAT_EXIT; } return R_INCOMPAT_CONFIGURED; - } else { + } + else { BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye..."; return R_INCOMPAT_EXIT; } + } else if (updates.updates.size() > 0) { + + bool incompatible_version = false; + for (const auto& update : updates.updates) { + incompatible_version = (update.forced_update ? true : incompatible_version); + //td::cout << update.forced_update << std::endl; + //BOOST_LOG_TRIVIAL(info) << boost::format("Update requires higher version."); + } + + //forced update + if(incompatible_version) + { + BOOST_LOG_TRIVIAL(info) << boost::format("Update of %1% bundles available. At least one requires higher version of Slicer.") % updates.updates.size(); + + std::vector updates_msg; + for (const auto& update : updates.updates) { + std::string changelog_url = update.version.config_version.prerelease() == nullptr ? update.changelog_url : std::string(); + updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url)); + } + + GUI::MsgUpdateForced dlg(updates_msg); + + const auto res = dlg.ShowModal(); + if (res == wxID_OK) { + BOOST_LOG_TRIVIAL(info) << "User wants to update..."; + + p->perform_updates(std::move(updates)); + + // Reload global configuration + auto* app_config = GUI::wxGetApp().app_config; + GUI::wxGetApp().preset_bundle->load_presets(*app_config); + GUI::wxGetApp().load_current_presets(); + GUI::wxGetApp().plater()->set_bed_shape(); + return R_UPDATE_INSTALLED; + } + else { + BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye..."; + return R_INCOMPAT_EXIT; + } + } + + // regular update BOOST_LOG_TRIVIAL(info) << boost::format("Update of %1% bundles available. Asking for confirmation ...") % updates.updates.size(); std::vector updates_msg; -- cgit v1.2.3