evolution r37482 - in branches/kill-bonobo: calendar/gui/dialogs composer shell widgets/misc



Author: mbarnes
Date: Mon Mar 30 03:38:36 2009
New Revision: 37482
URL: http://svn.gnome.org/viewvc/evolution?rev=37482&view=rev

Log:
Saving progress again on the attachment rewrite.


Modified:
   branches/kill-bonobo/calendar/gui/dialogs/comp-editor.c
   branches/kill-bonobo/composer/e-composer-private.c
   branches/kill-bonobo/composer/e-msg-composer.c
   branches/kill-bonobo/composer/evolution-composer.ui
   branches/kill-bonobo/shell/e-shell.c
   branches/kill-bonobo/widgets/misc/e-attachment-icon-view.c
   branches/kill-bonobo/widgets/misc/e-attachment-paned.c
   branches/kill-bonobo/widgets/misc/e-attachment-store.c
   branches/kill-bonobo/widgets/misc/e-attachment-store.h
   branches/kill-bonobo/widgets/misc/e-attachment-tree-view.c
   branches/kill-bonobo/widgets/misc/e-attachment-view.c
   branches/kill-bonobo/widgets/misc/e-attachment-view.h
   branches/kill-bonobo/widgets/misc/e-attachment.c
   branches/kill-bonobo/widgets/misc/e-attachment.h

Modified: branches/kill-bonobo/calendar/gui/dialogs/comp-editor.c
==============================================================================
--- branches/kill-bonobo/calendar/gui/dialogs/comp-editor.c	(original)
+++ branches/kill-bonobo/calendar/gui/dialogs/comp-editor.c	Mon Mar 30 03:38:36 2009
@@ -158,8 +158,6 @@
 "    <toolitem action='print'/>"
 "    <toolitem action='close'/>"
 "    <separator/>"
-"    <toolitem action='attach'/>"
-"    <separator/>"
 "  </toolbar>"
 "</ui>";
 

Modified: branches/kill-bonobo/composer/e-composer-private.c
==============================================================================
--- branches/kill-bonobo/composer/e-composer-private.c	(original)
+++ branches/kill-bonobo/composer/e-composer-private.c	Mon Mar 30 03:38:36 2009
@@ -152,7 +152,7 @@
 	/* Construct the attachment paned. */
 
 	widget = e_attachment_paned_new ();
-	gtk_container_set_border_width (GTK_CONTAINER (widget), 6);
+	/*gtk_container_set_border_width (GTK_CONTAINER (widget), 6);*/
 	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
 	priv->attachment_paned = g_object_ref (widget);
 	gtk_widget_show (widget);

Modified: branches/kill-bonobo/composer/e-msg-composer.c
==============================================================================
--- branches/kill-bonobo/composer/e-msg-composer.c	(original)
+++ branches/kill-bonobo/composer/e-msg-composer.c	Mon Mar 30 03:38:36 2009
@@ -2285,6 +2285,7 @@
 
 	/* Drag-and-Drop Support */
 
+#if 0  /* KILL-BONOBO */
 	gtk_drag_dest_set (
 		GTK_WIDGET (composer), GTK_DEST_DEFAULT_ALL,
 		drop_types, G_N_ELEMENTS (drop_types),
@@ -2293,6 +2294,7 @@
 	g_signal_connect (
 		html, "drag-data-received",
 		G_CALLBACK (msg_composer_drag_data_received), NULL);
+#endif
 
 	/* Configure Headers */
 

Modified: branches/kill-bonobo/composer/evolution-composer.ui
==============================================================================
--- branches/kill-bonobo/composer/evolution-composer.ui	(original)
+++ branches/kill-bonobo/composer/evolution-composer.ui	Mon Mar 30 03:38:36 2009
@@ -9,7 +9,7 @@
         <menuitem action='save'/>
         <menuitem action='save-as'/>
         <menuitem action='save-draft'/>
-	<placeholder name='template-holder'/>
+        <placeholder name='template-holder'/>
         <separator/>
         <menuitem action='print-preview'/>
         <menuitem action='print'/>
@@ -58,7 +58,6 @@
       <toolitem action='send'/>
       <separator/>
       <toolitem action='save-draft'/>
-      <toolitem action='attach'/>
       <separator/>
     </placeholder>
   </toolbar>

Modified: branches/kill-bonobo/shell/e-shell.c
==============================================================================
--- branches/kill-bonobo/shell/e-shell.c	(original)
+++ branches/kill-bonobo/shell/e-shell.c	Mon Mar 30 03:38:36 2009
@@ -801,7 +801,17 @@
 		g_cclosure_marshal_VOID__VOID,
 		G_TYPE_NONE, 0);
 
-	/* Install some desktop-wide settings. */
+	/* Install some application-wide settings. */
+
+	e_shell_settings_install_property (
+		g_param_spec_int (
+			"attachment-view",
+			NULL,
+			NULL,
+			0,
+			1,
+			0,
+			G_PARAM_READWRITE));
 
 	e_shell_settings_install_property (
 		g_param_spec_boolean (
@@ -842,6 +852,14 @@
 			NULL,
 			FALSE,
 			G_PARAM_READWRITE));
+
+	e_shell_settings_install_property (
+		g_param_spec_string (
+			"file-chooser-folder",
+			NULL,
+			NULL,
+			NULL,
+			G_PARAM_READWRITE));
 }
 
 static void

Modified: branches/kill-bonobo/widgets/misc/e-attachment-icon-view.c
==============================================================================
--- branches/kill-bonobo/widgets/misc/e-attachment-icon-view.c	(original)
+++ branches/kill-bonobo/widgets/misc/e-attachment-icon-view.c	Mon Mar 30 03:38:36 2009
@@ -101,10 +101,8 @@
 {
 	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
 
-	if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
-		e_attachment_view_show_popup_menu (view, event);
+	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)->
@@ -112,6 +110,20 @@
 }
 
 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)
 {
@@ -127,6 +139,43 @@
 		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,
@@ -139,6 +188,23 @@
 	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,
@@ -271,6 +337,55 @@
 }
 
 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;
@@ -288,8 +403,13 @@
 
 	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;
 
@@ -313,6 +433,11 @@
 	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
@@ -320,7 +445,6 @@
 {
 	GtkCellLayout *cell_layout;
 	GtkCellRenderer *renderer;
-
 	cell_layout = GTK_CELL_LAYOUT (icon_view);
 	icon_view->priv = E_ATTACHMENT_ICON_VIEW_GET_PRIVATE (icon_view);
 
@@ -331,7 +455,7 @@
 
 	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, TRUE);
