evolution r37487 - in branches/kill-bonobo: mail widgets/misc



Author: mbarnes
Date: Tue Mar 31 21:48:38 2009
New Revision: 37487
URL: http://svn.gnome.org/viewvc/evolution?rev=37487&view=rev

Log:
Extend EAttachmentView by subclassing EAttachmentHandler.

So far it only supports extending the attachment pop-up menu, but I
also plan to support extending drag and drop types.  Might even end
up replacing EMFormatHook.

Two subclasses created so far:

   EAttachmentHandlerImage	handles images (built-in)
   EAttachmentHandlerMail	handles emails (defined in mail module)



Added:
   branches/kill-bonobo/mail/e-attachment-handler-mail.c
   branches/kill-bonobo/mail/e-attachment-handler-mail.h
   branches/kill-bonobo/widgets/misc/e-attachment-handler-image.c
   branches/kill-bonobo/widgets/misc/e-attachment-handler-image.h
   branches/kill-bonobo/widgets/misc/e-attachment-handler.c
   branches/kill-bonobo/widgets/misc/e-attachment-handler.h
Removed:
   branches/kill-bonobo/widgets/misc/e-attachment-bar.c
   branches/kill-bonobo/widgets/misc/e-attachment-bar.h
Modified:
   branches/kill-bonobo/mail/Makefile.am
   branches/kill-bonobo/mail/e-mail-attachment-bar.c
   branches/kill-bonobo/mail/e-mail-shell-module.c
   branches/kill-bonobo/widgets/misc/Makefile.am
   branches/kill-bonobo/widgets/misc/e-attachment-icon-view.c
   branches/kill-bonobo/widgets/misc/e-attachment-store.c
   branches/kill-bonobo/widgets/misc/e-attachment-tree-view.c
   branches/kill-bonobo/widgets/misc/e-attachment-view.c
   branches/kill-bonobo/widgets/misc/e-attachment-view.h
   branches/kill-bonobo/widgets/misc/e-attachment.c
   branches/kill-bonobo/widgets/misc/e-attachment.h

Modified: branches/kill-bonobo/mail/Makefile.am
==============================================================================
--- branches/kill-bonobo/mail/Makefile.am	(original)
+++ branches/kill-bonobo/mail/Makefile.am	Tue Mar 31 21:48:38 2009
@@ -35,6 +35,8 @@
 	libevolution-module-mail.la
 
 libevolution_module_mail_la_SOURCES =			\
+	e-attachment-handler-mail.c			\
+	e-attachment-handler-mail.h			\
 	e-mail-attachment-bar.c				\
 	e-mail-attachment-bar.h				\
 	e-mail-browser.c				\

Added: branches/kill-bonobo/mail/e-attachment-handler-mail.c
==============================================================================
--- (empty file)
+++ branches/kill-bonobo/mail/e-attachment-handler-mail.c	Tue Mar 31 21:48:38 2009
@@ -0,0 +1,269 @@
+/*
+ * e-attachment-handler-mail.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>  
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-attachment-handler-mail.h"
+
+#include <config.h>
+#include <glib/gi18n.h>
+
+#include "mail/em-composer-utils.h"
+
+#define E_ATTACHMENT_HANDLER_MAIL_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_ATTACHMENT_HANDLER_MAIL, EAttachmentHandlerMailPrivate))
+
+struct _EAttachmentHandlerMailPrivate {
+	gint placeholder;
+};
+
+static gpointer parent_class;
+
+static const gchar *ui =
+"<ui>"
+"  <popup name='context'>"
+"    <placeholder name='custom-actions'>"
+"      <menuitem action='mail-reply-sender'/>"
+"      <menuitem action='mail-reply-all'/>"
+"      <menuitem action='mail-forward'/>"
+"    </placeholder>"
+"  </popup>"
+"</ui>";
+
+static void
+action_mail_forward_cb (GtkAction *action,
+                        EAttachmentHandler *handler)
+{
+	EAttachmentView *view;
+	EAttachment *attachment;
+	CamelMimePart *mime_part;
+	CamelDataWrapper *wrapper;
+	GList *selected;
+
+	view = e_attachment_handler_get_view (handler);
+	selected = e_attachment_view_get_selected_attachments (view);
+	g_return_if_fail (g_list_length (selected) == 1);
+
+	attachment = E_ATTACHMENT (selected->data);
+	mime_part = e_attachment_get_mime_part (attachment);
+	wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+
+	em_utils_forward_message (CAMEL_MIME_MESSAGE (wrapper), NULL);
+
+	g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+	g_list_free (selected);
+}
+
+static void
+action_mail_reply_all_cb (GtkAction *action,
+                          EAttachmentHandler *handler)
+{
+	EAttachmentView *view;
+	EAttachment *attachment;
+	CamelMimePart *mime_part;
+	CamelDataWrapper *wrapper;
+	GList *selected;
+
+	view = e_attachment_handler_get_view (handler);
+	selected = e_attachment_view_get_selected_attachments (view);
+	g_return_if_fail (g_list_length (selected) == 1);
+
+	attachment = E_ATTACHMENT (selected->data);
+	mime_part = e_attachment_get_mime_part (attachment);
+	wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+
+	em_utils_reply_to_message (
+		NULL, NULL, CAMEL_MIME_MESSAGE (wrapper),
+		REPLY_MODE_ALL, NULL);
+
+	g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+	g_list_free (selected);
+}
+
+static void
+action_mail_reply_sender_cb (GtkAction *action,
+                             EAttachmentHandler *handler)
+{
+	EAttachmentView *view;
+	EAttachment *attachment;
+	CamelMimePart *mime_part;
+	CamelDataWrapper *wrapper;
+	GList *selected;
+
+	view = e_attachment_handler_get_view (handler);
+	selected = e_attachment_view_get_selected_attachments (view);
+	g_return_if_fail (g_list_length (selected) == 1);
+
+	attachment = E_ATTACHMENT (selected->data);
+	mime_part = e_attachment_get_mime_part (attachment);
+	wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+
+	em_utils_reply_to_message (
+		NULL, NULL, CAMEL_MIME_MESSAGE (wrapper),
+		REPLY_MODE_SENDER, NULL);
+
+	g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+	g_list_free (selected);
+}
+
+static GtkActionEntry standard_entries[] = {
+
+	{ "mail-forward",
+	  "mail-forward",
+	  N_("_Forward"),
+	  NULL,
+	  NULL,  /* XXX Add a tooltip! */
+	  G_CALLBACK (action_mail_forward_cb) },
+
+	{ "mail-reply-all",
+	  "mail-reply-all",
+	  N_("Reply to _All"),
+	  NULL,
+	  NULL,  /* XXX Add a tooltip! */
+	  G_CALLBACK (action_mail_reply_all_cb) },
+
+	{ "mail-reply-sender",
+	  "mail-reply-sender",
+	  N_("_Reply to Sender"),
+	  NULL,
+	  NULL,  /* XXX Add a tooltip! */
+	  G_CALLBACK (action_mail_reply_sender_cb) }
+};
+
+static void
+attachment_handler_mail_update_actions_cb (EAttachmentView *view,
+                                           EAttachmentHandler *handler)
+{
+	EAttachment *attachment;
+	CamelMimePart *mime_part;
+	CamelDataWrapper *wrapper;
+	GtkActionGroup *action_group;
+	GList *selected;
+	gboolean visible = FALSE;
+
+	selected = e_attachment_view_get_selected_attachments (view);
+
+	if (g_list_length (selected) != 1)
+		goto exit;
+
+	attachment = E_ATTACHMENT (selected->data);
+	mime_part = e_attachment_get_mime_part (attachment);
+
+	if (!CAMEL_IS_MIME_PART (mime_part))
+		goto exit;
+
+	wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+
+	visible = CAMEL_IS_MIME_MESSAGE (wrapper);
+
+exit:
+	action_group = e_attachment_view_get_action_group (view, "mail");
+	gtk_action_group_set_visible (action_group, visible);
+
+	g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+	g_list_free (selected);
+}
+
+static void
+attachment_handler_mail_constructed (GObject *object)
+{
+	EAttachmentHandlerMailPrivate *priv;
+	EAttachmentHandler *handler;
+	EAttachmentView *view;
+	GtkActionGroup *action_group;
+	GtkUIManager *ui_manager;
+	const gchar *domain = GETTEXT_PACKAGE;
+	GError *error = NULL;
+
+	handler = E_ATTACHMENT_HANDLER (object);
+	priv = E_ATTACHMENT_HANDLER_MAIL_GET_PRIVATE (object);
+
+	/* Chain up to parent's constructed() method. */
+	G_OBJECT_CLASS (parent_class)->constructed (object);
+
+	view = e_attachment_handler_get_view (handler);
+	ui_manager = e_attachment_view_get_ui_manager (view);
+
+	action_group = gtk_action_group_new ("mail");
+	gtk_action_group_set_translation_domain (action_group, domain);
+	gtk_action_group_add_actions (
+		action_group, standard_entries,
+		G_N_ELEMENTS (standard_entries), object);
+	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+	g_object_unref (action_group);
+
+	gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+
+	if (error != NULL) {
+		g_warning ("%s", error->message);
+		g_error_free (error);
+	}
+
+	g_signal_connect (
+		view, "update-actions",
+		G_CALLBACK (attachment_handler_mail_update_actions_cb),
+		object);
+}
+
+static void
+attachment_handler_mail_class_init (EAttachmentHandlerMailClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EAttachmentHandlerMailPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->constructed = attachment_handler_mail_constructed;
+}
+
+static void
+attachment_handler_mail_init (EAttachmentHandlerMail *handler)
+{
+	handler->priv = E_ATTACHMENT_HANDLER_MAIL_GET_PRIVATE (handler);
+}
+
+GType
+e_attachment_handler_mail_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EAttachmentHandlerMailClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) attachment_handler_mail_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EAttachmentHandlerMail),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) attachment_handler_mail_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			E_TYPE_ATTACHMENT_HANDLER,
+			"EAttachmentHandlerMail",
+			&type_info, 0);
+	}
+
+	return type;
+}

