diff options
author | Matthew Barnes <mbarnes@src.gnome.org> | 2009-03-30 11:38:36 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@src.gnome.org> | 2009-03-30 11:38:36 +0800 |
commit | 8a1f639a670696e71daac8305ae0823668d29a14 (patch) | |
tree | 3b7c8ec8f856095865211a40ebcaf33ca572d94f /widgets/misc | |
parent | bc4f60ba176b64cc8fd8ffa47a01542f8eb25bdf (diff) | |
download | gsoc2013-evolution-8a1f639a670696e71daac8305ae0823668d29a14.tar.gz gsoc2013-evolution-8a1f639a670696e71daac8305ae0823668d29a14.tar.zst gsoc2013-evolution-8a1f639a670696e71daac8305ae0823668d29a14.zip |
Saving progress again on the attachment rewrite.
svn path=/branches/kill-bonobo/; revision=37482
Diffstat (limited to 'widgets/misc')
-rw-r--r-- | widgets/misc/e-attachment-icon-view.c | 139 | ||||
-rw-r--r-- | widgets/misc/e-attachment-paned.c | 142 | ||||
-rw-r--r-- | widgets/misc/e-attachment-store.c | 54 | ||||
-rw-r--r-- | widgets/misc/e-attachment-store.h | 2 | ||||
-rw-r--r-- | widgets/misc/e-attachment-tree-view.c | 134 | ||||
-rw-r--r-- | widgets/misc/e-attachment-view.c | 248 | ||||
-rw-r--r-- | widgets/misc/e-attachment-view.h | 45 | ||||
-rw-r--r-- | widgets/misc/e-attachment.c | 740 | ||||
-rw-r--r-- | widgets/misc/e-attachment.h | 1 |
9 files changed, 990 insertions, 515 deletions
diff --git a/widgets/misc/e-attachment-icon-view.c b/widgets/misc/e-attachment-icon-view.c index f6e42b2fbe..9588540f05 100644 --- a/widgets/misc/e-attachment-icon-view.c +++ b/widgets/misc/e-attachment-icon-view.c @@ -101,10 +101,8 @@ attachment_icon_view_button_press_event (GtkWidget *widget, { 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 @@ attachment_icon_view_button_press_event (GtkWidget *widget, } 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 @@ attachment_icon_view_key_press_event (GtkWidget *widget, 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 @@ attachment_icon_view_drag_motion (GtkWidget *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, @@ -271,6 +337,55 @@ attachment_icon_view_unselect_all (EAttachmentView *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; @@ -288,8 +403,13 @@ attachment_icon_view_class_init (EAttachmentIconViewClass *class) 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 @@ attachment_icon_view_iface_init (EAttachmentViewIface *iface) 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 @@ 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); @@ -331,7 +455,7 @@ attachment_icon_view_init (EAttachmentIconView *icon_view) 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 @@ attachment_icon_view_init (EAttachmentIconView *icon_view) 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", diff --git a/widgets/misc/e-attachment-paned.c b/widgets/misc/e-attachment-paned.c index 5134282526..42890236e9 100644 --- a/widgets/misc/e-attachment-paned.c +++ b/widgets/misc/e-attachment-paned.c @@ -467,10 +467,12 @@ attachment_paned_iface_init (EAttachmentViewIface *iface) 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 @@ attachment_paned_init (EAttachmentPaned *paned) /* 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 @@ attachment_paned_init (EAttachmentPaned *paned) 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 @@ attachment_paned_init (EAttachmentPaned *paned) 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)); diff --git a/widgets/misc/e-attachment-store.c b/widgets/misc/e-attachment-store.c index 3b273f929c..0187d2a8c8 100644 --- a/widgets/misc/e-attachment-store.c +++ b/widgets/misc/e-attachment-store.c @@ -674,23 +674,34 @@ exit: 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 @@ e_attachment_store_run_save_dialog (EAttachmentStore *store, 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 @@ e_attachment_store_run_save_dialog (EAttachmentStore *store, 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); diff --git a/widgets/misc/e-attachment-store.h b/widgets/misc/e-attachment-store.h index e7f74102be..88b2823313 100644 --- a/widgets/misc/e-attachment-store.h +++ b/widgets/misc/e-attachment-store.h @@ -103,7 +103,7 @@ void e_attachment_store_run_load_dialog GtkWindow *parent); void e_attachment_store_run_save_dialog (EAttachmentStore *store, - EAttachment *attachment, + GList *attachment_list, GtkWindow *parent); G_END_DECLS diff --git a/widgets/misc/e-attachment-tree-view.c b/widgets/misc/e-attachment-tree-view.c index 548ed3aa4f..5a613f4f17 100644 --- a/widgets/misc/e-attachment-tree-view.c +++ b/widgets/misc/e-attachment-tree-view.c @@ -119,10 +119,8 @@ attachment_tree_view_button_press_event (GtkWidget *widget, { 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 @@ attachment_tree_view_button_press_event (GtkWidget *widget, } 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 @@ attachment_tree_view_key_press_event (GtkWidget *widget, 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 @@ attachment_tree_view_drag_motion (GtkWidget *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, @@ -307,6 +373,55 @@ attachment_tree_view_unselect_all (EAttachmentView *view) } 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 @@ attachment_tree_view_class_init (EAttachmentTreeViewClass *class) 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 @@ attachment_tree_view_iface_init (EAttachmentViewIface *iface) 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 @@ attachment_tree_view_init (EAttachmentTreeView *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); @@ -417,7 +541,6 @@ attachment_tree_view_init (EAttachmentTreeView *tree_view) 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_init (EAttachmentTreeView *tree_view) 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); diff --git a/widgets/misc/e-attachment-view.c b/widgets/misc/e-attachment-view.c index cb735fbfec..e9e546e449 100644 --- a/widgets/misc/e-attachment-view.c +++ b/widgets/misc/e-attachment-view.c @@ -223,21 +223,16 @@ action_save_as_cb (GtkAction *action, 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 @@ static GtkActionEntry editable_entries[] = { 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 @@ e_attachment_view_init (EAttachmentView *view) 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 @@ e_attachment_view_remove_selected (EAttachmentView *view, 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 @@ e_attachment_view_sync_selection (EAttachmentView *view, } 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 @@ e_attachment_view_drag_motion (EAttachmentView *view, 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 @@ e_attachment_view_drag_motion (EAttachmentView *view, 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 @@ e_attachment_view_drag_data_received (EAttachmentView *view, 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 @@ e_attachment_view_drag_data_received (EAttachmentView *view, 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 @@ e_attachment_view_show_popup_menu (EAttachmentView *view, 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 @@ e_attachment_view_update_actions (EAttachmentView *view) 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); diff --git a/widgets/misc/e-attachment-view.h b/widgets/misc/e-attachment-view.h index 96a0f2dc16..d33ad951ac 100644 --- a/widgets/misc/e-attachment-view.h +++ b/widgets/misc/e-attachment-view.h @@ -72,6 +72,19 @@ struct _EAttachmentViewIface { 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 @@ void e_attachment_view_remove_selected (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_unselect_all (EAttachmentView *view); 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 @@ gboolean e_attachment_view_drag_motion (EAttachmentView *view, 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, diff --git a/widgets/misc/e-attachment.c b/widgets/misc/e-attachment.c index 7044aa8cde..d06e49dc30 100644 --- a/widgets/misc/e-attachment.c +++ b/widgets/misc/e-attachment.c @@ -135,6 +135,7 @@ attachment_update_file_info_columns (EAttachment *attachment) 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 @@ attachment_update_file_info_columns (EAttachment *attachment) 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 @@ attachment_update_file_info_columns (EAttachment *attachment) 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 @@ attachment_update_icon_column (EAttachment *attachment) } 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 @@ attachment_update_percent_column (EAttachment *attachment) 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 @@ attachment_set_file_info (EAttachment *attachment, 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 @@ attachment_set_loading (EAttachment *attachment, 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 @@ attachment_init (EAttachment *attachment) 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 @@ attachment_init (EAttachment *attachment) 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 @@ attachment_init (EAttachment *attachment) 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 @@ e_attachment_list_apps (EAttachment *attachment) 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 @@ exit: 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 @@ struct _AttachmentLoadContext { 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 @@ attachment_load_context_new (EAttachment *attachment, } 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 @@ attachment_load_context_free (AttachmentLoadContext *load_context) 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 @@ attachment_load_finish (AttachmentLoadContext *load_context) 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 @@ attachment_load_write_cb (GOutputStream *output_stream, 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 @@ attachment_load_write_cb (GOutputStream *output_stream, 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 @@ attachment_load_stream_read_cb (GInputStream *input_stream, 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 @@ attachment_load_stream_read_cb (GInputStream *input_stream, 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 @@ attachment_load_file_read_cb (GFile *file, 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 @@ attachment_load_file_read_cb (GFile *file, 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_load_query_info_cb (GFile *file, 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 @@ attachment_load_query_info_cb (GFile *file, } 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 @@ attachment_load_from_mime_part (AttachmentLoadContext *load_context) 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 @@ e_attachment_load_async (EAttachment *attachment, GAsyncReadyCallback callback, gpointer user_data) { - AttachmentLoadContext *load_context; + LoadContext *load_context; GCancellable *cancellable; CamelMimePart *mime_part; GFile *file; @@ -1823,8 +1659,21 @@ e_attachment_load_async (EAttachment *attachment, 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 @@ e_attachment_load_async (EAttachment *attachment, 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 @@ e_attachment_load_handle_error (EAttachment *attachment, { GtkWidget *dialog; GFileInfo *file_info; + GtkTreeRowReference *reference; const gchar *display_name; const gchar *primary_text; GError *error = NULL; @@ -1890,6 +1740,18 @@ e_attachment_load_handle_error (EAttachment *attachment, 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_load_handle_error (EAttachment *attachment, /************************* 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 @@ attachment_open_context_new (EAttachment *attachment, } 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 @@ attachment_open_context_free (AttachmentOpenContext *open_context) 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 @@ exit: 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 @@ e_attachment_open_async (EAttachment *attachment, 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_open_handle_error (EAttachment *attachment, /************************* 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 @@ attachment_save_context_new (EAttachment *attachment, } 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 @@ attachment_save_write_cb (GOutputStream *output_stream, 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 @@ attachment_save_write_cb (GOutputStream *output_stream, 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 @@ attachment_save_read_cb (GInputStream *input_stream, 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 @@ attachment_save_read_cb (GInputStream *input_stream, } 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 @@ attachment_save_replace_cb (GFile *destination, 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 @@ e_attachment_save_async (EAttachment *attachment, 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 diff --git a/widgets/misc/e-attachment.h b/widgets/misc/e-attachment.h index 1ca5b3121b..8a13da2909 100644 --- a/widgets/misc/e-attachment.h +++ b/widgets/misc/e-attachment.h @@ -101,7 +101,6 @@ const gchar * e_attachment_get_thumbnail_path (EAttachment *attachment); gboolean e_attachment_is_image (EAttachment *attachment); gboolean e_attachment_is_rfc822 (EAttachment *attachment); GList * e_attachment_list_apps (EAttachment *attachment); -GList * e_attachment_list_emblems (EAttachment *attachment); /* Asynchronous Operations */ void e_attachment_load_async (EAttachment *attachment, |