From 57e47a3296c94e9ed879e5bf281aef69f1942409 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Wed, 28 Mar 2018 11:36:36 +0200 Subject: [PATCH] AppConfig: Support for vendor / model / variant enable state --- lib/Slic3r/GUI.pm | 24 ++- xs/CMakeLists.txt | 6 +- xs/src/perlglue.cpp | 1 + xs/src/slic3r/GUI/AppConfig.cpp | 75 +++++++++- xs/src/slic3r/GUI/AppConfig.hpp | 16 ++ xs/src/slic3r/Utils/PresetUpdate.cpp | 104 ------------- xs/src/slic3r/Utils/PresetUpdater.cpp | 137 ++++++++++++++++++ .../{PresetUpdate.hpp => PresetUpdater.hpp} | 12 +- xs/xsp/GUI_AppConfig.xsp | 1 + xs/xsp/Utils_PresetUpdate.xsp | 18 --- xs/xsp/Utils_PresetUpdater.xsp | 11 ++ xs/xsp/my.map | 4 + 12 files changed, 267 insertions(+), 142 deletions(-) delete mode 100644 xs/src/slic3r/Utils/PresetUpdate.cpp create mode 100644 xs/src/slic3r/Utils/PresetUpdater.cpp rename xs/src/slic3r/Utils/{PresetUpdate.hpp => PresetUpdater.hpp} (71%) delete mode 100644 xs/xsp/Utils_PresetUpdate.xsp create mode 100644 xs/xsp/Utils_PresetUpdater.xsp diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 0c5e87429..495dd9ecc 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -70,6 +70,8 @@ our $grey = Wx::Colour->new(200,200,200); our $LANGUAGE_CHANGE_EVENT = Wx::NewEventType; # 2) To inform about a change of Preferences. our $PREFERENCES_EVENT = Wx::NewEventType; +# To inform AppConfig about Slic3r version available online +our $VERSION_ONLINE_EVENT = Wx::NewEventType; sub OnInit { my ($self) = @_; @@ -101,7 +103,9 @@ sub OnInit { $self->{app_config}->set('version', $Slic3r::VERSION); $self->{app_config}->save; - my $slic3r_update_avail = $self->{app_config}->get("version_check") && $self->{app_config}->get("version_online") != $Slic3r::VERSION; + # my $version_check = $self->{app_config}->get('version_check'); + $self->{preset_updater} = Slic3r::PresetUpdater->new($VERSION_ONLINE_EVENT, $self->{app_config}); + my $slic3r_update = $self->{app_config}->slic3r_update_avail; Slic3r::GUI::set_app_config($self->{app_config}); Slic3r::GUI::load_language(); @@ -140,15 +144,17 @@ sub OnInit { # On OSX the UI was not initialized correctly if the wizard was called # before the UI was up and running. $self->CallAfter(sub { - if ($slic3r_update_avail) { + # XXX: recreate_GUI ??? + if ($slic3r_update) { # TODO - } elsif ($run_wizard) { + } + # XXX: ? + if ($run_wizard) { # Run the config wizard, don't offer the "reset user profile" checkbox. $self->{mainframe}->config_wizard(1); } - # XXX: recreate_GUI ??? - Slic3r::PresetUpdater::download($self->{app_config}, $self->{preset_bundle}); + $self->{preset_updater}->download($self->{preset_bundle}); }); # The following event is emited by the C++ menu implementation of application language change. @@ -161,6 +167,14 @@ sub OnInit { $self->update_ui_from_settings; }); + # The following event is emited by PresetUpdater (C++) + EVT_COMMAND($self, -1, $VERSION_ONLINE_EVENT, sub { + my ($self, $event) = @_; + my $version = $event->GetString; + $self->{app_config}->set('version_online', $version); + $self->{app_config}->save; + }); + return 1; } diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index e73c8429a..3ce499d2b 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -213,8 +213,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/Utils/OctoPrint.hpp ${LIBDIR}/slic3r/Utils/Bonjour.cpp ${LIBDIR}/slic3r/Utils/Bonjour.hpp - ${LIBDIR}/slic3r/Utils/PresetUpdate.cpp - ${LIBDIR}/slic3r/Utils/PresetUpdate.hpp + ${LIBDIR}/slic3r/Utils/PresetUpdater.cpp + ${LIBDIR}/slic3r/Utils/PresetUpdater.hpp ) add_library(admesh STATIC @@ -360,7 +360,7 @@ set(XS_XSP_FILES ${XSP_DIR}/SurfaceCollection.xsp ${XSP_DIR}/TriangleMesh.xsp ${XSP_DIR}/Utils_OctoPrint.xsp - ${XSP_DIR}/Utils_PresetUpdate.xsp + ${XSP_DIR}/Utils_PresetUpdater.xsp ${XSP_DIR}/XS.xsp ) foreach (file ${XS_XSP_FILES}) diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index d7c9a590a..9706ced2c 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -64,6 +64,7 @@ REGISTER_CLASS(PresetCollection, "GUI::PresetCollection"); REGISTER_CLASS(PresetBundle, "GUI::PresetBundle"); REGISTER_CLASS(PresetHints, "GUI::PresetHints"); REGISTER_CLASS(TabIface, "GUI::Tab"); +REGISTER_CLASS(PresetUpdater, "PresetUpdater"); REGISTER_CLASS(OctoPrint, "OctoPrint"); SV* ConfigBase__as_hash(ConfigBase* THIS) diff --git a/xs/src/slic3r/GUI/AppConfig.cpp b/xs/src/slic3r/GUI/AppConfig.cpp index 4bdf88dda..e951beb44 100644 --- a/xs/src/slic3r/GUI/AppConfig.cpp +++ b/xs/src/slic3r/GUI/AppConfig.cpp @@ -1,5 +1,3 @@ -#include - #include "../../libslic3r/libslic3r.h" #include "../../libslic3r/Utils.hpp" #include "AppConfig.hpp" @@ -9,15 +7,20 @@ #include #include #include +#include #include #include #include #include #include +#include namespace Slic3r { +static const std::string VENDOR_PREFIX = "vendor:"; +static const std::string MODEL_PREFIX = "model:"; + void AppConfig::reset() { m_storage.clear(); @@ -45,6 +48,8 @@ void AppConfig::set_defaults() // Version check is enabled by default in the config, but it is not implemented yet. // XXX if (get("version_check").empty()) set("version_check", "1"); + if (get("preset_update").empty()) + set("preset_update", "1"); // Use OpenGL 1.1 even if OpenGL 2.0 is available. This is mainly to support some buggy Intel HD Graphics drivers. // https://github.com/prusa3d/Slic3r/issues/233 if (get("use_legacy_opengl").empty()) @@ -67,6 +72,19 @@ void AppConfig::load() if (! data.empty()) // If there is a non-empty data, then it must be a top-level (without a section) config entry. m_storage[""][section.first] = data; + } else if (boost::starts_with(section.first, VENDOR_PREFIX)) { + // This is a vendor section listing enabled model / variants + const auto vendor_name = section.first.substr(VENDOR_PREFIX.size()); + auto &vendor = m_vendors[vendor_name]; + for (const auto &kvp : section.second) { + if (! boost::starts_with(kvp.first, MODEL_PREFIX)) { continue; } + const auto model_name = kvp.first.substr(MODEL_PREFIX.size()); + std::vector variants; + if (! unescape_strings_cstyle(kvp.second.data(), variants)) { continue; } + for (const auto &variant : variants) { + vendor[model_name].insert(variant); + } + } } else { // This must be a section name. Read the entries of a section. std::map &storage = m_storage[section.first]; @@ -96,10 +114,53 @@ void AppConfig::save() for (const std::pair &kvp : category.second) c << kvp.first << " = " << kvp.second << std::endl; } + // Write vendor sections + for (const auto &vendor : m_vendors) { + size_t size_sum = 0; + for (const auto &model : vendor.second) { size_sum += model.second.size(); } + if (size_sum == 0) { continue; } + + c << std::endl << "[" << VENDOR_PREFIX << vendor.first << "]" << std::endl; + + for (const auto &model : vendor.second) { + if (model.second.size() == 0) { continue; } + const std::vector variants(model.second.begin(), model.second.end()); + const auto escaped = escape_strings_cstyle(variants); + c << MODEL_PREFIX << model.first << " = " << escaped << std::endl; + } + } c.close(); m_dirty = false; } +bool AppConfig::get_variant(const std::string &vendor, const std::string &model, const std::string &variant) const +{ + std::cerr << "AppConfig::get_variant(" << vendor << ", " << model << ", " << variant << ") "; + + const auto it_v = m_vendors.find(vendor); + if (it_v == m_vendors.end()) { return false; } + const auto it_m = it_v->second.find(model); + return it_m == it_v->second.end() ? false : it_m->second.find(variant) != it_m->second.end(); +} + +void AppConfig::set_variant(const std::string &vendor, const std::string &model, const std::string &variant, bool enable) +{ + if (enable) { + if (get_variant(vendor, model, variant)) { return; } + m_vendors[vendor][model].insert(variant); + } else { + auto it_v = m_vendors.find(vendor); + if (it_v == m_vendors.end()) { return; } + auto it_m = it_v->second.find(model); + if (it_m == it_v->second.end()) { return; } + auto it_var = it_m->second.find(variant); + if (it_var == it_m->second.end()) { return; } + it_m->second.erase(it_var); + } + // If we got here, there was an update + m_dirty = true; +} + std::string AppConfig::get_last_dir() const { const auto it = m_storage.find("recent"); @@ -156,6 +217,16 @@ void AppConfig::reset_selections() } } +bool AppConfig::version_check_enabled() const +{ + return get("version_check") == "1"; +} + +bool AppConfig::slic3r_update_avail() const +{ + return version_check_enabled() && get("version_online") != SLIC3R_VERSION; +} + std::string AppConfig::config_path() { return (boost::filesystem::path(Slic3r::data_dir()) / "slic3r.ini").make_preferred().string(); diff --git a/xs/src/slic3r/GUI/AppConfig.hpp b/xs/src/slic3r/GUI/AppConfig.hpp index 9b1d5a712..4ccd51304 100644 --- a/xs/src/slic3r/GUI/AppConfig.hpp +++ b/xs/src/slic3r/GUI/AppConfig.hpp @@ -1,9 +1,12 @@ #ifndef slic3r_AppConfig_hpp_ #define slic3r_AppConfig_hpp_ +#include #include #include +#include "libslic3r/Config.hpp" + namespace Slic3r { class AppConfig @@ -65,6 +68,13 @@ public: void clear_section(const std::string §ion) { m_storage[section].clear(); } + // TODO: remove / upgrade + // ConfigOptionStrings get_strings(const std::string §ion, const std::string &key) const; + // void set_strings(const std::string §ion, const std::string &key, const ConfigOptionStrings &value); + // TODO: + bool get_variant(const std::string &vendor, const std::string &model, const std::string &variant) const; + void set_variant(const std::string &vendor, const std::string &model, const std::string &variant, bool enable); + // return recent/skein_directory or recent/config_directory or empty string. std::string get_last_dir() const; void update_config_dir(const std::string &dir); @@ -78,6 +88,10 @@ public: // the first non-default preset when called. void reset_selections(); + // Whether the Slic3r version available online differs from this one + bool version_check_enabled() const; + bool slic3r_update_avail() const; + // Get the default config path from Slic3r::data_dir(). static std::string config_path(); @@ -87,6 +101,8 @@ public: private: // Map of section, name -> value std::map> m_storage; + // Map of enabled vendors / models / variants + std::map>> m_vendors; // Has any value been modified since the config.ini has been last saved or loaded? bool m_dirty; }; diff --git a/xs/src/slic3r/Utils/PresetUpdate.cpp b/xs/src/slic3r/Utils/PresetUpdate.cpp deleted file mode 100644 index 0e4c62af9..000000000 --- a/xs/src/slic3r/Utils/PresetUpdate.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "PresetUpdate.hpp" - -#include // XXX -#include -#include -#include - -#include "libslic3r/Utils.hpp" -#include "slic3r/GUI/PresetBundle.hpp" -#include "slic3r/Utils/Http.hpp" - -namespace fs = boost::filesystem; - - -namespace Slic3r { - - -struct PresetUpdater::priv -{ - PresetBundle *bundle; - fs::path cache_path; - std::thread thread; - - priv(PresetBundle *bundle); - - void download(); -}; - - -PresetUpdater::priv::priv(PresetBundle *bundle) : - bundle(bundle), - cache_path(fs::path(Slic3r::data_dir()) / "cache") -{} - -void PresetUpdater::priv::download() -{ - std::cerr << "PresetUpdater::priv::download()" << std::endl; - - std::cerr << "Bundle vendors: " << bundle->vendors.size() << std::endl; - for (const auto &vendor : bundle->vendors) { - std::cerr << "vendor: " << vendor.name << std::endl; - std::cerr << " URL: " << vendor.config_update_url << std::endl; - - // TODO: Proper caching - - auto target_path = cache_path / vendor.id; - target_path += ".ini"; - std::cerr << "target_path: " << target_path << std::endl; - - Http::get(vendor.config_update_url) - .on_complete([&](std::string body, unsigned http_status) { - std::cerr << "Got ini: " << http_status << ", body: " << body.size() << std::endl; - fs::fstream file(target_path, std::ios::out | std::ios::binary | std::ios::trunc); - file.write(body.c_str(), body.size()); - }) - .on_error([](std::string body, std::string error, unsigned http_status) { - // TODO: what about errors? - std::cerr << "Error: " << http_status << ", " << error << std::endl; - }) - .perform_sync(); - } -} - -PresetUpdater::PresetUpdater(PresetBundle *preset_bundle) : p(new priv(preset_bundle)) {} - - -// Public - -PresetUpdater::~PresetUpdater() -{ - if (p && p->thread.joinable()) { - p->thread.detach(); - } -} - -void PresetUpdater::download(AppConfig *app_config, PresetBundle *preset_bundle) -{ - std::cerr << "PresetUpdater::download()" << std::endl; - - auto self = std::make_shared(preset_bundle); - auto thread = std::thread([self](){ - self->p->download(); - }); - self->p->thread = std::move(thread); -} - - -// TODO: remove -namespace Utils { - -void preset_update_check() -{ - std::cerr << "preset_update_check()" << std::endl; - - // TODO: - // 1. Get a version tag or the whole bundle from the web - // 2. Store into temporary location (?) - // 3. ??? - // 4. Profit! -} - -} - -} diff --git a/xs/src/slic3r/Utils/PresetUpdater.cpp b/xs/src/slic3r/Utils/PresetUpdater.cpp new file mode 100644 index 000000000..11064d2f0 --- /dev/null +++ b/xs/src/slic3r/Utils/PresetUpdater.cpp @@ -0,0 +1,137 @@ +#include "PresetUpdater.hpp" + +#include // XXX +#include +#include +#include +#include + +#include +#include + +#include "libslic3r/Utils.hpp" +#include "slic3r/GUI/GUI.hpp" +// #include "slic3r/GUI/AppConfig.hpp" +#include "slic3r/GUI/PresetBundle.hpp" +#include "slic3r/Utils/Http.hpp" + +namespace fs = boost::filesystem; + + +namespace Slic3r { + + +// TODO: proper URL +static const std::string SLIC3R_VERSION_URL = "https://gist.githubusercontent.com/vojtechkral/4d8fd4a3b8699a01ec892c264178461c/raw/e9187c3e15ceaf1a90f29b7c43cf3ccc746140f0/slic3rPE.version"; +enum { + SLIC3R_VERSION_BODY_MAX = 256, +}; + +struct PresetUpdater::priv +{ + int version_online_event; + bool version_check; + bool preset_update; + + fs::path cache_path; + bool cancel; + std::thread thread; + + priv(int event); + + void download(const std::set vendors) const; +}; + +PresetUpdater::priv::priv(int event) : + version_online_event(event), + version_check(false), + preset_update(false), + cache_path(fs::path(Slic3r::data_dir()) / "cache"), + cancel(false) +{} + +void PresetUpdater::priv::download(const std::set vendors) const +{ + std::cerr << "PresetUpdater::priv::download()" << std::endl; + + if (!version_check) { return; } + + // Download current Slic3r version + Http::get(SLIC3R_VERSION_URL) + .size_limit(SLIC3R_VERSION_BODY_MAX) + .on_progress([this](Http::Progress, bool &cancel) { + cancel = this->cancel; + }) + .on_complete([&](std::string body, unsigned http_status) { + boost::trim(body); + std::cerr << "Got version: " << http_status << ", body: \"" << body << '"' << std::endl; + wxCommandEvent* evt = new wxCommandEvent(version_online_event); + evt->SetString(body); + GUI::get_app()->QueueEvent(evt); + }) + .perform_sync(); + + if (!preset_update) { return; } + + // Donwload vendor preset bundles + std::cerr << "Bundle vendors: " << vendors.size() << std::endl; + for (const auto &vendor : vendors) { + std::cerr << "vendor: " << vendor.name << std::endl; + std::cerr << " URL: " << vendor.config_update_url << std::endl; + + if (cancel) { return; } + + // TODO: Proper caching + + auto target_path = cache_path / vendor.id; + target_path += ".ini"; + std::cerr << "target_path: " << target_path << std::endl; + + Http::get(vendor.config_update_url) + .on_progress([this](Http::Progress, bool &cancel) { + cancel = this->cancel; + }) + .on_complete([&](std::string body, unsigned http_status) { + std::cerr << "Got ini: " << http_status << ", body: " << body.size() << std::endl; + fs::fstream file(target_path, std::ios::out | std::ios::binary | std::ios::trunc); + file.write(body.c_str(), body.size()); + }) + .perform_sync(); + } +} + +PresetUpdater::PresetUpdater(int version_online_event, AppConfig *app_config) : + p(new priv(version_online_event)) +{ + p->preset_update = app_config->get("preset_update") == "1"; + // preset_update implies version_check: + p->version_check = p->preset_update || app_config->get("version_check") == "1"; +} + + +// Public + +PresetUpdater::~PresetUpdater() +{ + if (p && p->thread.joinable()) { + p->cancel = true; + p->thread.join(); + } +} + +void PresetUpdater::download(PresetBundle *preset_bundle) +{ + std::cerr << "PresetUpdater::download()" << std::endl; + + // Copy the whole vendors data for use in the background thread + // Unfortunatelly as of C++11, it needs to be copied again + // into the closure (but perhaps the compiler can elide this). + std::set vendors = preset_bundle->vendors; + + p->thread = std::move(std::thread([this, vendors]() { + this->p->download(std::move(vendors)); + })); +} + + +} diff --git a/xs/src/slic3r/Utils/PresetUpdate.hpp b/xs/src/slic3r/Utils/PresetUpdater.hpp similarity index 71% rename from xs/src/slic3r/Utils/PresetUpdate.hpp rename to xs/src/slic3r/Utils/PresetUpdater.hpp index 431134097..aafe9569b 100644 --- a/xs/src/slic3r/Utils/PresetUpdate.hpp +++ b/xs/src/slic3r/Utils/PresetUpdater.hpp @@ -12,27 +12,19 @@ class PresetBundle; class PresetUpdater { public: - PresetUpdater(PresetBundle *preset_bundle); + PresetUpdater(int version_online_event, AppConfig *app_config); PresetUpdater(PresetUpdater &&) = delete; PresetUpdater(const PresetUpdater &) = delete; PresetUpdater &operator=(PresetUpdater &&) = delete; PresetUpdater &operator=(const PresetUpdater &) = delete; ~PresetUpdater(); - static void download(AppConfig *app_config, PresetBundle *preset_bundle); + void download(PresetBundle *preset_bundle); private: struct priv; std::unique_ptr p; }; -// TODO: Remove -namespace Utils { - -void preset_update_check(); - } - -} - #endif diff --git a/xs/xsp/GUI_AppConfig.xsp b/xs/xsp/GUI_AppConfig.xsp index 08a88883d..de0e5a22b 100644 --- a/xs/xsp/GUI_AppConfig.xsp +++ b/xs/xsp/GUI_AppConfig.xsp @@ -43,4 +43,5 @@ void update_last_output_dir(char *dir); void reset_selections(); + bool slic3r_update_avail() const; }; diff --git a/xs/xsp/Utils_PresetUpdate.xsp b/xs/xsp/Utils_PresetUpdate.xsp deleted file mode 100644 index 3596b7c86..000000000 --- a/xs/xsp/Utils_PresetUpdate.xsp +++ /dev/null @@ -1,18 +0,0 @@ -%module{Slic3r::XS}; - -%{ -#include -#include "slic3r/Utils/PresetUpdate.hpp" -%} - -%name{Slic3r::PresetUpdater} class PresetUpdater { - static void download(PresetBundle *preset_bundle); -}; - - -# TODO: remove: - -%package{Slic3r::Utils}; - -void preset_update_check() - %code%{ Slic3r::Utils::preset_update_check(); %}; diff --git a/xs/xsp/Utils_PresetUpdater.xsp b/xs/xsp/Utils_PresetUpdater.xsp new file mode 100644 index 000000000..666379f02 --- /dev/null +++ b/xs/xsp/Utils_PresetUpdater.xsp @@ -0,0 +1,11 @@ +%module{Slic3r::XS}; + +%{ +#include +#include "slic3r/Utils/PresetUpdater.hpp" +%} + +%name{Slic3r::PresetUpdater} class PresetUpdater { + PresetUpdater(int version_online_event, AppConfig *app_config); + void download(PresetBundle* preset_bundle); +}; diff --git a/xs/xsp/my.map b/xs/xsp/my.map index d817af052..9c0f60c1d 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -239,6 +239,10 @@ Ref O_OBJECT_SLIC3R_T # ConfigWizard* O_OBJECT_SLIC3R # Ref O_OBJECT_SLIC3R_T +PresetUpdater* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +Clone O_OBJECT_SLIC3R_T + OctoPrint* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T