Added: branches/kill-bonobo/mail/e-attachment-handler-mail.h
==============================================================================
--- (empty file)
+++ branches/kill-bonobo/mail/e-attachment-handler-mail.h	Tue Mar 31 21:48:38 2009
@@ -0,0 +1,65 @@
+/*
+ * e-attachment-handler-mail.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>  
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_ATTACHMENT_HANDLER_MAIL_H
+#define E_ATTACHMENT_HANDLER_MAIL_H
+
+#include <widgets/misc/e-attachment-handler.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_HANDLER_MAIL \
+	(e_attachment_handler_mail_get_type ())
+#define E_ATTACHMENT_HANDLER_MAIL(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_ATTACHMENT_HANDLER_MAIL, EAttachmentHandlerMail))
+#define E_ATTACHMENT_HANDLER_MAIL_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_ATTACHMENT_HANDLER_MAIL, EAttachmentHandlerMailClass))
+#define E_IS_ATTACHMENT_HANDLER_MAIL(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_ATTACHMENT_HANDLER_MAIL))
+#define E_IS_ATTACHMENT_HANDLER_MAIL_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_ATTACHMENT_HANDLER_MAIL))
+#define E_ATTACHMENT_HANDLER_MAIL_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_ATTACHMENT_HANDLER_MAIL, EAttachmentHandlerMailClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentHandlerMail EAttachmentHandlerMail;
+typedef struct _EAttachmentHandlerMailClass EAttachmentHandlerMailClass;
+typedef struct _EAttachmentHandlerMailPrivate EAttachmentHandlerMailPrivate;
+
+struct _EAttachmentHandlerMail {
+	EAttachmentHandler parent;
+	EAttachmentHandlerMailPrivate *priv;
+};
+
+struct _EAttachmentHandlerMailClass {
+	EAttachmentHandlerClass parent_class;
+};
+
+GType		e_attachment_handler_mail_get_type	(void);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_HANDLER_MAIL_H */