+	gtk_cell_layout_pack_start (cell_layout, renderer, FALSE);
 
 	gtk_cell_layout_add_attribute (
 		cell_layout, renderer, "gicon",
@@ -340,8 +464,9 @@
 	renderer = gtk_cell_renderer_text_new ();
 	g_object_set (
 		renderer, "alignment", PANGO_ALIGN_CENTER,
-		"xalign", 0.5, NULL);
-	gtk_cell_layout_pack_start (cell_layout, renderer, TRUE);
+		"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",

Modified: branches/kill-bonobo/widgets/misc/e-attachment-paned.c
==============================================================================
--- branches/kill-bonobo/widgets/misc/e-attachment-paned.c	(original)
+++ branches/kill-bonobo/widgets/misc/e-attachment-paned.c	Mon Mar 30 03:38:36 2009
@@ -467,10 +467,12 @@
 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 ();
@@ -478,6 +480,58 @@
 	/* 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);
@@ -501,6 +555,16 @@
 	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"));
@@ -511,85 +575,45 @@
 
 	container = paned->priv->expander;
 
-	widget = gtk_hbox_new (FALSE, 0);
+	/* 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_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
-	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 6);
+	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_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);
+	widget = gtk_alignment_new (0.5, 0.5, 0.0, 1.0);
 	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 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_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
-	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 6);
-	paned->priv->status_label = g_object_ref (widget);
-	gtk_widget_hide (widget);
-
-	/* 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);
+	widget = gtk_hbox_new (FALSE, 6);
 	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);
+	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));

Modified: branches/kill-bonobo/widgets/misc/e-attachment-store.c
==============================================================================
--- branches/kill-bonobo/widgets/misc/e-attachment-store.c	(original)
+++ branches/kill-bonobo/widgets/misc/e-attachment-store.c	Mon Mar 30 03:38:36 2009
@@ -674,23 +674,34 @@
 
 void
 e_attachment_store_run_save_dialog (EAttachmentStore *store,
-                                    EAttachment *attachment,
+                                    GList *attachment_list,
                                     GtkWindow *parent)
 {
 	GtkFileChooser *file_chooser;
+	GtkFileChooserAction action;
 	GtkWidget *dialog;
 	GFile *destination;
-	GFileInfo *file_info;
-	const gchar *display_name;
+	const gchar *title;
 	gint response;
+	guint length;
 
 	g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
-	g_return_if_fail (E_IS_ATTACHMENT (attachment));
 	g_return_if_fail (GTK_IS_WINDOW (parent));
 
+	length = g_list_length (attachment_list);
+
+	if (length == 0)
+		return;
+
+	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 (
-		_("Save Attachment"), parent,
-		GTK_FILE_CHOOSER_ACTION_SAVE,
+		title, parent, action,
 		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 		GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL);
 
@@ -700,13 +711,20 @@
 	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
 	gtk_window_set_icon_name (GTK_WINDOW (dialog), "mail-attachment");
 
-	file_info = e_attachment_get_file_info (attachment);
-	if (file_info != NULL)
-		display_name = g_file_info_get_display_name (file_info);
-	else
-		display_name = NULL;
-	if (display_name != NULL)
-		gtk_file_chooser_set_current_name (file_chooser, display_name);
+	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);
 
@@ -715,9 +733,13 @@
 
 	destination = gtk_file_chooser_get_file (file_chooser);
 
-	e_attachment_save_async (
-		attachment, destination, (GAsyncReadyCallback)
-		e_attachment_save_handle_error, parent);
+	while (attachment_list != NULL) {
+		e_attachment_save_async (
+			attachment_list->data,
+			destination, (GAsyncReadyCallback)
+			e_attachment_save_handle_error, parent);
+		attachment_list = g_list_next (attachment_list);
+	}
 
 	g_object_unref (destination);
 

Modified: branches/kill-bonobo/widgets/misc/e-attachment-store.h
==============================================================================
--- branches/kill-bonobo/widgets/misc/e-attachment-store.h	(original)
+++ branches/kill-bonobo/widgets/misc/e-attachment-store.h	Mon Mar 30 03:38:36 2009
@@ -103,7 +103,7 @@
 						 GtkWindow *parent);
 void		e_attachment_store_run_save_dialog
 						(EAttachmentStore *store,
-						 EAttachment *attachment,
+						 GList *attachment_list,
 						 GtkWindow *parent);
 
 G_END_DECLS

Modified: branches/kill-bonobo/widgets/misc/e-attachment-tree-view.c
==============================================================================
--- branches/kill-bonobo/widgets/misc/e-attachment-tree-view.c	(original)
+++ branches/kill-bonobo/widgets/misc/e-attachment-tree-view.c	Mon Mar 30 03:38:36 2009
@@ -119,10 +119,8 @@
 {
 	EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
 
-	if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
-		e_attachment_view_show_popup_menu (view, event);
+	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)->
@@ -130,6 +128,20 @@
 }
 
 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)
 {
@@ -145,6 +157,43 @@
 		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,
@@ -157,6 +206,23 @@
 	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,
@@ -307,6 +373,55 @@
 }
 
 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;
@@ -324,8 +439,13 @@
 
 	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;
 
@@ -349,6 +469,11 @@
 	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
@@ -368,7 +493,6 @@
 	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);
@@ -417,7 +541,6 @@
 		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);
@@ -430,7 +553,6 @@
 		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);

Modified: branches/kill-bonobo/widgets/misc/e-attachment-view.c
==============================================================================
--- branches/kill-bonobo/widgets/misc/e-attachment-view.c	(original)
+++ branches/kill-bonobo/widgets/misc/e-attachment-view.c	Mon Mar 30 03:38:36 2009
@@ -223,21 +223,16 @@
                    EAttachmentView *view)
 {
 	EAttachmentStore *store;
-	EAttachment *attachment;
 	GList *selected;
 	gpointer parent;
 
 	store = e_attachment_view_get_store (view);
 
-	selected = e_attachment_view_get_selected_attachments (view);
-	g_return_if_fail (g_list_length (selected) == 1);
-	attachment = selected->data;
-
 	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
 	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
 
-	e_attachment_store_run_save_dialog (store, attachment, parent);
-
+	selected = e_attachment_view_get_selected_attachments (view);
+	e_attachment_store_run_save_dialog (store, selected, parent);
 	g_list_foreach (selected, (GFunc) g_object_unref, NULL);
 	g_list_free (selected);
 }
@@ -300,7 +295,7 @@
 	  GTK_STOCK_ADD,
 	  N_("A_dd Attachment..."),
 	  NULL,
-	  NULL,  /* XXX Add a tooltip! */
+	  N_("Attach a file"),
 	  G_CALLBACK (action_add_cb) },
 
 	{ "properties",
@@ -563,10 +558,8 @@
 
 	priv = e_attachment_view_get_private (view);
 
-	gtk_drag_dest_set (
-		GTK_WIDGET (view), GTK_DEST_DEFAULT_ALL,
-		drop_types, G_N_ELEMENTS (drop_types),
-		GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK);
+	e_attachment_view_drag_source_set (view);
+	e_attachment_view_drag_dest_set (view);
 
 	ui_manager = gtk_ui_manager_new ();
 	priv->merge_id = gtk_ui_manager_new_merge_id (ui_manager);
@@ -809,6 +802,70 @@
 	g_list_free (selected);
 }
 
