1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
4
* Copyright (C) Philip Withnall 2010 <philip@tecnocode.co.uk>
6
* Almanah is free software: you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation, either version 3 of the License, or
9
* (at your option) any later version.
11
* Almanah 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.
16
* You should have received a copy of the GNU General Public License
17
* along with Almanah. If not, see <http://www.gnu.org/licenses/>.
22
#include <glib/gi18n.h>
25
#include "export-operation.h"
27
#include "storage-manager.h"
30
typedef gboolean (*ExportFunc) (AlmanahExportOperation *self, GFile *destination, AlmanahExportProgressCallback progress_callback,
31
gpointer progress_user_data, GCancellable *cancellable, GError **error);
34
const gchar *name; /* translatable */
35
const gchar *description; /* translatable */
36
GtkFileChooserAction action;
37
ExportFunc export_func;
40
static gboolean export_text_files (AlmanahExportOperation *self, GFile *destination, AlmanahExportProgressCallback progress_callback,
41
gpointer progress_user_data, GCancellable *cancellable, GError **error);
42
static gboolean export_database (AlmanahExportOperation *self, GFile *destination, AlmanahExportProgressCallback progress_callback,
43
gpointer progress_user_data, GCancellable *cancellable, GError **error);
45
static const ExportModeDetails export_modes[] = {
47
N_("Select a _folder to export the entries to as text files, one per entry, with names in the format 'yyyy-mm-dd', and no extension. "
48
"All entries will be exported, unencrypted in plain text format."),
49
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
52
N_("Select a _filename for a complete copy of the unencrypted Almanah Diary database to be given."),
53
GTK_FILE_CHOOSER_ACTION_SAVE,
57
static void almanah_export_operation_dispose (GObject *object);
59
struct _AlmanahExportOperationPrivate {
60
gint current_mode; /* index into export_modes */
64
G_DEFINE_TYPE (AlmanahExportOperation, almanah_export_operation, G_TYPE_OBJECT)
65
#define ALMANAH_EXPORT_OPERATION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_EXPORT_OPERATION, AlmanahExportOperationPrivate))
68
almanah_export_operation_class_init (AlmanahExportOperationClass *klass)
70
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
72
g_type_class_add_private (klass, sizeof (AlmanahExportOperationPrivate));
74
gobject_class->dispose = almanah_export_operation_dispose;
78
almanah_export_operation_init (AlmanahExportOperation *self)
80
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_EXPORT_OPERATION, AlmanahExportOperationPrivate);
81
self->priv->current_mode = -1; /* no mode selected */
85
almanah_export_operation_dispose (GObject *object)
87
AlmanahExportOperationPrivate *priv = ALMANAH_EXPORT_OPERATION (object)->priv;
89
if (priv->destination != NULL)
90
g_object_unref (priv->destination);
91
priv->destination = NULL;
93
/* Chain up to the parent class */
94
G_OBJECT_CLASS (almanah_export_operation_parent_class)->dispose (object);
97
AlmanahExportOperation *
98
almanah_export_operation_new (AlmanahExportOperationType type_id, GFile *destination)
100
AlmanahExportOperation *export_operation = g_object_new (ALMANAH_TYPE_EXPORT_OPERATION, NULL);
101
export_operation->priv->current_mode = type_id;
102
export_operation->priv->destination = g_object_ref (destination);
104
return export_operation;
108
AlmanahExportProgressCallback callback;
114
progress_idle_callback_cb (ProgressData *data)
116
g_assert (data->callback != NULL);
117
data->callback (data->date, data->user_data);
120
g_date_free (data->date);
121
g_slice_free (ProgressData, data);
127
progress_idle_callback (AlmanahExportProgressCallback callback, gpointer user_data, const GDate *date)
132
data = g_slice_new (ProgressData);
133
data->callback = callback;
134
data->user_data = user_data;
135
data->date = g_date_new_dmy (g_date_get_day (date), g_date_get_month (date), g_date_get_year (date));
137
/* We can't just use g_idle_add() here, since GSimpleAsyncResult uses default priority, so the finished callback will skip any outstanding
138
* progress callbacks in the main loop's priority queue, causing Bad Things to happen. We need to guarantee that no more progress callbacks
139
* will occur after the finished callback has been called; this is one hacky way of achieving that. */
140
source = g_idle_source_new ();
141
g_source_set_priority (source, G_PRIORITY_DEFAULT);
143
g_source_set_callback (source, (GSourceFunc) progress_idle_callback_cb, data, NULL);
144
g_source_attach (source, NULL);
146
g_source_unref (source);
150
export_text_files (AlmanahExportOperation *self, GFile *destination, AlmanahExportProgressCallback progress_callback, gpointer progress_user_data,
151
GCancellable *cancellable, GError **error)
153
AlmanahStorageManagerIter iter;
155
GtkTextBuffer *buffer;
156
gboolean success = FALSE;
157
GError *child_error = NULL;
159
/* Build a text buffer to use when getting all the entries */
160
buffer = gtk_text_buffer_new (NULL);
162
/* Iterate through the entries */
163
almanah_storage_manager_iter_init (&iter);
164
while ((entry = almanah_storage_manager_get_entries (almanah->storage_manager, &iter)) != NULL) {
166
gchar *filename, *content;
168
GtkTextIter start_iter, end_iter;
170
/* Get the filename */
171
almanah_entry_get_date (entry, &date);
172
filename = g_strdup_printf ("%04u-%02u-%02u", g_date_get_year (&date), g_date_get_month (&date), g_date_get_day (&date));
173
file = g_file_get_child (destination, filename);
176
/* Get the entry contents */
177
if (almanah_entry_get_content (entry, buffer, TRUE, &child_error) == FALSE) {
179
g_object_unref (file);
180
g_object_unref (entry);
184
g_object_unref (entry);
186
gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
187
content = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, FALSE);
189
/* Create the file */
190
if (g_file_replace_contents (file, content, strlen (content), NULL, FALSE,
191
G_FILE_CREATE_PRIVATE | G_FILE_CREATE_REPLACE_DESTINATION, NULL, NULL, &child_error) == FALSE) {
193
g_object_unref (file);
198
/* Progress callback */
199
progress_idle_callback (progress_callback, progress_user_data, &date);
201
g_object_unref (file);
204
/* Check for cancellation */
205
if (cancellable != NULL && g_cancellable_set_error_if_cancelled (cancellable, &child_error) == TRUE)
209
/* Check if the loop was broken due to an error */
210
if (child_error != NULL) {
211
g_propagate_error (error, child_error);
218
g_object_unref (buffer);
224
export_database (AlmanahExportOperation *self, GFile *destination, AlmanahExportProgressCallback progress_callback, gpointer progress_user_data,
225
GCancellable *cancellable, GError **error)
230
/* We ignore the progress callbacks, since this is a fairly fast operation, and it exports all the entries at once. */
232
/* Get the input file (current unencrypted database) */
233
source = g_file_new_for_path (almanah_storage_manager_get_filename (almanah->storage_manager, TRUE));
235
/* Copy the current database to that location */
236
success = g_file_copy (source, destination, G_FILE_COPY_OVERWRITE, cancellable, NULL, NULL, error);
238
g_object_unref (source);
244
AlmanahExportProgressCallback progress_callback;
245
gpointer progress_user_data;
249
export_data_free (ExportData *data)
251
g_slice_free (ExportData, data);
255
export_thread (GSimpleAsyncResult *result, AlmanahExportOperation *operation, GCancellable *cancellable)
257
GError *error = NULL;
258
ExportData *data = g_simple_async_result_get_op_res_gpointer (result);
260
/* Check to see if the operation's been cancelled already */
261
if (g_cancellable_set_error_if_cancelled (cancellable, &error) == TRUE) {
262
g_simple_async_result_set_from_error (result, error);
263
g_error_free (error);
267
/* Export and return */
268
if (export_modes[operation->priv->current_mode].export_func (operation, operation->priv->destination, data->progress_callback,
269
data->progress_user_data, cancellable, &error) == FALSE) {
270
g_simple_async_result_set_from_error (result, error);
271
g_error_free (error);
276
almanah_export_operation_run (AlmanahExportOperation *self, GCancellable *cancellable, AlmanahExportProgressCallback progress_callback,
277
gpointer progress_user_data,GAsyncReadyCallback callback, gpointer user_data)
279
GSimpleAsyncResult *result;
282
g_return_if_fail (ALMANAH_IS_EXPORT_OPERATION (self));
283
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
285
data = g_slice_new (ExportData);
286
data->progress_callback = progress_callback;
287
data->progress_user_data = progress_user_data;
289
result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, almanah_export_operation_run);
290
g_simple_async_result_set_op_res_gpointer (result, data, (GDestroyNotify) export_data_free);
291
g_simple_async_result_run_in_thread (result, (GSimpleAsyncThreadFunc) export_thread, G_PRIORITY_DEFAULT, cancellable);
292
g_object_unref (result);
296
almanah_export_operation_finish (AlmanahExportOperation *self, GAsyncResult *async_result, GError **error)
298
GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (async_result);
300
g_return_val_if_fail (ALMANAH_IS_EXPORT_OPERATION (self), FALSE);
301
g_return_val_if_fail (G_IS_ASYNC_RESULT (async_result), FALSE);
302
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
304
g_warn_if_fail (g_simple_async_result_get_source_tag (result) == almanah_export_operation_run);
306
if (g_simple_async_result_propagate_error (result, error) == TRUE)
313
almanah_export_operation_populate_model (GtkListStore *store, guint type_id_column, guint name_column, guint description_column, guint action_column)
317
for (i = 0; i < G_N_ELEMENTS (export_modes); i++) {
320
gtk_list_store_append (store, &iter);
321
gtk_list_store_set (store, &iter,
323
name_column, _(export_modes[i].name),
324
description_column, _(export_modes[i].description),
325
action_column, export_modes[i].action,