Modified: branches/kill-bonobo/mail/e-mail-attachment-bar.c
==============================================================================
--- branches/kill-bonobo/mail/e-mail-attachment-bar.c	(original)
+++ branches/kill-bonobo/mail/e-mail-attachment-bar.c	Tue Mar 31 21:48:38 2009
@@ -299,8 +299,12 @@
 mail_attachment_bar_size_request (GtkWidget *widget,
                                   GtkRequisition *requisition)
 {
-	/* XXX This works around GtkHTMLEmbedded brokenness.
-	 *     Once we finally move to WebKit, remove this. */
+	/* XXX This works around GtkHTMLEmbedded not taking visibility
+	 *     into account when calculating its size (at least I think
+	 *     that's where it's broken).  Without the workaround, we
+	 *     get a sizable gap between the headers and body when this
+	 *     widget is invisible.  Once we finally move to WebKit,
+	 *     remove this. */
 	if (!GTK_WIDGET_VISIBLE (widget)) {
 		requisition->width = 0;
 		requisition->height = 0;

Modified: branches/kill-bonobo/mail/e-mail-shell-module.c
==============================================================================
--- branches/kill-bonobo/mail/e-mail-shell-module.c	(original)
+++ branches/kill-bonobo/mail/e-mail-shell-module.c	Tue Mar 31 21:48:38 2009
@@ -39,6 +39,7 @@
 #include "e-mail-shell-module-migrate.h"
 #include "e-mail-shell-module-settings.h"
 
+#include "e-attachment-handler-mail.h"
 #include "e-mail-browser.h"
 #include "e-mail-reader.h"
 #include "em-account-prefs.h"
@@ -886,6 +887,8 @@
 	mail_shell_module_init_hooks ();
 	mail_shell_module_init_importers ();
 
+	e_attachment_handler_mail_get_type ();
+
 	/* XXX This never gets unreffed. */
 	mail_shell_module = g_object_ref (shell_module);
 

Modified: branches/kill-bonobo/widgets/misc/Makefile.am
==============================================================================
--- branches/kill-bonobo/widgets/misc/Makefile.am	(original)
+++ branches/kill-bonobo/widgets/misc/Makefile.am	Tue Mar 31 21:48:38 2009
@@ -41,6 +41,8 @@
 	e-alert-activity.h			\
 	e-attachment.h				\
 	e-attachment-dialog.h			\
+	e-attachment-handler.h			\
+	e-attachment-handler-image.h		\
 	e-attachment-icon-view.h		\
 	e-attachment-paned.h			\
 	e-attachment-store.h			\
@@ -103,6 +105,8 @@
 	e-alert-activity.c			\
 	e-attachment.c				\
 	e-attachment-dialog.c			\
+	e-attachment-handler.c			\
+	e-attachment-handler-image.c		\
 	e-attachment-icon-view.c		\
 	e-attachment-paned.c			\
 	e-attachment-store.c			\

Added: branches/kill-bonobo/widgets/misc/e-attachment-handler-image.c
==============================================================================
--- (empty file)
+++ branches/kill-bonobo/widgets/misc/e-attachment-handler-image.c	Tue Mar 31 21:48:38 2009
@@ -0,0 +1,285 @@
+/*
+ * e-attachment-handler-image.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>  
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-attachment-handler-image.h"
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include <gconf/gconf-client.h>
+
+#define E_ATTACHMENT_HANDLER_IMAGE_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_ATTACHMENT_HANDLER_IMAGE, EAttachmentHandlerImagePrivate))
+
+struct _EAttachmentHandlerImagePrivate {
+	gint placeholder;
+};
+
+static gpointer parent_class;
+
+static const gchar *ui =
+"<ui>"
+"  <popup name='context'>"
+"    <placeholder name='custom-actions'>"
+"      <menuitem action='image-set-as-background'/>"
+"    </placeholder>"
+"  </popup>"
+"</ui>";
+
+static void
+action_image_set_as_background_saved_cb (EAttachment *attachment,
+                                         GAsyncResult *result,
+                                         EAttachmentHandler *handler)
+{
+	EAttachmentView *view;
+	GConfClient *client;
+	GtkWidget *dialog;
+	GFile *file;
+	const gchar *key;
+	gpointer parent;
+	gchar *value;
+	GError *error = NULL;
+
+	client = gconf_client_get_default ();
+	view = e_attachment_handler_get_view (handler);
+
+	file = e_attachment_save_finish (attachment, result, &error);
+
+	if (error != NULL)
+		goto error;
+
+	value = g_file_get_path (file);
+	g_object_unref (file);
+
+	key = "/desktop/gnome/background/picture_filename";
+	gconf_client_set_string (client, key, value, &error);
+	g_free (value);
+
+	if (error != NULL)
+		goto error;
+
+	/* Ignore errors for this part. */
+	key = "/desktop/gnome/background/picture_options";
+	value = gconf_client_get_string (client, key, NULL);
+	if (g_strcmp0 (value, "none") == 0)
+		gconf_client_set_string (client, key, "wallpaper", NULL);
+	g_free (value);
+
+	goto exit;
+
+error:
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	dialog = gtk_message_dialog_new_with_markup (
+		parent, GTK_DIALOG_DESTROY_WITH_PARENT,
+		GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+		"<big><b>%s</b></big>",
+		_("Could not set as background"));
+
+	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);
+
+exit:
+	g_object_unref (client);
+	g_object_unref (handler);
+}
+
+static void
+action_image_set_as_background_cb (GtkAction *action,
+                                   EAttachmentHandler *handler)
+{
+	EAttachmentView *view;
+	EAttachment *attachment;
+	GFile *destination;
+	GList *selected;
+	gchar *path;
+
+	view = e_attachment_handler_get_view (handler);
+	selected = e_attachment_view_get_selected_attachments (view);
+	g_return_if_fail (g_list_length (selected) == 1);
+	attachment = E_ATTACHMENT (selected->data);
+
+	/* Save the image under ~/.gnome2/wallpapers/. */
+	path = g_build_filename (
+		g_get_home_dir (), ".gnome2", "wallpapers", NULL);
+	destination = g_file_new_for_path (path);
+	g_mkdir_with_parents (path, 0755);
+	g_free (path);
+
+	e_attachment_save_async (
+		attachment, destination, (GAsyncReadyCallback)
+		action_image_set_as_background_saved_cb,
+		g_object_ref (handler));
+
+	g_object_unref (destination);
+
+	g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+	g_list_free (selected);
+}
+
+static GtkActionEntry standard_entries[] = {
+
+	{ "image-set-as-background",
+	  NULL,
+	  N_("Set as _Background"),
+	  NULL,
+	  NULL,  /* XXX Add a tooltip! */
+	  G_CALLBACK (action_image_set_as_background_cb) }
+};
+
+static void
+attachment_handler_image_update_actions_cb (EAttachmentView *view,
+                                            EAttachmentHandler *handler)
+{
+	EAttachmentHandlerImagePrivate *priv;
+	EAttachment *attachment;
+	GFileInfo *file_info;
+	GtkActionGroup *action_group;
+	const gchar *content_type;
+	gchar *mime_type;
+	GList *selected;
+	gboolean visible = FALSE;
+
+	priv = E_ATTACHMENT_HANDLER_IMAGE_GET_PRIVATE (handler);
+
+	selected = e_attachment_view_get_selected_attachments (view);
+
+	if (g_list_length (selected) != 1)
+		goto exit;
+
+	attachment = E_ATTACHMENT (selected->data);
+	file_info = e_attachment_get_file_info (attachment);
+
+	if (file_info == NULL)
+		goto exit;
+
+	if (e_attachment_get_loading (attachment))
+		goto exit;
+
+	if (e_attachment_get_saving (attachment))
+		goto exit;
+
+	content_type = g_file_info_get_content_type (file_info);
+
+	mime_type = g_content_type_get_mime_type (content_type);
+	visible = (g_ascii_strncasecmp (mime_type, "image/", 6) == 0);
+	g_free (mime_type);
+
+exit:
+	action_group = e_attachment_view_get_action_group (view, "image");
+	gtk_action_group_set_visible (action_group, visible);
+
+	g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+	g_list_free (selected);
+}
+
+static void
+attachment_handler_image_constructed (GObject *object)
+{
+	EAttachmentHandlerImagePrivate *priv;
+	EAttachmentHandler *handler;
+	EAttachmentView *view;
+	GtkActionGroup *action_group;
+	GtkUIManager *ui_manager;
+	const gchar *domain = GETTEXT_PACKAGE;
+	GError *error = NULL;
+
+	handler = E_ATTACHMENT_HANDLER (object);
+	priv = E_ATTACHMENT_HANDLER_IMAGE_GET_PRIVATE (object);
+
+	/* Chain up to parent's constructed() method. */
+	G_OBJECT_CLASS (parent_class)->constructed (object);
+
+	view = e_attachment_handler_get_view (handler);
+	ui_manager = e_attachment_view_get_ui_manager (view);
+
+	action_group = gtk_action_group_new ("image");
+	gtk_action_group_set_translation_domain (action_group, domain);
+	gtk_action_group_add_actions (
+		action_group, standard_entries,
+		G_N_ELEMENTS (standard_entries), object);
+	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+	g_object_unref (action_group);
+
+	gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+
+	if (error != NULL) {
+		g_warning ("%s", error->message);
+		g_error_free (error);
+	}
+
+	g_signal_connect (
+		view, "update-actions",
+		G_CALLBACK (attachment_handler_image_update_actions_cb),
+		object);
+}
+
+static void
+attachment_handler_image_class_init (EAttachmentHandlerImageClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EAttachmentHandlerImagePrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->constructed = attachment_handler_image_constructed;
+}
+
+static void
+attachment_handler_image_init (EAttachmentHandlerImage *handler)
+{
+	handler->priv = E_ATTACHMENT_HANDLER_IMAGE_GET_PRIVATE (handler);
+}
+
+GType
+e_attachment_handler_image_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EAttachmentHandlerImageClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) attachment_handler_image_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EAttachmentHandlerImage),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) attachment_handler_image_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			E_TYPE_ATTACHMENT_HANDLER,
+			"EAttachmentHandlerImage",
+			&type_info, 0);
+	}
+
+	return type;
+}