+gboolean
+e_attachment_view_button_press_event (EAttachmentView *view,
+                                      GdkEventButton *event)
+{
+	GtkTreePath *path;
+
+	g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
+	g_return_val_if_fail (event != NULL, FALSE);
+
+	/* 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);
+	} else
+		e_attachment_view_unselect_all (view);
+
+	/* 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) {
+		e_attachment_view_show_popup_menu (view, event);
+		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;
+}
+
 GtkTreePath *
 e_attachment_view_get_path_at_pos (EAttachmentView *view,
                                    gint x,
@@ -928,6 +985,118 @@
 }
 
 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));
+}
+
+void
+e_attachment_view_drag_data_get (EAttachmentView *view,
+                                 GdkDragContext *context,
+                                 GtkSelectionData *selection,
+                                 guint info,
+                                 guint time)
+{
+	GList *selected, *iter;
+	gchar **uris;
+	gint ii = 0;
+
+	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+	g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+	g_return_if_fail (selection != NULL);
+
+	selected = e_attachment_view_get_selected_attachments (view);
+	if (selected == NULL)
+		return;
+
+	uris = g_malloc0 (sizeof (gchar *) * (g_list_length (selected) + 1));
+
+	for (iter = selected; iter != NULL; iter = iter->next) {
+		EAttachment *attachment = iter->data;
+		GFile *file;
+
+		/* FIXME Need to handle attachments with no GFile. */
+		file = e_attachment_get_file (attachment);
+		if (file == NULL)
+			continue;
+
+		uris[ii++] = g_file_get_uri (file);
+	}
+
+	gtk_selection_data_set_uris (selection, uris);
+
+	g_strfreev (uris);
+}
+
+void
+e_attachment_view_drag_dest_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);
+	/* FIXME Add targets here... */
+	targets = gtk_target_table_new_from_list (list, &n_targets);
+
+	gtk_drag_dest_set (
+		GTK_WIDGET (view), GTK_DEST_DEFAULT_ALL,
+		targets, n_targets, GDK_ACTION_COPY);
+
+	gtk_target_table_free (targets, n_targets);
+	gtk_target_list_unref (list);
+}
+
+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));
+}
+
+void
 e_attachment_view_drag_action (EAttachmentView *view,
                                GdkDragAction action)
 {
@@ -994,6 +1163,11 @@
 	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);
+
+	/* Disallow drops if we're not editable. */
+	if (!e_attachment_view_get_editable (view))
+		return FALSE;
 
 	for (iter = context->targets; iter != NULL; iter = iter->next) {
 		GdkAtom atom = iter->data;
@@ -1020,12 +1194,29 @@
 	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,
+                                      GdkDragContext *context,
                                       gint x,
                                       gint y,
-                                      GtkSelectionData *selection_data,
+                                      GtkSelectionData *selection,
                                       guint info,
                                       guint time)
 {
@@ -1034,16 +1225,18 @@
 	GdkDragAction action;
 
 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+	g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+	g_return_if_fail (selection != NULL);
 
 	priv = e_attachment_view_get_private (view);
 	ui_manager = e_attachment_view_get_ui_manager (view);
 
-	action = drag_context->action;
+	action = context->action;
 
-	if (gtk_selection_data_get_data (selection_data) == NULL)
+	if (gtk_selection_data_get_data (selection) == NULL)
 		return;
 
-	if (gtk_selection_data_get_length (selection_data) == -1)
+	if (gtk_selection_data_get_length (selection) == -1)
 		return;
 
 	if (priv->drag_context != NULL)
@@ -1052,8 +1245,8 @@
 	if (priv->selection_data != NULL)
 		gtk_selection_data_free (priv->selection_data);
 
-	priv->drag_context = g_object_ref (drag_context);
-	priv->selection_data = gtk_selection_data_copy (selection_data);
+	priv->drag_context = g_object_ref (context);
+	priv->selection_data = gtk_selection_data_copy (selection);
 	priv->info = info;
 	priv->time = time;
 
@@ -1147,21 +1340,6 @@
 
 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
 
-	if (event != NULL) {
-		GtkTreePath *path;
-
-		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);
-		} else
-			e_attachment_view_unselect_all (view);
-	}
-
 	e_attachment_view_update_actions (view);
 
 	ui_manager = e_attachment_view_get_ui_manager (view);
@@ -1218,7 +1396,7 @@
 	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 == 1);
+	gtk_action_set_visible (action, !busy && n_selected > 0);
 
 	action = e_attachment_view_get_action (view, "set-background");
 	gtk_action_set_visible (action, !busy && is_image);

