diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index b9a6dbfee2..e01f11b263 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 91f02ab257..33c7ef82ff 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 1d02c8650b..3c77542735 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 0000000000..a4e4d846b6
--- /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 0000000000..70187ee995
--- /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 48d56ff119..0a163bf285 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 7fc3b8033a..8aa8cc53ff 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 c4123c7bba..a2b2328af6 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 6b2fbf8930..7b670bd3ee 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