Added: branches/kill-bonobo/widgets/misc/e-attachment-handler-image.h
==============================================================================
--- (empty file)
+++ branches/kill-bonobo/widgets/misc/e-attachment-handler-image.h	Tue Mar 31 21:48:38 2009
@@ -0,0 +1,65 @@
+/*
+ * e-attachment-handler-image.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>  
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_ATTACHMENT_HANDLER_IMAGE_H
+#define E_ATTACHMENT_HANDLER_IMAGE_H
+
+#include <widgets/misc/e-attachment-handler.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_HANDLER_IMAGE \
+	(e_attachment_handler_image_get_type ())
+#define E_ATTACHMENT_HANDLER_IMAGE(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_ATTACHMENT_HANDLER_IMAGE, EAttachmentHandlerImage))
+#define E_ATTACHMENT_HANDLER_IMAGE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_ATTACHMENT_HANDLER_IMAGE, EAttachmentHandlerImageClass))
+#define E_IS_ATTACHMENT_HANDLER_IMAGE(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_ATTACHMENT_HANDLER_IMAGE))
+#define E_IS_ATTACHMENT_HANDLER_IMAGE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_ATTACHMENT_HANDLER_IMAGE))
+#define E_ATTACHMENT_HANDLER_IMAGE_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_ATTACHMENT_HANDLER_IMAGE, EAttachmentHandlerImageClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentHandlerImage EAttachmentHandlerImage;
+typedef struct _EAttachmentHandlerImageClass EAttachmentHandlerImageClass;
+typedef struct _EAttachmentHandlerImagePrivate EAttachmentHandlerImagePrivate;
+
+struct _EAttachmentHandlerImage {
+	EAttachmentHandler parent;
+	EAttachmentHandlerImagePrivate *priv;
+};
+
+struct _EAttachmentHandlerImageClass {
+	EAttachmentHandlerClass parent_class;
+};
+
+GType		e_attachment_handler_image_get_type	(void);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_HANDLER_IMAGE_H */

