summaryrefslogtreecommitdiffstats
path: root/src/auto-save.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/auto-save.cpp193
1 files changed, 193 insertions, 0 deletions
diff --git a/src/auto-save.cpp b/src/auto-save.cpp
new file mode 100644
index 0000000..3ababc9
--- /dev/null
+++ b/src/auto-save.cpp
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Auto-save
+ *
+ * Copyright (C) 2020 Tavmjong Bah
+ *
+ * Re-write of code formerly in inkscape.cpp and originally written by Jon Cruz and others.
+ *
+ * The contents of this file may be used under the GNU General Public License Version 2 or later.
+ *
+ */
+
+#include <ctime>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+#include <glibmm/i18n.h> // Internationalization
+
+#include "auto-save.h"
+#include "document.h"
+#include "inkscape-application.h"
+#include "preferences.h"
+
+#include "extension/output.h"
+#include "io/sys.h"
+#include "xml/repr.h"
+
+#ifdef _WIN32
+#include <process.h>
+typedef int uid_t;
+#define getuid() 0
+#endif
+
+namespace Inkscape {
+
+void
+AutoSave::init(InkscapeApplication* app)
+{
+ _app = app;
+ start();
+}
+
+void
+AutoSave::start()
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ static sigc::connection autosave_connection;
+
+ // Turn off any previous timeout.
+ autosave_connection.disconnect();
+
+ if (prefs->getBool("/options/autosave/enable", true)) {
+ // Turn on autosave (timeout is in seconds).
+ guint32 timeout = std::max(prefs->getInt("/options/autosave/interval", 10), 1) * 60;
+ if (timeout > 60 * 60 * 24) {
+ // Sanity check
+ std::cerr << "AutoSave::start: auto-save interval set to greater than one day. Not enabling." << std::endl;
+ return;
+ }
+ autosave_connection = Glib::signal_timeout().connect_seconds(sigc::mem_fun(*this, &AutoSave::save), timeout);
+ }
+}
+
+bool
+AutoSave::save()
+{
+ std::vector<SPDocument *> documents = _app->get_documents();
+ if (documents.empty()) {
+ // Nothing to save!
+ return true;
+ }
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
+ // Find/create autosave directory
+ std::string autosave_dir = prefs->getString("/options/autosave/path"); // Filenames should be std::string
+ if (autosave_dir.empty()) {
+ autosave_dir = Glib::build_filename(Glib::get_user_cache_dir(), "inkscape");
+ }
+
+ Glib::RefPtr<Gio::File> dir_file = Gio::File::create_for_path(autosave_dir);
+ if (!dir_file->query_exists()) {
+ if (!dir_file->make_directory_with_parents()) {
+ std::cerr << "InkscapeApplication::document_autosave: Failed to create autosave directory: " << Glib::filename_to_utf8(autosave_dir) << std::endl;
+ return true;
+ }
+ }
+
+ // Get unique info
+ uid_t uid = getuid(); // Avoid naming conflicts between users
+ int pid = ::getpid(); // Avoid naming conflicts between processes
+
+ // Get time stamp
+ std::time_t time = std::time(nullptr);
+ std::tm tm = *std::localtime(&time);
+ std::stringstream datetime;
+ datetime << std::put_time(&tm, "%Y_%m_%d_%H_%M_%S");
+
+ int docnum = 0;
+ int autosave_max = prefs->getInt("/options/autosave/max", 10);
+ for (auto document : documents) {
+
+ ++docnum; // Give each document a unique number.
+
+ if (document->isModifiedSinceAutoSave()) {
+
+ std::string base_name = "automatic-save-" + std::to_string(uid);
+
+ // The following we do for each document (rather wasteful...) so that
+ // we make room for each document that needs saving. We probably should
+ // be counting per document and not overall documents.
+
+ // Open directory
+ Glib::Dir directory(autosave_dir);
+ std::vector<std::string> file_names(directory.begin(), directory.end());
+
+ // Sort them so that oldest are last (file name encodes time).
+ std::sort(file_names.begin(), file_names.end(), std::greater<std::string>());
+
+ // Delete oldest files.
+ int count = 0;
+ for (auto &file_name : file_names) {
+ if (file_name.compare(0, base_name.size(), base_name) == 0) {
+ ++count;
+ if (count >= autosave_max) {
+ // Delete (making room for one more).
+ std::string path = Glib::build_filename(autosave_dir, file_name);
+ if (unlink(path.c_str()) == -1) {
+ std::cerr << "InkscapeApplication::document_autosave: Failed to unlink file: "
+ << path << ": " << strerror(errno) << std::endl;
+ }
+ }
+ }
+ }
+
+ // Construct save file path
+ // datetime MUST happen first, otherwise the above sorting will fail
+ std::string filename = base_name + "-" + datetime.str() + "-" + std::to_string(pid) + "-" + std::to_string(docnum) + ".svg";
+ std::string path = Glib::build_filename(autosave_dir, filename.c_str());
+
+ // Try to save the file
+ // Following code needs to be reviewed
+ FILE *file = Inkscape::IO::fopen_utf8name(path.c_str(), "w");
+ gchar *errortext = nullptr;
+ if (file) {
+ try {
+ Inkscape::XML::Node *repr = document->getReprRoot();
+ sp_repr_save_stream(repr->document(), file, SP_SVG_NS_URI);
+ } catch (Inkscape::Extension::Output::no_extension_found &e) {
+ errortext = g_strdup(_("Autosave failed! Could not find inkscape extension to save document."));
+ } catch (Inkscape::Extension::Output::save_failed &e) {
+ gchar *safeUri = Inkscape::IO::sanitizeString(path.c_str());
+ errortext = g_strdup_printf(_("Autosave failed! File %s could not be saved."), safeUri);
+ g_free(safeUri);
+ }
+ fclose(file);
+ } else {
+ gchar *safeUri = Inkscape::IO::sanitizeString(path.c_str());
+ errortext = g_strdup_printf(_("Autosave failed! File %s could not be saved."), safeUri);
+ g_free(safeUri);
+ }
+
+ if (errortext) {
+ g_warning("%s", errortext);
+ g_free(errortext);
+ } else {
+ document->setModifiedSinceAutoSaveFalse();
+ }
+ }
+ } // Loop over documents
+
+ return true;
+}
+
+void
+AutoSave::restart()
+{
+ AutoSave::getInstance().start();
+}
+
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :