aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2009-04-28 22:57:05 +0800
committerMatthew Barnes <mbarnes@redhat.com>2009-04-28 22:57:05 +0800
commit6f2f7292a7934a93e18d36594a8b9ef8dc4454e7 (patch)
treed2c915af4de525703ba9c7fef599a6ffb5a02fdc
parent9ec72b2da8880ca93ecff50cb83c5b2ce5ec206c (diff)
downloadgsoc2013-evolution-6f2f7292a7934a93e18d36594a8b9ef8dc4454e7.tar.gz
gsoc2013-evolution-6f2f7292a7934a93e18d36594a8b9ef8dc4454e7.tar.zst
gsoc2013-evolution-6f2f7292a7934a93e18d36594a8b9ef8dc4454e7.zip
Resolve some differences between this branch and master.
-rw-r--r--calendar/gui/dialogs/comp-editor.c129
-rw-r--r--composer/e-msg-composer.c123
-rw-r--r--configure.in2
-rw-r--r--e-util/e-util.c30
-rw-r--r--plugins/external-editor/external-editor.c2
-rw-r--r--widgets/misc/e-attachment.c620
6 files changed, 706 insertions, 200 deletions
diff --git a/calendar/gui/dialogs/comp-editor.c b/calendar/gui/dialogs/comp-editor.c
index ceab3d3068..466973d88e 100644
--- a/calendar/gui/dialogs/comp-editor.c
+++ b/calendar/gui/dialogs/comp-editor.c
@@ -194,37 +194,6 @@ comp_editor_weak_notify_cb (gpointer unused,
active_editors = g_list_remove (active_editors, where_the_object_was);
}
-static void
-drag_data_received (CompEditor *editor,
- GdkDragContext *context,
- gint x,
- gint y,
- GtkSelectionData *selection,
- guint info,
- guint time)
-{
- EAttachmentView *view;
-
- view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
-
- e_attachment_view_drag_data_received (
- view, context, x, y, selection, info, time);
-}
-
-static gboolean
-drag_motion (CompEditor *editor,
- GdkDragContext *context,
- gint x,
- gint y,
- guint time)
-{
- EAttachmentView *view;
-
- view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
-
- return e_attachment_view_drag_motion (view, context, x, y, time);
-}
-
static GSList *
get_attachment_list (CompEditor *editor)
{
@@ -232,7 +201,7 @@ get_attachment_list (CompEditor *editor)
EAttachmentView *view;
GtkTreeModel *model;
GtkTreeIter iter;
- GSList *parts = NULL, *list = NULL, *p = NULL;
+ GSList *parts = NULL, *list = NULL;
const char *comp_uid = NULL;
const char *local_store = e_cal_get_local_attachment_store (editor->priv->client);
gboolean valid;
@@ -266,9 +235,6 @@ get_attachment_list (CompEditor *editor)
if (mime_part == NULL)
continue;
- if (mime_part == NULL)
- continue;
-
wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
/* Extract the content from the stream and write it down
@@ -663,6 +629,14 @@ action_attach_cb (GtkAction *action,
}
static void
+action_classification_cb (GtkRadioAction *action,
+ GtkRadioAction *current,
+ CompEditor *editor)
+{
+ comp_editor_set_changed (editor, TRUE);
+}
+
+static void
action_close_cb (GtkAction *action,
CompEditor *editor)
{
@@ -822,9 +796,11 @@ action_save_cb (GtkAction *action,
}
commit_all_fields (editor);
- if (e_cal_component_is_instance (priv->comp))
+ if (e_cal_component_has_recurrences (priv->comp)) {
if (!recur_component_dialog (priv->client, priv->comp, &priv->mod, GTK_WINDOW (editor), delegated))
return;
+ } else if (e_cal_component_is_instance (priv->comp))
+ priv->mod = CALOBJ_MOD_THIS;
comp = comp_editor_get_current_comp (editor, &correct);
e_cal_component_get_summary (comp, &text);
@@ -1441,6 +1417,46 @@ comp_editor_key_press_event (GtkWidget *widget,
key_press_event (widget, event);
}
+static gboolean
+comp_editor_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ CompEditorPrivate *priv;
+ EAttachmentView *view;
+
+ priv = COMP_EDITOR_GET_PRIVATE (widget);
+ view = E_ATTACHMENT_VIEW (priv->attachment_view);
+
+ return e_attachment_view_drag_motion (view, context, x, y, time);
+}
+
+static void
+comp_editor_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ CompEditorPrivate *priv;
+ EAttachmentView *view;
+
+ priv = COMP_EDITOR_GET_PRIVATE (widget);
+ view = E_ATTACHMENT_VIEW (priv->attachment_view);
+
+ /* Forward the data to the attachment view. Note that calling
+ * e_attachment_view_drag_data_received() will not work because
+ * that function only handles the case where all the other drag
+ * handlers have failed. */
+ e_attachment_paned_drag_data_received (
+ E_ATTACHMENT_PANED (view),
+ context, x, y, selection, info, time);
+}
+
static void
comp_editor_class_init (CompEditorClass *class)
{
@@ -1459,6 +1475,8 @@ comp_editor_class_init (CompEditorClass *class)
widget_class->map = comp_editor_map;
widget_class->delete_event = comp_editor_delete_event;
widget_class->key_press_event = comp_editor_key_press_event;
+ widget_class->drag_motion = comp_editor_drag_motion;
+ widget_class->drag_data_received = comp_editor_drag_data_received;
class->help_section = "usage-calendar";
class->edit_comp = real_edit_comp;
@@ -1544,6 +1562,7 @@ comp_editor_init (CompEditor *editor)
GtkWidget *container;
GtkWidget *widget;
EShell *shell;
+ gint n_targets;
GError *error = NULL;
editor->priv = priv = COMP_EDITOR_GET_PRIVATE (editor);
@@ -1594,7 +1613,7 @@ comp_editor_init (CompEditor *editor)
action_group, classification_radio_entries,
G_N_ELEMENTS (classification_radio_entries),
E_CAL_COMPONENT_CLASS_PUBLIC,
- NULL, NULL); /* no callback */
+ G_CALLBACK (action_classification_cb), editor);
gtk_ui_manager_insert_action_group (
priv->ui_manager, action_group, 0);
g_object_unref (action_group);
@@ -1659,12 +1678,22 @@ comp_editor_init (CompEditor *editor)
comp_editor_setup_recent_menu (editor);
- /* DND support */
- gtk_drag_dest_set (GTK_WIDGET (editor), GTK_DEST_DEFAULT_ALL, drop_types, num_drop_types, GDK_ACTION_COPY|GDK_ACTION_ASK|GDK_ACTION_MOVE);
- g_signal_connect(editor, "drag_data_received", G_CALLBACK (drag_data_received), NULL);
- g_signal_connect(editor, "drag-motion", G_CALLBACK(drag_motion), NULL);
+ /* Drag-and-Drop Support */
+
+ view = E_ATTACHMENT_VIEW (priv->attachment_view);
+ target_list = e_attachment_view_get_target_list (view);
+ drag_actions = e_attachment_view_get_drag_actions (view);
+
+ targets = gtk_target_table_new_from_list (target_list, &n_targets);
+
+ gtk_drag_dest_set (
+ GTK_WIDGET (editor), GTK_DEST_DEFAULT_ALL,
+ targets, n_targets, drag_actions);
- gtk_window_set_type_hint (GTK_WINDOW (editor), GDK_WINDOW_TYPE_HINT_NORMAL);
+ gtk_target_table_free (targets, n_targets);
+
+ gtk_window_set_type_hint (
+ GTK_WINDOW (editor), GDK_WINDOW_TYPE_HINT_NORMAL);
/* FIXME Shell should be passed in. */
shell = e_shell_get_default ();
@@ -2318,7 +2347,8 @@ fill_widgets (CompEditor *editor)
EAttachmentStore *store;
EAttachmentView *view;
CompEditorPrivate *priv;
- GList *l;
+ GtkAction *action;
+ GList *iter;
view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
store = e_attachment_view_get_store (view);
@@ -2336,15 +2366,22 @@ fill_widgets (CompEditor *editor)
store, G_CALLBACK (attachment_store_changed_cb),
editor);
set_attachment_list (editor, attachment_list);
- g_signal_handlers_unblock_by_func(
+ g_signal_handlers_unblock_by_func (
store, G_CALLBACK (attachment_store_changed_cb),
editor);
g_slist_foreach (attachment_list, (GFunc)g_free, NULL);
g_slist_free (attachment_list);
}
- for (l = priv->pages; l != NULL; l = l->next)
- comp_editor_page_fill_widgets (l->data, priv->comp);
+ action = comp_editor_get_action (editor, "classify-public");
+ g_signal_handlers_block_by_func (
+ action, G_CALLBACK (action_classification_cb), editor);
+
+ for (iter = priv->pages; iter != NULL; iter = iter->next)
+ comp_editor_page_fill_widgets (iter->data, priv->comp);
+
+ g_signal_handlers_unblock_by_func (
+ action, G_CALLBACK (action_classification_cb), editor);
}
static void
diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c
index 5e8943d478..d1a6abb222 100644
--- a/composer/e-msg-composer.c
+++ b/composer/e-msg-composer.c
@@ -1109,13 +1109,12 @@ get_signature_html (EMsgComposer *composer)
"<!--+GtkHTML:<DATA class=\"ClueFlow\" key=\"signature_name\" value=\"uid:%s\">-->"
"<TABLE WIDTH=\"100%%\" CELLSPACING=\"0\" CELLPADDING=\"0\"><TR><TD><BR>"
"%s%s%s%s"
- "%s</TD></TR></TABLE>",
+ "</TD></TR></TABLE>",
encoded_uid ? encoded_uid : "",
format_html ? "" : "<PRE>\n",
format_html || (!strncmp ("-- \n", text, 4) || strstr (text, "\n-- \n")) ? "" : "-- \n",
text,
- format_html ? "" : "</PRE>\n",
- is_top_signature () ? "<BR>" : "");
+ format_html ? "" : "</PRE>\n");
g_free (text);
g_free (encoded_uid);
text = html;
@@ -1440,117 +1439,6 @@ struct _drop_data {
guint time;
};
-#if 0 /* KILL-BONOBO */
-static void
-drop_action (EMsgComposer *composer,
- GdkDragContext *context,
- guint32 action,
- GtkSelectionData *selection,
- guint info,
- guint time,
- gboolean html_dnd)
-{
- CamelMimePart *mime_part;
- CamelMimeMessage *msg;
- int i, success = FALSE, delete = FALSE;
- EMsgComposerPrivate *p = composer->priv;
-
- switch (info) {
- case DND_TYPE_X_UID_LIST: {
- GPtrArray *uids;
- char *inptr, *inend;
- CamelFolder *folder;
- CamelException ex = CAMEL_EXCEPTION_INITIALISER;
-
- /* NB: This all runs synchronously, could be very slow/hang/block the ui */
-
- uids = g_ptr_array_new ();
-
- inptr = (char*)selection->data;
- inend = (char*)(selection->data + selection->length);
- while (inptr < inend) {
- char *start = inptr;
-
- while (inptr < inend && *inptr)
- inptr++;
-
- if (start > (char *)selection->data)
- g_ptr_array_add (uids, g_strndup (start, inptr-start));
-
- inptr++;
- }
-
- if (uids->len > 0) {
- folder = mail_tool_uri_to_folder ((const gchar *)selection->data, 0, &ex);
- if (folder) {
- if (uids->len == 1) {
- msg = camel_folder_get_message (folder, uids->pdata[0], &ex);
- if (msg == NULL)
- goto fail;
- msg_composer_attach_message (composer, msg);
- } else {
- CamelMultipart *mp = camel_multipart_new ();
- char *desc;
-
- camel_data_wrapper_set_mime_type ((CamelDataWrapper *)mp, "multipart/digest");
- camel_multipart_set_boundary (mp, NULL);
- for (i=0;i<uids->len;i++) {
- msg = camel_folder_get_message (folder, uids->pdata[i], &ex);
- if (msg) {
- mime_part = camel_mime_part_new ();
- camel_mime_part_set_disposition (mime_part, "inline");
- camel_medium_set_content_object ((CamelMedium *)mime_part, (CamelDataWrapper *)msg);
- camel_mime_part_set_content_type (mime_part, "message/rfc822");
- camel_multipart_add_part (mp, mime_part);
- camel_object_unref (mime_part);
- camel_object_unref (msg);
- } else {
- camel_object_unref (mp);
- goto fail;
- }
- }
- mime_part = camel_mime_part_new ();
- camel_medium_set_content_object ((CamelMedium *)mime_part, (CamelDataWrapper *)mp);
- /* translators, this count will always be >1 */
- desc = g_strdup_printf (ngettext ("Attached message", "%d attached messages", uids->len), uids->len);
- camel_mime_part_set_description (mime_part, desc);
- g_free (desc);
- e_attachment_bar_attach_mime_part (E_ATTACHMENT_BAR(p->attachment_bar), mime_part);
- camel_object_unref (mime_part);
- camel_object_unref (mp);
- }
- success = TRUE;
- delete = action == GDK_ACTION_MOVE;
- fail:
- if (camel_exception_is_set (&ex)) {
- char *name;
-
- camel_object_get (folder, NULL, CAMEL_FOLDER_NAME, &name, NULL);
- e_error_run ((GtkWindow *)composer, "mail-composer:attach-nomessages",
- name?name:(char *)selection->data, camel_exception_get_description (&ex), NULL);
- camel_object_free (folder, CAMEL_FOLDER_NAME, name);
- }
- camel_object_unref (folder);
- } else {
- e_error_run ((GtkWindow *)composer, "mail-composer:attach-nomessages",
- (const gchar*)selection->data, camel_exception_get_description (&ex), NULL);
- }
-
- camel_exception_clear (&ex);
- }
-
- g_ptr_array_free (uids, TRUE);
-
- break; }
- default:
- d (printf ("dropping an unknown\n"));
- break;
- }
-
- gtk_drag_finish (context, success, delete, time);
-}
-#endif
-
static void
msg_composer_notify_header_cb (EMsgComposer *composer)
{
@@ -3973,7 +3861,6 @@ e_msg_composer_show_sig_file (EMsgComposer *composer)
GtkhtmlEditor *editor;
GtkHTML *html;
gchar *html_text;
- gboolean top_signature;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
@@ -4000,8 +3887,6 @@ e_msg_composer_show_sig_file (EMsgComposer *composer)
}
gtkhtml_editor_run_command (editor, "unblock-selection");
- top_signature = is_top_signature ();
-
html_text = get_signature_html (composer);
if (html_text) {
gtkhtml_editor_run_command (editor, "insert-paragraph");
@@ -4015,10 +3900,6 @@ e_msg_composer_show_sig_file (EMsgComposer *composer)
gtkhtml_editor_run_command (editor, "style-normal");
gtkhtml_editor_insert_html (editor, html_text);
g_free (html_text);
- } else if (top_signature) {
- /* insert paragraph after the signature ClueFlow things */
- gtkhtml_editor_run_command (editor, "cursor-forward");
- gtkhtml_editor_run_command (editor, "insert-paragraph");
}
gtkhtml_editor_undo_end (editor);
diff --git a/configure.in b/configure.in
index 4df6e2cd4a..9827d2f606 100644
--- a/configure.in
+++ b/configure.in
@@ -1790,7 +1790,7 @@ plugins_experimental="$plugins_experimental_always $IPOD_SYNC $TNEF_ATTACHMENTS
all_plugins_experimental="$plugins_experimental_always ipod-sync tnef-attachments"
dnl Temporary KILL-BONOBO hack
-enable_plugins="attachment-reminder addressbook-file audio-inline bbdb bogo-junk-plugin caldav calendar-file calendar-http copy-tool default-source external-editor google-account-setup hula-account-setup imap-features mail-notification mail-to-meeting mark-all-read plugin-manager profiler sa-junk-plugin save-calendar subject-thread $TNEF_ATTACHMENTS vcard-inline webdav-account-setup"
+enable_plugins="attachment-reminder addressbook-file audio-inline bbdb bogo-junk-plugin caldav calendar-file calendar-http copy-tool default-source external-editor google-account-setup hula-account-setup imap-features mail-notification mark-all-read plugin-manager profiler sa-junk-plugin save-calendar subject-thread $TNEF_ATTACHMENTS vcard-inline webdav-account-setup"
dnl PLUGINS NOT BUILDING YET
dnl ------------------------
diff --git a/e-util/e-util.c b/e-util/e-util.c
index 04be119657..071f5e58b8 100644
--- a/e-util/e-util.c
+++ b/e-util/e-util.c
@@ -1531,33 +1531,3 @@ e_camel_object_get_type (void)
return type;
}
-
-static gpointer
-e_camel_object_copy (gpointer camel_object)
-{
- if (CAMEL_IS_OBJECT (camel_object))
- camel_object_ref (camel_object);
-
- return camel_object;
-}
-
-static void
-e_camel_object_free (gpointer camel_object)
-{
- if (CAMEL_IS_OBJECT (camel_object))
- camel_object_unref (camel_object);
-}
-
-GType
-e_camel_object_get_type (void)
-{
- static GType type = 0;
-
- if (G_UNLIKELY (type == 0))
- type = g_boxed_type_register_static (
- "ECamelObject",
- (GBoxedCopyFunc) e_camel_object_copy,
- (GBoxedFreeFunc) e_camel_object_free);
-
- return type;
-}
diff --git a/plugins/external-editor/external-editor.c b/plugins/external-editor/external-editor.c
index c541124e53..a2c86af392 100644
--- a/plugins/external-editor/external-editor.c
+++ b/plugins/external-editor/external-editor.c
@@ -183,8 +183,6 @@ read_file (char *filename)
*/
composer = e_msg_composer_new_with_message (message);
- g_signal_connect (GTK_OBJECT (composer), "send", G_CALLBACK (em_utils_composer_send_cb), NULL);
- g_signal_connect (GTK_OBJECT (composer), "save-draft", G_CALLBACK (em_utils_composer_save_draft_cb), NULL);
gtk_widget_show (GTK_WIDGET (composer));
diff --git a/widgets/misc/e-attachment.c b/widgets/misc/e-attachment.c
index b2c3e1579e..537a3470d7 100644
--- a/widgets/misc/e-attachment.c
+++ b/widgets/misc/e-attachment.c
@@ -1915,6 +1915,626 @@ attachment_open_context_free (OpenContext *open_context)
g_slice_free (OpenContext, open_context);
}
+static gboolean
+attachment_open_check_for_error (OpenContext *open_context,
+ GError *error)
+{
+ GSimpleAsyncResult *simple;
+
+ if (error == NULL)
+ return FALSE;
+
+ /* Steal the result. */
+ 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 TRUE;
+}
+
+static void
+attachment_open_file (GFile *file,
+ OpenContext *open_context)
+{
+ GdkAppLaunchContext *context;
+ GSimpleAsyncResult *simple;
+ GList *file_list;
+ gboolean success;
+ GError *error = NULL;
+
+ /* Steal the result. */
+ simple = open_context->simple;
+ open_context->simple = NULL;
+
+ /* Find a default app based on content type. */
+ if (open_context->app_info == NULL) {
+ EAttachment *attachment;
+ GFileInfo *file_info;
+ const gchar *content_type;
+
+ attachment = open_context->attachment;
+ file_info = e_attachment_get_file_info (attachment);
+ if (file_info == NULL)
+ goto exit;
+
+ content_type = g_file_info_get_content_type (file_info);
+ if (content_type == NULL)
+ goto exit;
+
+ open_context->app_info = g_app_info_get_default_for_type (
+ content_type, FALSE);
+ }
+
+ if (open_context->app_info == NULL)
+ goto exit;
+
+ context = gdk_app_launch_context_new ();
+ file_list = g_list_prepend (NULL, 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 (context);
+
+exit:
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+
+ g_simple_async_result_complete (simple);
+ attachment_open_context_free (open_context);
+}
+
+static void
+attachment_open_save_finished_cb (EAttachment *attachment,
+ GAsyncResult *result,
+ OpenContext *open_context)
+{
+ GFile *file;
+ GError *error = NULL;
+
+ file = e_attachment_save_finish (attachment, result, &error);
+
+ if (attachment_open_check_for_error (open_context, error))
+ return;
+
+ attachment_open_file (file, open_context);
+ g_object_unref (file);
+}
+
+static void
+attachment_open_save_temporary (OpenContext *open_context)
+{
+ GFile *file;
+ gchar *template;
+ gchar *path;
+ GError *error = NULL;
+
+ errno = 0;
+
+ /* XXX This could trigger a blocking temp directory cleanup. */
+ template = g_strdup_printf (PACKAGE "-%s-XXXXXX", g_get_user_name ());
+ path = e_mktemp (template);
+ g_free (template);
+
+ /* XXX Let's hope errno got set properly. */
+ if (path == NULL)
+ g_set_error (
+ &error, G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ "%s", g_strerror (errno));
+
+ /* We already know if there's an error, but this does the cleanup. */
+ if (attachment_open_check_for_error (open_context, error))
+ return;
+
+ file = g_file_new_for_path (path);
+
+ g_free (path);
+
+ e_attachment_save_async (
+ open_context->attachment, file, (GAsyncReadyCallback)
+ attachment_open_save_finished_cb, open_context);
+
+ g_object_unref (file);
+}
+
+void
+e_attachment_open_async (EAttachment *attachment,
+ GAppInfo *app_info,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ OpenContext *open_context;
+ CamelMimePart *mime_part;
+ GFile *file;
+
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ g_return_if_fail (callback != NULL);
+
+ 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 (file != NULL) {
+ attachment_open_file (file, open_context);
+
+ } 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 (E_IS_ATTACHMENT (attachment), FALSE);
+ g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), 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() *************************/
+
+typedef struct _SaveContext SaveContext;
+
+struct _SaveContext {
+ EAttachment *attachment;
+ GSimpleAsyncResult *simple;
+
+ GFile *directory;
+ GFile *destination;
+ GInputStream *input_stream;
+ GOutputStream *output_stream;
+ goffset total_num_bytes;
+ gssize bytes_read;
+ gchar buffer[4096];
+ gint count;
+};
+
+/* Forward Declaration */
+static void
+attachment_save_read_cb (GInputStream *input_stream,
+ GAsyncResult *result,
+ SaveContext *save_context);
+
+static SaveContext *
+attachment_save_context_new (EAttachment *attachment,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SaveContext *save_context;
+ GSimpleAsyncResult *simple;
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (attachment), callback,
+ user_data, e_attachment_save_async);
+
+ save_context = g_slice_new0 (SaveContext);
+ save_context->attachment = g_object_ref (attachment);
+ save_context->simple = simple;
+
+ attachment_set_saving (save_context->attachment, TRUE);
+
+ return save_context;
+}
+
+static void
+attachment_save_context_free (SaveContext *save_context)
+{
+ /* Do not free the GSimpleAsyncResult. */
+ g_object_unref (save_context->attachment);
+
+ if (save_context->directory != NULL)
+ g_object_unref (save_context->directory);
+
+ if (save_context->destination != NULL)
+ g_object_unref (save_context->destination);
+
+ 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);
+
+ g_slice_free (SaveContext, save_context);
+}
+
+static gboolean
+attachment_save_check_for_error (SaveContext *save_context,
+ GError *error)
+{
+ GSimpleAsyncResult *simple;
+
+ if (error == NULL)
+ return FALSE;
+
+ /* Steal the result. */
+ 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 TRUE;
+}
+
+static GFile *
+attachment_save_new_candidate (SaveContext *save_context)
+{
+ GFile *candidate;
+ GFileInfo *file_info;
+ EAttachment *attachment;
+ const gchar *display_name;
+ gchar *basename;
+
+ attachment = save_context->attachment;
+ file_info = e_attachment_get_file_info (attachment);
+
+ if (file_info != NULL)
+ display_name = g_file_info_get_display_name (file_info);
+ if (display_name == NULL)
+ /* Translators: Default attachment filename. */
+ display_name = _("attachment.dat");
+
+ if (save_context->count == 0)
+ basename = g_strdup (display_name);
+ else {
+ GString *string;
+ const gchar *ext;
+ gsize length;
+
+ string = g_string_sized_new (strlen (display_name));
+ ext = g_utf8_strchr (display_name, -1, '.');
+
+ if (ext != NULL)
+ length = ext - display_name;
+ else
+ length = strlen (display_name);
+
+ g_string_append_len (string, display_name, length);
+ g_string_append_printf (string, " (%d)", save_context->count);
+ g_string_append (string, (ext != NULL) ? ext : "");
+
+ basename = g_string_free (string, FALSE);
+ }
+
+ save_context->count++;
+
+ candidate = g_file_get_child (save_context->directory, basename);
+
+ g_free (basename);
+
+ return candidate;
+}
+
+static void
+attachment_save_write_cb (GOutputStream *output_stream,
+ GAsyncResult *result,
+ SaveContext *save_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 (attachment_save_check_for_error (save_context, error))
+ return;
+
+ 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,
+ SaveContext *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);
+
+ if (attachment_save_check_for_error (save_context, error))
+ return;
+
+ if (bytes_read == 0) {
+ GSimpleAsyncResult *simple;
+ GFile *destination;
+
+ /* Steal the result. */
+ simple = save_context->simple;
+ save_context->simple = NULL;
+
+ /* Steal the destination. */
+ destination = save_context->destination;
+ save_context->destination = NULL;
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, destination, (GDestroyNotify) g_object_unref);
+ g_simple_async_result_complete (simple);
+
+ 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_got_output_stream (SaveContext *save_context)
+{
+ GCancellable *cancellable;
+ GInputStream *input_stream;
+ CamelDataWrapper *wrapper;
+ CamelMimePart *mime_part;
+ CamelStream *stream;
+ EAttachment *attachment;
+ GByteArray *buffer;
+
+ 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 ();
+ camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (stream), 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);
+}
+
+static void
+attachment_save_create_cb (GFile *destination,
+ GAsyncResult *result,
+ SaveContext *save_context)
+{
+ EAttachment *attachment;
+ GCancellable *cancellable;
+ GFileOutputStream *output_stream;
+ GError *error = NULL;
+
+ /* Output stream might be NULL, so don't use cast macro. */
+ output_stream = g_file_create_finish (destination, result, &error);
+ save_context->output_stream = (GOutputStream *) output_stream;
+
+ attachment = save_context->attachment;
+ cancellable = attachment->priv->cancellable;
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
+ destination = attachment_save_new_candidate (save_context);
+
+ g_file_create_async (
+ destination, G_FILE_CREATE_NONE,
+ G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) attachment_save_create_cb,
+ save_context);
+
+ g_object_unref (destination);
+ g_error_free (error);
+ return;
+ }
+
+ if (attachment_save_check_for_error (save_context, error))
+ return;
+
+ save_context->destination = g_object_ref (destination);
+ attachment_save_got_output_stream (save_context);
+}
+
+static void
+attachment_save_replace_cb (GFile *destination,
+ GAsyncResult *result,
+ SaveContext *save_context)
+{
+ GFileOutputStream *output_stream;
+ GError *error = NULL;
+
+ /* Output stream might be NULL, so don't use cast macro. */
+ output_stream = g_file_replace_finish (destination, result, &error);
+ save_context->output_stream = (GOutputStream *) output_stream;
+
+ if (attachment_save_check_for_error (save_context, error))
+ return;
+
+ save_context->destination = g_object_ref (destination);
+ attachment_save_got_output_stream (save_context);
+}
+
+static void
+attachment_save_query_info_cb (GFile *destination,
+ GAsyncResult *result,
+ SaveContext *save_context)
+{
+ EAttachment *attachment;
+ GCancellable *cancellable;
+ GFileInfo *file_info;
+ GFileType file_type;
+ GError *error = NULL;
+
+ attachment = save_context->attachment;
+ cancellable = attachment->priv->cancellable;
+
+ file_info = g_file_query_info_finish (destination, result, &error);
+
+ /* G_IO_ERROR_NOT_FOUND just means we're creating a new file. */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
+ g_error_free (error);
+ goto replace;
+ }
+
+ if (attachment_save_check_for_error (save_context, error))
+ return;
+
+ file_type = g_file_info_get_file_type (file_info);
+ g_object_unref (file_info);
+
+ if (file_type == G_FILE_TYPE_DIRECTORY) {
+ save_context->directory = g_object_ref (destination);
+ destination = attachment_save_new_candidate (save_context);
+
+ g_file_create_async (
+ destination, G_FILE_CREATE_NONE,
+ G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) attachment_save_create_cb,
+ save_context);
+
+ g_object_unref (destination);
+
+ return;
+ }
+
+replace:
+ g_file_replace_async (
+ destination, NULL, FALSE,
+ G_FILE_CREATE_REPLACE_DESTINATION,
G_PRIORITY_DEFAULT, cancellable,
(GAsyncReadyCallback) attachment_save_replace_cb,
save_context);