Added: branches/kill-bonobo/widgets/misc/e-attachment-handler.c
==============================================================================
--- (empty file)
+++ branches/kill-bonobo/widgets/misc/e-attachment-handler.c	Tue Mar 31 21:48:38 2009
@@ -0,0 +1,157 @@
+/*
+ * e-attachment-handler.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>  
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-attachment-handler.h"
+
+#define E_ATTACHMENT_HANDLER_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_ATTACHMENT_HANDLER, EAttachmentHandlerPrivate))
+
+struct _EAttachmentHandlerPrivate {
+	gpointer view;  /* weak pointer */
+};
+
+enum {
+	PROP_0,
+	PROP_VIEW
+};
+
+static gpointer parent_class;
+
+static void
+attachment_handler_set_view (EAttachmentHandler *handler,
+                             EAttachmentView *view)
+{
+	g_return_if_fail (handler->priv->view == NULL);
+
+	handler->priv->view = view;
+
+	g_object_add_weak_pointer (
+		G_OBJECT (view), &handler->priv->view);
+}
+
+static void
+attachment_handler_set_property (GObject *object,
+                                 guint property_id,
+                                 const GValue *value,
+                                 GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_VIEW:
+			attachment_handler_set_view (
+				E_ATTACHMENT_HANDLER (object),
+				g_value_get_object (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_handler_get_property (GObject *object,
+                                 guint property_id,
+                                 GValue *value,
+                                 GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_VIEW:
+			g_value_set_object (
+				value, e_attachment_handler_get_view (
+				E_ATTACHMENT_HANDLER (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_handler_constructed (GObject *object)
+{
+	/* This allows subclasses to chain up safely since GObject
+	 * does not implement this method, and we might want to do
+	 * something here in the future. */
+}
+
+static void
+attachment_handler_class_init (EAttachmentHandlerClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EAttachmentHandlerPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = attachment_handler_set_property;
+	object_class->get_property = attachment_handler_get_property;
+	object_class->constructed = attachment_handler_constructed;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_VIEW,
+		g_param_spec_object (
+			"view",
+			"View",
+			NULL,
+			E_TYPE_ATTACHMENT_VIEW,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+attachment_handler_init (EAttachmentHandler *handler)
+{
+	handler->priv = E_ATTACHMENT_HANDLER_GET_PRIVATE (handler);
+}
+
+GType
+e_attachment_handler_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EAttachmentHandlerClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) attachment_handler_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EAttachmentHandler),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) attachment_handler_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			G_TYPE_OBJECT, "EAttachmentHandler",
+			&type_info, G_TYPE_FLAG_ABSTRACT);
+	}
+
+	return type;
+}
+
+EAttachmentView *
+e_attachment_handler_get_view (EAttachmentHandler *handler)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT_HANDLER (handler), NULL);
+
+	return E_ATTACHMENT_VIEW (handler->priv->view);
+}

Added: branches/kill-bonobo/widgets/misc/e-attachment-handler.h
==============================================================================
--- (empty file)
+++ branches/kill-bonobo/widgets/misc/e-attachment-handler.h	Tue Mar 31 21:48:38 2009
@@ -0,0 +1,67 @@
+/*
+ * e-attachment-handler.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>  
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_ATTACHMENT_HANDLER_H
+#define E_ATTACHMENT_HANDLER_H
+
+#include <widgets/misc/e-attachment-view.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_HANDLER \
+	(e_attachment_handler_get_type ())
+#define E_ATTACHMENT_HANDLER(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_ATTACHMENT_HANDLER, EAttachmentHandler))
+#define E_ATTACHMENT_HANDLER_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_ATTACHMENT_HANDLER, EAttachmentHandlerClass))
+#define E_IS_ATTACHMENT_HANDLER(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_ATTACHMENT_HANDLER))
+#define E_IS_ATTACHMENT_HANDLER_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_ATTACHMENT_HANDLER))
+#define E_ATTACHMENT_HANDLER_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_ATTACHMENT_HANDLER, EAttachmentHandlerClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentHandler EAttachmentHandler;
+typedef struct _EAttachmentHandlerClass EAttachmentHandlerClass;
+typedef struct _EAttachmentHandlerPrivate EAttachmentHandlerPrivate;
+
+struct _EAttachmentHandler {
+	GObject parent;
+	EAttachmentHandlerPrivate *priv;
+};
+
+struct _EAttachmentHandlerClass {
+	GObjectClass parent_class;
+};
+
+GType		e_attachment_handler_get_type	(void);
+EAttachmentView *
+		e_attachment_handler_get_view	(EAttachmentHandler *handler);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_HANDLER_H */

Modified: branches/kill-bonobo/widgets/misc/e-attachment-icon-view.c
==============================================================================
--- branches/kill-bonobo/widgets/misc/e-attachment-icon-view.c	(original)
+++ branches/kill-bonobo/widgets/misc/e-attachment-icon-view.c	Tue Mar 31 21:48:38 2009
@@ -22,7 +22,6 @@
 #include "e-attachment-icon-view.h"
 
 #include <glib/gi18n.h>
-#include <gdk/gdkkeysyms.h>
 
 #include "e-attachment.h"
 #include "e-attachment-store.h"
@@ -129,10 +128,8 @@
 {
 	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
 
-	if (event->keyval == GDK_Delete) {
-		e_attachment_view_remove_selected (view, TRUE);
+	if (e_attachment_view_key_press_event (view, event))
 		return TRUE;
-	}
 
 	/* Chain up to parent's key_press_event() method. */
 	return GTK_WIDGET_CLASS (parent_class)->

Modified: branches/kill-bonobo/widgets/misc/e-attachment-store.c
==============================================================================
--- branches/kill-bonobo/widgets/misc/e-attachment-store.c	(original)
+++ branches/kill-bonobo/widgets/misc/e-attachment-store.c	Tue Mar 31 21:48:38 2009
@@ -790,10 +790,13 @@
                                         GAsyncResult *result,
                                         SaveContext *save_context)
 {
+	GFile *file;
 	GSimpleAsyncResult *simple;
 	GError *error = NULL;
 
-	e_attachment_save_finish (attachment, result, &error);
+	file = e_attachment_save_finish (attachment, result, &error);
+	if (file != NULL)
+		g_object_unref (file);
 
 	/* Remove the attachment from the list. */
 	save_context->attachment_list = g_list_remove (

Modified: branches/kill-bonobo/widgets/misc/e-attachment-tree-view.c
==============================================================================
--- branches/kill-bonobo/widgets/misc/e-attachment-tree-view.c	(original)
+++ branches/kill-bonobo/widgets/misc/e-attachment-tree-view.c	Tue Mar 31 21:48:38 2009
@@ -22,7 +22,6 @@
 #include "e-attachment-tree-view.h"
 
 #include <glib/gi18n.h>
-#include <gdk/gdkkeysyms.h>
 
 #include "e-attachment.h"
 #include "e-attachment-store.h"
@@ -147,10 +146,8 @@
 {
 	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
 
-	if (event->keyval == GDK_Delete) {
-		e_attachment_view_remove_selected (view, TRUE);
+	if (e_attachment_view_key_press_event (view, event))
 		return TRUE;
-	}
 
 	/* Chain up to parent's key_press_event() method. */
 	return GTK_WIDGET_CLASS (parent_class)->

Modified: branches/kill-bonobo/widgets/misc/e-attachment-view.c
==============================================================================
--- branches/kill-bonobo/widgets/misc/e-attachment-view.c	(original)
+++ branches/kill-bonobo/widgets/misc/e-attachment-view.c	Tue Mar 31 21:48:38 2009
@@ -23,12 +23,18 @@
 
 #include <config.h>
 #include <glib/gi18n.h>
+#include <gdk/gdkkeysyms.h>
 #include <camel/camel-stream-mem.h>
 
 #include "e-util/e-binding.h"
-#include "e-util/e-plugin-ui.h"
 #include "e-util/e-util.h"
 #include "e-attachment-dialog.h"
+#include "e-attachment-handler-image.h"
+
+enum {
+	UPDATE_ACTIONS,
+	LAST_SIGNAL
+};
 
 enum {
 	DND_TYPE_MESSAGE_RFC822,
@@ -69,9 +75,9 @@
 "  <popup name='context'>"
 "    <menuitem action='cancel'/>"
 "    <menuitem action='save-as'/>"
-"    <menuitem action='set-background'/>"
 "    <menuitem action='remove'/>"
 "    <menuitem action='properties'/>"
+"    <separator/>"
 "    <placeholder name='custom-actions'/>"
 "    <separator/>"
 "    <menuitem action='add'/>"
@@ -86,6 +92,8 @@
 "  </popup>"
 "</ui>";
 
+static gulong signals[LAST_SIGNAL];
+
 static void
 action_add_cb (GtkAction *action,
                EAttachmentView *view)
@@ -295,13 +303,6 @@
 	g_list_free (selected);
 }
 
-static void
-action_set_background_cb (GtkAction *action,
-                          EAttachmentView *view)
-{
-	/* FIXME */
-}
-
 static GtkActionEntry standard_entries[] = {
 
 	{ "cancel",
@@ -354,13 +355,6 @@
 	  NULL,
 	  NULL,  /* XXX Add a tooltip! */
 	  G_CALLBACK (action_save_all_cb) },
-
-	{ "set-background",
-	  NULL,
-	  N_("Set as _Background"),
-	  NULL,
-	  NULL,  /* XXX Add a tooltip! */
-	  G_CALLBACK (action_set_background_cb) }
 };
 
 static GtkActionEntry editable_entries[] = {
@@ -573,10 +567,114 @@
 }
 
 static void
+attachment_view_update_actions (EAttachmentView *view)
+{
+	EAttachmentViewPrivate *priv;
+	EAttachment *attachment;
+	GtkAction *action;
+	GList *list, *iter;
+	guint n_selected;
+	gboolean busy = FALSE;
+
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+	priv = e_attachment_view_get_private (view);
+	list = e_attachment_view_get_selected_attachments (view);
+	n_selected = g_list_length (list);
+
+	if (n_selected == 1) {
+		attachment = g_object_ref (list->data);
+		busy |= e_attachment_get_loading (attachment);
+		busy |= e_attachment_get_saving (attachment);
+	} else
+		attachment = NULL;
+
+	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, !busy && n_selected == 1);
+
+	action = e_attachment_view_get_action (view, "remove");
+	gtk_action_set_visible (action, !busy && n_selected > 0);
+
+	action = e_attachment_view_get_action (view, "save-as");
+	gtk_action_set_visible (action, !busy && n_selected > 0);
+
+	/* 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 || busy)
+		return;
+
+	list = e_attachment_list_apps (attachment);
+
+	for (iter = list; iter != NULL; iter = iter->next) {
+		GAppInfo *app_info = iter->data;
+		GtkAction *action;
+		const gchar *app_executable;
+		const gchar *app_name;
+		gchar *action_tooltip;
+		gchar *action_label;
+		gchar *action_name;
+
+		if (!g_app_info_should_show (app_info))
+			continue;
+
+		app_executable = g_app_info_get_executable (app_info);
+		app_name = g_app_info_get_name (app_info);
+
+		action_name = g_strdup_printf ("open-in-%s", app_executable);
+		action_label = g_strdup_printf (_("Open in %s..."), app_name);
+
+		action_tooltip = g_strdup_printf (
+			_("Open this attachment in %s"), app_name);
+
+		action = gtk_action_new (
+			action_name, action_label, action_tooltip, NULL);
+
+		g_object_set_data_full (
+			G_OBJECT (action),
+			"app-info", g_object_ref (app_info),
+			(GDestroyNotify) g_object_unref);
+
+		g_object_set_data_full (
+			G_OBJECT (action),
+			"attachment", g_object_ref (attachment),
+			(GDestroyNotify) g_object_unref);
+
+		g_signal_connect (
+			action, "activate",
+			G_CALLBACK (action_open_in_cb), view);
+
+		gtk_action_group_add_action (priv->openwith_actions, action);
+
+		gtk_ui_manager_add_ui (
+			priv->ui_manager, priv->merge_id,
+			"/context/open-actions", action_name,
+			action_name, GTK_UI_MANAGER_AUTO, FALSE);
+
+		g_free (action_name);
+		g_free (action_label);
+		g_free (action_tooltip);
+	}
+
+	g_object_unref (attachment);
+	g_list_foreach (list, (GFunc) g_object_unref, NULL);
+	g_list_free (list);
+}
+
+static void
 attachment_view_class_init (EAttachmentViewIface *iface)
 {
 	gint ii;
 
+	iface->update_actions = attachment_view_update_actions;
+
 	g_object_interface_install_property (
 		iface,
 		g_param_spec_boolean (
@@ -587,6 +685,15 @@
 			G_PARAM_READWRITE |
 			G_PARAM_CONSTRUCT));
 
+	signals[UPDATE_ACTIONS] = g_signal_new (
+		"update-actions",
+		G_TYPE_FROM_INTERFACE (iface),
+		G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+		G_STRUCT_OFFSET (EAttachmentViewIface, update_actions),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+
 	for (ii = 0; ii < G_N_ELEMENTS (drag_info); ii++) {
 		const gchar *target = drag_info[ii].target;
 		drag_info[ii].atom = gdk_atom_intern (target, FALSE);
@@ -616,6 +723,9 @@
 			G_TYPE_INTERFACE, "EAttachmentView", &type_info, 0);
 
 		g_type_interface_add_prerequisite (type, GTK_TYPE_WIDGET);
+
+		/* Register known handler types. */
+		e_attachment_handler_image_get_type ();
 	}
 
 	return type;
@@ -628,6 +738,8 @@
 	GtkUIManager *ui_manager;
 	GtkActionGroup *action_group;
 	const gchar *domain = GETTEXT_PACKAGE;
+	GType *children;
+	guint ii;
 	GError *error = NULL;
 
 	priv = e_attachment_view_get_private (view);
@@ -671,7 +783,14 @@
 		G_OBJECT (view), "editable",
 		G_OBJECT (priv->editable_actions), "visible");
 
-	e_plugin_ui_register_manager (ui_manager, "attachment-view", view);
+	/* Instantiate attachment handlers. */
+	children = g_type_children (E_TYPE_ATTACHMENT_HANDLER, NULL);
+	for (ii = 0; children[ii] != G_TYPE_INVALID; ii++) {
+		EAttachmentHandler *handler;
+		handler = g_object_new (children[ii], "view", view, NULL);
+		priv->handlers = g_list_prepend (priv->handlers, handler);
+	}
+	g_free (children);
 }
 
 void
@@ -681,6 +800,10 @@
 
 	priv = e_attachment_view_get_private (view);
 
+	g_list_foreach (priv->handlers, (GFunc) g_object_unref, NULL);
+	g_list_free (priv->handlers);
+	priv->handlers = NULL;
+
 	if (priv->ui_manager != NULL) {
 		g_object_unref (priv->ui_manager);
 		priv->ui_manager = NULL;
@@ -952,6 +1075,25 @@
 	return FALSE;
 }
 
+gboolean
+e_attachment_view_key_press_event (EAttachmentView *view,
+                                   GdkEventKey *event)
+{
+	gboolean editable;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
+	g_return_val_if_fail (event != NULL, FALSE);
+
+	editable = e_attachment_view_get_editable (view);
+
+	if (event->keyval == GDK_Delete && editable) {
+		e_attachment_view_remove_selected (view, TRUE);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
 GtkTreePath *
 e_attachment_view_get_path_at_pos (EAttachmentView *view,
                                    gint x,
@@ -1445,108 +1587,7 @@
 void
 e_attachment_view_update_actions (EAttachmentView *view)
 {
-	EAttachmentViewPrivate *priv;
-	EAttachment *attachment;
-	GtkAction *action;
-	GList *list, *iter;
-	guint n_selected;
-	gboolean is_image;
-	gboolean busy = FALSE;
-
 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
 
-	priv = e_attachment_view_get_private (view);
-	list = e_attachment_view_get_selected_attachments (view);
-	n_selected = g_list_length (list);
-
-	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;
-	}
-
-	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, !busy && n_selected == 1);
-
-	action = e_attachment_view_get_action (view, "remove");
-	gtk_action_set_visible (action, !busy && n_selected > 0);
-
-	action = e_attachment_view_get_action (view, "save-as");
-	gtk_action_set_visible (action, !busy && n_selected > 0);
-
-	action = e_attachment_view_get_action (view, "set-background");
-	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 || busy)
-		return;
-
-	list = e_attachment_list_apps (attachment);
-
-	for (iter = list; iter != NULL; iter = iter->next) {
-		GAppInfo *app_info = iter->data;
-		GtkAction *action;
-		const gchar *app_executable;
-		const gchar *app_name;
-		gchar *action_tooltip;
-		gchar *action_label;
-		gchar *action_name;
-
-		if (!g_app_info_should_show (app_info))
-			continue;
-
-		app_executable = g_app_info_get_executable (app_info);
-		app_name = g_app_info_get_name (app_info);
-
-		action_name = g_strdup_printf ("open-in-%s", app_executable);
-		action_label = g_strdup_printf (_("Open in %s..."), app_name);
-
-		action_tooltip = g_strdup_printf (
-			_("Open this attachment in %s"), app_name);
-
-		action = gtk_action_new (
-			action_name, action_label, action_tooltip, NULL);
-
-		g_object_set_data_full (
-			G_OBJECT (action),
-			"app-info", g_object_ref (app_info),
-			(GDestroyNotify) g_object_unref);
-
-		g_object_set_data_full (
-			G_OBJECT (action),
-			"attachment", g_object_ref (attachment),
-			(GDestroyNotify) g_object_unref);
-
-		g_signal_connect (
-			action, "activate",
-			G_CALLBACK (action_open_in_cb), view);
-
-		gtk_action_group_add_action (priv->openwith_actions, action);
-
-		gtk_ui_manager_add_ui (
-			priv->ui_manager, priv->merge_id,
-			"/context/open-actions", action_name,
-			action_name, GTK_UI_MANAGER_AUTO, FALSE);
-
-		g_free (action_name);
-		g_free (action_label);
-		g_free (action_tooltip);
-	}
-
-	g_object_unref (attachment);
-	g_list_foreach (list, (GFunc) g_object_unref, NULL);
-	g_list_free (list);
+	g_signal_emit (view, signals[UPDATE_ACTIONS], 0);
 }

Modified: branches/kill-bonobo/widgets/misc/e-attachment-view.h
==============================================================================
--- branches/kill-bonobo/widgets/misc/e-attachment-view.h	(original)
+++ branches/kill-bonobo/widgets/misc/e-attachment-view.h	Tue Mar 31 21:48:38 2009
@@ -85,10 +85,16 @@
 						 GdkDragAction actions);
 	void		(*drag_source_unset)	(EAttachmentView *view);
 	void		(*drag_dest_unset)	(EAttachmentView *view);
