From 0694fad01626578cea4828ba19749f38e8d5e146 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 10 Apr 2018 16:27:42 +0200 Subject: [PATCH] Initial implementation of the config snapshot dialog. --- xs/CMakeLists.txt | 6 +- xs/src/slic3r/Config/Snapshot.cpp | 8 +- xs/src/slic3r/Config/Snapshot.hpp | 2 +- xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp | 110 +++++++++++++++++++++ xs/src/slic3r/GUI/ConfigSnapshotDialog.hpp | 30 ++++++ xs/src/slic3r/GUI/GUI.cpp | 14 +++ xs/src/slic3r/Utils/Semver.hpp | 16 ++- xs/src/slic3r/Utils/Time.cpp | 44 ++++++--- xs/src/slic3r/Utils/Time.hpp | 5 +- 9 files changed, 213 insertions(+), 22 deletions(-) create mode 100644 xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp create mode 100644 xs/src/slic3r/GUI/ConfigSnapshotDialog.hpp diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index b9a6dbfee..e01f11b26 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -176,6 +176,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/AppConfig.hpp ${LIBDIR}/slic3r/GUI/BitmapCache.cpp ${LIBDIR}/slic3r/GUI/BitmapCache.hpp + ${LIBDIR}/slic3r/GUI/ConfigSnapshotDialog.cpp + ${LIBDIR}/slic3r/GUI/ConfigSnapshotDialog.hpp ${LIBDIR}/slic3r/GUI/3DScene.cpp ${LIBDIR}/slic3r/GUI/3DScene.hpp ${LIBDIR}/slic3r/GUI/GLShader.cpp @@ -218,6 +220,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/Utils/OctoPrint.hpp ${LIBDIR}/slic3r/Utils/Bonjour.cpp ${LIBDIR}/slic3r/Utils/Bonjour.hpp + ${LIBDIR}/slic3r/Utils/Time.cpp + ${LIBDIR}/slic3r/Utils/Time.hpp ) add_library(admesh STATIC @@ -408,7 +412,7 @@ if(APPLE) # Ignore undefined symbols of the perl interpreter, they will be found in the caller image. target_link_libraries(XS "-undefined dynamic_lookup") endif() -target_link_libraries(XS libslic3r libslic3r_gui admesh miniz clipper nowide polypartition poly2tri) +target_link_libraries(XS libslic3r libslic3r_gui admesh miniz clipper nowide polypartition poly2tri semver) if(SLIC3R_PROFILE) target_link_libraries(XS Shiny) endif() diff --git a/xs/src/slic3r/Config/Snapshot.cpp b/xs/src/slic3r/Config/Snapshot.cpp index 91f02ab25..33c7ef82f 100644 --- a/xs/src/slic3r/Config/Snapshot.cpp +++ b/xs/src/slic3r/Config/Snapshot.cpp @@ -241,7 +241,7 @@ static void delete_existing_ini_files(const boost::filesystem::path &path) boost::filesystem::remove(dir_entry.path()); } -const Snapshot& SnapshotDB::make_snapshot(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment) +const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment) { boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir()); boost::filesystem::path snapshot_db_dir = SnapshotDB::create_db_dir(); @@ -267,8 +267,10 @@ const Snapshot& SnapshotDB::make_snapshot(const AppConfig &app_config, Snapshot: } // Vendor specific config bundles and installed printers. + boost::filesystem::path snapshot_dir = snapshot_db_dir / snapshot.id; + boost::filesystem::create_directory(snapshot_dir); + // Backup the presets. - boost::filesystem::path snapshot_dir = snapshot_db_dir / snapshot.id; for (const char *subdir : { "print", "filament", "printer", "vendor" }) copy_config_dir_single_level(data_dir / subdir, snapshot_dir / subdir); snapshot.save_ini((snapshot_dir / "snapshot.ini").string()); @@ -322,7 +324,7 @@ boost::filesystem::path SnapshotDB::create_db_dir() SnapshotDB& SnapshotDB::singleton() { static SnapshotDB instance; - bool loaded = false; + static bool loaded = false; if (! loaded) { try { loaded = true; diff --git a/xs/src/slic3r/Config/Snapshot.hpp b/xs/src/slic3r/Config/Snapshot.hpp index 1d02c8650..3c7754273 100644 --- a/xs/src/slic3r/Config/Snapshot.hpp +++ b/xs/src/slic3r/Config/Snapshot.hpp @@ -91,7 +91,7 @@ public: // Create a snapshot directory, copy the vendor config bundles, user print/filament/printer profiles, // create an index. - const Snapshot& make_snapshot(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment); + const Snapshot& take_snapshot(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment); void restore_snapshot(const std::string &id, AppConfig &app_config); void restore_snapshot(const Snapshot &snapshot, AppConfig &app_config); diff --git a/xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp b/xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp new file mode 100644 index 000000000..a4e4d846b --- /dev/null +++ b/xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp @@ -0,0 +1,110 @@ +#include "ConfigSnapshotDialog.hpp" + +#include "../Config/Snapshot.hpp" +#include "../Utils/Time.hpp" + +#include "../../libslic3r/Utils.hpp" + +namespace Slic3r { +namespace GUI { + +static std::string generate_html_row(const Config::Snapshot &snapshot, bool row_even) +{ + // Start by declaring a row with an alternating background color. + std::string text = ""; + text += ""; +// text += _(L("ID:")) + " " + snapshot.id + "
"; + text += _(L("time captured:")) + " " + Utils::format_local_date_time(snapshot.time_captured) + "
"; + text += _(L("slic3r version:")) + " " + snapshot.slic3r_version_captured.to_string() + "
"; + if (! snapshot.comment.empty()) + text += _(L("user comment:")) + " " + snapshot.comment + "
"; +// text += "reason: " + snapshot.reason + "
"; + text += "print: " + snapshot.print + "
"; + text += "filaments: " + snapshot.filaments.front() + "
"; + text += "printer: " + snapshot.printer + "
"; + + for (const Config::Snapshot::VendorConfig &vc : snapshot.vendor_configs) { + text += "vendor: " + vc.name + ", ver: " + vc.version.to_string() + ", min slic3r ver: " + vc.min_slic3r_version.to_string() + ", max slic3r ver: " + vc.max_slic3r_version.to_string() + "
"; + } + + text += "

Activate

"; + text += ""; + text += ""; + return text; +} + +static std::string generate_html_page(const Config::SnapshotDB &snapshot_db) +{ + std::string text = + "" + "" + ""; + text += ""; + for (size_t i_row = 0; i_row < snapshot_db.snapshots().size(); ++ i_row) { + const Config::Snapshot &snapshot = snapshot_db.snapshots()[i_row]; + text += generate_html_row(snapshot, i_row & 1); + } + text += + "
" + "
" + "" + ""; + return text; +} + +ConfigSnapshotDialog::ConfigSnapshotDialog(const Config::SnapshotDB &snapshot_db) + : wxDialog(NULL, wxID_ANY, _(L("Configuration Snapshots")), wxDefaultPosition, wxSize(600, 500), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX) +{ + this->SetBackgroundColour(*wxWHITE); + + wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL); + this->SetSizer(vsizer); + + // text + wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO); + { + wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + #ifdef __WXMSW__ + int size[] = {8,8,8,8,8,8,8}; + #else + int size[] = {11,11,11,11,11,11,11}; + #endif + html->SetFonts(font.GetFaceName(), font.GetFaceName(), size); + html->SetBorders(2); + std::string text = generate_html_page(snapshot_db); + FILE *file = ::fopen("d:\\temp\\configsnapshotdialog.html", "wt"); + fwrite(text.data(), 1, text.size(), file); + fclose(file); + html->SetPage(text.c_str()); + vsizer->Add(html, 1, wxEXPAND | wxALIGN_LEFT | wxRIGHT | wxBOTTOM, 0); + html->Bind(wxEVT_HTML_LINK_CLICKED, &ConfigSnapshotDialog::onLinkClicked, this); + } + + wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCLOSE); + this->SetEscapeId(wxID_CLOSE); + this->Bind(wxEVT_BUTTON, &ConfigSnapshotDialog::onCloseDialog, this, wxID_CLOSE); + vsizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3); + +/* + this->Bind(wxEVT_LEFT_DOWN, &ConfigSnapshotDialog::onCloseDialog, this); + logo->Bind(wxEVT_LEFT_DOWN, &ConfigSnapshotDialog::onCloseDialog, this); + html->Bind(wxEVT_LEFT_DOWN, &ConfigSnapshotDialog::onCloseDialog, this); +*/ +} + +void ConfigSnapshotDialog::onLinkClicked(wxHtmlLinkEvent &event) +{ + wxLaunchDefaultBrowser(event.GetLinkInfo().GetHref()); + event.Skip(false); +} + +void ConfigSnapshotDialog::onCloseDialog(wxEvent &) +{ + this->EndModal(wxID_CLOSE); + this->Close(); +} + +} // namespace GUI +} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/ConfigSnapshotDialog.hpp b/xs/src/slic3r/GUI/ConfigSnapshotDialog.hpp new file mode 100644 index 000000000..70187ee99 --- /dev/null +++ b/xs/src/slic3r/GUI/ConfigSnapshotDialog.hpp @@ -0,0 +1,30 @@ +#ifndef slic3r_GUI_ConfigSnapshotDialog_hpp_ +#define slic3r_GUI_ConfigSnapshotDialog_hpp_ + +#include "GUI.hpp" + +#include +#include +#include + +namespace Slic3r { +namespace GUI { + +namespace Config { + class SnapshotDB; +} + +class ConfigSnapshotDialog : public wxDialog +{ +public: + ConfigSnapshotDialog(const Config::SnapshotDB &snapshot_db); + +private: + void onLinkClicked(wxHtmlLinkEvent &event); + void onCloseDialog(wxEvent &); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif /* slic3r_GUI_ConfigSnapshotDialog_hpp_ */ diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 48d56ff11..0a163bf28 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -44,10 +44,13 @@ #include "TabIface.hpp" #include "AboutDialog.hpp" #include "AppConfig.hpp" +#include "ConfigSnapshotDialog.hpp" #include "Utils.hpp" #include "Preferences.hpp" #include "PresetBundle.hpp" +#include "../Config/Snapshot.hpp" + namespace Slic3r { namespace GUI { #if __APPLE__ @@ -357,6 +360,17 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l local_menu->Append(config_id_base + ConfigMenuLanguage, _(L("Change Application Language"))); local_menu->Bind(wxEVT_MENU, [config_id_base, event_language_change, event_preferences_changed](wxEvent &event){ switch (event.GetId() - config_id_base) { + case ConfigMenuTakeSnapshot: + // Take a configuration snapshot. + Slic3r::GUI::Config::SnapshotDB::singleton().take_snapshot(*g_AppConfig, Slic3r::GUI::Config::Snapshot::SNAPSHOT_USER, ""); + break; + case ConfigMenuSnapshots: + { + ConfigSnapshotDialog dlg(Slic3r::GUI::Config::SnapshotDB::singleton()); + dlg.ShowModal(); + dlg.Destroy(); + break; + } case ConfigMenuPreferences: { auto dlg = new PreferencesDialog(g_wxMainFrame, event_preferences_changed); diff --git a/xs/src/slic3r/Utils/Semver.hpp b/xs/src/slic3r/Utils/Semver.hpp index 7fc3b8033..8aa8cc53f 100644 --- a/xs/src/slic3r/Utils/Semver.hpp +++ b/xs/src/slic3r/Utils/Semver.hpp @@ -46,11 +46,23 @@ public: return Semver(ver); } - Semver(Semver &&other) { *this = std::move(other); } - Semver(const Semver &other) { *this = other; } + Semver(Semver &&other) : ver(other.ver) + { + other.ver.major = other.ver.minor = other.ver.patch = 0; + other.ver.metadata = other.ver.prerelease = nullptr; + } + + Semver(const Semver &other) : ver(other.ver) + { + if (other.ver.metadata != nullptr) + std::strcpy(ver.metadata, other.ver.metadata); + if (other.ver.prerelease != nullptr) + std::strcpy(ver.prerelease, other.ver.prerelease); + } Semver &operator=(Semver &&other) { + ::semver_free(&ver); ver = other.ver; other.ver.major = other.ver.minor = other.ver.patch = 0; other.ver.metadata = other.ver.prerelease = nullptr; diff --git a/xs/src/slic3r/Utils/Time.cpp b/xs/src/slic3r/Utils/Time.cpp index c4123c7bb..a2b2328af 100644 --- a/xs/src/slic3r/Utils/Time.cpp +++ b/xs/src/slic3r/Utils/Time.cpp @@ -1,13 +1,18 @@ #include "Time.hpp" +#ifdef WIN32 + #define WIN32_LEAN_AND_MEAN + #include + #undef WIN32_LEAN_AND_MEAN +#endif /* WIN32 */ + namespace Slic3r { namespace Utils { time_t parse_time_ISO8601Z(const std::string &sdate) { - int y, M, d, h, m; - float s; - if (sscanf(sdate.c_str(), "%d-%d-%dT%d:%d:%fZ", &y, &M, &d, &h, &m, &s) != 6) + int y, M, d, h, m, s; + if (sscanf(sdate.c_str(), "%04d%02d%02dT%02d%02d%02dZ", &y, &M, &d, &h, &m, &s) != 6) return (time_t)-1; struct tm tms; tms.tm_year = y - 1900; // Year since 1900 @@ -15,7 +20,7 @@ time_t parse_time_ISO8601Z(const std::string &sdate) tms.tm_mday = d; // 1-31 tms.tm_hour = h; // 0-23 tms.tm_min = m; // 0-59 - tms.tm_sec = (int)s; // 0-61 (0-60 in C++11) + tms.tm_sec = s; // 0-61 (0-60 in C++11) return mktime(&tms); } @@ -23,21 +28,34 @@ std::string format_time_ISO8601Z(time_t time) { struct tm tms; #ifdef WIN32 - gmtime_s(time, &tms); + gmtime_s(&tms, &time); #else - gmtime_r(&tms, time); + gmtime_r(&time, &tms); #endif char buf[128]; - sprintf(buf, "%d-%d-%dT%d:%d:%fZ", - tms.tm_year + 1900 - tms.tm_mon + 1 - tms.tm_mday - tms.tm_hour - tms.tm_min + sprintf(buf, "%04d%02d%02dT%02d%02d%02dZ", + tms.tm_year + 1900, + tms.tm_mon + 1, + tms.tm_mday, + tms.tm_hour, + tms.tm_min, tms.tm_sec); return buf; } +std::string format_local_date_time(time_t time) +{ + struct tm tms; +#ifdef WIN32 + localtime_s(&tms, &time); +#else + localtime_r(&time, &tms); +#endif + char buf[80]; + strftime(buf, 80, "%x %X", &tms); + return buf; +} + time_t get_current_time_utc() { #ifdef WIN32 @@ -59,5 +77,3 @@ time_t get_current_time_utc() }; // namespace Utils }; // namespace Slic3r - -#endif /* slic3r_Utils_Time_hpp_ */ diff --git a/xs/src/slic3r/Utils/Time.hpp b/xs/src/slic3r/Utils/Time.hpp index 6b2fbf893..7b670bd3e 100644 --- a/xs/src/slic3r/Utils/Time.hpp +++ b/xs/src/slic3r/Utils/Time.hpp @@ -13,8 +13,11 @@ namespace Utils { extern time_t parse_time_ISO8601Z(const std::string &s); extern std::string format_time_ISO8601Z(time_t time); +// Format the date and time from an UTC time according to the active locales and a local time zone. +extern std::string format_local_date_time(time_t time); + // There is no gmtime() on windows. -time_t get_current_time_utc(); +extern time_t get_current_time_utc(); }; // namespace Utils }; // namespace Slic3r