GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-autosave.c
1 /*
2  * gnc-autosave.c -- Functions related to the auto-save feature.
3  *
4  * Copyright (C) 2007 Christian Stimming <[email protected]>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, contact:
18  *
19  * Free Software Foundation Voice: +1-617-542-5942
20  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
21  * Boston, MA 02110-1301, USA [email protected]
22  */
23 
24 #include "config.h"
25 
26 #include "gnc-autosave.h"
27 
28 #include <glib/gi18n.h>
29 #include "gnc-engine.h"
30 #include "gnc-session.h"
31 #include "gnc-ui.h"
32 #include "gnc-file.h"
33 #include "gnc-window.h"
34 #include "gnc-prefs.h"
35 #include "gnc-main-window.h"
36 #include "gnc-gui-query.h"
37 
38 #define GNC_PREF_AUTOSAVE_SHOW_EXPLANATION "autosave-show-explanation"
39 #define GNC_PREF_AUTOSAVE_INTERVAL "autosave-interval-minutes"
40 #define AUTOSAVE_SOURCE_ID "autosave_source_id"
41 
42 #ifdef G_LOG_DOMAIN
43 # undef G_LOG_DOMAIN
44 #endif
45 #define G_LOG_DOMAIN "gnc.gui.autosave"
46 
47 static void
48 autosave_remove_timer_cb(QofBook *book, gpointer key, gpointer user_data);
49 
50 /* Here's how autosave works:
51  *
52  * Initially, the book is in state "undirty". Once the book changes
53  * state to "dirty", hence calling
54  * gnc_main_window_autosave_dirty(true), the auto-save timer is added
55  * and started. Now one out of two state changes can occur (well,
56  * three actually), depending on which event occurs first:
57  *
58  * - Either the book changes state to "undirty", hence calling
59  * gnc_main_window_autosave_dirty(false). In this case the auto-save
60  * timer is removed and all returns to the initial state with the book
61  * "undirty".
62  *
63  * - Or the auto-save timer hits its timeout, hence calling
64  * autosave_timeout_cb(). In this case gnc_file_save() is invoked, the
65  * auto-save timer is removed, and all returns to the initial state
66  * with the book "undirty". (As an exceptional addition to this, on
67  * the very first call to autosave_timeout_cb, if the key
68  * autosave_show_explanation is true, an explanation dialog of this
69  * feature is shown to the user, and the key autosave_show_explanation
70  * is set to false to not show this dialog again.)
71  *
72  * - As a third possibility, the book can also change state to
73  * "closing", in which case the autosave_remove_timer_cb is called
74  * that removes the auto-save timer and all returns to the initial
75  * state with the book "undirty".
76  */
77 
78 static gboolean autosave_confirm(GtkWidget *toplevel)
79 {
80  GtkWidget *dialog;
81  guint interval_mins =
82  gnc_prefs_get_float(GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTOSAVE_INTERVAL);
83  gboolean switch_off_autosave, show_expl_again, save_now;
84  gint response;
85 
86 #define YES_THIS_TIME 1
87 #define YES_ALWAYS 2
88 #define NO_NEVER 3
89 #define NO_NOT_THIS_TIME 4
90  /* The autosave timeout has occurred, and we should show the
91  explanation dialog. */
92  dialog =
93  gtk_message_dialog_new(GTK_WINDOW(toplevel),
94  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
95  GTK_MESSAGE_QUESTION,
96  GTK_BUTTONS_NONE,
97  "%s",
98  _("Save file automatically?"));
99  gtk_message_dialog_format_secondary_text
100  (GTK_MESSAGE_DIALOG(dialog),
101  ngettext("Your data file needs to be saved to your hard disk to save your changes. "
102  "GnuCash has a feature to save the file automatically every %d minute, "
103  "just as if you had pressed the \"Save\" button each time. \n\n"
104  "You can change the time interval or turn off this feature under "
105  "Edit -> Preferences -> General -> Auto-save time interval. \n\n"
106  "Should your file be saved automatically?",
107  "Your data file needs to be saved to your hard disk to save your changes. "
108  "GnuCash has a feature to save the file automatically every %d minutes, "
109  "just as if you had pressed the \"Save\" button each time. \n\n"
110  "You can change the time interval or turn off this feature under "
111  "Edit -> Preferences -> General -> Auto-save time interval. \n\n"
112  "Should your file be saved automatically?",
113  interval_mins),
114  interval_mins);
115  gtk_dialog_add_buttons(GTK_DIALOG(dialog),
116  _("_Yes, this time"), YES_THIS_TIME,
117  _("Yes, _always"), YES_ALWAYS,
118  _("No, n_ever"), NO_NEVER,
119  _("_No, not this time"), NO_NOT_THIS_TIME,
120  NULL);
121  gtk_dialog_set_default_response( GTK_DIALOG(dialog), NO_NOT_THIS_TIME);
122 
123  /* Run the modal dialog */
124  response = gtk_dialog_run( GTK_DIALOG( dialog ) );
125  gtk_widget_destroy( dialog );
126 
127  /* Evaluate the response */
128  switch (response)
129  {
130  case YES_THIS_TIME:
131  switch_off_autosave = FALSE;
132  show_expl_again = TRUE;
133  save_now = TRUE;
134  break;
135  case YES_ALWAYS:
136  switch_off_autosave = FALSE;
137  show_expl_again = FALSE;
138  save_now = TRUE;
139  break;
140  case NO_NEVER:
141  switch_off_autosave = TRUE;
142  show_expl_again = FALSE;
143  save_now = FALSE;
144  break;
145  default:
146  case NO_NOT_THIS_TIME:
147  switch_off_autosave = FALSE;
148  show_expl_again = TRUE;
149  save_now = FALSE;
150  };
151 
152  /* Should we show this explanation again? */
153  gnc_prefs_set_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTOSAVE_SHOW_EXPLANATION, show_expl_again);
154  g_debug("autosave_timeout_cb: Show explanation again=%s\n",
155  (show_expl_again ? "TRUE" : "FALSE"));
156 
157  /* Should we switch off autosave? */
158  if (switch_off_autosave)
159  {
160  gnc_prefs_set_float(GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTOSAVE_INTERVAL, 0);
161  g_debug("autosave_timeout_cb: User chose to disable auto-save.\n");
162  }
163 
164  return save_now;
165 }
166 
167 
168 static gboolean autosave_timeout_cb(gpointer user_data)
169 {
170  QofBook *book = user_data;
171  gboolean show_explanation;
172  gboolean save_now = TRUE;
173  GtkWidget *toplevel;
174 
175  g_debug("autosave_timeout_cb called\n");
176 
177  /* Is there already a save in progress? If yes, return FALSE so that
178  the timeout is automatically destroyed and the function will not
179  be called again. */
180  if (gnc_file_save_in_progress() || !gnc_current_session_exist()
181  || qof_book_is_readonly(book))
182  return FALSE;
183 
184  /* Store the current toplevel window for later use. */
185  toplevel = gnc_ui_get_toplevel();
186 
187  /* Lookup preference to show an explanatory dialog, if wanted. */
188  show_explanation =
189  gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTOSAVE_SHOW_EXPLANATION);
190  if (show_explanation)
191  {
192  save_now = autosave_confirm(toplevel);
193  }
194 
195  if (save_now)
196  {
197  g_debug("autosave_timeout_cb: Really trigger auto-save now.\n");
198 
199  /* Timeout has passed - save the file. */
200  if (GNC_IS_MAIN_WINDOW(toplevel))
201  gnc_main_window_set_progressbar_window( GNC_MAIN_WINDOW( toplevel ) );
202  else
203  g_debug("autosave_timeout_cb: toplevel is not a GNC_MAIN_WINDOW\n");
204  if (GNC_IS_WINDOW(toplevel))
205  gnc_window_set_progressbar_window( GNC_WINDOW( toplevel ) );
206  else
207  g_debug("autosave_timeout_cb: toplevel is not a GNC_WINDOW\n");
208 
209  gnc_file_save();
210 
212 
213  /* Return FALSE so that the timeout is automatically destroyed and
214  the function will not be called again. However, at least in my
215  glib-2.12.4 the timer event source still exists after returning
216  FALSE?! */
217  return FALSE;
218  }
219  else
220  {
221  g_debug("autosave_timeout_cb: No auto-save this time, let the timeout run again.\n");
222  /* Return TRUE so that the timeout is not removed but will be
223  triggered again after the next time interval. */
224  return TRUE;
225  }
226 }
227 
228 static void
229 autosave_remove_timer_cb(QofBook *book, gpointer key, gpointer user_data)
230 {
231  guint autosave_source_id = GPOINTER_TO_UINT(user_data);
232  gboolean res;
233  /* Remove the timer that would have triggered the next autosave */
234  if (autosave_source_id > 0)
235  {
236  res = g_source_remove (autosave_source_id);
237  g_debug("Removing auto save timer with id %d, result=%s\n",
238  autosave_source_id, (res ? "TRUE" : "FALSE"));
239 
240  /* Set the event source id to zero. */
241  qof_book_set_data_fin(book, AUTOSAVE_SOURCE_ID,
242  GUINT_TO_POINTER(0), autosave_remove_timer_cb);
243  }
244 }
245 
246 void gnc_autosave_remove_timer(QofBook *book)
247 {
248  autosave_remove_timer_cb(book, AUTOSAVE_SOURCE_ID,
249  qof_book_get_data(book, AUTOSAVE_SOURCE_ID));
250 }
251 
252 static void gnc_autosave_add_timer(QofBook *book)
253 {
254  guint interval_mins =
255  gnc_prefs_get_float(GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTOSAVE_INTERVAL);
256 
257  /* Interval zero means auto-save is turned off. */
258  if ( interval_mins > 0
259  && ( ! gnc_file_save_in_progress() )
260  && gnc_current_session_exist() )
261  {
262  /* Add a new timer (timeout) that runs until the next autosave
263  timeout. */
264  guint autosave_source_id =
265  g_timeout_add_seconds(interval_mins * 60,
266  autosave_timeout_cb, book);
267  g_debug("Adding new auto-save timer with id %d\n", autosave_source_id);
268 
269  /* Save the event source id for a potential removal, and also
270  set the callback upon book closing */
271  qof_book_set_data_fin(book, AUTOSAVE_SOURCE_ID,
272  GUINT_TO_POINTER(autosave_source_id),
273  autosave_remove_timer_cb);
274  }
275 }
276 
277 void gnc_autosave_dirty_handler (QofBook *book, gboolean dirty)
278 {
279  g_debug("gnc_main_window_autosave_dirty(dirty = %s)\n",
280  (dirty ? "TRUE" : "FALSE"));
281  if (dirty)
282  {
283  if (qof_book_is_readonly(book))
284  {
285  //g_debug("Book is read-only, ignoring dirty flag");
286  return;
287  }
288 
289  /* Book state changed from non-dirty to dirty. */
290  if (!qof_book_shutting_down(book))
291  {
292  /* Start the autosave timer.
293  First stop a potentially running old timer. */
294  gnc_autosave_remove_timer(book);
295  /* Add a new timer (timeout) that runs until the next autosave
296  timeout. */
297  gnc_autosave_add_timer(book);
298  }
299  else
300  {
301  g_debug("Shutting down book, ignoring dirty book");
302  }
303  }
304  else
305  {
306  /* Book state changed from dirty to non-dirty (probably due to
307  saving). Delete the running autosave timer. */
308  gnc_autosave_remove_timer(book);
309  }
310 }
Functions that are supported by all types of windows.
Functions for adding content to a window.
gboolean gnc_prefs_set_bool(const gchar *group, const gchar *pref_name, gboolean value)
Definition: gnc-prefs.c:282
void qof_book_set_data_fin(QofBook *book, const gchar *key, gpointer data, QofBookFinalCB)
gboolean gnc_prefs_set_float(const gchar *group, const gchar *pref_name, gdouble value)
Definition: gnc-prefs.c:313
All type declarations for the whole Gnucash engine.
Generic api to store and retrieve preferences.
gboolean qof_book_is_readonly(const QofBook *book)
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:196
GtkWidget * gnc_ui_get_toplevel(void)
gboolean qof_book_shutting_down(const QofBook *book)
gpointer qof_book_get_data(const QofBook *book, const gchar *key)
void gnc_main_window_set_progressbar_window(GncMainWindow *window)
gdouble gnc_prefs_get_float(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:227