diff --git a/src/gnome-utils/glade/preferences.glade b/src/gnome-utils/glade/preferences.glade index aeeb1a1238..872e011626 100644 --- a/src/gnome-utils/glade/preferences.glade +++ b/src/gnome-utils/glade/preferences.glade @@ -1640,7 +1640,7 @@ 6 True - 16 + 17 4 False 0 @@ -1776,8 +1776,8 @@ 1 3 - 12 - 13 + 13 + 14 fill fill @@ -1805,8 +1805,8 @@ 0 1 - 12 - 13 + 13 + 14 12 fill @@ -2071,8 +2071,8 @@ 0 1 - 13 - 14 + 14 + 15 fill @@ -2099,8 +2099,8 @@ 0 1 - 14 - 15 + 15 + 16 fill @@ -2128,8 +2128,8 @@ 0 1 - 15 - 16 + 16 + 17 12 fill @@ -2152,8 +2152,8 @@ 1 2 - 15 - 16 + 16 + 17 fill @@ -2182,6 +2182,97 @@ + + + + True + Auto-save time _interval: + True + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + gconf/general/autosave_interval_minutes + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 12 + 13 + 12 + fill + + + + + + + True + False + 6 + + + + True + The number of minutes until saving of the data file to harddisk will be started automatically. If zero, no saving will be started automatically. + True + 1 + 0 + False + GTK_UPDATE_ALWAYS + False + False + 3 0 99999 1 10 10 + + + 0 + False + True + + + + + + True + minutes + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + 1 + 3 + 12 + 13 + fill + fill + + False diff --git a/src/gnome-utils/gnc-main-window.c b/src/gnome-utils/gnc-main-window.c index c37b797900..a6844ba195 100644 --- a/src/gnome-utils/gnc-main-window.c +++ b/src/gnome-utils/gnc-main-window.c @@ -1271,12 +1271,140 @@ gnc_main_window_update_all_titles (void) NULL); } + +#define KEY_AUTOSAVE_SHOW_EXPLANATION "autosave_show_explanation" +#define KEY_AUTOSAVE_INTERVAL "autosave_interval_minutes" +#define AUTOSAVE_SOURCE_ID "autosave_source_id" +/* Here's how autosave works: + * + * Initially, the book is in state "undirty". Once the book changes + * state to "dirty", hence calling + * gnc_main_window_autosave_dirty(true), the auto-save timer is added + * and started. Now one out of two state changes can occur (well, + * three actually), depending on which event occurs first: + * + * - Either the book changes state to "undirty", hence calling + * gnc_main_window_autosave_dirty(false). In this case the auto-save + * timer is removed and all returns to the initial state with the book + * "undirty". + * + * - Or the auto-save timer hits its timeout, hence calling + * autosave_timeout_cb(). In this case gnc_file_save() is invoked, the + * auto-save timer is removed, and all returns to the initial state + * with the book "undirty". (As an exceptional addition to this, on + * the very first call to autosave_timeout_cb, if the key + * autosave_show_explanation is true, an explanation dialog of this + * feature is shown to the user, and the key autosave_show_explanation + * is set to false to not show this dialog again.) + * + * - As a third possibility, the book can also change state to + * "closing", in which case the autosave_remove_timer_cb is called + * that removes the auto-save timer and all returns to the initial + * state with the book "undirty". + */ +static gboolean autosave_timeout_cb(gpointer user_data) +{ + /* QofBook *book = user_data; */ + gboolean show_explanation; + + /* Is there already a save in progress? If yes, return FALSE so that + the timeout is automatically destroyed and the function will not + be called again. */ + if (gnc_file_save_in_progress() || !gnc_current_session_exist()) + return FALSE; + + /* Lookup gconf key to show an explanatory dialog the very first + time this becomes active. */ + show_explanation = + gnc_gconf_get_bool(GCONF_GENERAL, KEY_AUTOSAVE_SHOW_EXPLANATION, NULL); + if (show_explanation) { + guint interval_mins = + gnc_gconf_get_float(GCONF_GENERAL, KEY_AUTOSAVE_INTERVAL, NULL); + /* The autosave timeout has occurred for the very first + time. Explain this feature. */ + gnc_info_dialog(NULL, + _("Your data file needs to be saved to your harddisk to save your changes. GnuCash has a feature to save the file automatically every %d minutes. This feature is being activated the very first time right now. \n\n" + "If you like to change the time interval, you can do so under Edit -> Preferences -> General -> Auto-save time interval. If you like to switch off this feature, set the time interval to zero and no auto-save will occur anymore.\n\n" + "Press \"Close\" now so that your file will be saved."), + interval_mins); + /* Don't show this explanation again. */ + gnc_gconf_set_bool(GCONF_GENERAL, KEY_AUTOSAVE_SHOW_EXPLANATION, FALSE, NULL); + } + + /* Timeout has passed - save the file. */ + gnc_file_save(); + + /* Return FALSE so that the timeout is automatically destroyed and + the function will not be called again. */ + return FALSE; +} +static void +autosave_remove_timer_cb(QofBook *book, gpointer key, gpointer user_data) +{ + guint autosave_source_id = GPOINTER_TO_UINT(user_data); + /* Remove the timer that would have triggered the next autosave */ + if (autosave_source_id > 0) + g_source_remove (autosave_source_id); +} +static void autosave_remove_timer(QofBook *book) +{ + autosave_remove_timer_cb(book, AUTOSAVE_SOURCE_ID, + qof_book_get_data(book, AUTOSAVE_SOURCE_ID)); +} +static void autosave_add_timer(QofBook *book) +{ + guint interval_mins = + gnc_gconf_get_float(GCONF_GENERAL, KEY_AUTOSAVE_INTERVAL, NULL); + + /* Interval zero means auto-save is turned off. */ + if (interval_mins > 0) { + /* Add a new timer (timeout) that runs until the next autosave + timeout. */ + guint autosave_source_id = +#if GLIB_CHECK_VERSION(2, 14, 0) + /* g_timeout_add_seconds is much more suitable here, but is new in + glib-2.14. */ + g_timeout_add_seconds(interval_mins * 60, + autosave_timeout_cb, book); +#else + g_timeout_add(interval_mins * 60 * 1000, + autosave_timeout_cb, book); +#endif + g_debug("Added new auto save timer with id %d\n", autosave_source_id); + + /* Save the event source id for a potential removal, and also + set the callback upon book closing */ + qof_book_set_data_fin(book, AUTOSAVE_SOURCE_ID, + GUINT_TO_POINTER(autosave_source_id), + autosave_remove_timer_cb); + } +} +static void gnc_main_window_autosave_dirty (QofBook *book, gboolean dirty) +{ + if (dirty) { + /* Book state changed from non-dirty to dirty. Start the autosave + timer. */ + /* First stop a potentially running old timer. */ + autosave_remove_timer(book); + /* Add a new timer (timeout) that runs until the next autosave + timeout. */ + autosave_add_timer(book); + } else { + /* Book state changed from dirty to non-dirty (probably due to + saving). Delete the running autosave timer. */ + autosave_remove_timer(book); + } +} + static void gnc_main_window_book_dirty_cb (QofBook *book, gboolean dirty, gpointer user_data) { gnc_main_window_update_all_titles(); + + /* Auto-save feature */ + gnc_main_window_autosave_dirty(book, dirty); } static void diff --git a/src/gnome/schemas/apps_gnucash_general.schemas.in b/src/gnome/schemas/apps_gnucash_general.schemas.in index 0dee8870c9..a38b7c05c1 100644 --- a/src/gnome/schemas/apps_gnucash_general.schemas.in +++ b/src/gnome/schemas/apps_gnucash_general.schemas.in @@ -43,6 +43,30 @@ + + /schemas/apps/gnucash/general/autosave_show_explanation + /apps/gnucash/general/autosave_show_explanation + gnucash + bool + TRUE + + Show auto-save explanation + If active, GnuCash shows an explanation of the auto-save feature the first time that feature is started. Otherwise no extra explanation is shown. + + + + + /schemas/apps/gnucash/general/autosave_interval_minutes + /apps/gnucash/general/autosave_interval_minutes + gnucash + float + 3 + + Auto-save time interval + The number of minutes until saving of the data file to harddisk will be started automatically. If zero, no saving will be started automatically. + + + /schemas/apps/gnucash/general/negative_in_red /apps/gnucash/general/negative_in_red