Modified: branches/kill-bonobo/widgets/misc/e-attachment-view.h
==============================================================================
--- branches/kill-bonobo/widgets/misc/e-attachment-view.h	(original)
+++ branches/kill-bonobo/widgets/misc/e-attachment-view.h	Mon Mar 30 03:38:36 2009
@@ -72,6 +72,19 @@
 						 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);
 };
 
 struct _EAttachmentViewPrivate {
@@ -114,6 +127,13 @@
 						(EAttachmentView *view,
 						 gboolean select_next);
 
+gboolean	e_attachment_view_button_press_event
+						(EAttachmentView *view,
+						 GdkEventButton *event);
+gboolean	e_attachment_view_button_release_event
+						(EAttachmentView *view,
+						 GdkEventButton *event);
+
 /* Selection Management */
 GtkTreePath *	e_attachment_view_get_path_at_pos
 						(EAttachmentView *view,
@@ -133,7 +153,25 @@
 void		e_attachment_view_sync_selection(EAttachmentView *view,
 						 EAttachmentView *target);
 
-/* Drag and Drop Support */
+/* 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);
 void		e_attachment_view_drag_action	(EAttachmentView *view,
 						 GdkDragAction action);
 gboolean	e_attachment_view_drag_motion	(EAttachmentView *view,
@@ -141,6 +179,11 @@
 						 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,

Modified: branches/kill-bonobo/widgets/misc/e-attachment.c
==============================================================================
--- branches/kill-bonobo/widgets/misc/e-attachment.c	(original)
+++ branches/kill-bonobo/widgets/misc/e-attachment.c	Mon Mar 30 03:38:36 2009
@@ -135,6 +135,7 @@
 	GFileInfo *file_info;
 	const gchar *content_type;
 	const gchar *display_name;
+	gchar *content_desc;
 	gchar *display_size;
 	gchar *caption;
 	goffset size;
@@ -156,6 +157,7 @@
 	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);
 
 	if (size > 0)
@@ -167,11 +169,12 @@
 	gtk_list_store_set (
 		GTK_LIST_STORE (model), &iter,
 		E_ATTACHMENT_STORE_COLUMN_CAPTION, caption,
-		E_ATTACHMENT_STORE_COLUMN_CONTENT_TYPE, content_type,
+		E_ATTACHMENT_STORE_COLUMN_CONTENT_TYPE, content_desc,
 		E_ATTACHMENT_STORE_COLUMN_DISPLAY_NAME, display_name,
 		E_ATTACHMENT_STORE_COLUMN_SIZE, size,
 		-1);
 
+	g_free (content_desc);
 	g_free (display_size);
 	g_free (caption);
 }
@@ -297,44 +300,14 @@
 }
 
 static void
-attachment_update_loading_column (EAttachment *attachment)
+attachment_update_progress_columns (EAttachment *attachment)
 {
 	GtkTreeRowReference *reference;
 	GtkTreeModel *model;
 	GtkTreePath *path;
 	GtkTreeIter iter;
-	GFileInfo *file_info;
 	gboolean loading;
-
-	reference = e_attachment_get_reference (attachment);
-	if (!gtk_tree_row_reference_valid (reference))
-		return;
-
-	/* Don't show progress until we have a GFileInfo. */
-	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);
-
-	loading = e_attachment_get_loading (attachment);
-
-	gtk_list_store_set (
-		GTK_LIST_STORE (model), &iter,
-		E_ATTACHMENT_STORE_COLUMN_LOADING, loading,
-		-1);
-}
-
-static void
-attachment_update_percent_column (EAttachment *attachment)
-{
-	GtkTreeRowReference *reference;
-	GtkTreeModel *model;
-	GtkTreePath *path;
-	GtkTreeIter iter;
+	gboolean saving;
 	gint percent;
 
 	reference = e_attachment_get_reference (attachment);
@@ -346,36 +319,15 @@
 	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,
-		-1);
-}
-
-static void
-attachment_update_saving_column (EAttachment *attachment)
-{
-	GtkTreeRowReference *reference;
-	GtkTreeModel *model;
-	GtkTreePath *path;
-	GtkTreeIter iter;
-	gboolean saving;
-
-	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);
-
-	saving = e_attachment_get_saving (attachment);
-
-	gtk_list_store_set (
-		GTK_LIST_STORE (model), &iter,
 		E_ATTACHMENT_STORE_COLUMN_SAVING, saving,
 		-1);
 }
@@ -399,7 +351,7 @@
 	g_object_notify (G_OBJECT (attachment), "file-info");
 
 	/* Tell the EAttachmentStore its total size changed. */
-	if (reference != NULL) {
+	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");
@@ -422,7 +374,7 @@
 	g_object_notify (G_OBJECT (attachment), "loading");
 	g_object_thaw_notify (G_OBJECT (attachment));
 
-	if (reference != NULL) {
+	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");
@@ -799,20 +751,16 @@
 		G_CALLBACK (attachment_update_icon_column), NULL);
 
 	g_signal_connect (
-		attachment, "notify::file-info",
-		G_CALLBACK (attachment_update_loading_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_loading_column), NULL);
+		G_CALLBACK (attachment_update_progress_columns), NULL);
 
 	g_signal_connect (
 		attachment, "notify::percent",
-		G_CALLBACK (attachment_update_percent_column), NULL);
+		G_CALLBACK (attachment_update_progress_columns), NULL);
 
 	g_signal_connect (
 		attachment, "notify::reference",
@@ -824,15 +772,7 @@
 
 	g_signal_connect (
 		attachment, "notify::reference",
-		G_CALLBACK (attachment_update_loading_column), NULL);
-
-	g_signal_connect (
-		attachment, "notify::reference",
-		G_CALLBACK (attachment_update_saving_column), NULL);
-
-	g_signal_connect (
-		attachment, "notify::reference",
-		G_CALLBACK (attachment_update_percent_column), NULL);
+		G_CALLBACK (attachment_update_progress_columns), NULL);
 
 	g_signal_connect (
 		attachment, "notify::saving",
@@ -840,7 +780,7 @@
 
 	g_signal_connect (
 		attachment, "notify::saving",
-		G_CALLBACK (attachment_update_saving_column), NULL);
+		G_CALLBACK (attachment_update_progress_columns), NULL);
 
 	g_signal_connect (
 		attachment, "notify::signed",
@@ -1294,7 +1234,8 @@
 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
 
 	file_info = e_attachment_get_file_info (attachment);
-	g_return_val_if_fail (file_info != NULL, NULL);
+	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);
@@ -1316,92 +1257,14 @@
 	return app_info_list;
 }
 
-GList *
-e_attachment_list_emblems (EAttachment *attachment)
-{
-	GCancellable *cancellable;
-	GList *list = NULL;
-	GIcon *icon;
-
-	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
-
-	cancellable = attachment->priv->cancellable;
-
-	if (g_cancellable_is_cancelled (cancellable)) {
-		icon = g_themed_icon_new (EMBLEM_CANCELLED);
-		list = g_list_append (list, g_emblem_new (icon));
-		g_object_unref (icon);
-	}
-
-	if (e_attachment_get_loading (attachment)) {
-		icon = g_themed_icon_new (EMBLEM_LOADING);
-		list = g_list_append (list, g_emblem_new (icon));
-		g_object_unref (icon);
-	}
-
-	if (e_attachment_get_saving (attachment)) {
-		icon = g_themed_icon_new (EMBLEM_SAVING);
-		list = g_list_append (list, g_emblem_new (icon));
-		g_object_unref (icon);
-	}
-
-	switch (e_attachment_get_encrypted (attachment)) {
-		case CAMEL_CIPHER_VALIDITY_ENCRYPT_WEAK:
-			icon = g_themed_icon_new (EMBLEM_ENCRYPT_WEAK);
-			list = g_list_append (list, g_emblem_new (icon));
-			g_object_unref (icon);
-			break;
-
-		case CAMEL_CIPHER_VALIDITY_ENCRYPT_ENCRYPTED:
-			icon = g_themed_icon_new (EMBLEM_ENCRYPT_UNKNOWN);
-			list = g_list_append (list, g_emblem_new (icon));
-			g_object_unref (icon);
-			break;
-
-		case CAMEL_CIPHER_VALIDITY_ENCRYPT_STRONG:
-			icon = g_themed_icon_new (EMBLEM_ENCRYPT_STRONG);
-			list = g_list_append (list, g_emblem_new (icon));
-			g_object_unref (icon);
-			break;
-
-		default:
-			break;
-	}
-
-	switch (e_attachment_get_signed (attachment)) {
-		case CAMEL_CIPHER_VALIDITY_SIGN_GOOD:
-			icon = g_themed_icon_new (EMBLEM_SIGN_GOOD);
-			list = g_list_append (list, g_emblem_new (icon));
-			g_object_unref (icon);
-			break;
-
-		case CAMEL_CIPHER_VALIDITY_SIGN_BAD:
-			icon = g_themed_icon_new (EMBLEM_SIGN_BAD);
-			list = g_list_append (list, g_emblem_new (icon));
-			g_object_unref (icon);
-			break;
-
-		case CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN:
-		case CAMEL_CIPHER_VALIDITY_SIGN_NEED_PUBLIC_KEY:
-			icon = g_themed_icon_new (EMBLEM_SIGN_UNKNOWN);
-			list = g_list_append (list, g_emblem_new (icon));
-			g_object_unref (icon);
-			break;
-
-		default:
-			break;
-	}
-
-	return list;
-}
-
 /************************* e_attachment_load_async() *************************/
 