+
+	/* Signals */
+	void		(*update_actions)	(EAttachmentView *view);
 };
 
 struct _EAttachmentViewPrivate {
 
+	/* Attachment Handlers */
+	GList *handlers;
+
 	/* Popup Menu Management */
 	GtkUIManager *ui_manager;
 	GtkActionGroup *standard_actions;
@@ -127,12 +133,16 @@
 						(EAttachmentView *view,
 						 gboolean select_next);
 
+/* Event Support */
 gboolean	e_attachment_view_button_press_event
 						(EAttachmentView *view,
 						 GdkEventButton *event);
 gboolean	e_attachment_view_button_release_event
 						(EAttachmentView *view,
 						 GdkEventButton *event);
+gboolean	e_attachment_view_key_press_event
+						(EAttachmentView *view,
+						 GdkEventKey *event);
 
 /* Selection Management */
 GtkTreePath *	e_attachment_view_get_path_at_pos

Modified: branches/kill-bonobo/widgets/misc/e-attachment.c
==============================================================================
--- branches/kill-bonobo/widgets/misc/e-attachment.c	(original)
+++ branches/kill-bonobo/widgets/misc/e-attachment.c	Tue Mar 31 21:48:38 2009
@@ -52,7 +52,7 @@
 #define EMBLEM_SIGN_GOOD	"stock_signature-ok"
 #define EMBLEM_SIGN_UNKNOWN	"stock_signature"
 
-/* Attributes needed by EAttachmentStore, et al. */
+/* Attributes needed for EAttachmentStore columns. */
 #define ATTACHMENT_QUERY "standard::*,preview::*,thumbnail::*"
 
 struct _EAttachmentPrivate {
@@ -1185,31 +1185,6 @@
 }
 
 gboolean
-e_attachment_is_image (EAttachment *attachment)
-{
-	GFileInfo *file_info;
-	const gchar *content_type;
-	gchar *mime_type;
-	gboolean is_image;
-
-	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
-
-	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;
-
-	mime_type = g_content_type_get_mime_type (content_type);
-	is_image = (g_ascii_strncasecmp (mime_type, "image/", 6) == 0);
-	g_free (mime_type);
-
-	return is_image;
-}
-
-gboolean
 e_attachment_is_rfc822 (EAttachment *attachment)
 {
 	GFileInfo *file_info;
@@ -1805,7 +1780,6 @@
 	GSimpleAsyncResult *simple;
 
 	GAppInfo *app_info;
-	GFile *file;
 };
 
 static OpenContext *
@@ -1836,9 +1810,6 @@
 	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 (OpenContext, open_context);
 }
 
