[evolution] Bug 516933 – Rewrite attachment UI
- From: Matthew Barnes <mbarnes src gnome org>
- To: svn-commits-list gnome org
- Subject: [evolution] Bug 516933 – Rewrite attachment UI
- Date: Mon, 27 Apr 2009 15:16:21 -0400 (EDT)
commit e377ea5e61171e57f9e892652d0fd1f77953eda8
Author: Matthew Barnes <mbarnes redhat com>
Date: Mon Apr 27 14:53:18 2009 -0400
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
---
calendar/gui/e-attachment-handler-calendar.c | 511 ++++++++
calendar/gui/e-attachment-handler-calendar.h | 65 +
mail/e-attachment-handler-mail.c | 524 ++++++++
mail/e-attachment-handler-mail.h | 65 +
mail/e-mail-attachment-bar.c | 770 ++++++++++++
mail/e-mail-attachment-bar.h | 77 ++
widgets/misc/e-attachment-button.c | 725 ++++++++++++
widgets/misc/e-attachment-button.h | 83 ++
widgets/misc/e-attachment-dialog.c | 446 +++++++
widgets/misc/e-attachment-dialog.h | 73 ++
widgets/misc/e-attachment-handler-image.c | 280 +++++
widgets/misc/e-attachment-handler-image.h | 65 +
widgets/misc/e-attachment-handler.c | 191 +++
widgets/misc/e-attachment-handler.h | 78 ++
widgets/misc/e-attachment-icon-view.c | 537 +++++++++
widgets/misc/e-attachment-icon-view.h | 66 +
widgets/misc/e-attachment-paned.c | 781 ++++++++++++
widgets/misc/e-attachment-paned.h | 84 ++
widgets/misc/e-attachment-store.c | 874 ++++++++++++++
widgets/misc/e-attachment-store.h | 122 ++
widgets/misc/e-attachment-tree-view.c | 605 ++++++++++
widgets/misc/e-attachment-tree-view.h | 66 +
widgets/misc/e-attachment-view.c | 1637 ++++++++++++++++++++++++++
widgets/misc/e-attachment-view.h | 227 ++++
24 files changed, 8952 insertions(+), 0 deletions(-)
diff --git a/calendar/gui/e-attachment-handler-calendar.c b/calendar/gui/e-attachment-handler-calendar.c
new file mode 100644
index 0000000..5beb6e0
--- /dev/null
+++ b/calendar/gui/e-attachment-handler-calendar.c
@@ -0,0 +1,511 @@
+/*
+ * e-attachment-handler-calendar.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-calendar.h"
+
+#include <glib/gi18n.h>
+#include <libical/ical.h>
+#include <libecal/e-cal.h>
+#include <camel/camel-stream-mem.h>
+#include <libedataserverui/e-source-selector.h>
+
+#include "calendar/common/authentication.h"
+
+#define E_ATTACHMENT_HANDLER_CALENDAR_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_CALENDAR, EAttachmentHandlerCalendarPrivate))
+
+typedef struct _ImportContext ImportContext;
+
+struct _EAttachmentHandlerCalendarPrivate {
+ gint placeholder;
+};
+
+struct _ImportContext {
+ ECal *client;
+ icalcomponent *component;
+ ECalSourceType source_type;
+};
+
+static gpointer parent_class;
+
+static const gchar *ui =
+"<ui>"
+" <popup name='context'>"
+" <placeholder name='custom-actions'>"
+" <menuitem action='import-to-calendar'/>"
+" <menuitem action='import-to-tasks'/>"
+" </placeholder>"
+" </popup>"
+"</ui>";
+
+static icalcomponent *
+attachment_handler_get_component (EAttachment *attachment)
+{
+ CamelDataWrapper *wrapper;
+ CamelMimePart *mime_part;
+ CamelStream *stream;
+ GByteArray *buffer;
+ icalcomponent *component;
+ const gchar *key = "__icalcomponent__";
+
+ component = g_object_get_data (G_OBJECT (attachment), key);
+ if (component != NULL)
+ return component;
+
+ mime_part = e_attachment_get_mime_part (attachment);
+ if (!CAMEL_IS_MIME_PART (mime_part))
+ return NULL;
+
+ buffer = g_byte_array_new ();
+ stream = camel_stream_mem_new ();
+ camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (stream), buffer);
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+ camel_data_wrapper_decode_to_stream (wrapper, stream);
+ camel_object_unref (stream);
+
+ component = e_cal_util_parse_ics_string ((gchar *) buffer->data);
+
+ g_byte_array_free (buffer, TRUE);
+
+ if (component == NULL)
+ return NULL;
+
+ g_object_set_data_full (
+ G_OBJECT (attachment), key, component,
+ (GDestroyNotify) icalcomponent_free);
+
+ return component;
+}
+
+static gboolean
+attachment_handler_update_objects (ECal *client,
+ icalcomponent *component)
+{
+ icalcomponent_kind kind;
+ icalcomponent *vcalendar;
+ gboolean success;
+
+ kind = icalcomponent_isa (component);
+
+ switch (kind) {
+ case ICAL_VTODO_COMPONENT:
+ case ICAL_VEVENT_COMPONENT:
+ vcalendar = e_cal_util_new_top_level ();
+ if (icalcomponent_get_method (component) == ICAL_METHOD_CANCEL)
+ icalcomponent_set_method (vcalendar, ICAL_METHOD_CANCEL);
+ else
+ icalcomponent_set_method (vcalendar, ICAL_METHOD_PUBLISH);
+ icalcomponent_add_component (
+ vcalendar, icalcomponent_new_clone (component));
+ break;
+
+ case ICAL_VCALENDAR_COMPONENT:
+ vcalendar = icalcomponent_new_clone (component);
+ if (!icalcomponent_get_first_property (vcalendar, ICAL_METHOD_PROPERTY))
+ icalcomponent_set_method (vcalendar, ICAL_METHOD_PUBLISH);
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ success = e_cal_receive_objects (client, vcalendar, NULL);
+
+ icalcomponent_free (vcalendar);
+
+ return success;
+}
+
+static void
+attachment_handler_import_event (ECal *client,
+ ECalendarStatus status,
+ EAttachment *attachment)
+{
+ icalcomponent *component;
+ icalcomponent *subcomponent;
+ icalcompiter iter;
+
+ /* FIXME Notify the user somehow. */
+ g_return_if_fail (status == E_CALENDAR_STATUS_OK);
+
+ component = attachment_handler_get_component (attachment);
+ g_return_if_fail (component != NULL);
+
+ iter = icalcomponent_begin_component (component, ICAL_ANY_COMPONENT);
+
+ while ((subcomponent = icalcompiter_deref (&iter)) != NULL) {
+ icalcomponent_kind kind;
+
+ kind = icalcomponent_isa (subcomponent);
+ icalcompiter_next (&iter);
+
+ if (kind == ICAL_VEVENT_COMPONENT)
+ continue;
+
+ if (kind == ICAL_VTIMEZONE_COMPONENT)
+ continue;
+
+ icalcomponent_remove_component (component, subcomponent);
+ icalcomponent_free (subcomponent);
+ }
+
+ /* XXX Do something with the return value. */
+ attachment_handler_update_objects (client, component);
+
+ g_object_unref (attachment);
+ g_object_unref (client);
+}
+
+static void
+attachment_handler_import_todo (ECal *client,
+ ECalendarStatus status,
+ EAttachment *attachment)
+{
+ icalcomponent *component;
+ icalcomponent *subcomponent;
+ icalcompiter iter;
+
+ /* FIXME Notify the user somehow. */
+ g_return_if_fail (status == E_CALENDAR_STATUS_OK);
+
+ component = attachment_handler_get_component (attachment);
+ g_return_if_fail (component != NULL);
+
+ iter = icalcomponent_begin_component (component, ICAL_ANY_COMPONENT);
+
+ while ((subcomponent = icalcompiter_deref (&iter)) != NULL) {
+ icalcomponent_kind kind;
+
+ kind = icalcomponent_isa (subcomponent);
+ icalcompiter_next (&iter);
+
+ if (kind == ICAL_VTODO_COMPONENT)
+ continue;
+
+ if (kind == ICAL_VTIMEZONE_COMPONENT)
+ continue;
+
+ icalcomponent_remove_component (component, subcomponent);
+ icalcomponent_free (subcomponent);
+ }
+
+ /* XXX Do something with the return value. */
+ attachment_handler_update_objects (client, component);
+
+ g_object_unref (attachment);
+ g_object_unref (client);
+}
+
+static void
+attachment_handler_row_activated_cb (GtkDialog *dialog)
+{
+ gtk_dialog_response (dialog, GTK_RESPONSE_OK);
+}
+
+static void
+attachment_handler_run_dialog (GtkWindow *parent,
+ EAttachment *attachment,
+ ECalSourceType source_type,
+ const gchar *title)
+{
+ GtkWidget *dialog;
+ GtkWidget *container;
+ GtkWidget *widget;
+ GCallback callback;
+ ESourceSelector *selector;
+ ESourceList *source_list;
+ ESource *source;
+ ECal *client;
+ icalcomponent *component;
+ GError *error = NULL;
+
+ component = attachment_handler_get_component (attachment);
+ g_return_if_fail (component != NULL);
+
+ e_cal_get_sources (&source_list, source_type, &error);
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ source = e_source_list_peek_source_any (source_list);
+ g_return_if_fail (source != NULL);
+
+ dialog = gtk_dialog_new_with_buttons (
+ title, parent, GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
+
+ widget = gtk_button_new_with_mnemonic (_("I_mport"));
+ gtk_button_set_image (
+ GTK_BUTTON (widget), gtk_image_new_from_icon_name (
+ "stock_mail-import", GTK_ICON_SIZE_MENU));
+ gtk_dialog_add_action_widget (
+ GTK_DIALOG (dialog), widget, GTK_RESPONSE_OK);
+ gtk_widget_show (widget);
+
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+ gtk_window_set_default_size (GTK_WINDOW (dialog), 300, 400);
+
+ container = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ 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_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = e_source_selector_new (source_list);
+ selector = E_SOURCE_SELECTOR (widget);
+ e_source_selector_set_primary_selection (selector, source);
+ e_source_selector_show_selection (selector, FALSE);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ gtk_widget_show (widget);
+
+ g_signal_connect_swapped (
+ widget, "row-activated",
+ G_CALLBACK (attachment_handler_row_activated_cb), dialog);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK)
+ goto exit;
+
+ source = e_source_selector_peek_primary_selection (selector);
+ if (source == NULL)
+ goto exit;
+
+ client = auth_new_cal_from_source (source, source_type);
+ if (client == NULL)
+ goto exit;
+
+ if (source_type == E_CAL_SOURCE_TYPE_EVENT)
+ callback = G_CALLBACK (attachment_handler_import_event);
+ else if (source_type == E_CAL_SOURCE_TYPE_TODO)
+ callback = G_CALLBACK (attachment_handler_import_todo);
+ else
+ goto exit;
+
+ g_object_ref (attachment);
+ g_signal_connect (client, "cal-opened", callback, attachment);
+ e_cal_open_async (client, FALSE);
+
+exit:
+ gtk_widget_destroy (dialog);
+}
+
+static void
+attachment_handler_import_to_calendar (GtkAction *action,
+ EAttachmentHandler *handler)
+{
+ EAttachment *attachment;
+ EAttachmentView *view;
+ GList *selected;
+ gpointer parent;
+
+ view = e_attachment_handler_get_view (handler);
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+ selected = e_attachment_view_get_selected_attachments (view);
+ g_return_if_fail (g_list_length (selected) == 1);
+ attachment = E_ATTACHMENT (selected->data);
+
+ attachment_handler_run_dialog (
+ parent, attachment,
+ E_CAL_SOURCE_TYPE_EVENT,
+ _("Select a Calendar"));
+
+ g_object_unref (attachment);
+ g_list_free (selected);
+}
+
+static void
+attachment_handler_import_to_tasks (GtkAction *action,
+ EAttachmentHandler *handler)
+{
+ EAttachment *attachment;
+ EAttachmentView *view;
+ GList *selected;
+ gpointer parent;
+
+ view = e_attachment_handler_get_view (handler);
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+ selected = e_attachment_view_get_selected_attachments (view);
+ g_return_if_fail (g_list_length (selected) == 1);
+ attachment = E_ATTACHMENT (selected->data);
+
+ attachment_handler_run_dialog (
+ parent, attachment,
+ E_CAL_SOURCE_TYPE_TODO,
+ _("Select a Task List"));
+
+ g_object_unref (attachment);
+ g_list_free (selected);
+}
+
+static GtkActionEntry standard_entries[] = {
+
+ { "import-to-calendar",
+ "stock_mail-import",
+ N_("I_mport to Calendar"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (attachment_handler_import_to_calendar) },
+
+ { "import-to-tasks",
+ "stock_mail-import",
+ N_("I_mport to Tasks"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (attachment_handler_import_to_tasks) }
+};
+
+static void
+attachment_handler_calendar_update_actions (EAttachmentView *view)
+{
+ EAttachment *attachment;
+ GtkAction *action;
+ GList *selected;
+ icalcomponent *component;
+ icalcomponent *subcomponent;
+ icalcomponent_kind kind;
+ gboolean is_vevent = FALSE;
+ gboolean is_vtodo = FALSE;
+
+ selected = e_attachment_view_get_selected_attachments (view);
+
+ if (g_list_length (selected) != 1)
+ goto exit;
+
+ attachment = E_ATTACHMENT (selected->data);
+ component = attachment_handler_get_component (attachment);
+
+ if (component == NULL)
+ goto exit;
+
+ subcomponent = icalcomponent_get_inner (component);
+
+ if (subcomponent == NULL)
+ goto exit;
+
+ kind = icalcomponent_isa (subcomponent);
+ is_vevent = (kind == ICAL_VEVENT_COMPONENT);
+ is_vtodo = (kind == ICAL_VTODO_COMPONENT);
+
+exit:
+ action = e_attachment_view_get_action (view, "import-to-calendar");
+ gtk_action_set_visible (action, is_vevent);
+
+ action = e_attachment_view_get_action (view, "import-to-tasks");
+ gtk_action_set_visible (action, is_vtodo);
+
+ g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+}
+
+static void
+attachment_handler_calendar_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, "calendar");
+ gtk_action_group_add_actions (
+ action_group, standard_entries,
+ G_N_ELEMENTS (standard_entries), handler);
+
+ 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_calendar_update_actions),
+ NULL);
+}
+
+static void
+attachment_handler_calendar_class_init (EAttachmentHandlerCalendarClass *class)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EAttachmentHandlerCalendarPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->constructed = attachment_handler_calendar_constructed;
+}
+
+static void
+attachment_handler_calendar_init (EAttachmentHandlerCalendar *handler)
+{
+ handler->priv = E_ATTACHMENT_HANDLER_CALENDAR_GET_PRIVATE (handler);
+}
+
+GType
+e_attachment_handler_calendar_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EAttachmentHandlerCalendarClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) attachment_handler_calendar_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EAttachmentHandlerCalendar),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) attachment_handler_calendar_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ E_TYPE_ATTACHMENT_HANDLER,
+ "EAttachmentHandlerCalendar", &type_info, 0);
+ }
+
+ return type;
+}
diff --git a/calendar/gui/e-attachment-handler-calendar.h b/calendar/gui/e-attachment-handler-calendar.h
new file mode 100644
index 0000000..b678861
--- /dev/null
+++ b/calendar/gui/e-attachment-handler-calendar.h
@@ -0,0 +1,65 @@
+/*
+ * e-attachment-handler-calendar.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_CALENDAR_H
+#define E_ATTACHMENT_HANDLER_CALENDAR_H
+
+#include <widgets/misc/e-attachment-handler.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_HANDLER_CALENDAR \
+ (e_attachment_handler_calendar_get_type ())
+#define E_ATTACHMENT_HANDLER_CALENDAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_CALENDAR, EAttachmentHandlerCalendar))
+#define E_ATTACHMENT_HANDLER_CALENDAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_HANDLER_CALENDAR, EAttachmentHandlerCalendarClass))
+#define E_IS_ATTACHMENT_HANDLER_CALENDAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_CALENDAR))
+#define E_IS_ATTACHMENT_HANDLER_CALENDAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_HANDLER_CALENDAR))
+#define E_ATTACHMENT_HANDLER_CALENDAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_CALENDAR, EAttachmentHandlerCalendarClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentHandlerCalendar EAttachmentHandlerCalendar;
+typedef struct _EAttachmentHandlerCalendarClass EAttachmentHandlerCalendarClass;
+typedef struct _EAttachmentHandlerCalendarPrivate EAttachmentHandlerCalendarPrivate;
+
+struct _EAttachmentHandlerCalendar {
+ EAttachmentHandler parent;
+ EAttachmentHandlerCalendarPrivate *priv;
+};
+
+struct _EAttachmentHandlerCalendarClass {
+ EAttachmentHandlerClass parent_class;
+};
+
+GType e_attachment_handler_calendar_get_type (void);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_HANDLER_CALENDAR_H */
diff --git a/mail/e-attachment-handler-mail.c b/mail/e-attachment-handler-mail.c
new file mode 100644
index 0000000..df4c0a9
--- /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 0000000..da4ff23
--- /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 0000000..d3432ca
--- /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 0000000..e32f6e2
--- /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 */
diff --git a/widgets/misc/e-attachment-button.c b/widgets/misc/e-attachment-button.c
new file mode 100644
index 0000000..a9c230b
--- /dev/null
+++ b/widgets/misc/e-attachment-button.c
@@ -0,0 +1,725 @@
+/*
+ * e-attachment-button.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)
+ *
+ */
+
+/* Much of the popup menu logic here was ripped from GtkMenuToolButton. */
+
+#include "e-attachment-button.h"
+
+#include "e-util/e-binding.h"
+
+#define E_ATTACHMENT_BUTTON_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_BUTTON, EAttachmentButtonPrivate))
+
+struct _EAttachmentButtonPrivate {
+
+ EAttachmentView *view;
+ EAttachment *attachment;
+ gulong reference_handler_id;
+
+ EMutualBinding *can_show_binding;
+ EMutualBinding *shown_binding;
+
+ GtkWidget *expand_button;
+ GtkWidget *toggle_button;
+ GtkWidget *cell_view;
+
+ guint expandable : 1;
+ guint expanded : 1;
+};
+
+enum {
+ PROP_0,
+ PROP_ATTACHMENT,
+ PROP_EXPANDABLE,
+ PROP_EXPANDED,
+ PROP_VIEW
+};
+
+static gpointer parent_class;
+
+static void
+attachment_button_menu_deactivate_cb (EAttachmentButton *button)
+{
+ EAttachmentView *view;
+ GtkActionGroup *action_group;
+ GtkToggleButton *toggle_button;
+
+ view = e_attachment_button_get_view (button);
+ action_group = e_attachment_view_get_action_group (view, "inline");
+ toggle_button = GTK_TOGGLE_BUTTON (button->priv->toggle_button);
+
+ gtk_toggle_button_set_active (toggle_button, FALSE);
+
+ gtk_action_group_set_visible (action_group, FALSE);
+}
+
+static void
+attachment_button_menu_position (GtkMenu *menu,
+ gint *x,
+ gint *y,
+ gboolean *push_in,
+ EAttachmentButton *button)
+{
+ GtkRequisition menu_requisition;
+ GtkTextDirection direction;
+ GdkRectangle monitor;
+ GdkScreen *screen;
+ GdkWindow *window;
+ GtkWidget *widget;
+ GtkWidget *toggle_button;
+ gint monitor_num;
+
+ widget = GTK_WIDGET (button);
+ toggle_button = button->priv->toggle_button;
+ gtk_widget_size_request (GTK_WIDGET (menu), &menu_requisition);
+
+ window = gtk_widget_get_parent_window (widget);
+ screen = gtk_widget_get_screen (GTK_WIDGET (menu));
+ monitor_num = gdk_screen_get_monitor_at_window (screen, window);
+ if (monitor_num < 0)
+ monitor_num = 0;
+ gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+ gdk_window_get_origin (window, x, y);
+ *x += widget->allocation.x;
+ *y += widget->allocation.y;
+
+ direction = gtk_widget_get_direction (widget);
+ if (direction == GTK_TEXT_DIR_LTR)
+ *x += MAX (widget->allocation.width - menu_requisition.width, 0);
+ else if (menu_requisition.width > widget->allocation.width)
+ *x -= menu_requisition.width - widget->allocation.width;
+
+ if ((*y + toggle_button->allocation.height + menu_requisition.height) <= monitor.y + monitor.height)
+ *y += toggle_button->allocation.height;
+ else if ((*y - menu_requisition.height) >= monitor.y)
+ *y -= menu_requisition.height;
+ else if (monitor.y + monitor.height - (*y + toggle_button->allocation.height) > *y)
+ *y += toggle_button->allocation.height;
+ else
+ *y -= menu_requisition.height;
+
+ *push_in = FALSE;
+}
+
+static void
+attachment_button_select_path (EAttachmentButton *button)
+{
+ EAttachmentView *view;
+ EAttachment *attachment;
+ GtkTreeRowReference *reference;
+ GtkTreePath *path;
+
+ attachment = e_attachment_button_get_attachment (button);
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+ reference = e_attachment_get_reference (attachment);
+ g_return_if_fail (gtk_tree_row_reference_valid (reference));
+
+ view = e_attachment_button_get_view (button);
+ path = gtk_tree_row_reference_get_path (reference);
+
+ e_attachment_view_unselect_all (view);
+ e_attachment_view_select_path (view, path);
+
+ gtk_tree_path_free (path);
+}
+
+static void
+attachment_button_show_popup_menu (EAttachmentButton *button,
+ GdkEventButton *event)
+{
+ EAttachmentView *view;
+ GtkActionGroup *action_group;
+ GtkToggleButton *toggle_button;
+
+ view = e_attachment_button_get_view (button);
+ action_group = e_attachment_view_get_action_group (view, "inline");
+ toggle_button = GTK_TOGGLE_BUTTON (button->priv->toggle_button);
+
+ attachment_button_select_path (button);
+ gtk_toggle_button_set_active (toggle_button, TRUE);
+
+ e_attachment_view_show_popup_menu (
+ view, event, (GtkMenuPositionFunc)
+ attachment_button_menu_position, button);
+
+ gtk_action_group_set_visible (action_group, TRUE);
+}
+
+static void
+attachment_button_update_cell_view (EAttachmentButton *button)
+{
+ GtkCellView *cell_view;
+ EAttachment *attachment;
+ GtkTreeRowReference *reference;
+ GtkTreeModel *model = NULL;
+ GtkTreePath *path = NULL;
+
+ cell_view = GTK_CELL_VIEW (button->priv->cell_view);
+
+ attachment = e_attachment_button_get_attachment (button);
+ if (attachment == NULL)
+ goto exit;
+
+ reference = e_attachment_get_reference (attachment);
+ if (reference == NULL)
+ goto exit;
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+
+exit:
+ gtk_cell_view_set_model (cell_view, model);
+ gtk_cell_view_set_displayed_row (cell_view, path);
+
+ if (path != NULL)
+ gtk_tree_path_free (path);
+}
+
+static void
+attachment_button_update_pixbufs (EAttachmentButton *button)
+{
+ GtkCellView *cell_view;
+ GtkCellRenderer *renderer;
+ GtkIconTheme *icon_theme;
+ GdkPixbuf *pixbuf_expander_open;
+ GdkPixbuf *pixbuf_expander_closed;
+ GList *list;
+
+ icon_theme = gtk_icon_theme_get_default ();
+
+ /* Grab the first cell renderer. */
+ cell_view = GTK_CELL_VIEW (button->priv->cell_view);
+ list = gtk_cell_view_get_cell_renderers (cell_view);
+ renderer = GTK_CELL_RENDERER (list->data);
+ g_list_free (list);
+
+ pixbuf_expander_open = gtk_widget_render_icon (
+ GTK_WIDGET (button), GTK_STOCK_GO_DOWN,
+ GTK_ICON_SIZE_BUTTON, NULL);
+
+ pixbuf_expander_closed = gtk_widget_render_icon (
+ GTK_WIDGET (button), GTK_STOCK_GO_FORWARD,
+ GTK_ICON_SIZE_BUTTON, NULL);
+
+ g_object_set (
+ renderer,
+ "pixbuf-expander-open", pixbuf_expander_open,
+ "pixbuf-expander-closed", pixbuf_expander_closed,
+ NULL);
+
+ g_object_unref (pixbuf_expander_open);
+ g_object_unref (pixbuf_expander_closed);
+}
+
+static void
+attachment_button_expand_clicked_cb (EAttachmentButton *button)
+{
+ gboolean expanded;
+
+ expanded = e_attachment_button_get_expanded (button);
+ e_attachment_button_set_expanded (button, !expanded);
+}
+
+static void
+attachment_button_expand_drag_data_get_cb (EAttachmentButton *button,
+ GdkDragContext *context,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ EAttachmentView *view;
+
+ attachment_button_select_path (button);
+
+ view = e_attachment_button_get_view (button);
+
+ e_attachment_view_drag_data_get (
+ view, context, selection, info, time);
+}
+
+static gboolean
+attachment_button_toggle_button_press_event_cb (EAttachmentButton *button,
+ GdkEventButton *event)
+{
+ if (event->button == 1) {
+ attachment_button_show_popup_menu (button, event);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+attachment_button_set_view (EAttachmentButton *button,
+ EAttachmentView *view)
+{
+ GtkWidget *menu;
+
+ g_return_if_fail (button->priv->view == NULL);
+
+ button->priv->view = g_object_ref (view);
+
+ menu = e_attachment_view_get_popup_menu (view);
+
+ g_signal_connect_swapped (
+ menu, "deactivate",
+ G_CALLBACK (attachment_button_menu_deactivate_cb), button);
+}
+
+static void
+attachment_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ATTACHMENT:
+ e_attachment_button_set_attachment (
+ E_ATTACHMENT_BUTTON (object),
+ g_value_get_object (value));
+ return;
+
+ case PROP_EXPANDABLE:
+ e_attachment_button_set_expandable (
+ E_ATTACHMENT_BUTTON (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_EXPANDED:
+ e_attachment_button_set_expanded (
+ E_ATTACHMENT_BUTTON (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_VIEW:
+ attachment_button_set_view (
+ E_ATTACHMENT_BUTTON (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ATTACHMENT:
+ g_value_set_object (
+ value,
+ e_attachment_button_get_attachment (
+ E_ATTACHMENT_BUTTON (object)));
+ return;
+
+ case PROP_EXPANDABLE:
+ g_value_set_boolean (
+ value,
+ e_attachment_button_get_expandable (
+ E_ATTACHMENT_BUTTON (object)));
+ return;
+
+ case PROP_EXPANDED:
+ g_value_set_boolean (
+ value,
+ e_attachment_button_get_expanded (
+ E_ATTACHMENT_BUTTON (object)));
+ return;
+
+ case PROP_VIEW:
+ g_value_set_object (
+ value,
+ e_attachment_button_get_view (
+ E_ATTACHMENT_BUTTON (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_button_dispose (GObject *object)
+{
+ EAttachmentButtonPrivate *priv;
+
+ priv = E_ATTACHMENT_BUTTON_GET_PRIVATE (object);
+
+ if (priv->view != NULL) {
+ g_object_unref (priv->view);
+ priv->view = NULL;
+ }
+
+ if (priv->attachment != NULL) {
+ g_signal_handler_disconnect (
+ priv->attachment,
+ priv->reference_handler_id);
+ g_object_unref (priv->attachment);
+ priv->attachment = NULL;
+ }
+
+ if (priv->expand_button != NULL) {
+ g_object_unref (priv->expand_button);
+ priv->expand_button = NULL;
+ }
+
+ if (priv->toggle_button != NULL) {
+ g_object_unref (priv->toggle_button);
+ priv->toggle_button = NULL;
+ }
+
+ if (priv->cell_view != NULL) {
+ g_object_unref (priv->cell_view);
+ priv->cell_view = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+attachment_button_style_set (GtkWidget *widget,
+ GtkStyle *previous_style)
+{
+ EAttachmentButton *button;
+
+ /* Chain up to parent's style_set() method. */
+ GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
+
+ button = E_ATTACHMENT_BUTTON (widget);
+ attachment_button_update_pixbufs (button);
+}
+
+static void
+attachment_button_class_init (EAttachmentButtonClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EAttachmentButtonPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = attachment_button_set_property;
+ object_class->get_property = attachment_button_get_property;
+ object_class->dispose = attachment_button_dispose;
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->style_set = attachment_button_style_set;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ATTACHMENT,
+ g_param_spec_object (
+ "attachment",
+ "Attachment",
+ NULL,
+ E_TYPE_ATTACHMENT,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_EXPANDABLE,
+ g_param_spec_boolean (
+ "expandable",
+ "Expandable",
+ NULL,
+ TRUE,
+ 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_install_property (
+ object_class,
+ PROP_VIEW,
+ g_param_spec_object (
+ "view",
+ "View",
+ NULL,
+ E_TYPE_ATTACHMENT_VIEW,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+attachment_button_init (EAttachmentButton *button)
+{
+ GtkCellRenderer *renderer;
+ GtkCellLayout *cell_layout;
+ GtkTargetEntry *targets;
+ GtkTargetList *list;
+ GtkWidget *container;
+ GtkWidget *widget;
+ gint n_targets;
+
+ button->priv = E_ATTACHMENT_BUTTON_GET_PRIVATE (button);
+
+ /* Configure Widgets */
+
+ container = GTK_WIDGET (button);
+
+ widget = gtk_button_new ();
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ button->priv->expand_button = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ e_mutual_binding_new (
+ G_OBJECT (button), "expandable",
+ G_OBJECT (widget), "sensitive");
+
+ widget = gtk_toggle_button_new ();
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ button->priv->toggle_button = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = button->priv->expand_button;
+
+ widget = gtk_cell_view_new ();
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ button->priv->cell_view = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = button->priv->toggle_button;
+
+ widget = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ gtk_widget_show (widget);
+
+ /* Configure Renderers */
+
+ cell_layout = GTK_CELL_LAYOUT (button->priv->cell_view);
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ g_object_set (renderer, "is-expander", TRUE, NULL);
+ gtk_cell_layout_pack_start (cell_layout, renderer, FALSE);
+
+ e_mutual_binding_new (
+ G_OBJECT (button), "expanded",
+ G_OBJECT (renderer), "is-expanded");
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ g_object_set (renderer, "stock-size", GTK_ICON_SIZE_BUTTON, NULL);
+ gtk_cell_layout_pack_start (cell_layout, renderer, FALSE);
+
+ gtk_cell_layout_add_attribute (
+ cell_layout, renderer, "gicon",
+ E_ATTACHMENT_STORE_COLUMN_ICON);
+
+ /* Configure Drag and Drop */
+
+ 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 (
+ button->priv->expand_button, GDK_BUTTON1_MASK,
+ targets, n_targets, GDK_ACTION_COPY);
+
+ gtk_drag_source_set (
+ button->priv->toggle_button, GDK_BUTTON1_MASK,
+ targets, n_targets, GDK_ACTION_COPY);
+
+ gtk_target_table_free (targets, n_targets);
+ gtk_target_list_unref (list);
+
+ /* Configure Signal Handlers */
+
+ g_signal_connect_swapped (
+ button->priv->expand_button, "clicked",
+ G_CALLBACK (attachment_button_expand_clicked_cb), button);
+
+ g_signal_connect_swapped (
+ button->priv->expand_button, "drag-data-get",
+ G_CALLBACK (attachment_button_expand_drag_data_get_cb),
+ button);
+
+ g_signal_connect_swapped (
+ button->priv->toggle_button, "button-press-event",
+ G_CALLBACK (attachment_button_toggle_button_press_event_cb),
+ button);
+
+ g_signal_connect_swapped (
+ button->priv->toggle_button, "drag-data-get",
+ G_CALLBACK (attachment_button_expand_drag_data_get_cb),
+ button);
+
+}
+
+GType
+e_attachment_button_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EAttachmentButtonClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) attachment_button_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EAttachmentButton),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) attachment_button_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ GTK_TYPE_HBOX, "EAttachmentButton", &type_info, 0);
+ }
+
+ return type;
+}
+
+GtkWidget *
+e_attachment_button_new (EAttachmentView *view)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+
+ return g_object_new (
+ E_TYPE_ATTACHMENT_BUTTON,
+ "view", view, NULL);
+}
+
+EAttachmentView *
+e_attachment_button_get_view (EAttachmentButton *button)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_BUTTON (button), NULL);
+
+ return button->priv->view;
+}
+
+EAttachment *
+e_attachment_button_get_attachment (EAttachmentButton *button)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_BUTTON (button), NULL);
+
+ return button->priv->attachment;
+}
+
+void
+e_attachment_button_set_attachment (EAttachmentButton *button,
+ EAttachment *attachment)
+{
+ g_return_if_fail (E_IS_ATTACHMENT_BUTTON (button));
+
+ if (attachment != NULL) {
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ g_object_ref (attachment);
+ }
+
+ if (button->priv->attachment != NULL) {
+ e_mutual_binding_unbind (
+ button->priv->can_show_binding);
+ button->priv->can_show_binding = NULL;
+ e_mutual_binding_unbind (
+ button->priv->shown_binding);
+ button->priv->shown_binding = NULL;
+ g_signal_handler_disconnect (
+ button->priv->attachment,
+ button->priv->reference_handler_id);
+ g_object_unref (button->priv->attachment);
+ }
+
+ button->priv->attachment = attachment;
+
+ if (attachment != NULL) {
+ EMutualBinding *binding;
+ gulong handler_id;
+
+ binding = e_mutual_binding_new (
+ G_OBJECT (attachment), "can-show",
+ G_OBJECT (button), "expandable");
+ button->priv->can_show_binding = binding;
+
+ binding = e_mutual_binding_new (
+ G_OBJECT (attachment), "shown",
+ G_OBJECT (button), "expanded");
+ button->priv->shown_binding = binding;
+
+ handler_id = g_signal_connect_swapped (
+ attachment, "notify::reference",
+ G_CALLBACK (attachment_button_update_cell_view),
+ button);
+ button->priv->reference_handler_id = handler_id;
+
+ attachment_button_update_cell_view (button);
+ attachment_button_update_pixbufs (button);
+ }
+
+ g_object_notify (G_OBJECT (button), "attachment");
+}
+
+gboolean
+e_attachment_button_get_expandable (EAttachmentButton *button)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_BUTTON (button), FALSE);
+
+ return button->priv->expandable;
+}
+
+void
+e_attachment_button_set_expandable (EAttachmentButton *button,
+ gboolean expandable)
+{
+ g_return_if_fail (E_IS_ATTACHMENT_BUTTON (button));
+
+ button->priv->expandable = expandable;
+
+ if (!expandable)
+ e_attachment_button_set_expanded (button, FALSE);
+
+ g_object_notify (G_OBJECT (button), "expandable");
+}
+
+gboolean
+e_attachment_button_get_expanded (EAttachmentButton *button)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_BUTTON (button), FALSE);
+
+ return button->priv->expanded;
+}
+
+void
+e_attachment_button_set_expanded (EAttachmentButton *button,
+ gboolean expanded)
+{
+ g_return_if_fail (E_IS_ATTACHMENT_BUTTON (button));
+
+ button->priv->expanded = expanded;
+
+ g_object_notify (G_OBJECT (button), "expanded");
+}
diff --git a/widgets/misc/e-attachment-button.h b/widgets/misc/e-attachment-button.h
new file mode 100644
index 0000000..6e2f567
--- /dev/null
+++ b/widgets/misc/e-attachment-button.h
@@ -0,0 +1,83 @@
+/*
+ * e-attachment-button.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_BUTTON_H
+#define E_ATTACHMENT_BUTTON_H
+
+#include <gtk/gtk.h>
+#include <widgets/misc/e-attachment.h>
+#include <widgets/misc/e-attachment-view.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_BUTTON \
+ (e_attachment_button_get_type ())
+#define E_ATTACHMENT_BUTTON(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_BUTTON, EAttachmentButton))
+#define E_ATTACHMENT_BUTTON_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_BUTTON, EAttachmentButtonClass))
+#define E_IS_ATTACHMENT_BUTTON(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_BUTTON))
+#define E_IS_ATTACHMENT_BUTTON_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_BUTTON))
+#define E_ATTACHMENT_BUTTON_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_BUTTON, EAttachmentButtonClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentButton EAttachmentButton;
+typedef struct _EAttachmentButtonClass EAttachmentButtonClass;
+typedef struct _EAttachmentButtonPrivate EAttachmentButtonPrivate;
+
+struct _EAttachmentButton {
+ GtkHBox parent;
+ EAttachmentButtonPrivate *priv;
+};
+
+struct _EAttachmentButtonClass {
+ GtkHBoxClass parent_class;
+};
+
+GType e_attachment_button_get_type (void);
+GtkWidget * e_attachment_button_new (EAttachmentView *view);
+EAttachmentView *
+ e_attachment_button_get_view (EAttachmentButton *button);
+EAttachment * e_attachment_button_get_attachment
+ (EAttachmentButton *button);
+void e_attachment_button_set_attachment
+ (EAttachmentButton *button,
+ EAttachment *attachment);
+gboolean e_attachment_button_get_expandable
+ (EAttachmentButton *button);
+void e_attachment_button_set_expandable
+ (EAttachmentButton *button,
+ gboolean expandable);
+gboolean e_attachment_button_get_expanded(EAttachmentButton *button);
+void e_attachment_button_set_expanded(EAttachmentButton *button,
+ gboolean expanded);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_BUTTON_H */
diff --git a/widgets/misc/e-attachment-dialog.c b/widgets/misc/e-attachment-dialog.c
new file mode 100644
index 0000000..e8c599a
--- /dev/null
+++ b/widgets/misc/e-attachment-dialog.c
@@ -0,0 +1,446 @@
+/*
+ * e-attachment-dialog.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-dialog.h"
+
+#include <glib/gi18n.h>
+
+#define E_ATTACHMENT_DIALOG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_DIALOG, EAttachmentDialogPrivate))
+
+struct _EAttachmentDialogPrivate {
+ EAttachment *attachment;
+ GtkWidget *display_name_entry;
+ GtkWidget *description_entry;
+ GtkWidget *content_type_label;
+ GtkWidget *disposition_checkbox;
+};
+
+enum {
+ PROP_0,
+ PROP_ATTACHMENT
+};
+
+static gpointer parent_class;
+
+static void
+attachment_dialog_update (EAttachmentDialog *dialog)
+{
+ EAttachment *attachment;
+ GFileInfo *file_info;
+ GtkWidget *widget;
+ const gchar *content_type;
+ const gchar *display_name;
+ const gchar *description;
+ const gchar *disposition;
+ gchar *type_description = NULL;
+ gboolean sensitive;
+ gboolean active;
+
+ attachment = e_attachment_dialog_get_attachment (dialog);
+
+ if (attachment != NULL) {
+ file_info = e_attachment_get_file_info (attachment);
+ description = e_attachment_get_description (attachment);
+ disposition = e_attachment_get_disposition (attachment);
+ } else {
+ file_info = NULL;
+ description = NULL;
+ disposition = NULL;
+ }
+
+ if (file_info != NULL) {
+ content_type = g_file_info_get_content_type (file_info);
+ display_name = g_file_info_get_display_name (file_info);
+ } else {
+ content_type = NULL;
+ display_name = NULL;
+ }
+
+ if (content_type != NULL) {
+ gchar *comment;
+ gchar *mime_type;
+
+ comment = g_content_type_get_description (content_type);
+ mime_type = g_content_type_get_mime_type (content_type);
+
+ type_description =
+ g_strdup_printf ("%s (%s)", comment, mime_type);
+
+ g_free (comment);
+ g_free (mime_type);
+ }
+
+ sensitive = G_IS_FILE_INFO (file_info);
+
+ gtk_dialog_set_response_sensitive (
+ GTK_DIALOG (dialog), GTK_RESPONSE_OK, sensitive);
+
+ widget = dialog->priv->display_name_entry;
+ gtk_widget_set_sensitive (widget, sensitive);
+ if (display_name != NULL)
+ gtk_entry_set_text (GTK_ENTRY (widget), display_name);
+
+ widget = dialog->priv->description_entry;
+ gtk_widget_set_sensitive (widget, sensitive);
+ if (description != NULL)
+ gtk_entry_set_text (GTK_ENTRY (widget), description);
+
+ widget = dialog->priv->content_type_label;
+ gtk_label_set_text (GTK_LABEL (widget), type_description);
+
+ active = (g_strcmp0 (disposition, "inline") == 0);
+ widget = dialog->priv->disposition_checkbox;
+ gtk_widget_set_sensitive (widget, sensitive);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), active);
+
+ g_free (type_description);
+}
+
+static void
+attachment_dialog_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ATTACHMENT:
+ e_attachment_dialog_set_attachment (
+ E_ATTACHMENT_DIALOG (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_dialog_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ATTACHMENT:
+ g_value_set_object (
+ value, e_attachment_dialog_get_attachment (
+ E_ATTACHMENT_DIALOG (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_dialog_dispose (GObject *object)
+{
+ EAttachmentDialogPrivate *priv;
+
+ priv = E_ATTACHMENT_DIALOG_GET_PRIVATE (object);
+
+ if (priv->attachment != NULL) {
+ g_object_unref (priv->attachment);
+ priv->attachment = NULL;
+ }
+
+ if (priv->display_name_entry != NULL) {
+ g_object_unref (priv->display_name_entry);
+ priv->display_name_entry = NULL;
+ }
+
+ if (priv->description_entry != NULL) {
+ g_object_unref (priv->description_entry);
+ priv->description_entry = NULL;
+ }
+
+ if (priv->content_type_label != NULL) {
+ g_object_unref (priv->content_type_label);
+ priv->content_type_label = NULL;
+ }
+
+ if (priv->disposition_checkbox != NULL) {
+ g_object_unref (priv->disposition_checkbox);
+ priv->disposition_checkbox = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+attachment_dialog_map (GtkWidget *widget)
+{
+ GtkWidget *action_area;
+ GtkWidget *content_area;
+
+ /* Chain up to parent's map() method. */
+ GTK_WIDGET_CLASS (parent_class)->map (widget);
+
+ /* XXX Override GtkDialog's broken style property defaults. */
+ action_area = gtk_dialog_get_action_area (GTK_DIALOG (widget));
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (widget));
+
+ gtk_box_set_spacing (GTK_BOX (content_area), 12);
+ gtk_container_set_border_width (GTK_CONTAINER (action_area), 0);
+ gtk_container_set_border_width (GTK_CONTAINER (content_area), 12);
+}
+
+static void
+attachment_dialog_response (GtkDialog *dialog,
+ gint response_id)
+{
+ EAttachmentDialogPrivate *priv;
+ EAttachment *attachment;
+ GtkToggleButton *button;
+ GFileInfo *file_info;
+ CamelMimePart *mime_part;
+ const gchar *attribute;
+ const gchar *text;
+ gboolean active;
+
+ if (response_id != GTK_RESPONSE_OK)
+ return;
+
+ priv = E_ATTACHMENT_DIALOG_GET_PRIVATE (dialog);
+ g_return_if_fail (E_IS_ATTACHMENT (priv->attachment));
+ attachment = priv->attachment;
+
+ file_info = e_attachment_get_file_info (attachment);
+ g_return_if_fail (G_IS_FILE_INFO (file_info));
+
+ mime_part = e_attachment_get_mime_part (attachment);
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME;
+ text = gtk_entry_get_text (GTK_ENTRY (priv->display_name_entry));
+ g_file_info_set_attribute_string (file_info, attribute, text);
+
+ if (mime_part != NULL)
+ camel_mime_part_set_filename (mime_part, text);
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
+ text = gtk_entry_get_text (GTK_ENTRY (priv->description_entry));
+ g_file_info_set_attribute_string (file_info, attribute, text);
+
+ if (mime_part != NULL)
+ camel_mime_part_set_description (mime_part, text);
+
+ button = GTK_TOGGLE_BUTTON (priv->disposition_checkbox);
+ active = gtk_toggle_button_get_active (button);
+ text = active ? "inline" : "attachment";
+ e_attachment_set_disposition (attachment, text);
+
+ if (mime_part != NULL)
+ camel_mime_part_set_disposition (mime_part, text);
+
+ g_object_notify (G_OBJECT (attachment), "file-info");
+}
+
+static void
+attachment_dialog_class_init (EAttachmentDialogClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkDialogClass *dialog_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EAttachmentDialogPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = attachment_dialog_set_property;
+ object_class->get_property = attachment_dialog_get_property;
+ object_class->dispose = attachment_dialog_dispose;
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->map = attachment_dialog_map;
+
+ dialog_class = GTK_DIALOG_CLASS (class);
+ dialog_class->response = attachment_dialog_response;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ATTACHMENT,
+ g_param_spec_object (
+ "attachment",
+ "Attachment",
+ NULL,
+ E_TYPE_ATTACHMENT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+attachment_dialog_init (EAttachmentDialog *dialog)
+{
+ GtkWidget *container;
+ GtkWidget *widget;
+
+ dialog->priv = E_ATTACHMENT_DIALOG_GET_PRIVATE (dialog);
+
+ gtk_dialog_add_button (
+ GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_button (
+ GTK_DIALOG (dialog), GTK_STOCK_OK, GTK_RESPONSE_OK);
+ gtk_window_set_icon_name (
+ GTK_WINDOW (dialog), "mail-attachment");
+ gtk_window_set_title (
+ GTK_WINDOW (dialog), _("Attachment Properties"));
+
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ container = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ widget = gtk_table_new (4, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (widget), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (widget), 6);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_entry_new ();
+ gtk_entry_set_activates_default (GTK_ENTRY (widget), TRUE);
+ gtk_table_attach (
+ GTK_TABLE (container), widget,
+ 1, 2, 0, 1, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ dialog->priv->display_name_entry = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new_with_mnemonic (_("_Filename:"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (widget), dialog->priv->display_name_entry);
+ gtk_table_attach (
+ GTK_TABLE (container), widget,
+ 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (widget);
+
+ widget = gtk_entry_new ();
+ gtk_entry_set_activates_default (GTK_ENTRY (widget), TRUE);
+ gtk_table_attach (
+ GTK_TABLE (container), widget,
+ 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ dialog->priv->description_entry = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new_with_mnemonic (_("_Description:"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (widget), dialog->priv->description_entry);
+ gtk_table_attach (
+ GTK_TABLE (container), widget,
+ 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new (NULL);
+ gtk_label_set_selectable (GTK_LABEL (widget), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_table_attach (
+ GTK_TABLE (container), widget,
+ 1, 2, 2, 3, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ dialog->priv->content_type_label = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new (_("MIME Type:"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
+ gtk_table_attach (
+ GTK_TABLE (container), widget,
+ 0, 1, 2, 3, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (widget);
+
+ widget = gtk_check_button_new_with_mnemonic (
+ _("_Suggest automatic display of attachment"));
+ gtk_table_attach (
+ GTK_TABLE (container), widget,
+ 0, 2, 3, 4, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ dialog->priv->disposition_checkbox = g_object_ref (widget);
+ gtk_widget_show (widget);
+}
+
+GType
+e_attachment_dialog_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EAttachmentDialogClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) attachment_dialog_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_init */
+ sizeof (EAttachmentDialog),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) attachment_dialog_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ GTK_TYPE_DIALOG, "EAttachmentDialog", &type_info, 0);
+ }
+
+ return type;
+}
+
+GtkWidget *
+e_attachment_dialog_new (GtkWindow *parent,
+ EAttachment *attachment)
+{
+ if (parent != NULL)
+ g_return_val_if_fail (GTK_IS_WINDOW (parent), NULL);
+ if (attachment != NULL)
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+
+ return g_object_new (
+ E_TYPE_ATTACHMENT_DIALOG,
+ "transient-for", parent, "attachment", attachment, NULL);
+}
+
+EAttachment *
+e_attachment_dialog_get_attachment (EAttachmentDialog *dialog)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_DIALOG (dialog), NULL);
+
+ return dialog->priv->attachment;
+}
+
+void
+e_attachment_dialog_set_attachment (EAttachmentDialog *dialog,
+ EAttachment *attachment)
+{
+ g_return_if_fail (E_IS_ATTACHMENT_DIALOG (dialog));
+
+ if (attachment != NULL) {
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ g_object_ref (attachment);
+ }
+
+ if (dialog->priv->attachment != NULL)
+ g_object_unref (dialog->priv->attachment);
+
+ dialog->priv->attachment = attachment;
+
+ attachment_dialog_update (dialog);
+
+ g_object_notify (G_OBJECT (dialog), "attachment");
+}
diff --git a/widgets/misc/e-attachment-dialog.h b/widgets/misc/e-attachment-dialog.h
new file mode 100644
index 0000000..8e24e18
--- /dev/null
+++ b/widgets/misc/e-attachment-dialog.h
@@ -0,0 +1,73 @@
+/*
+ * e-attachment-dialog.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_DIALOG_H
+#define E_ATTACHMENT_DIALOG_H
+
+#include <gtk/gtk.h>
+#include <widgets/misc/e-attachment.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_DIALOG \
+ (e_attachment_dialog_get_type ())
+#define E_ATTACHMENT_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_DIALOG, EAttachmentDialog))
+#define E_ATTACHMENT_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_DIALOG, EAttachmentDialogClass))
+#define E_IS_ATTACHMENT_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_DIALOG))
+#define E_IS_ATTACHMENT_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_DIALOG))
+#define E_ATTACHMENT_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_DIALOG, EAttachmentDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentDialog EAttachmentDialog;
+typedef struct _EAttachmentDialogClass EAttachmentDialogClass;
+typedef struct _EAttachmentDialogPrivate EAttachmentDialogPrivate;
+
+struct _EAttachmentDialog {
+ GtkDialog parent;
+ EAttachmentDialogPrivate *priv;
+};
+
+struct _EAttachmentDialogClass {
+ GtkDialogClass parent_class;
+};
+
+GType e_attachment_dialog_get_type (void);
+GtkWidget * e_attachment_dialog_new (GtkWindow *parent,
+ EAttachment *attachment);
+EAttachment * e_attachment_dialog_get_attachment
+ (EAttachmentDialog *dialog);
+void e_attachment_dialog_set_attachment
+ (EAttachmentDialog *dialog,
+ EAttachment *attachment);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_DIALOG_H */
diff --git a/widgets/misc/e-attachment-handler-image.c b/widgets/misc/e-attachment-handler-image.c
new file mode 100644
index 0000000..ba5f8d7
--- /dev/null
+++ b/widgets/misc/e-attachment-handler-image.c
@@ -0,0 +1,280 @@
+/*
+ * e-attachment-handler-image.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-image.h"
+
+#include <glib/gi18n.h>
+#include <gconf/gconf-client.h>
+
+#define E_ATTACHMENT_HANDLER_IMAGE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_IMAGE, EAttachmentHandlerImagePrivate))
+
+struct _EAttachmentHandlerImagePrivate {
+ gint placeholder;
+};
+
+static gpointer parent_class;
+
+static const gchar *ui =
+"<ui>"
+" <popup name='context'>"
+" <placeholder name='custom-actions'>"
+" <menuitem action='image-set-as-background'/>"
+" </placeholder>"
+" </popup>"
+"</ui>";
+
+static void
+action_image_set_as_background_saved_cb (EAttachment *attachment,
+ GAsyncResult *result,
+ EAttachmentHandler *handler)
+{
+ EAttachmentView *view;
+ GConfClient *client;
+ GtkWidget *dialog;
+ GFile *file;
+ const gchar *key;
+ gpointer parent;
+ gchar *value;
+ GError *error = NULL;
+
+ client = gconf_client_get_default ();
+ view = e_attachment_handler_get_view (handler);
+
+ file = e_attachment_save_finish (attachment, result, &error);
+
+ if (error != NULL)
+ goto error;
+
+ value = g_file_get_path (file);
+ g_object_unref (file);
+
+ key = "/desktop/gnome/background/picture_filename";
+ gconf_client_set_string (client, key, value, &error);
+ g_free (value);
+
+ if (error != NULL)
+ goto error;
+
+ /* Ignore errors for this part. */
+ key = "/desktop/gnome/background/picture_options";
+ value = gconf_client_get_string (client, key, NULL);
+ if (g_strcmp0 (value, "none") == 0)
+ gconf_client_set_string (client, key, "wallpaper", NULL);
+ g_free (value);
+
+ goto exit;
+
+error:
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+ dialog = gtk_message_dialog_new_with_markup (
+ parent, GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ "<big><b>%s</b></big>",
+ _("Could not set as background"));
+
+ gtk_message_dialog_format_secondary_text (
+ GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+ g_error_free (error);
+
+exit:
+ g_object_unref (client);
+ g_object_unref (handler);
+}
+
+static void
+action_image_set_as_background_cb (GtkAction *action,
+ EAttachmentHandler *handler)
+{
+ EAttachmentView *view;
+ EAttachment *attachment;
+ GFile *destination;
+ GList *selected;
+ gchar *path;
+
+ view = e_attachment_handler_get_view (handler);
+ selected = e_attachment_view_get_selected_attachments (view);
+ g_return_if_fail (g_list_length (selected) == 1);
+ attachment = E_ATTACHMENT (selected->data);
+
+ /* Save the image under ~/.gnome2/wallpapers/. */
+ path = g_build_filename (
+ g_get_home_dir (), ".gnome2", "wallpapers", NULL);
+ destination = g_file_new_for_path (path);
+ g_mkdir_with_parents (path, 0755);
+ g_free (path);
+
+ e_attachment_save_async (
+ attachment, destination, (GAsyncReadyCallback)
+ action_image_set_as_background_saved_cb,
+ g_object_ref (handler));
+
+ g_object_unref (destination);
+
+ g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+}
+
+static GtkActionEntry standard_entries[] = {
+
+ { "image-set-as-background",
+ NULL,
+ N_("Set as _Background"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_image_set_as_background_cb) }
+};
+
+static void
+attachment_handler_image_update_actions_cb (EAttachmentView *view,
+ EAttachmentHandler *handler)
+{
+ EAttachmentHandlerImagePrivate *priv;
+ EAttachment *attachment;
+ GFileInfo *file_info;
+ GtkActionGroup *action_group;
+ const gchar *content_type;
+ gchar *mime_type;
+ GList *selected;
+ gboolean visible = FALSE;
+
+ priv = E_ATTACHMENT_HANDLER_IMAGE_GET_PRIVATE (handler);
+
+ selected = e_attachment_view_get_selected_attachments (view);
+
+ if (g_list_length (selected) != 1)
+ goto exit;
+
+ attachment = E_ATTACHMENT (selected->data);
+ file_info = e_attachment_get_file_info (attachment);
+
+ if (file_info == NULL)
+ goto exit;
+
+ if (e_attachment_get_loading (attachment))
+ goto exit;
+
+ if (e_attachment_get_saving (attachment))
+ goto exit;
+
+ content_type = g_file_info_get_content_type (file_info);
+
+ mime_type = g_content_type_get_mime_type (content_type);
+ visible = (g_ascii_strncasecmp (mime_type, "image/", 6) == 0);
+ g_free (mime_type);
+
+exit:
+ action_group = e_attachment_view_get_action_group (view, "image");
+ 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_image_constructed (GObject *object)
+{
+ EAttachmentHandlerImagePrivate *priv;
+ EAttachmentHandler *handler;
+ EAttachmentView *view;
+ GtkActionGroup *action_group;
+ GtkUIManager *ui_manager;
+ GError *error = NULL;
+
+ handler = E_ATTACHMENT_HANDLER (object);
+ priv = E_ATTACHMENT_HANDLER_IMAGE_GET_PRIVATE (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, "image");
+ gtk_action_group_add_actions (
+ action_group, standard_entries,
+ G_N_ELEMENTS (standard_entries), object);
+
+ 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_image_update_actions_cb),
+ object);
+}
+
+static void
+attachment_handler_image_class_init (EAttachmentHandlerImageClass *class)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EAttachmentHandlerImagePrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->constructed = attachment_handler_image_constructed;
+}
+
+static void
+attachment_handler_image_init (EAttachmentHandlerImage *handler)
+{
+ handler->priv = E_ATTACHMENT_HANDLER_IMAGE_GET_PRIVATE (handler);
+}
+
+GType
+e_attachment_handler_image_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EAttachmentHandlerImageClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) attachment_handler_image_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EAttachmentHandlerImage),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) attachment_handler_image_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ E_TYPE_ATTACHMENT_HANDLER,
+ "EAttachmentHandlerImage",
+ &type_info, 0);
+ }
+
+ return type;
+}
diff --git a/widgets/misc/e-attachment-handler-image.h b/widgets/misc/e-attachment-handler-image.h
new file mode 100644
index 0000000..53b076c
--- /dev/null
+++ b/widgets/misc/e-attachment-handler-image.h
@@ -0,0 +1,65 @@
+/*
+ * e-attachment-handler-image.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_IMAGE_H
+#define E_ATTACHMENT_HANDLER_IMAGE_H
+
+#include <widgets/misc/e-attachment-handler.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_HANDLER_IMAGE \
+ (e_attachment_handler_image_get_type ())
+#define E_ATTACHMENT_HANDLER_IMAGE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_IMAGE, EAttachmentHandlerImage))
+#define E_ATTACHMENT_HANDLER_IMAGE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_HANDLER_IMAGE, EAttachmentHandlerImageClass))
+#define E_IS_ATTACHMENT_HANDLER_IMAGE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_IMAGE))
+#define E_IS_ATTACHMENT_HANDLER_IMAGE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_HANDLER_IMAGE))
+#define E_ATTACHMENT_HANDLER_IMAGE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER_IMAGE, EAttachmentHandlerImageClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentHandlerImage EAttachmentHandlerImage;
+typedef struct _EAttachmentHandlerImageClass EAttachmentHandlerImageClass;
+typedef struct _EAttachmentHandlerImagePrivate EAttachmentHandlerImagePrivate;
+
+struct _EAttachmentHandlerImage {
+ EAttachmentHandler parent;
+ EAttachmentHandlerImagePrivate *priv;
+};
+
+struct _EAttachmentHandlerImageClass {
+ EAttachmentHandlerClass parent_class;
+};
+
+GType e_attachment_handler_image_get_type (void);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_HANDLER_IMAGE_H */
diff --git a/widgets/misc/e-attachment-handler.c b/widgets/misc/e-attachment-handler.c
new file mode 100644
index 0000000..03af1ee
--- /dev/null
+++ b/widgets/misc/e-attachment-handler.c
@@ -0,0 +1,191 @@
+/*
+ * e-attachment-handler.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.h"
+
+#define E_ATTACHMENT_HANDLER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER, EAttachmentHandlerPrivate))
+
+struct _EAttachmentHandlerPrivate {
+ gpointer view; /* weak pointer */
+};
+
+enum {
+ PROP_0,
+ PROP_VIEW
+};
+
+static gpointer parent_class;
+
+static void
+attachment_handler_set_view (EAttachmentHandler *handler,
+ EAttachmentView *view)
+{
+ g_return_if_fail (handler->priv->view == NULL);
+
+ handler->priv->view = view;
+
+ g_object_add_weak_pointer (
+ G_OBJECT (view), &handler->priv->view);
+}
+
+static void
+attachment_handler_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_VIEW:
+ attachment_handler_set_view (
+ E_ATTACHMENT_HANDLER (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_handler_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_VIEW:
+ g_value_set_object (
+ value, e_attachment_handler_get_view (
+ E_ATTACHMENT_HANDLER (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_handler_constructed (GObject *object)
+{
+ /* This allows subclasses to chain up safely since GObject
+ * does not implement this method, and we might want to do
+ * something here in the future. */
+}
+
+static void
+attachment_handler_class_init (EAttachmentHandlerClass *class)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EAttachmentHandlerPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = attachment_handler_set_property;
+ object_class->get_property = attachment_handler_get_property;
+ object_class->constructed = attachment_handler_constructed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_VIEW,
+ g_param_spec_object (
+ "view",
+ "View",
+ NULL,
+ E_TYPE_ATTACHMENT_VIEW,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+attachment_handler_init (EAttachmentHandler *handler)
+{
+ handler->priv = E_ATTACHMENT_HANDLER_GET_PRIVATE (handler);
+}
+
+GType
+e_attachment_handler_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EAttachmentHandlerClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) attachment_handler_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EAttachmentHandler),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) attachment_handler_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ G_TYPE_OBJECT, "EAttachmentHandler",
+ &type_info, G_TYPE_FLAG_ABSTRACT);
+ }
+
+ return type;
+}
+
+EAttachmentView *
+e_attachment_handler_get_view (EAttachmentHandler *handler)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_HANDLER (handler), NULL);
+
+ return E_ATTACHMENT_VIEW (handler->priv->view);
+}
+
+GdkDragAction
+e_attachment_handler_get_drag_actions (EAttachmentHandler *handler)
+{
+ EAttachmentHandlerClass *class;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_HANDLER (handler), 0);
+
+ class = E_ATTACHMENT_HANDLER_GET_CLASS (handler);
+
+ if (class->get_drag_actions != NULL)
+ return class->get_drag_actions (handler);
+
+ return 0;
+}
+
+const GtkTargetEntry *
+e_attachment_handler_get_target_table (EAttachmentHandler *handler,
+ guint *n_targets)
+{
+ EAttachmentHandlerClass *class;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_HANDLER (handler), NULL);
+
+ class = E_ATTACHMENT_HANDLER_GET_CLASS (handler);
+
+ if (class->get_target_table != NULL)
+ return class->get_target_table (handler, n_targets);
+
+ if (n_targets != NULL)
+ *n_targets = 0;
+
+ return NULL;
+}
diff --git a/widgets/misc/e-attachment-handler.h b/widgets/misc/e-attachment-handler.h
new file mode 100644
index 0000000..05df3e8
--- /dev/null
+++ b/widgets/misc/e-attachment-handler.h
@@ -0,0 +1,78 @@
+/*
+ * e-attachment-handler.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_H
+#define E_ATTACHMENT_HANDLER_H
+
+#include <widgets/misc/e-attachment-view.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_HANDLER \
+ (e_attachment_handler_get_type ())
+#define E_ATTACHMENT_HANDLER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER, EAttachmentHandler))
+#define E_ATTACHMENT_HANDLER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_HANDLER, EAttachmentHandlerClass))
+#define E_IS_ATTACHMENT_HANDLER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER))
+#define E_IS_ATTACHMENT_HANDLER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_HANDLER))
+#define E_ATTACHMENT_HANDLER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_HANDLER, EAttachmentHandlerClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentHandler EAttachmentHandler;
+typedef struct _EAttachmentHandlerClass EAttachmentHandlerClass;
+typedef struct _EAttachmentHandlerPrivate EAttachmentHandlerPrivate;
+
+struct _EAttachmentHandler {
+ GObject parent;
+ EAttachmentHandlerPrivate *priv;
+};
+
+struct _EAttachmentHandlerClass {
+ GObjectClass parent_class;
+
+ GdkDragAction (*get_drag_actions) (EAttachmentHandler *handler);
+ const GtkTargetEntry *
+ (*get_target_table) (EAttachmentHandler *handler,
+ guint *n_targets);
+};
+
+GType e_attachment_handler_get_type (void);
+EAttachmentView *
+ e_attachment_handler_get_view (EAttachmentHandler *handler);
+GdkDragAction e_attachment_handler_get_drag_actions
+ (EAttachmentHandler *handler);
+const GtkTargetEntry *
+ e_attachment_handler_get_target_table
+ (EAttachmentHandler *handler,
+ guint *n_targets);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_HANDLER_H */
diff --git a/widgets/misc/e-attachment-icon-view.c b/widgets/misc/e-attachment-icon-view.c
new file mode 100644
index 0000000..0845f82
--- /dev/null
+++ b/widgets/misc/e-attachment-icon-view.c
@@ -0,0 +1,537 @@
+/*
+ * e-attachment-icon-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)
+ *
+ */
+
+#include "e-attachment-icon-view.h"
+
+#include <glib/gi18n.h>
+
+#include "e-attachment.h"
+#include "e-attachment-store.h"
+#include "e-attachment-view.h"
+
+#define E_ATTACHMENT_ICON_VIEW_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_ICON_VIEW, EAttachmentIconViewPrivate))
+
+struct _EAttachmentIconViewPrivate {
+ EAttachmentViewPrivate view_priv;
+};
+
+enum {
+ PROP_0,
+ PROP_EDITABLE
+};
+
+static gpointer parent_class;
+
+static void
+attachment_icon_view_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_EDITABLE:
+ e_attachment_view_set_editable (
+ E_ATTACHMENT_VIEW (object),
+ g_value_get_boolean (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_icon_view_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_EDITABLE:
+ g_value_set_boolean (
+ value, e_attachment_view_get_editable (
+ E_ATTACHMENT_VIEW (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_icon_view_dispose (GObject *object)
+{
+ e_attachment_view_dispose (E_ATTACHMENT_VIEW (object));
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+attachment_icon_view_finalize (GObject *object)
+{
+ e_attachment_view_finalize (E_ATTACHMENT_VIEW (object));
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+attachment_icon_view_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ 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)->
+ button_press_event (widget, event);
+}
+
+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)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ if (e_attachment_view_key_press_event (view, event))
+ return TRUE;
+
+ /* Chain up to parent's key_press_event() method. */
+ return GTK_WIDGET_CLASS (parent_class)->
+ 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,
+ gint x,
+ gint y,
+ guint time)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (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,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ e_attachment_view_drag_data_received (
+ view, context, x, y, selection, info, time);
+}
+
+static gboolean
+attachment_icon_view_popup_menu (GtkWidget *widget)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ e_attachment_view_show_popup_menu (view, NULL, NULL, NULL);
+
+ return TRUE;
+}
+
+static void
+attachment_icon_view_item_activated (GtkIconView *icon_view,
+ GtkTreePath *path)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (icon_view);
+
+ e_attachment_view_open_path (view, path, NULL);
+}
+
+static EAttachmentViewPrivate *
+attachment_icon_view_get_private (EAttachmentView *view)
+{
+ EAttachmentIconViewPrivate *priv;
+
+ priv = E_ATTACHMENT_ICON_VIEW_GET_PRIVATE (view);
+
+ return &priv->view_priv;
+}
+
+static EAttachmentStore *
+attachment_icon_view_get_store (EAttachmentView *view)
+{
+ GtkIconView *icon_view;
+ GtkTreeModel *model;
+
+ icon_view = GTK_ICON_VIEW (view);
+ model = gtk_icon_view_get_model (icon_view);
+
+ return E_ATTACHMENT_STORE (model);
+}
+
+static GtkTreePath *
+attachment_icon_view_get_path_at_pos (EAttachmentView *view,
+ gint x,
+ gint y)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ return gtk_icon_view_get_path_at_pos (icon_view, x, y);
+}
+
+static GList *
+attachment_icon_view_get_selected_paths (EAttachmentView *view)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ return gtk_icon_view_get_selected_items (icon_view);
+}
+
+static gboolean
+attachment_icon_view_path_is_selected (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ return gtk_icon_view_path_is_selected (icon_view, path);
+}
+
+static void
+attachment_icon_view_select_path (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ gtk_icon_view_select_path (icon_view, path);
+}
+
+static void
+attachment_icon_view_unselect_path (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ gtk_icon_view_unselect_path (icon_view, path);
+}
+
+static void
+attachment_icon_view_select_all (EAttachmentView *view)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ gtk_icon_view_select_all (icon_view);
+}
+
+static void
+attachment_icon_view_unselect_all (EAttachmentView *view)
+{
+ GtkIconView *icon_view;
+
+ icon_view = GTK_ICON_VIEW (view);
+
+ gtk_icon_view_unselect_all (icon_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;
+ GtkWidgetClass *widget_class;
+ GtkIconViewClass *icon_view_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EAttachmentViewPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = attachment_icon_view_set_property;
+ object_class->get_property = attachment_icon_view_get_property;
+ object_class->dispose = attachment_icon_view_dispose;
+ object_class->finalize = attachment_icon_view_finalize;
+
+ 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;
+
+ icon_view_class = GTK_ICON_VIEW_CLASS (class);
+ icon_view_class->item_activated = attachment_icon_view_item_activated;
+
+ g_object_class_override_property (
+ object_class, PROP_EDITABLE, "editable");
+}
+
+static void
+attachment_icon_view_iface_init (EAttachmentViewIface *iface)
+{
+ iface->get_private = attachment_icon_view_get_private;
+ iface->get_store = attachment_icon_view_get_store;
+
+ iface->get_path_at_pos = attachment_icon_view_get_path_at_pos;
+ iface->get_selected_paths = attachment_icon_view_get_selected_paths;
+ iface->path_is_selected = attachment_icon_view_path_is_selected;
+ iface->select_path = attachment_icon_view_select_path;
+ 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
+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);
+
+ e_attachment_view_init (E_ATTACHMENT_VIEW (icon_view));
+
+ gtk_icon_view_set_selection_mode (
+ GTK_ICON_VIEW (icon_view), GTK_SELECTION_MULTIPLE);
+
+ 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, FALSE);
+
+ gtk_cell_layout_add_attribute (
+ cell_layout, renderer, "gicon",
+ E_ATTACHMENT_STORE_COLUMN_ICON);
+
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (
+ renderer, "alignment", PANGO_ALIGN_CENTER,
+ "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",
+ E_ATTACHMENT_STORE_COLUMN_CAPTION);
+
+ renderer = gtk_cell_renderer_progress_new ();
+ g_object_set (renderer, "text", _("Loading"), NULL);
+ gtk_cell_layout_pack_start (cell_layout, renderer, TRUE);
+
+ gtk_cell_layout_add_attribute (
+ cell_layout, renderer, "value",
+ E_ATTACHMENT_STORE_COLUMN_PERCENT);
+
+ gtk_cell_layout_add_attribute (
+ cell_layout, renderer, "visible",
+ E_ATTACHMENT_STORE_COLUMN_LOADING);
+
+ renderer = gtk_cell_renderer_progress_new ();
+ g_object_set (renderer, "text", _("Saving"), NULL);
+ gtk_cell_layout_pack_start (cell_layout, renderer, TRUE);
+
+ gtk_cell_layout_add_attribute (
+ cell_layout, renderer, "value",
+ E_ATTACHMENT_STORE_COLUMN_PERCENT);
+
+ gtk_cell_layout_add_attribute (
+ cell_layout, renderer, "visible",
+ E_ATTACHMENT_STORE_COLUMN_SAVING);
+}
+
+GType
+e_attachment_icon_view_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EAttachmentIconViewClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) attachment_icon_view_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EAttachmentIconView),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) attachment_icon_view_init,
+ NULL /* value_table */
+ };
+
+ static const GInterfaceInfo iface_info = {
+ (GInterfaceInitFunc) attachment_icon_view_iface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL /* interface_data */
+ };
+
+ type = g_type_register_static (
+ GTK_TYPE_ICON_VIEW, "EAttachmentIconView",
+ &type_info, 0);
+
+ g_type_add_interface_static (
+ type, E_TYPE_ATTACHMENT_VIEW, &iface_info);
+ }
+
+ return type;
+}
+
+GtkWidget *
+e_attachment_icon_view_new (void)
+{
+ return g_object_new (E_TYPE_ATTACHMENT_ICON_VIEW, NULL);
+}
diff --git a/widgets/misc/e-attachment-icon-view.h b/widgets/misc/e-attachment-icon-view.h
new file mode 100644
index 0000000..ab9360e
--- /dev/null
+++ b/widgets/misc/e-attachment-icon-view.h
@@ -0,0 +1,66 @@
+/*
+ * e-attachment-icon-view.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_ICON_VIEW_H
+#define E_ATTACHMENT_ICON_VIEW_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_ICON_VIEW \
+ (e_attachment_icon_view_get_type ())
+#define E_ATTACHMENT_ICON_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_ICON_VIEW, EAttachmentIconView))
+#define E_ATTACHMENT_ICON_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_ICON_VIEW, EAttachmentIconView))
+#define E_IS_ATTACHMENT_ICON_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_ICON_VIEW))
+#define E_IS_ATTACHMENT_ICON_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_ICON_VIEW))
+#define E_ATTACHMENT_ICON_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_ICON_VIEW))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentIconView EAttachmentIconView;
+typedef struct _EAttachmentIconViewClass EAttachmentIconViewClass;
+typedef struct _EAttachmentIconViewPrivate EAttachmentIconViewPrivate;
+
+struct _EAttachmentIconView {
+ GtkIconView parent;
+ EAttachmentIconViewPrivate *priv;
+};
+
+struct _EAttachmentIconViewClass {
+ GtkIconViewClass parent_class;
+};
+
+GType e_attachment_icon_view_get_type (void);
+GtkWidget * e_attachment_icon_view_new (void);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_ICON_VIEW_H */
diff --git a/widgets/misc/e-attachment-paned.c b/widgets/misc/e-attachment-paned.c
new file mode 100644
index 0000000..853ff48
--- /dev/null
+++ b/widgets/misc/e-attachment-paned.c
@@ -0,0 +1,781 @@
+/*
+ * e-attachment-paned.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-paned.h"
+
+#include <glib/gi18n.h>
+
+#include "e-util/e-binding.h"
+#include "e-util/gconf-bridge.h"
+
+#include "e-attachment-view.h"
+#include "e-attachment-store.h"
+#include "e-attachment-icon-view.h"
+#include "e-attachment-tree-view.h"
+
+#define E_ATTACHMENT_PANED_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_PANED, EAttachmentPanedPrivate))
+
+#define NUM_VIEWS 2
+
+/* Initial height of the lower pane. */
+#define INITIAL_HEIGHT 150
+
+struct _EAttachmentPanedPrivate {
+ GtkTreeModel *model;
+ GtkWidget *expander;
+ GtkWidget *notebook;
+ GtkWidget *combo_box;
+ GtkWidget *icon_view;
+ GtkWidget *tree_view;
+ GtkWidget *show_hide_label;
+ GtkWidget *status_icon;
+ GtkWidget *status_label;
+ GtkWidget *content_area;
+
+ gint active_view;
+ guint expanded : 1;
+};
+
+enum {
+ PROP_0,
+ PROP_ACTIVE_VIEW,
+ PROP_EDITABLE,
+ PROP_EXPANDED
+};
+
+static gpointer parent_class;
+
+static void
+attachment_paned_notify_cb (EAttachmentPaned *paned,
+ GParamSpec *pspec,
+ GtkExpander *expander)
+{
+ GtkLabel *label;
+ const gchar *text;
+
+ label = GTK_LABEL (paned->priv->show_hide_label);
+
+ /* Update the expander label. */
+ if (gtk_expander_get_expanded (expander))
+ text = _("Hide _Attachment Bar");
+ else
+ text = _("Show _Attachment Bar");
+
+ gtk_label_set_text_with_mnemonic (label, text);
+}
+
+static void
+attachment_paned_sync_icon_view (EAttachmentPaned *paned)
+{
+ EAttachmentView *source;
+ EAttachmentView *target;
+
+ source = E_ATTACHMENT_VIEW (paned->priv->tree_view);
+ target = E_ATTACHMENT_VIEW (paned->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_attachment_paned_get_active_view (paned) == 1)
+ e_attachment_view_sync_selection (source, target);
+}
+
+static void
+attachment_paned_sync_tree_view (EAttachmentPaned *paned)
+{
+ EAttachmentView *source;
+ EAttachmentView *target;
+
+ source = E_ATTACHMENT_VIEW (paned->priv->icon_view);
+ target = E_ATTACHMENT_VIEW (paned->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_attachment_paned_get_active_view (paned) == 0)
+ e_attachment_view_sync_selection (source, target);
+}
+
+static void
+attachment_paned_update_status (EAttachmentPaned *paned)
+{
+ EAttachmentView *view;
+ EAttachmentStore *store;
+ GtkExpander *expander;
+ GtkLabel *label;
+ guint num_attachments;
+ guint64 total_size;
+ gchar *display_size;
+ gchar *markup;
+
+ view = E_ATTACHMENT_VIEW (paned);
+ store = e_attachment_view_get_store (view);
+ expander = GTK_EXPANDER (paned->priv->expander);
+ label = GTK_LABEL (paned->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);
+
+ g_free (display_size);
+
+ if (num_attachments > 0) {
+ gtk_widget_show (paned->priv->status_icon);
+ gtk_widget_show (paned->priv->status_label);
+ gtk_expander_set_expanded (expander, TRUE);
+ } else {
+ gtk_widget_hide (paned->priv->status_icon);
+ gtk_widget_hide (paned->priv->status_label);
+ gtk_expander_set_expanded (expander, FALSE);
+ }
+}
+
+static void
+attachment_paned_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTIVE_VIEW:
+ e_attachment_paned_set_active_view (
+ E_ATTACHMENT_PANED (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_attachment_paned_set_expanded (
+ E_ATTACHMENT_PANED (object),
+ g_value_get_boolean (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_paned_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTIVE_VIEW:
+ g_value_set_int (
+ value, e_attachment_paned_get_active_view (
+ E_ATTACHMENT_PANED (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_attachment_paned_get_expanded (
+ E_ATTACHMENT_PANED (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_paned_dispose (GObject *object)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (object);
+
+ if (priv->model != NULL) {
+ g_object_unref (priv->model);
+ priv->model = NULL;
+ }
+
+ if (priv->expander != NULL) {
+ g_object_unref (priv->expander);
+ priv->expander = NULL;
+ }
+
+ if (priv->notebook != NULL) {
+ g_object_unref (priv->notebook);
+ priv->notebook = 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->show_hide_label != NULL) {
+ g_object_unref (priv->show_hide_label);
+ priv->show_hide_label = 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->content_area != NULL) {
+ g_object_unref (priv->content_area);
+ priv->content_area = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+attachment_paned_constructed (GObject *object)
+{
+ EAttachmentPanedPrivate *priv;
+ GConfBridge *bridge;
+ const gchar *key;
+
+ priv = E_ATTACHMENT_PANED_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), "active-view",
+ G_OBJECT (priv->notebook), "page");
+
+ 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), "sensitive");
+
+ e_mutual_binding_new (
+ G_OBJECT (object), "expanded",
+ G_OBJECT (priv->notebook), "visible");
+
+ /* Set up property-to-GConf bindings. */
+
+ key = "/apps/evolution/shell/attachment_view";
+ gconf_bridge_bind_property (bridge, key, object, "active-view");
+}
+
+static EAttachmentViewPrivate *
+attachment_paned_get_private (EAttachmentView *view)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ return e_attachment_view_get_private (view);
+}
+
+static EAttachmentStore *
+attachment_paned_get_store (EAttachmentView *view)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ return e_attachment_view_get_store (view);
+}
+
+static GtkTreePath *
+attachment_paned_get_path_at_pos (EAttachmentView *view,
+ gint x,
+ gint y)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ return e_attachment_view_get_path_at_pos (view, x, y);
+}
+
+static GList *
+attachment_paned_get_selected_paths (EAttachmentView *view)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ return e_attachment_view_get_selected_paths (view);
+}
+
+static gboolean
+attachment_paned_path_is_selected (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ return e_attachment_view_path_is_selected (view, path);
+}
+
+static void
+attachment_paned_select_path (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ e_attachment_view_select_path (view, path);
+}
+
+static void
+attachment_paned_unselect_path (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ e_attachment_view_unselect_path (view, path);
+}
+
+static void
+attachment_paned_select_all (EAttachmentView *view)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ e_attachment_view_select_all (view);
+}
+
+static void
+attachment_paned_unselect_all (EAttachmentView *view)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ e_attachment_view_unselect_all (view);
+}
+
+static void
+attachment_paned_update_actions (EAttachmentView *view)
+{
+ EAttachmentPanedPrivate *priv;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (view);
+ view = E_ATTACHMENT_VIEW (priv->icon_view);
+
+ e_attachment_view_update_actions (view);
+}
+
+static void
+attachment_paned_class_init (EAttachmentPanedClass *class)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EAttachmentPanedPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = attachment_paned_set_property;
+ object_class->get_property = attachment_paned_get_property;
+ object_class->dispose = attachment_paned_dispose;
+ object_class->constructed = attachment_paned_constructed;
+
+ 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
+attachment_paned_iface_init (EAttachmentViewIface *iface)
+{
+ iface->get_private = attachment_paned_get_private;
+ iface->get_store = attachment_paned_get_store;
+ iface->get_path_at_pos = attachment_paned_get_path_at_pos;
+ iface->get_selected_paths = attachment_paned_get_selected_paths;
+ iface->path_is_selected = attachment_paned_path_is_selected;
+ iface->select_path = attachment_paned_select_path;
+ iface->unselect_path = attachment_paned_unselect_path;
+ iface->select_all = attachment_paned_select_all;
+ iface->unselect_all = attachment_paned_unselect_all;
+ iface->update_actions = attachment_paned_update_actions;
+}
+
+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 ();
+
+ /* 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, INITIAL_HEIGHT);
+ 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, FALSE, 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);
+
+ widget = gtk_vbox_new (FALSE, 6);
+ gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, FALSE);
+ paned->priv->content_area = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_hbox_new (FALSE, 6);
+ 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, TRUE, TRUE, 0);
+ 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 ());
+#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);
+ 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"));
+ gtk_combo_box_append_text (GTK_COMBO_BOX (widget), _("List View"));
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ paned->priv->combo_box = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = paned->priv->expander;
+
+ /* 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_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_alignment_new (0.5, 0.5, 0.0, 1.0);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_hbox_new (FALSE, 6);
+ gtk_container_add (GTK_CONTAINER (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);
+ 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));
+
+ g_signal_connect_swapped (
+ selection, "changed",
+ G_CALLBACK (attachment_paned_sync_icon_view), paned);
+
+ g_signal_connect_swapped (
+ paned->priv->icon_view, "selection-changed",
+ G_CALLBACK (attachment_paned_sync_tree_view), paned);
+
+ g_signal_connect_swapped (
+ paned->priv->expander, "notify::expanded",
+ G_CALLBACK (attachment_paned_notify_cb), paned);
+
+ g_signal_connect_swapped (
+ paned->priv->model, "notify::num-attachments",
+ G_CALLBACK (attachment_paned_update_status), paned);
+
+ g_signal_connect_swapped (
+ paned->priv->model, "notify::total-size",
+ G_CALLBACK (attachment_paned_update_status), paned);
+
+ g_object_unref (size_group);
+}
+
+GType
+e_attachment_paned_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EAttachmentPanedClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) attachment_paned_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EAttachmentPaned),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) attachment_paned_init,
+ NULL /* value_table */
+ };
+
+ static const GInterfaceInfo iface_info = {
+ (GInterfaceInitFunc) attachment_paned_iface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL /* interface_data */
+ };
+
+ type = g_type_register_static (
+ GTK_TYPE_VPANED, "EAttachmentPaned",
+ &type_info, 0);
+
+ g_type_add_interface_static (
+ type, E_TYPE_ATTACHMENT_VIEW, &iface_info);
+ }
+
+ return type;
+}
+
+GtkWidget *
+e_attachment_paned_new (void)
+{
+ return g_object_new (E_TYPE_ATTACHMENT_PANED, NULL);
+}
+
+GtkWidget *
+e_attachment_paned_get_content_area (EAttachmentPaned *paned)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_PANED (paned), NULL);
+
+ return paned->priv->content_area;
+}
+
+gint
+e_attachment_paned_get_active_view (EAttachmentPaned *paned)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_PANED (paned), 0);
+
+ return paned->priv->active_view;
+}
+
+void
+e_attachment_paned_set_active_view (EAttachmentPaned *paned,
+ gint active_view)
+{
+ g_return_if_fail (E_IS_ATTACHMENT_PANED (paned));
+ g_return_if_fail (active_view >= 0 && active_view < NUM_VIEWS);
+
+ paned->priv->active_view = active_view;
+
+ g_object_notify (G_OBJECT (paned), "active-view");
+}
+
+gboolean
+e_attachment_paned_get_expanded (EAttachmentPaned *paned)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_PANED (paned), FALSE);
+
+ return paned->priv->expanded;
+}
+
+void
+e_attachment_paned_set_expanded (EAttachmentPaned *paned,
+ gboolean expanded)
+{
+ g_return_if_fail (E_IS_ATTACHMENT_PANED (paned));
+
+ paned->priv->expanded = expanded;
+
+ g_object_notify (G_OBJECT (paned), "expanded");
+}
+
+void
+e_attachment_paned_drag_data_received (EAttachmentPaned *paned,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ g_return_if_fail (E_IS_ATTACHMENT_PANED (paned));
+
+ /* XXX Dirty hack for forwarding drop events. */
+ g_signal_emit_by_name (
+ paned->priv->icon_view, "drag-data-received",
+ context, x, y, selection, info, time);
+}
diff --git a/widgets/misc/e-attachment-paned.h b/widgets/misc/e-attachment-paned.h
new file mode 100644
index 0000000..076ab60
--- /dev/null
+++ b/widgets/misc/e-attachment-paned.h
@@ -0,0 +1,84 @@
+/*
+ * e-attachment-paned.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_PANED_H
+#define E_ATTACHMENT_PANED_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_PANED \
+ (e_attachment_paned_get_type ())
+#define E_ATTACHMENT_PANED(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_PANED, EAttachmentPaned))
+#define E_ATTACHMENT_PANED_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_PANED, EAttachmentPanedClass))
+#define E_IS_ATTACHMENT_PANED(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_PANED))
+#define E_IS_ATTACHMENT_PANED_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_PANED))
+#define E_ATTACHMENT_PANED_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_PANED, EAttachmentPanedClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentPaned EAttachmentPaned;
+typedef struct _EAttachmentPanedClass EAttachmentPanedClass;
+typedef struct _EAttachmentPanedPrivate EAttachmentPanedPrivate;
+
+struct _EAttachmentPaned {
+ GtkVPaned parent;
+ EAttachmentPanedPrivate *priv;
+};
+
+struct _EAttachmentPanedClass {
+ GtkVPanedClass parent_class;
+};
+
+GType e_attachment_paned_get_type (void);
+GtkWidget * e_attachment_paned_new (void);
+GtkWidget * e_attachment_paned_get_content_area
+ (EAttachmentPaned *paned);
+gint e_attachment_paned_get_active_view
+ (EAttachmentPaned *paned);
+void e_attachment_paned_set_active_view
+ (EAttachmentPaned *paned,
+ gint active_view);
+gboolean e_attachment_paned_get_expanded (EAttachmentPaned *paned);
+void e_attachment_paned_set_expanded (EAttachmentPaned *paned,
+ gboolean expanded);
+void e_attachment_paned_drag_data_received
+ (EAttachmentPaned *paned,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_PANED_H */
diff --git a/widgets/misc/e-attachment-store.c b/widgets/misc/e-attachment-store.c
new file mode 100644
index 0000000..0b6e5a2
--- /dev/null
+++ b/widgets/misc/e-attachment-store.c
@@ -0,0 +1,874 @@
+/*
+ * e-attachment-store.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-store.h"
+
+#include <errno.h>
+#include <config.h>
+#include <glib/gi18n.h>
+
+#include "e-util/e-util.h"
+#include "e-util/e-mktemp.h"
+#include "e-util/gconf-bridge.h"
+
+#define E_ATTACHMENT_STORE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_STORE, EAttachmentStorePrivate))
+
+struct _EAttachmentStorePrivate {
+ GHashTable *attachment_index;
+ gchar *current_folder;
+
+ guint ignore_row_changed : 1;
+};
+
+enum {
+ PROP_0,
+ PROP_CURRENT_FOLDER,
+ PROP_NUM_ATTACHMENTS,
+ PROP_NUM_LOADING,
+ PROP_TOTAL_SIZE
+};
+
+static gpointer parent_class;
+
+static void
+attachment_store_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CURRENT_FOLDER:
+ e_attachment_store_set_current_folder (
+ E_ATTACHMENT_STORE (object),
+ g_value_get_string (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_store_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CURRENT_FOLDER:
+ g_value_set_string (
+ value,
+ e_attachment_store_get_current_folder (
+ E_ATTACHMENT_STORE (object)));
+ return;
+
+ case PROP_NUM_ATTACHMENTS:
+ g_value_set_uint (
+ value,
+ e_attachment_store_get_num_attachments (
+ E_ATTACHMENT_STORE (object)));
+ return;
+
+ case PROP_NUM_LOADING:
+ g_value_set_uint (
+ value,
+ e_attachment_store_get_num_loading (
+ E_ATTACHMENT_STORE (object)));
+ return;
+
+ case PROP_TOTAL_SIZE:
+ g_value_set_uint64 (
+ value,
+ e_attachment_store_get_total_size (
+ E_ATTACHMENT_STORE (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_store_dispose (GObject *object)
+{
+ EAttachmentStorePrivate *priv;
+
+ priv = E_ATTACHMENT_STORE_GET_PRIVATE (object);
+
+ g_hash_table_remove_all (priv->attachment_index);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+attachment_store_finalize (GObject *object)
+{
+ EAttachmentStorePrivate *priv;
+
+ priv = E_ATTACHMENT_STORE_GET_PRIVATE (object);
+
+ g_hash_table_destroy (priv->attachment_index);
+
+ g_free (priv->current_folder);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+attachment_store_constructed (GObject *object)
+{
+ GConfBridge *bridge;
+ const gchar *key;
+
+ bridge = gconf_bridge_get ();
+
+ key = "/apps/evolution/shell/current_folder";
+ gconf_bridge_bind_property (bridge, key, object, "current-folder");
+}
+
+static void
+attachment_store_class_init (EAttachmentStoreClass *class)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EAttachmentStorePrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = attachment_store_set_property;
+ object_class->get_property = attachment_store_get_property;
+ object_class->dispose = attachment_store_dispose;
+ object_class->finalize = attachment_store_finalize;
+ object_class->constructed = attachment_store_constructed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_CURRENT_FOLDER,
+ g_param_spec_string (
+ "current-folder",
+ "Current Folder",
+ NULL,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_NUM_ATTACHMENTS,
+ g_param_spec_uint (
+ "num-attachments",
+ "Num Attachments",
+ NULL,
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_NUM_LOADING,
+ g_param_spec_uint (
+ "num-loading",
+ "Num Loading",
+ NULL,
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_TOTAL_SIZE,
+ g_param_spec_uint64 (
+ "total-size",
+ "Total Size",
+ NULL,
+ 0,
+ G_MAXUINT64,
+ 0,
+ G_PARAM_READABLE));
+}
+
+static void
+attachment_store_init (EAttachmentStore *store)
+{
+ GType types[E_ATTACHMENT_STORE_NUM_COLUMNS];
+ GHashTable *attachment_index;
+ gint column = 0;
+
+ attachment_index = g_hash_table_new_full (
+ g_direct_hash, g_direct_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) gtk_tree_row_reference_free);
+
+ store->priv = E_ATTACHMENT_STORE_GET_PRIVATE (store);
+ store->priv->attachment_index = attachment_index;
+
+ types[column++] = E_TYPE_ATTACHMENT; /* COLUMN_ATTACHMENT */
+ types[column++] = G_TYPE_STRING; /* COLUMN_CAPTION */
+ types[column++] = G_TYPE_STRING; /* COLUMN_CONTENT_TYPE */
+ types[column++] = G_TYPE_STRING; /* COLUMN_DESCRIPTION */
+ types[column++] = G_TYPE_ICON; /* COLUMN_ICON */
+ types[column++] = G_TYPE_BOOLEAN; /* COLUMN_LOADING */
+ types[column++] = G_TYPE_INT; /* COLUMN_PERCENT */
+ types[column++] = G_TYPE_BOOLEAN; /* COLUMN_SAVING */
+ types[column++] = G_TYPE_UINT64; /* COLUMN_SIZE */
+
+ g_assert (column == E_ATTACHMENT_STORE_NUM_COLUMNS);
+
+ gtk_list_store_set_column_types (
+ GTK_LIST_STORE (store), G_N_ELEMENTS (types), types);
+}
+
+GType
+e_attachment_store_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EAttachmentStoreClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) attachment_store_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EAttachmentStore),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) attachment_store_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ GTK_TYPE_LIST_STORE, "EAttachmentStore",
+ &type_info, 0);
+ }
+
+ return type;
+}
+
+GtkTreeModel *
+e_attachment_store_new (void)
+{
+ return g_object_new (E_TYPE_ATTACHMENT_STORE, NULL);
+}
+
+void
+e_attachment_store_add_attachment (EAttachmentStore *store,
+ EAttachment *attachment)
+{
+ GtkTreeRowReference *reference;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ GFile *file;
+
+ g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+ gtk_list_store_append (GTK_LIST_STORE (store), &iter);
+
+ gtk_list_store_set (
+ GTK_LIST_STORE (store), &iter,
+ E_ATTACHMENT_STORE_COLUMN_ATTACHMENT, attachment, -1);
+
+ model = GTK_TREE_MODEL (store);
+ path = gtk_tree_model_get_path (model, &iter);
+ reference = gtk_tree_row_reference_new (model, path);
+ gtk_tree_path_free (path);
+
+ g_hash_table_insert (
+ store->priv->attachment_index,
+ g_object_ref (attachment), reference);
+
+ file = e_attachment_get_file (attachment);
+
+ /* This lets the attachment tell us when to update. */
+ e_attachment_set_reference (attachment, reference);
+
+ g_object_freeze_notify (G_OBJECT (store));
+ g_object_notify (G_OBJECT (store), "num-attachments");
+ g_object_notify (G_OBJECT (store), "total-size");
+ g_object_thaw_notify (G_OBJECT (store));
+}
+
+gboolean
+e_attachment_store_remove_attachment (EAttachmentStore *store,
+ EAttachment *attachment)
+{
+ GtkTreeRowReference *reference;
+ GHashTable *hash_table;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), FALSE);
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+
+ hash_table = store->priv->attachment_index;
+ reference = g_hash_table_lookup (hash_table, attachment);
+
+ if (reference == NULL)
+ return FALSE;
+
+ if (!gtk_tree_row_reference_valid (reference)) {
+ g_hash_table_remove (hash_table, attachment);
+ return FALSE;
+ }
+
+ e_attachment_cancel (attachment);
+ e_attachment_set_reference (attachment, NULL);
+
+ 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);
+
+ gtk_list_store_remove (GTK_LIST_STORE (store), &iter);
+ g_hash_table_remove (hash_table, attachment);
+
+ g_object_freeze_notify (G_OBJECT (store));
+ g_object_notify (G_OBJECT (store), "num-attachments");
+ g_object_notify (G_OBJECT (store), "total-size");
+ g_object_thaw_notify (G_OBJECT (store));
+
+ return TRUE;
+}
+
+void
+e_attachment_store_add_to_multipart (EAttachmentStore *store,
+ CamelMultipart *multipart,
+ const gchar *default_charset)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean valid;
+
+ g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+ g_return_if_fail (CAMEL_MULTIPART (multipart));
+
+ model = GTK_TREE_MODEL (store);
+ valid = gtk_tree_model_get_iter_first (model, &iter);
+
+ while (valid) {
+ EAttachment *attachment;
+ gint column_id;
+
+ column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+ gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
+
+ /* Skip the attachment if it's still loading. */
+ if (!e_attachment_get_loading (attachment))
+ e_attachment_add_to_multipart (
+ attachment, multipart, default_charset);
+
+ g_object_unref (attachment);
+
+ valid = gtk_tree_model_iter_next (model, &iter);
+ }
+}
+
+const gchar *
+e_attachment_store_get_current_folder (EAttachmentStore *store)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), NULL);
+
+ return store->priv->current_folder;
+}
+
+void
+e_attachment_store_set_current_folder (EAttachmentStore *store,
+ const gchar *current_folder)
+{
+ g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+
+ if (current_folder == NULL)
+ current_folder = g_get_home_dir ();
+
+ g_free (store->priv->current_folder);
+ store->priv->current_folder = g_strdup (current_folder);
+
+ g_object_notify (G_OBJECT (store), "current-folder");
+}
+
+guint
+e_attachment_store_get_num_attachments (EAttachmentStore *store)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), 0);
+
+ return g_hash_table_size (store->priv->attachment_index);
+}
+
+guint
+e_attachment_store_get_num_loading (EAttachmentStore *store)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ guint num_loading = 0;
+ gboolean valid;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), 0);
+
+ model = GTK_TREE_MODEL (store);
+ valid = gtk_tree_model_get_iter_first (model, &iter);
+
+ while (valid) {
+ EAttachment *attachment;
+ gint column_id;
+
+ column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+ gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
+ if (e_attachment_get_loading (attachment))
+ num_loading++;
+ g_object_unref (attachment);
+
+ valid = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ return num_loading;
+}
+
+goffset
+e_attachment_store_get_total_size (EAttachmentStore *store)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ goffset total_size = 0;
+ gboolean valid;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), 0);
+
+ model = GTK_TREE_MODEL (store);
+ valid = gtk_tree_model_get_iter_first (model, &iter);
+
+ while (valid) {
+ EAttachment *attachment;
+ GFileInfo *file_info;
+ gint column_id;
+
+ column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+ gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
+ file_info = e_attachment_get_file_info (attachment);
+ if (file_info != NULL)
+ total_size += g_file_info_get_size (file_info);
+ g_object_unref (attachment);
+
+ valid = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ return total_size;
+}
+
+gint
+e_attachment_store_run_file_chooser_dialog (EAttachmentStore *store,
+ GtkWidget *dialog)
+{
+ GtkFileChooser *file_chooser;
+ gint response = GTK_RESPONSE_NONE;
+ const gchar *current_folder;
+ gboolean update_folder;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), response);
+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER_DIALOG (dialog), response);
+
+ file_chooser = GTK_FILE_CHOOSER (dialog);
+ current_folder = e_attachment_store_get_current_folder (store);
+ gtk_file_chooser_set_current_folder (file_chooser, current_folder);
+
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ update_folder =
+ (response == GTK_RESPONSE_ACCEPT) ||
+ (response == GTK_RESPONSE_OK) ||
+ (response == GTK_RESPONSE_YES) ||
+ (response == GTK_RESPONSE_APPLY);
+
+ if (update_folder) {
+ gchar *folder;
+
+ folder = gtk_file_chooser_get_current_folder (file_chooser);
+ e_attachment_store_set_current_folder (store, folder);
+ g_free (folder);
+ }
+
+ return response;
+}
+
+void
+e_attachment_store_run_load_dialog (EAttachmentStore *store,
+ GtkWindow *parent)
+{
+ GtkFileChooser *file_chooser;
+ GtkWidget *dialog;
+ GtkWidget *option;
+ GSList *files, *iter;
+ const gchar *disposition;
+ gboolean active;
+ gint response;
+
+ g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+ g_return_if_fail (GTK_IS_WINDOW (parent));
+
+ dialog = gtk_file_chooser_dialog_new (
+ _("Add Attachment"), parent,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ _("A_ttach"), GTK_RESPONSE_OK, NULL);
+
+ file_chooser = GTK_FILE_CHOOSER (dialog);
+ gtk_file_chooser_set_local_only (file_chooser, FALSE);
+ gtk_file_chooser_set_select_multiple (file_chooser, TRUE);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+ gtk_window_set_icon_name (GTK_WINDOW (dialog), "mail-attachment");
+
+ option = gtk_check_button_new_with_mnemonic (
+ _("_Suggest automatic display of attachment"));
+ gtk_file_chooser_set_extra_widget (file_chooser, option);
+ gtk_widget_show (option);
+
+ response = e_attachment_store_run_file_chooser_dialog (store, dialog);
+
+ if (response != GTK_RESPONSE_OK)
+ goto exit;
+
+ files = gtk_file_chooser_get_files (file_chooser);
+ active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (option));
+ disposition = active ? "inline" : "attachment";
+
+ for (iter = files; iter != NULL; iter = g_slist_next (iter)) {
+ EAttachment *attachment;
+ GFile *file = iter->data;
+
+ attachment = e_attachment_new ();
+ e_attachment_set_file (attachment, file);
+ e_attachment_store_add_attachment (store, attachment);
+ e_attachment_load_async (
+ attachment, (GAsyncReadyCallback)
+ e_attachment_load_handle_error, parent);
+ g_object_unref (attachment);
+ }
+
+ g_slist_foreach (files, (GFunc) g_object_unref, NULL);
+ g_slist_free (files);
+
+exit:
+ gtk_widget_destroy (dialog);
+}
+
+GFile *
+e_attachment_store_run_save_dialog (EAttachmentStore *store,
+ GList *attachment_list,
+ GtkWindow *parent)
+{
+ GtkFileChooser *file_chooser;
+ GtkFileChooserAction action;
+ GtkWidget *dialog;
+ GFile *destination;
+ const gchar *title;
+ gint response;
+ guint length;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), NULL);
+
+ length = g_list_length (attachment_list);
+
+ if (length == 0)
+ return NULL;
+
+ 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 (
+ title, parent, action,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL);
+
+ file_chooser = GTK_FILE_CHOOSER (dialog);
+ gtk_file_chooser_set_local_only (file_chooser, FALSE);
+ gtk_file_chooser_set_do_overwrite_confirmation (file_chooser, TRUE);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+ gtk_window_set_icon_name (GTK_WINDOW (dialog), "mail-attachment");
+
+ 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);
+
+ if (response == GTK_RESPONSE_OK)
+ destination = gtk_file_chooser_get_file (file_chooser);
+ else
+ destination = NULL;
+
+ gtk_widget_destroy (dialog);
+
+ return destination;
+}
+
+/******************** e_attachment_store_get_uris_async() ********************/
+
+typedef struct _UriContext UriContext;
+
+struct _UriContext {
+ GSimpleAsyncResult *simple;
+ GList *attachment_list;
+ GError *error;
+ gchar **uris;
+ gint index;
+};
+
+static UriContext *
+attachment_store_uri_context_new (EAttachmentStore *store,
+ GList *attachment_list,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ UriContext *uri_context;
+ GSimpleAsyncResult *simple;
+ guint length;
+ gchar **uris;
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (store), callback, user_data,
+ e_attachment_store_get_uris_async);
+
+ /* Add one for NULL terminator. */
+ length = g_list_length (attachment_list) + 1;
+ uris = g_malloc0 (sizeof (gchar *) * length);
+
+ uri_context = g_slice_new0 (UriContext);
+ uri_context->simple = simple;
+ uri_context->attachment_list = g_list_copy (attachment_list);
+ uri_context->uris = uris;
+
+ g_list_foreach (
+ uri_context->attachment_list,
+ (GFunc) g_object_ref, NULL);
+
+ return uri_context;
+}
+
+static void
+attachment_store_uri_context_free (UriContext *uri_context)
+{
+ /* Do not free the GSimpleAsyncResult. */
+
+ /* The attachment list should be empty now. */
+ g_warn_if_fail (uri_context->attachment_list == NULL);
+
+ /* So should the error. */
+ g_warn_if_fail (uri_context->error == NULL);
+
+ g_strfreev (uri_context->uris);
+
+ g_slice_free (UriContext, uri_context);
+}
+
+static void
+attachment_store_get_uris_save_cb (EAttachment *attachment,
+ GAsyncResult *result,
+ UriContext *uri_context)
+{
+ GSimpleAsyncResult *simple;
+ GFile *file;
+ gchar **uris;
+ gchar *uri;
+ GError *error = NULL;
+
+ file = e_attachment_save_finish (attachment, result, &error);
+
+ /* Remove the attachment from the list. */
+ uri_context->attachment_list = g_list_remove (
+ uri_context->attachment_list, attachment);
+ g_object_unref (attachment);
+
+ if (file != NULL) {
+ uri = g_file_get_uri (file);
+ uri_context->uris[uri_context->index++] = uri;
+ g_object_unref (file);
+
+ } else if (error != NULL) {
+ /* If this is the first error, cancel the other jobs. */
+ if (uri_context->error == NULL) {
+ g_propagate_error (&uri_context->error, error);
+ g_list_foreach (
+ uri_context->attachment_list,
+ (GFunc) e_attachment_cancel, NULL);
+ error = NULL;
+
+ /* Otherwise, we can only report back one error. So if
+ * this is something other than cancellation, dump it to
+ * the terminal. */
+ } else if (!g_error_matches (
+ error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("%s", error->message);
+ }
+
+ if (error != NULL)
+ g_error_free (error);
+
+ /* If there's still jobs running, let them finish. */
+ if (uri_context->attachment_list != NULL)
+ return;
+
+ /* Steal the result. */
+ simple = uri_context->simple;
+ uri_context->simple = NULL;
+
+ /* And the URI list. */
+ uris = uri_context->uris;
+ uri_context->uris = NULL;
+
+ /* And the error. */
+ error = uri_context->error;
+ uri_context->error = NULL;
+
+ if (error == NULL)
+ g_simple_async_result_set_op_res_gpointer (simple, uris, NULL);
+ else {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+
+ g_simple_async_result_complete (simple);
+
+ attachment_store_uri_context_free (uri_context);
+}
+
+void
+e_attachment_store_get_uris_async (EAttachmentStore *store,
+ GList *attachment_list,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GFile *temp_directory;
+ UriContext *uri_context;
+ GList *iter, *trash = NULL;
+ gchar *template;
+ gchar *path;
+
+ g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+ g_return_if_fail (callback != NULL);
+
+ uri_context = attachment_store_uri_context_new (
+ store, attachment_list, callback, user_data);
+
+ /* Grab the copied attachment list. */
+ attachment_list = uri_context->attachment_list;
+
+ /* First scan the list for attachments with a GFile. */
+ for (iter = attachment_list; iter != NULL; iter = iter->next) {
+ EAttachment *attachment = iter->data;
+ GFile *file;
+ gchar *uri;
+
+ file = e_attachment_get_file (attachment);
+ if (file == NULL)
+ continue;
+
+ uri = g_file_get_uri (file);
+ uri_context->uris[uri_context->index++] = uri;
+
+ /* Mark the list node for deletion. */
+ trash = g_list_prepend (trash, iter);
+ g_object_unref (attachment);
+ }
+
+ /* Expunge the list. */
+ for (iter = trash; iter != NULL; iter = iter->next) {
+ GList *link = iter->data;
+ attachment_list = g_list_delete_link (attachment_list, link);
+ }
+ g_list_free (trash);
+
+ uri_context->attachment_list = attachment_list;
+
+ /* Any remaining attachments in the list should have MIME parts
+ * only, so we need to save them all to a temporary directory.
+ * We use a directory so the files can retain their basenames. */
+ template = g_strdup_printf (PACKAGE "-%s-XXXXXX", g_get_user_name ());
+ path = e_mkdtemp (template);
+ g_free (template);
+
+ if (path == NULL) {
+ GSimpleAsyncResult *simple;
+
+ /* Steal the result. */
+ simple = uri_context->simple;
+ uri_context->simple = NULL;
+
+ g_simple_async_result_set_error (
+ simple, G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ "%s", g_strerror (errno));
+
+ g_simple_async_result_complete_in_idle (simple);
+ attachment_store_uri_context_free (uri_context);
+ return;
+ }
+
+ temp_directory = g_file_new_for_path (path);
+
+ for (iter = attachment_list; iter != NULL; iter = iter->next)
+ e_attachment_save_async (
+ E_ATTACHMENT (iter->data),
+ temp_directory, (GAsyncReadyCallback)
+ attachment_store_get_uris_save_cb,
+ uri_context);
+
+ g_object_unref (temp_directory);
+}
+
+gchar **
+e_attachment_store_get_uris_finish (EAttachmentStore *store,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ gchar **uris;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), NULL);
+ g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ uris = g_simple_async_result_get_op_res_gpointer (simple);
+ g_simple_async_result_propagate_error (simple, error);
+ g_object_unref (simple);
+
+ return uris;
+}
diff --git a/widgets/misc/e-attachment-store.h b/widgets/misc/e-attachment-store.h
new file mode 100644
index 0000000..5d2271c
--- /dev/null
+++ b/widgets/misc/e-attachment-store.h
@@ -0,0 +1,122 @@
+/*
+ * e-attachment-store.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_STORE_H
+#define E_ATTACHMENT_STORE_H
+
+#include <gtk/gtk.h>
+#include <widgets/misc/e-attachment.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_STORE \
+ (e_attachment_store_get_type ())
+#define E_ATTACHMENT_STORE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_STORE, EAttachmentStore))
+#define E_ATTACHMENT_STORE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_STORE, EAttachmentStoreClass))
+#define E_IS_ATTACHMENT_STORE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_STORE))
+#define E_IS_ATTACHMENT_STORE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_STORE))
+#define E_ATTACHMENT_STORE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_STORE, EAttachmentStoreClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentStore EAttachmentStore;
+typedef struct _EAttachmentStoreClass EAttachmentStoreClass;
+typedef struct _EAttachmentStorePrivate EAttachmentStorePrivate;
+
+struct _EAttachmentStore {
+ GtkListStore parent;
+ EAttachmentStorePrivate *priv;
+};
+
+struct _EAttachmentStoreClass {
+ GtkListStoreClass parent_class;
+};
+
+enum {
+ E_ATTACHMENT_STORE_COLUMN_ATTACHMENT, /* E_TYPE_ATTACHMENT */
+ E_ATTACHMENT_STORE_COLUMN_CAPTION, /* G_TYPE_STRING */
+ E_ATTACHMENT_STORE_COLUMN_CONTENT_TYPE, /* G_TYPE_STRING */
+ E_ATTACHMENT_STORE_COLUMN_DESCRIPTION, /* G_TYPE_STRING */
+ E_ATTACHMENT_STORE_COLUMN_ICON, /* G_TYPE_ICON */
+ E_ATTACHMENT_STORE_COLUMN_LOADING, /* G_TYPE_BOOLEAN */
+ E_ATTACHMENT_STORE_COLUMN_PERCENT, /* G_TYPE_INT */
+ E_ATTACHMENT_STORE_COLUMN_SAVING, /* G_TYPE_BOOLEAN */
+ E_ATTACHMENT_STORE_COLUMN_SIZE, /* G_TYPE_UINT64 */
+ E_ATTACHMENT_STORE_NUM_COLUMNS
+};
+
+GType e_attachment_store_get_type (void);
+GtkTreeModel * e_attachment_store_new (void);
+void e_attachment_store_add_attachment
+ (EAttachmentStore *store,
+ EAttachment *attachment);
+gboolean e_attachment_store_remove_attachment
+ (EAttachmentStore *store,
+ EAttachment *attachment);
+void e_attachment_store_add_to_multipart
+ (EAttachmentStore *store,
+ CamelMultipart *multipart,
+ const gchar *default_charset);
+const gchar * e_attachment_store_get_current_folder
+ (EAttachmentStore *store);
+void e_attachment_store_set_current_folder
+ (EAttachmentStore *store,
+ const gchar *current_folder);
+guint e_attachment_store_get_num_attachments
+ (EAttachmentStore *store);
+guint e_attachment_store_get_num_loading
+ (EAttachmentStore *store);
+goffset e_attachment_store_get_total_size
+ (EAttachmentStore *store);
+gint e_attachment_store_run_file_chooser_dialog
+ (EAttachmentStore *store,
+ GtkWidget *dialog);
+void e_attachment_store_run_load_dialog
+ (EAttachmentStore *store,
+ GtkWindow *parent);
+GFile * e_attachment_store_run_save_dialog
+ (EAttachmentStore *store,
+ GList *attachment_list,
+ GtkWindow *parent);
+
+/* Asynchronous Operations */
+void e_attachment_store_get_uris_async
+ (EAttachmentStore *store,
+ GList *attachment_list,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gchar ** e_attachment_store_get_uris_finish
+ (EAttachmentStore *store,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_STORE_H */
diff --git a/widgets/misc/e-attachment-tree-view.c b/widgets/misc/e-attachment-tree-view.c
new file mode 100644
index 0000000..f2c17cb
--- /dev/null
+++ b/widgets/misc/e-attachment-tree-view.c
@@ -0,0 +1,605 @@
+/*
+ * e-attachment-tree-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)
+ *
+ */
+
+#include "e-attachment-tree-view.h"
+
+#include <glib/gi18n.h>
+
+#include "e-attachment.h"
+#include "e-attachment-store.h"
+#include "e-attachment-view.h"
+
+#define E_ATTACHMENT_TREE_VIEW_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_TREE_VIEW, EAttachmentTreeViewPrivate))
+
+struct _EAttachmentTreeViewPrivate {
+ EAttachmentViewPrivate view_priv;
+};
+
+enum {
+ PROP_0,
+ PROP_EDITABLE
+};
+
+static gpointer parent_class;
+
+static void
+attachment_tree_view_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_EDITABLE:
+ e_attachment_view_set_editable (
+ E_ATTACHMENT_VIEW (object),
+ g_value_get_boolean (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_tree_view_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_EDITABLE:
+ g_value_set_boolean (
+ value, e_attachment_view_get_editable (
+ E_ATTACHMENT_VIEW (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+attachment_tree_view_dispose (GObject *object)
+{
+ e_attachment_view_dispose (E_ATTACHMENT_VIEW (object));
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+attachment_tree_view_finalize (GObject *object)
+{
+ e_attachment_view_finalize (E_ATTACHMENT_VIEW (object));
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+attachment_tree_view_render_size (GtkTreeViewColumn *column,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ gchar *display_size;
+ gint column_id;
+ guint64 size;
+
+ column_id = E_ATTACHMENT_STORE_COLUMN_SIZE;
+ gtk_tree_model_get (model, iter, column_id, &size, -1);
+
+ display_size = g_format_size_for_display ((goffset) size);
+ g_object_set (renderer, "text", display_size, NULL);
+ g_free (display_size);
+}
+
+static gboolean
+attachment_tree_view_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ 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)->
+ button_press_event (widget, event);
+}
+
+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)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ if (e_attachment_view_key_press_event (view, event))
+ return TRUE;
+
+ /* Chain up to parent's key_press_event() method. */
+ return GTK_WIDGET_CLASS (parent_class)->
+ 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,
+ gint x,
+ gint y,
+ guint time)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (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,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ e_attachment_view_drag_data_received (
+ view, context, x, y, selection, info, time);
+}
+
+static gboolean
+attachment_tree_view_popup_menu (GtkWidget *widget)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ e_attachment_view_show_popup_menu (view, NULL, NULL, NULL);
+
+ return TRUE;
+}
+
+static void
+attachment_tree_view_row_activated (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (tree_view);
+
+ e_attachment_view_open_path (view, path, NULL);
+}
+
+static EAttachmentViewPrivate *
+attachment_tree_view_get_private (EAttachmentView *view)
+{
+ EAttachmentTreeViewPrivate *priv;
+
+ priv = E_ATTACHMENT_TREE_VIEW_GET_PRIVATE (view);
+
+ return &priv->view_priv;
+}
+
+static EAttachmentStore *
+attachment_tree_view_get_store (EAttachmentView *view)
+{
+ GtkTreeView *tree_view;
+ GtkTreeModel *model;
+
+ tree_view = GTK_TREE_VIEW (view);
+ model = gtk_tree_view_get_model (tree_view);
+
+ return E_ATTACHMENT_STORE (model);
+}
+
+static GtkTreePath *
+attachment_tree_view_get_path_at_pos (EAttachmentView *view,
+ gint x,
+ gint y)
+{
+ GtkTreeView *tree_view;
+ GtkTreePath *path;
+ gboolean row_exists;
+
+ tree_view = GTK_TREE_VIEW (view);
+
+ row_exists = gtk_tree_view_get_path_at_pos (
+ tree_view, x, y, &path, NULL, NULL, NULL);
+
+ return row_exists ? path : NULL;
+}
+
+static GList *
+attachment_tree_view_get_selected_paths (EAttachmentView *view)
+{
+ GtkTreeView *tree_view;
+ GtkTreeSelection *selection;
+
+ tree_view = GTK_TREE_VIEW (view);
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ return gtk_tree_selection_get_selected_rows (selection, NULL);
+}
+
+static gboolean
+attachment_tree_view_path_is_selected (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ GtkTreeView *tree_view;
+ GtkTreeSelection *selection;
+
+ tree_view = GTK_TREE_VIEW (view);
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ return gtk_tree_selection_path_is_selected (selection, path);
+}
+
+static void
+attachment_tree_view_select_path (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ GtkTreeView *tree_view;
+ GtkTreeSelection *selection;
+
+ tree_view = GTK_TREE_VIEW (view);
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ gtk_tree_selection_select_path (selection, path);
+}
+
+static void
+attachment_tree_view_unselect_path (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ GtkTreeView *tree_view;
+ GtkTreeSelection *selection;
+
+ tree_view = GTK_TREE_VIEW (view);
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ gtk_tree_selection_unselect_path (selection, path);
+}
+
+static void
+attachment_tree_view_select_all (EAttachmentView *view)
+{
+ GtkTreeView *tree_view;
+ GtkTreeSelection *selection;
+
+ tree_view = GTK_TREE_VIEW (view);
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ gtk_tree_selection_select_all (selection);
+}
+
+static void
+attachment_tree_view_unselect_all (EAttachmentView *view)
+{
+ GtkTreeView *tree_view;
+ GtkTreeSelection *selection;
+
+ tree_view = GTK_TREE_VIEW (view);
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ gtk_tree_selection_unselect_all (selection);
+}
+
+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;
+ GtkWidgetClass *widget_class;
+ GtkTreeViewClass *tree_view_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EAttachmentViewPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = attachment_tree_view_set_property;
+ object_class->get_property = attachment_tree_view_get_property;
+ object_class->dispose = attachment_tree_view_dispose;
+ object_class->finalize = attachment_tree_view_finalize;
+
+ 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;
+
+ tree_view_class = GTK_TREE_VIEW_CLASS (class);
+ tree_view_class->row_activated = attachment_tree_view_row_activated;
+
+ g_object_class_override_property (
+ object_class, PROP_EDITABLE, "editable");
+}
+
+static void
+attachment_tree_view_iface_init (EAttachmentViewIface *iface)
+{
+ iface->get_private = attachment_tree_view_get_private;
+ iface->get_store = attachment_tree_view_get_store;
+
+ iface->get_path_at_pos = attachment_tree_view_get_path_at_pos;
+ iface->get_selected_paths = attachment_tree_view_get_selected_paths;
+ iface->path_is_selected = attachment_tree_view_path_is_selected;
+ iface->select_path = attachment_tree_view_select_path;
+ 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
+attachment_tree_view_init (EAttachmentTreeView *tree_view)
+{
+ GtkTreeSelection *selection;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+
+ tree_view->priv = E_ATTACHMENT_TREE_VIEW_GET_PRIVATE (tree_view);
+
+ e_attachment_view_init (E_ATTACHMENT_VIEW (tree_view));
+
+ gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree_view), TRUE);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (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);
+ gtk_tree_view_column_set_title (column, _("Description"));
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+
+ g_object_set (renderer, "stock-size", GTK_ICON_SIZE_MENU, NULL);
+
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "gicon",
+ E_ATTACHMENT_STORE_COLUMN_ICON);
+
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "text",
+ E_ATTACHMENT_STORE_COLUMN_DESCRIPTION);
+
+ renderer = gtk_cell_renderer_progress_new ();
+ g_object_set (renderer, "text", _("Loading"), NULL);
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "value",
+ E_ATTACHMENT_STORE_COLUMN_PERCENT);
+
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "visible",
+ E_ATTACHMENT_STORE_COLUMN_LOADING);
+
+ renderer = gtk_cell_renderer_progress_new ();
+ g_object_set (renderer, "text", _("Saving"), NULL);
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "value",
+ E_ATTACHMENT_STORE_COLUMN_PERCENT);
+
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "visible",
+ 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);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+
+ gtk_tree_view_column_set_cell_data_func (
+ column, renderer, (GtkTreeCellDataFunc)
+ 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);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "text",
+ E_ATTACHMENT_STORE_COLUMN_CONTENT_TYPE);
+}
+
+GType
+e_attachment_tree_view_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EAttachmentTreeViewClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) attachment_tree_view_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EAttachmentTreeView),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) attachment_tree_view_init,
+ NULL /* value_table */
+ };
+
+ static const GInterfaceInfo iface_info = {
+ (GInterfaceInitFunc) attachment_tree_view_iface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL /* interface_data */
+ };
+
+ type = g_type_register_static (
+ GTK_TYPE_TREE_VIEW, "EAttachmentTreeView",
+ &type_info, 0);
+
+ g_type_add_interface_static (
+ type, E_TYPE_ATTACHMENT_VIEW, &iface_info);
+ }
+
+ return type;
+}
+
+GtkWidget *
+e_attachment_tree_view_new (void)
+{
+ return g_object_new (E_TYPE_ATTACHMENT_TREE_VIEW, NULL);
+}
diff --git a/widgets/misc/e-attachment-tree-view.h b/widgets/misc/e-attachment-tree-view.h
new file mode 100644
index 0000000..7f16ba5
--- /dev/null
+++ b/widgets/misc/e-attachment-tree-view.h
@@ -0,0 +1,66 @@
+/*
+ * e-attachment-tree-view.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_TREE_VIEW_H
+#define E_ATTACHMENT_TREE_VIEW_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_TREE_VIEW \
+ (e_attachment_tree_view_get_type ())
+#define E_ATTACHMENT_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_TREE_VIEW, EAttachmentTreeView))
+#define E_ATTACHMENT_TREE_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_TREE_VIEW, EAttachmentTreeViewClass))
+#define E_IS_ATTACHMENT_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_TREE_VIEW))
+#define E_IS_ATTACHMENT_TREE_VIEW_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_TREE_VIEW))
+#define E_ATTACHMENT_TREE_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ATTACHMENT_TREE_VIEW, EAttachmentTreeViewClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentTreeView EAttachmentTreeView;
+typedef struct _EAttachmentTreeViewClass EAttachmentTreeViewClass;
+typedef struct _EAttachmentTreeViewPrivate EAttachmentTreeViewPrivate;
+
+struct _EAttachmentTreeView {
+ GtkTreeView parent;
+ EAttachmentTreeViewPrivate *priv;
+};
+
+struct _EAttachmentTreeViewClass {
+ GtkTreeViewClass parent_class;
+};
+
+GType e_attachment_tree_view_get_type (void);
+GtkWidget * e_attachment_tree_view_new (void);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_TREE_VIEW_H */
diff --git a/widgets/misc/e-attachment-view.c b/widgets/misc/e-attachment-view.c
new file mode 100644
index 0000000..53f719c
--- /dev/null
+++ b/widgets/misc/e-attachment-view.c
@@ -0,0 +1,1637 @@
+/*
+ * 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)
+ *
+ */
+
+#include "e-attachment-view.h"
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include <gdk/gdkkeysyms.h>
+#include <camel/camel-stream-mem.h>
+
+#include "e-util/e-binding.h"
+#include "e-util/e-util.h"
+#include "e-attachment-dialog.h"
+#include "e-attachment-handler-image.h"
+
+enum {
+ UPDATE_ACTIONS,
+ LAST_SIGNAL
+};
+
+/* Note: Do not use the info field. */
+static GtkTargetEntry target_table[] = {
+ { "_NETSCAPE_URL", 0, 0 },
+ { "text/x-vcard", 0, 0 },
+ { "text/calendar", 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='hide'/>"
+" </placeholder>"
+" <separator/>"
+" <placeholder name='custom-actions'/>"
+" <separator/>"
+" <menuitem action='add'/>"
+" <separator/>"
+" <placeholder name='open-actions'/>"
+" </popup>"
+"</ui>";
+
+static gulong signals[LAST_SIGNAL];
+
+static void
+action_add_cb (GtkAction *action,
+ EAttachmentView *view)
+{
+ EAttachmentStore *store;
+ gpointer parent;
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = GTK_WIDGET_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 *selected;
+
+ selected = e_attachment_view_get_selected_attachments (view);
+ g_return_if_fail (g_list_length (selected) == 1);
+ attachment = selected->data;
+
+ e_attachment_cancel (attachment);
+
+ g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+}
+
+static void
+action_hide_cb (GtkAction *action,
+ EAttachmentView *view)
+{
+ EAttachment *attachment;
+ GList *selected;
+
+ selected = e_attachment_view_get_selected_attachments (view);
+ g_return_if_fail (g_list_length (selected) == 1);
+ attachment = selected->data;
+
+ e_attachment_set_shown (attachment, FALSE);
+
+ g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+}
+
+static void
+action_open_in_cb (GtkAction *action,
+ EAttachmentView *view)
+{
+ GAppInfo *app_info;
+ GtkTreePath *path;
+ GList *selected;
+
+ selected = e_attachment_view_get_selected_paths (view);
+ g_return_if_fail (g_list_length (selected) == 1);
+ path = selected->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 (selected, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (selected);
+}
+
+static void
+action_properties_cb (GtkAction *action,
+ EAttachmentView *view)
+{
+ EAttachment *attachment;
+ GtkWidget *dialog;
+ GList *selected;
+ gpointer parent;
+
+ 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;
+
+ dialog = e_attachment_dialog_new (parent, attachment);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+}
+
+static void
+action_recent_cb (GtkAction *action,
+ EAttachmentView *view)
+{
+ GtkRecentChooser *chooser;
+ EAttachmentStore *store;
+ EAttachment *attachment;
+ gpointer parent;
+ gchar *uri;
+
+ chooser = GTK_RECENT_CHOOSER (action);
+ store = e_attachment_view_get_store (view);
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+ uri = gtk_recent_chooser_get_current_uri (chooser);
+ attachment = e_attachment_new_for_uri (uri);
+ e_attachment_store_add_attachment (store, attachment);
+ e_attachment_load_async (
+ attachment, (GAsyncReadyCallback)
+ e_attachment_load_handle_error, parent);
+ g_free (uri);
+}
+
+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 *selected, *iter;
+ GFile *destination;
+ gpointer parent;
+
+ store = e_attachment_view_get_store (view);
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+ /* XXX We lose the previous selection. */
+ e_attachment_view_select_all (view);
+ selected = e_attachment_view_get_selected_attachments (view);
+ e_attachment_view_unselect_all (view);
+
+ destination = e_attachment_store_run_save_dialog (
+ store, selected, parent);
+
+ if (destination == NULL)
+ goto exit;
+
+ for (iter = selected; 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 (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+}
+
+static void
+action_save_as_cb (GtkAction *action,
+ EAttachmentView *view)
+{
+ EAttachmentStore *store;
+ GList *selected, *iter;
+ GFile *destination;
+ gpointer parent;
+
+ store = e_attachment_view_get_store (view);
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+ selected = e_attachment_view_get_selected_attachments (view);
+
+ destination = e_attachment_store_run_save_dialog (
+ store, selected, parent);
+
+ if (destination == NULL)
+ goto exit;
+
+ for (iter = selected; 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 (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+}
+
+static void
+action_show_cb (GtkAction *action,
+ EAttachmentView *view)
+{
+ EAttachment *attachment;
+ GList *selected;
+
+ selected = e_attachment_view_get_selected_attachments (view);
+ g_return_if_fail (g_list_length (selected) == 1);
+ attachment = selected->data;
+
+ e_attachment_set_shown (attachment, TRUE);
+
+ g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+}
+
+static GtkActionEntry standard_entries[] = {
+
+ { "cancel",
+ GTK_STOCK_CANCEL,
+ NULL,
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_cancel_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) },
+
+ { "show",
+ NULL,
+ N_("_View Inline"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_show_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_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)
+{
+ static GdkAtom atom = GDK_NONE;
+ EAttachmentStore *store;
+ EAttachment *attachment;
+ CamelMimePart *mime_part;
+ GdkAtom data_type;
+ const gchar *data;
+ gpointer parent;
+ gchar *content_type;
+ gint length;
+
+ if (G_UNLIKELY (atom = GDK_NONE))
+ atom = gdk_atom_intern_static_string ("text/calendar");
+
+ 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);
+ 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_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);
+
+ camel_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)
+{
+ static GdkAtom atom = GDK_NONE;
+ EAttachmentStore *store;
+ EAttachment *attachment;
+ CamelMimePart *mime_part;
+ GdkAtom data_type;
+ const gchar *data;
+ gpointer parent;
+ gchar *content_type;
+ gint length;
+
+ if (G_UNLIKELY (atom = GDK_NONE))
+ atom = gdk_atom_intern_static_string ("text/x-vcard");
+
+ 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);
+ 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_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);
+
+ camel_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_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;
+ GtkActionGroup *action_group;
+ GtkAction *action;
+ GList *list, *iter;
+ guint n_selected;
+ gboolean busy = FALSE;
+ gboolean can_show = FALSE;
+ gboolean shown = FALSE;
+
+ g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+ priv = e_attachment_view_get_private (view);
+ 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);
+
+ 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);
+
+ /* 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);
+
+ 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;
+
+ if (!g_app_info_should_show (app_info))
+ continue;
+
+ 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-in-%s", app_executable);
+ action_label = g_strdup_printf (_("Open in %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);
+
+#if GTK_CHECK_VERSION(2,16,0)
+ gtk_action_set_gicon (action, app_icon);
+#endif
+
+ 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_in_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_handlers (EAttachmentView *view)
+{
+ EAttachmentViewPrivate *priv;
+ GtkTargetList *target_list;
+ GType *children;
+ guint ii;
+
+ 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);
+
+ priv->handlers = g_ptr_array_new ();
+ priv->target_list = target_list;
+ priv->drag_actions = GDK_ACTION_COPY;
+
+ children = g_type_children (E_TYPE_ATTACHMENT_HANDLER, NULL);
+
+ for (ii = 0; children[ii] != G_TYPE_INVALID; ii++) {
+ EAttachmentHandler *handler;
+ const GtkTargetEntry *targets;
+ guint n_targets;
+
+ handler = g_object_new (children[ii], "view", view, NULL);
+
+ targets = e_attachment_handler_get_target_table (
+ handler, &n_targets);
+ gtk_target_list_add_table (target_list, targets, n_targets);
+ priv->drag_actions |=
+ e_attachment_handler_get_drag_actions (handler);
+
+ g_ptr_array_add (priv->handlers, handler);
+ }
+
+ g_free (children);
+}
+
+static void
+attachment_view_class_init (EAttachmentViewIface *iface)
+{
+ iface->update_actions = attachment_view_update_actions;
+
+ g_object_interface_install_property (
+ iface,
+ 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 (iface),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EAttachmentViewIface, update_actions),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+GType
+e_attachment_view_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EAttachmentViewIface),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) attachment_view_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ 0, /* instance_size */
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) NULL,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ G_TYPE_INTERFACE, "EAttachmentView", &type_info, 0);
+
+ g_type_interface_add_prerequisite (type, GTK_TYPE_WIDGET);
+
+ /* Register known handler types. */
+ e_attachment_handler_image_get_type ();
+ }
+
+ return 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 = gtk_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");
+
+ e_mutual_binding_new (
+ G_OBJECT (view), "editable",
+ G_OBJECT (action_group), "visible");
+ 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_handlers (view);
+
+ e_attachment_view_drag_source_set (view);
+ e_attachment_view_drag_dest_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);
+
+ g_ptr_array_foreach (priv->handlers, (GFunc) g_object_unref, NULL);
+ g_ptr_array_set_size (priv->handlers, 0);
+
+ 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_ptr_array_free (priv->handlers, TRUE);
+}
+
+EAttachmentViewPrivate *
+e_attachment_view_get_private (EAttachmentView *view)
+{
+ EAttachmentViewIface *iface;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+
+ iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+ g_return_val_if_fail (iface->get_private != NULL, NULL);
+
+ return iface->get_private (view);
+}
+
+EAttachmentStore *
+e_attachment_view_get_store (EAttachmentView *view)
+{
+ EAttachmentViewIface *iface;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+
+ iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+ g_return_val_if_fail (iface->get_store != NULL, NULL);
+
+ return iface->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;
+
+ g_object_notify (G_OBJECT (view), "editable");
+}
+
+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;
+}
+
+GList *
+e_attachment_view_get_selected_attachments (EAttachmentView *view)
+{
+ EAttachmentStore *store;
+ GtkTreeModel *model;
+ GList *selected, *item;
+ gint column_id;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+
+ column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+ selected = 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 = selected; 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 selected;
+}
+
+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_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 *selected, *item;
+ gint column_id;
+
+ g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+ column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+ selected = e_attachment_view_get_selected_paths (view);
+ store = e_attachment_view_get_store (view);
+ model = GTK_TREE_MODEL (store);
+
+ for (item = selected; 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 (selected) == 1) {
+ GtkTreePath *path = selected->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 (selected, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (selected);
+}
+
+gboolean
+e_attachment_view_button_press_event (EAttachmentView *view,
+ GdkEventButton *event)
+{
+ GtkTreePath *path;
+ gboolean editable;
+ gboolean item_clicked;
+
+ 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 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);
+ item_clicked = TRUE;
+ } else {
+ e_attachment_view_unselect_all (view);
+ item_clicked = FALSE;
+ }
+
+ /* 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) {
+ /* 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 (item_clicked || editable) {
+ e_attachment_view_show_popup_menu (
+ view, event, NULL, NULL);
+ 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;
+}
+
+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_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)
+{
+ EAttachmentViewIface *iface;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+
+ iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+ g_return_val_if_fail (iface->get_path_at_pos != NULL, NULL);
+
+ return iface->get_path_at_pos (view, x, y);
+}
+
+GList *
+e_attachment_view_get_selected_paths (EAttachmentView *view)
+{
+ EAttachmentViewIface *iface;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+
+ iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+ g_return_val_if_fail (iface->get_selected_paths != NULL, NULL);
+
+ return iface->get_selected_paths (view);
+}
+
+gboolean
+e_attachment_view_path_is_selected (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ EAttachmentViewIface *iface;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+ g_return_val_if_fail (iface->path_is_selected != NULL, FALSE);
+
+ return iface->path_is_selected (view, path);
+}
+
+void
+e_attachment_view_select_path (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ EAttachmentViewIface *iface;
+
+ g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+ g_return_if_fail (path != NULL);
+
+ iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+ g_return_if_fail (iface->select_path != NULL);
+
+ iface->select_path (view, path);
+}
+
+void
+e_attachment_view_unselect_path (EAttachmentView *view,
+ GtkTreePath *path)
+{
+ EAttachmentViewIface *iface;
+
+ g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+ g_return_if_fail (path != NULL);
+
+ iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+ g_return_if_fail (iface->unselect_path != NULL);
+
+ iface->unselect_path (view, path);
+}
+
+void
+e_attachment_view_select_all (EAttachmentView *view)
+{
+ EAttachmentViewIface *iface;
+
+ g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+ iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+ g_return_if_fail (iface->select_all != NULL);
+
+ iface->select_all (view);
+}
+
+void
+e_attachment_view_unselect_all (EAttachmentView *view)
+{
+ EAttachmentViewIface *iface;
+
+ g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+ iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+ g_return_if_fail (iface->unselect_all != NULL);
+
+ iface->unselect_all (view);
+}
+
+void
+e_attachment_view_sync_selection (EAttachmentView *view,
+ EAttachmentView *target)
+{
+ GList *selected, *iter;
+
+ g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+ g_return_if_fail (E_IS_ATTACHMENT_VIEW (target));
+
+ selected = e_attachment_view_get_selected_paths (view);
+ e_attachment_view_unselect_all (target);
+
+ for (iter = selected; iter != NULL; iter = iter->next)
+ e_attachment_view_select_path (target, iter->data);
+
+ g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (selected);
+}
+
+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));
+}
+
+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)
+{
+ EAttachmentStore *store;
+ GList *selected;
+
+ 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;
+
+ store = e_attachment_view_get_store (view);
+
+ selected = e_attachment_view_get_selected_attachments (view);
+ if (selected == NULL)
+ return;
+
+ e_attachment_store_get_uris_async (
+ store, selected, (GAsyncReadyCallback)
+ attachment_view_got_uris_cb, &status);
+
+ g_list_foreach (selected, (GFunc) g_object_unref, NULL);
+ g_list_free (selected);
+
+ /* 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;
+ GtkTargetEntry *targets;
+ gint n_targets;
+
+ g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+ priv = e_attachment_view_get_private (view);
+
+ targets = gtk_target_table_new_from_list (
+ priv->target_list, &n_targets);
+
+ gtk_drag_dest_set (
+ GTK_WIDGET (view), GTK_DEST_DEFAULT_ALL,
+ targets, n_targets, priv->drag_actions);
+
+ gtk_target_table_free (targets, n_targets);
+}
+
+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));
+}
+
+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;
+
+ actions = priv->drag_actions & context->actions;
+ chosen_action = context->suggested_action;
+
+ 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'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,
+ 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;
+}
+
+GtkAction *
+e_attachment_view_recent_action_new (EAttachmentView *view,
+ const gchar *action_name,
+ const gchar *action_label)
+{
+ GtkAction *action;
+ GtkRecentChooser *chooser;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
+ g_return_val_if_fail (action_name != NULL, NULL);
+
+ action = gtk_recent_action_new (
+ action_name, action_label, NULL, NULL);
+ gtk_recent_action_set_show_numbers (GTK_RECENT_ACTION (action), TRUE);
+
+ chooser = GTK_RECENT_CHOOSER (action);
+ gtk_recent_chooser_set_show_icons (chooser, TRUE);
+ gtk_recent_chooser_set_show_not_found (chooser, FALSE);
+ gtk_recent_chooser_set_show_private (chooser, FALSE);
+ gtk_recent_chooser_set_show_tips (chooser, TRUE);
+ gtk_recent_chooser_set_sort_type (chooser, GTK_RECENT_SORT_MRU);
+
+ g_signal_connect (
+ action, "item-activated",
+ G_CALLBACK (action_recent_cb), view);
+
+ return action;
+}
+
+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);
+}
diff --git a/widgets/misc/e-attachment-view.h b/widgets/misc/e-attachment-view.h
new file mode 100644
index 0000000..0fc5118
--- /dev/null
+++ b/widgets/misc/e-attachment-view.h
@@ -0,0 +1,227 @@
+/*
+ * e-attachment-view.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_VIEW_H
+#define E_ATTACHMENT_VIEW_H
+
+#include <gtk/gtk.h>
+#include <widgets/misc/e-attachment-store.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ATTACHMENT_VIEW \
+ (e_attachment_view_get_type ())
+#define E_ATTACHMENT_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ATTACHMENT_VIEW, EAttachmentView))
+#define E_ATTACHMENT_VIEW_IFACE(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ATTACHMENT_VIEW, EAttachmentViewIface))
+#define E_IS_ATTACHMENT_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ATTACHMENT_VIEW))
+#define E_IS_ATTACHMENT_VIEW_IFACE(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ATTACHMENT_VIEW))
+#define E_ATTACHMENT_VIEW_GET_IFACE(obj) \
+ (G_TYPE_INSTANCE_GET_INTERFACE \
+ ((obj), E_TYPE_ATTACHMENT_VIEW, EAttachmentViewIface))
+
+G_BEGIN_DECLS
+
+typedef struct _EAttachmentView EAttachmentView;
+typedef struct _EAttachmentViewIface EAttachmentViewIface;
+typedef struct _EAttachmentViewPrivate EAttachmentViewPrivate;
+
+struct _EAttachmentViewIface {
+ GTypeInterface parent_iface;
+
+ /* General Methods */
+ EAttachmentViewPrivate *
+ (*get_private) (EAttachmentView *view);
+ EAttachmentStore *
+ (*get_store) (EAttachmentView *view);
+
+ /* Selection Methods */
+ GtkTreePath * (*get_path_at_pos) (EAttachmentView *view,
+ gint x,
+ gint y);
+ GList * (*get_selected_paths) (EAttachmentView *view);
+ gboolean (*path_is_selected) (EAttachmentView *view,
+ GtkTreePath *path);
+ void (*select_path) (EAttachmentView *view,
+ GtkTreePath *path);
+ void (*unselect_path) (EAttachmentView *view,
+ 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);
+
+ /* Signals */
+ void (*update_actions) (EAttachmentView *view);
+};
+
+struct _EAttachmentViewPrivate {
+
+ /* Attachment Handlers */
+ GPtrArray *handlers;
+
+ /* Drag Destination */
+ GtkTargetList *target_list;
+ GdkDragAction drag_actions;
+
+ /* Popup Menu Management */
+ GtkUIManager *ui_manager;
+ guint merge_id;
+
+ guint editable : 1;
+};
+
+GType e_attachment_view_get_type (void);
+
+void e_attachment_view_init (EAttachmentView *view);
+void e_attachment_view_dispose (EAttachmentView *view);
+void e_attachment_view_finalize (EAttachmentView *view);
+
+EAttachmentViewPrivate *
+ e_attachment_view_get_private (EAttachmentView *view);
+EAttachmentStore *
+ e_attachment_view_get_store (EAttachmentView *view);
+gboolean e_attachment_view_get_editable (EAttachmentView *view);
+void e_attachment_view_set_editable (EAttachmentView *view,
+ gboolean editable);
+GtkTargetList * e_attachment_view_get_target_list
+ (EAttachmentView *view);
+GdkDragAction e_attachment_view_get_drag_actions
+ (EAttachmentView *view);
+GList * e_attachment_view_get_selected_attachments
+ (EAttachmentView *view);
+void e_attachment_view_open_path (EAttachmentView *view,
+ GtkTreePath *path,
+ GAppInfo *app_info);
+void e_attachment_view_remove_selected
+ (EAttachmentView *view,
+ gboolean select_next);
+
+/* Event Support */
+gboolean e_attachment_view_button_press_event
+ (EAttachmentView *view,
+ GdkEventButton *event);
+gboolean e_attachment_view_button_release_event
+ (EAttachmentView *view,
+ GdkEventButton *event);
+gboolean e_attachment_view_key_press_event
+ (EAttachmentView *view,
+ GdkEventKey *event);
+
+/* Selection Management */
+GtkTreePath * e_attachment_view_get_path_at_pos
+ (EAttachmentView *view,
+ gint x,
+ gint y);
+GList * e_attachment_view_get_selected_paths
+ (EAttachmentView *view);
+gboolean e_attachment_view_path_is_selected
+ (EAttachmentView *view,
+ GtkTreePath *path);
+void e_attachment_view_select_path (EAttachmentView *view,
+ GtkTreePath *path);
+void e_attachment_view_unselect_path (EAttachmentView *view,
+ GtkTreePath *path);
+void e_attachment_view_select_all (EAttachmentView *view);
+void e_attachment_view_unselect_all (EAttachmentView *view);
+void e_attachment_view_sync_selection(EAttachmentView *view,
+ EAttachmentView *target);
+
+/* 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);
+gboolean e_attachment_view_drag_motion (EAttachmentView *view,
+ GdkDragContext *context,
+ 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,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time);
+
+/* Popup Menu Management */
+GtkAction * e_attachment_view_get_action (EAttachmentView *view,
+ const gchar *action_name);
+GtkActionGroup *e_attachment_view_add_action_group
+ (EAttachmentView *view,
+ const gchar *group_name);
+GtkActionGroup *e_attachment_view_get_action_group
+ (EAttachmentView *view,
+ const gchar *group_name);
+GtkWidget * e_attachment_view_get_popup_menu(EAttachmentView *view);
+GtkUIManager * e_attachment_view_get_ui_manager(EAttachmentView *view);
+GtkAction * e_attachment_view_recent_action_new
+ (EAttachmentView *view,
+ const gchar *action_name,
+ const gchar *action_label);
+void e_attachment_view_show_popup_menu
+ (EAttachmentView *view,
+ GdkEventButton *event,
+ GtkMenuPositionFunc func,
+ gpointer user_data);
+void e_attachment_view_update_actions(EAttachmentView *view);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_VIEW_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]