No issues found
1 /*
2 * e-composer-autosave.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <libebackend/libebackend.h>
24
25 #include <libevolution-utils/e-alert-dialog.h>
26 #include <composer/e-msg-composer.h>
27
28 #include "e-autosave-utils.h"
29
30 /* Standard GObject macros */
31 #define E_TYPE_COMPOSER_AUTOSAVE \
32 (e_composer_autosave_get_type ())
33 #define E_COMPOSER_AUTOSAVE(obj) \
34 (G_TYPE_CHECK_INSTANCE_CAST \
35 ((obj), E_TYPE_COMPOSER_AUTOSAVE, EComposerAutosave))
36
37 #define AUTOSAVE_INTERVAL 60 /* seconds */
38
39 typedef struct _EComposerAutosave EComposerAutosave;
40 typedef struct _EComposerAutosaveClass EComposerAutosaveClass;
41
42 struct _EComposerAutosave {
43 EExtension parent;
44
45 GCancellable *cancellable;
46 guint timeout_id;
47
48 /* Composer contents have changed since
49 * the last auto-save or explicit save. */
50 gboolean changed;
51
52 /* Prevent error dialogs from piling up. */
53 gboolean error_shown;
54 };
55
56 struct _EComposerAutosaveClass {
57 EExtensionClass parent_class;
58 };
59
60 /* Forward Declarations */
61 GType e_composer_autosave_get_type (void);
62 void e_composer_autosave_type_register (GTypeModule *type_module);
63
64 G_DEFINE_DYNAMIC_TYPE (
65 EComposerAutosave,
66 e_composer_autosave,
67 E_TYPE_EXTENSION)
68
69 static void
70 composer_autosave_finished_cb (EMsgComposer *composer,
71 GAsyncResult *result,
72 EComposerAutosave *autosave)
73 {
74 GFile *snapshot_file;
75 GError *error = NULL;
76
77 snapshot_file = e_composer_get_snapshot_file (composer);
78 e_composer_save_snapshot_finish (composer, result, &error);
79
80 /* Return silently if we were cancelled. */
81 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
82 g_error_free (error);
83
84 else if (error != NULL) {
85 gchar *basename;
86
87 if (G_IS_FILE (snapshot_file))
88 basename = g_file_get_basename (snapshot_file);
89 else
90 basename = g_strdup (" ");
91
92 /* Only show one error dialog at a time. */
93 if (!autosave->error_shown) {
94 autosave->error_shown = TRUE;
95 e_alert_run_dialog_for_args (
96 GTK_WINDOW (composer),
97 "mail-composer:no-autosave",
98 basename, error->message, NULL);
99 autosave->error_shown = FALSE;
100 } else
101 g_warning ("%s: %s", basename, error->message);
102
103 g_free (basename);
104 g_error_free (error);
105 }
106
107 g_object_unref (autosave);
108 }
109
110 static gboolean
111 composer_autosave_timeout_cb (EComposerAutosave *autosave)
112 {
113 EExtensible *extensible;
114
115 extensible = e_extension_get_extensible (E_EXTENSION (autosave));
116
117 /* User may have reverted or explicitly saved
118 * the changes since the timeout was scheduled. */
119 if (autosave->changed) {
120
121 /* Cancel the previous snapshot if it's still in
122 * progress and start a new snapshot operation. */
123 g_cancellable_cancel (autosave->cancellable);
124 g_object_unref (autosave->cancellable);
125 autosave->cancellable = g_cancellable_new ();
126
127 e_composer_save_snapshot (
128 E_MSG_COMPOSER (extensible),
129 autosave->cancellable,
130 (GAsyncReadyCallback)
131 composer_autosave_finished_cb,
132 g_object_ref (autosave));
133 }
134
135 autosave->timeout_id = 0;
136 autosave->changed = FALSE;
137
138 return FALSE;
139 }
140
141 static void
142 composer_autosave_changed_cb (EComposerAutosave *autosave)
143 {
144 GtkhtmlEditor *editor;
145 EExtensible *extensible;
146
147 extensible = e_extension_get_extensible (E_EXTENSION (autosave));
148
149 editor = GTKHTML_EDITOR (extensible);
150 autosave->changed = gtkhtml_editor_get_changed (editor);
151
152 if (autosave->changed && autosave->timeout_id == 0)
153 autosave->timeout_id = g_timeout_add_seconds (
154 AUTOSAVE_INTERVAL, (GSourceFunc)
155 composer_autosave_timeout_cb, autosave);
156 }
157
158 static void
159 composer_autosave_dispose (GObject *object)
160 {
161 EComposerAutosave *autosave;
162 GObjectClass *parent_class;
163
164 autosave = E_COMPOSER_AUTOSAVE (object);
165
166 /* Cancel any snapshots in progress. */
167 if (autosave->cancellable != NULL) {
168 g_cancellable_cancel (autosave->cancellable);
169 g_object_unref (autosave->cancellable);
170 autosave->cancellable = NULL;
171 }
172
173 if (autosave->timeout_id > 0) {
174 g_source_remove (autosave->timeout_id);
175 autosave->timeout_id = 0;
176 }
177
178 /* Chain up to parent's dispose() method. */
179 parent_class = G_OBJECT_CLASS (e_composer_autosave_parent_class);
180 parent_class->dispose (object);
181 }
182
183 static void
184 composer_autosave_constructed (GObject *object)
185 {
186 EExtensible *extensible;
187 GObjectClass *parent_class;
188
189 /* Chain up to parent's constructed() method. */
190 parent_class = G_OBJECT_CLASS (e_composer_autosave_parent_class);
191 parent_class->constructed (object);
192
193 extensible = e_extension_get_extensible (E_EXTENSION (object));
194
195 g_signal_connect_swapped (
196 extensible, "notify::changed",
197 G_CALLBACK (composer_autosave_changed_cb), object);
198 }
199
200 static void
201 e_composer_autosave_class_init (EComposerAutosaveClass *class)
202 {
203 GObjectClass *object_class;
204 EExtensionClass *extension_class;
205
206 object_class = G_OBJECT_CLASS (class);
207 object_class->dispose = composer_autosave_dispose;
208 object_class->constructed = composer_autosave_constructed;
209
210 extension_class = E_EXTENSION_CLASS (class);
211 extension_class->extensible_type = E_TYPE_MSG_COMPOSER;
212 }
213
214 static void
215 e_composer_autosave_class_finalize (EComposerAutosaveClass *class)
216 {
217 }
218
219 static void
220 e_composer_autosave_init (EComposerAutosave *autosave)
221 {
222 autosave->cancellable = g_cancellable_new ();
223 }
224
225 void
226 e_composer_autosave_type_register (GTypeModule *type_module)
227 {
228 /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
229 * function, so we have to wrap it with a public function in
230 * order to register types from a separate compilation unit. */
231 e_composer_autosave_register_type (type_module);
232 }