diff options
Diffstat (limited to 'e-util/e-attachment-view.c')
-rw-r--r-- | e-util/e-attachment-view.c | 1906 |
1 files changed, 1906 insertions, 0 deletions
diff --git a/e-util/e-attachment-view.c b/e-util/e-attachment-view.c new file mode 100644 index 0000000000..e468c14120 --- /dev/null +++ b/e-util/e-attachment-view.c @@ -0,0 +1,1906 @@ +/* + * e-attachment-view.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-attachment-view.h" + +#include <glib/gi18n.h> +#include <gdk/gdkkeysyms.h> + +#include "e-attachment-dialog.h" +#include "e-attachment-handler-image.h" +#include "e-attachment-handler-sendto.h" +#include "e-misc-utils.h" +#include "e-selection.h" +#include "e-ui-manager.h" + +enum { + UPDATE_ACTIONS, + LAST_SIGNAL +}; + +/* Note: Do not use the info field. */ +static GtkTargetEntry target_table[] = { + { (gchar *) "_NETSCAPE_URL", 0, 0 } +}; + +static const gchar *ui = +"<ui>" +" <popup name='context'>" +" <menuitem action='cancel'/>" +" <menuitem action='save-as'/>" +" <menuitem action='remove'/>" +" <menuitem action='properties'/>" +" <separator/>" +" <placeholder name='inline-actions'>" +" <menuitem action='show'/>" +" <menuitem action='show-all'/>" +" <separator/>" +" <menuitem action='hide'/>" +" <menuitem action='hide-all'/>" +" </placeholder>" +" <separator/>" +" <placeholder name='custom-actions'/>" +" <separator/>" +" <menuitem action='add'/>" +" <separator/>" +" <placeholder name='open-actions'/>" +" <menuitem action='open-with'/>" +" </popup>" +"</ui>"; + +static gulong signals[LAST_SIGNAL]; + +G_DEFINE_INTERFACE ( + EAttachmentView, + e_attachment_view, + GTK_TYPE_WIDGET) + +static void +action_add_cb (GtkAction *action, + EAttachmentView *view) +{ + EAttachmentStore *store; + gpointer parent; + + parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); + parent = gtk_widget_is_toplevel (parent) ? parent : NULL; + + store = e_attachment_view_get_store (view); + e_attachment_store_run_load_dialog (store, parent); +} + +static void +action_cancel_cb (GtkAction *action, + EAttachmentView *view) +{ + EAttachment *attachment; + GList *list; + + list = e_attachment_view_get_selected_attachments (view); + g_return_if_fail (g_list_length (list) == 1); + attachment = list->data; + + e_attachment_cancel (attachment); + + g_list_foreach (list, (GFunc) g_object_unref, NULL); + g_list_free (list); +} + +static void +action_hide_cb (GtkAction *action, + EAttachmentView *view) +{ + EAttachment *attachment; + GList *list; + + list = e_attachment_view_get_selected_attachments (view); + g_return_if_fail (g_list_length (list) == 1); + attachment = list->data; + + e_attachment_set_shown (attachment, FALSE); + + g_list_foreach (list, (GFunc) g_object_unref, NULL); + g_list_free (list); +} + +static void +action_hide_all_cb (GtkAction *action, + EAttachmentView *view) +{ + EAttachmentStore *store; + GList *list, *iter; + + store = e_attachment_view_get_store (view); + list = e_attachment_store_get_attachments (store); + + for (iter = list; iter != NULL; iter = iter->next) { + EAttachment *attachment; + + attachment = E_ATTACHMENT (iter->data); + e_attachment_set_shown (attachment, FALSE); + } + + g_list_foreach (list, (GFunc) g_object_unref, NULL); + g_list_free (list); +} + +static void +action_open_with_cb (GtkAction *action, + EAttachmentView *view) +{ + EAttachment *attachment; + EAttachmentStore *store; + GtkWidget *dialog; + GtkTreePath *path; + GtkTreeIter iter; + GAppInfo *app_info = NULL; + GFileInfo *file_info; + GList *list; + gpointer parent; + const gchar *content_type; + + parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); + parent = gtk_widget_is_toplevel (parent) ? parent : NULL; + + list = e_attachment_view_get_selected_paths (view); + g_return_if_fail (g_list_length (list) == 1); + path = list->data; + + store = e_attachment_view_get_store (view); + gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path); + gtk_tree_model_get ( + GTK_TREE_MODEL (store), &iter, + E_ATTACHMENT_STORE_COLUMN_ATTACHMENT, &attachment, -1); + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + + file_info = e_attachment_get_file_info (attachment); + g_return_if_fail (file_info != NULL); + + content_type = g_file_info_get_content_type (file_info); + + dialog = gtk_app_chooser_dialog_new_for_content_type ( + parent, 0, content_type); + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { + GtkAppChooser *app_chooser = GTK_APP_CHOOSER (dialog); + app_info = gtk_app_chooser_get_app_info (app_chooser); + } + gtk_widget_destroy (dialog); + + if (app_info != NULL) { + e_attachment_view_open_path (view, path, app_info); + g_object_unref (app_info); + } + + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); +} + +static void +action_open_with_app_info_cb (GtkAction *action, + EAttachmentView *view) +{ + GAppInfo *app_info; + GtkTreePath *path; + GList *list; + + list = e_attachment_view_get_selected_paths (view); + g_return_if_fail (g_list_length (list) == 1); + path = list->data; + + app_info = g_object_get_data (G_OBJECT (action), "app-info"); + g_return_if_fail (G_IS_APP_INFO (app_info)); + + e_attachment_view_open_path (view, path, app_info); + + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); +} + +static void +action_properties_cb (GtkAction *action, + EAttachmentView *view) +{ + EAttachment *attachment; + GtkWidget *dialog; + GList *list; + gpointer parent; + + list = e_attachment_view_get_selected_attachments (view); + g_return_if_fail (g_list_length (list) == 1); + attachment = list->data; + + parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); + parent = gtk_widget_is_toplevel (parent) ? parent : NULL; + + dialog = e_attachment_dialog_new (parent, attachment); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + g_list_foreach (list, (GFunc) g_object_unref, NULL); + g_list_free (list); +} + +static void +action_remove_cb (GtkAction *action, + EAttachmentView *view) +{ + e_attachment_view_remove_selected (view, FALSE); +} + +static void +action_save_all_cb (GtkAction *action, + EAttachmentView *view) +{ + EAttachmentStore *store; + GList *list, *iter; + GFile *destination; + gpointer parent; + + store = e_attachment_view_get_store (view); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); + parent = gtk_widget_is_toplevel (parent) ? parent : NULL; + + /* XXX We lose the previous selection. */ + e_attachment_view_select_all (view); + list = e_attachment_view_get_selected_attachments (view); + e_attachment_view_unselect_all (view); + + destination = e_attachment_store_run_save_dialog ( + store, list, parent); + + if (destination == NULL) + goto exit; + + for (iter = list; iter != NULL; iter = iter->next) { + EAttachment *attachment = iter->data; + + e_attachment_save_async ( + attachment, destination, (GAsyncReadyCallback) + e_attachment_save_handle_error, parent); + } + + g_object_unref (destination); + +exit: + g_list_foreach (list, (GFunc) g_object_unref, NULL); + g_list_free (list); +} + +static void +action_save_as_cb (GtkAction *action, + EAttachmentView *view) +{ + EAttachmentStore *store; + GList *list, *iter; + GFile *destination; + gpointer parent; + + store = e_attachment_view_get_store (view); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); + parent = gtk_widget_is_toplevel (parent) ? parent : NULL; + + list = e_attachment_view_get_selected_attachments (view); + + destination = e_attachment_store_run_save_dialog ( + store, list, parent); + + if (destination == NULL) + goto exit; + + for (iter = list; iter != NULL; iter = iter->next) { + EAttachment *attachment = iter->data; + + e_attachment_save_async ( + attachment, destination, (GAsyncReadyCallback) + e_attachment_save_handle_error, parent); + } + + g_object_unref (destination); + +exit: + g_list_foreach (list, (GFunc) g_object_unref, NULL); + g_list_free (list); +} + +static void +action_show_cb (GtkAction *action, + EAttachmentView *view) +{ + EAttachment *attachment; + GList *list; + + list = e_attachment_view_get_selected_attachments (view); + g_return_if_fail (g_list_length (list) == 1); + attachment = list->data; + + e_attachment_set_shown (attachment, TRUE); + + g_list_foreach (list, (GFunc) g_object_unref, NULL); + g_list_free (list); +} + +static void +action_show_all_cb (GtkAction *action, + EAttachmentView *view) +{ + EAttachmentStore *store; + GList *list, *iter; + + store = e_attachment_view_get_store (view); + list = e_attachment_store_get_attachments (store); + + for (iter = list; iter != NULL; iter = iter->next) { + EAttachment *attachment; + + attachment = E_ATTACHMENT (iter->data); + e_attachment_set_shown (attachment, TRUE); + } + + g_list_foreach (list, (GFunc) g_object_unref, NULL); + g_list_free (list); +} + +static GtkActionEntry standard_entries[] = { + + { "cancel", + GTK_STOCK_CANCEL, + NULL, + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_cancel_cb) }, + + { "open-with", + NULL, + N_("Open With Other Application..."), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_open_with_cb) }, + + { "save-all", + GTK_STOCK_SAVE_AS, + N_("S_ave All"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_save_all_cb) }, + + { "save-as", + GTK_STOCK_SAVE_AS, + NULL, + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_save_as_cb) }, + + /* Alternate "save-all" label, for when + * the attachment store has one row. */ + { "save-one", + GTK_STOCK_SAVE_AS, + NULL, + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_save_all_cb) }, +}; + +static GtkActionEntry editable_entries[] = { + + { "add", + GTK_STOCK_ADD, + N_("A_dd Attachment..."), + NULL, + N_("Attach a file"), + G_CALLBACK (action_add_cb) }, + + { "properties", + GTK_STOCK_PROPERTIES, + NULL, + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_properties_cb) }, + + { "remove", + GTK_STOCK_REMOVE, + NULL, + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_remove_cb) } +}; + +static GtkActionEntry inline_entries[] = { + + { "hide", + NULL, + N_("_Hide"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_hide_cb) }, + + { "hide-all", + NULL, + N_("Hid_e All"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_hide_all_cb) }, + + { "show", + NULL, + N_("_View Inline"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_show_cb) }, + + { "show-all", + NULL, + N_("Vie_w All Inline"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_show_all_cb) } +}; + +static void +attachment_view_netscape_url (EAttachmentView *view, + GdkDragContext *drag_context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + static GdkAtom atom = GDK_NONE; + EAttachmentStore *store; + EAttachment *attachment; + const gchar *data; + gpointer parent; + gchar *copied_data; + gchar **strv; + gint length; + + if (G_UNLIKELY (atom == GDK_NONE)) + atom = gdk_atom_intern_static_string ("_NETSCAPE_URL"); + + if (gtk_selection_data_get_target (selection_data) != atom) + return; + + g_signal_stop_emission_by_name (view, "drag-data-received"); + + /* _NETSCAPE_URL is represented as "URI\nTITLE" */ + + data = (const gchar *) gtk_selection_data_get_data (selection_data); + length = gtk_selection_data_get_length (selection_data); + + copied_data = g_strndup (data, length); + strv = g_strsplit (copied_data, "\n", 2); + g_free (copied_data); + + store = e_attachment_view_get_store (view); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); + parent = gtk_widget_is_toplevel (parent) ? parent : NULL; + + attachment = e_attachment_new_for_uri (strv[0]); + e_attachment_store_add_attachment (store, attachment); + e_attachment_load_async ( + attachment, (GAsyncReadyCallback) + e_attachment_load_handle_error, parent); + g_object_unref (attachment); + + g_strfreev (strv); + + gtk_drag_finish (drag_context, TRUE, FALSE, time); +} + +static void +attachment_view_text_calendar (EAttachmentView *view, + GdkDragContext *drag_context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + EAttachmentStore *store; + EAttachment *attachment; + CamelMimePart *mime_part; + GdkAtom data_type; + GdkAtom target; + const gchar *data; + gpointer parent; + gchar *content_type; + gint length; + + target = gtk_selection_data_get_target (selection_data); + if (!e_targets_include_calendar (&target, 1)) + return; + + g_signal_stop_emission_by_name (view, "drag-data-received"); + + data = (const gchar *) gtk_selection_data_get_data (selection_data); + length = gtk_selection_data_get_length (selection_data); + data_type = gtk_selection_data_get_data_type (selection_data); + + mime_part = camel_mime_part_new (); + + content_type = gdk_atom_name (data_type); + camel_mime_part_set_content (mime_part, data, length, content_type); + camel_mime_part_set_disposition (mime_part, "inline"); + g_free (content_type); + + store = e_attachment_view_get_store (view); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); + parent = gtk_widget_is_toplevel (parent) ? parent : NULL; + + attachment = e_attachment_new (); + e_attachment_set_mime_part (attachment, mime_part); + e_attachment_store_add_attachment (store, attachment); + e_attachment_load_async ( + attachment, (GAsyncReadyCallback) + e_attachment_load_handle_error, parent); + g_object_unref (attachment); + + g_object_unref (mime_part); + + gtk_drag_finish (drag_context, TRUE, FALSE, time); +} + +static void +attachment_view_text_x_vcard (EAttachmentView *view, + GdkDragContext *drag_context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + EAttachmentStore *store; + EAttachment *attachment; + CamelMimePart *mime_part; + GdkAtom data_type; + GdkAtom target; + const gchar *data; + gpointer parent; + gchar *content_type; + gint length; + + target = gtk_selection_data_get_target (selection_data); + if (!e_targets_include_directory (&target, 1)) + return; + + g_signal_stop_emission_by_name (view, "drag-data-received"); + + data = (const gchar *) gtk_selection_data_get_data (selection_data); + length = gtk_selection_data_get_length (selection_data); + data_type = gtk_selection_data_get_data_type (selection_data); + + mime_part = camel_mime_part_new (); + + content_type = gdk_atom_name (data_type); + camel_mime_part_set_content (mime_part, data, length, content_type); + camel_mime_part_set_disposition (mime_part, "inline"); + g_free (content_type); + + store = e_attachment_view_get_store (view); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); + parent = gtk_widget_is_toplevel (parent) ? parent : NULL; + + attachment = e_attachment_new (); + e_attachment_set_mime_part (attachment, mime_part); + e_attachment_store_add_attachment (store, attachment); + e_attachment_load_async ( + attachment, (GAsyncReadyCallback) + e_attachment_load_handle_error, parent); + g_object_unref (attachment); + + g_object_unref (mime_part); + + gtk_drag_finish (drag_context, TRUE, FALSE, time); +} + +static void +attachment_view_uris (EAttachmentView *view, + GdkDragContext *drag_context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + EAttachmentStore *store; + gpointer parent; + gchar **uris; + gint ii; + + uris = gtk_selection_data_get_uris (selection_data); + + if (uris == NULL) + return; + + g_signal_stop_emission_by_name (view, "drag-data-received"); + + store = e_attachment_view_get_store (view); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); + parent = gtk_widget_is_toplevel (parent) ? parent : NULL; + + for (ii = 0; uris[ii] != NULL; ii++) { + EAttachment *attachment; + + attachment = e_attachment_new_for_uri (uris[ii]); + e_attachment_store_add_attachment (store, attachment); + e_attachment_load_async ( + attachment, (GAsyncReadyCallback) + e_attachment_load_handle_error, parent); + g_object_unref (attachment); + } + + g_strfreev (uris); + + gtk_drag_finish (drag_context, TRUE, FALSE, time); +} + +static void +attachment_view_update_actions (EAttachmentView *view) +{ + EAttachmentViewPrivate *priv; + EAttachment *attachment; + EAttachmentStore *store; + GtkActionGroup *action_group; + GtkAction *action; + GList *list, *iter; + guint n_shown = 0; + guint n_hidden = 0; + guint n_selected; + gboolean busy = FALSE; + gboolean can_show = FALSE; + gboolean shown = FALSE; + gboolean visible; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + + priv = e_attachment_view_get_private (view); + + store = e_attachment_view_get_store (view); + list = e_attachment_store_get_attachments (store); + + for (iter = list; iter != NULL; iter = iter->next) { + attachment = iter->data; + + if (!e_attachment_get_can_show (attachment)) + continue; + + if (e_attachment_get_shown (attachment)) + n_shown++; + else + n_hidden++; + } + + g_list_foreach (list, (GFunc) g_object_unref, NULL); + g_list_free (list); + + list = e_attachment_view_get_selected_attachments (view); + n_selected = g_list_length (list); + + if (n_selected == 1) { + attachment = g_object_ref (list->data); + busy |= e_attachment_get_loading (attachment); + busy |= e_attachment_get_saving (attachment); + can_show = e_attachment_get_can_show (attachment); + shown = e_attachment_get_shown (attachment); + } else + attachment = NULL; + + g_list_foreach (list, (GFunc) g_object_unref, NULL); + g_list_free (list); + + action = e_attachment_view_get_action (view, "cancel"); + gtk_action_set_visible (action, busy); + + action = e_attachment_view_get_action (view, "hide"); + gtk_action_set_visible (action, can_show && shown); + + /* Show this action if there are multiple viewable + * attachments, and at least one of them is shown. */ + visible = (n_shown + n_hidden > 1) && (n_shown > 0); + action = e_attachment_view_get_action (view, "hide-all"); + gtk_action_set_visible (action, visible); + + action = e_attachment_view_get_action (view, "open-with"); + gtk_action_set_visible (action, !busy && n_selected == 1); + + action = e_attachment_view_get_action (view, "properties"); + gtk_action_set_visible (action, !busy && n_selected == 1); + + action = e_attachment_view_get_action (view, "remove"); + gtk_action_set_visible (action, !busy && n_selected > 0); + + action = e_attachment_view_get_action (view, "save-as"); + gtk_action_set_visible (action, !busy && n_selected > 0); + + action = e_attachment_view_get_action (view, "show"); + gtk_action_set_visible (action, can_show && !shown); + + /* Show this action if there are multiple viewable + * attachments, and at least one of them is hidden. */ + visible = (n_shown + n_hidden > 1) && (n_hidden > 0); + action = e_attachment_view_get_action (view, "show-all"); + gtk_action_set_visible (action, visible); + + /* Clear out the "openwith" action group. */ + gtk_ui_manager_remove_ui (priv->ui_manager, priv->merge_id); + action_group = e_attachment_view_get_action_group (view, "openwith"); + e_action_group_remove_all_actions (action_group); + gtk_ui_manager_ensure_update (priv->ui_manager); + + if (attachment == NULL || busy) + return; + + list = e_attachment_list_apps (attachment); + + for (iter = list; iter != NULL; iter = iter->next) { + GAppInfo *app_info = iter->data; + GtkAction *action; + GIcon *app_icon; + const gchar *app_executable; + const gchar *app_name; + gchar *action_tooltip; + gchar *action_label; + gchar *action_name; + + app_executable = g_app_info_get_executable (app_info); + app_icon = g_app_info_get_icon (app_info); + app_name = g_app_info_get_name (app_info); + + action_name = g_strdup_printf ("open-with-%s", app_executable); + action_label = g_strdup_printf (_("Open With \"%s\""), app_name); + + action_tooltip = g_strdup_printf ( + _("Open this attachment in %s"), app_name); + + action = gtk_action_new ( + action_name, action_label, action_tooltip, NULL); + + gtk_action_set_gicon (action, app_icon); + + g_object_set_data_full ( + G_OBJECT (action), + "app-info", g_object_ref (app_info), + (GDestroyNotify) g_object_unref); + + g_object_set_data_full ( + G_OBJECT (action), + "attachment", g_object_ref (attachment), + (GDestroyNotify) g_object_unref); + + g_signal_connect ( + action, "activate", + G_CALLBACK (action_open_with_app_info_cb), view); + + gtk_action_group_add_action (action_group, action); + + gtk_ui_manager_add_ui ( + priv->ui_manager, priv->merge_id, + "/context/open-actions", action_name, + action_name, GTK_UI_MANAGER_AUTO, FALSE); + + g_free (action_name); + g_free (action_label); + g_free (action_tooltip); + } + + g_object_unref (attachment); + g_list_foreach (list, (GFunc) g_object_unref, NULL); + g_list_free (list); +} + +static void +attachment_view_init_drag_dest (EAttachmentView *view) +{ + EAttachmentViewPrivate *priv; + GtkTargetList *target_list; + + priv = e_attachment_view_get_private (view); + + target_list = gtk_target_list_new ( + target_table, G_N_ELEMENTS (target_table)); + + gtk_target_list_add_uri_targets (target_list, 0); + e_target_list_add_calendar_targets (target_list, 0); + e_target_list_add_directory_targets (target_list, 0); + + priv->target_list = target_list; + priv->drag_actions = GDK_ACTION_COPY; +} + +static void +e_attachment_view_default_init (EAttachmentViewInterface *interface) +{ + interface->update_actions = attachment_view_update_actions; + + g_object_interface_install_property ( + interface, + g_param_spec_boolean ( + "dragging", + "Dragging", + NULL, + FALSE, + G_PARAM_READWRITE)); + + g_object_interface_install_property ( + interface, + g_param_spec_boolean ( + "editable", + "Editable", + NULL, + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + signals[UPDATE_ACTIONS] = g_signal_new ( + "update-actions", + G_TYPE_FROM_INTERFACE (interface), + G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EAttachmentViewInterface, update_actions), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /* Register known handler types. */ + e_attachment_handler_image_get_type (); + e_attachment_handler_sendto_get_type (); +} + +void +e_attachment_view_init (EAttachmentView *view) +{ + EAttachmentViewPrivate *priv; + GtkUIManager *ui_manager; + GtkActionGroup *action_group; + GError *error = NULL; + + priv = e_attachment_view_get_private (view); + + ui_manager = e_ui_manager_new (); + priv->merge_id = gtk_ui_manager_new_merge_id (ui_manager); + priv->ui_manager = ui_manager; + + action_group = e_attachment_view_add_action_group (view, "standard"); + + gtk_action_group_add_actions ( + action_group, standard_entries, + G_N_ELEMENTS (standard_entries), view); + + action_group = e_attachment_view_add_action_group (view, "editable"); + + g_object_bind_property ( + view, "editable", + action_group, "visible", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE); + gtk_action_group_add_actions ( + action_group, editable_entries, + G_N_ELEMENTS (editable_entries), view); + + action_group = e_attachment_view_add_action_group (view, "inline"); + + gtk_action_group_add_actions ( + action_group, inline_entries, + G_N_ELEMENTS (inline_entries), view); + gtk_action_group_set_visible (action_group, FALSE); + + e_attachment_view_add_action_group (view, "openwith"); + + /* Because we are loading from a hard-coded string, there is + * no chance of I/O errors. Failure here implies a malformed + * UI definition. Full stop. */ + gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error); + if (error != NULL) + g_error ("%s", error->message); + + attachment_view_init_drag_dest (view); + + e_attachment_view_drag_source_set (view); + + /* Connect built-in drag and drop handlers. */ + + g_signal_connect ( + view, "drag-data-received", + G_CALLBACK (attachment_view_netscape_url), NULL); + + g_signal_connect ( + view, "drag-data-received", + G_CALLBACK (attachment_view_text_calendar), NULL); + + g_signal_connect ( + view, "drag-data-received", + G_CALLBACK (attachment_view_text_x_vcard), NULL); + + g_signal_connect ( + view, "drag-data-received", + G_CALLBACK (attachment_view_uris), NULL); +} + +void +e_attachment_view_dispose (EAttachmentView *view) +{ + EAttachmentViewPrivate *priv; + + priv = e_attachment_view_get_private (view); + + if (priv->target_list != NULL) { + gtk_target_list_unref (priv->target_list); + priv->target_list = NULL; + } + + if (priv->ui_manager != NULL) { + g_object_unref (priv->ui_manager); + priv->ui_manager = NULL; + } +} + +void +e_attachment_view_finalize (EAttachmentView *view) +{ + EAttachmentViewPrivate *priv; + + priv = e_attachment_view_get_private (view); + + g_list_foreach (priv->event_list, (GFunc) gdk_event_free, NULL); + g_list_free (priv->event_list); + + g_list_foreach (priv->selected, (GFunc) g_object_unref, NULL); + g_list_free (priv->selected); +} + +EAttachmentViewPrivate * +e_attachment_view_get_private (EAttachmentView *view) +{ + EAttachmentViewInterface *interface; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL); + + interface = E_ATTACHMENT_VIEW_GET_INTERFACE (view); + g_return_val_if_fail (interface->get_private != NULL, NULL); + + return interface->get_private (view); +} + +EAttachmentStore * +e_attachment_view_get_store (EAttachmentView *view) +{ + EAttachmentViewInterface *interface; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL); + + interface = E_ATTACHMENT_VIEW_GET_INTERFACE (view); + g_return_val_if_fail (interface->get_store != NULL, NULL); + + return interface->get_store (view); +} + +gboolean +e_attachment_view_get_editable (EAttachmentView *view) +{ + EAttachmentViewPrivate *priv; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE); + + priv = e_attachment_view_get_private (view); + + return priv->editable; +} + +void +e_attachment_view_set_editable (EAttachmentView *view, + gboolean editable) +{ + EAttachmentViewPrivate *priv; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + + priv = e_attachment_view_get_private (view); + + priv->editable = editable; + + if (editable) + e_attachment_view_drag_dest_set (view); + else + e_attachment_view_drag_dest_unset (view); + + g_object_notify (G_OBJECT (view), "editable"); +} + +gboolean +e_attachment_view_get_dragging (EAttachmentView *view) +{ + EAttachmentViewPrivate *priv; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE); + + priv = e_attachment_view_get_private (view); + + return priv->dragging; +} + +void +e_attachment_view_set_dragging (EAttachmentView *view, + gboolean dragging) +{ + EAttachmentViewPrivate *priv; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + + priv = e_attachment_view_get_private (view); + + priv->dragging = dragging; + + g_object_notify (G_OBJECT (view), "dragging"); +} + +GtkTargetList * +e_attachment_view_get_target_list (EAttachmentView *view) +{ + EAttachmentViewPrivate *priv; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL); + + priv = e_attachment_view_get_private (view); + + return priv->target_list; +} + +GdkDragAction +e_attachment_view_get_drag_actions (EAttachmentView *view) +{ + EAttachmentViewPrivate *priv; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), 0); + + priv = e_attachment_view_get_private (view); + + return priv->drag_actions; +} + +void +e_attachment_view_add_drag_actions (EAttachmentView *view, + GdkDragAction drag_actions) +{ + EAttachmentViewPrivate *priv; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + + priv = e_attachment_view_get_private (view); + + priv->drag_actions |= drag_actions; +} + +GList * +e_attachment_view_get_selected_attachments (EAttachmentView *view) +{ + EAttachmentStore *store; + GtkTreeModel *model; + GList *list, *item; + gint column_id; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL); + + column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT; + list = e_attachment_view_get_selected_paths (view); + store = e_attachment_view_get_store (view); + model = GTK_TREE_MODEL (store); + + /* Convert the GtkTreePaths to EAttachments. */ + for (item = list; item != NULL; item = item->next) { + EAttachment *attachment; + GtkTreePath *path; + GtkTreeIter iter; + + path = item->data; + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, column_id, &attachment, -1); + gtk_tree_path_free (path); + + item->data = attachment; + } + + return list; +} + +void +e_attachment_view_open_path (EAttachmentView *view, + GtkTreePath *path, + GAppInfo *app_info) +{ + EAttachmentStore *store; + EAttachment *attachment; + GtkTreeModel *model; + GtkTreeIter iter; + gpointer parent; + gint column_id; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + g_return_if_fail (path != NULL); + + column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT; + store = e_attachment_view_get_store (view); + model = GTK_TREE_MODEL (store); + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, column_id, &attachment, -1); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); + parent = gtk_widget_is_toplevel (parent) ? parent : NULL; + + e_attachment_open_async ( + attachment, app_info, (GAsyncReadyCallback) + e_attachment_open_handle_error, parent); + + g_object_unref (attachment); +} + +void +e_attachment_view_remove_selected (EAttachmentView *view, + gboolean select_next) +{ + EAttachmentStore *store; + GtkTreeModel *model; + GList *list, *item; + gint column_id; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + + column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT; + list = e_attachment_view_get_selected_paths (view); + store = e_attachment_view_get_store (view); + model = GTK_TREE_MODEL (store); + + /* Remove attachments in reverse order to avoid invalidating + * tree paths as we iterate over the list. Note, the list is + * probably already sorted but we sort again just to be safe. */ + list = g_list_reverse (g_list_sort ( + list, (GCompareFunc) gtk_tree_path_compare)); + + for (item = list; item != NULL; item = item->next) { + EAttachment *attachment; + GtkTreePath *path = item->data; + GtkTreeIter iter; + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, column_id, &attachment, -1); + e_attachment_store_remove_attachment (store, attachment); + g_object_unref (attachment); + } + + /* If we only removed one attachment, try to select another. */ + if (select_next && g_list_length (list) == 1) { + GtkTreePath *path = list->data; + + e_attachment_view_select_path (view, path); + if (!e_attachment_view_path_is_selected (view, path)) + if (gtk_tree_path_prev (path)) + e_attachment_view_select_path (view, path); + } + + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); +} + +gboolean +e_attachment_view_button_press_event (EAttachmentView *view, + GdkEventButton *event) +{ + EAttachmentViewPrivate *priv; + GtkTreePath *path; + gboolean editable; + gboolean handled = FALSE; + gboolean path_is_selected = FALSE; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + priv = e_attachment_view_get_private (view); + + if (g_list_find (priv->event_list, event) != NULL) + return FALSE; + + if (priv->event_list != NULL) { + /* Save the event to be propagated in order. */ + priv->event_list = g_list_append ( + priv->event_list, + gdk_event_copy ((GdkEvent *) event)); + return TRUE; + } + + editable = e_attachment_view_get_editable (view); + path = e_attachment_view_get_path_at_pos (view, event->x, event->y); + path_is_selected = e_attachment_view_path_is_selected (view, path); + + if (event->button == 1 && event->type == GDK_BUTTON_PRESS) { + GList *list, *iter; + gboolean busy = FALSE; + + list = e_attachment_view_get_selected_attachments (view); + + for (iter = list; iter != NULL; iter = iter->next) { + EAttachment *attachment = iter->data; + busy |= e_attachment_get_loading (attachment); + busy |= e_attachment_get_saving (attachment); + } + + /* Prepare for dragging if the clicked item is selected + * and none of the selected items are loading or saving. */ + if (path_is_selected && !busy) { + priv->start_x = event->x; + priv->start_y = event->y; + priv->event_list = g_list_append ( + priv->event_list, + gdk_event_copy ((GdkEvent *) event)); + handled = TRUE; + } + + g_list_foreach (list, (GFunc) g_object_unref, NULL); + g_list_free (list); + } + + if (event->button == 3 && event->type == GDK_BUTTON_PRESS) { + /* 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. */ + if (path == NULL) + e_attachment_view_unselect_all (view); + else if (!path_is_selected) { + e_attachment_view_unselect_all (view); + e_attachment_view_select_path (view, path); + } + + /* Non-editable attachment views should only show a + * popup menu when right-clicking on an attachment, + * but editable views can show the menu any time. */ + if (path != NULL || editable) { + e_attachment_view_show_popup_menu ( + view, event, NULL, NULL); + handled = TRUE; + } + } + + if (path != NULL) + gtk_tree_path_free (path); + + return handled; +} + +gboolean +e_attachment_view_button_release_event (EAttachmentView *view, + GdkEventButton *event) +{ + EAttachmentViewPrivate *priv; + GtkWidget *widget = GTK_WIDGET (view); + GList *iter; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + priv = e_attachment_view_get_private (view); + + for (iter = priv->event_list; iter != NULL; iter = iter->next) { + GdkEvent *event = iter->data; + + gtk_propagate_event (widget, event); + gdk_event_free (event); + } + + g_list_free (priv->event_list); + priv->event_list = NULL; + + return FALSE; +} + +gboolean +e_attachment_view_motion_notify_event (EAttachmentView *view, + GdkEventMotion *event) +{ + EAttachmentViewPrivate *priv; + GtkWidget *widget = GTK_WIDGET (view); + GtkTargetList *targets; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + priv = e_attachment_view_get_private (view); + + if (priv->event_list == NULL) + return FALSE; + + if (!gtk_drag_check_threshold ( + widget, priv->start_x, priv->start_y, event->x, event->y)) + return TRUE; + + g_list_foreach (priv->event_list, (GFunc) gdk_event_free, NULL); + g_list_free (priv->event_list); + priv->event_list = NULL; + + targets = gtk_drag_source_get_target_list (widget); + + gtk_drag_begin ( + widget, targets, GDK_ACTION_COPY, 1, (GdkEvent *) event); + + return TRUE; +} + +gboolean +e_attachment_view_key_press_event (EAttachmentView *view, + GdkEventKey *event) +{ + gboolean editable; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + editable = e_attachment_view_get_editable (view); + + if (event->keyval == GDK_KEY_Delete && editable) { + e_attachment_view_remove_selected (view, TRUE); + return TRUE; + } + + return FALSE; +} + +GtkTreePath * +e_attachment_view_get_path_at_pos (EAttachmentView *view, + gint x, + gint y) +{ + EAttachmentViewInterface *interface; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL); + + interface = E_ATTACHMENT_VIEW_GET_INTERFACE (view); + g_return_val_if_fail (interface->get_path_at_pos != NULL, NULL); + + return interface->get_path_at_pos (view, x, y); +} + +GList * +e_attachment_view_get_selected_paths (EAttachmentView *view) +{ + EAttachmentViewInterface *interface; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL); + + interface = E_ATTACHMENT_VIEW_GET_INTERFACE (view); + g_return_val_if_fail (interface->get_selected_paths != NULL, NULL); + + return interface->get_selected_paths (view); +} + +gboolean +e_attachment_view_path_is_selected (EAttachmentView *view, + GtkTreePath *path) +{ + EAttachmentViewInterface *interface; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE); + + /* Handle NULL paths gracefully. */ + if (path == NULL) + return FALSE; + + interface = E_ATTACHMENT_VIEW_GET_INTERFACE (view); + g_return_val_if_fail (interface->path_is_selected != NULL, FALSE); + + return interface->path_is_selected (view, path); +} + +void +e_attachment_view_select_path (EAttachmentView *view, + GtkTreePath *path) +{ + EAttachmentViewInterface *interface; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + g_return_if_fail (path != NULL); + + interface = E_ATTACHMENT_VIEW_GET_INTERFACE (view); + g_return_if_fail (interface->select_path != NULL); + + interface->select_path (view, path); +} + +void +e_attachment_view_unselect_path (EAttachmentView *view, + GtkTreePath *path) +{ + EAttachmentViewInterface *interface; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + g_return_if_fail (path != NULL); + + interface = E_ATTACHMENT_VIEW_GET_INTERFACE (view); + g_return_if_fail (interface->unselect_path != NULL); + + interface->unselect_path (view, path); +} + +void +e_attachment_view_select_all (EAttachmentView *view) +{ + EAttachmentViewInterface *interface; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + + interface = E_ATTACHMENT_VIEW_GET_INTERFACE (view); + g_return_if_fail (interface->select_all != NULL); + + interface->select_all (view); +} + +void +e_attachment_view_unselect_all (EAttachmentView *view) +{ + EAttachmentViewInterface *interface; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + + interface = E_ATTACHMENT_VIEW_GET_INTERFACE (view); + g_return_if_fail (interface->unselect_all != NULL); + + interface->unselect_all (view); +} + +void +e_attachment_view_sync_selection (EAttachmentView *view, + EAttachmentView *target) +{ + GList *list, *iter; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + g_return_if_fail (E_IS_ATTACHMENT_VIEW (target)); + + list = e_attachment_view_get_selected_paths (view); + e_attachment_view_unselect_all (target); + + for (iter = list; iter != NULL; iter = iter->next) + e_attachment_view_select_path (target, iter->data); + + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); +} + +void +e_attachment_view_drag_source_set (EAttachmentView *view) +{ + EAttachmentViewInterface *interface; + GtkTargetEntry *targets; + GtkTargetList *list; + gint n_targets; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + + interface = E_ATTACHMENT_VIEW_GET_INTERFACE (view); + if (interface->drag_source_set == NULL) + return; + + 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); + + interface->drag_source_set ( + 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) +{ + EAttachmentViewInterface *interface; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + + interface = E_ATTACHMENT_VIEW_GET_INTERFACE (view); + if (interface->drag_source_unset == NULL) + return; + + interface->drag_source_unset (view); +} + +void +e_attachment_view_drag_begin (EAttachmentView *view, + GdkDragContext *context) +{ + EAttachmentViewPrivate *priv; + guint n_selected; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + g_return_if_fail (GDK_IS_DRAG_CONTEXT (context)); + + priv = e_attachment_view_get_private (view); + + e_attachment_view_set_dragging (view, TRUE); + + g_warn_if_fail (priv->selected == NULL); + priv->selected = e_attachment_view_get_selected_attachments (view); + n_selected = g_list_length (priv->selected); + + if (n_selected > 1) + gtk_drag_set_icon_stock ( + context, GTK_STOCK_DND_MULTIPLE, 0, 0); + + else if (n_selected == 1) { + EAttachment *attachment; + GtkIconTheme *icon_theme; + GtkIconInfo *icon_info; + GIcon *icon; + gint width, height; + + attachment = E_ATTACHMENT (priv->selected->data); + icon = e_attachment_get_icon (attachment); + g_return_if_fail (icon != NULL); + + icon_theme = gtk_icon_theme_get_default (); + gtk_icon_size_lookup (GTK_ICON_SIZE_DND, &width, &height); + + icon_info = gtk_icon_theme_lookup_by_gicon ( + icon_theme, icon, MIN (width, height), + GTK_ICON_LOOKUP_USE_BUILTIN); + + if (icon_info != NULL) { + GdkPixbuf *pixbuf; + GError *error = NULL; + + pixbuf = gtk_icon_info_load_icon (icon_info, &error); + + if (pixbuf != NULL) { + gtk_drag_set_icon_pixbuf ( + context, pixbuf, 0, 0); + g_object_unref (pixbuf); + } else if (error != NULL) { + g_warning ("%s", error->message); + g_error_free (error); + } + + gtk_icon_info_free (icon_info); + } + } +} + +void +e_attachment_view_drag_end (EAttachmentView *view, + GdkDragContext *context) +{ + EAttachmentViewPrivate *priv; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + g_return_if_fail (GDK_IS_DRAG_CONTEXT (context)); + + priv = e_attachment_view_get_private (view); + + e_attachment_view_set_dragging (view, FALSE); + + g_list_foreach (priv->selected, (GFunc) g_object_unref, NULL); + g_list_free (priv->selected); + priv->selected = NULL; +} + +static void +attachment_view_got_uris_cb (EAttachmentStore *store, + GAsyncResult *result, + gpointer user_data) +{ + struct { + gchar **uris; + gboolean done; + } *status = user_data; + + /* XXX Since this is a best-effort function, + * should we care about errors? */ + status->uris = e_attachment_store_get_uris_finish ( + store, result, NULL); + + status->done = TRUE; +} + +void +e_attachment_view_drag_data_get (EAttachmentView *view, + GdkDragContext *context, + GtkSelectionData *selection, + guint info, + guint time) +{ + EAttachmentViewPrivate *priv; + EAttachmentStore *store; + + struct { + gchar **uris; + gboolean done; + } status; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + g_return_if_fail (GDK_IS_DRAG_CONTEXT (context)); + g_return_if_fail (selection != NULL); + + status.uris = NULL; + status.done = FALSE; + + priv = e_attachment_view_get_private (view); + store = e_attachment_view_get_store (view); + + if (priv->selected == NULL) + return; + + e_attachment_store_get_uris_async ( + store, priv->selected, (GAsyncReadyCallback) + attachment_view_got_uris_cb, &status); + + /* We can't return until we have results, so crank + * the main loop until the callback gets triggered. */ + while (!status.done) + if (gtk_main_iteration ()) + break; + + if (status.uris != NULL) + gtk_selection_data_set_uris (selection, status.uris); + + g_strfreev (status.uris); +} + +void +e_attachment_view_drag_dest_set (EAttachmentView *view) +{ + EAttachmentViewPrivate *priv; + EAttachmentViewInterface *interface; + GtkTargetEntry *targets; + gint n_targets; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + + interface = E_ATTACHMENT_VIEW_GET_INTERFACE (view); + if (interface->drag_dest_set == NULL) + return; + + priv = e_attachment_view_get_private (view); + + targets = gtk_target_table_new_from_list ( + priv->target_list, &n_targets); + + interface->drag_dest_set ( + view, targets, n_targets, priv->drag_actions); + + gtk_target_table_free (targets, n_targets); +} + +void +e_attachment_view_drag_dest_unset (EAttachmentView *view) +{ + EAttachmentViewInterface *interface; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + + interface = E_ATTACHMENT_VIEW_GET_INTERFACE (view); + if (interface->drag_dest_unset == NULL) + return; + + interface->drag_dest_unset (view); +} + +gboolean +e_attachment_view_drag_motion (EAttachmentView *view, + GdkDragContext *context, + gint x, + gint y, + guint time) +{ + EAttachmentViewPrivate *priv; + GdkDragAction actions; + GdkDragAction chosen_action; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE); + g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), FALSE); + + priv = e_attachment_view_get_private (view); + + /* Disallow drops if we're not editable. */ + if (!e_attachment_view_get_editable (view)) + return FALSE; + + /* Disallow drops if we initiated the drag. + * This helps prevent duplicate attachments. */ + if (e_attachment_view_get_dragging (view)) + return FALSE; + + actions = gdk_drag_context_get_actions (context); + actions &= priv->drag_actions; + chosen_action = gdk_drag_context_get_suggested_action (context); + + if (chosen_action == GDK_ACTION_ASK) { + GdkDragAction mask; + + mask = GDK_ACTION_COPY | GDK_ACTION_MOVE; + if ((actions & mask) != mask) + chosen_action = GDK_ACTION_COPY; + } + + gdk_drag_status (context, chosen_action, time); + + return (chosen_action != 0); +} + +gboolean +e_attachment_view_drag_drop (EAttachmentView *view, + GdkDragContext *context, + gint x, + gint y, + guint time) +{ + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE); + g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), FALSE); + + /* Disallow drops if we initiated the drag. + * This helps prevent duplicate attachments. */ + return !e_attachment_view_get_dragging (view); +} + +void +e_attachment_view_drag_data_received (EAttachmentView *view, + GdkDragContext *drag_context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + GdkAtom atom; + gchar *name; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + g_return_if_fail (GDK_IS_DRAG_CONTEXT (drag_context)); + + /* Drop handlers are supposed to stop further emission of the + * "drag-data-received" signal if they can handle the data. If + * we get this far it means none of the handlers were successful, + * so report the drop as failed. */ + + atom = gtk_selection_data_get_target (selection_data); + + name = gdk_atom_name (atom); + g_warning ("Unknown selection target: %s", name); + g_free (name); + + gtk_drag_finish (drag_context, FALSE, FALSE, time); +} + +GtkAction * +e_attachment_view_get_action (EAttachmentView *view, + const gchar *action_name) +{ + GtkUIManager *ui_manager; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL); + g_return_val_if_fail (action_name != NULL, NULL); + + ui_manager = e_attachment_view_get_ui_manager (view); + + return e_lookup_action (ui_manager, action_name); +} + +GtkActionGroup * +e_attachment_view_add_action_group (EAttachmentView *view, + const gchar *group_name) +{ + GtkActionGroup *action_group; + GtkUIManager *ui_manager; + const gchar *domain; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL); + g_return_val_if_fail (group_name != NULL, NULL); + + ui_manager = e_attachment_view_get_ui_manager (view); + domain = GETTEXT_PACKAGE; + + action_group = gtk_action_group_new (group_name); + gtk_action_group_set_translation_domain (action_group, domain); + gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); + g_object_unref (action_group); + + return action_group; +} + +GtkActionGroup * +e_attachment_view_get_action_group (EAttachmentView *view, + const gchar *group_name) +{ + GtkUIManager *ui_manager; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL); + g_return_val_if_fail (group_name != NULL, NULL); + + ui_manager = e_attachment_view_get_ui_manager (view); + + return e_lookup_action_group (ui_manager, group_name); +} + +GtkWidget * +e_attachment_view_get_popup_menu (EAttachmentView *view) +{ + GtkUIManager *ui_manager; + GtkWidget *menu; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL); + + ui_manager = e_attachment_view_get_ui_manager (view); + menu = gtk_ui_manager_get_widget (ui_manager, "/context"); + g_return_val_if_fail (GTK_IS_MENU (menu), NULL); + + return menu; +} + +GtkUIManager * +e_attachment_view_get_ui_manager (EAttachmentView *view) +{ + EAttachmentViewPrivate *priv; + + g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL); + + priv = e_attachment_view_get_private (view); + + return priv->ui_manager; +} + +void +e_attachment_view_show_popup_menu (EAttachmentView *view, + GdkEventButton *event, + GtkMenuPositionFunc func, + gpointer user_data) +{ + GtkWidget *menu; + + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + + e_attachment_view_update_actions (view); + + menu = e_attachment_view_get_popup_menu (view); + + if (event != NULL) + gtk_menu_popup ( + GTK_MENU (menu), NULL, NULL, func, + user_data, event->button, event->time); + else + gtk_menu_popup ( + GTK_MENU (menu), NULL, NULL, func, + user_data, 0, gtk_get_current_event_time ()); +} + +void +e_attachment_view_update_actions (EAttachmentView *view) +{ + g_return_if_fail (E_IS_ATTACHMENT_VIEW (view)); + + g_signal_emit (view, signals[UPDATE_ACTIONS], 0); +} |