-typedef struct _AttachmentLoadContext AttachmentLoadContext;
+typedef struct _LoadContext LoadContext;
 
-struct _AttachmentLoadContext {
+struct _LoadContext {
 	EAttachment *attachment;
 	GSimpleAsyncResult *simple;
+
 	GInputStream *input_stream;
 	GOutputStream *output_stream;
 	GFileInfo *file_info;
@@ -1414,21 +1277,21 @@
 static void
 attachment_load_stream_read_cb (GInputStream *input_stream,
                                 GAsyncResult *result,
-                                AttachmentLoadContext *load_context);
+                                LoadContext *load_context);
 
-static AttachmentLoadContext *
+static LoadContext *
 attachment_load_context_new (EAttachment *attachment,
                              GAsyncReadyCallback callback,
                              gpointer user_data)
 {
-	AttachmentLoadContext *load_context;
+	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 (AttachmentLoadContext);
+	load_context = g_slice_new0 (LoadContext);
 	load_context->attachment = g_object_ref (attachment);
 	load_context->simple = simple;
 
@@ -1438,7 +1301,7 @@
 }
 
 static void
-attachment_load_context_free (AttachmentLoadContext *load_context)
+attachment_load_context_free (LoadContext *load_context)
 {
 	/* Do not free the GSimpleAsyncResult. */
 	g_object_unref (load_context->attachment);
@@ -1452,11 +1315,33 @@
 	if (load_context->file_info != NULL)
 		g_object_unref (load_context->file_info);
 
-	g_slice_free (AttachmentLoadContext, load_context);
+	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 reference. */
+	simple = load_context->simple;
+	load_context->simple = NULL;
+
+	g_simple_async_result_set_from_error (simple, error);
+	g_simple_async_result_complete (simple);
+	g_error_free (error);
+
+	attachment_load_context_free (load_context);
+
+	return TRUE;
 }
 
 static void
-attachment_load_finish (AttachmentLoadContext *load_context)
+attachment_load_finish (LoadContext *load_context)
 {
 	GFileInfo *file_info;
 	EAttachment *attachment;
@@ -1529,7 +1414,7 @@
 static void
 attachment_load_write_cb (GOutputStream *output_stream,
                           GAsyncResult *result,
-                          AttachmentLoadContext *load_context)
+                          LoadContext *load_context)
 {
 	EAttachment *attachment;
 	GCancellable *cancellable;
@@ -1540,21 +1425,8 @@
 	bytes_written = g_output_stream_write_finish (
 		output_stream, result, &error);
 
-	if (error != NULL) {
-		GSimpleAsyncResult *simple;
-
-		/* Steal the reference. */
-		simple = load_context->simple;
-		load_context->simple = NULL;
-
-		g_simple_async_result_set_from_error (simple, error);
-		g_simple_async_result_complete (simple);
-		g_error_free (error);
-
-		attachment_load_context_free (load_context);
-
+	if (attachment_load_check_for_error (load_context, error))
 		return;
-	}
 
 	attachment = load_context->attachment;
 	cancellable = attachment->priv->cancellable;
@@ -1591,7 +1463,7 @@
 static void
 attachment_load_stream_read_cb (GInputStream *input_stream,
                                 GAsyncResult *result,
-                                AttachmentLoadContext *load_context)
+                                LoadContext *load_context)
 {
 	EAttachment *attachment;
 	GCancellable *cancellable;
@@ -1602,21 +1474,8 @@
 	bytes_read = g_input_stream_read_finish (
 		input_stream, result, &error);
 
-	if (error != NULL) {
-		GSimpleAsyncResult *simple;
-
-		/* Steal the reference. */
-		simple = load_context->simple;
-		load_context->simple = NULL;
-
-		g_simple_async_result_set_from_error (simple, error);
-		g_simple_async_result_complete (simple);
-		g_error_free (error);
-
-		attachment_load_context_free (load_context);
-
+	if (attachment_load_check_for_error (load_context, error))
 		return;
-	}
 
 	if (bytes_read == 0) {
 		attachment_load_finish (load_context);
@@ -1640,7 +1499,7 @@
 static void
 attachment_load_file_read_cb (GFile *file,
                               GAsyncResult *result,
-                              AttachmentLoadContext *load_context)
+                              LoadContext *load_context)
 {
 	EAttachment *attachment;
 	GCancellable *cancellable;
@@ -1648,24 +1507,12 @@
 	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 = G_INPUT_STREAM (input_stream);
-
-	if (error != NULL) {
-		GSimpleAsyncResult *simple;
-
-		/* Steal the reference. */
-		simple = load_context->simple;
-		load_context->simple = NULL;
-
-		g_simple_async_result_set_from_error (simple, error);
-		g_simple_async_result_complete (simple);
-		g_error_free (error);
-
-		attachment_load_context_free (load_context);
+	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 (
@@ -1687,7 +1534,7 @@
 static void
 attachment_load_query_info_cb (GFile *file,
                                GAsyncResult *result,
-                               AttachmentLoadContext *load_context)
+                               LoadContext *load_context)
 {
 	EAttachment *attachment;
 	GCancellable *cancellable;
@@ -1701,21 +1548,8 @@
 	attachment_set_file_info (attachment, file_info);
 	load_context->file_info = file_info;
 
-	if (error != NULL) {
-		GSimpleAsyncResult *simple;
-
-		/* Steal the reference. */
-		simple = load_context->simple;
-		load_context->simple = NULL;
-
-		g_simple_async_result_set_from_error (simple, error);
-		g_simple_async_result_complete (simple);
-		g_error_free (error);
-
-		attachment_load_context_free (load_context);
-
+	if (attachment_load_check_for_error (load_context, error))
 		return;
-	}
 
 	load_context->total_num_bytes = g_file_info_get_size (file_info);
 
@@ -1726,7 +1560,7 @@
 }
 
 static void
-attachment_load_from_mime_part (AttachmentLoadContext *load_context)
+attachment_load_from_mime_part (LoadContext *load_context)
 {
 	GFileInfo *file_info;
 	EAttachment *attachment;
@@ -1780,8 +1614,10 @@
 	g_free (allocated);
 
 	string = camel_mime_part_get_filename (mime_part);
-	if (string != NULL)
-		g_file_info_set_display_name (file_info, string);
+	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);
@@ -1815,7 +1651,7 @@
                          GAsyncReadyCallback callback,
                          gpointer user_data)
 {
-	AttachmentLoadContext *load_context;
+	LoadContext *load_context;
 	GCancellable *cancellable;
 	CamelMimePart *mime_part;
 	GFile *file;
@@ -1823,8 +1659,21 @@
 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
 	g_return_if_fail (callback != NULL);
 
-	g_return_if_fail (!e_attachment_get_loading (attachment));
-	g_return_if_fail (!e_attachment_get_saving (attachment));
+	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);
@@ -1836,16 +1685,16 @@
 	cancellable = attachment->priv->cancellable;
 	g_cancellable_reset (cancellable);
 
-	/* Handle the trivial case first. */
-	if (mime_part != NULL)
-		attachment_load_from_mime_part (load_context);
-
-	else if (file != NULL)
+	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);
+
+	else if (mime_part != NULL)
+		attachment_load_from_mime_part (load_context);
+
 }
 
 gboolean
@@ -1879,6 +1728,7 @@
 {
 	GtkWidget *dialog;
 	GFileInfo *file_info;
+	GtkTreeRowReference *reference;
 	const gchar *display_name;
 	const gchar *primary_text;
 	GError *error = NULL;
@@ -1890,6 +1740,18 @@
 	if (e_attachment_load_finish (attachment, result, &error))
 		return;
 
+	/* 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;
+
+		model = gtk_tree_row_reference_get_model (reference);
+
+		e_attachment_store_remove_attachment (
+			E_ATTACHMENT_STORE (model), attachment);
+	}
+
 	/* Ignore cancellations. */
 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		return;
@@ -1924,28 +1786,29 @@
 
 /************************* e_attachment_open_async() *************************/
 
-typedef struct _AttachmentOpenContext AttachmentOpenContext;
+typedef struct _OpenContext OpenContext;
 
-struct _AttachmentOpenContext {
+struct _OpenContext {
 	EAttachment *attachment;
 	GSimpleAsyncResult *simple;
+
 	GAppInfo *app_info;
 	GFile *file;
 };
 
-static AttachmentOpenContext *
+static OpenContext *
 attachment_open_context_new (EAttachment *attachment,
                              GAsyncReadyCallback callback,
                              gpointer user_data)
 {
-	AttachmentOpenContext *open_context;
+	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 (AttachmentOpenContext);
+	open_context = g_slice_new0 (OpenContext);
 	open_context->attachment = g_object_ref (attachment);
 	open_context->simple = simple;
 
@@ -1953,7 +1816,7 @@
 }
 
 static void
-attachment_open_context_free (AttachmentOpenContext *open_context)
+attachment_open_context_free (OpenContext *open_context)
 {
 	/* Do not free the GSimpleAsyncResult. */
 	g_object_unref (open_context->attachment);
@@ -1964,11 +1827,33 @@
 	if (open_context->file != NULL)
 		g_object_unref (open_context->file);
 
-	g_slice_free (AttachmentOpenContext, open_context);
+	g_slice_free (OpenContext, open_context);
+}
+
+static gboolean
+attachment_open_check_for_error (OpenContext *open_context,
+                                 GError *error)
+{
+	GSimpleAsyncResult *simple;
+
+	if (error == NULL)
+		return FALSE;
+
+	/* Steal the reference. */
+	simple = open_context->simple;
+	open_context->simple = NULL;
+
+	g_simple_async_result_set_from_error (simple, error);
+	g_simple_async_result_complete (simple);
+	g_error_free (error);
+
+	attachment_open_context_free (open_context);
+
+	return TRUE;
 }
 
 static void
-attachment_open_file (AttachmentOpenContext *open_context)
+attachment_open_file (OpenContext *open_context)
 {
 	GdkAppLaunchContext *context;
 	GSimpleAsyncResult *simple;
@@ -2027,49 +1912,29 @@
 static void
 attachment_open_save_finished_cb (EAttachment *attachment,
                                   GAsyncResult *result,
-                                  AttachmentOpenContext *open_context)
+                                  OpenContext *open_context)
 {
 	GError *error = NULL;
 
-	if (e_attachment_save_finish (attachment, result, &error))
-		attachment_open_file (open_context);
-	else {
-		GSimpleAsyncResult *simple;
-
-		/* Steal the reference. */
-		simple = open_context->simple;
-		open_context->simple = NULL;
+	e_attachment_save_finish (attachment, result, &error);
 
-		g_simple_async_result_set_from_error (simple, error);
-		g_simple_async_result_complete (simple);
-		g_error_free (error);
+	if (attachment_open_check_for_error (open_context, error))
+		return;
 
-		attachment_open_context_free (open_context);
-	}
+	attachment_open_file (open_context);
 }
 
 static void
-attachment_open_save_temporary (AttachmentOpenContext *open_context)
+attachment_open_save_temporary (OpenContext *open_context)
 {
 	gchar *path;
 	gint fd;
 	GError *error = NULL;
 
 	fd = e_file_open_tmp (&path, &error);
-	if (error != NULL) {
-		GSimpleAsyncResult *simple;
-
-		/* Steal the reference. */
-		simple = open_context->simple;
-		open_context->simple = NULL;
-
-		g_simple_async_result_set_from_error (simple, error);
-		g_simple_async_result_complete (simple);
-		g_error_free (error);
 
-		attachment_open_context_free (open_context);
+	if (attachment_open_check_for_error (open_context, error))
 		return;
-	}
 
 	close (fd);
 
@@ -2087,16 +1952,13 @@
                          GAsyncReadyCallback callback,
                          gpointer user_data)
 {
-	AttachmentOpenContext *open_context;
+	OpenContext *open_context;
 	CamelMimePart *mime_part;
 	GFile *file;
 
 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
 	g_return_if_fail (callback != NULL);
 
-	g_return_if_fail (!e_attachment_get_loading (attachment));
-	g_return_if_fail (!e_attachment_get_saving (attachment));
-
 	file = e_attachment_get_file (attachment);
 	mime_part = e_attachment_get_mime_part (attachment);
 	g_return_if_fail (file != NULL || mime_part != NULL);
@@ -2190,37 +2052,40 @@
 
 /************************* e_attachment_save_async() *************************/
 
-typedef struct _AttachmentSaveContext AttachmentSaveContext;
+typedef struct _SaveContext SaveContext;
 
-struct _AttachmentSaveContext {
+struct _SaveContext {
 	EAttachment *attachment;
 	GSimpleAsyncResult *simple;
+
+	GFile *directory;
 	GInputStream *input_stream;
 	GOutputStream *output_stream;
 	goffset total_num_bytes;
 	gssize bytes_read;
 	gchar buffer[4096];
+	gint count;
 };
 
 /* Forward Declaration */
 static void
 attachment_save_read_cb (GInputStream *input_stream,
                          GAsyncResult *result,
-                         AttachmentSaveContext *save_context);
+                         SaveContext *save_context);
 
-static AttachmentSaveContext *
+static SaveContext *
 attachment_save_context_new (EAttachment *attachment,
                              GAsyncReadyCallback callback,
                              gpointer user_data)
 {
-	AttachmentSaveContext *save_context;
+	SaveContext *save_context;
 	GSimpleAsyncResult *simple;
 
 	simple = g_simple_async_result_new (
 		G_OBJECT (attachment), callback,
 		user_data, e_attachment_save_async);
 
-	save_context = g_slice_new0 (AttachmentSaveContext);
+	save_context = g_slice_new0 (SaveContext);
 	save_context->attachment = g_object_ref (attachment);
 	save_context->simple = simple;
 
@@ -2230,49 +2095,98 @@
 }
 
 static void
-attachment_save_context_free (AttachmentSaveContext *save_context)
+attachment_save_context_free (SaveContext *save_context)
 {
 	/* Do not free the GSimpleAsyncResult. */
 	g_object_unref (save_context->attachment);
 
+	if (save_context->directory != NULL)
+		g_object_unref (save_context->directory);
+
 	if (save_context->input_stream != NULL)
 		g_object_unref (save_context->input_stream);
 
 	if (save_context->output_stream != NULL)
 		g_object_unref (save_context->output_stream);
 
-	g_slice_free (AttachmentSaveContext, save_context);
+	g_slice_free (SaveContext, save_context);
 }
 
-static void
-attachment_save_file_cb (GFile *source,
-                         GAsyncResult *result,
-                         AttachmentSaveContext *save_context)
+static gboolean
+attachment_save_check_for_error (SaveContext *save_context,
+                                 GError *error)
 {
 	GSimpleAsyncResult *simple;
-	gboolean success;
-	GError *error = NULL;
+
+	if (error == NULL)
+		return FALSE;
 
 	/* Steal the reference. */
 	simple = save_context->simple;
 	save_context->simple = NULL;
 
-	success = g_file_copy_finish (source, result, &error);
-	g_simple_async_result_set_op_res_gboolean (simple, success);
+	g_simple_async_result_set_from_error (simple, error);
+	g_simple_async_result_complete (simple);
+	g_error_free (error);
 
-	if (error != NULL) {
-		g_simple_async_result_set_from_error (simple, error);
-		g_error_free (error);
+	attachment_save_context_free (save_context);
+
+	return TRUE;
+}
+
+static GFile *
+attachment_save_new_candidate (SaveContext *save_context)
+{
+	GFile *candidate;
+	GFileInfo *file_info;
+	EAttachment *attachment;
+	const gchar *display_name;
+	gchar *basename;
+
+	attachment = save_context->attachment;
+	file_info = e_attachment_get_file_info (attachment);
+
+	if (file_info != NULL)
+		display_name = g_file_info_get_display_name (file_info);
+	if (display_name == NULL)
+		/* Translators: Default attachment filename. */
+		display_name = _("attachment.dat");
+
+	if (save_context->count == 0)
+		basename = g_strdup (display_name);
+	else {
+		GString *string;
+		const gchar *ext;
+		gsize length;
+
+		string = g_string_sized_new (strlen (display_name));
+		ext = g_utf8_strchr (display_name, -1, '.');
+
+		if (ext != NULL)
+			length = ext - display_name;
+		else
+			length = strlen (display_name);
+
+		g_string_append_len (string, display_name, length);
+		g_string_append_printf (string, " (%d)", save_context->count);
+		g_string_append (string, (ext != NULL) ? ext : "");
+
+		basename = g_string_free (string, FALSE);
 	}
 
-	g_simple_async_result_complete (simple);
-	attachment_save_context_free (save_context);
+	save_context->count++;
+
+	candidate = g_file_get_child (save_context->directory, basename);
+
+	g_free (basename);
+
+	return candidate;
 }
 
 static void
 attachment_save_write_cb (GOutputStream *output_stream,
                           GAsyncResult *result,
-                          AttachmentSaveContext *save_context)
+                          SaveContext *save_context)
 {
 	EAttachment *attachment;
 	GCancellable *cancellable;
@@ -2283,21 +2197,8 @@
 	bytes_written = g_output_stream_write_finish (
 		output_stream, result, &error);
 
-	if (error != NULL) {
-		GSimpleAsyncResult *simple;
-
-		/* Steal the reference. */
-		simple = save_context->simple;
-		save_context->simple = NULL;
-
-		g_simple_async_result_set_from_error (simple, error);
-		g_simple_async_result_complete (simple);
-		g_error_free (error);
-
-		attachment_save_context_free (save_context);
-
+	if (attachment_save_check_for_error (save_context, error))
 		return;
-	}
 
 	attachment = save_context->attachment;
 	cancellable = attachment->priv->cancellable;
@@ -2330,7 +2231,7 @@
 static void
 attachment_save_read_cb (GInputStream *input_stream,
                          GAsyncResult *result,
-                         AttachmentSaveContext *save_context)
+                         SaveContext *save_context)
 {
 	EAttachment *attachment;
 	GCancellable *cancellable;
@@ -2341,21 +2242,8 @@
 	bytes_read = g_input_stream_read_finish (
 		input_stream, result, &error);
 
-	if (error != NULL) {
-		GSimpleAsyncResult *simple;
-
-		/* Steal the reference. */
-		simple = save_context->simple;
-		save_context->simple = NULL;
-
-		g_simple_async_result_set_from_error (simple, error);
-		g_simple_async_result_complete (simple);
-		g_error_free (error);
-
-		attachment_save_context_free (save_context);
-
+	if (attachment_save_check_for_error (save_context, error))
 		return;
-	}
 
 	if (bytes_read == 0) {
 		GSimpleAsyncResult *simple;
@@ -2391,38 +2279,15 @@
 }
 
 static void
-attachment_save_replace_cb (GFile *destination,
-                            GAsyncResult *result,
-                            AttachmentSaveContext *save_context)
+attachment_save_got_output_stream (SaveContext *save_context)
 {
 	GCancellable *cancellable;
 	GInputStream *input_stream;
-	GFileOutputStream *output_stream;
 	CamelDataWrapper *wrapper;
 	CamelMimePart *mime_part;
 	CamelStream *stream;
 	EAttachment *attachment;
 	GByteArray *buffer;
-	GError *error = NULL;
-
-	output_stream = g_file_replace_finish (destination, result, &error);
-	save_context->output_stream = G_OUTPUT_STREAM (output_stream);
-
-	if (error != NULL) {
-		GSimpleAsyncResult *simple;
-
-		/* Steal the reference. */
-		simple = save_context->simple;
-		save_context->simple = NULL;
-
-		g_simple_async_result_set_from_error (simple, error);
-		g_simple_async_result_complete (simple);
-		g_error_free (error);
-
-		attachment_save_context_free (save_context);
-
-		return;
-	}
 
 	attachment = save_context->attachment;
 	cancellable = attachment->priv->cancellable;
@@ -2455,30 +2320,149 @@
 		save_context);
 }
 
+static void
+attachment_save_create_cb (GFile *destination,
+                           GAsyncResult *result,
+                           SaveContext *save_context)
+{
+	EAttachment *attachment;
+	GCancellable *cancellable;
+	GFileOutputStream *output_stream;
+	GError *error = NULL;
+
+	/* Output stream might be NULL, so don't use cast macro. */
+	output_stream = g_file_create_finish (destination, result, &error);
+	save_context->output_stream = (GOutputStream *) output_stream;
+
+	attachment = save_context->attachment;
+	cancellable = attachment->priv->cancellable;
+
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
+		destination = attachment_save_new_candidate (save_context);
+
+		g_file_create_async (
+			destination, G_FILE_CREATE_NONE,
+			G_PRIORITY_DEFAULT, cancellable,
+			(GAsyncReadyCallback) attachment_save_create_cb,
+			save_context);
+
+		g_object_unref (destination);
+		g_error_free (error);
+		return;
+	}
+
+	if (attachment_save_check_for_error (save_context, error))
+		return;
+
+	attachment_save_got_output_stream (save_context);
+}
+
+static void
+attachment_save_replace_cb (GFile *destination,
+                            GAsyncResult *result,
+                            SaveContext *save_context)
+{
+	GFileOutputStream *output_stream;
+	GError *error = NULL;
+
+	/* Output stream might be NULL, so don't use cast macro. */
+	output_stream = g_file_replace_finish (destination, result, &error);
+	save_context->output_stream = (GOutputStream *) output_stream;
+
+	if (attachment_save_check_for_error (save_context, error))
+		return;
+
+	attachment_save_got_output_stream (save_context);
+}
+
+static void
+attachment_save_query_info_cb (GFile *destination,
+                               GAsyncResult *result,
+                               SaveContext *save_context)
+{
+	EAttachment *attachment;
+	GCancellable *cancellable;
+	GFileInfo *file_info;
+	GFileType file_type;
+	GError *error = NULL;
+
+	attachment = save_context->attachment;
+	cancellable = attachment->priv->cancellable;
+
+	file_info = g_file_query_info_finish (destination, result, &error);
+
+	/* G_IO_ERROR_NOT_FOUND just means we're creating a new file. */
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
+		g_error_free (error);
+		goto replace;
+	}
+
+	if (attachment_save_check_for_error (save_context, error))
+		return;
+
+	file_type = g_file_info_get_file_type (file_info);
+	g_object_unref (file_info);
+
+	if (file_type == G_FILE_TYPE_DIRECTORY) {
+		save_context->directory = g_object_ref (destination);
+		destination = attachment_save_new_candidate (save_context);
+
+		g_file_create_async (
+			destination, G_FILE_CREATE_NONE,
+			G_PRIORITY_DEFAULT, cancellable,
+			(GAsyncReadyCallback) attachment_save_create_cb,
+			save_context);
+
+		g_object_unref (destination);
+
+		return;
+	}
+
+replace:
+	g_file_replace_async (
+		destination, NULL, FALSE,
+		G_FILE_CREATE_REPLACE_DESTINATION,
+		G_PRIORITY_DEFAULT, cancellable,
+		(GAsyncReadyCallback) attachment_save_replace_cb,
+		save_context);
+}
+
 void
 e_attachment_save_async (EAttachment *attachment,
                          GFile *destination,
                          GAsyncReadyCallback callback,
                          gpointer user_data)
 {
-	AttachmentSaveContext *save_context;
+	SaveContext *save_context;
 	GCancellable *cancellable;
-	CamelMimePart *mime_part;
-	GFile *source;
 
 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
 	g_return_if_fail (G_IS_FILE (destination));
 	g_return_if_fail (callback != NULL);
 
-	g_return_if_fail (!e_attachment_get_loading (attachment));
-	g_return_if_fail (!e_attachment_get_saving (attachment));
+	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;
+	}
 
-	/* The attachment content is either a GFile (on disk) or a
-	 * CamelMimePart (in memory).  Each is saved differently. */
+	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;
+	}
 
-	source = e_attachment_get_file (attachment);
-	mime_part = e_attachment_get_mime_part (attachment);
-	g_return_if_fail (source != NULL || mime_part != NULL);
+	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;
+	}
 
 	save_context = attachment_save_context_new (
 		attachment, callback, user_data);
