[evolution/kill-bonobo] Resolve some differences between this branch and master.



commit 6f2f7292a7934a93e18d36594a8b9ef8dc4454e7
Author: Matthew Barnes <mbarnes redhat com>
Date:   Tue Apr 28 10:57:05 2009 -0400

    Resolve some differences between this branch and master.
---
 calendar/gui/dialogs/comp-editor.c        |  129 ++++---
 composer/e-msg-composer.c                 |  123 +------
 configure.in                              |    2 +-
 e-util/e-util.c                           |   30 --
 plugins/external-editor/external-editor.c |    2 -
 widgets/misc/e-attachment.c               |  620 +++++++++++++++++++++++++++++
 6 files changed, 706 insertions(+), 200 deletions(-)

diff --git a/calendar/gui/dialogs/comp-editor.c b/calendar/gui/dialogs/comp-editor.c
index ceab3d3..466973d 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 5e8943d..d1a6abb 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 4df6e2c..9827d2f 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 04be119..071f5e5 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 c541124..a2c86af 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 b2c3e15..537a347 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);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]