Index: shell/apps_evolution_shell.schemas.in
===================================================================
--- shell/apps_evolution_shell.schemas.in	(revision 37527)
+++ shell/apps_evolution_shell.schemas.in	(working copy)
@@ -41,7 +41,35 @@
       </locale>
     </schema>
 
- <!-- Offline Mode -->
+    <!-- Initial attachment view (icon or list) -->
+
+    <schema>
+      <key>/schemas/apps/evolution/shell/attachment_view</key>
+      <applyto>/apps/evolution/shell/attachment_view</applyto>
+      <owner>evolution</owner>
+      <type>int</type>
+      <default>0</default>
+      <locale name="C">
+        <short>Initial attachment view</short>
+        <long>Initial view for attachment bar widgets.
+        "0" is Icon View, "1" is List View.</long>
+      </locale>
+    </schema>
+
+    <!-- Initial GtkFileChooser Folder -->
+
+    <schema>
+      <key>/schemas/apps/evolution/shell/current_folder</key>
+      <applyto>/apps/evolution/shell/current-folder</applyto>
+      <owner>evolution</owner>
+      <type>string</type>
+      <locale name="C">
+        <short>Initial file chooser folder</short>
+        <long>Initial folder for GtkFileChooser dialogs.</long>
+      </locale>
+    </schema>
+
+    <!-- Offline Mode -->
 
     <schema>
       <key>/schemas/apps/evolution/shell/start_offline</key>
Index: plugins/import-ics-attachments/icsimporter.c
===================================================================
--- plugins/import-ics-attachments/icsimporter.c	(revision 37527)
+++ plugins/import-ics-attachments/icsimporter.c	(working copy)
@@ -1,435 +0,0 @@
-/*
- * 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/>  
- *
- *
- * Authors:
- *		Johnny Jacob <johnnyjacob@gmail.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <string.h>
-#include <glib.h>
-#include <gtk/gtk.h>
-#include <glib/gi18n.h>
-#include <e-util/e-config.h>
-#include <e-util/e-popup.h>
-#include <mail/em-popup.h>
-#include <mail/em-menu.h>
-#include <mail/mail-ops.h>
-#include <mail/mail-mt.h>
-#include <mail/em-folder-view.h>
-#include <mail/em-format-html-display.h>
-#include <mail/em-utils.h>
-#include "e-attachment-bar.h"
-#include <camel/camel-vee-folder.h>
-#include "e-util/e-error.h"
-#include <libedataserverui/e-source-selector.h>
-#include <libecal/e-cal.h>
-#include <libical/icalvcal.h>
-#include <calendar/common/authentication.h>
-
-typedef struct {
-	ECal *client;
-	int source_type;
-	icalcomponent *icalcomp;
-	GtkWidget *window;
-	GtkWidget *selector;
-} ICalImporterData;
-
-
-static void import_ics (EPlugin *ep, EPopupTarget *t, void *data);
-static icalcomponent* get_icalcomponent_from_file(char *filename);
-static void prepare_events (icalcomponent *icalcomp, GList **vtodos);
-static void prepare_tasks (icalcomponent *icalcomp, GList *vtodos);
-static void import_items(ICalImporterData *icidata);
-static gboolean update_objects (ECal *client, icalcomponent *icalcomp);
-static void dialog_response_cb (GtkDialog *dialog, gint response_id, ICalImporterData *icidata);
-static void ical_import_done(ICalImporterData *icidata);
-static void init_widgets (char *path);
-static icalcomponent_kind get_menu_type (void *data);
-
-void org_gnome_evolution_import_ics_attachments (EPlugin *ep, EMPopupTargetAttachments *t);
-void org_gnome_evolution_import_ics_part (EPlugin *ep, EMPopupTargetPart *t);
-
-static void
-popup_free (EPopup *ep, GSList *items, void *data)
-{
-	g_slist_free (items);
-}
-
-static EPopupItem popup_calendar_items[] = {
-	{ E_POPUP_BAR, "25.display.00"},
-	{ E_POPUP_ITEM, "25.display.01", N_("_Import to Calendar"), (EPopupActivateFunc)import_ics, NULL, "stock_mail-import"}
-};
-
-static EPopupItem popup_tasks_items[] = {
-	{ E_POPUP_BAR, "25.display.00"},
-	{ E_POPUP_ITEM, "25.display.01", N_("_Import to Tasks"), (EPopupActivateFunc)import_ics, NULL, "stock_mail-import"}
-};
-
-
-void org_gnome_evolution_import_ics_attachments (EPlugin *ep, EMPopupTargetAttachments *t)
-{
-	GSList *menus = NULL;
-	icalcomponent_kind kind;
-	int len = 0;
-	int i = 0;
-	CamelContentType *type;
-
-	len = g_slist_length(t->attachments);
-
-	if (len != 1)
-		return;
-
-	type = camel_data_wrapper_get_mime_type_field (((CamelDataWrapper *) ((EAttachment *) t->attachments->data)->body));
-	if (type && camel_content_type_is(type, "text", "calendar")) {
-
-		kind = get_menu_type (t);
-
-		if (kind == ICAL_VTODO_COMPONENT ) {
-			for (i = 0; i < sizeof (popup_tasks_items) / sizeof (popup_tasks_items[0]); i++)
-				menus = g_slist_prepend (menus, &popup_tasks_items[i]);
-		} else if ( kind == ICAL_VEVENT_COMPONENT) {
-			for (i = 0; i < sizeof (popup_calendar_items) / sizeof (popup_calendar_items[0]); i++)
-				menus = g_slist_prepend (menus, &popup_calendar_items[i]);
-		}
-
-		e_popup_add_items (t->target.popup, menus, NULL, popup_free, t);
-	}
-}
-
-void org_gnome_evolution_import_ics_part (EPlugin*ep, EMPopupTargetPart *t)
-{
-	GSList *menus = NULL;
-	icalcomponent_kind kind;
-	int i = 0;
-
-	if (!camel_content_type_is(((CamelDataWrapper *) t->part)->mime_type, "text", "calendar"))
-		return;
-
-	kind = get_menu_type (t);
-
-	if (kind == ICAL_VTODO_COMPONENT ) {
-		for (i = 0; i < sizeof (popup_tasks_items) / sizeof (popup_tasks_items[0]); i++)
-			menus = g_slist_prepend (menus, &popup_tasks_items[i]);
-	} else if ( kind == ICAL_VEVENT_COMPONENT) {
-		for (i = 0; i < sizeof (popup_calendar_items) / sizeof (popup_calendar_items[0]); i++)
-			menus = g_slist_prepend (menus, &popup_calendar_items[i]);
-	}
-
-	e_popup_add_items (t->target.popup, menus, NULL, popup_free, t);
-}
-
-static icalcomponent_kind
-get_menu_type (void *data)
-{
-	CamelMimePart *part;
-	char *path;
-	icalcomponent *icalcomp, *subcomp;
-	icalcomponent_kind kind;
-	EPopupTarget *target = (EPopupTarget *) data;
-
-	if (target->type == EM_POPUP_TARGET_ATTACHMENTS)
-		part = ((EAttachment *) ((EMPopupTargetAttachments *) target)->attachments->data)->body;
-	else
-		part = ((EMPopupTargetPart *) target)->part;
-
-	path = em_utils_temp_save_part (NULL, part, FALSE);
-
-	icalcomp = get_icalcomponent_from_file (path);
-
-	subcomp = icalcomponent_get_inner(icalcomp);
-	kind = icalcomponent_isa (subcomp);
-
-	if (kind == ICAL_VTODO_COMPONENT ) {
-		return ICAL_VTODO_COMPONENT;
-	} else if ( kind == ICAL_VEVENT_COMPONENT) {
-		return ICAL_VEVENT_COMPONENT;
-	}
-	return ICAL_NO_COMPONENT;
-}
-
-static void
-import_ics (EPlugin *ep, EPopupTarget *t, void *data)
-{
-	CamelMimePart *part;
-	char *path;
-	EPopupTarget *target = (EPopupTarget *) data;
-
-	if (target->type == EM_POPUP_TARGET_ATTACHMENTS)
-		part = ((EAttachment *) ((EMPopupTargetAttachments *) target)->attachments->data)->body;
-	else
-		part = ((EMPopupTargetPart *) target)->part;
-
-	path = em_utils_temp_save_part (NULL, part, FALSE);
-	init_widgets(path);
-}
-
-static void
-init_widgets(char *path)
-{
-
-	GtkWidget *vbox, *hbox, *dialog;
-	icalcomponent_kind kind;
-	icalcomponent *subcomp;
-	GtkWidget *selector, *label;
-	ESourceList *source_list;
-	ESource *primary;
-	GtkWidget *scrolled;
-	ICalImporterData *icidata = g_malloc0(sizeof(*icidata));
-	GtkWidget *icon, *button;
-	char *label_str = NULL;
-	char *markup;
-
-	g_return_if_fail ( path != NULL);
-	dialog = gtk_dialog_new_with_buttons (_("Import ICS"),
-						NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
-						GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-						NULL);
-	icidata->window = dialog;
-	g_signal_connect (dialog,
-			  "response",
-			  G_CALLBACK (dialog_response_cb),
-			  icidata);
-
-	vbox = GTK_DIALOG(dialog)->vbox;
-	hbox = gtk_hbox_new (FALSE, FALSE);
-	label = gtk_label_new(NULL);
-
-	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 6);
-
-	icidata->icalcomp = get_icalcomponent_from_file (path);
-
-	subcomp = icalcomponent_get_inner(icidata->icalcomp);
-	kind = icalcomponent_isa (subcomp);
-
-	if (kind == ICAL_VTODO_COMPONENT ) {
-		e_cal_get_sources (&source_list, E_CAL_SOURCE_TYPE_TODO, NULL);
-		label_str = _("Select Task List");
-		icidata->source_type = E_CAL_SOURCE_TYPE_TODO;
-	} else if ( kind == ICAL_VEVENT_COMPONENT) {
-		e_cal_get_sources (&source_list, E_CAL_SOURCE_TYPE_EVENT, NULL);
-		label_str = _("Select Calendar");
-		icidata->source_type = E_CAL_SOURCE_TYPE_EVENT;
-	}
-
-	markup = g_markup_printf_escaped ("<b>%s</b>", label_str);
-	gtk_label_set_markup (GTK_LABEL (label), markup);
-	g_free (markup);
-	hbox = gtk_hbox_new (FALSE, FALSE);
-	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 6);
-	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 6);
-
-	selector = e_source_selector_new (source_list);
-	e_source_selector_show_selection (E_SOURCE_SELECTOR (selector), FALSE);
-	scrolled = gtk_scrolled_window_new(NULL, NULL);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-	gtk_container_add((GtkContainer *)scrolled, selector);
-	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN);
-	hbox = gtk_hbox_new (FALSE, FALSE);
-	gtk_box_pack_start (GTK_BOX (hbox), scrolled, TRUE, TRUE, 6);
-	gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 6);
-	icidata->selector = selector;
-
-
-	/* FIXME What if no sources? */
-	primary = e_source_list_peek_source_any (source_list);
-	e_source_selector_set_primary_selection (E_SOURCE_SELECTOR (selector), primary);
-
-	g_object_unref (source_list);
-	hbox = gtk_hbox_new (FALSE, FALSE);
-	icon = gtk_image_new_from_icon_name (
-		"stock_mail-import", GTK_ICON_SIZE_MENU);
-	gtk_box_pack_start (GTK_BOX(hbox), icon, FALSE, FALSE, 6);
-	label = gtk_label_new_with_mnemonic (_("_Import"));
-	gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 6);
-	gtk_widget_show(label);
-	button = gtk_button_new ();
-	gtk_container_add (GTK_CONTAINER (button), hbox);
-	gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, GTK_RESPONSE_OK);
-	gtk_widget_grab_focus (button);
-
-	gtk_window_set_default_size (GTK_WINDOW (dialog), 210,340);
-	gtk_widget_show_all (dialog);
-	gtk_dialog_run (GTK_DIALOG (dialog));
-	gtk_widget_destroy (dialog);
-}
-
-static void
-dialog_response_cb (GtkDialog *dialog, gint response_id, ICalImporterData *icidata)
-{
-	switch (response_id) {
-		case GTK_RESPONSE_OK :
-			import_items(icidata);
-		break;
-
-		case GTK_RESPONSE_CANCEL :
-		case GTK_RESPONSE_DELETE_EVENT :
-		break;
-	}
-}
-
-/* This removes all components except VEVENTs and VTIMEZONEs from the toplevel */
-static void
-prepare_events (icalcomponent *icalcomp, GList **vtodos)
-{
-	icalcomponent *subcomp;
-	icalcompiter iter;
-
-	if (vtodos)
-		*vtodos = NULL;
-
-	iter = icalcomponent_begin_component (icalcomp, ICAL_ANY_COMPONENT);
-	while ((subcomp = icalcompiter_deref (&iter)) != NULL) {
-		icalcomponent_kind child_kind = icalcomponent_isa (subcomp);
-		if (child_kind != ICAL_VEVENT_COMPONENT
-		    && child_kind != ICAL_VTIMEZONE_COMPONENT) {
-
-			icalcompiter_next (&iter);
-
-			icalcomponent_remove_component (icalcomp, subcomp);
-			if (child_kind == ICAL_VTODO_COMPONENT && vtodos)
-				*vtodos = g_list_prepend (*vtodos, subcomp);
-			else
-                                icalcomponent_free (subcomp);
-		} else {
-			icalcompiter_next (&iter);
-		}
-	}
-}
-
-/* This removes all components except VTODOs and VTIMEZONEs from the toplevel
-   icalcomponent, and adds the given list of VTODO components. The list is
-   freed afterwards. */
-static void
-prepare_tasks (icalcomponent *icalcomp, GList *vtodos)
-{
-	icalcomponent *subcomp;
-	GList *elem;
-	icalcompiter iter;
-
-	iter = icalcomponent_begin_component (icalcomp, ICAL_ANY_COMPONENT);
-	while ((subcomp = icalcompiter_deref (&iter)) != NULL) {
-		icalcomponent_kind child_kind = icalcomponent_isa (subcomp);
-		if (child_kind != ICAL_VTODO_COMPONENT
-		    && child_kind != ICAL_VTIMEZONE_COMPONENT) {
-			icalcompiter_next (&iter);
-			icalcomponent_remove_component (icalcomp, subcomp);
-			icalcomponent_free (subcomp);
-		} else {
-			icalcompiter_next (&iter);
-		}
-	}
-
-	for (elem = vtodos; elem; elem = elem->next) {
-		icalcomponent_add_component (icalcomp, elem->data);
-	}
-	g_list_free (vtodos);
-}
-
-static void
-import_items(ICalImporterData *icidata)
-{
-	ESource *source;
-	g_return_if_fail (icidata != NULL);
-
-	source = e_source_selector_peek_primary_selection ((ESourceSelector *)icidata->selector);
-	g_return_if_fail ( source != NULL);
-
-	icidata->client = auth_new_cal_from_source (source, icidata->source_type);
-	e_cal_open (icidata->client, FALSE, NULL);
-
-	switch (icidata->source_type) {
-	case E_CAL_SOURCE_TYPE_EVENT:
-		prepare_events (icidata->icalcomp, NULL);
-		if (!update_objects (icidata->client, icidata->icalcomp))
-			/* FIXME: e_error ... */;
-		break;
-	case E_CAL_SOURCE_TYPE_TODO:
-		prepare_tasks (icidata->icalcomp, NULL);
-		if (!update_objects (icidata->client, icidata->icalcomp))
-			/* FIXME: e_error ... */;
-		break;
-	default:
-		g_assert_not_reached ();
-	}
-	ical_import_done (icidata);
-}
-
-static gboolean
-update_objects (ECal *client, icalcomponent *icalcomp)
-{
-	icalcomponent_kind kind;
-	icalcomponent *vcal;
-	gboolean success = TRUE;
-
-	kind = icalcomponent_isa (icalcomp);
-
-	if (kind == ICAL_VTODO_COMPONENT || kind == ICAL_VEVENT_COMPONENT) {
-		vcal = e_cal_util_new_top_level ();
-		if (icalcomponent_get_method (icalcomp) == ICAL_METHOD_CANCEL)
-			icalcomponent_set_method (vcal, ICAL_METHOD_CANCEL);
-		else
-			icalcomponent_set_method (vcal, ICAL_METHOD_PUBLISH);
-		icalcomponent_add_component (vcal, icalcomponent_new_clone (icalcomp));
-	} else if (kind == ICAL_VCALENDAR_COMPONENT) {
-		vcal = icalcomponent_new_clone (icalcomp);
-		if (!icalcomponent_get_first_property (vcal, ICAL_METHOD_PROPERTY))
-			icalcomponent_set_method (vcal, ICAL_METHOD_PUBLISH);
-	} else
-		return FALSE;
-
-	if (!e_cal_receive_objects (client, vcal, NULL))
-		success = FALSE;
-
-	icalcomponent_free (vcal);
-
-	return success;
-}
-
-static void
-ical_import_done(ICalImporterData *icidata)
-{
-	g_object_unref (icidata->client);
-	icalcomponent_free (icidata->icalcomp);
-	g_free (icidata);
-}
-
-static icalcomponent *
-get_icalcomponent_from_file(char *filename)
-{
-	char *contents;
-	icalcomponent *icalcomp;
-
-	g_return_val_if_fail (filename != NULL, NULL);
-
-	if (!g_file_get_contents (filename, &contents, NULL, NULL)) {
-		g_free (filename);
-		return NULL;
-	}
-	g_free (filename);
-
-	icalcomp = e_cal_util_parse_ics_string (contents);
-	g_free (contents);
-
-	if (icalcomp) {
-		return icalcomp;
-	}
-	return NULL;
-}
Index: plugins/import-ics-attachments/org-gnome-evolution-mail-attachments-import-ics.eplug.xml
===================================================================
--- plugins/import-ics-attachments/org-gnome-evolution-mail-attachments-import-ics.eplug.xml	(revision 37527)
+++ plugins/import-ics-attachments/org-gnome-evolution-mail-attachments-import-ics.eplug.xml	(working copy)
@@ -1,26 +0,0 @@
-<?xml version="1.0"?>
-<e-plugin-list>
-  <!-- the path to the shared library -->
-  <e-plugin
-    id="org.gnome.evolution.mail.attachments.import.ics"
-    type="shlib"
-    location="@PLUGINDIR@/liborg-gnome-evolution-mail-attachments-import-ics@SOEXT@"
-    _name="Import to Calendar">
-    
-    <author name="Johnny Jacob" email="johnnyjacob@gmail.com"/>
-    <_description>Imports ICS attachments to calendar.</_description>
-
-    	<hook class="org.gnome.evolution.mail.popup:1.0">
-		<menu id="org.gnome.evolution.mail.formathtmldisplay.popup" target="part" factory="org_gnome_evolution_import_ics_part">
-		</menu>
-	</hook>
-    	<hook class="org.gnome.evolution.mail.popup:1.0">
-		<menu id="org.gnome.evolution.mail.attachments.popup" target="attachments" factory="org_gnome_evolution_import_ics_attachments">
-		</menu>
-	</hook>
-	<hook class="org.gnome.evolution.mail.popup:1.0">
-		<menu target="attachments" id="org.gnome.evolution.mail.composer.attachmentbar.popup" factory="org_gnome_evolution_import_ics_attachments">
-		</menu>
-	</hook>
-  </e-plugin>
-</e-plugin-list>
Index: plugins/import-ics-attachments/ChangeLog
===================================================================
--- plugins/import-ics-attachments/ChangeLog	(revision 37527)
+++ plugins/import-ics-attachments/ChangeLog	(working copy)
@@ -1,109 +0,0 @@
-2009-01-21  Milan Crha  <mcrha@redhat.com>
-
-	* Makefile.am: Use also EVOLUTION_CALENDAR_CFLAGS.
-
-2008-09-02  Sankar P  <psankar@novell.com>
-
-License Changes
-
-	* icsimporter.c:
-
-2008-08-12  Bharath Acharya  <abharath@novell.com>
-
-	* Makefile.am: Added necessary libraries to link to. Build break while 
-	compiling on Windows.
-
-2008-08-11  Matthew Barnes  <mbarnes@redhat.com>
-
-	** Fixes part of bug #546892
-
-	* icsimporter.c:
-	Prefer gtk_image_new_from_icon_name() over e_icon_factory_get_image().
-
-2008-04-17  Milan Crha  <mcrha@redhat.com>
-
-	** Part of fix for bug #526739
-
-	* icsimporter.c: Do not include gnome-vfs.
-
-2007-02-20  Paul Bolle  <pebolle@tiscali.nl>
-
-	** Fix for bug #517082
-
-	* icsimporter.c: (init_widgets): Fix small leak.
-
-2008-02-06  Milan Crha  <mcrha@redhat.com>
-
-	** Fix for bug #514622
-
-	* icsimporter.c: (dialog_close_cb): Drop this function.
-	* icsimporter.c: (init_widgets), (dialog_response_cb),
-	(ical_import_done): Do not call "close" on already closed dialog.
-
-2007-10-01  Milan Crha  <mcrha@redhat.com>
-
-	* icsimporter.c: (prepare_events), (prepare_tasks):
-	Really go through every component when removing one.
-
-2007-06-03  Srinivasa Ragavan  <sragavan@novell.com>
-
-	** Fix for version removal from Installed files from Gilles Dartiguelongue 
-
-	* Makefile.am:
-
-2007-05-12  Matthew Barnes  <mbarnes@redhat.com>
-
-	** Fixes part of bug #337616
-
-	* Makefile.am: Add "eplug" file to CLEANFILES.
-
-2007-03-27  Matthew Barnes  <mbarnes@redhat.com>
-
-	* icsimporter.c: Don't mix declarations and code (#405495).
-
-2007-03-20  Matthew Barnes  <mbarnes@redhat.com>
-
-	** Fixes part of bug #419524
-
-	* Include <glib/gi18n.h> instead of <libgnome/gnome-i18n.h>.
-
-2006-11-28  Parthasarathi Susarla <ajaysusarla@gmail.com>
-	
-	** Fix bug #348679
-
-	* icsimporter.c: (org_gnome_evolution_import_ics_attachment):
-	Do not access structure elements directly. Use the methods of the
-	class in the CamelDataWrapper Class
-	  
-2006-08-28  Kjartan Maraas  <kmaraas@gnome.org>
-
-	* org-gnome-evolution-mail-attachments-import-ics.eplug.xml: Mark
-	name and description for translation.
-
-2006-08-23  Srinivasa Ragavan  <sragavan@novell.com>
-
-	** Fix for bug #347248
-	
-	* icsimporter.c: (get_menu_type), (import_ics): Update the
-	em_utils_temp_save_part to use readwrite mode.
-
-2006-02-21  Chenthill Palanisamy  <pchenthill@novell.com>
-
-	reviewed by Veerapuram Varadhan	 <vvaradhan@novell.com>
-
-	Fixes a crash
-	* icsimporter.c: (org_gnome_evolution_import_ics_attachments):
-	If the number of attachments selected is not equal to one, just return;
-	
-
-2006-01-22  Harish Krishnaswamy  <kharish@novell.com>
-
-	* icsimporter.c (get_menu_type), (dialog_response_cb),
-	 (dialog_close_cb), (get_icalcomponent_from_file):
-	Fix twenty odd compiler warnings and style oddness.
-
-2006-01-17  Harish Krishnaswamy  <kharish@novell.com>
-
-	* import-ics-attachments : Initial commits. Plugin written and submitted by
-	Johnny Jacob <johnnyjacob@gmail.com>
-
Index: plugins/import-ics-attachments/Makefile.am
===================================================================
--- plugins/import-ics-attachments/Makefile.am	(revision 37527)
+++ plugins/import-ics-attachments/Makefile.am	(working copy)
@@ -1,35 +0,0 @@
-INCLUDES =						\
-	-I$(top_srcdir)\
-	-I$(top_srcdir)/camel                           \
-	-I$(top_srcdir)/widgets/misc			\
-	$(EVOLUTION_MAIL_CFLAGS)			\
-	$(EVOLUTION_CALENDAR_CFLAGS)			\
-	-DEVOLUTION_DATADIR=\""$(datadir)"\"		\
-	-DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\"	\
-	-DEVOLUTION_ICONSDIR=\""$(imagesdir)"\"		\
-	-DEVOLUTION_IMAGES=\""$(imagesdir)"\"		\
-	-DEVOLUTION_BUTTONSDIR=\""$(buttonsdir)"\"	\
-	-DEVOLUTION_LOCALEDIR=\""$(localedir)"\"	\
-	-DEVOLUTION_UIDIR=\""$(evolutionuidir)"\"	\
-	-DCAMEL_PROVIDERDIR=\""$(camel_providerdir)"\"	\
-	-DPLUGINDIR=\""$(plugindir)"\"			\
-	-DPREFIX=\""$(prefix)"\"			
-
-@EVO_PLUGIN_RULE@
-
-plugin_DATA = org-gnome-evolution-mail-attachments-import-ics.eplug
-plugin_LTLIBRARIES = liborg-gnome-evolution-mail-attachments-import-ics.la
-
-liborg_gnome_evolution_mail_attachments_import_ics_la_SOURCES = icsimporter.c
-liborg_gnome_evolution_mail_attachments_import_ics_la_LDFLAGS = -module -avoid-version $(NO_UNDEFINED)
-liborg_gnome_evolution_mail_attachments_import_ics_la_LIBADD = 			\
-	$(top_builddir)/e-util/libeutil.la		\
-	$(top_builddir)/mail/libevolution-mail.la	\
-	$(top_builddir)/calendar/common/libevolution-calendarprivate.la	\
-	$(EVOLUTION_CALENDAR_LIBS)	\
-	$(EVOLUTION_MAIL_LIBS)
-
-EXTRA_DIST = org-gnome-evolution-mail-attachments-import-ics.eplug.xml
-
-BUILT_SOURCES = $(plugin_DATA)
-CLEANFILES = $(BUILT_SOURCES)
Index: plugins/attachment-reminder/attachment-reminder.c
===================================================================
--- plugins/attachment-reminder/attachment-reminder.c	(revision 37527)
+++ plugins/attachment-reminder/attachment-reminder.c	(working copy)
@@ -24,6 +24,7 @@
 #include <glib/gi18n.h>
 #include <string.h>
 
+#include <glade/glade-xml.h>
 #include <gconf/gconf-client.h>
 
 #include <e-util/e-config.h>
@@ -43,9 +44,10 @@
 
 #include <mail/em-utils.h>
 
-#include "widgets/misc/e-attachment-bar.h"
 #include "composer/e-msg-composer.h"
 #include "composer/e-composer-actions.h"
+#include "widgets/misc/e-attachment-view.h"
+#include "widgets/misc/e-attachment-store.h"
 
 #define GCONF_KEY_ATTACH_REMINDER_CLUES "/apps/evolution/mail/attachment_reminder_clues"
 #define SIGNATURE "-- "
@@ -182,12 +184,13 @@ check_for_attachment_clues (gchar *msg)
 static gboolean
 check_for_attachment (EMsgComposer *composer)
 {
-	EAttachmentBar* bar = (EAttachmentBar*)e_msg_composer_get_attachment_bar (composer);
+	EAttachmentView *view;
+	EAttachmentStore *store;
 
-	if (e_attachment_bar_get_num_attachments (bar))
-		return TRUE;
+	view = e_msg_composer_get_attachment_view (composer);
+	store = e_attachment_view_get_store (view);
 
-	return FALSE;
+	return (e_attachment_store_get_num_attachments (store) > 0);
 }
 
 static gchar*
Index: plugins/save-attachments/ChangeLog
===================================================================
--- plugins/save-attachments/ChangeLog	(revision 37527)
+++ plugins/save-attachments/ChangeLog	(working copy)
@@ -1,102 +0,0 @@
-2009-01-28  Tor Lillqvist  <tml@novell.com>
-
-	* Makefile.am: Use -no-undefined and link with more libs on
-	Windows.
-
-2008-09-19  Sankar P  <psankar@novell.com>
-
-License Changes
-
-	* save-attachments.c:
-
-2007-12-20  Matthew Barnes  <mbarnes@redhat.com>
-
-	** Fixes part of bug #362638
-
-	* save-attachments.c:
-	Use the new MailMsg API for messages.
-
-2007-04-02  Sankar P  <psankar@novell.com>
-
-	* Committed on behalf of Gilles Dartiguelongue <dartigug@esiee.fr>
-
-	* org-gnome-save-attachments.eplug.xml:
-	Cleanup. 
-	Fixes part of #301149
-
-2007-03-29  Matthew Barnes  <mbarnes@redhat.com>
-
-	* save-attachments.c:
-	Evolution requires GLib 2.10 now; remove dead backward-compatibility
-	code for GLib < 2.8 (#418971).
-
-2007-01-27  Nickolay V. Shmyrev  <nshmyrev@yandex.ru>
-
-	* save-attachments.c: (save_got_message):
-	mark strings as translatable. See bug #399381 for details.
-
-2006-07-07  Hiroyuki Ikezoe  <poincare@ikezoe.net>
-
-	** Fixes bug #341369
-	* save-attachments.c: fixing a memory leak.
-
-2006-02-28  Shi Pu <shi.pu@sun.com>
-
-	** Fixes #323853
-
-	* save-attachments.c: (save_response), (entry_changed),
-	(save_got_message):
-	replace GnomeFileEntry by GtkFileChooserButton.
-
-2005-12-17  Tor Lillqvist  <tml@novell.com>
-
-	* save-attachments.c (entry_changed): Use GLib API to manipulate
-	pathname.
-
-2005-05-16  Not Zed  <NotZed@Ximian.com>
-
-	* save-attachments.c: moved e-error to e-util
-
-2005-05-13  Rodney Dawes  <dobey@novell.com>
-
-	* org-gnome-save-attachments.xml: Update for new menu layout
-
-2005-05-12  Not Zed  <NotZed@Ximian.com>
-
-	* Makefile.am: setup built_sources/cleanfiles.
-
-2005-05-06  Not Zed  <NotZed@Ximian.com>
-
-	* Makefile.am: 
-	* org-gnome-save-attachments.eplug.xml: s/.in/.xml/ & i18n.
-
-2005-02-24  Björn Torkelsson  <torkel@acc.umu.se>
-
-	* org-gnome-save-attachments.eplug.in: Removed plugin from the
-	name.
-	Fixed description and added author
-
-2005-02-07  Not Zed  <NotZed@Ximian.com>
-
-	* save-attachments.c (save_part): fix the access() call test.
-
-2005-01-04  Philip Van Hoof  <pvanhoof@gnome.org>
-
-	* save-attachments.c: Use standard error messages
-
-2004-12-27  Philip Van Hoof  <pvanhoof@gnome.org>
-
-	* save-attachments.c: Warning when overwriting file
-
-2004-11-01  JP Rosevear  <jpr@novell.com>
-
-	* Makefile.am: dist xml menu file
-
-2004-11-01  JP Rosevear  <jpr@novell.com>
-
-	* Makefile.am: dist .eplug.in file
-
-2004-10-20  Not Zed  <NotZed@Ximian.com>
-
-	* Imported save-attachments example plugin.
-
Index: plugins/save-attachments/save-attachments.c
===================================================================
--- plugins/save-attachments/save-attachments.c	(revision 37527)
+++ plugins/save-attachments/save-attachments.c	(working copy)
@@ -1,392 +0,0 @@
-/*
- * 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/>  
- *
- *
- * Authors:
- *		Michael Zucchi <notzed@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-/* This is prototype code only, this may, or may not, use undocumented
- * unstable or private internal function calls. */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdio.h>
-
-#include <gtk/gtk.h>
-#include <glib/gi18n.h>
-#include <glib/gstdio.h>
-
-#include <camel/camel-folder.h>
-#include <camel/camel-exception.h>
-#include <camel/camel-mime-message.h>
-#include <camel/camel-multipart.h>
-#include <camel/camel-utf8.h>
-
-#include "e-util/e-error.h"
-
-#include "mail/em-menu.h"
-#include "mail/em-utils.h"
-
-/* these are sort of mail-internal */
-#include "mail/mail-mt.h"
-#include "mail/mail-ops.h"
-
-void org_gnome_save_attachments_save(EPlugin *ep, EMMenuTargetSelect *target);
-
-struct _save_data {
-	CamelFolder *folder;
-	char *uid;
-	CamelMimeMessage *msg;
-
-	char *path;
-	char *base;
-
-	GtkWidget *entry;
-	GtkWidget *tree;
-	GtkTreeStore *model;
-};
-
-static void
-free_data(struct _save_data *data)
-{
-	if (data->msg)
-		camel_object_unref(data->msg);
-	g_free(data->base);
-	g_free(data->path);
-	g_free(data->uid);
-	camel_object_unref(data->folder);
-	if (data->model)
-		g_object_unref(data->model);
-	g_free(data);
-}
-
-static char *
-clean_name(const char *s)
-{
-	GString *out = g_string_new("");
-	int c;
-	char *r;
-
-	while ( (c = camel_utf8_getc((const unsigned char **)&s)) ) {
-		if (!g_unichar_isprint(c) || ( c < 0x7f && strchr(" /'\"`&();|<>$%{}!", c )))
-			c = '_';
-		g_string_append_u(out, c);
-	}
-
-	r = g_strdup(out->str);
-	g_string_free(out, TRUE);
-
-	return r;
-}
-
-static void
-fill_model_rec(CamelMimeMessage *msg, CamelMimePart *part, GtkTreeStore *model, GtkTreeIter *parent, GString *name)
-{
-	CamelDataWrapper *containee;
-	int parts, i;
-	char *type;
-	GtkTreeIter iter;
-	int len = name->len;
-	CamelContentType *mime;
-
-	containee = camel_medium_get_content_object((CamelMedium *)part);
-	if (containee == NULL)
-		return;
-
-	mime = ((CamelDataWrapper *)containee)->mime_type;
-	type = camel_content_type_simple(mime);
-
-	if (CAMEL_IS_MULTIPART(containee)) {
-		gtk_tree_store_append(model, &iter, parent);
-		g_string_append_printf(name, ".multipart");
-		gtk_tree_store_set(model, &iter, 0, FALSE, 1, type, 2, name->str, 3, name->str, 4, part, -1);
-
-		parts = camel_multipart_get_number((CamelMultipart *)containee);
-		for (i = 0; i < parts; i++) {
-			CamelMimePart *mpart = camel_multipart_get_part((CamelMultipart *)containee, i);
-
-			g_string_truncate(name, len);
-			g_string_append_printf(name, ".%d", i);
-			fill_model_rec(msg, mpart, model, &iter, name);
-		}
-	} else if (CAMEL_IS_MIME_MESSAGE(containee)) {
-		gtk_tree_store_append(model, &iter, parent);
-		g_string_append_printf(name, ".msg");
-		gtk_tree_store_set(model, &iter, 0, FALSE, 1, type, 2, name->str, 3, name->str, 4, part, -1);
-		fill_model_rec(msg, (CamelMimePart *)containee, model, &iter, name);
-	} else {
-		char *filename = NULL;
-		const char *ext = NULL, *tmp;
-		int save = FALSE;
-
-		gtk_tree_store_append(model, &iter, parent);
-		tmp = camel_mime_part_get_filename(part);
-		if (tmp) {
-			filename = clean_name(tmp);
-			ext = strrchr(filename, '.');
-		}
-		tmp = camel_mime_part_get_disposition(part);
-		if (tmp && !strcmp(tmp, "attachment"))
-			save = TRUE;
-
-		if (camel_content_type_is(mime, "text", "*")) {
-			if (ext == NULL) {
-				if ((ext = mime->subtype) == NULL || !strcmp(ext, "plain"))
-					ext = "text";
-			}
-		} else if (camel_content_type_is(mime, "image", "*")) {
-			if (ext == NULL) {
-				if ((ext = mime->subtype) == NULL)
-					ext = "image";
-			}
-			save = TRUE;
-		}
-
-		g_string_append_printf(name, ".%s", ext);
-		gtk_tree_store_set(model, &iter, 0, save, 1, type, 2, filename?filename:name->str, 3, filename?NULL:name->str, 4, part, -1);
-		g_free(filename);
-	}
-	g_free(type);
-
-	g_string_truncate(name, len);
-}
-
-static void
-fill_model(CamelMimeMessage *msg, GtkTreeStore *model)
-{
-	GString *name = g_string_new("");
-	GtkTreeIter iter;
-
-	gtk_tree_store_append(model, &iter, NULL);
-	gtk_tree_store_set(model, &iter, 0, FALSE, 1, "message/rfc822", 2, ".msg", 3, ".msg", 4, msg, -1);
-	fill_model_rec(msg, (CamelMimePart *)msg, model, &iter, name);
-	g_string_free(name, TRUE);
-}
-
-static gboolean
-save_part(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, void *d)
-{
-	struct _save_data *data = d;
-	char *filename, *ext, *save;
-	CamelMimePart *part;
-	gboolean doit;
-
-	/* TODO: check for existing file */
-
-	gtk_tree_model_get(model, iter, 0, &doit, -1);
-	if (!doit)
-		return FALSE;
-
-	gtk_tree_model_get(model, iter, 2, &filename, 3, &ext, 4, &part, -1);
-	if (ext == NULL)
-		save = g_build_filename(data->path, filename, NULL);
-	else
-		save = g_strdup_printf("%s%s", data->base, ext);
-
-	/* FIXME: if part == data->msg then we need to save this
-	 * differently, not using the envelope MimePart */
-
-	/*
-	 * The underlying em_utils_save_part_to_file ain't using gnome-vfs. Therefor
-	 * the POSIX access-call should suffice for checking the file existence.
-	 */
-
-	if (g_access(save, F_OK) == 0)
-		doit = e_error_run(NULL, E_ERROR_ASK_FILE_EXISTS_OVERWRITE, save, NULL) == GTK_RESPONSE_OK;
-
-	if (doit)
-		em_utils_save_part_to_file(NULL, save, part);
-
-	g_free(ext);
-	g_free(filename);
-
-	return FALSE;
-}
-
-static void
-save_response(GtkWidget *d, int id, struct _save_data *data)
-{
-	if (id == GTK_RESPONSE_OK) {
-		char *tmp;
-
-		data->base = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (data->entry));
-		data->path = g_strdup(data->base);
-		tmp = strrchr(data->path, '/');
-		if (tmp)
-			*tmp = 0;
-		gtk_tree_model_foreach((GtkTreeModel *)data->model, save_part, data);
-	}
-
-	gtk_widget_destroy(d);
-	free_data(data);
-}
-
-static gboolean
-entry_changed_update(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, void *d)
-{
-	const char *name = d;
-	char *filename, *ext;
-
-	gtk_tree_model_get(model, iter, 3, &ext, -1);
-	if (ext) {
-		filename = g_strdup_printf("%s%s", name, ext);
-		gtk_tree_store_set((GtkTreeStore *)model, iter, 2, filename, -1);
-		g_free(filename);
-		g_free(ext);
-	}
-
-	return FALSE;
-}
-
-static void
-entry_changed(GtkWidget *entry, struct _save_data *data)
-{
-	char *path;
-	char *basename = NULL;
-	const char *file;
-
-	path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (data->entry));
-	if (path == NULL
-	    || G_IS_DIR_SEPARATOR (path[strlen(path)-1])
-	    || (basename = g_path_get_basename(path)) == NULL
-	    || (basename[0] == '.' && basename[1] == '\0')
-	    || (g_file_test(path, G_FILE_TEST_IS_DIR)))
-		file = "attachment";
-	else
-		file = basename;
-
-	gtk_tree_model_foreach((GtkTreeModel *)data->model, entry_changed_update, (void *)file);
-	g_free(path);
-	g_free(basename);
-}
-
-static void
-toggle_changed(GtkWidget *entry, const char *spath, struct _save_data *data)
-{
-        GtkTreePath *path;
-        GtkTreeIter iter;
-
-        path = gtk_tree_path_new_from_string(spath);
-        if (gtk_tree_model_get_iter((GtkTreeModel *)data->model, &iter, path)) {
-		gboolean on;
-
-                gtk_tree_model_get((GtkTreeModel *)data->model, &iter, 0, &on, -1);
-		gtk_tree_store_set(data->model, &iter, 0, !on, -1);
-        }
-
-        gtk_tree_path_free (path);
-}
-
-static void
-save_got_message(CamelFolder *folder, const char *uid, CamelMimeMessage *msg, void *d)
-{
-	struct _save_data *data = d;
-	GtkDialog *dialog;
-	GtkWidget *w, *tree;
-	GtkTreeStore *model;
-	GtkCellRenderer *renderer;
-
-	/* not found, the mailer will show an error box for this */
-	if (msg == NULL) {
-		free_data(data);
-		return;
-	}
-
-	data->msg = msg;
-	camel_object_ref(msg);
-
-	dialog = (GtkDialog *)gtk_dialog_new_with_buttons(_("Save attachments"),
-							  NULL, /* target->parent? */
-							  0,
-							  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-							  GTK_STOCK_SAVE, GTK_RESPONSE_OK,
-							  NULL);
-	w = gtk_file_chooser_button_new (_("Select save base name"), GTK_FILE_CHOOSER_ACTION_OPEN);
-	data->entry = w;
-	g_object_set(w, "filechooser_action", GTK_FILE_CHOOSER_ACTION_SAVE, NULL);
-	gtk_widget_show(w);
-	gtk_box_pack_start((GtkBox *)dialog->vbox, w, FALSE, TRUE, 6);
-
-	g_signal_connect(GTK_FILE_CHOOSER_BUTTON (w), "selection-changed", G_CALLBACK(entry_changed), data);
-
-	model = gtk_tree_store_new(5, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
-	data->model = model;
-	fill_model(msg, model);
-
-	tree = gtk_tree_view_new_with_model((GtkTreeModel *)model);
-	data->tree = tree;
-	gtk_widget_show(tree);
-	gtk_tree_view_expand_all((GtkTreeView *)tree);
-
-	renderer = gtk_cell_renderer_text_new();
-	gtk_tree_view_insert_column_with_attributes((GtkTreeView *)tree, -1,
-						    _("MIME Type"), renderer, "text", 1, NULL);
-	gtk_tree_view_set_expander_column((GtkTreeView *)tree, gtk_tree_view_get_column((GtkTreeView *)tree, 0));
-
-	renderer = gtk_cell_renderer_toggle_new();
-	g_object_set(renderer, "activatable", TRUE, NULL);
-	g_signal_connect(renderer, "toggled", G_CALLBACK(toggle_changed), data);
-
-	gtk_tree_view_insert_column_with_attributes((GtkTreeView *)tree, -1,
-						    _("Save"), renderer, "active", 0, NULL);
-	renderer = gtk_cell_renderer_text_new();
-	gtk_tree_view_insert_column_with_attributes((GtkTreeView *)tree, -1,
-						    _("Name"), renderer, "text", 2, NULL);
-
-	w = g_object_new(gtk_frame_get_type(),
-			 "shadow_type", GTK_SHADOW_NONE,
-			 "label_widget", g_object_new(gtk_label_get_type(),
-						      "label", "<span weight=\"bold\">Attachments</span>",
-						      "use_markup", TRUE,
-						      "xalign", 0.0, NULL),
-			 "child", g_object_new(gtk_alignment_get_type(),
-					       "left_padding", 12,
-					       "top_padding", 6,
-					       "child", g_object_new(gtk_scrolled_window_get_type(),
-								     "hscrollbar_policy", GTK_POLICY_AUTOMATIC,
-								     "vscrollbar_policy", GTK_POLICY_AUTOMATIC,
-								     "shadow_type", GTK_SHADOW_IN,
-								     "child", tree,
-								     NULL),
-					       NULL),
-			 NULL);
-	gtk_widget_show_all(w);
-
-	gtk_box_pack_start((GtkBox *)dialog->vbox, w, TRUE, TRUE, 0);
-	g_signal_connect(dialog, "response", G_CALLBACK(save_response), data);
-	gtk_window_set_default_size((GtkWindow *)dialog, 500, 500);
-	gtk_widget_show((GtkWidget *)dialog);
-}
-
-void
-org_gnome_save_attachments_save(EPlugin *ep, EMMenuTargetSelect *target)
-{
-	struct _save_data *data;
-
-	if (target->uids->len != 1)
-		return;
-
-	data = g_malloc0(sizeof(*data));
-	data->folder = target->folder;
-	camel_object_ref(data->folder);
-	data->uid = g_strdup(target->uids->pdata[0]);
-
-	mail_get_message(data->folder, data->uid, save_got_message, data, mail_msg_unordered_push);
-}
Index: plugins/save-attachments/Makefile.am
===================================================================
--- plugins/save-attachments/Makefile.am	(revision 37527)
+++ plugins/save-attachments/Makefile.am	(working copy)
@@ -1,31 +0,0 @@
-if OS_WIN32
-NO_UNDEFINED_REQUIRED_LIBS =				\
-	$(top_builddir)/mail/libevolution-mail.la	\
-	$(EVOLUTION_MAIL_LIBS)				\
-	$(GNOME_PLATFORM_LIBS)				\
-	$(top_builddir)/e-util/libeutil.la
-endif
-
-INCLUDES =						\
-	-I$(top_srcdir)					\
-	$(EVOLUTION_MAIL_CFLAGS)
-
-@EVO_PLUGIN_RULE@
-
-plugin_DATA = org-gnome-save-attachments.eplug org-gnome-save-attachments.xml
-plugin_LTLIBRARIES = liborg-gnome-save-attachments.la
-
-liborg_gnome_save_attachments_la_SOURCES = save-attachments.c
-liborg_gnome_save_attachments_la_LDFLAGS = -module \
-	-avoid-version $(NO_UNDEFINED)
-
-liborg_gnome_save_attachments_la_LIBADD =	\
-	$(NO_UNDEFINED_REQUIRED_LIBS)
-
-
-EXTRA_DIST = 					\
-	org-gnome-save-attachments.eplug.xml	\
-	org-gnome-save-attachments.xml
-
-BUILT_SOURCES = org-gnome-save-attachments.eplug
-CLEANFILES = $(BUILT_SOURCES)
Index: plugins/save-attachments/org-gnome-save-attachments.eplug.xml
===================================================================
--- plugins/save-attachments/org-gnome-save-attachments.eplug.xml	(revision 37527)
+++ plugins/save-attachments/org-gnome-save-attachments.eplug.xml	(working copy)
@@ -1,26 +0,0 @@
-<?xml version="1.0"?>
-<e-plugin-list>
-  <!-- the path to the shared library -->
-  <e-plugin
-    id="org.gnome.plugin.attachments.save"
-    type="shlib"
-    location="@PLUGINDIR@/liborg-gnome-save-attachments@SOEXT@"
-    _name="Save attachments">
-    
-    <author name="Not Zed" email="NotZed@Ximian.com"/>
-    <_description>A plugin for saving all attachments or parts of a message at once.</_description>
-
-    <hook class="org.gnome.evolution.mail.bonobomenu:1.0">
-      <menu id="org.gnome.evolution.mail.browser" target="select">
-	<!-- the path to the bonobo menu description -->
-	<ui file="@PLUGINDIR@/org-gnome-save-attachments.xml"/>
-	<item
-	  type="item"
-	  verb="EPSASaveAttachments"
-	  path="/commands/EPSASaveAttachments"
-	  enable="one"
-	  activate="org_gnome_save_attachments_save"/>
-      </menu>
-    </hook>
-  </e-plugin>
-</e-plugin-list>
Index: plugins/save-attachments/org-gnome-save-attachments.xml
===================================================================
--- plugins/save-attachments/org-gnome-save-attachments.xml	(revision 37527)
+++ plugins/save-attachments/org-gnome-save-attachments.xml	(working copy)
@@ -1,20 +0,0 @@
-<Root>
-  <commands>
-    <cmd name="EPSASaveAttachments" _label="Save Attachments..."
-      _tip="Save all attachments"
-      pixtype="stock" pixname="Save"/>
-  </commands>
-
-  <menu>
-<!--
-    <placeholder name="MessagePlaceholder">
-      <submenu name="Message">
-        <placeholder name="MailMessageActions">
-	  <separator f="" name="emaillist5"/>
-	  <menuitem name="EPSASaveAttachments" verb=""/>
-        </placeholder>
-   </submenu>
-    </placeholder>
--->
-  </menu>
-</Root>
Index: mail/e-attachment-handler-mail.c
===================================================================
--- mail/e-attachment-handler-mail.c	(revision 0)
+++ mail/e-attachment-handler-mail.c	(revision 0)
@@ -0,0 +1,524 @@
+/*
+ * 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 <glib/gi18n.h>
+#include <camel/camel-folder.h>
+#include <camel/camel-stream-mem.h>
+
+#include "e-util/e-error.h"
+#include "mail/em-composer-utils.h"
+#include "mail/mail-tools.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>";
+
+/* Note: Do not use the info field. */
+static GtkTargetEntry target_table[] = {
+	{ "message/rfc822",	0, 0 },
+	{ "x-uid-list",		0, 0 }
+};
+
+static void
+attachment_handler_mail_forward (GtkAction *action,
+                                 EAttachmentView *view)
+{
+	EAttachment *attachment;
+	CamelMimePart *mime_part;
+	CamelDataWrapper *wrapper;
+	GList *selected;
+
+	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
+attachment_handler_mail_reply_all (GtkAction *action,
+                                   EAttachmentView *view)
+{
+	EAttachment *attachment;
+	CamelMimePart *mime_part;
+	CamelDataWrapper *wrapper;
+	GList *selected;
+
+	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
+attachment_handler_mail_reply_sender (GtkAction *action,
+                                      EAttachmentView *view)
+{
+	EAttachment *attachment;
+	CamelMimePart *mime_part;
+	CamelDataWrapper *wrapper;
+	GList *selected;
+
+	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 (attachment_handler_mail_forward) },
+
+	{ "mail-reply-all",
+	  "mail-reply-all",
+	  N_("Reply to _All"),
+	  NULL,
+	  NULL,  /* XXX Add a tooltip! */
+	  G_CALLBACK (attachment_handler_mail_reply_all) },
+
+	{ "mail-reply-sender",
+	  "mail-reply-sender",
+	  N_("_Reply to Sender"),
+	  NULL,
+	  NULL,  /* XXX Add a tooltip! */
+	  G_CALLBACK (attachment_handler_mail_reply_sender) }
+};
+
+static void
+attachment_handler_mail_message_rfc822 (EAttachmentView *view,
+                                        GdkDragContext *drag_context,
+                                        gint x,
+                                        gint y,
+                                        GtkSelectionData *selection_data,
+                                        guint info,
+                                        guint time)
+{
+	static GdkAtom atom = GDK_NONE;
+	EAttachmentStore *store;
+	EAttachment *attachment;
+	CamelMimeMessage *message;
+	CamelDataWrapper *wrapper;
+	CamelStream *stream;
+	const gchar *data;
+	gboolean success = FALSE;
+	gpointer parent;
+	gint length;
+
+	if (G_UNLIKELY (atom == GDK_NONE))
+		atom = gdk_atom_intern_static_string ("message/rfc822");
+
+	if (gtk_selection_data_get_target (selection_data) != atom)
+		return;
+
+	g_signal_stop_emission_by_name (view, "drag-data-received");
+
+	data = (const gchar *) gtk_selection_data_get_data (selection_data);
+	length = gtk_selection_data_get_length (selection_data);
+
+	stream = camel_stream_mem_new ();
+	camel_stream_write (stream, data, length);
+	camel_stream_reset (stream);
+
+	message = camel_mime_message_new ();
+	wrapper = CAMEL_DATA_WRAPPER (message);
+
+	if (camel_data_wrapper_construct_from_stream (wrapper, stream) == -1)
+		goto exit;
+
+	store = e_attachment_view_get_store (view);
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	attachment = e_attachment_new_for_message (message);
+	e_attachment_store_add_attachment (store, attachment);
+	e_attachment_load_async (
+		attachment, (GAsyncReadyCallback)
+		e_attachment_load_handle_error, parent);
+	g_object_unref (attachment);
+
+	success = TRUE;
+
+exit:
+	camel_object_unref (message);
+	camel_object_unref (stream);
+
+	gtk_drag_finish (drag_context, success, FALSE, time);
+}
+
+static void
+attachment_handler_mail_x_uid_list (EAttachmentView *view,
+                                    GdkDragContext *drag_context,
+                                    gint x,
+                                    gint y,
+                                    GtkSelectionData *selection_data,
+                                    guint info,
+                                    guint time)
+{
+	static GdkAtom atom = GDK_NONE;
+	CamelException ex = CAMEL_EXCEPTION_INITIALISER;
+	CamelDataWrapper *wrapper;
+	CamelMimeMessage *message;
+	CamelMultipart *multipart;
+	CamelMimePart *mime_part;
+	CamelFolder *folder = NULL;
+	EAttachment *attachment;
+	EAttachmentStore *store;
+	GPtrArray *uids;
+	const gchar *data;
+	const gchar *cp, *end;
+	gchar *description;
+	gpointer parent;
+	gint length;
+	guint ii;
+
+	if (G_UNLIKELY (atom == GDK_NONE))
+		atom = gdk_atom_intern_static_string ("x-uid-list");
+
+	if (gtk_selection_data_get_target (selection_data) != atom)
+		return;
+
+	store = e_attachment_view_get_store (view);
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	uids = g_ptr_array_new ();
+
+	data = (const gchar *) gtk_selection_data_get_data (selection_data);
+	length = gtk_selection_data_get_length (selection_data);
+
+	/* The UID list is delimited by NUL characters.
+	 * Brilliant.  So we can't use g_strsplit(). */
+
+	cp = data;
+	end = data + length;
+
+	while (cp < end) {
+		const gchar *start = cp;
+
+		while (cp < end && *cp != '\0')
+			cp++;
+
+		/* Skip the first string. */
+		if (start > data)
+			g_ptr_array_add (uids, g_strndup (start, cp - start));
+
+		cp++;
+	}
+
+	if (uids->len == 0)
+		goto exit;
+
+	/* The first string is the folder URI. */
+	folder = mail_tool_uri_to_folder (data, 0, &ex);
+	if (folder == NULL)
+		goto exit;
+
+	/* Handle one message. */
+	if (uids->len == 1) {
+		message = camel_folder_get_message (
+			folder, uids->pdata[0], &ex);
+		if (message == NULL)
+			goto exit;
+
+		attachment = e_attachment_new_for_message (message);
+		e_attachment_store_add_attachment (store, attachment);
+		e_attachment_load_async (
+			attachment, (GAsyncReadyCallback)
+			e_attachment_load_handle_error, parent);
+		g_object_unref (attachment);
+
+		camel_object_unref (message);
+		goto exit;
+	}
+
+	/* Build a multipart/digest message out of the UIDs. */
+
+	multipart = camel_multipart_new ();
+	wrapper = CAMEL_DATA_WRAPPER (multipart);
+	camel_data_wrapper_set_mime_type (wrapper, "multipart/digest");
+	camel_multipart_set_boundary (multipart, NULL);
+
+	for (ii = 0; ii < uids->len; ii++) {
+		message = camel_folder_get_message (
+			folder, uids->pdata[ii], &ex);
+		if (message == NULL) {
+			camel_object_unref (multipart);
+			goto exit;
+		}
+
+		mime_part = camel_mime_part_new ();
+		wrapper = CAMEL_DATA_WRAPPER (message);
+		camel_mime_part_set_disposition (mime_part, "inline");
+		camel_medium_set_content_object (
+			CAMEL_MEDIUM (mime_part), wrapper);
+		camel_mime_part_set_content_type (mime_part, "message/rfc822");
+		camel_multipart_add_part (multipart, mime_part);
+		camel_object_unref (mime_part);
+
+		camel_object_unref (message);
+	}
+
+	mime_part = camel_mime_part_new ();
+	wrapper = CAMEL_DATA_WRAPPER (multipart);
+	camel_medium_set_content_object (CAMEL_MEDIUM (mime_part), wrapper);
+
+	/* Translators: This is only for multiple messages. */
+	description = g_strdup_printf (_("%d attached messages"), uids->len);
+	camel_mime_part_set_description (mime_part, description);
+	g_free (description);
+
+	attachment = e_attachment_new ();
+	e_attachment_set_mime_part (attachment, mime_part);
+	e_attachment_store_add_attachment (store, attachment);
+	e_attachment_load_async (
+		attachment, (GAsyncReadyCallback)
+		e_attachment_load_handle_error, parent);
+	g_object_unref (attachment);
+
+	camel_object_unref (mime_part);
+	camel_object_unref (multipart);
+
+exit:
+	if (camel_exception_is_set (&ex)) {
+		gchar *folder_name;
+
+		if (folder != NULL)
+			camel_object_get (
+				folder, NULL, CAMEL_FOLDER_NAME,
+				&folder_name, NULL);
+		else
+			folder_name = g_strdup (data);
+
+		e_error_run (
+			parent, "mail-composer:attach-nomessages",
+			folder_name, camel_exception_get_description (&ex),
+			NULL);
+
+		if (folder != NULL)
+			camel_object_free (
+				folder, CAMEL_FOLDER_NAME, folder_name);
+		else
+			g_free (folder_name);
+
+		camel_exception_clear (&ex);
+	}
+
+	if (folder != NULL)
+		camel_object_unref (folder);
+
+	g_ptr_array_free (uids, TRUE);
+
+	g_signal_stop_emission_by_name (view, "drag-data-received");
+}
+
+static void
+attachment_handler_mail_update_actions (EAttachmentView *view)
+{
+	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)
+{
+	EAttachmentHandler *handler;
+	EAttachmentView *view;
+	GtkActionGroup *action_group;
+	GtkUIManager *ui_manager;
+	GError *error = NULL;
+
+	handler = E_ATTACHMENT_HANDLER (object);
+
+	/* Chain up to parent's constructed() method. */
+	G_OBJECT_CLASS (parent_class)->constructed (object);
+
+	view = e_attachment_handler_get_view (handler);
+
+	action_group = e_attachment_view_add_action_group (view, "mail");
+	gtk_action_group_add_actions (
+		action_group, standard_entries,
+		G_N_ELEMENTS (standard_entries), view);
+
+	ui_manager = e_attachment_view_get_ui_manager (view);
+	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),
+		NULL);
+
+	g_signal_connect (
+		view, "drag-data-received",
+		G_CALLBACK (attachment_handler_mail_message_rfc822),
+		NULL);
+
+	g_signal_connect (
+		view, "drag-data-received",
+		G_CALLBACK (attachment_handler_mail_x_uid_list),
+		NULL);
+}
+
+static GdkDragAction
+attachment_handler_mail_get_drag_actions (EAttachmentHandler *handler)
+{
+	return GDK_ACTION_COPY;
+}
+
+static const GtkTargetEntry *
+attachment_handler_mail_get_target_table (EAttachmentHandler *handler,
+                                          guint *n_targets)
+{
+	if (n_targets != NULL)
+		*n_targets = G_N_ELEMENTS (target_table);
+
+	return target_table;
+}
+
+static void
+attachment_handler_mail_class_init (EAttachmentHandlerMailClass *class)
+{
+	GObjectClass *object_class;
+	EAttachmentHandlerClass *handler_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;
+
+	handler_class = E_ATTACHMENT_HANDLER_CLASS (class);
+	handler_class->get_drag_actions = attachment_handler_mail_get_drag_actions;
+	handler_class->get_target_table = attachment_handler_mail_get_target_table;
+}
+
+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;
+}
Index: mail/e-attachment-handler-mail.h
===================================================================
--- mail/e-attachment-handler-mail.h	(revision 0)
+++ mail/e-attachment-handler-mail.h	(revision 0)
@@ -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 */
Index: mail/em-format-html-display.c
===================================================================
--- mail/em-format-html-display.c	(revision 37527)
+++ mail/em-format-html-display.c	(working copy)
@@ -80,15 +80,16 @@
 
 #include "mail-config.h"
 
+#include "e-mail-attachment-bar.h"
 #include "em-format-html-display.h"
 #include "em-marshal.h"
 #include "e-searching-tokenizer.h"
 #include "em-icon-stream.h"
 #include "em-utils.h"
 #include "em-popup.h"
-#include "e-attachment.h"
-#include "e-attachment-bar.h"
 #include "e-icon-entry.h"
+#include "widgets/misc/e-attachment-button.h"
+#include "widgets/misc/e-attachment-view.h"
 
 #ifdef G_OS_WIN32
 /* Undefine the similar macro from <pthread.h>,it doesn't check if
@@ -114,18 +115,7 @@ struct _EMFormatHTMLDisplayPrivate {
 	int search_wrap;	/* are we doing a wrap search */
 	gboolean search_active; /* if the search is active */
 
-	/* for Attachment bar */
-	GtkWidget *attachment_bar;
-	GtkWidget *attachment_box;
-	GtkWidget *label;
-	GtkWidget *save_txt;
-	GtkWidget *arrow;
-	GtkWidget *forward;
-	GtkWidget *down;
-	GtkWidget *attachment_area;
-	gboolean  show_bar;
-	GHashTable *files;
-	gboolean updated;
+	GtkWidget *attachment_view;  /* weak reference */
 };
 
 static int efhd_html_button_press_event (GtkWidget *widget, GdkEventButton *event, EMFormatHTMLDisplay *efh);
@@ -135,8 +125,6 @@ static void efhd_html_on_url (GtkHTML *h
 static void efhd_attachment_frame(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri);
 static gboolean efhd_attachment_image(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject);
 static void efhd_message_add_bar(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info);
-static void efhd_message_update_bar(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info);
-static void efhd_attachment_bar_refresh (EMFormatHTMLDisplay *efhd);
 
 struct _attach_puri {
 	EMFormatPURI puri;
@@ -187,7 +175,6 @@ static void efhd_format_attachment(EMFor
 static void efhd_format_optional(EMFormat *, CamelStream *, CamelMimePart *, CamelStream *);
 static void efhd_format_secure(EMFormat *emf, CamelStream *stream, CamelMimePart *part, CamelCipherValidity *valid);
 static void efhd_complete(EMFormat *);
-gboolean efhd_mnemonic_show_bar (GtkWidget *widget, gboolean focus, GtkWidget *efhd);
 
 static gboolean efhd_bonobo_object(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject); 	 
 static gboolean efhd_use_component(const char *mime_type);
@@ -280,9 +267,6 @@ efhd_init(GObject *o)
 #undef efh
 
 	efhd->nobar = getenv("EVOLUTION_NO_BAR") != NULL;
-
-	efhd->priv->show_bar = FALSE;
-	efhd->priv->files = NULL;
 }
 
 static void
@@ -292,9 +276,6 @@ efhd_finalise(GObject *o)
 
 	/* check pending stuff */
 
-	if (efhd->priv->files)
-		g_hash_table_destroy(efhd->priv->files);
-
 	g_free(efhd->priv->search_text);
 	g_free(efhd->priv);
 
@@ -432,12 +413,6 @@ void em_format_html_display_set_caret_mo
 	gtk_html_set_caret_mode(((EMFormatHTML *)efhd)->html, state);
 }
 
-EAttachmentBar *
-em_format_html_display_get_bar (EMFormatHTMLDisplay *efhd)
-{
-	return E_ATTACHMENT_BAR (efhd->priv->attachment_bar);
-}
-
 void
 em_format_html_display_set_search(EMFormatHTMLDisplay *efhd, int type, GSList *strings)
 {
@@ -934,11 +909,6 @@ efhd_complete(EMFormat *emf)
 
 	if (efhd->priv->search_dialog && efhd->priv->search_active)
 		efhd_update_matches(efhd);
-
-	if (efhd->priv->files) {
-		g_hash_table_destroy (efhd->priv->files);
-		efhd->priv->files = NULL;
-	}
 }
 
 /* ********************************************************************** */
@@ -1294,9 +1264,7 @@ static EMFormatHandler type_builtin_tabl
 	{ "image/pjpeg", (EMFormatFunc)efhd_image },
 
 	{ "x-evolution/message/prefix", (EMFormatFunc)efhd_message_prefix },
-	{ "x-evolution/message/post-header", (EMFormatFunc)efhd_message_add_bar },
-	{ "x-evolution/message/post-header-closure", (EMFormatFunc)efhd_message_update_bar },
-
+	{ "x-evolution/message/post-header", (EMFormatFunc)efhd_message_add_bar }
 };
 
 static void
@@ -1344,15 +1312,8 @@ static const EMFormatHandler *efhd_find_
 
 static void efhd_format_clone(EMFormat *emf, CamelFolder *folder, const char *uid, CamelMimeMessage *msg, EMFormat *src)
 {
-	EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *) emf;
-
-	if (emf != src) {
-		if (src)
-			efhd->priv->show_bar = ((EMFormatHTMLDisplay *)src)->priv->show_bar;
-		else
-			efhd->priv->show_bar = FALSE;
+	if (emf != src)
 		((EMFormatHTML *) emf)->header_wrap_flags = 0;
-	}
 
 	((EMFormatClass *)efhd_parent)->format_clone(emf, folder, uid, msg, src);
 }
@@ -1468,12 +1429,14 @@ efhd_attachment_show(EPopup *ep, EPopupI
 }
 
 static void
-efhd_attachment_button_show(GtkWidget *w, void *data)
+efhd_attachment_button_expanded (GtkWidget *widget,
+                                 GParamSpec *pspec,
+                                 struct _attach_puri *info)
 {
-	if (!efhd_can_process_attachment (w))
+	if (!efhd_can_process_attachment (widget))
 		return;
 
-	efhd_attachment_show(NULL, NULL, data);
+	efhd_attachment_show (NULL, NULL, info);
 }
 
 static void
@@ -1812,16 +1775,12 @@ static gboolean
 efhd_attachment_button(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject)
 {
 	EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *)efh;
-	EAttachment *new;
 	struct _attach_puri *info;
-	GtkWidget *hbox, *w, *button, *mainbox;
-	char *simple_type, *tmp, *new_file = NULL;
-	const char *file;
-	GtkTargetEntry drag_types[] = {
-		{ NULL, 0, 0 },
-		{ "text/uri-list", 0, 1 },
-	};
-	AtkObject *a11y;
+	EAttachmentView *view;
+	EAttachmentStore *store;
+	EAttachment *attachment;
+	GtkWidget *widget;
+	gpointer parent;
 
 	/* FIXME: handle default shown case */
 	d(printf("adding attachment button/content\n"));
@@ -1833,139 +1792,36 @@ efhd_attachment_button(EMFormatHTML *efh
 		return TRUE;
 	}
 
-	if (efhd->priv->attachment_bar) {
-		file = camel_mime_part_get_filename(info->puri.part);
-
-		new = info->attachment;
-
-		if (!file) {
-			file = "attachment.dat";
-			new->file_name = g_strdup(file);
-		}
-
-		tmp = g_hash_table_lookup (efhd->priv->files, file);
-		if (tmp) {
-			guint count = GPOINTER_TO_UINT(tmp);
-			char *ext;
-			char *tmp_file = g_strdup (file);
-
-			if ((ext = strrchr(tmp_file, '.'))) {
-				ext[0] = 0;
-				new_file = g_strdup_printf("%s(%d).%s", tmp_file, count++, ext+1);
-			} else {
-				new_file = g_strdup_printf("%s(%d)", tmp_file, count++);
-			}
-
-			g_free (tmp_file);
-			g_hash_table_insert (efhd->priv->files, g_strdup(file), GUINT_TO_POINTER(count));
-			g_free (new->file_name);
-			new->file_name = new_file;
-		} else {
-			g_hash_table_insert (efhd->priv->files, g_strdup(file), GUINT_TO_POINTER(1));
-		}
-
-		/* Store the status of encryption / signature on the attachment for emblem display
-		 * FIXME: May not work well always
-		 */
-		new->sign = info->sign;
-		new->encrypt = info->encrypt;
-
-		/* Add the attachment to the bar.*/
-		e_attachment_bar_add_attachment_silent (E_ATTACHMENT_BAR(efhd->priv->attachment_bar), new);
-		efhd_attachment_bar_refresh(efhd);
-	}
-
-	mainbox = gtk_hbox_new(FALSE, 0);
-
-	button = gtk_button_new();
-
-	if (info->handle) {
-		g_signal_connect(button, "clicked", G_CALLBACK(efhd_attachment_button_show), info);
-		g_object_set_data (G_OBJECT (button), "efh", efh);
-	} else {
-		gtk_widget_set_sensitive(button, FALSE);
-		GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
-	}
-
-	hbox = gtk_hbox_new(FALSE, 2);
-	info->forward = gtk_image_new_from_stock(GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_BUTTON);
-	gtk_box_pack_start((GtkBox *)hbox, info->forward, TRUE, TRUE, 0);
-	if (info->handle) {
-		info->down = gtk_image_new_from_stock(GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_BUTTON);
-		gtk_box_pack_start((GtkBox *)hbox, info->down, TRUE, TRUE, 0);
-	}
-
-	w = gtk_image_new();
-	gtk_widget_set_size_request(w, 24, 24);
-	gtk_box_pack_start((GtkBox *)hbox, w, TRUE, TRUE, 0);
-	gtk_container_add((GtkContainer *)button, hbox);
-	gtk_box_pack_start((GtkBox *)mainbox, button, TRUE, TRUE, 0);
-
-	/* Check for snooped type to get the right icon/processing */
-	if (info->snoop_mime_type)
-		simple_type = g_strdup(info->snoop_mime_type);
-	else
-		simple_type = camel_content_type_simple (((CamelDataWrapper *)pobject->part)->mime_type);
-	camel_strdown(simple_type);
-
-	/* FIXME: offline parts, just get icon */
-	if (camel_content_type_is(((CamelDataWrapper *)pobject->part)->mime_type, "image", "*")) {
-		EMFormatHTMLJob *job;
-		GdkPixbuf *mini;
-		char *key;
-
-		key = pobject->classid;
-		mini = em_icon_stream_get_image(key, 24, 24);
-		if (mini) {
-			d(printf("got image from cache '%s'\n", key));
-			gtk_image_set_from_pixbuf((GtkImage *)w, mini);
-			g_object_unref(mini);
-		} else {
-			d(printf("need to create icon image '%s'\n", key));
-			job = em_format_html_job_new(efh, efhd_write_icon_job, pobject);
-			job->stream = (CamelStream *)em_icon_stream_new((GtkImage *)w, key, 24, 24, FALSE);
-			em_format_html_job_queue(efh, job);
-		}
-	} else {
-		GdkPixbuf *pixbuf, *mini;
-
-		if ((pixbuf = e_icon_for_mime_type (simple_type, 24))) {
-			if ((mini = e_icon_factory_pixbuf_scale (pixbuf, 24, 24))) {
-				gtk_image_set_from_pixbuf ((GtkImage *) w, mini);
-				g_object_unref (mini);
-			}
-			g_object_unref (pixbuf);
-		}
-	}
-
-	drag_types[0].target = simple_type;
-	gtk_drag_source_set(button, GDK_BUTTON1_MASK, drag_types, sizeof(drag_types)/sizeof(drag_types[0]), GDK_ACTION_COPY);
-	g_signal_connect(button, "drag-data-get", G_CALLBACK(efhd_drag_data_get), pobject);
-	g_signal_connect (button, "drag-data-delete", G_CALLBACK(efhd_drag_data_delete), pobject);
-	g_free(simple_type);
-
-	button = gtk_button_new();
-	/*GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);*/
-	gtk_container_add((GtkContainer *)button, gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE));
-
-	a11y = gtk_widget_get_accessible (button);
-	atk_object_set_name (a11y, _("Attachment"));
-
-	g_signal_connect(button, "button_press_event", G_CALLBACK(efhd_attachment_popup), info);
-	g_signal_connect(button, "popup_menu", G_CALLBACK(efhd_attachment_popup_menu), info);
-	g_signal_connect(button, "clicked", G_CALLBACK(efhd_attachment_popup_menu), info);
-	gtk_box_pack_start((GtkBox *)mainbox, button, TRUE, TRUE, 0);
-
-	g_object_set_data (G_OBJECT (button), "efh", efh);
-
-	gtk_widget_show_all(mainbox);
-
-	if (info->shown)
-		gtk_widget_hide(info->forward);
-	else if (info->down)
-		gtk_widget_hide(info->down);
-
-	gtk_container_add((GtkContainer *)eb, mainbox);
+	attachment = info->attachment;
+	e_attachment_set_shown (attachment, info->shown);
+	e_attachment_set_signed (attachment, info->sign);
+	e_attachment_set_encrypted (attachment, info->encrypt);
+	e_attachment_set_can_show (attachment, info->handle != NULL);
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (efh->html));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	view = E_ATTACHMENT_VIEW (efhd->priv->attachment_view);
+	gtk_widget_show (efhd->priv->attachment_view);
+
+	store = e_attachment_view_get_store (view);
+	e_attachment_store_add_attachment (store, info->attachment);
+
+	e_attachment_load_async (
+		info->attachment, (GAsyncReadyCallback)
+		e_attachment_load_handle_error, parent);
+
+	widget = e_attachment_button_new (view);
+	e_attachment_button_set_attachment (
+		E_ATTACHMENT_BUTTON (widget), attachment);
+	gtk_container_add (GTK_CONTAINER (eb), widget);
+	gtk_widget_show (widget);
+
+	g_object_set_data (G_OBJECT (widget), "efh", efh);
+
+	g_signal_connect (
+		widget, "notify::expanded",
+		G_CALLBACK (efhd_attachment_button_expanded), info);
 
 	return TRUE;
 }
@@ -2139,330 +1995,57 @@ type_ok: 	 
 }
 
 static void
-attachment_bar_arrow_clicked(GtkWidget *w, EMFormatHTMLDisplay *efhd)
+efhd_bar_resize (EMFormatHTML *efh,
+                 GtkAllocation *event)
 {
+	EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *) efh;
+	GtkWidget *widget;
+	gint width;
 
-	efhd->priv->show_bar = !efhd->priv->show_bar;
+	widget = GTK_WIDGET (efh->html);
+	width = widget->allocation.width - 12;
 
-	if (efhd->priv->show_bar) {
-		gtk_widget_show(efhd->priv->attachment_box);
-		gtk_widget_show(efhd->priv->down);
-		gtk_widget_hide(efhd->priv->forward);
-	} else {
-		gtk_widget_hide(efhd->priv->attachment_box);
-		gtk_widget_show(efhd->priv->forward);
-		gtk_widget_hide(efhd->priv->down);
+	if (width > 0) {
+		widget = efhd->priv->attachment_view;
+		gtk_widget_set_size_request (widget, width, -1);
 	}
 }
 
-static void
-attachments_save_all_clicked (GtkWidget *widget, EMFormatHTMLDisplay *efhd)
-{
-	GSList *attachment_parts;
-	guint n_attachment_parts;
-
-	attachment_parts = e_attachment_bar_get_parts (
-		E_ATTACHMENT_BAR (efhd->priv->attachment_bar));
-	n_attachment_parts = g_slist_length (attachment_parts);
-	g_return_if_fail (n_attachment_parts > 0);
-
-	if (n_attachment_parts == 1)
-		em_utils_save_part (
-			widget, _("Save attachment as"),
-			attachment_parts->data);
-	else
-		em_utils_save_parts (
-			widget, _("Select folder to save all attachments"),
-			attachment_parts);
-
-        g_slist_free (attachment_parts);
-}
-
-static void
-efhd_bar_popup_position(GtkMenu *menu, int *x, int *y, gboolean *push_in, gpointer user_data)
-{
-	EAttachmentBar *bar = user_data;
-	GnomeIconList *icon_list = user_data;
-	GList *selection;
-	GnomeCanvasPixbuf *image;
-
-	gdk_window_get_origin (((GtkWidget*) bar)->window, x, y);
-
-	selection = gnome_icon_list_get_selection (icon_list);
-	if (selection == NULL)
-		return;
-
-	image = gnome_icon_list_get_icon_pixbuf_item (icon_list, GPOINTER_TO_INT(selection->data));
-	if (image == NULL)
-		return;
-
-	/* Put menu to the center of icon. */
-	*x += (int)(image->item.x1 + image->item.x2) / 2;
-	*y += (int)(image->item.y1 + image->item.y2) / 2;
-}
-
-static void
-efhd_bar_save_selected(EPopup *ep, EPopupItem *item, void *data)
-{
-	EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *)data;
-	GSList *attachment_parts, *tmp;
-	GSList *parts = NULL;
-
-	attachment_parts = e_attachment_bar_get_selected(E_ATTACHMENT_BAR(efhd->priv->attachment_bar));
-
-	for (tmp = attachment_parts; tmp; tmp=tmp->next)
-		parts = g_slist_prepend(parts, ((EAttachment *)tmp->data)->body);
-
-	parts = g_slist_reverse(parts);
-	em_utils_save_parts(efhd->priv->attachment_bar, _("Select folder to save selected attachments..."), parts);
-        g_slist_free (parts);
-
-	g_slist_foreach(attachment_parts, (GFunc)g_object_unref, NULL);
-	g_slist_free (attachment_parts);
-}
-
-static EPopupItem efhd_bar_menu_items[] = {
-	{ E_POPUP_BAR, "05.display", },
-	{ E_POPUP_ITEM, "05.display.01", N_("_Save Selected..."), efhd_bar_save_selected, NULL, NULL, EM_POPUP_ATTACHMENTS_MULTIPLE},
-};
-
-static gboolean
-efhd_bar_button_press_event(EAttachmentBar *bar, GdkEventButton *event, EMFormat *emf)
-{
-	GtkMenu *menu;
-	GSList *list=NULL;
-	EPopupTarget *target;
-	EMPopup *emp;
-	GSList *menus = NULL;
-	int i;
-
-	if (event && event->button != 3)
-		return FALSE;
-
-	/** @HookPoint-EMPopup: Attachment Bar Context Menu
-	 * @Id: org.gnome.evolution.mail.attachments.popup
-	 * @Class: org.gnome.evolution.mail.popup:1.0
-	 * @Target: EMPopupTargetPart
-	 *
-	 * This is the drop-down menu shown when a user clicks on the attachment bar
-	 * when attachments are selected.
-	 */
-	emp = em_popup_new("org.gnome.evolution.mail.attachments.popup");
-
-	/* Add something like save-selected, foward selected attachments in a mail etc....*/
-	list = e_attachment_bar_get_selected(bar);
-
-	/* Lets not propagate any more the r-click which is intended to us*/
-	if ( g_slist_length (list) == 0)
-		return TRUE;
-
-	target = (EPopupTarget *)em_popup_target_new_attachments(emp, list);
-	for (i=0; i<2; i++)
-		menus = g_slist_prepend(menus, &efhd_bar_menu_items[i]);
-	e_popup_add_items((EPopup *)emp, menus, NULL, efhd_menu_items_free, emf);
-
-	((EMPopupTargetPart *)target)->target.widget = (GtkWidget *)bar;
-	menu = e_popup_create_menu_once((EPopup *)emp, (EPopupTarget *)target, 0);
-	if (event)
-		gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button, event->time);
-	else
-		gtk_menu_popup(menu, NULL, NULL, (GtkMenuPositionFunc)efhd_bar_popup_position, bar, 0, gtk_get_current_event_time());
-
-	return TRUE;
-}
-
 static gboolean
-efhd_bar_popup_menu_event (EAttachmentBar *bar, EMFormat *emf)
+efhd_add_bar (EMFormatHTML *efh,
+              GtkHTMLEmbedded *eb,
+              EMFormatHTMLPObject *pobject)
 {
-	return efhd_bar_button_press_event(bar, NULL, emf);
-}
-
-static void
-efhd_attachment_bar_refresh (EMFormatHTMLDisplay *efhd)
-{
-	int nattachments;
-
-	if (!efhd->priv->attachment_bar)
-		return;
-
-	nattachments = e_attachment_bar_get_num_attachments (E_ATTACHMENT_BAR(efhd->priv->attachment_bar));
-	if (nattachments) {
-		char *txt;
-
-		/* Cant i put in the number of attachments here ?*/
-		txt = g_strdup_printf(ngettext("%d at_tachment", "%d at_tachments", nattachments), nattachments);
-		gtk_label_set_text_with_mnemonic ((GtkLabel *)efhd->priv->label, txt);
-		g_free (txt);
-
-		/* Show the bar even when the first attachment is added */
-		if (nattachments == 1) {
-			gtk_widget_show_all (efhd->priv->attachment_area);
-			gtk_label_set_text_with_mnemonic ((GtkLabel *)efhd->priv->save_txt, _("S_ave"));
-
-			if (efhd->priv->show_bar) {
-				gtk_widget_show(efhd->priv->down);
-				gtk_widget_hide(efhd->priv->forward);
-			} else {
-				gtk_widget_show(efhd->priv->forward);
-				gtk_widget_hide(efhd->priv->down);
-				gtk_widget_hide(efhd->priv->attachment_box);
-			}
-		} else if (nattachments > 1) {
-			gtk_label_set_text_with_mnemonic ((GtkLabel *)efhd->priv->save_txt, _("S_ave All"));
-		}
-	}
-}
-
-static void
-efhd_bar_resize(GtkWidget *w, GtkAllocation *event, EMFormatHTML *efh)
-{
-	int width;
-	GtkRequisition req;
 	EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *) efh;
+	GtkWidget *widget;
 
-	gtk_widget_size_request (efhd->priv->attachment_bar, &req);
-	width = ((GtkWidget *) efh->html)->allocation.width - 16;
-
-	/* Update the width of the bar when the width is greater than 1*/
-	if (width > 0)
-		e_attachment_bar_set_width(E_ATTACHMENT_BAR(efhd->priv->attachment_bar), width);
-}
-
-static gboolean
-efhd_bar_scroll_event(GtkWidget *w, GdkEventScroll *event, EMFormatHTMLDisplay *efhd)
-{
-	gboolean ret;
-
-	/* Emulate the scroll over the attachment bar, as if it is scrolled in the window.
-	*  It doesnt go automatically since the GnomeIconList is a layout by itself
-	*/
-	g_signal_emit_by_name (gtk_widget_get_parent((GtkWidget *)efhd->formathtml.html), "scroll_event", event, &ret);
-
-	return TRUE;
-}
-
-gboolean
-efhd_mnemonic_show_bar (GtkWidget *widget, gboolean focus, GtkWidget *efhd)
-{
-	attachment_bar_arrow_clicked (NULL, (EMFormatHTMLDisplay *)efhd);
-
-	return TRUE;
-}
-
-static gboolean
-efhd_update_bar(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject)
-{
-	EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *)efh;
-	struct _EMFormatHTMLDisplayPrivate *priv = efhd->priv;
-
-	if (priv->attachment_bar)
-		e_attachment_bar_refresh (E_ATTACHMENT_BAR (priv->attachment_bar));
-
-	return TRUE;
-}
-
-static gboolean
-efhd_add_bar(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject)
-{
-	EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *)efh;
-	struct _EMFormatHTMLDisplayPrivate *priv = efhd->priv;
-	GtkWidget *hbox1, *hbox2, *hbox3, *vbox, *txt, *image, *save, *scroll;
-	int width, height, bar_width;
-
-	priv->attachment_bar = e_attachment_bar_new(NULL);
-	scroll = gtk_scrolled_window_new (NULL, NULL);
-	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
-	((EAttachmentBar *)priv->attachment_bar)->expand = TRUE;
-
-	priv->forward = gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
-	priv->down = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
-	hbox3 = gtk_hbox_new (FALSE, 0);
-	gtk_box_pack_start ((GtkBox *)hbox3, priv->forward, FALSE, FALSE, 0);
-	gtk_box_pack_start ((GtkBox *)hbox3, priv->down, FALSE, FALSE, 0);
-	priv->arrow = (GtkWidget *)gtk_tool_button_new(hbox3, NULL);
-	g_signal_connect (priv->arrow, "mnemonic_activate", G_CALLBACK (efhd_mnemonic_show_bar), efh);
-	atk_object_set_name (gtk_widget_get_accessible (priv->arrow), _("Show Attachments"));
-
-	priv->label = gtk_label_new(_("No Attachment"));
-	gtk_label_set_mnemonic_widget (GTK_LABEL (priv->label), priv->arrow);
-	save = gtk_button_new();
-	image = gtk_image_new_from_stock ("gtk-save", GTK_ICON_SIZE_BUTTON);
-	txt = gtk_label_new_with_mnemonic(_("S_ave"));
-	priv->save_txt = txt;
-	hbox1 = gtk_hbox_new(FALSE, 0);
-	gtk_box_pack_start((GtkBox *)hbox1, image, FALSE, FALSE, 2);
-	gtk_box_pack_start((GtkBox *)hbox1, txt, FALSE, FALSE, 0);
-
-	gtk_container_add((GtkContainer *)save, hbox1);
-
-	hbox2 = gtk_hbox_new (FALSE, 0);
-	gtk_box_pack_start ((GtkBox *)hbox2, priv->arrow, FALSE, FALSE, 0);
-	gtk_box_pack_start ((GtkBox *)hbox2, priv->label, FALSE, FALSE, 2);
-	gtk_box_pack_start ((GtkBox *)hbox2, save, FALSE, FALSE, 2);
-
-	priv->attachment_box = scroll;
-	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN);
-	gtk_container_add ((GtkContainer *)priv->attachment_box, priv->attachment_bar);
-
-	gtk_widget_get_size_request(priv->attachment_bar, &width, &height);
-
-	/* FIXME: What if the text is more?. Should we reduce the text with appending ...?
-	 * or resize the bar? How to figure out that, it needs more space? */
-	bar_width = ((GtkWidget *)efh->html)->parent->allocation.width - /* FIXME */16;
-	gtk_widget_set_size_request (priv->attachment_bar,
-				     bar_width > 0 ? bar_width : 0,
-				     84 /* FIXME: Default show only one row, Dont hardcode size*/);
-
-	vbox = gtk_vbox_new (FALSE, 0);
-	gtk_box_pack_start ((GtkBox *)vbox, hbox2, FALSE, FALSE, 2);
-	gtk_box_pack_start ((GtkBox *)vbox, priv->attachment_box, TRUE, TRUE, 2);
-
-	gtk_container_add ((GtkContainer *)eb, vbox);
-	gtk_widget_show ((GtkWidget *)eb);
-
-	/* Lets hide it by default and show only when there are attachments */
-	priv->attachment_area = vbox;
-	gtk_widget_hide_all (priv->attachment_area);
-
-	g_signal_connect (priv->arrow, "clicked", G_CALLBACK(attachment_bar_arrow_clicked), efh);
-	g_signal_connect (priv->attachment_bar, "button_press_event", G_CALLBACK(efhd_bar_button_press_event), efhd);
-	g_signal_connect (priv->attachment_bar, "popup-menu", G_CALLBACK(efhd_bar_popup_menu_event), efhd);
-	g_signal_connect (save, "clicked", G_CALLBACK(attachments_save_all_clicked), efh);
-	g_signal_connect (eb, "size_allocate", G_CALLBACK (efhd_bar_resize), efh);
-	g_signal_connect (priv->attachment_bar, "scroll_event", G_CALLBACK(efhd_bar_scroll_event), efhd);
+	widget = e_mail_attachment_bar_new ();
+	gtk_container_add (GTK_CONTAINER (eb), widget);
+	efhd->priv->attachment_view = widget;
+	gtk_widget_hide (widget);
+
+	g_signal_connect_swapped (
+		eb, "size-allocate",
+		G_CALLBACK (efhd_bar_resize), efh);
 
 	return TRUE;
 }
-static void
-efhd_message_update_bar(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
-{
-	EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *) emf;
-	const char *classid = "attachment-bar-refresh";
-
-	if (efhd->nobar || efhd->priv->updated)
-		return;
-
-	efhd->priv->files = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-	efhd->priv->updated = TRUE;
-	em_format_html_add_pobject((EMFormatHTML *)emf, sizeof(EMFormatHTMLPObject), classid, part, efhd_update_bar);
-	camel_stream_printf(stream, "<td><object classid=\"%s\"></object></td>", classid);
-
-}
 
 static void
-efhd_message_add_bar(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
+efhd_message_add_bar (EMFormat *emf,
+                      CamelStream *stream,
+                      CamelMimePart *part,
+                      const EMFormatHandler *info)
 {
-	EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *) emf;
 	const char *classid = "attachment-bar";
 
-	if (efhd->nobar || efhd->priv->files)
-		return;
-
-	efhd->priv->files = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-	efhd->priv->updated = FALSE;
+	em_format_html_add_pobject (
+		(EMFormatHTML *) emf,
+		sizeof (EMFormatHTMLPObject),
+		classid, part, efhd_add_bar);
 
-	em_format_html_add_pobject((EMFormatHTML *)emf, sizeof(EMFormatHTMLPObject), classid, part, efhd_add_bar);
-	camel_stream_printf(stream, "<td><object classid=\"%s\"></object></td>", classid);
+	camel_stream_printf (
+		stream, "<td><object classid=\"%s\"></object></td>", classid);
 }
 
 static void
@@ -2471,43 +2054,50 @@ efhd_format_attachment(EMFormat *emf, Ca
 	char *classid, *text, *html;
 	struct _attach_puri *info;
 
-	classid = g_strdup_printf("attachment%s", emf->part_id->str);
-	info = (struct _attach_puri *)em_format_add_puri(emf, sizeof(*info), classid, part, efhd_attachment_frame);
-	em_format_html_add_pobject((EMFormatHTML *)emf, sizeof(EMFormatHTMLPObject), classid, part, efhd_attachment_button);
+	classid = g_strdup_printf ("attachment%s", emf->part_id->str);
+	info = (struct _attach_puri *)em_format_add_puri (
+		emf, sizeof (*info), classid, part, efhd_attachment_frame);
+	em_format_html_add_pobject (
+		(EMFormatHTML *) emf, sizeof (EMFormatHTMLPObject),
+		classid, part, efhd_attachment_button);
 	info->handle = handle;
-	info->shown = em_format_is_inline(emf, info->puri.part_id, info->puri.part, handle);
+	info->shown = em_format_is_inline (
+		emf, info->puri.part_id, info->puri.part, handle);
 	info->snoop_mime_type = emf->snoop_mime_type;
-	info->attachment = e_attachment_new_from_mime_part (info->puri.part);
-	e_attachment_bar_create_attachment_cache (info->attachment);
+	info->attachment = e_attachment_new ();
+	e_attachment_set_mime_part (info->attachment, info->puri.part);
 
 	if (emf->valid) {
 		info->sign = emf->valid->sign.status;
 		info->encrypt = emf->valid->encrypt.status;
 	}
 
-	camel_stream_write_string(stream,
-				  EM_FORMAT_HTML_VPAD
-				  "<table cellspacing=0 cellpadding=0><tr><td>"
-				  "<table width=10 cellspacing=0 cellpadding=0>"
-				  "<tr><td></td></tr></table></td>");
-
-	camel_stream_printf(stream, "<td><object classid=\"%s\"></object></td>", classid);
-
-	camel_stream_write_string(stream,
-				  "<td><table width=3 cellspacing=0 cellpadding=0>"
-				  "<tr><td></td></tr></table></td><td><font size=-1>");
+	camel_stream_write_string (
+		stream, EM_FORMAT_HTML_VPAD
+		"<table cellspacing=0 cellpadding=0><tr><td>"
+		"<table width=10 cellspacing=0 cellpadding=0>"
+		"<tr><td></td></tr></table></td>");
+
+	camel_stream_printf (
+		stream, "<td><object classid=\"%s\"></object></td>", classid);
+
+	camel_stream_write_string (
+		stream, "<td><table width=3 cellspacing=0 cellpadding=0>"
+		"<tr><td></td></tr></table></td><td><font size=-1>");
 
 	/* output some info about it */
 	/* FIXME: should we look up mime_type from object again? */
-	text = em_format_describe_part(part, mime_type);
-	html = camel_text_to_html(text, ((EMFormatHTML *)emf)->text_html_flags & CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
-	camel_stream_write_string(stream, html);
-	g_free(html);
-	g_free(text);
-
-	camel_stream_write_string(stream,
-				  "</font></td></tr><tr></table>\n"
-				  EM_FORMAT_HTML_VPAD);
+	text = em_format_describe_part (part, mime_type);
+	html = camel_text_to_html (
+		text, ((EMFormatHTML *)emf)->text_html_flags &
+		CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
+	camel_stream_write_string (stream, html);
+	g_free (html);
+	g_free (text);
+
+	camel_stream_write_string (
+		stream, "</font></td></tr><tr></table>\n"
+		EM_FORMAT_HTML_VPAD);
 
 	if (handle) {
 		if (info->shown)
@@ -2654,7 +2244,8 @@ efhd_format_optional(EMFormat *emf, Came
 	info->handle = em_format_find_handler(emf, "text/plain");
 	info->shown = FALSE;
 	info->snoop_mime_type = "text/plain";
-	info->attachment = e_attachment_new_from_mime_part (info->puri.part);
+	info->attachment = e_attachment_new ();
+	e_attachment_set_mime_part (info->attachment, info->puri.part);
 	info->mstream = (CamelStreamMem *)mstream;
 	if (emf->valid) {
 		info->sign = emf->valid->sign.status;
Index: mail/em-format-html-display.h
===================================================================
--- mail/em-format-html-display.h	(revision 37527)
+++ mail/em-format-html-display.h	(working copy)
@@ -26,7 +26,6 @@
 #define _EM_FORMAT_HTML_DISPLAY_H
 
 #include "mail/em-format-html.h"
-#include "widgets/misc/e-attachment-bar.h"
 
 typedef struct _EMFormatHTMLDisplay EMFormatHTMLDisplay;
 typedef struct _EMFormatHTMLDisplayClass EMFormatHTMLDisplayClass;
@@ -82,7 +81,6 @@ void em_format_html_display_paste (EMFor
 void em_format_html_display_zoom_in (EMFormatHTMLDisplay *efhd);
 void em_format_html_display_zoom_out (EMFormatHTMLDisplay *efhd);
 void em_format_html_display_zoom_reset (EMFormatHTMLDisplay *efhd);
-EAttachmentBar *em_format_html_display_get_bar (EMFormatHTMLDisplay *efhd);
 
 gboolean em_format_html_display_popup_menu (EMFormatHTMLDisplay *efhd);
 
Index: mail/Makefile.am
===================================================================
--- mail/Makefile.am	(revision 37527)
+++ mail/Makefile.am	(working copy)
@@ -54,6 +54,7 @@ idl_DATA = $(MAIL_IDL)
 # plugin mail api
 mailinclude_HEADERS =				\
 	$(MAIL_IDL_GENERATED_H)			\
+	e-mail-attachment-bar.h			\
 	em-composer-utils.h			\
 	em-config.h				\
 	em-event.h				\
@@ -94,6 +95,9 @@ libevolution_mail_la_SOURCES =			\
 	$(MAIL_IDL_GENERATED)			\
 	$(MARSHAL_GENERATED)			\
 	$(mailinclude_HEADERS)			\
+	e-attachment-handler-mail.c		\
+	e-attachment-handler-mail.h		\
+	e-mail-attachment-bar.c			\
 	e-searching-tokenizer.c			\
 	e-searching-tokenizer.h			\
 	em-account-editor.c			\
Index: mail/e-mail-attachment-bar.c
===================================================================
--- mail/e-mail-attachment-bar.c	(revision 0)
+++ mail/e-mail-attachment-bar.c	(revision 0)
@@ -0,0 +1,751 @@
+/*
+ * e-mail-attachment-bar.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-mail-attachment-bar.h"
+
+#include <glib/gi18n.h>
+
+#include "e-util/e-binding.h"
+#include "e-util/gconf-bridge.h"
+
+#include "e-attachment-store.h"
+#include "e-attachment-icon-view.h"
+#include "e-attachment-tree-view.h"
+
+#define E_MAIL_ATTACHMENT_BAR_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_MAIL_ATTACHMENT_BAR, EMailAttachmentBarPrivate))
+
+#define NUM_VIEWS 2
+
+struct _EMailAttachmentBarPrivate {
+	GtkTreeModel *model;
+	GtkWidget *vbox;
+	GtkWidget *expander;
+	GtkWidget *combo_box;
+	GtkWidget *icon_view;
+	GtkWidget *tree_view;
+	GtkWidget *icon_frame;
+	GtkWidget *tree_frame;
+	GtkWidget *status_icon;
+	GtkWidget *status_label;
+	GtkWidget *save_all_button;
+	GtkWidget *save_one_button;
+
+	gint active_view;
+	guint expanded : 1;
+};
+
+enum {
+	PROP_0,
+	PROP_ACTIVE_VIEW,
+	PROP_EDITABLE,
+	PROP_EXPANDED
+};
+
+static gpointer parent_class;
+
+static void
+mail_attachment_bar_sync_icon_view (EMailAttachmentBar *bar)
+{
+	EAttachmentView *source;
+	EAttachmentView *target;
+
+	source = E_ATTACHMENT_VIEW (bar->priv->tree_view);
+	target = E_ATTACHMENT_VIEW (bar->priv->icon_view);
+
+	/* Only sync if the tree view is active.  This prevents the
+	 * two views from endlessly trying to sync with each other. */
+	if (e_mail_attachment_bar_get_active_view (bar) == 1)
+		e_attachment_view_sync_selection (source, target);
+}
+
+static void
+mail_attachment_bar_sync_tree_view (EMailAttachmentBar *bar)
+{
+	EAttachmentView *source;
+	EAttachmentView *target;
+
+	source = E_ATTACHMENT_VIEW (bar->priv->icon_view);
+	target = E_ATTACHMENT_VIEW (bar->priv->tree_view);
+
+	/* Only sync if the icon view is active.  This prevents the
+	 * two views from endlessly trying to sync with each other. */
+	if (e_mail_attachment_bar_get_active_view (bar) == 0)
+		e_attachment_view_sync_selection (source, target);
+}
+
+static void
+mail_attachment_bar_update_status (EMailAttachmentBar *bar)
+{
+	EAttachmentView *view;
+	EAttachmentStore *store;
+	GtkActivatable *activatable;
+	GtkExpander *expander;
+	GtkAction *action;
+	GtkLabel *label;
+	gint num_attachments;
+	guint64 total_size;
+	gchar *display_size;
+	gchar *markup;
+
+	view = E_ATTACHMENT_VIEW (bar);
+	store = e_attachment_view_get_store (view);
+	expander = GTK_EXPANDER (bar->priv->expander);
+	label = GTK_LABEL (bar->priv->status_label);
+
+	num_attachments = e_attachment_store_get_num_attachments (store);
+	total_size = e_attachment_store_get_total_size (store);
+	display_size = g_format_size_for_display (total_size);
+
+	markup = g_strdup_printf (
+		"<b>%d</b> %s (%s)", num_attachments, ngettext (
+		"Attachment", "Attachments", num_attachments),
+		display_size);
+	gtk_label_set_markup (label, markup);
+	g_free (markup);
+
+	activatable = GTK_ACTIVATABLE (bar->priv->save_all_button);
+	action = gtk_activatable_get_related_action (activatable);
+	gtk_action_set_visible (action, (num_attachments > 1));
+
+	activatable = GTK_ACTIVATABLE (bar->priv->save_one_button);
+	action = gtk_activatable_get_related_action (activatable);
+	gtk_action_set_visible (action, (num_attachments == 1));
+
+	g_free (display_size);
+}
+
+static void
+mail_attachment_bar_set_property (GObject *object,
+                                  guint property_id,
+                                  const GValue *value,
+                                  GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ACTIVE_VIEW:
+			e_mail_attachment_bar_set_active_view (
+				E_MAIL_ATTACHMENT_BAR (object),
+				g_value_get_int (value));
+			return;
+
+		case PROP_EDITABLE:
+			e_attachment_view_set_editable (
+				E_ATTACHMENT_VIEW (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_EXPANDED:
+			e_mail_attachment_bar_set_expanded (
+				E_MAIL_ATTACHMENT_BAR (object),
+				g_value_get_boolean (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_attachment_bar_get_property (GObject *object,
+                                  guint property_id,
+                                  GValue *value,
+                                  GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ACTIVE_VIEW:
+			g_value_set_int (
+				value,
+				e_mail_attachment_bar_get_active_view (
+				E_MAIL_ATTACHMENT_BAR (object)));
+			return;
+
+		case PROP_EDITABLE:
+			g_value_set_boolean (
+				value,
+				e_attachment_view_get_editable (
+				E_ATTACHMENT_VIEW (object)));
+			return;
+
+		case PROP_EXPANDED:
+			g_value_set_boolean (
+				value,
+				e_mail_attachment_bar_get_expanded (
+				E_MAIL_ATTACHMENT_BAR (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_attachment_bar_dispose (GObject *object)
+{
+	EMailAttachmentBarPrivate *priv;
+
+	priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (object);
+
+	if (priv->model != NULL) {
+		g_object_unref (priv->model);
+		priv->model = NULL;
+	}
+
+	if (priv->vbox != NULL) {
+		g_object_unref (priv->vbox);
+		priv->vbox = NULL;
+	}
+
+	if (priv->expander != NULL) {
+		g_object_unref (priv->expander);
+		priv->expander = NULL;
+	}
+
+	if (priv->combo_box != NULL) {
+		g_object_unref (priv->combo_box);
+		priv->combo_box = NULL;
+	}
+
+	if (priv->icon_view != NULL) {
+		g_object_unref (priv->icon_view);
+		priv->icon_view = NULL;
+	}
+
+	if (priv->tree_view != NULL) {
+		g_object_unref (priv->tree_view);
+		priv->tree_view = NULL;
+	}
+
+	if (priv->icon_frame != NULL) {
+		g_object_unref (priv->icon_frame);
+		priv->icon_frame = NULL;
+	}
+
+	if (priv->tree_frame != NULL) {
+		g_object_unref (priv->tree_frame);
+		priv->tree_frame = NULL;
+	}
+
+	if (priv->status_icon != NULL) {
+		g_object_unref (priv->status_icon);
+		priv->status_icon = NULL;
+	}
+
+	if (priv->status_label != NULL) {
+		g_object_unref (priv->status_label);
+		priv->status_label = NULL;
+	}
+
+	if (priv->save_all_button != NULL) {
+		g_object_unref (priv->save_all_button);
+		priv->save_all_button = NULL;
+	}
+
+	if (priv->save_one_button != NULL) {
+		g_object_unref (priv->save_one_button);
+		priv->save_one_button = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+mail_attachment_bar_constructed (GObject *object)
+{
+	EMailAttachmentBarPrivate *priv;
+	GConfBridge *bridge;
+	const gchar *key;
+
+	priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (object);
+
+	bridge = gconf_bridge_get ();
+
+	/* Set up property-to-property bindings. */
+
+	e_mutual_binding_new (
+		G_OBJECT (object), "active-view",
+		G_OBJECT (priv->combo_box), "active");
+
+	e_mutual_binding_new (
+		G_OBJECT (object), "editable",
+		G_OBJECT (priv->icon_view), "editable");
+
+	e_mutual_binding_new (
+		G_OBJECT (object), "editable",
+		G_OBJECT (priv->tree_view), "editable");
+
+	e_mutual_binding_new (
+		G_OBJECT (object), "expanded",
+		G_OBJECT (priv->expander), "expanded");
+
+	e_mutual_binding_new (
+		G_OBJECT (object), "expanded",
+		G_OBJECT (priv->combo_box), "visible");
+
+	e_mutual_binding_new (
+		G_OBJECT (object), "expanded",
+		G_OBJECT (priv->vbox), "visible");
+
+	/* Set up property-to-GConf bindings. */
+
+	key = "/apps/evolution/shell/attachment_view";
+	gconf_bridge_bind_property (bridge, key, object, "active-view");
+}
+
+static void
+mail_attachment_bar_size_request (GtkWidget *widget,
+                                  GtkRequisition *requisition)
+{
+	/* 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;
+		return;
+	}
+
+	/* Chain up to parent's size_request() method. */
+	GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
+}
+
+static EAttachmentViewPrivate *
+mail_attachment_bar_get_private (EAttachmentView *view)
+{
+	EMailAttachmentBarPrivate *priv;
+
+	priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	return e_attachment_view_get_private (view);
+}
+
+static EAttachmentStore *
+mail_attachment_bar_get_store (EAttachmentView *view)
+{
+	EMailAttachmentBarPrivate *priv;
+
+	priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	return e_attachment_view_get_store (view);
+}
+
+static GtkTreePath *
+mail_attachment_bar_get_path_at_pos (EAttachmentView *view,
+                                     gint x,
+                                     gint y)
+{
+	EMailAttachmentBarPrivate *priv;
+
+	priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	return e_attachment_view_get_path_at_pos (view, x, y);
+}
+
+static GList *
+mail_attachment_bar_get_selected_paths (EAttachmentView *view)
+{
+	EMailAttachmentBarPrivate *priv;
+
+	priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	return e_attachment_view_get_selected_paths (view);
+}
+
+static gboolean
+mail_attachment_bar_path_is_selected (EAttachmentView *view,
+                                      GtkTreePath *path)
+{
+	EMailAttachmentBarPrivate *priv;
+
+	priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	return e_attachment_view_path_is_selected (view, path);
+}
+
+static void
+mail_attachment_bar_select_path (EAttachmentView *view,
+                                 GtkTreePath *path)
+{
+	EMailAttachmentBarPrivate *priv;
+
+	priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	e_attachment_view_select_path (view, path);
+}
+
+static void
+mail_attachment_bar_unselect_path (EAttachmentView *view,
+                                   GtkTreePath *path)
+{
+	EMailAttachmentBarPrivate *priv;
+
+	priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	e_attachment_view_unselect_path (view, path);
+}
+
+static void
+mail_attachment_bar_select_all (EAttachmentView *view)
+{
+	EMailAttachmentBarPrivate *priv;
+
+	priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	e_attachment_view_select_all (view);
+}
+
+static void
+mail_attachment_bar_unselect_all (EAttachmentView *view)
+{
+	EMailAttachmentBarPrivate *priv;
+
+	priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	e_attachment_view_unselect_all (view);
+}
+
+static void
+mail_attachment_bar_update_actions (EAttachmentView *view)
+{
+	EMailAttachmentBarPrivate *priv;
+
+	priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	e_attachment_view_update_actions (view);
+}
+
+static void
+mail_attachment_bar_class_init (EMailAttachmentBarClass *class)
+{
+	GObjectClass *object_class;
+	GtkWidgetClass *widget_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EMailAttachmentBarPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = mail_attachment_bar_set_property;
+	object_class->get_property = mail_attachment_bar_get_property;
+	object_class->dispose = mail_attachment_bar_dispose;
+	object_class->constructed = mail_attachment_bar_constructed;
+
+	widget_class = GTK_WIDGET_CLASS (class);
+	widget_class->size_request = mail_attachment_bar_size_request;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ACTIVE_VIEW,
+		g_param_spec_int (
+			"active-view",
+			"Active View",
+			NULL,
+			0,
+			NUM_VIEWS,
+			0,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_EXPANDED,
+		g_param_spec_boolean (
+			"expanded",
+			"Expanded",
+			NULL,
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	g_object_class_override_property (
+		object_class, PROP_EDITABLE, "editable");
+}
+
+static void
+mail_attachment_bar_iface_init (EAttachmentViewIface *iface)
+{
+	iface->get_private = mail_attachment_bar_get_private;
+	iface->get_store = mail_attachment_bar_get_store;
+	iface->get_path_at_pos = mail_attachment_bar_get_path_at_pos;
+	iface->get_selected_paths = mail_attachment_bar_get_selected_paths;
+	iface->path_is_selected = mail_attachment_bar_path_is_selected;
+	iface->select_path = mail_attachment_bar_select_path;
+	iface->unselect_path = mail_attachment_bar_unselect_path;
+	iface->select_all = mail_attachment_bar_select_all;
+	iface->unselect_all = mail_attachment_bar_unselect_all;
+	iface->update_actions = mail_attachment_bar_update_actions;
+}
+
+static void
+mail_attachment_bar_init (EMailAttachmentBar *bar)
+{
+	EAttachmentView *view;
+	GtkTreeSelection *selection;
+	GtkSizeGroup *size_group;
+	GtkWidget *container;
+	GtkWidget *widget;
+	GtkAction *action;
+
+	bar->priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (bar);
+	bar->priv->model = e_attachment_store_new ();
+
+	gtk_box_set_spacing (GTK_BOX (bar), 6);
+
+	/* Keep the expander label and save button the same height. */
+	size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
+
+	/* Construct the Attachment Views */
+
+	container = GTK_WIDGET (bar);
+
+	widget = gtk_vbox_new (FALSE, 0);
+	gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	bar->priv->vbox = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	container = bar->priv->vbox;
+
+	widget = gtk_frame_new (NULL);
+	gtk_frame_set_shadow_type (GTK_FRAME (widget), GTK_SHADOW_IN);
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	bar->priv->icon_frame = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = e_attachment_icon_view_new ();
+	GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
+	gtk_icon_view_set_model (GTK_ICON_VIEW (widget), bar->priv->model);
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	bar->priv->icon_view = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	container = bar->priv->vbox;
+
+	widget = gtk_frame_new (NULL);
+	gtk_frame_set_shadow_type (GTK_FRAME (widget), GTK_SHADOW_IN);
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	bar->priv->tree_frame = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = e_attachment_tree_view_new ();
+	GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
+	gtk_tree_view_set_model (GTK_TREE_VIEW (widget), bar->priv->model);
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	bar->priv->tree_view = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	/* Construct the Controls */
+
+	container = GTK_WIDGET (bar);
+
+	widget = gtk_hbox_new (FALSE, 12);
+	gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_expander_new (NULL);
+	gtk_expander_set_spacing (GTK_EXPANDER (widget), 0);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	bar->priv->expander = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	/* The "Save All" button proxies the "save-all" action from
+	 * one of the two attachment views.  Doesn't matter which. */
+	widget = gtk_button_new ();
+	view = E_ATTACHMENT_VIEW (bar->priv->icon_view);
+	action = e_attachment_view_get_action (view, "save-all");
+	gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new ());
+	gtk_activatable_set_related_action (GTK_ACTIVATABLE (widget), action);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	bar->priv->save_all_button = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	/* Same deal with the "Save" button. */
+	widget = gtk_button_new ();
+	view = E_ATTACHMENT_VIEW (bar->priv->icon_view);
+	action = e_attachment_view_get_action (view, "save-one");
+	gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new ());
+	gtk_activatable_set_related_action (GTK_ACTIVATABLE (widget), action);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	bar->priv->save_one_button = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	widget = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_combo_box_new_text ();
+	gtk_size_group_add_widget (size_group, widget);
+	gtk_combo_box_append_text (GTK_COMBO_BOX (widget), _("Icon View"));
+	gtk_combo_box_append_text (GTK_COMBO_BOX (widget), _("List View"));
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	bar->priv->combo_box = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	container = bar->priv->expander;
+
+	widget = gtk_hbox_new (FALSE, 6);
+	gtk_size_group_add_widget (size_group, widget);
+	gtk_expander_set_label_widget (GTK_EXPANDER (container), widget);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_image_new_from_icon_name (
+		"mail-attachment", GTK_ICON_SIZE_MENU);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	bar->priv->status_icon = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	widget = gtk_label_new (NULL);
+	gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	bar->priv->status_label = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	selection = gtk_tree_view_get_selection (
+		GTK_TREE_VIEW (bar->priv->tree_view));
+
+	g_signal_connect_swapped (
+		selection, "changed",
+		G_CALLBACK (mail_attachment_bar_sync_icon_view), bar);
+
+	g_signal_connect_swapped (
+		bar->priv->icon_view, "selection-changed",
+		G_CALLBACK (mail_attachment_bar_sync_tree_view), bar);
+
+	g_signal_connect_swapped (
+		bar->priv->model, "notify::num-attachments",
+		G_CALLBACK (mail_attachment_bar_update_status), bar);
+
+	g_signal_connect_swapped (
+		bar->priv->model, "notify::total-size",
+		G_CALLBACK (mail_attachment_bar_update_status), bar);
+
+	g_object_unref (size_group);
+}
+
+GType
+e_mail_attachment_bar_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EMailAttachmentBarClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) mail_attachment_bar_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EMailAttachmentBar),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) mail_attachment_bar_init,
+			NULL   /* value_table */
+		};
+
+		static const GInterfaceInfo iface_info = {
+			(GInterfaceInitFunc) mail_attachment_bar_iface_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL   /* interface_data */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_VBOX, "EMailAttachmentBar", &type_info, 0);
+
+		g_type_add_interface_static (
+			type, E_TYPE_ATTACHMENT_VIEW, &iface_info);
+	}
+
+	return type;
+}
+
+GtkWidget *
+e_mail_attachment_bar_new (void)
+{
+	return g_object_new (
+		E_TYPE_MAIL_ATTACHMENT_BAR,
+		"editable", FALSE, NULL);
+}
+
+gint
+e_mail_attachment_bar_get_active_view (EMailAttachmentBar *bar)
+{
+	g_return_val_if_fail (E_IS_MAIL_ATTACHMENT_BAR (bar), 0);
+
+	return bar->priv->active_view;
+}
+
+void
+e_mail_attachment_bar_set_active_view (EMailAttachmentBar *bar,
+                                       gint active_view)
+{
+	g_return_if_fail (E_IS_MAIL_ATTACHMENT_BAR (bar));
+	g_return_if_fail (active_view >= 0 && active_view < NUM_VIEWS);
+
+	bar->priv->active_view = active_view;
+
+	if (active_view == 0) {
+		gtk_widget_show (bar->priv->icon_frame);
+		gtk_widget_hide (bar->priv->tree_frame);
+	} else {
+		gtk_widget_hide (bar->priv->icon_frame);
+		gtk_widget_show (bar->priv->tree_frame);
+	}
+
+	g_object_notify (G_OBJECT (bar), "active-view");
+}
+
+gboolean
+e_mail_attachment_bar_get_expanded (EMailAttachmentBar *bar)
+{
+	g_return_val_if_fail (E_IS_MAIL_ATTACHMENT_BAR (bar), FALSE);
+
+	return bar->priv->expanded;
+}
+
+void
+e_mail_attachment_bar_set_expanded (EMailAttachmentBar *bar,
+                                    gboolean expanded)
+{
+	g_return_if_fail (E_IS_MAIL_ATTACHMENT_BAR (bar));
+
+	bar->priv->expanded = expanded;
+
+	g_object_notify (G_OBJECT (bar), "expanded");
+}
Index: mail/e-mail-attachment-bar.h
===================================================================
--- mail/e-mail-attachment-bar.h	(revision 0)
+++ mail/e-mail-attachment-bar.h	(revision 0)
@@ -0,0 +1,77 @@
+/*
+ * e-mail-attachment-bar.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_MAIL_ATTACHMENT_BAR_H
+#define E_MAIL_ATTACHMENT_BAR_H
+
+#include <gtk/gtk.h>
+#include <widgets/misc/e-attachment-view.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MAIL_ATTACHMENT_BAR \
+	(e_mail_attachment_bar_get_type ())
+#define E_MAIL_ATTACHMENT_BAR(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_MAIL_ATTACHMENT_BAR, EMailAttachmentBar))
+#define E_MAIL_ATTACHMENT_BAR_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_MAIL_ATTACHMENT_BAR, EMailAttachmentBarClass))
+#define E_IS_MAIL_ATTACHMENT_BAR(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_MAIL_ATTACHMENT_BAR))
+#define E_IS_MAIL_ATTACHMENT_BAR_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_MAIL_ATTACHMENT_BAR))
+#define E_MAIL_ATTACHMENT_BAR_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_MAIL_ATTACHMENT_BAR, EMailAttachmentBarClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMailAttachmentBar EMailAttachmentBar;
+typedef struct _EMailAttachmentBarClass EMailAttachmentBarClass;
+typedef struct _EMailAttachmentBarPrivate EMailAttachmentBarPrivate;
+
+struct _EMailAttachmentBar {
+	GtkVBox parent;
+	EMailAttachmentBarPrivate *priv;
+};
+
+struct _EMailAttachmentBarClass {
+	GtkVBoxClass parent_class;
+};
+
+GType		e_mail_attachment_bar_get_type	(void);
+GtkWidget *	e_mail_attachment_bar_new	(void);
+gint		e_mail_attachment_bar_get_active_view
+						(EMailAttachmentBar *bar);
+void		e_mail_attachment_bar_set_active_view
+						(EMailAttachmentBar *bar,
+						 gint active_view);
+gboolean	e_mail_attachment_bar_get_expanded
+						(EMailAttachmentBar *bar);
+void		e_mail_attachment_bar_set_expanded
+						(EMailAttachmentBar *bar,
+						 gboolean expanded);
+
+G_END_DECLS
+
+#endif /* E_MAIL_ATTACHMENT_BAR_H */
Index: mail/em-popup.c
===================================================================
--- mail/em-popup.c	(revision 37527)
+++ mail/em-popup.c	(working copy)
@@ -100,12 +100,6 @@ emp_target_free(EPopup *ep, EPopupTarget
 
 		g_free(s->uri);
 		break; }
-	case EM_POPUP_TARGET_ATTACHMENTS: {
-		EMPopupTargetAttachments *s = (EMPopupTargetAttachments *)t;
-
-		g_slist_foreach(s->attachments, (GFunc)g_object_unref, NULL);
-		g_slist_free(s->attachments);
-		break; }
 	}
 
 	((EPopupClass *)emp_parent)->target_free(ep, t);
@@ -361,42 +355,6 @@ done:
 	return t;
 }
 
-/**
- * em_popup_target_new_attachments:
- * @emp:
- * @attachments: A list of EMsgComposerAttachment objects, reffed for
- * the list.  Will be unreff'd once finished with.
- *
- * Owns the list @attachments and their items after they're passed in.
- *
- * Return value:
- **/
-EMPopupTargetAttachments *
-em_popup_target_new_attachments(EMPopup *emp, GSList *attachments)
-{
-	EMPopupTargetAttachments *t = e_popup_target_new(&emp->popup, EM_POPUP_TARGET_ATTACHMENTS, sizeof(*t));
-	guint32 mask = ~0;
-	int len = g_slist_length(attachments);
-
-	t->attachments = attachments;
-	if (len > 0)
-		mask &= ~ EM_POPUP_ATTACHMENTS_MANY;
-	if (len == 1 && ((EAttachment *)attachments->data)->is_available_local) {
-
-		if (camel_content_type_is(((CamelDataWrapper *) ((EAttachment *) attachments->data)->body)->mime_type, "image", "*"))
-			mask &= ~ EM_POPUP_ATTACHMENTS_IMAGE;
-		if (CAMEL_IS_MIME_MESSAGE(camel_medium_get_content_object((CamelMedium *) ((EAttachment *) attachments->data)->body)))
-			mask &= ~EM_POPUP_ATTACHMENTS_MESSAGE;
-
-		mask &= ~ EM_POPUP_ATTACHMENTS_ONE;
-	}
-	if (len > 1)
-		mask &= ~ EM_POPUP_ATTACHMENTS_MULTIPLE;
-	t->target.mask = mask;
-
-	return t;
-}
-
 /* ********************************************************************** */
 
 static void
@@ -405,11 +363,7 @@ emp_part_popup_saveas(EPopup *ep, EPopup
 	EPopupTarget *t = ep->target;
 	CamelMimePart *part = NULL;
 
-	/* If it is of type EM_POPUP_TARGET_ATTACHMENTS, we can assume the length is one. */
-	if (t->type == EM_POPUP_TARGET_ATTACHMENTS)
-		part = ((EAttachment *) ((EMPopupTargetAttachments *) t)->attachments->data)->body;
-	else
-		part = ((EMPopupTargetPart *) t)->part;
+	part = ((EMPopupTargetPart *) t)->part;
 
 	em_utils_save_part(ep->target->widget, _("Save As..."), part);
 }
@@ -423,10 +377,7 @@ emp_part_popup_set_background(EPopup *ep
 	unsigned int i=1;
 	CamelMimePart *part = NULL;
 
-	if (t->type == EM_POPUP_TARGET_ATTACHMENTS)
-		part = ((EAttachment *) ((EMPopupTargetAttachments *) t)->attachments->data)->body;
-	else
-		part = ((EMPopupTargetPart *) t)->part;
+	part = ((EMPopupTargetPart *) t)->part;
 
 	if (!part)
 		return;
@@ -497,10 +448,7 @@ emp_part_popup_reply_sender(EPopup *ep, 
 	CamelMimeMessage *message;
 	CamelMimePart *part;
 
-	if (t->type == EM_POPUP_TARGET_ATTACHMENTS)
-		part = ((EAttachment *) ((EMPopupTargetAttachments *) t)->attachments->data)->body;
-	else
-		part = ((EMPopupTargetPart *) t)->part;
+	part = ((EMPopupTargetPart *) t)->part;
 
 	message = (CamelMimeMessage *)camel_medium_get_content_object((CamelMedium *)part);
 	em_utils_reply_to_message(NULL, NULL, message, REPLY_MODE_SENDER, NULL);
@@ -513,10 +461,7 @@ emp_part_popup_reply_list (EPopup *ep, E
 	CamelMimeMessage *message;
 	CamelMimePart *part;
 
-	if (t->type == EM_POPUP_TARGET_ATTACHMENTS)
-		part = ((EAttachment *) ((EMPopupTargetAttachments *) t)->attachments->data)->body;
-	else
-		part = ((EMPopupTargetPart *) t)->part;
+	part = ((EMPopupTargetPart *) t)->part;
 
 	message = (CamelMimeMessage *)camel_medium_get_content_object((CamelMedium *)part);
 	em_utils_reply_to_message(NULL, NULL, message, REPLY_MODE_LIST, NULL);
@@ -529,10 +474,7 @@ emp_part_popup_reply_all (EPopup *ep, EP
 	CamelMimeMessage *message;
 	CamelMimePart *part;
 
-	if (t->type == EM_POPUP_TARGET_ATTACHMENTS)
-		part = ((EAttachment *) ((EMPopupTargetAttachments *) t)->attachments->data)->body;
-	else
-		part = ((EMPopupTargetPart *) t)->part;
+	part = ((EMPopupTargetPart *) t)->part;
 
 	message = (CamelMimeMessage *)camel_medium_get_content_object((CamelMedium *)part);
 	em_utils_reply_to_message(NULL, NULL, message, REPLY_MODE_ALL, NULL);
@@ -545,10 +487,7 @@ emp_part_popup_forward (EPopup *ep, EPop
 	CamelMimeMessage *message;
 	CamelMimePart *part;
 
-	if (t->type == EM_POPUP_TARGET_ATTACHMENTS)
-		part = ((EAttachment *) ((EMPopupTargetAttachments *) t)->attachments->data)->body;
-	else
-		part = ((EMPopupTargetPart *) t)->part;
+	part = ((EMPopupTargetPart *) t)->part;
 
 	/* TODO: have a emfv specific override so we can get the parent folder uri */
 	message = (CamelMimeMessage *)camel_medium_get_content_object((CamelMedium *) part);
@@ -566,17 +505,6 @@ static EMPopupItem emp_standard_object_p
 	{ E_POPUP_ITEM, "20.part.00", N_("_Forward"), emp_part_popup_forward, NULL, "mail-forward", EM_POPUP_PART_MESSAGE },
 };
 
-static EMPopupItem emp_attachment_object_popups[] = {
-	{ E_POPUP_ITEM, "00.attach.00", N_("_Save As..."), emp_part_popup_saveas, NULL, "document-save-as", 0 },
-	{ E_POPUP_ITEM, "00.attach.10", N_("Set as _Background"), emp_part_popup_set_background, NULL, NULL, EM_POPUP_ATTACHMENTS_IMAGE },
-	{ E_POPUP_BAR, "05.attach", NULL, NULL, NULL, NULL, EM_POPUP_ATTACHMENTS_MESSAGE },
-	{ E_POPUP_ITEM, "05.attach.00", N_("_Reply to sender"), emp_part_popup_reply_sender, NULL, "mail-reply-sender" , EM_POPUP_ATTACHMENTS_MESSAGE },
-	{ E_POPUP_ITEM, "05.attach.01", N_("Reply to _List"), emp_part_popup_reply_list, NULL, NULL, EM_POPUP_ATTACHMENTS_MESSAGE},
-	{ E_POPUP_ITEM, "05.attach.03", N_("Reply to _All"), emp_part_popup_reply_all, NULL, "mail-reply-all", EM_POPUP_ATTACHMENTS_MESSAGE},
-	{ E_POPUP_BAR, "05.attach.10", NULL, NULL, NULL, NULL, EM_POPUP_ATTACHMENTS_MESSAGE },
-	{ E_POPUP_ITEM, "05.attach.15", N_("_Forward"), emp_part_popup_forward, NULL, "mail-forward", EM_POPUP_ATTACHMENTS_MESSAGE },
-};
-
 static const EPopupItem emp_standard_part_apps_bar = { E_POPUP_BAR, "99.object" };
 
 /* ********************************************************************** */
@@ -634,10 +562,7 @@ emp_apps_open_in(EPopup *ep, EPopupItem 
 	EPopupTarget *target = ep->target;
 	CamelMimePart *part;
 
-	if (target->type == EM_POPUP_TARGET_ATTACHMENTS)
-		part = ((EAttachment *) ((EMPopupTargetAttachments *) target)->attachments->data)->body;
-	else
-		part = ((EMPopupTargetPart *) target)->part;
+	part = ((EMPopupTargetPart *) target)->part;
 
 	path = em_utils_temp_save_part(target->widget, part, TRUE);
 	if (path) {
@@ -705,10 +630,7 @@ emp_add_vcard (EPopup *ep, EPopupItem *i
 	CamelStreamMem *mem;
 	
 
-	if (target->type == EM_POPUP_TARGET_ATTACHMENTS)
-		part = ((EAttachment *) ((EMPopupTargetAttachments *) target)->attachments->data)->body;
-	else
-		part = ((EMPopupTargetPart *) target)->part;
+	part = ((EMPopupTargetPart *) target)->part;
 
 	if (!part)
 		return;
@@ -762,25 +684,6 @@ emp_standard_menu_factory(EPopup *emp, v
 		items = emp_standard_object_popups;
 		len = LEN(emp_standard_object_popups);
 		break; }
-	case EM_POPUP_TARGET_ATTACHMENTS: {
-		EMPopupTargetAttachments *t = (EMPopupTargetAttachments *)emp->target;
-		GSList *list = t->attachments;
-		EAttachment *attachment;
-
-		if (g_slist_length(list) != 1 || !((EAttachment *)list->data)->is_available_local) {
-			items = NULL;
-			len = 0;
-			break;
-		}
-
-		/* Only one attachment selected */
-		attachment = list->data;
-		mime_type = camel_data_wrapper_get_mime_type((CamelDataWrapper *)attachment->body);
-		filename = camel_mime_part_get_filename(attachment->body);
-
-		items = emp_attachment_object_popups;
-		len = LEN(emp_attachment_object_popups);
-		break; }
 	default:
 		items = NULL;
 		len = 0;
@@ -942,21 +845,11 @@ static const EPopupHookTargetMask emph_f
 	{ NULL }
 };
 
-static const EPopupHookTargetMask emph_attachments_masks[] = {
-	{ "one", EM_POPUP_ATTACHMENTS_ONE },
-	{ "many", EM_POPUP_ATTACHMENTS_MANY },
-	{ "multiple", EM_POPUP_ATTACHMENTS_MULTIPLE },
-	{ "image", EM_POPUP_ATTACHMENTS_IMAGE },
-	{ "message", EM_POPUP_ATTACHMENTS_MESSAGE },
-	{ NULL }
-};
-
 static const EPopupHookTargetMap emph_targets[] = {
 	{ "select", EM_POPUP_TARGET_SELECT, emph_select_masks },
 	{ "uri", EM_POPUP_TARGET_URI, emph_uri_masks },
 	{ "part", EM_POPUP_TARGET_PART, emph_part_masks },
 	{ "folder", EM_POPUP_TARGET_FOLDER, emph_folder_masks },
-	{ "attachments", EM_POPUP_TARGET_ATTACHMENTS, emph_attachments_masks },
 	{ NULL }
 };
 
Index: mail/em-popup.h
===================================================================
--- mail/em-popup.h	(revision 37527)
+++ mail/em-popup.h	(working copy)
@@ -43,7 +43,6 @@ typedef struct _EMPopupClass EMPopupClas
  * @EM_POPUP_TARGET_URI: A URI.
  * @EM_POPUP_TARGET_PART: A CamelMimePart message part.
  * @EM_POPUP_TARGET_FOLDER: A folder URI.
- * @EM_POPUP_TARGET_ATTACHMENTS: A list of attachments.
  *
  * Defines the value of the targetid for all EMPopup target types.
  **/
@@ -51,8 +50,7 @@ enum _em_popup_target_t {
 	EM_POPUP_TARGET_SELECT,
 	EM_POPUP_TARGET_URI,
 	EM_POPUP_TARGET_PART,
-	EM_POPUP_TARGET_FOLDER,
-	EM_POPUP_TARGET_ATTACHMENTS,
+	EM_POPUP_TARGET_FOLDER
 };
 
 /**
@@ -158,26 +156,10 @@ enum _em_popup_target_folder_t {
 	EM_POPUP_FOLDER_NONSTATIC = 1<<6, /* Except static folders like Outbox.*/
 };
 
-/**
- * enum _em_popup_target_attachments_t - EMPopupTargetAttachments qualifiers.
- *
- * @EM_POPUP_ATTACHMENTS_ONE: There is one and only one attachment selected.
- * @EM_POPUP_ATTACHMENTS_MANY: There is one or more attachments selected.
- *
- **/
-enum _em_popup_target_attachments_t {
-	EM_POPUP_ATTACHMENTS_ONE = 1<<0, /* only 1 selected */
-	EM_POPUP_ATTACHMENTS_MANY = 1<<1, /* one or more selected */
-	EM_POPUP_ATTACHMENTS_MULTIPLE = 1<<2, /* More than 1 selected */
-	EM_POPUP_ATTACHMENTS_IMAGE = 1<<3, /* Image selected */
-	EM_POPUP_ATTACHMENTS_MESSAGE = 1<<4 /* Message selected */
-};
-
 typedef struct _EMPopupTargetSelect EMPopupTargetSelect;
 typedef struct _EMPopupTargetURI EMPopupTargetURI;
 typedef struct _EMPopupTargetPart EMPopupTargetPart;
 typedef struct _EMPopupTargetFolder EMPopupTargetFolder;
-typedef struct _EMPopupTargetAttachments EMPopupTargetAttachments;
 
 /**
  * struct _EMPopupTargetURI - An inline URI.
@@ -241,20 +223,6 @@ struct _EMPopupTargetFolder {
 	char *uri;
 };
 
-/**
- * struct _EMPopupTargetAttachments - A list of composer attachments.
- *
- * @target: Superclass.
- * @attachments: A GSList list of EMsgComposer attachments.
- *
- * This target is used to represent a selected list of attachments in
- * the message composer attachment area.
- **/
-struct _EMPopupTargetAttachments {
-	EPopupTarget target;
-	GSList *attachments;
-};
-
 typedef struct _EPopupItem EMPopupItem;
 
 /* The object */
@@ -276,7 +244,6 @@ EMPopupTargetURI *em_popup_target_new_ur
 EMPopupTargetSelect *em_popup_target_new_select(EMPopup *emp, struct _CamelFolder *folder, const char *folder_uri, GPtrArray *uids);
 EMPopupTargetPart *em_popup_target_new_part(EMPopup *emp, struct _CamelMimePart *part, const char *mime_type);
 EMPopupTargetFolder *em_popup_target_new_folder(EMPopup *emp, const char *uri, guint32 info_flags, guint32 popup_flags);
-EMPopupTargetAttachments *em_popup_target_new_attachments(EMPopup *emp, GSList *attachments);
 
 /* ********************************************************************** */
 
Index: mail/mail-component.c
===================================================================
--- mail/mail-component.c	(revision 37527)
+++ mail/mail-component.c	(working copy)
@@ -92,6 +92,8 @@
 
 #include "e-util/e-non-intrusive-error-dialog.h"
 
+#include "e-attachment-handler-mail.h"
+
 #define MAILER_ERROR_LEVEL_KEY "/apps/evolution/mail/display/error_level"
 #define MAILER_ERROR_TIME_OUT_KEY "/apps/evolution/mail/display/error_timeout"
 
@@ -1259,6 +1261,9 @@ mail_component_class_init (MailComponent
 	epv->setLineStatus	     = impl_setLineStatus;
 
 	mepv->test = impl_mail_test;
+
+	/* Register attachment handler types. */
+	e_attachment_handler_mail_get_type ();
 }
 
 static void
Index: configure.in
===================================================================
--- configure.in	(revision 37527)
+++ configure.in	(working copy)
@@ -1775,12 +1775,12 @@ plugins_base_always="calendar-file calen
 plugins_base="$plugins_base_always $SA_JUNK_PLUGIN $BF_JUNK_PLUGIN $EXCHANGE_PLUGIN $MONO_PLUGIN " 
 all_plugins_base="$plugins_base_always sa-junk-plugin bogo-junk-plugin exchange-operations mono"
 
-plugins_standard_always="bbdb subject-thread save-calendar select-one-source copy-tool mail-to-task audio-inline mailing-list-actions default-mailer import-ics-attachments prefer-plain mail-notification attachment-reminder face backup-restore email-custom-header templates pst-import" 
+plugins_standard_always="bbdb subject-thread save-calendar select-one-source copy-tool mail-to-task audio-inline mailing-list-actions default-mailer prefer-plain mail-notification attachment-reminder face backup-restore email-custom-header templates pst-import" 
 
 plugins_standard="$plugins_standard_always"
 all_plugins_standard="$plugins_standard"
 
-plugins_experimental_always="folder-unsubscribe mail-to-meeting save-attachments external-editor hula-account-setup"
+plugins_experimental_always="folder-unsubscribe mail-to-meeting external-editor hula-account-setup"
 plugins_experimental="$plugins_experimental_always $IPOD_SYNC $TNEF_ATTACHMENTS $PYTHON_PLUGIN"
 all_plugins_experimental="$plugins_experimental_always ipod-sync tnef-attachments"
 
@@ -2081,7 +2081,6 @@ plugins/mail-to-meeting/Makefile
 plugins/mail-to-task/Makefile
 plugins/mono/Makefile
 plugins/subject-thread/Makefile
-plugins/save-attachments/Makefile
 plugins/save-calendar/Makefile
 plugins/select-one-source/Makefile
 plugins/prefer-plain/Makefile
@@ -2106,7 +2105,6 @@ plugins/sa-junk-plugin/Makefile
 plugins/bogo-junk-plugin/Makefile
 plugins/ipod-sync/Makefile
 plugins/publish-calendar/Makefile
-plugins/import-ics-attachments/Makefile
 plugins/imap-features/Makefile
 plugins/tnef-attachments/Makefile
 plugins/templates/Makefile
Index: composer/e-msg-composer.h
===================================================================
--- composer/e-msg-composer.h	(revision 37527)
+++ composer/e-msg-composer.h	(working copy)
@@ -28,6 +28,7 @@
 #include <libedataserver/e-account.h>
 #include <libebook/e-destination.h>
 #include <gtkhtml-editor.h>
+#include <widgets/misc/e-attachment-view.h>
 
 #include "e-composer-header-table.h"
 
@@ -100,7 +101,7 @@ void		e_msg_composer_modify_header	(EMsg
 void		e_msg_composer_remove_header	(EMsgComposer *composer,
 						 const gchar *name);
 void		e_msg_composer_attach		(EMsgComposer *composer,
-						 CamelMimePart *attachment);
+						 CamelMimePart *mime_part);
 CamelMimePart *	e_msg_composer_add_inline_image_from_file
 						(EMsgComposer *composer,
 						 const gchar *filename);
@@ -139,22 +140,19 @@ void		e_msg_composer_add_message_attachm
 gboolean	e_msg_composer_request_close_all(void);
 EMsgComposer *	e_msg_composer_load_from_file	(const gchar *filename);
 void		e_msg_composer_check_autosave	(GtkWindow *parent);
-gint		e_msg_composer_get_remote_download_count
-						(EMsgComposer *composer);
 
 void		e_msg_composer_reply_indent	(EMsgComposer *composer);
 
 EComposerHeaderTable *
 		e_msg_composer_get_header_table	(EMsgComposer *composer);
+EAttachmentView *
+		e_msg_composer_get_attachment_view
+						(EMsgComposer *composer);
 void		e_msg_composer_set_send_options	(EMsgComposer *composer,
 						 gboolean send_enable);
 GByteArray *	e_msg_composer_get_raw_message_text
 						(EMsgComposer *composer);
 
-struct _EAttachmentBar *
-		e_msg_composer_get_attachment_bar
-						(EMsgComposer *composer);
-
 gboolean	e_msg_composer_is_exiting	(EMsgComposer *composer);
 
 GList *		e_load_spell_languages		(void);
Index: composer/e-composer-actions.c
===================================================================
--- composer/e-composer-actions.c	(revision 37527)
+++ composer/e-composer-actions.c	(working copy)
@@ -31,69 +31,13 @@ static void
 action_attach_cb (GtkAction *action,
                   EMsgComposer *composer)
 {
-	EAttachmentBar *bar;
-	GtkWidget *dialog;
-	GtkWidget *option;
-	GSList *uris, *iter;
-	const gchar *disposition;
-	gboolean active;
-	gint response;
-
-	bar = E_ATTACHMENT_BAR (composer->priv->attachment_bar);
-
-	dialog = gtk_file_chooser_dialog_new (
-		_("Insert Attachment"),
-		GTK_WINDOW (composer),
-		GTK_FILE_CHOOSER_ACTION_OPEN,
-		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-		_("A_ttach"), GTK_RESPONSE_OK,
-		NULL);
-
-	gtk_dialog_set_default_response (
-		GTK_DIALOG (dialog), GTK_RESPONSE_OK);
-	gtk_file_chooser_set_local_only (
-		GTK_FILE_CHOOSER (dialog), FALSE);
-	gtk_file_chooser_set_select_multiple (
-		GTK_FILE_CHOOSER (dialog), TRUE);
-	gtk_window_set_icon_name (
-		GTK_WINDOW (dialog), "mail-message-new");
-
-	option = gtk_check_button_new_with_mnemonic (
-		_("_Suggest automatic display of attachment"));
-	gtk_widget_show (option);
-	gtk_file_chooser_set_extra_widget (
-		GTK_FILE_CHOOSER (dialog), option);
-
-	response = gtkhtml_editor_file_chooser_dialog_run (
-		GTKHTML_EDITOR (composer), dialog);
-
-	if (response != GTK_RESPONSE_OK)
-		goto exit;
+	EAttachmentView *view;
+	EAttachmentStore *store;
 
-	uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
-	active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (option));
-	disposition = active ? "inline" : "attachment";
-
-	for (iter = uris; iter != NULL; iter = iter->next) {
-		CamelURL *url;
-
-		url = camel_url_new (iter->data, NULL);
-		if (url == NULL)
-			continue;
-
-		if (!g_ascii_strcasecmp (url->protocol, "file"))
-			e_attachment_bar_attach (bar, url->path, disposition);
-		else
-			e_attachment_bar_attach_remote_file (bar, iter->data, disposition);
-
-		camel_url_free (url);
-	}
+	view = e_msg_composer_get_attachment_view (composer);
+	store = e_attachment_view_get_store (view);
 
-	g_slist_foreach (uris, (GFunc) g_free, NULL);
-	g_slist_free (uris);
-
-exit:
-	gtk_widget_destroy (dialog);
+	e_attachment_store_run_load_dialog (store, GTK_WINDOW (composer));
 }
 
 static void
Index: composer/e-composer-private.c
===================================================================
--- composer/e-composer-private.c	(revision 37527)
+++ composer/e-composer-private.c	(working copy)
@@ -49,22 +49,25 @@ composer_setup_charset_menu (EMsgCompose
 static void
 composer_setup_recent_menu (EMsgComposer *composer)
 {
+	EAttachmentView *view;
 	GtkUIManager *manager;
 	GtkAction *action = NULL;
-	const gchar *path, *action_name;
+	const gchar *action_name;
+	const gchar *path;
 	guint merge_id;
 
+	view = e_msg_composer_get_attachment_view (composer);
 	manager = gtkhtml_editor_get_ui_manager (GTKHTML_EDITOR (composer));
-	action_name = "recent-menu";
 	path = "/main-menu/insert-menu/insert-menu-top/recent-placeholder";
 	merge_id = gtk_ui_manager_new_merge_id (manager);
+	action_name = "recent-menu";
 
-	action = e_attachment_bar_recent_action_new (
-			e_msg_composer_get_attachment_bar (composer), 
-			action_name, _("Recent _Documents"));
+	action = e_attachment_view_recent_action_new (
+		view, action_name, _("Recent _Documents"));
 
 	if (action != NULL) {
-		gtk_action_group_add_action (composer->priv->composer_actions, action);
+		gtk_action_group_add_action (
+			composer->priv->composer_actions, action);
 
 		gtk_ui_manager_add_ui ( 
 			manager, merge_id, path,
@@ -84,7 +87,6 @@ e_composer_private_init (EMsgComposer *c
 	GtkhtmlEditor *editor;
 	GtkUIManager *manager;
 	GtkWidget *widget;
-	GtkWidget *expander;
 	GtkWidget *container;
 	GtkWidget *send_widget;
 	const gchar *path;
@@ -136,6 +138,8 @@ e_composer_private_init (EMsgComposer *c
 
 	/* Construct the header table. */
 
+	container = editor->vbox;
+
 	widget = e_composer_header_table_new ();
 	gtk_container_set_border_width (GTK_CONTAINER (widget), 6);
 	gtk_box_pack_start (GTK_BOX (editor->vbox), widget, FALSE, FALSE, 0);
@@ -143,59 +147,23 @@ e_composer_private_init (EMsgComposer *c
 	priv->header_table = g_object_ref (widget);
 	gtk_widget_show (widget);
 
-	/* Construct attachment widgets.
-	 * XXX Move this stuff into a new custom widget. */
+	/* Construct the attachment paned. */
 
-	widget = gtk_expander_new (NULL);
-	gtk_expander_set_expanded (GTK_EXPANDER (widget), FALSE);
-	gtk_container_set_border_width (GTK_CONTAINER (widget), 6);
-	gtk_box_pack_start (GTK_BOX (editor->vbox), widget, FALSE, FALSE, 0);
-	priv->attachment_expander = g_object_ref (widget);
-	gtk_widget_show (widget);
-	expander = widget;
-
-	widget = gtk_scrolled_window_new (NULL, NULL);
-	gtk_scrolled_window_set_policy (
-		GTK_SCROLLED_WINDOW (widget),
-		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-	gtk_scrolled_window_set_shadow_type (
-		GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
-	gtk_container_add (GTK_CONTAINER (expander), widget);
-	priv->attachment_scrolled_window = g_object_ref (widget);
-	gtk_widget_show (widget);
-	container = widget;
-
-	widget = e_attachment_bar_new (NULL);
-	GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
-	gtk_container_add (GTK_CONTAINER (container), widget);
-	priv->attachment_bar = g_object_ref (widget);
-	gtk_widget_show (widget);
-
-	widget = gtk_hbox_new (FALSE, 0);
-	gtk_expander_set_label_widget (GTK_EXPANDER (expander), widget);
-	gtk_widget_show (widget);
-	container = widget;
-
-	widget = gtk_label_new_with_mnemonic (_("Show _Attachment Bar"));
-	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
-	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 6);
-	priv->attachment_expander_label = g_object_ref (widget);
+	widget = e_attachment_paned_new ();
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	priv->attachment_paned = g_object_ref (widget);
 	gtk_widget_show (widget);
 
-	widget = gtk_image_new_from_icon_name (
-		"mail-attachment", GTK_ICON_SIZE_MENU);
-	gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
-	gtk_widget_set_size_request (widget, 100, -1);
-	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
-	priv->attachment_expander_icon = g_object_ref (widget);
-	gtk_widget_hide (widget);
+	/* Reparent the scrolled window containing the GtkHTML widget
+	 * into the content area of the top attachment pane. */
 
-	widget = gtk_label_new (NULL);
-	gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
-	gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
-	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 6);
-	priv->attachment_expander_num = g_object_ref (widget);
-	gtk_widget_show (widget);
+	widget = GTK_WIDGET (gtkhtml_editor_get_html (editor));
+	widget = gtk_widget_get_parent (widget);
+	container = e_attachment_paned_get_content_area (
+		E_ATTACHMENT_PANED (priv->attachment_paned));
+	gtk_widget_reparent (widget, container);
+	gtk_box_set_child_packing (
+		GTK_BOX (container), widget, TRUE, TRUE, 0, GTK_PACK_START);
 
 	composer_setup_recent_menu (composer);
 }
Index: composer/e-composer-private.h
===================================================================
--- composer/e-composer-private.h	(revision 37527)
+++ composer/e-composer-private.h	(working copy)
@@ -25,11 +25,12 @@
 
 #include <camel/camel-iconv.h>
 
-#include "e-attachment-bar.h"
 #include "e-composer-actions.h"
 #include "e-composer-autosave.h"
 #include "e-composer-header-table.h"
 #include "e-util/gconf-bridge.h"
+#include "widgets/misc/e-attachment-paned.h"
+#include "widgets/misc/e-attachment-store.h"
 
 #define E_MSG_COMPOSER_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -94,6 +95,8 @@ struct _EMsgComposerPrivate {
 
 	GtkWidget *html_editor;
 	GtkWidget *header_table;
+	GtkWidget *attachment_paned;
+
 	GtkActionGroup *charset_actions;
 	GtkActionGroup *composer_actions;
 
@@ -102,13 +105,6 @@ struct _EMsgComposerPrivate {
 
 	GtkWidget *focused_entry;
 
-	GtkWidget *attachment_bar;
-	GtkWidget *attachment_scrolled_window;
-	GtkWidget *attachment_expander;
-	GtkWidget *attachment_expander_label;
-	GtkWidget *attachment_expander_icon;
-	GtkWidget *attachment_expander_num;
-
 	GtkWidget *address_dialog;
 
 	GHashTable *inline_images;
Index: composer/e-msg-composer.c
===================================================================
--- composer/e-msg-composer.c	(revision 37527)
+++ composer/e-msg-composer.c	(working copy)
@@ -98,7 +98,6 @@
 
 #include "e-msg-composer.h"
 #include "e-attachment.h"
-#include "e-attachment-bar.h"
 #include "e-composer-autosave.h"
 #include "e-composer-private.h"
 #include "e-composer-header-table.h"
@@ -152,39 +151,6 @@ enum {
 	LAST_SIGNAL
 };
 
-enum {
-	DND_TYPE_MESSAGE_RFC822,
-	DND_TYPE_X_UID_LIST,
-	DND_TYPE_TEXT_URI_LIST,
-	DND_TYPE_NETSCAPE_URL,
-	DND_TYPE_TEXT_VCARD,
-	DND_TYPE_TEXT_CALENDAR
-};
-
-static GtkTargetEntry drop_types[] = {
-	{ "message/rfc822", 0, DND_TYPE_MESSAGE_RFC822 },
-	{ "x-uid-list",     0, DND_TYPE_X_UID_LIST },
-	{ "text/uri-list",  0, DND_TYPE_TEXT_URI_LIST },
-	{ "_NETSCAPE_URL",  0, DND_TYPE_NETSCAPE_URL },
-	{ "text/x-vcard",   0, DND_TYPE_TEXT_VCARD },
-	{ "text/calendar",  0, DND_TYPE_TEXT_CALENDAR }
-};
-
-static struct {
-	gchar *target;
-	GdkAtom atom;
-	guint32 actions;
-} drag_info[] = {
-	{ "message/rfc822", NULL, GDK_ACTION_COPY },
-	{ "x-uid-list",     NULL, GDK_ACTION_ASK |
-                                  GDK_ACTION_MOVE |
-                                  GDK_ACTION_COPY },
-	{ "text/uri-list",  NULL, GDK_ACTION_COPY },
-	{ "_NETSCAPE_URL",  NULL, GDK_ACTION_COPY },
-	{ "text/x-vcard",   NULL, GDK_ACTION_COPY },
-	{ "text/calendar",  NULL, GDK_ACTION_COPY }
-};
-
 static gpointer parent_class;
 static guint signals[LAST_SIGNAL];
 
@@ -195,7 +161,6 @@ static GSList *all_composers = NULL;
 static GList *add_recipients (GList *list, const gchar *recips);
 
 static void handle_mailto (EMsgComposer *composer, const gchar *mailto);
-static void handle_uri    (EMsgComposer *composer, const gchar *uri, gboolean html_dnd);
 
 /* used by e_msg_composer_add_message_attachments () */
 static void add_attachments_from_multipart (EMsgComposer *composer, CamelMultipart *multipart,
@@ -527,7 +492,8 @@ build_message (EMsgComposer *composer,
 	GtkhtmlEditor *editor;
 	EMsgComposerPrivate *p = composer->priv;
 
-	EAttachmentBar *attachment_bar;
+	EAttachmentView *view;
+	EAttachmentStore *store;
 	EComposerHeaderTable *table;
 	GtkToggleAction *action;
 	CamelDataWrapper *plain, *html, *current;
@@ -554,7 +520,8 @@ build_message (EMsgComposer *composer,
 	editor = GTKHTML_EDITOR (composer);
 	table = e_msg_composer_get_header_table (composer);
 	account = e_composer_header_table_get_account (table);
-	attachment_bar = E_ATTACHMENT_BAR (p->attachment_bar);
+	view = e_msg_composer_get_attachment_view (composer);
+	store = e_attachment_view_get_store (view);
 
 	/* evil kludgy hack for Redirect */
 	if (p->redirect) {
@@ -714,7 +681,7 @@ build_message (EMsgComposer *composer,
 	} else
 		current = plain;
 
-	if (e_attachment_bar_get_num_attachments (attachment_bar)) {
+	if (e_attachment_store_get_num_attachments (store) > 0) {
 		CamelMultipart *multipart = camel_multipart_new ();
 
 		if (p->is_alternative) {
@@ -733,7 +700,8 @@ build_message (EMsgComposer *composer,
 		camel_multipart_add_part (multipart, part);
 		camel_object_unref (part);
 
-		e_attachment_bar_to_multipart (attachment_bar, multipart, p->charset);
+		e_attachment_store_add_to_multipart (
+			store, multipart, p->charset);
 
 		if (p->is_alternative) {
 			for (i = camel_multipart_get_number (multipart); i > 1; i--) {
@@ -976,107 +944,6 @@ skip_content:
 	return NULL;
 }
 
-/* Attachment Bar */
-
-static void
-emcab_add (EPopup *ep, EPopupItem *item, gpointer data)
-{
-	GtkWidget *widget = data;
-	GtkWidget *composer;
-
-	composer = gtk_widget_get_toplevel (widget);
-	gtk_action_activate (ACTION (ATTACH));
-}
-
-static void
-emcab_properties (EPopup *ep, EPopupItem *item, gpointer data)
-{
-	EAttachmentBar *attachment_bar = data;
-
-	e_attachment_bar_edit_selected (attachment_bar);
-}
-
-static void
-emcab_remove (EPopup *ep, EPopupItem *item, gpointer data)
-{
-	EAttachmentBar *attachment_bar = data;
-
-	e_attachment_bar_remove_selected (attachment_bar);
-}
-
-static void
-emcab_popup_position (GtkMenu *menu, int *x, int *y, gboolean *push_in, gpointer user_data)
-{
-	GtkWidget *widget = user_data;
-	GnomeIconList *icon_list = user_data;
-	GList *selection;
-	GnomeCanvasPixbuf *image;
-
-	gdk_window_get_origin (widget->window, x, y);
-
-	selection = gnome_icon_list_get_selection (icon_list);
-	if (selection == NULL)
-		return;
-
-	image = gnome_icon_list_get_icon_pixbuf_item (
-		icon_list, GPOINTER_TO_INT(selection->data));
-	if (image == NULL)
-		return;
-
-	/* Put menu to the center of icon. */
-	*x += (int)(image->item.x1 + image->item.x2) / 2;
-	*y += (int)(image->item.y1 + image->item.y2) / 2;
-}
-
-static void
-emcab_popups_free (EPopup *ep, GSList *list, gpointer data)
-{
-	g_slist_free (list);
-}
-
-/* Popup menu handling.  */
-static EPopupItem emcab_popups[] = {
-	{ E_POPUP_ITEM, "10.attach", N_("_Remove"), emcab_remove, NULL, GTK_STOCK_REMOVE, EM_POPUP_ATTACHMENTS_MANY },
-	{ E_POPUP_ITEM, "20.attach", N_("_Properties"), emcab_properties, NULL, GTK_STOCK_PROPERTIES, EM_POPUP_ATTACHMENTS_ONE },
-	{ E_POPUP_BAR, "30.attach.00", NULL, NULL, NULL, NULL, EM_POPUP_ATTACHMENTS_MANY|EM_POPUP_ATTACHMENTS_ONE },
-	{ E_POPUP_ITEM, "30.attach.01", N_("_Add attachment..."), emcab_add, NULL, GTK_STOCK_ADD, 0 },
-};
-
-/* if id != -1, then use it as an index for target of the popup */
-
-static void
-emcab_popup (EAttachmentBar *bar, GdkEventButton *event, int id)
-{
-	GSList *attachments = NULL, *menus = NULL;
-	int i;
-	EMPopup *emp;
-	EMPopupTargetAttachments *t;
-	GtkMenu *menu;
-
-	attachments = e_attachment_bar_get_attachment (bar, id);
-
-	for (i=0;i<sizeof (emcab_popups)/sizeof (emcab_popups[0]);i++)
-		menus = g_slist_prepend (menus, &emcab_popups[i]);
-
-	/** @HookPoint-EMPopup: Composer Attachment Bar Context Menu
-	 * @Id: org.gnome.evolution.mail.composer.attachmentbar.popup
-	 * @Class: org.gnome.evolution.mail.popup:1.0
-	 * @Target: EMPopupTargetAttachments
-	 *
-	 * This is the context menu on the composer attachment bar.
-	 */
-	emp = em_popup_new ("org.gnome.evolution.mail.composer.attachmentbar.popup");
-	e_popup_add_items ((EPopup *)emp, menus, NULL, emcab_popups_free, bar);
-	t = em_popup_target_new_attachments (emp, attachments);
-	t->target.widget = (GtkWidget *)bar;
-	menu = e_popup_create_menu_once ((EPopup *)emp, (EPopupTarget *)t, 0);
-
-	if (event == NULL)
-		gtk_menu_popup (menu, NULL, NULL, emcab_popup_position, bar, 0, gtk_get_current_event_time ());
-	else
-		gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event->button, event->time);
-}
-
 /* Signatures */
 
 static gchar *
@@ -1473,108 +1340,17 @@ autosave_load_draft (const gchar *filena
 
 /* Miscellaneous callbacks.  */
 
-static gint
-attachment_bar_button_press_event_cb (EAttachmentBar *attachment_bar,
-                                      GdkEventButton *event)
-{
-	GnomeIconList *icon_list;
-	gint icon_number;
-
-	if (event->button != 3)
-		return FALSE;
-
-	icon_list = GNOME_ICON_LIST (attachment_bar);
-	icon_number = gnome_icon_list_get_icon_at (
-		icon_list, event->x, event->y);
-	if (icon_number >= 0) {
-		gnome_icon_list_unselect_all (icon_list);
-		gnome_icon_list_select_icon (icon_list, icon_number);
-	}
-
-	emcab_popup (attachment_bar, event, icon_number);
-
-	return TRUE;
-}
-
 static void
-attachment_bar_changed_cb (EAttachmentBar *attachment_bar,
-                           EMsgComposer *composer)
+attachment_store_changed_cb (EMsgComposer *composer)
 {
 	GtkhtmlEditor *editor;
-	GtkWidget *widget;
-	guint attachment_num;
-
-	editor = GTKHTML_EDITOR (composer);
-	attachment_num = e_attachment_bar_get_num_attachments (attachment_bar);
-
-	if (attachment_num > 0) {
-		gchar *markup;
-
-		markup = g_strdup_printf (
-			"<b>%d</b> %s", attachment_num, ngettext (
-			"Attachment", "Attachments", attachment_num));
-		widget = composer->priv->attachment_expander_num;
-		gtk_label_set_markup (GTK_LABEL (widget), markup);
-		g_free (markup);
-
-		gtk_widget_show (composer->priv->attachment_expander_icon);
-
-		widget = composer->priv->attachment_expander;
-		gtk_expander_set_expanded (GTK_EXPANDER (widget), TRUE);
-	} else {
-		widget = composer->priv->attachment_expander_num;
-		gtk_label_set_text (GTK_LABEL (widget), "");
-
-		gtk_widget_hide (composer->priv->attachment_expander_icon);
-
-		widget = composer->priv->attachment_expander;
-		gtk_expander_set_expanded (GTK_EXPANDER (widget), FALSE);
-	}
 
 	/* Mark the editor as changed so it prompts about unsaved
-	   changes on close. */
+	 * changes on close. */
+	editor = GTKHTML_EDITOR (composer);
 	gtkhtml_editor_set_changed (editor, TRUE);
 }
 
-static gint
-attachment_bar_key_press_event_cb (EAttachmentBar *attachment_bar,
-                                   GdkEventKey *event)
-{
-	if (event->keyval == GDK_Delete) {
-		e_attachment_bar_remove_selected (attachment_bar);
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-static gboolean
-attachment_bar_popup_menu_cb (EAttachmentBar *attachment_bar)
-{
-	emcab_popup (attachment_bar, NULL, -1);
-
-	return TRUE;
-}
-
-static void
-attachment_expander_notify_cb (GtkExpander *expander,
-                               GParamSpec *pspec,
-                               EMsgComposer *composer)
-{
-	GtkLabel *label;
-	const gchar *text;
-
-	label = GTK_LABEL (composer->priv->attachment_expander_label);
-
-	/* Update the expander label */
-	if (gtk_expander_get_expanded (expander))
-		text = _("Hide _Attachment Bar");
-	else
-		text = _("Show _Attachment Bar");
-
-	gtk_label_set_text_with_mnemonic (label, text);
-}
-
 static void
 msg_composer_subject_changed_cb (EMsgComposer *composer)
 {
@@ -1771,35 +1547,6 @@ msg_composer_account_list_changed_cb (EM
 }
 
 static void
-msg_composer_attach_message (EMsgComposer *composer,
-                             CamelMimeMessage *msg)
-{
-	CamelMimePart *mime_part;
-	GString *description;
-	const gchar *subject;
-	EMsgComposerPrivate *p = composer->priv;
-
-	mime_part = camel_mime_part_new ();
-	camel_mime_part_set_disposition (mime_part, "inline");
-	subject = camel_mime_message_get_subject (msg);
-
-	description = g_string_new (_("Attached message"));
-	if (subject != NULL)
-		g_string_append_printf (description, " - %s", subject);
-	camel_mime_part_set_description (mime_part, description->str);
-	g_string_free (description, TRUE);
-
-	camel_medium_set_content_object (
-		(CamelMedium *) mime_part, (CamelDataWrapper *) msg);
-	camel_mime_part_set_content_type (mime_part, "message/rfc822");
-
-	e_attachment_bar_attach_mime_part (
-		E_ATTACHMENT_BAR (p->attachment_bar), mime_part);
-
-	camel_object_unref (mime_part);
-}
-
-static void
 msg_composer_update_preferences (GConfClient *client,
                                  guint cnxn_id,
                                  GConfEntry *entry,
@@ -1855,17 +1602,7 @@ struct _drop_data {
 	unsigned int aborted:1;
 };
 
-int
-e_msg_composer_get_remote_download_count (EMsgComposer *composer)
-{
-	EAttachmentBar *attachment_bar;
-
-	g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), 0);
-
-	attachment_bar = E_ATTACHMENT_BAR (composer->priv->attachment_bar);
-	return e_attachment_bar_get_download_count (attachment_bar);
-}
-
+#if 0  /* FIXME */
 static void
 drop_action (EMsgComposer *composer,
              GdkDragContext *context,
@@ -2039,55 +1776,7 @@ drop_action (EMsgComposer *composer,
 
 	gtk_drag_finish (context, success, delete, time);
 }
-
-static void
-drop_popup_copy (EPopup *ep, EPopupItem *item, gpointer data)
-{
-	struct _drop_data *m = data;
-
-	drop_action (
-		m->composer, m->context, GDK_ACTION_COPY,
-		m->selection, m->info, m->time, FALSE);
-}
-
-static void
-drop_popup_move (EPopup *ep, EPopupItem *item, gpointer data)
-{
-	struct _drop_data *m = data;
-
-	drop_action (
-		m->composer, m->context, GDK_ACTION_MOVE,
-		m->selection, m->info, m->time, FALSE);
-}
-
-static void
-drop_popup_cancel (EPopup *ep, EPopupItem *item, gpointer data)
-{
-	struct _drop_data *m = data;
-
-	gtk_drag_finish (m->context, FALSE, FALSE, m->time);
-}
-
-static EPopupItem drop_popup_menu[] = {
-	{ E_POPUP_ITEM, "00.emc.02", N_("_Copy"), drop_popup_copy, NULL, "mail-copy", 0 },
-	{ E_POPUP_ITEM, "00.emc.03", N_("_Move"), drop_popup_move, NULL, "mail-move", 0 },
-	{ E_POPUP_BAR, "10.emc" },
-	{ E_POPUP_ITEM, "99.emc.00", N_("Cancel _Drag"), drop_popup_cancel, NULL, NULL, 0 },
-};
-
-static void
-drop_popup_free (EPopup *ep, GSList *items, gpointer data)
-{
-	struct _drop_data *m = data;
-
-	g_slist_free (items);
-
-	g_object_unref (m->context);
-	g_object_unref (m->composer);
-	g_free (m->selection->data);
-	g_free (m->selection);
-	g_free (m);
-}
+#endif
 
 static void
 msg_composer_notify_header_cb (EMsgComposer *composer)
@@ -2347,33 +2036,14 @@ msg_composer_drag_motion (GtkWidget *wid
                           gint y,
                           guint time)
 {
-	GList *targets;
-	GdkDragAction actions = 0;
-	GdkDragAction chosen_action;
-
-	targets = context->targets;
-	while (targets != NULL) {
-		gint ii;
-
-		for (ii = 0; ii < G_N_ELEMENTS (drag_info); ii++)
-			if (targets->data == (gpointer) drag_info[ii].atom)
-				actions |= drag_info[ii].actions;
-
-		targets = g_list_next (targets);
-	}
-
-	actions &= context->actions;
-	chosen_action = context->suggested_action;
-
-	/* we default to copy */
-	if (chosen_action == GDK_ACTION_ASK &&
-		(actions & (GDK_ACTION_MOVE|GDK_ACTION_COPY)) !=
-		(GDK_ACTION_MOVE|GDK_ACTION_COPY))
-		chosen_action = GDK_ACTION_COPY;
+	EMsgComposer *composer;
+	EAttachmentView *view;
 
-	gdk_drag_status (context, chosen_action, time);
+	/* Widget may be EMsgComposer or GtkHTML. */
+	composer = E_MSG_COMPOSER (gtk_widget_get_toplevel (widget));
+	view = e_msg_composer_get_attachment_view (composer);
 
-	return (chosen_action != 0);
+	return e_attachment_view_drag_motion (view, context, x, y, time);
 }
 
 static void
@@ -2386,46 +2056,19 @@ msg_composer_drag_data_received (GtkWidg
                                  guint time)
 {
 	EMsgComposer *composer;
+	EAttachmentView *view;
 
 	/* Widget may be EMsgComposer or GtkHTML. */
 	composer = E_MSG_COMPOSER (gtk_widget_get_toplevel (widget));
+	view = e_msg_composer_get_attachment_view (composer);
 
-	if (selection->data == NULL)
-		return;
-
-	if (selection->length == -1)
-		return;
-
-	if (context->action == GDK_ACTION_ASK) {
-		EMPopup *emp;
-		GSList *menus = NULL;
-		GtkMenu *menu;
-		gint ii;
-		struct _drop_data *m;
-
-		m = g_malloc0(sizeof (*m));
-		m->context = g_object_ref (context);
-		m->composer = g_object_ref (composer);
-		m->action = context->action;
-		m->info = info;
-		m->time = time;
-		m->selection = g_malloc0(sizeof (*m->selection));
-		m->selection->data = g_malloc (selection->length);
-		memcpy (m->selection->data, selection->data, selection->length);
-		m->selection->length = selection->length;
-
-		emp = em_popup_new ("org.gnome.evolution.mail.composer.popup.drop");
-		for (ii = 0; ii < G_N_ELEMENTS (drop_popup_menu); ii++)
-			menus = g_slist_append (menus, &drop_popup_menu[ii]);
-
-		e_popup_add_items ((EPopup *)emp, menus, NULL, drop_popup_free, m);
-		menu = e_popup_create_menu_once ((EPopup *)emp, NULL, 0);
-		gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0, time);
-	} else {
-		drop_action (
-			composer, context, context->action, selection,
-			info, time, !GTK_WIDGET_TOPLEVEL (widget));
-	}
+	/* 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
@@ -2472,11 +2115,20 @@ static void
 msg_composer_paste_clipboard (GtkhtmlEditor *editor)
 {
 	EMsgComposer *composer;
+	EAttachmentView *view;
+	EAttachmentStore *store;
+	GtkClipboard *clipboard;
+	GdkPixbuf *pixbuf;
 	GtkWidget *parent;
 	GtkWidget *widget;
-	GtkClipboard *clipboard;
+	gchar *filename;
+	gchar *uri;
+	GError *error = NULL;
 
 	composer = E_MSG_COMPOSER (editor);
+	view = e_msg_composer_get_attachment_view (composer);
+	store = e_attachment_view_get_store (view);
+
 	widget = gtk_window_get_focus (GTK_WINDOW (editor));
 	parent = gtk_widget_get_parent (widget);
 
@@ -2486,36 +2138,63 @@ msg_composer_paste_clipboard (GtkhtmlEdi
 	}
 
 	clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
-	if (clipboard && gtk_clipboard_wait_is_image_available (clipboard)) {
-		GdkPixbuf *pixbuf;
 
-		pixbuf = gtk_clipboard_wait_for_image (clipboard);
-		if (pixbuf) {
-			char *tmpl = g_strconcat (_("Image"), "-XXXXXX", NULL);
-			char *filename = e_mktemp (tmpl);
-
-			g_free (tmpl);
-
-			if (filename && gdk_pixbuf_save (pixbuf, filename, "png", NULL, NULL)) {
-				if (gtkhtml_editor_get_html_mode (editor)) {
-					char *uri = g_strconcat ("file://", filename, NULL);
-					/* this loads image async, thus cannot remove file from this */
-					gtkhtml_editor_insert_image (editor, uri);
-					g_free (uri);
-				} else {
-					/* this loads image immediately, remove file from cache to free up disk space */
-					e_attachment_bar_attach (E_ATTACHMENT_BAR (composer->priv->attachment_bar), filename, "image/png");
-					g_remove (filename);
-				}
-			}
+	/* Assume the clipboard has an image.  The return
+	 * value will be NULL we we assumed wrong. */
+	pixbuf = gtk_clipboard_wait_for_image (clipboard);
+	if (!GDK_IS_PIXBUF (pixbuf))
+		goto chainup;
+
+	/* Reserve a temporary file. */
+	filename = e_mktemp (NULL);
+	if (filename == NULL) {
+		g_warning ("%s", g_strerror (errno));
+		g_object_unref (pixbuf);
+		g_error_free (error);
+		return;
+	}
 
-			g_free (filename);
-			g_object_unref (pixbuf);
-		}
-	} else {
-		/* Chain up to parent's paste_clipboard() method. */
-		GTKHTML_EDITOR_CLASS (parent_class)->paste_clipboard (editor);
+	/* Save the pixbuf as a temporary file in image/png format. */
+	if (!gdk_pixbuf_save (pixbuf, filename, "png", &error, NULL)) {
+		g_warning ("%s", error->message);
+		g_object_unref (pixbuf);
+		g_error_free (error);
+		g_free (filename);
+		return;
+	}
+
+	/* Convert the filename to a URI. */
+	uri = g_filename_to_uri (filename, NULL, &error);
+	if (error != NULL) {
+		g_warning ("%s", error->message);
+		g_object_unref (pixbuf);
+		g_error_free (error);
+		g_free (filename);
+		return;
 	}
+
+	if (gtkhtml_editor_get_html_mode (editor))
+		gtkhtml_editor_insert_image (editor, uri);
+	else {
+		EAttachment *attachment;
+
+		attachment = e_attachment_new_for_uri (uri);
+		e_attachment_store_add_attachment (store, attachment);
+		e_attachment_load_async (
+			attachment, (GAsyncReadyCallback)
+			e_attachment_load_handle_error, composer);
+		g_object_unref (attachment);
+	}
+
+	g_object_unref (pixbuf);
+	g_free (filename);
+	g_free (uri);
+
+	return;
+
+chainup:
+	/* Chain up to parent's paste_clipboard() method. */
+	GTKHTML_EDITOR_CLASS (parent_class)->paste_clipboard (editor);
 }
 
 static void
@@ -2739,11 +2418,6 @@ msg_composer_class_init (EMsgComposerCla
 	GtkObjectClass *gtk_object_class;
 	GtkWidgetClass *widget_class;
 	GtkhtmlEditorClass *editor_class;
-	gint ii;
-
-	for (ii = 0; ii < G_N_ELEMENTS (drag_info); ii++)
-		drag_info[ii].atom =
-			gdk_atom_intern (drag_info[ii].target, FALSE);
 
 	parent_class = g_type_class_peek_parent (class);
 	g_type_class_add_private (class, sizeof (EMsgComposerPrivate));
@@ -2795,10 +2469,16 @@ msg_composer_class_init (EMsgComposerCla
 static void
 msg_composer_init (EMsgComposer *composer)
 {
+	EAttachmentView *view;
+	EAttachmentStore *store;
 	EComposerHeaderTable *table;
+	GdkDragAction drag_actions;
+	GtkTargetList *target_list;
+	GtkTargetEntry *targets;
 	GtkUIManager *manager;
 	GtkhtmlEditor *editor;
 	GtkHTML *html;
+	gint n_targets;
 
 	composer->priv = E_MSG_COMPOSER_GET_PRIVATE (composer);
 
@@ -2807,6 +2487,7 @@ msg_composer_init (EMsgComposer *compose
 	editor = GTKHTML_EDITOR (composer);
 	html = gtkhtml_editor_get_html (editor);
 	manager = gtkhtml_editor_get_ui_manager (editor);
+	view = e_msg_composer_get_attachment_view (composer);
 	all_composers = g_slist_prepend (all_composers, composer);
 	table = E_COMPOSER_HEADER_TABLE (composer->priv->header_table);
 
@@ -2815,36 +2496,21 @@ msg_composer_init (EMsgComposer *compose
 
 	/* Drag-and-Drop Support */
 
-	gtk_drag_dest_set (
-		GTK_WIDGET (composer), GTK_DEST_DEFAULT_ALL,
-		drop_types, G_N_ELEMENTS (drop_types),
-		GDK_ACTION_COPY | GDK_ACTION_ASK | GDK_ACTION_MOVE);
+	target_list = e_attachment_view_get_target_list (view);
+	drag_actions = e_attachment_view_get_drag_actions (view);
 
-	/* XXX I'm not sure why we have to explicitly configure the
-	 *     attachment bar as a drag destination when CompEditor
-	 *     doesn't and previous Evolution releases (2.22 and
-	 *     prior) don't, but this is the only way I could figure
-	 *     out how to get drag-and-drop to the attachment bar
-	 *     working again.  I'm probably overlooking something
-	 *     simple... */
+	targets = gtk_target_table_new_from_list (target_list, &n_targets);
 
 	gtk_drag_dest_set (
-		composer->priv->attachment_bar, GTK_DEST_DEFAULT_ALL,
-		drop_types, G_N_ELEMENTS (drop_types),
-		GDK_ACTION_COPY | GDK_ACTION_ASK | GDK_ACTION_MOVE);
-
-	g_signal_connect (
-		composer->priv->attachment_bar, "drag-motion",
-		G_CALLBACK (msg_composer_drag_motion), NULL);
-
-	g_signal_connect (
-		composer->priv->attachment_bar, "drag-data-received",
-		G_CALLBACK (msg_composer_drag_data_received), NULL);
+		GTK_WIDGET (composer), GTK_DEST_DEFAULT_ALL,
+		targets, n_targets, drag_actions);
 
 	g_signal_connect (
 		html, "drag-data-received",
 		G_CALLBACK (msg_composer_drag_data_received), NULL);
 
+	gtk_target_table_free (targets, n_targets);
+
 	/* Configure Headers */
 
 	e_composer_header_table_set_account_list (
@@ -2883,23 +2549,17 @@ msg_composer_init (EMsgComposer *compose
 	msg_composer_account_changed_cb (composer);
 	msg_composer_account_list_changed_cb (composer);
 
-	/* Attachment Bar */
+	/* Attachments */
 
-	g_signal_connect (
-		composer->priv->attachment_bar, "button_press_event",
-		G_CALLBACK (attachment_bar_button_press_event_cb), NULL);
-	g_signal_connect (
-		composer->priv->attachment_bar, "key_press_event",
-		G_CALLBACK (attachment_bar_key_press_event_cb), NULL);
-	g_signal_connect (
-		composer->priv->attachment_bar, "popup-menu",
-		G_CALLBACK (attachment_bar_popup_menu_cb), NULL);
-	g_signal_connect (
-		composer->priv->attachment_bar, "changed",
-		G_CALLBACK (attachment_bar_changed_cb), composer);
-	g_signal_connect_after (
-		composer->priv->attachment_expander, "notify::expanded",
-		G_CALLBACK (attachment_expander_notify_cb), composer);
+	store = e_attachment_view_get_store (view);
+
+	g_signal_connect_swapped (
+		store, "row-deleted",
+		G_CALLBACK (attachment_store_changed_cb), composer);
+
+	g_signal_connect_swapped (
+		store, "row-inserted",
+		G_CALLBACK (attachment_store_changed_cb), composer);
 
 	e_composer_autosave_register (composer);
 
@@ -3744,7 +3404,7 @@ disable_editor (EMsgComposer *composer)
 	action = GTKHTML_EDITOR_ACTION_INSERT_MENU (composer);
 	gtk_action_set_sensitive (action, FALSE);
 
-	gtk_widget_set_sensitive (composer->priv->attachment_bar, FALSE);
+	gtk_widget_set_sensitive (composer->priv->attachment_paned, FALSE);
 }
 
 /**
@@ -3843,7 +3503,8 @@ add_recipients (GList *list, const gchar
 static void
 handle_mailto (EMsgComposer *composer, const gchar *mailto)
 {
-	EMsgComposerPrivate *priv = composer->priv;
+	EAttachmentView *view;
+	EAttachmentStore *store;
 	EComposerHeaderTable *table;
 	GList *to = NULL, *cc = NULL, *bcc = NULL;
 	EDestination **tov, **ccv, **bccv;
@@ -3852,9 +3513,10 @@ handle_mailto (EMsgComposer *composer, c
 	gsize nread, nwritten;
 	const gchar *p;
 	gint len, clen;
-	CamelURL *url;
 
 	table = e_msg_composer_get_header_table (composer);
+	view = e_msg_composer_get_attachment_view (composer);
+	store = e_attachment_view_get_store (view);
 
 	buf = g_strdup (mailto);
 
@@ -3926,20 +3588,17 @@ handle_mailto (EMsgComposer *composer, c
 				}
 			} else if (!g_ascii_strcasecmp (header, "attach") ||
 				   !g_ascii_strcasecmp (header, "attachment")) {
-				/* Change file url to absolute path */
-				if (!g_ascii_strncasecmp (content, "file:", 5)) {
-					url = camel_url_new (content, NULL);
-					e_attachment_bar_attach (E_ATTACHMENT_BAR (priv->attachment_bar),
-							 	 url->path,
-								 "attachment");
-					camel_url_free (url);
-				} else {
-					e_attachment_bar_attach (E_ATTACHMENT_BAR (priv->attachment_bar),
-								 content,
-								 "attachment");
-				}
-				gtk_widget_show (priv->attachment_expander);
-				gtk_widget_show (priv->attachment_scrolled_window);
+				EAttachment *attachment;
+
+				if (g_ascii_strncasecmp (content, "file:", 5) == 0)
+					attachment = e_attachment_new_for_uri (content);
+				else
+					attachment = e_attachment_new_for_path (content);
+				e_attachment_store_add_attachment (store, attachment);
+				e_attachment_load_async (
+					attachment, (GAsyncReadyCallback)
+					e_attachment_load_handle_error, composer);
+				g_object_unref (attachment);
 			} else if (!g_ascii_strcasecmp (header, "from")) {
 				/* Ignore */
 			} else if (!g_ascii_strcasecmp (header, "reply-to")) {
@@ -3991,45 +3650,6 @@ handle_mailto (EMsgComposer *composer, c
 	}
 }
 
-static void
-handle_uri (EMsgComposer *composer,
-            const gchar *uri,
-            gboolean html_dnd)
-{
-	EMsgComposerPrivate *p = composer->priv;
-	GtkhtmlEditor *editor;
-	gboolean html_content;
-
-	editor = GTKHTML_EDITOR (composer);
-	html_content = gtkhtml_editor_get_html_mode (editor);
-
-	if (!g_ascii_strncasecmp (uri, "mailto:", 7)) {
-		handle_mailto (composer, uri);
-	} else {
-		CamelURL *url = camel_url_new (uri, NULL);
-		gchar *type;
-
-		if (!url)
-			return;
-
-		if (!g_ascii_strcasecmp (url->protocol, "file")) {
-			type = e_util_guess_mime_type (uri + strlen ("file://"), TRUE);
-			if (!type)
-				return;
-
-			if (strncmp (type, "image", 5) || !html_dnd || (!html_content && !strncmp (type, "image", 5))) {
-				e_attachment_bar_attach (E_ATTACHMENT_BAR (p->attachment_bar),
-						url->path, "attachment");
-			}
-			g_free (type);
-		} else	{
-			e_attachment_bar_attach_remote_file (E_ATTACHMENT_BAR (p->attachment_bar),
-					uri, "attachment");
-		}
-		camel_url_free (url);
-	}
-}
-
 /**
  * e_msg_composer_new_from_url:
  * @url: a mailto URL
@@ -4193,21 +3813,31 @@ e_msg_composer_remove_header (EMsgCompos
 /**
  * e_msg_composer_attach:
  * @composer: a composer object
- * @attachment: the CamelMimePart to attach
+ * @mime_part: the #CamelMimePart to attach
  *
  * Attaches @attachment to the message being composed in the composer.
  **/
 void
-e_msg_composer_attach (EMsgComposer *composer, CamelMimePart *attachment)
+e_msg_composer_attach (EMsgComposer *composer,
+                       CamelMimePart *mime_part)
 {
-	EAttachmentBar *bar;
-	EMsgComposerPrivate *p = composer->priv;
+	EAttachmentView *view;
+	EAttachmentStore *store;
+	EAttachment *attachment;
 
 	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
-	g_return_if_fail (CAMEL_IS_MIME_PART (attachment));
+	g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
 
-	bar = E_ATTACHMENT_BAR (p->attachment_bar);
-	e_attachment_bar_attach_mime_part (bar, attachment);
+	view = e_msg_composer_get_attachment_view (composer);
+	store = e_attachment_view_get_store (view);
+
+	attachment = e_attachment_new ();
+	e_attachment_set_mime_part (attachment, mime_part);
+	e_attachment_store_add_attachment (store, attachment);
+	e_attachment_load_async (
+		attachment, (GAsyncReadyCallback)
+		e_attachment_load_handle_error, composer);
+	g_object_unref (attachment);
 }
 
 /**
@@ -4321,12 +3951,17 @@ CamelMimeMessage *
 e_msg_composer_get_message (EMsgComposer *composer,
                             gboolean save_html_object_data)
 {
+	EAttachmentView *view;
+	EAttachmentStore *store;
 	GtkhtmlEditor *editor;
 	gboolean html_content;
 
 	g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
 
-	if (e_msg_composer_get_remote_download_count (composer) != 0) {
+	view = e_msg_composer_get_attachment_view (composer);
+	store = e_attachment_view_get_store (view);
+
+	if (e_attachment_store_get_num_loading (store) > 0) {
 		if (!em_utils_prompt_user (GTK_WINDOW (composer), NULL,
 			"mail-composer:ask-send-message-pending-download", NULL)) {
 			return NULL;
@@ -4621,14 +4256,6 @@ e_msg_composer_get_raw_message_text (EMs
 	return array;
 }
 
-EAttachmentBar *
-e_msg_composer_get_attachment_bar (EMsgComposer *composer)
-{
-	g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
-
-	return E_ATTACHMENT_BAR (composer->priv->attachment_bar);
-}
-
 void
 e_msg_composer_set_enable_autosave (EMsgComposer *composer,
                                     gboolean enabled)
@@ -4792,6 +4419,14 @@ e_msg_composer_get_header_table (EMsgCom
 	return E_COMPOSER_HEADER_TABLE (composer->priv->header_table);
 }
 
+EAttachmentView *
+e_msg_composer_get_attachment_view (EMsgComposer *composer)
+{
+	g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
+
+	return E_ATTACHMENT_VIEW (composer->priv->attachment_paned);
+}
+
 void
 e_msg_composer_set_send_options (EMsgComposer *composer,
                                  gboolean send_enable)
Index: e-util/e-binding.h
===================================================================
--- e-util/e-binding.h	(revision 0)
+++ e-util/e-binding.h	(revision 0)
@@ -0,0 +1,118 @@
+/*
+ * e-binding.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)
+ *
+ */
+
+/* This is a direct rip-off of Xfce's excellent ExoBinding API,
+ * which binds two GObject properties together.  ExoBinding was
+ * written by Benedikt Meurer <benny@xfce.org>. */
+
+#ifndef E_BINDING_H
+#define E_BINDING_H
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EBinding EBinding;
+typedef struct _EBindingBase EBindingBase;
+typedef struct _EBindingLink EBindingLink;
+typedef struct _EMutualBinding EMutualBinding;
+
+typedef gboolean	(*EBindingTransform)	(const GValue *src_value,
+						 GValue *dst_value,
+						 gpointer user_data);
+
+struct _EBindingBase {
+	GDestroyNotify destroy;
+};
+
+struct _EBindingLink {
+	GObject *dst_object;
+	GParamSpec *dst_pspec;
+	gulong dst_handler; /* only set for mutual bindings */
+	gulong handler;
+	EBindingTransform transform;
+	gpointer user_data;
+};
+
+struct _EBinding {
+	/*< private >*/
+	GObject *src_object;
+	EBindingBase base;
+	EBindingLink link;
+};
+
+struct _EMutualBinding {
+	/*< private >*/
+	EBindingBase  base;
+	EBindingLink  direct;
+	EBindingLink  reverse;
+};
+
+EBinding *	e_binding_new			(GObject *src_object,
+						 const gchar *src_property,
+						 GObject *dst_object,
+						 const gchar *dst_property);
+EBinding *	e_binding_new_full		(GObject *src_object,
+						 const gchar *src_property,
+						 GObject *dst_object,
+						 const gchar *dst_property,
+						 EBindingTransform transform,
+						 GDestroyNotify destroy_notify,
+						 gpointer user_data);
+EBinding *	e_binding_new_with_negation	(GObject *src_object,
+						 const gchar *src_property,
+						 GObject *dst_object,
+						 const gchar *dst_property);
+void		e_binding_unbind		(EBinding *binding);
+
+EMutualBinding *e_mutual_binding_new		(GObject *object1,
+						 const gchar *property1,
+						 GObject *object2,
+						 const gchar *property2);
+EMutualBinding *e_mutual_binding_new_full	(GObject *object1,
+						 const gchar *property1,
+						 GObject *object2,
+						 const gchar *property2,
+						 EBindingTransform transform,
+						 EBindingTransform reverse_transform,
+						 GDestroyNotify destroy_notify,
+						 gpointer user_data);
+EMutualBinding *e_mutual_binding_new_with_negation
+						(GObject *object1,
+						 const gchar *property1,
+						 GObject *object2,
+						 const gchar *property2);
+void		e_mutual_binding_unbind		(EMutualBinding *binding);
+
+
+/* Useful transformation functions */
+gboolean	e_binding_transform_color_to_string
+						(const GValue *src_value,
+						 GValue *dst_value,
+						 gpointer user_data);
+gboolean	e_binding_transform_string_to_color
+						(const GValue *src_value,
+						 GValue *dst_value,
+						 gpointer user_data);
+
+G_END_DECLS
+
+#endif /* E_BINDING_H */
Index: e-util/e-util.c
===================================================================
--- e-util/e-util.c	(revision 37527)
+++ e-util/e-util.c	(working copy)
@@ -186,6 +186,183 @@ exit:
 }
 
 /**
+ * e_lookup_action:
+ * @ui_manager: a #GtkUIManager
+ * @action_name: the name of an action
+ *
+ * Returns the first #GtkAction named @action_name by traversing the
+ * list of action groups in @ui_manager.  If no such action exists, the
+ * function emits a critical warning before returning %NULL, since this
+ * probably indicates a programming error and most code is not prepared
+ * to deal with lookup failures.
+ *
+ * Returns: the first #GtkAction named @action_name
+ **/
+GtkAction *
+e_lookup_action (GtkUIManager *ui_manager,
+                 const gchar *action_name)
+{
+	GtkAction *action = NULL;
+	GList *iter;
+
+	g_return_val_if_fail (GTK_IS_UI_MANAGER (ui_manager), NULL);
+	g_return_val_if_fail (action_name != NULL, NULL);
+
+	iter = gtk_ui_manager_get_action_groups (ui_manager);
+
+	while (iter != NULL) {
+		GtkActionGroup *action_group = iter->data;
+
+		action = gtk_action_group_get_action (
+			action_group, action_name);
+		if (action != NULL)
+			return action;
+
+		iter = g_list_next (iter);
+	}
+
+	g_critical ("%s: action `%s' not found", G_STRFUNC, action_name);
+
+	return NULL;
+}
+
+/**
+ * e_lookup_action_group:
+ * @ui_manager: a #GtkUIManager
+ * @group_name: the name of an action group
+ *
+ * Returns the #GtkActionGroup in @ui_manager named @group_name.  If no
+ * such action group exists, the function emits a critical warnings before
+ * returning %NULL, since this probably indicates a programming error and
+ * most code is not prepared to deal with lookup failures.
+ *
+ * Returns: the #GtkActionGroup named @group_name
+ **/
+GtkActionGroup *
+e_lookup_action_group (GtkUIManager *ui_manager,
+                       const gchar *group_name)
+{
+	GList *iter;
+
+	g_return_val_if_fail (GTK_IS_UI_MANAGER (ui_manager), NULL);
+	g_return_val_if_fail (group_name != NULL, NULL);
+
+	iter = gtk_ui_manager_get_action_groups (ui_manager);
+
+	while (iter != NULL) {
+		GtkActionGroup *action_group = iter->data;
+		const gchar *name;
+
+		name = gtk_action_group_get_name (action_group);
+		if (strcmp (name, group_name) == 0)
+			return action_group;
+
+		iter = g_list_next (iter);
+	}
+
+	g_critical ("%s: action group `%s' not found", G_STRFUNC, group_name);
+
+	return NULL;
+}
+
+/**
+ * e_load_ui_definition:
+ * @ui_manager: a #GtkUIManager
+ * @basename: basename of the UI definition file
+ *
+ * Loads a UI definition into @ui_manager from Evolution's UI directory.
+ * Failure here is fatal, since the application can't function without
+ * its UI definitions.
+ *
+ * Returns: The merge ID for the merged UI.  The merge ID can be used to
+ *          unmerge the UI with gtk_ui_manager_remove_ui().
+ **/
+guint
+e_load_ui_definition (GtkUIManager *ui_manager,
+                      const gchar *basename)
+{
+	gchar *filename;
+	guint merge_id;
+	GError *error = NULL;
+
+	g_return_val_if_fail (GTK_IS_UI_MANAGER (ui_manager), 0);
+	g_return_val_if_fail (basename != NULL, 0);
+
+	filename = g_build_filename (EVOLUTION_UIDIR, basename, NULL);
+	merge_id = gtk_ui_manager_add_ui_from_file (
+		ui_manager, filename, &error);
+	g_free (filename);
+
+	if (error != NULL) {
+		g_error ("%s: %s", basename, error->message);
+		g_assert_not_reached ();
+	}
+
+	return merge_id;
+}
+
+/**
+ * e_action_compare_by_label:
+ * @action1: a #GtkAction
+ * @action2: a #GtkAction
+ *
+ * Compares the labels for @action1 and @action2 using g_utf8_collate().
+ *
+ * Returns: &lt; 0 if @action1 compares before @action2, 0 if they
+ *          compare equal, &gt; 0 if @action1 compares after @action2
+ **/
+gint
+e_action_compare_by_label (GtkAction *action1,
+                           GtkAction *action2)
+{
+	gchar *label1;
+	gchar *label2;
+	gint result;
+
+	/* XXX This is horribly inefficient but will generally only be
+	 *     used on short lists of actions during UI construction. */
+
+	if (action1 == action2)
+		return 0;
+
+	g_object_get (action1, "label", &label1, NULL);
+	g_object_get (action2, "label", &label2, NULL);
+
+	result = g_utf8_collate (label1, label2);
+
+	g_free (label1);
+	g_free (label2);
+
+	return result;
+}
+
+/**
+ * e_action_group_remove_all_actions:
+ * @action_group: a #GtkActionGroup
+ *
+ * Removes all actions from the action group.
+ **/
+void
+e_action_group_remove_all_actions (GtkActionGroup *action_group)
+{
+	GList *list, *iter;
+
+	/* XXX I've proposed this function for inclusion in GTK+.
+         *     GtkActionGroup stores actions in an internal hash
+         *     table and can do this more efficiently by calling
+         *     g_hash_table_remove_all().
+         *
+         *     http://bugzilla.gnome.org/show_bug.cgi?id=550485 */
+
+	g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
+
+	list = gtk_action_group_list_actions (action_group);
+	for (iter = list; iter != NULL; iter = iter->next)
+		gtk_action_group_remove_action (action_group, iter->data);
+	g_list_free (list);
+}
+
+/**
  * e_str_without_underscores:
  * @s: the string to strip underscores from.
  *
@@ -1325,3 +1502,33 @@ e_util_get_category_filter_options (void
 
 	return g_slist_reverse (res);
 }
+
+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;
+}
Index: e-util/e-util.h
===================================================================
--- e-util/e-util.h	(revision 37527)
+++ e-util/e-util.h	(working copy)
@@ -27,6 +27,7 @@
 #include <gtk/gtk.h>
 #include <limits.h>
 #include <gconf/gconf-client.h>
+#include <camel/camel-object.h>
 #include <cairo.h>
 
 #include <e-util/e-util-marshal.h>
@@ -46,6 +47,16 @@ void		e_show_uri			(GtkWindow *parent,
 						 const gchar *uri);
 void		e_display_help			(GtkWindow *parent,
 						 const gchar *link_id);
+GtkAction *	e_lookup_action			(GtkUIManager *ui_manager,
+						 const gchar *action_name);
+GtkActionGroup *e_lookup_action_group		(GtkUIManager *ui_manager,
+						 const gchar *group_name);
+guint		e_load_ui_definition		(GtkUIManager *ui_manager,
+						 const gchar *basename);
+gint		e_action_compare_by_label	(GtkAction *action1,
+						 GtkAction *action2);
+void		e_action_group_remove_all_actions
+						(GtkActionGroup *action_group);
 
 char *		e_str_without_underscores	(const char *s);
 gint		e_str_compare			(gconstpointer x,
@@ -130,6 +141,11 @@ gboolean	e_util_read_file		(const gchar 
 
 GSList *e_util_get_category_filter_options      (void);
 
+/* Camel uses its own object system, so we have to box
+ * CamelObjects to safely use them as GObject properties. */
+#define E_TYPE_CAMEL_OBJECT (e_camel_object_get_type ())
+GType		e_camel_object_get_type		(void);
+
 G_END_DECLS
 
 #endif /* _E_UTIL_H_ */
Index: e-util/Makefile.am
===================================================================
--- e-util/Makefile.am	(revision 37527)
+++ e-util/Makefile.am	(working copy)
@@ -41,6 +41,7 @@ privsolib_LTLIBRARIES = libeutil.la libe
 
 eutilinclude_HEADERS = 				\
 	e-bconf-map.h				\
+	e-binding.h				\
 	e-categories-config.h			\
 	e-config.h				\
 	e-config-listener.h			\
@@ -83,6 +84,7 @@ libeutil_la_SOURCES =				\
 	$(eutilinclude_HEADERS)			\
 	e-util-marshal.c			\
 	e-bconf-map.c				\
+	e-binding.c				\
 	e-categories-config.c			\
 	e-config.c				\
 	e-config-listener.c			\
Index: e-util/e-binding.c
===================================================================
--- e-util/e-binding.c	(revision 0)
+++ e-util/e-binding.c	(revision 0)
@@ -0,0 +1,536 @@
+/*
+ * e-binding.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)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-binding.h"
+
+static gboolean
+e_binding_transform_negate (const GValue *src_value,
+                            GValue *dst_value)
+{
+	if (!g_value_transform (src_value, dst_value))
+		return FALSE;
+
+	g_value_set_boolean (dst_value, !g_value_get_boolean (dst_value));
+
+	return TRUE;
+}
+
+static void
+e_bind_properties_transfer (GObject *src_object,
+                            GParamSpec *src_pspec,
+                            GObject *dst_object,
+                            GParamSpec *dst_pspec,
+                            EBindingTransform  transform,
+                            gpointer user_data)
+{
+	const gchar *src_name;
+	const gchar *dst_name;
+	gboolean result;
+	GValue src_value = { 0, };
+	GValue dst_value = { 0, };
+
+	src_name = g_param_spec_get_name (src_pspec);
+	dst_name = g_param_spec_get_name (dst_pspec);
+
+	g_value_init (&src_value, G_PARAM_SPEC_VALUE_TYPE (src_pspec));
+	g_object_get_property (src_object, src_name, &src_value);
+
+	g_value_init (&dst_value, G_PARAM_SPEC_VALUE_TYPE (dst_pspec));
+	result = (*transform) (&src_value, &dst_value, user_data);
+
+	g_value_unset (&src_value);
+
+	g_return_if_fail (result);
+
+	g_param_value_validate (dst_pspec, &dst_value);
+	g_object_set_property (dst_object, dst_name, &dst_value);
+	g_value_unset (&dst_value);
+}
+
+static void
+e_bind_properties_notify (GObject *src_object,
+                          GParamSpec *src_pspec,
+                          gpointer data)
+{
+	EBindingLink *link = data;
+
+	/* Block the destination handler for mutual bindings,
+	 * so we don't recurse here. */
+	if (link->dst_handler != 0)
+		g_signal_handler_block (link->dst_object, link->dst_handler);
+
+	e_bind_properties_transfer (
+		src_object, src_pspec,
+		link->dst_object, link->dst_pspec,
+		link->transform, link->user_data);
+
+	/* Unblock destination handler. */
+	if (link->dst_handler != 0)
+		g_signal_handler_unblock (link->dst_object, link->dst_handler);
+}
+
+static void
+e_binding_on_dst_object_destroy (gpointer  data,
+                                 GObject  *object)
+{
+	EBinding *binding = data;
+
+	binding->link.dst_object = NULL;
+
+	/* Calls e_binding_on_disconnect() */
+	g_signal_handler_disconnect (
+		binding->src_object, binding->link.handler);
+}
+
+static void
+e_binding_on_disconnect (gpointer data,
+                         GClosure *closure)
+{
+	EBindingLink *link = data;
+	EBinding *binding;
+
+	binding = (EBinding *)
+		(((gchar *) link) - G_STRUCT_OFFSET (EBinding, link));
+
+	if (binding->base.destroy != NULL)
+		binding->base.destroy (link->user_data);
+
+	if (link->dst_object != NULL)
+		g_object_weak_unref (
+			link->dst_object,
+			e_binding_on_dst_object_destroy, binding);
+
+	g_slice_free (EBinding, binding);
+}
+
+/* Recursively calls e_mutual_binding_on_disconnect_object2() */
+static void
+e_mutual_binding_on_disconnect_object1 (gpointer data,
+                                        GClosure *closure)
+{
+	EMutualBinding *binding;
+	EBindingLink *link = data;
+	GObject *object2;
+
+	binding = (EMutualBinding *)
+		(((gchar *) link) - G_STRUCT_OFFSET (EMutualBinding, direct));
+	binding->reverse.dst_object = NULL;
+
+	object2 = binding->direct.dst_object;
+	if (object2 != NULL) {
+		if (binding->base.destroy != NULL)
+			binding->base.destroy (binding->direct.user_data);
+		binding->direct.dst_object = NULL;
+		g_signal_handler_disconnect (object2, binding->reverse.handler);
+		g_slice_free (EMutualBinding, binding);
+	}
+}
+
+/* Recursively calls e_mutual_binding_on_disconnect_object1() */
+static void
+e_mutual_binding_on_disconnect_object2 (gpointer data,
+                                        GClosure *closure)
+{
+	EMutualBinding *binding;
+	EBindingLink *link = data;
+	GObject *object1;
+
+	binding = (EMutualBinding *)
+		(((gchar *) link) - G_STRUCT_OFFSET (EMutualBinding, reverse));
+	binding->direct.dst_object = NULL;
+
+	object1 = binding->reverse.dst_object;
+	if (object1 != NULL) {
+		binding->reverse.dst_object = NULL;
+		g_signal_handler_disconnect (object1, binding->direct.handler);
+	}
+}
+
+static void
+e_binding_link_init (EBindingLink *link,
+                     GObject *src_object,
+                     const gchar *src_property,
+                     GObject *dst_object,
+                     GParamSpec *dst_pspec,
+                     EBindingTransform transform,
+                     GClosureNotify destroy_notify,
+                     gpointer user_data)
+{
+	gchar *signal_name;
+
+	link->dst_object = dst_object;
+	link->dst_pspec = dst_pspec;
+	link->dst_handler = 0;
+	link->transform = transform;
+	link->user_data = user_data;
+
+	signal_name = g_strconcat ("notify::", src_property, NULL);
+	link->handler = g_signal_connect_data (
+		src_object, signal_name,
+		G_CALLBACK (e_bind_properties_notify),
+		link, destroy_notify, 0);
+	g_free (signal_name);
+}
+
+/**
+ * e_binding_new:
+ * @src_object: The source #GObject.
+ * @src_property: The name of the property to bind from.
+ * @dst_object: The destination #GObject.
+ * @dst_property: The name of the property to bind to.
+ *
+ * One-way binds @src_property in @src_object to @dst_property
+ * in @dst_object.
+ *
+ * Before binding the value of @dst_property is set to the
+ * value of @src_property.
+ *
+ * Returns: The descriptor of the binding. It is automatically
+ *          removed if one of the objects is finalized.
+ **/
+EBinding *
+e_binding_new (GObject *src_object,
+               const gchar *src_property,
+               GObject *dst_object,
+               const gchar *dst_property)
+{
+	return e_binding_new_full (
+		src_object, src_property,
+		dst_object, dst_property,
+		NULL, NULL, NULL);
+}
+
+/**
+ * e_binding_new_full:
+ * @src_object: The source #GObject.
+ * @src_property: The name of the property to bind from.
+ * @dst_object: The destination #GObject.
+ * @dst_property: The name of the property to bind to.
+ * @transform: Transformation function or %NULL.
+ * @destroy_notify: Callback function that is called on
+ *                  disconnection with @user_data or %NULL.
+ * @user_data: User data associated with the binding.
+ *
+ * One-way binds @src_property in @src_object to @dst_property
+ * in @dst_object.
+ *
+ * Before binding the value of @dst_property is set to the
+ * value of @src_property.
+ *
+ * Returns: The descriptor of the binding. It is automatically
+ *          removed if one of the objects is finalized.
+ **/
+EBinding *
+e_binding_new_full (GObject *src_object,
+                    const gchar *src_property,
+                    GObject *dst_object,
+                    const gchar *dst_property,
+                    EBindingTransform transform,
+                    GDestroyNotify destroy_notify,
+                    gpointer user_data)
+{
+	EBinding *binding;
+	GParamSpec *src_pspec;
+	GParamSpec *dst_pspec;
+
+	g_return_val_if_fail (G_IS_OBJECT (src_object), NULL);
+	g_return_val_if_fail (G_IS_OBJECT (dst_object), NULL);
+
+	src_pspec = g_object_class_find_property (
+		G_OBJECT_GET_CLASS (src_object), src_property);
+	dst_pspec = g_object_class_find_property (
+		G_OBJECT_GET_CLASS (dst_object), dst_property);
+
+	if (transform == NULL)
+		transform = (EBindingTransform) g_value_transform;
+
+	e_bind_properties_transfer (
+		src_object, src_pspec,
+		dst_object, dst_pspec,
+		transform, user_data);
+
+	binding = g_slice_new (EBinding);
+	binding->src_object = src_object;
+	binding->base.destroy = destroy_notify;
+
+	e_binding_link_init (
+		&binding->link, src_object, src_property, dst_object,
+		dst_pspec, transform, e_binding_on_disconnect, user_data);
+
+	g_object_weak_ref (
+		dst_object, e_binding_on_dst_object_destroy, binding);
+
+	return binding;
+}
+
+/**
+ * e_binding_new_with_negation:
+ * @src_object: The source #GObject.
+ * @src_property: The name of the property to bind from.
+ * @dst_object: The destination #GObject.
+ * @dst_property: The name of the property to bind to.
+ *
+ * Convenience function for binding with boolean negation of value.
+ *
+ * Return: The descriptor of the binding. It is automatically
+ *         removed if one of the objects is finalized.
+ **/
+EBinding *
+e_binding_new_with_negation (GObject *src_object,
+                             const gchar *src_property,
+                             GObject *dst_object,
+                             const gchar *dst_property)
+{
+	EBindingTransform transform;
+
+	transform = (EBindingTransform) e_binding_transform_negate;
+
+	return e_binding_new_full (
+		src_object, src_property,
+		dst_object, dst_property,
+		transform, NULL, NULL);
+}
+
+/**
+ * e_binding_unbind:
+ * @binding: An #EBinding to unbind.
+ *
+ * Disconnects the binding between two properties. Should be
+ * rarely used by applications.
+ *
+ * This functions also calls the @destroy_notify function that
+ * was specified when @binding was created.
+ **/
+void
+e_binding_unbind (EBinding *binding)
+{
+	g_signal_handler_disconnect (
+		binding->src_object, binding->link.handler);
+}
+
+/**
+ * e_mutual_binding_new:
+ * @object1 : The first #GObject.
+ * @property1: The first property to bind.
+ * @object2 : The second #GObject.
+ * @property2: The second property to bind.
+ *
+ * Mutually binds values of two properties.
+ *
+ * Before binding the value of @property2 is set to the value
+ * of @property1.
+ *
+ * Returns: The descriptor of the binding. It is automatically
+ *          removed if one of the objects is finalized.
+ **/
+EMutualBinding *
+e_mutual_binding_new (GObject *object1,
+                      const gchar *property1,
+                      GObject *object2,
+                      const gchar *property2)
+{
+	return e_mutual_binding_new_full (
+		object1, property1,
+		object2, property2,
+		NULL, NULL, NULL, NULL);
+}
+
+/**
+ * e_mutual_binding_new_full:
+ * @object1: The first #GObject.
+ * @property1: The first property to bind.
+ * @object2: The second #GObject.
+ * @property2: The second property to bind.
+ * @transform: Transformation function or %NULL.
+ * @reverse_transform: The inverse transformation function or %NULL.
+ * @destroy_notify: Callback function called on disconnection with
+ *                  @user_data as argument or %NULL.
+ * @user_data: User data associated with the binding.
+ *
+ * Mutually binds values of two properties.
+ *
+ * Before binding the value of @property2 is set to the value of
+ * @property1.
+ *
+ * Both @transform and @reverse_transform should simultaneously be
+ * %NULL or non-%NULL. If they are non-%NULL, they should be reverse
+ * in each other.
+ *
+ * Returns: The descriptor of the binding. It is automatically
+ *          removed if one of the objects is finalized.
+ **/
+EMutualBinding *
+e_mutual_binding_new_full (GObject *object1,
+                           const gchar *property1,
+                           GObject *object2,
+                           const gchar *property2,
+                           EBindingTransform transform,
+                           EBindingTransform reverse_transform,
+                           GDestroyNotify destroy_notify,
+                           gpointer user_data)
+{
+	EMutualBinding *binding;
+	GParamSpec *pspec1;
+	GParamSpec *pspec2;
+
+	g_return_val_if_fail (G_IS_OBJECT (object1), NULL);
+	g_return_val_if_fail (G_IS_OBJECT (object2), NULL);
+
+	pspec1 = g_object_class_find_property (
+		G_OBJECT_GET_CLASS (object1), property1);
+	pspec2 = g_object_class_find_property (
+		G_OBJECT_GET_CLASS (object2), property2);
+
+	if (transform == NULL)
+		transform = (EBindingTransform) g_value_transform;
+
+	if (reverse_transform == NULL)
+		reverse_transform = (EBindingTransform) g_value_transform;
+
+	e_bind_properties_transfer (
+		object1, pspec1, object2,
+		pspec2, transform, user_data);
+
+	binding = g_slice_new (EMutualBinding);
+	binding->base.destroy = destroy_notify;
+
+	e_binding_link_init (
+		&binding->direct,
+		object1, property1, object2, pspec2, transform,
+		e_mutual_binding_on_disconnect_object1, user_data);
+
+	e_binding_link_init (
+		&binding->reverse,
+		object2, property2, object1, pspec1, reverse_transform,
+		e_mutual_binding_on_disconnect_object2, user_data);
+
+	/* Tell each link about the reverse link for mutual bindings,
+	 * to make sure that we do not ever recurse in notify (yeah,
+	 * the GObject notify dispatching is really weird!). */
+	binding->direct.dst_handler = binding->reverse.handler;
+	binding->reverse.dst_handler = binding->direct.handler;
+
+	return binding;
+}
+
+/**
+ * e_mutual_binding_new_with_negation:
+ * @object1: The first #GObject.
+ * @property1: The first property to bind.
+ * @object2: The second #GObject.
+ * @property2: The second property to bind.
+ *
+ * Convenience function for binding with boolean negation of value.
+ *
+ * Returns: The descriptor of the binding. It is automatically removed
+ *          if one of the objects if finalized.
+ **/
+EMutualBinding*
+e_mutual_binding_new_with_negation (GObject *object1,
+                                    const gchar *property1,
+                                    GObject *object2,
+                                    const gchar *property2)
+{
+	EBindingTransform transform;
+
+	transform = (EBindingTransform) e_binding_transform_negate;
+
+	return e_mutual_binding_new_full (
+		object1, property1,
+		object2, property2,
+		transform, transform,
+		NULL, NULL);
+}
+
+/**
+ * e_mutual_binding_unbind:
+ * @binding: An #EMutualBinding to unbind.
+ *
+ * Disconnects the binding between two properties. Should be
+ * rarely used by applications.
+ *
+ * This functions also calls the @destroy_notify function that
+ * was specified when @binding was created.
+ **/
+void
+e_mutual_binding_unbind (EMutualBinding *binding)
+{
+	g_signal_handler_disconnect (
+		binding->reverse.dst_object, binding->direct.handler);
+}
+
+/**
+ * e_binding_transform_color_to_string:
+ * @src_value: a #GValue of type #GDK_TYPE_COLOR
+ * @dst_value: a #GValue of type #G_TYPE_STRING
+ * @user_data: not used
+ *
+ * Transforms a #GdkColor value to a color string specification.
+ *
+ * Returns: %TRUE always
+ **/
+gboolean
+e_binding_transform_color_to_string (const GValue *src_value,
+                                     GValue *dst_value,
+                                     gpointer user_data)
+{
+	const GdkColor *color;
+	gchar *string;
+
+	color = g_value_get_boxed (src_value);
+	string = gdk_color_to_string (color);
+	g_value_set_string (dst_value, string);
+	g_free (string);
+
+	return TRUE;
+}
+
+/**
+ * e_binding_transform_string_to_color:
+ * @src_value: a #GValue of type #G_TYPE_STRING
+ * @dst_value: a #GValue of type #GDK_TYPE_COLOR
+ * @user_data: not used
+ *
+ * Transforms a color string specification to a #GdkColor.
+ *
+ * Returns: %TRUE if color string specification was valid
+ **/
+gboolean
+e_binding_transform_string_to_color (const GValue *src_value,
+                                     GValue *dst_value,
+                                     gpointer user_data)
+{
+	GdkColor color;
+	const gchar *string;
+	gboolean success = FALSE;
+
+	string = g_value_get_string (src_value);
+	if (gdk_color_parse (string, &color)) {
+		g_value_set_boxed (dst_value, &color);
+		success = TRUE;
+	}
+
+	return success;
+}
Index: widgets/text/Makefile.am
===================================================================
--- widgets/text/Makefile.am	(revision 37527)
+++ widgets/text/Makefile.am	(working copy)
@@ -5,6 +5,7 @@ endif
 INCLUDES =					\
 	-I$(top_srcdir)				\
 	-I$(top_srcdir)/widgets			\
+	$(E_UTIL_CFLAGS)			\
 	$(GNOME_PLATFORM_CFLAGS)		\
 	-DG_LOG_DOMAIN=\"e-text\"
 
@@ -30,5 +31,6 @@ libetext_la_LIBADD = 					\
 	$(top_builddir)/e-util/libeutil.la		\
 	$(top_builddir)/a11y/libevolution-a11y.la	\
 	$(top_builddir)/widgets/table/libetable.la	\
+	$(E_UTIL_LIBS)					\
 	$(GNOME_PLATFORM_LIBS)				\
 	$(REGEX_LIBS)
Index: widgets/misc/e-attachment-dialog.c
===================================================================
--- widgets/misc/e-attachment-dialog.c	(revision 0)
+++ widgets/misc/e-attachment-dialog.c	(revision 0)
@@ -0,0 +1,432 @@
+/*
+ * e-attachment-dialog.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-dialog.h"
+
+#include <glib/gi18n.h>
+
+#define E_ATTACHMENT_DIALOG_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_ATTACHMENT_DIALOG, EAttachmentDialogPrivate))
+
+struct _EAttachmentDialogPrivate {
+	EAttachment *attachment;
+	GtkWidget *display_name_entry;
+	GtkWidget *description_entry;
+	GtkWidget *content_type_label;
+	GtkWidget *disposition_checkbox;
+};
+
+enum {
+	PROP_0,
+	PROP_ATTACHMENT
+};
+
+static gpointer parent_class;
+
+static void
+attachment_dialog_update (EAttachmentDialog *dialog)
+{
+	EAttachment *attachment;
+	GFileInfo *file_info;
+	GtkWidget *widget;
+	const gchar *content_type;
+	const gchar *display_name;
+	const gchar *description;
+	const gchar *disposition;
+	gchar *type_description = NULL;
+	gboolean sensitive;
+	gboolean active;
+
+	attachment = e_attachment_dialog_get_attachment (dialog);
+
+	if (attachment != NULL) {
+		file_info = e_attachment_get_file_info (attachment);
+		description = e_attachment_get_description (attachment);
+		disposition = e_attachment_get_disposition (attachment);
+	} else {
+		file_info = NULL;
+		description = NULL;
+		disposition = NULL;
+	}
+
+	if (file_info != NULL) {
+		content_type = g_file_info_get_content_type (file_info);
+		display_name = g_file_info_get_display_name (file_info);
+	} else {
+		content_type = NULL;
+		display_name = NULL;
+	}
+
+	if (content_type != NULL) {
+		gchar *comment;
+		gchar *mime_type;
+
+		comment = g_content_type_get_description (content_type);
+		mime_type = g_content_type_get_mime_type (content_type);
+
+		type_description =
+			g_strdup_printf ("%s (%s)", comment, mime_type);
+
+		g_free (comment);
+		g_free (mime_type);
+	}
+
+	sensitive = G_IS_FILE_INFO (file_info);
+
+	gtk_dialog_set_response_sensitive (
+		GTK_DIALOG (dialog), GTK_RESPONSE_OK, sensitive);
+
+	widget = dialog->priv->display_name_entry;
+	gtk_widget_set_sensitive (widget, sensitive);
+	if (display_name != NULL)
+		gtk_entry_set_text (GTK_ENTRY (widget), display_name);
+
+	widget = dialog->priv->description_entry;
+	gtk_widget_set_sensitive (widget, sensitive);
+	if (description != NULL)
+		gtk_entry_set_text (GTK_ENTRY (widget), description);
+
+	widget = dialog->priv->content_type_label;
+	gtk_label_set_text (GTK_LABEL (widget), type_description);
+
+	active = (g_strcmp0 (disposition, "inline") == 0);
+	widget = dialog->priv->disposition_checkbox;
+	gtk_widget_set_sensitive (widget, sensitive);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), active);
+
+	g_free (type_description);
+}
+
+static void
+attachment_dialog_set_property (GObject *object,
+                                guint property_id,
+                                const GValue *value,
+                                GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ATTACHMENT:
+			e_attachment_dialog_set_attachment (
+				E_ATTACHMENT_DIALOG (object),
+				g_value_get_object (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_dialog_get_property (GObject *object,
+                                guint property_id,
+                                GValue *value,
+                                GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ATTACHMENT:
+			g_value_set_object (
+				value, e_attachment_dialog_get_attachment (
+				E_ATTACHMENT_DIALOG (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_dialog_dispose (GObject *object)
+{
+	EAttachmentDialogPrivate *priv;
+
+	priv = E_ATTACHMENT_DIALOG_GET_PRIVATE (object);
+
+	if (priv->attachment != NULL) {
+		g_object_unref (priv->attachment);
+		priv->attachment = NULL;
+	}
+
+	if (priv->display_name_entry != NULL) {
+		g_object_unref (priv->display_name_entry);
+		priv->display_name_entry = NULL;
+	}
+
+	if (priv->description_entry != NULL) {
+		g_object_unref (priv->description_entry);
+		priv->description_entry = NULL;
+	}
+
+	if (priv->content_type_label != NULL) {
+		g_object_unref (priv->content_type_label);
+		priv->content_type_label = NULL;
+	}
+
+	if (priv->disposition_checkbox != NULL) {
+		g_object_unref (priv->disposition_checkbox);
+		priv->disposition_checkbox = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+attachment_dialog_map (GtkWidget *widget)
+{
+	GtkWidget *action_area;
+	GtkWidget *content_area;
+
+	/* Chain up to parent's map() method. */
+	GTK_WIDGET_CLASS (parent_class)->map (widget);
+
+	/* XXX Override GtkDialog's broken style property defaults. */
+	action_area = gtk_dialog_get_action_area (GTK_DIALOG (widget));
+	content_area = gtk_dialog_get_content_area (GTK_DIALOG (widget));
+
+	gtk_box_set_spacing (GTK_BOX (content_area), 12);
+	gtk_container_set_border_width (GTK_CONTAINER (action_area), 0);
+	gtk_container_set_border_width (GTK_CONTAINER (content_area), 12);
+}
+
+static void
+attachment_dialog_response (GtkDialog *dialog,
+                            gint response_id)
+{
+	EAttachmentDialogPrivate *priv;
+	EAttachment *attachment;
+	GtkToggleButton *button;
+	GFileInfo *file_info;
+	const gchar *attribute;
+	const gchar *text;
+	gboolean active;
+
+	if (response_id != GTK_RESPONSE_OK)
+		return;
+
+	priv = E_ATTACHMENT_DIALOG_GET_PRIVATE (dialog);
+	g_return_if_fail (E_IS_ATTACHMENT (priv->attachment));
+	attachment = priv->attachment;
+
+	file_info = e_attachment_get_file_info (attachment);
+	g_return_if_fail (G_IS_FILE_INFO (file_info));
+
+	attribute = G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME;
+	text = gtk_entry_get_text (GTK_ENTRY (priv->display_name_entry));
+	g_file_info_set_attribute_string (file_info, attribute, text);
+
+	attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
+	text = gtk_entry_get_text (GTK_ENTRY (priv->description_entry));
+	g_file_info_set_attribute_string (file_info, attribute, text);
+
+	button = GTK_TOGGLE_BUTTON (priv->disposition_checkbox);
+	active = gtk_toggle_button_get_active (button);
+	text = active ? "inline" : "attachment";
+	e_attachment_set_disposition (attachment, text);
+}
+
+static void
+attachment_dialog_class_init (EAttachmentDialogClass *class)
+{
+	GObjectClass *object_class;
+	GtkWidgetClass *widget_class;
+	GtkDialogClass *dialog_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EAttachmentDialogPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = attachment_dialog_set_property;
+	object_class->get_property = attachment_dialog_get_property;
+	object_class->dispose = attachment_dialog_dispose;
+
+	widget_class = GTK_WIDGET_CLASS (class);
+	widget_class->map = attachment_dialog_map;
+
+	dialog_class = GTK_DIALOG_CLASS (class);
+	dialog_class->response = attachment_dialog_response;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ATTACHMENT,
+		g_param_spec_object (
+			"attachment",
+			"Attachment",
+			NULL,
+			E_TYPE_ATTACHMENT,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+}
+
+static void
+attachment_dialog_init (EAttachmentDialog *dialog)
+{
+	GtkWidget *container;
+	GtkWidget *widget;
+
+	dialog->priv = E_ATTACHMENT_DIALOG_GET_PRIVATE (dialog);
+
+	gtk_dialog_add_button (
+		GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+	gtk_dialog_add_button (
+		GTK_DIALOG (dialog), GTK_STOCK_OK, GTK_RESPONSE_OK);
+	gtk_window_set_icon_name (
+		GTK_WINDOW (dialog), "mail-attachment");
+	gtk_window_set_title (
+		GTK_WINDOW (dialog), _("Attachment Properties"));
+
+	gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+	container = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+	widget = gtk_table_new (4, 2, FALSE);
+	gtk_table_set_col_spacings (GTK_TABLE (widget), 6);
+	gtk_table_set_row_spacings (GTK_TABLE (widget), 6);
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_entry_new ();
+	gtk_entry_set_activates_default (GTK_ENTRY (widget), TRUE);
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		1, 2, 0, 1, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+	dialog->priv->display_name_entry = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	widget = gtk_label_new_with_mnemonic (_("_Filename:"));
+	gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
+	gtk_label_set_mnemonic_widget (
+		GTK_LABEL (widget), dialog->priv->display_name_entry);
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		0, 1, 0, 1, GTK_FILL, 0, 0, 0);
+	gtk_widget_show (widget);
+
+	widget = gtk_entry_new ();
+	gtk_entry_set_activates_default (GTK_ENTRY (widget), TRUE);
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		1, 2, 1, 2, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+	dialog->priv->description_entry = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	widget = gtk_label_new_with_mnemonic (_("_Description:"));
+	gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
+	gtk_label_set_mnemonic_widget (
+		GTK_LABEL (widget), dialog->priv->description_entry);
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		0, 1, 1, 2, GTK_FILL, 0, 0, 0);
+	gtk_widget_show (widget);
+
+	widget = gtk_label_new (NULL);
+	gtk_label_set_selectable (GTK_LABEL (widget), TRUE);
+	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		1, 2, 2, 3, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+	dialog->priv->content_type_label = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	widget = gtk_label_new (_("MIME Type:"));
+	gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		0, 1, 2, 3, GTK_FILL, 0, 0, 0);
+	gtk_widget_show (widget);
+
+	widget = gtk_check_button_new_with_mnemonic (
+		_("_Suggest automatic display of attachment"));
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		0, 2, 3, 4, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+	dialog->priv->disposition_checkbox = g_object_ref (widget);
+	gtk_widget_show (widget);
+}
+
+GType
+e_attachment_dialog_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EAttachmentDialogClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) attachment_dialog_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_init */
+			sizeof (EAttachmentDialog),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) attachment_dialog_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_DIALOG, "EAttachmentDialog", &type_info, 0);
+	}
+
+	return type;
+}
+
+GtkWidget *
+e_attachment_dialog_new (GtkWindow *parent,
+                         EAttachment *attachment)
+{
+	if (parent != NULL)
+	 	g_return_val_if_fail (GTK_IS_WINDOW (parent), NULL);
+	if (attachment != NULL)
+		g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+
+	return g_object_new (
+		E_TYPE_ATTACHMENT_DIALOG,
+		"transient-for", parent, "attachment", attachment, NULL);
+}
+
+EAttachment *
+e_attachment_dialog_get_attachment (EAttachmentDialog *dialog)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT_DIALOG (dialog), NULL);
+
+	return dialog->priv->attachment;
+}
+
+void
+e_attachment_dialog_set_attachment (EAttachmentDialog *dialog,
+                                    EAttachment *attachment)
+{
+	g_return_if_fail (E_IS_ATTACHMENT_DIALOG (dialog));
+
+	if (attachment != NULL) {
+		g_return_if_fail (E_IS_ATTACHMENT (attachment));
+		g_object_ref (attachment);
+	}
+
+	if (dialog->priv->attachment != NULL)
+		g_object_unref (dialog->priv->attachment);
+
+	dialog->priv->attachment = attachment;
+
+	attachment_dialog_update (dialog);
+
+	g_object_notify (G_OBJECT (dialog), "attachment");
+}
Index: widgets/misc/e-attachment.c
===================================================================
--- widgets/misc/e-attachment.c	(revision 37505)
+++ widgets/misc/e-attachment.c	(working copy)
@@ -1,4 +1,5 @@
 /*
+ * e-attachment.c
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -14,870 +15,2647 @@
  * License along with the program; if not, see <http://www.gnu.org/licenses/>  
  *
  *
- * Authors:
- *			Ettore Perazzoli <ettore@ximian.com>
- *          Jeffrey Stedfast <fejj@ximian.com>
- *	     	Srinivasa Ragavan <sragavan@novell.com>
- *
  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  *
  */
 
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+#include "e-attachment.h"
+
+#include <errno.h>
+#include <config.h>
+#include <glib/gi18n.h>
+#include <camel/camel-iconv.h>
+#include <camel/camel-data-wrapper.h>
+#include <camel/camel-mime-message.h>
+#include <camel/camel-stream-filter.h>
+#include <camel/camel-stream-mem.h>
+#include <camel/camel-stream-null.h>
+#include <camel/camel-stream-vfs.h>
+
+#include "e-util/e-util.h"
+#include "e-util/e-mktemp.h"
+#include "e-attachment-store.h"
+
+#define E_ATTACHMENT_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_ATTACHMENT, EAttachmentPrivate))
+
+/* Fallback Icon */
+#define DEFAULT_ICON_NAME	"mail-attachment"
+
+/* Emblems */
+#define EMBLEM_CANCELLED	"gtk-cancel"
+#define EMBLEM_LOADING		"emblem-downloads"
+#define EMBLEM_SAVING		"document-save"
+#define EMBLEM_ENCRYPT_WEAK	"security-low"
+#define EMBLEM_ENCRYPT_STRONG	"security-high"
+#define EMBLEM_ENCRYPT_UNKNOWN	"security-medium"
+#define EMBLEM_SIGN_BAD		"stock_signature_bad"
+#define EMBLEM_SIGN_GOOD	"stock_signature-ok"
+#define EMBLEM_SIGN_UNKNOWN	"stock_signature"
+
+/* Attributes needed for EAttachmentStore columns. */
+#define ATTACHMENT_QUERY "standard::*,preview::*,thumbnail::*"
+
+struct _EAttachmentPrivate {
+	GFile *file;
+	GFileInfo *file_info;
+	GCancellable *cancellable;
+	CamelMimePart *mime_part;
+	guint emblem_timeout_id;
+	gchar *disposition;
+	gint percent;
+
+	guint can_show : 1;
+	guint loading  : 1;
+	guint saving   : 1;
+	guint shown    : 1;
+
+	camel_cipher_validity_encrypt_t encrypted;
+	camel_cipher_validity_sign_t signed_;
+
+	/* This is a reference to our row in an EAttachmentStore,
+	 * serving as a means of broadcasting "row-changed" signals.
+	 * If we are removed from the store, we lazily free the
+	 * reference when it is found to be to be invalid. */
+	GtkTreeRowReference *reference;
+};
+
+enum {
+	PROP_0,
+	PROP_CAN_SHOW,
+	PROP_DISPOSITION,
+	PROP_ENCRYPTED,
+	PROP_FILE,
+	PROP_FILE_INFO,
+	PROP_LOADING,
+	PROP_MIME_PART,
+	PROP_PERCENT,
+	PROP_REFERENCE,
+	PROP_SAVING,
+	PROP_SHOWN,
+	PROP_SIGNED
+};
+
+static gpointer parent_class;
+
+static gchar *
+attachment_get_default_charset (void)
+{
+	GConfClient *client;
+	const gchar *key;
+	gchar *charset;
+
+	/* XXX This doesn't really belong here. */
+
+	client = gconf_client_get_default ();
+	key = "/apps/evolution/mail/composer/charset";
+	charset = gconf_client_get_string (client, key, NULL);
+	if (charset == NULL || *charset == '\0') {
+		g_free (charset);
+		key = "/apps/evolution/mail/format/charset";
+		charset = gconf_client_get_string (client, key, NULL);
+		if (charset == NULL || *charset == '\0') {
+			g_free (charset);
+			charset = NULL;
+		}
+	}
+	g_object_unref (client);
+
+	if (charset == NULL)
+		charset = g_strdup (camel_iconv_locale_charset ());
+
+	if (charset == NULL)
+		charset = g_strdup ("us-ascii");
+
+	return charset;
+}
+
+static void
+attachment_update_file_info_columns (EAttachment *attachment)
+{
+	GtkTreeRowReference *reference;
+	GtkTreeModel *model;
+	GtkTreePath *path;
+	GtkTreeIter iter;
+	GFileInfo *file_info;
+	const gchar *content_type;
+	const gchar *description;
+	const gchar *display_name;
+	gchar *content_desc;
+	gchar *display_size;
+	gchar *caption;
+	goffset size;
+
+	reference = e_attachment_get_reference (attachment);
+	if (!gtk_tree_row_reference_valid (reference))
+		return;
+
+	file_info = e_attachment_get_file_info (attachment);
+	if (file_info == NULL)
+		return;
+
+	model = gtk_tree_row_reference_get_model (reference);
+	path = gtk_tree_row_reference_get_path (reference);
+	gtk_tree_model_get_iter (model, &iter, path);
+	gtk_tree_path_free (path);
+
+	content_type = g_file_info_get_content_type (file_info);
+	display_name = g_file_info_get_display_name (file_info);
+	size = g_file_info_get_size (file_info);
+
+	content_desc = g_content_type_get_description (content_type);
+	display_size = g_format_size_for_display (size);
+
+	description = e_attachment_get_description (attachment);
+	description = (description != NULL) ? description : display_name;
+
+	if (size > 0)
+		caption = g_strdup_printf (
+			"%s\n(%s)", description, display_size);
+	else
+		caption = g_strdup (description);
+
+	gtk_list_store_set (
+		GTK_LIST_STORE (model), &iter,
+		E_ATTACHMENT_STORE_COLUMN_CAPTION, caption,
+		E_ATTACHMENT_STORE_COLUMN_CONTENT_TYPE, content_desc,
+		E_ATTACHMENT_STORE_COLUMN_DESCRIPTION, description,
+		E_ATTACHMENT_STORE_COLUMN_SIZE, size,
+		-1);
+
+	g_free (content_desc);
+	g_free (display_size);
+	g_free (caption);
+}
+
+static void
+attachment_update_icon_column (EAttachment *attachment)
+{
+	GtkTreeRowReference *reference;
+	GtkTreeModel *model;
+	GtkTreePath *path;
+	GtkTreeIter iter;
+	GFileInfo *file_info;
+	GCancellable *cancellable;
+	GIcon *icon = NULL;
+	const gchar *emblem_name = NULL;
+	const gchar *thumbnail_path = NULL;
+
+	reference = e_attachment_get_reference (attachment);
+	if (!gtk_tree_row_reference_valid (reference))
+		return;
+
+	model = gtk_tree_row_reference_get_model (reference);
+	path = gtk_tree_row_reference_get_path (reference);
+	gtk_tree_model_get_iter (model, &iter, path);
+	gtk_tree_path_free (path);
+
+	cancellable = attachment->priv->cancellable;
+	file_info = e_attachment_get_file_info (attachment);
+
+	if (file_info != NULL) {
+		icon = g_file_info_get_icon (file_info);
+		thumbnail_path = g_file_info_get_attribute_byte_string (
+			file_info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
+	}
+
+	/* Prefer the thumbnail if we have one. */
+	if (thumbnail_path != NULL && *thumbnail_path != '\0') {
+		GFile *file;
+
+		file = g_file_new_for_path (thumbnail_path);
+		icon = g_file_icon_new (file);
+		g_object_unref (file);
+
+	/* Else use the standard icon for the content type. */
+	} else if (icon != NULL)
+		g_object_ref (icon);
+
+	/* Last ditch fallback.  (GFileInfo not yet loaded?) */
+	else
+		icon = g_themed_icon_new (DEFAULT_ICON_NAME);
+
+	/* Pick an emblem, limit one.  Choices listed by priority. */
+
+	if (g_cancellable_is_cancelled (cancellable))
+		emblem_name = EMBLEM_CANCELLED;
+
+	else if (e_attachment_get_loading (attachment))
+		emblem_name = EMBLEM_LOADING;
+
+	else if (e_attachment_get_saving (attachment))
+		emblem_name = EMBLEM_SAVING;
+
+	else if (e_attachment_get_encrypted (attachment))
+		switch (e_attachment_get_encrypted (attachment)) {
+			case CAMEL_CIPHER_VALIDITY_ENCRYPT_WEAK:
+				emblem_name = EMBLEM_ENCRYPT_WEAK;
+				break;
+
+			case CAMEL_CIPHER_VALIDITY_ENCRYPT_ENCRYPTED:
+				emblem_name = EMBLEM_ENCRYPT_UNKNOWN;
+				break;
+
+			case CAMEL_CIPHER_VALIDITY_ENCRYPT_STRONG:
+				emblem_name = EMBLEM_ENCRYPT_STRONG;
+				break;
+
+			default:
+				g_warn_if_reached ();
+				break;
+		}
+
+	else if (e_attachment_get_signed (attachment))
+		switch (e_attachment_get_signed (attachment)) {
+			case CAMEL_CIPHER_VALIDITY_SIGN_GOOD:
+				emblem_name = EMBLEM_SIGN_GOOD;
+				break;
+
+			case CAMEL_CIPHER_VALIDITY_SIGN_BAD:
+				emblem_name = EMBLEM_SIGN_BAD;
+				break;
+
+			case CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN:
+			case CAMEL_CIPHER_VALIDITY_SIGN_NEED_PUBLIC_KEY:
+				emblem_name = EMBLEM_SIGN_UNKNOWN;
+				break;
+
+			default:
+				g_warn_if_reached ();
+				break;
+		}
+
+	if (emblem_name != NULL) {
+		GIcon *emblemed_icon;
+		GEmblem *emblem;
+
+		emblemed_icon = g_themed_icon_new (emblem_name);
+		emblem = g_emblem_new (emblemed_icon);
+		g_object_unref (emblemed_icon);
+
+		emblemed_icon = g_emblemed_icon_new (icon, emblem);
+		g_object_unref (emblem);
+		g_object_unref (icon);
+
+		icon = emblemed_icon;
+	}
+
+	gtk_list_store_set (
+		GTK_LIST_STORE (model), &iter,
+		E_ATTACHMENT_STORE_COLUMN_ICON, icon,
+		-1);
+
+	g_object_unref (icon);
+}
+
+static void
+attachment_update_progress_columns (EAttachment *attachment)
+{
+	GtkTreeRowReference *reference;
+	GtkTreeModel *model;
+	GtkTreePath *path;
+	GtkTreeIter iter;
+	gboolean loading;
+	gboolean saving;
+	gint percent;
+
+	reference = e_attachment_get_reference (attachment);
+	if (!gtk_tree_row_reference_valid (reference))
+		return;
+
+	model = gtk_tree_row_reference_get_model (reference);
+	path = gtk_tree_row_reference_get_path (reference);
+	gtk_tree_model_get_iter (model, &iter, path);
+	gtk_tree_path_free (path);
+
+	/* Don't show progress bars until we have progress to report. */
+	percent = e_attachment_get_percent (attachment);
+	loading = e_attachment_get_loading (attachment) && (percent > 0);
+	saving = e_attachment_get_saving (attachment) && (percent > 0);
+
+	gtk_list_store_set (
+		GTK_LIST_STORE (model), &iter,
+		E_ATTACHMENT_STORE_COLUMN_LOADING, loading,
+		E_ATTACHMENT_STORE_COLUMN_PERCENT, percent,
+		E_ATTACHMENT_STORE_COLUMN_SAVING, saving,
+		-1);
+}
+
+static void
+attachment_set_file_info (EAttachment *attachment,
+                          GFileInfo *file_info)
+{
+	GtkTreeRowReference *reference;
+
+	reference = e_attachment_get_reference (attachment);
+
+	if (file_info != NULL)
+		g_object_ref (file_info);
+
+	if (attachment->priv->file_info != NULL)
+		g_object_unref (attachment->priv->file_info);
+
+	attachment->priv->file_info = file_info;
+
+	g_object_notify (G_OBJECT (attachment), "file-info");
+
+	/* Tell the EAttachmentStore its total size changed. */
+	if (gtk_tree_row_reference_valid (reference)) {
+		GtkTreeModel *model;
+		model = gtk_tree_row_reference_get_model (reference);
+		g_object_notify (G_OBJECT (model), "total-size");
+	}
+}
+
+static void
+attachment_set_loading (EAttachment *attachment,
+                        gboolean loading)
+{
+	GtkTreeRowReference *reference;
+
+	reference = e_attachment_get_reference (attachment);
+
+	attachment->priv->percent = 0;
+	attachment->priv->loading = loading;
+
+	g_object_freeze_notify (G_OBJECT (attachment));
+	g_object_notify (G_OBJECT (attachment), "percent");
+	g_object_notify (G_OBJECT (attachment), "loading");
+	g_object_thaw_notify (G_OBJECT (attachment));
+
+	if (gtk_tree_row_reference_valid (reference)) {
+		GtkTreeModel *model;
+		model = gtk_tree_row_reference_get_model (reference);
+		g_object_notify (G_OBJECT (model), "num-loading");
+	}
+}
+
+static void
+attachment_set_saving (EAttachment *attachment,
+                       gboolean saving)
+{
+	attachment->priv->percent = 0;
+	attachment->priv->saving = saving;
+
+	g_object_freeze_notify (G_OBJECT (attachment));
+	g_object_notify (G_OBJECT (attachment), "percent");
+	g_object_notify (G_OBJECT (attachment), "saving");
+	g_object_thaw_notify (G_OBJECT (attachment));
+}
+
+static void
+attachment_progress_cb (goffset current_num_bytes,
+                        goffset total_num_bytes,
+                        EAttachment *attachment)
+{
+	attachment->priv->percent =
+		(current_num_bytes * 100) / total_num_bytes;
+
+	g_object_notify (G_OBJECT (attachment), "percent");
+}
+
+static gboolean
+attachment_cancelled_timeout_cb (EAttachment *attachment)
+{
+	attachment->priv->emblem_timeout_id = 0;
+	g_cancellable_reset (attachment->priv->cancellable);
+
+	attachment_update_icon_column (attachment);
+
+	return FALSE;
+}
+
+static void
+attachment_cancelled_cb (EAttachment *attachment)
+{
+	/* Reset the GCancellable after one second.  This causes a
+	 * cancel emblem to be briefly shown on the attachment icon
+	 * as visual feedback that an operation was cancelled. */
+
+	if (attachment->priv->emblem_timeout_id > 0)
+		g_source_remove (attachment->priv->emblem_timeout_id);
+
+	attachment->priv->emblem_timeout_id = g_timeout_add_seconds (
+		1, (GSourceFunc) attachment_cancelled_timeout_cb, attachment);
+
+	attachment_update_icon_column (attachment);
+}
+
+static void
+attachment_set_property (GObject *object,
+                         guint property_id,
+                         const GValue *value,
+                         GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_CAN_SHOW:
+			e_attachment_set_can_show (
+				E_ATTACHMENT (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_DISPOSITION:
+			e_attachment_set_disposition (
+				E_ATTACHMENT (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_ENCRYPTED:
+			e_attachment_set_encrypted (
+				E_ATTACHMENT (object),
+				g_value_get_int (value));
+			return;
+
+		case PROP_FILE:
+			e_attachment_set_file (
+				E_ATTACHMENT (object),
+				g_value_get_object (value));
+			return;
+
+		case PROP_SHOWN:
+			e_attachment_set_shown (
+				E_ATTACHMENT (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_MIME_PART:
+			e_attachment_set_mime_part (
+				E_ATTACHMENT (object),
+				g_value_get_boxed (value));
+			return;
+
+		case PROP_REFERENCE:
+			e_attachment_set_reference (
+				E_ATTACHMENT (object),
+				g_value_get_boxed (value));
+			return;
+
+		case PROP_SIGNED:
+			e_attachment_set_signed (
+				E_ATTACHMENT (object),
+				g_value_get_int (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_get_property (GObject *object,
+                         guint property_id,
+                         GValue *value,
+                         GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_CAN_SHOW:
+			g_value_set_boolean (
+				value, e_attachment_get_can_show (
+				E_ATTACHMENT (object)));
+			return;
+
+		case PROP_DISPOSITION:
+			g_value_set_string (
+				value, e_attachment_get_disposition (
+				E_ATTACHMENT (object)));
+			return;
+
+		case PROP_ENCRYPTED:
+			g_value_set_int (
+				value, e_attachment_get_encrypted (
+				E_ATTACHMENT (object)));
+			return;
+
+		case PROP_FILE:
+			g_value_set_object (
+				value, e_attachment_get_file (
+				E_ATTACHMENT (object)));
+			return;
+
+		case PROP_FILE_INFO:
+			g_value_set_object (
+				value, e_attachment_get_file_info (
+				E_ATTACHMENT (object)));
+			return;
+
+		case PROP_SHOWN:
+			g_value_set_boolean (
+				value, e_attachment_get_shown (
+				E_ATTACHMENT (object)));
+			return;
+
+		case PROP_LOADING:
+			g_value_set_boolean (
+				value, e_attachment_get_loading (
+				E_ATTACHMENT (object)));
+			return;
+
+		case PROP_MIME_PART:
+			g_value_set_boxed (
+				value, e_attachment_get_mime_part (
+				E_ATTACHMENT (object)));
+			return;
+
+		case PROP_PERCENT:
+			g_value_set_int (
+				value, e_attachment_get_percent (
+				E_ATTACHMENT (object)));
+			return;
+
+		case PROP_REFERENCE:
+			g_value_set_boxed (
+				value, e_attachment_get_reference (
+				E_ATTACHMENT (object)));
+			return;
+
+		case PROP_SAVING:
+			g_value_set_boolean (
+				value, e_attachment_get_saving (
+				E_ATTACHMENT (object)));
+			return;
+
+		case PROP_SIGNED:
+			g_value_set_int (
+				value, e_attachment_get_signed (
+				E_ATTACHMENT (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_dispose (GObject *object)
+{
+	EAttachmentPrivate *priv;
+
+	priv = E_ATTACHMENT_GET_PRIVATE (object);
+
+	if (priv->file != NULL) {
+		g_object_unref (priv->file);
+		priv->file = NULL;
+	}
+
+	if (priv->file_info != NULL) {
+		g_object_unref (priv->file_info);
+		priv->file_info = NULL;
+	}
+
+	if (priv->cancellable != NULL) {
+		g_object_unref (priv->cancellable);
+		priv->cancellable = NULL;
+	}
+
+	if (priv->mime_part != NULL) {
+		camel_object_unref (priv->mime_part);
+		priv->mime_part = NULL;
+	}
+
+	if (priv->emblem_timeout_id > 0) {
+		g_source_remove (priv->emblem_timeout_id);
+		priv->emblem_timeout_id = 0;
+	}
+
+	/* This accepts NULL arguments. */
+	gtk_tree_row_reference_free (priv->reference);
+	priv->reference = NULL;
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+attachment_finalize (GObject *object)
+{
+	EAttachmentPrivate *priv;
+
+	priv = E_ATTACHMENT_GET_PRIVATE (object);
+
+	g_free (priv->disposition);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+attachment_class_init (EAttachmentClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EAttachmentPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = attachment_set_property;
+	object_class->get_property = attachment_get_property;
+	object_class->dispose = attachment_dispose;
+	object_class->finalize = attachment_finalize;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_CAN_SHOW,
+		g_param_spec_boolean (
+			"can-show",
+			"Can Show",
+			NULL,
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_DISPOSITION,
+		g_param_spec_string (
+			"disposition",
+			"Disposition",
+			NULL,
+			"attachment",
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	/* FIXME Define a GEnumClass for this. */
+	g_object_class_install_property (
+		object_class,
+		PROP_ENCRYPTED,
+		g_param_spec_int (
+			"encrypted",
+			"Encrypted",
+			NULL,
+			CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE,
+			CAMEL_CIPHER_VALIDITY_ENCRYPT_STRONG,
+			CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_FILE,
+		g_param_spec_object (
+			"file",
+			"File",
+			NULL,
+			G_TYPE_FILE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_FILE_INFO,
+		g_param_spec_object (
+			"file-info",
+			"File Info",
+			NULL,
+			G_TYPE_FILE_INFO,
+			G_PARAM_READABLE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_LOADING,
+		g_param_spec_boolean (
+			"loading",
+			"Loading",
+			NULL,
+			FALSE,
+			G_PARAM_READABLE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_MIME_PART,
+		g_param_spec_boxed (
+			"mime-part",
+			"MIME Part",
+			NULL,
+			E_TYPE_CAMEL_OBJECT,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_PERCENT,
+		g_param_spec_int (
+			"percent",
+			"Percent",
+			NULL,
+			0,
+			100,
+			0,
+			G_PARAM_READABLE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_REFERENCE,
+		g_param_spec_boxed (
+			"reference",
+			"Reference",
+			NULL,
+			GTK_TYPE_TREE_ROW_REFERENCE,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SAVING,
+		g_param_spec_boolean (
+			"saving",
+			"Saving",
+			NULL,
+			FALSE,
+			G_PARAM_READABLE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SHOWN,
+		g_param_spec_boolean (
+			"shown",
+			"Shown",
+			NULL,
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	/* FIXME Define a GEnumClass for this. */
+	g_object_class_install_property (
+		object_class,
+		PROP_SIGNED,
+		g_param_spec_int (
+			"signed",
+			"Signed",
+			NULL,
+			CAMEL_CIPHER_VALIDITY_SIGN_NONE,
+			CAMEL_CIPHER_VALIDITY_SIGN_NEED_PUBLIC_KEY,
+			CAMEL_CIPHER_VALIDITY_SIGN_NONE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+}
+
+static void
+attachment_init (EAttachment *attachment)
+{
+	attachment->priv = E_ATTACHMENT_GET_PRIVATE (attachment);
+	attachment->priv->cancellable = g_cancellable_new ();
+	attachment->priv->encrypted = CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE;
+	attachment->priv->signed_ = CAMEL_CIPHER_VALIDITY_SIGN_NONE;
+
+	g_signal_connect (
+		attachment, "notify::encrypted",
+		G_CALLBACK (attachment_update_icon_column), NULL);
+
+	g_signal_connect (
+		attachment, "notify::file-info",
+		G_CALLBACK (attachment_update_file_info_columns), NULL);
+
+	g_signal_connect (
+		attachment, "notify::file-info",
+		G_CALLBACK (attachment_update_icon_column), NULL);
+
+	g_signal_connect (
+		attachment, "notify::loading",
+		G_CALLBACK (attachment_update_icon_column), NULL);
+
+	g_signal_connect (
+		attachment, "notify::loading",
+		G_CALLBACK (attachment_update_progress_columns), NULL);
+
+	g_signal_connect (
+		attachment, "notify::percent",
+		G_CALLBACK (attachment_update_progress_columns), NULL);
+
+	g_signal_connect (
+		attachment, "notify::reference",
+		G_CALLBACK (attachment_update_file_info_columns), NULL);
+
+	g_signal_connect (
+		attachment, "notify::reference",
+		G_CALLBACK (attachment_update_icon_column), NULL);
+
+	g_signal_connect (
+		attachment, "notify::reference",
+		G_CALLBACK (attachment_update_progress_columns), NULL);
+
+	g_signal_connect (
+		attachment, "notify::saving",
+		G_CALLBACK (attachment_update_icon_column), NULL);
+
+	g_signal_connect (
+		attachment, "notify::saving",
+		G_CALLBACK (attachment_update_progress_columns), NULL);
+
+	g_signal_connect (
+		attachment, "notify::signed",
+		G_CALLBACK (attachment_update_icon_column), NULL);
+
+	g_signal_connect_swapped (
+		attachment->priv->cancellable, "cancelled",
+		G_CALLBACK (attachment_cancelled_cb), attachment);
+}
+
+GType
+e_attachment_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EAttachmentClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) attachment_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EAttachment),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) attachment_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			G_TYPE_OBJECT, "EAttachment", &type_info, 0);
+	}
+
+	return type;
+}
+
+EAttachment *
+e_attachment_new (void)
+{
+	return g_object_new (E_TYPE_ATTACHMENT, NULL);
+}
+
+EAttachment *
+e_attachment_new_for_path (const gchar *path)
+{
+	EAttachment *attachment;
+	GFile *file;
+
+	g_return_val_if_fail (path != NULL, NULL);
+
+	file = g_file_new_for_path (path);
+	attachment = g_object_new (E_TYPE_ATTACHMENT, "file", file, NULL);
+	g_object_unref (file);
+
+	return attachment;
+}
+
+EAttachment *
+e_attachment_new_for_uri (const gchar *uri)
+{
+	EAttachment *attachment;
+	GFile *file;
+
+	g_return_val_if_fail (uri != NULL, NULL);
+
+	file = g_file_new_for_uri (uri);
+	attachment = g_object_new (E_TYPE_ATTACHMENT, "file", file, NULL);
+	g_object_unref (file);
+
+	return attachment;
+}
+
+EAttachment *
+e_attachment_new_for_message (CamelMimeMessage *message)
+{
+	CamelDataWrapper *wrapper;
+	CamelMimePart *mime_part;
+	EAttachment *attachment;
+	GString *description;
+	const gchar *subject;
+
+	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
+
+	mime_part = camel_mime_part_new ();
+	camel_mime_part_set_disposition (mime_part, "inline");
+	subject = camel_mime_message_get_subject (message);
+
+	description = g_string_new (_("Attached message"));
+	if (subject != NULL)
+		g_string_append_printf (description, " - %s", subject);
+	camel_mime_part_set_description (mime_part, description->str);
+	g_string_free (description, TRUE);
+
+	wrapper = CAMEL_DATA_WRAPPER (message);
+	camel_medium_set_content_object (CAMEL_MEDIUM (mime_part), wrapper);
+	camel_mime_part_set_content_type (mime_part, "message/rfc822");
+
+	attachment = e_attachment_new ();
+	e_attachment_set_mime_part (attachment, mime_part);
+	camel_object_unref (mime_part);
+
+	return attachment;
+}
+
+void
+e_attachment_add_to_multipart (EAttachment *attachment,
+                               CamelMultipart *multipart,
+                               const gchar *default_charset)
+{
+	CamelContentType *content_type;
+	CamelDataWrapper *wrapper;
+	CamelMimePart *mime_part;
+
+	/* XXX EMsgComposer might be a better place for this function. */
+
+	g_return_if_fail (E_IS_ATTACHMENT (attachment));
+	g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
+
+	/* Still loading?  Too bad. */
+	mime_part = e_attachment_get_mime_part (attachment);
+	if (mime_part == NULL)
+		return;
+
+	content_type = camel_mime_part_get_content_type (mime_part);
+	wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+
+	if (CAMEL_IS_MULTIPART (wrapper))
+		goto exit;
+
+	/* For text content, determine the best encoding and character set. */
+	if (camel_content_type_is (content_type, "text", "*")) {
+		CamelTransferEncoding encoding;
+		CamelStreamFilter *filtered_stream;
+		CamelMimeFilterBestenc *filter;
+		CamelStream *stream;
+		const gchar *charset;
+
+		charset = camel_content_type_param (content_type, "charset");
+
+		/* Determine the best encoding by writing the MIME
+		 * part to a NULL stream with a "bestenc" filter. */
+		stream = camel_stream_null_new ();
+		filtered_stream = camel_stream_filter_new_with_stream (stream);
+		filter = camel_mime_filter_bestenc_new (
+			CAMEL_BESTENC_GET_ENCODING);
+		camel_stream_filter_add (
+			filtered_stream, CAMEL_MIME_FILTER (filter));
+		camel_data_wrapper_decode_to_stream (
+			wrapper, CAMEL_STREAM (filtered_stream));
+		camel_object_unref (filtered_stream);
+		camel_object_unref (stream);
+
+		/* Retrieve the best encoding from the filter. */
+		encoding = camel_mime_filter_bestenc_get_best_encoding (
+			filter, CAMEL_BESTENC_8BIT);
+		camel_mime_part_set_encoding (mime_part, encoding);
+		camel_object_unref (filter);
+
+		if (encoding == CAMEL_TRANSFER_ENCODING_7BIT) {
+			/* The text fits within us-ascii, so this is safe.
+			 * FIXME Check that this isn't iso-2022-jp? */
+			default_charset = "us-ascii";
+
+		} else if (charset == NULL && default_charset == NULL) {
+			default_charset = attachment_get_default_charset ();
+			/* FIXME Check that this fits within the
+			 *       default_charset and if not, find one
+			 *       that does and/or allow the user to
+			 *       specify. */
+		}
+
+		if (charset == NULL) {
+			gchar *type;
+
+			camel_content_type_set_param (
+				content_type, "charset", default_charset);
+			type = camel_content_type_format (content_type);
+			camel_mime_part_set_content_type (mime_part, type);
+			g_free (type);
+		}
+
+	/* Otherwise, unless it's a message/rfc822, Base64 encode it. */
+	} else if (!CAMEL_IS_MIME_MESSAGE (wrapper))
+		camel_mime_part_set_encoding (
+			mime_part, CAMEL_TRANSFER_ENCODING_BASE64);
+
+exit:
+	camel_multipart_add_part (multipart, mime_part);
+}
+
+void
+e_attachment_cancel (EAttachment *attachment)
+{
+	g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+	g_cancellable_cancel (attachment->priv->cancellable);
+}
+
+gboolean
+e_attachment_get_can_show (EAttachment *attachment)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+
+	return attachment->priv->can_show;
+}
+
+void
+e_attachment_set_can_show (EAttachment *attachment,
+                           gboolean can_show)
+{
+	g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+	attachment->priv->can_show = can_show;
+
+	g_object_notify (G_OBJECT (attachment), "can-show");
+}
+
+const gchar *
+e_attachment_get_disposition (EAttachment *attachment)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+
+	return attachment->priv->disposition;
+}
+
+void
+e_attachment_set_disposition (EAttachment *attachment,
+                              const gchar *disposition)
+{
+	g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+	g_free (attachment->priv->disposition);
+	attachment->priv->disposition = g_strdup (disposition);
+
+	g_object_notify (G_OBJECT (attachment), "disposition");
+}
+
+GFile *
+e_attachment_get_file (EAttachment *attachment)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+
+	return attachment->priv->file;
+}
+
+void
+e_attachment_set_file (EAttachment *attachment,
+                       GFile *file)
+{
+	g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+	if (file != NULL) {
+		g_return_if_fail (G_IS_FILE (file));
+		g_object_ref (file);
+	}
+
+	if (attachment->priv->file != NULL)
+		g_object_unref (attachment->priv->file);
+
+	attachment->priv->file = file;
+
+	g_object_notify (G_OBJECT (attachment), "file");
+}
+
+GFileInfo *
+e_attachment_get_file_info (EAttachment *attachment)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+
+	return attachment->priv->file_info;
+}
+
+gboolean
+e_attachment_get_loading (EAttachment *attachment)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+
+	return attachment->priv->loading;
+}
+
+CamelMimePart *
+e_attachment_get_mime_part (EAttachment *attachment)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+
+	return attachment->priv->mime_part;
+}
+
+void
+e_attachment_set_mime_part (EAttachment *attachment,
+                            CamelMimePart *mime_part)
+{
+	g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+	if (mime_part != NULL) {
+		g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
+		camel_object_ref (mime_part);
+	}
+
+	if (attachment->priv->mime_part != NULL)
+		camel_object_unref (attachment->priv->mime_part);
+
+	attachment->priv->mime_part = mime_part;
+
+	g_object_notify (G_OBJECT (attachment), "mime-part");
+}
+
+gint
+e_attachment_get_percent (EAttachment *attachment)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), 0);
+
+	return attachment->priv->percent;
+}
+
+GtkTreeRowReference *
+e_attachment_get_reference (EAttachment *attachment)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+
+	return attachment->priv->reference;
+}
+
+void
+e_attachment_set_reference (EAttachment *attachment,
+                            GtkTreeRowReference *reference)
+{
+	g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+	if (reference != NULL)
+		reference = gtk_tree_row_reference_copy (reference);
+
+	gtk_tree_row_reference_free (attachment->priv->reference);
+	attachment->priv->reference = reference;
+
+	g_object_notify (G_OBJECT (attachment), "reference");
+}
+
+gboolean
+e_attachment_get_saving (EAttachment *attachment)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+
+	return attachment->priv->saving;
+}
+
+gboolean
+e_attachment_get_shown (EAttachment *attachment)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+
+	return attachment->priv->shown;
+}
+
+void
+e_attachment_set_shown (EAttachment *attachment,
+                        gboolean shown)
+{
+	g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+	attachment->priv->shown = shown;
+
+	g_object_notify (G_OBJECT (attachment), "shown");
+}
+
+camel_cipher_validity_encrypt_t
+e_attachment_get_encrypted (EAttachment *attachment)
+{
+	g_return_val_if_fail (
+		E_IS_ATTACHMENT (attachment),
+		CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE);
+
+	return attachment->priv->encrypted;
+}
+
+void
+e_attachment_set_encrypted (EAttachment *attachment,
+                            camel_cipher_validity_encrypt_t encrypted)
+{
+	g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+	attachment->priv->encrypted = encrypted;
+
+	g_object_notify (G_OBJECT (attachment), "encrypted");
+}
+
+camel_cipher_validity_sign_t
+e_attachment_get_signed (EAttachment *attachment)
+{
+	g_return_val_if_fail (
+		E_IS_ATTACHMENT (attachment),
+		CAMEL_CIPHER_VALIDITY_SIGN_NONE);
+
+	return attachment->priv->signed_;
+}
+
+void
+e_attachment_set_signed (EAttachment *attachment,
+                         camel_cipher_validity_sign_t signed_)
+{
+	g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+	attachment->priv->signed_ = signed_;
+
+	g_object_notify (G_OBJECT (attachment), "signed");
+}
+
+const gchar *
+e_attachment_get_description (EAttachment *attachment)
+{
+	GFileInfo *file_info;
+	const gchar *attribute;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+
+	attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
+	file_info = e_attachment_get_file_info (attachment);
+
+	if (file_info == NULL)
+		return NULL;
+
+	return g_file_info_get_attribute_string (file_info, attribute);
+}
+
+const gchar *
+e_attachment_get_thumbnail_path (EAttachment *attachment)
+{
+	GFileInfo *file_info;
+	const gchar *attribute;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+
+	attribute = G_FILE_ATTRIBUTE_THUMBNAIL_PATH;
+	file_info = e_attachment_get_file_info (attachment);
+
+	if (file_info == NULL)
+		return NULL;
+
+	return g_file_info_get_attribute_byte_string (file_info, attribute);
+}
+
+gboolean
+e_attachment_is_rfc822 (EAttachment *attachment)
+{
+	GFileInfo *file_info;
+	const gchar *content_type;
+	gchar *mime_type;
+	gboolean is_rfc822;
+
+	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_rfc822 = (g_ascii_strcasecmp (mime_type, "message/rfc822") == 0);
+	g_free (mime_type);
+
+	return is_rfc822;
+}
+
+GList *
+e_attachment_list_apps (EAttachment *attachment)
+{
+	GList *app_info_list;
+	GFileInfo *file_info;
+	const gchar *content_type;
+	const gchar *display_name;
+	gchar *allocated;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+
+	file_info = e_attachment_get_file_info (attachment);
+	if (file_info == NULL)
+		return NULL;
+
+	content_type = g_file_info_get_content_type (file_info);
+	display_name = g_file_info_get_display_name (file_info);
+	g_return_val_if_fail (content_type != NULL, NULL);
+
+	app_info_list = g_app_info_get_all_for_type (content_type);
+
+	if (app_info_list != NULL || display_name == NULL)
+		goto exit;
+
+	if (!g_content_type_is_unknown (content_type))
+		goto exit;
+
+	allocated = g_content_type_guess (display_name, NULL, 0, NULL);
+	app_info_list = g_app_info_get_all_for_type (allocated);
+	g_free (allocated);
+
+exit:
+	return app_info_list;
+}
+
+/************************* e_attachment_load_async() *************************/
+
+typedef struct _LoadContext LoadContext;
+
+struct _LoadContext {
+	EAttachment *attachment;
+	GSimpleAsyncResult *simple;
+
+	GInputStream *input_stream;
+	GOutputStream *output_stream;
+	GFileInfo *file_info;
+	goffset total_num_bytes;
+	gssize bytes_read;
+	gchar buffer[4096];
+};
+
+/* Forward Declaration */
+static void
+attachment_load_stream_read_cb (GInputStream *input_stream,
+                                GAsyncResult *result,
+                                LoadContext *load_context);
+
+static LoadContext *
+attachment_load_context_new (EAttachment *attachment,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data)
+{
+	LoadContext *load_context;
+	GSimpleAsyncResult *simple;
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (attachment), callback,
+		user_data, e_attachment_load_async);
+
+	load_context = g_slice_new0 (LoadContext);
+	load_context->attachment = g_object_ref (attachment);
+	load_context->simple = simple;
+
+	attachment_set_loading (load_context->attachment, TRUE);
+
+	return load_context;
+}
+
+static void
+attachment_load_context_free (LoadContext *load_context)
+{
+	/* Do not free the GSimpleAsyncResult. */
+	g_object_unref (load_context->attachment);
+
+	if (load_context->input_stream != NULL)
+		g_object_unref (load_context->input_stream);
+
+	if (load_context->output_stream != NULL)
+		g_object_unref (load_context->output_stream);
+
+	if (load_context->file_info != NULL)
+		g_object_unref (load_context->file_info);
+
+	g_slice_free (LoadContext, load_context);
+}
+
+static gboolean
+attachment_load_check_for_error (LoadContext *load_context,
+                                 GError *error)
+{
+	GSimpleAsyncResult *simple;
+
+	if (error == NULL)
+		return FALSE;
+
+	/* Steal the result. */
+	simple = load_context->simple;
+	load_context->simple = NULL;
+
+	g_simple_async_result_set_from_error (simple, error);
+	g_simple_async_result_complete (simple);
+	g_error_free (error);
+
+	attachment_load_context_free (load_context);
+
+	return TRUE;
+}
+
+static void
+attachment_load_finish (LoadContext *load_context)
+{
+	GFileInfo *file_info;
+	EAttachment *attachment;
+	GMemoryOutputStream *output_stream;
+	GSimpleAsyncResult *simple;
+	CamelDataWrapper *wrapper;
+	CamelMimePart *mime_part;
+	CamelStream *stream;
+	const gchar *attribute;
+	const gchar *content_type;
+	const gchar *display_name;
+	const gchar *description;
+	const gchar *disposition;
+	gchar *mime_type;
+	gpointer data;
+	gsize size;
+
+	/* Steal the result. */
+	simple = load_context->simple;
+	load_context->simple = NULL;
+
+	file_info = load_context->file_info;
+	attachment = load_context->attachment;
+	output_stream = G_MEMORY_OUTPUT_STREAM (load_context->output_stream);
+
+	if (e_attachment_is_rfc822 (attachment))
+		wrapper = (CamelDataWrapper *) camel_mime_message_new ();
+	else
+		wrapper = camel_data_wrapper_new ();
+
+	content_type = g_file_info_get_content_type (file_info);
+	mime_type = g_content_type_get_mime_type (content_type);
+
+	data = g_memory_output_stream_get_data (output_stream);
+	size = g_memory_output_stream_get_data_size (output_stream);
+
+	stream = camel_stream_mem_new_with_buffer (data, size);
+	camel_data_wrapper_construct_from_stream (wrapper, stream);
+	camel_data_wrapper_set_mime_type (wrapper, mime_type);
+	camel_stream_close (stream);
+	camel_object_unref (stream);
+
+	mime_part = camel_mime_part_new ();
+	camel_medium_set_content_object (CAMEL_MEDIUM (mime_part), wrapper);
+
+	camel_object_unref (wrapper);
+	g_free (mime_type);
+
+	display_name = g_file_info_get_display_name (file_info);
+	if (display_name != NULL)
+		camel_mime_part_set_filename (mime_part, display_name);
+
+	attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
+	description = g_file_info_get_attribute_string (file_info, attribute);
+	if (description != NULL)
+		camel_mime_part_set_description (mime_part, description);
+
+	disposition = e_attachment_get_disposition (attachment);
+	if (disposition != NULL)
+		camel_mime_part_set_disposition (mime_part, disposition);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, mime_part, (GDestroyNotify) camel_object_unref);
+
+	g_simple_async_result_complete (simple);
+
+	attachment_load_context_free (load_context);
+}
+
+static void
+attachment_load_write_cb (GOutputStream *output_stream,
+                          GAsyncResult *result,
+                          LoadContext *load_context)
+{
+	EAttachment *attachment;
+	GCancellable *cancellable;
+	GInputStream *input_stream;
+	gssize bytes_written;
+	GError *error = NULL;
+
+	bytes_written = g_output_stream_write_finish (
+		output_stream, result, &error);
+
+	if (attachment_load_check_for_error (load_context, error))
+		return;
+
+	attachment = load_context->attachment;
+	cancellable = attachment->priv->cancellable;
+	input_stream = load_context->input_stream;
+
+	attachment_progress_cb (
+		g_seekable_tell (G_SEEKABLE (output_stream)),
+		load_context->total_num_bytes, attachment);
+
+	if (bytes_written < load_context->bytes_read) {
+		g_memmove (
+			load_context->buffer,
+			load_context->buffer + bytes_written,
+			load_context->bytes_read - bytes_written);
+		load_context->bytes_read -= bytes_written;
+
+		g_output_stream_write_async (
+			output_stream,
+			load_context->buffer,
+			load_context->bytes_read,
+			G_PRIORITY_DEFAULT, cancellable,
+			(GAsyncReadyCallback) attachment_load_write_cb,
+			load_context);
+	} else
+		g_input_stream_read_async (
+			input_stream,
+			load_context->buffer,
+			sizeof (load_context->buffer),
+			G_PRIORITY_DEFAULT, cancellable,
+			(GAsyncReadyCallback) attachment_load_stream_read_cb,
+			load_context);
+}
+
+static void
+attachment_load_stream_read_cb (GInputStream *input_stream,
+                                GAsyncResult *result,
+                                LoadContext *load_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_load_check_for_error (load_context, error))
+		return;
+
+	if (bytes_read == 0) {
+		attachment_load_finish (load_context);
+		return;
+	}
+
+	attachment = load_context->attachment;
+	cancellable = attachment->priv->cancellable;
+	output_stream = load_context->output_stream;
+	load_context->bytes_read = bytes_read;
+
+	g_output_stream_write_async (
+		output_stream,
+		load_context->buffer,
+		load_context->bytes_read,
+		G_PRIORITY_DEFAULT, cancellable,
+		(GAsyncReadyCallback) attachment_load_write_cb,
+		load_context);
+}
+
+static void
+attachment_load_file_read_cb (GFile *file,
+                              GAsyncResult *result,
+                              LoadContext *load_context)
+{
+	EAttachment *attachment;
+	GCancellable *cancellable;
+	GFileInputStream *input_stream;
+	GOutputStream *output_stream;
+	GError *error = NULL;
+
+	/* Input stream might be NULL, so don't use cast macro. */
+	input_stream = g_file_read_finish (file, result, &error);
+	load_context->input_stream = (GInputStream *) input_stream;
+
+	if (attachment_load_check_for_error (load_context, error))
+		return;
+
+	/* Load the contents into a GMemoryOutputStream. */
+	output_stream = g_memory_output_stream_new (
+		NULL, 0, g_realloc, g_free);
+
+	attachment = load_context->attachment;
+	cancellable = attachment->priv->cancellable;
+	load_context->output_stream = output_stream;
+
+	g_input_stream_read_async (
+		load_context->input_stream,
+		load_context->buffer,
+		sizeof (load_context->buffer),
+		G_PRIORITY_DEFAULT, cancellable,
+		(GAsyncReadyCallback) attachment_load_stream_read_cb,
+		load_context);
+}
+
+static void
+attachment_load_query_info_cb (GFile *file,
+                               GAsyncResult *result,
+                               LoadContext *load_context)
+{
+	EAttachment *attachment;
+	GCancellable *cancellable;
+	GFileInfo *file_info;
+	GError *error = NULL;
+
+	attachment = load_context->attachment;
+	cancellable = attachment->priv->cancellable;
+
+	file_info = g_file_query_info_finish (file, result, &error);
+	attachment_set_file_info (attachment, file_info);
+	load_context->file_info = file_info;
+
+	if (attachment_load_check_for_error (load_context, error))
+		return;
+
+	load_context->total_num_bytes = g_file_info_get_size (file_info);
+
+	g_file_read_async (
+		file, G_PRIORITY_DEFAULT,
+		cancellable, (GAsyncReadyCallback)
+		attachment_load_file_read_cb, load_context);
+}
+
+static void
+attachment_load_from_mime_part (LoadContext *load_context)
+{
+	GFileInfo *file_info;
+	EAttachment *attachment;
+	GSimpleAsyncResult *simple;
+	CamelContentType *content_type;
+	CamelMimePart *mime_part;
+	const gchar *attribute;
+	const gchar *string;
+	gchar *allocated;
+	goffset size;
+
+	attachment = load_context->attachment;
+	mime_part = e_attachment_get_mime_part (attachment);
+
+	file_info = g_file_info_new ();
+	load_context->file_info = file_info;
+
+	content_type = camel_mime_part_get_content_type (mime_part);
+	allocated = camel_content_type_simple (content_type);
+	if (allocated != NULL) {
+		GIcon *icon;
+		gchar *cp;
+
+		/* GIO expects lowercase MIME types. */
+		for (cp = allocated; *cp != '\0'; cp++)
+			*cp = g_ascii_tolower (*cp);
+
+		/* Swap the MIME type for a content type. */
+		cp = g_content_type_from_mime_type (allocated);
+		g_free (allocated);
+		allocated = cp;
+
+		/* Use the MIME part's filename if we have to. */
+		if (g_content_type_is_unknown (allocated)) {
+			string = camel_mime_part_get_filename (mime_part);
+			if (string != NULL) {
+				g_free (allocated);
+				allocated = g_content_type_guess (
+					string, NULL, 0, NULL);
+			}
+		}
+
+		g_file_info_set_content_type (file_info, allocated);
+
+		icon = g_content_type_get_icon (allocated);
+		if (icon != NULL) {
+			g_file_info_set_icon (file_info, icon);
+			g_object_unref (icon);
+		}
+	}
+	g_free (allocated);
+
+	string = camel_mime_part_get_filename (mime_part);
+	if (string == NULL)
+		/* Translators: Default attachment filename. */
+		string = _("attachment.dat");
+	g_file_info_set_display_name (file_info, string);
+
+	attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
+	string = camel_mime_part_get_description (mime_part);
+	if (string != NULL)
+		g_file_info_set_attribute_string (
+			file_info, attribute, string);
+
+	size = (goffset) camel_mime_part_get_content_size (mime_part);
+	g_file_info_set_size (file_info, size);
+
+	string = camel_mime_part_get_disposition (mime_part);
+	e_attachment_set_disposition (attachment, string);
+
+	attachment_set_file_info (attachment, file_info);
+
+	/* Steal the result. */
+	simple = load_context->simple;
+	load_context->simple = NULL;
+
+	camel_object_ref (mime_part);
+	g_simple_async_result_set_op_res_gpointer (
+		simple, mime_part,
+		(GDestroyNotify) camel_object_unref);
+	g_simple_async_result_complete_in_idle (simple);
+
+	attachment_load_context_free (load_context);
+}
+
+void
+e_attachment_load_async (EAttachment *attachment,
+                         GAsyncReadyCallback callback,
+                         gpointer user_data)
+{
+	LoadContext *load_context;
+	GCancellable *cancellable;
+	CamelMimePart *mime_part;
+	GFile *file;
+
+	g_return_if_fail (E_IS_ATTACHMENT (attachment));
+	g_return_if_fail (callback != NULL);
+
+	if (e_attachment_get_loading (attachment)) {
+		g_simple_async_report_error_in_idle (
+			G_OBJECT (attachment), callback, user_data,
+			G_IO_ERROR, G_IO_ERROR_BUSY,
+			_("A load operation is already in progress"));
+		return;
+	}
+
+	if (e_attachment_get_saving (attachment)) {
+		g_simple_async_report_error_in_idle (
+			G_OBJECT (attachment), callback, user_data,
+			G_IO_ERROR, G_IO_ERROR_BUSY,
+			_("A save operation is already in progress"));
+		return;
+	}
+
+	file = e_attachment_get_file (attachment);
+	mime_part = e_attachment_get_mime_part (attachment);
+	g_return_if_fail (file != NULL || mime_part != NULL);
 
-#ifdef G_OS_WIN32
-/* Include <windows.h> early (as the gio stuff below will
- * include it anyway, sigh) to workaround the DATADIR problem.
- * <windows.h> (and the headers it includes) stomps all over the
- * namespace like a baboon on crack, and especially the DATADIR enum
- * in objidl.h causes problems.
- */
-#undef DATADIR
-#define DATADIR crap_DATADIR
-#include <windows.h>
-#undef DATADIR
-#endif
+	load_context = attachment_load_context_new (
+		attachment, callback, user_data);
 
-#include <sys/stat.h>
-#include <string.h>
-#include <errno.h>
+	cancellable = attachment->priv->cancellable;
+	g_cancellable_reset (cancellable);
 
-#include <camel/camel.h>
+	if (file != NULL)
+		g_file_query_info_async (
+			file, ATTACHMENT_QUERY,
+			G_FILE_QUERY_INFO_NONE,G_PRIORITY_DEFAULT,
+			cancellable, (GAsyncReadyCallback)
+			attachment_load_query_info_cb, load_context);
 
-#include <glib/gi18n.h>
-#include <glib/gstdio.h>
+	else if (mime_part != NULL)
+		attachment_load_from_mime_part (load_context);
 
-#include <libebook/e-vcard.h>
+}
 
-#include "e-util/e-util.h"
-#include "e-util/e-error.h"
-#include "e-util/e-mktemp.h"
-#include "e-util/e-util-private.h"
+gboolean
+e_attachment_load_finish (EAttachment *attachment,
+                          GAsyncResult *result,
+                          GError **error)
+{
+	GSimpleAsyncResult *simple;
+	CamelMimePart *mime_part;
 
-#include "e-attachment.h"
+	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+	g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
 
-enum {
-	CHANGED,
-	UPDATE,
-	LAST_SIGNAL
-};
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	mime_part = g_simple_async_result_get_op_res_gpointer (simple);
+	if (mime_part != NULL)
+		e_attachment_set_mime_part (attachment, mime_part);
+	g_simple_async_result_propagate_error (simple, error);
+	g_object_unref (simple);
 
-static guint signals[LAST_SIGNAL] = { 0 };
+	attachment_set_loading (attachment, FALSE);
 
-static GObjectClass *parent_class = NULL;
+	return (mime_part != NULL);
+}
 
-static void
-changed (EAttachment *attachment)
+void
+e_attachment_load_handle_error (EAttachment *attachment,
+                                GAsyncResult *result,
+                                GtkWindow *parent)
 {
-	g_signal_emit (attachment, signals[CHANGED], 0);
-}
+	GtkWidget *dialog;
+	GFileInfo *file_info;
+	GtkTreeRowReference *reference;
+	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));
 
-/* GtkObject methods.  */
+	if (e_attachment_load_finish (attachment, result, &error))
+		return;
 
-static void
-finalise (GObject *object)
-{
-	EAttachment *attachment = (EAttachment *) object;
-	GtkWidget *dialog;
+	/* XXX Calling EAttachmentStore functions from here violates
+	 *     the abstraction, but for now it's not hurting anything. */
+	reference = e_attachment_get_reference (attachment);
+	if (gtk_tree_row_reference_valid (reference)) {
+		GtkTreeModel *model;
 
-	if (attachment->editor_gui != NULL) {
-		dialog = glade_xml_get_widget (attachment->editor_gui, "dialog");
-		g_signal_emit_by_name (dialog, "response", GTK_RESPONSE_CLOSE);
-	}
+		model = gtk_tree_row_reference_get_model (reference);
 
-	if (attachment->is_available_local) {
-		camel_object_unref (attachment->body);
-		if (attachment->pixbuf_cache != NULL)
-			g_object_unref (attachment->pixbuf_cache);
-	} else {
-		if (attachment->cancellable) {
-			/* the operation is still running, so cancel it */
-			g_cancellable_cancel (attachment->cancellable);
-			attachment->cancellable = NULL;
-		}
-		g_free (attachment->description);
+		e_attachment_store_remove_attachment (
+			E_ATTACHMENT_STORE (model), attachment);
 	}
 
-	g_free (attachment->file_name);
-	g_free (attachment->store_uri);
+	/* Ignore cancellations. */
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
 
-	G_OBJECT_CLASS (parent_class)->finalize (object);
-}
+	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;
 
-/* Signals.  */
+	if (display_name != NULL)
+		primary_text = g_strdup_printf (
+			_("Could not load '%s'"), display_name);
+	else
+		primary_text = g_strdup_printf (
+			_("Could not load the attachment"));
 
-static void
-real_changed (EAttachment *attachment)
-{
-	g_return_if_fail (E_IS_ATTACHMENT (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);
 
-static void
-real_update_attachment (EAttachment *attachment, char *msg)
-{
-	g_return_if_fail (E_IS_ATTACHMENT (attachment));
+	gtk_message_dialog_format_secondary_text (
+		GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
+
+	gtk_dialog_run (GTK_DIALOG (dialog));
+
+	gtk_widget_destroy (dialog);
+	g_error_free (error);
 }
 
+/************************* e_attachment_open_async() *************************/
 
-static void
-class_init (EAttachmentClass *klass)
-{
-	GObjectClass *object_class;
+typedef struct _OpenContext OpenContext;
 
-	object_class = (GObjectClass*) klass;
-	parent_class = g_type_class_ref (G_TYPE_OBJECT);
+struct _OpenContext {
+	EAttachment *attachment;
+	GSimpleAsyncResult *simple;
 
-	object_class->finalize = finalise;
-	klass->changed = real_changed;
-	klass->update = real_update_attachment;
-
-	signals[CHANGED] = g_signal_new ("changed",
-					 E_TYPE_ATTACHMENT,
-					 G_SIGNAL_RUN_FIRST,
-					 G_STRUCT_OFFSET (EAttachmentClass, changed),
-					 NULL,
-					 NULL,
-					 g_cclosure_marshal_VOID__VOID,
-					 G_TYPE_NONE, 0);
-	signals[UPDATE] = g_signal_new ("update",
-					 E_TYPE_ATTACHMENT,
-					 G_SIGNAL_RUN_FIRST,
-					 G_STRUCT_OFFSET (EAttachmentClass, update),
-					 NULL,
-					 NULL,
-					 g_cclosure_marshal_VOID__VOID,
-					 G_TYPE_NONE, 0);
+	GAppInfo *app_info;
+};
 
-}
+static OpenContext *
+attachment_open_context_new (EAttachment *attachment,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data)
+{
+	OpenContext *open_context;
+	GSimpleAsyncResult *simple;
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (attachment), callback,
+		user_data, e_attachment_open_async);
+
+	open_context = g_slice_new0 (OpenContext);
+	open_context->attachment = g_object_ref (attachment);
+	open_context->simple = simple;
 
-static void
-init (EAttachment *attachment)
-{
-	attachment->editor_gui = NULL;
-	attachment->body = NULL;
-	attachment->size = 0;
-	attachment->pixbuf_cache = NULL;
-	attachment->index = -1;
-	attachment->file_name = NULL;
-	attachment->percentage = -1;
-	attachment->description = NULL;
-	attachment->disposition = FALSE;
-	attachment->sign = CAMEL_CIPHER_VALIDITY_SIGN_NONE;
-	attachment->encrypt = CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE;
-	attachment->store_uri = NULL;
-	attachment->cancellable = NULL;
+	return open_context;
 }
 
-GType
-e_attachment_get_type (void)
+static void
+attachment_open_context_free (OpenContext *open_context)
 {
-	static GType type = 0;
-
-	if (type == 0) {
-		static const GTypeInfo info = {
-			sizeof (EAttachmentClass),
-			NULL,
-			NULL,
-			(GClassInitFunc) class_init,
-			NULL,
-			NULL,
-			sizeof (EAttachment),
-			0,
-			(GInstanceInitFunc) init,
-		};
+	/* Do not free the GSimpleAsyncResult. */
+	g_object_unref (open_context->attachment);
 
-		type = g_type_register_static (G_TYPE_OBJECT, "EAttachment", &info, 0);
-	}
+	if (open_context->app_info != NULL)
+		g_object_unref (open_context->app_info);
 
-	return type;
+	g_slice_free (OpenContext, open_context);
 }
 
-/**
- * file_ext_is:
- * @param file_name: path for file
- * @param ext: desired extension, with a dot
- * @return if file_name has extension ext or not
- **/
-
 static gboolean
-file_ext_is (const char *file_name, const char *ext)
+attachment_open_check_for_error (OpenContext *open_context,
+                                 GError *error)
 {
-	int i, dot = -1;
+	GSimpleAsyncResult *simple;
 
-	if (!file_name || !ext)
+	if (error == NULL)
 		return FALSE;
 
-	for (i = 0; file_name[i]; i++) {
-		if (file_name [i] == '.')
-			dot = i;
-	}
+	/* Steal the result. */
+	simple = open_context->simple;
+	open_context->simple = NULL;
 
-	if (dot > 0) {
-		return 0 == g_ascii_strcasecmp (file_name + dot, ext);
-	}
+	g_simple_async_result_set_from_error (simple, error);
+	g_simple_async_result_complete (simple);
+	g_error_free (error);
 
-	return FALSE;
+	attachment_open_context_free (open_context);
+
+	return TRUE;
 }
 
-static char *
-attachment_guess_mime_type (const char *file_name)
+static void
+attachment_open_file (GFile *file,
+                      OpenContext *open_context)
 {
-	char *type;
-	gchar *content = NULL;
+	GdkAppLaunchContext *context;
+	GSimpleAsyncResult *simple;
+	GList *file_list;
+	gboolean success;
+	GError *error = NULL;
 
-	type = e_util_guess_mime_type (file_name, TRUE);
+	/* Steal the result. */
+	simple = open_context->simple;
+	open_context->simple = NULL;
 
-	if (type && strcmp (type, "text/directory") == 0 &&
-	    file_ext_is (file_name, ".vcf") &&
-	    g_file_get_contents (file_name, &content, NULL, NULL) &&
-	    content) {
-		EVCard *vc = e_vcard_new_from_string (content);
+	/* Find a default app based on content type. */
+	if (open_context->app_info == NULL) {
+		EAttachment *attachment;
+		GFileInfo *file_info;
+		const gchar *content_type;
 
-		if (vc) {
-			g_free (type);
-			g_object_unref (G_OBJECT (vc));
+		attachment = open_context->attachment;
+		file_info = e_attachment_get_file_info (attachment);
+		if (file_info == NULL)
+			goto exit;
 
-			type = g_strdup ("text/x-vcard");
-		}
+		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);
 	}
 
-	g_free (content);
+	if (open_context->app_info == NULL)
+		goto exit;
 
-	if (type) {
-		/* Check if returned mime_type is valid */
-		CamelContentType *ctype = camel_content_type_decode (type);
+	context = gdk_app_launch_context_new ();
+	file_list = g_list_prepend (NULL, file);
 
-		if (!ctype) {
-			g_free (type);
-			type = NULL;
-		} else
-			camel_content_type_unref (ctype);
+	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);
 	}
 
-	return type;
+	g_simple_async_result_complete (simple);
+	attachment_open_context_free (open_context);
 }
 
-
-/**
- * e_attachment_new:
- * @file_name: filename to attach
- * @disposition: Content-Disposition of the attachment
- * @ex: exception
- *
- * Return value: the new attachment, or %NULL on error
- **/
-EAttachment *
-e_attachment_new (const char *file_name, const char *disposition, CamelException *ex)
+static void
+attachment_open_save_finished_cb (EAttachment *attachment,
+                                  GAsyncResult *result,
+                                  OpenContext *open_context)
 {
-	EAttachment *new;
-	CamelMimePart *part;
-	CamelDataWrapper *wrapper;
-	CamelStream *stream;
-	struct stat statbuf;
-	char *mime_type;
-	char *filename;
-	CamelURL *url;
-
-	g_return_val_if_fail (file_name != NULL, NULL);
-
-	if (g_stat (file_name, &statbuf) < 0) {
-		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
-				      _("Cannot attach file %s: %s"),
-				      file_name, g_strerror (errno));
-		return NULL;
-	}
+	GFile *file;
+	GError *error = NULL;
 
-	/* return if it's not a regular file */
-	if (!S_ISREG (statbuf.st_mode)) {
-		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
-				      _("Cannot attach file %s: not a regular file"),
-				      file_name);
-		return NULL;
-	}
+	file = e_attachment_save_finish (attachment, result, &error);
 
-	if (!(stream = camel_stream_fs_new_with_name (file_name, O_RDONLY, 0))) {
-		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
-				      _("Cannot attach file %s: %s"),
-				      file_name, g_strerror (errno));
-		return NULL;
-	}
+	if (attachment_open_check_for_error (open_context, error))
+		return;
 
-	if ((mime_type = attachment_guess_mime_type (file_name))) {
-		if (!g_ascii_strcasecmp (mime_type, "message/rfc822")) {
-			wrapper = (CamelDataWrapper *) camel_mime_message_new ();
-		} else {
-			wrapper = camel_data_wrapper_new ();
-		}
+	attachment_open_file (file, open_context);
+	g_object_unref (file);
+}
 
-		camel_data_wrapper_construct_from_stream (wrapper, stream);
-		camel_data_wrapper_set_mime_type (wrapper, mime_type);
-		g_free (mime_type);
-	} else {
-		wrapper = camel_data_wrapper_new ();
-		camel_data_wrapper_construct_from_stream (wrapper, stream);
-		camel_data_wrapper_set_mime_type (wrapper, "application/octet-stream");
-	}
+static void
+attachment_open_save_temporary (OpenContext *open_context)
+{
+	GFile *file;
+	gchar *template;
+	gchar *path;
+	GError *error = NULL;
 
-	camel_object_unref (stream);
+	errno = 0;
 
-	part = camel_mime_part_new ();
-	camel_medium_set_content_object (CAMEL_MEDIUM (part), wrapper);
-	camel_object_unref (wrapper);
+	/* 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));
 
-	camel_mime_part_set_disposition (part, disposition);
-	filename = g_path_get_basename (file_name);
-	camel_mime_part_set_filename (part, filename);
+	/* We already know if there's an error, but this does the cleanup. */
+	if (attachment_open_check_for_error (open_context, error))
+		return;
 
-#if 0
-	/* Note: Outlook 2002 is broken with respect to Content-Ids on
-           non-multipart/related parts, so as an interoperability
-           workaround, don't set a Content-Id on these parts. Fixes
-           bug #10032 */
-	/* set the Content-Id */
-	content_id = camel_header_msgid_generate ();
-	camel_mime_part_set_content_id (part, content_id);
-	g_free (content_id);
-#endif
+	file = g_file_new_for_path (path);
 
-	new = g_object_new (E_TYPE_ATTACHMENT, NULL);
-	new->editor_gui = NULL;
-	new->body = part;
-	new->size = statbuf.st_size;
-	new->guessed_type = TRUE;
-	new->cancellable = NULL;
-	new->is_available_local = TRUE;
-	new->file_name = filename;
+	g_free (path);
 
-	url = camel_url_new ("file://", NULL);
-	camel_url_set_path (url, file_name);
-	new->store_uri = camel_url_to_string (url, 0);
-	camel_url_free (url);
+	e_attachment_save_async (
+		open_context->attachment, file, (GAsyncReadyCallback)
+		attachment_open_save_finished_cb, open_context);
 
-	return new;
+	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;
 
-typedef struct {
-	EAttachment *attachment;
-	char *file_name;
-	char *uri;
-	GtkWindow *parent; /* for error dialog */
+	g_return_if_fail (E_IS_ATTACHMENT (attachment));
+	g_return_if_fail (callback != NULL);
 
-	guint64 file_size; /* zero indicates unknown size */
-	GInputStream *istream; /* read from here ... */
-	GOutputStream *ostream; /* ...and write into this. */
-	gboolean was_error;
-	GCancellable *cancellable;
+	file = e_attachment_get_file (attachment);
+	mime_part = e_attachment_get_mime_part (attachment);
+	g_return_if_fail (file != NULL || mime_part != NULL);
 
-	void *buffer; /* read into this, not more than buffer_size bytes */
-	gsize buffer_size;
-} DownloadInfo;
+	open_context = attachment_open_context_new (
+		attachment, callback, user_data);
 
-static void
-download_info_free (DownloadInfo *download_info)
-{
-	/* if there was an error, then free attachment too */
-	if (download_info->was_error)
-		g_object_unref (download_info->attachment);
+	if (G_IS_APP_INFO (app_info))
+		open_context->app_info = g_object_ref (app_info);
 
-	if (download_info->ostream)
-		g_object_unref (download_info->ostream);
+	/* 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);
 
-	if (download_info->istream)
-		g_object_unref (download_info->istream);
+	} 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);
 
-	if (download_info->cancellable)
-		g_object_unref (download_info->cancellable);
+	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);
 
-	g_free (download_info->file_name);
-	g_free (download_info->uri);
-	g_free (download_info->buffer);
-	g_free (download_info);
+	return success;
 }
 
-static void
-data_ready_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+void
+e_attachment_open_handle_error (EAttachment *attachment,
+                                GAsyncResult *result,
+                                GtkWindow *parent)
 {
-	DownloadInfo *download_info = (DownloadInfo *)user_data;
+	GtkWidget *dialog;
+	GFileInfo *file_info;
+	const gchar *display_name;
+	const gchar *primary_text;
 	GError *error = NULL;
-	gssize read;
-
-	g_return_if_fail (download_info != NULL);
 
-	if (g_cancellable_is_cancelled (download_info->cancellable)) {
-		/* finish the operation and close both streams */
-		g_input_stream_read_finish (G_INPUT_STREAM (source_object), res, 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));
 
-		g_output_stream_close (download_info->ostream, NULL, NULL);
-		g_input_stream_close  (download_info->istream, NULL, NULL);
+	if (e_attachment_open_finish (attachment, result, &error))
+		return;
 
-		/* The only way how to get this canceled is in EAttachment's finalize method,
-		   and because the download_info_free free's the attachment on error,
-		   then do not consider cancellation as an error. */
-		download_info_free (download_info);
+	/* Ignore cancellations. */
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		return;
-	}
 
-	read = g_input_stream_read_finish (G_INPUT_STREAM (source_object), res, &error);
+	file_info = e_attachment_get_file_info (attachment);
 
-	if (!error)
-		g_output_stream_write_all (download_info->ostream, download_info->buffer, read, NULL, download_info->cancellable, &error);
+	if (file_info != NULL)
+		display_name = g_file_info_get_display_name (file_info);
+	else
+		display_name = NULL;
 
-	if (error) {
-		download_info->was_error = error->domain != G_IO_ERROR || error->code != G_IO_ERROR_CANCELLED;
-		if (download_info->was_error)
-			e_error_run (download_info->parent, "mail-composer:no-attach", download_info->uri, error->message, 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"));
 
-		g_error_free (error);
+	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);
 
-		download_info->attachment->cancellable = NULL;
-		download_info_free (download_info);
-		return;
-	}
+	gtk_message_dialog_format_secondary_text (
+		GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
 
-	if (read == 0) {
-		CamelException ex;
+	gtk_dialog_run (GTK_DIALOG (dialog));
 
-		/* done with reading */
-		g_output_stream_close (download_info->ostream, NULL, NULL);
-		g_input_stream_close  (download_info->istream, NULL, NULL);
+	gtk_widget_destroy (dialog);
+	g_error_free (error);
+}
 
-		download_info->attachment->cancellable = NULL;
+/************************* e_attachment_save_async() *************************/
 
-		camel_exception_init (&ex);
-		e_attachment_build_remote_file (download_info->file_name, download_info->attachment, "attachment", &ex);
+typedef struct _SaveContext SaveContext;
 
-		if (camel_exception_is_set (&ex)) {
-			download_info->was_error = TRUE;
-			e_error_run (download_info->parent, "mail-composer:no-attach", download_info->uri, camel_exception_get_description (&ex), NULL);
-			camel_exception_clear (&ex);
-		}
+struct _SaveContext {
+	EAttachment *attachment;
+	GSimpleAsyncResult *simple;
 
-		download_info->attachment->percentage = -1;
-		download_info->attachment->is_available_local = TRUE;
-		g_signal_emit (download_info->attachment, signals[UPDATE], 0);
-
-		download_info_free (download_info);
-		return;
-	} else 	if (download_info->file_size) {
-		download_info->attachment->percentage = read * 100 / download_info->file_size;
-		download_info->file_size -= MIN (download_info->file_size, read);
-		g_signal_emit (download_info->attachment, signals[UPDATE], 0);
-	} else {
-		download_info->attachment->percentage = 0;
-		g_signal_emit (download_info->attachment, signals[UPDATE], 0);
-	}
+	GFile *directory;
+	GFile *destination;
+	GInputStream *input_stream;
+	GOutputStream *output_stream;
+	goffset total_num_bytes;
+	gssize bytes_read;
+	gchar buffer[4096];
+	gint count;
+};
 
-	/* read next chunk */
-	g_input_stream_read_async (download_info->istream, download_info->buffer, download_info->buffer_size, G_PRIORITY_DEFAULT, download_info->cancellable, data_ready_cb, download_info);
-}
+/* Forward Declaration */
+static void
+attachment_save_read_cb (GInputStream *input_stream,
+                         GAsyncResult *result,
+                         SaveContext *save_context);
 
-static gboolean
-download_to_local_path (DownloadInfo *download_info, CamelException *ex)
+static SaveContext *
+attachment_save_context_new (EAttachment *attachment,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data)
 {
-	GError *error = NULL;
-	GFile *src = g_file_new_for_uri (download_info->uri);
-	GFile *des = g_file_new_for_path (download_info->file_name);
-	gboolean res = FALSE;
-
-	g_return_val_if_fail (src != NULL && des != NULL, FALSE);
-
-	download_info->ostream = G_OUTPUT_STREAM (g_file_replace (des, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error));
-
-	if (download_info->ostream && !error) {
-		GFileInfo *fi;
+	SaveContext *save_context;
+	GSimpleAsyncResult *simple;
 
-		fi = g_file_query_info (src, G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, NULL, NULL);
+	simple = g_simple_async_result_new (
+		G_OBJECT (attachment), callback,
+		user_data, e_attachment_save_async);
 
-		if (fi) {
-			download_info->file_size = g_file_info_get_attribute_uint64 (fi, G_FILE_ATTRIBUTE_STANDARD_SIZE);
-			g_object_unref (fi);
-		} else {
-			download_info->file_size = 0;
-		}
+	save_context = g_slice_new0 (SaveContext);
+	save_context->attachment = g_object_ref (attachment);
+	save_context->simple = simple;
 
-		download_info->istream = G_INPUT_STREAM (g_file_read (src, NULL, &error));
+	attachment_set_saving (save_context->attachment, TRUE);
 
-		if (download_info->istream && !error) {
-			download_info->cancellable = g_cancellable_new ();
-			download_info->attachment->cancellable = download_info->cancellable;
-			download_info->buffer_size = 10240; /* max 10KB chunk */
-			download_info->buffer = g_malloc (sizeof (char) * download_info->buffer_size);
+	return save_context;
+}
 
-			g_input_stream_read_async (download_info->istream, download_info->buffer, download_info->buffer_size, G_PRIORITY_DEFAULT, download_info->cancellable, data_ready_cb, download_info);
+static void
+attachment_save_context_free (SaveContext *save_context)
+{
+	/* Do not free the GSimpleAsyncResult. */
+	g_object_unref (save_context->attachment);
 
-			res = TRUE;
-		}
-	}
+	if (save_context->directory != NULL)
+		g_object_unref (save_context->directory);
 
-	if (error) {
-		/* propagate error */
-		if (ex)
-			camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, error->message);
+	if (save_context->destination != NULL)
+		g_object_unref (save_context->destination);
 
-		g_error_free (error);
-		download_info->was_error = TRUE;
-		download_info_free (download_info);
-	}
+	if (save_context->input_stream != NULL)
+		g_object_unref (save_context->input_stream);
 
-	g_object_unref (src);
-	g_object_unref (des);
+	if (save_context->output_stream != NULL)
+		g_object_unref (save_context->output_stream);
 
-	return res;
+	g_slice_free (SaveContext, save_context);
 }
 
-EAttachment *
-e_attachment_new_remote_file (GtkWindow *error_dlg_parent, const char *uri, const char *disposition, const char *path, CamelException *ex)
+static gboolean
+attachment_save_check_for_error (SaveContext *save_context,
+                                 GError *error)
 {
-	EAttachment *new;
-	DownloadInfo *download_info;
-	CamelURL *url;
-	char *base;
+	GSimpleAsyncResult *simple;
 
-	g_return_val_if_fail (uri != NULL, NULL);
+	if (error == NULL)
+		return FALSE;
 
-	url = camel_url_new (uri, NULL);
-	base = g_path_get_basename (url->path);
-	camel_url_free (url);
-
-	new = g_object_new (E_TYPE_ATTACHMENT, NULL);
-	new->editor_gui = NULL;
-	new->body = NULL;
-	new->size = 0;
-	new->guessed_type = FALSE;
-	new->cancellable = NULL;
-	new->is_available_local = FALSE;
-	new->percentage = 0;
-	new->file_name = g_build_filename (path, base, NULL);
-
-	g_free (base);
-
-	download_info = g_new0 (DownloadInfo, 1);
-	download_info->attachment = new;
-	download_info->file_name = g_strdup (new->file_name);
-	download_info->uri = g_strdup (uri);
-	download_info->parent = error_dlg_parent;
-	download_info->was_error = FALSE;
+	/* Steal the result. */
+	simple = save_context->simple;
+	save_context->simple = NULL;
 
-	/* it frees all on the error, so do not free it twice */
-	if (!download_to_local_path (download_info, ex))
-		return NULL;
+	g_simple_async_result_set_from_error (simple, error);
+	g_simple_async_result_complete (simple);
+	g_error_free (error);
 
-	return new;
-}
+	attachment_save_context_free (save_context);
 
+	return TRUE;
+}
 
-void
-e_attachment_build_remote_file (const char *file_name, EAttachment *attachment, const char *disposition, CamelException *ex)
+static GFile *
+attachment_save_new_candidate (SaveContext *save_context)
 {
-	CamelMimePart *part;
-	CamelDataWrapper *wrapper;
-	CamelStream *stream;
-	struct stat statbuf;
-	char *mime_type;
-	char *filename;
-	CamelURL *url;
+	GFile *candidate;
+	GFileInfo *file_info;
+	EAttachment *attachment;
+	const gchar *display_name;
+	gchar *basename;
 
-	g_return_if_fail (file_name != NULL);
+	attachment = save_context->attachment;
+	file_info = e_attachment_get_file_info (attachment);
 
-	if (g_stat (file_name, &statbuf) == -1) {
-		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
-				      _("Cannot attach file %s: %s"),
-				      file_name, g_strerror (errno));
-		g_message ("Cannot attach file %s: %s\n", file_name, g_strerror (errno));
-		return;
-	}
+	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");
 
-	/* return if it's not a regular file */
-	if (!S_ISREG (statbuf.st_mode)) {
-		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
-				      _("Cannot attach file %s: not a regular file"),
-				      file_name);
-		g_message ("Cannot attach file %s: not a regular file", file_name);
-		return;
-	}
+	if (save_context->count == 0)
+		basename = g_strdup (display_name);
+	else {
+		GString *string;
+		const gchar *ext;
+		gsize length;
 
-	if (!(stream = camel_stream_fs_new_with_name (file_name, O_RDONLY, 0))) {
-		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
-				      _("Cannot attach file %s: %s"),
-				      file_name, g_strerror (errno));
-		return;
-	}
+		string = g_string_sized_new (strlen (display_name));
+		ext = g_utf8_strchr (display_name, -1, '.');
 
-	if ((mime_type = attachment_guess_mime_type (file_name))) {
-		if (!g_ascii_strcasecmp (mime_type, "message/rfc822")) {
-			wrapper = (CamelDataWrapper *) camel_mime_message_new ();
-		} else {
-			wrapper = camel_data_wrapper_new ();
-		}
+		if (ext != NULL)
+			length = ext - display_name;
+		else
+			length = strlen (display_name);
 
-		camel_data_wrapper_construct_from_stream (wrapper, stream);
-		camel_data_wrapper_set_mime_type (wrapper, mime_type);
-		g_free (mime_type);
-	} else {
-		wrapper = camel_data_wrapper_new ();
-		camel_data_wrapper_construct_from_stream (wrapper, stream);
-		camel_data_wrapper_set_mime_type (wrapper, "application/octet-stream");
-	}
+		g_string_append_len (string, display_name, length);
+		g_string_append_printf (string, " (%d)", save_context->count);
+		g_string_append (string, (ext != NULL) ? ext : "");
 
-	camel_object_unref (stream);
+		basename = g_string_free (string, FALSE);
+	}
 
-	part = camel_mime_part_new ();
-	camel_medium_set_content_object (CAMEL_MEDIUM (part), wrapper);
-	camel_object_unref (wrapper);
+	save_context->count++;
 
-	if (attachment->disposition)
-		camel_mime_part_set_disposition (part, "inline");
-	else
-		camel_mime_part_set_disposition (part, "attachment");
+	candidate = g_file_get_child (save_context->directory, basename);
 
-	if (!attachment->file_name)
-		filename = g_path_get_basename (file_name);
-	else
-		filename = g_path_get_basename (attachment->file_name);
+	g_free (basename);
 
-	camel_mime_part_set_filename (part, filename);
+	return candidate;
+}
 
-	if (attachment->description) {
-		camel_mime_part_set_description (part, attachment->description);
-		g_free (attachment->description);
-		attachment->description = NULL;
-	}
+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;
 
-	attachment->editor_gui = NULL;
-	attachment->body = part;
-	attachment->size = statbuf.st_size;
-	attachment->guessed_type = TRUE;
-	g_free (attachment->file_name);
-	attachment->file_name = filename;
+	bytes_written = g_output_stream_write_finish (
+		output_stream, result, &error);
 
-	url = camel_url_new ("file://", NULL);
-	camel_url_set_path (url, file_name);
-	attachment->store_uri = camel_url_to_string (url, 0);
-	camel_url_free (url);
+	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);
 }
 
-
-/**
- * e_attachment_new_from_mime_part:
- * @part: a CamelMimePart
- *
- * Return value: a new EAttachment based on the mime part
- **/
-EAttachment *
-e_attachment_new_from_mime_part (CamelMimePart *part)
+static void
+attachment_save_read_cb (GInputStream *input_stream,
+                         GAsyncResult *result,
+                         SaveContext *save_context)
 {
-	EAttachment *new;
+	EAttachment *attachment;
+	GCancellable *cancellable;
+	GOutputStream *output_stream;
+	gssize bytes_read;
+	GError *error = NULL;
 
-	g_return_val_if_fail (CAMEL_IS_MIME_PART (part), NULL);
+	bytes_read = g_input_stream_read_finish (
+		input_stream, result, &error);
 
-	new = g_object_new (E_TYPE_ATTACHMENT, NULL);
-	new->editor_gui = NULL;
-	camel_object_ref (part);
-	new->body = part;
-	new->guessed_type = FALSE;
-	new->is_available_local = TRUE;
-	new->size = camel_mime_part_get_content_size (part);
-	new->file_name = g_strdup (camel_mime_part_get_filename(part));
+	if (attachment_save_check_for_error (save_context, error))
+		return;
 
-	return new;
-}
+	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);
 
-
-/* The attachment property dialog.  */
+		attachment_save_context_free (save_context);
 
-typedef struct {
-	GtkWidget *dialog;
-	GtkEntry *file_name_entry;
-	GtkEntry *description_entry;
-	GtkEntry *mime_type_entry;
-	GtkToggleButton *disposition_checkbox;
-	EAttachment *attachment;
-} DialogData;
+		return;
+	}
 
-static void
-destroy_dialog_data (DialogData *data)
-{
-	g_free (data);
+	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);
 }
 
-/*
- * fixme: I am converting EVERYTHING to/from UTF-8, although mime types
- * are in ASCII. This is not strictly necessary, but we want to be
- * consistent and possibly check for errors somewhere.
- */
-
 static void
-set_entry (GladeXML *xml, const char *widget_name, const char *value)
+attachment_save_got_output_stream (SaveContext *save_context)
 {
-	GtkEntry *entry;
+	GCancellable *cancellable;
+	GInputStream *input_stream;
+	CamelDataWrapper *wrapper;
+	CamelMimePart *mime_part;
+	CamelStream *stream;
+	EAttachment *attachment;
+	GByteArray *buffer;
 
-	entry = GTK_ENTRY (glade_xml_get_widget (xml, widget_name));
-	if (entry == NULL)
-		g_warning ("Entry for `%s' not found.", widget_name);
-	else
-		gtk_entry_set_text (entry, value ? value : "");
+	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
-connect_widget (GladeXML *gui, const char *name, const char *signal_name,
-		GCallback func, gpointer data)
+attachment_save_create_cb (GFile *destination,
+                           GAsyncResult *result,
+                           SaveContext *save_context)
 {
-	GtkWidget *widget;
+	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;
 
-	widget = glade_xml_get_widget (gui, name);
-	g_signal_connect (widget, signal_name, func, data);
+	save_context->destination = g_object_ref (destination);
+	attachment_save_got_output_stream (save_context);
 }
 
 static void
-close_cb (GtkWidget *widget, gpointer data)
+attachment_save_replace_cb (GFile *destination,
+                            GAsyncResult *result,
+                            SaveContext *save_context)
 {
-	EAttachment *attachment;
-	DialogData *dialog_data;
+	GFileOutputStream *output_stream;
+	GError *error = NULL;
 
-	dialog_data = (DialogData *) data;
-	attachment = dialog_data->attachment;
+	/* 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;
 
-	gtk_widget_destroy (dialog_data->dialog);
-	g_object_unref (attachment->editor_gui);
-	attachment->editor_gui = NULL;
+	if (attachment_save_check_for_error (save_context, error))
+		return;
 
-	destroy_dialog_data (dialog_data);
+	save_context->destination = g_object_ref (destination);
+	attachment_save_got_output_stream (save_context);
 }
 
 static void
-ok_cb (GtkWidget *widget, gpointer data)
+attachment_save_query_info_cb (GFile *destination,
+                               GAsyncResult *result,
+                               SaveContext *save_context)
 {
-	DialogData *dialog_data;
 	EAttachment *attachment;
-	const char *str;
+	GCancellable *cancellable;
+	GFileInfo *file_info;
+	GFileType file_type;
+	GError *error = NULL;
 
-	dialog_data = (DialogData *) data;
-	attachment = dialog_data->attachment;
+	attachment = save_context->attachment;
+	cancellable = attachment->priv->cancellable;
 
-	str = gtk_entry_get_text (dialog_data->file_name_entry);
-	if (attachment->is_available_local)
-		camel_mime_part_set_filename (attachment->body, str);
-	g_free (attachment->file_name);
-	attachment->file_name = g_strdup (str);
+	file_info = g_file_query_info_finish (destination, result, &error);
 
-	str = gtk_entry_get_text (dialog_data->description_entry);
-	if (attachment->is_available_local) {
-		camel_mime_part_set_description (attachment->body, str);
-	} else {
-		g_free (attachment->description);
-		attachment->description = g_strdup (str);
+	/* 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;
 	}
 
-	str = gtk_entry_get_text (dialog_data->mime_type_entry);
-	if (attachment->is_available_local) {
-		camel_mime_part_set_content_type (attachment->body, str);
-		camel_data_wrapper_set_mime_type(camel_medium_get_content_object(CAMEL_MEDIUM (attachment->body)), str);
-	}
+	if (attachment_save_check_for_error (save_context, error))
+		return;
 
-	if (attachment->is_available_local) {
-		switch (gtk_toggle_button_get_active (dialog_data->disposition_checkbox)) {
-		case 0:
-			camel_mime_part_set_disposition (attachment->body, "attachment");
-			break;
-		case 1:
-			camel_mime_part_set_disposition (attachment->body, "inline");
-			break;
-		default:
-			/* Hmmmm? */
-			break;
-		}
-	} else {
-		attachment->disposition = gtk_toggle_button_get_active (dialog_data->disposition_checkbox);
-	}
+	file_type = g_file_info_get_file_type (file_info);
+	g_object_unref (file_info);
 
-	changed (attachment);
-	close_cb (widget, data);
-}
+	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);
 
-static void
-response_cb (GtkWidget *widget, gint response, gpointer data)
-{
-	if (response == GTK_RESPONSE_OK)
-		ok_cb (widget, data);
-	else
-		close_cb (widget, data);
+		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);
 }
 
 void
-e_attachment_edit (EAttachment *attachment, GtkWidget *parent)
+e_attachment_save_async (EAttachment *attachment,
+                         GFile *destination,
+                         GAsyncReadyCallback callback,
+                         gpointer user_data)
 {
-	CamelContentType *content_type;
-	const char *disposition;
-	DialogData *dialog_data;
-	GladeXML *editor_gui;
-	GtkWidget *window;
-	char *type;
-	char *filename;
+	SaveContext *save_context;
+	GCancellable *cancellable;
 
 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
+	g_return_if_fail (G_IS_FILE (destination));
+	g_return_if_fail (callback != NULL);
 
-	if (attachment->editor_gui != NULL) {
-		window = glade_xml_get_widget (attachment->editor_gui, "dialog");
-		gdk_window_show (window->window);
+	if (e_attachment_get_loading (attachment)) {
+		g_simple_async_report_error_in_idle (
+			G_OBJECT (attachment), callback, user_data,
+			G_IO_ERROR, G_IO_ERROR_BUSY,
+			_("A load operation is already in progress"));
 		return;
 	}
 
-	filename = g_build_filename (EVOLUTION_GLADEDIR, "e-attachment.glade", NULL);
-	editor_gui = glade_xml_new (filename, NULL, NULL);
-	g_free (filename);
+	if (e_attachment_get_saving (attachment)) {
+		g_simple_async_report_error_in_idle (
+			G_OBJECT (attachment), callback, user_data,
+			G_IO_ERROR, G_IO_ERROR_BUSY,
+			_("A save operation is already in progress"));
+		return;
+	}
 
-	if (editor_gui == NULL) {
-		g_warning ("Cannot load `e-attachment.glade'");
+	if (e_attachment_get_mime_part (attachment) == NULL) {
+		g_simple_async_report_error_in_idle (
+			G_OBJECT (attachment), callback, user_data,
+			G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+			_("Attachment contents not loaded"));
 		return;
 	}
 
-	attachment->editor_gui = editor_gui;
+	save_context = attachment_save_context_new (
+		attachment, callback, user_data);
+
+	cancellable = attachment->priv->cancellable;
+	g_cancellable_reset (cancellable);
+
+	/* First we need to know if destination is a directory. */
+	g_file_query_info_async (
+		destination, G_FILE_ATTRIBUTE_STANDARD_TYPE,
+		G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT,
+		cancellable, (GAsyncReadyCallback)
+		attachment_save_query_info_cb, save_context);
+}
 
-	gtk_window_set_transient_for (GTK_WINDOW (glade_xml_get_widget (editor_gui, "dialog")),
-				      GTK_WINDOW (gtk_widget_get_toplevel (parent)));
+GFile *
+e_attachment_save_finish (EAttachment *attachment,
+                          GAsyncResult *result,
+                          GError **error)
+{
+	GSimpleAsyncResult *simple;
+	GFile *destination;
 
-	dialog_data = g_new (DialogData, 1);
-	dialog_data->attachment = attachment;
-	dialog_data->dialog = glade_xml_get_widget (editor_gui, "dialog");
-	dialog_data->file_name_entry = GTK_ENTRY (glade_xml_get_widget (editor_gui, "file_name_entry"));
-	dialog_data->description_entry = GTK_ENTRY (glade_xml_get_widget (editor_gui, "description_entry"));
-	dialog_data->mime_type_entry = GTK_ENTRY (glade_xml_get_widget (editor_gui, "mime_type_entry"));
-	dialog_data->disposition_checkbox = GTK_TOGGLE_BUTTON (glade_xml_get_widget (editor_gui, "disposition_checkbox"));
+	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+	g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
 
-	if (attachment->is_available_local && attachment->body) {
-		set_entry (editor_gui, "file_name_entry", camel_mime_part_get_filename (attachment->body));
-		set_entry (editor_gui, "description_entry", camel_mime_part_get_description (attachment->body));
-		content_type = camel_mime_part_get_content_type (attachment->body);
-		type = camel_content_type_simple (content_type);
-		set_entry (editor_gui, "mime_type_entry", type);
-		g_free (type);
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	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);
 
-		disposition = camel_mime_part_get_disposition (attachment->body);
-		gtk_toggle_button_set_active (dialog_data->disposition_checkbox,
-					      disposition && !g_ascii_strcasecmp (disposition, "inline"));
-	} else {
-		set_entry (editor_gui, "file_name_entry", attachment->file_name);
-		set_entry (editor_gui, "description_entry", attachment->description);
-		if ((type = attachment_guess_mime_type (attachment->file_name))) {
-			set_entry (editor_gui, "mime_type_entry", type);
-			g_free (type);
-		} else {
-			set_entry (editor_gui, "mime_type_entry", "");
-		}
+	attachment_set_saving (attachment, FALSE);
+
+	return destination;
+}
+
+void
+e_attachment_save_handle_error (EAttachment *attachment,
+                                GAsyncResult *result,
+                                GtkWindow *parent)
+{
+	GFile *file;
+	GFileInfo *file_info;
+	GtkWidget *dialog;
+	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));
+
+	file = e_attachment_save_finish (attachment, result, &error);
 
-		gtk_toggle_button_set_active (dialog_data->disposition_checkbox, attachment->disposition);
+	if (file != NULL) {
+		g_object_unref (file);
+		return;
 	}
 
-	connect_widget (editor_gui, "dialog", "response", (GCallback)response_cb, dialog_data);
+	/* 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 save '%s'"), display_name);
+	else
+		primary_text = g_strdup_printf (
+			_("Could not save 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));
 
-	/* make sure that when the parent gets hidden/closed that our windows also close */
-	parent = gtk_widget_get_toplevel (parent);
-	gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog_data->dialog), TRUE);
+	gtk_widget_destroy (dialog);
+	g_error_free (error);
 }
Index: widgets/misc/e-attachment-dialog.h
===================================================================
--- widgets/misc/e-attachment-dialog.h	(revision 0)
+++ widgets/misc/e-attachment-dialog.h	(revision 0)
@@ -0,0 +1,73 @@
+/*
+ * e-attachment-dialog.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_DIALOG_H
+#define E_ATTACHMENT_DIALOG_H
+
+#include <gtk/gtk.h>
+#include <widgets/misc/e-attachment.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_DIALOG \
+	(e_attachment_dialog_get_type ())
+#define E_ATTACHMENT_DIALOG(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_ATTACHMENT_DIALOG, EAttachmentDialog))
+#define E_ATTACHMENT_DIALOG_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_ATTACHMENT_DIALOG, EAttachmentDialogClass))
+#define E_IS_ATTACHMENT_DIALOG(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_ATTACHMENT_DIALOG))
+#define E_IS_ATTACHMENT_DIALOG_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_ATTACHMENT_DIALOG))
+#define E_ATTACHMENT_DIALOG_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_ATTACHMENT_DIALOG, EAttachmentDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentDialog EAttachmentDialog;
+typedef struct _EAttachmentDialogClass EAttachmentDialogClass;
+typedef struct _EAttachmentDialogPrivate EAttachmentDialogPrivate;
+
+struct _EAttachmentDialog {
+	GtkDialog parent;
+	EAttachmentDialogPrivate *priv;
+};
+
+struct _EAttachmentDialogClass {
+	GtkDialogClass parent_class;
+};
+
+GType		e_attachment_dialog_get_type	(void);
+GtkWidget *	e_attachment_dialog_new		(GtkWindow *parent,
+						 EAttachment *attachment);
+EAttachment *	e_attachment_dialog_get_attachment
+						(EAttachmentDialog *dialog);
+void		e_attachment_dialog_set_attachment
+						(EAttachmentDialog *dialog,
+						 EAttachment *attachment);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_DIALOG_H */
Index: widgets/misc/e-attachment.h
===================================================================
--- widgets/misc/e-attachment.h	(revision 37505)
+++ widgets/misc/e-attachment.h	(working copy)
@@ -1,4 +1,6 @@
 /*
+ * e-attachment.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
@@ -13,91 +15,131 @@
  * License along with the program; if not, see <http://www.gnu.org/licenses/>  
  *
  *
- * Authors:
- *		Ettore Perazzoli <ettore@ximian.com>
- * 	   	Srinivasa Ragavan <sragavan@novell.com>
- *
  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  *
  */
 
-#ifndef __E_ATTACHMENT_H__
-#define __E_ATTACHMENT_H__
+#ifndef E_ATTACHMENT_H
+#define E_ATTACHMENT_H
 
-#include <gio/gio.h>
 #include <gtk/gtk.h>
-#include <glade/glade-xml.h>
-#include <camel/camel-mime-part.h>
-#include <camel/camel-exception.h>
 #include <camel/camel-cipher-context.h>
+#include <camel/camel-mime-part.h>
+#include <camel/camel-mime-message.h>
+#include <camel/camel-multipart.h>
 
-#ifdef __cplusplus
-extern "C" {
-#pragma }
-#endif /* __cplusplus */
-
-#define E_TYPE_ATTACHMENT				(e_attachment_get_type ())
-#define E_ATTACHMENT(obj)				(G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_ATTACHMENT, EAttachment))
-#define E_ATTACHMENT_CLASS(klass)			(G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_ATTACHMENT, EAttachmentClass))
-#define E_IS_ATTACHMENT(obj)				(G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_ATTACHMENT))
-#define E_IS_ATTACHMENT_CLASS(klass)			(G_TYPE_CHECK_CLASS_TYPE ((obj), E_TYPE_ATTACHMENT))
-
-
-typedef struct _EAttachment       EAttachment;
-typedef struct _EAttachmentClass  EAttachmentClass;
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT \
+	(e_attachment_get_type ())
+#define E_ATTACHMENT(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_ATTACHMENT, EAttachment))
+#define E_ATTACHMENT_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_ATTACHMENT, EAttachmentClass))
+#define E_IS_ATTACHMENT(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_ATTACHMENT))
+#define E_IS_ATTACHMENT_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_ATTACHMENT))
+#define E_ATTACHMENT_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_ATTACHMENT, EAttachmentClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachment EAttachment;
+typedef struct _EAttachmentClass EAttachmentClass;
+typedef struct _EAttachmentPrivate EAttachmentPrivate;
 
 struct _EAttachment {
 	GObject parent;
-
-	GladeXML *editor_gui;
-
-	CamelMimePart *body;
-	gboolean guessed_type;
-	gulong size;
-
-	GdkPixbuf *pixbuf_cache;
-
-	GCancellable *cancellable;
-
-	gboolean is_available_local;
-	int percentage;
-	char *file_name;
-	char *description;
-	gboolean disposition;
-	int index;
-	char *store_uri;
-
-	/* Status of signed/encrypted attachments */
-	camel_cipher_validity_sign_t sign;
-	camel_cipher_validity_encrypt_t encrypt;
+	EAttachmentPrivate *priv;
 };
 
 struct _EAttachmentClass {
 	GObjectClass parent_class;
-
-	void (*changed)	(EAttachment *attachment);
-	void (*update) (EAttachment *attachment, char *msg);
 };
 
-GType e_attachment_get_type (void);
-EAttachment *e_attachment_new (const char *file_name,
-			       const char *disposition,
-			       CamelException *ex);
-EAttachment * e_attachment_new_remote_file (GtkWindow *error_dlg_parent,
-					    const char *url,
-			       		    const char *disposition,
-					    const char *path,
-	  	   			    CamelException *ex);
-void e_attachment_build_remote_file (const char *filename,
-				     EAttachment *attachment,
-		 	   	     const char *disposition,
-				     CamelException *ex);
-EAttachment *e_attachment_new_from_mime_part (CamelMimePart *part);
-void e_attachment_edit (EAttachment *attachment,
-			GtkWidget *parent);
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
+GType		e_attachment_get_type		(void);
+EAttachment *	e_attachment_new		(void);
+EAttachment *	e_attachment_new_for_path	(const gchar *path);
+EAttachment *	e_attachment_new_for_uri	(const gchar *uri);
+EAttachment *	e_attachment_new_for_message	(CamelMimeMessage *message);
+void		e_attachment_add_to_multipart	(EAttachment *attachment,
+						 CamelMultipart *multipart,
+						 const gchar *default_charset);
+void		e_attachment_cancel		(EAttachment *attachment);
+gboolean	e_attachment_get_can_show	(EAttachment *attachment);
+void		e_attachment_set_can_show	(EAttachment *attachment,
+						 gboolean can_show);
+const gchar *	e_attachment_get_disposition	(EAttachment *attachment);
+void		e_attachment_set_disposition	(EAttachment *attachment,
+						 const gchar *disposition);
+GFile *		e_attachment_get_file		(EAttachment *attachment);
+void		e_attachment_set_file		(EAttachment *attachment,
+						 GFile *file);
+GFileInfo *	e_attachment_get_file_info	(EAttachment *attachment);
+gboolean	e_attachment_get_loading	(EAttachment *attachment);
+CamelMimePart *	e_attachment_get_mime_part	(EAttachment *attachment);
+void		e_attachment_set_mime_part	(EAttachment *attachment,
+						 CamelMimePart *mime_part);
+gint		e_attachment_get_percent	(EAttachment *attachment);
+GtkTreeRowReference *
+		e_attachment_get_reference	(EAttachment *attachment);
+void		e_attachment_set_reference	(EAttachment *attachment,
+						 GtkTreeRowReference *reference);
+gboolean	e_attachment_get_saving		(EAttachment *attachment);
+gboolean	e_attachment_get_shown		(EAttachment *attachment);
+void		e_attachment_set_shown		(EAttachment *attachment,
+						 gboolean shown);
+camel_cipher_validity_encrypt_t
+		e_attachment_get_encrypted	(EAttachment *attachment);
+void		e_attachment_set_encrypted	(EAttachment *attachment,
+						 camel_cipher_validity_encrypt_t encrypted);
+camel_cipher_validity_sign_t
+		e_attachment_get_signed		(EAttachment *attachment);
+void		e_attachment_set_signed		(EAttachment *attachment,
+						 camel_cipher_validity_sign_t signed_);
+const gchar *	e_attachment_get_description	(EAttachment *attachment);
+const gchar *	e_attachment_get_thumbnail_path	(EAttachment *attachment);
+gboolean	e_attachment_is_rfc822		(EAttachment *attachment);
+GList *		e_attachment_list_apps		(EAttachment *attachment);
+
+/* Asynchronous Operations */
+void		e_attachment_load_async		(EAttachment *attachment,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_attachment_load_finish	(EAttachment *attachment,
+						 GAsyncResult *result,
+						 GError **error);
+void		e_attachment_open_async		(EAttachment *attachment,
+						 GAppInfo *app_info,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_attachment_open_finish	(EAttachment *attachment,
+						 GAsyncResult *result,
+						 GError **error);
+void		e_attachment_save_async		(EAttachment *attachment,
+						 GFile *destination,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+GFile *		e_attachment_save_finish	(EAttachment *attachment,
+						 GAsyncResult *result,
+						 GError **error);
+
+/* Handy GAsyncReadyCallback Functions */
+void		e_attachment_load_handle_error	(EAttachment *attachment,
+						 GAsyncResult *result,
+						 GtkWindow *parent);
+void		e_attachment_open_handle_error	(EAttachment *attachment,
+						 GAsyncResult *result,
+						 GtkWindow *parent);
+void		e_attachment_save_handle_error	(EAttachment *attachment,
+						 GAsyncResult *result,
+						 GtkWindow *parent);
+
+G_END_DECLS
 
-#endif /* __E_ATTACHMENT_H__ */
+#endif /* E_ATTACHMENT_H */
Index: widgets/misc/e-attachment-button.c
===================================================================
--- widgets/misc/e-attachment-button.c	(revision 0)
+++ widgets/misc/e-attachment-button.c	(revision 0)
@@ -0,0 +1,725 @@
+/*
+ * e-attachment-button.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)
+ *
+ */
+
+/* Much of the popup menu logic here was ripped from GtkMenuToolButton. */
+
+#include "e-attachment-button.h"
+
+#include "e-util/e-binding.h"
+
+#define E_ATTACHMENT_BUTTON_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_ATTACHMENT_BUTTON, EAttachmentButtonPrivate))
+
+struct _EAttachmentButtonPrivate {
+
+	EAttachmentView *view;
+	EAttachment *attachment;
+	gulong reference_handler_id;
+
+	EMutualBinding *can_show_binding;
+	EMutualBinding *shown_binding;
+
+	GtkWidget *expand_button;
+	GtkWidget *toggle_button;
+	GtkWidget *cell_view;
+
+	guint expandable : 1;
+	guint expanded   : 1;
+};
+
+enum {
+	PROP_0,
+	PROP_ATTACHMENT,
+	PROP_EXPANDABLE,
+	PROP_EXPANDED,
+	PROP_VIEW
+};
+
+static gpointer parent_class;
+
+static void
+attachment_button_menu_deactivate_cb (EAttachmentButton *button)
+{
+	EAttachmentView *view;
+	GtkActionGroup *action_group;
+	GtkToggleButton *toggle_button;
+
+	view = e_attachment_button_get_view (button);
+	action_group = e_attachment_view_get_action_group (view, "inline");
+	toggle_button = GTK_TOGGLE_BUTTON (button->priv->toggle_button);
+
+	gtk_toggle_button_set_active (toggle_button, FALSE);
+
+	gtk_action_group_set_visible (action_group, FALSE);
+}
+
+static void
+attachment_button_menu_position (GtkMenu *menu,
+                                 gint *x,
+                                 gint *y,
+                                 gboolean *push_in,
+                                 EAttachmentButton *button)
+{
+	GtkRequisition menu_requisition;
+	GtkTextDirection direction;
+	GdkRectangle monitor;
+	GdkScreen *screen;
+	GdkWindow *window;
+	GtkWidget *widget;
+	GtkWidget *toggle_button;
+	gint monitor_num;
+
+	widget = GTK_WIDGET (button);
+	toggle_button = button->priv->toggle_button;
+	gtk_widget_size_request (GTK_WIDGET (menu), &menu_requisition);
+
+	window = gtk_widget_get_parent_window (widget);
+	screen = gtk_widget_get_screen (GTK_WIDGET (menu));
+	monitor_num = gdk_screen_get_monitor_at_window (screen, window);
+	if (monitor_num < 0)
+		monitor_num = 0;
+	gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+	gdk_window_get_origin (window, x, y);
+	*x += widget->allocation.x;
+	*y += widget->allocation.y;
+
+	direction = gtk_widget_get_direction (widget);
+	if (direction == GTK_TEXT_DIR_LTR)
+		*x += MAX (widget->allocation.width - menu_requisition.width, 0);
+	else if (menu_requisition.width > widget->allocation.width)
+		*x -= menu_requisition.width - widget->allocation.width;
+
+	if ((*y + toggle_button->allocation.height + menu_requisition.height) <= monitor.y + monitor.height)
+		*y += toggle_button->allocation.height;
+	else if ((*y - menu_requisition.height) >= monitor.y)
+		*y -= menu_requisition.height;
+	else if (monitor.y + monitor.height - (*y + toggle_button->allocation.height) > *y)
+		*y += toggle_button->allocation.height;
+	else
+		*y -= menu_requisition.height;
+
+	*push_in = FALSE;
+}
+
+static void
+attachment_button_select_path (EAttachmentButton *button)
+{
+	EAttachmentView *view;
+	EAttachment *attachment;
+	GtkTreeRowReference *reference;
+	GtkTreePath *path;
+
+	attachment = e_attachment_button_get_attachment (button);
+	g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+	reference = e_attachment_get_reference (attachment);
+	g_return_if_fail (gtk_tree_row_reference_valid (reference));
+
+	view = e_attachment_button_get_view (button);
+	path = gtk_tree_row_reference_get_path (reference);
+
+	e_attachment_view_unselect_all (view);
+	e_attachment_view_select_path (view, path);
+
+	gtk_tree_path_free (path);
+}
+
+static void
+attachment_button_show_popup_menu (EAttachmentButton *button,
+                                   GdkEventButton *event)
+{
+	EAttachmentView *view;
+	GtkActionGroup *action_group;
+	GtkToggleButton *toggle_button;
+
+	view = e_attachment_button_get_view (button);
+	action_group = e_attachment_view_get_action_group (view, "inline");
+	toggle_button = GTK_TOGGLE_BUTTON (button->priv->toggle_button);
+
+	attachment_button_select_path (button);
+	gtk_toggle_button_set_active (toggle_button, TRUE);
+
+	e_attachment_view_show_popup_menu (
+		view, event, (GtkMenuPositionFunc)
+		attachment_button_menu_position, button);
+
+	gtk_action_group_set_visible (action_group, TRUE);
+}
+
+static void
+attachment_button_update_cell_view (EAttachmentButton *button)
+{
+	GtkCellView *cell_view;
+	EAttachment *attachment;
+	GtkTreeRowReference *reference;
+	GtkTreeModel *model = NULL;
+	GtkTreePath *path = NULL;
+
+	cell_view = GTK_CELL_VIEW (button->priv->cell_view);
+
+	attachment = e_attachment_button_get_attachment (button);
+	if (attachment == NULL)
+		goto exit;
+
+	reference = e_attachment_get_reference (attachment);
+	if (reference == NULL)
+		goto exit;
+
+	model = gtk_tree_row_reference_get_model (reference);
+	path = gtk_tree_row_reference_get_path (reference);
+
+exit:
+	gtk_cell_view_set_model (cell_view, model);
+	gtk_cell_view_set_displayed_row (cell_view, path);
+
+	if (path != NULL)
+		gtk_tree_path_free (path);
+}
+
+static void
+attachment_button_update_pixbufs (EAttachmentButton *button)
+{
+	GtkCellView *cell_view;
+	GtkCellRenderer *renderer;
+	GtkIconTheme *icon_theme;
+	GdkPixbuf *pixbuf_expander_open;
+	GdkPixbuf *pixbuf_expander_closed;
+	GList *list;
+
+	icon_theme = gtk_icon_theme_get_default ();
+
+	/* Grab the first cell renderer. */
+	cell_view = GTK_CELL_VIEW (button->priv->cell_view);
+	list = gtk_cell_view_get_cell_renderers (cell_view);
+	renderer = GTK_CELL_RENDERER (list->data);
+	g_list_free (list);
+
+	pixbuf_expander_open = gtk_widget_render_icon (
+		GTK_WIDGET (button), GTK_STOCK_GO_DOWN,
+		GTK_ICON_SIZE_BUTTON, NULL);
+
+	pixbuf_expander_closed = gtk_widget_render_icon (
+		GTK_WIDGET (button), GTK_STOCK_GO_FORWARD,
+		GTK_ICON_SIZE_BUTTON, NULL);
+
+	g_object_set (
+		renderer,
+		"pixbuf-expander-open", pixbuf_expander_open,
+		"pixbuf-expander-closed", pixbuf_expander_closed,
+		NULL);
+
+	g_object_unref (pixbuf_expander_open);
+	g_object_unref (pixbuf_expander_closed);
+}
+
+static void
+attachment_button_expand_clicked_cb (EAttachmentButton *button)
+{
+	gboolean expanded;
+
+	expanded = e_attachment_button_get_expanded (button);
+	e_attachment_button_set_expanded (button, !expanded);
+}
+
+static void
+attachment_button_expand_drag_data_get_cb (EAttachmentButton *button,
+                                           GdkDragContext *context,
+                                           GtkSelectionData *selection,
+                                           guint info,
+                                           guint time)
+{
+	EAttachmentView *view;
+
+	attachment_button_select_path (button);
+
+	view = e_attachment_button_get_view (button);
+
+	e_attachment_view_drag_data_get (
+		view, context, selection, info, time);
+}
+
+static gboolean
+attachment_button_toggle_button_press_event_cb (EAttachmentButton *button,
+                                                GdkEventButton *event)
+{
+	if (event->button == 1) {
+		attachment_button_show_popup_menu (button, event);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void
+attachment_button_set_view (EAttachmentButton *button,
+                            EAttachmentView *view)
+{
+	GtkWidget *menu;
+
+	g_return_if_fail (button->priv->view == NULL);
+
+	button->priv->view = g_object_ref (view);
+
+	menu = e_attachment_view_get_popup_menu (view);
+
+	g_signal_connect_swapped (
+		menu, "deactivate",
+		G_CALLBACK (attachment_button_menu_deactivate_cb), button);
+}
+
+static void
+attachment_button_set_property (GObject *object,
+                                guint property_id,
+                                const GValue *value,
+                                GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ATTACHMENT:
+			e_attachment_button_set_attachment (
+				E_ATTACHMENT_BUTTON (object),
+				g_value_get_object (value));
+			return;
+
+		case PROP_EXPANDABLE:
+			e_attachment_button_set_expandable (
+				E_ATTACHMENT_BUTTON (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_EXPANDED:
+			e_attachment_button_set_expanded (
+				E_ATTACHMENT_BUTTON (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_VIEW:
+			attachment_button_set_view (
+				E_ATTACHMENT_BUTTON (object),
+				g_value_get_object (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_button_get_property (GObject *object,
+                                guint property_id,
+                                GValue *value,
+                                GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ATTACHMENT:
+			g_value_set_object (
+				value,
+				e_attachment_button_get_attachment (
+				E_ATTACHMENT_BUTTON (object)));
+			return;
+
+		case PROP_EXPANDABLE:
+			g_value_set_boolean (
+				value,
+				e_attachment_button_get_expandable (
+				E_ATTACHMENT_BUTTON (object)));
+			return;
+
+		case PROP_EXPANDED:
+			g_value_set_boolean (
+				value,
+				e_attachment_button_get_expanded (
+				E_ATTACHMENT_BUTTON (object)));
+			return;
+
+		case PROP_VIEW:
+			g_value_set_object (
+				value,
+				e_attachment_button_get_view (
+				E_ATTACHMENT_BUTTON (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_button_dispose (GObject *object)
+{
+	EAttachmentButtonPrivate *priv;
+
+	priv = E_ATTACHMENT_BUTTON_GET_PRIVATE (object);
+
+	if (priv->view != NULL) {
+		g_object_unref (priv->view);
+		priv->view = NULL;
+	}
+
+	if (priv->attachment != NULL) {
+		g_signal_handler_disconnect (
+			priv->attachment,
+			priv->reference_handler_id);
+		g_object_unref (priv->attachment);
+		priv->attachment = NULL;
+	}
+
+	if (priv->expand_button != NULL) {
+		g_object_unref (priv->expand_button);
+		priv->expand_button = NULL;
+	}
+
+	if (priv->toggle_button != NULL) {
+		g_object_unref (priv->toggle_button);
+		priv->toggle_button = NULL;
+	}
+
+	if (priv->cell_view != NULL) {
+		g_object_unref (priv->cell_view);
+		priv->cell_view = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+attachment_button_style_set (GtkWidget *widget,
+                             GtkStyle *previous_style)
+{
+	EAttachmentButton *button;
+
+	/* Chain up to parent's style_set() method. */
+	GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
+
+	button = E_ATTACHMENT_BUTTON (widget);
+	attachment_button_update_pixbufs (button);
+}
+
+static void
+attachment_button_class_init (EAttachmentButtonClass *class)
+{
+	GObjectClass *object_class;
+	GtkWidgetClass *widget_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EAttachmentButtonPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = attachment_button_set_property;
+	object_class->get_property = attachment_button_get_property;
+	object_class->dispose = attachment_button_dispose;
+
+	widget_class = GTK_WIDGET_CLASS (class);
+	widget_class->style_set = attachment_button_style_set;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ATTACHMENT,
+		g_param_spec_object (
+			"attachment",
+			"Attachment",
+			NULL,
+			E_TYPE_ATTACHMENT,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_EXPANDABLE,
+		g_param_spec_boolean (
+			"expandable",
+			"Expandable",
+			NULL,
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_EXPANDED,
+		g_param_spec_boolean (
+			"expanded",
+			"Expanded",
+			NULL,
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	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_button_init (EAttachmentButton *button)
+{
+	GtkCellRenderer *renderer;
+	GtkCellLayout *cell_layout;
+	GtkTargetEntry *targets;
+	GtkTargetList *list;
+	GtkWidget *container;
+	GtkWidget *widget;
+	gint n_targets;
+
+	button->priv = E_ATTACHMENT_BUTTON_GET_PRIVATE (button);
+
+	/* Configure Widgets */
+
+	container = GTK_WIDGET (button);
+
+	widget = gtk_button_new ();
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	button->priv->expand_button = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	e_mutual_binding_new (
+		G_OBJECT (button), "expandable",
+		G_OBJECT (widget), "sensitive");
+
+	widget = gtk_toggle_button_new ();
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	button->priv->toggle_button = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	container = button->priv->expand_button;
+
+	widget = gtk_cell_view_new ();
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	button->priv->cell_view = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	container = button->priv->toggle_button;
+
+	widget = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	gtk_widget_show (widget);
+
+	/* Configure Renderers */
+
+	cell_layout = GTK_CELL_LAYOUT (button->priv->cell_view);
+
+	renderer = gtk_cell_renderer_pixbuf_new ();
+	g_object_set (renderer, "is-expander", TRUE, NULL);
+	gtk_cell_layout_pack_start (cell_layout, renderer, FALSE);
+
+	e_mutual_binding_new (
+		G_OBJECT (button), "expanded",
+		G_OBJECT (renderer), "is-expanded");
+
+	renderer = gtk_cell_renderer_pixbuf_new ();
+	g_object_set (renderer, "stock-size", GTK_ICON_SIZE_BUTTON, NULL);
+	gtk_cell_layout_pack_start (cell_layout, renderer, FALSE);
+
+	gtk_cell_layout_add_attribute (
+		cell_layout, renderer, "gicon",
+		E_ATTACHMENT_STORE_COLUMN_ICON);
+
+	/* Configure Drag and Drop */
+
+	list = gtk_target_list_new (NULL, 0);
+	gtk_target_list_add_uri_targets (list, 0);
+	targets = gtk_target_table_new_from_list (list, &n_targets);
+
+	gtk_drag_source_set (
+		button->priv->expand_button, GDK_BUTTON1_MASK,
+		targets, n_targets, GDK_ACTION_COPY);
+
+	gtk_drag_source_set (
+		button->priv->toggle_button, GDK_BUTTON1_MASK,
+		targets, n_targets, GDK_ACTION_COPY);
+
+	gtk_target_table_free (targets, n_targets);
+	gtk_target_list_unref (list);
+
+	/* Configure Signal Handlers */
+
+	g_signal_connect_swapped (
+		button->priv->expand_button, "clicked",
+		G_CALLBACK (attachment_button_expand_clicked_cb), button);
+
+	g_signal_connect_swapped (
+		button->priv->expand_button, "drag-data-get",
+		G_CALLBACK (attachment_button_expand_drag_data_get_cb),
+		button);
+
+	g_signal_connect_swapped (
+		button->priv->toggle_button, "button-press-event",
+		G_CALLBACK (attachment_button_toggle_button_press_event_cb),
+		button);
+
+	g_signal_connect_swapped (
+		button->priv->toggle_button, "drag-data-get",
+		G_CALLBACK (attachment_button_expand_drag_data_get_cb),
+		button);
+
+}
+
+GType
+e_attachment_button_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EAttachmentButtonClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) attachment_button_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EAttachmentButton),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) attachment_button_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_HBOX, "EAttachmentButton", &type_info, 0);
+	}
+
+	return type;
+}
+
+GtkWidget *
+e_attachment_button_new (EAttachmentView *view)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+
+	return g_object_new (
+		E_TYPE_ATTACHMENT_BUTTON,
+		"view", view, NULL);
+}
+
+EAttachmentView *
+e_attachment_button_get_view (EAttachmentButton *button)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT_BUTTON (button), NULL);
+
+	return button->priv->view;
+}
+
+EAttachment *
+e_attachment_button_get_attachment (EAttachmentButton *button)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT_BUTTON (button), NULL);
+
+	return button->priv->attachment;
+}
+
+void
+e_attachment_button_set_attachment (EAttachmentButton *button,
+                                    EAttachment *attachment)
+{
+	g_return_if_fail (E_IS_ATTACHMENT_BUTTON (button));
+
+	if (attachment != NULL) {
+		g_return_if_fail (E_IS_ATTACHMENT (attachment));
+		g_object_ref (attachment);
+	}
+
+	if (button->priv->attachment != NULL) {
+		e_mutual_binding_unbind (
+			button->priv->can_show_binding);
+		button->priv->can_show_binding = NULL;
+		e_mutual_binding_unbind (
+			button->priv->shown_binding);
+		button->priv->shown_binding = NULL;
+		g_signal_handler_disconnect (
+			button->priv->attachment,
+			button->priv->reference_handler_id);
+		g_object_unref (button->priv->attachment);
+	}
+
+	button->priv->attachment = attachment;
+
+	if (attachment != NULL) {
+		EMutualBinding *binding;
+		gulong handler_id;
+
+		binding = e_mutual_binding_new (
+			G_OBJECT (attachment), "can-show",
+			G_OBJECT (button), "expandable");
+		button->priv->can_show_binding = binding;
+
+		binding = e_mutual_binding_new (
+			G_OBJECT (attachment), "shown",
+			G_OBJECT (button), "expanded");
+		button->priv->shown_binding = binding;
+
+		handler_id = g_signal_connect_swapped (
+			attachment, "notify::reference",
+			G_CALLBACK (attachment_button_update_cell_view),
+			button);
+		button->priv->reference_handler_id = handler_id;
+
+		attachment_button_update_cell_view (button);
+		attachment_button_update_pixbufs (button);
+	}
+
+	g_object_notify (G_OBJECT (button), "attachment");
+}
+
+gboolean
+e_attachment_button_get_expandable (EAttachmentButton *button)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT_BUTTON (button), FALSE);
+
+	return button->priv->expandable;
+}
+
+void
+e_attachment_button_set_expandable (EAttachmentButton *button,
+                                    gboolean expandable)
+{
+	g_return_if_fail (E_IS_ATTACHMENT_BUTTON (button));
+
+	button->priv->expandable = expandable;
+
+	if (!expandable)
+		e_attachment_button_set_expanded (button, FALSE);
+
+	g_object_notify (G_OBJECT (button), "expandable");
+}
+
+gboolean
+e_attachment_button_get_expanded (EAttachmentButton *button)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT_BUTTON (button), FALSE);
+
+	return button->priv->expanded;
+}
+
+void
+e_attachment_button_set_expanded (EAttachmentButton *button,
+                                  gboolean expanded)
+{
+	g_return_if_fail (E_IS_ATTACHMENT_BUTTON (button));
+
+	button->priv->expanded = expanded;
+
+	g_object_notify (G_OBJECT (button), "expanded");
+}
Index: widgets/misc/e-attachment-button.h
===================================================================
--- widgets/misc/e-attachment-button.h	(revision 0)
+++ widgets/misc/e-attachment-button.h	(revision 0)
@@ -0,0 +1,83 @@
+/*
+ * e-attachment-button.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_BUTTON_H
+#define E_ATTACHMENT_BUTTON_H
+
+#include <gtk/gtk.h>
+#include <widgets/misc/e-attachment.h>
+#include <widgets/misc/e-attachment-view.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_BUTTON \
+	(e_attachment_button_get_type ())
+#define E_ATTACHMENT_BUTTON(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_ATTACHMENT_BUTTON, EAttachmentButton))
+#define E_ATTACHMENT_BUTTON_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_ATTACHMENT_BUTTON, EAttachmentButtonClass))
+#define E_IS_ATTACHMENT_BUTTON(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_ATTACHMENT_BUTTON))
+#define E_IS_ATTACHMENT_BUTTON_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_ATTACHMENT_BUTTON))
+#define E_ATTACHMENT_BUTTON_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_ATTACHMENT_BUTTON, EAttachmentButtonClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentButton EAttachmentButton;
+typedef struct _EAttachmentButtonClass EAttachmentButtonClass;
+typedef struct _EAttachmentButtonPrivate EAttachmentButtonPrivate;
+
+struct _EAttachmentButton {
+	GtkHBox parent;
+	EAttachmentButtonPrivate *priv;
+};
+
+struct _EAttachmentButtonClass {
+	GtkHBoxClass parent_class;
+};
+
+GType		e_attachment_button_get_type	(void);
+GtkWidget *	e_attachment_button_new	(EAttachmentView *view);
+EAttachmentView *
+		e_attachment_button_get_view	(EAttachmentButton *button);
+EAttachment *	e_attachment_button_get_attachment
+						(EAttachmentButton *button);
+void		e_attachment_button_set_attachment
+						(EAttachmentButton *button,
+						 EAttachment *attachment);
+gboolean	e_attachment_button_get_expandable
+						(EAttachmentButton *button);
+void		e_attachment_button_set_expandable
+						(EAttachmentButton *button,
+						 gboolean expandable);
+gboolean	e_attachment_button_get_expanded(EAttachmentButton *button);
+void		e_attachment_button_set_expanded(EAttachmentButton *button,
+						 gboolean expanded);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_BUTTON_H */
Index: widgets/misc/e-attachment-tree-view.c
===================================================================
--- widgets/misc/e-attachment-tree-view.c	(revision 0)
+++ widgets/misc/e-attachment-tree-view.c	(revision 0)
@@ -0,0 +1,605 @@
+/*
+ * e-attachment-tree-view.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-tree-view.h"
+
+#include <glib/gi18n.h>
+
+#include "e-attachment.h"
+#include "e-attachment-store.h"
+#include "e-attachment-view.h"
+
+#define E_ATTACHMENT_TREE_VIEW_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_ATTACHMENT_TREE_VIEW, EAttachmentTreeViewPrivate))
+
+struct _EAttachmentTreeViewPrivate {
+	EAttachmentViewPrivate view_priv;
+};
+
+enum {
+	PROP_0,
+	PROP_EDITABLE
+};
+
+static gpointer parent_class;
+
+static void
+attachment_tree_view_set_property (GObject *object,
+                                   guint property_id,
+                                   const GValue *value,
+                                   GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_EDITABLE:
+			e_attachment_view_set_editable (
+				E_ATTACHMENT_VIEW (object),
+				g_value_get_boolean (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_tree_view_get_property (GObject *object,
+                                   guint property_id,
+                                   GValue *value,
+                                   GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_EDITABLE:
+			g_value_set_boolean (
+				value, e_attachment_view_get_editable (
+				E_ATTACHMENT_VIEW (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_tree_view_dispose (GObject *object)
+{
+	e_attachment_view_dispose (E_ATTACHMENT_VIEW (object));
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+attachment_tree_view_finalize (GObject *object)
+{
+	e_attachment_view_finalize (E_ATTACHMENT_VIEW (object));
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+attachment_tree_view_render_size (GtkTreeViewColumn *column,
+                                  GtkCellRenderer *renderer,
+                                  GtkTreeModel *model,
+                                  GtkTreeIter *iter)
+{
+	gchar *display_size;
+	gint column_id;
+	guint64 size;
+
+	column_id = E_ATTACHMENT_STORE_COLUMN_SIZE;
+	gtk_tree_model_get (model, iter, column_id, &size, -1);
+
+	display_size = g_format_size_for_display ((goffset) size);
+	g_object_set (renderer, "text", display_size, NULL);
+	g_free (display_size);
+}
+
+static gboolean
+attachment_tree_view_button_press_event (GtkWidget *widget,
+                                         GdkEventButton *event)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	if (e_attachment_view_button_press_event (view, event))
+		return TRUE;
+
+	/* Chain up to parent's button_press_event() method. */
+	return GTK_WIDGET_CLASS (parent_class)->
+		button_press_event (widget, event);
+}
+
+static gboolean
+attachment_tree_view_button_release_event (GtkWidget *widget,
+                                           GdkEventButton *event)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	if (e_attachment_view_button_release_event (view, event))
+		return TRUE;
+
+	/* Chain up to parent's button_release_event() method. */
+	return GTK_WIDGET_CLASS (parent_class)->
+		button_release_event (widget, event);
+}
+
+static gboolean
+attachment_tree_view_key_press_event (GtkWidget *widget,
+                                      GdkEventKey *event)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	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)->
+		key_press_event (widget, event);
+}
+
+static void
+attachment_tree_view_drag_begin (GtkWidget *widget,
+                                 GdkDragContext *context)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	/* Chain up to parent's drag_begin() method. */
+	GTK_WIDGET_CLASS (parent_class)->drag_begin (widget, context);
+
+	e_attachment_view_drag_begin (view, context);
+}
+
+static void
+attachment_tree_view_drag_end (GtkWidget *widget,
+                               GdkDragContext *context)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	/* Chain up to parent's drag_end() method. */
+	GTK_WIDGET_CLASS (parent_class)->drag_end (widget, context);
+
+	e_attachment_view_drag_end (view, context);
+}
+
+static void
+attachment_tree_view_drag_data_get (GtkWidget *widget,
+                                    GdkDragContext *context,
+                                    GtkSelectionData *selection,
+                                    guint info,
+                                    guint time)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	e_attachment_view_drag_data_get (
+		view, context, selection, info, time);
+}
+
+static gboolean
+attachment_tree_view_drag_motion (GtkWidget *widget,
+                                  GdkDragContext *context,
+                                  gint x,
+                                  gint y,
+                                  guint time)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	return e_attachment_view_drag_motion (view, context, x, y, time);
+}
+
+static gboolean
+attachment_tree_view_drag_drop (GtkWidget *widget,
+                                GdkDragContext *context,
+                                gint x,
+                                gint y,
+                                guint time)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	if (!e_attachment_view_drag_drop (view, context, x, y, time))
+		return FALSE;
+
+	/* Chain up to parent's drag_drop() method. */
+	return GTK_WIDGET_CLASS (parent_class)->drag_drop (
+		widget, context, x, y, time);
+}
+
+static void
+attachment_tree_view_drag_data_received (GtkWidget *widget,
+                                         GdkDragContext *context,
+                                         gint x,
+                                         gint y,
+                                         GtkSelectionData *selection,
+                                         guint info,
+                                         guint time)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	e_attachment_view_drag_data_received (
+		view, context, x, y, selection, info, time);
+}
+
+static gboolean
+attachment_tree_view_popup_menu (GtkWidget *widget)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	e_attachment_view_show_popup_menu (view, NULL, NULL, NULL);
+
+	return TRUE;
+}
+
+static void
+attachment_tree_view_row_activated (GtkTreeView *tree_view,
+                                    GtkTreePath *path,
+                                    GtkTreeViewColumn *column)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (tree_view);
+
+	e_attachment_view_open_path (view, path, NULL);
+}
+
+static EAttachmentViewPrivate *
+attachment_tree_view_get_private (EAttachmentView *view)
+{
+	EAttachmentTreeViewPrivate *priv;
+
+	priv = E_ATTACHMENT_TREE_VIEW_GET_PRIVATE (view);
+
+	return &priv->view_priv;
+}
+
+static EAttachmentStore *
+attachment_tree_view_get_store (EAttachmentView *view)
+{
+	GtkTreeView *tree_view;
+	GtkTreeModel *model;
+
+	tree_view = GTK_TREE_VIEW (view);
+	model = gtk_tree_view_get_model (tree_view);
+
+	return E_ATTACHMENT_STORE (model);
+}
+
+static GtkTreePath *
+attachment_tree_view_get_path_at_pos (EAttachmentView *view,
+                                      gint x,
+                                      gint y)
+{
+	GtkTreeView *tree_view;
+	GtkTreePath *path;
+	gboolean row_exists;
+
+	tree_view = GTK_TREE_VIEW (view);
+
+	row_exists = gtk_tree_view_get_path_at_pos (
+		tree_view, x, y, &path, NULL, NULL, NULL);
+
+	return row_exists ? path : NULL;
+}
+
+static GList *
+attachment_tree_view_get_selected_paths (EAttachmentView *view)
+{
+	GtkTreeView *tree_view;
+	GtkTreeSelection *selection;
+
+	tree_view = GTK_TREE_VIEW (view);
+	selection = gtk_tree_view_get_selection (tree_view);
+
+	return gtk_tree_selection_get_selected_rows (selection, NULL);
+}
+
+static gboolean
+attachment_tree_view_path_is_selected (EAttachmentView *view,
+                                       GtkTreePath *path)
+{
+	GtkTreeView *tree_view;
+	GtkTreeSelection *selection;
+
+	tree_view = GTK_TREE_VIEW (view);
+	selection = gtk_tree_view_get_selection (tree_view);
+
+	return gtk_tree_selection_path_is_selected (selection, path);
+}
+
+static void
+attachment_tree_view_select_path (EAttachmentView *view,
+                                  GtkTreePath *path)
+{
+	GtkTreeView *tree_view;
+	GtkTreeSelection *selection;
+
+	tree_view = GTK_TREE_VIEW (view);
+	selection = gtk_tree_view_get_selection (tree_view);
+
+	gtk_tree_selection_select_path (selection, path);
+}
+
+static void
+attachment_tree_view_unselect_path (EAttachmentView *view,
+                                    GtkTreePath *path)
+{
+	GtkTreeView *tree_view;
+	GtkTreeSelection *selection;
+
+	tree_view = GTK_TREE_VIEW (view);
+	selection = gtk_tree_view_get_selection (tree_view);
+
+	gtk_tree_selection_unselect_path (selection, path);
+}
+
+static void
+attachment_tree_view_select_all (EAttachmentView *view)
+{
+	GtkTreeView *tree_view;
+	GtkTreeSelection *selection;
+
+	tree_view = GTK_TREE_VIEW (view);
+	selection = gtk_tree_view_get_selection (tree_view);
+
+	gtk_tree_selection_select_all (selection);
+}
+
+static void
+attachment_tree_view_unselect_all (EAttachmentView *view)
+{
+	GtkTreeView *tree_view;
+	GtkTreeSelection *selection;
+
+	tree_view = GTK_TREE_VIEW (view);
+	selection = gtk_tree_view_get_selection (tree_view);
+
+	gtk_tree_selection_unselect_all (selection);
+}
+
+static void
+attachment_tree_view_drag_source_set (EAttachmentView *view,
+                                      GdkModifierType start_button_mask,
+                                      const GtkTargetEntry *targets,
+                                      gint n_targets,
+                                      GdkDragAction actions)
+{
+	GtkTreeView *tree_view;
+
+	tree_view = GTK_TREE_VIEW (view);
+
+	gtk_tree_view_enable_model_drag_source (
+		tree_view, start_button_mask, targets, n_targets, actions);
+}
+
+static void
+attachment_tree_view_drag_dest_set (EAttachmentView *view,
+                                    const GtkTargetEntry *targets,
+                                    gint n_targets,
+                                    GdkDragAction actions)
+{
+	GtkTreeView *tree_view;
+
+	tree_view = GTK_TREE_VIEW (view);
+
+	gtk_tree_view_enable_model_drag_dest (
+		tree_view, targets, n_targets, actions);
+}
+
+static void
+attachment_tree_view_drag_source_unset (EAttachmentView *view)
+{
+	GtkTreeView *tree_view;
+
+	tree_view = GTK_TREE_VIEW (view);
+
+	gtk_tree_view_unset_rows_drag_source (tree_view);
+}
+
+static void
+attachment_tree_view_drag_dest_unset (EAttachmentView *view)
+{
+	GtkTreeView *tree_view;
+
+	tree_view = GTK_TREE_VIEW (view);
+
+	gtk_tree_view_unset_rows_drag_dest (tree_view);
+}
+
+static void
+attachment_tree_view_class_init (EAttachmentTreeViewClass *class)
+{
+	GObjectClass *object_class;
+	GtkWidgetClass *widget_class;
+	GtkTreeViewClass *tree_view_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EAttachmentViewPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = attachment_tree_view_set_property;
+	object_class->get_property = attachment_tree_view_get_property;
+	object_class->dispose = attachment_tree_view_dispose;
+	object_class->finalize = attachment_tree_view_finalize;
+
+	widget_class = GTK_WIDGET_CLASS (class);
+	widget_class->button_press_event = attachment_tree_view_button_press_event;
+	widget_class->button_release_event = attachment_tree_view_button_release_event;
+	widget_class->key_press_event = attachment_tree_view_key_press_event;
+	widget_class->drag_begin = attachment_tree_view_drag_begin;
+	widget_class->drag_end = attachment_tree_view_drag_end;
+	widget_class->drag_data_get = attachment_tree_view_drag_data_get;
+	widget_class->drag_motion = attachment_tree_view_drag_motion;
+	widget_class->drag_drop = attachment_tree_view_drag_drop;
+	widget_class->drag_data_received = attachment_tree_view_drag_data_received;
+	widget_class->popup_menu = attachment_tree_view_popup_menu;
+
+	tree_view_class = GTK_TREE_VIEW_CLASS (class);
+	tree_view_class->row_activated = attachment_tree_view_row_activated;
+
+	g_object_class_override_property (
+		object_class, PROP_EDITABLE, "editable");
+}
+
+static void
+attachment_tree_view_iface_init (EAttachmentViewIface *iface)
+{
+	iface->get_private = attachment_tree_view_get_private;
+	iface->get_store = attachment_tree_view_get_store;
+
+	iface->get_path_at_pos = attachment_tree_view_get_path_at_pos;
+	iface->get_selected_paths = attachment_tree_view_get_selected_paths;
+	iface->path_is_selected = attachment_tree_view_path_is_selected;
+	iface->select_path = attachment_tree_view_select_path;
+	iface->unselect_path = attachment_tree_view_unselect_path;
+	iface->select_all = attachment_tree_view_select_all;
+	iface->unselect_all = attachment_tree_view_unselect_all;
+
+	iface->drag_source_set = attachment_tree_view_drag_source_set;
+	iface->drag_dest_set = attachment_tree_view_drag_dest_set;
+	iface->drag_source_unset = attachment_tree_view_drag_source_unset;
+	iface->drag_dest_unset = attachment_tree_view_drag_dest_unset;
+}
+
+static void
+attachment_tree_view_init (EAttachmentTreeView *tree_view)
+{
+	GtkTreeSelection *selection;
+	GtkTreeViewColumn *column;
+	GtkCellRenderer *renderer;
+
+	tree_view->priv = E_ATTACHMENT_TREE_VIEW_GET_PRIVATE (tree_view);
+
+	e_attachment_view_init (E_ATTACHMENT_VIEW (tree_view));
+
+	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree_view), TRUE);
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+
+	/* Name Column */
+	column = gtk_tree_view_column_new ();
+	gtk_tree_view_column_set_expand (column, TRUE);
+	gtk_tree_view_column_set_spacing (column, 3);
+	gtk_tree_view_column_set_title (column, _("Description"));
+	gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+
+	renderer = gtk_cell_renderer_pixbuf_new ();
+	gtk_tree_view_column_pack_start (column, renderer, FALSE);
+
+	g_object_set (renderer, "stock-size", GTK_ICON_SIZE_MENU, NULL);
+
+	gtk_tree_view_column_add_attribute (
+		column, renderer, "gicon",
+		E_ATTACHMENT_STORE_COLUMN_ICON);
+
+	renderer = gtk_cell_renderer_text_new ();
+	g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+	gtk_tree_view_column_pack_start (column, renderer, TRUE);
+
+	gtk_tree_view_column_add_attribute (
+		column, renderer, "text",
+		E_ATTACHMENT_STORE_COLUMN_DESCRIPTION);
+
+	renderer = gtk_cell_renderer_progress_new ();
+	g_object_set (renderer, "text", _("Loading"), NULL);
+	gtk_tree_view_column_pack_start (column, renderer, TRUE);
+
+	gtk_tree_view_column_add_attribute (
+		column, renderer, "value",
+		E_ATTACHMENT_STORE_COLUMN_PERCENT);
+
+	gtk_tree_view_column_add_attribute (
+		column, renderer, "visible",
+		E_ATTACHMENT_STORE_COLUMN_LOADING);
+
+	renderer = gtk_cell_renderer_progress_new ();
+	g_object_set (renderer, "text", _("Saving"), NULL);
+	gtk_tree_view_column_pack_start (column, renderer, TRUE);
+
+	gtk_tree_view_column_add_attribute (
+		column, renderer, "value",
+		E_ATTACHMENT_STORE_COLUMN_PERCENT);
+
+	gtk_tree_view_column_add_attribute (
+		column, renderer, "visible",
+		E_ATTACHMENT_STORE_COLUMN_SAVING);
+
+	/* Size Column */
+	column = gtk_tree_view_column_new ();
+	gtk_tree_view_column_set_title (column, _("Size"));
+	gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_tree_view_column_pack_start (column, renderer, TRUE);
+
+	gtk_tree_view_column_set_cell_data_func (
+		column, renderer, (GtkTreeCellDataFunc)
+		attachment_tree_view_render_size, NULL, NULL);
+
+	/* Type Column */
+	column = gtk_tree_view_column_new ();
+	gtk_tree_view_column_set_title (column, _("Type"));
+	gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_tree_view_column_pack_start (column, renderer, TRUE);
+
+	gtk_tree_view_column_add_attribute (
+		column, renderer, "text",
+		E_ATTACHMENT_STORE_COLUMN_CONTENT_TYPE);
+}
+
+GType
+e_attachment_tree_view_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EAttachmentTreeViewClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) attachment_tree_view_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EAttachmentTreeView),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) attachment_tree_view_init,
+			NULL   /* value_table */
+		};
+
+		static const GInterfaceInfo iface_info = {
+			(GInterfaceInitFunc) attachment_tree_view_iface_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL   /* interface_data */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_TREE_VIEW, "EAttachmentTreeView",
+			&type_info, 0);
+
+		g_type_add_interface_static (
+			type, E_TYPE_ATTACHMENT_VIEW, &iface_info);
+	}
+
+	return type;
+}
+
+GtkWidget *
+e_attachment_tree_view_new (void)
+{
+	return g_object_new (E_TYPE_ATTACHMENT_TREE_VIEW, NULL);
+}
Index: widgets/misc/e-attachment-tree-view.h
===================================================================
--- widgets/misc/e-attachment-tree-view.h	(revision 0)
+++ widgets/misc/e-attachment-tree-view.h	(revision 0)
@@ -0,0 +1,66 @@
+/*
+ * e-attachment-tree-view.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_TREE_VIEW_H
+#define E_ATTACHMENT_TREE_VIEW_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_TREE_VIEW \
+	(e_attachment_tree_view_get_type ())
+#define E_ATTACHMENT_TREE_VIEW(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_ATTACHMENT_TREE_VIEW, EAttachmentTreeView))
+#define E_ATTACHMENT_TREE_VIEW_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_ATTACHMENT_TREE_VIEW, EAttachmentTreeViewClass))
+#define E_IS_ATTACHMENT_TREE_VIEW(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_ATTACHMENT_TREE_VIEW))
+#define E_IS_ATTACHMENT_TREE_VIEW_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_ATTACHMENT_TREE_VIEW))
+#define E_ATTACHMENT_TREE_VIEW_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_ATTACHMENT_TREE_VIEW, EAttachmentTreeViewClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentTreeView EAttachmentTreeView;
+typedef struct _EAttachmentTreeViewClass EAttachmentTreeViewClass;
+typedef struct _EAttachmentTreeViewPrivate EAttachmentTreeViewPrivate;
+
+struct _EAttachmentTreeView {
+	GtkTreeView parent;
+	EAttachmentTreeViewPrivate *priv;
+};
+
+struct _EAttachmentTreeViewClass {
+	GtkTreeViewClass parent_class;
+};
+
+GType		e_attachment_tree_view_get_type		(void);
+GtkWidget *	e_attachment_tree_view_new		(void);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_TREE_VIEW_H */
Index: widgets/misc/e-attachment-store.c
===================================================================
--- widgets/misc/e-attachment-store.c	(revision 0)
+++ widgets/misc/e-attachment-store.c	(revision 0)
@@ -0,0 +1,874 @@
+/*
+ * e-attachment-store.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-store.h"
+
+#include <errno.h>
+#include <config.h>
+#include <glib/gi18n.h>
+
+#include "e-util/e-util.h"
+#include "e-util/e-mktemp.h"
+#include "e-util/gconf-bridge.h"
+
+#define E_ATTACHMENT_STORE_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_ATTACHMENT_STORE, EAttachmentStorePrivate))
+
+struct _EAttachmentStorePrivate {
+	GHashTable *attachment_index;
+	gchar *current_folder;
+
+	guint ignore_row_changed : 1;
+};
+
+enum {
+	PROP_0,
+	PROP_CURRENT_FOLDER,
+	PROP_NUM_ATTACHMENTS,
+	PROP_NUM_LOADING,
+	PROP_TOTAL_SIZE
+};
+
+static gpointer parent_class;
+
+static void
+attachment_store_set_property (GObject *object,
+                               guint property_id,
+                               const GValue *value,
+                               GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_CURRENT_FOLDER:
+			e_attachment_store_set_current_folder (
+				E_ATTACHMENT_STORE (object),
+				g_value_get_string (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_store_get_property (GObject *object,
+                               guint property_id,
+                               GValue *value,
+                               GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_CURRENT_FOLDER:
+			g_value_set_string (
+				value,
+				e_attachment_store_get_current_folder (
+				E_ATTACHMENT_STORE (object)));
+			return;
+
+		case PROP_NUM_ATTACHMENTS:
+			g_value_set_uint (
+				value,
+				e_attachment_store_get_num_attachments (
+				E_ATTACHMENT_STORE (object)));
+			return;
+
+		case PROP_NUM_LOADING:
+			g_value_set_uint (
+				value,
+				e_attachment_store_get_num_loading (
+				E_ATTACHMENT_STORE (object)));
+			return;
+
+		case PROP_TOTAL_SIZE:
+			g_value_set_uint64 (
+				value,
+				e_attachment_store_get_total_size (
+				E_ATTACHMENT_STORE (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_store_dispose (GObject *object)
+{
+	EAttachmentStorePrivate *priv;
+
+	priv = E_ATTACHMENT_STORE_GET_PRIVATE (object);
+
+	g_hash_table_remove_all (priv->attachment_index);
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+attachment_store_finalize (GObject *object)
+{
+	EAttachmentStorePrivate *priv;
+
+	priv = E_ATTACHMENT_STORE_GET_PRIVATE (object);
+
+	g_hash_table_destroy (priv->attachment_index);
+
+	g_free (priv->current_folder);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+attachment_store_constructed (GObject *object)
+{
+	GConfBridge *bridge;
+	const gchar *key;
+
+	bridge = gconf_bridge_get ();
+
+	key = "/apps/evolution/shell/current_folder";
+	gconf_bridge_bind_property (bridge, key, object, "current-folder");
+}
+
+static void
+attachment_store_class_init (EAttachmentStoreClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EAttachmentStorePrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = attachment_store_set_property;
+	object_class->get_property = attachment_store_get_property;
+	object_class->dispose = attachment_store_dispose;
+	object_class->finalize = attachment_store_finalize;
+	object_class->constructed = attachment_store_constructed;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_CURRENT_FOLDER,
+		g_param_spec_string (
+			"current-folder",
+			"Current Folder",
+			NULL,
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_NUM_ATTACHMENTS,
+		g_param_spec_uint (
+			"num-attachments",
+			"Num Attachments",
+			NULL,
+			0,
+			G_MAXUINT,
+			0,
+			G_PARAM_READABLE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_NUM_LOADING,
+		g_param_spec_uint (
+			"num-loading",
+			"Num Loading",
+			NULL,
+			0,
+			G_MAXUINT,
+			0,
+			G_PARAM_READABLE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_TOTAL_SIZE,
+		g_param_spec_uint64 (
+			"total-size",
+			"Total Size",
+			NULL,
+			0,
+			G_MAXUINT64,
+			0,
+			G_PARAM_READABLE));
+}
+
+static void
+attachment_store_init (EAttachmentStore *store)
+{
+	GType types[E_ATTACHMENT_STORE_NUM_COLUMNS];
+	GHashTable *attachment_index;
+	gint column = 0;
+
+	attachment_index = g_hash_table_new_full (
+		g_direct_hash, g_direct_equal,
+		(GDestroyNotify) g_object_unref,
+		(GDestroyNotify) gtk_tree_row_reference_free);
+
+	store->priv = E_ATTACHMENT_STORE_GET_PRIVATE (store);
+	store->priv->attachment_index = attachment_index;
+
+	types[column++] = E_TYPE_ATTACHMENT;	/* COLUMN_ATTACHMENT */
+	types[column++] = G_TYPE_STRING;	/* COLUMN_CAPTION */
+	types[column++] = G_TYPE_STRING;	/* COLUMN_CONTENT_TYPE */
+	types[column++] = G_TYPE_STRING;	/* COLUMN_DESCRIPTION */
+	types[column++] = G_TYPE_ICON;		/* COLUMN_ICON */
+	types[column++] = G_TYPE_BOOLEAN;	/* COLUMN_LOADING */
+	types[column++] = G_TYPE_INT;		/* COLUMN_PERCENT */
+	types[column++] = G_TYPE_BOOLEAN;	/* COLUMN_SAVING */
+	types[column++] = G_TYPE_UINT64;	/* COLUMN_SIZE */
+
+	g_assert (column == E_ATTACHMENT_STORE_NUM_COLUMNS);
+
+	gtk_list_store_set_column_types (
+		GTK_LIST_STORE (store), G_N_ELEMENTS (types), types);
+}
+
+GType
+e_attachment_store_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EAttachmentStoreClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) attachment_store_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EAttachmentStore),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) attachment_store_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_LIST_STORE, "EAttachmentStore",
+			&type_info, 0);
+	}
+
+	return type;
+}
+
+GtkTreeModel *
+e_attachment_store_new (void)
+{
+	return g_object_new (E_TYPE_ATTACHMENT_STORE, NULL);
+}
+
+void
+e_attachment_store_add_attachment (EAttachmentStore *store,
+                                   EAttachment *attachment)
+{
+	GtkTreeRowReference *reference;
+	GtkTreeModel *model;
+	GtkTreePath *path;
+	GtkTreeIter iter;
+	GFile *file;
+
+	g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+	g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+	gtk_list_store_append (GTK_LIST_STORE (store), &iter);
+
+	gtk_list_store_set (
+		GTK_LIST_STORE (store), &iter,
+		E_ATTACHMENT_STORE_COLUMN_ATTACHMENT, attachment, -1);
+
+	model = GTK_TREE_MODEL (store);
+	path = gtk_tree_model_get_path (model, &iter);
+	reference = gtk_tree_row_reference_new (model, path);
+	gtk_tree_path_free (path);
+
+	g_hash_table_insert (
+		store->priv->attachment_index,
+		g_object_ref (attachment), reference);
+
+	file = e_attachment_get_file (attachment);
+
+	/* This lets the attachment tell us when to update. */
+	e_attachment_set_reference (attachment, reference);
+
+	g_object_freeze_notify (G_OBJECT (store));
+	g_object_notify (G_OBJECT (store), "num-attachments");
+	g_object_notify (G_OBJECT (store), "total-size");
+	g_object_thaw_notify (G_OBJECT (store));
+}
+
+gboolean
+e_attachment_store_remove_attachment (EAttachmentStore *store,
+                                      EAttachment *attachment)
+{
+	GtkTreeRowReference *reference;
+	GHashTable *hash_table;
+	GtkTreeModel *model;
+	GtkTreePath *path;
+	GtkTreeIter iter;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), FALSE);
+	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+
+	hash_table = store->priv->attachment_index;
+	reference = g_hash_table_lookup (hash_table, attachment);
+
+	if (reference == NULL)
+		return FALSE;
+
+	if (!gtk_tree_row_reference_valid (reference)) {
+		g_hash_table_remove (hash_table, attachment);
+		return FALSE;
+	}
+
+	e_attachment_cancel (attachment);
+	e_attachment_set_reference (attachment, NULL);
+
+	model = gtk_tree_row_reference_get_model (reference);
+	path = gtk_tree_row_reference_get_path (reference);
+	gtk_tree_model_get_iter (model, &iter, path);
+	gtk_tree_path_free (path);
+
+	gtk_list_store_remove (GTK_LIST_STORE (store), &iter);
+	g_hash_table_remove (hash_table, attachment);
+
+	g_object_freeze_notify (G_OBJECT (store));
+	g_object_notify (G_OBJECT (store), "num-attachments");
+	g_object_notify (G_OBJECT (store), "total-size");
+	g_object_thaw_notify (G_OBJECT (store));
+
+	return TRUE;
+}
+
+void
+e_attachment_store_add_to_multipart (EAttachmentStore *store,
+                                     CamelMultipart *multipart,
+                                     const gchar *default_charset)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	gboolean valid;
+
+	g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+	g_return_if_fail (CAMEL_MULTIPART (multipart));
+
+	model = GTK_TREE_MODEL (store);
+	valid = gtk_tree_model_get_iter_first (model, &iter);
+
+	while (valid) {
+		EAttachment *attachment;
+		gint column_id;
+
+		column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+		gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
+
+		/* Skip the attachment if it's still loading. */
+		if (!e_attachment_get_loading (attachment))
+			e_attachment_add_to_multipart (
+				attachment, multipart, default_charset);
+
+		g_object_unref (attachment);
+
+		valid = gtk_tree_model_iter_next (model, &iter);
+	}
+}
+
+const gchar *
+e_attachment_store_get_current_folder (EAttachmentStore *store)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), NULL);
+
+	return store->priv->current_folder;
+}
+
+void
+e_attachment_store_set_current_folder (EAttachmentStore *store,
+                                       const gchar *current_folder)
+{
+	g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+
+	if (current_folder == NULL)
+		current_folder = g_get_home_dir ();
+
+	g_free (store->priv->current_folder);
+	store->priv->current_folder = g_strdup (current_folder);
+
+	g_object_notify (G_OBJECT (store), "current-folder");
+}
+
+guint
+e_attachment_store_get_num_attachments (EAttachmentStore *store)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), 0);
+
+	return g_hash_table_size (store->priv->attachment_index);
+}
+
+guint
+e_attachment_store_get_num_loading (EAttachmentStore *store)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	guint num_loading = 0;
+	gboolean valid;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), 0);
+
+	model = GTK_TREE_MODEL (store);
+	valid = gtk_tree_model_get_iter_first (model, &iter);
+
+	while (valid) {
+		EAttachment *attachment;
+		gint column_id;
+
+		column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+		gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
+		if (e_attachment_get_loading (attachment))
+			num_loading++;
+		g_object_unref (attachment);
+
+		valid = gtk_tree_model_iter_next (model, &iter);
+	}
+
+	return num_loading;
+}
+
+goffset
+e_attachment_store_get_total_size (EAttachmentStore *store)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	goffset total_size = 0;
+	gboolean valid;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), 0);
+
+	model = GTK_TREE_MODEL (store);
+	valid = gtk_tree_model_get_iter_first (model, &iter);
+
+	while (valid) {
+		EAttachment *attachment;
+		GFileInfo *file_info;
+		gint column_id;
+
+		column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+		gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
+		file_info = e_attachment_get_file_info (attachment);
+		if (file_info != NULL)
+			total_size += g_file_info_get_size (file_info);
+		g_object_unref (attachment);
+
+		valid = gtk_tree_model_iter_next (model, &iter);
+	}
+
+	return total_size;
+}
+
+gint
+e_attachment_store_run_file_chooser_dialog (EAttachmentStore *store,
+                                            GtkWidget *dialog)
+{
+	GtkFileChooser *file_chooser;
+	gint response = GTK_RESPONSE_NONE;
+	const gchar *current_folder;
+	gboolean update_folder;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), response);
+	g_return_val_if_fail (GTK_IS_FILE_CHOOSER_DIALOG (dialog), response);
+
+	file_chooser = GTK_FILE_CHOOSER (dialog);
+	current_folder = e_attachment_store_get_current_folder (store);
+	gtk_file_chooser_set_current_folder (file_chooser, current_folder);
+
+	response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+	update_folder =
+		(response == GTK_RESPONSE_ACCEPT) ||
+		(response == GTK_RESPONSE_OK) ||
+		(response == GTK_RESPONSE_YES) ||
+		(response == GTK_RESPONSE_APPLY);
+
+	if (update_folder) {
+		gchar *folder;
+
+		folder = gtk_file_chooser_get_current_folder (file_chooser);
+		e_attachment_store_set_current_folder (store, folder);
+		g_free (folder);
+	}
+
+	return response;
+}
+
+void
+e_attachment_store_run_load_dialog (EAttachmentStore *store,
+                                    GtkWindow *parent)
+{
+	GtkFileChooser *file_chooser;
+	GtkWidget *dialog;
+	GtkWidget *option;
+	GSList *files, *iter;
+	const gchar *disposition;
+	gboolean active;
+	gint response;
+
+	g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+	g_return_if_fail (GTK_IS_WINDOW (parent));
+
+	dialog = gtk_file_chooser_dialog_new (
+		_("Add Attachment"), parent,
+		GTK_FILE_CHOOSER_ACTION_OPEN,
+		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+		_("A_ttach"), GTK_RESPONSE_OK, NULL);
+
+	file_chooser = GTK_FILE_CHOOSER (dialog);
+	gtk_file_chooser_set_local_only (file_chooser, FALSE);
+	gtk_file_chooser_set_select_multiple (file_chooser, TRUE);
+	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+	gtk_window_set_icon_name (GTK_WINDOW (dialog), "mail-attachment");
+
+	option = gtk_check_button_new_with_mnemonic (
+		_("_Suggest automatic display of attachment"));
+	gtk_file_chooser_set_extra_widget (file_chooser, option);
+	gtk_widget_show (option);
+
+	response = e_attachment_store_run_file_chooser_dialog (store, dialog);
+
+	if (response != GTK_RESPONSE_OK)
+		goto exit;
+
+	files = gtk_file_chooser_get_files (file_chooser);
+	active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (option));
+	disposition = active ? "inline" : "attachment";
+
+	for (iter = files; iter != NULL; iter = g_slist_next (iter)) {
+		EAttachment *attachment;
+		GFile *file = iter->data;
+
+		attachment = e_attachment_new ();
+		e_attachment_set_file (attachment, file);
+		e_attachment_store_add_attachment (store, attachment);
+		e_attachment_load_async (
+			attachment, (GAsyncReadyCallback)
+			e_attachment_load_handle_error, parent);
+		g_object_unref (attachment);
+	}
+
+	g_slist_foreach (files, (GFunc) g_object_unref, NULL);
+	g_slist_free (files);
+
+exit:
+	gtk_widget_destroy (dialog);
+}
+
+GFile *
+e_attachment_store_run_save_dialog (EAttachmentStore *store,
+                                    GList *attachment_list,
+                                    GtkWindow *parent)
+{
+	GtkFileChooser *file_chooser;
+	GtkFileChooserAction action;
+	GtkWidget *dialog;
+	GFile *destination;
+	const gchar *title;
+	gint response;
+	guint length;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), NULL);
+
+	length = g_list_length (attachment_list);
+
+	if (length == 0)
+		return NULL;
+
+	title = ngettext ("Save Attachment", "Save Attachments", length);
+
+	if (length == 1)
+		action = GTK_FILE_CHOOSER_ACTION_SAVE;
+	else
+		action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
+
+	dialog = gtk_file_chooser_dialog_new (
+		title, parent, action,
+		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+		GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL);
+
+	file_chooser = GTK_FILE_CHOOSER (dialog);
+	gtk_file_chooser_set_local_only (file_chooser, FALSE);
+	gtk_file_chooser_set_do_overwrite_confirmation (file_chooser, TRUE);
+	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+	gtk_window_set_icon_name (GTK_WINDOW (dialog), "mail-attachment");
+
+	if (action == GTK_FILE_CHOOSER_ACTION_SAVE) {
+		EAttachment *attachment;
+		GFileInfo *file_info;
+		const gchar *name = NULL;
+
+		attachment = attachment_list->data;
+		file_info = e_attachment_get_file_info (attachment);
+		if (file_info != NULL)
+			name = g_file_info_get_display_name (file_info);
+		if (name == NULL)
+			/* Translators: Default attachment filename. */
+			name = _("attachment.dat");
+		gtk_file_chooser_set_current_name (file_chooser, name);
+	}
+
+	response = e_attachment_store_run_file_chooser_dialog (store, dialog);
+
+	if (response == GTK_RESPONSE_OK)
+		destination = gtk_file_chooser_get_file (file_chooser);
+	else
+		destination = NULL;
+
+	gtk_widget_destroy (dialog);
+
+	return destination;
+}
+
+/******************** e_attachment_store_get_uris_async() ********************/
+
+typedef struct _UriContext UriContext;
+
+struct _UriContext {
+	GSimpleAsyncResult *simple;
+	GList *attachment_list;
+	GError *error;
+	gchar **uris;
+	gint index;
+};
+
+static UriContext *
+attachment_store_uri_context_new (EAttachmentStore *store,
+                                  GList *attachment_list,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data)
+{
+	UriContext *uri_context;
+	GSimpleAsyncResult *simple;
+	guint length;
+	gchar **uris;
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (store), callback, user_data,
+		e_attachment_store_get_uris_async);
+
+	/* Add one for NULL terminator. */
+	length = g_list_length (attachment_list) + 1;
+	uris = g_malloc0 (sizeof (gchar *) * length);
+
+	uri_context = g_slice_new0 (UriContext);
+	uri_context->simple = simple;
+	uri_context->attachment_list = g_list_copy (attachment_list);
+	uri_context->uris = uris;
+
+	g_list_foreach (
+		uri_context->attachment_list,
+		(GFunc) g_object_ref, NULL);
+
+	return uri_context;
+}
+
+static void
+attachment_store_uri_context_free (UriContext *uri_context)
+{
+	/* Do not free the GSimpleAsyncResult. */
+
+	/* The attachment list should be empty now. */
+	g_warn_if_fail (uri_context->attachment_list == NULL);
+
+	/* So should the error. */
+	g_warn_if_fail (uri_context->error == NULL);
+
+	g_strfreev (uri_context->uris);
+
+	g_slice_free (UriContext, uri_context);
+}
+
+static void
+attachment_store_get_uris_save_cb (EAttachment *attachment,
+                                   GAsyncResult *result,
+                                   UriContext *uri_context)
+{
+	GSimpleAsyncResult *simple;
+	GFile *file;
+	gchar **uris;
+	gchar *uri;
+	GError *error = NULL;
+
+	file = e_attachment_save_finish (attachment, result, &error);
+
+	/* Remove the attachment from the list. */
+	uri_context->attachment_list = g_list_remove (
+		uri_context->attachment_list, attachment);
+	g_object_unref (attachment);
+
+	if (file != NULL) {
+		uri = g_file_get_uri (file);
+		uri_context->uris[uri_context->index++] = uri;
+		g_object_unref (file);
+
+	} else if (error != NULL) {
+		/* If this is the first error, cancel the other jobs. */
+		if (uri_context->error == NULL) {
+			g_propagate_error (&uri_context->error, error);
+			g_list_foreach (
+				uri_context->attachment_list,
+				(GFunc) e_attachment_cancel, NULL);
+			error = NULL;
+
+		/* Otherwise, we can only report back one error.  So if
+		 * this is something other than cancellation, dump it to
+		 * the terminal. */
+		} else if (!g_error_matches (
+			error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+			g_warning ("%s", error->message);
+	}
+
+	if (error != NULL)
+		g_error_free (error);
+
+	/* If there's still jobs running, let them finish. */
+	if (uri_context->attachment_list != NULL)
+		return;
+
+	/* Steal the result. */
+	simple = uri_context->simple;
+	uri_context->simple = NULL;
+
+	/* And the URI list. */
+	uris = uri_context->uris;
+	uri_context->uris = NULL;
+
+	/* And the error. */
+	error = uri_context->error;
+	uri_context->error = NULL;
+
+	if (error == NULL)
+		g_simple_async_result_set_op_res_gpointer (simple, uris, NULL);
+	else {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+
+	g_simple_async_result_complete (simple);
+
+	attachment_store_uri_context_free (uri_context);
+}
+
+void
+e_attachment_store_get_uris_async (EAttachmentStore *store,
+                                   GList *attachment_list,
+                                   GAsyncReadyCallback callback,
+                                   gpointer user_data)
+{
+	GFile *temp_directory;
+	UriContext *uri_context;
+	GList *iter, *trash = NULL;
+	gchar *template;
+	gchar *path;
+
+	g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+	g_return_if_fail (callback != NULL);
+
+	uri_context = attachment_store_uri_context_new (
+		store, attachment_list, callback, user_data);
+
+	/* Grab the copied attachment list. */
+	attachment_list = uri_context->attachment_list;
+
+	/* First scan the list for attachments with a GFile. */
+	for (iter = attachment_list; iter != NULL; iter = iter->next) {
+		EAttachment *attachment = iter->data;
+		GFile *file;
+		gchar *uri;
+
+		file = e_attachment_get_file (attachment);
+		if (file == NULL)
+			continue;
+
+		uri = g_file_get_uri (file);
+		uri_context->uris[uri_context->index++] = uri;
+
+		/* Mark the list node for deletion. */
+		trash = g_list_prepend (trash, iter);
+		g_object_unref (attachment);
+	}
+
+	/* Expunge the list. */
+	for (iter = trash; iter != NULL; iter = iter->next) {
+		GList *link = iter->data;
+		attachment_list = g_list_delete_link (attachment_list, link);
+	}
+	g_list_free (trash);
+
+	uri_context->attachment_list = attachment_list;
+
+	/* Any remaining attachments in the list should have MIME parts
+	 * only, so we need to save them all to a temporary directory.
+	 * We use a directory so the files can retain their basenames. */
+	template = g_strdup_printf (PACKAGE "-%s-XXXXXX", g_get_user_name ());
+	path = e_mkdtemp (template);
+	g_free (template);
+
+	if (path == NULL) {
+		GSimpleAsyncResult *simple;
+
+		/* Steal the result. */
+		simple = uri_context->simple;
+		uri_context->simple = NULL;
+
+		g_simple_async_result_set_error (
+			simple, G_FILE_ERROR,
+			g_file_error_from_errno (errno),
+			"%s", g_strerror (errno));
+
+		g_simple_async_result_complete_in_idle (simple);
+		attachment_store_uri_context_free (uri_context);
+		return;
+	}
+
+	temp_directory = g_file_new_for_path (path);
+
+	for (iter = attachment_list; iter != NULL; iter = iter->next)
+		e_attachment_save_async (
+			E_ATTACHMENT (iter->data),
+			temp_directory, (GAsyncReadyCallback)
+			attachment_store_get_uris_save_cb,
+			uri_context);
+
+	g_object_unref (temp_directory);
+}
+
+gchar **
+e_attachment_store_get_uris_finish (EAttachmentStore *store,
+                                    GAsyncResult *result,
+                                    GError **error)
+{
+	GSimpleAsyncResult *simple;
+	gchar **uris;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), NULL);
+	g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	uris = g_simple_async_result_get_op_res_gpointer (simple);
+	g_simple_async_result_propagate_error (simple, error);
+	g_object_unref (simple);
+
+	return uris;
+}
Index: widgets/misc/e-attachment-handler-image.c
===================================================================
--- widgets/misc/e-attachment-handler-image.c	(revision 0)
+++ widgets/misc/e-attachment-handler-image.c	(revision 0)
@@ -0,0 +1,280 @@
+/*
+ * 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 <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;
+	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);
+
+	action_group = e_attachment_view_add_action_group (view, "image");
+	gtk_action_group_add_actions (
+		action_group, standard_entries,
+		G_N_ELEMENTS (standard_entries), object);
+
+	ui_manager = e_attachment_view_get_ui_manager (view);
+	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;
+}
Index: widgets/misc/e-attachment-store.h
===================================================================
--- widgets/misc/e-attachment-store.h	(revision 0)
+++ widgets/misc/e-attachment-store.h	(revision 0)
@@ -0,0 +1,122 @@
+/*
+ * e-attachment-store.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_STORE_H
+#define E_ATTACHMENT_STORE_H
+
+#include <gtk/gtk.h>
+#include <widgets/misc/e-attachment.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_STORE \
+	(e_attachment_store_get_type ())
+#define E_ATTACHMENT_STORE(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_ATTACHMENT_STORE, EAttachmentStore))
+#define E_ATTACHMENT_STORE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_ATTACHMENT_STORE, EAttachmentStoreClass))
+#define E_IS_ATTACHMENT_STORE(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_ATTACHMENT_STORE))
+#define E_IS_ATTACHMENT_STORE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_ATTACHMENT_STORE))
+#define E_ATTACHMENT_STORE_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_ATTACHMENT_STORE, EAttachmentStoreClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentStore EAttachmentStore;
+typedef struct _EAttachmentStoreClass EAttachmentStoreClass;
+typedef struct _EAttachmentStorePrivate EAttachmentStorePrivate;
+
+struct _EAttachmentStore {
+	GtkListStore parent;
+	EAttachmentStorePrivate *priv;
+};
+
+struct _EAttachmentStoreClass {
+	GtkListStoreClass parent_class;
+};
+
+enum {
+	E_ATTACHMENT_STORE_COLUMN_ATTACHMENT,	/* E_TYPE_ATTACHMENT */
+	E_ATTACHMENT_STORE_COLUMN_CAPTION,	/* G_TYPE_STRING */
+	E_ATTACHMENT_STORE_COLUMN_CONTENT_TYPE, /* G_TYPE_STRING */
+	E_ATTACHMENT_STORE_COLUMN_DESCRIPTION,	/* G_TYPE_STRING */
+	E_ATTACHMENT_STORE_COLUMN_ICON,		/* G_TYPE_ICON */
+	E_ATTACHMENT_STORE_COLUMN_LOADING,	/* G_TYPE_BOOLEAN */
+	E_ATTACHMENT_STORE_COLUMN_PERCENT,	/* G_TYPE_INT */
+	E_ATTACHMENT_STORE_COLUMN_SAVING,	/* G_TYPE_BOOLEAN */
+	E_ATTACHMENT_STORE_COLUMN_SIZE,		/* G_TYPE_UINT64 */
+	E_ATTACHMENT_STORE_NUM_COLUMNS
+};
+
+GType		e_attachment_store_get_type	(void);
+GtkTreeModel *	e_attachment_store_new		(void);
+void		e_attachment_store_add_attachment
+						(EAttachmentStore *store,
+						 EAttachment *attachment);
+gboolean	e_attachment_store_remove_attachment
+						(EAttachmentStore *store,
+						 EAttachment *attachment);
+void		e_attachment_store_add_to_multipart
+						(EAttachmentStore *store,
+						 CamelMultipart *multipart,
+						 const gchar *default_charset);
+const gchar *	e_attachment_store_get_current_folder
+						(EAttachmentStore *store);
+void		e_attachment_store_set_current_folder
+						(EAttachmentStore *store,
+						 const gchar *current_folder);
+guint		e_attachment_store_get_num_attachments
+						(EAttachmentStore *store);
+guint		e_attachment_store_get_num_loading
+						(EAttachmentStore *store);
+goffset		e_attachment_store_get_total_size
+						(EAttachmentStore *store);
+gint		e_attachment_store_run_file_chooser_dialog
+						(EAttachmentStore *store,
+						 GtkWidget *dialog);
+void		e_attachment_store_run_load_dialog
+						(EAttachmentStore *store,
+						 GtkWindow *parent);
+GFile *		e_attachment_store_run_save_dialog
+						(EAttachmentStore *store,
+						 GList *attachment_list,
+						 GtkWindow *parent);
+
+/* Asynchronous Operations */
+void		e_attachment_store_get_uris_async
+						(EAttachmentStore *store,
+						 GList *attachment_list,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gchar **	e_attachment_store_get_uris_finish
+						(EAttachmentStore *store,
+						 GAsyncResult *result,
+						 GError **error);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_STORE_H */
Index: widgets/misc/e-attachment-handler-image.h
===================================================================
--- widgets/misc/e-attachment-handler-image.h	(revision 0)
+++ widgets/misc/e-attachment-handler-image.h	(revision 0)
@@ -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 */
Index: widgets/misc/e-attachment-paned.c
===================================================================
--- widgets/misc/e-attachment-paned.c	(revision 0)
+++ widgets/misc/e-attachment-paned.c	(revision 0)
@@ -0,0 +1,774 @@
+/*
+ * e-attachment-paned.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-paned.h"
+
+#include <glib/gi18n.h>
+
+#include "e-util/e-binding.h"
+#include "e-util/gconf-bridge.h"
+
+#include "e-attachment-view.h"
+#include "e-attachment-store.h"
+#include "e-attachment-icon-view.h"
+#include "e-attachment-tree-view.h"
+
+#define E_ATTACHMENT_PANED_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_ATTACHMENT_PANED, EAttachmentPanedPrivate))
+
+#define NUM_VIEWS 2
+
+struct _EAttachmentPanedPrivate {
+	GtkTreeModel *model;
+	GtkWidget *expander;
+	GtkWidget *notebook;
+	GtkWidget *combo_box;
+	GtkWidget *icon_view;
+	GtkWidget *tree_view;
+	GtkWidget *show_hide_label;
+	GtkWidget *status_icon;
+	GtkWidget *status_label;
+	GtkWidget *content_area;
+
+	gint active_view;
+	guint expanded : 1;
+};
+
+enum {
+	PROP_0,
+	PROP_ACTIVE_VIEW,
+	PROP_EDITABLE,
+	PROP_EXPANDED
+};
+
+static gpointer parent_class;
+
+static void
+attachment_paned_notify_cb (EAttachmentPaned *paned,
+                            GParamSpec *pspec,
+                            GtkExpander *expander)
+{
+	GtkLabel *label;
+	const gchar *text;
+
+	label = GTK_LABEL (paned->priv->show_hide_label);
+
+	/* Update the expander label. */
+	if (gtk_expander_get_expanded (expander))
+		text = _("Hide _Attachment Bar");
+	else
+		text = _("Show _Attachment Bar");
+
+	gtk_label_set_text_with_mnemonic (label, text);
+}
+
+static void
+attachment_paned_sync_icon_view (EAttachmentPaned *paned)
+{
+	EAttachmentView *source;
+	EAttachmentView *target;
+
+	source = E_ATTACHMENT_VIEW (paned->priv->tree_view);
+	target = E_ATTACHMENT_VIEW (paned->priv->icon_view);
+
+	/* Only sync if the tree view is active.  This prevents the
+	 * two views from endlessly trying to sync with each other. */
+	if (e_attachment_paned_get_active_view (paned) == 1)
+		e_attachment_view_sync_selection (source, target);
+}
+
+static void
+attachment_paned_sync_tree_view (EAttachmentPaned *paned)
+{
+	EAttachmentView *source;
+	EAttachmentView *target;
+
+	source = E_ATTACHMENT_VIEW (paned->priv->icon_view);
+	target = E_ATTACHMENT_VIEW (paned->priv->tree_view);
+
+	/* Only sync if the icon view is active.  This prevents the
+	 * two views from endlessly trying to sync with each other. */
+	if (e_attachment_paned_get_active_view (paned) == 0)
+		e_attachment_view_sync_selection (source, target);
+}
+
+static void
+attachment_paned_update_status (EAttachmentPaned *paned)
+{
+	EAttachmentView *view;
+	EAttachmentStore *store;
+	GtkExpander *expander;
+	GtkLabel *label;
+	guint num_attachments;
+	guint64 total_size;
+	gchar *display_size;
+	gchar *markup;
+
+	view = E_ATTACHMENT_VIEW (paned);
+	store = e_attachment_view_get_store (view);
+	expander = GTK_EXPANDER (paned->priv->expander);
+	label = GTK_LABEL (paned->priv->status_label);
+
+	num_attachments = e_attachment_store_get_num_attachments (store);
+	total_size = e_attachment_store_get_total_size (store);
+	display_size = g_format_size_for_display (total_size);
+
+	markup = g_strdup_printf (
+		"<b>%d</b> %s (%s)", num_attachments, ngettext (
+		"Attachment", "Attachments", num_attachments),
+		display_size);
+	gtk_label_set_markup (label, markup);
+	g_free (markup);
+
+	g_free (display_size);
+
+	if (num_attachments > 0) {
+		gtk_widget_show (paned->priv->status_icon);
+		gtk_widget_show (paned->priv->status_label);
+		gtk_expander_set_expanded (expander, TRUE);
+	} else {
+		gtk_widget_hide (paned->priv->status_icon);
+		gtk_widget_hide (paned->priv->status_label);
+		gtk_expander_set_expanded (expander, FALSE);
+	}
+}
+
+static void
+attachment_paned_set_property (GObject *object,
+                               guint property_id,
+                               const GValue *value,
+                               GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ACTIVE_VIEW:
+			e_attachment_paned_set_active_view (
+				E_ATTACHMENT_PANED (object),
+				g_value_get_int (value));
+			return;
+
+		case PROP_EDITABLE:
+			e_attachment_view_set_editable (
+				E_ATTACHMENT_VIEW (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_EXPANDED:
+			e_attachment_paned_set_expanded (
+				E_ATTACHMENT_PANED (object),
+				g_value_get_boolean (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_paned_get_property (GObject *object,
+                               guint property_id,
+                               GValue *value,
+                               GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ACTIVE_VIEW:
+			g_value_set_int (
+				value, e_attachment_paned_get_active_view (
+				E_ATTACHMENT_PANED (object)));
+			return;
+
+		case PROP_EDITABLE:
+			g_value_set_boolean (
+				value, e_attachment_view_get_editable (
+				E_ATTACHMENT_VIEW (object)));
+			return;
+
+		case PROP_EXPANDED:
+			g_value_set_boolean (
+				value, e_attachment_paned_get_expanded (
+				E_ATTACHMENT_PANED (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_paned_dispose (GObject *object)
+{
+	EAttachmentPanedPrivate *priv;
+
+	priv = E_ATTACHMENT_PANED_GET_PRIVATE (object);
+
+	if (priv->model != NULL) {
+		g_object_unref (priv->model);
+		priv->model = NULL;
+	}
+
+	if (priv->expander != NULL) {
+		g_object_unref (priv->expander);
+		priv->expander = NULL;
+	}
+
+	if (priv->notebook != NULL) {
+		g_object_unref (priv->notebook);
+		priv->notebook = NULL;
+	}
+
+	if (priv->combo_box != NULL) {
+		g_object_unref (priv->combo_box);
+		priv->combo_box = NULL;
+	}
+
+	if (priv->icon_view != NULL) {
+		g_object_unref (priv->icon_view);
+		priv->icon_view = NULL;
+	}
+
+	if (priv->tree_view != NULL) {
+		g_object_unref (priv->tree_view);
+		priv->tree_view = NULL;
+	}
+
+	if (priv->show_hide_label != NULL) {
+		g_object_unref (priv->show_hide_label);
+		priv->show_hide_label = NULL;
+	}
+
+	if (priv->status_icon != NULL) {
+		g_object_unref (priv->status_icon);
+		priv->status_icon = NULL;
+	}
+
+	if (priv->status_label != NULL) {
+		g_object_unref (priv->status_label);
+		priv->status_label = NULL;
+	}
+
+	if (priv->content_area != NULL) {
+		g_object_unref (priv->content_area);
+		priv->content_area = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+attachment_paned_constructed (GObject *object)
+{
+	EAttachmentPanedPrivate *priv;
+	GConfBridge *bridge;
+	const gchar *key;
+
+	priv = E_ATTACHMENT_PANED_GET_PRIVATE (object);
+
+	bridge = gconf_bridge_get ();
+
+	/* Set up property-to-property bindings. */
+
+	e_mutual_binding_new (
+		G_OBJECT (object), "active-view",
+		G_OBJECT (priv->combo_box), "active");
+
+	e_mutual_binding_new (
+		G_OBJECT (object), "active-view",
+		G_OBJECT (priv->notebook), "page");
+
+	e_mutual_binding_new (
+		G_OBJECT (object), "editable",
+		G_OBJECT (priv->icon_view), "editable");
+
+	e_mutual_binding_new (
+		G_OBJECT (object), "editable",
+		G_OBJECT (priv->tree_view), "editable");
+
+	e_mutual_binding_new (
+		G_OBJECT (object), "expanded",
+		G_OBJECT (priv->expander), "expanded");
+
+	e_mutual_binding_new (
+		G_OBJECT (object), "expanded",
+		G_OBJECT (priv->combo_box), "sensitive");
+
+	e_mutual_binding_new (
+		G_OBJECT (object), "expanded",
+		G_OBJECT (priv->notebook), "visible");
+
+	/* Set up property-to-GConf bindings. */
+
+	key = "/apps/evolution/shell/attachment_view";
+	gconf_bridge_bind_property (bridge, key, object, "active-view");
+}
+
+static EAttachmentViewPrivate *
+attachment_paned_get_private (EAttachmentView *view)
+{
+	EAttachmentPanedPrivate *priv;
+
+	priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	return e_attachment_view_get_private (view);
+}
+
+static EAttachmentStore *
+attachment_paned_get_store (EAttachmentView *view)
+{
+	EAttachmentPanedPrivate *priv;
+
+	priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	return e_attachment_view_get_store (view);
+}
+
+static GtkTreePath *
+attachment_paned_get_path_at_pos (EAttachmentView *view,
+                                  gint x,
+                                  gint y)
+{
+	EAttachmentPanedPrivate *priv;
+
+	priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	return e_attachment_view_get_path_at_pos (view, x, y);
+}
+
+static GList *
+attachment_paned_get_selected_paths (EAttachmentView *view)
+{
+	EAttachmentPanedPrivate *priv;
+
+	priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	return e_attachment_view_get_selected_paths (view);
+}
+
+static gboolean
+attachment_paned_path_is_selected (EAttachmentView *view,
+                                   GtkTreePath *path)
+{
+	EAttachmentPanedPrivate *priv;
+
+	priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	return e_attachment_view_path_is_selected (view, path);
+}
+
+static void
+attachment_paned_select_path (EAttachmentView *view,
+                              GtkTreePath *path)
+{
+	EAttachmentPanedPrivate *priv;
+
+	priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	e_attachment_view_select_path (view, path);
+}
+
+static void
+attachment_paned_unselect_path (EAttachmentView *view,
+                                GtkTreePath *path)
+{
+	EAttachmentPanedPrivate *priv;
+
+	priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	e_attachment_view_unselect_path (view, path);
+}
+
+static void
+attachment_paned_select_all (EAttachmentView *view)
+{
+	EAttachmentPanedPrivate *priv;
+
+	priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	e_attachment_view_select_all (view);
+}
+
+static void
+attachment_paned_unselect_all (EAttachmentView *view)
+{
+	EAttachmentPanedPrivate *priv;
+
+	priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	e_attachment_view_unselect_all (view);
+}
+
+static void
+attachment_paned_update_actions (EAttachmentView *view)
+{
+	EAttachmentPanedPrivate *priv;
+
+	priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+	view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+	e_attachment_view_update_actions (view);
+}
+
+static void
+attachment_paned_class_init (EAttachmentPanedClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EAttachmentPanedPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = attachment_paned_set_property;
+	object_class->get_property = attachment_paned_get_property;
+	object_class->dispose = attachment_paned_dispose;
+	object_class->constructed = attachment_paned_constructed;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ACTIVE_VIEW,
+		g_param_spec_int (
+			"active-view",
+			"Active View",
+			NULL,
+			0,
+			NUM_VIEWS,
+			0,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_EXPANDED,
+		g_param_spec_boolean (
+			"expanded",
+			"Expanded",
+			NULL,
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	g_object_class_override_property (
+		object_class, PROP_EDITABLE, "editable");
+}
+
+static void
+attachment_paned_iface_init (EAttachmentViewIface *iface)
+{
+	iface->get_private = attachment_paned_get_private;
+	iface->get_store = attachment_paned_get_store;
+	iface->get_path_at_pos = attachment_paned_get_path_at_pos;
+	iface->get_selected_paths = attachment_paned_get_selected_paths;
+	iface->path_is_selected = attachment_paned_path_is_selected;
+	iface->select_path = attachment_paned_select_path;
+	iface->unselect_path = attachment_paned_unselect_path;
+	iface->select_all = attachment_paned_select_all;
+	iface->unselect_all = attachment_paned_unselect_all;
+	iface->update_actions = attachment_paned_update_actions;
+}
+
+static void
+attachment_paned_init (EAttachmentPaned *paned)
+{
+	EAttachmentView *view;
+	GtkTreeSelection *selection;
+	GtkSizeGroup *size_group;
+	GtkWidget *container;
+	GtkWidget *widget;
+	GtkAction *action;
+
+	paned->priv = E_ATTACHMENT_PANED_GET_PRIVATE (paned);
+	paned->priv->model = e_attachment_store_new ();
+
+	/* Keep the expander label and combo box the same height. */
+	size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
+
+	/* Construct the Attachment Views */
+
+	container = GTK_WIDGET (paned);
+
+	widget = gtk_notebook_new ();
+	gtk_widget_set_size_request (widget, -1, 40);
+	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE);
+	gtk_notebook_set_show_border (GTK_NOTEBOOK (widget), FALSE);
+	gtk_paned_pack2 (GTK_PANED (container), widget, TRUE, FALSE);
+	paned->priv->notebook = g_object_ref (widget);
+	gtk_widget_hide (widget);
+
+	container = paned->priv->notebook;
+
+	widget = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (
+		GTK_SCROLLED_WINDOW (widget),
+		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (
+		GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+	gtk_notebook_append_page (GTK_NOTEBOOK (container), widget, NULL);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = e_attachment_icon_view_new ();
+	GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
+	gtk_icon_view_set_model (GTK_ICON_VIEW (widget), paned->priv->model);
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	paned->priv->icon_view = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	container = paned->priv->notebook;
+
+	widget = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (
+		GTK_SCROLLED_WINDOW (widget),
+		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (
+		GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+	gtk_notebook_append_page (GTK_NOTEBOOK (container), widget, NULL);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = e_attachment_tree_view_new ();
+	GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
+	gtk_tree_view_set_model (GTK_TREE_VIEW (widget), paned->priv->model);
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	paned->priv->tree_view = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	/* Construct the Controls */
+
+	container = GTK_WIDGET (paned);
+
+	widget = gtk_vbox_new (FALSE, 6);
+	gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, FALSE);
+	paned->priv->content_area = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_hbox_new (FALSE, 6);
+	gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_expander_new (NULL);
+	gtk_expander_set_spacing (GTK_EXPANDER (widget), 0);
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	paned->priv->expander = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	/* The "Add Attachment" button proxies the "add" action from
+	 * one of the two attachment views.  Doesn't matter which. */
+	widget = gtk_button_new ();
+	view = E_ATTACHMENT_VIEW (paned->priv->icon_view);
+	action = e_attachment_view_get_action (view, "add");
+	gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new ());
+	gtk_activatable_set_related_action (GTK_ACTIVATABLE (widget), action);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	widget = gtk_combo_box_new_text ();
+	gtk_size_group_add_widget (size_group, widget);
+	gtk_combo_box_append_text (GTK_COMBO_BOX (widget), _("Icon View"));
+	gtk_combo_box_append_text (GTK_COMBO_BOX (widget), _("List View"));
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	paned->priv->combo_box = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	container = paned->priv->expander;
+
+	/* Request the width to be as large as possible, and let the
+	 * GtkExpander allocate what space there is.  This effectively
+	 * packs the widget to expand. */
+	widget = gtk_hbox_new (FALSE, 6);
+	gtk_size_group_add_widget (size_group, widget);
+	gtk_widget_set_size_request (widget, G_MAXINT, -1);
+	gtk_expander_set_label_widget (GTK_EXPANDER (container), widget);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_label_new_with_mnemonic (_("Show _Attachment Bar"));
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	paned->priv->show_hide_label = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	widget = gtk_alignment_new (0.5, 0.5, 0.0, 1.0);
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_hbox_new (FALSE, 6);
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_image_new_from_icon_name (
+		"mail-attachment", GTK_ICON_SIZE_MENU);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	paned->priv->status_icon = g_object_ref (widget);
+	gtk_widget_hide (widget);
+
+	widget = gtk_label_new (NULL);
+	gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	paned->priv->status_label = g_object_ref (widget);
+	gtk_widget_hide (widget);
+
+	selection = gtk_tree_view_get_selection (
+		GTK_TREE_VIEW (paned->priv->tree_view));
+
+	g_signal_connect_swapped (
+		selection, "changed",
+		G_CALLBACK (attachment_paned_sync_icon_view), paned);
+
+	g_signal_connect_swapped (
+		paned->priv->icon_view, "selection-changed",
+		G_CALLBACK (attachment_paned_sync_tree_view), paned);
+
+	g_signal_connect_swapped (
+		paned->priv->expander, "notify::expanded",
+		G_CALLBACK (attachment_paned_notify_cb), paned);
+
+	g_signal_connect_swapped (
+		paned->priv->model, "notify::num-attachments",
+		G_CALLBACK (attachment_paned_update_status), paned);
+
+	g_signal_connect_swapped (
+		paned->priv->model, "notify::total-size",
+		G_CALLBACK (attachment_paned_update_status), paned);
+
+	g_object_unref (size_group);
+}
+
+GType
+e_attachment_paned_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EAttachmentPanedClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) attachment_paned_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EAttachmentPaned),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) attachment_paned_init,
+			NULL   /* value_table */
+		};
+
+		static const GInterfaceInfo iface_info = {
+			(GInterfaceInitFunc) attachment_paned_iface_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL   /* interface_data */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_VPANED, "EAttachmentPaned",
+			&type_info, 0);
+
+		g_type_add_interface_static (
+			type, E_TYPE_ATTACHMENT_VIEW, &iface_info);
+	}
+
+	return type;
+}
+
+GtkWidget *
+e_attachment_paned_new (void)
+{
+	return g_object_new (E_TYPE_ATTACHMENT_PANED, NULL);
+}
+
+GtkWidget *
+e_attachment_paned_get_content_area (EAttachmentPaned *paned)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT_PANED (paned), NULL);
+
+	return paned->priv->content_area;
+}
+
+gint
+e_attachment_paned_get_active_view (EAttachmentPaned *paned)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT_PANED (paned), 0);
+
+	return paned->priv->active_view;
+}
+
+void
+e_attachment_paned_set_active_view (EAttachmentPaned *paned,
+                                    gint active_view)
+{
+	g_return_if_fail (E_IS_ATTACHMENT_PANED (paned));
+	g_return_if_fail (active_view >= 0 && active_view < NUM_VIEWS);
+
+	paned->priv->active_view = active_view;
+
+	g_object_notify (G_OBJECT (paned), "active-view");
+}
+
+gboolean
+e_attachment_paned_get_expanded (EAttachmentPaned *paned)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT_PANED (paned), FALSE);
+
+	return paned->priv->expanded;
+}
+
+void
+e_attachment_paned_set_expanded (EAttachmentPaned *paned,
+                                 gboolean expanded)
+{
+	g_return_if_fail (E_IS_ATTACHMENT_PANED (paned));
+
+	paned->priv->expanded = expanded;
+
+	g_object_notify (G_OBJECT (paned), "expanded");
+}
+
+void
+e_attachment_paned_drag_data_received (EAttachmentPaned *paned,
+                                       GdkDragContext *context,
+                                       gint x,
+                                       gint y,
+                                       GtkSelectionData *selection,
+                                       guint info,
+                                       guint time)
+{
+	g_return_if_fail (E_IS_ATTACHMENT_PANED (paned));
+
+	/* XXX Dirty hack for forwarding drop events. */
+	g_signal_emit_by_name (
+		paned->priv->icon_view, "drag-data-received",
+		context, x, y, selection, info, time);
+}
Index: widgets/misc/e-attachment-paned.h
===================================================================
--- widgets/misc/e-attachment-paned.h	(revision 0)
+++ widgets/misc/e-attachment-paned.h	(revision 0)
@@ -0,0 +1,84 @@
+/*
+ * e-attachment-paned.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_PANED_H
+#define E_ATTACHMENT_PANED_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_PANED \
+	(e_attachment_paned_get_type ())
+#define E_ATTACHMENT_PANED(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_ATTACHMENT_PANED, EAttachmentPaned))
+#define E_ATTACHMENT_PANED_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_ATTACHMENT_PANED, EAttachmentPanedClass))
+#define E_IS_ATTACHMENT_PANED(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_ATTACHMENT_PANED))
+#define E_IS_ATTACHMENT_PANED_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_ATTACHMENT_PANED))
+#define E_ATTACHMENT_PANED_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_ATTACHMENT_PANED, EAttachmentPanedClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentPaned EAttachmentPaned;
+typedef struct _EAttachmentPanedClass EAttachmentPanedClass;
+typedef struct _EAttachmentPanedPrivate EAttachmentPanedPrivate;
+
+struct _EAttachmentPaned {
+	GtkVPaned parent;
+	EAttachmentPanedPrivate *priv;
+};
+
+struct _EAttachmentPanedClass {
+	GtkVPanedClass parent_class;
+};
+
+GType		e_attachment_paned_get_type	(void);
+GtkWidget *	e_attachment_paned_new		(void);
+GtkWidget *	e_attachment_paned_get_content_area
+						(EAttachmentPaned *paned);
+gint		e_attachment_paned_get_active_view
+						(EAttachmentPaned *paned);
+void		e_attachment_paned_set_active_view
+						(EAttachmentPaned *paned,
+						 gint active_view);
+gboolean	e_attachment_paned_get_expanded	(EAttachmentPaned *paned);
+void		e_attachment_paned_set_expanded	(EAttachmentPaned *paned,
+						 gboolean expanded);
+void		e_attachment_paned_drag_data_received
+						(EAttachmentPaned *paned,
+						 GdkDragContext *context,
+						 gint x,
+						 gint y,
+						 GtkSelectionData *selection,
+						 guint info,
+						 guint time);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_PANED_H */
Index: widgets/misc/e-attachment-icon-view.c
===================================================================
--- widgets/misc/e-attachment-icon-view.c	(revision 0)
+++ widgets/misc/e-attachment-icon-view.c	(revision 0)
@@ -0,0 +1,537 @@
+/*
+ * e-attachment-icon-view.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-icon-view.h"
+
+#include <glib/gi18n.h>
+
+#include "e-attachment.h"
+#include "e-attachment-store.h"
+#include "e-attachment-view.h"
+
+#define E_ATTACHMENT_ICON_VIEW_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_ATTACHMENT_ICON_VIEW, EAttachmentIconViewPrivate))
+
+struct _EAttachmentIconViewPrivate {
+	EAttachmentViewPrivate view_priv;
+};
+
+enum {
+	PROP_0,
+	PROP_EDITABLE
+};
+
+static gpointer parent_class;
+
+static void
+attachment_icon_view_set_property (GObject *object,
+                                   guint property_id,
+                                   const GValue *value,
+                                   GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_EDITABLE:
+			e_attachment_view_set_editable (
+				E_ATTACHMENT_VIEW (object),
+				g_value_get_boolean (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_icon_view_get_property (GObject *object,
+                                   guint property_id,
+                                   GValue *value,
+                                   GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_EDITABLE:
+			g_value_set_boolean (
+				value, e_attachment_view_get_editable (
+				E_ATTACHMENT_VIEW (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_icon_view_dispose (GObject *object)
+{
+	e_attachment_view_dispose (E_ATTACHMENT_VIEW (object));
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+attachment_icon_view_finalize (GObject *object)
+{
+	e_attachment_view_finalize (E_ATTACHMENT_VIEW (object));
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+attachment_icon_view_button_press_event (GtkWidget *widget,
+                                         GdkEventButton *event)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	if (e_attachment_view_button_press_event (view, event))
+		return TRUE;
+
+	/* Chain up to parent's button_press_event() method. */
+	return GTK_WIDGET_CLASS (parent_class)->
+		button_press_event (widget, event);
+}
+
+static gboolean
+attachment_icon_view_button_release_event (GtkWidget *widget,
+                                           GdkEventButton *event)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	if (e_attachment_view_button_release_event (view, event))
+		return TRUE;
+
+	/* Chain up to parent's button_release_event() method. */
+	return GTK_WIDGET_CLASS (parent_class)->
+		button_release_event (widget, event);
+}
+
+static gboolean
+attachment_icon_view_key_press_event (GtkWidget *widget,
+                                      GdkEventKey *event)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	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)->
+		key_press_event (widget, event);
+}
+
+static void
+attachment_icon_view_drag_begin (GtkWidget *widget,
+                                 GdkDragContext *context)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	/* Chain up to parent's drag_begin() method. */
+	GTK_WIDGET_CLASS (parent_class)->drag_begin (widget, context);
+
+	e_attachment_view_drag_begin (view, context);
+}
+
+static void
+attachment_icon_view_drag_end (GtkWidget *widget,
+                               GdkDragContext *context)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	/* Chain up to parent's drag_end() method. */
+	GTK_WIDGET_CLASS (parent_class)->drag_end (widget, context);
+
+	e_attachment_view_drag_end (view, context);
+}
+
+static void
+attachment_icon_view_drag_data_get (GtkWidget *widget,
+                                    GdkDragContext *context,
+                                    GtkSelectionData *selection,
+                                    guint info,
+                                    guint time)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	e_attachment_view_drag_data_get (
+		view, context, selection, info, time);
+}
+
+static gboolean
+attachment_icon_view_drag_motion (GtkWidget *widget,
+                                  GdkDragContext *context,
+                                  gint x,
+                                  gint y,
+                                  guint time)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	return e_attachment_view_drag_motion (view, context, x, y, time);
+}
+
+static gboolean
+attachment_icon_view_drag_drop (GtkWidget *widget,
+                                GdkDragContext *context,
+                                gint x,
+                                gint y,
+                                guint time)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	if (!e_attachment_view_drag_drop (view, context, x, y, time))
+		return FALSE;
+
+	/* Chain up to parent's drag_drop() method. */
+	return GTK_WIDGET_CLASS (parent_class)->drag_drop (
+		widget, context, x, y, time);
+}
+
+static void
+attachment_icon_view_drag_data_received (GtkWidget *widget,
+                                         GdkDragContext *context,
+                                         gint x,
+                                         gint y,
+                                         GtkSelectionData *selection,
+                                         guint info,
+                                         guint time)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	e_attachment_view_drag_data_received (
+		view, context, x, y, selection, info, time);
+}
+
+static gboolean
+attachment_icon_view_popup_menu (GtkWidget *widget)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+	e_attachment_view_show_popup_menu (view, NULL, NULL, NULL);
+
+	return TRUE;
+}
+
+static void
+attachment_icon_view_item_activated (GtkIconView *icon_view,
+                                     GtkTreePath *path)
+{
+	EAttachmentView *view = E_ATTACHMENT_VIEW (icon_view);
+
+	e_attachment_view_open_path (view, path, NULL);
+}
+
+static EAttachmentViewPrivate *
+attachment_icon_view_get_private (EAttachmentView *view)
+{
+	EAttachmentIconViewPrivate *priv;
+
+	priv = E_ATTACHMENT_ICON_VIEW_GET_PRIVATE (view);
+
+	return &priv->view_priv;
+}
+
+static EAttachmentStore *
+attachment_icon_view_get_store (EAttachmentView *view)
+{
+	GtkIconView *icon_view;
+	GtkTreeModel *model;
+
+	icon_view = GTK_ICON_VIEW (view);
+	model = gtk_icon_view_get_model (icon_view);
+
+	return E_ATTACHMENT_STORE (model);
+}
+
+static GtkTreePath *
+attachment_icon_view_get_path_at_pos (EAttachmentView *view,
+                                      gint x,
+                                      gint y)
+{
+	GtkIconView *icon_view;
+
+	icon_view = GTK_ICON_VIEW (view);
+
+	return gtk_icon_view_get_path_at_pos (icon_view, x, y);
+}
+
+static GList *
+attachment_icon_view_get_selected_paths (EAttachmentView *view)
+{
+	GtkIconView *icon_view;
+
+	icon_view = GTK_ICON_VIEW (view);
+
+	return gtk_icon_view_get_selected_items (icon_view);
+}
+
+static gboolean
+attachment_icon_view_path_is_selected (EAttachmentView *view,
+                                       GtkTreePath *path)
+{
+	GtkIconView *icon_view;
+
+	icon_view = GTK_ICON_VIEW (view);
+
+	return gtk_icon_view_path_is_selected (icon_view, path);
+}
+
+static void
+attachment_icon_view_select_path (EAttachmentView *view,
+                                  GtkTreePath *path)
+{
+	GtkIconView *icon_view;
+
+	icon_view = GTK_ICON_VIEW (view);
+
+	gtk_icon_view_select_path (icon_view, path);
+}
+
+static void
+attachment_icon_view_unselect_path (EAttachmentView *view,
+                                    GtkTreePath *path)
+{
+	GtkIconView *icon_view;
+
+	icon_view = GTK_ICON_VIEW (view);
+
+	gtk_icon_view_unselect_path (icon_view, path);
+}
+
+static void
+attachment_icon_view_select_all (EAttachmentView *view)
+{
+	GtkIconView *icon_view;
+
+	icon_view = GTK_ICON_VIEW (view);
+
+	gtk_icon_view_select_all (icon_view);
+}
+
+static void
+attachment_icon_view_unselect_all (EAttachmentView *view)
+{
+	GtkIconView *icon_view;
+
+	icon_view = GTK_ICON_VIEW (view);
+
+	gtk_icon_view_unselect_all (icon_view);
+}
+
+static void
+attachment_icon_view_drag_source_set (EAttachmentView *view,
+                                      GdkModifierType start_button_mask,
+                                      const GtkTargetEntry *targets,
+                                      gint n_targets,
+                                      GdkDragAction actions)
+{
+	GtkIconView *icon_view;
+
+	icon_view = GTK_ICON_VIEW (view);
+
+	gtk_icon_view_enable_model_drag_source (
+		icon_view, start_button_mask, targets, n_targets, actions);
+}
+
+static void
+attachment_icon_view_drag_dest_set (EAttachmentView *view,
+                                    const GtkTargetEntry *targets,
+                                    gint n_targets,
+                                    GdkDragAction actions)
+{
+	GtkIconView *icon_view;
+
+	icon_view = GTK_ICON_VIEW (view);
+
+	gtk_icon_view_enable_model_drag_dest (
+		icon_view, targets, n_targets, actions);
+}
+
+static void
+attachment_icon_view_drag_source_unset (EAttachmentView *view)
+{
+	GtkIconView *icon_view;
+
+	icon_view = GTK_ICON_VIEW (view);
+
+	gtk_icon_view_unset_model_drag_source (icon_view);
+}
+
+static void
+attachment_icon_view_drag_dest_unset (EAttachmentView *view)
+{
+	GtkIconView *icon_view;
+
+	icon_view = GTK_ICON_VIEW (view);
+
+	gtk_icon_view_unset_model_drag_dest (icon_view);
+}
+
+static void
+attachment_icon_view_class_init (EAttachmentIconViewClass *class)
+{
+	GObjectClass *object_class;
+	GtkWidgetClass *widget_class;
+	GtkIconViewClass *icon_view_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EAttachmentViewPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = attachment_icon_view_set_property;
+	object_class->get_property = attachment_icon_view_get_property;
+	object_class->dispose = attachment_icon_view_dispose;
+	object_class->finalize = attachment_icon_view_finalize;
+
+	widget_class = GTK_WIDGET_CLASS (class);
+	widget_class->button_press_event = attachment_icon_view_button_press_event;
+	widget_class->button_release_event = attachment_icon_view_button_release_event;
+	widget_class->key_press_event = attachment_icon_view_key_press_event;
+	widget_class->drag_begin = attachment_icon_view_drag_begin;
+	widget_class->drag_end = attachment_icon_view_drag_end;
+	widget_class->drag_data_get = attachment_icon_view_drag_data_get;
+	widget_class->drag_motion = attachment_icon_view_drag_motion;
+	widget_class->drag_drop = attachment_icon_view_drag_drop;
+	widget_class->drag_data_received = attachment_icon_view_drag_data_received;
+	widget_class->popup_menu = attachment_icon_view_popup_menu;
+
+	icon_view_class = GTK_ICON_VIEW_CLASS (class);
+	icon_view_class->item_activated = attachment_icon_view_item_activated;
+
+	g_object_class_override_property (
+		object_class, PROP_EDITABLE, "editable");
+}
+
+static void
+attachment_icon_view_iface_init (EAttachmentViewIface *iface)
+{
+	iface->get_private = attachment_icon_view_get_private;
+	iface->get_store = attachment_icon_view_get_store;
+
+	iface->get_path_at_pos = attachment_icon_view_get_path_at_pos;
+	iface->get_selected_paths = attachment_icon_view_get_selected_paths;
+	iface->path_is_selected = attachment_icon_view_path_is_selected;
+	iface->select_path = attachment_icon_view_select_path;
+	iface->unselect_path = attachment_icon_view_unselect_path;
+	iface->select_all = attachment_icon_view_select_all;
+	iface->unselect_all = attachment_icon_view_unselect_all;
+
+	iface->drag_source_set = attachment_icon_view_drag_source_set;
+	iface->drag_dest_set = attachment_icon_view_drag_dest_set;
+	iface->drag_source_unset = attachment_icon_view_drag_source_unset;
+	iface->drag_dest_unset = attachment_icon_view_drag_dest_unset;
+}
+
+static void
+attachment_icon_view_init (EAttachmentIconView *icon_view)
+{
+	GtkCellLayout *cell_layout;
+	GtkCellRenderer *renderer;
+	cell_layout = GTK_CELL_LAYOUT (icon_view);
+	icon_view->priv = E_ATTACHMENT_ICON_VIEW_GET_PRIVATE (icon_view);
+
+	e_attachment_view_init (E_ATTACHMENT_VIEW (icon_view));
+
+	gtk_icon_view_set_selection_mode (
+		GTK_ICON_VIEW (icon_view), GTK_SELECTION_MULTIPLE);
+
+	renderer = gtk_cell_renderer_pixbuf_new ();
+	g_object_set (renderer, "stock-size", GTK_ICON_SIZE_DIALOG, NULL);
+	gtk_cell_layout_pack_start (cell_layout, renderer, FALSE);
+
+	gtk_cell_layout_add_attribute (
+		cell_layout, renderer, "gicon",
+		E_ATTACHMENT_STORE_COLUMN_ICON);
+
+	renderer = gtk_cell_renderer_text_new ();
+	g_object_set (
+		renderer, "alignment", PANGO_ALIGN_CENTER,
+		"wrap-mode", PANGO_WRAP_WORD, "wrap-width", 150,
+		"yalign", 0.0, NULL);
+	gtk_cell_layout_pack_start (cell_layout, renderer, FALSE);
+
+	gtk_cell_layout_add_attribute (
+		cell_layout, renderer, "text",
+		E_ATTACHMENT_STORE_COLUMN_CAPTION);
+
+	renderer = gtk_cell_renderer_progress_new ();
+	g_object_set (renderer, "text", _("Loading"), NULL);
+	gtk_cell_layout_pack_start (cell_layout, renderer, TRUE);
+
+	gtk_cell_layout_add_attribute (
+		cell_layout, renderer, "value",
+		E_ATTACHMENT_STORE_COLUMN_PERCENT);
+
+	gtk_cell_layout_add_attribute (
+		cell_layout, renderer, "visible",
+		E_ATTACHMENT_STORE_COLUMN_LOADING);
+
+	renderer = gtk_cell_renderer_progress_new ();
+	g_object_set (renderer, "text", _("Saving"), NULL);
+	gtk_cell_layout_pack_start (cell_layout, renderer, TRUE);
+
+	gtk_cell_layout_add_attribute (
+		cell_layout, renderer, "value",
+		E_ATTACHMENT_STORE_COLUMN_PERCENT);
+
+	gtk_cell_layout_add_attribute (
+		cell_layout, renderer, "visible",
+		E_ATTACHMENT_STORE_COLUMN_SAVING);
+}
+
+GType
+e_attachment_icon_view_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EAttachmentIconViewClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) attachment_icon_view_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EAttachmentIconView),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) attachment_icon_view_init,
+			NULL   /* value_table */
+		};
+
+		static const GInterfaceInfo iface_info = {
+			(GInterfaceInitFunc) attachment_icon_view_iface_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL   /* interface_data */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_ICON_VIEW, "EAttachmentIconView",
+			&type_info, 0);
+
+		g_type_add_interface_static (
+			type, E_TYPE_ATTACHMENT_VIEW, &iface_info);
+	}
+
+	return type;
+}
+
+GtkWidget *
+e_attachment_icon_view_new (void)
+{
+	return g_object_new (E_TYPE_ATTACHMENT_ICON_VIEW, NULL);
+}
Index: widgets/misc/e-attachment-bar.c
===================================================================
--- widgets/misc/e-attachment-bar.c	(revision 37527)
+++ widgets/misc/e-attachment-bar.c	(working copy)
@@ -1,1504 +0,0 @@
-/*
- * 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/>  
- *
- *
- * Authors:
- *		Ettore Perazzoli <ettore@ximian.com>
- *      Jeffrey Stedfast <fejj@ximian.com>
- *	    Srinivasa Ragavan <sragavan@novell.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <gtk/gtk.h>
-#include <gio/gio.h>
-#include <glade/glade.h>
-#include <gconf/gconf.h>
-#include <gconf/gconf-client.h>
-#include <gdk/gdkkeysyms.h>
-#include <glib/gi18n.h>
-#include <glib/gstdio.h>
-#include <libgnome/libgnome.h>
-
-#ifdef HAVE_LIBGNOMEUI_GNOME_THUMBNAIL_H
-#include <libgnomeui/gnome-thumbnail.h>
-#endif
-
-#include "e-attachment.h"
-#include "e-attachment-bar.h"
-
-#include <libedataserver/e-data-server-util.h>
-
-#include <camel/camel-data-wrapper.h>
-#include <camel/camel-iconv.h>
-#include <camel/camel-mime-message.h>
-#include <camel/camel-stream-fs.h>
-#include <camel/camel-stream-null.h>
-#include <camel/camel-stream-mem.h>
-#include <camel/camel-stream-filter.h>
-#include <camel/camel-mime-filter-bestenc.h>
-#include <camel/camel-mime-part.h>
-
-#include "e-util/e-util.h"
-#include "e-util/e-gui-utils.h"
-#include "e-util/e-icon-factory.h"
-#include "e-util/e-error.h"
-#include "e-util/e-mktemp.h"
-
-#define ICON_WIDTH 64
-#define ICON_SEPARATORS " /-_"
-#define ICON_SPACING 2
-#define ICON_ROW_SPACING ICON_SPACING
-#define ICON_COL_SPACING ICON_SPACING
-#define ICON_BORDER 2
-#define ICON_TEXT_SPACING 2
-
-
-static GnomeIconListClass *parent_class = NULL;
-
-struct _EAttachmentBarPrivate {
-	GtkWidget *attach;	/* attachment file dialogue, if active */
-
-	/* Recent documents. Use this widget directly when bonoboui is obsoleted */
-	GtkWidget *recent;
-
-	gboolean batch_unref;
-	GPtrArray *attachments;
-	char *path;
-};
-
-
-enum {
-	CHANGED,
-	LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-
-static void update (EAttachmentBar *bar);
-
-/* Attachment handling functions.  */
-
-static void
-attachment_destroy (EAttachmentBar *bar, EAttachment *attachment)
-{
-	if (bar->priv->batch_unref)
-		return;
-
-	if (g_ptr_array_remove (bar->priv->attachments, attachment)) {
-		update (bar);
-		g_signal_emit (bar, signals[CHANGED], 0);
-	}
-}
-
-static void
-attachment_changed_cb (EAttachment *attachment,
-		       gpointer data)
-{
-	update (E_ATTACHMENT_BAR (data));
-}
-
-static void
-add_common (EAttachmentBar *bar, EAttachment *attachment)
-{
-	g_return_if_fail (attachment != NULL);
-
-	g_ptr_array_add (bar->priv->attachments, attachment);
-	g_object_weak_ref ((GObject *) attachment, (GWeakNotify) attachment_destroy, bar);
-	g_signal_connect (attachment, "changed", G_CALLBACK (attachment_changed_cb), bar);
-
-	update (bar);
-
-	g_signal_emit (bar, signals[CHANGED], 0);
-}
-
-static void
-add_from_mime_part (EAttachmentBar *bar, CamelMimePart *part)
-{
-	add_common (bar, e_attachment_new_from_mime_part (part));
-}
-
-static void
-add_from_file (EAttachmentBar *bar, const char *file_name, const char *disposition)
-{
-	EAttachment *attachment;
-	CamelException ex;
-
-	camel_exception_init (&ex);
-
-	if ((attachment = e_attachment_new (file_name, disposition, &ex))) {
-		add_common (bar, attachment);
-	} else {
-		/* FIXME: Avoid using error from mailer */
-		e_error_run ((GtkWindow *) gtk_widget_get_toplevel ((GtkWidget *) bar), "mail-composer:no-attach",
-			     file_name, camel_exception_get_description (&ex), NULL);
-		camel_exception_clear (&ex);
-	}
-}
-
-/* get_system_thumbnail:
- * If filled store_uri, then creating thumbnail for it, otherwise, if is_available_local,
- * and attachment is not an application, then save to temp and create a thumbnail for the body.
- * Otherwise returns NULL (or if something goes wrong/library not available).
- */
-static GdkPixbuf *
-get_system_thumbnail (EAttachment *attachment, CamelContentType *content_type)
-{
-	GdkPixbuf *pixbuf = NULL;
-#ifdef HAVE_LIBGNOMEUI_GNOME_THUMBNAIL_H
-	struct stat file_stat;
-	char *file_uri = NULL;
-	gboolean is_tmp = FALSE;
-
-	if (!attachment || !attachment->is_available_local)
-		return NULL;
-
-	if (attachment->store_uri && g_str_has_prefix (attachment->store_uri, "file://"))
-		file_uri = attachment->store_uri;
-	else if (attachment->body) {
-		/* save part to the temp directory */
-		char *tmp_file;
-
-		is_tmp = TRUE;
-
-		tmp_file = e_mktemp ("tmp-XXXXXX");
-		if (tmp_file) {
-			CamelStream *stream;
-			char *mfilename = NULL;
-			const char * filename;
-
-			filename = camel_mime_part_get_filename (attachment->body);
-			if (filename == NULL)
-				filename = "unknown";
-			else {
-				char *utf8_mfilename;
-
-				utf8_mfilename = g_strdup (filename);
-				e_filename_make_safe (utf8_mfilename);
-				mfilename = g_filename_from_utf8 ((const char *) utf8_mfilename, -1, NULL, NULL, NULL);
-				g_free (utf8_mfilename);
-
-				filename = (const char *) mfilename;
-			}
-
-			file_uri = g_strjoin (NULL, "file://", tmp_file, "-", filename, NULL);
-
-			stream = camel_stream_fs_new_with_name (file_uri + 7, O_WRONLY | O_CREAT | O_TRUNC, 0644);
-			if (stream) {
-				CamelDataWrapper *content;
-
-				content = camel_medium_get_content_object (CAMEL_MEDIUM (attachment->body));
-
-				if (camel_data_wrapper_decode_to_stream (content, stream) == -1
-				    || camel_stream_flush (stream) == -1) {
-					g_free (file_uri);
-					file_uri = NULL;
-				}
-
-				camel_object_unref (stream);
-			} else {
-				g_free (file_uri);
-				file_uri = NULL;
-			}
-
-			g_free (mfilename);
-			g_free (tmp_file);
-		}
-	}
-
-	if (!file_uri || !g_str_has_prefix (file_uri, "file://")) {
-		if (is_tmp)
-			g_free (file_uri);
-
-		return NULL;
-	}
-
-	if (stat (file_uri + 7, &file_stat) != -1 && S_ISREG (file_stat.st_mode)) {
-		GnomeThumbnailFactory *th_factory;
-		char *th_file;
-
-		th_factory = gnome_thumbnail_factory_new (GNOME_THUMBNAIL_SIZE_NORMAL);
-		th_file = gnome_thumbnail_factory_lookup (th_factory, file_uri, file_stat.st_mtime);
-
-		if (th_file) {
-			pixbuf = gdk_pixbuf_new_from_file (th_file, NULL);
-			g_free (th_file);
-		} else if (content_type) {
-			char *mime = camel_content_type_simple (content_type);
-
-			if (gnome_thumbnail_factory_can_thumbnail (th_factory, file_uri, mime, file_stat.st_mtime)) {
-				pixbuf = gnome_thumbnail_factory_generate_thumbnail (th_factory, file_uri, mime);
-				
-				if (pixbuf && !is_tmp)
-					gnome_thumbnail_factory_save_thumbnail (th_factory, pixbuf, file_uri, file_stat.st_mtime);
-			}
-
-			g_free (mime);
-		}
-
-		g_object_unref (th_factory);
-	}
-
-	if (is_tmp) {
-		/* clear the temp */
-		g_remove (file_uri + 7);
-		g_free (file_uri);
-	}
-#endif
-
-	return pixbuf;
-}
-
-static GdkPixbuf *
-scale_pixbuf (GdkPixbuf *pixbuf)
-{
-	int ratio, width, height;
-
-	if (!pixbuf)
-		return NULL;
-
-	width = gdk_pixbuf_get_width (pixbuf);
-	height = gdk_pixbuf_get_height (pixbuf);
-	if (width >= height) {
-		if (width > 48) {
-			ratio = width / 48;
-			width = 48;
-			height = height / ratio;
-			if (height == 0)
-				height = 1;
-		}
-	} else {
-		if (height > 48) {
-			ratio = height / 48;
-			height = 48;
-			width = width / ratio;
-			if (width == 0)
-				width = 1;
-		}
-	}
-
-	return e_icon_factory_pixbuf_scale (pixbuf, width, height);
-}
-
-/* Icon list contents handling.  */
-
-static void
-calculate_height_width(EAttachmentBar *bar, int *new_width, int *new_height)
-{
-        int width, height, icon_width;
-        PangoFontMetrics *metrics;
-        PangoContext *context;
-
-        context = gtk_widget_get_pango_context ((GtkWidget *) bar);
-        metrics = pango_context_get_metrics (context, ((GtkWidget *) bar)->style->font_desc, pango_context_get_language (context));
-        width = PANGO_PIXELS (pango_font_metrics_get_approximate_char_width (metrics)) * 15;
-	/* This should be *2, but the icon list creates too much space above ... */
-	height = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) + pango_font_metrics_get_descent (metrics)) * 3;
-	pango_font_metrics_unref (metrics);
-	icon_width = ICON_WIDTH + ICON_SPACING + ICON_BORDER + ICON_TEXT_SPACING;
-
-	if (new_width)
-		*new_width = MAX (icon_width, width);
-
-	if (new_height)
-		*new_height = ICON_WIDTH + ICON_SPACING + ICON_BORDER + ICON_TEXT_SPACING + height;
-
-	return;
-}
-
-void
-e_attachment_bar_create_attachment_cache (EAttachment *attachment)
-{
-
-	CamelContentType *content_type;
-
-	if (!attachment->body)
-		return;
-
-	content_type = camel_mime_part_get_content_type (attachment->body);
-
-	if (camel_content_type_is(content_type, "image", "*")) {
-		CamelDataWrapper *wrapper;
-		CamelStreamMem *mstream;
-		GdkPixbufLoader *loader;
-		gboolean error = TRUE;
-		GdkPixbuf *pixbuf;
-
-		wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (attachment->body));
-		mstream = (CamelStreamMem *) camel_stream_mem_new ();
-
-		camel_data_wrapper_decode_to_stream (wrapper, (CamelStream *) mstream);
-
-		/* Stream image into pixbuf loader */
-		loader = gdk_pixbuf_loader_new ();
-		error = !gdk_pixbuf_loader_write (loader, mstream->buffer->data, mstream->buffer->len, NULL);
-		gdk_pixbuf_loader_close (loader, NULL);
-
-		if (!error) {
-			pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
-			attachment->pixbuf_cache = scale_pixbuf (pixbuf);
-			pixbuf = attachment->pixbuf_cache;
-			g_object_ref(pixbuf);
-		} else {
-			attachment->pixbuf_cache = NULL;
-			g_warning ("GdkPixbufLoader Error");
-		}
-
-		/* Destroy everything */
-		g_object_unref (loader);
-		camel_object_unref (mstream);
-	}
-}
-
-static void
-update (EAttachmentBar *bar)
-{
-	struct _EAttachmentBarPrivate *priv;
-	GnomeIconList *icon_list;
-	int bar_width, bar_height;
-	int i;
-
-	priv = bar->priv;
-	icon_list = GNOME_ICON_LIST (bar);
-
-	gnome_icon_list_freeze (icon_list);
-
-	gnome_icon_list_clear (icon_list);
-
-	/* FIXME could be faster, but we don't care.  */
-	for (i = 0; i < priv->attachments->len; i++) {
-		EAttachment *attachment;
-		CamelContentType *content_type;
-		char *size_string, *label;
-		GdkPixbuf *pixbuf = NULL;
-		char *desc;
-
-		attachment = priv->attachments->pdata[i];
-
-		if (!attachment->is_available_local || !attachment->body) {
-			if ((pixbuf = e_icon_factory_get_icon("mail-attachment", E_ICON_SIZE_DIALOG))) {
-				attachment->index = gnome_icon_list_append_pixbuf (icon_list, pixbuf, NULL, "");
-				g_object_unref (pixbuf);
-			}
-			continue;
-		}
-
-		content_type = camel_mime_part_get_content_type (attachment->body);
-		/* Get the image out of the attachment
-		   and create a thumbnail for it */
-		if ((pixbuf = attachment->pixbuf_cache)) {
-			g_object_ref(pixbuf);
-		} else if (camel_content_type_is(content_type, "image", "*")) {
-			CamelDataWrapper *wrapper;
-			CamelStreamMem *mstream;
-			GdkPixbufLoader *loader;
-			gboolean error = TRUE;
-
-			wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (attachment->body));
-			mstream = (CamelStreamMem *) camel_stream_mem_new ();
-
-			camel_data_wrapper_decode_to_stream (wrapper, (CamelStream *) mstream);
-
-			/* Stream image into pixbuf loader */
-			loader = gdk_pixbuf_loader_new ();
-			error = !gdk_pixbuf_loader_write (loader, mstream->buffer->data, mstream->buffer->len, NULL);
-			gdk_pixbuf_loader_close (loader, NULL);
-
-			if (!error) {
-				pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
-				attachment->pixbuf_cache = scale_pixbuf (pixbuf);
-				pixbuf = attachment->pixbuf_cache;
-				g_object_ref (pixbuf);
-			} else {
-				pixbuf = NULL;
-				g_warning ("GdkPixbufLoader Error");
-			}
-
-			/* Destroy everything */
-			g_object_unref (loader);
-			camel_object_unref (mstream);
-		} else if (!bar->expand && (pixbuf = get_system_thumbnail (attachment, content_type))) {
-			attachment->pixbuf_cache = scale_pixbuf (pixbuf);
-			pixbuf = attachment->pixbuf_cache;
-			g_object_ref (pixbuf);
-		}
-
-		desc = camel_mime_part_get_description (attachment->body);
-    
-		if (!desc || *desc == '\0') {
-			if (attachment->file_name) {
-				desc = g_filename_to_utf8 (attachment->file_name, -1, NULL, NULL, NULL);
-			} else {
-				desc = camel_mime_part_get_filename (attachment->body);
-				if (desc)
-					desc = g_strdup (desc);
-			}
-		} else {
-			desc = g_strdup (desc);
-		}
-
-		if (!desc)
-			desc = g_strdup (_("attachment"));
-
-		if (attachment->size && (size_string = g_format_size_for_display (attachment->size))) {
-			label = g_strdup_printf ("%s (%s)", desc, size_string);
-			g_free (desc);
-			g_free (size_string);
-		} else {
-			label = g_strdup (desc);
-			g_free (desc);
-		}
-
-		if (pixbuf == NULL) {
-			char *mime_type;
-
-			mime_type = camel_content_type_simple (content_type);
-			pixbuf = e_icon_for_mime_type (mime_type, 48);
-			if (pixbuf == NULL) {
-				g_warning("cannot find icon for mime type %s (installation problem?)", mime_type);
-				pixbuf = e_icon_factory_get_icon("mail-attachment", E_ICON_SIZE_DIALOG);
-			}
-			g_free (mime_type);
-
-			/* remember this picture and use it later again */
-			if (pixbuf)
-				attachment->pixbuf_cache = g_object_ref (pixbuf);
-		}
-
-		if (pixbuf) {
-			GdkPixbuf *pixbuf_orig = pixbuf;
-			pixbuf = gdk_pixbuf_add_alpha (pixbuf_orig, TRUE, 255, 255, 255);
-
-			/* gdk_pixbuf_add_alpha returns a newly allocated pixbuf,
-			   free the original one.
-			*/
-			g_object_unref (pixbuf_orig);
-
-			/* In case of a attachment bar, in a signed/encrypted part, display the status as a emblem*/
-			if (attachment->sign) {
-				/* Show the signature status at the right-bottom.*/
-				GdkPixbuf *sign = NULL;
-				int x, y;
-
-				if (attachment->sign == CAMEL_CIPHER_VALIDITY_SIGN_BAD)
-					sign = e_icon_factory_get_icon ("stock_signature-bad", E_ICON_SIZE_MENU);
-				else if (attachment->sign == CAMEL_CIPHER_VALIDITY_SIGN_GOOD)
-					sign = e_icon_factory_get_icon ("stock_signature-ok", E_ICON_SIZE_MENU);
-				else
-					sign = e_icon_factory_get_icon ("stock_signature", E_ICON_SIZE_MENU);
-
-				x = gdk_pixbuf_get_width (pixbuf) - 17;
-				y = gdk_pixbuf_get_height (pixbuf) - 17;
-
-				gdk_pixbuf_copy_area (sign, 0, 0, 16, 16, pixbuf, x, y);
-				g_object_unref (sign);
-			}
-
-			if (attachment->encrypt) {
-				/* Show the encryption status at the top left.*/
-				GdkPixbuf *encrypt = e_icon_factory_get_icon ("stock_lock-ok", E_ICON_SIZE_MENU);
-
-				gdk_pixbuf_copy_area (encrypt, 0, 0, 16, 16, pixbuf, 1, 1);
-				g_object_unref (encrypt);
-			}
-
-			gnome_icon_list_append_pixbuf (icon_list, pixbuf, NULL, label);
-			g_object_unref (pixbuf);
-		}
-
-		g_free (label);
-	}
-
-	gnome_icon_list_thaw (icon_list);
-
-	/* Resize */
-	if (bar->expand) {
-		gtk_widget_get_size_request ((GtkWidget *) bar, &bar_width, &bar_height);
-
-		if (bar->priv->attachments->len) {
-			int per_col, rows, height, width;
-
-			calculate_height_width(bar, &width, &height);
-			per_col = bar_width / width;
-			per_col = (per_col ? per_col : 1);
-			rows = (bar->priv->attachments->len + per_col -1) / per_col;
-			gtk_widget_set_size_request ((GtkWidget *) bar, bar_width, rows * height);
-		}
-	}
-}
-
-static void
-update_remote_file (EAttachment *attachment, EAttachmentBar *bar)
-{
-	GnomeIconList *icon_list;
-	GnomeIconTextItem *item;
-	char *msg, *base;
-
-	if (attachment->percentage == -1) {
-		update (bar);
-		return;
-	}
-
-	base = g_path_get_basename(attachment->file_name);
-	msg = g_strdup_printf("%s (%d%%)", base, attachment->percentage);
-	g_free(base);
-
-	icon_list = GNOME_ICON_LIST (bar);
-
-	gnome_icon_list_freeze (icon_list);
-
-	item = gnome_icon_list_get_icon_text_item (icon_list, attachment->index);
-	if (!item->is_text_allocated)
-		g_free (item->text);
-
-	gnome_icon_text_item_configure (item, item->x, item->y, item->width, item->fontname, msg, item->is_editable, TRUE);
-
-	gnome_icon_list_thaw (icon_list);
-}
-
-void
-e_attachment_bar_remove_selected (EAttachmentBar *bar)
-{
-	struct _EAttachmentBarPrivate *priv;
-	EAttachment *attachment;
-	int id, left, nrem = 0;
-	GList *items;
-	GPtrArray *temp_arr;
-
-	g_return_if_fail (E_IS_ATTACHMENT_BAR (bar));
-
-	priv = bar->priv;
-
-	if (!(items = gnome_icon_list_get_selection ((GnomeIconList *) bar)))
-		return;
-
-	temp_arr = g_ptr_array_new ();
-	while (items != NULL) {
-		if ((id = GPOINTER_TO_INT (items->data) - nrem) < priv->attachments->len) {
-			attachment = E_ATTACHMENT(g_ptr_array_index (priv->attachments, id));
-			g_ptr_array_add (temp_arr, (gpointer)attachment);
-			g_ptr_array_remove_index (priv->attachments, id);
-			nrem++;
-		}
-
-		items = items->next;
-	}
-
-	g_ptr_array_foreach (temp_arr, (GFunc)g_object_unref, NULL);
-	g_ptr_array_free (temp_arr, TRUE);
-
-	update (bar);
-
-	g_signal_emit (bar, signals[CHANGED], 0);
-
-	id++;
-
-	if ((left = gnome_icon_list_get_num_icons ((GnomeIconList *) bar)) > 0)
-		gnome_icon_list_focus_icon ((GnomeIconList *) bar, left > id ? id : left - 1);
-}
-
-void
-e_attachment_bar_set_width(EAttachmentBar *bar, int bar_width)
-{
-	int per_col, rows, height, width;
-
-	calculate_height_width(bar, &width, &height);
-	per_col = bar_width / width;
-	per_col = (per_col ? per_col : 1);
-	rows = (bar->priv->attachments->len + per_col - 1) / per_col;
-	gtk_widget_set_size_request ((GtkWidget *)bar, bar_width, rows * height);
-}
-
-void
-e_attachment_bar_edit_selected (EAttachmentBar *bar)
-{
-	struct _EAttachmentBarPrivate *priv;
-	EAttachment *attachment;
-	GList *items;
-	int id;
-
-	g_return_if_fail (E_IS_ATTACHMENT_BAR (bar));
-
-	priv = bar->priv;
-
-	items = gnome_icon_list_get_selection ((GnomeIconList *) bar);
-	while (items != NULL) {
-		if ((id = GPOINTER_TO_INT (items->data)) < priv->attachments->len) {
-			attachment = priv->attachments->pdata[id];
-			e_attachment_edit (attachment, GTK_WIDGET (bar));
-		}
-
-		items = items->next;
-	}
-}
-
-GtkWidget **
-e_attachment_bar_get_selector(EAttachmentBar *bar)
-{
-	g_return_val_if_fail (E_IS_ATTACHMENT_BAR (bar), NULL);
-
-	return &bar->priv->attach;
-}
-
-/**
- * e_attachment_bar_get_selected:
- * @bar: an #EAttachmentBar object
- *
- * Returns a newly allocated #GSList of ref'd #EAttachment objects
- * representing the selected items in the #EAttachmentBar Icon List.
- **/
-GSList *
-e_attachment_bar_get_selected (EAttachmentBar *bar)
-{
-	struct _EAttachmentBarPrivate *priv;
-	GSList *attachments = NULL;
-	EAttachment *attachment;
-	GList *items;
-	int id;
-
-	g_return_val_if_fail (E_IS_ATTACHMENT_BAR (bar), NULL);
-
-	priv = bar->priv;
-
-	items = gnome_icon_list_get_selection ((GnomeIconList *) bar);
-
-	while (items != NULL) {
-		if ((id = GPOINTER_TO_INT (items->data)) < priv->attachments->len) {
-			attachment = priv->attachments->pdata[id];
-			attachments = g_slist_prepend (attachments, attachment);
-			g_object_ref (attachment);
-		}
-
-		items = items->next;
-	}
-
-	attachments = g_slist_reverse (attachments);
-
-	return attachments;
-}
-
-/* FIXME: Cleanup this, since there is a api to get selected attachments */
-/**
- * e_attachment_bar_get_attachment:
- * @bar: an #EAttachmentBar object
- * @id: Index of the desired attachment or -1 to request all selected attachments
- *
- * Returns a newly allocated #GSList of ref'd #EAttachment objects
- * representing the requested item(s) in the #EAttachmentBar Icon
- * List.
- **/
-GSList *
-e_attachment_bar_get_attachment (EAttachmentBar *bar, int id)
-{
-	struct _EAttachmentBarPrivate *priv;
-	EAttachment *attachment;
-	GSList *attachments;
-
-	g_return_val_if_fail (E_IS_ATTACHMENT_BAR (bar), NULL);
-
-	priv = bar->priv;
-
-	if (id == -1 || id > priv->attachments->len)
-		return e_attachment_bar_get_selected (bar);
-
-	attachment = priv->attachments->pdata[id];
-	attachments = g_slist_prepend (NULL, attachment);
-	g_object_ref (attachment);
-
-	return attachments;
-}
-
-
-/**
- * e_attachment_bar_get_all_attachments:
- * @bar: an #EAttachmentBar object
- *
- * Returns a newly allocated #GSList of ref'd #EAttachment objects.
- **/
-GSList *
-e_attachment_bar_get_all_attachments (EAttachmentBar *bar)
-{
-	struct _EAttachmentBarPrivate *priv;
-	GSList *attachments = NULL;
-	EAttachment *attachment;
-	int i;
-
-	g_return_val_if_fail (E_IS_ATTACHMENT_BAR (bar), NULL);
-
-	priv = bar->priv;
-
-	for (i = priv->attachments->len - 1; i >= 0; i--) {
-		attachment = priv->attachments->pdata[i];
-		if (attachment->is_available_local) {
-			attachments = g_slist_prepend (attachments, attachment);
-			g_object_ref (attachment);
-		}
-	}
-
-	return attachments;
-}
-
-/* Just the GSList has to be freed by the caller */
-GSList *
-e_attachment_bar_get_parts (EAttachmentBar *bar)
-{
-	struct _EAttachmentBarPrivate *priv;
-	EAttachment *attachment;
-	GSList *parts = NULL;
-	int i;
-
-	g_return_val_if_fail (E_IS_ATTACHMENT_BAR (bar), NULL);
-
-	priv = bar->priv;
-
-	for (i = 0; i < priv->attachments->len; i++) {
-		attachment = priv->attachments->pdata[i];
-		if (attachment->is_available_local)
-			parts = g_slist_prepend (parts, attachment->body);
-	}
-
-        return parts;
-}
-
-/* GtkObject methods.  */
-
-static void
-destroy (GtkObject *object)
-{
-	EAttachmentBar *bar = (EAttachmentBar *) object;
-	struct _EAttachmentBarPrivate *priv = bar->priv;
-	EAttachment *attachment;
-	int i;
-
-	if ((priv = bar->priv)) {
-		priv->batch_unref = TRUE;
-		for (i = 0; i < priv->attachments->len; i++) {
-			attachment = priv->attachments->pdata[i];
-			g_object_weak_unref ((GObject *) attachment, (GWeakNotify) attachment_destroy, bar);
-			g_object_unref (attachment);
-		}
-		g_ptr_array_free (priv->attachments, TRUE);
-
-		if (priv->attach)
-			gtk_widget_destroy (priv->attach);
-
-		if (priv->recent)
-			gtk_widget_destroy (priv->recent);
-
-		if (priv->path)
-			g_free (priv->path);
-
-		g_free (priv);
-		bar->priv = NULL;
-	}
-
-	if (GTK_OBJECT_CLASS (parent_class)->destroy != NULL)
-		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
-}
-
-static char *
-temp_save_part (CamelMimePart *part, gboolean readonly)
-{
-	const char *filename;
-	char *tmpdir, *path, *mfilename = NULL, *utf8_mfilename = NULL;
-	CamelStream *stream;
-	CamelDataWrapper *wrapper;
-
-	if (!(tmpdir = e_mkdtemp ("evolution-tmp-XXXXXX")))
-		return NULL;
-
-	if (!(filename = camel_mime_part_get_filename (part))) {
-		/* This is the default filename used for temporary file creation */
-		filename = _("Unknown");
-	} else {
-		utf8_mfilename = g_strdup (filename);
-		e_filename_make_safe (utf8_mfilename);
-		mfilename = g_filename_from_utf8 ((const char *) utf8_mfilename, -1, NULL, NULL, NULL);
-		g_free (utf8_mfilename);
-		filename = (const char *) mfilename;
-	}
-
-	path = g_build_filename (tmpdir, filename, NULL);
-	g_free (tmpdir);
-	g_free (mfilename);
-
-	wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part));
-	if (readonly)
-		stream = camel_stream_fs_new_with_name (path, O_RDWR|O_CREAT|O_TRUNC, 0444);
-	else
-		stream = camel_stream_fs_new_with_name (path, O_RDWR|O_CREAT|O_TRUNC, 0644);
-
-	if (!stream) {
-		/* TODO handle error conditions */
-		g_message ("DEBUG: could not open the file to write\n");
-		g_free (path);
-		return NULL;
-	}
-
-	if (camel_data_wrapper_decode_to_stream (wrapper, (CamelStream *) stream) == -1) {
-		g_free (path);
-		camel_stream_close (stream);
-		camel_object_unref (stream);
-		g_message ("DEBUG: could not write to file\n");
-		return NULL;
-	}
-
-	camel_stream_close(stream);
-	camel_object_unref(stream);
-
-	return path;
-}
-
-static void
-eab_drag_data_get(EAttachmentBar *bar, GdkDragContext *drag, GtkSelectionData *data, guint info, guint time)
-{
-	struct _EAttachmentBarPrivate *priv = bar->priv;
-	EAttachment *attachment;
-	char *path, **uris;
-	int len, n, i = 0;
-	CamelURL *url;
-	GList *items;
-
-	if (info)
-		return;
-
-	items = gnome_icon_list_get_selection (GNOME_ICON_LIST (bar));
-	len = g_list_length (items);
-
-	uris = g_malloc0 (sizeof (char *) * (len + 1));
-
-	for ( ; items != NULL; items = items->next) {
-		if (!((n = GPOINTER_TO_INT (items->data)) < priv->attachments->len))
-			continue;
-
-		attachment = priv->attachments->pdata[n];
-
-		if (!attachment->is_available_local)
-			continue;
-
-		if (attachment->store_uri) {
-			uris[i++] = attachment->store_uri;
-			continue;
-		}
-
-		/* If we are not able to save, ignore it */
-		if (!(path = temp_save_part (attachment->body, FALSE)))
-			continue;
-
-		url = camel_url_new ("file://", NULL);
-		camel_url_set_path (url, path);
-		attachment->store_uri = camel_url_to_string (url, 0);
-		camel_url_free (url);
-		g_free (path);
-
-		uris[i++] = attachment->store_uri;
-	}
-
-	uris[i] = NULL;
-
-	gtk_selection_data_set_uris (data, uris);
-
-	g_free (uris);
-
-	return;
-}
-
-static gboolean
-eab_button_release_event(EAttachmentBar *bar, GdkEventButton *event, gpointer dummy)
-{
-	GnomeIconList *icon_list = GNOME_ICON_LIST(bar);
-	GList *selected;
-	int length;
-	GtkTargetEntry drag_types[] = {
-		{ "text/uri-list", 0, 0 },
-	};
-
-	if (event && event->button == 1) {
-		selected = gnome_icon_list_get_selection(icon_list);
-		length = g_list_length (selected);
-		if (length)
-			gtk_drag_source_set((GtkWidget *)bar, GDK_BUTTON1_MASK, drag_types, G_N_ELEMENTS(drag_types), GDK_ACTION_COPY);
-		else
-			gtk_drag_source_unset((GtkWidget *)bar);
-	}
-
-	return FALSE;
-}
-
-static gboolean
-eab_button_press_event(EAttachmentBar *bar, GdkEventButton *event, gpointer dummy)
-{
-	GnomeIconList *icon_list = GNOME_ICON_LIST(bar);
-	GList *selected = NULL, *tmp;
-	int length, icon_number;
-	gboolean take_selected = FALSE;
-	GtkTargetEntry drag_types[] = {
-		{ "text/uri-list", 0, 0 },
-	};
-
-	selected = gnome_icon_list_get_selection(icon_list);
-	length = g_list_length (selected);
-
-	if (event) {
-		icon_number = gnome_icon_list_get_icon_at(icon_list, event->x, event->y);
-		if (icon_number < 0) {
-			/* When nothing is selected, deselect all */
-			gnome_icon_list_unselect_all (icon_list);
-			length = 0;
-			selected = NULL;
-		}
-
-		if (event->button == 1) {
-			/* If something is selected, then allow drag or else help to select */
-			if (length)
-				gtk_drag_source_set((GtkWidget *)bar, GDK_BUTTON1_MASK, drag_types, G_N_ELEMENTS(drag_types), GDK_ACTION_COPY);
-			else
-				gtk_drag_source_unset((GtkWidget *)bar);
-			return FALSE;
-		}
-
-		/* If not r-click dont progress any more.*/
-		if (event->button != 3)
-			return FALSE;
-
-		/* When a r-click on something, if it is in the already selected list, consider a r-click of multiple things
-		 * or deselect all and select only this for r-click
-		 */
-		if (icon_number >= 0) {
-			for (tmp = selected; tmp; tmp = tmp->next) {
-				if (GPOINTER_TO_INT(tmp->data) == icon_number)
-					take_selected = TRUE;
-			}
-
-			if (!take_selected) {
-				gnome_icon_list_unselect_all(icon_list);
-				gnome_icon_list_select_icon(icon_list, icon_number);
-			}
-		}
-	}
-
-	return FALSE;
-}
-
-static gboolean
-eab_icon_clicked_cb (EAttachmentBar *bar, GdkEvent *event, gpointer *dummy)
-{
-	EAttachment *attachment;
-	gboolean ret = FALSE;
-	CamelURL *url;
-	char *path;
-	GSList *p;
-
-	if (E_IS_ATTACHMENT_BAR (bar) && event->type == GDK_2BUTTON_PRESS) {
-		p = e_attachment_bar_get_selected (bar);
-		/* check if has body already, remote files can take longer to fetch */
-		if (p && p->next == NULL && ((EAttachment *)p->data)->body) {
-			attachment = p->data;
-
-			/* Check if the file is stored already */
-			if (!attachment->store_uri) {
-				path = temp_save_part (attachment->body, TRUE);
-				url = camel_url_new ("file://", NULL);
-				camel_url_set_path (url, path);
-				attachment->store_uri = camel_url_to_string (url, 0);
-				camel_url_free (url);
-				g_free (path);
-			}
-
-			/* FIXME Pass a parent window. */
-			e_show_uri (NULL, attachment->store_uri);
-
-			ret = TRUE;
-		}
-
-		if (p) {
-			g_slist_foreach (p, (GFunc) g_object_unref, NULL);
-			g_slist_free (p);
-		}
-	}
-
-	return ret;
-}
-
-/* Initialization.  */
-
-static void
-class_init (EAttachmentBarClass *klass)
-{
-	GtkObjectClass *object_class;
-
-	object_class = GTK_OBJECT_CLASS (klass);
-
-	parent_class = g_type_class_ref (gnome_icon_list_get_type ());
-
-	object_class->destroy = destroy;
-
-	/* Setup signals.  */
-
-	signals[CHANGED] =
-		g_signal_new ("changed",
-			      E_TYPE_ATTACHMENT_BAR,
-			      G_SIGNAL_RUN_LAST,
-			      G_STRUCT_OFFSET (EAttachmentBarClass, changed),
-			      NULL, NULL,
-			      g_cclosure_marshal_VOID__VOID,
-			      G_TYPE_NONE, 0);
-}
-
-static void
-init (EAttachmentBar *bar)
-{
-	struct _EAttachmentBarPrivate *priv;
-
-	priv = g_new (struct _EAttachmentBarPrivate, 1);
-
-	priv->attach = NULL;
-	priv->batch_unref = FALSE;
-	priv->attachments = g_ptr_array_new ();
-
-	priv->recent = gtk_recent_chooser_menu_new ();
-	gtk_recent_chooser_menu_set_show_numbers (GTK_RECENT_CHOOSER_MENU (priv->recent), TRUE);
-	gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (priv->recent), GTK_RECENT_SORT_MRU);
-	gtk_recent_chooser_set_show_not_found (GTK_RECENT_CHOOSER (priv->recent), FALSE);
-	gtk_recent_chooser_set_show_private (GTK_RECENT_CHOOSER (priv->recent), FALSE);
-	gtk_recent_chooser_set_show_icons (GTK_RECENT_CHOOSER (priv->recent), TRUE);
-	gtk_recent_chooser_set_show_tips (GTK_RECENT_CHOOSER (priv->recent), TRUE);
-
-	priv->path = NULL;
-
-	bar->priv = priv;
-	bar->expand = FALSE;
-}
-
-
-GType
-e_attachment_bar_get_type (void)
-{
-	static GType type = 0;
-
-	if (type == 0) {
-		static const GTypeInfo info = {
-			sizeof (EAttachmentBarClass),
-			NULL, NULL,
-			(GClassInitFunc) class_init,
-			NULL, NULL,
-			sizeof (EAttachmentBar),
-			0,
-			(GInstanceInitFunc) init,
-		};
-
-		type = g_type_register_static (GNOME_TYPE_ICON_LIST, "EAttachmentBar", &info, 0);
-	}
-
-	return type;
-}
-
-GtkWidget *
-e_attachment_bar_new (GtkAdjustment *adj)
-{
-	EAttachmentBar *new;
-	GnomeIconList *icon_list;
-	int icon_width, window_height;
-
-	new = g_object_new (e_attachment_bar_get_type (), NULL);
-
-	icon_list = GNOME_ICON_LIST (new);
-
-	calculate_height_width (new, &icon_width, &window_height);
-
-	gnome_icon_list_construct (icon_list, icon_width, adj, 0);
-
-	gtk_widget_set_size_request (GTK_WIDGET (new), icon_width * 4, window_height);
-
-        GTK_WIDGET_SET_FLAGS (new, GTK_CAN_FOCUS);
-
-	gnome_icon_list_set_separators (icon_list, ICON_SEPARATORS);
-	gnome_icon_list_set_row_spacing (icon_list, ICON_ROW_SPACING);
-	gnome_icon_list_set_col_spacing (icon_list, ICON_COL_SPACING);
-	gnome_icon_list_set_icon_border (icon_list, ICON_BORDER);
-	gnome_icon_list_set_text_spacing (icon_list, ICON_TEXT_SPACING);
-	gnome_icon_list_set_selection_mode (icon_list, GTK_SELECTION_MULTIPLE);
-
-	atk_object_set_name (gtk_widget_get_accessible (GTK_WIDGET (new)),
-			     _("Attachment Bar"));
-
-	g_signal_connect (new, "button_release_event", G_CALLBACK(eab_button_release_event), NULL);
-	g_signal_connect (new, "button_press_event", G_CALLBACK(eab_button_press_event), NULL);
-	g_signal_connect (new, "drag-data-get", G_CALLBACK(eab_drag_data_get), NULL);
-	g_signal_connect (icon_list, "event", G_CALLBACK (eab_icon_clicked_cb), NULL);
-
-	return GTK_WIDGET (new);
-}
-
-static char *
-get_default_charset (void)
-{
-	GConfClient *gconf;
-	const char *locale;
-	char *charset;
-
-	gconf = gconf_client_get_default ();
-	charset = gconf_client_get_string (gconf, "/apps/evolution/mail/composer/charset", NULL);
-
-	if (!charset || charset[0] == '\0') {
-		g_free (charset);
-		charset = gconf_client_get_string (gconf, "/apps/evolution/mail/format/charset", NULL);
-		if (charset && charset[0] == '\0') {
-			g_free (charset);
-			charset = NULL;
-		}
-	}
-
-	g_object_unref (gconf);
-
-	if (!charset && (locale = camel_iconv_locale_charset ()))
-		charset = g_strdup (locale);
-
-	return charset ? charset : g_strdup ("us-ascii");
-}
-
-static void
-attach_to_multipart (CamelMultipart *multipart,
-		     EAttachment *attachment,
-		     const char *default_charset)
-{
-	CamelContentType *content_type;
-	CamelDataWrapper *content;
-
-	if (!attachment->body)
-		return;
-
-	content_type = camel_mime_part_get_content_type (attachment->body);
-	content = camel_medium_get_content_object (CAMEL_MEDIUM (attachment->body));
-
-	if (!CAMEL_IS_MULTIPART (content)) {
-		if (camel_content_type_is (content_type, "text", "*")) {
-			CamelTransferEncoding encoding;
-			CamelStreamFilter *filter_stream;
-			CamelMimeFilterBestenc *bestenc;
-			CamelStream *stream;
-			const char *charset;
-			char *buf = NULL;
-			char *type;
-
-			charset = camel_content_type_param (content_type, "charset");
-
-			stream = camel_stream_null_new ();
-			filter_stream = camel_stream_filter_new_with_stream (stream);
-			bestenc = camel_mime_filter_bestenc_new (CAMEL_BESTENC_GET_ENCODING);
-			camel_stream_filter_add (filter_stream, CAMEL_MIME_FILTER (bestenc));
-			camel_object_unref (stream);
-
-			camel_data_wrapper_decode_to_stream (content, CAMEL_STREAM (filter_stream));
-			camel_object_unref (filter_stream);
-
-			encoding = camel_mime_filter_bestenc_get_best_encoding (bestenc, CAMEL_BESTENC_8BIT);
-			camel_mime_part_set_encoding (attachment->body, encoding);
-
-			if (encoding == CAMEL_TRANSFER_ENCODING_7BIT) {
-				/* the text fits within us-ascii so this is safe */
-				/* FIXME: check that this isn't iso-2022-jp? */
-				default_charset = "us-ascii";
-			} else if (!charset) {
-				if (!default_charset)
-					default_charset = buf = get_default_charset ();
-
-				/* FIXME: We should really check that this fits within the
-                                   default_charset and if not find one that does and/or
-				   allow the user to specify? */
-			}
-
-			if (!charset) {
-				/* looks kinda nasty, but this is how ya have to do it */
-				camel_content_type_set_param (content_type, "charset", default_charset);
-				type = camel_content_type_format (content_type);
-				camel_mime_part_set_content_type (attachment->body, type);
-				g_free (type);
-				g_free (buf);
-			}
-
-			camel_object_unref (bestenc);
-		} else if (!CAMEL_IS_MIME_MESSAGE (content)) {
-			camel_mime_part_set_encoding (attachment->body, CAMEL_TRANSFER_ENCODING_BASE64);
-		}
-	}
-
-	camel_multipart_add_part (multipart, attachment->body);
-}
-
-void
-e_attachment_bar_to_multipart (EAttachmentBar *bar, CamelMultipart *multipart, const char *default_charset)
-{
-	struct _EAttachmentBarPrivate *priv;
-	EAttachment *attachment;
-	int i;
-
-	g_return_if_fail (E_IS_ATTACHMENT_BAR (bar));
-	g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
-
-	priv = bar->priv;
-
-	for (i = 0; i < priv->attachments->len; i++) {
-		attachment = priv->attachments->pdata[i];
-		if (attachment->is_available_local)
-			attach_to_multipart (multipart, attachment, default_charset);
-	}
-}
-
-guint
-e_attachment_bar_get_num_attachments (EAttachmentBar *bar)
-{
-	g_return_val_if_fail (E_IS_ATTACHMENT_BAR (bar), 0);
-
-	return bar->priv->attachments->len;
-}
-
-void
-e_attachment_bar_attach (EAttachmentBar *bar, const char *file_name, const char *disposition)
-{
-	g_return_if_fail (E_IS_ATTACHMENT_BAR (bar));
-	g_return_if_fail (file_name != NULL && disposition != NULL);
-
-	add_from_file (bar, file_name, disposition);
-}
-
-void
-e_attachment_bar_add_attachment (EAttachmentBar *bar, EAttachment *attachment)
-{
-	g_return_if_fail (E_IS_ATTACHMENT_BAR (bar));
-
-	add_common (bar, attachment);
-}
-
-void
-e_attachment_bar_add_attachment_silent (EAttachmentBar *bar, EAttachment *attachment)
-{
-	g_return_if_fail (E_IS_ATTACHMENT_BAR (bar));
-	g_return_if_fail (attachment != NULL);
-
-	g_ptr_array_add (bar->priv->attachments, attachment);
-	g_object_weak_ref ((GObject *) attachment, (GWeakNotify) attachment_destroy, bar);
-	g_signal_connect (attachment, "changed", G_CALLBACK (attachment_changed_cb), bar);
-
-
-	g_signal_emit (bar, signals[CHANGED], 0);
-}
-
-void
-e_attachment_bar_refresh (EAttachmentBar *bar)
-{
-	update (bar);
-
-}
-
-int
-e_attachment_bar_get_download_count (EAttachmentBar *bar)
-{
-	struct _EAttachmentBarPrivate *priv;
-	EAttachment *attachment;
-	int i, n = 0;
-
-	g_return_val_if_fail (E_IS_ATTACHMENT_BAR (bar), 0);
-
-	priv = bar->priv;
-
-	for (i = 0; i < priv->attachments->len; i++) {
-		attachment = priv->attachments->pdata[i];
-		if (!attachment->is_available_local)
-			n++;
-	}
-
-	return n;
-}
-
-void
-e_attachment_bar_attach_remote_file (EAttachmentBar *bar, const char *url, const char *disposition)
-{
-	EAttachment *attachment;
-	CamelException ex;
-	GtkWindow *parent;
-
-	g_return_if_fail (E_IS_ATTACHMENT_BAR (bar));
-
-	if (!bar->priv->path)
-		bar->priv->path = e_mkdtemp ("attach-XXXXXX");
-
-	parent = (GtkWindow *) gtk_widget_get_toplevel ((GtkWidget *) bar);
-	camel_exception_init (&ex);
-	if ((attachment = e_attachment_new_remote_file (parent, url, disposition, bar->priv->path, &ex))) {
-		add_common (bar, attachment);
-		g_signal_connect (attachment, "update", G_CALLBACK (update_remote_file), bar);
-	} else {
-		e_error_run (parent, "mail-composer:no-attach",
-			     url, camel_exception_get_description (&ex), NULL);
-		camel_exception_clear (&ex);
-	}
-}
-
-void
-e_attachment_bar_attach_mime_part (EAttachmentBar *bar, CamelMimePart *part)
-{
-	g_return_if_fail (E_IS_ATTACHMENT_BAR (bar));
-
-	add_from_mime_part (bar, part);
-}
-
-/* FIXME: Remove this API if nobody uses it */
-void 
-e_attachment_bar_bonobo_ui_populate_with_recent (BonoboUIComponent *uic, const char *path,
-						 EAttachmentBar *bar, 
-						 BonoboUIVerbFn verb_cb, gpointer user_data)
-{
-	struct _EAttachmentBarPrivate *priv;
-	GList *items, *l;
-	gint limit, i;
-	GString *menuitems;
-	char *encoded_label, *label;
-
-	g_return_if_fail (E_IS_ATTACHMENT_BAR (bar));
-
-	priv = bar->priv;
-	limit = gtk_recent_chooser_get_limit (GTK_RECENT_CHOOSER (priv->recent));
-	items = gtk_recent_chooser_get_items (GTK_RECENT_CHOOSER (priv->recent));
-
-	menuitems = g_string_new ("<submenu");
-	g_string_append (menuitems, " name=\"RecentDocsSubmenu\"");
-	g_string_append_printf (menuitems, " sensitive=\"%s\"", items ? "1" : "0");
-	g_string_append_printf (menuitems, " label=\"%s\"", _("Recent _Documents"));
-	g_string_append (menuitems, ">\n");
-
-	for (l = g_list_first (items), i = 1; l && i <= limit; l = l->next, ++i) {
-		GtkRecentInfo *info = ((GtkRecentInfo *)(l->data));
-		const gchar *info_dn = gtk_recent_info_get_display_name (info);
-		char *display_name, *u;
-
-		/* escape _'s in the display name so that it doesn't become an underline in a GtkLabel */
-		if ((u = strchr (info_dn, '_'))) {
-			int extra = 1;
-			char *d;
-			const char *s;
-
-			while ((u = strchr (u + 1, '_')))
-				extra++;
-
-			d = display_name = g_alloca (strlen (info_dn) + extra + 1);
-			s = info_dn;
-			while (*s != '\0') {
-				if (*s == '_')
-					*d++ = '_';
-				*d++ = *s++;
-			}
-			*d = '\0';
-		} else
-			display_name = (char *) info_dn;
-
-		/* Add menu item */
-		label = g_strdup (display_name);
-		encoded_label = bonobo_ui_util_encode_str (label);
-		g_string_append_printf (menuitems, 
-					"  <menuitem name=\"Recent-%d\" verb=\"\" label=\"%s\"/>\n",
-					i, encoded_label);
-		g_free (encoded_label);
-		g_free (label);
-	}
-
-	g_string_append (menuitems, "</submenu>\n");
-
-	bonobo_ui_component_set (uic, path, menuitems->str, NULL);
-
-	g_string_free (menuitems, TRUE);
-
-	/* Add uri prop */
-	for (l = g_list_first (items), i = 1; l && i <= limit; l = l->next, ++i) {
-		GtkRecentInfo *info = ((GtkRecentInfo *)(l->data));
-		const gchar *info_uri = gtk_recent_info_get_uri (info);
-		label = g_strdup_printf ("/commands/Recent-%d", i);
-		bonobo_ui_component_set_prop (uic, label, "uri", info_uri, NULL);
-		g_free (label);
-	}
-
-	/* Add verb */
-	for (l = g_list_first (items), i = 1; l && i <= limit; l = l->next, ++i) {
-		label = g_strdup_printf ("Recent-%d", i);
-		bonobo_ui_component_add_verb (uic, label, verb_cb, user_data);
-		g_free (label);
-	}
-
-	for (l = g_list_first (items); l; l = l->next)
-		gtk_recent_info_unref ((GtkRecentInfo *)(l->data));
-	g_list_free (items);
-}
-
-static void
-action_recent_cb (GtkAction *action, 
-		  EAttachmentBar *attachment_bar)
-{
-	GtkRecentChooser *chooser;
-	GFile *file;
-	gchar *uri;
-
-	chooser = GTK_RECENT_CHOOSER (action);
-
-	/* Wish: gtk_recent_chooser_get_current_file() */
-	uri = gtk_recent_chooser_get_current_uri (chooser);
-	file = g_file_new_for_uri (uri);
-	g_free (uri);
-
-	if (g_file_is_native (file))
-		e_attachment_bar_attach (
-			E_ATTACHMENT_BAR (attachment_bar),
-			g_file_get_path (file), "attachment");
-	else
-		e_attachment_bar_attach_remote_file (
-			E_ATTACHMENT_BAR (attachment_bar),
-			g_file_get_uri (file), "attachment");
-
-	g_object_unref (file);
-}
-
-GtkAction *
-e_attachment_bar_recent_action_new (EAttachmentBar *bar, 
-				const gchar *action_name,
-				const gchar *action_label)
-{
-	GtkAction *action;
-	GtkRecentChooser *chooser;
-
-	g_return_val_if_fail (E_IS_ATTACHMENT_BAR (bar), NULL);
-
-	action = gtk_recent_action_new (
-		action_name, action_label, NULL, NULL);
-	gtk_recent_action_set_show_numbers (GTK_RECENT_ACTION (action), TRUE);
-
-	chooser = GTK_RECENT_CHOOSER (action);
-	gtk_recent_chooser_set_show_icons (chooser, TRUE);
-	gtk_recent_chooser_set_show_not_found (chooser, FALSE);
-	gtk_recent_chooser_set_show_private (chooser, FALSE);
-	gtk_recent_chooser_set_show_tips (chooser, TRUE);
-	gtk_recent_chooser_set_sort_type (chooser, GTK_RECENT_SORT_MRU);
-
-	g_signal_connect (
-		action, "item-activated",
-		G_CALLBACK (action_recent_cb), bar);
-
-	return action;
-}
-
Index: widgets/misc/e-attachment-icon-view.h
===================================================================
--- widgets/misc/e-attachment-icon-view.h	(revision 0)
+++ widgets/misc/e-attachment-icon-view.h	(revision 0)
@@ -0,0 +1,66 @@
+/*
+ * e-attachment-icon-view.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_ICON_VIEW_H
+#define E_ATTACHMENT_ICON_VIEW_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_ICON_VIEW \
+	(e_attachment_icon_view_get_type ())
+#define E_ATTACHMENT_ICON_VIEW(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_ATTACHMENT_ICON_VIEW, EAttachmentIconView))
+#define E_ATTACHMENT_ICON_VIEW_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_ATTACHMENT_ICON_VIEW, EAttachmentIconView))
+#define E_IS_ATTACHMENT_ICON_VIEW(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_ATTACHMENT_ICON_VIEW))
+#define E_IS_ATTACHMENT_ICON_VIEW_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_ATTACHMENT_ICON_VIEW))
+#define E_ATTACHMENT_ICON_VIEW_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_ATTACHMENT_ICON_VIEW))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentIconView EAttachmentIconView;
+typedef struct _EAttachmentIconViewClass EAttachmentIconViewClass;
+typedef struct _EAttachmentIconViewPrivate EAttachmentIconViewPrivate;
+
+struct _EAttachmentIconView {
+	GtkIconView parent;
+	EAttachmentIconViewPrivate *priv;
+};
+
+struct _EAttachmentIconViewClass {
+	GtkIconViewClass parent_class;
+};
+
+GType		e_attachment_icon_view_get_type		(void);
+GtkWidget *	e_attachment_icon_view_new		(void);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_ICON_VIEW_H */
Index: widgets/misc/e-attachment-bar.h
===================================================================
--- widgets/misc/e-attachment-bar.h	(revision 37527)
+++ widgets/misc/e-attachment-bar.h	(working copy)
@@ -1,105 +0,0 @@
-/*
- * 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/>  
- *
- *
- * Authors:
- *		Ettore Perazzoli <ettore@ximian.com>
- * 	   	Srinivasa Ragavan <sragavan@novell.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifndef __E_ATTACHMENT_BAR_H__
-#define __E_ATTACHMENT_BAR_H__
-
-#include <libgnomeui/gnome-icon-list.h>
-
-#include <bonobo/bonobo-ui-node.h>
-#include <bonobo/bonobo-ui-util.h>
-
-#include <camel/camel-multipart.h>
-#include "e-attachment.h"
-
-#ifdef __cplusplus
-extern "C" {
-#pragma }
-#endif /* __cplusplus */
-
-#define E_TYPE_ATTACHMENT_BAR \
-	(e_attachment_bar_get_type ())
-#define E_ATTACHMENT_BAR(obj) \
-	(G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_ATTACHMENT_BAR, EAttachmentBar))
-#define E_ATTACHMENT_BAR_CLASS(klass) \
-	(G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_ATTACHMENT_BAR, EAttachmentBarClass))
-#define E_IS_ATTACHMENT_BAR(obj) \
-	(G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_ATTACHMENT_BAR))
-#define E_IS_ATTACHMENT_BAR_CLASS(klass) \
-	(G_TYPE_CHECK_CLASS_TYPE ((obj), E_TYPE_ATTACHMENT_BAR))
-
-typedef struct _EAttachmentBar EAttachmentBar;
-typedef struct _EAttachmentBarClass EAttachmentBarClass;
-
-struct _EAttachmentBar {
-	GnomeIconList parent;
-	gboolean expand;
-
-	struct _EAttachmentBarPrivate *priv;
-};
-
-struct _EAttachmentBarClass {
-	GnomeIconListClass parent_class;
-
-	void (* changed) (EAttachmentBar *bar);
-};
-
-
-GType e_attachment_bar_get_type (void);
-
-GtkWidget *e_attachment_bar_new (GtkAdjustment *adj);
-void e_attachment_bar_to_multipart (EAttachmentBar *bar, CamelMultipart *multipart,
-				    const char *default_charset);
-guint e_attachment_bar_get_num_attachments (EAttachmentBar *bar);
-void e_attachment_bar_attach (EAttachmentBar *bar, const char *file_name, const char *disposition);
-void e_attachment_bar_attach_mime_part (EAttachmentBar *bar, CamelMimePart *part);
-int e_attachment_bar_get_download_count (EAttachmentBar *bar);
-void e_attachment_bar_attach_remote_file (EAttachmentBar *bar, const char *url, const char *disposition);
-GSList *e_attachment_bar_get_attachment (EAttachmentBar *bar, int id);
-void e_attachment_bar_add_attachment (EAttachmentBar *bar, EAttachment *attachment);
-void e_attachment_bar_edit_selected (EAttachmentBar *bar);
-void e_attachment_bar_remove_selected (EAttachmentBar *bar);
-GtkWidget ** e_attachment_bar_get_selector(EAttachmentBar *bar);
-GSList *e_attachment_bar_get_parts (EAttachmentBar *bar);
-GSList *e_attachment_bar_get_selected (EAttachmentBar *bar);
-void e_attachment_bar_set_width(EAttachmentBar *bar, int bar_width);
-GSList * e_attachment_bar_get_all_attachments (EAttachmentBar *bar);
-void e_attachment_bar_create_attachment_cache (EAttachment *attachment);
-void 
-e_attachment_bar_bonobo_ui_populate_with_recent (BonoboUIComponent *uic, const char *path,
-						 EAttachmentBar *bar, 
-						 BonoboUIVerbFn verb_cb, gpointer user_data);
-GtkAction *
-e_attachment_bar_recent_action_new (EAttachmentBar *bar, 
-				const gchar *action_name,
-				const gchar *action_label);
-void 
-e_attachment_bar_add_attachment_silent (EAttachmentBar *bar, EAttachment *attachment);
-void 
-e_attachment_bar_refresh (EAttachmentBar *bar);
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* __E_ATTACHMENT_BAR_H__ */
Index: widgets/misc/e-attachment-view.c
===================================================================
--- widgets/misc/e-attachment-view.c	(revision 0)
+++ widgets/misc/e-attachment-view.c	(revision 0)
@@ -0,0 +1,1637 @@
+/*
+ * e-attachment-view.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-view.h"
+
+#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-util.h"
+#include "e-attachment-dialog.h"
+#include "e-attachment-handler-image.h"
+
+enum {
+	UPDATE_ACTIONS,
+	LAST_SIGNAL
+};
+
+/* Note: Do not use the info field. */
+static GtkTargetEntry target_table[] = {
+	{ "_NETSCAPE_URL",	0, 0 },
+	{ "text/x-vcard",	0, 0 },
+	{ "text/calendar",	0, 0 }
+};
+
+static const gchar *ui =
+"<ui>"
+"  <popup name='context'>"
+"    <menuitem action='cancel'/>"
+"    <menuitem action='save-as'/>"
+"    <menuitem action='remove'/>"
+"    <menuitem action='properties'/>"
+"    <separator/>"
+"    <placeholder name='inline-actions'>"
+"      <menuitem action='show'/>"
+"      <menuitem action='hide'/>"
+"    </placeholder>"
+"    <separator/>"
+"    <placeholder name='custom-actions'/>"
+"    <separator/>"
+"    <menuitem action='add'/>"
+"    <separator/>"
+"    <placeholder name='open-actions'/>"
+"  </popup>"
+"</ui>";
+
+static gulong signals[LAST_SIGNAL];
+
+static void
+action_add_cb (GtkAction *action,
+               EAttachmentView *view)
+{
+	EAttachmentStore *store;
+	gpointer parent;
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	store = e_attachment_view_get_store (view);
+	e_attachment_store_run_load_dialog (store, parent);
+}
+
+static void
+action_cancel_cb (GtkAction *action,
+                  EAttachmentView *view)
+{
+	EAttachment *attachment;
+	GList *selected;
+
+	selected = e_attachment_view_get_selected_attachments (view);
+	g_return_if_fail (g_list_length (selected) == 1);
+	attachment = selected->data;
+
+	e_attachment_cancel (attachment);
+
+	g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+	g_list_free (selected);
+}
+
+static void
+action_hide_cb (GtkAction *action,
+                EAttachmentView *view)
+{
+	EAttachment *attachment;
+	GList *selected;
+
+	selected = e_attachment_view_get_selected_attachments (view);
+	g_return_if_fail (g_list_length (selected) == 1);
+	attachment = selected->data;
+
+	e_attachment_set_shown (attachment, FALSE);
+
+	g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+	g_list_free (selected);
+}
+
+static void
+action_open_in_cb (GtkAction *action,
+                   EAttachmentView *view)
+{
+	GAppInfo *app_info;
+	GtkTreePath *path;
+	GList *selected;
+
+	selected = e_attachment_view_get_selected_paths (view);
+	g_return_if_fail (g_list_length (selected) == 1);
+	path = selected->data;
+
+	app_info = g_object_get_data (G_OBJECT (action), "app-info");
+	g_return_if_fail (G_IS_APP_INFO (app_info));
+
+	e_attachment_view_open_path (view, path, app_info);
+
+	g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
+	g_list_free (selected);
+}
+
+static void
+action_properties_cb (GtkAction *action,
+                      EAttachmentView *view)
+{
+	EAttachment *attachment;
+	GtkWidget *dialog;
+	GList *selected;
+	gpointer parent;
+
+	selected = e_attachment_view_get_selected_attachments (view);
+	g_return_if_fail (g_list_length (selected) == 1);
+	attachment = selected->data;
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	dialog = e_attachment_dialog_new (parent, attachment);
+	gtk_dialog_run (GTK_DIALOG (dialog));
+	gtk_widget_destroy (dialog);
+
+	g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+	g_list_free (selected);
+}
+
+static void
+action_recent_cb (GtkAction *action,
+                  EAttachmentView *view)
+{
+	GtkRecentChooser *chooser;
+	EAttachmentStore *store;
+	EAttachment *attachment;
+	gpointer parent;
+	gchar *uri;
+
+	chooser = GTK_RECENT_CHOOSER (action);
+	store = e_attachment_view_get_store (view);
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	uri = gtk_recent_chooser_get_current_uri (chooser);
+	attachment = e_attachment_new_for_uri (uri);
+	e_attachment_store_add_attachment (store, attachment);
+	e_attachment_load_async (
+		attachment, (GAsyncReadyCallback)
+		e_attachment_load_handle_error, parent);
+	g_free (uri);
+}
+
+static void
+action_remove_cb (GtkAction *action,
+                  EAttachmentView *view)
+{
+	e_attachment_view_remove_selected (view, FALSE);
+}
+
+static void
+action_save_all_cb (GtkAction *action,
+                    EAttachmentView *view)
+{
+	EAttachmentStore *store;
+	GList *selected, *iter;
+	GFile *destination;
+	gpointer parent;
+
+	store = e_attachment_view_get_store (view);
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	/* XXX We lose the previous selection. */
+	e_attachment_view_select_all (view);
+	selected = e_attachment_view_get_selected_attachments (view);
+	e_attachment_view_unselect_all (view);
+
+	destination = e_attachment_store_run_save_dialog (
+		store, selected, parent);
+
+	if (destination == NULL)
+		goto exit;
+
+	for (iter = selected; iter != NULL; iter = iter->next) {
+		EAttachment *attachment = iter->data;
+
+		e_attachment_save_async (
+			attachment, destination, (GAsyncReadyCallback)
+			e_attachment_save_handle_error, parent);
+	}
+
+	g_object_unref (destination);
+
+exit:
+	g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+	g_list_free (selected);
+}
+
+static void
+action_save_as_cb (GtkAction *action,
+                   EAttachmentView *view)
+{
+	EAttachmentStore *store;
+	GList *selected, *iter;
+	GFile *destination;
+	gpointer parent;
+
+	store = e_attachment_view_get_store (view);
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	selected = e_attachment_view_get_selected_attachments (view);
+
+	destination = e_attachment_store_run_save_dialog (
+		store, selected, parent);
+
+	if (destination == NULL)
+		goto exit;
+
+	for (iter = selected; iter != NULL; iter = iter->next) {
+		EAttachment *attachment = iter->data;
+
+		e_attachment_save_async (
+			attachment, destination, (GAsyncReadyCallback)
+			e_attachment_save_handle_error, parent);
+	}
+
+	g_object_unref (destination);
+
+exit:
+	g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+	g_list_free (selected);
+}
+
+static void
+action_show_cb (GtkAction *action,
+                EAttachmentView *view)
+{
+	EAttachment *attachment;
+	GList *selected;
+
+	selected = e_attachment_view_get_selected_attachments (view);
+	g_return_if_fail (g_list_length (selected) == 1);
+	attachment = selected->data;
+
+	e_attachment_set_shown (attachment, TRUE);
+
+	g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+	g_list_free (selected);
+}
+
+static GtkActionEntry standard_entries[] = {
+
+	{ "cancel",
+	  GTK_STOCK_CANCEL,
+	  NULL,
+	  NULL,
+	  NULL,  /* XXX Add a tooltip! */
+	  G_CALLBACK (action_cancel_cb) },
+
+	{ "save-all",
+	  GTK_STOCK_SAVE_AS,
+	  N_("S_ave All"),
+	  NULL,
+	  NULL,  /* XXX Add a tooltip! */
+	  G_CALLBACK (action_save_all_cb) },
+
+	{ "save-as",
+	  GTK_STOCK_SAVE_AS,
+	  NULL,
+	  NULL,
+	  NULL,  /* XXX Add a tooltip! */
+	  G_CALLBACK (action_save_as_cb) },
+
+	/* Alternate "save-all" label, for when
+	 * the attachment store has one row. */
+	{ "save-one",
+	  GTK_STOCK_SAVE_AS,
+	  NULL,
+	  NULL,
+	  NULL,  /* XXX Add a tooltip! */
+	  G_CALLBACK (action_save_all_cb) },
+};
+
+static GtkActionEntry editable_entries[] = {
+
+	{ "add",
+	  GTK_STOCK_ADD,
+	  N_("A_dd Attachment..."),
+	  NULL,
+	  N_("Attach a file"),
+	  G_CALLBACK (action_add_cb) },
+
+	{ "properties",
+	  GTK_STOCK_PROPERTIES,
+	  NULL,
+	  NULL,
+	  NULL,  /* XXX Add a tooltip! */
+	  G_CALLBACK (action_properties_cb) },
+
+	{ "remove",
+	  GTK_STOCK_REMOVE,
+	  NULL,
+	  NULL,
+	  NULL,  /* XXX Add a tooltip! */
+	  G_CALLBACK (action_remove_cb) }
+};
+
+static GtkActionEntry inline_entries[] = {
+
+	{ "hide",
+	  NULL,
+	  N_("_Hide"),
+	  NULL,
+	  NULL,  /* XXX Add a tooltip! */
+	  G_CALLBACK (action_hide_cb) },
+
+	{ "show",
+	  NULL,
+	  N_("_View Inline"),
+	  NULL,
+	  NULL,  /* XXX Add a tooltip! */
+	  G_CALLBACK (action_show_cb) }
+};
+
+static void
+attachment_view_netscape_url (EAttachmentView *view,
+                              GdkDragContext *drag_context,
+                              gint x,
+                              gint y,
+                              GtkSelectionData *selection_data,
+                              guint info,
+                              guint time)
+{
+	static GdkAtom atom = GDK_NONE;
+	EAttachmentStore *store;
+	EAttachment *attachment;
+	const gchar *data;
+	gpointer parent;
+	gchar *copied_data;
+	gchar **strv;
+	gint length;
+
+	if (G_UNLIKELY (atom == GDK_NONE))
+		atom = gdk_atom_intern_static_string ("_NETSCAPE_URL");
+
+	if (gtk_selection_data_get_target (selection_data) != atom)
+		return;
+
+	g_signal_stop_emission_by_name (view, "drag-data-received");
+
+	/* _NETSCAPE_URL is represented as "URI\nTITLE" */
+
+	data = (const gchar *) gtk_selection_data_get_data (selection_data);
+	length = gtk_selection_data_get_length (selection_data);
+
+	copied_data = g_strndup (data, length);
+	strv = g_strsplit (copied_data, "\n", 2);
+	g_free (copied_data);
+
+	store = e_attachment_view_get_store (view);
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	attachment = e_attachment_new_for_uri (strv[0]);
+	e_attachment_store_add_attachment (store, attachment);
+	e_attachment_load_async (
+		attachment, (GAsyncReadyCallback)
+		e_attachment_load_handle_error, parent);
+	g_object_unref (attachment);
+
+	g_strfreev (strv);
+
+	gtk_drag_finish (drag_context, TRUE, FALSE, time);
+}
+
+static void
+attachment_view_text_calendar (EAttachmentView *view,
+                               GdkDragContext *drag_context,
+                               gint x,
+                               gint y,
+                               GtkSelectionData *selection_data,
+                               guint info,
+                               guint time)
+{
+	static GdkAtom atom = GDK_NONE;
+	EAttachmentStore *store;
+	EAttachment *attachment;
+	CamelMimePart *mime_part;
+	GdkAtom data_type;
+	const gchar *data;
+	gpointer parent;
+	gchar *content_type;
+	gint length;
+
+	if (G_UNLIKELY (atom = GDK_NONE))
+		atom = gdk_atom_intern_static_string ("text/calendar");
+
+	if (gtk_selection_data_get_target (selection_data) != atom)
+		return;
+
+	g_signal_stop_emission_by_name (view, "drag-data-received");
+
+	data = (const gchar *) gtk_selection_data_get_data (selection_data);
+	length = gtk_selection_data_get_length (selection_data);
+	data_type = gtk_selection_data_get_data_type (selection_data);
+
+	mime_part = camel_mime_part_new ();
+
+	content_type = gdk_atom_name (data_type);
+	camel_mime_part_set_content (mime_part, data, length, content_type);
+	camel_mime_part_set_disposition (mime_part, "inline");
+	g_free (content_type);
+
+	store = e_attachment_view_get_store (view);
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	attachment = e_attachment_new ();
+	e_attachment_set_mime_part (attachment, mime_part);
+	e_attachment_store_add_attachment (store, attachment);
+	e_attachment_load_async (
+		attachment, (GAsyncReadyCallback)
+		e_attachment_load_handle_error, parent);
+	g_object_unref (attachment);
+
+	camel_object_unref (mime_part);
+
+	gtk_drag_finish (drag_context, TRUE, FALSE, time);
+}
+
+static void
+attachment_view_text_x_vcard (EAttachmentView *view,
+                              GdkDragContext *drag_context,
+                              gint x,
+                              gint y,
+                              GtkSelectionData *selection_data,
+                              guint info,
+                              guint time)
+{
+	static GdkAtom atom = GDK_NONE;
+	EAttachmentStore *store;
+	EAttachment *attachment;
+	CamelMimePart *mime_part;
+	GdkAtom data_type;
+	const gchar *data;
+	gpointer parent;
+	gchar *content_type;
+	gint length;
+
+	if (G_UNLIKELY (atom = GDK_NONE))
+		atom = gdk_atom_intern_static_string ("text/x-vcard");
+
+	if (gtk_selection_data_get_target (selection_data) != atom)
+		return;
+
+	g_signal_stop_emission_by_name (view, "drag-data-received");
+
+	data = (const gchar *) gtk_selection_data_get_data (selection_data);
+	length = gtk_selection_data_get_length (selection_data);
+	data_type = gtk_selection_data_get_data_type (selection_data);
+
+	mime_part = camel_mime_part_new ();
+
+	content_type = gdk_atom_name (data_type);
+	camel_mime_part_set_content (mime_part, data, length, content_type);
+	camel_mime_part_set_disposition (mime_part, "inline");
+	g_free (content_type);
+
+	store = e_attachment_view_get_store (view);
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	attachment = e_attachment_new ();
+	e_attachment_set_mime_part (attachment, mime_part);
+	e_attachment_store_add_attachment (store, attachment);
+	e_attachment_load_async (
+		attachment, (GAsyncReadyCallback)
+		e_attachment_load_handle_error, parent);
+	g_object_unref (attachment);
+
+	camel_object_unref (mime_part);
+
+	gtk_drag_finish (drag_context, TRUE, FALSE, time);
+}
+
+static void
+attachment_view_uris (EAttachmentView *view,
+                      GdkDragContext *drag_context,
+                      gint x,
+                      gint y,
+                      GtkSelectionData *selection_data,
+                      guint info,
+                      guint time)
+{
+	EAttachmentStore *store;
+	gpointer parent;
+	gchar **uris;
+	gint ii;
+
+	uris = gtk_selection_data_get_uris (selection_data);
+
+	if (uris == NULL)
+		return;
+
+	g_signal_stop_emission_by_name (view, "drag-data-received");
+
+	store = e_attachment_view_get_store (view);
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	for (ii = 0; uris[ii] != NULL; ii++) {
+		EAttachment *attachment;
+
+		attachment = e_attachment_new_for_uri (uris[ii]);
+		e_attachment_store_add_attachment (store, attachment);
+		e_attachment_load_async (
+			attachment, (GAsyncReadyCallback)
+			e_attachment_load_handle_error, parent);
+		g_object_unref (attachment);
+	}
+
+	g_strfreev (uris);
+
+	gtk_drag_finish (drag_context, TRUE, FALSE, time);
+}
+
+static void
+attachment_view_update_actions (EAttachmentView *view)
+{
+	EAttachmentViewPrivate *priv;
+	EAttachment *attachment;
+	GtkActionGroup *action_group;
+	GtkAction *action;
+	GList *list, *iter;
+	guint n_selected;
+	gboolean busy = FALSE;
+	gboolean can_show = FALSE;
+	gboolean shown = 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);
+		can_show = e_attachment_get_can_show (attachment);
+		shown = e_attachment_get_shown (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, "hide");
+	gtk_action_set_visible (action, can_show && shown);
+
+	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, "show");
+	gtk_action_set_visible (action, can_show && !shown);
+
+	/* Clear out the "openwith" action group. */
+	gtk_ui_manager_remove_ui (priv->ui_manager, priv->merge_id);
+	action_group = e_attachment_view_get_action_group (view, "openwith");
+	e_action_group_remove_all_actions (action_group);
+
+	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;
+		GIcon *app_icon;
+		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_icon = g_app_info_get_icon (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);
+
+#if GTK_CHECK_VERSION(2,16,0)
+		gtk_action_set_gicon (action, app_icon);
+#endif
+
+		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 (action_group, 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_init_handlers (EAttachmentView *view)
+{
+	EAttachmentViewPrivate *priv;
+	GtkTargetList *target_list;
+	GType *children;
+	guint ii;
+
+	priv = e_attachment_view_get_private (view);
+
+	target_list = gtk_target_list_new (
+		target_table, G_N_ELEMENTS (target_table));
+
+	gtk_target_list_add_uri_targets (target_list, 0);
+
+	priv->handlers = g_ptr_array_new ();
+	priv->target_list = target_list;
+	priv->drag_actions = GDK_ACTION_COPY;
+
+	children = g_type_children (E_TYPE_ATTACHMENT_HANDLER, NULL);
+
+	for (ii = 0; children[ii] != G_TYPE_INVALID; ii++) {
+		EAttachmentHandler *handler;
+		const GtkTargetEntry *targets;
+		guint n_targets;
+
+		handler = g_object_new (children[ii], "view", view, NULL);
+
+		targets = e_attachment_handler_get_target_table (
+			handler, &n_targets);
+		gtk_target_list_add_table (target_list, targets, n_targets);
+		priv->drag_actions |=
+			e_attachment_handler_get_drag_actions (handler);
+
+		g_ptr_array_add (priv->handlers, handler);
+	}
+
+	g_free (children);
+}
+
+static void
+attachment_view_class_init (EAttachmentViewIface *iface)
+{
+	iface->update_actions = attachment_view_update_actions;
+
+	g_object_interface_install_property (
+		iface,
+		g_param_spec_boolean (
+			"editable",
+			"Editable",
+			NULL,
+			TRUE,
+			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);
+}
+
+GType
+e_attachment_view_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EAttachmentViewIface),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) attachment_view_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			0,     /* instance_size */
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) NULL,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			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;
+}
+
+void
+e_attachment_view_init (EAttachmentView *view)
+{
+	EAttachmentViewPrivate *priv;
+	GtkUIManager *ui_manager;
+	GtkActionGroup *action_group;
+	GError *error = NULL;
+
+	priv = e_attachment_view_get_private (view);
+
+	ui_manager = gtk_ui_manager_new ();
+	priv->merge_id = gtk_ui_manager_new_merge_id (ui_manager);
+	priv->ui_manager = ui_manager;
+
+	action_group = e_attachment_view_add_action_group (view, "standard");
+
+	gtk_action_group_add_actions (
+		action_group, standard_entries,
+		G_N_ELEMENTS (standard_entries), view);
+
+	action_group = e_attachment_view_add_action_group (view, "editable");
+
+	e_mutual_binding_new (
+		G_OBJECT (view), "editable",
+		G_OBJECT (action_group), "visible");
+	gtk_action_group_add_actions (
+		action_group, editable_entries,
+		G_N_ELEMENTS (editable_entries), view);
+
+	action_group = e_attachment_view_add_action_group (view, "inline");
+
+	gtk_action_group_add_actions (
+		action_group, inline_entries,
+		G_N_ELEMENTS (inline_entries), view);
+	gtk_action_group_set_visible (action_group, FALSE);
+
+	e_attachment_view_add_action_group (view, "openwith");
+
+	/* Because we are loading from a hard-coded string, there is
+	 * no chance of I/O errors.  Failure here implies a malformed
+	 * UI definition.  Full stop. */
+	gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+	if (error != NULL)
+		g_error ("%s", error->message);
+
+	attachment_view_init_handlers (view);
+
+	e_attachment_view_drag_source_set (view);
+	e_attachment_view_drag_dest_set (view);
+
+	/* Connect built-in drag and drop handlers. */
+
+	g_signal_connect (
+		view, "drag-data-received",
+		G_CALLBACK (attachment_view_netscape_url), NULL);
+
+	g_signal_connect (
+		view, "drag-data-received",
+		G_CALLBACK (attachment_view_text_calendar), NULL);
+
+	g_signal_connect (
+		view, "drag-data-received",
+		G_CALLBACK (attachment_view_text_x_vcard), NULL);
+
+	g_signal_connect (
+		view, "drag-data-received",
+		G_CALLBACK (attachment_view_uris), NULL);
+}
+
+void
+e_attachment_view_dispose (EAttachmentView *view)
+{
+	EAttachmentViewPrivate *priv;
+
+	priv = e_attachment_view_get_private (view);
+
+	g_ptr_array_foreach (priv->handlers, (GFunc) g_object_unref, NULL);
+	g_ptr_array_set_size (priv->handlers, 0);
+
+	if (priv->target_list != NULL) {
+		gtk_target_list_unref (priv->target_list);
+		priv->target_list = NULL;
+	}
+
+	if (priv->ui_manager != NULL) {
+		g_object_unref (priv->ui_manager);
+		priv->ui_manager = NULL;
+	}
+}
+
+void
+e_attachment_view_finalize (EAttachmentView *view)
+{
+	EAttachmentViewPrivate *priv;
+
+	priv = e_attachment_view_get_private (view);
+
+	g_ptr_array_free (priv->handlers, TRUE);
+}
+
+EAttachmentViewPrivate *
+e_attachment_view_get_private (EAttachmentView *view)
+{
+	EAttachmentViewIface *iface;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+
+	iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+	g_return_val_if_fail (iface->get_private != NULL, NULL);
+
+	return iface->get_private (view);
+}
+
+EAttachmentStore *
+e_attachment_view_get_store (EAttachmentView *view)
+{
+	EAttachmentViewIface *iface;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+
+	iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+	g_return_val_if_fail (iface->get_store != NULL, NULL);
+
+	return iface->get_store (view);
+}
+
+gboolean
+e_attachment_view_get_editable (EAttachmentView *view)
+{
+	EAttachmentViewPrivate *priv;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
+
+	priv = e_attachment_view_get_private (view);
+
+	return priv->editable;
+}
+
+void
+e_attachment_view_set_editable (EAttachmentView *view,
+                                gboolean editable)
+{
+	EAttachmentViewPrivate *priv;
+
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+	priv = e_attachment_view_get_private (view);
+	priv->editable = editable;
+
+	g_object_notify (G_OBJECT (view), "editable");
+}
+
+GtkTargetList *
+e_attachment_view_get_target_list (EAttachmentView *view)
+{
+	EAttachmentViewPrivate *priv;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+
+	priv = e_attachment_view_get_private (view);
+
+	return priv->target_list;
+}
+
+GdkDragAction
+e_attachment_view_get_drag_actions (EAttachmentView *view)
+{
+	EAttachmentViewPrivate *priv;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), 0);
+
+	priv = e_attachment_view_get_private (view);
+
+	return priv->drag_actions;
+}
+
+GList *
+e_attachment_view_get_selected_attachments (EAttachmentView *view)
+{
+	EAttachmentStore *store;
+	GtkTreeModel *model;
+	GList *selected, *item;
+	gint column_id;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+
+	column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+	selected = e_attachment_view_get_selected_paths (view);
+	store = e_attachment_view_get_store (view);
+	model = GTK_TREE_MODEL (store);
+
+	/* Convert the GtkTreePaths to EAttachments. */
+	for (item = selected; item != NULL; item = item->next) {
+		EAttachment *attachment;
+		GtkTreePath *path;
+		GtkTreeIter iter;
+
+		path = item->data;
+
+		gtk_tree_model_get_iter (model, &iter, path);
+		gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
+		gtk_tree_path_free (path);
+
+		item->data = attachment;
+	}
+
+	return selected;
+}
+
+void
+e_attachment_view_open_path (EAttachmentView *view,
+                             GtkTreePath *path,
+                             GAppInfo *app_info)
+{
+	EAttachmentStore *store;
+	EAttachment *attachment;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	gpointer parent;
+	gint column_id;
+
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+	g_return_if_fail (path != NULL);
+
+	column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+	store = e_attachment_view_get_store (view);
+	model = GTK_TREE_MODEL (store);
+
+	gtk_tree_model_get_iter (model, &iter, path);
+	gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	e_attachment_open_async (
+		attachment, app_info, (GAsyncReadyCallback)
+		e_attachment_open_handle_error, parent);
+
+	g_object_unref (attachment);
+}
+
+void
+e_attachment_view_remove_selected (EAttachmentView *view,
+                                   gboolean select_next)
+{
+	EAttachmentStore *store;
+	GtkTreeModel *model;
+	GList *selected, *item;
+	gint column_id;
+
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+	column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+	selected = e_attachment_view_get_selected_paths (view);
+	store = e_attachment_view_get_store (view);
+	model = GTK_TREE_MODEL (store);
+
+	for (item = selected; item != NULL; item = item->next) {
+		EAttachment *attachment;
+		GtkTreePath *path = item->data;
+		GtkTreeIter iter;
+
+		gtk_tree_model_get_iter (model, &iter, path);
+		gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
+		e_attachment_store_remove_attachment (store, attachment);
+		g_object_unref (attachment);
+	}
+
+	/* If we only removed one attachment, try to select another. */
+	if (select_next && g_list_length (selected) == 1) {
+		GtkTreePath *path = selected->data;
+
+		e_attachment_view_select_path (view, path);
+		if (!e_attachment_view_path_is_selected (view, path))
+			if (gtk_tree_path_prev (path))
+				e_attachment_view_select_path (view, path);
+	}
+
+	g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
+	g_list_free (selected);
+}
+
+gboolean
+e_attachment_view_button_press_event (EAttachmentView *view,
+                                      GdkEventButton *event)
+{
+	GtkTreePath *path;
+	gboolean editable;
+	gboolean item_clicked;
+
+	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 the user clicked on a selected item, retain the current
+	 * selection.  If the user clicked on an unselected item, select
+	 * the clicked item only.  If the user did not click on an item,
+	 * clear the current selection. */
+	path = e_attachment_view_get_path_at_pos (view, event->x, event->y);
+	if (path != NULL) {
+		if (!e_attachment_view_path_is_selected (view, path)) {
+			e_attachment_view_unselect_all (view);
+			e_attachment_view_select_path (view, path);
+		}
+		gtk_tree_path_free (path);
+		item_clicked = TRUE;
+	} else {
+		e_attachment_view_unselect_all (view);
+		item_clicked = FALSE;
+	}
+
+	/* Cancel drag and drop if there are no selected items,
+	 * or if any of the selected items are loading or saving. */
+	if (event->button == 1 && event->type == GDK_BUTTON_PRESS) {
+		GList *selected, *iter;
+		gboolean busy = FALSE;
+
+		selected = e_attachment_view_get_selected_attachments (view);
+		for (iter = selected; iter != NULL; iter = iter->next) {
+			EAttachment *attachment = iter->data;
+			busy |= e_attachment_get_loading (attachment);
+			busy |= e_attachment_get_saving (attachment);
+		}
+		if (selected == NULL || busy)
+			e_attachment_view_drag_source_unset (view);
+		g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+		g_list_free (selected);
+	}
+
+	if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
+		/* Non-editable attachment views should only show a
+		 * popup menu when right-clicking on an attachment,
+		 * but editable views can show the menu any time. */
+		if (item_clicked || editable) {
+			e_attachment_view_show_popup_menu (
+				view, event, NULL, NULL);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+gboolean
+e_attachment_view_button_release_event (EAttachmentView *view,
+                                        GdkEventButton *event)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
+	g_return_val_if_fail (event != NULL, FALSE);
+
+	/* Restore the attachment view as a drag source, in case
+	 * we had to cancel during a button press event. */
+	if (event->button == 1)
+		e_attachment_view_drag_source_set (view);
+
+	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,
+                                   gint y)
+{
+	EAttachmentViewIface *iface;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+
+	iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+	g_return_val_if_fail (iface->get_path_at_pos != NULL, NULL);
+
+	return iface->get_path_at_pos (view, x, y);
+}
+
+GList *
+e_attachment_view_get_selected_paths (EAttachmentView *view)
+{
+	EAttachmentViewIface *iface;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+
+	iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+	g_return_val_if_fail (iface->get_selected_paths != NULL, NULL);
+
+	return iface->get_selected_paths (view);
+}
+
+gboolean
+e_attachment_view_path_is_selected (EAttachmentView *view,
+                                    GtkTreePath *path)
+{
+	EAttachmentViewIface *iface;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
+	g_return_val_if_fail (path != NULL, FALSE);
+
+	iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+	g_return_val_if_fail (iface->path_is_selected != NULL, FALSE);
+
+	return iface->path_is_selected (view, path);
+}
+
+void
+e_attachment_view_select_path (EAttachmentView *view,
+                               GtkTreePath *path)
+{
+	EAttachmentViewIface *iface;
+
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+	g_return_if_fail (path != NULL);
+
+	iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+	g_return_if_fail (iface->select_path != NULL);
+
+	iface->select_path (view, path);
+}
+
+void
+e_attachment_view_unselect_path (EAttachmentView *view,
+                                 GtkTreePath *path)
+{
+	EAttachmentViewIface *iface;
+
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+	g_return_if_fail (path != NULL);
+
+	iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+	g_return_if_fail (iface->unselect_path != NULL);
+
+	iface->unselect_path (view, path);
+}
+
+void
+e_attachment_view_select_all (EAttachmentView *view)
+{
+	EAttachmentViewIface *iface;
+
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+	iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+	g_return_if_fail (iface->select_all != NULL);
+
+	iface->select_all (view);
+}
+
+void
+e_attachment_view_unselect_all (EAttachmentView *view)
+{
+	EAttachmentViewIface *iface;
+
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+	iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+	g_return_if_fail (iface->unselect_all != NULL);
+
+	iface->unselect_all (view);
+}
+
+void
+e_attachment_view_sync_selection (EAttachmentView *view,
+                                  EAttachmentView *target)
+{
+	GList *selected, *iter;
+
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (target));
+
+	selected = e_attachment_view_get_selected_paths (view);
+	e_attachment_view_unselect_all (target);
+
+	for (iter = selected; iter != NULL; iter = iter->next)
+		e_attachment_view_select_path (target, iter->data);
+
+	g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
+	g_list_free (selected);
+}
+
+void
+e_attachment_view_drag_source_set (EAttachmentView *view)
+{
+	GtkTargetEntry *targets;
+	GtkTargetList *list;
+	gint n_targets;
+
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+	list = gtk_target_list_new (NULL, 0);
+	gtk_target_list_add_uri_targets (list, 0);
+	targets = gtk_target_table_new_from_list (list, &n_targets);
+
+	gtk_drag_source_set (
+		GTK_WIDGET (view), GDK_BUTTON1_MASK,
+		targets, n_targets, GDK_ACTION_COPY);
+
+	gtk_target_table_free (targets, n_targets);
+	gtk_target_list_unref (list);
+}
+
+void
+e_attachment_view_drag_source_unset (EAttachmentView *view)
+{
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+	gtk_drag_source_unset (GTK_WIDGET (view));
+}
+
+void
+e_attachment_view_drag_begin (EAttachmentView *view,
+                              GdkDragContext *context)
+{
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+	g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+}
+
+void
+e_attachment_view_drag_end (EAttachmentView *view,
+                            GdkDragContext *context)
+{
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+	g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+}
+
+static void
+attachment_view_got_uris_cb (EAttachmentStore *store,
+                             GAsyncResult *result,
+                             gpointer user_data)
+{
+	struct {
+		gchar **uris;
+		gboolean done;
+	} *status = user_data;
+
+	/* XXX Since this is a best-effort function,
+	 *     should we care about errors? */
+	status->uris = e_attachment_store_get_uris_finish (
+		store, result, NULL);
+
+	status->done = TRUE;
+}
+
+void
+e_attachment_view_drag_data_get (EAttachmentView *view,
+                                 GdkDragContext *context,
+                                 GtkSelectionData *selection,
+                                 guint info,
+                                 guint time)
+{
+	EAttachmentStore *store;
+	GList *selected;
+
+	struct {
+		gchar **uris;
+		gboolean done;
+	} status;
+
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+	g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+	g_return_if_fail (selection != NULL);
+
+	status.uris = NULL;
+	status.done = FALSE;
+
+	store = e_attachment_view_get_store (view);
+
+	selected = e_attachment_view_get_selected_attachments (view);
+	if (selected == NULL)
+		return;
+
+	e_attachment_store_get_uris_async (
+		store, selected, (GAsyncReadyCallback)
+		attachment_view_got_uris_cb, &status);
+
+	g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+	g_list_free (selected);
+
+	/* We can't return until we have results, so crank
+	 * the main loop until the callback gets triggered. */
+	while (!status.done)
+		if (gtk_main_iteration ())
+			break;
+
+	if (status.uris != NULL)
+		gtk_selection_data_set_uris (selection, status.uris);
+
+	g_strfreev (status.uris);
+}
+
+void
+e_attachment_view_drag_dest_set (EAttachmentView *view)
+{
+	EAttachmentViewPrivate *priv;
+	GtkTargetEntry *targets;
+	gint n_targets;
+
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+	priv = e_attachment_view_get_private (view);
+
+	targets = gtk_target_table_new_from_list (
+		priv->target_list, &n_targets);
+
+	gtk_drag_dest_set (
+		GTK_WIDGET (view), GTK_DEST_DEFAULT_ALL,
+		targets, n_targets, priv->drag_actions);
+
+	gtk_target_table_free (targets, n_targets);
+}
+
+void
+e_attachment_view_drag_dest_unset (EAttachmentView *view)
+{
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+	gtk_drag_dest_unset (GTK_WIDGET (view));
+}
+
+gboolean
+e_attachment_view_drag_motion (EAttachmentView *view,
+                               GdkDragContext *context,
+                               gint x,
+                               gint y,
+                               guint time)
+{
+	EAttachmentViewPrivate *priv;
+	GdkDragAction actions;
+	GdkDragAction chosen_action;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
+	g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), FALSE);
+
+	priv = e_attachment_view_get_private (view);
+
+	/* Disallow drops if we're not editable. */
+	if (!e_attachment_view_get_editable (view))
+		return FALSE;
+
+	actions = priv->drag_actions & context->actions;
+	chosen_action = context->suggested_action;
+
+	if (chosen_action == GDK_ACTION_ASK) {
+		GdkDragAction mask;
+
+		mask = GDK_ACTION_COPY | GDK_ACTION_MOVE;
+		if ((actions & mask) != mask)
+			chosen_action = GDK_ACTION_COPY;
+	}
+
+	gdk_drag_status (context, chosen_action, time);
+
+	return (chosen_action != 0);
+}
+
+gboolean
+e_attachment_view_drag_drop (EAttachmentView *view,
+                             GdkDragContext *context,
+                             gint x,
+                             gint y,
+                             guint time)
+{
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
+	g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), FALSE);
+
+	/* Disallow drops if we're not editable. */
+	if (!e_attachment_view_get_editable (view))
+		return FALSE;
+
+	return TRUE;
+}
+
+void
+e_attachment_view_drag_data_received (EAttachmentView *view,
+                                      GdkDragContext *drag_context,
+                                      gint x,
+                                      gint y,
+                                      GtkSelectionData *selection_data,
+                                      guint info,
+                                      guint time)
+{
+	GdkAtom atom;
+	gchar *name;
+
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+	g_return_if_fail (GDK_IS_DRAG_CONTEXT (drag_context));
+
+	/* Drop handlers are supposed to stop further emission of the
+	 * "drag-data-received" signal if they can handle the data.  If
+	 * we get this far it means none of the handlers were successful,
+	 * so report the drop as failed. */
+
+	atom = gtk_selection_data_get_target (selection_data);
+
+	name = gdk_atom_name (atom);
+	g_warning ("Unknown selection target: %s", name);
+	g_free (name);
+
+	gtk_drag_finish (drag_context, FALSE, FALSE, time);
+}
+
+GtkAction *
+e_attachment_view_get_action (EAttachmentView *view,
+                              const gchar *action_name)
+{
+	GtkUIManager *ui_manager;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+	g_return_val_if_fail (action_name != NULL, NULL);
+
+	ui_manager = e_attachment_view_get_ui_manager (view);
+
+	return e_lookup_action (ui_manager, action_name);
+}
+
+GtkActionGroup *
+e_attachment_view_add_action_group (EAttachmentView *view,
+                                    const gchar *group_name)
+{
+	GtkActionGroup *action_group;
+	GtkUIManager *ui_manager;
+	const gchar *domain;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+	g_return_val_if_fail (group_name != NULL, NULL);
+
+	ui_manager = e_attachment_view_get_ui_manager (view);
+	domain = GETTEXT_PACKAGE;
+
+	action_group = gtk_action_group_new (group_name);
+	gtk_action_group_set_translation_domain (action_group, domain);
+	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+	g_object_unref (action_group);
+
+	return action_group;
+}
+
+GtkActionGroup *
+e_attachment_view_get_action_group (EAttachmentView *view,
+                                    const gchar *group_name)
+{
+	GtkUIManager *ui_manager;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+	g_return_val_if_fail (group_name != NULL, NULL);
+
+	ui_manager = e_attachment_view_get_ui_manager (view);
+
+	return e_lookup_action_group (ui_manager, group_name);
+}
+
+GtkWidget *
+e_attachment_view_get_popup_menu (EAttachmentView *view)
+{
+	GtkUIManager *ui_manager;
+	GtkWidget *menu;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+
+	ui_manager = e_attachment_view_get_ui_manager (view);
+	menu = gtk_ui_manager_get_widget (ui_manager, "/context");
+	g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
+
+	return menu;
+}
+
+GtkUIManager *
+e_attachment_view_get_ui_manager (EAttachmentView *view)
+{
+	EAttachmentViewPrivate *priv;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+
+	priv = e_attachment_view_get_private (view);
+
+	return priv->ui_manager;
+}
+
+GtkAction *
+e_attachment_view_recent_action_new (EAttachmentView *view,
+                                     const gchar *action_name,
+                                     const gchar *action_label)
+{
+	GtkAction *action;
+	GtkRecentChooser *chooser;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+	g_return_val_if_fail (action_name != NULL, NULL);
+
+	action = gtk_recent_action_new (
+		action_name, action_label, NULL, NULL);
+	gtk_recent_action_set_show_numbers (GTK_RECENT_ACTION (action), TRUE);
+
+	chooser = GTK_RECENT_CHOOSER (action);
+	gtk_recent_chooser_set_show_icons (chooser, TRUE);
+	gtk_recent_chooser_set_show_not_found (chooser, FALSE);
+	gtk_recent_chooser_set_show_private (chooser, FALSE);
+	gtk_recent_chooser_set_show_tips (chooser, TRUE);
+	gtk_recent_chooser_set_sort_type (chooser, GTK_RECENT_SORT_MRU);
+
+	g_signal_connect (
+		action, "item-activated",
+		G_CALLBACK (action_recent_cb), view);
+
+	return action;
+}
+
+void
+e_attachment_view_show_popup_menu (EAttachmentView *view,
+                                   GdkEventButton *event,
+                                   GtkMenuPositionFunc func,
+                                   gpointer user_data)
+{
+	GtkWidget *menu;
+
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+	e_attachment_view_update_actions (view);
+
+	menu = e_attachment_view_get_popup_menu (view);
+
+	if (event != NULL)
+		gtk_menu_popup (
+			GTK_MENU (menu), NULL, NULL, func,
+			user_data, event->button, event->time);
+	else
+		gtk_menu_popup (
+			GTK_MENU (menu), NULL, NULL, func,
+			user_data, 0, gtk_get_current_event_time ());
+}
+
+void
+e_attachment_view_update_actions (EAttachmentView *view)
+{
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+	g_signal_emit (view, signals[UPDATE_ACTIONS], 0);
+}
Index: widgets/misc/e-attachment-handler.c
===================================================================
--- widgets/misc/e-attachment-handler.c	(revision 0)
+++ widgets/misc/e-attachment-handler.c	(revision 0)
@@ -0,0 +1,191 @@
+/*
+ * 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);
+}
+
+GdkDragAction
+e_attachment_handler_get_drag_actions (EAttachmentHandler *handler)
+{
+	EAttachmentHandlerClass *class;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_HANDLER (handler), 0);
+
+	class = E_ATTACHMENT_HANDLER_GET_CLASS (handler);
+
+	if (class->get_drag_actions != NULL)
+		return class->get_drag_actions (handler);
+
+	return 0;
+}
+
+const GtkTargetEntry *
+e_attachment_handler_get_target_table (EAttachmentHandler *handler,
+                                       guint *n_targets)
+{
+	EAttachmentHandlerClass *class;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_HANDLER (handler), NULL);
+
+	class = E_ATTACHMENT_HANDLER_GET_CLASS (handler);
+
+	if (class->get_target_table != NULL)
+		return class->get_target_table (handler, n_targets);
+
+	if (n_targets != NULL)
+		*n_targets = 0;
+
+	return NULL;
+}
Index: widgets/misc/e-attachment-view.h
===================================================================
--- widgets/misc/e-attachment-view.h	(revision 0)
+++ widgets/misc/e-attachment-view.h	(revision 0)
@@ -0,0 +1,227 @@
+/*
+ * e-attachment-view.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_VIEW_H
+#define E_ATTACHMENT_VIEW_H
+
+#include <gtk/gtk.h>
+#include <widgets/misc/e-attachment-store.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_VIEW \
+	(e_attachment_view_get_type ())
+#define E_ATTACHMENT_VIEW(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_ATTACHMENT_VIEW, EAttachmentView))
+#define E_ATTACHMENT_VIEW_IFACE(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_ATTACHMENT_VIEW, EAttachmentViewIface))
+#define E_IS_ATTACHMENT_VIEW(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_ATTACHMENT_VIEW))
+#define E_IS_ATTACHMENT_VIEW_IFACE(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_ATTACHMENT_VIEW))
+#define E_ATTACHMENT_VIEW_GET_IFACE(obj) \
+	(G_TYPE_INSTANCE_GET_INTERFACE \
+	((obj), E_TYPE_ATTACHMENT_VIEW, EAttachmentViewIface))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentView EAttachmentView;
+typedef struct _EAttachmentViewIface EAttachmentViewIface;
+typedef struct _EAttachmentViewPrivate EAttachmentViewPrivate;
+
+struct _EAttachmentViewIface {
+	GTypeInterface parent_iface;
+
+	/* General Methods */
+	EAttachmentViewPrivate *
+			(*get_private)		(EAttachmentView *view);
+	EAttachmentStore *
+			(*get_store)		(EAttachmentView *view);
+
+	/* Selection Methods */
+	GtkTreePath *	(*get_path_at_pos)	(EAttachmentView *view,
+						 gint x,
+						 gint y);
+	GList *		(*get_selected_paths)	(EAttachmentView *view);
+	gboolean	(*path_is_selected)	(EAttachmentView *view,
+						 GtkTreePath *path);
+	void		(*select_path)		(EAttachmentView *view,
+						 GtkTreePath *path);
+	void		(*unselect_path)	(EAttachmentView *view,
+						 GtkTreePath *path);
+	void		(*select_all)		(EAttachmentView *view);
+	void		(*unselect_all)		(EAttachmentView *view);
+
+	/* Drag and Drop Methods */
+	void		(*drag_source_set)	(EAttachmentView *view,
+						 GdkModifierType start_button_mask,
+						 const GtkTargetEntry *targets,
+						 gint n_targets,
+						 GdkDragAction actions);
+	void		(*drag_dest_set)	(EAttachmentView *view,
+						 const GtkTargetEntry *targets,
+						 gint n_targets,
+						 GdkDragAction actions);
+	void		(*drag_source_unset)	(EAttachmentView *view);
+	void		(*drag_dest_unset)	(EAttachmentView *view);
+
+	/* Signals */
+	void		(*update_actions)	(EAttachmentView *view);
+};
+
+struct _EAttachmentViewPrivate {
+
+	/* Attachment Handlers */
+	GPtrArray *handlers;
+
+	/* Drag Destination */
+	GtkTargetList *target_list;
+	GdkDragAction drag_actions;
+
+	/* Popup Menu Management */
+	GtkUIManager *ui_manager;
+	guint merge_id;
+
+	guint editable : 1;
+};
+
+GType		e_attachment_view_get_type	(void);
+
+void		e_attachment_view_init		(EAttachmentView *view);
+void		e_attachment_view_dispose	(EAttachmentView *view);
+void		e_attachment_view_finalize	(EAttachmentView *view);
+
+EAttachmentViewPrivate *
+		e_attachment_view_get_private	(EAttachmentView *view);
+EAttachmentStore *
+		e_attachment_view_get_store	(EAttachmentView *view);
+gboolean	e_attachment_view_get_editable	(EAttachmentView *view);
+void		e_attachment_view_set_editable	(EAttachmentView *view,
+						 gboolean editable);
+GtkTargetList *	e_attachment_view_get_target_list
+						(EAttachmentView *view);
+GdkDragAction	e_attachment_view_get_drag_actions
+						(EAttachmentView *view);
+GList *		e_attachment_view_get_selected_attachments
+						(EAttachmentView *view);
+void		e_attachment_view_open_path	(EAttachmentView *view,
+						 GtkTreePath *path,
+						 GAppInfo *app_info);
+void		e_attachment_view_remove_selected
+						(EAttachmentView *view,
+						 gboolean select_next);
+
+/* 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
+						(EAttachmentView *view,
+						 gint x,
+						 gint y);
+GList *		e_attachment_view_get_selected_paths
+						(EAttachmentView *view);
+gboolean	e_attachment_view_path_is_selected
+						(EAttachmentView *view,
+						 GtkTreePath *path);
+void		e_attachment_view_select_path	(EAttachmentView *view,
+						 GtkTreePath *path);
+void		e_attachment_view_unselect_path	(EAttachmentView *view,
+						 GtkTreePath *path);
+void		e_attachment_view_select_all	(EAttachmentView *view);
+void		e_attachment_view_unselect_all	(EAttachmentView *view);
+void		e_attachment_view_sync_selection(EAttachmentView *view,
+						 EAttachmentView *target);
+
+/* Drag Source Support */
+void		e_attachment_view_drag_source_set
+						(EAttachmentView *view);
+void		e_attachment_view_drag_source_unset
+						(EAttachmentView *view);
+void		e_attachment_view_drag_begin	(EAttachmentView *view,
+						 GdkDragContext *context);
+void		e_attachment_view_drag_end	(EAttachmentView *view,
+						 GdkDragContext *context);
+void		e_attachment_view_drag_data_get	(EAttachmentView *view,
+						 GdkDragContext *context,
+						 GtkSelectionData *selection,
+						 guint info,
+						 guint time);
+
+/* Drag Destination Support */
+void		e_attachment_view_drag_dest_set	(EAttachmentView *view);
+void		e_attachment_view_drag_dest_unset
+						(EAttachmentView *view);
+gboolean	e_attachment_view_drag_motion	(EAttachmentView *view,
+						 GdkDragContext *context,
+						 gint x,
+						 gint y,
+						 guint time);
+gboolean	e_attachment_view_drag_drop	(EAttachmentView *view,
+						 GdkDragContext *context,
+						 gint x,
+						 gint y,
+						 guint time);
+void		e_attachment_view_drag_data_received
+						(EAttachmentView *view,
+						 GdkDragContext *context,
+						 gint x,
+						 gint y,
+						 GtkSelectionData *selection,
+						 guint info,
+						 guint time);
+
+/* Popup Menu Management */
+GtkAction *	e_attachment_view_get_action	(EAttachmentView *view,
+						 const gchar *action_name);
+GtkActionGroup *e_attachment_view_add_action_group
+						(EAttachmentView *view,
+						 const gchar *group_name);
+GtkActionGroup *e_attachment_view_get_action_group
+						(EAttachmentView *view,
+						 const gchar *group_name);
+GtkWidget *	e_attachment_view_get_popup_menu(EAttachmentView *view);
+GtkUIManager *	e_attachment_view_get_ui_manager(EAttachmentView *view);
+GtkAction *	e_attachment_view_recent_action_new
+						(EAttachmentView *view,
+						 const gchar *action_name,
+						 const gchar *action_label);
+void		e_attachment_view_show_popup_menu
+						(EAttachmentView *view,
+						 GdkEventButton *event,
+						 GtkMenuPositionFunc func,
+						 gpointer user_data);
+void		e_attachment_view_update_actions(EAttachmentView *view);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_VIEW_H */
Index: widgets/misc/Makefile.am
===================================================================
--- widgets/misc/Makefile.am	(revision 37527)
+++ widgets/misc/Makefile.am	(working copy)
@@ -37,7 +37,15 @@ widgetsinclude_HEADERS =			\
 	e-account-combo-box.h			\
 	e-activity-handler.h			\
 	e-attachment.h				\
-	e-attachment-bar.h			\
+	e-attachment-button.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			\
+	e-attachment-tree-view.h		\
+	e-attachment-view.h			\
 	e-spinner.c				\
 	e-spinner.h				\
 	e-calendar.h				\
@@ -88,7 +96,15 @@ libemiscwidgets_la_SOURCES =			\
 	e-activity-handler.c			\
 	e-calendar.c				\
 	e-attachment.c				\
-	e-attachment-bar.c			\
+	e-attachment-button.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			\
+	e-attachment-tree-view.c		\
+	e-attachment-view.c			\
 	e-calendar-item.c			\
 	e-cell-date-edit.c			\
 	e-cell-percent.c			\
Index: widgets/misc/e-attachment-handler.h
===================================================================
--- widgets/misc/e-attachment-handler.h	(revision 0)
+++ widgets/misc/e-attachment-handler.h	(revision 0)
@@ -0,0 +1,78 @@
+/*
+ * 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;
+
+	GdkDragAction	(*get_drag_actions)	(EAttachmentHandler *handler);
+	const GtkTargetEntry *
+			(*get_target_table)	(EAttachmentHandler *handler,
+						 guint *n_targets);
+};
+
+GType		e_attachment_handler_get_type	(void);
+EAttachmentView *
+		e_attachment_handler_get_view	(EAttachmentHandler *handler);
+GdkDragAction	e_attachment_handler_get_drag_actions
+						(EAttachmentHandler *handler);
+const GtkTargetEntry *
+		e_attachment_handler_get_target_table
+						(EAttachmentHandler *handler,
+						 guint *n_targets);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_HANDLER_H */
Index: calendar/gui/dialogs/comp-editor.c
===================================================================
--- calendar/gui/dialogs/comp-editor.c	(revision 37527)
+++ calendar/gui/dialogs/comp-editor.c	(working copy)
@@ -63,8 +63,9 @@
 #include "../e-cal-popup.h"
 #include "../calendar-config-keys.h"
 #include "cal-attachment-select-file.h"
+#include "widgets/misc/e-attachment-view.h"
+#include "widgets/misc/e-attachment-paned.h"
 
-#include "e-attachment-bar.h"
 #include "misc/e-expander.h"
 #include "e-util/e-error.h"
 
@@ -95,12 +96,7 @@ struct _CompEditorPrivate {
 	GtkNotebook *notebook;
 
 	/* Attachment handling */
-	GtkWidget *attachment_bar;
-	GtkWidget *attachment_scrolled_window;
-	GtkWidget *attachment_expander;
-	GtkWidget *attachment_expander_label;
-	GtkWidget *attachment_expander_icon;
-	GtkWidget *attachment_expander_num;
+	GtkWidget *attachment_view;
 
 	/* Manages menus and toolbars */
 	GtkUIManager *manager;
@@ -169,7 +165,6 @@ static const gchar *ui =
 "</ui>";
 
 static void comp_editor_show_help (CompEditor *editor);
-static void setup_widgets (CompEditor *editor);
 
 static void real_edit_comp (CompEditor *editor, ECalComponent *comp);
 static gboolean real_send_comp (CompEditor *editor, ECalComponentItipMethod method, gboolean strip_alarms);
@@ -180,441 +175,63 @@ static void page_dates_changed_cb (CompE
 
 static void obj_modified_cb (ECal *client, GList *objs, gpointer data);
 static void obj_removed_cb (ECal *client, GList *uids, gpointer data);
-static gboolean open_attachment (EAttachmentBar *bar, CompEditor *editor);
 
 G_DEFINE_TYPE (CompEditor, comp_editor, GTK_TYPE_WINDOW)
 
 enum {
-	DND_TYPE_MESSAGE_RFC822,
-	DND_TYPE_X_UID_LIST,
-	DND_TYPE_TEXT_URI_LIST,
-	DND_TYPE_NETSCAPE_URL,
-	DND_TYPE_TEXT_VCARD,
-	DND_TYPE_TEXT_CALENDAR,
-};
-
-static GtkTargetEntry drop_types[] = {
-	{ "message/rfc822", 0, DND_TYPE_MESSAGE_RFC822 },
-	{ "x-uid-list", 0, DND_TYPE_X_UID_LIST },
-	{ "text/uri-list", 0, DND_TYPE_TEXT_URI_LIST },
-	{ "_NETSCAPE_URL", 0, DND_TYPE_NETSCAPE_URL },
-	{ "text/x-vcard", 0, DND_TYPE_TEXT_VCARD },
-	{ "text/calendar", 0, DND_TYPE_TEXT_CALENDAR },
-};
-
-#define num_drop_types (sizeof (drop_types) / sizeof (drop_types[0]))
-
-static struct {
-	char *target;
-	GdkAtom atom;
-	guint32 actions;
-} drag_info[] = {
-	{ "message/rfc822", NULL, GDK_ACTION_COPY },
-	{ "x-uid-list", NULL, GDK_ACTION_ASK|GDK_ACTION_MOVE|GDK_ACTION_COPY },
-	{ "text/uri-list", NULL, GDK_ACTION_COPY },
-	{ "_NETSCAPE_URL", NULL, GDK_ACTION_COPY },
-	{ "text/x-vcard", NULL, GDK_ACTION_COPY },
-	{ "text/calendar", NULL, GDK_ACTION_COPY },
-};
-
-enum {
 	OBJECT_CREATED,
 	LAST_SIGNAL
 };
 
 static guint comp_editor_signals[LAST_SIGNAL] = { 0 };
 
-static void
-attach_message(CompEditor *editor, CamelMimeMessage *msg)
-{
-	CamelMimePart *mime_part;
-	const char *subject;
-	guint i;
-	char *filename = NULL;
-
-	mime_part = camel_mime_part_new();
-	camel_mime_part_set_disposition(mime_part, "inline");
-	subject = camel_mime_message_get_subject(msg);
-	if (subject) {
-		char *desc = g_strdup_printf(_("Attached message - %s"), subject);
-
-		camel_mime_part_set_description(mime_part, desc);
-		g_free(desc);
-	} else
-		camel_mime_part_set_description(mime_part, _("Attached message"));
-
-	i = e_attachment_bar_get_num_attachments (E_ATTACHMENT_BAR (editor->priv->attachment_bar));
-	i++;
-	filename = g_strdup_printf ("email%d",i);
-	camel_mime_part_set_filename (mime_part, filename);
-
-	camel_medium_set_content_object((CamelMedium *)mime_part, (CamelDataWrapper *)msg);
-	camel_mime_part_set_content_type(mime_part, "message/rfc822");
-	e_attachment_bar_attach_mime_part(E_ATTACHMENT_BAR(editor->priv->attachment_bar), mime_part);
-	camel_object_unref(mime_part);
-	g_free (filename);
-}
-
-struct _drop_data {
-	CompEditor *editor;
-
-	GdkDragContext *context;
-	/* Only selection->data and selection->length are valid */
-	GtkSelectionData *selection;
-
-	guint32 action;
-	guint info;
-	guint time;
-
-	unsigned int move:1;
-	unsigned int moved:1;
-	unsigned int aborted:1;
-};
-
-static void
-drop_action(CompEditor *editor, GdkDragContext *context, guint32 action, GtkSelectionData *selection, guint info, guint time)
-{
-	char *tmp, *str, **urls;
-	CamelMimePart *mime_part;
-	CamelStream *stream;
-	CamelURL *url;
-	CamelMimeMessage *msg;
-	char *content_type;
-	int i, success=FALSE, delete=FALSE;
-
-	switch (info) {
-	case DND_TYPE_MESSAGE_RFC822:
-		d(printf ("dropping a message/rfc822\n"));
-		/* write the message(s) out to a CamelStream so we can use it */
-		stream = camel_stream_mem_new ();
-		camel_stream_write (stream, (char *)selection->data, selection->length);
-		camel_stream_reset (stream);
-
-		msg = camel_mime_message_new ();
-		if (camel_data_wrapper_construct_from_stream((CamelDataWrapper *)msg, stream) != -1) {
-			attach_message(editor, msg);
-			success = TRUE;
-			delete = action == GDK_ACTION_MOVE;
-		}
-
-		camel_object_unref(msg);
-		camel_object_unref(stream);
-		break;
-	case DND_TYPE_TEXT_URI_LIST:
-	case DND_TYPE_NETSCAPE_URL:
-		d(printf ("dropping a text/uri-list\n"));
-		tmp = g_strndup ((char *)selection->data, selection->length);
-		urls = g_strsplit (tmp, "\n", 0);
-		g_free (tmp);
-
-		for (i = 0; urls[i] != NULL; i++) {
-			str = g_strstrip (urls[i]);
-			if (urls[i][0] == '#')
-				continue;
-
-			if (!g_ascii_strncasecmp (str, "mailto:", 7)) {
-				/* TODO does not handle mailto now */
-			} else {
-				url = camel_url_new (str, NULL);
-
-				if (url == NULL)
-					continue;
-
-				if (!g_ascii_strcasecmp (url->protocol, "file"))
-					e_attachment_bar_attach
-						(E_ATTACHMENT_BAR (editor->priv->attachment_bar),
-					 	url->path,
-						"attachment");
-				else
-					e_attachment_bar_attach_remote_file
-						(E_ATTACHMENT_BAR (editor->priv->attachment_bar),
-						 str, "attachment");
-
-				camel_url_free (url);
-			}
-		}
-
-		g_strfreev (urls);
-		success = TRUE;
-		break;
-	case DND_TYPE_TEXT_VCARD:
-	case DND_TYPE_TEXT_CALENDAR:
-		content_type = gdk_atom_name (selection->type);
-		d(printf ("dropping a %s\n", content_type));
-
-		mime_part = camel_mime_part_new ();
-		camel_mime_part_set_content (mime_part, (char *)selection->data, selection->length, content_type);
-		camel_mime_part_set_disposition (mime_part, "inline");
-
-		e_attachment_bar_attach_mime_part
-			(E_ATTACHMENT_BAR (editor->priv->attachment_bar),
-			 mime_part);
-
-		camel_object_unref (mime_part);
-		g_free (content_type);
-
-		success = TRUE;
-		break;
-	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((char *)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;
-					attach_message(editor, msg);
-				} else {
-					CamelMultipart *mp = camel_multipart_new();
-					char *desc;
-					char *filename = NULL;
-					guint num;
-
-					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);
-
-					num = e_attachment_bar_get_num_attachments (E_ATTACHMENT_BAR (editor->priv->attachment_bar));
-					num++;
-					filename = g_strdup_printf ("email%d", num);
-					camel_mime_part_set_filename (mime_part, filename);
-
-					e_attachment_bar_attach_mime_part
-						(E_ATTACHMENT_BAR(editor->priv->attachment_bar), mime_part);
-					camel_object_unref(mime_part);
-					camel_object_unref(mp);
-					g_free (filename);
-				}
-				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 *)editor, "mail-editor: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 *)editor, "mail-editor:attach-nomessages",
-					    (char *)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;
-	}
-
-	printf("Drag finished, success %d delete %d\n", success, delete);
-
-	gtk_drag_finish(context, success, delete, time);
-}
-
-static void
-drop_popup_copy (EPopup *ep, EPopupItem *item, void *data)
-{
-	struct _drop_data *m = data;
-	drop_action(m->editor, m->context, GDK_ACTION_COPY, m->selection, m->info, m->time);
-}
-
-static void
-drop_popup_move (EPopup *ep, EPopupItem *item, void *data)
-{
-	struct _drop_data *m = data;
-	drop_action(m->editor, m->context, GDK_ACTION_MOVE, m->selection, m->info, m->time);
-}
-
-static void
-drop_popup_cancel(EPopup *ep, EPopupItem *item, void *data)
-{
-	struct _drop_data *m = data;
-	gtk_drag_finish(m->context, FALSE, FALSE, m->time);
-}
-
-static EPopupItem drop_popup_menu[] = {
-	{ E_POPUP_ITEM, "00.emc.02", N_("_Copy"), drop_popup_copy, NULL, "mail-copy", 0 },
-	{ E_POPUP_ITEM, "00.emc.03", N_("_Move"), drop_popup_move, NULL, "mail-move", 0 },
-	{ E_POPUP_BAR, "10.emc" },
-	{ E_POPUP_ITEM, "99.emc.00", N_("Cancel _Drag"), drop_popup_cancel, NULL, NULL, 0 },
-};
-
-static void
-drop_popup_free(EPopup *ep, GSList *items, void *data)
-{
-	struct _drop_data *m = data;
-
-	g_slist_free(items);
-
-	g_object_unref(m->context);
-	g_object_unref(m->editor);
-	g_free(m->selection->data);
-	g_free(m->selection);
-	g_free(m);
-}
-
-static void
-drag_data_received (CompEditor *editor, GdkDragContext *context,
-		    int x, int y, GtkSelectionData *selection,
-		    guint info, guint time)
-{
-	if (selection->data == NULL || selection->length == -1)
-		return;
-
-	if (context->action == GDK_ACTION_ASK) {
-		ECalPopup *ecp;
-		GSList *menus = NULL;
-		GtkMenu *menu;
-		int i;
-		struct _drop_data *m;
-
-		m = g_malloc0(sizeof(*m));
-		m->context = context;
-		g_object_ref(context);
-		m->editor = editor;
-		g_object_ref(editor);
-		m->action = context->action;
-		m->info = info;
-		m->time = time;
-		m->selection = g_malloc0(sizeof(*m->selection));
-		m->selection->data = g_malloc(selection->length);
-		memcpy(m->selection->data, selection->data, selection->length);
-		m->selection->length = selection->length;
-
-		ecp = e_cal_popup_new("org.gnome.evolution.calendar.editor.popup.drop");
-		for (i=0;i<sizeof(drop_popup_menu)/sizeof(drop_popup_menu[0]);i++)
-			menus = g_slist_append(menus, &drop_popup_menu[i]);
-
-		e_popup_add_items((EPopup *)ecp, menus, NULL, drop_popup_free, m);
-		menu = e_popup_create_menu_once((EPopup *)ecp, NULL, 0);
-		gtk_menu_popup(menu, NULL, NULL, NULL, NULL, 0, time);
-	} else {
-		drop_action(editor, context, context->action, selection, info, time);
-	}
-}
-
-static gboolean
-drag_motion(GObject *o, GdkDragContext *context, gint x, gint y, guint time, CompEditor *editor)
-{
-	GList *targets;
-	GdkDragAction action, actions = 0;
-
-	for (targets = context->targets; targets; targets = targets->next) {
-		int i;
-
-		for (i=0;i<sizeof(drag_info)/sizeof(drag_info[0]);i++)
-			if (targets->data == (void *)drag_info[i].atom)
-				actions |= drag_info[i].actions;
-	}
-
-	actions &= context->actions;
-	action = context->suggested_action;
-	/* we default to copy */
-	if (action == GDK_ACTION_ASK && (actions & (GDK_ACTION_MOVE|GDK_ACTION_COPY)) != (GDK_ACTION_MOVE|GDK_ACTION_COPY))
-		action = GDK_ACTION_COPY;
-
-	gdk_drag_status(context, action, time);
-
-	return action != 0;
-}
-
-static void
-add_to_bar (CompEditor *editor, GPtrArray *file_list, int is_inline)
-{
-	CompEditorPrivate *priv = editor->priv;
-	int i;
-
-	for (i = 0; i < file_list->len; i++) {
-		CamelURL *url;
-
-		if (!(url = camel_url_new (file_list->pdata[i], NULL)))
-			continue;
-
-		if (!g_ascii_strcasecmp (url->protocol, "file")) {
-			e_attachment_bar_attach((EAttachmentBar *)priv->attachment_bar, url->path, is_inline ? "inline" : "attachment");
-		} else {
-			e_attachment_bar_attach_remote_file ((EAttachmentBar *)priv->attachment_bar, file_list->pdata[i], is_inline ? "inline" : "attachment");
-		}
-
-		camel_url_free (url);
-	}
-}
-
 static GSList *
 get_attachment_list (CompEditor *editor)
 {
-	GSList *parts = NULL, *list = NULL, *p = NULL;
+	EAttachmentStore *store;
+	EAttachmentView *view;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	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;
 	int ticker=0;
+
 	e_cal_component_get_uid (editor->priv->comp, &comp_uid);
 
-	parts = e_attachment_bar_get_parts((EAttachmentBar *)editor->priv->attachment_bar);
+	view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
+	store = e_attachment_view_get_store (view);
 
-	for (p = parts; p!=NULL ; p = p->next) {
+	model = GTK_TREE_MODEL (store);
+	valid = gtk_tree_model_get_iter_first (model, &iter);
+
+	while (valid) {
+		EAttachment *attachment;
 		CamelDataWrapper *wrapper;
+		CamelMimePart *mime_part;
 		CamelStream *stream;
 		char *attach_file_url;
 		char *safe_fname, *utf8_safe_fname;
 		char *filename;
+		gint column_id;
+
+		column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+		gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
+		mime_part = e_attachment_get_mime_part (attachment);
+		g_object_unref (attachment);
+
+		valid = gtk_tree_model_iter_next (model, &iter);
 
-		wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (p->data));
+		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
 		 * as a mime part file into the directory denoting the
 		 * calendar source */
-		utf8_safe_fname = camel_file_util_safe_filename (camel_mime_part_get_filename ((CamelMimePart *) p->data));
+		utf8_safe_fname = camel_file_util_safe_filename (camel_mime_part_get_filename (mime_part));
 
 		/* It is absolutely fine to get a NULL from the filename of 
 		 * mime part. We assume that it is named "Attachment"
@@ -993,20 +610,21 @@ static void
 action_attach_cb (GtkAction *action,
                   CompEditor *editor)
 {
-	GPtrArray *file_list;
-	gboolean is_inline = FALSE;
-	int i;
-
-	file_list = comp_editor_select_file_attachments (editor, &is_inline);
+	EAttachmentStore *store;
+	EAttachmentView *view;
 
-	if (file_list) {
-		add_to_bar (editor, file_list, is_inline);
+	view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
+	store = e_attachment_view_get_store (view);
 
-		for (i = 0; i < file_list->len; i++)
-			g_free (file_list->pdata[i]);
+	e_attachment_store_run_load_dialog (store, GTK_WINDOW (editor));
+}
 
-		g_ptr_array_free (file_list, TRUE);
-	}
+static void
+action_classification_cb (GtkRadioAction *action,
+                          GtkRadioAction *current,
+                          CompEditor *editor)
+{
+	comp_editor_set_changed (editor, TRUE);
 }
 
 static void
@@ -1132,12 +750,17 @@ action_save_cb (GtkAction *action,
                 CompEditor *editor)
 {
 	CompEditorPrivate *priv = editor->priv;
+	EAttachmentStore *store;
+	EAttachmentView *view;
 	ECalComponentText text;
 	gboolean delegated = FALSE;
 	gboolean read_only, correct = FALSE;
 	ECalComponent *comp;
 
-	if (e_attachment_bar_get_download_count (E_ATTACHMENT_BAR (editor->priv->attachment_bar)) ){
+	view = E_ATTACHMENT_VIEW (priv->attachment_view);
+	store = e_attachment_view_get_store (view);
+
+	if (e_attachment_store_get_num_loading (store) > 0) {
 		gboolean response = 1;
 	/*FIXME: Cannot use mail functions from calendar!!!! */
 #if 0
@@ -1693,15 +1316,89 @@ comp_editor_map (GtkWidget *widget)
 	GTK_WIDGET_CLASS (comp_editor_parent_class)->map (widget);
 }
 
+static gboolean
+comp_editor_delete_event (GtkWidget *widget,
+                          GdkEventAny *event)
+{
+	CompEditor *editor;
+
+	editor = COMP_EDITOR (widget);
+
+	commit_all_fields (editor);
+
+	if (prompt_and_save_changes (editor, TRUE))
+		close_dialog (editor);
+
+	return TRUE;
+}
+
+static gboolean
+comp_editor_key_press_event (GtkWidget *widget,
+                             GdkEventKey *event)
+{
+	CompEditor *editor;
+
+	editor = COMP_EDITOR (widget);
+
+	if (event->keyval == GDK_Escape) {
+		commit_all_fields (editor);
+
+		if (prompt_and_save_changes (editor, TRUE))
+			close_dialog (editor);
+
+		return TRUE;
+	}
+
+	/* Chain up to parent's key_press_event() method. */
+	return GTK_WIDGET_CLASS (comp_editor_parent_class)->
+		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)
 {
 	GObjectClass *object_class;
 	GtkWidgetClass *widget_class;
-	int i;
-
-	for (i = 0; i < G_N_ELEMENTS (drag_info); i++)
-		drag_info[i].atom = gdk_atom_intern(drag_info[i].target, FALSE);
 
 	g_type_class_add_private (class, sizeof (CompEditorPrivate));
 
@@ -1713,6 +1410,10 @@ comp_editor_class_init (CompEditorClass 
 
 	widget_class = GTK_WIDGET_CLASS (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;
@@ -1775,19 +1476,18 @@ comp_editor_class_init (CompEditorClass 
 }
 
 static void
-classification_changed_cb (GtkRadioAction *action, GtkRadioAction *current, CompEditor *editor)
-{
-	g_return_if_fail (IS_COMP_EDITOR (editor));
-
-	comp_editor_set_changed (editor, TRUE);
-}
-
-static void
 comp_editor_init (CompEditor *editor)
 {
 	CompEditorPrivate *priv;
+	EAttachmentView *view;
+	GdkDragAction drag_actions;
+	GtkTargetList *target_list;
+	GtkTargetEntry *targets;
 	GtkActionGroup *action_group;
 	GtkAction *action;
+	GtkWidget *container;
+	GtkWidget *widget;
+	gint n_targets;
 	GError *error = NULL;
 
 	editor->priv = priv = COMP_EDITOR_GET_PRIVATE (editor);
@@ -1796,17 +1496,18 @@ comp_editor_init (CompEditor *editor)
 	priv->changed = FALSE;
 	priv->needs_send = FALSE;
 	priv->mod = CALOBJ_MOD_ALL;
- 	priv->existing_org = FALSE;
- 	priv->user_org = FALSE;
- 	priv->warned = FALSE;
+	priv->existing_org = FALSE;
+	priv->user_org = FALSE;
+	priv->warned = FALSE;
 	priv->is_group_item = FALSE;
 
-	priv->attachment_bar = e_attachment_bar_new (NULL);
 	priv->manager = gtk_ui_manager_new ();
 
-        gtk_window_add_accel_group (
-                GTK_WINDOW (editor),
-                gtk_ui_manager_get_accel_group (priv->manager));
+	gtk_window_add_accel_group (
+		GTK_WINDOW (editor),
+		gtk_ui_manager_get_accel_group (priv->manager));
+
+	/* Setup Action Groups */
 
 	action_group = gtk_action_group_new ("core");
 	gtk_action_group_set_translation_domain (
@@ -1831,11 +1532,7 @@ comp_editor_init (CompEditor *editor)
 		action_group, classification_radio_entries,
 		G_N_ELEMENTS (classification_radio_entries),
 		E_CAL_COMPONENT_CLASS_PUBLIC,
-		G_CALLBACK (classification_changed_cb), editor);
-	action = e_attachment_bar_recent_action_new (
-		E_ATTACHMENT_BAR (priv->attachment_bar),
-		"attach-recent", _("Recent _Documents"));
-	gtk_action_group_add_action (action_group, action);
+		G_CALLBACK (action_classification_cb), editor);
 	gtk_ui_manager_insert_action_group (
 		priv->manager, action_group, 0);
 	g_object_unref (action_group);
@@ -1865,14 +1562,62 @@ comp_editor_init (CompEditor *editor)
 		g_error_free (error);
 	}
 
-	setup_widgets (editor);
+	/* Setup Widgets */
+
+	container = GTK_WIDGET (editor);
+
+	widget = gtk_vbox_new (FALSE, 0);
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = comp_editor_get_managed_widget (editor, "/main-menu");
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	widget = comp_editor_get_managed_widget (editor, "/main-toolbar");
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	widget = e_attachment_paned_new ();
+	gtk_container_set_border_width (GTK_CONTAINER (widget), 6);
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	priv->attachment_view = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	container = e_attachment_paned_get_content_area (
+		E_ATTACHMENT_PANED (priv->attachment_view));
+
+	widget = gtk_notebook_new ();
+	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE);
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	priv->notebook = GTK_NOTEBOOK (widget);
+	gtk_widget_show (widget);
+
+	/* Add a GtkRecentAction to the "individual" action group. */
+	action_group = comp_editor_get_action_group (editor, "individual");
+	view = E_ATTACHMENT_VIEW (priv->attachment_view);
+	action = e_attachment_view_recent_action_new (
+		view, "attach-recent", _("Recent _Documents"));
+	gtk_action_group_add_action (action_group, action);
+	g_object_unref (action);
+
+	/* Drag-and-Drop Support */
 
-	/* 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), editor);
+	target_list = e_attachment_view_get_target_list (view);
+	drag_actions = e_attachment_view_get_drag_actions (view);
 
-	gtk_window_set_type_hint (GTK_WINDOW (editor), GDK_WINDOW_TYPE_HINT_NORMAL);
+	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_target_table_free (targets, n_targets);
+
+	gtk_window_set_type_hint (
+		GTK_WINDOW (editor), GDK_WINDOW_TYPE_HINT_NORMAL);
 }
 
 static gboolean
@@ -1925,402 +1670,17 @@ prompt_and_save_changes (CompEditor *edi
 	}
 }
 
-static int
-delete_event_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
-{
-	CompEditor *editor = COMP_EDITOR (data);
-
-	commit_all_fields (editor);
-
-	if (prompt_and_save_changes (editor, TRUE))
-		close_dialog (editor);
-
-	return TRUE;
-}
-
 static void
-attachment_bar_changed_cb (EAttachmentBar *bar,
-			   void *data)
+attachment_store_changed_cb (CompEditor *editor)
 {
-	CompEditor *editor = COMP_EDITOR (data);
-
-	guint attachment_num = e_attachment_bar_get_num_attachments (
-		E_ATTACHMENT_BAR (editor->priv->attachment_bar));
-	if (attachment_num) {
-		gchar *num_text = g_strdup_printf (
-			ngettext ("<b>%d</b> Attachment", "<b>%d</b> Attachments", attachment_num),
-			attachment_num);
-		gtk_label_set_markup (GTK_LABEL (editor->priv->attachment_expander_num),
-				      num_text);
-		g_free (num_text);
-
-		gtk_widget_show (editor->priv->attachment_expander_icon);
-		e_expander_set_expanded(E_EXPANDER(editor->priv->attachment_expander),TRUE);
-
-	} else {
-		gtk_label_set_text (GTK_LABEL (editor->priv->attachment_expander_num), "");
-		gtk_widget_hide (editor->priv->attachment_expander_icon);
-		e_expander_set_expanded(E_EXPANDER(editor->priv->attachment_expander),FALSE);
-	}
-
-
 	/* Mark the editor as changed so it prompts about unsaved
            changes on close */
 	comp_editor_set_changed (editor, TRUE);
-
-}
-
-static void
-attachment_expander_activate_cb (EExpander *expander,
-				 void *data)
-{
-	CompEditor *editor = COMP_EDITOR (data);
-	gboolean show = e_expander_get_expanded (expander);
-
-	/* Update the expander label */
-	if (show)
-		gtk_label_set_text_with_mnemonic (GTK_LABEL (editor->priv->attachment_expander_label),
-					          _("Hide Attachment _Bar"));
-	else
-		gtk_label_set_text_with_mnemonic (GTK_LABEL (editor->priv->attachment_expander_label),
-						  _("Show Attachment _Bar"));
-}
-
-static gboolean
-open_attachment (EAttachmentBar *bar, CompEditor *editor)
-{
-	GnomeIconList *icon_list;
-	GList *p;
-	int num;
-	char *attach_file_url;
-
-	if (E_IS_ATTACHMENT_BAR (bar)) {
-		icon_list = GNOME_ICON_LIST (bar);
-		p = gnome_icon_list_get_selection (icon_list);
-		if (p) {
-			EAttachment *attachment;
-			GSList *list;
-			const char *comp_uid = NULL;
-			char *filename = NULL;
-			const char *local_store = e_cal_get_local_attachment_store (editor->priv->client);
-
-			e_cal_component_get_uid (editor->priv->comp, &comp_uid);
-			num = GPOINTER_TO_INT (p->data);
-			list = e_attachment_bar_get_attachment (bar, num);
-			attachment = list->data;
-			g_slist_free (list);
-
-			filename = g_strdup_printf ("%s-%s",
-						    comp_uid,
-						    camel_mime_part_get_filename(attachment->body));
-
-			attach_file_url = g_build_path ("/", local_store, filename, NULL);
-
-			/* launch the url now */
-			e_show_uri (GTK_WINDOW (editor), attach_file_url);
-
-			g_free (filename);
-			g_free (attach_file_url); }
-		return TRUE;
-	} else
-		return FALSE;
-}
-
-static	gboolean
-attachment_bar_icon_clicked_cb (EAttachmentBar *bar, GdkEvent *event, CompEditor *editor)
-{
-	if (E_IS_ATTACHMENT_BAR (bar) && event->type == GDK_2BUTTON_PRESS)
-		if (open_attachment (bar, editor))
-				return TRUE;
-	return FALSE;
-}
-
-/* Callbacks.  */
-
-static void
-cab_open(EPopup *ep, EPopupItem *item, void *data)
-{
-	EAttachmentBar *bar = data;
-	CompEditor *editor = COMP_EDITOR (gtk_widget_get_toplevel (GTK_WIDGET (bar)));
-
-	if (!open_attachment (bar, editor))
-		g_message ("\n Open failed");
-}
-
-static void
-cab_add(EPopup *ep, EPopupItem *item, void *data)
-{
-	EAttachmentBar *bar = data;
-        CompEditor *editor = COMP_EDITOR (gtk_widget_get_toplevel (GTK_WIDGET (bar)));
-	GPtrArray *file_list;
-	gboolean is_inline = FALSE;
-	int i;
-
-	file_list = comp_editor_select_file_attachments (editor, &is_inline);
-	/*TODO add a good implementation here */
-	if (!file_list)
-		return;
-	for (i = 0; i < file_list->len; i++) {
-		CamelURL *url;
-
-		url = camel_url_new (file_list->pdata[i], NULL);
-		if (url == NULL)
-			continue;
-
-		if (!g_ascii_strcasecmp (url->protocol, "file"))
-			 e_attachment_bar_attach (bar, url->path, is_inline ? "inline" : "attachment");
-		else
-			 e_attachment_bar_attach_remote_file (bar, file_list->pdata[i], is_inline ? "inline" : "attachment");
-		g_free (file_list->pdata[i]);
-		camel_url_free (url);
-	}
-
-	g_ptr_array_free (file_list, TRUE);
-}
-
-static void
-cab_properties(EPopup *ep, EPopupItem *item, void *data)
-{
-	EAttachmentBar *bar = data;
-
-	e_attachment_bar_edit_selected(bar);
-}
-
-static void
-cab_remove(EPopup *ep, EPopupItem *item, void *data)
-{
-	EAttachmentBar *bar = data;
-
-	e_attachment_bar_remove_selected(bar);
-}
-
-/* Popup menu handling.  */
-static EPopupItem cab_popups[] = {
-	{ E_POPUP_ITEM, "10.attach", N_("_Open"), cab_open, NULL, GTK_STOCK_OPEN, E_CAL_POPUP_ATTACHMENTS_ONE},
-	{ E_POPUP_ITEM, "20.attach", N_("_Remove"), cab_remove, NULL, GTK_STOCK_REMOVE, E_CAL_POPUP_ATTACHMENTS_MANY | E_CAL_POPUP_ATTACHMENTS_MODIFY },
-	{ E_POPUP_ITEM, "30.attach", N_("_Properties"), cab_properties, NULL, GTK_STOCK_PROPERTIES, E_CAL_POPUP_ATTACHMENTS_ONE },
-	{ E_POPUP_BAR, "40.attach.00", NULL, NULL, NULL, NULL, E_CAL_POPUP_ATTACHMENTS_MANY|E_CAL_POPUP_ATTACHMENTS_ONE },
-	{ E_POPUP_ITEM, "40.attach.01", N_("_Add attachment..."), cab_add, NULL, GTK_STOCK_ADD, E_CAL_POPUP_ATTACHMENTS_MODIFY},
-};
-
-static void
-cab_popup_position(GtkMenu *menu, int *x, int *y, gboolean *push_in, gpointer user_data)
-{
-	EAttachmentBar *bar = user_data;
-	GnomeIconList *icon_list = user_data;
-	GList *selection;
-	GnomeCanvasPixbuf *image;
-
-	gdk_window_get_origin (((GtkWidget*) bar)->window, x, y);
-
-	selection = gnome_icon_list_get_selection (icon_list);
-	if (selection == NULL)
-		return;
-
-	image = gnome_icon_list_get_icon_pixbuf_item (icon_list, GPOINTER_TO_INT(selection->data));
-	if (image == NULL)
-		return;
-
-	/* Put menu to the center of icon. */
-	*x += (int)(image->item.x1 + image->item.x2) / 2;
-	*y += (int)(image->item.y1 + image->item.y2) / 2;
-}
-
-static void
-cab_popups_free(EPopup *ep, GSList *l, void *data)
-{
-	g_slist_free(l);
-}
-
-/* if id != -1, then use it as an index for target of the popup */
-static void
-cab_popup(EAttachmentBar *bar, GdkEventButton *event, int id)
-{
-	GSList *attachments = NULL, *menus = NULL;
-	int i;
-	ECalPopup *ecp;
-	ECalPopupTargetAttachments *t;
-	GtkMenu *menu;
-	CompEditor *editor = COMP_EDITOR (gtk_widget_get_toplevel (GTK_WIDGET (bar)));
-
-        attachments = e_attachment_bar_get_attachment(bar, id);
-
-	for (i=0;i<sizeof(cab_popups)/sizeof(cab_popups[0]);i++)
-		menus = g_slist_prepend(menus, &cab_popups[i]);
-
-	/** @HookPoint-ECalPopup: Calendar Attachment Bar Context Menu
-	 * @Id: org.gnome.evolution.calendar.attachmentbar.popup
-	 * @Class: org.gnome.evolution.mail.popup:1.0
-	 * @Target: ECalPopupTargetAttachments
-	 *
-	 * This is the context menu on the calendar attachment bar.
-	 */
-	ecp = e_cal_popup_new("org.gnome.evolution.calendar.attachmentbar.popup");
-	e_popup_add_items((EPopup *)ecp, menus, NULL, cab_popups_free, bar);
-	t = e_cal_popup_target_new_attachments(ecp, editor, attachments);
-	t->target.widget = (GtkWidget *)bar;
-	menu = e_popup_create_menu_once((EPopup *)ecp, (EPopupTarget *)t, 0);
-
-	if (event == NULL)
-		gtk_menu_popup(menu, NULL, NULL, cab_popup_position, bar, 0, gtk_get_current_event_time());
-	else
-		gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button, event->time);
-}
-
-/* GtkWidget methods.  */
-
-static gboolean
-popup_menu_event (EAttachmentBar *bar)
-{
-	cab_popup (bar, NULL, -1);
-
-	return TRUE;
-}
-
-
-static int
-button_press_event (EAttachmentBar *bar,
-                    GdkEventButton *event)
-{
-	GnomeIconList *icon_list = GNOME_ICON_LIST (bar);
-	int icon_number = -1;
-
-	if (event->button != 3)
-		return FALSE;
-
-	if (!gnome_icon_list_get_selection (icon_list)) {
-		icon_number = gnome_icon_list_get_icon_at (icon_list, event->x, event->y);
-		if (icon_number >= 0) {
-			gnome_icon_list_unselect_all(icon_list);
-			gnome_icon_list_select_icon (icon_list, icon_number);
-		}
-	}
-
-	cab_popup(bar, event, icon_number);
-
-	return TRUE;
-}
-
-static gint
-key_press_event (EAttachmentBar *bar,
-                 GdkEventKey *event)
-{
-	if (event->keyval == GDK_Delete) {
-                e_attachment_bar_remove_selected (bar);
-                return TRUE;
-        }
-
-        return FALSE;
-}
-
-static gint
-editor_key_press_event (CompEditor *editor,
-                        GdkEventKey *event)
-{
-        if (event->keyval == GDK_Escape) {
-		commit_all_fields (editor);
-
-		if (prompt_and_save_changes (editor, TRUE))
-			close_dialog (editor);
-
-                return TRUE;
-        }
-
-        return FALSE;
 }
 
 /* Menu callbacks */
 
 static void
-setup_widgets (CompEditor *editor)
-{
-	CompEditorPrivate *priv;
-	GtkWidget *expander_hbox;
-	GtkWidget *widget;
-	GtkWidget *vbox;
-
-	priv = editor->priv;
-
-	/* Useful vbox */
-	vbox = gtk_vbox_new (FALSE, 0);
-	gtk_container_add (GTK_CONTAINER (editor), vbox);
-	gtk_widget_show (vbox);
-
-	/* Main Menu */
-	widget = comp_editor_get_managed_widget (editor, "/main-menu");
-	gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
-	gtk_widget_show (widget);
-
-	/* Main Toolbar */
-	widget = comp_editor_get_managed_widget (editor, "/main-toolbar");
-	gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
-	gtk_widget_show (widget);
-
-	/* Notebook */
-	widget = gtk_notebook_new ();
-	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE);
-	gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
-	gtk_widget_show (widget);
-	priv->notebook = GTK_NOTEBOOK (widget);
-
-	g_signal_connect (editor, "delete_event", G_CALLBACK (delete_event_cb), editor);
-	g_signal_connect (editor, "key_press_event", G_CALLBACK (editor_key_press_event), editor);
-
-	/*Attachments */
-	priv->attachment_scrolled_window = gtk_scrolled_window_new (NULL, NULL);
-	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->attachment_scrolled_window),
-					     GTK_SHADOW_IN);
-	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->attachment_scrolled_window),
-					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-
-	g_signal_connect (priv->attachment_bar, "button_press_event", G_CALLBACK (button_press_event), NULL);
-        g_signal_connect (priv->attachment_bar, "key_press_event", G_CALLBACK (key_press_event), NULL);
-        g_signal_connect (priv->attachment_bar, "popup-menu", G_CALLBACK (popup_menu_event), NULL);
-
-	GTK_WIDGET_SET_FLAGS (priv->attachment_bar, GTK_CAN_FOCUS);
-	gtk_container_add (GTK_CONTAINER (priv->attachment_scrolled_window),
-			   priv->attachment_bar);
-	gtk_widget_show (priv->attachment_bar);
-	g_signal_connect (priv->attachment_bar, "changed",
-			  G_CALLBACK (attachment_bar_changed_cb), editor);
-	g_signal_connect (GNOME_ICON_LIST (priv->attachment_bar), "event",
-			  G_CALLBACK (attachment_bar_icon_clicked_cb), editor);
-	priv->attachment_expander_label =
-		gtk_label_new_with_mnemonic (_("Show Attachment _Bar"));
-	priv->attachment_expander_num = gtk_label_new ("");
-	gtk_label_set_use_markup (GTK_LABEL (priv->attachment_expander_num), TRUE);
-	gtk_misc_set_alignment (GTK_MISC (priv->attachment_expander_label), 0.0, 0.5);
-	gtk_misc_set_alignment (GTK_MISC (priv->attachment_expander_num), 1.0, 0.5);
-	expander_hbox = gtk_hbox_new (FALSE, 0);
-
-	priv->attachment_expander_icon = gtk_image_new_from_icon_name ("mail-attachment", GTK_ICON_SIZE_MENU);
-	gtk_misc_set_alignment (GTK_MISC (priv->attachment_expander_icon), 1, 0.5);
-	gtk_widget_set_size_request (priv->attachment_expander_icon, 100, -1);
-
-	gtk_box_pack_start (GTK_BOX (expander_hbox), priv->attachment_expander_label,
-			    TRUE, TRUE, 0);
-	gtk_box_pack_start (GTK_BOX (expander_hbox), priv->attachment_expander_icon,
-			    TRUE, TRUE, 0);
-	gtk_box_pack_start (GTK_BOX (expander_hbox), priv->attachment_expander_num,
-			    TRUE, TRUE, 0);
-	gtk_widget_show_all (expander_hbox);
-	gtk_widget_hide (priv->attachment_expander_icon);
-
-	priv->attachment_expander = e_expander_new ("");
-	e_expander_set_label_widget (E_EXPANDER (priv->attachment_expander), expander_hbox);
-	atk_object_set_name (gtk_widget_get_accessible (priv->attachment_expander), _("Show Attachments"));
-	atk_object_set_description (gtk_widget_get_accessible (priv->attachment_expander), _("Press space key to toggle attachment bar"));
-	gtk_container_add (GTK_CONTAINER (priv->attachment_expander), priv->attachment_scrolled_window);
-
-	gtk_box_pack_start (GTK_BOX (vbox), priv->attachment_expander, FALSE, FALSE, 4);
-	gtk_widget_show (priv->attachment_expander);
-	e_expander_set_expanded (E_EXPANDER (priv->attachment_expander), FALSE);
-	g_signal_connect_after (priv->attachment_expander, "activate",
-				G_CALLBACK (attachment_expander_activate_cb), editor);
-}
-
-
-static void
 comp_editor_show_help (CompEditor *editor)
 {
 	CompEditorClass *class;
@@ -2859,12 +2219,14 @@ comp_editor_get_client (CompEditor *edit
 static void
 set_attachment_list (CompEditor *editor, GSList *attach_list)
 {
-	GSList *p = NULL;
-	const char *comp_uid= NULL;
+	EAttachmentStore *store;
+	EAttachmentView *view;
+	GSList *iter = NULL;
 
-	e_cal_component_get_uid (editor->priv->comp, &comp_uid);
+	view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
+	store = e_attachment_view_get_store (view);
 
-	if (e_attachment_bar_get_num_attachments (E_ATTACHMENT_BAR (editor->priv->attachment_bar))) {
+	if (e_attachment_store_get_num_attachments (store) > 0) {
 		/* To prevent repopulating the
 		 * bar due to redraw functions in fill_widget.
 		 * Assumes it can be set only once.
@@ -2872,112 +2234,55 @@ set_attachment_list (CompEditor *editor,
 		return;
 	}
 
-	for (p = attach_list; p != NULL; p = p->next) {
-		char *attach_filename;
-		CamelMimePart *part;
-		CamelDataWrapper *wrapper;
-		CamelStream *stream;
-		struct stat statbuf;
-		char *mime_type, *file_name;
-		char *ptr;
-
-		attach_filename = (char *) p->data;
-		/* should we assert if g_str_has_prefix (attach_filename, "file://"))
-		 * here
-		*/
-		/* get url sans protocol and add it to the bar.
-		 * how to set the filename properly */
-		file_name = g_filename_from_uri (attach_filename, NULL, NULL);
-		if (!file_name)
-			continue;
-
-		if (g_stat (file_name, &statbuf) < 0) {
-			g_warning ("Cannot attach file %s: %s", file_name, g_strerror (errno));
-			g_free (file_name);
-			continue;
-		}
-
-		/* return if it's not a regular file */
-		if (!S_ISREG (statbuf.st_mode)) {
-			g_warning ("Cannot attach file %s: not a regular file", file_name);
-			g_free (file_name);
-			return;
-		}
-
-		stream = camel_stream_fs_new_with_name (file_name, O_RDONLY, 0);
-		if (!stream) {
-			g_warning ("Cannot attach file %s: %s", file_name, g_strerror (errno));
-			g_free (file_name);
-			return;
-		}
-
-		mime_type = e_util_guess_mime_type (file_name, TRUE);
-		if (mime_type) {
-			if (!g_ascii_strcasecmp (mime_type, "message/rfc822")) {
-				wrapper = (CamelDataWrapper *) camel_mime_message_new ();
-			} else {
-				wrapper = camel_data_wrapper_new ();
-			}
-
-			camel_data_wrapper_construct_from_stream (wrapper, stream);
-			camel_data_wrapper_set_mime_type (wrapper, mime_type);
-			g_free (mime_type);
-		} else {
-			wrapper = camel_data_wrapper_new ();
-			camel_data_wrapper_construct_from_stream (wrapper, stream);
-			camel_data_wrapper_set_mime_type (wrapper, "application/octet-stream");
-		}
-
-		camel_object_unref (stream);
-
-		part = camel_mime_part_new ();
-		camel_medium_set_content_object (CAMEL_MEDIUM (part), wrapper);
-		camel_object_unref (wrapper);
-
-		camel_mime_part_set_disposition (part, "attachment");
-
-		ptr = strstr (file_name, comp_uid);
-		if (ptr) {
-			ptr += strlen(comp_uid);
-			if (*ptr++ == '-')
-				camel_mime_part_set_filename (part, ptr);
-		}
-		g_free (file_name);
-
-		e_attachment_bar_attach_mime_part ((EAttachmentBar *) editor->priv->attachment_bar, part);
-		e_expander_set_expanded (E_EXPANDER (editor->priv->attachment_expander), TRUE);
-
-		camel_object_unref (part);
+	for (iter = attach_list; iter != NULL; iter = iter->next) {
+		EAttachment *attachment;
+		const gchar *uri = iter->data;
+
+		attachment = e_attachment_new_for_uri (uri);
+		e_attachment_store_add_attachment (store, attachment);
+		e_attachment_load_async (
+			attachment, (GAsyncReadyCallback)
+			e_attachment_load_handle_error, editor);
+		g_object_unref (attachment);
 	}
 }
 
 static void
 fill_widgets (CompEditor *editor)
 {
+	EAttachmentStore *store;
+	EAttachmentView *view;
 	CompEditorPrivate *priv;
 	GList *l;
 	GtkAction *action;
 
+	view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
+	store = e_attachment_view_get_store (view);
+
 	priv = editor->priv;
 
 	/*Check if attachments are available here and set them*/
 	if (e_cal_component_has_attachments (priv->comp)) {
 		GSList *attachment_list = NULL;
 		e_cal_component_get_attachment_list (priv->comp, &attachment_list);
-		g_signal_handlers_block_by_func(priv->attachment_bar, G_CALLBACK (attachment_bar_changed_cb), editor);
+		g_signal_handlers_block_by_func (
+			store, G_CALLBACK (attachment_store_changed_cb),
+			editor);
 		set_attachment_list (editor, attachment_list);
-		g_signal_handlers_unblock_by_func(priv->attachment_bar, G_CALLBACK (attachment_bar_changed_cb), editor);
+		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);
 	}
 
 	action = comp_editor_get_action (editor, "classify-public");
-	g_signal_handlers_block_by_func (action, G_CALLBACK (classification_changed_cb), editor);
+	g_signal_handlers_block_by_func (action, G_CALLBACK (action_classification_cb), editor);
 
 	for (l = priv->pages; l != NULL; l = l->next)
 		comp_editor_page_fill_widgets (l->data, priv->comp);
 
-	g_signal_handlers_unblock_by_func (action, G_CALLBACK (classification_changed_cb), editor);
+	g_signal_handlers_unblock_by_func (action, G_CALLBACK (action_classification_cb), editor);
 }
 
 static void
@@ -3297,20 +2602,41 @@ comp_editor_close (CompEditor *editor)
 GSList *
 comp_editor_get_mime_attach_list (CompEditor *editor)
 {
+	EAttachmentStore *store;
+	EAttachmentView *view;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
 	struct CalMimeAttach *cal_mime_attach;
-	GSList *attach_list = NULL, *l, *parts;
+	GSList *attach_list = NULL;
+	gboolean valid;
+
+	view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
+	store = e_attachment_view_get_store (view);
 
-	/* TODO assert sanity of bar */
-	parts = e_attachment_bar_get_parts (E_ATTACHMENT_BAR (editor->priv->attachment_bar));
-	for (l = parts; l ; l = l->next) {
+	model = GTK_TREE_MODEL (store);
+	valid = gtk_tree_model_get_iter_first (model, &iter);
 
+	while (valid) {
+		EAttachment *attachment;
 		CamelDataWrapper *wrapper;
+		CamelMimePart *mime_part;
 		CamelStreamMem *mstream;
 		unsigned char *buffer = NULL;
 		const char *desc, *disp;
+		gint column_id;
+
+		column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+		gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
+		mime_part = e_attachment_get_mime_part (attachment);
+		g_object_unref (attachment);
+
+		valid = gtk_tree_model_iter_next (model, &iter);
+
+		if (mime_part == NULL)
+			continue;
 
 		cal_mime_attach = g_malloc0 (sizeof (struct CalMimeAttach));
-		wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (l->data));
+		wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
 		mstream = (CamelStreamMem *) camel_stream_mem_new ();
 
 		camel_data_wrapper_decode_to_stream (wrapper, (CamelStream *) mstream);
@@ -3318,15 +2644,14 @@ comp_editor_get_mime_attach_list (CompEd
 
 		cal_mime_attach->encoded_data = (char *)buffer;
 		cal_mime_attach->length = mstream->buffer->len;
-		cal_mime_attach->filename = g_strdup (camel_mime_part_get_filename
-			((CamelMimePart *) l->data));
-		desc = camel_mime_part_get_description ((CamelMimePart *) l->data);
+		cal_mime_attach->filename = g_strdup (camel_mime_part_get_filename (mime_part));
+		desc = camel_mime_part_get_description (mime_part);
 		if (!desc || *desc == '\0')
 			desc = _("attachment");
 		cal_mime_attach->description = g_strdup (desc);
 		cal_mime_attach->content_type = g_strdup (camel_data_wrapper_get_mime_type (wrapper));
 
-		disp = camel_mime_part_get_disposition ((CamelMimePart *)l->data);
+		disp = camel_mime_part_get_disposition (mime_part);
 		if (disp && !g_ascii_strcasecmp(disp, "inline"))
 			cal_mime_attach->disposition = TRUE;
 
@@ -3336,8 +2661,6 @@ comp_editor_get_mime_attach_list (CompEd
 
 	}
 
-	g_slist_free (parts);
-
 	return attach_list;
 }
 
Index: calendar/gui/dialogs/event-editor.c
===================================================================
--- calendar/gui/dialogs/event-editor.c	(revision 37527)
+++ calendar/gui/dialogs/event-editor.c	(working copy)
@@ -512,8 +512,6 @@ event_editor_init (EventEditor *ee)
 	g_signal_connect_swapped (
 		ee->priv->model, "row_deleted",
 		G_CALLBACK (event_editor_model_changed_cb), ee);
-
-	gtk_window_set_default_size (GTK_WINDOW (ee), 300, 225);
 }
 
 static void
Index: calendar/gui/e-cal-popup.c
===================================================================
--- calendar/gui/e-cal-popup.c	(revision 37527)
+++ calendar/gui/e-cal-popup.c	(working copy)
@@ -86,378 +86,11 @@ ecalp_target_free(EPopup *ep, EPopupTarg
 
 /* Standard menu code */
 
-static char *
-temp_save_part(CamelMimePart *part, char *path, gboolean file)
-{
-	const char *filename;
-	char *tmpdir, *utf8_mfilename = NULL, *mfilename = NULL, *usepath;
-	CamelStream *stream;
-	CamelDataWrapper *wrapper;
-
-	if (!path) {
-		tmpdir = e_mkdtemp("evolution-tmp-XXXXXX");
-		if (tmpdir == NULL) {
-			return NULL;
-		}
-
-		filename = camel_mime_part_get_filename (part);
-		if (filename == NULL) {
-			/* This is the default filename used for temporary file creation */
-			filename = _("Unknown");
-		} else {
-			utf8_mfilename = g_strdup (filename);
-			e_filename_make_safe (utf8_mfilename);
-			mfilename = g_filename_from_utf8 ((const char *) utf8_mfilename, -1, NULL, NULL, NULL);
-			g_free (utf8_mfilename);
-			filename = (const char *) mfilename;
-		}
-
-		path = g_build_filename(tmpdir, filename, NULL);
-		g_free(tmpdir);
-		g_free(mfilename);
-	} else if (!file) {
-		tmpdir = path;
-		filename = camel_mime_part_get_filename (part);
-		if (filename == NULL) {
-			/* This is the default filename used for temporary file creation */
-			filename = _("Unknown");
-		} else {
-			utf8_mfilename = g_strdup (filename);
-			e_filename_make_safe (utf8_mfilename);
-			mfilename = g_filename_from_utf8 ((const char *)utf8_mfilename, -1, NULL, NULL, NULL);
-			g_free (utf8_mfilename);
-			filename = (const char *) mfilename;
-		}
-
-		path = g_build_filename(tmpdir, filename, NULL);
-		g_free(mfilename);
-	}
-
-	if (strstr (path, "://"))
-		usepath = path;
-	else
-		usepath = g_strjoin (NULL, "file://", path, NULL);
-
-	wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part));
-	stream = camel_stream_vfs_new_with_uri (usepath, CAMEL_STREAM_VFS_CREATE);
-
-	if (usepath != path)
-		g_free (usepath);
-
-	if (!stream) {
-		/* TODO handle error conditions */
-		g_message ("DEBUG: could not open the file to write\n");
-		return NULL;
-	}
-
-	if (camel_data_wrapper_decode_to_stream (wrapper, (CamelStream *) stream) == -1) {
-		camel_stream_close (stream);
-		camel_object_unref (stream);
-		g_message ("DEBUG: could not write to file\n");
-		return NULL;
-	}
-
-	camel_stream_close(stream);
-	camel_object_unref(stream);
-
-	return path;
-}
-
-static void
-ecalp_part_popup_saveas(EPopup *ep, EPopupItem *item, void *data)
-{
-	EPopupTarget *t = ep->target;
-	CamelMimePart *part = NULL;
-	char *file, *mfilename = NULL;
-	const char *filename;
-
-	part = ((EAttachment *) ((ECalPopupTargetAttachments *) t)->attachments->data)->body;
-	filename = camel_mime_part_get_filename (part);
-	if (filename == NULL) {
-		/* This is the default filename used for temporary file creation */
-		filename = _("Unknown");
-	} else {
-		mfilename = g_strdup(filename);
-		e_filename_make_safe(mfilename);
-		filename = mfilename;
-	}
-	file = e_file_dialog_save (_("Save As..."), filename);
-
-	if (file)
-		temp_save_part (part, file, TRUE);
-
-	g_free (file);
-	g_free (mfilename);
-}
-
-static void
-ecalp_part_popup_save_selected(EPopup *ep, EPopupItem *item, void *data)
-{
-	GSList *parts;
-	EPopupTarget *t = ep->target;
-	char *dir, *path;
-
-	dir = e_file_dialog_save_folder (_("Select folder to save selected attachments..."));
-	parts = ((ECalPopupTargetAttachments *) t)->attachments;
-
-	for (;parts; parts=parts->next) {
-		path = temp_save_part (((EAttachment *)parts->data)->body, dir, FALSE);
-		/* Probably we 'll do some reporting in next release, like listing the saved files and locations */
-		g_free (path);
-	}
-}
-
-static void
-ecalp_part_popup_set_background(EPopup *ep, EPopupItem *item, void *data)
-{
-	EPopupTarget *t = ep->target;
-	GConfClient *gconf;
-	char *str, *filename, *path, *extension;
-	unsigned int i=1;
-	CamelMimePart *part = NULL;
-
-	part = ((EAttachment *) ((ECalPopupTargetAttachments *) t)->attachments->data)->body;
-
-	if (!part)
-		return;
-
-	filename = g_strdup(camel_mime_part_get_filename(part));
-
-	/* if filename is blank, create a default filename based on MIME type */
-	if (!filename || !filename[0]) {
-		CamelContentType *ct;
-
-		ct = camel_mime_part_get_content_type(part);
-		g_free (filename);
-		filename = g_strdup_printf (_("untitled_image.%s"), ct->subtype);
-	}
-
-	e_filename_make_safe(filename);
-
-	path = g_build_filename(g_get_home_dir(), ".gnome2", "wallpapers", filename, NULL);
-
-	extension = strrchr(filename, '.');
-	if (extension)
-		*extension++ = 0;
-
-	/* if file exists, stick a (number) on the end */
-	while (g_file_test(path, G_FILE_TEST_EXISTS)) {
-		char *name;
-		name = g_strdup_printf(extension?"%s (%d).%s":"%s (%d)", filename, i++, extension);
-		g_free(path);
-		path = g_build_filename(g_get_home_dir(), ".gnome2", "wallpapers", name, NULL);
-		g_free(name);
-	}
-
-	g_free(filename);
-
-	if (temp_save_part(part, path, TRUE)) {
-		gconf = gconf_client_get_default();
-
-		/* if the filename hasn't changed, blank the filename before
-		*  setting it so that gconf detects a change and updates it */
-		if ((str = gconf_client_get_string(gconf, "/desktop/gnome/background/picture_filename", NULL)) != NULL
-		     && strcmp (str, path) == 0) {
-			gconf_client_set_string(gconf, "/desktop/gnome/background/picture_filename", "", NULL);
-		}
-
-		g_free (str);
-		gconf_client_set_string(gconf, "/desktop/gnome/background/picture_filename", path, NULL);
-
-		/* if GNOME currently doesn't display a picture, set to "wallpaper"
-		 * display mode, otherwise leave it alone */
-		if ((str = gconf_client_get_string(gconf, "/desktop/gnome/background/picture_options", NULL)) == NULL
-		     || strcmp(str, "none") == 0) {
-			gconf_client_set_string(gconf, "/desktop/gnome/background/picture_options", "wallpaper", NULL);
-		}
-
-		gconf_client_suggest_sync(gconf, NULL);
-
-		g_free(str);
-		g_object_unref(gconf);
-	}
-
-	g_free(path);
-}
-
-static const EPopupItem ecalp_standard_part_apps_bar = { E_POPUP_BAR, "99.object" };
-
-static ECalPopupItem ecalp_attachment_object_popups[] = {
-	{ E_POPUP_ITEM, "00.attach.00", N_("_Save As..."), ecalp_part_popup_saveas, NULL, "document-save-as", E_CAL_POPUP_ATTACHMENTS_ONE },
-	{ E_POPUP_ITEM, "00.attach.10", N_("Set as _Background"), ecalp_part_popup_set_background, NULL, NULL, E_CAL_POPUP_ATTACHMENTS_IMAGE },
-	{ E_POPUP_ITEM, "00.attach.20", N_("_Save Selected"), ecalp_part_popup_save_selected, NULL, "document-save-as", E_CAL_POPUP_ATTACHMENTS_MULTIPLE },
-	{ E_POPUP_BAR, "05.attach", },
-};
-
-static void
-ecalp_apps_open_in(EPopup *ep, EPopupItem *item, void *data)
-{
-	char *path;
-	EPopupTarget *target = ep->target;
-	CamelMimePart *part;
-
-	part = ((EAttachment *) ((ECalPopupTargetAttachments *) target)->attachments->data)->body;
-
-	path = temp_save_part(part, NULL, FALSE);
-	if (path) {
-		GAppInfo *app = item->user_data;
-		GList *uris = NULL;
-		GError *error = NULL;
-
-		if (g_app_info_supports_files (app)) {
-			GFile *file = g_file_new_for_path (path);
-
-			uris = g_list_append (uris, file);
-			g_app_info_launch (app, uris, NULL, &error);
-			g_object_unref (file);
-		} else {
-			char *uri;
-
-			uri = e_util_filename_to_uri (path);
-			uris = g_list_append (uris, uri);
-
-			g_app_info_launch_uris (app, uris, NULL, &error);
-			g_free (uri);
-		}
-
-		if (error) {
-			g_warning ("%s", error->message);
-			g_error_free (error);
-		}
-
-		g_list_free (uris);
-		g_free (path);
-	}
-}
-
-static void
-ecalp_apps_popup_free(EPopup *ep, GSList *free_list, void *data)
-{
-	while (free_list) {
-		GSList *n = free_list->next;
-		EPopupItem *item = free_list->data;
-
-		if (item->user_data && item->activate == ecalp_apps_open_in)
-			g_object_unref (item->user_data);
-
-		g_free(item->path);
-		g_free(item->label);
-		g_free(item);
-		g_slist_free_1(free_list);
-
-		free_list = n;
-	}
-}
-
-static void
-ecalp_standard_items_free(EPopup *ep, GSList *items, void *data)
-{
-	g_slist_free(items);
-}
-
-static void
-ecalp_standard_menu_factory (EPopup *ecalp, void *data)
-{
-	int i, len;
-	EPopupItem *items;
-	GSList *menus = NULL;
-	GList *apps = NULL;
-	char *mime_type = NULL;
-	const char *filename = NULL;
-
-	switch (ecalp->target->type) {
-	case E_CAL_POPUP_TARGET_ATTACHMENTS: {
-		ECalPopupTargetAttachments *t = (ECalPopupTargetAttachments *)ecalp->target;
-		GSList *list = t->attachments;
-		EAttachment *attachment;
-
-		items = ecalp_attachment_object_popups;
-		len = G_N_ELEMENTS(ecalp_attachment_object_popups);
-
-		if (g_slist_length(list) != 1 || !((EAttachment *)list->data)->is_available_local) {
-			break;
-		}
-
-		/* Only one attachment selected */
-		attachment = list->data;
-		mime_type = camel_data_wrapper_get_mime_type((CamelDataWrapper *)attachment->body);
-		filename = camel_mime_part_get_filename(attachment->body);
-
-
-		break; }
-	default:
-		items = NULL;
-		len = 0;
-	}
-
-	if (mime_type) {
-                gchar *cp;
-
-                /* does gvfs expect lowercase MIME types? */
-                for (cp = mime_type; *cp != '\0'; cp++)
-                        *cp = g_ascii_tolower (*cp);
-
-		cp = g_content_type_from_mime_type (mime_type);
-		apps = g_app_info_get_all_for_type (cp ? cp : mime_type);
-		g_free (cp);
-
-		if (apps == NULL || strcmp(mime_type, "application/octet-stream") == 0) {
-			if (filename) {
-				gchar *name_type;
-
-				name_type = e_util_guess_mime_type (filename, FALSE);
-				cp = g_content_type_from_mime_type (name_type);
-				/* show alternative apps first */
-				apps = g_list_concat (g_app_info_get_all_for_type (cp ? cp : name_type), apps);
-				g_free (cp);
-				g_free (name_type);
-			}
-		}
-		g_free (mime_type);
-
-		if (apps) {
-			GSList *open_menus = NULL;
-			GList *l;
-
-			menus = g_slist_prepend(menus, (void *)&ecalp_standard_part_apps_bar);
-
-			for (l = apps, i = 0; l; l = l->next, i++) {
-				GAppInfo *app = l->data;
-				EPopupItem *item;
-
-				item = g_malloc0(sizeof(*item));
-				item->type = E_POPUP_ITEM;
-				item->path = g_strdup_printf("99.object.%02d", i);
-				item->label = g_strdup_printf(_("Open in %s..."), g_app_info_get_name (app));
-				item->activate = ecalp_apps_open_in;
-				item->user_data = app;
-
-				open_menus = g_slist_prepend(open_menus, item);
-			}
-
-			if (open_menus)
-				e_popup_add_items(ecalp, open_menus, NULL, ecalp_apps_popup_free, NULL);
-
-			g_list_free (apps);
-		}
-	}
-
-	for (i=0;i<len;i++) {
-		if ((items[i].visible & ecalp->target->mask) == 0)
-			menus = g_slist_prepend(menus, &items[i]);
-	}
-
-	if (menus)
-		e_popup_add_items(ecalp, menus, NULL, ecalp_standard_items_free, NULL);
-}
-
 static void
 ecalp_class_init(GObjectClass *klass)
 {
 	klass->finalize = ecalp_finalise;
 	((EPopupClass *)klass)->target_free = ecalp_target_free;
-
-	e_popup_class_add_factory((EPopupClass *)klass, NULL, ecalp_standard_menu_factory, NULL);
 }
 
 GType
@@ -750,56 +383,6 @@ e_cal_popup_target_new_source(ECalPopup 
 	return t;
 }
 
-/**
- * e_cal_popup_target_new_attachments:
- * @ecp:
- * @attachments: A list of CalAttachment objects, reffed for
- * the list.  Will be unreff'd once finished with.
- *
- * Owns the list @attachments and their items after they're passed in.
- *
- * Return value:
- **/
-ECalPopupTargetAttachments *
-e_cal_popup_target_new_attachments(ECalPopup *ecp, CompEditor *editor, GSList *attachments)
-{
-	ECalPopupTargetAttachments *t = e_popup_target_new(&ecp->popup, E_CAL_POPUP_TARGET_ATTACHMENTS, sizeof(*t));
-	guint32 mask = ~0;
-	int len = g_slist_length(attachments);
-	ECal *client = comp_editor_get_client (editor);
-	CompEditorFlags flags = comp_editor_get_flags (editor);
-	gboolean read_only = FALSE;
-	GError *error = NULL;
-
-	if (!e_cal_is_read_only (client, &read_only, &error)) {
-		if (error->code != E_CALENDAR_STATUS_BUSY)
-			read_only = TRUE;
-		g_error_free (error);
-	}
-
-	if (!read_only && (!(flags & COMP_EDITOR_MEETING) ||
-				(flags & COMP_EDITOR_NEW_ITEM) ||
-				(flags & COMP_EDITOR_USER_ORG)))
-		mask &= ~ E_CAL_POPUP_ATTACHMENTS_MODIFY;
-
-	t->attachments = attachments;
-	if (len > 0)
-		mask &= ~ E_CAL_POPUP_ATTACHMENTS_MANY;
-
-	if (len == 1 && ((EAttachment *)attachments->data)->is_available_local) {
-		if (camel_content_type_is(((CamelDataWrapper *) ((EAttachment *) attachments->data)->body)->mime_type, "image", "*"))
-			mask &= ~ E_CAL_POPUP_ATTACHMENTS_IMAGE;
-		mask &= ~ E_CAL_POPUP_ATTACHMENTS_ONE;
-	}
-
-	if (len > 1)
-		mask &= ~ E_CAL_POPUP_ATTACHMENTS_MULTIPLE;
-
-	t->target.mask = mask;
-
-	return t;
-}
-
 /* ********************************************************************** */
 /* Popup menu plugin handler */
 
Index: calendar/gui/e-cal-popup.h
===================================================================
--- calendar/gui/e-cal-popup.h	(revision 37527)
+++ calendar/gui/e-cal-popup.h	(working copy)
@@ -195,8 +195,6 @@ ECalPopup *e_cal_popup_new(const char *m
 
 ECalPopupTargetSelect *e_cal_popup_target_new_select(ECalPopup *eabp, struct _ECalModel *model, GPtrArray *events);
 ECalPopupTargetSource *e_cal_popup_target_new_source(ECalPopup *eabp, struct _ESourceSelector *selector);
-ECalPopupTargetAttachments * e_cal_popup_target_new_attachments (ECalPopup *ecp,
-								CompEditor *editor, GSList *attachments);
 
 /* ********************************************************************** */
 
Index: calendar/gui/Makefile.am
===================================================================
--- calendar/gui/Makefile.am	(revision 37527)
+++ calendar/gui/Makefile.am	(working copy)
@@ -74,6 +74,8 @@ etspec_DATA =				\
 libevolution_calendar_la_SOURCES =		\
 	$(IDL_GENERATED)			\
 	$(MARSHAL_GENERATED)			\
+	e-attachment-handler-calendar.c		\
+	e-attachment-handler-calendar.h		\
 	cal-search-bar.c			\
 	cal-search-bar.h			\
 	calendar-config.c			\
Index: calendar/gui/calendar-component.c
===================================================================
--- calendar/gui/calendar-component.c	(revision 37527)
+++ calendar/gui/calendar-component.c	(working copy)
@@ -58,6 +58,7 @@
 #include "e-util/e-error.h"
 #include "e-cal-menu.h"
 #include "e-cal-popup.h"
+#include "e-attachment-handler-calendar.h"
 
 /* IDs for user creatable items */
 #define CREATE_EVENT_ID        "event"
@@ -1627,6 +1628,9 @@ calendar_component_class_init (CalendarC
 
 	object_class->dispose  = impl_dispose;
 	object_class->finalize = impl_finalize;
+
+	/* Register attachment handler types. */
+	e_attachment_handler_calendar_get_type ();
 }
 
 static void
Index: calendar/gui/e-attachment-handler-calendar.c
===================================================================
--- calendar/gui/e-attachment-handler-calendar.c	(revision 0)
+++ calendar/gui/e-attachment-handler-calendar.c	(revision 0)
@@ -0,0 +1,511 @@
+/*
+ * e-attachment-handler-calendar.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-calendar.h"
+
+#include <glib/gi18n.h>
+#include <libical/ical.h>
+#include <libecal/e-cal.h>
+#include <camel/camel-stream-mem.h>
+#include <libedataserverui/e-source-selector.h>
+
+#include "calendar/common/authentication.h"
+
+#define E_ATTACHMENT_HANDLER_CALENDAR_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_ATTACHMENT_HANDLER_CALENDAR, EAttachmentHandlerCalendarPrivate))
+
+typedef struct _ImportContext ImportContext;
+
+struct _EAttachmentHandlerCalendarPrivate {
+	gint placeholder;
+};
+
+struct _ImportContext {
+	ECal *client;
+	icalcomponent *component;
+	ECalSourceType source_type;
+};
+
+static gpointer parent_class;
+
+static const gchar *ui =
+"<ui>"
+"  <popup name='context'>"
+"    <placeholder name='custom-actions'>"
+"      <menuitem action='import-to-calendar'/>"
+"      <menuitem action='import-to-tasks'/>"
+"    </placeholder>"
+"  </popup>"
+"</ui>";
+
+static icalcomponent *
+attachment_handler_get_component (EAttachment *attachment)
+{
+	CamelDataWrapper *wrapper;
+	CamelMimePart *mime_part;
+	CamelStream *stream;
+	GByteArray *buffer;
+	icalcomponent *component;
+	const gchar *key = "__icalcomponent__";
+
+	component = g_object_get_data (G_OBJECT (attachment), key);
+	if (component != NULL)
+		return component;
+
+	mime_part = e_attachment_get_mime_part (attachment);
+	if (!CAMEL_IS_MIME_PART (mime_part))
+		return NULL;
+
+	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);
+
+	component = e_cal_util_parse_ics_string ((gchar *) buffer->data);
+
+	g_byte_array_free (buffer, TRUE);
+
+	if (component == NULL)
+		return NULL;
+
+	g_object_set_data_full (
+		G_OBJECT (attachment), key, component,
+		(GDestroyNotify) icalcomponent_free);
+
+	return component;
+}
+
+static gboolean
+attachment_handler_update_objects (ECal *client,
+                                   icalcomponent *component)
+{
+	icalcomponent_kind kind;
+	icalcomponent *vcalendar;
+	gboolean success;
+
+	kind = icalcomponent_isa (component);
+
+	switch (kind) {
+		case ICAL_VTODO_COMPONENT:
+		case ICAL_VEVENT_COMPONENT:
+			vcalendar = e_cal_util_new_top_level ();
+			if (icalcomponent_get_method (component) == ICAL_METHOD_CANCEL)
+				icalcomponent_set_method (vcalendar, ICAL_METHOD_CANCEL);
+			else
+				icalcomponent_set_method (vcalendar, ICAL_METHOD_PUBLISH);
+			icalcomponent_add_component (
+				vcalendar, icalcomponent_new_clone (component));
+			break;
+
+		case ICAL_VCALENDAR_COMPONENT:
+			vcalendar = icalcomponent_new_clone (component);
+			if (!icalcomponent_get_first_property (vcalendar, ICAL_METHOD_PROPERTY))
+				icalcomponent_set_method (vcalendar, ICAL_METHOD_PUBLISH);
+			break;
+
+		default:
+			return FALSE;
+	}
+
+	success = e_cal_receive_objects (client, vcalendar, NULL);
+
+	icalcomponent_free (vcalendar);
+
+	return success;
+}
+
+static void
+attachment_handler_import_event (ECal *client,
+                                 ECalendarStatus status,
+                                 EAttachment *attachment)
+{
+	icalcomponent *component;
+	icalcomponent *subcomponent;
+	icalcompiter iter;
+
+	/* FIXME Notify the user somehow. */
+	g_return_if_fail (status == E_CALENDAR_STATUS_OK);
+
+	component = attachment_handler_get_component (attachment);
+	g_return_if_fail (component != NULL);
+
+	iter = icalcomponent_begin_component (component, ICAL_ANY_COMPONENT);
+
+	while ((subcomponent = icalcompiter_deref (&iter)) != NULL) {
+		icalcomponent_kind kind;
+
+		kind = icalcomponent_isa (subcomponent);
+		icalcompiter_next (&iter);
+
+		if (kind == ICAL_VEVENT_COMPONENT)
+			continue;
+
+		if (kind == ICAL_VTIMEZONE_COMPONENT)
+			continue;
+
+		icalcomponent_remove_component (component, subcomponent);
+		icalcomponent_free (subcomponent);
+	}
+
+	/* XXX Do something with the return value. */
+	attachment_handler_update_objects (client, component);
+
+	g_object_unref (attachment);
+	g_object_unref (client);
+}
+
+static void
+attachment_handler_import_todo (ECal *client,
+                                ECalendarStatus status,
+                                EAttachment *attachment)
+{
+	icalcomponent *component;
+	icalcomponent *subcomponent;
+	icalcompiter iter;
+
+	/* FIXME Notify the user somehow. */
+	g_return_if_fail (status == E_CALENDAR_STATUS_OK);
+
+	component = attachment_handler_get_component (attachment);
+	g_return_if_fail (component != NULL);
+
+	iter = icalcomponent_begin_component (component, ICAL_ANY_COMPONENT);
+
+	while ((subcomponent = icalcompiter_deref (&iter)) != NULL) {
+		icalcomponent_kind kind;
+
+		kind = icalcomponent_isa (subcomponent);
+		icalcompiter_next (&iter);
+
+		if (kind == ICAL_VTODO_COMPONENT)
+			continue;
+
+		if (kind == ICAL_VTIMEZONE_COMPONENT)
+			continue;
+
+		icalcomponent_remove_component (component, subcomponent);
+		icalcomponent_free (subcomponent);
+	}
+
+	/* XXX Do something with the return value. */
+	attachment_handler_update_objects (client, component);
+
+	g_object_unref (attachment);
+	g_object_unref (client);
+}
+
+static void
+attachment_handler_row_activated_cb (GtkDialog *dialog)
+{
+	gtk_dialog_response (dialog, GTK_RESPONSE_OK);
+}
+
+static void
+attachment_handler_run_dialog (GtkWindow *parent,
+                               EAttachment *attachment,
+                               ECalSourceType source_type,
+                               const gchar *title)
+{
+	GtkWidget *dialog;
+	GtkWidget *container;
+	GtkWidget *widget;
+	GCallback callback;
+	ESourceSelector *selector;
+	ESourceList *source_list;
+	ESource *source;
+	ECal *client;
+	icalcomponent *component;
+	GError *error = NULL;
+
+	component = attachment_handler_get_component (attachment);
+	g_return_if_fail (component != NULL);
+
+	e_cal_get_sources (&source_list, source_type, &error);
+	if (error != NULL) {
+		g_warning ("%s", error->message);
+		g_error_free (error);
+		return;
+	}
+
+	source = e_source_list_peek_source_any (source_list);
+	g_return_if_fail (source != NULL);
+
+	dialog = gtk_dialog_new_with_buttons (
+		title, parent, GTK_DIALOG_DESTROY_WITH_PARENT,
+		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
+
+	widget = gtk_button_new_with_mnemonic (_("I_mport"));
+	gtk_button_set_image (
+		GTK_BUTTON (widget), gtk_image_new_from_icon_name (
+		"stock_mail-import", GTK_ICON_SIZE_MENU));
+	gtk_dialog_add_action_widget (
+		GTK_DIALOG (dialog), widget, GTK_RESPONSE_OK);
+	gtk_widget_show (widget);
+
+	gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+	gtk_window_set_default_size (GTK_WINDOW (dialog), 300, 400);
+
+	container = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+	widget = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (
+		GTK_SCROLLED_WINDOW (widget),
+		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (
+		GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = e_source_selector_new (source_list);
+	selector = E_SOURCE_SELECTOR (widget);
+	e_source_selector_set_primary_selection (selector, source);
+	e_source_selector_show_selection (selector, FALSE);
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	gtk_widget_show (widget);
+
+	g_signal_connect_swapped (
+		widget, "row-activated",
+		G_CALLBACK (attachment_handler_row_activated_cb), dialog);
+
+	if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK)
+		goto exit;
+
+	source = e_source_selector_peek_primary_selection (selector);
+	if (source == NULL)
+		goto exit;
+
+	client = auth_new_cal_from_source (source, source_type);
+	if (client == NULL)
+		goto exit;
+
+	if (source_type == E_CAL_SOURCE_TYPE_EVENT)
+		callback = G_CALLBACK (attachment_handler_import_event);
+	else if (source_type == E_CAL_SOURCE_TYPE_TODO)
+		callback = G_CALLBACK (attachment_handler_import_todo);
+	else
+		goto exit;
+
+	g_object_ref (attachment);
+	g_signal_connect (client, "cal-opened", callback, attachment);
+	e_cal_open_async (client, FALSE);
+
+exit:
+	gtk_widget_destroy (dialog);
+}
+
+static void
+attachment_handler_import_to_calendar (GtkAction *action,
+                                       EAttachmentHandler *handler)
+{
+	EAttachment *attachment;
+	EAttachmentView *view;
+	GList *selected;
+	gpointer parent;
+
+	view = e_attachment_handler_get_view (handler);
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	selected = e_attachment_view_get_selected_attachments (view);
+	g_return_if_fail (g_list_length (selected) == 1);
+	attachment = E_ATTACHMENT (selected->data);
+
+	attachment_handler_run_dialog (
+		parent, attachment,
+		E_CAL_SOURCE_TYPE_EVENT,
+		_("Select a Calendar"));
+
+	g_object_unref (attachment);
+	g_list_free (selected);
+}
+
+static void
+attachment_handler_import_to_tasks (GtkAction *action,
+                                    EAttachmentHandler *handler)
+{
+	EAttachment *attachment;
+	EAttachmentView *view;
+	GList *selected;
+	gpointer parent;
+
+	view = e_attachment_handler_get_view (handler);
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	selected = e_attachment_view_get_selected_attachments (view);
+	g_return_if_fail (g_list_length (selected) == 1);
+	attachment = E_ATTACHMENT (selected->data);
+
+	attachment_handler_run_dialog (
+		parent, attachment,
+		E_CAL_SOURCE_TYPE_TODO,
+		_("Select a Task List"));
+
+	g_object_unref (attachment);
+	g_list_free (selected);
+}
+
+static GtkActionEntry standard_entries[] = {
+
+	{ "import-to-calendar",
+	  "stock_mail-import",
+	  N_("I_mport to Calendar"),
+	  NULL,
+	  NULL,  /* XXX Add a tooltip! */
+	  G_CALLBACK (attachment_handler_import_to_calendar) },
+
+	{ "import-to-tasks",
+	  "stock_mail-import",
+	  N_("I_mport to Tasks"),
+	  NULL,
+	  NULL,  /* XXX Add a tooltip! */
+	  G_CALLBACK (attachment_handler_import_to_tasks) }
+};
+
+static void
+attachment_handler_calendar_update_actions (EAttachmentView *view)
+{
+	EAttachment *attachment;
+	GtkAction *action;
+	GList *selected;
+	icalcomponent *component;
+	icalcomponent *subcomponent;
+	icalcomponent_kind kind;
+	gboolean is_vevent = FALSE;
+	gboolean is_vtodo = FALSE;
+
+	selected = e_attachment_view_get_selected_attachments (view);
+
+	if (g_list_length (selected) != 1)
+		goto exit;
+
+	attachment = E_ATTACHMENT (selected->data);
+	component = attachment_handler_get_component (attachment);
+
+	if (component == NULL)
+		goto exit;
+
+	subcomponent = icalcomponent_get_inner (component);
+
+	if (subcomponent == NULL)
+		goto exit;
+
+	kind = icalcomponent_isa (subcomponent);
+	is_vevent = (kind == ICAL_VEVENT_COMPONENT);
+	is_vtodo = (kind == ICAL_VTODO_COMPONENT);
+
+exit:
+	action = e_attachment_view_get_action (view, "import-to-calendar");
+	gtk_action_set_visible (action, is_vevent);
+
+	action = e_attachment_view_get_action (view, "import-to-tasks");
+	gtk_action_set_visible (action, is_vtodo);
+
+	g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+	g_list_free (selected);
+}
+
+static void
+attachment_handler_calendar_constructed (GObject *object)
+{
+	EAttachmentHandler *handler;
+	EAttachmentView *view;
+	GtkActionGroup *action_group;
+	GtkUIManager *ui_manager;
+	GError *error = NULL;
+
+	handler = E_ATTACHMENT_HANDLER (object);
+
+	/* Chain up to parent's constructed() method. */
+	G_OBJECT_CLASS (parent_class)->constructed (object);
+
+	view = e_attachment_handler_get_view (handler);
+
+	action_group = e_attachment_view_add_action_group (view, "calendar");
+	gtk_action_group_add_actions (
+		action_group, standard_entries,
+		G_N_ELEMENTS (standard_entries), handler);
+
+	ui_manager = e_attachment_view_get_ui_manager (view);
+	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_calendar_update_actions),
+		NULL);
+}
+
+static void
+attachment_handler_calendar_class_init (EAttachmentHandlerCalendarClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EAttachmentHandlerCalendarPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->constructed = attachment_handler_calendar_constructed;
+}
+
+static void
+attachment_handler_calendar_init (EAttachmentHandlerCalendar *handler)
+{
+	handler->priv = E_ATTACHMENT_HANDLER_CALENDAR_GET_PRIVATE (handler);
+}
+
+GType
+e_attachment_handler_calendar_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EAttachmentHandlerCalendarClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) attachment_handler_calendar_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EAttachmentHandlerCalendar),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) attachment_handler_calendar_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			E_TYPE_ATTACHMENT_HANDLER,
+			"EAttachmentHandlerCalendar", &type_info, 0);
+	}
+
+	return type;
+}
Index: calendar/gui/e-attachment-handler-calendar.h
===================================================================
--- calendar/gui/e-attachment-handler-calendar.h	(revision 0)
+++ calendar/gui/e-attachment-handler-calendar.h	(revision 0)
@@ -0,0 +1,65 @@
+/*
+ * e-attachment-handler-calendar.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_CALENDAR_H
+#define E_ATTACHMENT_HANDLER_CALENDAR_H
+
+#include <widgets/misc/e-attachment-handler.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_HANDLER_CALENDAR \
+	(e_attachment_handler_calendar_get_type ())
+#define E_ATTACHMENT_HANDLER_CALENDAR(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_ATTACHMENT_HANDLER_CALENDAR, EAttachmentHandlerCalendar))
+#define E_ATTACHMENT_HANDLER_CALENDAR_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_ATTACHMENT_HANDLER_CALENDAR, EAttachmentHandlerCalendarClass))
+#define E_IS_ATTACHMENT_HANDLER_CALENDAR(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_ATTACHMENT_HANDLER_CALENDAR))
+#define E_IS_ATTACHMENT_HANDLER_CALENDAR_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_ATTACHMENT_HANDLER_CALENDAR))
+#define E_ATTACHMENT_HANDLER_CALENDAR_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_ATTACHMENT_HANDLER_CALENDAR, EAttachmentHandlerCalendarClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentHandlerCalendar EAttachmentHandlerCalendar;
+typedef struct _EAttachmentHandlerCalendarClass EAttachmentHandlerCalendarClass;
+typedef struct _EAttachmentHandlerCalendarPrivate EAttachmentHandlerCalendarPrivate;
+
+struct _EAttachmentHandlerCalendar {
+	EAttachmentHandler parent;
+	EAttachmentHandlerCalendarPrivate *priv;
+};
+
+struct _EAttachmentHandlerCalendarClass {
+	EAttachmentHandlerClass parent_class;
+};
+
+GType		e_attachment_handler_calendar_get_type	(void);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_HANDLER_CALENDAR_H */
Index: a11y/e-table/Makefile.am
===================================================================
--- a11y/e-table/Makefile.am	(revision 37527)
+++ a11y/e-table/Makefile.am	(working copy)
@@ -1,6 +1,7 @@
 INCLUDES =					\
-	-I$(top_srcdir)                         \
-	-I$(top_srcdir)/widgets                 \
+	-I$(top_srcdir)				\
+	-I$(top_srcdir)/widgets			\
+	$(E_UTIL_CFLAGS)			\
 	$(GNOME_PLATFORM_CFLAGS)		\
         -DG_LOG_DOMAIN=\"e-table\"
 