@@ -1865,7 +1836,8 @@
 }
 
 static void
-attachment_open_file (OpenContext *open_context)
+attachment_open_file (GFile *file,
+                      OpenContext *open_context)
 {
 	GdkAppLaunchContext *context;
 	GSimpleAsyncResult *simple;
@@ -1900,7 +1872,7 @@
 		goto exit;
 
 	context = gdk_app_launch_context_new ();
-	file_list = g_list_prepend (NULL, open_context->file);
+	file_list = g_list_prepend (NULL, file);
 
 	success = g_app_info_launch (
 		open_context->app_info, file_list,
@@ -1926,19 +1898,22 @@
                                   GAsyncResult *result,
                                   OpenContext *open_context)
 {
+	GFile *file;
 	GError *error = NULL;
 
-	e_attachment_save_finish (attachment, result, &error);
+	file = e_attachment_save_finish (attachment, result, &error);
 
 	if (attachment_open_check_for_error (open_context, error))
 		return;
 
-	attachment_open_file (open_context);
+	attachment_open_file (file, open_context);
+	g_object_unref (file);
 }
 
 static void
 attachment_open_save_temporary (OpenContext *open_context)
 {
+	GFile *file;
 	gchar *path;
 	gint fd;
 	GError *error = NULL;
@@ -1948,14 +1923,16 @@
 	if (attachment_open_check_for_error (open_context, error))
 		return;
 
-	close (fd);
+	file = g_file_new_for_path (path);
 
-	open_context->file = g_file_new_for_path (path);
+	close (fd);
+	g_free (path);
 
 	e_attachment_save_async (
-		open_context->attachment, open_context->file,
-		(GAsyncReadyCallback) attachment_open_save_finished_cb,
-		open_context);
+		open_context->attachment, file, (GAsyncReadyCallback)
+		attachment_open_save_finished_cb, open_context);
+
+	g_object_unref (file);
 }
 
 void
