Code

Committing fixed patch for autosave. Fixes bug #171092.
authorjoncruz <joncruz@users.sourceforge.net>
Thu, 12 Jun 2008 09:53:24 +0000 (09:53 +0000)
committerjoncruz <joncruz@users.sourceforge.net>
Thu, 12 Jun 2008 09:53:24 +0000 (09:53 +0000)
src/inkscape.cpp
src/inkscape.h
src/preferences-skeleton.h
src/ui/dialog/inkscape-preferences.cpp
src/ui/dialog/inkscape-preferences.h

index b4f6808ce758d3b6df72a1f22e929824e2bfb566..e3667e1f3f66bdf7824d856d547fdb30046cf76a 100644 (file)
@@ -41,6 +41,8 @@ using Inkscape::Extension::Internal::PrintWin32;
 
 #include <gtk/gtkmain.h>
 #include <gtk/gtkmessagedialog.h>
+#include <glib.h>
+#include <glib/gstdio.h>
 
 #include <glibmm/i18n.h>
 #include <string>
@@ -61,8 +63,12 @@ using Inkscape::Extension::Internal::PrintWin32;
 #include "prefs-utils.h"
 #include "xml/repr.h"
 #include "io/sys.h"
+#include "message-stack.h"
 
 #include "extension/init.h"
+#include "extension/db.h"
+#include "extension/output.h"
+#include "extension/system.h"
 
 static Inkscape::Application *inkscape = NULL;
 
@@ -287,6 +293,167 @@ inkscape_class_init (Inkscape::ApplicationClass * klass)
     klass->deactivate_desktop = inkscape_deactivate_desktop_private;
 }
 
