aboutsummaryrefslogtreecommitdiffstats
path: root/widgets
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@src.gnome.org>2009-03-26 12:48:21 +0800
committerMatthew Barnes <mbarnes@src.gnome.org>2009-03-26 12:48:21 +0800
commitc05c973cff53769ef575bfc5257a2a414117b323 (patch)
tree0fbf2bedde0eccc6062d2240890ab53875f6aa71 /widgets
parent6e163b39c75dbba470d073b4f79a897aa6fb0e54 (diff)
downloadgsoc2013-evolution-c05c973cff53769ef575bfc5257a2a414117b323.tar
gsoc2013-evolution-c05c973cff53769ef575bfc5257a2a414117b323.tar.gz
gsoc2013-evolution-c05c973cff53769ef575bfc5257a2a414117b323.tar.bz2
gsoc2013-evolution-c05c973cff53769ef575bfc5257a2a414117b323.tar.lz
gsoc2013-evolution-c05c973cff53769ef575bfc5257a2a414117b323.tar.xz
gsoc2013-evolution-c05c973cff53769ef575bfc5257a2a414117b323.tar.zst
gsoc2013-evolution-c05c973cff53769ef575bfc5257a2a414117b323.zip
Saving progress again on the attachment rewrite.
svn path=/branches/kill-bonobo/; revision=37476
Diffstat (limited to 'widgets')
-rw-r--r--widgets/misc/e-attachment-dialog.c14
-rw-r--r--widgets/misc/e-attachment-icon-view.c42
-rw-r--r--widgets/misc/e-attachment-store.c303
-rw-r--r--widgets/misc/e-attachment-store.h4
-rw-r--r--widgets/misc/e-attachment-tree-view.c44
-rw-r--r--widgets/misc/e-attachment-view.c151
-rw-r--r--widgets/misc/e-attachment-view.h3
-rw-r--r--widgets/misc/e-attachment.c1911
-rw-r--r--widgets/misc/e-attachment.h58
9 files changed, 1571 insertions, 959 deletions
diff --git a/widgets/misc/e-attachment-dialog.c b/widgets/misc/e-attachment-dialog.c
index da3c3a855c..a844c228eb 100644
--- a/widgets/misc/e-attachment-dialog.c
+++ b/widgets/misc/e-attachment-dialog.c
@@ -58,20 +58,24 @@ attachment_dialog_update (EAttachmentDialog *dialog)
attachment = e_attachment_dialog_get_attachment (dialog);
- if (E_IS_ATTACHMENT (attachment)) {
+ if (attachment != NULL) {
file_info = e_attachment_get_file_info (attachment);
- content_type = e_attachment_get_content_type (attachment);
- display_name = e_attachment_get_display_name (attachment);
description = e_attachment_get_description (attachment);
disposition = e_attachment_get_disposition (attachment);
} else {
file_info = NULL;
- content_type = NULL;
- display_name = NULL;
description = NULL;
disposition = NULL;
}
+ if (file_info != NULL) {
+ content_type = g_file_info_get_content_type (file_info);
+ display_name = g_file_info_get_display_name (file_info);
+ } else {
+ content_type = NULL;
+ display_name = NULL;
+ }
+
if (content_type != NULL) {
gchar *comment;
gchar *mime_type;
diff --git a/widgets/misc/e-attachment-icon-view.c b/widgets/misc/e-attachment-icon-view.c
index 6d27429743..f6e42b2fbe 100644
--- a/widgets/misc/e-attachment-icon-view.c
+++ b/widgets/misc/e-attachment-icon-view.c
@@ -21,6 +21,7 @@
#include "e-attachment-icon-view.h"
+#include <glib/gi18n.h>
#include <gdk/gdkkeysyms.h>
#include "e-attachment.h"
@@ -163,6 +164,15 @@ attachment_icon_view_popup_menu (GtkWidget *widget)
return TRUE;
}
+static void
+attachment_icon_view_item_activated (GtkIconView *icon_view,
+ GtkTreePath *path)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (icon_view);
+
+ e_attachment_view_open_path (view, path, NULL);
+}
+
static EAttachmentViewPrivate *
attachment_icon_view_get_private (EAttachmentView *view)
{
@@ -265,6 +275,7 @@ attachment_icon_view_class_init (EAttachmentIconViewClass *class)
{
GObjectClass *object_class;
GtkWidgetClass *widget_class;
+ GtkIconViewClass *icon_view_class;
parent_class = g_type_class_peek_parent (class);
g_type_class_add_private (class, sizeof (EAttachmentViewPrivate));
@@ -282,6 +293,9 @@ attachment_icon_view_class_init (EAttachmentIconViewClass *class)
widget_class->drag_data_received = attachment_icon_view_drag_data_received;
widget_class->popup_menu = attachment_icon_view_popup_menu;
+ icon_view_class = GTK_ICON_VIEW_CLASS (class);
+ icon_view_class->item_activated = attachment_icon_view_item_activated;
+
g_object_class_override_property (
object_class, PROP_EDITABLE, "editable");
}
@@ -317,7 +331,7 @@ attachment_icon_view_init (EAttachmentIconView *icon_view)
renderer = gtk_cell_renderer_pixbuf_new ();
g_object_set (renderer, "stock-size", GTK_ICON_SIZE_DIALOG, NULL);
- gtk_cell_layout_pack_start (cell_layout, renderer, FALSE);
+ gtk_cell_layout_pack_start (cell_layout, renderer, TRUE);
gtk_cell_layout_add_attribute (
cell_layout, renderer, "gicon",
@@ -327,11 +341,35 @@ attachment_icon_view_init (EAttachmentIconView *icon_view)
g_object_set (
renderer, "alignment", PANGO_ALIGN_CENTER,
"xalign", 0.5, NULL);
- gtk_cell_layout_pack_start (cell_layout, renderer, FALSE);
+ gtk_cell_layout_pack_start (cell_layout, renderer, TRUE);
gtk_cell_layout_add_attribute (
cell_layout, renderer, "text",
E_ATTACHMENT_STORE_COLUMN_CAPTION);
+
+ renderer = gtk_cell_renderer_progress_new ();
+ g_object_set (renderer, "text", _("Loading"), NULL);
+ gtk_cell_layout_pack_start (cell_layout, renderer, TRUE);
+
+ gtk_cell_layout_add_attribute (
+ cell_layout, renderer, "value",
+ E_ATTACHMENT_STORE_COLUMN_PERCENT);
+
+ gtk_cell_layout_add_attribute (
+ cell_layout, renderer, "visible",
+ E_ATTACHMENT_STORE_COLUMN_LOADING);
+
+ renderer = gtk_cell_renderer_progress_new ();
+ g_object_set (renderer, "text", _("Saving"), NULL);
+ gtk_cell_layout_pack_start (cell_layout, renderer, TRUE);
+
+ gtk_cell_layout_add_attribute (
+ cell_layout, renderer, "value",
+ E_ATTACHMENT_STORE_COLUMN_PERCENT);
+
+ gtk_cell_layout_add_attribute (
+ cell_layout, renderer, "visible",
+ E_ATTACHMENT_STORE_COLUMN_SAVING);
}
GType
diff --git a/widgets/misc/e-attachment-store.c b/widgets/misc/e-attachment-store.c
index bd6cb18481..6086ed4d2f 100644
--- a/widgets/misc/e-attachment-store.c
+++ b/widgets/misc/e-attachment-store.c
@@ -26,8 +26,6 @@
#include "e-util/e-util.h"
#include "e-util/gconf-bridge.h"
-#include "e-file-activity.h"
-
#define E_ATTACHMENT_STORE_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_ATTACHMENT_STORE, EAttachmentStorePrivate))
@@ -35,7 +33,6 @@
#define DEFAULT_ICON_NAME "mail-attachment"
struct _EAttachmentStorePrivate {
- GHashTable *activity_index;
GHashTable *attachment_index;
gchar *background_filename;
gchar *background_options;
@@ -54,13 +51,7 @@ enum {
PROP_TOTAL_SIZE
};
-enum {
- NEW_ACTIVITY,
- LAST_SIGNAL
-};
-
static gpointer parent_class;
-static guint signals[LAST_SIGNAL];
static const gchar *
attachment_store_get_background_filename (EAttachmentStore *store)
@@ -101,183 +92,6 @@ attachment_store_set_background_options (EAttachmentStore *store,
}
static void
-attachment_store_remove_activity (EAttachmentStore *store,
- EActivity *activity)
-{
- GtkTreeRowReference *reference;
- GHashTable *hash_table;
-
- hash_table = store->priv->activity_index;
- reference = g_hash_table_lookup (hash_table, activity);
-
- if (gtk_tree_row_reference_valid (reference)) {
- GtkTreeModel *model;
- GtkTreePath *path;
- GtkTreeIter iter;
-
- model = gtk_tree_row_reference_get_model (reference);
- path = gtk_tree_row_reference_get_path (reference);
- gtk_tree_model_get_iter (model, &iter, path);
- gtk_tree_path_free (path);
-
- gtk_list_store_set (
- GTK_LIST_STORE (store), &iter,
- E_ATTACHMENT_STORE_COLUMN_ACTIVITY, NULL, -1);
- }
-
- g_hash_table_remove (hash_table, activity);
-
- g_object_notify (G_OBJECT (store), "num-loading");
-}
-
-static void
-attachment_store_copy_ready (GFile *source,
- GAsyncResult *result,
- GtkTreeRowReference *reference)
-{
- EAttachmentStore *store;
- EAttachment *attachment;
- EActivity *activity;
- GFile *destination;
- GtkTreeModel *model;
- GtkTreePath *path;
- GtkTreeIter iter;
- gboolean valid;
- GError *error = NULL;
-
- model = gtk_tree_row_reference_get_model (reference);
- path = gtk_tree_row_reference_get_path (reference);
- valid = gtk_tree_model_get_iter (model, &iter, path);
- gtk_tree_path_free (path);
- g_return_if_fail (valid);
-
- gtk_tree_model_get (
- model, &iter,
- E_ATTACHMENT_STORE_COLUMN_ACTIVITY, &activity,
- E_ATTACHMENT_STORE_COLUMN_ATTACHMENT, &attachment, -1);
-
- gtk_tree_row_reference_free (reference);
-
- store = E_ATTACHMENT_STORE (model);
-
- if (!g_file_copy_finish (source, result, &error))
- goto fail;
-
- gtk_list_store_set (
- GTK_LIST_STORE (store), &iter,
- E_ATTACHMENT_STORE_COLUMN_ACTIVITY, NULL, -1);
-
- destination = e_file_activity_get_file (E_FILE_ACTIVITY (activity));
- e_attachment_set_file (attachment, destination);
-
- e_activity_complete (activity);
-
- g_object_unref (attachment);
- g_object_unref (activity);
-
- return;
-
-fail:
- e_attachment_store_remove_attachment (store, attachment);
-
- g_object_unref (attachment);
- g_object_unref (activity);
-
- /* XXX Do something more useful with the error. */
- g_warning ("%s", error->message);
- g_error_free (error);
-}
-
-static void
-attachment_store_copy_async (EAttachmentStore *store,
- EAttachment *attachment)
-{
- EActivity *activity;
- GCancellable *cancellable;
- GtkTreeRowReference *reference;
- GtkTreeModel *model;
- GtkTreePath *path;
- GtkTreeIter iter;
- GHashTable *hash_table;
- GFile *destination;
- GFile *source;
- gboolean valid;
- gchar *filename;
- gchar *uri;
- gint fd;
- GError *error = NULL;
-
- hash_table = store->priv->attachment_index;
- reference = g_hash_table_lookup (hash_table, attachment);
- g_return_if_fail (reference != NULL);
-
- fd = e_file_open_tmp (&filename, &error);
- if (error != NULL)
- goto fail;
-
- source = e_attachment_get_file (attachment);
- destination = g_file_new_for_path (filename);
-
- g_free (filename);
- close (fd);
-
- model = gtk_tree_row_reference_get_model (reference);
- path = gtk_tree_row_reference_get_path (reference);
- valid = gtk_tree_model_get_iter (model, &iter, path);
- gtk_tree_path_free (path);
- g_return_if_fail (valid);
-
- uri = g_file_get_uri (source);
- activity = e_file_activity_newv (_("Downloading '%s'"), uri);
- g_free (uri);
-
- gtk_list_store_set (
- GTK_LIST_STORE (store), &iter,
- E_ATTACHMENT_STORE_COLUMN_ACTIVITY, activity, -1);
-
- reference = gtk_tree_row_reference_copy (reference);
-
- hash_table = store->priv->activity_index;
- g_hash_table_insert (hash_table, g_object_ref (activity), reference);
-
- g_signal_connect_swapped (
- activity, "cancelled",
- G_CALLBACK (attachment_store_remove_activity), store);
-
- g_signal_connect_swapped (
- activity, "completed",
- G_CALLBACK (attachment_store_remove_activity), store);
-
- reference = gtk_tree_row_reference_copy (reference);
-
- cancellable = e_file_activity_get_cancellable (
- E_FILE_ACTIVITY (activity));
-
- g_file_copy_async (
- source, destination, G_FILE_COPY_OVERWRITE,
- G_PRIORITY_DEFAULT, cancellable, (GFileProgressCallback)
- e_file_activity_progress, activity, (GAsyncReadyCallback)
- attachment_store_copy_ready, reference);
-
- e_file_activity_set_file (E_FILE_ACTIVITY (activity), destination);
- g_signal_emit (store, signals[NEW_ACTIVITY], 0, activity);
-
- g_object_notify (G_OBJECT (store), "num-loading");
-
- g_object_unref (activity);
- g_object_unref (destination);
-
- return;
-
-fail:
- e_attachment_store_remove_attachment (store, attachment);
-
- /* XXX Do something more useful with the error. */
- g_warning ("%s", error->message);
- g_error_free (error);
-}
-
-static void
attachment_store_set_property (GObject *object,
guint property_id,
const GValue *value,
@@ -366,7 +180,6 @@ attachment_store_dispose (GObject *object)
priv = E_ATTACHMENT_STORE_GET_PRIVATE (object);
- g_hash_table_remove_all (priv->activity_index);
g_hash_table_remove_all (priv->attachment_index);
/* Chain up to parent's dispose() method. */
@@ -380,7 +193,6 @@ attachment_store_finalize (GObject *object)
priv = E_ATTACHMENT_STORE_GET_PRIVATE (object);
- g_hash_table_destroy (priv->activity_index);
g_hash_table_destroy (priv->attachment_index);
g_free (priv->background_filename);
@@ -418,6 +230,7 @@ attachment_store_row_changed (GtkTreeModel *model,
{
EAttachmentStorePrivate *priv;
EAttachment *attachment;
+ GFileInfo *file_info;
GFile *file;
GIcon *icon;
GList *list;
@@ -429,8 +242,9 @@ attachment_store_row_changed (GtkTreeModel *model,
gchar *caption;
gboolean loading;
gboolean saving;
- guint64 size;
+ goffset size;
gint column_id;
+ gint percent;
priv = E_ATTACHMENT_STORE_GET_PRIVATE (model);
@@ -441,17 +255,24 @@ attachment_store_row_changed (GtkTreeModel *model,
gtk_tree_model_get (model, iter, column_id, &attachment, -1);
g_return_if_fail (E_IS_ATTACHMENT (attachment));
- content_type = e_attachment_get_content_type (attachment);
- display_name = e_attachment_get_display_name (attachment);
+ file_info = e_attachment_get_file_info (attachment);
+ if (file_info == NULL) {
+ g_object_unref (attachment);
+ return;
+ }
+
+ content_type = g_file_info_get_content_type (file_info);
+ display_name = g_file_info_get_display_name (file_info);
thumbnail_path = e_attachment_get_thumbnail_path (attachment);
loading = e_attachment_get_loading (attachment);
+ percent = e_attachment_get_percent (attachment);
saving = e_attachment_get_saving (attachment);
- icon = e_attachment_get_icon (attachment);
- size = e_attachment_get_size (attachment);
+ icon = g_file_info_get_icon (file_info);
+ size = g_file_info_get_size (file_info);
content_type = (content_type != NULL) ? content_type : "";
content_description = g_content_type_get_description (content_type);
- display_size = g_format_size_for_display ((goffset) size);
+ display_size = g_format_size_for_display (size);
if (size > 0)
caption = g_strdup_printf (
@@ -507,6 +328,7 @@ attachment_store_row_changed (GtkTreeModel *model,
E_ATTACHMENT_STORE_COLUMN_CAPTION, caption,
E_ATTACHMENT_STORE_COLUMN_ICON, icon,
E_ATTACHMENT_STORE_COLUMN_LOADING, loading,
+ E_ATTACHMENT_STORE_COLUMN_PERCENT, percent,
E_ATTACHMENT_STORE_COLUMN_SAVING, saving,
E_ATTACHMENT_STORE_COLUMN_SIZE, size,
-1);
@@ -613,31 +435,24 @@ static void
attachment_store_init (EAttachmentStore *store)
{
GType types[E_ATTACHMENT_STORE_NUM_COLUMNS];
- GHashTable *activity_index;
GHashTable *attachment_index;
gint column = 0;
- activity_index = g_hash_table_new_full (
- g_direct_hash, g_direct_equal,
- (GDestroyNotify) g_object_unref,
- (GDestroyNotify) gtk_tree_row_reference_free);
-
attachment_index = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
(GDestroyNotify) g_object_unref,
(GDestroyNotify) gtk_tree_row_reference_free);
store->priv = E_ATTACHMENT_STORE_GET_PRIVATE (store);
- store->priv->activity_index = activity_index;
store->priv->attachment_index = attachment_index;
- types[column++] = E_TYPE_ACTIVITY; /* COLUMN_ACTIVITY */
types[column++] = E_TYPE_ATTACHMENT; /* COLUMN_ATTACHMENT */
types[column++] = G_TYPE_STRING; /* COLUMN_CAPTION */
types[column++] = G_TYPE_STRING; /* COLUMN_CONTENT_TYPE */
types[column++] = G_TYPE_STRING; /* COLUMN_DISPLAY_NAME */
types[column++] = G_TYPE_ICON; /* COLUMN_ICON */
types[column++] = G_TYPE_BOOLEAN; /* COLUMN_LOADING */
+ types[column++] = G_TYPE_INT; /* COLUMN_PERCENT */
types[column++] = G_TYPE_BOOLEAN; /* COLUMN_SAVING */
types[column++] = G_TYPE_UINT64; /* COLUMN_SIZE */
@@ -720,10 +535,7 @@ e_attachment_store_add_attachment (EAttachmentStore *store,
file = e_attachment_get_file (attachment);
/* This lets the attachment tell us when to update. */
- _e_attachment_set_reference (attachment, reference);
-
- if (file != NULL && !g_file_is_native (file))
- attachment_store_copy_async (store, attachment);
+ e_attachment_set_reference (attachment, reference);
g_object_freeze_notify (G_OBJECT (store));
g_object_notify (G_OBJECT (store), "num-attachments");
@@ -737,7 +549,6 @@ e_attachment_store_remove_attachment (EAttachmentStore *store,
{
GtkTreeRowReference *reference;
GHashTable *hash_table;
- EActivity *activity;
GtkTreeModel *model;
GtkTreePath *path;
GtkTreeIter iter;
@@ -756,21 +567,14 @@ e_attachment_store_remove_attachment (EAttachmentStore *store,
return FALSE;
}
+ e_attachment_cancel (attachment);
+ e_attachment_set_reference (attachment, NULL);
+
model = gtk_tree_row_reference_get_model (reference);
path = gtk_tree_row_reference_get_path (reference);
gtk_tree_model_get_iter (model, &iter, path);
gtk_tree_path_free (path);
- gtk_tree_model_get (
- model, &iter,
- E_ATTACHMENT_STORE_COLUMN_ACTIVITY, &activity, -1);
-
- if (activity != NULL) {
- /* Cancel the file transfer. */
- e_activity_cancel (activity);
- g_object_unref (activity);
- }
-
gtk_list_store_remove (GTK_LIST_STORE (store), &iter);
g_hash_table_remove (hash_table, attachment);
@@ -804,8 +608,10 @@ e_attachment_store_add_to_multipart (EAttachmentStore *store,
column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
- e_attachment_add_to_multipart (
- attachment, multipart, default_charset);
+ /* Skip the attachment if it's still loading. */
+ if (!e_attachment_get_loading (attachment))
+ e_attachment_add_to_multipart (
+ attachment, multipart, default_charset);
g_object_unref (attachment);
@@ -847,17 +653,38 @@ e_attachment_store_get_num_attachments (EAttachmentStore *store)
guint
e_attachment_store_get_num_loading (EAttachmentStore *store)
{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ guint num_loading = 0;
+ gboolean valid;
+
g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), 0);
- return g_hash_table_size (store->priv->activity_index);
+ model = GTK_TREE_MODEL (store);
+ valid = gtk_tree_model_get_iter_first (model, &iter);
+
+ while (valid) {
+ EAttachment *attachment;
+ gint column_id;
+
+ column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+ gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
+ if (e_attachment_get_loading (attachment))
+ num_loading++;
+ g_object_unref (attachment);
+
+ valid = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ return num_loading;
}
-guint64
+goffset
e_attachment_store_get_total_size (EAttachmentStore *store)
{
GtkTreeModel *model;
GtkTreeIter iter;
- guint64 total_size = 0;
+ goffset total_size = 0;
gboolean valid;
g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), 0);
@@ -867,11 +694,14 @@ e_attachment_store_get_total_size (EAttachmentStore *store)
while (valid) {
EAttachment *attachment;
+ GFileInfo *file_info;
gint column_id;
column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
- total_size += e_attachment_get_size (attachment);
+ file_info = e_attachment_get_file_info (attachment);
+ if (file_info != NULL)
+ total_size += g_file_info_get_size (file_info);
g_object_unref (attachment);
valid = gtk_tree_model_iter_next (model, &iter);
@@ -928,6 +758,7 @@ e_attachment_store_run_load_dialog (EAttachmentStore *store,
gint response;
g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+ g_return_if_fail (GTK_IS_WINDOW (parent));
dialog = gtk_file_chooser_dialog_new (
_("Add Attachment"), parent,
@@ -962,6 +793,9 @@ e_attachment_store_run_load_dialog (EAttachmentStore *store,
attachment = e_attachment_new ();
e_attachment_set_file (attachment, file);
e_attachment_store_add_attachment (store, attachment);
+ e_attachment_load_async (
+ attachment, (GAsyncReadyCallback)
+ e_attachment_load_handle_error, parent);
g_object_unref (attachment);
}
@@ -979,13 +813,14 @@ e_attachment_store_run_save_dialog (EAttachmentStore *store,
{
GtkFileChooser *file_chooser;
GtkWidget *dialog;
- GFile *file;
- EActivity *activity;
+ GFile *destination;
+ GFileInfo *file_info;
const gchar *display_name;
gint response;
g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ g_return_if_fail (GTK_IS_WINDOW (parent));
dialog = gtk_file_chooser_dialog_new (
_("Save Attachment"), parent,
@@ -999,7 +834,11 @@ e_attachment_store_run_save_dialog (EAttachmentStore *store,
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
gtk_window_set_icon_name (GTK_WINDOW (dialog), "mail-attachment");
- display_name = e_attachment_get_display_name (attachment);
+ file_info = e_attachment_get_file_info (attachment);
+ if (file_info != NULL)
+ display_name = g_file_info_get_display_name (file_info);
+ else
+ display_name = NULL;
if (display_name != NULL)
gtk_file_chooser_set_current_name (file_chooser, display_name);
@@ -1008,13 +847,13 @@ e_attachment_store_run_save_dialog (EAttachmentStore *store,
if (response != GTK_RESPONSE_OK)
goto exit;
- file = gtk_file_chooser_get_file (file_chooser);
- activity = e_file_activity_new (_("Saving attachment"));
+ destination = gtk_file_chooser_get_file (file_chooser);
+
e_attachment_save_async (
- attachment, E_FILE_ACTIVITY (activity), file);
- g_signal_emit (store, signals[NEW_ACTIVITY], 0, activity);
- g_object_unref (activity);
- g_object_unref (file);
+ attachment, destination, (GAsyncReadyCallback)
+ e_attachment_save_handle_error, parent);
+
+ g_object_unref (destination);
exit:
gtk_widget_destroy (dialog);
diff --git a/widgets/misc/e-attachment-store.h b/widgets/misc/e-attachment-store.h
index 971868258f..e7f74102be 100644
--- a/widgets/misc/e-attachment-store.h
+++ b/widgets/misc/e-attachment-store.h
@@ -60,13 +60,13 @@ struct _EAttachmentStoreClass {
};
enum {
- E_ATTACHMENT_STORE_COLUMN_ACTIVITY, /* E_TYPE_ACTIVITY */
E_ATTACHMENT_STORE_COLUMN_ATTACHMENT, /* E_TYPE_ATTACHMENT */
E_ATTACHMENT_STORE_COLUMN_CAPTION, /* G_TYPE_STRING */
E_ATTACHMENT_STORE_COLUMN_CONTENT_TYPE, /* G_TYPE_STRING */
E_ATTACHMENT_STORE_COLUMN_DISPLAY_NAME, /* G_TYPE_STRING */
E_ATTACHMENT_STORE_COLUMN_ICON, /* G_TYPE_ICON */
E_ATTACHMENT_STORE_COLUMN_LOADING, /* G_TYPE_BOOLEAN */
+ E_ATTACHMENT_STORE_COLUMN_PERCENT, /* G_TYPE_INT */
E_ATTACHMENT_STORE_COLUMN_SAVING, /* G_TYPE_BOOLEAN */
E_ATTACHMENT_STORE_COLUMN_SIZE, /* G_TYPE_UINT64 */
E_ATTACHMENT_STORE_NUM_COLUMNS
@@ -93,7 +93,7 @@ guint e_attachment_store_get_num_attachments
(EAttachmentStore *store);
guint e_attachment_store_get_num_loading
(EAttachmentStore *store);
-guint64 e_attachment_store_get_total_size
+goffset e_attachment_store_get_total_size
(EAttachmentStore *store);
gint e_attachment_store_run_file_chooser_dialog
(EAttachmentStore *store,
diff --git a/widgets/misc/e-attachment-tree-view.c b/widgets/misc/e-attachment-tree-view.c
index df01e07040..548ed3aa4f 100644
--- a/widgets/misc/e-attachment-tree-view.c
+++ b/widgets/misc/e-attachment-tree-view.c
@@ -182,6 +182,16 @@ attachment_tree_view_popup_menu (GtkWidget *widget)
return TRUE;
}
+static void
+attachment_tree_view_row_activated (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (tree_view);
+
+ e_attachment_view_open_path (view, path, NULL);
+}
+
static EAttachmentViewPrivate *
attachment_tree_view_get_private (EAttachmentView *view)
{
@@ -301,6 +311,7 @@ attachment_tree_view_class_init (EAttachmentTreeViewClass *class)
{
GObjectClass *object_class;
GtkWidgetClass *widget_class;
+ GtkTreeViewClass *tree_view_class;
parent_class = g_type_class_peek_parent (class);
g_type_class_add_private (class, sizeof (EAttachmentViewPrivate));
@@ -318,6 +329,9 @@ attachment_tree_view_class_init (EAttachmentTreeViewClass *class)
widget_class->drag_data_received = attachment_tree_view_drag_data_received;
widget_class->popup_menu = attachment_tree_view_popup_menu;
+ tree_view_class = GTK_TREE_VIEW_CLASS (class);
+ tree_view_class->row_activated = attachment_tree_view_row_activated;
+
g_object_class_override_property (
object_class, PROP_EDITABLE, "editable");
}
@@ -353,6 +367,8 @@ attachment_tree_view_init (EAttachmentTreeView *tree_view)
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+ /* Name Column */
+
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_expand (column, TRUE);
gtk_tree_view_column_set_spacing (column, 3);
@@ -376,6 +392,32 @@ attachment_tree_view_init (EAttachmentTreeView *tree_view)
column, renderer, "text",
E_ATTACHMENT_STORE_COLUMN_DISPLAY_NAME);
+ renderer = gtk_cell_renderer_progress_new ();
+ g_object_set (renderer, "text", _("Loading"), NULL);
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "value",
+ E_ATTACHMENT_STORE_COLUMN_PERCENT);
+
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "visible",
+ E_ATTACHMENT_STORE_COLUMN_LOADING);
+
+ renderer = gtk_cell_renderer_progress_new ();
+ g_object_set (renderer, "text", _("Saving"), NULL);
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "value",
+ E_ATTACHMENT_STORE_COLUMN_PERCENT);
+
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "visible",
+ E_ATTACHMENT_STORE_COLUMN_SAVING);
+
+ /* Size Column */
+
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, _("Size"));
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
@@ -387,6 +429,8 @@ attachment_tree_view_init (EAttachmentTreeView *tree_view)
column, renderer, (GtkTreeCellDataFunc)
attachment_tree_view_render_size, NULL, NULL);
+ /* Type Column */
+
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, _("Type"));
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
diff --git a/widgets/misc/e-attachment-view.c b/widgets/misc/e-attachment-view.c
index bdb0a0067e..cb735fbfec 100644
--- a/widgets/misc/e-attachment-view.c
+++ b/widgets/misc/e-attachment-view.c
@@ -67,6 +67,7 @@ static struct {
static const gchar *ui =
"<ui>"
" <popup name='context'>"
+" <menuitem action='cancel'/>"
" <menuitem action='save-as'/>"
" <menuitem action='set-background'/>"
" <menuitem action='remove'/>"
@@ -100,6 +101,23 @@ action_add_cb (GtkAction *action,
}
static void
+action_cancel_cb (GtkAction *action,
+ EAttachmentView *view)
+{
+ EAttachment *attachment;
+ GList *selected;
+
+ selected = e_attachment_view_get_selected_attachments (view);
+ g_return_if_fail (g_list_length (selected) == 1);
+ attachment = selected->data;
+
+ e_attachment_cancel (attachment);
+
+ g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+}
+
+static void
action_drag_cancel_cb (GtkAction *action,
EAttachmentView *view)
{
@@ -128,23 +146,20 @@ action_open_in_cb (GtkAction *action,
EAttachmentView *view)
{
GAppInfo *app_info;
- EActivity *activity;
- EAttachment *attachment;
+ GtkTreePath *path;
+ GList *selected;
+
+ selected = e_attachment_view_get_selected_paths (view);
+ g_return_if_fail (g_list_length (selected) == 1);
+ path = selected->data;
app_info = g_object_get_data (G_OBJECT (action), "app-info");
g_return_if_fail (G_IS_APP_INFO (app_info));
- attachment = g_object_get_data (G_OBJECT (action), "attachment");
- g_return_if_fail (E_IS_ATTACHMENT (attachment));
-
- activity = e_file_activity_newv (
- _("Opening attachment in %s"),
- g_app_info_get_name (app_info));
+ e_attachment_view_open_path (view, path, app_info);
- e_attachment_launch_async (
- attachment, E_FILE_ACTIVITY (activity), app_info);
-
- g_object_unref (activity);
+ g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (selected);
}
static void
@@ -178,14 +193,21 @@ action_recent_cb (GtkAction *action,
GtkRecentChooser *chooser;
EAttachmentStore *store;
EAttachment *attachment;
+ gpointer parent;
gchar *uri;
chooser = GTK_RECENT_CHOOSER (action);
store = e_attachment_view_get_store (view);
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
uri = gtk_recent_chooser_get_current_uri (chooser);
attachment = e_attachment_new_for_uri (uri);
e_attachment_store_add_attachment (store, attachment);
+ e_attachment_load_async (
+ attachment, (GAsyncReadyCallback)
+ e_attachment_load_handle_error, parent);
g_free (uri);
}
@@ -200,16 +222,42 @@ static void
action_save_as_cb (GtkAction *action,
EAttachmentView *view)
{
+ EAttachmentStore *store;
+ EAttachment *attachment;
+ GList *selected;
+ gpointer parent;
+
+ store = e_attachment_view_get_store (view);
+
+ selected = e_attachment_view_get_selected_attachments (view);
+ g_return_if_fail (g_list_length (selected) == 1);
+ attachment = selected->data;
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+ e_attachment_store_run_save_dialog (store, attachment, parent);
+
+ g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
}
static void
action_set_background_cb (GtkAction *action,
EAttachmentView *view)
{
+ /* FIXME */
}
static GtkActionEntry standard_entries[] = {
+ { "cancel",
+ GTK_STOCK_CANCEL,
+ NULL,
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_cancel_cb) },
+
{ "drag-cancel",
NULL,
N_("Cancel _Drag"),
@@ -284,6 +332,7 @@ drop_message_rfc822 (EAttachmentView *view,
const gchar *data;
gboolean success = FALSE;
gboolean delete = FALSE;
+ gpointer parent;
gint length;
priv = e_attachment_view_get_private (view);
@@ -301,8 +350,14 @@ drop_message_rfc822 (EAttachmentView *view,
if (camel_data_wrapper_construct_from_stream (wrapper, stream) == -1)
goto exit;
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
attachment = e_attachment_new_for_message (message);
e_attachment_store_add_attachment (store, attachment);
+ e_attachment_load_async (
+ attachment, (GAsyncReadyCallback)
+ e_attachment_load_handle_error, parent);
g_object_unref (attachment);
success = TRUE;
@@ -324,6 +379,7 @@ drop_netscape_url (EAttachmentView *view,
EAttachmentViewPrivate *priv;
EAttachment *attachment;
const gchar *data;
+ gpointer parent;
gchar *copied_data;
gchar **strv;
gint length;
@@ -339,8 +395,14 @@ drop_netscape_url (EAttachmentView *view,
strv = g_strsplit (copied_data, "\n", 2);
g_free (copied_data);
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
attachment = e_attachment_new_for_uri (strv[0]);
e_attachment_store_add_attachment (store, attachment);
+ e_attachment_load_async (
+ attachment, (GAsyncReadyCallback)
+ e_attachment_load_handle_error, parent);
g_object_unref (attachment);
g_strfreev (strv);
@@ -355,6 +417,7 @@ drop_text_uri_list (EAttachmentView *view,
GdkDragAction action)
{
EAttachmentViewPrivate *priv;
+ gpointer parent;
gchar **uris;
gint ii;
@@ -362,11 +425,17 @@ drop_text_uri_list (EAttachmentView *view,
uris = gtk_selection_data_get_uris (selection_data);
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
for (ii = 0; uris[ii] != NULL; ii++) {
EAttachment *attachment;
attachment = e_attachment_new_for_uri (uris[ii]);
e_attachment_store_add_attachment (store, attachment);
+ e_attachment_load_async (
+ attachment, (GAsyncReadyCallback)
+ e_attachment_load_handle_error, parent);
g_object_unref (attachment);
}
@@ -386,6 +455,7 @@ drop_text_generic (EAttachmentView *view,
CamelMimePart *mime_part;
GdkAtom atom;
const gchar *data;
+ gpointer parent;
gchar *content_type;
gint length;
@@ -402,9 +472,15 @@ drop_text_generic (EAttachmentView *view,
camel_mime_part_set_disposition (mime_part, "inline");
g_free (content_type);
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
attachment = e_attachment_new ();
e_attachment_set_mime_part (attachment, mime_part);
e_attachment_store_add_attachment (store, attachment);
+ e_attachment_load_async (
+ attachment, (GAsyncReadyCallback)
+ e_attachment_load_handle_error, parent);
g_object_unref (attachment);
camel_object_unref (mime_part);
@@ -635,6 +711,8 @@ e_attachment_view_get_selected_attachments (EAttachmentView *view)
GList *selected, *item;
gint column_id;
+ g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+
column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
selected = e_attachment_view_get_selected_paths (view);
store = e_attachment_view_get_store (view);
@@ -657,6 +735,39 @@ e_attachment_view_get_selected_attachments (EAttachmentView *view)
return selected;
}
+
+void
+e_attachment_view_open_path (EAttachmentView *view,
+ GtkTreePath *path,
+ GAppInfo *app_info)
+{
+ EAttachmentStore *store;
+ EAttachment *attachment;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gpointer parent;
+ gint column_id;
+
+ g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+ g_return_if_fail (path != NULL);
+
+ column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+ store = e_attachment_view_get_store (view);
+ model = GTK_TREE_MODEL (store);
+
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+ e_attachment_open_async (
+ attachment, app_info, (GAsyncReadyCallback)
+ e_attachment_open_handle_error, parent);
+
+ g_object_unref (attachment);
+}
+
void
e_attachment_view_remove_selected (EAttachmentView *view,
gboolean select_next)
@@ -1076,6 +1187,7 @@ e_attachment_view_update_actions (EAttachmentView *view)
GList *list, *iter;
guint n_selected;
gboolean is_image;
+ gboolean busy = FALSE;
g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
@@ -1086,6 +1198,8 @@ e_attachment_view_update_actions (EAttachmentView *view)
if (n_selected == 1) {
attachment = g_object_ref (list->data);
is_image = e_attachment_is_image (attachment);
+ busy |= e_attachment_get_loading (attachment);
+ busy |= e_attachment_get_saving (attachment);
} else {
attachment = NULL;
is_image = FALSE;
@@ -1094,23 +1208,26 @@ e_attachment_view_update_actions (EAttachmentView *view)
g_list_foreach (list, (GFunc) g_object_unref, NULL);
g_list_free (list);
+ action = e_attachment_view_get_action (view, "cancel");
+ gtk_action_set_visible (action, busy);
+
action = e_attachment_view_get_action (view, "properties");
- gtk_action_set_visible (action, n_selected == 1);
+ gtk_action_set_visible (action, !busy && n_selected == 1);
action = e_attachment_view_get_action (view, "remove");
- gtk_action_set_visible (action, n_selected > 0);
+ gtk_action_set_visible (action, !busy && n_selected > 0);
action = e_attachment_view_get_action (view, "save-as");
- gtk_action_set_visible (action, n_selected > 0);
+ gtk_action_set_visible (action, !busy && n_selected == 1);
action = e_attachment_view_get_action (view, "set-background");
- gtk_action_set_visible (action, is_image);
+ gtk_action_set_visible (action, !busy && is_image);
/* Clear out the "openwith" action group. */
gtk_ui_manager_remove_ui (priv->ui_manager, priv->merge_id);
e_action_group_remove_all_actions (priv->openwith_actions);
- if (attachment == NULL)
+ if (attachment == NULL || busy)
return;
list = e_attachment_list_apps (attachment);
diff --git a/widgets/misc/e-attachment-view.h b/widgets/misc/e-attachment-view.h
index e6e04a82a3..96a0f2dc16 100644
--- a/widgets/misc/e-attachment-view.h
+++ b/widgets/misc/e-attachment-view.h
@@ -107,6 +107,9 @@ void e_attachment_view_set_editable (EAttachmentView *view,
gboolean editable);
GList * e_attachment_view_get_selected_attachments
(EAttachmentView *view);
+void e_attachment_view_open_path (EAttachmentView *view,
+ GtkTreePath *path,
+ GAppInfo *app_info);
void e_attachment_view_remove_selected
(EAttachmentView *view,
gboolean select_next);
diff --git a/widgets/misc/e-attachment.c b/widgets/misc/e-attachment.c
index 015a706dc4..55b3280e87 100644
--- a/widgets/misc/e-attachment.c
+++ b/widgets/misc/e-attachment.c
@@ -27,6 +27,7 @@
#include <camel/camel-data-wrapper.h>
#include <camel/camel-mime-message.h>
#include <camel/camel-stream-filter.h>
+#include <camel/camel-stream-mem.h>
#include <camel/camel-stream-null.h>
#include <camel/camel-stream-vfs.h>
@@ -37,6 +38,7 @@
((obj), E_TYPE_ATTACHMENT, EAttachmentPrivate))
/* Emblems */
+#define EMBLEM_CANCELLED "gtk-cancel"
#define EMBLEM_LOADING "emblem-downloads"
#define EMBLEM_SAVING "document-save"
#define EMBLEM_ENCRYPT_WEAK "security-low"
@@ -54,7 +56,10 @@ struct _EAttachmentPrivate {
GFileInfo *file_info;
GCancellable *cancellable;
CamelMimePart *mime_part;
+ guint emblem_timeout_id;
gchar *disposition;
+ gint percent;
+
guint loading : 1;
guint saving : 1;
@@ -76,44 +81,14 @@ enum {
PROP_FILE_INFO,
PROP_LOADING,
PROP_MIME_PART,
+ PROP_PERCENT,
+ PROP_REFERENCE,
+ PROP_SAVING,
PROP_SIGNED
};
static gpointer parent_class;
-static void
-attachment_notify_model (EAttachment *attachment)
-{
- GtkTreeRowReference *reference;
- GtkTreeModel *model;
- GtkTreePath *path;
- GtkTreeIter iter;
-
- reference = attachment->priv->reference;
-
- if (reference == NULL)
- return;
-
- /* Free the reference if it's no longer valid.
- * It means we've been removed from the store. */
- if (!gtk_tree_row_reference_valid (reference)) {
- gtk_tree_row_reference_free (reference);
- attachment->priv->reference = NULL;
- return;
- }
-
- model = gtk_tree_row_reference_get_model (reference);
- path = gtk_tree_row_reference_get_path (reference);
-
- gtk_tree_model_get_iter (model, &iter, path);
- gtk_tree_model_row_changed (model, path, &iter);
-
- /* XXX This doesn't really belong here. */
- g_object_notify (G_OBJECT (model), "total-size");
-
- gtk_tree_path_free (path);
-}
-
static gchar *
attachment_get_default_charset (void)
{
@@ -147,18 +122,34 @@ attachment_get_default_charset (void)
}
static void
+attachment_notify_model (EAttachment *attachment)
+{
+ GtkTreeRowReference *reference;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ reference = e_attachment_get_reference (attachment);
+
+ if (reference == NULL)
+ return;
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_row_changed (model, path, &iter);
+
+ gtk_tree_path_free (path);
+}
+
+static void
attachment_set_file_info (EAttachment *attachment,
GFileInfo *file_info)
{
- GCancellable *cancellable;
-
- cancellable = attachment->priv->cancellable;
+ GtkTreeRowReference *reference;
- /* Cancel any query operations in progress. */
- if (!g_cancellable_is_cancelled (cancellable)) {
- g_cancellable_cancel (cancellable);
- g_cancellable_reset (cancellable);
- }
+ reference = e_attachment_get_reference (attachment);
if (file_info != NULL)
g_object_ref (file_info);
@@ -169,6 +160,14 @@ attachment_set_file_info (EAttachment *attachment,
attachment->priv->file_info = file_info;
g_object_notify (G_OBJECT (attachment), "file-info");
+
+ /* Tell the EAttachmentStore its total size changed. */
+ if (reference != NULL) {
+ GtkTreeModel *model;
+ model = gtk_tree_row_reference_get_model (reference);
+ g_object_notify (G_OBJECT (model), "total-size");
+ }
+
attachment_notify_model (attachment);
}
@@ -176,9 +175,24 @@ static void
attachment_set_loading (EAttachment *attachment,
gboolean loading)
{
+ GtkTreeRowReference *reference;
+
+ reference = e_attachment_get_reference (attachment);
+
+ attachment->priv->percent = 0;
attachment->priv->loading = loading;
+ g_object_freeze_notify (G_OBJECT (attachment));
+ g_object_notify (G_OBJECT (attachment), "percent");
g_object_notify (G_OBJECT (attachment), "loading");
+ g_object_thaw_notify (G_OBJECT (attachment));
+
+ if (reference != NULL) {
+ GtkTreeModel *model;
+ model = gtk_tree_row_reference_get_model (reference);
+ g_object_notify (G_OBJECT (model), "num-loading");
+ }
+
attachment_notify_model (attachment);
}
@@ -186,170 +200,53 @@ static void
attachment_set_saving (EAttachment *attachment,
gboolean saving)
{
+ attachment->priv->percent = 0;
attachment->priv->saving = saving;
- g_object_notify (G_OBJECT (attachment), "saving");
- attachment_notify_model (attachment);
-}
-
-static void
-attachment_reset (EAttachment *attachment)
-{
- GCancellable *cancellable;
-
- cancellable = attachment->priv->cancellable;
-
g_object_freeze_notify (G_OBJECT (attachment));
-
- /* Cancel any I/O operations in progress. */
- if (!g_cancellable_is_cancelled (cancellable)) {
- g_cancellable_cancel (cancellable);
- g_cancellable_reset (cancellable);
- }
-
- if (attachment->priv->file != NULL) {
- g_object_notify (G_OBJECT (attachment), "file");
- g_object_unref (attachment->priv->file);
- attachment->priv->file = NULL;
- }
-
- if (attachment->priv->mime_part != NULL) {
- g_object_notify (G_OBJECT (attachment), "mime-part");
- g_object_unref (attachment->priv->mime_part);
- attachment->priv->mime_part = NULL;
- }
-
- attachment_set_file_info (attachment, NULL);
-
+ g_object_notify (G_OBJECT (attachment), "percent");
+ g_object_notify (G_OBJECT (attachment), "saving");
g_object_thaw_notify (G_OBJECT (attachment));
+
+ attachment_notify_model (attachment);
}
static void
-attachment_file_info_ready_cb (GFile *file,
- GAsyncResult *result,
- EAttachment *attachment)
+attachment_progress_cb (goffset current_num_bytes,
+ goffset total_num_bytes,
+ EAttachment *attachment)
{
- GFileInfo *file_info;
- GError *error = NULL;
+ attachment->priv->percent =
+ (current_num_bytes * 100) / total_num_bytes;
- /* Even if we failed to obtain a GFileInfo, we still emit a
- * "notify::file-info" to signal the async operation finished. */
- file_info = g_file_query_info_finish (file, result, &error);
- attachment_set_file_info (attachment, file_info);
+ g_object_notify (G_OBJECT (attachment), "percent");
- if (file_info != NULL)
- g_object_unref (file_info);
- else {
- g_warning ("%s", error->message);
- g_error_free (error);
- }
+ attachment_notify_model (attachment);
}
-static void
-attachment_file_info_to_mime_part (EAttachment *attachment,
- CamelMimePart *mime_part)
+static gboolean
+attachment_cancelled_timeout_cb (EAttachment *attachment)
{
- GFileInfo *file_info;
- const gchar *attribute;
- const gchar *string;
- gchar *allocated;
-
- file_info = e_attachment_get_file_info (attachment);
-
- if (file_info == NULL || mime_part == NULL)
- return;
-
- /* XXX Note that we skip "standard::size" here.
- * The CamelMimePart already knows the size. */
+ attachment->priv->emblem_timeout_id = 0;
+ g_cancellable_reset (attachment->priv->cancellable);
- attribute = G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE;
- string = g_file_info_get_attribute_string (file_info, attribute);
- allocated = g_content_type_get_mime_type (string);
- camel_mime_part_set_content_type (mime_part, allocated);
- g_free (allocated);
-
- attribute = G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME;
- string = g_file_info_get_attribute_string (file_info, attribute);
- camel_mime_part_set_filename (mime_part, string);
-
- attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
- string = g_file_info_get_attribute_string (file_info, attribute);
- camel_mime_part_set_description (mime_part, string);
+ attachment_notify_model (attachment);
- string = e_attachment_get_disposition (attachment);
- camel_mime_part_set_disposition (mime_part, string);
+ return FALSE;
}
static void
-attachment_populate_file_info (EAttachment *attachment,
- GFileInfo *file_info)
+attachment_cancelled_cb (EAttachment *attachment)
{
- CamelContentType *content_type;
- CamelMimePart *mime_part;
- const gchar *attribute;
- const gchar *string;
- gchar *allocated;
- guint64 v_uint64;
-
- mime_part = e_attachment_get_mime_part (attachment);
-
- attribute = G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE;
- content_type = camel_mime_part_get_content_type (mime_part);
- allocated = camel_content_type_simple (content_type);
- if (allocated != NULL) {
- GIcon *icon;
- gchar *cp;
-
- /* GIO expects lowercase MIME types. */
- for (cp = allocated; *cp != '\0'; cp++)
- *cp = g_ascii_tolower (*cp);
+ /* Reset the GCancellable after one second. This causes a
+ * cancel emblem to be briefly shown on the attachment icon
+ * as visual feedback that an operation was cancelled. */
- /* Swap the MIME type for a content type. */
- cp = g_content_type_from_mime_type (allocated);
- g_free (allocated);
- allocated = cp;
-
- /* Use the MIME part's filename if we have to. */
- if (g_content_type_is_unknown (allocated)) {
- string = camel_mime_part_get_filename (mime_part);
- if (string != NULL) {
- g_free (allocated);
- allocated = g_content_type_guess (
- string, NULL, 0, NULL);
- }
- }
-
- g_file_info_set_attribute_string (
- file_info, attribute, allocated);
-
- attribute = G_FILE_ATTRIBUTE_STANDARD_ICON;
- icon = g_content_type_get_icon (allocated);
- if (icon != NULL) {
- g_file_info_set_attribute_object (
- file_info, attribute, G_OBJECT (icon));
- g_object_unref (icon);
- }
- }
- g_free (allocated);
+ if (attachment->priv->emblem_timeout_id > 0)
+ g_source_remove (attachment->priv->emblem_timeout_id);
- attribute = G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME;
- string = camel_mime_part_get_filename (mime_part);
- if (string != NULL)
- g_file_info_set_attribute_string (
- file_info, attribute, string);
-
- attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
- string = camel_mime_part_get_description (mime_part);
- if (string != NULL)
- g_file_info_set_attribute_string (
- file_info, attribute, string);
-
- attribute = G_FILE_ATTRIBUTE_STANDARD_SIZE;
- v_uint64 = camel_mime_part_get_content_size (mime_part);
- g_file_info_set_attribute_uint64 (file_info, attribute, v_uint64);
-
- string = camel_mime_part_get_disposition (mime_part);
- e_attachment_set_disposition (attachment, string);
+ attachment->priv->emblem_timeout_id = g_timeout_add_seconds (
+ 1, (GSourceFunc) attachment_cancelled_timeout_cb, attachment);
}
static void
@@ -383,6 +280,12 @@ attachment_set_property (GObject *object,
g_value_get_boxed (value));
return;
+ case PROP_REFERENCE:
+ e_attachment_set_reference (
+ E_ATTACHMENT (object),
+ g_value_get_boxed (value));
+ return;
+
case PROP_SIGNED:
e_attachment_set_signed (
E_ATTACHMENT (object),
@@ -436,6 +339,24 @@ attachment_get_property (GObject *object,
E_ATTACHMENT (object)));
return;
+ case PROP_PERCENT:
+ g_value_set_int (
+ value, e_attachment_get_percent (
+ E_ATTACHMENT (object)));
+ return;
+
+ case PROP_REFERENCE:
+ g_value_set_boxed (
+ value, e_attachment_get_reference (
+ E_ATTACHMENT (object)));
+ return;
+
+ case PROP_SAVING:
+ g_value_set_boolean (
+ value, e_attachment_get_saving (
+ E_ATTACHMENT (object)));
+ return;
+
case PROP_SIGNED:
g_value_set_int (
value, e_attachment_get_signed (
@@ -453,12 +374,6 @@ attachment_dispose (GObject *object)
priv = E_ATTACHMENT_GET_PRIVATE (object);
- if (priv->cancellable != NULL) {
- g_cancellable_cancel (priv->cancellable);
- g_object_unref (priv->cancellable);
- priv->cancellable = NULL;
- }
-
if (priv->file != NULL) {
g_object_unref (priv->file);
priv->file = NULL;
@@ -469,11 +384,21 @@ attachment_dispose (GObject *object)
priv->file_info = NULL;
}
+ if (priv->cancellable != NULL) {
+ g_object_unref (priv->cancellable);
+ priv->cancellable = NULL;
+ }
+
if (priv->mime_part != NULL) {
camel_object_unref (priv->mime_part);
priv->mime_part = NULL;
}
+ if (priv->emblem_timeout_id > 0) {
+ g_source_remove (priv->emblem_timeout_id);
+ priv->emblem_timeout_id = 0;
+ }
+
/* This accepts NULL arguments. */
gtk_tree_row_reference_free (priv->reference);
priv->reference = NULL;
@@ -575,6 +500,38 @@ attachment_class_init (EAttachmentClass *class)
E_TYPE_CAMEL_OBJECT,
G_PARAM_READWRITE));
+ g_object_class_install_property (
+ object_class,
+ PROP_PERCENT,
+ g_param_spec_int (
+ "percent",
+ "Percent",
+ NULL,
+ 0,
+ 100,
+ 0,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_REFERENCE,
+ g_param_spec_boxed (
+ "reference",
+ "Reference",
+ NULL,
+ GTK_TYPE_TREE_ROW_REFERENCE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SAVING,
+ g_param_spec_boolean (
+ "saving",
+ "Saving",
+ NULL,
+ FALSE,
+ G_PARAM_READABLE));
+
/* FIXME Define a GEnumClass for this. */
g_object_class_install_property (
object_class,
@@ -597,6 +554,10 @@ attachment_init (EAttachment *attachment)
attachment->priv->cancellable = g_cancellable_new ();
attachment->priv->encrypted = CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE;
attachment->priv->signed_ = CAMEL_CIPHER_VALIDITY_SIGN_NONE;
+
+ g_signal_connect_swapped (
+ attachment->priv->cancellable, "cancelled",
+ G_CALLBACK (attachment_cancelled_cb), attachment);
}
GType
@@ -779,6 +740,14 @@ exit:
camel_multipart_add_part (multipart, mime_part);
}
+void
+e_attachment_cancel (EAttachment *attachment)
+{
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+ g_cancellable_cancel (attachment->priv->cancellable);
+}
+
const gchar *
e_attachment_get_disposition (EAttachment *attachment)
{
@@ -811,34 +780,19 @@ void
e_attachment_set_file (EAttachment *attachment,
GFile *file)
{
- GCancellable *cancellable;
-
g_return_if_fail (E_IS_ATTACHMENT (attachment));
- g_object_freeze_notify (G_OBJECT (attachment));
-
if (file != NULL) {
g_return_if_fail (G_IS_FILE (file));
g_object_ref (file);
}
- attachment_reset (attachment);
- attachment->priv->file = file;
-
- cancellable = attachment->priv->cancellable;
+ if (attachment->priv->file != NULL)
+ g_object_unref (attachment->priv->file);
- if (file != NULL)
- g_file_query_info_async (
- file, ATTACHMENT_QUERY,
- G_FILE_QUERY_INFO_NONE,
- G_PRIORITY_DEFAULT, cancellable,
- (GAsyncReadyCallback)
- attachment_file_info_ready_cb,
- attachment);
+ attachment->priv->file = file;
g_object_notify (G_OBJECT (attachment), "file");
-
- g_object_thaw_notify (G_OBJECT (attachment));
}
GFileInfo *
@@ -849,6 +803,14 @@ e_attachment_get_file_info (EAttachment *attachment)
return attachment->priv->file_info;
}
+gboolean
+e_attachment_get_loading (EAttachment *attachment)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+
+ return attachment->priv->loading;
+}
+
CamelMimePart *
e_attachment_get_mime_part (EAttachment *attachment)
{
@@ -863,28 +825,60 @@ e_attachment_set_mime_part (EAttachment *attachment,
{
g_return_if_fail (E_IS_ATTACHMENT (attachment));
- g_object_freeze_notify (G_OBJECT (attachment));
-
if (mime_part != NULL) {
g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
camel_object_ref (mime_part);
}
- attachment_reset (attachment);
+ if (attachment->priv->mime_part != NULL)
+ camel_object_unref (attachment->priv->mime_part);
+
attachment->priv->mime_part = mime_part;
- if (mime_part != NULL) {
- GFileInfo *file_info;
+ g_object_notify (G_OBJECT (attachment), "mime-part");
+}
- file_info = g_file_info_new ();
- attachment_populate_file_info (attachment, file_info);
- attachment_set_file_info (attachment, file_info);
- g_object_unref (file_info);
- }
+gint
+e_attachment_get_percent (EAttachment *attachment)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), 0);
- g_object_notify (G_OBJECT (attachment), "mime-part");
+ return attachment->priv->percent;
+}
- g_object_thaw_notify (G_OBJECT (attachment));
+GtkTreeRowReference *
+e_attachment_get_reference (EAttachment *attachment)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+
+ /* Don't return an invalid tree row reference. */
+ if (!gtk_tree_row_reference_valid (attachment->priv->reference))
+ e_attachment_set_reference (attachment, NULL);
+
+ return attachment->priv->reference;
+}
+
+void
+e_attachment_set_reference (EAttachment *attachment,
+ GtkTreeRowReference *reference)
+{
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+ if (reference != NULL)
+ reference = gtk_tree_row_reference_copy (reference);
+
+ gtk_tree_row_reference_free (attachment->priv->reference);
+ attachment->priv->reference = reference;
+
+ g_object_notify (G_OBJECT (attachment), "reference");
+}
+
+gboolean
+e_attachment_get_saving (EAttachment *attachment)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+
+ return attachment->priv->saving;
}
camel_cipher_validity_encrypt_t
@@ -932,40 +926,6 @@ e_attachment_set_signed (EAttachment *attachment,
}
const gchar *
-e_attachment_get_content_type (EAttachment *attachment)
-{
- GFileInfo *file_info;
- const gchar *attribute;
-
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
-
- attribute = G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE;
- file_info = e_attachment_get_file_info (attachment);
-
- if (file_info == NULL)
- return NULL;
-
- return g_file_info_get_attribute_string (file_info, attribute);
-}
-
-const gchar *
-e_attachment_get_display_name (EAttachment *attachment)
-{
- GFileInfo *file_info;
- const gchar *attribute;
-
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
-
- attribute = G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME;
- file_info = e_attachment_get_file_info (attachment);
-
- if (file_info == NULL)
- return NULL;
-
- return g_file_info_get_attribute_string (file_info, attribute);
-}
-
-const gchar *
e_attachment_get_description (EAttachment *attachment)
{
GFileInfo *file_info;
@@ -982,32 +942,6 @@ e_attachment_get_description (EAttachment *attachment)
return g_file_info_get_attribute_string (file_info, attribute);
}
-GIcon *
-e_attachment_get_icon (EAttachment *attachment)
-{
- GFileInfo *file_info;
- const gchar *attribute;
-
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
-
- attribute = G_FILE_ATTRIBUTE_STANDARD_ICON;
- file_info = e_attachment_get_file_info (attachment);
-
- if (file_info == NULL)
- return NULL;
-
- return (GIcon *)
- g_file_info_get_attribute_object (file_info, attribute);
-}
-
-gboolean
-e_attachment_get_loading (EAttachment *attachment)
-{
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
-
- return attachment->priv->loading;
-}
-
const gchar *
e_attachment_get_thumbnail_path (EAttachment *attachment)
{
@@ -1026,39 +960,18 @@ e_attachment_get_thumbnail_path (EAttachment *attachment)
}
gboolean
-e_attachment_get_saving (EAttachment *attachment)
-{
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
-
- return attachment->priv->saving;
-}
-
-guint64
-e_attachment_get_size (EAttachment *attachment)
-{
- GFileInfo *file_info;
- const gchar *attribute;
-
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), 0);
-
- attribute = G_FILE_ATTRIBUTE_STANDARD_SIZE;
- file_info = e_attachment_get_file_info (attachment);
-
- if (file_info == NULL)
- return 0;
-
- return g_file_info_get_attribute_uint64 (file_info, attribute);
-}
-
-gboolean
e_attachment_is_image (EAttachment *attachment)
{
+ GFileInfo *file_info;
const gchar *content_type;
g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
- content_type = e_attachment_get_content_type (attachment);
+ file_info = e_attachment_get_file_info (attachment);
+ if (file_info == NULL)
+ return FALSE;
+ content_type = g_file_info_get_content_type (file_info);
if (content_type == NULL)
return FALSE;
@@ -1068,12 +981,16 @@ e_attachment_is_image (EAttachment *attachment)
gboolean
e_attachment_is_rfc822 (EAttachment *attachment)
{
+ GFileInfo *file_info;
const gchar *content_type;
g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
- content_type = e_attachment_get_content_type (attachment);
+ file_info = e_attachment_get_file_info (attachment);
+ if (file_info == NULL)
+ return FALSE;
+ content_type = g_file_info_get_content_type (file_info);
if (content_type == NULL)
return FALSE;
@@ -1084,14 +1001,18 @@ GList *
e_attachment_list_apps (EAttachment *attachment)
{
GList *app_info_list;
+ GFileInfo *file_info;
const gchar *content_type;
const gchar *display_name;
gchar *allocated;
g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- content_type = e_attachment_get_content_type (attachment);
- display_name = e_attachment_get_display_name (attachment);
+ file_info = e_attachment_get_file_info (attachment);
+ g_return_val_if_fail (file_info != NULL, NULL);
+
+ content_type = g_file_info_get_content_type (file_info);
+ display_name = g_file_info_get_display_name (file_info);
g_return_val_if_fail (content_type != NULL, NULL);
app_info_list = g_app_info_get_all_for_type (content_type);
@@ -1113,11 +1034,20 @@ exit:
GList *
e_attachment_list_emblems (EAttachment *attachment)
{
+ GCancellable *cancellable;
GList *list = NULL;
GIcon *icon;
g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+ cancellable = attachment->priv->cancellable;
+
+ if (g_cancellable_is_cancelled (cancellable)) {
+ icon = g_themed_icon_new (EMBLEM_CANCELLED);
+ list = g_list_append (list, g_emblem_new (icon));
+ g_object_unref (icon);
+ }
+
if (e_attachment_get_loading (attachment)) {
icon = g_themed_icon_new (EMBLEM_LOADING);
list = g_list_append (list, g_emblem_new (icon));
@@ -1180,62 +1110,782 @@ e_attachment_list_emblems (EAttachment *attachment)
return list;
}
-/************************ e_attachment_launch_async() ************************/
+/************************* e_attachment_load_async() *************************/
+
+typedef struct _AttachmentLoadContext AttachmentLoadContext;
+
+struct _AttachmentLoadContext {
+ EAttachment *attachment;
+ GSimpleAsyncResult *simple;
+ GInputStream *input_stream;
+ GOutputStream *output_stream;
+ GFileInfo *file_info;
+ goffset total_num_bytes;
+ gssize bytes_read;
+ gchar buffer[4096];
+};
+
+/* Forward Declaration */
+static void
+attachment_load_stream_read_cb (GInputStream *input_stream,
+ GAsyncResult *result,
+ AttachmentLoadContext *load_context);
+
+static AttachmentLoadContext *
+attachment_load_context_new (EAttachment *attachment,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ AttachmentLoadContext *load_context;
+ GSimpleAsyncResult *simple;
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (attachment), callback,
+ user_data, e_attachment_load_async);
+
+ load_context = g_slice_new0 (AttachmentLoadContext);
+ load_context->attachment = g_object_ref (attachment);
+ load_context->simple = simple;
+
+ attachment_set_loading (load_context->attachment, TRUE);
+
+ return load_context;
+}
+
+static void
+attachment_load_context_free (AttachmentLoadContext *load_context)
+{
+ /* Do not free the GSimpleAsyncResult. */
+ g_object_unref (load_context->attachment);
+
+ if (load_context->input_stream != NULL)
+ g_object_unref (load_context->input_stream);
+
+ if (load_context->output_stream != NULL)
+ g_object_unref (load_context->output_stream);
+
+ if (load_context->file_info != NULL)
+ g_object_unref (load_context->file_info);
+
+ g_slice_free (AttachmentLoadContext, load_context);
+}
+
+static void
+attachment_load_finish (AttachmentLoadContext *load_context)
+{
+ GFileInfo *file_info;
+ EAttachment *attachment;
+ GMemoryOutputStream *output_stream;
+ GSimpleAsyncResult *simple;
+ CamelDataWrapper *wrapper;
+ CamelMimePart *mime_part;
+ CamelStream *stream;
+ const gchar *attribute;
+ const gchar *content_type;
+ const gchar *display_name;
+ const gchar *description;
+ const gchar *disposition;
+ gchar *mime_type;
+ gpointer data;
+ gsize size;
+
+ /* Steal the reference. */
+ simple = load_context->simple;
+ load_context->simple = NULL;
+
+ file_info = load_context->file_info;
+ attachment = load_context->attachment;
+ output_stream = G_MEMORY_OUTPUT_STREAM (load_context->output_stream);
+
+ if (e_attachment_is_rfc822 (attachment))
+ wrapper = (CamelDataWrapper *) camel_mime_message_new ();
+ else
+ wrapper = camel_data_wrapper_new ();
+
+ content_type = g_file_info_get_content_type (file_info);
+ mime_type = g_content_type_get_mime_type (content_type);
+
+ data = g_memory_output_stream_get_data (output_stream);
+ size = g_memory_output_stream_get_data_size (output_stream);
+
+ stream = camel_stream_mem_new_with_buffer (data, size);
+ camel_data_wrapper_construct_from_stream (wrapper, stream);
+ camel_data_wrapper_set_mime_type (wrapper, mime_type);
+ camel_stream_close (stream);
+ camel_object_unref (stream);
+
+ mime_part = camel_mime_part_new ();
+ camel_medium_set_content_object (CAMEL_MEDIUM (mime_part), wrapper);
+
+ camel_object_unref (wrapper);
+ g_free (mime_type);
+
+ display_name = g_file_info_get_display_name (file_info);
+ if (display_name != NULL)
+ camel_mime_part_set_filename (mime_part, display_name);
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
+ description = g_file_info_get_attribute_string (file_info, attribute);
+ if (description != NULL)
+ camel_mime_part_set_description (mime_part, description);
+
+ disposition = e_attachment_get_disposition (attachment);
+ if (disposition != NULL)
+ camel_mime_part_set_disposition (mime_part, disposition);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, mime_part, (GDestroyNotify) camel_object_unref);
+
+ g_simple_async_result_complete (simple);
+
+ attachment_load_context_free (load_context);
+}
+
+static void
+attachment_load_write_cb (GOutputStream *output_stream,
+ GAsyncResult *result,
+ AttachmentLoadContext *load_context)
+{
+ EAttachment *attachment;
+ GCancellable *cancellable;
+ GInputStream *input_stream;
+ gssize bytes_written;
+ GError *error = NULL;
+
+ bytes_written = g_output_stream_write_finish (
+ output_stream, result, &error);
+
+ if (error != NULL) {
+ GSimpleAsyncResult *simple;
+
+ /* Steal the reference. */
+ simple = load_context->simple;
+ load_context->simple = NULL;
+
+ g_simple_async_result_set_from_error (simple, error);
+ g_simple_async_result_complete (simple);
+ g_error_free (error);
+
+ attachment_load_context_free (load_context);
+
+ return;
+ }
+
+ attachment = load_context->attachment;
+ cancellable = attachment->priv->cancellable;
+ input_stream = load_context->input_stream;
+
+ attachment_progress_cb (
+ g_seekable_tell (G_SEEKABLE (output_stream)),
+ load_context->total_num_bytes, attachment);
+
+ if (bytes_written < load_context->bytes_read) {
+ g_memmove (
+ load_context->buffer,
+ load_context->buffer + bytes_written,
+ load_context->bytes_read - bytes_written);
+ load_context->bytes_read -= bytes_written;
+
+ g_output_stream_write_async (
+ output_stream,
+ load_context->buffer,
+ load_context->bytes_read,
+ G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) attachment_load_write_cb,
+ load_context);
+ } else
+ g_input_stream_read_async (
+ input_stream,
+ load_context->buffer,
+ sizeof (load_context->buffer),
+ G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) attachment_load_stream_read_cb,
+ load_context);
+}
static void
-attachment_launch_file (EActivity *activity,
- GAppInfo *app_info,
- GFile *file)
+attachment_load_stream_read_cb (GInputStream *input_stream,
+ GAsyncResult *result,
+ AttachmentLoadContext *load_context)
{
- GdkAppLaunchContext *launch_context;
+ EAttachment *attachment;
+ GCancellable *cancellable;
+ GOutputStream *output_stream;
+ gssize bytes_read;
+ GError *error = NULL;
+
+ bytes_read = g_input_stream_read_finish (
+ input_stream, result, &error);
+
+ if (error != NULL) {
+ GSimpleAsyncResult *simple;
+
+ /* Steal the reference. */
+ simple = load_context->simple;
+ load_context->simple = NULL;
+
+ g_simple_async_result_set_from_error (simple, error);
+ g_simple_async_result_complete (simple);
+ g_error_free (error);
+
+ attachment_load_context_free (load_context);
+
+ return;
+ }
+
+ if (bytes_read == 0) {
+ attachment_load_finish (load_context);
+ return;
+ }
+
+ attachment = load_context->attachment;
+ cancellable = attachment->priv->cancellable;
+ output_stream = load_context->output_stream;
+ load_context->bytes_read = bytes_read;
+
+ g_output_stream_write_async (
+ output_stream,
+ load_context->buffer,
+ load_context->bytes_read,
+ G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) attachment_load_write_cb,
+ load_context);
+}
+
+static void
+attachment_load_file_read_cb (GFile *file,
+ GAsyncResult *result,
+ AttachmentLoadContext *load_context)
+{
+ EAttachment *attachment;
+ GCancellable *cancellable;
+ GFileInputStream *input_stream;
+ GOutputStream *output_stream;
+ GError *error = NULL;
+
+ input_stream = g_file_read_finish (file, result, &error);
+ load_context->input_stream = G_INPUT_STREAM (input_stream);
+
+ if (error != NULL) {
+ GSimpleAsyncResult *simple;
+
+ /* Steal the reference. */
+ simple = load_context->simple;
+ load_context->simple = NULL;
+
+ g_simple_async_result_set_from_error (simple, error);
+ g_simple_async_result_complete (simple);
+ g_error_free (error);
+
+ attachment_load_context_free (load_context);
+
+ return;
+ }
+
+ /* Load the contents into a GMemoryOutputStream. */
+ output_stream = g_memory_output_stream_new (
+ NULL, 0, g_realloc, g_free);
+
+ attachment = load_context->attachment;
+ cancellable = attachment->priv->cancellable;
+ load_context->output_stream = output_stream;
+
+ g_input_stream_read_async (
+ load_context->input_stream,
+ load_context->buffer,
+ sizeof (load_context->buffer),
+ G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) attachment_load_stream_read_cb,
+ load_context);
+}
+
+static void
+attachment_load_query_info_cb (GFile *file,
+ GAsyncResult *result,
+ AttachmentLoadContext *load_context)
+{
+ EAttachment *attachment;
+ GCancellable *cancellable;
+ GFileInfo *file_info;
+ GError *error = NULL;
+
+ attachment = load_context->attachment;
+ cancellable = attachment->priv->cancellable;
+
+ file_info = g_file_query_info_finish (file, result, &error);
+ attachment_set_file_info (attachment, file_info);
+ load_context->file_info = file_info;
+
+ if (error != NULL) {
+ GSimpleAsyncResult *simple;
+
+ /* Steal the reference. */
+ simple = load_context->simple;
+ load_context->simple = NULL;
+
+ g_simple_async_result_set_from_error (simple, error);
+ g_simple_async_result_complete (simple);
+ g_error_free (error);
+
+ attachment_load_context_free (load_context);
+
+ return;
+ }
+
+ load_context->total_num_bytes = g_file_info_get_size (file_info);
+
+ g_file_read_async (
+ file, G_PRIORITY_DEFAULT,
+ cancellable, (GAsyncReadyCallback)
+ attachment_load_file_read_cb, load_context);
+}
+
+static void
+attachment_load_from_mime_part (AttachmentLoadContext *load_context)
+{
+ GFileInfo *file_info;
+ EAttachment *attachment;
+ GSimpleAsyncResult *simple;
+ CamelContentType *content_type;
+ CamelMimePart *mime_part;
+ const gchar *attribute;
+ const gchar *string;
+ gchar *allocated;
+ goffset size;
+
+ attachment = load_context->attachment;
+ mime_part = e_attachment_get_mime_part (attachment);
+
+ file_info = g_file_info_new ();
+ load_context->file_info = file_info;
+
+ content_type = camel_mime_part_get_content_type (mime_part);
+ allocated = camel_content_type_simple (content_type);
+ if (allocated != NULL) {
+ GIcon *icon;
+ gchar *cp;
+
+ /* GIO expects lowercase MIME types. */
+ for (cp = allocated; *cp != '\0'; cp++)
+ *cp = g_ascii_tolower (*cp);
+
+ /* Swap the MIME type for a content type. */
+ cp = g_content_type_from_mime_type (allocated);
+ g_free (allocated);
+ allocated = cp;
+
+ /* Use the MIME part's filename if we have to. */
+ if (g_content_type_is_unknown (allocated)) {
+ string = camel_mime_part_get_filename (mime_part);
+ if (string != NULL) {
+ g_free (allocated);
+ allocated = g_content_type_guess (
+ string, NULL, 0, NULL);
+ }
+ }
+
+ g_file_info_set_content_type (file_info, allocated);
+
+ icon = g_content_type_get_icon (allocated);
+ if (icon != NULL) {
+ g_file_info_set_icon (file_info, icon);
+ g_object_unref (icon);
+ }
+ }
+ g_free (allocated);
+
+ string = camel_mime_part_get_filename (mime_part);
+ if (string != NULL)
+ g_file_info_set_display_name (file_info, string);
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
+ string = camel_mime_part_get_description (mime_part);
+ if (string != NULL)
+ g_file_info_set_attribute_string (
+ file_info, attribute, string);
+
+ size = (goffset) camel_mime_part_get_content_size (mime_part);
+ g_file_info_set_size (file_info, size);
+
+ string = camel_mime_part_get_disposition (mime_part);
+ e_attachment_set_disposition (attachment, string);
+
+ attachment_set_file_info (attachment, file_info);
+
+ /* Steal the reference. */
+ simple = load_context->simple;
+ load_context->simple = NULL;
+
+ camel_object_ref (mime_part);
+ g_simple_async_result_set_op_res_gpointer (
+ simple, mime_part,
+ (GDestroyNotify) camel_object_unref);
+ g_simple_async_result_complete_in_idle (simple);
+
+ attachment_load_context_free (load_context);
+}
+
+void
+e_attachment_load_async (EAttachment *attachment,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ AttachmentLoadContext *load_context;
+ GCancellable *cancellable;
+ CamelMimePart *mime_part;
+ GFile *file;
+
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ g_return_if_fail (callback != NULL);
+
+ g_return_if_fail (!e_attachment_get_loading (attachment));
+ g_return_if_fail (!e_attachment_get_saving (attachment));
+
+ file = e_attachment_get_file (attachment);
+ mime_part = e_attachment_get_mime_part (attachment);
+ g_return_if_fail (file != NULL || mime_part != NULL);
+
+ load_context = attachment_load_context_new (
+ attachment, callback, user_data);
+
+ cancellable = attachment->priv->cancellable;
+ g_cancellable_reset (cancellable);
+
+ /* Handle the trivial case first. */
+ if (mime_part != NULL)
+ attachment_load_from_mime_part (load_context);
+
+ else if (file != NULL)
+ g_file_query_info_async (
+ file, ATTACHMENT_QUERY,
+ G_FILE_QUERY_INFO_NONE,G_PRIORITY_DEFAULT,
+ cancellable, (GAsyncReadyCallback)
+ attachment_load_query_info_cb, load_context);
+}
+
+gboolean
+e_attachment_load_finish (EAttachment *attachment,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ CamelMimePart *mime_part;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (result,
+ G_OBJECT (attachment), e_attachment_load_async), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ mime_part = g_simple_async_result_get_op_res_gpointer (simple);
+ if (mime_part != NULL)
+ e_attachment_set_mime_part (attachment, mime_part);
+ g_simple_async_result_propagate_error (simple, error);
+ g_object_unref (simple);
+
+ attachment_set_loading (attachment, FALSE);
+
+ return (mime_part != NULL);
+}
+
+void
+e_attachment_load_handle_error (EAttachment *attachment,
+ GAsyncResult *result,
+ GtkWindow *parent)
+{
+ GtkWidget *dialog;
+ GFileInfo *file_info;
+ const gchar *display_name;
+ const gchar *primary_text;
+ GError *error = NULL;
+
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ g_return_if_fail (G_IS_ASYNC_RESULT (result));
+ g_return_if_fail (GTK_IS_WINDOW (parent));
+
+ if (e_attachment_load_finish (attachment, result, &error))
+ return;
+
+ /* Ignore cancellations. */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ file_info = e_attachment_get_file_info (attachment);
+
+ if (file_info != NULL)
+ display_name = g_file_info_get_display_name (file_info);
+ else
+ display_name = NULL;
+
+ if (display_name != NULL)
+ primary_text = g_strdup_printf (
+ _("Could not load '%s'"), display_name);
+ else
+ primary_text = g_strdup_printf (
+ _("Could not load the attachment"));
+
+ dialog = gtk_message_dialog_new_with_markup (
+ parent, GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ "<big><b>%s</b></big>", primary_text);
+
+ gtk_message_dialog_format_secondary_text (
+ GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+ g_error_free (error);
+}
+
+/************************* e_attachment_open_async() *************************/
+
+typedef struct _AttachmentOpenContext AttachmentOpenContext;
+
+struct _AttachmentOpenContext {
+ EAttachment *attachment;
+ GSimpleAsyncResult *simple;
+ GAppInfo *app_info;
+ GFile *file;
+};
+
+static AttachmentOpenContext *
+attachment_open_context_new (EAttachment *attachment,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ AttachmentOpenContext *open_context;
+ GSimpleAsyncResult *simple;
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (attachment), callback,
+ user_data, e_attachment_open_async);
+
+ open_context = g_slice_new0 (AttachmentOpenContext);
+ open_context->attachment = g_object_ref (attachment);
+ open_context->simple = simple;
+
+ return open_context;
+}
+
+static void
+attachment_open_context_free (AttachmentOpenContext *open_context)
+{
+ /* Do not free the GSimpleAsyncResult. */
+ g_object_unref (open_context->attachment);
+
+ if (open_context->app_info != NULL)
+ g_object_unref (open_context->app_info);
+
+ if (open_context->file != NULL)
+ g_object_unref (open_context->file);
+
+ g_slice_free (AttachmentOpenContext, open_context);
+}
+
+static void
+attachment_open_file (AttachmentOpenContext *open_context)
+{
+ GdkAppLaunchContext *context;
+ GSimpleAsyncResult *simple;
GList *file_list;
+ gboolean success;
GError *error = NULL;
- file_list = g_list_prepend (NULL, file);
- launch_context = gdk_app_launch_context_new ();
+ /* Steal the reference. */
+ simple = open_context->simple;
+ open_context->simple = NULL;
- g_app_info_launch (
- app_info, file_list,
- G_APP_LAUNCH_CONTEXT (launch_context), &error);
+ if (open_context->app_info == NULL)
+ open_context->app_info = g_file_query_default_handler (
+ open_context->file, NULL, &error);
+
+ if (open_context->app_info == NULL)
+ goto exit;
+
+ context = gdk_app_launch_context_new ();
+ file_list = g_list_prepend (NULL, open_context->file);
+
+ success = g_app_info_launch (
+ open_context->app_info, file_list,
+ G_APP_LAUNCH_CONTEXT (context), &error);
+
+ g_simple_async_result_set_op_res_gboolean (simple, success);
g_list_free (file_list);
- g_object_unref (launch_context);
+ g_object_unref (context);
+exit:
if (error != NULL) {
- e_activity_set_error (activity, error);
+ g_simple_async_result_set_from_error (simple, error);
g_error_free (error);
}
- e_activity_complete (activity);
- g_object_unref (activity);
+ g_simple_async_result_complete (simple);
+ attachment_open_context_free (open_context);
+}
+
+static void
+attachment_open_save_finished_cb (EAttachment *attachment,
+ GAsyncResult *result,
+ AttachmentOpenContext *open_context)
+{
+ GError *error = NULL;
+
+ if (e_attachment_save_finish (attachment, result, &error))
+ attachment_open_file (open_context);
+ else {
+ GSimpleAsyncResult *simple;
+
+ /* Steal the reference. */
+ simple = open_context->simple;
+ open_context->simple = NULL;
+
+ g_simple_async_result_set_from_error (simple, error);
+ g_simple_async_result_complete (simple);
+ g_error_free (error);
+
+ attachment_open_context_free (open_context);
+ }
+}
+
+static void
+attachment_open_save_temporary (AttachmentOpenContext *open_context)
+{
+ gchar *path;
+ gint fd;
+ GError *error = NULL;
+
+ fd = e_file_open_tmp (&path, &error);
+ if (error != NULL) {
+ GSimpleAsyncResult *simple;
+
+ /* Steal the reference. */
+ simple = open_context->simple;
+ open_context->simple = NULL;
+
+ g_simple_async_result_set_from_error (simple, error);
+ g_simple_async_result_complete (simple);
+ g_error_free (error);
+
+ attachment_open_context_free (open_context);
+ return;
+ }
+
+ close (fd);
+
+ open_context->file = g_file_new_for_path (path);
+
+ e_attachment_save_async (
+ open_context->attachment, open_context->file,
+ (GAsyncReadyCallback) attachment_open_save_finished_cb,
+ open_context);
}
void
-e_attachment_launch_async (EAttachment *attachment,
- EFileActivity *file_activity,
- GAppInfo *app_info)
+e_attachment_open_async (EAttachment *attachment,
+ GAppInfo *app_info,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ AttachmentOpenContext *open_context;
CamelMimePart *mime_part;
GFile *file;
g_return_if_fail (E_IS_ATTACHMENT (attachment));
- g_return_if_fail (E_IS_FILE_ACTIVITY (file_activity));
- g_return_if_fail (G_IS_APP_INFO (app_info));
+ g_return_if_fail (callback != NULL);
+
+ g_return_if_fail (!e_attachment_get_loading (attachment));
+ g_return_if_fail (!e_attachment_get_saving (attachment));
file = e_attachment_get_file (attachment);
mime_part = e_attachment_get_mime_part (attachment);
g_return_if_fail (file != NULL || mime_part != NULL);
+ open_context = attachment_open_context_new (
+ attachment, callback, user_data);
+
+ if (G_IS_APP_INFO (app_info))
+ open_context->app_info = g_object_ref (app_info);
+
/* If the attachment already references a GFile, we can launch
* the application directly. Otherwise we have to save the MIME
* part to a temporary file and launch the application from that. */
- if (G_IS_FILE (file)) {
- EActivity *activity = g_object_ref (file_activity);
- attachment_launch_file (activity, app_info, file);
+ if (file != NULL) {
+ open_context->file = g_object_ref (file);
+ attachment_open_file (open_context);
- } else if (CAMEL_IS_MIME_PART (mime_part)) {
- /* XXX Not done yet. */
- }
+ } else if (mime_part != NULL)
+ attachment_open_save_temporary (open_context);
+}
+
+gboolean
+e_attachment_open_finish (EAttachment *attachment,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ gboolean success;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (result,
+ G_OBJECT (attachment), e_attachment_open_async), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ success = g_simple_async_result_get_op_res_gboolean (simple);
+ g_simple_async_result_propagate_error (simple, error);
+ g_object_unref (simple);
+
+ return success;
+}
+
+void
+e_attachment_open_handle_error (EAttachment *attachment,
+ GAsyncResult *result,
+ GtkWindow *parent)
+{
+ GtkWidget *dialog;
+ GFileInfo *file_info;
+ const gchar *display_name;
+ const gchar *primary_text;
+ GError *error = NULL;
+
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ g_return_if_fail (G_IS_ASYNC_RESULT (result));
+ g_return_if_fail (GTK_IS_WINDOW (parent));
+
+ if (e_attachment_open_finish (attachment, result, &error))
+ return;
+
+ /* Ignore cancellations. */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ file_info = e_attachment_get_file_info (attachment);
+
+ if (file_info != NULL)
+ display_name = g_file_info_get_display_name (file_info);
+ else
+ display_name = NULL;
+
+ if (display_name != NULL)
+ primary_text = g_strdup_printf (
+ _("Could not open '%s'"), display_name);
+ else
+ primary_text = g_strdup_printf (
+ _("Could not open the attachment"));
+
+ dialog = gtk_message_dialog_new_with_markup (
+ parent, GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ "<big><b>%s</b></big>", primary_text);
+
+ gtk_message_dialog_format_secondary_text (
+ GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+ g_error_free (error);
}
/************************* e_attachment_save_async() *************************/
@@ -1244,20 +1894,35 @@ typedef struct _AttachmentSaveContext AttachmentSaveContext;
struct _AttachmentSaveContext {
EAttachment *attachment;
- EFileActivity *file_activity;
+ GSimpleAsyncResult *simple;
+ GInputStream *input_stream;
GOutputStream *output_stream;
+ goffset total_num_bytes;
+ gssize bytes_read;
+ gchar buffer[4096];
};
+/* Forward Declaration */
+static void
+attachment_save_read_cb (GInputStream *input_stream,
+ GAsyncResult *result,
+ AttachmentSaveContext *save_context);
+
static AttachmentSaveContext *
attachment_save_context_new (EAttachment *attachment,
- EFileActivity *file_activity)
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
AttachmentSaveContext *save_context;
+ GSimpleAsyncResult *simple;
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (attachment), callback,
+ user_data, e_attachment_save_async);
- save_context = g_slice_new (AttachmentSaveContext);
+ save_context = g_slice_new0 (AttachmentSaveContext);
save_context->attachment = g_object_ref (attachment);
- save_context->file_activity = g_object_ref (file_activity);
- save_context->output_stream = NULL;
+ save_context->simple = simple;
attachment_set_saving (save_context->attachment, TRUE);
@@ -1267,10 +1932,11 @@ attachment_save_context_new (EAttachment *attachment,
static void
attachment_save_context_free (AttachmentSaveContext *save_context)
{
- attachment_set_saving (save_context->attachment, FALSE);
-
+ /* Do not free the GSimpleAsyncResult. */
g_object_unref (save_context->attachment);
- g_object_unref (save_context->file_activity);
+
+ if (save_context->input_stream != NULL)
+ g_object_unref (save_context->input_stream);
if (save_context->output_stream != NULL)
g_object_unref (save_context->output_stream);
@@ -1283,114 +1949,228 @@ attachment_save_file_cb (GFile *source,
GAsyncResult *result,
AttachmentSaveContext *save_context)
{
- EActivity *activity;
+ GSimpleAsyncResult *simple;
+ gboolean success;
GError *error = NULL;
- activity = E_ACTIVITY (save_context->file_activity);
+ /* Steal the reference. */
+ simple = save_context->simple;
+ save_context->simple = NULL;
- if (!g_file_copy_finish (source, result, &error)) {
- e_activity_set_error (activity, error);
+ success = g_file_copy_finish (source, result, &error);
+ g_simple_async_result_set_op_res_gboolean (simple, success);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
g_error_free (error);
}
- e_activity_complete (activity);
+ g_simple_async_result_complete (simple);
attachment_save_context_free (save_context);
}
-static gpointer
-attachment_save_part_thread (AttachmentSaveContext *save_context)
+static void
+attachment_save_write_cb (GOutputStream *output_stream,
+ GAsyncResult *result,
+ AttachmentSaveContext *save_context)
{
- GObject *object;
EAttachment *attachment;
GCancellable *cancellable;
- GOutputStream *output_stream;
- EFileActivity *file_activity;
- CamelDataWrapper *wrapper;
- CamelMimePart *mime_part;
- CamelStream *stream;
+ GInputStream *input_stream;
+ gssize bytes_written;
GError *error = NULL;
- attachment = save_context->attachment;
- file_activity = save_context->file_activity;
- output_stream = save_context->output_stream;
+ bytes_written = g_output_stream_write_finish (
+ output_stream, result, &error);
- /* Last chance to cancel. */
- cancellable = e_file_activity_get_cancellable (file_activity);
- if (g_cancellable_set_error_if_cancelled (cancellable, &error))
- goto exit;
+ if (error != NULL) {
+ GSimpleAsyncResult *simple;
- object = g_object_ref (output_stream);
- stream = camel_stream_vfs_new_with_stream (object);
- mime_part = e_attachment_get_mime_part (attachment);
- wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+ /* Steal the reference. */
+ simple = save_context->simple;
+ save_context->simple = NULL;
+
+ g_simple_async_result_set_from_error (simple, error);
+ g_simple_async_result_complete (simple);
+ g_error_free (error);
- if (camel_data_wrapper_decode_to_stream (wrapper, stream) < 0)
- g_set_error (
- &error, G_IO_ERROR,
- g_io_error_from_errno (errno),
- g_strerror (errno));
+ attachment_save_context_free (save_context);
- else if (camel_stream_flush (stream) < 0)
- g_set_error (
- &error, G_IO_ERROR,
- g_io_error_from_errno (errno),
- g_strerror (errno));
+ return;
+ }
- camel_object_unref (stream);
+ attachment = save_context->attachment;
+ cancellable = attachment->priv->cancellable;
+ input_stream = save_context->input_stream;
+
+ if (bytes_written < save_context->bytes_read) {
+ g_memmove (
+ save_context->buffer,
+ save_context->buffer + bytes_written,
+ save_context->bytes_read - bytes_written);
+ save_context->bytes_read -= bytes_written;
+
+ g_output_stream_write_async (
+ output_stream,
+ save_context->buffer,
+ save_context->bytes_read,
+ G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) attachment_save_write_cb,
+ save_context);
+ } else
+ g_input_stream_read_async (
+ input_stream,
+ save_context->buffer,
+ sizeof (save_context->buffer),
+ G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) attachment_save_read_cb,
+ save_context);
+}
+
+static void
+attachment_save_read_cb (GInputStream *input_stream,
+ GAsyncResult *result,
+ AttachmentSaveContext *save_context)
+{
+ EAttachment *attachment;
+ GCancellable *cancellable;
+ GOutputStream *output_stream;
+ gssize bytes_read;
+ GError *error = NULL;
+
+ bytes_read = g_input_stream_read_finish (
+ input_stream, result, &error);
-exit:
if (error != NULL) {
- e_activity_set_error (E_ACTIVITY (file_activity), error);
+ GSimpleAsyncResult *simple;
+
+ /* Steal the reference. */
+ simple = save_context->simple;
+ save_context->simple = NULL;
+
+ g_simple_async_result_set_from_error (simple, error);
+ g_simple_async_result_complete (simple);
g_error_free (error);
+
+ attachment_save_context_free (save_context);
+
+ return;
}
- e_activity_complete_in_idle (E_ACTIVITY (file_activity));
- attachment_save_context_free (save_context);
+ if (bytes_read == 0) {
+ GSimpleAsyncResult *simple;
+
+ /* Steal the reference. */
+ simple = save_context->simple;
+ save_context->simple = NULL;
+
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_simple_async_result_complete (simple);
- return NULL;
+ attachment_save_context_free (save_context);
+
+ return;
+ }
+
+ attachment = save_context->attachment;
+ cancellable = attachment->priv->cancellable;
+ output_stream = save_context->output_stream;
+ save_context->bytes_read = bytes_read;
+
+ attachment_progress_cb (
+ g_seekable_tell (G_SEEKABLE (input_stream)),
+ save_context->total_num_bytes, attachment);
+
+ g_output_stream_write_async (
+ output_stream,
+ save_context->buffer,
+ save_context->bytes_read,
+ G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) attachment_save_write_cb,
+ save_context);
}
static void
-attachment_save_part_cb (GFile *destination,
- GAsyncResult *result,
- AttachmentSaveContext *save_context)
+attachment_save_replace_cb (GFile *destination,
+ GAsyncResult *result,
+ AttachmentSaveContext *save_context)
{
+ GCancellable *cancellable;
+ GInputStream *input_stream;
GFileOutputStream *output_stream;
- EActivity *activity;
+ CamelDataWrapper *wrapper;
+ CamelMimePart *mime_part;
+ CamelStream *stream;
+ EAttachment *attachment;
+ GByteArray *buffer;
GError *error = NULL;
- activity = E_ACTIVITY (save_context->file_activity);
output_stream = g_file_replace_finish (destination, result, &error);
save_context->output_stream = G_OUTPUT_STREAM (output_stream);
- if (output_stream != NULL)
- g_thread_create (
- (GThreadFunc) attachment_save_part_thread,
- save_context, FALSE, &error);
-
if (error != NULL) {
- e_activity_set_error (activity, error);
- e_activity_complete (activity);
+ GSimpleAsyncResult *simple;
+
+ /* Steal the reference. */
+ simple = save_context->simple;
+ save_context->simple = NULL;
+
+ g_simple_async_result_set_from_error (simple, error);
+ g_simple_async_result_complete (simple);
g_error_free (error);
attachment_save_context_free (save_context);
+
+ return;
}
+
+ attachment = save_context->attachment;
+ cancellable = attachment->priv->cancellable;
+ mime_part = e_attachment_get_mime_part (attachment);
+
+ /* Decode the MIME part to an in-memory buffer. We have to do
+ * this because CamelStream is synchronous-only, and using threads
+ * is dangerous because CamelDataWrapper is not reentrant. */
+ buffer = g_byte_array_new ();
+ stream = camel_stream_mem_new_with_byte_array (buffer);
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+ camel_data_wrapper_decode_to_stream (wrapper, stream);
+ camel_object_unref (stream);
+
+ /* Load the buffer into a GMemoryInputStream. */
+ input_stream = g_memory_input_stream_new_from_data (
+ buffer->data, (gssize) buffer->len,
+ (GDestroyNotify) g_free);
+ save_context->input_stream = input_stream;
+ save_context->total_num_bytes = (goffset) buffer->len;
+ g_byte_array_free (buffer, FALSE);
+
+ g_input_stream_read_async (
+ input_stream,
+ save_context->buffer,
+ sizeof (save_context->buffer),
+ G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) attachment_save_read_cb,
+ save_context);
}
void
e_attachment_save_async (EAttachment *attachment,
- EFileActivity *file_activity,
- GFile *destination)
+ GFile *destination,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
AttachmentSaveContext *save_context;
- GFileProgressCallback progress_callback;
GCancellable *cancellable;
CamelMimePart *mime_part;
GFile *source;
g_return_if_fail (E_IS_ATTACHMENT (attachment));
- g_return_if_fail (E_IS_FILE_ACTIVITY (file_activity));
g_return_if_fail (G_IS_FILE (destination));
+ g_return_if_fail (callback != NULL);
+
+ g_return_if_fail (!e_attachment_get_loading (attachment));
+ g_return_if_fail (!e_attachment_get_saving (attachment));
/* The attachment content is either a GFile (on disk) or a
* CamelMimePart (in memory). Each is saved differently. */
@@ -1399,9 +2179,11 @@ e_attachment_save_async (EAttachment *attachment,
mime_part = e_attachment_get_mime_part (attachment);
g_return_if_fail (source != NULL || mime_part != NULL);
- save_context = attachment_save_context_new (attachment, file_activity);
- cancellable = e_file_activity_get_cancellable (file_activity);
- progress_callback = e_file_activity_progress;
+ save_context = attachment_save_context_new (
+ attachment, callback, user_data);
+
+ cancellable = attachment->priv->cancellable;
+ g_cancellable_reset (cancellable);
/* GFile is the easier, but probably less common case. The
* attachment already references an on-disk file, so we can
@@ -1414,7 +2196,8 @@ e_attachment_save_async (EAttachment *attachment,
source, destination,
G_FILE_COPY_OVERWRITE,
G_PRIORITY_DEFAULT, cancellable,
- progress_callback, file_activity,
+ (GFileProgressCallback) attachment_progress_cb,
+ attachment,
(GAsyncReadyCallback) attachment_save_file_cb,
save_context);
@@ -1423,309 +2206,83 @@ e_attachment_save_async (EAttachment *attachment,
* destination file for writing. Stage two spawns a thread that
* decodes the MIME part to the destination file. This stage is
* not cancellable, unfortunately. */
- else if (CAMEL_IS_MIME_PART (mime_part)) {
- g_object_set_data_full (
- G_OBJECT (file_activity),
- "attachment", g_object_ref (attachment),
- (GDestroyNotify) g_object_unref);
+ else if (CAMEL_IS_MIME_PART (mime_part))
g_file_replace_async (
destination, NULL, FALSE,
G_FILE_CREATE_REPLACE_DESTINATION,
G_PRIORITY_DEFAULT, cancellable,
- (GAsyncReadyCallback) attachment_save_part_cb,
+ (GAsyncReadyCallback) attachment_save_replace_cb,
save_context);
- }
}
-#if 0
-typedef struct {
- gint io_priority;
- GCancellable *cancellable;
- GSimpleAsyncResult *simple;
- GFileInfo *file_info;
-} BuildMimePartData;
-
-static BuildMimePartData *
-attachment_build_mime_part_data_new (EAttachment *attachment,
- gint io_priority,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data,
- gpointer source_tag)
+gboolean
+e_attachment_save_finish (EAttachment *attachment,
+ GAsyncResult *result,
+ GError **error)
{
- BuildMimePartData *data;
GSimpleAsyncResult *simple;
+ gboolean success;
- simple = g_simple_async_result_new (
- G_OBJECT (attachment), callback, user_data, source_tag);
-
- if (G_IS_CANCELLABLE (cancellable))
- g_object_ref (cancellable);
-
- data = g_slice_new0 (BuildMimePartData);
- data->io_priority = io_priority;
- data->cancellable = cancellable;
- data->simple = simple;
- return data;
-}
-
-static void
-attachment_build_mime_part_data_free (BuildMimePartData *data)
-{
- if (data->attachment != NULL)
- g_object_unref (data->attachment);
-
- if (data->cancellable != NULL)
- g_object_unref (data->cancellable);
-
- if (data->simple != NULL)
- g_object_unref (data->simple);
-
- if (data->file_info != NULL)
- g_object_unref (data->file_info);
-
- g_slice_free (BuildMimePartData, data);
-}
-
-static void
-attachment_build_mime_part_splice_cb (GObject *source,
- GAsyncResult *result,
- gpointer user_data)
-{
- GSimpleAsyncResult *final_result;
- GCancellable *cancellable;
- EAttachment *attachment;
- CamelDataWrapper *wrapper;
- CamelMimePart *mime_part;
- CamelStream *stream;
- const gchar *content_type;
- gchar *mime_type;
- gssize length;
- gpointer data;
- GError *error = NULL;
-
- final_result = G_SIMPLE_ASYNC_RESULT (user_data);
-
- cancellable = g_cancellable_get_current ();
- g_cancellable_pop_current (cancellable);
- g_object_unref (cancellable);
-
- length = g_output_stream_splice_finish (
- G_OUTPUT_STREAM (source), result, &error);
- if (error != NULL)
- goto fail;
-
- data = g_memory_output_stream_get_data (
- G_MEMORY_OUTPUT_STREAM (source));
-
- attachment = E_ATTACHMENT (
- g_async_result_get_source_object (
- G_ASYNC_RESULT (final_result)));
-
- if (e_attachment_is_rfc822 (attachment))
- wrapper = (CamelDataWrapper *) camel_mime_message_new ();
- else
- wrapper = camel_data_wrapper_new ();
-
- content_type = e_attachment_get_content_type (attachment);
- mime_type = g_content_type_get_mime_type (content_type);
-
- stream = camel_stream_mem_new_with_buffer (data, length);
- camel_data_wrapper_construct_from_stream (wrapper, stream);
- camel_data_wrapper_set_mime_type (wrapper, mime_type);
- camel_object_unref (stream);
-
- mime_part = camel_mime_part_new ();
- camel_medium_set_content_object (CAMEL_MEDIUM (mime_part), wrapper);
-
- g_simple_async_result_set_op_res_gpointer (
- final_result, mime_part, camel_object_unref);
-
- g_simple_async_result_complete (final_result);
-
- camel_object_unref (wrapper);
- g_free (mime_type);
-
- return;
-
-fail:
- g_simple_async_result_set_from_error (final_result, error);
- g_simple_async_result_complete (final_result);
- g_error_free (error);
-}
-
-static void
-attachment_build_mime_part_read_cb (GObject *source,
- GAsyncResult *result,
- BuildMimePartData *data)
-{
- GFileInputStream *input_stream;
- GOutputStream *output_stream;
- GCancellable *cancellable;
- GError *error = NULL;
-
- input_stream = g_file_read_finish (G_FILE (source), result, &error);
- if (error != NULL)
- goto fail;
-
- output_stream = g_memory_output_stream_new (
- NULL, 0, g_realloc, g_free);
-
- g_output_stream_splice_async (
- output_stream, G_INPUT_STREAM (input_stream),
- G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
- G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
- G_PRIORITY_DEFAULT, cancellable,
- attachment_build_mime_part_splice_cb, result);
-
- g_cancellable_push_current (cancellable);
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (result,
+ G_OBJECT (attachment), e_attachment_save_async), FALSE);
- g_object_unref (input_stream);
- g_object_unref (output_stream);
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ success = g_simple_async_result_get_op_res_gboolean (simple);
+ g_simple_async_result_propagate_error (simple, error);
+ g_object_unref (simple);
- return;
+ attachment_set_saving (attachment, FALSE);
-fail:
- g_simple_async_result_set_from_error (final_result, error);
- g_simple_async_result_complete (final_result);
- g_error_free (error);
+ return success;
}
-static gboolean
-attachment_build_mime_part_idle_cb (BuildMimePartData *data)
+void
+e_attachment_save_handle_error (EAttachment *attachment,
+ GAsyncResult *result,
+ GtkWindow *parent)
{
- GObject *source;
- GAsyncResult *result;
+ GtkWidget *dialog;
GFileInfo *file_info;
- GFile *file;
+ const gchar *display_name;
+ const gchar *primary_text;
GError *error = NULL;
- if (g_cancellable_set_error_if_cancelled (data->cancellable, &error))
- goto cancelled;
-
- result = G_ASYNC_RESULT (data->simple);
- source = g_async_result_get_source_object (result);
- file_info = e_attachment_get_file_info (E_ATTACHMENT (source));
-
- /* Poll again on the next idle. */
- if (!G_IS_FILE_INFO (file_info))
- return TRUE;
-
- /* We have a GFileInfo, so on to step 2. */
-
- data->file_info = g_file_info_dup (file_info);
- file = e_attachment_get_file (E_ATTACHMENT (source));
-
- /* Because Camel's stream API is synchronous and not
- * cancellable, we have to asynchronously read the file
- * into memory and then encode it to a MIME part. That
- * means double buffering the file contents in memory,
- * unfortunately. */
- g_file_read_async (
- file, data->io_priority, data->cancellable,
- attachment_build_mime_part_read_cb, data);
-
- return FALSE;
-
-cancelled:
- g_simple_async_result_set_op_res_gboolean (data->simple, FALSE);
- g_simple_async_result_set_from_error (data->simple, error);
- g_simple_async_result_complete (data->simple);
-
- build_mime_part_data_free (data);
- g_error_free (error);
-
- return FALSE;
-}
-
-void
-e_attachment_build_mime_part_async (EAttachment *attachment,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- CamelMimePart *mime_part;
- GSimpleAsyncResult *result;
- GFile *file;
-
g_return_if_fail (E_IS_ATTACHMENT (attachment));
- g_return_if_fail (callback != NULL);
+ g_return_if_fail (G_IS_ASYNC_RESULT (result));
+ g_return_if_fail (GTK_IS_WINDOW (parent));
- file = e_attachment_get_file (attachment);
- mime_part = e_attachment_get_mime_part (attachment);
- g_return_if_fail (file != NULL || mime_part != NULL);
-
- result = g_simple_async_result_new (
- G_OBJECT (attachment), callback, user_data,
- e_attachment_build_mime_part_async);
-
- /* First try the easy way out. */
- if (CAMEL_IS_MIME_PART (mime_part)) {
- camel_object_ref (mime_part);
- g_simple_async_result_set_op_res_gpointer (
- result, mime_part, camel_object_unref);
- g_simple_async_result_complete_in_idle (result);
+ if (e_attachment_save_finish (attachment, result, &error))
return;
- }
-
- /* XXX g_cancellable_push_current() documentation lies.
- * The function rejects NULL pointers, so create a
- * dummy GCancellable if necessary. */
- if (cancellable == NULL)
- cancellable = g_cancellable_new ();
- else
- g_object_ref (cancellable);
-
- /* Because Camel's stream API is synchronous and not
- * cancellable, we have to asynchronously read the file
- * into memory and then encode it to a MIME part. That
- * means it's double buffered, unfortunately. */
- g_file_read_async (
- file, G_PRIORITY_DEFAULT, cancellable,
- attachment_build_mime_part_read_cb, result);
-
- g_cancellable_push_current (cancellable);
-}
-
-CamelMimePart *
-e_attachment_build_mime_part_finish (EAttachment *attachment,
- GAsyncResult *result,
- GError **error)
-{
- CamelMimePart *mime_part;
- GSimpleAsyncResult *simple_result;
- gboolean async_result_is_valid;
- gpointer source_tag;
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+ /* Ignore cancellations. */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
- source_tag = e_attachment_build_mime_part_async;
- async_result_is_valid = g_simple_async_result_is_valid (
- result, G_OBJECT (attachment), source_tag);
- g_return_val_if_fail (async_result_is_valid, NULL);
+ file_info = e_attachment_get_file_info (attachment);
- simple_result = G_SIMPLE_ASYNC_RESULT (result);
- g_simple_async_result_propagate_error (simple_result, error);
- mime_part = g_simple_async_result_get_op_res_gpointer (simple_result);
- attachment_file_info_to_mime_part (attachment, mime_part);
+ if (file_info != NULL)
+ display_name = g_file_info_get_display_name (file_info);
+ else
+ display_name = NULL;
- if (CAMEL_IS_MIME_PART (mime_part))
- camel_object_ref (mime_part);
+ if (display_name != NULL)
+ primary_text = g_strdup_printf (
+ _("Could not save '%s'"), display_name);
+ else
+ primary_text = g_strdup_printf (
+ _("Could not save the attachment"));
- g_object_unref (result);
+ dialog = gtk_message_dialog_new_with_markup (
+ parent, GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ "<big><b>%s</b></big>", primary_text);
- return mime_part;
-}
-#endif
+ gtk_message_dialog_format_secondary_text (
+ GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
-void
-_e_attachment_set_reference (EAttachment *attachment,
- GtkTreeRowReference *reference)
-{
- g_return_if_fail (E_IS_ATTACHMENT (attachment));
- g_return_if_fail (reference != NULL);
+ gtk_dialog_run (GTK_DIALOG (dialog));
- gtk_tree_row_reference_free (attachment->priv->reference);
- attachment->priv->reference = gtk_tree_row_reference_copy (reference);
+ gtk_widget_destroy (dialog);
+ g_error_free (error);
}
diff --git a/widgets/misc/e-attachment.h b/widgets/misc/e-attachment.h
index d9ef68bf39..1ca5b3121b 100644
--- a/widgets/misc/e-attachment.h
+++ b/widgets/misc/e-attachment.h
@@ -22,12 +22,11 @@
#ifndef E_ATTACHMENT_H
#define E_ATTACHMENT_H
-#include <gio/gio.h>
+#include <gtk/gtk.h>
#include <camel/camel-cipher-context.h>
#include <camel/camel-mime-part.h>
#include <camel/camel-mime-message.h>
#include <camel/camel-multipart.h>
-#include <widgets/misc/e-file-activity.h>
/* Standard GObject macros */
#define E_TYPE_ATTACHMENT \
@@ -71,6 +70,7 @@ EAttachment * e_attachment_new_for_message (CamelMimeMessage *message);
void e_attachment_add_to_multipart (EAttachment *attachment,
CamelMultipart *multipart,
const gchar *default_charset);
+void e_attachment_cancel (EAttachment *attachment);
const gchar * e_attachment_get_disposition (EAttachment *attachment);
void e_attachment_set_disposition (EAttachment *attachment,
const gchar *disposition);
@@ -78,9 +78,16 @@ GFile * e_attachment_get_file (EAttachment *attachment);
void e_attachment_set_file (EAttachment *attachment,
GFile *file);
GFileInfo * e_attachment_get_file_info (EAttachment *attachment);
+gboolean e_attachment_get_loading (EAttachment *attachment);
CamelMimePart * e_attachment_get_mime_part (EAttachment *attachment);
void e_attachment_set_mime_part (EAttachment *attachment,
CamelMimePart *mime_part);
+gint e_attachment_get_percent (EAttachment *attachment);
+GtkTreeRowReference *
+ e_attachment_get_reference (EAttachment *attachment);
+void e_attachment_set_reference (EAttachment *attachment,
+ GtkTreeRowReference *reference);
+gboolean e_attachment_get_saving (EAttachment *attachment);
camel_cipher_validity_encrypt_t
e_attachment_get_encrypted (EAttachment *attachment);
void e_attachment_set_encrypted (EAttachment *attachment,
@@ -89,42 +96,45 @@ camel_cipher_validity_sign_t
e_attachment_get_signed (EAttachment *attachment);
void e_attachment_set_signed (EAttachment *attachment,
camel_cipher_validity_sign_t signed_);
-const gchar * e_attachment_get_content_type (EAttachment *attachment);
-const gchar * e_attachment_get_display_name (EAttachment *attachment);
const gchar * e_attachment_get_description (EAttachment *attachment);
-GIcon * e_attachment_get_icon (EAttachment *attachment);
-gboolean e_attachment_get_loading (EAttachment *attachment);
const gchar * e_attachment_get_thumbnail_path (EAttachment *attachment);
-gboolean e_attachment_get_saving (EAttachment *attachment);
-guint64 e_attachment_get_size (EAttachment *attachment);
gboolean e_attachment_is_image (EAttachment *attachment);
gboolean e_attachment_is_rfc822 (EAttachment *attachment);
GList * e_attachment_list_apps (EAttachment *attachment);
GList * e_attachment_list_emblems (EAttachment *attachment);
/* Asynchronous Operations */
-void e_attachment_launch_async (EAttachment *attachment,
- EFileActivity *file_activity,
- GAppInfo *app_info);
+void e_attachment_load_async (EAttachment *attachment,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_attachment_load_finish (EAttachment *attachment,
+ GAsyncResult *result,
+ GError **error);
+void e_attachment_open_async (EAttachment *attachment,
+ GAppInfo *app_info,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_attachment_open_finish (EAttachment *attachment,
+ GAsyncResult *result,
+ GError **error);
void e_attachment_save_async (EAttachment *attachment,
- EFileActivity *file_activity,
- GFile *destination);
-
-#if 0
-void e_attachment_build_mime_part_async
- (EAttachment *attachment,
- GCancellable *cancellable,
+ GFile *destination,
GAsyncReadyCallback callback,
gpointer user_data);
-CamelMimePart * e_attachment_build_mime_part_finish
- (EAttachment *attachment,
+gboolean e_attachment_save_finish (EAttachment *attachment,
GAsyncResult *result,
GError **error);
-#endif
-/* For use by EAttachmentStore only. */
-void _e_attachment_set_reference (EAttachment *attachment,
- GtkTreeRowReference *reference);
+/* Handy GAsyncReadyCallback Functions */
+void e_attachment_load_handle_error (EAttachment *attachment,
+ GAsyncResult *result,
+ GtkWindow *parent);
+void e_attachment_open_handle_error (EAttachment *attachment,
+ GAsyncResult *result,
+ GtkWindow *parent);
+void e_attachment_save_handle_error (EAttachment *attachment,
+ GAsyncResult *result,
+ GtkWindow *parent);
G_END_DECLS