diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2009-04-28 02:53:18 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2009-04-28 02:53:18 +0800 |
commit | e377ea5e61171e57f9e892652d0fd1f77953eda8 (patch) | |
tree | d0387074398b82952680766ade0d3df12ae60810 /mail | |
parent | 59f095bfb5b92f8bd0205238f91fc3a2277de76c (diff) | |
download | gsoc2013-evolution-e377ea5e61171e57f9e892652d0fd1f77953eda8.tar.gz gsoc2013-evolution-e377ea5e61171e57f9e892652d0fd1f77953eda8.tar.zst gsoc2013-evolution-e377ea5e61171e57f9e892652d0fd1f77953eda8.zip |
Bug 516933 – Rewrite attachment UI
Rewrite the attachment UI to better utilize GIO and also to migrate from
GnomeIconList to GtkIconView. This also introduces a "List View" option
similar to Nautilus, as well as the EAttachmentHandler framework for
extending attachment handling (may eventually replace EMFormatHook).
This commit also fixes a number of secondary attachment bugs:
Bug 311609 – new attachment bar should use regular gtk+ expander
Bug 314923 – Drag and Drop in attachment window is inconsistent and
requires additional click
Bug 338179 – attachment saving ...
Bug 350364 – Action to get info about attachments
Bug 383047 – Viewing mail attachments
Bug 427235 – Can't copy attachment mime type string
Bug 454091 – Cannot save multiple attachments who have the same name
Bug 494629 – Rethink composer's attachment UI
Bug 553970 – Evolution ignores umask when saving attachments
Bug 577375 – mailto: and attach doesn't URL un-escape
Diffstat (limited to 'mail')
-rw-r--r-- | mail/e-attachment-handler-mail.c | 524 | ||||
-rw-r--r-- | mail/e-attachment-handler-mail.h | 65 | ||||
-rw-r--r-- | mail/e-mail-attachment-bar.c | 770 | ||||
-rw-r--r-- | mail/e-mail-attachment-bar.h | 77 |
4 files changed, 1436 insertions, 0 deletions
diff --git a/mail/e-attachment-handler-mail.c b/mail/e-attachment-handler-mail.c new file mode 100644 index 0000000000..df4c0a9e3e --- /dev/null +++ b/mail/e-attachment-handler-mail.c @@ -0,0 +1,524 @@ +/* + * e-attachment-handler-mail.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-attachment-handler-mail.h" + +#include <glib/gi18n.h> +#include <camel/camel-folder.h> +#include <camel/camel-stream-mem.h> + +#include "e-util/e-error.h" +#include "mail/em-composer-utils.h" +#include "mail/mail-tools.h" + +#define E_ATTACHMENT_HANDLER_MAIL_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_ATTACHMENT_HANDLER_MAIL, EAttachmentHandlerMailPrivate)) + +struct _EAttachmentHandlerMailPrivate { + gint placeholder; +}; + +static gpointer parent_class; + +static const gchar *ui = +"<ui>" +" <popup name='context'>" +" <placeholder name='custom-actions'>" +" <menuitem action='mail-reply-sender'/>" +" <menuitem action='mail-reply-all'/>" +" <menuitem action='mail-forward'/>" +" </placeholder>" +" </popup>" +"</ui>"; + +/* Note: Do not use the info field. */ +static GtkTargetEntry target_table[] = { + { "message/rfc822", 0, 0 }, + { "x-uid-list", 0, 0 } +}; + +static void +attachment_handler_mail_forward (GtkAction *action, + EAttachmentView *view) +{ + EAttachment *attachment; + CamelMimePart *mime_part; + CamelDataWrapper *wrapper; + GList *selected; + + selected = e_attachment_view_get_selected_attachments (view); + g_return_if_fail (g_list_length (selected) == 1); + + attachment = E_ATTACHMENT (selected->data); + mime_part = e_attachment_get_mime_part (attachment); + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + + em_utils_forward_message (CAMEL_MIME_MESSAGE (wrapper), NULL); + + g_list_foreach (selected, (GFunc) g_object_unref, NULL); + g_list_free (selected); +} + +static void +attachment_handler_mail_reply_all (GtkAction *action, + EAttachmentView *view) +{ + EAttachment *attachment; + CamelMimePart *mime_part; + CamelDataWrapper *wrapper; + GList *selected; + + selected = e_attachment_view_get_selected_attachments (view); + g_return_if_fail (g_list_length (selected) == 1); + + attachment = E_ATTACHMENT (selected->data); + mime_part = e_attachment_get_mime_part (attachment); + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + + em_utils_reply_to_message ( + NULL, NULL, CAMEL_MIME_MESSAGE (wrapper), + REPLY_MODE_ALL, NULL); + + g_list_foreach (selected, (GFunc) g_object_unref, NULL); + g_list_free (selected); +} + +static void +attachment_handler_mail_reply_sender (GtkAction *action, + EAttachmentView *view) +{ + EAttachment *attachment; + CamelMimePart *mime_part; + CamelDataWrapper *wrapper; + GList *selected; + + selected = e_attachment_view_get_selected_attachments (view); + g_return_if_fail (g_list_length (selected) == 1); + + attachment = E_ATTACHMENT (selected->data); + mime_part = e_attachment_get_mime_part (attachment); + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + + em_utils_reply_to_message ( + NULL, NULL, CAMEL_MIME_MESSAGE (wrapper), + REPLY_MODE_SENDER, NULL); + + g_list_foreach (selected, (GFunc) g_object_unref, NULL); + g_list_free (selected); +} + +static GtkActionEntry standard_entries[] = { + + { "mail-forward", + "mail-forward", + N_("_Forward"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (attachment_handler_mail_forward) }, + + { "mail-reply-all", + "mail-reply-all", + N_("Reply to _All"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (attachment_handler_mail_reply_all) }, + + { "mail-reply-sender", + "mail-reply-sender", + N_("_Reply to Sender"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (attachment_handler_mail_reply_sender) } +}; + +static void +attachment_handler_mail_message_rfc822 (EAttachmentView *view, + GdkDragContext *drag_context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + static GdkAtom atom = GDK_NONE; + EAttachmentStore *store; + EAttachment *attachment; + CamelMimeMessage *message; + CamelDataWrapper *wrapper; + CamelStream *stream; + const gchar *data; + gboolean success = FALSE; + gpointer parent; + gint length; + + if (G_UNLIKELY (atom == GDK_NONE)) + atom = gdk_atom_intern_static_string ("message/rfc822"); + + if (gtk_selection_data_get_target (selection_data) != atom) + return; + + g_signal_stop_emission_by_name (view, "drag-data-received"); + + data = (const gchar *) gtk_selection_data_get_data (selection_data); + length = gtk_selection_data_get_length (selection_data); + + stream = camel_stream_mem_new (); + camel_stream_write (stream, data, length); + camel_stream_reset (stream); + + message = camel_mime_message_new (); + wrapper = CAMEL_DATA_WRAPPER (message); + + if (camel_data_wrapper_construct_from_stream (wrapper, stream) == -1) + goto exit; + + store = e_attachment_view_get_store (view); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); + parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL; + + attachment = e_attachment_new_for_message (message); + e_attachment_store_add_attachment (store, attachment); + e_attachment_load_async ( + attachment, (GAsyncReadyCallback) + e_attachment_load_handle_error, parent); + g_object_unref (attachment); + + success = TRUE; + +exit: + camel_object_unref (message); + camel_object_unref (stream); + + gtk_drag_finish (drag_context, success, FALSE, time); +} + +static void +attachment_handler_mail_x_uid_list (EAttachmentView *view, + GdkDragContext *drag_context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + static GdkAtom atom = GDK_NONE; + CamelException ex = CAMEL_EXCEPTION_INITIALISER; + CamelDataWrapper *wrapper; + CamelMimeMessage *message; + CamelMultipart *multipart; + CamelMimePart *mime_part; + CamelFolder *folder = NULL; + EAttachment *attachment; + EAttachmentStore *store; + GPtrArray *uids; + const gchar *data; + const gchar *cp, *end; + gchar *description; + gpointer parent; + gint length; + guint ii; + + if (G_UNLIKELY (atom == GDK_NONE)) + atom = gdk_atom_intern_static_string ("x-uid-list"); + + if (gtk_selection_data_get_target (selection_data) != atom) + return; + + store = e_attachment_view_get_store (view); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); + parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL; + + uids = g_ptr_array_new (); + + data = (const gchar *) gtk_selection_data_get_data (selection_data); + length = gtk_selection_data_get_length (selection_data); + + /* The UID list is delimited by NUL characters. + * Brilliant. So we can't use g_strsplit(). */ + + cp = data; + end = data + length; + + while (cp < end) { + const gchar *start = cp; + + while (cp < end && *cp != '\0') + cp++; + + /* Skip the first string. */ + if (start > data) + g_ptr_array_add (uids, g_strndup (start, cp - start)); + + cp++; + } + + if (uids->len == 0) + goto exit; + + /* The first string is the folder URI. */ + folder = mail_tool_uri_to_folder (data, 0, &ex); + if (folder == NULL) + goto exit; + + /* Handle one message. */ + if (uids->len == 1) { + message = camel_folder_get_message ( + folder, uids->pdata[0], &ex); + if (message == NULL) + goto exit; + + attachment = e_attachment_new_for_message (message); + e_attachment_store_add_attachment (store, attachment); + e_attachment_load_async ( + attachment, (GAsyncReadyCallback) + e_attachment_load_handle_error, parent); + g_object_unref (attachment); + + camel_object_unref (message); + goto exit; + } + + /* Build a multipart/digest message out of the UIDs. */ + + multipart = camel_multipart_new (); + wrapper = CAMEL_DATA_WRAPPER (multipart); + camel_data_wrapper_set_mime_type (wrapper, "multipart/digest"); + camel_multipart_set_boundary (multipart, NULL); + + for (ii = 0; ii < uids->len; ii++) { + message = camel_folder_get_message ( + folder, uids->pdata[ii], &ex); + if (message == NULL) { + camel_object_unref (multipart); + goto exit; + } + + mime_part = camel_mime_part_new (); + wrapper = CAMEL_DATA_WRAPPER (message); + camel_mime_part_set_disposition (mime_part, "inline"); + camel_medium_set_content_object ( + CAMEL_MEDIUM (mime_part), wrapper); + camel_mime_part_set_content_type (mime_part, "message/rfc822"); + camel_multipart_add_part (multipart, mime_part); + camel_object_unref (mime_part); + + camel_object_unref (message); + } + + mime_part = camel_mime_part_new (); + wrapper = CAMEL_DATA_WRAPPER (multipart); + camel_medium_set_content_object (CAMEL_MEDIUM (mime_part), wrapper); + + /* Translators: This is only for multiple messages. */ + description = g_strdup_printf (_("%d attached messages"), uids->len); + camel_mime_part_set_description (mime_part, description); + g_free (description); + + attachment = e_attachment_new (); + e_attachment_set_mime_part (attachment, mime_part); + e_attachment_store_add_attachment (store, attachment); + e_attachment_load_async ( + attachment, (GAsyncReadyCallback) + e_attachment_load_handle_error, parent); + g_object_unref (attachment); + + camel_object_unref (mime_part); + camel_object_unref (multipart); + +exit: + if (camel_exception_is_set (&ex)) { + gchar *folder_name; + + if (folder != NULL) + camel_object_get ( + folder, NULL, CAMEL_FOLDER_NAME, + &folder_name, NULL); + else + folder_name = g_strdup (data); + + e_error_run ( + parent, "mail-composer:attach-nomessages", + folder_name, camel_exception_get_description (&ex), + NULL); + + if (folder != NULL) + camel_object_free ( + folder, CAMEL_FOLDER_NAME, folder_name); + else + g_free (folder_name); + + camel_exception_clear (&ex); + } + + if (folder != NULL) + camel_object_unref (folder); + + g_ptr_array_free (uids, TRUE); + + g_signal_stop_emission_by_name (view, "drag-data-received"); +} + +static void +attachment_handler_mail_update_actions (EAttachmentView *view) +{ + EAttachment *attachment; + CamelMimePart *mime_part; + CamelDataWrapper *wrapper; + GtkActionGroup *action_group; + GList *selected; + gboolean visible = FALSE; + + selected = e_attachment_view_get_selected_attachments (view); + + if (g_list_length (selected) != 1) + goto exit; + + attachment = E_ATTACHMENT (selected->data); + mime_part = e_attachment_get_mime_part (attachment); + + if (!CAMEL_IS_MIME_PART (mime_part)) + goto exit; + + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + + visible = CAMEL_IS_MIME_MESSAGE (wrapper); + +exit: + action_group = e_attachment_view_get_action_group (view, "mail"); + gtk_action_group_set_visible (action_group, visible); + + g_list_foreach (selected, (GFunc) g_object_unref, NULL); + g_list_free (selected); +} + +static void +attachment_handler_mail_constructed (GObject *object) +{ + EAttachmentHandler *handler; + EAttachmentView *view; + GtkActionGroup *action_group; + GtkUIManager *ui_manager; + GError *error = NULL; + + handler = E_ATTACHMENT_HANDLER (object); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (parent_class)->constructed (object); + + view = e_attachment_handler_get_view (handler); + + action_group = e_attachment_view_add_action_group (view, "mail"); + gtk_action_group_add_actions ( + action_group, standard_entries, + G_N_ELEMENTS (standard_entries), view); + + ui_manager = e_attachment_view_get_ui_manager (view); + gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error); + + if (error != NULL) { + g_warning ("%s", error->message); + g_error_free (error); + } + + g_signal_connect ( + view, "update-actions", + G_CALLBACK (attachment_handler_mail_update_actions), + NULL); + + g_signal_connect ( + view, "drag-data-received", + G_CALLBACK (attachment_handler_mail_message_rfc822), + NULL); + + g_signal_connect ( + view, "drag-data-received", + G_CALLBACK (attachment_handler_mail_x_uid_list), + NULL); +} + +static GdkDragAction +attachment_handler_mail_get_drag_actions (EAttachmentHandler *handler) +{ + return GDK_ACTION_COPY; +} + +static const GtkTargetEntry * +attachment_handler_mail_get_target_table (EAttachmentHandler *handler, + guint *n_targets) +{ + if (n_targets != NULL) + *n_targets = G_N_ELEMENTS (target_table); + + return target_table; +} + +static void +attachment_handler_mail_class_init (EAttachmentHandlerMailClass *class) +{ + GObjectClass *object_class; + EAttachmentHandlerClass *handler_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EAttachmentHandlerMailPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->constructed = attachment_handler_mail_constructed; + + handler_class = E_ATTACHMENT_HANDLER_CLASS (class); + handler_class->get_drag_actions = attachment_handler_mail_get_drag_actions; + handler_class->get_target_table = attachment_handler_mail_get_target_table; +} + +static void +attachment_handler_mail_init (EAttachmentHandlerMail *handler) +{ + handler->priv = E_ATTACHMENT_HANDLER_MAIL_GET_PRIVATE (handler); +} + +GType +e_attachment_handler_mail_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { + sizeof (EAttachmentHandlerMailClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) attachment_handler_mail_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EAttachmentHandlerMail), + 0, /* n_preallocs */ + (GInstanceInitFunc) attachment_handler_mail_init, + NULL /* value_table */ + }; + + type = g_type_register_static ( + E_TYPE_ATTACHMENT_HANDLER, + "EAttachmentHandlerMail", &type_info, 0); + } + + return type; +} diff --git a/mail/e-attachment-handler-mail.h b/mail/e-attachment-handler-mail.h new file mode 100644 index 0000000000..da4ff23d89 --- /dev/null +++ b/mail/e-attachment-handler-mail.h @@ -0,0 +1,65 @@ +/* + * e-attachment-handler-mail.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_ATTACHMENT_HANDLER_MAIL_H +#define E_ATTACHMENT_HANDLER_MAIL_H + +#include <widgets/misc/e-attachment-handler.h> + +/* Standard GObject macros */ +#define E_TYPE_ATTACHMENT_HANDLER_MAIL \ + (e_attachment_handler_mail_get_type ()) +#define E_ATTACHMENT_HANDLER_MAIL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_ATTACHMENT_HANDLER_MAIL, EAttachmentHandlerMail)) +#define E_ATTACHMENT_HANDLER_MAIL_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_ATTACHMENT_HANDLER_MAIL, EAttachmentHandlerMailClass)) +#define E_IS_ATTACHMENT_HANDLER_MAIL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_ATTACHMENT_HANDLER_MAIL)) +#define E_IS_ATTACHMENT_HANDLER_MAIL_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_ATTACHMENT_HANDLER_MAIL)) +#define E_ATTACHMENT_HANDLER_MAIL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_ATTACHMENT_HANDLER_MAIL, EAttachmentHandlerMailClass)) + +G_BEGIN_DECLS + +typedef struct _EAttachmentHandlerMail EAttachmentHandlerMail; +typedef struct _EAttachmentHandlerMailClass EAttachmentHandlerMailClass; +typedef struct _EAttachmentHandlerMailPrivate EAttachmentHandlerMailPrivate; + +struct _EAttachmentHandlerMail { + EAttachmentHandler parent; + EAttachmentHandlerMailPrivate *priv; +}; + +struct _EAttachmentHandlerMailClass { + EAttachmentHandlerClass parent_class; +}; + +GType e_attachment_handler_mail_get_type (void); + +G_END_DECLS + +#endif /* E_ATTACHMENT_HANDLER_MAIL_H */ diff --git a/mail/e-mail-attachment-bar.c b/mail/e-mail-attachment-bar.c new file mode 100644 index 0000000000..d3432ca9cd --- /dev/null +++ b/mail/e-mail-attachment-bar.c @@ -0,0 +1,770 @@ +/* + * e-mail-attachment-bar.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-mail-attachment-bar.h" + +#include <glib/gi18n.h> + +#include "e-util/e-binding.h" +#include "e-util/gconf-bridge.h" + +#include "e-attachment-store.h" +#include "e-attachment-icon-view.h" +#include "e-attachment-tree-view.h" + +#define E_MAIL_ATTACHMENT_BAR_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_ATTACHMENT_BAR, EMailAttachmentBarPrivate)) + +#define NUM_VIEWS 2 + +struct _EMailAttachmentBarPrivate { + GtkTreeModel *model; + GtkWidget *vbox; + GtkWidget *expander; + GtkWidget *combo_box; + GtkWidget *icon_view; + GtkWidget *tree_view; + GtkWidget *icon_frame; + GtkWidget *tree_frame; + GtkWidget *status_icon; + GtkWidget *status_label; + GtkWidget *save_all_button; + GtkWidget *save_one_button; + + gint active_view; + guint expanded : 1; +}; + +enum { + PROP_0, + PROP_ACTIVE_VIEW, + PROP_EDITABLE, + PROP_EXPANDED +}; + +static gpointer parent_class; + +static void +mail_attachment_bar_sync_icon_view (EMailAttachmentBar *bar) +{ + EAttachmentView *source; + EAttachmentView *target; + + source = E_ATTACHMENT_VIEW (bar->priv->tree_view); + target = E_ATTACHMENT_VIEW (bar->priv->icon_view); + + /* Only sync if the tree view is active. This prevents the + * two views from endlessly trying to sync with each other. */ + if (e_mail_attachment_bar_get_active_view (bar) == 1) + e_attachment_view_sync_selection (source, target); +} + +static void +mail_attachment_bar_sync_tree_view (EMailAttachmentBar *bar) +{ + EAttachmentView *source; + EAttachmentView *target; + + source = E_ATTACHMENT_VIEW (bar->priv->icon_view); + target = E_ATTACHMENT_VIEW (bar->priv->tree_view); + + /* Only sync if the icon view is active. This prevents the + * two views from endlessly trying to sync with each other. */ + if (e_mail_attachment_bar_get_active_view (bar) == 0) + e_attachment_view_sync_selection (source, target); +} + +static void +mail_attachment_bar_update_status (EMailAttachmentBar *bar) +{ + EAttachmentView *view; + EAttachmentStore *store; + GtkExpander *expander; + GtkAction *action; + GtkLabel *label; + gint num_attachments; + guint64 total_size; + gchar *display_size; + gchar *markup; + +#if GTK_CHECK_VERSION(2,16,0) + GtkActivatable *activatable; +#endif + + view = E_ATTACHMENT_VIEW (bar); + store = e_attachment_view_get_store (view); + expander = GTK_EXPANDER (bar->priv->expander); + label = GTK_LABEL (bar->priv->status_label); + + num_attachments = e_attachment_store_get_num_attachments (store); + total_size = e_attachment_store_get_total_size (store); + display_size = g_format_size_for_display (total_size); + + markup = g_strdup_printf ( + "<b>%d</b> %s (%s)", num_attachments, ngettext ( + "Attachment", "Attachments", num_attachments), + display_size); + gtk_label_set_markup (label, markup); + g_free (markup); + +#if GTK_CHECK_VERSION(2,16,0) + activatable = GTK_ACTIVATABLE (bar->priv->save_all_button); + action = gtk_activatable_get_related_action (activatable); +#else + action = e_attachment_view_get_action (view, "save-all"); +#endif + gtk_action_set_visible (action, (num_attachments > 1)); + +#if GTK_CHECK_VERSION(2,16,0) + activatable = GTK_ACTIVATABLE (bar->priv->save_one_button); + action = gtk_activatable_get_related_action (activatable); +#else + action = e_attachment_view_get_action (view, "save-one"); +#endif + gtk_action_set_visible (action, (num_attachments == 1)); + + g_free (display_size); +} + +static void +mail_attachment_bar_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ACTIVE_VIEW: + e_mail_attachment_bar_set_active_view ( + E_MAIL_ATTACHMENT_BAR (object), + g_value_get_int (value)); + return; + + case PROP_EDITABLE: + e_attachment_view_set_editable ( + E_ATTACHMENT_VIEW (object), + g_value_get_boolean (value)); + return; + + case PROP_EXPANDED: + e_mail_attachment_bar_set_expanded ( + E_MAIL_ATTACHMENT_BAR (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +mail_attachment_bar_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ACTIVE_VIEW: + g_value_set_int ( + value, + e_mail_attachment_bar_get_active_view ( + E_MAIL_ATTACHMENT_BAR (object))); + return; + + case PROP_EDITABLE: + g_value_set_boolean ( + value, + e_attachment_view_get_editable ( + E_ATTACHMENT_VIEW (object))); + return; + + case PROP_EXPANDED: + g_value_set_boolean ( + value, + e_mail_attachment_bar_get_expanded ( + E_MAIL_ATTACHMENT_BAR (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +mail_attachment_bar_dispose (GObject *object) +{ + EMailAttachmentBarPrivate *priv; + + priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (object); + + if (priv->model != NULL) { + g_object_unref (priv->model); + priv->model = NULL; + } + + if (priv->vbox != NULL) { + g_object_unref (priv->vbox); + priv->vbox = NULL; + } + + if (priv->expander != NULL) { + g_object_unref (priv->expander); + priv->expander = NULL; + } + + if (priv->combo_box != NULL) { + g_object_unref (priv->combo_box); + priv->combo_box = NULL; + } + + if (priv->icon_view != NULL) { + g_object_unref (priv->icon_view); + priv->icon_view = NULL; + } + + if (priv->tree_view != NULL) { + g_object_unref (priv->tree_view); + priv->tree_view = NULL; + } + + if (priv->icon_frame != NULL) { + g_object_unref (priv->icon_frame); + priv->icon_frame = NULL; + } + + if (priv->tree_frame != NULL) { + g_object_unref (priv->tree_frame); + priv->tree_frame = NULL; + } + + if (priv->status_icon != NULL) { + g_object_unref (priv->status_icon); + priv->status_icon = NULL; + } + + if (priv->status_label != NULL) { + g_object_unref (priv->status_label); + priv->status_label = NULL; + } + + if (priv->save_all_button != NULL) { + g_object_unref (priv->save_all_button); + priv->save_all_button = NULL; + } + + if (priv->save_one_button != NULL) { + g_object_unref (priv->save_one_button); + priv->save_one_button = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +mail_attachment_bar_constructed (GObject *object) +{ + EMailAttachmentBarPrivate *priv; + GConfBridge *bridge; + const gchar *key; + + priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (object); + + bridge = gconf_bridge_get (); + + /* Set up property-to-property bindings. */ + + e_mutual_binding_new ( + G_OBJECT (object), "active-view", + G_OBJECT (priv->combo_box), "active"); + + e_mutual_binding_new ( + G_OBJECT (object), "editable", + G_OBJECT (priv->icon_view), "editable"); + + e_mutual_binding_new ( + G_OBJECT (object), "editable", + G_OBJECT (priv->tree_view), "editable"); + + e_mutual_binding_new ( + G_OBJECT (object), "expanded", + G_OBJECT (priv->expander), "expanded"); + + e_mutual_binding_new ( + G_OBJECT (object), "expanded", + G_OBJECT (priv->combo_box), "visible"); + + e_mutual_binding_new ( + G_OBJECT (object), "expanded", + G_OBJECT (priv->vbox), "visible"); + + /* Set up property-to-GConf bindings. */ + + key = "/apps/evolution/shell/attachment_view"; + gconf_bridge_bind_property (bridge, key, object, "active-view"); +} + +static void +mail_attachment_bar_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + /* XXX This works around GtkHTMLEmbedded not taking visibility + * into account when calculating its size (at least I think + * that's where it's broken). Without the workaround, we + * get a sizable gap between the headers and body when this + * widget is invisible. Once we finally move to WebKit, + * remove this. */ + if (!GTK_WIDGET_VISIBLE (widget)) { + requisition->width = 0; + requisition->height = 0; + return; + } + + /* Chain up to parent's size_request() method. */ + GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition); +} + +static EAttachmentViewPrivate * +mail_attachment_bar_get_private (EAttachmentView *view) +{ + EMailAttachmentBarPrivate *priv; + + priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view); + view = E_ATTACHMENT_VIEW (priv->icon_view); + + return e_attachment_view_get_private (view); +} + +static EAttachmentStore * +mail_attachment_bar_get_store (EAttachmentView *view) +{ + EMailAttachmentBarPrivate *priv; + + priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view); + view = E_ATTACHMENT_VIEW (priv->icon_view); + + return e_attachment_view_get_store (view); +} + +static GtkTreePath * +mail_attachment_bar_get_path_at_pos (EAttachmentView *view, + gint x, + gint y) +{ + EMailAttachmentBarPrivate *priv; + + priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view); + view = E_ATTACHMENT_VIEW (priv->icon_view); + + return e_attachment_view_get_path_at_pos (view, x, y); +} + +static GList * +mail_attachment_bar_get_selected_paths (EAttachmentView *view) +{ + EMailAttachmentBarPrivate *priv; + + priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view); + view = E_ATTACHMENT_VIEW (priv->icon_view); + + return e_attachment_view_get_selected_paths (view); +} + +static gboolean +mail_attachment_bar_path_is_selected (EAttachmentView *view, + GtkTreePath *path) +{ + EMailAttachmentBarPrivate *priv; + + priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view); + view = E_ATTACHMENT_VIEW (priv->icon_view); + + return e_attachment_view_path_is_selected (view, path); +} + +static void +mail_attachment_bar_select_path (EAttachmentView *view, + GtkTreePath *path) +{ + EMailAttachmentBarPrivate *priv; + + priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view); + view = E_ATTACHMENT_VIEW (priv->icon_view); + + e_attachment_view_select_path (view, path); +} + +static void +mail_attachment_bar_unselect_path (EAttachmentView *view, + GtkTreePath *path) +{ + EMailAttachmentBarPrivate *priv; + + priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view); + view = E_ATTACHMENT_VIEW (priv->icon_view); + + e_attachment_view_unselect_path (view, path); +} + +static void +mail_attachment_bar_select_all (EAttachmentView *view) +{ + EMailAttachmentBarPrivate *priv; + + priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view); + view = E_ATTACHMENT_VIEW (priv->icon_view); + + e_attachment_view_select_all (view); +} + +static void +mail_attachment_bar_unselect_all (EAttachmentView *view) +{ + EMailAttachmentBarPrivate *priv; + + priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view); + view = E_ATTACHMENT_VIEW (priv->icon_view); + + e_attachment_view_unselect_all (view); +} + +static void +mail_attachment_bar_update_actions (EAttachmentView *view) +{ + EMailAttachmentBarPrivate *priv; + + priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (view); + view = E_ATTACHMENT_VIEW (priv->icon_view); + + e_attachment_view_update_actions (view); +} + +static void +mail_attachment_bar_class_init (EMailAttachmentBarClass *class) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EMailAttachmentBarPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = mail_attachment_bar_set_property; + object_class->get_property = mail_attachment_bar_get_property; + object_class->dispose = mail_attachment_bar_dispose; + object_class->constructed = mail_attachment_bar_constructed; + + widget_class = GTK_WIDGET_CLASS (class); + widget_class->size_request = mail_attachment_bar_size_request; + + g_object_class_install_property ( + object_class, + PROP_ACTIVE_VIEW, + g_param_spec_int ( + "active-view", + "Active View", + NULL, + 0, + NUM_VIEWS, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property ( + object_class, + PROP_EXPANDED, + g_param_spec_boolean ( + "expanded", + "Expanded", + NULL, + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_override_property ( + object_class, PROP_EDITABLE, "editable"); +} + +static void +mail_attachment_bar_iface_init (EAttachmentViewIface *iface) +{ + iface->get_private = mail_attachment_bar_get_private; + iface->get_store = mail_attachment_bar_get_store; + iface->get_path_at_pos = mail_attachment_bar_get_path_at_pos; + iface->get_selected_paths = mail_attachment_bar_get_selected_paths; + iface->path_is_selected = mail_attachment_bar_path_is_selected; + iface->select_path = mail_attachment_bar_select_path; + iface->unselect_path = mail_attachment_bar_unselect_path; + iface->select_all = mail_attachment_bar_select_all; + iface->unselect_all = mail_attachment_bar_unselect_all; + iface->update_actions = mail_attachment_bar_update_actions; +} + +static void +mail_attachment_bar_init (EMailAttachmentBar *bar) +{ + EAttachmentView *view; + GtkTreeSelection *selection; + GtkSizeGroup *size_group; + GtkWidget *container; + GtkWidget *widget; + GtkAction *action; + + bar->priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (bar); + bar->priv->model = e_attachment_store_new (); + + gtk_box_set_spacing (GTK_BOX (bar), 6); + + /* Keep the expander label and save button the same height. */ + size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL); + + /* Construct the Attachment Views */ + + container = GTK_WIDGET (bar); + + widget = gtk_vbox_new (FALSE, 0); + gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0); + bar->priv->vbox = g_object_ref (widget); + gtk_widget_show (widget); + + container = bar->priv->vbox; + + widget = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (widget), GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); + bar->priv->icon_frame = g_object_ref (widget); + gtk_widget_show (widget); + + container = widget; + + widget = e_attachment_icon_view_new (); + GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS); + gtk_icon_view_set_model (GTK_ICON_VIEW (widget), bar->priv->model); + gtk_container_add (GTK_CONTAINER (container), widget); + bar->priv->icon_view = g_object_ref (widget); + gtk_widget_show (widget); + + container = bar->priv->vbox; + + widget = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (widget), GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); + bar->priv->tree_frame = g_object_ref (widget); + gtk_widget_show (widget); + + container = widget; + + widget = e_attachment_tree_view_new (); + GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS); + gtk_tree_view_set_model (GTK_TREE_VIEW (widget), bar->priv->model); + gtk_container_add (GTK_CONTAINER (container), widget); + bar->priv->tree_view = g_object_ref (widget); + gtk_widget_show (widget); + + /* Construct the Controls */ + + container = GTK_WIDGET (bar); + + widget = gtk_hbox_new (FALSE, 12); + gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0); + gtk_widget_show (widget); + + container = widget; + + widget = gtk_expander_new (NULL); + gtk_expander_set_spacing (GTK_EXPANDER (widget), 0); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + bar->priv->expander = g_object_ref (widget); + gtk_widget_show (widget); + + /* The "Save All" button proxies the "save-all" action from + * one of the two attachment views. Doesn't matter which. */ + widget = gtk_button_new (); + view = E_ATTACHMENT_VIEW (bar->priv->icon_view); + action = e_attachment_view_get_action (view, "save-all"); + gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new ()); +#if GTK_CHECK_VERSION(2,16,0) + gtk_activatable_set_related_action (GTK_ACTIVATABLE (widget), action); +#else + gtk_action_connect_proxy (action, widget); /* XXX Deprecated */ +#endif + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + bar->priv->save_all_button = g_object_ref (widget); + gtk_widget_show (widget); + + /* Same deal with the "Save" button. */ + widget = gtk_button_new (); + view = E_ATTACHMENT_VIEW (bar->priv->icon_view); + action = e_attachment_view_get_action (view, "save-one"); + gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new ()); +#if GTK_CHECK_VERSION(2,16,0) + gtk_activatable_set_related_action (GTK_ACTIVATABLE (widget), action); +#else + gtk_action_connect_proxy (action, widget); /* XXX Deprecated */ +#endif + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + bar->priv->save_one_button = g_object_ref (widget); + gtk_widget_show (widget); + + widget = gtk_alignment_new (1.0, 0.5, 0.0, 0.0); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); + gtk_widget_show (widget); + + container = widget; + + widget = gtk_combo_box_new_text (); + gtk_size_group_add_widget (size_group, widget); + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), _("Icon View")); + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), _("List View")); + gtk_container_add (GTK_CONTAINER (container), widget); + bar->priv->combo_box = g_object_ref (widget); + gtk_widget_show (widget); + + container = bar->priv->expander; + + widget = gtk_hbox_new (FALSE, 6); + gtk_size_group_add_widget (size_group, widget); + gtk_expander_set_label_widget (GTK_EXPANDER (container), widget); + gtk_widget_show (widget); + + container = widget; + + widget = gtk_image_new_from_icon_name ( + "mail-attachment", GTK_ICON_SIZE_MENU); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + bar->priv->status_icon = g_object_ref (widget); + gtk_widget_show (widget); + + widget = gtk_label_new (NULL); + gtk_label_set_use_markup (GTK_LABEL (widget), TRUE); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + bar->priv->status_label = g_object_ref (widget); + gtk_widget_show (widget); + + selection = gtk_tree_view_get_selection ( + GTK_TREE_VIEW (bar->priv->tree_view)); + + g_signal_connect_swapped ( + selection, "changed", + G_CALLBACK (mail_attachment_bar_sync_icon_view), bar); + + g_signal_connect_swapped ( + bar->priv->icon_view, "selection-changed", + G_CALLBACK (mail_attachment_bar_sync_tree_view), bar); + + g_signal_connect_swapped ( + bar->priv->model, "notify::num-attachments", + G_CALLBACK (mail_attachment_bar_update_status), bar); + + g_signal_connect_swapped ( + bar->priv->model, "notify::total-size", + G_CALLBACK (mail_attachment_bar_update_status), bar); + + g_object_unref (size_group); +} + +GType +e_mail_attachment_bar_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { + sizeof (EMailAttachmentBarClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) mail_attachment_bar_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EMailAttachmentBar), + 0, /* n_preallocs */ + (GInstanceInitFunc) mail_attachment_bar_init, + NULL /* value_table */ + }; + + static const GInterfaceInfo iface_info = { + (GInterfaceInitFunc) mail_attachment_bar_iface_init, + (GInterfaceFinalizeFunc) NULL, + NULL /* interface_data */ + }; + + type = g_type_register_static ( + GTK_TYPE_VBOX, "EMailAttachmentBar", &type_info, 0); + + g_type_add_interface_static ( + type, E_TYPE_ATTACHMENT_VIEW, &iface_info); + } + + return type; +} + +GtkWidget * +e_mail_attachment_bar_new (void) +{ + return g_object_new ( + E_TYPE_MAIL_ATTACHMENT_BAR, + "editable", FALSE, NULL); +} + +gint +e_mail_attachment_bar_get_active_view (EMailAttachmentBar *bar) +{ + g_return_val_if_fail (E_IS_MAIL_ATTACHMENT_BAR (bar), 0); + + return bar->priv->active_view; +} + +void +e_mail_attachment_bar_set_active_view (EMailAttachmentBar *bar, + gint active_view) +{ + g_return_if_fail (E_IS_MAIL_ATTACHMENT_BAR (bar)); + g_return_if_fail (active_view >= 0 && active_view < NUM_VIEWS); + + bar->priv->active_view = active_view; + + if (active_view == 0) { + gtk_widget_show (bar->priv->icon_frame); + gtk_widget_hide (bar->priv->tree_frame); + } else { + gtk_widget_hide (bar->priv->icon_frame); + gtk_widget_show (bar->priv->tree_frame); + } + + g_object_notify (G_OBJECT (bar), "active-view"); +} + +gboolean +e_mail_attachment_bar_get_expanded (EMailAttachmentBar *bar) +{ + g_return_val_if_fail (E_IS_MAIL_ATTACHMENT_BAR (bar), FALSE); + + return bar->priv->expanded; +} + +void +e_mail_attachment_bar_set_expanded (EMailAttachmentBar *bar, + gboolean expanded) +{ + g_return_if_fail (E_IS_MAIL_ATTACHMENT_BAR (bar)); + + bar->priv->expanded = expanded; + + g_object_notify (G_OBJECT (bar), "expanded"); +} diff --git a/mail/e-mail-attachment-bar.h b/mail/e-mail-attachment-bar.h new file mode 100644 index 0000000000..e32f6e2ede --- /dev/null +++ b/mail/e-mail-attachment-bar.h @@ -0,0 +1,77 @@ +/* + * e-mail-attachment-bar.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_MAIL_ATTACHMENT_BAR_H +#define E_MAIL_ATTACHMENT_BAR_H + +#include <gtk/gtk.h> +#include <widgets/misc/e-attachment-view.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_ATTACHMENT_BAR \ + (e_mail_attachment_bar_get_type ()) +#define E_MAIL_ATTACHMENT_BAR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_ATTACHMENT_BAR, EMailAttachmentBar)) +#define E_MAIL_ATTACHMENT_BAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_ATTACHMENT_BAR, EMailAttachmentBarClass)) +#define E_IS_MAIL_ATTACHMENT_BAR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_ATTACHMENT_BAR)) +#define E_IS_MAIL_ATTACHMENT_BAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_ATTACHMENT_BAR)) +#define E_MAIL_ATTACHMENT_BAR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_ATTACHMENT_BAR, EMailAttachmentBarClass)) + +G_BEGIN_DECLS + +typedef struct _EMailAttachmentBar EMailAttachmentBar; +typedef struct _EMailAttachmentBarClass EMailAttachmentBarClass; +typedef struct _EMailAttachmentBarPrivate EMailAttachmentBarPrivate; + +struct _EMailAttachmentBar { + GtkVBox parent; + EMailAttachmentBarPrivate *priv; +}; + +struct _EMailAttachmentBarClass { + GtkVBoxClass parent_class; +}; + +GType e_mail_attachment_bar_get_type (void); +GtkWidget * e_mail_attachment_bar_new (void); +gint e_mail_attachment_bar_get_active_view + (EMailAttachmentBar *bar); +void e_mail_attachment_bar_set_active_view + (EMailAttachmentBar *bar, + gint active_view); +gboolean e_mail_attachment_bar_get_expanded + (EMailAttachmentBar *bar); +void e_mail_attachment_bar_set_expanded + (EMailAttachmentBar *bar, + gboolean expanded); + +G_END_DECLS + +#endif /* E_MAIL_ATTACHMENT_BAR_H */ |