+#ifdef WIN32
+typedef int uid_t;
+#define getuid() 0
+#endif
+
+/**
+ * static gint inkscape_autosave(gpointer);
+ *
+ * Callback passed to g_timeout_add_seconds()
+ * Responsible for autosaving all open documents
+ */
+static gint inkscape_autosave(gpointer)
+{
+    // Use UID for separating autosave-documents between users if directory is multiuser
+    uid_t uid = getuid();
+
+    Glib::ustring autosave_dir;
+    {
+        gchar const* tmp = prefs_get_string_attribute("options.autosave", "path");
+        if ( tmp ) {
+            autosave_dir = tmp;
+        } else {
+            autosave_dir = Glib::get_tmp_dir();
+        }
+    }
+
+    GDir *autosave_dir_ptr = g_dir_open(autosave_dir.c_str(), 0, NULL);
+    if( !autosave_dir_ptr ){
+        g_warning("Cannot open autosave directory!");
+        return TRUE;
+    }
+
+    time_t sptime = time(NULL);
+    struct tm *sptm = localtime(&sptime);
+    gchar sptstr[256];
+    strftime(sptstr, 256, "%Y_%m_%d_%H_%M_%S", sptm);
+
+    gint autosave_max = prefs_get_int_attribute("options.autosave", "max", 10);
+
+    gint docnum = 0;
+
+    SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Autosaving documents..."));
+    for (GSList *docList = inkscape->documents; docList; docList = docList->next) {
+        ++docnum;
+
+        // TODO replace this with SP_DOCUMENT() when linking issues are addressed:
+        SPDocument *doc = static_cast<SPDocument *>(docList->data);
+        Inkscape::XML::Node *repr = sp_document_repr_root(doc);
+        // g_debug("Document %d: \"%s\" %s", docnum, doc ? doc->name : "(null)", doc ? (doc->isModifiedSinceSave() ? "(dirty)" : "(clean)") : "(null)");
+
+        if (doc->isModifiedSinceSave()) {
+            gchar *oldest_autosave = 0;
+            const gchar  *filename = 0;
+            struct stat sb;
+            time_t min_time = 0;
+            gint count = 0;
+            
+            // Look for previous autosaves
+            gchar* baseName = g_strdup_printf( "inkscape-autosave-%d", uid );
+            g_dir_rewind(autosave_dir_ptr);
+            while( (filename = g_dir_read_name(autosave_dir_ptr)) != NULL ){
+                if ( strncmp(filename, baseName, strlen(baseName)) == 0 ){
+                    gchar* full_path = g_build_filename( autosave_dir.c_str(), filename, NULL );
+                    if ( g_stat(full_path, &sb) != -1 ) {
+                        if ( difftime(sb.st_ctime, min_time) < 0 || min_time == 0 ){
+                            min_time = sb.st_ctime;
+                            if ( oldest_autosave ) {
+                                g_free(oldest_autosave);
+                            }
+                            oldest_autosave = g_strdup(full_path);
+                        }
+                        count ++;
+                    }
+                    g_free(full_path);
+                }
+            }
+
+            // g_debug("%d previous autosaves exists. Max = %d", count, autosave_max);
+            
+            // Have we reached the limit for number of autosaves?
+            if ( count >= autosave_max ){
+                // Remove the oldest file
+                if ( oldest_autosave ) {
+                    unlink(oldest_autosave);
+                }
+            }
+
+            if ( oldest_autosave ) {
+                g_free(oldest_autosave);
+                oldest_autosave = 0;
+            }
+
+
+            // Set the filename we will actually save to
+            g_free(baseName);
+            baseName = g_strdup_printf("inkscape-autosave-%d-%s-%03d.svg", uid, sptstr, docnum);
+            gchar* full_path = g_build_filename(autosave_dir.c_str(), baseName, NULL);
+            g_free(baseName);
+            baseName = 0;
+
+            // g_debug("Filename: %s", full_path);
+
+            // Try to save the file
+            FILE *file = Inkscape::IO::fopen_utf8name(full_path, "w");
+            gchar *errortext = 0;
+            if (file) {
+                try{
+                    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(full_path);
+                    errortext = g_strdup_printf(_("Autosave failed! File %s could not be saved."), safeUri);
+                    g_free(safeUri);
+                }
+                fclose(file);
+            }
+            else {
+                gchar *safeUri = Inkscape::IO::sanitizeString(full_path);
+                errortext = g_strdup_printf(_("Autosave failed! File %s could not be saved."), safeUri);
+                g_free(safeUri);
+            }
+
+            if (errortext) {
+                SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, errortext);
+                g_warning("%s", errortext);
+                g_free(errortext);
+            }
+
+            g_free(full_path);
+        }
+    }
+    g_dir_close(autosave_dir_ptr);
+
+    SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Autosave complete."));
+
+    return TRUE;
+}
+
+void inkscape_autosave_init()
+{
+    static guint32 autosave_timeout_id = 0;
+
+    // Turn off any previously initiated timeouts
+    if ( autosave_timeout_id ) {
+        g_source_remove(autosave_timeout_id);
+        autosave_timeout_id = 0;
+    }
+
+    // g_debug("options.autosave.enable = %lld", prefs_get_int_attribute_limited("options.autosave", "enable", 1, 0, 1));
+    // Is autosave enabled?
+    if( prefs_get_int_attribute_limited("options.autosave", "enable", 1, 0, 1) != 1 ){
+        autosave_timeout_id = 0;
+    } else {
+        // Turn on autosave
+        guint32 timeout = prefs_get_int_attribute("options.autosave", "interval", 10) * 60;
+        // g_debug("options.autosave.interval = %lld", prefs_get_int_attribute("options.autosave", "interval", 10));
+        autosave_timeout_id = g_timeout_add_seconds(timeout, inkscape_autosave, NULL);
+    }
+}
+
 
 static void
 inkscape_init (SPObject * object)
@@ -623,6 +790,8 @@ inkscape_application_init (const gchar *argv0, gboolean use_gui)
     /* Initialize the extensions */
     Inkscape::Extension::init();
 
+    inkscape_autosave_init();
+
     return;
 }
 