@@ -1985,8 +1962,7 @@
 	 * the application directly.  Otherwise we have to save the MIME
 	 * part to a temporary file and launch the application from that. */
 	if (file != NULL) {
-		open_context->file = g_object_ref (file);
-		attachment_open_file (open_context);
+		attachment_open_file (file, open_context);
 
 	} else if (mime_part != NULL)
 		attachment_open_save_temporary (open_context);
@@ -2071,6 +2047,7 @@
 	GSimpleAsyncResult *simple;
 
 	GFile *directory;
+	GFile *destination;
 	GInputStream *input_stream;
 	GOutputStream *output_stream;
 	goffset total_num_bytes;
@@ -2115,6 +2092,9 @@
 	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);
 
@@ -2259,12 +2239,18 @@
 
 	if (bytes_read == 0) {
 		GSimpleAsyncResult *simple;
+		GFile *destination;
 
 		/* Steal the result. */
 		simple = save_context->simple;
 		save_context->simple = NULL;
 
-		g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+		/* 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);
@@ -2366,6 +2352,7 @@
 	if (attachment_save_check_for_error (save_context, error))
 		return;
 
+	save_context->destination = g_object_ref (destination);
 	attachment_save_got_output_stream (save_context);
 }
 
@@ -2384,6 +2371,7 @@
 	if (attachment_save_check_for_error (save_context, error))
 		return;
 
+	save_context->destination = g_object_ref (destination);
 	attachment_save_got_output_stream (save_context);
 }
 
@@ -2490,26 +2478,28 @@
 		attachment_save_query_info_cb, save_context);
 }
 
-gboolean
+GFile *
 e_attachment_save_finish (EAttachment *attachment,
                           GAsyncResult *result,
                           GError **error)
 {
 	GSimpleAsyncResult *simple;
-	gboolean success;
+	GFile *destination;
 
 	g_return_val_if_fail (
 		g_simple_async_result_is_valid (result,
 		G_OBJECT (attachment), e_attachment_save_async), FALSE);
 
 	simple = G_SIMPLE_ASYNC_RESULT (result);
-	success = g_simple_async_result_get_op_res_gboolean (simple);
+	destination = g_simple_async_result_get_op_res_gpointer (simple);
+	if (destination != NULL)
+		g_object_ref (destination);
 	g_simple_async_result_propagate_error (simple, error);
 	g_object_unref (simple);
 
 	attachment_set_saving (attachment, FALSE);
 
-	return success;
+	return destination;
 }
 
 void
@@ -2517,8 +2507,9 @@
                                 GAsyncResult *result,
                                 GtkWindow *parent)
 {
-	GtkWidget *dialog;
+	GFile *file;
 	GFileInfo *file_info;
+	GtkWidget *dialog;
 	const gchar *display_name;
 	const gchar *primary_text;
 	GError *error = NULL;
@@ -2527,8 +2518,9 @@
 	g_return_if_fail (G_IS_ASYNC_RESULT (result));
 	g_return_if_fail (GTK_IS_WINDOW (parent));
 
-	if (e_attachment_save_finish (attachment, result, &error))
-		return;
+	file = e_attachment_save_finish (attachment, result, &error);
+	if (file != NULL)
+		g_object_unref (file);
 
 	/* Ignore cancellations. */
 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))

Modified: branches/kill-bonobo/widgets/misc/e-attachment.h
==============================================================================
--- branches/kill-bonobo/widgets/misc/e-attachment.h	(original)
+++ branches/kill-bonobo/widgets/misc/e-attachment.h	Tue Mar 31 21:48:38 2009
@@ -98,7 +98,6 @@
 						 camel_cipher_validity_sign_t signed_);
 const gchar *	e_attachment_get_description	(EAttachment *attachment);
 const gchar *	e_attachment_get_thumbnail_path	(EAttachment *attachment);
-gboolean	e_attachment_is_image		(EAttachment *attachment);
 gboolean	e_attachment_is_rfc822		(EAttachment *attachment);
 GList *		e_attachment_list_apps		(EAttachment *attachment);
 
@@ -120,7 +119,7 @@
 						 GFile *destination,
 						 GAsyncReadyCallback callback,
 						 gpointer user_data);
-gboolean	e_attachment_save_finish	(EAttachment *attachment,
+GFile *		e_attachment_save_finish	(EAttachment *attachment,
 						 GAsyncResult *result,
 						 GError **error);
 



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