diff --git a/resources/localization/list.txt b/resources/localization/list.txt index 3efd729736..4d66c35f6c 100644 --- a/resources/localization/list.txt +++ b/resources/localization/list.txt @@ -139,3 +139,4 @@ src/slic3r/Utils/PresetUpdater.cpp src/slic3r/Utils/Process.cpp src/slic3r/Utils/PrusaConnect.cpp src/slic3r/Utils/Repetier.cpp +src/slic3r/Utils/PresetUpdaterWrapper.cpp diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index 5b430442f4..5bdf954a92 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -127,7 +127,6 @@ public: TYPE_PHYSICAL_PRINTER, // This type is here to support search through the Preferences TYPE_PREFERENCES, - TYPE_WEBVIEW, }; Type type = TYPE_INVALID; diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 78369233d1..608043efdf 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -356,6 +356,8 @@ set(SLIC3R_GUI_SOURCES Utils/Bonjour.hpp Utils/PresetUpdater.cpp Utils/PresetUpdater.hpp + Utils/PresetUpdaterWrapper.cpp + Utils/PresetUpdaterWrapper.hpp Utils/Process.cpp Utils/Process.hpp Utils/RaycastManager.cpp diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index acedf3b8ef..a052b6b4e3 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -62,12 +62,10 @@ #include "Field.hpp" #include "DesktopIntegrationDialog.hpp" #include "slic3r/Config/Snapshot.hpp" -#include "slic3r/Utils/PresetUpdater.hpp" #include "format.hpp" #include "MsgDialog.hpp" #include "UnsavedChangesDialog.hpp" #include "UpdatesUIManager.hpp" -#include "PresetArchiveDatabase.hpp" #include "Plater.hpp" // #ysFIXME - implement getter for preset_archive_database from GetApp()??? #include "slic3r/Utils/AppUpdater.hpp" #include "slic3r/GUI/I18N.hpp" @@ -86,30 +84,9 @@ namespace Slic3r { namespace GUI { - using Config::Snapshot; using Config::SnapshotDB; - - -ConfigWizardLoadingDialog::ConfigWizardLoadingDialog(wxWindow* parent, const wxString& message) - : wxDialog(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxFRAME_FLOAT_ON_PARENT) -{ - auto* text = new wxStaticText(this, wxID_ANY, message, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL); - auto* vsizer = new wxBoxSizer(wxVERTICAL); - auto *top_sizer = new wxBoxSizer(wxVERTICAL); - vsizer->Add(text, 1, wxEXPAND); - top_sizer->Add(vsizer, 1, wxEXPAND | wxALL, 15); - SetSizer(top_sizer); - #ifdef _WIN32 - wxGetApp().UpdateDlgDarkUI(this); - #endif - Fit(); -} - - -// Configuration data structures extensions needed for the wizard - bool Bundle::load(fs::path source_path, BundleLocation location, bool ais_prusa_bundle) { this->preset_bundle = std::make_unique(); @@ -138,6 +115,7 @@ bool Bundle::load(fs::path source_path, BundleLocation location, bool ais_prusa_ return true; } +// Configuration data structures extensions needed for the wizard Bundle::Bundle(Bundle &&other) : preset_bundle(std::move(other.preset_bundle)) , vendor_profile(other.vendor_profile) @@ -151,7 +129,7 @@ BundleMap BundleMap::load() { BundleMap res; - const PresetArchiveDatabase* pad = wxGetApp().plater()->get_preset_archive_database(); + const Slic3r::PresetUpdaterWrapper* preset_updater_wrapper = wxGetApp().get_preset_updater_wrapper(); const auto vendor_dir = (boost::filesystem::path(Slic3r::data_dir()) / "vendor").make_preferred(); const auto archive_dir = (boost::filesystem::path(Slic3r::data_dir()) / "cache" / "vendor").make_preferred(); const auto rsrc_vendor_dir = (boost::filesystem::path(resources_dir()) / "profiles").make_preferred(); @@ -228,7 +206,7 @@ BundleMap BundleMap::load() BOOST_LOG_TRIVIAL(error) << format("Could not load bundle %1% due to corrupted profile file %2%. Message: %3%", id, dir_entry.path().string(), e.what()); continue; } - if (vp.repo_id.empty() || !pad->is_selected_repository_by_id(vp.repo_id)) { + if (vp.repo_id.empty() || !preset_updater_wrapper->is_selected_repository_by_id(vp.repo_id)) { continue; } // Don't load @@ -655,7 +633,7 @@ PageUpdateManager::PageUpdateManager(ConfigWizard* parent_in) const int em = em_unit(this); - manager = std::make_unique(this, wxGetApp().plater()->get_preset_archive_database(), em); + manager = std::make_unique(this, wxGetApp().get_preset_updater_wrapper(), em); warning_text = new wxStaticText(this, wxID_ANY, _L("WARNING: Select at least one source.")); warning_text->SetFont(wxGetApp().bold_font()); @@ -3327,7 +3305,7 @@ static std::string get_first_added_preset(const std::map 0) { // Install bundles from resources or cache / vendor. // Don't create snapshot - we've already done that above if applicable. - GUI_App& app = wxGetApp(); - const auto* archive_db = app.plater()->get_preset_archive_database(); - bool install_result = updater->install_bundles_rsrc_or_cache_vendor(std::move(install_bundles), archive_db->get_selected_archive_repositories(), false); + bool install_result = updater->install_bundles_rsrc_or_cache_vendor(std::move(install_bundles), false); if (!install_result) return false; } else { @@ -3594,7 +3570,7 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese used_repo_ids.emplace_back(repo_id); } } - wxGetApp().plater()->get_preset_archive_database()->set_installed_printer_repositories(std::move(used_repo_ids)); + wxGetApp().get_preset_updater_wrapper()->set_installed_printer_repositories(std::move(used_repo_ids)); // apply materials in app_config for (const std::string& section_name : {AppConfig::SECTION_FILAMENTS, AppConfig::SECTION_MATERIALS}) @@ -3704,18 +3680,9 @@ bool ConfigWizard::priv::check_sla_selected() void ConfigWizard::priv::set_config_updated_from_archive(bool load_installed_printers, bool run_preset_updater) { - if (run_preset_updater) { - // This block of preset_updater functions is done in GUI_App::run_wizard before ConfigWizard::run() - // It needs to be also done when repos are confirmed inside wizard. - // Possible optimalization - do not run this block if no repos were changed. - GUI_App& app = wxGetApp(); - // Do blocking sync on every change of archive repos, so user is always offered recent profiles. - const SharedArchiveRepositoryVector &repos = app.plater()->get_preset_archive_database()->get_selected_archive_repositories(); - app.preset_updater->sync_blocking(app.preset_bundle, &app, repos); - // Offer update installation. It used to be offered only when wizard run reason was RR_USER. - app.preset_updater->update_index_db(); - app.preset_updater->config_update(app.app_config->orig_version(), PresetUpdater::UpdateParams::SHOW_TEXT_BOX, repos); - + if (run_preset_updater) { + // TRN: Progress dialog title + wxGetApp().get_preset_updater_wrapper()->wizard_sync(wxGetApp().preset_bundle, wxGetApp().app_config->orig_version(), q, true, _L("Updating Configuration sources")); // We have now probably changed data. We need to rebuild database from which wizards constructs. // Just reload bundles and upadte installed printer from appconfig_new. bundles = BundleMap::load(); @@ -3751,8 +3718,7 @@ bool ConfigWizard::priv::any_installed_vendor_for_repo(const std::string& repo_i static bool to_delete(PagePrinters* page, const std::set& selected_uuids) { - const PresetArchiveDatabase* pad = wxGetApp().plater()->get_preset_archive_database(); - const SharedArchiveRepositoryVector& archs = pad->get_all_archive_repositories(); + const SharedArchiveRepositoryVector& archs = wxGetApp().get_preset_updater_wrapper()->get_all_archive_repositories(); bool unselect_all = true; @@ -3768,14 +3734,14 @@ static bool to_delete(PagePrinters* page, const std::set& selected_ static void unselect(PagePrinters* page) { - const PresetArchiveDatabase* pad = wxGetApp().plater()->get_preset_archive_database(); - const SharedArchiveRepositoryVector& archs = pad->get_all_archive_repositories(); + const Slic3r::PresetUpdaterWrapper* puw = wxGetApp().get_preset_updater_wrapper(); + const SharedArchiveRepositoryVector& archs = puw->get_all_archive_repositories(); bool unselect_all = true; for (const auto* archive : archs) { if (page->get_vendor_repo_id() == archive->get_manifest().id) { - if (pad->is_selected_repository_by_uuid(archive->get_uuid())) + if (puw->is_selected_repository_by_uuid(archive->get_uuid())) unselect_all = false; //break; ! don't break here, because there can be several archives with same repo_id } @@ -3866,16 +3832,16 @@ void ConfigWizard::priv::load_pages_from_archive() // fill vendors and printers pages from Update manager - auto pad = wxGetApp().plater()->get_preset_archive_database(); + const auto* puw = wxGetApp().get_preset_updater_wrapper(); - const SharedArchiveRepositoryVector& archs = pad->get_all_archive_repositories(); + const SharedArchiveRepositoryVector& archs = puw->get_all_archive_repositories(); only_sla_mode = true; bool is_primary_printer_page_set = false; for (const auto* archive : archs) { const auto& data = archive->get_manifest(); - const bool is_selected_arch = pad->is_selected_repository_by_uuid(archive->get_uuid()); + const bool is_selected_arch = puw->is_selected_repository_by_uuid(archive->get_uuid()); std::vector vendors; const bool any_installed_vendor = any_installed_vendor_for_repo(data.id, vendors); @@ -4148,7 +4114,7 @@ bool ConfigWizard::run(RunReason reason, StartPage start_page) if (ShowModal() == wxID_OK) { bool apply_keeped_changes = false; - if (! p->apply_config(app.app_config, app.preset_bundle, app.preset_updater, apply_keeped_changes)) + if (! p->apply_config(app.app_config, app.preset_bundle, app.get_preset_updater_wrapper(), apply_keeped_changes)) return false; if (apply_keeped_changes) @@ -4169,7 +4135,8 @@ void ConfigWizard::update_login() { if (p->page_login && p->page_login->login_changed()) { // repos changed - we need rebuild - wxGetApp().plater()->get_preset_archive_database()->sync_blocking(); + // TRN: Progress dialog title + wxGetApp().get_preset_updater_wrapper()->wizard_sync(wxGetApp().preset_bundle, wxGetApp().app_config->orig_version(), this, false, _L("Updating Configuration sources")); // now change PageUpdateManager p->page_update_manager->manager->update(); } diff --git a/src/slic3r/GUI/ConfigWizard.hpp b/src/slic3r/GUI/ConfigWizard.hpp index cc9d36d806..f8d79b4bd4 100644 --- a/src/slic3r/GUI/ConfigWizard.hpp +++ b/src/slic3r/GUI/ConfigWizard.hpp @@ -22,15 +22,11 @@ namespace Slic3r { class PresetBundle; class PresetUpdater; +class AppConfig; +class PresetArchiveDatabase; namespace GUI { -class ConfigWizardLoadingDialog : public wxDialog -{ -public: - ConfigWizardLoadingDialog(wxWindow* parent, const wxString& message); -}; - namespace DownloaderUtils { class Worker : public wxBoxSizer { @@ -102,8 +98,6 @@ private: friend struct ConfigWizardPage; }; - - } } diff --git a/src/slic3r/GUI/ConfigWizardWebViewPage.cpp b/src/slic3r/GUI/ConfigWizardWebViewPage.cpp index c5fca80faf..7d1a915c66 100644 --- a/src/slic3r/GUI/ConfigWizardWebViewPage.cpp +++ b/src/slic3r/GUI/ConfigWizardWebViewPage.cpp @@ -132,9 +132,9 @@ void ConfigWizardWebViewPage::on_navigation_request(wxWebViewEvent &evt) evt.Veto(); m_vetoed = true; wxPostEvent(wxGetApp().plater(), Event(EVT_LOGIN_VIA_WIZARD, into_u8(url))); - } else if (url.Find("accounts.google.com") != wxString::npos - || url.Find("appleid.apple.com") != wxString::npos - || url.Find("facebook.com") != wxString::npos) + } else if (url.Find("accounts.google.com") != wxNOT_FOUND + || url.Find("appleid.apple.com") != wxNOT_FOUND + || url.Find("facebook.com") != wxNOT_FOUND) { auto& sc = Utils::ServiceConfig::instance(); if (!m_evt_sent && !url.starts_with(GUI::from_u8(sc.account_url()))) { diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index 507448adcd..a62ba7808d 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -26,7 +26,7 @@ #include "libslic3r/PrintConfig.hpp" #include "libslic3r/PresetBundle.hpp" -#include "slic3r/Utils/PresetUpdater.hpp" +#include "slic3r/Utils/PresetUpdaterWrapper.hpp" #include "BedShapeDialog.hpp" #include "GUI.hpp" #include "SavePresetDialog.hpp" @@ -702,7 +702,7 @@ struct ConfigWizard::priv bool can_select_all(); bool on_bnt_finish(); bool check_and_install_missing_materials(Technology technology, const std::string &only_for_model_id = std::string()); - bool apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater, bool& apply_keeped_changes); + bool apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdaterWrapper *updater, bool& apply_keeped_changes); // #ys_FIXME_alise void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add); //#ifdef __linux__ diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index dd39fed1a2..879fc090ba 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -75,6 +75,7 @@ #include "GLCanvas3D.hpp" #include "../Utils/PresetUpdater.hpp" +#include "../Utils/PresetUpdaterWrapper.hpp" #include "../Utils/PrintHost.hpp" #include "../Utils/Process.hpp" #include "../Utils/MacDarkMode.hpp" @@ -847,17 +848,11 @@ void GUI_App::post_init() // 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. - if (this->preset_updater) { // G-Code Viewer does not initialize preset_updater. - -#if 0 // This code was moved to EVT_CONFIG_UPDATER_SYNC_DONE bind - after preset_updater finishes synchronization. - if (! this->check_updates(false)) - // Configuration is not compatible and reconfigure was refused by the user. Application is closing. - return; -#endif + if (this->get_preset_updater_wrapper()) { // G-Code Viewer does not initialize preset_updater. CallAfter([this] { // preset_updater->sync downloads profile updates and than via event checks updates and incompatible presets. We need to run it on startup. // start before cw so it is canceled by cw if needed? - this->preset_updater->sync(preset_bundle, this, plater()->get_preset_archive_database()->get_selected_archive_repositories()); + this->get_preset_updater_wrapper()->sync_preset_updater(this, preset_bundle); bool cw_showed = this->config_wizard_startup(); if (! cw_showed) { // The CallAfter is needed as well, without it, GL extensions did not show. @@ -900,7 +895,6 @@ GUI_App::~GUI_App() { delete app_config; delete preset_bundle; - delete preset_updater; } // If formatted for github, plaintext with OpenGL extensions enclosed into
. @@ -1435,7 +1429,7 @@ bool GUI_App::on_init_inner() associate_stl_files(); #endif // __WXMSW__ - preset_updater = new PresetUpdater(); + m_preset_updater_wrapper = std::make_unique(); Bind(EVT_SLIC3R_VERSION_ONLINE, &GUI_App::on_version_read, this); Bind(EVT_SLIC3R_EXPERIMENTAL_VERSION_ONLINE, [this](const wxCommandEvent& evt) { if (this->plater_ != nullptr && (m_app_updater->get_triggered_by_user() || app_config->get("notify_release") == "all")) { @@ -3279,48 +3273,16 @@ bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage { wxCHECK_MSG(mainframe != nullptr, false, "Internal error: Main frame not created / null"); - // Cancel sync before starting wizard to prevent two downloads at same time. - preset_updater->cancel_sync(); - // Show login dialog before wizard. -#if 0 - bool user_was_logged = plater()->get_user_account()->is_logged(); - if (!user_was_logged) { - m_login_dialog = std::make_unique(mainframe, plater()->get_user_account()); - m_login_dialog->ShowModal(); - mainframe->RemoveChild(m_login_dialog.get()); - m_login_dialog->Destroy(); - // Destructor does not call Destroy. - m_login_dialog.reset(); - } -#endif // 0 - - // ConfigWizard can take some time to start. Because it is a wxWidgets window, it has to be done - // in UI thread, so displaying a nice modal dialog and letting the CW start in a worker thread - // is not an option. Let's at least show a modeless dialog before the UI thread freezes. - // TRN: Text showing while the ConfigWizard is loading, so the user knows something is happening. - auto cw_loading_dlg = new ConfigWizardLoadingDialog(mainframe, _L("Loading Configuration Wizard...")); - cw_loading_dlg->CenterOnParent(); - cw_loading_dlg->Show(); - wxYield(); - - // We have to update repos - plater()->get_preset_archive_database()->sync_blocking(); - - if (reason == ConfigWizard::RunReason::RR_USER) { - // Since there might be new repos, we need to sync preset updater - const SharedArchiveRepositoryVector &repos = plater()->get_preset_archive_database()->get_selected_archive_repositories(); - preset_updater->sync_blocking(preset_bundle, this, repos); - preset_updater->update_index_db(); - // Offer update installation. - preset_updater->config_update(app_config->orig_version(), PresetUpdater::UpdateParams::SHOW_TEXT_BOX, repos); - } - + // Loading of Config Wizard takes some time. + // First part is to download neccessary data. + // That is done on worker thread while nice modal progress is shown. + // TRN: Progress dialog title + get_preset_updater_wrapper()->wizard_sync(preset_bundle, app_config->orig_version(), mainframe, reason == ConfigWizard::RunReason::RR_USER, _L("Opening Configuration Wizard")); + // Then the wizard itself will start and that also takes time. + // But for now no ui is shown until then. (Showing modal progress dialog while showing another would be a headacke) m_config_wizard = new ConfigWizard(mainframe); - cw_loading_dlg->Close(); - const bool res = m_config_wizard->run(reason, start_page); - // !!! Deallocate memory after close ConfigWizard. // Note, that mainframe is a parent of ConfigWizard. // So, wizard will be destroyed only during destroying of mainframe @@ -3541,43 +3503,24 @@ bool GUI_App::config_wizard_startup() bool GUI_App::check_updates(const bool invoked_by_user) { - if (invoked_by_user) { - // do preset_updater sync so if user runs slicer for a long time, check for updates actually delivers updates. - // for preset_updater sync we need to sync archive database first - plater()->get_preset_archive_database()->sync_blocking(); - // Now re-extract offline repos - std::string failed_paths; - if (!plater()->get_preset_archive_database()->extract_archives_with_check(failed_paths)) { - int cnt = std::count(failed_paths.begin(), failed_paths.end(), '\n') + 1; - // TRN: %1% contains paths from which loading failed. They are separated by \n, there is no \n at the end. - failed_paths = GUI::format(_L_PLURAL("It was not possible to extract data from %1%. The source will not be updated.", - "It was not possible to extract data for following local sources. They will not be updated.\n\n %1%", cnt), failed_paths); - show_error(nullptr, failed_paths); - } - // then its time for preset_updater sync - preset_updater->sync_blocking(preset_bundle, this, plater()->get_preset_archive_database()->get_selected_archive_repositories()); - // and then we check updates + PresetUpdater::UpdateResult updater_result; + if (invoked_by_user) + { + updater_result = get_preset_updater_wrapper()->check_updates_on_user_request(preset_bundle, app_config->orig_version(), mainframe); + } else { + updater_result = get_preset_updater_wrapper()->check_updates_on_startup( app_config->orig_version()); } - - PresetUpdater::UpdateResult updater_result; - try { - preset_updater->update_index_db(); - updater_result = preset_updater->config_update(app_config->orig_version(), invoked_by_user ? PresetUpdater::UpdateParams::SHOW_TEXT_BOX : PresetUpdater::UpdateParams::SHOW_NOTIFICATION, plater()->get_preset_archive_database()->get_selected_archive_repositories()); - if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) { - mainframe->Close(); - // Applicaiton is closing. - return false; - } - else if (updater_result == PresetUpdater::R_INCOMPAT_CONFIGURED) { - m_app_conf_exists = true; - } - else if (invoked_by_user && updater_result == PresetUpdater::R_NOOP) { - MsgNoUpdates dlg; - dlg.ShowModal(); - } + if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) { + mainframe->Close(); + // Applicaiton is closing. + return false; } - catch (const std::exception & ex) { - show_error(nullptr, ex.what()); + else if (updater_result == PresetUpdater::R_INCOMPAT_CONFIGURED) { + m_app_conf_exists = true; + } + else if (invoked_by_user && updater_result == PresetUpdater::R_NOOP) { + MsgNoUpdates dlg; + dlg.ShowModal(); } // Applicaiton will continue. return true; @@ -4151,7 +4094,7 @@ void GUI_App::handle_connect_request_printer_select(const std::string& msg) } void GUI_App::handle_connect_request_printer_select_inner(const std::string & msg) { - BOOST_LOG_TRIVIAL(debug) << "Handling web request: " << msg; + BOOST_LOG_TRIVIAL(debug) << "Handling web request: " << msg; // return to plater this->mainframe->select_tab(size_t(0)); if (!select_printer_from_connect(msg)) { diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 56fc09c8ff..08ef7667ca 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -34,7 +34,7 @@ namespace Slic3r { class AppConfig; class PresetBundle; -class PresetUpdater; +class PresetUpdaterWrapper; class ModelObject; class PrintHostJobQueue; class Model; @@ -357,11 +357,9 @@ public: AppConfig* app_config{ nullptr }; PresetBundle* preset_bundle{ nullptr }; - PresetUpdater* preset_updater{ nullptr }; MainFrame* mainframe{ nullptr }; Plater* plater_{ nullptr }; - - PresetUpdater* get_preset_updater() { return preset_updater; } + PresetUpdaterWrapper* get_preset_updater_wrapper() { return m_preset_updater_wrapper.get(); } wxBookCtrlBase* tab_panel() const ; int extruders_cnt() const; @@ -468,7 +466,7 @@ private: std::map< ConfigMenuIDs, wxMenuItem*> m_config_menu_updatable_items; ConfigWizard* m_config_wizard {nullptr}; - + std::unique_ptr m_preset_updater_wrapper; }; DECLARE_APP(GUI_App) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index fa85a3c242..9438c6c22b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -111,6 +111,7 @@ #include "../Utils/PrintHost.hpp" #include "../Utils/UndoRedo.hpp" #include "../Utils/PresetUpdater.hpp" +#include "../Utils/PresetUpdaterWrapper.hpp" #include "../Utils/Process.hpp" #include "RemovableDriveManager.hpp" #include "InstanceCheck.hpp" @@ -272,7 +273,6 @@ struct Plater::priv Preview *preview; std::unique_ptr notification_manager; std::unique_ptr user_account; - std::unique_ptr preset_archive_database; // Login dialog needs to be kept somewhere. // It is created inside evt Bind. But it might be closed from another event. LoginWebViewDialog* login_dialog { nullptr }; @@ -625,7 +625,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) , sidebar(new Sidebar(q)) , notification_manager(std::make_unique(q)) , user_account(std::make_unique(q, wxGetApp().app_config, wxGetApp().get_instance_hash_string())) - , preset_archive_database(std::make_unique(wxGetApp().app_config, q)) , m_worker{q, std::make_unique(notification_manager.get()), "ui_worker"} , m_sla_import_dlg{new SLAImportDialog{q}} , delayed_scene_refresh(false) @@ -791,7 +790,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) this->q->Bind(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, [this](ExportGcodeNotificationClickedEvent&) { this->q->export_gcode(true); }); this->q->Bind(EVT_PRESET_UPDATE_AVAILABLE_CLICKED, [](PresetUpdateAvailableClickedEvent&) { GUI_App &app = wxGetApp(); - app.get_preset_updater()->on_update_notification_confirm(app.plater()->get_preset_archive_database()->get_selected_archive_repositories()); + app.get_preset_updater_wrapper()->on_update_notification_confirm(); }); this->q->Bind(EVT_REMOVABLE_DRIVE_EJECTED, [this, q](RemovableDriveEjectEvent &evt) { if (evt.data.second) { @@ -903,11 +902,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) #endif // SLIC3R_DESKTOP_INTEGRATION #endif // __linux__ std::string service; - if (evt.GetString().Find(L"accounts.google.com") != wxString::npos) { + if (evt.GetString().Find(L"accounts.google.com") != wxNOT_FOUND) { service = "google"; - } else if (evt.GetString().Find(L"appleid.apple.com") != wxString::npos) { + } else if (evt.GetString().Find(L"appleid.apple.com") != wxNOT_FOUND) { service = "apple"; - } else if (evt.GetString().Find(L"facebook.com") != wxString::npos) { + } else if (evt.GetString().Find(L"facebook.com") != wxNOT_FOUND) { service = "facebook"; } wxString url = user_account->get_login_redirect_url(service); @@ -6996,16 +6995,6 @@ const NotificationManager * Plater::get_notification_manager() const return p->notification_manager.get(); } -PresetArchiveDatabase* Plater::get_preset_archive_database() -{ - return p->preset_archive_database.get(); -} - -const PresetArchiveDatabase* Plater::get_preset_archive_database() const -{ - return p->preset_archive_database.get(); -} - UserAccount* Plater::get_user_account() { return p->user_account.get(); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 37ca2de49c..c492912d3a 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -360,9 +360,6 @@ public: NotificationManager* get_notification_manager(); const NotificationManager* get_notification_manager() const; - PresetArchiveDatabase* get_preset_archive_database(); - const PresetArchiveDatabase* get_preset_archive_database() const; - UserAccount* get_user_account(); const UserAccount* get_user_account() const; diff --git a/src/slic3r/GUI/PresetArchiveDatabase.cpp b/src/slic3r/GUI/PresetArchiveDatabase.cpp index 9cf66376a8..ddb697448c 100644 --- a/src/slic3r/GUI/PresetArchiveDatabase.cpp +++ b/src/slic3r/GUI/PresetArchiveDatabase.cpp @@ -6,6 +6,8 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/UserAccount.hpp" +#include "slic3r/Utils/PresetUpdaterWrapper.hpp" +#include "slic3r/GUI/Field.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/AppConfig.hpp" #include "libslic3r/miniz_extension.hpp" @@ -25,7 +27,6 @@ namespace pt = boost::property_tree; namespace fs = boost::filesystem; namespace Slic3r { -namespace GUI { static const char* TMP_EXTENSION = ".download"; @@ -191,7 +192,7 @@ void add_authorization_header(Http& http) } -bool OnlineArchiveRepository::get_file_inner(const std::string& url, const fs::path& target_path) const +bool OnlineArchiveRepository::get_file_inner(const std::string& url, const fs::path& target_path, PresetUpdaterUIStatus* ui_status) const { bool res = false; @@ -210,10 +211,9 @@ bool OnlineArchiveRepository::get_file_inner(const std::string& url, const fs::p //if (cancel) { cancel = true; } }) .on_error([&](std::string body, std::string error, unsigned http_status) { - BOOST_LOG_TRIVIAL(error) << format("Error getting: `%1%`: HTTP %2%, %3%", - url, - http_status, - body); + BOOST_LOG_TRIVIAL(error) << format("Error getting: `%1%`: HTTP %2%, %3%", url, http_status, body); + ui_status->set_error(error); + res = false; }) .on_complete([&](std::string body, unsigned /* http_status */) { if (body.empty()) { @@ -225,30 +225,38 @@ bool OnlineArchiveRepository::get_file_inner(const std::string& url, const fs::p fs::rename(tmp_path, target_path); res = true; }) - .perform_sync(); + .on_retry([&](int attempt, unsigned delay) { + return !ui_status->on_attempt(attempt, delay); + }) + .perform_sync(ui_status->get_retry_policy()); return res; } -bool OnlineArchiveRepository::get_archive(const fs::path& target_path) const +bool OnlineArchiveRepository::get_archive(const fs::path& target_path, PresetUpdaterUIStatus* ui_status) const { - return get_file_inner(m_data.index_url.empty() ? m_data.url + "vendor_indices.zip" : m_data.index_url, target_path); + return get_file_inner(m_data.index_url.empty() ? m_data.url + "vendor_indices.zip" : m_data.index_url, target_path, ui_status); } -bool OnlineArchiveRepository::get_file(const std::string& source_subpath, const fs::path& target_path, const std::string& repository_id) const +bool OnlineArchiveRepository::get_file(const std::string& source_subpath, const fs::path& target_path, const std::string& repository_id, PresetUpdaterUIStatus* ui_status) const { if (repository_id != m_data.id) { BOOST_LOG_TRIVIAL(error) << "Error getting file " << source_subpath << ". The repository_id was not matching."; return false; } + + ui_status->set_target(target_path.filename().string()); + const std::string escaped_source_subpath = escape_path_by_element(source_subpath); - return get_file_inner(m_data.url + escaped_source_subpath, target_path); + return get_file_inner(m_data.url + escaped_source_subpath, target_path, ui_status); } -bool OnlineArchiveRepository::get_ini_no_id(const std::string& source_subpath, const fs::path& target_path) const +bool OnlineArchiveRepository::get_ini_no_id(const std::string& source_subpath, const fs::path& target_path, PresetUpdaterUIStatus* ui_status) const { + ui_status->set_target(target_path.filename().string()); + const std::string escaped_source_subpath = escape_path_by_element(source_subpath); - return get_file_inner(m_data.url + escaped_source_subpath, target_path); + return get_file_inner(m_data.url + escaped_source_subpath, target_path, ui_status); } bool LocalArchiveRepository::get_file_inner(const fs::path& source_path, const fs::path& target_path) const @@ -277,7 +285,7 @@ bool LocalArchiveRepository::get_file_inner(const fs::path& source_path, const f return true; } -bool LocalArchiveRepository::get_file(const std::string& source_subpath, const fs::path& target_path, const std::string& repository_id) const +bool LocalArchiveRepository::get_file(const std::string& source_subpath, const fs::path& target_path, const std::string& repository_id, PresetUpdaterUIStatus* ui_status) const { if (repository_id != m_data.id) { BOOST_LOG_TRIVIAL(error) << "Error getting file " << source_subpath << ". The repository_id was not matching."; @@ -285,11 +293,11 @@ bool LocalArchiveRepository::get_file(const std::string& source_subpath, const f } return get_file_inner(m_data.tmp_path / source_subpath, target_path); } -bool LocalArchiveRepository::get_ini_no_id(const std::string& source_subpath, const fs::path& target_path) const +bool LocalArchiveRepository::get_ini_no_id(const std::string& source_subpath, const fs::path& target_path, PresetUpdaterUIStatus* ui_status) const { return get_file_inner(m_data.tmp_path / source_subpath, target_path); } -bool LocalArchiveRepository::get_archive(const fs::path& target_path) const +bool LocalArchiveRepository::get_archive(const fs::path& target_path, PresetUpdaterUIStatus* ui_status) const { fs::path source_path = fs::path(m_data.tmp_path) / "vendor_indices.zip"; return get_file_inner(std::move(source_path), target_path); @@ -306,10 +314,9 @@ void LocalArchiveRepository::do_extract() //-------------------------------------PresetArchiveDatabase------------------------------------------------------------------------------------------------------------------------- -PresetArchiveDatabase::PresetArchiveDatabase(AppConfig* app_config, wxEvtHandler* evt_handler) +PresetArchiveDatabase::PresetArchiveDatabase() { - // - boost::system::error_code ec; + boost::system::error_code ec; m_unq_tmp_path = fs::temp_directory_path() / fs::unique_path(); fs::create_directories(m_unq_tmp_path, ec); assert(!ec); @@ -362,7 +369,8 @@ bool PresetArchiveDatabase::set_selected_repositories(const std::vector& pair : m_selected_repositories_uuid) { + // std::map m_selected_repositories_uuid + for (const auto& pair : m_selected_repositories_uuid) { if (!pair.second) { continue; } @@ -868,7 +876,7 @@ std::string PresetArchiveDatabase::get_next_uuid() } namespace { -bool sync_inner(std::string& manifest) +bool sync_inner(std::string& manifest, PresetUpdaterUIStatus* ui_status) { bool ret = false; std::string url = Utils::ServiceConfig::instance().preset_repo_repos_url(); @@ -878,23 +886,33 @@ bool sync_inner(std::string& manifest) .timeout_max(30) .on_error([&](std::string body, std::string error, unsigned http_status) { BOOST_LOG_TRIVIAL(error) << "Failed to get online archive source manifests: "<< body << " ; " << error << " ; " << http_status; + ui_status->set_error(error); ret = false; }) .on_complete([&](std::string body, unsigned /* http_status */) { manifest = body; ret = true; }) - .perform_sync(); + .on_retry([&](int attempt, unsigned delay) { + return !ui_status->on_attempt(attempt, delay); + }) + .perform_sync(ui_status->get_retry_policy()); return ret; } } -void PresetArchiveDatabase::sync_blocking() +bool PresetArchiveDatabase::sync_blocking(PresetUpdaterUIStatus* ui_status) { + assert(ui_status); std::string manifest; - if (!sync_inner(manifest)) - return; + bool sync_res = false; + ui_status->set_target("Archive Database Mainfest"); + sync_res = sync_inner(manifest, ui_status); + if (!sync_res) { + return false; + } read_server_manifest(std::move(manifest)); + return true; } -}} // Slic3r::GUI +} // Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/PresetArchiveDatabase.hpp b/src/slic3r/GUI/PresetArchiveDatabase.hpp index c0ed33362d..359df8fbef 100644 --- a/src/slic3r/GUI/PresetArchiveDatabase.hpp +++ b/src/slic3r/GUI/PresetArchiveDatabase.hpp @@ -13,7 +13,8 @@ namespace Slic3r { class AppConfig; -namespace GUI { + +class PresetUpdaterUIStatus; struct ArchiveRepositoryGetFileArgs { boost::filesystem::path target_path; @@ -75,12 +76,12 @@ public: {} virtual ~ArchiveRepository() {} // Gets vendor_indices.zip to target_path - virtual bool get_archive(const boost::filesystem::path& target_path) const = 0; + virtual bool get_archive(const boost::filesystem::path& target_path, PresetUpdaterUIStatus* ui_status) const = 0; // Gets file if repository_id arg matches m_id. // Should be used to get the most recent ini file and every missing resource. - virtual bool get_file(const std::string& source_subpath, const boost::filesystem::path& target_path, const std::string& repository_id) const = 0; + virtual bool get_file(const std::string& source_subpath, const boost::filesystem::path& target_path, const std::string& repository_id, PresetUpdaterUIStatus* ui_status) const = 0; // Gets file without id check - for not yet encountered vendors only! - virtual bool get_ini_no_id(const std::string& source_subpath, const boost::filesystem::path& target_path) const = 0; + virtual bool get_ini_no_id(const std::string& source_subpath, const boost::filesystem::path& target_path, PresetUpdaterUIStatus* ui_status) const = 0; const RepositoryManifest& get_manifest() const { return m_data; } std::string get_uuid() const { return m_uuid; } // Only local archvies can return false @@ -103,15 +104,15 @@ public: } } // Gets vendor_indices.zip to target_path. - bool get_archive(const boost::filesystem::path& target_path) const override; + bool get_archive(const boost::filesystem::path& target_path, PresetUpdaterUIStatus* ui_status) const override; // Gets file if repository_id arg matches m_id. // Should be used to get the most recent ini file and every missing resource. - bool get_file(const std::string& source_subpath, const boost::filesystem::path& target_path, const std::string& repository_id) const override; + bool get_file(const std::string& source_subpath, const boost::filesystem::path& target_path, const std::string& repository_id, PresetUpdaterUIStatus* ui_status) const override; // Gets file without checking id. // Should be used only if no previous ini file exists. - bool get_ini_no_id(const std::string& source_subpath, const boost::filesystem::path& target_path) const override; + bool get_ini_no_id(const std::string& source_subpath, const boost::filesystem::path& target_path, PresetUpdaterUIStatus* ui_status) const override; private: - bool get_file_inner(const std::string& url, const boost::filesystem::path& target_path) const; + bool get_file_inner(const std::string& url, const boost::filesystem::path& target_path, PresetUpdaterUIStatus* ui_status) const; }; class LocalArchiveRepository : public ArchiveRepository @@ -120,13 +121,13 @@ public: LocalArchiveRepository(const std::string& uuid, RepositoryManifest&& data, bool extracted) : ArchiveRepository(uuid, std::move(data)), m_extracted(extracted) {} // Gets vendor_indices.zip to target_path. - bool get_archive(const boost::filesystem::path& target_path) const override; + bool get_archive(const boost::filesystem::path& target_path, PresetUpdaterUIStatus* ui_status) const override; // Gets file if repository_id arg matches m_id. // Should be used to get the most recent ini file and every missing resource. - bool get_file(const std::string& source_subpath, const boost::filesystem::path& target_path, const std::string& repository_id) const override; + bool get_file(const std::string& source_subpath, const boost::filesystem::path& target_path, const std::string& repository_id, PresetUpdaterUIStatus* ui_status) const override; // Gets file without checking id. // Should be used only if no previous ini file exists. - bool get_ini_no_id(const std::string& source_subpath, const boost::filesystem::path& target_path) const override; + bool get_ini_no_id(const std::string& source_subpath, const boost::filesystem::path& target_path, PresetUpdaterUIStatus* ui_status) const override; bool is_extracted() const override { return m_extracted; } void do_extract() override; @@ -141,10 +142,10 @@ typedef std::vector SharedArchiveRepositoryVector; class PresetArchiveDatabase { public: - PresetArchiveDatabase(AppConfig* app_config, wxEvtHandler* evt_handler); + PresetArchiveDatabase(); ~PresetArchiveDatabase() {} - - void sync_blocking(); + // returns true if successfully got the data + bool sync_blocking(PresetUpdaterUIStatus* ui_status = nullptr); // Do not use get_all_archive_repositories to perform any GET calls. Use get_selected_archive_repositories instead. SharedArchiveRepositoryVector get_all_archive_repositories() const; @@ -179,6 +180,6 @@ private: boost::uuids::random_generator m_uuid_generator; }; -}} // Slic3r::GUI +} // Slic3r #endif // PresetArchiveDatabase \ No newline at end of file diff --git a/src/slic3r/GUI/UpdateDialogs.cpp b/src/slic3r/GUI/UpdateDialogs.cpp index 3f578db6ce..c0fce8fb8a 100644 --- a/src/slic3r/GUI/UpdateDialogs.cpp +++ b/src/slic3r/GUI/UpdateDialogs.cpp @@ -277,9 +277,9 @@ boost::filesystem::path AppUpdateDownloadDialog::get_download_path() const // MsgUpdateConfig -MsgUpdateConfig::MsgUpdateConfig(const std::vector &updates, bool force_before_wizard/* = false*/) : - MsgDialog(nullptr, force_before_wizard ? _L("Opening Configuration Wizard") : _L("Configuration update"), - force_before_wizard ? _L("PrusaSlicer is not using the newest configuration available.\n" +MsgUpdateConfig::MsgUpdateConfig(const std::vector &updates, PresetUpdater::UpdateParams update_params) : + MsgDialog(nullptr, update_params == PresetUpdater::UpdateParams::FORCED_BEFORE_WIZARD ? _L("Opening Configuration Wizard") : _L("Configuration update"), + update_params == PresetUpdater::UpdateParams::FORCED_BEFORE_WIZARD ? _L("PrusaSlicer is not using the newest configuration available.\n" "Configuration Wizard may not offer the latest printers, filaments and SLA materials to be installed.") : _L("Configuration update is available"), wxICON_ERROR) { @@ -333,12 +333,19 @@ MsgUpdateConfig::MsgUpdateConfig(const std::vector &updates, bool force_ content_sizer->Add(versions); content_sizer->AddSpacer(2*VERT_SPACING); - add_button(wxID_OK, true, force_before_wizard ? _L("Install") : "OK"); - if (force_before_wizard) { - auto* btn = add_button(wxID_CLOSE, false, _L("Don't install")); + if (update_params == PresetUpdater::UpdateParams::FORCED_BEFORE_WIZARD) { + add_button(wxID_OK, true, _L("Install")); + auto* btn = add_button(wxID_CLOSE, false, _L("Don't install")); btn->Bind(wxEVT_BUTTON, [this](const wxCommandEvent&) { this->EndModal(wxID_CLOSE); }); - } - add_button(wxID_CANCEL); + add_button(wxID_CANCEL); + } else if (update_params == PresetUpdater::UpdateParams::SHOW_TEXT_BOX_YES_NO) { + add_button(wxID_OK, true, _L("Yes")); + add_button(wxID_CANCEL, false, _L("No")); + } else { + assert(update_params == PresetUpdater::UpdateParams::SHOW_TEXT_BOX); + add_button(wxID_OK, true, "OK"); + add_button(wxID_CANCEL); + } finalize(); } diff --git a/src/slic3r/GUI/UpdateDialogs.hpp b/src/slic3r/GUI/UpdateDialogs.hpp index 432da9812b..dd8e6810e6 100644 --- a/src/slic3r/GUI/UpdateDialogs.hpp +++ b/src/slic3r/GUI/UpdateDialogs.hpp @@ -15,6 +15,8 @@ #include "libslic3r/Semver.hpp" #include "MsgDialog.hpp" +#include "slic3r/Utils/PresetUpdater.hpp" + class wxBoxSizer; class wxCheckBox; @@ -102,7 +104,7 @@ public: }; // force_before_wizard - indicates that check of updated is forced before ConfigWizard opening - MsgUpdateConfig(const std::vector &updates, bool force_before_wizard = false); + MsgUpdateConfig(const std::vector &updates, PresetUpdater::UpdateParams update_params); MsgUpdateConfig(MsgUpdateConfig &&) = delete; MsgUpdateConfig(const MsgUpdateConfig &) = delete; MsgUpdateConfig &operator=(MsgUpdateConfig &&) = delete; diff --git a/src/slic3r/GUI/UpdatesUIManager.cpp b/src/slic3r/GUI/UpdatesUIManager.cpp index a60e6a281e..8878e92b06 100644 --- a/src/slic3r/GUI/UpdatesUIManager.cpp +++ b/src/slic3r/GUI/UpdatesUIManager.cpp @@ -5,7 +5,7 @@ #include "UpdatesUIManager.hpp" #include "I18N.hpp" #include "wxExtensions.hpp" -#include "PresetArchiveDatabase.hpp" +#include "slic3r/Utils/PresetUpdaterWrapper.hpp" #include "GUI.hpp" #include "GUI_App.hpp" @@ -22,9 +22,9 @@ namespace fs = boost::filesystem; namespace Slic3r { namespace GUI { -RepositoryUpdateUIManager::RepositoryUpdateUIManager(wxWindow* parent, PresetArchiveDatabase* pad, int em) : +RepositoryUpdateUIManager::RepositoryUpdateUIManager(wxWindow* parent, Slic3r::PresetUpdaterWrapper* puw, int em) : m_parent(parent) - ,m_pad(pad) + ,m_puw(puw) ,m_main_sizer(new wxBoxSizer(wxVERTICAL)) { auto online_label = new wxStaticText(m_parent, wxID_ANY, _L("Online sources")); @@ -104,10 +104,10 @@ void RepositoryUpdateUIManager::fill_entries(bool init_selection/* = false*/) m_online_entries.clear(); m_offline_entries.clear(); - const SharedArchiveRepositoryVector& archs = m_pad->get_all_archive_repositories(); + const SharedArchiveRepositoryVector& archs = m_puw->get_all_archive_repositories(); for (const auto* archive : archs) { const std::string& uuid = archive->get_uuid(); - if (init_selection && m_pad->is_selected_repository_by_uuid(uuid)) + if (init_selection && m_puw->is_selected_repository_by_uuid(uuid)) m_selected_uuids.emplace(uuid); const bool is_selected = m_selected_uuids.find(uuid) != m_selected_uuids.end(); @@ -260,7 +260,7 @@ void RepositoryUpdateUIManager::update() void RepositoryUpdateUIManager::remove_offline_repos(const std::string& id) { - m_pad->remove_local_archive(id); + m_puw->remove_local_archive(id); m_selected_uuids.erase(id); check_selection(); @@ -291,7 +291,7 @@ void RepositoryUpdateUIManager::load_offline_repos() try { fs::path input_path = fs::path(input_file); std::string msg; - std::string uuid = m_pad->add_local_archive(input_path, msg); + std::string uuid = m_puw->add_local_archive(input_path, msg); if (uuid.empty()) { ErrorDialog(m_parent, from_u8(msg), false).ShowModal(); } @@ -314,7 +314,7 @@ bool RepositoryUpdateUIManager::set_selected_repositories() std::string msg; - if (m_pad->set_selected_repositories(used_ids, msg)) { + if (m_puw->set_selected_repositories(used_ids, msg)) { check_selection(); return true; } @@ -328,7 +328,7 @@ bool RepositoryUpdateUIManager::set_selected_repositories() void RepositoryUpdateUIManager::check_selection() { - for (const auto& [uuid, is_selected] : m_pad->get_selected_repositories_uuid() ) + for (const auto& [uuid, is_selected] : m_puw->get_selected_repositories_uuid() ) if ((is_selected && m_selected_uuids.find(uuid) == m_selected_uuids.end() )|| (!is_selected && m_selected_uuids.find(uuid) != m_selected_uuids.end())) { m_is_selection_changed = true; @@ -338,7 +338,7 @@ void RepositoryUpdateUIManager::check_selection() m_is_selection_changed = false; } -ManagePresetRepositoriesDialog::ManagePresetRepositoriesDialog(PresetArchiveDatabase* pad) +ManagePresetRepositoriesDialog::ManagePresetRepositoriesDialog(Slic3r::PresetUpdaterWrapper* puw) : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, format_wxstr("%1% - %2%", SLIC3R_APP_NAME, _L("Manage Updates")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) @@ -346,7 +346,7 @@ ManagePresetRepositoriesDialog::ManagePresetRepositoriesDialog(PresetArchiveData this->SetFont(wxGetApp().normal_font()); const int em = em_unit(); - m_manager = std::make_unique(this, pad, em); + m_manager = std::make_unique(this, puw, em); auto sizer = m_manager->get_sizer(); diff --git a/src/slic3r/GUI/UpdatesUIManager.hpp b/src/slic3r/GUI/UpdatesUIManager.hpp index 28a2b80f99..38878bb494 100644 --- a/src/slic3r/GUI/UpdatesUIManager.hpp +++ b/src/slic3r/GUI/UpdatesUIManager.hpp @@ -14,10 +14,10 @@ class wxSizer; class wxFlexGridSizer; namespace Slic3r { + +class PresetUpdaterWrapper; + namespace GUI { - -class PresetArchiveDatabase; - class RepositoryUpdateUIManager { struct OnlineEntry { @@ -44,7 +44,7 @@ class RepositoryUpdateUIManager boost::filesystem::path source_path; }; - PresetArchiveDatabase* m_pad { nullptr }; + PresetUpdaterWrapper* m_puw { nullptr }; wxWindow* m_parent { nullptr }; wxSizer* m_main_sizer { nullptr }; @@ -68,7 +68,7 @@ class RepositoryUpdateUIManager public: RepositoryUpdateUIManager() {} - RepositoryUpdateUIManager(wxWindow* parent, PresetArchiveDatabase* pad, int em); + RepositoryUpdateUIManager(wxWindow* parent, Slic3r::PresetUpdaterWrapper* puw, int em); ~RepositoryUpdateUIManager() {} void update(); @@ -83,7 +83,7 @@ public: class ManagePresetRepositoriesDialog : public DPIDialog { public: - ManagePresetRepositoriesDialog(PresetArchiveDatabase* pad); + ManagePresetRepositoriesDialog(PresetUpdaterWrapper* puw); ~ManagePresetRepositoriesDialog() {} protected: diff --git a/src/slic3r/GUI/UserAccountUtils.cpp b/src/slic3r/GUI/UserAccountUtils.cpp index f74a730e80..8b0b6db1a4 100644 --- a/src/slic3r/GUI/UserAccountUtils.cpp +++ b/src/slic3r/GUI/UserAccountUtils.cpp @@ -26,17 +26,6 @@ std::string parse_tree_for_param(const pt::ptree& tree, const std::string& param return {}; } -/*void parse_tree_for_param_vector( -const pt::ptree &tree, const std::string& param, std::vector& results) { - for (const auto §ion : tree) { - if (section.first == param) { - results.emplace_back(section.second.data()); - } else { - parse_tree_for_param_vector(section.second, param, results); - } - } -}*/ - pt::ptree parse_tree_for_subtree(const pt::ptree& tree, const std::string& param) { for (const auto §ion : tree) { if (section.first == param) { diff --git a/src/slic3r/GUI/WebViewDialog.cpp b/src/slic3r/GUI/WebViewDialog.cpp index 9ba68d63d9..9ce243c6ca 100644 --- a/src/slic3r/GUI/WebViewDialog.cpp +++ b/src/slic3r/GUI/WebViewDialog.cpp @@ -1539,9 +1539,9 @@ void LoginWebViewDialog::on_navigation_request(wxWebViewEvent &evt) evt.Veto(); m_ret_val = into_u8(url); EndModal(wxID_OK); - } else if (url.Find(L"accounts.google.com") != wxString::npos - || url.Find(L"appleid.apple.com") != wxString::npos - || url.Find(L"facebook.com") != wxString::npos) { + } else if (url.Find(L"accounts.google.com") != wxNOT_FOUND + || url.Find(L"appleid.apple.com") != wxNOT_FOUND + || url.Find(L"facebook.com") != wxNOT_FOUND) { auto& sc = Utils::ServiceConfig::instance(); if (!m_evt_sent && !url.starts_with(GUI::from_u8(sc.account_url()))) { wxCommandEvent* evt = new wxCommandEvent(EVT_OPEN_EXTERNAL_LOGIN); diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index 6895b6678b..2f04467ac7 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -136,6 +136,7 @@ struct Http::priv Http::ErrorFn errorfn; Http::ProgressFn progressfn; Http::IPResolveFn ipresolvefn; + Http::RetryFn retryfn; priv(const std::string &url); ~priv(); @@ -395,6 +396,12 @@ void Http::priv::http_perform(const HttpRetryOpt& retry_opts) std::chrono::milliseconds delay = std::chrono::milliseconds(randomized_delay(generator)); size_t num_retries = 0; do { + // break if canceled outside + if (retryfn && !retryfn(num_retries + 1, num_retries < retry_opts.max_retries ? delay.count() : 0)) { + res = CURLE_ABORTED_BY_CALLBACK; + cancel = true; + break; + } res = ::curl_easy_perform(curl); if (res == CURLE_OK) @@ -409,6 +416,7 @@ void Http::priv::http_perform(const HttpRetryOpt& retry_opts) << "), retrying in " << delay.count() / 1000.0f << " s"; std::this_thread::sleep_for(delay); delay = std::min(delay * 2, retry_opts.max_delay); + } } while (retry); @@ -643,6 +651,12 @@ Http& Http::on_ip_resolve(IPResolveFn fn) return *this; } +Http& Http::on_retry(RetryFn fn) +{ + if (p) { p->retryfn = std::move(fn); } + return *this; +} + Http& Http::cookie_file(const std::string& file_path) { if (p) { diff --git a/src/slic3r/Utils/Http.hpp b/src/slic3r/Utils/Http.hpp index f190c40e1f..56d1fcfabf 100644 --- a/src/slic3r/Utils/Http.hpp +++ b/src/slic3r/Utils/Http.hpp @@ -61,6 +61,8 @@ public: typedef std::function ProgressFn; typedef std::function IPResolveFn; + // + typedef std::function RetryFn; Http(Http &&other); @@ -140,6 +142,8 @@ public: // Called if curl_easy_getinfo resolved just used IP address. Http& on_ip_resolve(IPResolveFn fn); + Http& on_retry(RetryFn fn); + Http& cookie_file(const std::string& file_path); Http& cookie_jar(const std::string& file_path); Http& set_referer(const std::string& referer); diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 23a8c55233..fcfc824305 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -31,7 +31,7 @@ #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/UpdateDialogs.hpp" -#include "slic3r/GUI/ConfigWizard.hpp" +#include "slic3r/Utils/PresetUpdaterWrapper.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/format.hpp" @@ -74,24 +74,8 @@ void copy_file_fix(const fs::path &source, const fs::path &target) static constexpr const auto perms = fs::owner_read | fs::owner_write | fs::group_read | fs::others_read; fs::permissions(target, perms); } -/*std::string escape_string_url(const std::string& unescaped) -{ - std::string ret_val; - CURL* curl = curl_easy_init(); - if (curl) { - char* decoded = curl_easy_escape(curl, unescaped.c_str(), unescaped.size()); - if (decoded) { - ret_val = std::string(decoded); - curl_free(decoded); - } - curl_easy_cleanup(curl); - } - return ret_val; -}*/ } -wxDEFINE_EVENT(EVT_CONFIG_UPDATER_SYNC_DONE, wxCommandEvent); - struct Update { fs::path source; @@ -170,18 +154,13 @@ struct PresetUpdater::priv { std::vector index_db; - bool enabled_version_check; bool enabled_config_update; - std::string version_check_url; fs::path cache_path; fs::path cache_vendor_path; fs::path rsrc_path; fs::path vendor_path; - bool cancel; - std::thread thread; - bool has_waiting_updates { false }; Updates waiting_updates; @@ -190,16 +169,16 @@ struct PresetUpdater::priv void set_download_prefs(const AppConfig *app_config); void prune_tmps() const; void clear_cache_vendor() const; - void sync_config(const VendorMap& vendors, const GUI::ArchiveRepository* archive); + void sync_config(const VendorMap& vendors, const ArchiveRepository* archive, PresetUpdaterUIStatus* ui_status); void check_install_indices() const; Updates get_config_updates(const Semver& old_slic3r_version) const; - bool perform_updates(Updates &&updates, const SharedArchiveRepositoryVector& repositories, bool snapshot = true) const; + bool perform_updates(Updates &&updates, const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status, bool snapshot = true) const; void set_waiting_updates(Updates u); // checks existence and downloads resource to cache - void get_missing_resource(const GUI::ArchiveRepository* archive, const std::string& vendor, const std::string& filename, const std::string& repository_id_from_ini) const; + void get_missing_resource(const ArchiveRepository* archive, const std::string& vendor, const std::string& filename, const std::string& repository_id_from_ini, PresetUpdaterUIStatus* ui_status) const; // checks existence and downloads resource to vendor or copy from cache to vendor - void get_or_copy_missing_resource(const GUI::ArchiveRepository* archive, const std::string& vendor, const std::string& filename, const std::string& repository_id_from_ini) const; + void get_or_copy_missing_resource(const ArchiveRepository* archive, const std::string& vendor, const std::string& filename, const std::string& repository_id_from_ini, PresetUpdaterUIStatus* ui_status) const; void update_index_db(); }; @@ -208,7 +187,7 @@ PresetUpdater::priv::priv() , cache_vendor_path(cache_path / "vendor") , rsrc_path(fs::path(resources_dir()) / "profiles") , vendor_path(fs::path(Slic3r::data_dir()) / "vendor") - , cancel(false) + //, cancel(false) { set_download_prefs(GUI::wxGetApp().app_config); // Install indicies from resources. Only installs those that are either missing or older than in resources. @@ -225,8 +204,6 @@ void PresetUpdater::priv::update_index_db() // Pull relevant preferences from AppConfig void PresetUpdater::priv::set_download_prefs(const AppConfig *app_config) { - enabled_version_check = app_config->get("notify_release") != "none"; - version_check_url = app_config->version_check_url(); enabled_config_update = app_config->get_bool("preset_update") && !app_config->legacy_datadir(); } @@ -253,7 +230,7 @@ void PresetUpdater::priv::clear_cache_vendor() const } // gets resource to cache// -void PresetUpdater::priv::get_missing_resource(const GUI::ArchiveRepository* archive, const std::string& vendor, const std::string& filename, const std::string& repository_id_from_ini) const +void PresetUpdater::priv::get_missing_resource(const ArchiveRepository* archive, const std::string& vendor, const std::string& filename, const std::string& repository_id_from_ini, PresetUpdaterUIStatus* ui_status) const { assert(!filename.empty() && !vendor.empty()); //if (filename.empty() || vendor.empty()) { @@ -282,13 +259,12 @@ void PresetUpdater::priv::get_missing_resource(const GUI::ArchiveRepository* arc if (!fs::exists(file_in_cache.parent_path())) fs::create_directory(file_in_cache.parent_path()); - //std::string escaped_filename = escape_string_url(filename); const std::string resource_subpath = GUI::format("%1%/%2%",vendor, filename); - archive->get_file(resource_subpath, file_in_cache, repository_id_from_ini); + archive->get_file(resource_subpath, file_in_cache, repository_id_from_ini, ui_status); return; } // gets resource to vendor// -void PresetUpdater::priv::get_or_copy_missing_resource(const GUI::ArchiveRepository* archive, const std::string& vendor, const std::string& filename, const std::string& repository_id_from_ini) const +void PresetUpdater::priv::get_or_copy_missing_resource(const ArchiveRepository* archive, const std::string& vendor, const std::string& filename, const std::string& repository_id_from_ini, PresetUpdaterUIStatus* ui_status) const { assert(!filename.empty() && !vendor.empty()); @@ -313,9 +289,8 @@ void PresetUpdater::priv::get_or_copy_missing_resource(const GUI::ArchiveReposit if (!fs::exists(file_in_cache)) { BOOST_LOG_TRIVIAL(info) << "Downloading resources missing in cache directory: " << vendor << " / " << filename; - //std::string escaped_filename = escape_string_url(filename); const std::string resource_subpath = GUI::format("%1%/%2%", vendor, filename); - archive->get_file(resource_subpath, file_in_vendor, repository_id_from_ini); + archive->get_file(resource_subpath, file_in_vendor, repository_id_from_ini, ui_status); return; } BOOST_LOG_TRIVIAL(debug) << "Copiing: " << file_in_cache << " to " << file_in_vendor; @@ -324,19 +299,22 @@ void PresetUpdater::priv::get_or_copy_missing_resource(const GUI::ArchiveReposit // Download vendor indices. Also download new bundles if an index indicates there's a new one available. // Both are saved in cache. -void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::ArchiveRepository* archive_repository) +void PresetUpdater::priv::sync_config(const VendorMap& vendors, const ArchiveRepository* archive_repository, PresetUpdaterUIStatus* ui_status) { BOOST_LOG_TRIVIAL(info) << "Syncing configuration cache"; if (!enabled_config_update) { return; } + assert(ui_status); + ui_status->set_target(archive_repository->get_manifest().id + " archive"); + // Download profiles archive zip fs::path archive_path(cache_path / "vendor_indices.zip"); - if (!archive_repository->get_archive(archive_path)) { - BOOST_LOG_TRIVIAL(error) << "Download of vedor profiles archive zip failed."; + if (!archive_repository->get_archive(archive_path, ui_status)) { + BOOST_LOG_TRIVIAL(error) << "Download of vedor profiles archive zip of " << archive_repository->get_manifest().id << " repository has failed."; return; } - if (cancel) { + if (ui_status->get_canceled()) { return; } @@ -406,7 +384,7 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi // Update vendor preset bundles if in Vendor // Over all indices from the cache directory: for (auto &index : index_db) { - if (cancel) { + if (ui_status->get_canceled()) { return; } auto archive_it = std::find_if(vendors_with_status.begin(), vendors_with_status.end(), @@ -452,7 +430,7 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi BOOST_LOG_TRIVIAL(error) << format("Could not load downloaded index %1% for vendor %2%: invalid index?", idx_path, vendor.name); continue; } - if (cancel) + if (ui_status->get_canceled()) return; } @@ -480,9 +458,9 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi BOOST_LOG_TRIVIAL(info) << "Downloading new bundle for vendor: " << vendor.name; const std::string source_subpath = GUI::format("%1%/%2%.ini", vendor.id, recommended.to_string()); const fs::path bundle_path = cache_path / (vendor.id + ".ini"); - if (!archive_repository->get_file(source_subpath, bundle_path, vendor.repo_id)) + if (!archive_repository->get_file(source_subpath, bundle_path, vendor.repo_id, ui_status)) continue; - if (cancel) + if (ui_status->get_canceled()) return; // vp is fully loaded to get all resources VendorProfile vp; @@ -499,7 +477,7 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi if (! res.empty()) { try { - get_missing_resource(archive_repository, vp.id, res, vendor.repo_id); + get_missing_resource(archive_repository, vp.id, res, vendor.repo_id, ui_status); } catch (const std::exception& e) { @@ -507,7 +485,7 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi } } - if (cancel) + if (ui_status->get_canceled()) return; } } @@ -539,7 +517,7 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi if (!fs::exists(ini_path_in_archive)){ // Download recommneded to vendor - we do not have any existing ini file so we have to use archive url. const std::string source_subpath = GUI::format("%1%/%2%.ini", vendor.first, recommended.to_string()); - if (!archive_repository->get_ini_no_id(source_subpath, ini_path_in_archive)) + if (!archive_repository->get_ini_no_id(source_subpath, ini_path_in_archive, ui_status)) continue; } else { // check existing ini version @@ -560,7 +538,7 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi if (vp.config_version != recommended) { // Take url from existing ini. This way we prevent downloading files from multiple sources. const std::string source_subpath = GUI::format("%1%/%2%.ini", vp.id, recommended.to_string()); - if (!archive_repository->get_file(source_subpath, ini_path_in_archive, vp.repo_id)) + if (!archive_repository->get_file(source_subpath, ini_path_in_archive, vp.repo_id, ui_status)) continue; } } @@ -577,14 +555,14 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi if (!model.thumbnail.empty()) { try { - get_missing_resource(archive_repository, vp.id, model.thumbnail, vp.repo_id); + get_missing_resource(archive_repository, vp.id, model.thumbnail, vp.repo_id, ui_status); } catch (const std::exception& e) { BOOST_LOG_TRIVIAL(error) << "Failed to get " << model.thumbnail << " for " << vp.id << " " << model.id << ": " << e.what(); } } - if (cancel) + if (ui_status->get_canceled()) return; } } else if (vendor.second == VendorStatus::IN_CACHE) { @@ -653,7 +631,7 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi continue; } const std::string source_subpath = GUI::format("%1%/%2%.ini", vp.id, recommended_archive.to_string()); - if (!archive_repository->get_file(source_subpath, ini_path_in_archive, vp.repo_id)) { + if (!archive_repository->get_file(source_subpath, ini_path_in_archive, vp.repo_id, ui_status)) { BOOST_LOG_TRIVIAL(error) << format("Failed to get new vendor .ini file when checking missing resources: %1%", ini_path_in_archive.string()); continue; } @@ -670,7 +648,7 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi } if (vp.config_version != recommended_archive) { const std::string source_subpath = GUI::format("%1%/%2%.ini", vp.id, recommended_archive.to_string()); - if (!archive_repository->get_file(source_subpath, ini_path_in_archive, vp.repo_id)) { + if (!archive_repository->get_file(source_subpath, ini_path_in_archive, vp.repo_id, ui_status)) { BOOST_LOG_TRIVIAL(error) << format("Failed to open vendor .ini file when checking missing resources: %1%", ini_path_in_archive); continue; } @@ -694,14 +672,14 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi if (!model.thumbnail.empty()) { try { - get_missing_resource(archive_repository, vp.id, model.thumbnail, vp.repo_id); + get_missing_resource(archive_repository, vp.id, model.thumbnail, vp.repo_id, ui_status); } catch (const std::exception& e) { BOOST_LOG_TRIVIAL(error) << "Failed to get " << model.thumbnail << " for " << vp.id << " " << model.id << ": " << e.what(); } } - if (cancel) + if (ui_status->get_canceled()) return; } } else if (vendor.second == VendorStatus::INSTALLED || vendor.second == VendorStatus::NEW_VERSION) { @@ -724,14 +702,14 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi if (!res.empty()) { try { - get_or_copy_missing_resource(archive_repository, vp.id, res, vp.repo_id); + get_or_copy_missing_resource(archive_repository, vp.id, res, vp.repo_id, ui_status); } catch (const std::exception& e) { BOOST_LOG_TRIVIAL(error) << "Failed to get " << res << " for " << vp.id << " " << model.id << ": " << e.what(); } } - if (cancel) + if (ui_status->get_canceled()) return; } } @@ -962,7 +940,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version return updates; } -bool PresetUpdater::priv::perform_updates(Updates &&updates, const SharedArchiveRepositoryVector& repositories, bool snapshot) const +bool PresetUpdater::priv::perform_updates(Updates &&updates, const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status, bool snapshot) const { if (updates.incompats.size() > 0) { if (snapshot) { @@ -1051,7 +1029,7 @@ bool PresetUpdater::priv::perform_updates(Updates &&updates, const SharedArchive { auto it = std::find_if(repositories.begin(), repositories.end(), [&vp](const auto* i){ return vp.repo_id == i->get_manifest().id; }); if (it != repositories.end()) - get_or_copy_missing_resource((*it), vp.id, resource, vp.repo_id); + get_or_copy_missing_resource((*it), vp.id, resource, vp.repo_id, ui_status); else { BOOST_LOG_TRIVIAL(error) << "Failed to prepare " << resource << " for " << vp.id << " " << model.id << ": Missing record for source with repo_id " << vp.repo_id; } @@ -1085,74 +1063,20 @@ PresetUpdater::PresetUpdater() : PresetUpdater::~PresetUpdater() { - if (p && p->thread.joinable()) { - // This will stop transfers being done by the thread, if any. - // Cancelling takes some time, but should complete soon enough. - p->cancel = true; - p->thread.join(); - } } -void PresetUpdater::sync(const PresetBundle *preset_bundle, wxEvtHandler* evt_handler,SharedArchiveRepositoryVector&& repositories) -{ - p->set_download_prefs(GUI::wxGetApp().app_config); - if (!p->enabled_config_update) { return; } - - p->thread = std::thread([this, &vendors = preset_bundle->vendors, repositories = std::move(repositories), evt_handler]() { - this->p->clear_cache_vendor(); - this->p->prune_tmps(); - for (const GUI::ArchiveRepository* archive : repositories) { - this->p->sync_config(vendors, archive); - } - wxCommandEvent* evt = new wxCommandEvent(EVT_CONFIG_UPDATER_SYNC_DONE); - evt_handler->QueueEvent(evt); - }); -} - -void PresetUpdater::cancel_sync() -{ - if (p && p->thread.joinable()) { - // This will stop transfers being done by the thread, if any. - // Cancelling takes some time, but should complete soon enough. - p->cancel = true; - p->thread.join(); - } - p->cancel = false; -} - -void PresetUpdater::sync_blocking(const PresetBundle* preset_bundle, wxEvtHandler* evt_handler, const SharedArchiveRepositoryVector& repositories) +void PresetUpdater::sync_blocking(const VendorMap& vendors, const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status) { p->set_download_prefs(GUI::wxGetApp().app_config); if (!p->enabled_config_update) { return; } this->p->clear_cache_vendor(); this->p->prune_tmps(); - for (const GUI::ArchiveRepository* archive : repositories) { - this->p->sync_config(preset_bundle->vendors, archive); - } -} - -void PresetUpdater::slic3r_update_notify() -{ - if (! p->enabled_version_check) - return; - auto* app_config = GUI::wxGetApp().app_config; - const auto ver_online_str = app_config->get("version_online"); - const auto ver_online = Semver::parse(ver_online_str); - const auto ver_online_seen = Semver::parse(app_config->get("version_online_seen")); - - 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 > Slic3r::SEMVER && (! ver_online_seen || *ver_online_seen < *ver_online)) { - GUI::MsgUpdateSlic3r notification(Slic3r::SEMVER, *ver_online); - notification.ShowModal(); - if (notification.disable_version_check()) { - app_config->set("notify_release", "none"); - p->enabled_version_check = false; - } - } - - app_config->set("version_online_seen", ver_online_str); + for (const ArchiveRepository* archive : repositories) { + if (ui_status && ui_status->get_canceled()) { + break; + } + this->p->sync_config(vendors, archive, ui_status); } } @@ -1175,7 +1099,7 @@ static bool reload_configs_update_gui() return true; } -PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3r_version, UpdateParams params, const SharedArchiveRepositoryVector& repositories) const +PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3r_version, UpdateParams params, const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status) const { if (! p->enabled_config_update) { return R_NOOP; } @@ -1209,7 +1133,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3 // This effectively removes the incompatible bundles: // (snapshot is taken beforehand) - if (! p->perform_updates(std::move(updates), repositories) || + if (! p->perform_updates(std::move(updates), repositories, ui_status) || ! GUI::wxGetApp().run_wizard(GUI::ConfigWizard::RR_DATA_INCOMPAT)) return R_INCOMPAT_EXIT; @@ -1251,7 +1175,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3 const auto res = dlg.ShowModal(); if (res == wxID_OK) { BOOST_LOG_TRIVIAL(info) << "User wants to update..."; - if (! p->perform_updates(std::move(updates), repositories) || + if (! p->perform_updates(std::move(updates), repositories, ui_status) || ! reload_configs_update_gui()) return R_INCOMPAT_EXIT; return R_UPDATE_INSTALLED; @@ -1289,12 +1213,12 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3 updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url), std::move(printers)); } - GUI::MsgUpdateConfig dlg(updates_msg, params == UpdateParams::FORCED_BEFORE_WIZARD); + GUI::MsgUpdateConfig dlg(updates_msg, params); const auto res = dlg.ShowModal(); if (res == wxID_OK) { BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update"; - if (! p->perform_updates(std::move(updates), repositories) || + if (! p->perform_updates(std::move(updates), repositories, ui_status) || ! reload_configs_update_gui()) return R_ALL_CANCELED; return R_UPDATE_INSTALLED; @@ -1315,7 +1239,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3 return R_NOOP; } -bool PresetUpdater::install_bundles_rsrc_or_cache_vendor(std::vector bundles, const SharedArchiveRepositoryVector& repositories, bool snapshot) const +bool PresetUpdater::install_bundles_rsrc_or_cache_vendor(std::vector bundles, const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status, bool snapshot) const { Updates updates; @@ -1418,10 +1342,10 @@ bool PresetUpdater::install_bundles_rsrc_or_cache_vendor(std::vectorperform_updates(std::move(updates), repositories, snapshot); + return p->perform_updates(std::move(updates), repositories, ui_status, snapshot); } -void PresetUpdater::on_update_notification_confirm(const SharedArchiveRepositoryVector& repositories) +void PresetUpdater::on_update_notification_confirm(const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status) { if (!p->has_waiting_updates) return; @@ -1439,12 +1363,12 @@ void PresetUpdater::on_update_notification_confirm(const SharedArchiveRepository updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url), std::move(printers)); } - GUI::MsgUpdateConfig dlg(updates_msg); + GUI::MsgUpdateConfig dlg(updates_msg, UpdateParams::SHOW_TEXT_BOX); const auto res = dlg.ShowModal(); if (res == wxID_OK) { BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update"; - if (p->perform_updates(std::move(p->waiting_updates), repositories) && + if (p->perform_updates(std::move(p->waiting_updates), repositories, ui_status) && reload_configs_update_gui()) { p->has_waiting_updates = false; } @@ -1454,28 +1378,10 @@ void PresetUpdater::on_update_notification_confirm(const SharedArchiveRepository } } -bool PresetUpdater::version_check_enabled() const -{ - return p->enabled_version_check; -} - void PresetUpdater::update_index_db() { p->update_index_db(); } -void PresetUpdater::add_additional_archive(const std::string& archive_url, const std::string& download_url) -{ - if (std::find_if(m_additional_archives.begin(), m_additional_archives.end(), [archive_url](const std::pair& it) { return it.first == archive_url; }) == m_additional_archives.end()) { - m_additional_archives.emplace_back(archive_url, download_url); - } -} - -void PresetUpdater::add_additional_archives(const std::vector>& archives) -{ - for (const auto& pair : archives) { - add_additional_archive(pair.first, pair.second); - } -} } diff --git a/src/slic3r/Utils/PresetUpdater.hpp b/src/slic3r/Utils/PresetUpdater.hpp index 25e8494253..c16ce85152 100644 --- a/src/slic3r/Utils/PresetUpdater.hpp +++ b/src/slic3r/Utils/PresetUpdater.hpp @@ -10,16 +10,16 @@ #include #include -#include - namespace Slic3r { - +class VendorProfile; +typedef std::map VendorMap; class AppConfig; class PresetBundle; class Semver; +class PresetUpdaterUIStatus; -typedef std::vector SharedArchiveRepositoryVector; +typedef std::vector SharedArchiveRepositoryVector; static constexpr const int SLIC3R_VERSION_BODY_MAX = 256; @@ -33,14 +33,7 @@ public: PresetUpdater &operator=(const PresetUpdater &) = delete; ~PresetUpdater(); - // If either version check or config updating is enabled, get the appropriate data in the background and cache it. - void sync(const PresetBundle *preset_bundle, wxEvtHandler* evt_handler, SharedArchiveRepositoryVector&& repositories); - void cancel_sync(); - - void sync_blocking(const PresetBundle* preset_bundle, wxEvtHandler* evt_handler, const SharedArchiveRepositoryVector& repositories); - - // If version check is enabled, check if chaced online slic3r version is newer, notify if so. - void slic3r_update_notify(); + void sync_blocking(const VendorMap& vendors, const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status); enum UpdateResult { R_NOOP, @@ -55,35 +48,26 @@ public: enum class UpdateParams { SHOW_TEXT_BOX, // force modal textbox SHOW_NOTIFICATION, // only shows notification - FORCED_BEFORE_WIZARD // indicates that check of updated is forced before ConfigWizard opening + FORCED_BEFORE_WIZARD, // indicates that check of updated is forced before ConfigWizard opening + SHOW_TEXT_BOX_YES_NO // like first option but different buttons in dialog }; // If updating is enabled, check if updates are available in cache, if so, ask about installation. // A false return value implies Slic3r should exit due to incompatibility of configuration. // Providing old slic3r version upgrade profiles on upgrade of an application even in case // that the config index installed from the Internet is equal to the index contained in the installation package. - UpdateResult config_update(const Semver &old_slic3r_version, UpdateParams params, const SharedArchiveRepositoryVector& repositories) const; + UpdateResult config_update(const Semver &old_slic3r_version, UpdateParams params, const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status) const; void update_index_db(); // "Update" a list of bundles from resources or cache/vendor (behaves like an online update). - bool install_bundles_rsrc_or_cache_vendor(std::vector bundles, const SharedArchiveRepositoryVector& repositories, bool snapshot = true) const; + bool install_bundles_rsrc_or_cache_vendor(std::vector bundles, const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status, bool snapshot = true) const; - void on_update_notification_confirm(const SharedArchiveRepositoryVector& repositories); + void on_update_notification_confirm(const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status); - bool version_check_enabled() const; - - void add_additional_archive(const std::string& archive_url, const std::string& download_url); - void add_additional_archives(const std::vector>& archives); private: struct priv; std::unique_ptr p; - - std::vector> m_additional_archives; }; - -//wxDECLARE_EVENT(EVT_SLIC3R_VERSION_ONLINE, wxCommandEvent); -//wxDECLARE_EVENT(EVT_SLIC3R_EXPERIMENTAL_VERSION_ONLINE, wxCommandEvent); -wxDECLARE_EVENT(EVT_CONFIG_UPDATER_SYNC_DONE, wxCommandEvent); } #endif diff --git a/src/slic3r/Utils/PresetUpdaterWrapper.cpp b/src/slic3r/Utils/PresetUpdaterWrapper.cpp new file mode 100644 index 0000000000..ca7072d41e --- /dev/null +++ b/src/slic3r/Utils/PresetUpdaterWrapper.cpp @@ -0,0 +1,354 @@ +#include "PresetUpdaterWrapper.hpp" + +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/MsgDialog.hpp" +#include "slic3r/GUI/format.hpp" +#include "libslic3r/PresetBundle.hpp" +#include "libslic3r/Semver.hpp" +#include "libslic3r/Utils.hpp" + +using namespace std::chrono; + +namespace Slic3r { + +wxDEFINE_EVENT(EVT_PRESET_UPDATER_STATUS_END, PresetUpdaterStatusSimpleEvent); +wxDEFINE_EVENT(EVT_PRESET_UPDATER_STATUS_PRINT, PresetUpdaterStatusMessageEvent); +wxDEFINE_EVENT(EVT_CONFIG_UPDATER_SYNC_DONE, wxCommandEvent); + +PresetUpdaterWrapper::PresetUpdaterWrapper() + : m_preset_updater(std::make_unique()) + , m_preset_archive_database(std::make_unique()) +{ +} +PresetUpdaterWrapper::~PresetUpdaterWrapper() +{ + if (m_worker_thread.joinable()) { + if (m_ui_status) + m_ui_status->set_canceled(true); + m_worker_thread.join(); + } +} + +bool PresetUpdaterWrapper::wizard_sync(const PresetBundle* preset_bundle, const Semver& old_slic3r_version, wxWindow* parent, bool full_sync, const wxString& headline) +{ + assert(!m_modal_thread.joinable()); + // Cancel sync before starting wizard to prevent two downloads at same time. + cancel_worker_thread(); + PresetUpdaterUIStatus* ui_status = new PresetUpdaterUIStatus(PresetUpdaterUIStatus::PresetUpdaterRetryPolicy::PURP_5_TRIES); + Slic3r::ScopeGuard sg([ui_status]() { delete ui_status; }); + GUI::ProgressUpdaterDialog* dialog = new GUI::ProgressUpdaterDialog(ui_status, parent, headline); + ui_status->set_handler(dialog); + VendorMap vendors_copy = preset_bundle->vendors; + auto worker_body = [ui_status, this, vendors_copy, full_sync]() + { + if (!m_preset_archive_database->sync_blocking(ui_status)) { + ui_status->end(); + return; + } + if (ui_status->get_canceled()) { ui_status->end(); return; } + + if (full_sync) { + // Since there might be new repos, we need to sync preset updater + const SharedArchiveRepositoryVector &repos = m_preset_archive_database->get_selected_archive_repositories(); + m_preset_updater->sync_blocking(vendors_copy, repos, ui_status); + if (ui_status->get_canceled()) { ui_status->end(); return; } + m_preset_updater->update_index_db(); + } + ui_status->end(); + }; + m_modal_thread = std::thread(worker_body); + // We need to call ShowModal here instead of prompting it from event callback. + // Otherwise UI thread would freez on job_thread.join(); + dialog->CenterOnParent(); + dialog->ShowModal(); + m_modal_thread.join(); + parent->RemoveChild(dialog); + dialog->Destroy(); + ui_status->set_handler(nullptr); + + // Only now it is possible to work with ui_status, that was previously used in worker thread. + + if (std::string s = ui_status->get_error(); !s.empty()) { + std::string err_text = GUI::format(_u8L("Failed to download %1%"), ui_status->get_target()); + GUI::ErrorDialog err_msg(nullptr, err_text + "\n\n" + s, false); + err_msg.ShowModal(); + return false; + } + + // Should m_preset_updater->config_update run even if there is cancel? + if (ui_status->get_canceled() /*&& !full_sync*/) { + return false; + } + + // Offer update installation. + if (full_sync) { + const SharedArchiveRepositoryVector &repos = m_preset_archive_database->get_selected_archive_repositories(); + m_preset_updater->config_update(old_slic3r_version, PresetUpdater::UpdateParams::SHOW_TEXT_BOX_YES_NO, repos, ui_status); + } + bool res = !ui_status->get_canceled(); + return res; +} + +PresetUpdater::UpdateResult PresetUpdaterWrapper::check_updates_on_user_request(const PresetBundle* preset_bundle, const Semver& old_slic3r_version, wxWindow* parent) +{ + assert(!m_modal_thread.joinable()); + cancel_worker_thread(); + PresetUpdaterUIStatus* ui_status = new PresetUpdaterUIStatus(PresetUpdaterUIStatus::PresetUpdaterRetryPolicy::PURP_5_TRIES); + Slic3r::ScopeGuard sg([ui_status]() { delete ui_status; }); + // TRN: Headline of Progress dialog + GUI::ProgressUpdaterDialog* dialog = new GUI::ProgressUpdaterDialog(ui_status, parent, _L("Checking for Configuration Updates")); + ui_status->set_handler(dialog); + VendorMap vendors_copy = preset_bundle->vendors; + std::string failed_paths; + PresetUpdater::UpdateResult updater_result = PresetUpdater::UpdateResult::R_ALL_CANCELED; + auto worker_body = [ui_status, this, vendors_copy, &failed_paths]() + { + if (!m_preset_archive_database->sync_blocking(ui_status)) { + ui_status->end(); + return; + } + if (ui_status->get_canceled()) { ui_status->end(); return; } + m_preset_archive_database->extract_archives_with_check(failed_paths); + const SharedArchiveRepositoryVector &repos = m_preset_archive_database->get_selected_archive_repositories(); + m_preset_updater->sync_blocking(vendors_copy, repos, ui_status); + if (ui_status->get_canceled()) { ui_status->end(); return; } + + m_preset_updater->update_index_db(); + ui_status->end(); + }; + + m_modal_thread = std::thread(worker_body); + // We need to call ShowModal here instead of prompting it from event callback. + // Otherwise UI thread would freez on job_thread.join(); + dialog->CenterOnParent(); + dialog->ShowModal(); + m_modal_thread.join(); + parent->RemoveChild(dialog); + dialog->Destroy(); + ui_status->set_handler(nullptr); + + // Only now it is possible to work with ui_status, that was previously used in worker thread. + + if (std::string s = ui_status->get_error(); !s.empty()) { + std::string err_text = GUI::format(_u8L("Failed to download %1%"), ui_status->get_target()); + GUI::ErrorDialog err_msg(nullptr, s, false); + err_msg.ShowModal(); + return PresetUpdater::UpdateResult::R_ALL_CANCELED; + } + + if (ui_status->get_canceled()) { + return PresetUpdater::UpdateResult::R_ALL_CANCELED; + } + + if (!failed_paths.empty()) { + int cnt = std::count(failed_paths.begin(), failed_paths.end(), '\n') + 1; + // TRN: %1% contains paths from which loading failed. They are separated by \n, there is no \n at the end. + failed_paths = GUI::format(_L_PLURAL("It was not possible to extract data from %1%. The source will not be updated.", + "It was not possible to extract data for following local sources. They will not be updated.\n\n %1%", cnt), failed_paths); + GUI::ErrorDialog err_msg(nullptr, failed_paths, false); + err_msg.ShowModal(); + } + // preset_updater::config_update does show wxDialog + updater_result = m_preset_updater->config_update(old_slic3r_version, PresetUpdater::UpdateParams::SHOW_TEXT_BOX, m_preset_archive_database->get_selected_archive_repositories(), ui_status); + return updater_result; +} + +PresetUpdater::UpdateResult PresetUpdaterWrapper::check_updates_on_startup(const Semver& old_slic3r_version) +{ + if (m_modal_thread.joinable()) { + return PresetUpdater::UpdateResult::R_ALL_CANCELED; + } + PresetUpdaterUIStatus ui_status(PresetUpdaterUIStatus::PresetUpdaterRetryPolicy::PURP_NO_RETRY); + // preset_updater::config_update does show wxDialog + m_preset_updater->update_index_db(); + return m_preset_updater->config_update(old_slic3r_version, PresetUpdater::UpdateParams::SHOW_NOTIFICATION, m_preset_archive_database->get_selected_archive_repositories(), &ui_status); +} + +void PresetUpdaterWrapper::on_update_notification_confirm() +{ + if (m_modal_thread.joinable()) { + return; + } + PresetUpdaterUIStatus ui_status(PresetUpdaterUIStatus::PresetUpdaterRetryPolicy::PURP_NO_RETRY); + // preset_updater::on_update_notification_confirm does show wxDialog + const SharedArchiveRepositoryVector &repos = m_preset_archive_database->get_selected_archive_repositories(); + m_preset_updater->on_update_notification_confirm(repos, &ui_status); +} + +bool PresetUpdaterWrapper::install_bundles_rsrc_or_cache_vendor(std::vector bundles, bool snapshot/* = true*/) const +{ + PresetUpdaterUIStatus ui_status(PresetUpdaterUIStatus::PresetUpdaterRetryPolicy::PURP_NO_RETRY); + const SharedArchiveRepositoryVector &repos = m_preset_archive_database->get_selected_archive_repositories(); + return m_preset_updater->install_bundles_rsrc_or_cache_vendor(bundles, repos, &ui_status, snapshot); +} + +void PresetUpdaterWrapper::sync_preset_updater(wxEvtHandler* end_evt_handler, const PresetBundle* preset_bundle) +{ + cancel_worker_thread(); + m_ui_status = new PresetUpdaterUIStatus(PresetUpdaterUIStatus::PresetUpdaterRetryPolicy::PURP_NO_RETRY); + VendorMap vendors_copy = preset_bundle->vendors; + + auto worker_body = [ this, vendors_copy, end_evt_handler]() + { + const SharedArchiveRepositoryVector &repos = m_preset_archive_database->get_selected_archive_repositories(); + m_preset_updater->sync_blocking(vendors_copy, repos, this->m_ui_status); + if (this->m_ui_status->get_canceled()) { return; } + wxCommandEvent* evt = new wxCommandEvent(EVT_CONFIG_UPDATER_SYNC_DONE); + wxQueueEvent(end_evt_handler, evt); + }; + + m_worker_thread = std::thread(worker_body); +} + +void PresetUpdaterWrapper::cancel_worker_thread() +{ + if (m_worker_thread.joinable()) { + if (m_ui_status) { + m_ui_status->set_canceled(true); + } else assert(true); + + m_worker_thread.join(); + + if (m_ui_status) { + delete m_ui_status; + m_ui_status = nullptr; + } + } +} + +const std::map PresetUpdaterUIStatus::policy_map = { + {PresetUpdaterUIStatus::PresetUpdaterRetryPolicy::PURP_5_TRIES, {500ms, 5s, 4}}, + {PresetUpdaterUIStatus::PresetUpdaterRetryPolicy::PURP_NO_RETRY, {0ms}} +}; +PresetUpdaterUIStatus::PresetUpdaterUIStatus(PresetUpdaterUIStatus::PresetUpdaterRetryPolicy policy) +{ + if (auto it = policy_map.find(policy); it != policy_map.end()) { + m_retry_policy = it->second; + } else { + assert(true); + m_retry_policy = {0ms}; + } +} +bool PresetUpdaterUIStatus::on_attempt(int attempt, unsigned delay) +{ + if (attempt == 1) { + // TRN: Text of progress dialog. %1% is a name of file. + set_status(GUI::format_wxstr(_L("Downloading Resources: %1%"), m_target)); + } else { + // TRN: Text of progress dialog. %1% is a name of file. %2% is a number of attept. + set_status(GUI::format_wxstr(_L("Downloading Resources: %1%. Attempt %2%."), m_target, std::to_string(attempt))); + } + return get_canceled(); +} +void PresetUpdaterUIStatus::set_target(const std::string& target) +{ + m_target = target; +} +void PresetUpdaterUIStatus::set_status(const wxString& status) +{ + if (m_evt_handler) + wxQueueEvent(m_evt_handler, new PresetUpdaterStatusMessageEvent(EVT_PRESET_UPDATER_STATUS_PRINT, status)); +} + +void PresetUpdaterUIStatus::end() +{ + if (m_evt_handler) + wxQueueEvent(m_evt_handler, new PresetUpdaterStatusSimpleEvent(EVT_PRESET_UPDATER_STATUS_END)); +} + +namespace GUI { +ProgressUpdaterDialog::ProgressUpdaterDialog(PresetUpdaterUIStatus* ui_status, wxWindow* parent, const wxString first_line) + // TRN: Text of progress dialog. + :wxGenericProgressDialog(first_line, _L("Initializing"), 100, parent, wxPD_AUTO_HIDE|wxPD_APP_MODAL|wxPD_CAN_ABORT) + , PresetUpdaterUIStatusCancel(ui_status) +{ + SetMinSize(wxSize(32 * wxGetApp().em_unit(),12 * wxGetApp().em_unit())); + Bind(EVT_PRESET_UPDATER_STATUS_END, &ProgressUpdaterDialog::on_end, this); + Bind(EVT_PRESET_UPDATER_STATUS_PRINT, &ProgressUpdaterDialog::on_set_status, this); +} +ProgressUpdaterDialog::~ProgressUpdaterDialog() +{ +} +void ProgressUpdaterDialog::on_set_status(const PresetUpdaterStatusMessageEvent& evt) +{ + if (!Pulse(evt.data)) { + set_cancel(true); + } +} +void ProgressUpdaterDialog::on_end(const PresetUpdaterStatusSimpleEvent& evt) +{ + EndModal(0); +} +#if 0 +CommonUpdaterDialog::CommonUpdaterDialog(PresetUpdaterUIStatus* ui_status, wxWindow* parent, const wxString first_line, int milisecond_until_cancel_shown) + : wxDialog(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxFRAME_FLOAT_ON_PARENT) + , PresetUpdaterUIStatusCancel(ui_status) +{ + auto* headline = new wxStaticText(this, wxID_ANY, first_line, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL); + m_status_text = new wxStaticText(this, wxID_ANY, _L("Initializing..."), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL); + m_cancel_button = new wxButton(this, wxID_CANCEL, "Cancel"); + // Layout using sizer + wxBoxSizer* hsizer = new wxBoxSizer(wxHORIZONTAL); + hsizer->Add(m_status_text, 1, wxALIGN_CENTER_VERTICAL | wxALL, 10); + hsizer->Add(m_cancel_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, 10); + wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL); + vsizer->Add(headline, 0, wxALIGN_CENTER | wxALL, 10); + vsizer->Add(hsizer, 0, wxEXPAND | wxALL, 5); + this->SetSizer(vsizer); + SetMinSize(wxSize(wxGetApp().em_unit() * 40, wxGetApp().em_unit() * 5)); + m_cancel_button->Bind(wxEVT_BUTTON, [this](const wxCommandEvent& event) { + set_cancel(true); + m_status_text->SetLabel("Canceling..."); + Update(); + Fit(); + }); + + if (milisecond_until_cancel_shown > 0) { + m_cancel_button->Show(false); + m_show_cancel_timer = new wxTimer(this); + Bind(wxEVT_TIMER, [this](wxTimerEvent&) + { + m_cancel_button->Show(); + Layout(); + Fit(); + }); + m_show_cancel_timer->StartOnce(milisecond_until_cancel_shown); + } + + Bind(EVT_PRESET_UPDATER_STATUS_END, &CommonUpdaterDialog::on_end, this); + Bind(EVT_PRESET_UPDATER_STATUS_PRINT, &CommonUpdaterDialog::on_set_status, this); + + #ifdef _WIN32 + wxGetApp().UpdateDlgDarkUI(this); + #endif + Fit(); +} +CommonUpdaterDialog::~CommonUpdaterDialog() +{ + if (m_show_cancel_timer) { + m_show_cancel_timer->Stop(); + delete m_show_cancel_timer; + } +} +void CommonUpdaterDialog::on_set_status(const PresetUpdaterStatusMessageEvent& evt) +{ + m_status_text->SetLabel(evt.data); + Update(); + Fit(); +} +void CommonUpdaterDialog::on_end(const PresetUpdaterStatusSimpleEvent& evt) +{ + EndModal(0); +} + +DummyPresetUpdaterUIStatusHandler::DummyPresetUpdaterUIStatusHandler() + :wxEvtHandler() +{ + Bind(EVT_PRESET_UPDATER_STATUS_END, &DummyPresetUpdaterUIStatusHandler::on_end, this); + Bind(EVT_PRESET_UPDATER_STATUS_PRINT, &DummyPresetUpdaterUIStatusHandler::on_set_status, this); +} +#endif +} +} + diff --git a/src/slic3r/Utils/PresetUpdaterWrapper.hpp b/src/slic3r/Utils/PresetUpdaterWrapper.hpp new file mode 100644 index 0000000000..bf21df057e --- /dev/null +++ b/src/slic3r/Utils/PresetUpdaterWrapper.hpp @@ -0,0 +1,215 @@ +#ifndef slic3r_PresetUpdateWrapper_hpp_ +#define slic3r_PresetUpdateWrapper_hpp_ + +#include "slic3r/GUI/PresetArchiveDatabase.hpp" +#include "slic3r/GUI/ConfigWizard.hpp" +#include "slic3r/GUI/Event.hpp" +#include "slic3r/Utils/PresetUpdater.hpp" +#include "slic3r/Utils/Http.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace Slic3r { + +using PresetUpdaterStatusSimpleEvent = GUI::SimpleEvent; +using PresetUpdaterStatusMessageEvent = GUI::Event; +wxDECLARE_EVENT(EVT_PRESET_UPDATER_STATUS_END, PresetUpdaterStatusSimpleEvent); +wxDECLARE_EVENT(EVT_PRESET_UPDATER_STATUS_PRINT, PresetUpdaterStatusMessageEvent); +wxDECLARE_EVENT(EVT_CONFIG_UPDATER_SYNC_DONE, wxCommandEvent); +class PresetBundle; +class Semver; + +// class that is passed to inner thread function. +// Each call from inner thread triggers event, that is handled by wx object on UI thread (m_evt_handler). +// Communication in opposite direction is done via pointer that should use only set_canceled. (PresetUpdaterUIStatusCancel) +class PresetUpdaterUIStatus +{ +public: + enum class PresetUpdaterRetryPolicy + { + PURP_5_TRIES, + PURP_NO_RETRY, + }; + // called from PresetUpdaterWrapper + PresetUpdaterUIStatus(PresetUpdaterUIStatus::PresetUpdaterRetryPolicy policy); + ~PresetUpdaterUIStatus(){} + void set_handler(wxEvtHandler* evt_handler) {m_evt_handler = evt_handler;} + + // called from worker thread + bool on_attempt(int attempt, unsigned delay); + void set_target(const std::string& target); + void set_status(const wxString& status); + void end(); + bool get_canceled() const {return m_canceled.load(); } + HttpRetryOpt get_retry_policy() const { return m_retry_policy; } + std::string get_error() const { return m_error_msg; } + std::string get_target() const { return m_target; } + + // called from PresetUpdaterUIStatusCancel (ui thread) + void set_canceled(bool val) { m_canceled.store(val); } + void set_error(const std::string& msg) { m_error_msg = msg; } +private: + wxEvtHandler* m_evt_handler {nullptr}; + std::atomic m_canceled {false}; + std::string m_error_msg; + + std::string m_target; + + HttpRetryOpt m_retry_policy; + static const std::map policy_map; +}; + +// Purpose of this class: +// Serves as a hub a entering 2 classes: PresetArchiveDatabase and PresetUpdater +// PresetUpdater: +// - Does not contain outside getters / setters +// - Sync function can be run in worker thread +// ! other functions like config_update does show modal dialogs or notification and must be run in UI tread +// PresetArchiveDatabase: +// - Many functions gets or sets its inner data +// ! Including sync function +// - Does not use direct UI components f.e. dialog +// This class is accessible via wxGetApp().get_preset_updater_wrapper() +// but it should be done only in certain cases: +// 1) Sync of PresetUpdater +// - Runs on worker thread +// - Is called only during startup +// - Needs to be canceled before other operations +// - Ends by queueing EVT_CONFIG_UPDATER_SYNC_DONE +// 2) Callbacks of EVT_CONFIG_UPDATER_SYNC_DONE +// - Needs to be run in UI thread +// - Might chain (evt -> PresetUpdater function -> notification -> Plater -> PresetUpdater function -> dialog) +// 3) Check of update triggered by user +// - Runs most of the operations in own thread while having Modal Dialog for user +// - Used before and inside Config Wizard or when Check for Config Updates +// - Might use UI thread PresetUpdater functions after its thread is done +// - The inner thread is stored as m_modal_thread due to +// 4) Config Wizard run +// - Config Wizard often needs to get or set data of PresetArchiveDatabase +// - Config Wizard runs in UI thread as Modal Dialog +// Possibility of conflicts: +// 1 x 2 - No conflict due 2 being triggered only by end of 1 +// 1 x 3 - No conflict due 3 calling Cancel on 1 before runing +// 1 x 4 - No conflict due 4 run after 3 +// 2 x 2 - All 2 functions does create modal window and are triggered by ui buttons - ui thread might work on other events but ui should be inaccessible +// 2 x 3 - If 1 finnished (2 starts) and 3 is triggered by user - both are triggered via events +// - If order of events is event triggering 3 first and then event queued by 1 is second +// - 2 would run when inner thread of 3 changes data - Therefor functions of 2 must check if inner thread of 3 (m_modal_thread) is joinable +// 2 x 4 - No conflict due 2 and 4 run on both UI thread +// 3 x 4 - No conflict due either 3 blocking ui or even calling 4 only after it finnishes + +class PresetUpdaterWrapper +{ +public: + PresetUpdaterWrapper(); + ~PresetUpdaterWrapper(); + + // 1) Sync of PresetUpdater functions + // runs worker thread and leaves + void sync_preset_updater(wxEvtHandler* end_evt_handler, const PresetBundle* preset_bundle); + + // 2) Callbacks of EVT_CONFIG_UPDATER_SYNC_DONE + // Runs on UI thread + PresetUpdater::UpdateResult check_updates_on_startup(const Semver& old_slic3r_version); + void on_update_notification_confirm(); + + // 3) Check of update triggered by user + // runs own thread but blocks until its done + bool wizard_sync(const PresetBundle* preset_bundle, const Semver& old_slic3r_version, wxWindow* parent, bool full_sync, const wxString& headline); + PresetUpdater::UpdateResult check_updates_on_user_request(const PresetBundle* preset_bundle, const Semver& old_slic3r_version, wxWindow* parent); + + // 4) Config Wizard run + // These function are either const reading from m_preset_archive_database, + // Or sets inner data of m_preset_archive_database + // problem would be if at same time worker thread runs m_preset_archive_database->sync_blocking + bool is_selected_repository_by_id(const std::string& repo_id) const { return m_preset_archive_database->is_selected_repository_by_id(repo_id); } + bool is_selected_repository_by_uuid(const std::string& uuid) const { return m_preset_archive_database->is_selected_repository_by_uuid(uuid); } + SharedArchiveRepositoryVector get_all_archive_repositories() const { return m_preset_archive_database->get_all_archive_repositories(); } + SharedArchiveRepositoryVector get_selected_archive_repositories() const { return m_preset_archive_database->get_selected_archive_repositories();} + const std::map& get_selected_repositories_uuid() const { return m_preset_archive_database->get_selected_repositories_uuid(); } + bool set_selected_repositories(const std::vector& used_uuids, std::string& msg) { return m_preset_archive_database->set_selected_repositories(used_uuids, msg); } + void set_installed_printer_repositories(const std::vector &used_ids) { m_preset_archive_database->set_installed_printer_repositories(used_ids); } + void remove_local_archive(const std::string& uuid) { m_preset_archive_database->remove_local_archive(uuid); } + std::string add_local_archive(const boost::filesystem::path path, std::string& msg) { return m_preset_archive_database->add_local_archive(path, msg); } + + bool install_bundles_rsrc_or_cache_vendor(std::vector bundles, bool snapshot = true) const ; + +private: + void cancel_worker_thread(); + + // Do not share these 2 out of PresetUpdaterWrapper + std::unique_ptr m_preset_archive_database; + std::unique_ptr m_preset_updater; + + // m_worker_thread runs on background while m_modal_thread runs only when modal window exists. + std::thread m_worker_thread; + PresetUpdaterUIStatus* m_ui_status {nullptr}; + std::thread m_modal_thread; +}; + +namespace GUI { + +class PresetUpdaterUIStatusCancel +{ +public: + PresetUpdaterUIStatusCancel(PresetUpdaterUIStatus* ui_status) : p_ui_status(ui_status) {} + ~PresetUpdaterUIStatusCancel() {} + void set_cancel(bool c) {p_ui_status->set_canceled(c);} +private: + PresetUpdaterUIStatus* p_ui_status; +}; + +class ProgressUpdaterDialog : public wxGenericProgressDialog, public PresetUpdaterUIStatusCancel +{ +public: + ProgressUpdaterDialog(PresetUpdaterUIStatus* ui_status, wxWindow* parent, const wxString first_line); + ~ProgressUpdaterDialog(); + void on_set_status(const PresetUpdaterStatusMessageEvent& evt); + void on_end(const PresetUpdaterStatusSimpleEvent& evt); +private: +}; + +#if 0 +// basic dialog +class CommonUpdaterDialog : public wxDialog, public PresetUpdaterUIStatusCancel +{ +public: + CommonUpdaterDialog(PresetUpdaterUIStatus* ui_status, wxWindow* parent, const wxString first_line, int milisecond_until_cancel_shown); + ~CommonUpdaterDialog(); + void on_set_status(const PresetUpdaterStatusMessageEvent& evt); + void on_end(const PresetUpdaterStatusSimpleEvent& evt); +private: + wxStaticText* m_status_text; + wxButton* m_cancel_button; + wxTimer* m_show_cancel_timer {nullptr}; +}; + +// testing purpose dummy class +class DummyPresetUpdaterUIStatusHandler : public wxEvtHandler +{ +public: + DummyPresetUpdaterUIStatusHandler(); + ~DummyPresetUpdaterUIStatusHandler() {} + void on_set_status(const PresetUpdaterStatusMessageEvent& evt) {} + void on_end(const PresetUpdaterStatusSimpleEvent& evt) + { + if(m_end_callback) + m_end_callback(); + } + void set_end_callback(std::function callback) {m_end_callback = callback; } +private: + std::function m_end_callback; +}; +#endif +} // namespace GUI +} // namespace Slic3r +#endif //slic3r_PresetUpdateWrapper_hpp_ \ No newline at end of file diff --git a/src/slic3r/Utils/PrusaConnect.cpp b/src/slic3r/Utils/PrusaConnect.cpp index 69fdb0c203..3812bc04f6 100644 --- a/src/slic3r/Utils/PrusaConnect.cpp +++ b/src/slic3r/Utils/PrusaConnect.cpp @@ -23,31 +23,6 @@ namespace pt = boost::property_tree; namespace Slic3r { namespace { -/*std::string escape_string(const std::string& unescaped) -{ - std::string ret_val; - CURL* curl = curl_easy_init(); - if (curl) { - char* decoded = curl_easy_escape(curl, unescaped.c_str(), unescaped.size()); - if (decoded) { - ret_val = std::string(decoded); - curl_free(decoded); - } - curl_easy_cleanup(curl); - } - return ret_val; -} -std::string escape_path_by_element(const boost::filesystem::path& path) -{ - std::string ret_val = escape_string(path.filename().string()); - boost::filesystem::path parent(path.parent_path()); - while (!parent.empty() && parent.string() != "/") // "/" check is for case "/file.gcode" was inserted. Then boost takes "/" as parent_path. - { - ret_val = escape_string(parent.filename().string()) + "/" + ret_val; - parent = parent.parent_path(); - } - return ret_val; -}*/ boost::optional get_error_message_from_response_body(const std::string& body) { @@ -112,19 +87,6 @@ bool PrusaConnectNew::init_upload(PrintHostUpload upload_data, std::string& out) const std::string upload_filename = upload_data.upload_path.filename().string(); std::string url = GUI::format("%1%/app/users/teams/%2%/uploads", get_host(), m_team_id); std::string request_body_json = upload_data.data_json; - // GUI::format( - // "{" - // "\"filename\": \"%1%\", " - // "\"size\": %2%, " - // "\"path\": \"%3%\", " - // "\"force\": true, " - // "\"printer_uuid\": \"%4%\"" - // "}" - // , upload_filename - // , file_size - // , upload_data.upload_path.generic_string() - // , m_uuid - //); // replace plaholder filename assert(request_body_json.find("%1%") != std::string::npos); @@ -189,20 +151,10 @@ bool PrusaConnectNew::upload(PrintHostUpload upload_data, ProgressFn progress_fn } const std::string name = get_name(); const std::string access_token = GUI::wxGetApp().plater()->get_user_account()->get_access_token(); -// const std::string escaped_upload_path = upload_data.storage + "/" + escape_path_by_element(upload_data.upload_path.string()); -// const std::string set_ready = upload_data.set_ready.empty() ? "" : "&set_ready=" + upload_data.set_ready; -// const std::string position = upload_data.position.empty() ? "" : "&position=" + upload_data.position; -// const std::string wait_until = upload_data.wait_until.empty() ? "" : "&wait_until=" + upload_data.wait_until; const std::string url = GUI::format( "%1%/app/teams/%2%/files/raw" "?upload_id=%3%" - // "&force=true" - // "&printer_uuid=%4%" - // "&path=%5%" - // "%6%" - // "%7%" - // "%8%" - , get_host(), m_team_id, upload_id/*, m_uuid, escaped_upload_path, set_ready, position, wait_until*/); + , get_host(), m_team_id, upload_id); bool res = true;