index 12f101901873a7629d4303020f54a9e861e802ff..90737cc41b130550fcd3674b508190ed3d1049e0 100644 (file)
@@ -29,6 +29,8 @@ namespace Inkscape {
 
 #define INKSCAPE inkscape_get_instance()
 
+void inkscape_autosave_init();
+
 void inkscape_application_init (const gchar *argv0, gboolean use_gui);
 
 bool inkscape_load_config (const gchar *filename, Inkscape::XML::Document *config, const gchar *skeleton, unsigned int skel_size, const gchar *e_notreg, const gchar *e_notxml, const gchar *e_notsp, const gchar *warn);
index d16e24e4434ff2f4537f62f261a44025cd42fb40..3fcb7f56b96f6fc764317768bfa2e5a2b66ba2c4 100644 (file)
@@ -264,6 +264,7 @@ static char const preferences_skeleton[] =
 "           masks=\"65535\"/>\n" // 0x0000ffff
 "    <group id=\"svgoutput\" usenamedcolors=\"0\" numericprecision=\"8\" minimumexponent=\"-8\" inlineattrs=\"0\" indent=\"2\" allowrelativecoordinates=\"1\" allowshorthands=\"1\" forcerepeatcommands=\"0\"/>\n"
 "    <group id=\"forkgradientvectors\" value=\"1\"/>\n"
+"    <group id=\"autosave\" enable=\"0\" interval=\"10\" path=\"\" max=\"10\"/>\n"
 "    <group id=\"grids\""
 "      no_emphasize_when_zoomedout=\"0\">\n"
 "      <group id=\"xy\" "
index abea076412b355ca2d26373f38c4551ccf4856bd..82bde38346924ea1685f4b0b44dea11f8941bd31 100644 (file)
@@ -986,6 +986,30 @@ void InkscapePreferences::initPageMisc()
     _misc_simpl.init("options.simplifythreshold", "value", 0.0001, 1.0, 0.0001, 0.0010, 0.0010, false, false);
 
 
+    // Autosave options 
+    _misc_autosave_enable.init( _("Enable auto-save of document"), "options.autosave", "enable", false);
+    _page_misc.add_line(false, "", _misc_autosave_enable, "", _("Automatically saves the current document to disk at a given interval, thus minimizing loss at a crash"), false);
+    _misc_autosave_interval.init("options.autosave", "interval", 1.0, 10800.0, 1.0, 10.0, 10.0, true, false);
+    _page_misc.add_line(true, _("Interval (in minutes):"), _misc_autosave_interval, "", _("Sets the interval (in minutes) at which a workspace will be automatically saved to disk"), false);
+    _misc_autosave_path.init("options.autosave", "path", true);
+    _page_misc.add_line(true, _("Path:"), _misc_autosave_path, "", _("Sets the directory where autosaves will be written"), false);
+    _misc_autosave_max.init("options.autosave", "max", 1.0, 100.0, 1.0, 10.0, 10.0, true, false);
+    _page_misc.add_line(true, _("Maximum number of autosaves"), _misc_autosave_max, "", _("Allows for limiting the space used by autosaves, by setting a maximum number of allowed files"), false);
+
+    /* When changing the interval or enabling/disabling the autosave function,
+     * update our running configuration
+     *
+     * FIXME!
+     * the inkscape_autosave_init should be called AFTER the values have been changed
+     * (which cannot be guaranteed from here)
+     *
+     * For now, autosave-settings will not change until restart
+     */
+    /*
+    _misc_autosave_enable.signal_toggled().connect( sigc::ptr_fun(inkscape_autosave_init), TRUE );
+    _misc_autosave_interval.signal_changed().connect( sigc::ptr_fun(inkscape_autosave_init), TRUE );
+    */
+
     // -----------
 
     _misc_bitmap_autoreload.init(_("Automatically reload bitmaps"), "options.bitmapautoreload", "value", true);
index ac378bf98e7c1d680c35fdaecbe5cbbb15026a34..22944d42a7b061dda43a1bf0cb17015fa0f4c83d 100644 (file)
@@ -169,6 +169,10 @@ protected:
     PrefCombo       _misc_overs_bitmap;
     PrefCombo       _misc_bitmap_editor;
     PrefCheckButton _misc_bitmap_autoreload;
+    PrefCheckButton _misc_autosave_enable;
+    PrefSpinButton  _misc_autosave_interval;
+    PrefEntry       _misc_autosave_path;
+    PrefSpinButton  _misc_autosave_max;
 
     Gtk::ComboBoxText   _cms_display_profile;
     PrefCheckButton     _cms_from_display;