@@ -2486,34 +2470,12 @@
 	cancellable = attachment->priv->cancellable;
 	g_cancellable_reset (cancellable);
 
-	/* GFile is the easier, but probably less common case.  The
-	 * attachment already references an on-disk file, so we can
-	 * just use GIO to copy it asynchronously.
-	 *
-	 * We use G_FILE_COPY_OVERWRITE because the user should have
-	 * already confirmed the overwrite through the save dialog. */
-	if (G_IS_FILE (source))
-		g_file_copy_async (
-			source, destination,
-			G_FILE_COPY_OVERWRITE,
-			G_PRIORITY_DEFAULT, cancellable,
-			(GFileProgressCallback) attachment_progress_cb,
-			attachment,
-			(GAsyncReadyCallback) attachment_save_file_cb,
-			save_context);
-
-	/* CamelMimePart can only be decoded to a file synchronously, so
-	 * we do this in two stages.  Stage one asynchronously opens the
-	 * destination file for writing.  Stage two spawns a thread that
-	 * decodes the MIME part to the destination file.  This stage is
-	 * not cancellable, unfortunately. */
-	else if (CAMEL_IS_MIME_PART (mime_part))
-		g_file_replace_async (
-			destination, NULL, FALSE,
-			G_FILE_CREATE_REPLACE_DESTINATION,
-			G_PRIORITY_DEFAULT, cancellable,
-			(GAsyncReadyCallback) attachment_save_replace_cb,
-			save_context);
+	/* 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);
 }
 
 gboolean

Modified: branches/kill-bonobo/widgets/misc/e-attachment.h
==============================================================================
--- branches/kill-bonobo/widgets/misc/e-attachment.h	(original)
+++ branches/kill-bonobo/widgets/misc/e-attachment.h	Mon Mar 30 03:38:36 2009
@@ -101,7 +101,6 @@
 gboolean	e_attachment_is_image		(EAttachment *attachment);
 gboolean	e_attachment_is_rfc822		(EAttachment *attachment);
 GList *		e_attachment_list_apps		(EAttachment *attachment);
-GList *		e_attachment_list_emblems	(EAttachment *attachment);
 
 /* Asynchronous Operations */
 void		e_attachment_load_async		(EAttachment *attachment,



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