aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--calendar/gui/dialogs/comp-editor.c2
-rw-r--r--composer/e-composer-private.c2
-rw-r--r--composer/e-msg-composer.c2
-rw-r--r--composer/evolution-composer.ui3
-rw-r--r--shell/e-shell.c20
-rw-r--r--widgets/misc/e-attachment-icon-view.c139
-rw-r--r--widgets/misc/e-attachment-paned.c142
-rw-r--r--widgets/misc/e-attachment-store.c54
-rw-r--r--widgets/misc/e-attachment-store.h2
-rw-r--r--widgets/misc/e-attachment-tree-view.c134
-rw-r--r--widgets/misc/e-attachment-view.c248
-rw-r--r--widgets/misc/e-attachment-view.h45
-rw-r--r--widgets/misc/e-attachment.c740
-rw-r--r--widgets/misc/e-attachment.h1
14 files changed, 1013 insertions, 521 deletions
diff --git a/calendar/gui/dialogs/comp-editor.c b/calendar/gui/dialogs/comp-editor.c
index ce53f8f79f..d2226e4d2a 100644
--- a/calendar/gui/dialogs/comp-editor.c
+++ b/calendar/gui/dialogs/comp-editor.c
@@ -158,8 +158,6 @@ static const gchar *ui =
" <toolitem action='print'/>"
" <toolitem action='close'/>"
" <separator/>"
-" <toolitem action='attach'/>"
-" <separator/>"
" </toolbar>"
"</ui>";
diff --git a/composer/e-composer-private.c b/composer/e-composer-private.c
index aba1fdbe9f..91d71850fd 100644
--- a/composer/e-composer-private.c
+++ b/composer/e-composer-private.c
@@ -152,7 +152,7 @@ e_composer_private_init (EMsgComposer *composer)
/* 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);
diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c
index fd7a9939f6..f6e85b78c8 100644
--- a/composer/e-msg-composer.c
+++ b/composer/e-msg-composer.c
@@ -2285,6 +2285,7 @@ msg_composer_init (EMsgComposer *composer)
/* 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 @@ msg_composer_init (EMsgComposer *composer)
g_signal_connect (
html, "drag-data-received",
G_CALLBACK (msg_composer_drag_data_received), NULL);
+#endif
/* Configure Headers */
diff --git a/composer/evolution-composer.ui b/composer/evolution-composer.ui
index 1b8957640f..8c422c06f4 100644
--- a/composer/evolution-composer.ui
+++ b/composer/evolution-composer.ui
@@ -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>
diff --git a/shell/e-shell.c b/shell/e-shell.c
index 5de46bcc65..49292c0c35 100644
--- a/shell/e-shell.c
+++ b/shell/e-shell.c
@@ -801,7 +801,17 @@ shell_class_init (EShellClass *class)
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 @@ shell_class_init (EShellClass *class)
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
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,