aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/misc
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@src.gnome.org>2009-03-21 03:06:59 +0800
committerMatthew Barnes <mbarnes@src.gnome.org>2009-03-21 03:06:59 +0800
commit4cec9fc7169dc3b810321555a70cda916720867d (patch)
tree8fe739ab0d249fb35bedc572bd34b3717512283b /widgets/misc
parent7a92d9cc82b7775a0f5cb1fde233119d435a79b6 (diff)
downloadgsoc2013-evolution-4cec9fc7169dc3b810321555a70cda916720867d.tar.gz
gsoc2013-evolution-4cec9fc7169dc3b810321555a70cda916720867d.tar.zst
gsoc2013-evolution-4cec9fc7169dc3b810321555a70cda916720867d.zip
Saving progress on a massive attachment handling rewrite.
svn path=/branches/kill-bonobo/; revision=37465
Diffstat (limited to 'widgets/misc')
-rw-r--r--widgets/misc/Makefile.am12
-rw-r--r--widgets/misc/e-activity.c112
-rw-r--r--widgets/misc/e-activity.h8
-rw-r--r--widgets/misc/e-attachment-bar.c445
-rw-r--r--widgets/misc/e-attachment-dialog.c112
-rw-r--r--widgets/misc/e-attachment-icon-view.c319
-rw-r--r--widgets/misc/e-attachment-icon-view.h66
-rw-r--r--widgets/misc/e-attachment-paned.c563
-rw-r--r--widgets/misc/e-attachment-paned.h79
-rw-r--r--widgets/misc/e-attachment-store.c1073
-rw-r--r--widgets/misc/e-attachment-store.h110
-rw-r--r--widgets/misc/e-attachment-tree-view.c396
-rw-r--r--widgets/misc/e-attachment-tree-view.h66
-rw-r--r--widgets/misc/e-attachment-view.c1041
-rw-r--r--widgets/misc/e-attachment-view.h163
-rw-r--r--widgets/misc/e-attachment.c1584
-rw-r--r--widgets/misc/e-attachment.h98
-rw-r--r--widgets/misc/e-file-activity.c133
-rw-r--r--widgets/misc/e-file-activity.h10
-rw-r--r--widgets/misc/e-timeout-activity.c18
-rw-r--r--widgets/misc/e-timeout-activity.h2
21 files changed, 5213 insertions, 1197 deletions
diff --git a/widgets/misc/Makefile.am b/widgets/misc/Makefile.am
index 8e79779d6d..f6fc7ed10a 100644
--- a/widgets/misc/Makefile.am
+++ b/widgets/misc/Makefile.am
@@ -40,8 +40,12 @@ widgetsinclude_HEADERS = \
e-activity-proxy.h \
e-alert-activity.h \
e-attachment.h \
- e-attachment-bar.h \
e-attachment-dialog.h \
+ e-attachment-icon-view.h \
+ e-attachment-paned.h \
+ e-attachment-store.h \
+ e-attachment-tree-view.h \
+ e-attachment-view.h \
e-calendar.h \
e-calendar-item.h \
e-canvas.h \
@@ -98,8 +102,12 @@ libemiscwidgets_la_SOURCES = \
e-activity-proxy.c \
e-alert-activity.c \
e-attachment.c \
- e-attachment-bar.c \
e-attachment-dialog.c \
+ e-attachment-icon-view.c \
+ e-attachment-paned.c \
+ e-attachment-store.c \
+ e-attachment-tree-view.c \
+ e-attachment-view.c \
e-calendar.c \
e-calendar-item.c \
e-canvas.c \
diff --git a/widgets/misc/e-activity.c b/widgets/misc/e-activity.c
index 352300f023..c65c3aaa0a 100644
--- a/widgets/misc/e-activity.c
+++ b/widgets/misc/e-activity.c
@@ -21,6 +21,7 @@
#include "e-activity.h"
+#include <stdarg.h>
#include <glib/gi18n.h>
#define E_ACTIVITY_GET_PRIVATE(obj) \
@@ -32,6 +33,8 @@ struct _EActivityPrivate {
gchar *primary_text;
gchar *secondary_text;
gdouble percent;
+ guint idle_id;
+ GError *error;
guint allow_cancel : 1;
guint blocking : 1;
@@ -61,6 +64,24 @@ enum {
static gpointer parent_class;
static gulong signals[LAST_SIGNAL];
+static gboolean
+activity_idle_cancel_cb (EActivity *activity)
+{
+ activity->priv->idle_id = 0;
+ e_activity_cancel (activity);
+
+ return FALSE;
+}
+
+static gboolean
+activity_idle_complete_cb (EActivity *activity)
+{
+ activity->priv->idle_id = 0;
+ e_activity_complete (activity);
+
+ return FALSE;
+}
+
static void
activity_set_property (GObject *object,
guint property_id,
@@ -178,6 +199,12 @@ activity_finalize (GObject *object)
g_free (priv->primary_text);
g_free (priv->secondary_text);
+ if (priv->idle_id > 0)
+ g_source_remove (priv->idle_id);
+
+ if (priv->error != NULL)
+ g_error_free (priv->error);
+
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -222,12 +249,22 @@ static void
activity_cancelled (EActivity *activity)
{
activity->priv->cancelled = TRUE;
+
+ if (activity->priv->idle_id > 0) {
+ g_source_remove (activity->priv->idle_id);
+ activity->priv->idle_id = 0;
+ }
}
static void
activity_completed (EActivity *activity)
{
activity->priv->completed = TRUE;
+
+ if (activity->priv->idle_id > 0) {
+ g_source_remove (activity->priv->idle_id);
+ activity->priv->idle_id = 0;
+ }
}
static void
@@ -401,11 +438,29 @@ e_activity_new (const gchar *primary_text)
"primary-text", primary_text, NULL);
}
+EActivity *
+e_activity_newv (const gchar *format, ...)
+{
+ EActivity *activity;
+ gchar *primary_text;
+ va_list args;
+
+ va_start (args, format);
+ primary_text = g_strdup_vprintf (format, args);
+ activity = e_activity_new (primary_text);
+ g_free (primary_text);
+ va_end (args);
+
+ return activity;
+}
+
void
e_activity_cancel (EActivity *activity)
{
g_return_if_fail (E_IS_ACTIVITY (activity));
- g_return_if_fail (activity->priv->allow_cancel);
+
+ if (!activity->priv->allow_cancel);
+ return;
if (activity->priv->cancelled)
return;
@@ -417,6 +472,18 @@ e_activity_cancel (EActivity *activity)
}
void
+e_activity_cancel_in_idle (EActivity *activity)
+{
+ g_return_if_fail (E_IS_ACTIVITY (activity));
+
+ if (activity->priv->idle_id > 0)
+ g_source_remove (activity->priv->idle_id);
+
+ activity->priv->idle_id = g_idle_add (
+ (GSourceFunc) activity_idle_cancel_cb, activity);
+}
+
+void
e_activity_complete (EActivity *activity)
{
g_return_if_fail (E_IS_ACTIVITY (activity));
@@ -431,6 +498,18 @@ e_activity_complete (EActivity *activity)
}
void
+e_activity_complete_in_idle (EActivity *activity)
+{
+ g_return_if_fail (E_IS_ACTIVITY (activity));
+
+ if (activity->priv->idle_id > 0)
+ g_source_remove (activity->priv->idle_id);
+
+ activity->priv->idle_id = g_idle_add (
+ (GSourceFunc) activity_idle_complete_cb, activity);
+}
+
+void
e_activity_clicked (EActivity *activity)
{
g_return_if_fail (E_IS_ACTIVITY (activity));
@@ -602,3 +681,34 @@ e_activity_set_secondary_text (EActivity *activity,
g_object_notify (G_OBJECT (activity), "secondary-text");
}
+
+void
+e_activity_set_error (EActivity *activity,
+ const GError *error)
+{
+ g_return_if_fail (E_IS_ACTIVITY (activity));
+
+ if (activity->priv->error != NULL) {
+ g_error_free (activity->priv->error);
+ activity->priv->error = NULL;
+ }
+
+ if (error != NULL)
+ activity->priv->error = g_error_copy (error);
+}
+
+gboolean
+e_activity_propagate_error (EActivity *activity,
+ GError **destination)
+{
+ gboolean propagated;
+
+ g_return_val_if_fail (E_IS_ACTIVITY (activity), FALSE);
+
+ if ((propagated = (activity->priv->error != NULL))) {
+ g_propagate_error (destination, activity->priv->error);
+ activity->priv->error = NULL;
+ }
+
+ return propagated;
+}
diff --git a/widgets/misc/e-activity.h b/widgets/misc/e-activity.h
index 9a14c073a0..68f0bf20be 100644
--- a/widgets/misc/e-activity.h
+++ b/widgets/misc/e-activity.h
@@ -68,8 +68,12 @@ struct _EActivityClass {
GType e_activity_get_type (void);
EActivity * e_activity_new (const gchar *primary_text);
+EActivity * e_activity_newv (const gchar *format,
+ ...) G_GNUC_PRINTF (1, 2);
void e_activity_cancel (EActivity *activity);
+void e_activity_cancel_in_idle (EActivity *activity);
void e_activity_complete (EActivity *activity);
+void e_activity_complete_in_idle (EActivity *activity);
void e_activity_clicked (EActivity *activity);
gchar * e_activity_describe (EActivity *activity);
gboolean e_activity_is_cancelled (EActivity *activity);
@@ -95,6 +99,10 @@ void e_activity_set_primary_text (EActivity *activity,
const gchar * e_activity_get_secondary_text (EActivity *activity);
void e_activity_set_secondary_text (EActivity *activity,
const gchar *secondary_text);
+void e_activity_set_error (EActivity *activity,
+ const GError *error);
+gboolean e_activity_propagate_error (EActivity *activity,
+ GError **destination);
G_END_DECLS
diff --git a/widgets/misc/e-attachment-bar.c b/widgets/misc/e-attachment-bar.c
index 5a1a6359de..aebc6bb423 100644
--- a/widgets/misc/e-attachment-bar.c
+++ b/widgets/misc/e-attachment-bar.c
@@ -80,7 +80,6 @@
struct _EAttachmentBarPrivate {
gboolean batch_unref;
GPtrArray *attachments;
- gchar *current_folder;
char *path;
GtkUIManager *ui_manager;
@@ -89,17 +88,11 @@ struct _EAttachmentBarPrivate {
GtkActionGroup *open_actions;
guint merge_id;
- gchar *background_filename;
- gchar *background_options;
-
guint editable : 1;
};
enum {
PROP_0,
- PROP_BACKGROUND_FILENAME,
- PROP_BACKGROUND_OPTIONS,
- PROP_CURRENT_FOLDER,
PROP_EDITABLE,
PROP_UI_MANAGER
};
@@ -129,78 +122,6 @@ static const gchar *ui =
"</ui>";
static void
-action_add_cb (GtkAction *action,
- EAttachmentBar *attachment_bar)
-{
- GtkWidget *dialog;
- GtkWidget *option;
- GSList *uris, *iter;
- const gchar *disposition;
- gboolean active;
- gpointer parent;
- gint response;
-
- parent = gtk_widget_get_toplevel (GTK_WIDGET (attachment_bar));
- parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
-
- dialog = gtk_file_chooser_dialog_new (
- _("Insert Attachment"), parent,
- GTK_FILE_CHOOSER_ACTION_OPEN,
- GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
- _("A_ttach"), GTK_RESPONSE_OK,
- NULL);
-
- gtk_dialog_set_default_response (
- GTK_DIALOG (dialog), GTK_RESPONSE_OK);
- gtk_file_chooser_set_local_only (
- GTK_FILE_CHOOSER (dialog), FALSE);
- gtk_file_chooser_set_select_multiple (
- GTK_FILE_CHOOSER (dialog), TRUE);
- 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 (
- GTK_FILE_CHOOSER (dialog), option);
- gtk_widget_show (option);
-
- response = e_attachment_bar_file_chooser_dialog_run (
- attachment_bar, dialog);
-
- if (response != GTK_RESPONSE_OK)
- goto exit;
-
- uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
- active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (option));
- disposition = active ? "inline" : "attachment";
-
- for (iter = uris; iter != NULL; iter = iter->next) {
- CamelURL *url;
-
- url = camel_url_new (iter->data, NULL);
- if (url == NULL)
- continue;
-
- /* XXX Do we really need two different attach functions? */
- if (g_ascii_strcasecmp (url->protocol, "file") == 0)
- e_attachment_bar_attach (
- attachment_bar, url->path, disposition);
- else
- e_attachment_bar_attach_remote_file (
- attachment_bar, iter->data, disposition);
-
- camel_url_free (url);
- }
-
- g_slist_foreach (uris, (GFunc) g_free, NULL);
- g_slist_free (uris);
-
-exit:
- gtk_widget_destroy (dialog);
-}
-
-static void
action_properties_cb (GtkAction *action,
EAttachmentBar *attachment_bar)
{
@@ -233,33 +154,6 @@ action_properties_cb (GtkAction *action,
}
static void
-action_recent_cb (GtkAction *action,
- EAttachmentBar *attachment_bar)
-{
- GtkRecentChooser *chooser;
- GFile *file;
- gchar *uri;
-
- chooser = GTK_RECENT_CHOOSER (action);
-
- /* Wish: gtk_recent_chooser_get_current_file() */
- uri = gtk_recent_chooser_get_current_uri (chooser);
- file = g_file_new_for_uri (uri);
- g_free (uri);
-
- if (g_file_is_native (file))
- e_attachment_bar_attach (
- E_ATTACHMENT_BAR (attachment_bar),
- g_file_get_path (file), "attachment");
- else
- e_attachment_bar_attach_remote_file (
- E_ATTACHMENT_BAR (attachment_bar),
- g_file_get_uri (file), "attachment");
-
- g_object_unref (file);
-}
-
-static void
action_remove_cb (GtkAction *action,
EAttachmentBar *attachment_bar)
{
@@ -409,47 +303,6 @@ action_set_background_cb (GtkAction *action,
}
}
-static GtkActionEntry standard_entries[] = {
-
- { "save-as",
- GTK_STOCK_SAVE_AS,
- NULL,
- NULL,
- NULL, /* XXX Add a tooltip! */
- G_CALLBACK (action_save_as_cb) },
-
- { "set-background",
- NULL,
- N_("Set as _Background"),
- NULL,
- NULL, /* XXX Add a tooltip! */
- G_CALLBACK (action_set_background_cb) }
-};
-
-static GtkActionEntry editable_entries[] = {
-
- { "add",
- GTK_STOCK_ADD,
- N_("A_dd Attachment..."),
- NULL,
- NULL, /* XXX Add a tooltip! */
- 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 void
attachment_bar_show_popup_menu (EAttachmentBar *attachment_bar,
GdkEventButton *event)
@@ -950,24 +803,6 @@ attachment_bar_set_property (GObject *object,
GParamSpec *pspec)
{
switch (property_id) {
- case PROP_BACKGROUND_FILENAME:
- e_attachment_bar_set_background_filename (
- E_ATTACHMENT_BAR (object),
- g_value_get_string (value));
- return;
-
- case PROP_BACKGROUND_OPTIONS:
- e_attachment_bar_set_background_options (
- E_ATTACHMENT_BAR (object),
- g_value_get_string (value));
- return;
-
- case PROP_CURRENT_FOLDER:
- e_attachment_bar_set_current_folder (
- E_ATTACHMENT_BAR (object),
- g_value_get_string (value));
- return;
-
case PROP_EDITABLE:
e_attachment_bar_set_editable (
E_ATTACHMENT_BAR (object),
@@ -985,27 +820,6 @@ attachment_bar_get_property (GObject *object,
GParamSpec *pspec)
{
switch (property_id) {
- case PROP_BACKGROUND_FILENAME:
- g_value_set_string (
- value,
- e_attachment_bar_get_background_filename (
- E_ATTACHMENT_BAR (object)));
- return;
-
- case PROP_BACKGROUND_OPTIONS:
- g_value_set_string (
- value,
- e_attachment_bar_get_background_options (
- E_ATTACHMENT_BAR (object)));
- return;
-
- case PROP_CURRENT_FOLDER:
- g_value_set_string (
- value,
- e_attachment_bar_get_current_folder (
- E_ATTACHMENT_BAR (object)));
- return;
-
case PROP_EDITABLE:
g_value_set_boolean (
value,
@@ -1077,12 +891,8 @@ attachment_bar_finalize (GObject *object)
priv = E_ATTACHMENT_BAR_GET_PRIVATE (object);
g_ptr_array_free (priv->attachments, TRUE);
- g_free (priv->current_folder);
g_free (priv->path);
- g_free (priv->background_filename);
- g_free (priv->background_options);
-
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -1092,25 +902,13 @@ attachment_bar_constructed (GObject *object)
{
EAttachmentBarPrivate *priv;
GtkActionGroup *action_group;
- GConfBridge *bridge;
- const gchar *prop;
- const gchar *key;
priv = E_ATTACHMENT_BAR_GET_PRIVATE (object);
action_group = priv->editable_actions;
- bridge = gconf_bridge_get ();
e_mutual_binding_new (
G_OBJECT (object), "editable",
G_OBJECT (action_group), "visible");
-
- prop = "background-filename";
- key = "/desktop/gnome/background/picture_filename";
- gconf_bridge_bind_property (bridge, key, object, prop);
-
- prop = "background-options";
- key = "/desktop/gnome/background/picture_options";
- gconf_bridge_bind_property (bridge, key, object, prop);
}
static gboolean
@@ -1428,39 +1226,6 @@ attachment_bar_class_init (EAttachmentBarClass *class)
g_object_class_install_property (
object_class,
- PROP_BACKGROUND_FILENAME,
- g_param_spec_string (
- "background-filename",
- "Background Filename",
- NULL,
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT));
-
- g_object_class_install_property (
- object_class,
- PROP_BACKGROUND_OPTIONS,
- g_param_spec_string (
- "background-options",
- "Background Options",
- NULL,
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT));
-
- 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_EDITABLE,
g_param_spec_boolean (
"editable",
@@ -1503,10 +1268,7 @@ static void
attachment_bar_init (EAttachmentBar *bar)
{
GnomeIconList *icon_list;
- GtkUIManager *ui_manager;
- GtkActionGroup *action_group;
gint icon_width, window_height;
- const gchar *domain = GETTEXT_PACKAGE;
GError *error = NULL;
bar->priv = E_ATTACHMENT_BAR_GET_PRIVATE (bar);
@@ -1532,38 +1294,6 @@ attachment_bar_init (EAttachmentBar *bar)
gnome_icon_list_set_icon_border (icon_list, ICON_BORDER);
gnome_icon_list_set_text_spacing (icon_list, ICON_TEXT_SPACING);
gnome_icon_list_set_selection_mode (icon_list, GTK_SELECTION_MULTIPLE);
-
- ui_manager = gtk_ui_manager_new ();
- bar->priv->ui_manager = ui_manager;
- bar->priv->merge_id = gtk_ui_manager_new_merge_id (ui_manager);
-
- action_group = gtk_action_group_new ("standard");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_action_group_add_actions (
- action_group, standard_entries,
- G_N_ELEMENTS (standard_entries), bar);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- bar->priv->standard_actions = action_group;
-
- action_group = gtk_action_group_new ("editable");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_action_group_add_actions (
- action_group, editable_entries,
- G_N_ELEMENTS (editable_entries), bar);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- bar->priv->editable_actions = action_group;
-
- action_group = gtk_action_group_new ("open");
- gtk_action_group_set_translation_domain (action_group, domain);
- gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
- bar->priv->open_actions = action_group;
-
- /* Because we are loading from a hard-coded string, there is
- * no chance of I/O errors. Failure here imples 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);
}
GType
@@ -2038,69 +1768,6 @@ e_attachment_bar_attach_mime_part (EAttachmentBar *bar,
e_attachment_bar_add_attachment (bar, attachment);
}
-GtkAction *
-e_attachment_bar_recent_action_new (EAttachmentBar *bar,
- const gchar *action_name,
- const gchar *action_label)
-{
- GtkAction *action;
- GtkRecentChooser *chooser;
-
- g_return_val_if_fail (E_IS_ATTACHMENT_BAR (bar), 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), bar);
-
- return action;
-}
-
-gint
-e_attachment_bar_file_chooser_dialog_run (EAttachmentBar *attachment_bar,
- GtkWidget *dialog)
-{
- GtkFileChooser *file_chooser;
- gint response = GTK_RESPONSE_NONE;
- const gchar *current_folder;
- gboolean save_folder;
-
- g_return_val_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar), response);
- g_return_val_if_fail (GTK_IS_FILE_CHOOSER_DIALOG (dialog), response);
-
- file_chooser = GTK_FILE_CHOOSER (dialog);
- current_folder = e_attachment_bar_get_current_folder (attachment_bar);
- gtk_file_chooser_set_current_folder (file_chooser, current_folder);
-
- response = gtk_dialog_run (GTK_DIALOG (dialog));
-
- save_folder =
- (response == GTK_RESPONSE_ACCEPT) ||
- (response == GTK_RESPONSE_OK) ||
- (response == GTK_RESPONSE_YES) ||
- (response == GTK_RESPONSE_APPLY);
-
- if (save_folder) {
- gchar *folder;
-
- folder = gtk_file_chooser_get_current_folder (file_chooser);
- e_attachment_bar_set_current_folder (attachment_bar, folder);
- g_free (folder);
- }
-
- return response;
-}
-
void
e_attachment_bar_update_actions (EAttachmentBar *attachment_bar)
{
@@ -2109,82 +1776,6 @@ e_attachment_bar_update_actions (EAttachmentBar *attachment_bar)
g_signal_emit (attachment_bar, signals[UPDATE_ACTIONS], 0);
}
-const gchar *
-e_attachment_bar_get_background_filename (EAttachmentBar *attachment_bar)
-{
- g_return_val_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar), NULL);
-
- return attachment_bar->priv->background_filename;
-}
-
-void
-e_attachment_bar_set_background_filename (EAttachmentBar *attachment_bar,
- const gchar *background_filename)
-{
- EAttachmentBarPrivate *priv;
-
- g_return_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar));
-
- if (background_filename == NULL)
- background_filename = "";
-
- priv = attachment_bar->priv;
- g_free (priv->background_filename);
- priv->background_filename = g_strdup (background_filename);
-
- g_object_notify (G_OBJECT (attachment_bar), "background-filename");
-}
-
-const gchar *
-e_attachment_bar_get_background_options (EAttachmentBar *attachment_bar)
-{
- g_return_val_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar), NULL);
-
- return attachment_bar->priv->background_options;
-}
-
-void
-e_attachment_bar_set_background_options (EAttachmentBar *attachment_bar,
- const gchar *background_options)
-{
- EAttachmentBarPrivate *priv;
-
- g_return_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar));
-
- if (background_options == NULL)
- background_options = "none";
-
- priv = attachment_bar->priv;
- g_free (priv->background_options);
- priv->background_options = g_strdup (background_options);
-
- g_object_notify (G_OBJECT (attachment_bar), "background-options");
-}
-
-const gchar *
-e_attachment_bar_get_current_folder (EAttachmentBar *attachment_bar)
-{
- g_return_val_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar), NULL);
-
- return attachment_bar->priv->current_folder;
-}
-
-void
-e_attachment_bar_set_current_folder (EAttachmentBar *attachment_bar,
- const gchar *current_folder)
-{
-
- g_return_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar));
-
- if (current_folder == NULL)
- current_folder = g_get_home_dir ();
-
- g_free (attachment_bar->priv->current_folder);
- attachment_bar->priv->current_folder = g_strdup (current_folder);
-
- g_object_notify (G_OBJECT (attachment_bar), "current-folder");
-}
-
gboolean
e_attachment_bar_get_editable (EAttachmentBar *attachment_bar)
{
@@ -2203,39 +1794,3 @@ e_attachment_bar_set_editable (EAttachmentBar *attachment_bar,
g_object_notify (G_OBJECT (attachment_bar), "editable");
}
-
-GtkUIManager *
-e_attachment_bar_get_ui_manager (EAttachmentBar *attachment_bar)
-{
- g_return_val_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar), NULL);
-
- return attachment_bar->priv->ui_manager;
-}
-
-GtkAction *
-e_attachment_bar_get_action (EAttachmentBar *attachment_bar,
- const gchar *action_name)
-{
- GtkUIManager *ui_manager;
-
- g_return_val_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar), NULL);
- g_return_val_if_fail (action_name != NULL, NULL);
-
- ui_manager = e_attachment_bar_get_ui_manager (attachment_bar);
-
- return e_lookup_action (ui_manager, action_name);
-}
-
-GtkActionGroup *
-e_attachment_bar_get_action_group (EAttachmentBar *attachment_bar,
- const gchar *group_name)
-{
- GtkUIManager *ui_manager;
-
- g_return_val_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar), NULL);
- g_return_val_if_fail (group_name != NULL, NULL);
-
- ui_manager = e_attachment_bar_get_ui_manager (attachment_bar);
-
- return e_lookup_action_group (ui_manager, group_name);
-}
diff --git a/widgets/misc/e-attachment-dialog.c b/widgets/misc/e-attachment-dialog.c
index 0668a7358d..f697df1fe9 100644
--- a/widgets/misc/e-attachment-dialog.c
+++ b/widgets/misc/e-attachment-dialog.c
@@ -29,9 +29,9 @@
struct _EAttachmentDialogPrivate {
EAttachment *attachment;
- GtkWidget *filename_entry;
+ GtkWidget *display_name_entry;
GtkWidget *description_entry;
- GtkWidget *mime_type_label;
+ GtkWidget *content_type_label;
GtkWidget *disposition_checkbox;
};
@@ -46,56 +46,54 @@ static void
attachment_dialog_update (EAttachmentDialog *dialog)
{
EAttachment *attachment;
- CamelMimePart *mime_part;
+ GFileInfo *file_info;
GtkWidget *widget;
+ const gchar *content_type;
+ const gchar *display_name;
+ const gchar *description;
+ const gchar *disposition;
gboolean sensitive;
- const gchar *text;
gboolean active;
- /* XXX This is too complex. I shouldn't have to use the
- * MIME part at all. */
-
attachment = e_attachment_dialog_get_attachment (dialog);
- if (attachment != NULL)
- mime_part = e_attachment_get_mime_part (attachment);
- else
- mime_part = NULL;
- sensitive = (attachment != NULL);
+ if (E_IS_ATTACHMENT (attachment)) {
+ file_info = e_attachment_get_file_info (attachment);
+ content_type = e_attachment_get_content_type (attachment);
+ display_name = e_attachment_get_display_name (attachment);
+ description = e_attachment_get_description (attachment);
+ disposition = e_attachment_get_disposition (attachment);
+ } else {
+ file_info = NULL;
+ content_type = NULL;
+ display_name = NULL;
+ description = NULL;
+ disposition = NULL;
+ }
+
+ sensitive = G_IS_FILE_INFO (file_info);
+
gtk_dialog_set_response_sensitive (
GTK_DIALOG (dialog), GTK_RESPONSE_OK, sensitive);
- text = NULL;
- if (attachment != NULL)
- text = e_attachment_get_filename (attachment);
- text = (text != NULL) ? text : "";
- widget = dialog->priv->filename_entry;
+ if (display_name == NULL)
+ display_name = "";
+ widget = dialog->priv->display_name_entry;
gtk_widget_set_sensitive (widget, sensitive);
- gtk_entry_set_text (GTK_ENTRY (widget), text);
+ gtk_entry_set_text (GTK_ENTRY (widget), display_name);
- text = NULL;
- if (attachment != NULL)
- text = e_attachment_get_description (attachment);
- text = (text != NULL) ? text : "";
+ if (description == NULL)
+ description = "";
widget = dialog->priv->description_entry;
gtk_widget_set_sensitive (widget, sensitive);
- gtk_entry_set_text (GTK_ENTRY (widget), text);
+ gtk_entry_set_text (GTK_ENTRY (widget), description);
- text = NULL;
- if (attachment != NULL)
- text = e_attachment_get_mime_type (attachment);
- text = (text != NULL) ? text : "";
- widget = dialog->priv->mime_type_label;
- gtk_label_set_text (GTK_LABEL (widget), text);
-
- active = FALSE;
- if (mime_part != NULL) {
- const gchar *disposition;
-
- disposition = camel_mime_part_get_disposition (mime_part);
- active = (g_ascii_strcasecmp (disposition, "inline") == 0);
- } else if (attachment != NULL)
- active = e_attachment_is_inline (attachment);
+ if (content_type == NULL)
+ content_type = "";
+ widget = dialog->priv->content_type_label;
+ gtk_label_set_text (GTK_LABEL (widget), content_type);
+
+ 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);
@@ -147,9 +145,9 @@ attachment_dialog_dispose (GObject *object)
priv->attachment = NULL;
}
- if (priv->filename_entry != NULL) {
- g_object_unref (priv->filename_entry);
- priv->filename_entry = NULL;
+ if (priv->display_name_entry != NULL) {
+ g_object_unref (priv->display_name_entry);
+ priv->display_name_entry = NULL;
}
if (priv->description_entry != NULL) {
@@ -157,9 +155,9 @@ attachment_dialog_dispose (GObject *object)
priv->description_entry = NULL;
}
- if (priv->mime_type_label != NULL) {
- g_object_unref (priv->mime_type_label);
- priv->mime_type_label = NULL;
+ if (priv->content_type_label != NULL) {
+ g_object_unref (priv->content_type_label);
+ priv->content_type_label = NULL;
}
if (priv->disposition_checkbox != NULL) {
@@ -196,7 +194,8 @@ attachment_dialog_response (GtkDialog *dialog,
EAttachmentDialogPrivate *priv;
EAttachment *attachment;
GtkToggleButton *button;
- GtkEntry *entry;
+ GFileInfo *file_info;
+ const gchar *attribute;
const gchar *text;
gboolean active;
@@ -204,16 +203,19 @@ attachment_dialog_response (GtkDialog *dialog,
return;
priv = E_ATTACHMENT_DIALOG_GET_PRIVATE (dialog);
- g_return_if_fail (priv->attachment != NULL);
+ g_return_if_fail (E_IS_ATTACHMENT (priv->attachment));
attachment = priv->attachment;
- entry = GTK_ENTRY (priv->filename_entry);
- text = gtk_entry_get_text (entry);
- e_attachment_set_filename (attachment, text);
+ file_info = e_attachment_get_file_info (attachment);
+ g_return_if_fail (G_IS_FILE_INFO (file_info));
+
+ 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);
- entry = GTK_ENTRY (priv->description_entry);
- text = gtk_entry_get_text (entry);
- e_attachment_set_description (attachment, 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);
button = GTK_TOGGLE_BUTTON (priv->disposition_checkbox);
active = gtk_toggle_button_get_active (button);
@@ -289,13 +291,13 @@ attachment_dialog_init (EAttachmentDialog *dialog)
gtk_table_attach (
GTK_TABLE (container), widget,
1, 2, 0, 1, GTK_FILL | GTK_EXPAND, 0, 0, 0);
- dialog->priv->filename_entry = g_object_ref (widget);
+ 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->filename_entry);
+ GTK_LABEL (widget), dialog->priv->display_name_entry);
gtk_table_attach (
GTK_TABLE (container), widget,
0, 1, 0, 1, GTK_FILL, 0, 0, 0);
@@ -324,7 +326,7 @@ attachment_dialog_init (EAttachmentDialog *dialog)
gtk_table_attach (
GTK_TABLE (container), widget,
1, 2, 2, 3, GTK_FILL | GTK_EXPAND, 0, 0, 0);
- dialog->priv->mime_type_label = g_object_ref (widget);
+ dialog->priv->content_type_label = g_object_ref (widget);
gtk_widget_show (widget);
widget = gtk_label_new (_("MIME Type:"));
diff --git a/widgets/misc/e-attachment-icon-view.c b/widgets/misc/e-attachment-icon-view.c
new file mode 100644
index 0000000000..8d39a76d75
--- /dev/null
+++ b/widgets/misc/e-attachment-icon-view.c
@@ -0,0 +1,319 @@
+/*
+ * 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 <gdk/gdkkeysyms.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;
+};
+
+static gpointer parent_class;
+
+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 (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
+ e_attachment_view_show_popup_menu (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_key_press_event (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ if (event->keyval == GDK_Delete) {
+ e_attachment_view_remove_selected (view, TRUE);
+ return TRUE;
+ }
+
+ /* Chain up to parent's key_press_event() method. */
+ return GTK_WIDGET_CLASS (parent_class)->
+ key_press_event (widget, event);
+}
+
+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 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);
+
+ return TRUE;
+}
+
+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_class_init (EAttachmentIconViewClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_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->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->key_press_event = attachment_icon_view_key_press_event;
+ widget_class->drag_motion = attachment_icon_view_drag_motion;
+ widget_class->drag_data_received = attachment_icon_view_drag_data_received;
+ widget_class->popup_menu = attachment_icon_view_popup_menu;
+}
+
+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;
+}
+
+static void
+attachment_icon_view_init (EAttachmentIconView *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);
+
+ gtk_icon_view_set_pixbuf_column (
+ GTK_ICON_VIEW (icon_view),
+ E_ATTACHMENT_STORE_COLUMN_LARGE_PIXBUF);
+
+ gtk_icon_view_set_text_column (
+ GTK_ICON_VIEW (icon_view),
+ E_ATTACHMENT_STORE_COLUMN_ICON_CAPTION);
+}
+
+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 0000000000..ab9360e42e
--- /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 0000000000..bb919e9789
--- /dev/null
+++ b/widgets/misc/e-attachment-paned.c
@@ -0,0 +1,563 @@
+/*
+ * 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-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
+
+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_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_paned_get_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_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_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;
+
+ priv = E_ATTACHMENT_PANED_GET_PRIVATE (object);
+
+ 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), "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");
+}
+
+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));
+}
+
+static void
+attachment_paned_init (EAttachmentPaned *paned)
+{
+ GtkTreeSelection *selection;
+ GtkSizeGroup *size_group;
+ GtkWidget *container;
+ GtkWidget *widget;
+
+ 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 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);
+
+ 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;
+
+ widget = gtk_hbox_new (FALSE, 0);
+ 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_label_new_with_mnemonic (_("Show _Attachment Bar"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 6);
+ paned->priv->show_hide_label = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ widget = gtk_image_new_from_icon_name (
+ "mail-attachment", GTK_ICON_SIZE_MENU);
+ gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
+ gtk_widget_set_size_request (widget, 100, -1);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ paned->priv->status_icon = g_object_ref (widget);
+ gtk_widget_hide (widget);
+
+ widget = gtk_label_new (NULL);
+ gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 6);
+ paned->priv->status_label = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ /* Construct the Attachment Views */
+
+ container = GTK_WIDGET (paned);
+
+ widget = gtk_notebook_new ();
+ gtk_widget_set_size_request (widget, -1, 40);
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE);
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (widget), FALSE);
+ gtk_paned_pack2 (GTK_PANED (container), widget, TRUE, FALSE);
+ paned->priv->notebook = g_object_ref (widget);
+ gtk_widget_hide (widget);
+
+ container = paned->priv->notebook;
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (
+ GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+ gtk_notebook_append_page (GTK_NOTEBOOK (container), widget, NULL);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = e_attachment_icon_view_new ();
+ GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
+ gtk_icon_view_set_model (GTK_ICON_VIEW (widget), paned->priv->model);
+ 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);
+
+ 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 */
+ };
+
+ type = g_type_register_static (
+ GTK_TYPE_VPANED, "EAttachmentPaned",
+ &type_info, 0);
+ }
+
+ return type;
+}
+
+GtkWidget *
+e_attachment_paned_new (void)
+{
+ return g_object_new (E_TYPE_ATTACHMENT_PANED, NULL);
+}
+
+EAttachmentView *
+e_attachment_paned_get_view (EAttachmentPaned *paned)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_PANED (paned), NULL);
+
+ return E_ATTACHMENT_VIEW (paned->priv->icon_view);
+}
+
+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");
+}
diff --git a/widgets/misc/e-attachment-paned.h b/widgets/misc/e-attachment-paned.h
new file mode 100644
index 0000000000..c6cad5226f
--- /dev/null
+++ b/widgets/misc/e-attachment-paned.h
@@ -0,0 +1,79 @@
+/*
+ * 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>
+#include <e-attachment-view.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);
+EAttachmentView *
+ e_attachment_paned_get_view (EAttachmentPaned *paned);
+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);
+
+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 0000000000..2658dd06c9
--- /dev/null
+++ b/widgets/misc/e-attachment-store.c
@@ -0,0 +1,1073 @@
+/*
+ * 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 <glib/gi18n.h>
+
+#include "e-util/e-util.h"
+#include "e-util/gconf-bridge.h"
+
+#include "e-file-activity.h"
+
+#define E_ATTACHMENT_STORE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ATTACHMENT_STORE, EAttachmentStorePrivate))
+
+#define DEFAULT_ICON_NAME "mail-attachment"
+
+/* XXX Unfortunate that we have to define this here. Would
+ * prefer the attachment view classes pick their own size,
+ * but GtkIconView requires a dedicated pixbuf column. */
+#define LARGE_ICON_SIZE GTK_ICON_SIZE_DIALOG
+#define SMALL_ICON_SIZE GTK_ICON_SIZE_MENU
+
+struct _EAttachmentStorePrivate {
+ GHashTable *activity_index;
+ GHashTable *attachment_index;
+ gchar *background_filename;
+ gchar *background_options;
+ gchar *current_folder;
+
+ guint ignore_row_changed : 1;
+};
+
+enum {
+ PROP_0,
+ PROP_BACKGROUND_FILENAME,
+ PROP_BACKGROUND_OPTIONS,
+ PROP_CURRENT_FOLDER,
+ PROP_NUM_ATTACHMENTS,
+ PROP_NUM_DOWNLOADING,
+ PROP_TOTAL_SIZE
+};
+
+enum {
+ NEW_ACTIVITY,
+ LAST_SIGNAL
+};
+
+static gpointer parent_class;
+static guint signals[LAST_SIGNAL];
+
+static const gchar *
+attachment_store_get_background_filename (EAttachmentStore *store)
+{
+ return store->priv->background_filename;
+}
+
+static void
+attachment_store_set_background_filename (EAttachmentStore *store,
+ const gchar *background_filename)
+{
+ if (background_filename == NULL)
+ background_filename = "";
+
+ g_free (store->priv->background_filename);
+ store->priv->background_filename = g_strdup (background_filename);
+
+ g_object_notify (G_OBJECT (store), "background-filename");
+}
+
+static const gchar *
+attachment_store_get_background_options (EAttachmentStore *store)
+{
+ return store->priv->background_options;
+}
+
+static void
+attachment_store_set_background_options (EAttachmentStore *store,
+ const gchar *background_options)
+{
+ if (background_options == NULL)
+ background_options = "";
+
+ g_free (store->priv->background_options);
+ store->priv->background_options = g_strdup (background_options);
+
+ g_object_notify (G_OBJECT (store), "background-options");
+}
+
+static void
+attachment_store_remove_activity (EAttachmentStore *store,
+ EActivity *activity)
+{
+ GtkTreeRowReference *reference;
+ GHashTable *hash_table;
+
+ hash_table = store->priv->activity_index;
+ reference = g_hash_table_lookup (hash_table, activity);
+
+ if (gtk_tree_row_reference_valid (reference)) {
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ 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_set (
+ GTK_LIST_STORE (store), &iter,
+ E_ATTACHMENT_STORE_COLUMN_ACTIVITY, NULL, -1);
+ }
+
+ g_hash_table_remove (hash_table, activity);
+
+ g_object_notify (G_OBJECT (store), "num-downloading");
+}
+
+static void
+attachment_store_copy_ready (GFile *source,
+ GAsyncResult *result,
+ GtkTreeRowReference *reference)
+{
+ EAttachmentStore *store;
+ EAttachment *attachment;
+ EActivity *activity;
+ GFile *destination;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gboolean valid;
+ GError *error = NULL;
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+ valid = gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
+ g_return_if_fail (valid);
+
+ gtk_tree_model_get (
+ model, &iter,
+ E_ATTACHMENT_STORE_COLUMN_ACTIVITY, &activity,
+ E_ATTACHMENT_STORE_COLUMN_ATTACHMENT, &attachment, -1);
+
+ gtk_tree_row_reference_free (reference);
+
+ store = E_ATTACHMENT_STORE (model);
+
+ if (!g_file_copy_finish (source, result, &error))
+ goto fail;
+
+ gtk_list_store_set (
+ GTK_LIST_STORE (store), &iter,
+ E_ATTACHMENT_STORE_COLUMN_ACTIVITY, NULL, -1);
+
+ destination = e_file_activity_get_file (E_FILE_ACTIVITY (activity));
+ e_attachment_set_file (attachment, destination);
+
+ e_activity_complete (activity);
+
+ path = gtk_tree_model_get_path (model, &iter);
+ gtk_tree_model_row_changed (model, path, &iter);
+ gtk_tree_path_free (path);
+
+ g_object_unref (attachment);
+ g_object_unref (activity);
+
+ return;
+
+fail:
+ e_attachment_store_remove_attachment (store, attachment);
+
+ g_object_unref (attachment);
+ g_object_unref (activity);
+
+ /* XXX Do something more useful with the error. */
+ g_warning ("%s", error->message);
+ g_error_free (error);
+}
+
+static void
+attachment_store_copy_async (EAttachmentStore *store,
+ EAttachment *attachment)
+{
+ EActivity *activity;
+ GCancellable *cancellable;
+ GtkTreeRowReference *reference;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ GHashTable *hash_table;
+ GFile *destination;
+ GFile *source;
+ gboolean valid;
+ gchar *filename;
+ gchar *uri;
+ gint fd;
+ GError *error = NULL;
+
+ hash_table = store->priv->attachment_index;
+ reference = g_hash_table_lookup (hash_table, attachment);
+ g_return_if_fail (reference != NULL);
+
+ fd = e_file_open_tmp (&filename, &error);
+ if (error != NULL)
+ goto fail;
+
+ source = e_attachment_get_file (attachment);
+ destination = g_file_new_for_path (filename);
+
+ g_free (filename);
+ close (fd);
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+ valid = gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
+ g_return_if_fail (valid);
+
+ uri = g_file_get_uri (source);
+ activity = e_file_activity_newv (_("Downloading '%s'"), uri);
+ g_free (uri);
+
+ gtk_list_store_set (
+ GTK_LIST_STORE (store), &iter,
+ E_ATTACHMENT_STORE_COLUMN_ACTIVITY, activity, -1);
+
+ reference = gtk_tree_row_reference_copy (reference);
+
+ g_hash_table_insert (hash_table, g_object_ref (activity), reference);
+
+ g_signal_connect_swapped (
+ activity, "cancelled",
+ G_CALLBACK (attachment_store_remove_activity), store);
+
+ g_signal_connect_swapped (
+ activity, "completed",
+ G_CALLBACK (attachment_store_remove_activity), store);
+
+ reference = gtk_tree_row_reference_copy (reference);
+
+ cancellable = e_file_activity_get_cancellable (
+ E_FILE_ACTIVITY (activity));
+
+ g_file_copy_async (
+ source, destination, G_FILE_COPY_OVERWRITE,
+ G_PRIORITY_DEFAULT, cancellable, (GFileProgressCallback)
+ e_file_activity_progress, activity, (GAsyncReadyCallback)
+ attachment_store_copy_ready, reference);
+
+ e_file_activity_set_file (E_FILE_ACTIVITY (activity), destination);
+ g_signal_emit (store, signals[NEW_ACTIVITY], 0, activity);
+
+ g_object_notify (G_OBJECT (store), "num-downloading");
+
+ g_object_unref (activity);
+ g_object_unref (destination);
+
+ return;
+
+fail:
+ e_attachment_store_remove_attachment (store, attachment);
+
+ /* XXX Do something more useful with the error. */
+ g_warning ("%s", error->message);
+ g_error_free (error);
+}
+
+static void
+attachment_store_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_BACKGROUND_FILENAME:
+ attachment_store_set_background_filename (
+ E_ATTACHMENT_STORE (object),
+ g_value_get_string (value));
+ return;
+
+ case PROP_BACKGROUND_OPTIONS:
+ attachment_store_set_background_options (
+ E_ATTACHMENT_STORE (object),
+ g_value_get_string (value));
+ return;
+
+ 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_BACKGROUND_FILENAME:
+ g_value_set_string (
+ value,
+ attachment_store_get_background_filename (
+ E_ATTACHMENT_STORE (object)));
+ return;
+
+ case PROP_BACKGROUND_OPTIONS:
+ g_value_set_string (
+ value,
+ attachment_store_get_background_options (
+ E_ATTACHMENT_STORE (object)));
+ return;
+
+ 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_DOWNLOADING:
+ g_value_set_uint (
+ value,
+ e_attachment_store_get_num_downloading (
+ 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->activity_index);
+ 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->activity_index);
+ g_hash_table_destroy (priv->attachment_index);
+
+ g_free (priv->background_filename);
+ g_free (priv->background_options);
+ 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)
+{
+ EAttachmentStorePrivate *priv;
+ GConfBridge *bridge;
+ const gchar *prop;
+ const gchar *key;
+
+ priv = E_ATTACHMENT_STORE_GET_PRIVATE (object);
+ bridge = gconf_bridge_get ();
+
+ prop = "background-filename";
+ key = "/desktop/gnome/background/picture_filename";
+ gconf_bridge_bind_property (bridge, key, object, prop);
+
+ prop = "background-options";
+ key = "/desktop/gnome/background/picture_options";
+ gconf_bridge_bind_property (bridge, key, object, prop);
+}
+
+static void
+attachment_store_row_changed (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter)
+{
+ EAttachmentStorePrivate *priv;
+ EAttachment *attachment;
+ GtkIconTheme *icon_theme;
+ GdkPixbuf *large_pixbuf;
+ GdkPixbuf *small_pixbuf;
+ GIcon *icon;
+ const gchar *content_type;
+ const gchar *display_name;
+ const gchar *thumbnail_path;
+ gchar *content_description;
+ gchar *display_size;
+ gchar *icon_caption;
+ gint large_icon_size;
+ gint small_icon_size;
+ guint64 size;
+ gint column_id;
+ GError *error = NULL;
+
+ priv = E_ATTACHMENT_STORE_GET_PRIVATE (model);
+
+ if (priv->ignore_row_changed)
+ return;
+
+ icon_theme = gtk_icon_theme_get_default ();
+ gtk_icon_size_lookup (LARGE_ICON_SIZE, &large_icon_size, NULL);
+ gtk_icon_size_lookup (SMALL_ICON_SIZE, &small_icon_size, NULL);
+
+ column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+ gtk_tree_model_get (model, iter, column_id, &attachment, -1);
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+ content_type = e_attachment_get_content_type (attachment);
+ display_name = e_attachment_get_display_name (attachment);
+ thumbnail_path = e_attachment_get_thumbnail_path (attachment);
+ icon = e_attachment_get_icon (attachment);
+ size = e_attachment_get_size (attachment);
+
+ content_type = (content_type != NULL) ? content_type : "";
+ content_description = g_content_type_get_description (content_type);
+ display_size = g_format_size_for_display ((goffset) size);
+
+ if (size > 0)
+ icon_caption = g_strdup_printf (
+ "%s\n(%s)", display_name, display_size);
+ else
+ icon_caption = g_strdup (display_name);
+
+ /* Prefer the thumbnail if we have one. */
+ if (thumbnail_path != NULL) {
+ gint width = -1;
+ gint height = -1;
+
+ gdk_pixbuf_get_file_info (thumbnail_path, &width, &height);
+
+ large_pixbuf = gdk_pixbuf_new_from_file_at_scale (
+ thumbnail_path,
+ (width > height) ? large_icon_size : -1,
+ (width > height) ? -1 : large_icon_size,
+ TRUE, &error);
+
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ small_pixbuf = gdk_pixbuf_new_from_file_at_scale (
+ thumbnail_path,
+ (width > height) ? small_icon_size : -1,
+ (width > height) ? -1 : small_icon_size,
+ TRUE, &error);
+
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ /* Otherwise fall back to the icon theme. */
+ } else {
+ GtkIconInfo *icon_info = NULL;
+ const gchar *filename;
+
+ if (G_IS_ICON (icon))
+ icon_info = gtk_icon_theme_lookup_by_gicon (
+ icon_theme, icon, large_icon_size, 0);
+ if (icon_info == NULL)
+ icon_info = gtk_icon_theme_lookup_icon (
+ icon_theme, DEFAULT_ICON_NAME,
+ large_icon_size, 0);
+ g_return_if_fail (icon_info != NULL);
+
+ filename = gtk_icon_info_get_filename (icon_info);
+ large_pixbuf = gdk_pixbuf_new_from_file (filename, &error);
+ gtk_icon_info_free (icon_info);
+
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ icon_info = NULL;
+
+ if (G_IS_ICON (icon))
+ icon_info = gtk_icon_theme_lookup_by_gicon (
+ icon_theme, icon, small_icon_size, 0);
+ if (icon_info == NULL)
+ icon_info = gtk_icon_theme_lookup_icon (
+ icon_theme, DEFAULT_ICON_NAME,
+ small_icon_size, 0);
+ g_return_if_fail (icon_info != NULL);
+
+ filename = gtk_icon_info_get_filename (icon_info);
+ small_pixbuf = gdk_pixbuf_new_from_file (filename, &error);
+ gtk_icon_info_free (icon_info);
+
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ /* We're about to trigger another "row-changed"
+ * signal, so this prevents infinite recursion. */
+ priv->ignore_row_changed = TRUE;
+
+ gtk_list_store_set (
+ GTK_LIST_STORE (model), iter,
+ E_ATTACHMENT_STORE_COLUMN_CONTENT_TYPE, content_description,
+ E_ATTACHMENT_STORE_COLUMN_DISPLAY_NAME, display_name,
+ E_ATTACHMENT_STORE_COLUMN_ICON_CAPTION, icon_caption,
+ E_ATTACHMENT_STORE_COLUMN_LARGE_PIXBUF, large_pixbuf,
+ E_ATTACHMENT_STORE_COLUMN_SMALL_PIXBUF, small_pixbuf,
+ E_ATTACHMENT_STORE_COLUMN_SIZE, size,
+ -1);
+
+ priv->ignore_row_changed = FALSE;
+
+ if (large_pixbuf != NULL)
+ g_object_unref (large_pixbuf);
+
+ if (small_pixbuf != NULL)
+ g_object_unref (small_pixbuf);
+
+ g_free (content_description);
+ g_free (display_size);
+}
+
+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_BACKGROUND_FILENAME,
+ g_param_spec_string (
+ "background-filename",
+ "Background Filename",
+ NULL,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_BACKGROUND_OPTIONS,
+ g_param_spec_string (
+ "background-options",
+ "Background Options",
+ NULL,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ 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_DOWNLOADING,
+ g_param_spec_uint (
+ "num-downloading",
+ "Num Downloading",
+ 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_iface_init (GtkTreeModelIface *iface)
+{
+ iface->row_changed = attachment_store_row_changed;
+}
+
+static void
+attachment_store_init (EAttachmentStore *store)
+{
+ GType types[E_ATTACHMENT_STORE_NUM_COLUMNS];
+ GHashTable *activity_index;
+ GHashTable *attachment_index;
+ gint column = 0;
+
+ activity_index = g_hash_table_new_full (
+ g_direct_hash, g_direct_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) gtk_tree_row_reference_free);
+
+ 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->activity_index = activity_index;
+ store->priv->attachment_index = attachment_index;
+
+ types[column++] = E_TYPE_ACTIVITY; /* COLUMN_ACTIVITY */
+ types[column++] = E_TYPE_ATTACHMENT; /* COLUMN_ATTACHMENT */
+ types[column++] = G_TYPE_STRING; /* COLUMN_CONTENT_TYPE */
+ types[column++] = G_TYPE_STRING; /* COLUMN_DISPLAY_NAME */
+ types[column++] = G_TYPE_STRING; /* COLUMN_ICON_CAPTION */
+ types[column++] = GDK_TYPE_PIXBUF; /* COLUMN_LARGE_PIXBUF */
+ types[column++] = GDK_TYPE_PIXBUF; /* COLUMN_SMALL_PIXBUF */
+ 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 */
+ };
+
+ static const GInterfaceInfo iface_info = {
+ (GInterfaceInitFunc) attachment_store_iface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL /* interface_data */
+ };
+
+ type = g_type_register_static (
+ GTK_TYPE_LIST_STORE, "EAttachmentStore",
+ &type_info, 0);
+
+ g_type_add_interface_static (
+ type, GTK_TYPE_TREE_MODEL, &iface_info);
+ }
+
+ 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);
+
+ if (!g_file_is_native (file))
+ attachment_store_copy_async (store, 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));
+}
+
+gboolean
+e_attachment_store_remove_attachment (EAttachmentStore *store,
+ EAttachment *attachment)
+{
+ GtkTreeRowReference *reference;
+ GHashTable *hash_table;
+ EActivity *activity;
+ 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;
+ }
+
+ 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_tree_model_get (
+ model, &iter,
+ E_ATTACHMENT_STORE_COLUMN_ACTIVITY, &activity, -1);
+
+ if (activity != NULL) {
+ /* Cancel the file transfer. */
+ e_activity_cancel (activity);
+ g_object_unref (activity);
+ }
+
+ 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);
+
+ 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_downloading (EAttachmentStore *store)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), 0);
+
+ return g_hash_table_size (store->priv->activity_index);
+}
+
+guint64
+e_attachment_store_get_total_size (EAttachmentStore *store)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ guint64 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;
+ gint column_id;
+
+ column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+ gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
+ total_size += e_attachment_get_size (attachment);
+ 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));
+
+ 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);
+ g_object_unref (attachment);
+ }
+
+ g_slist_foreach (files, (GFunc) g_object_unref, NULL);
+ g_slist_free (files);
+
+exit:
+ gtk_widget_destroy (dialog);
+}
+
+void
+e_attachment_store_run_save_dialog (EAttachmentStore *store,
+ EAttachment *attachment,
+ GtkWindow *parent)
+{
+ GtkFileChooser *file_chooser;
+ GtkWidget *dialog;
+ GFile *file;
+ EActivity *activity;
+ const gchar *display_name;
+ gint response;
+
+ g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+ dialog = gtk_file_chooser_dialog_new (
+ _("Save Attachment"), parent,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ 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");
+
+ display_name = e_attachment_get_display_name (attachment);
+ if (display_name != NULL)
+ gtk_file_chooser_set_current_name (file_chooser, display_name);
+
+ response = e_attachment_store_run_file_chooser_dialog (store, dialog);
+
+ if (response != GTK_RESPONSE_OK)
+ goto exit;
+
+ file = gtk_file_chooser_get_file (file_chooser);
+ activity = e_file_activity_new (_("Saving attachment"));
+ e_attachment_save_async (
+ attachment, E_FILE_ACTIVITY (activity), file);
+ g_signal_emit (store, signals[NEW_ACTIVITY], 0, activity);
+ g_object_unref (activity);
+ g_object_unref (file);
+
+exit:
+ gtk_widget_destroy (dialog);
+}
diff --git a/widgets/misc/e-attachment-store.h b/widgets/misc/e-attachment-store.h
new file mode 100644
index 0000000000..906aee6638
--- /dev/null
+++ b/widgets/misc/e-attachment-store.h
@@ -0,0 +1,110 @@
+/*
+ * 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_ACTIVITY, /* E_TYPE_ACTIVITY */
+ E_ATTACHMENT_STORE_COLUMN_ATTACHMENT, /* E_TYPE_ATTACHMENT */
+ E_ATTACHMENT_STORE_COLUMN_CONTENT_TYPE, /* G_TYPE_STRING */
+ E_ATTACHMENT_STORE_COLUMN_DISPLAY_NAME, /* G_TYPE_STRING */
+ E_ATTACHMENT_STORE_COLUMN_ICON_CAPTION, /* G_TYPE_STRING */
+ E_ATTACHMENT_STORE_COLUMN_LARGE_PIXBUF, /* GDK_TYPE_PIXBUF */
+ E_ATTACHMENT_STORE_COLUMN_SMALL_PIXBUF, /* GDK_TYPE_PIXBUF */
+ 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_downloading
+ (EAttachmentStore *store);
+guint64 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);
+void e_attachment_store_run_save_dialog
+ (EAttachmentStore *store,
+ EAttachment *attachment,
+ GtkWindow *parent);
+
+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 0000000000..817a6c7dab
--- /dev/null
+++ b/widgets/misc/e-attachment-tree-view.c
@@ -0,0 +1,396 @@
+/*
+ * 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 <gdk/gdkkeysyms.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;
+};
+
+static gpointer parent_class;
+
+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 (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
+ e_attachment_view_show_popup_menu (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_key_press_event (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ if (event->keyval == GDK_Delete) {
+ e_attachment_view_remove_selected (view, TRUE);
+ return TRUE;
+ }
+
+ /* Chain up to parent's key_press_event() method. */
+ return GTK_WIDGET_CLASS (parent_class)->
+ key_press_event (widget, event);
+}
+
+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 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);
+
+ return TRUE;
+}
+
+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_class_init (EAttachmentTreeViewClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_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->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->key_press_event = attachment_tree_view_key_press_event;
+ widget_class->drag_motion = attachment_tree_view_drag_motion;
+ widget_class->drag_data_received = attachment_tree_view_drag_data_received;
+ widget_class->popup_menu = attachment_tree_view_popup_menu;
+}
+
+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;
+}
+
+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);
+
+ 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, _("Name"));
+ 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);
+
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "pixbuf",
+ E_ATTACHMENT_STORE_COLUMN_SMALL_PIXBUF);
+
+ 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_DISPLAY_NAME);
+
+ 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);
+
+ 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 0000000000..7f16ba5ab2
--- /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 0000000000..198ce75d31
--- /dev/null
+++ b/widgets/misc/e-attachment-view.c
@@ -0,0 +1,1041 @@
+/*
+ * 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 <camel/camel-stream-mem.h>
+
+#include "e-util/e-plugin-ui.h"
+#include "e-util/e-util.h"
+#include "e-attachment-dialog.h"
+
+enum {
+ DND_TYPE_MESSAGE_RFC822,
+ DND_TYPE_X_UID_LIST,
+ DND_TYPE_TEXT_URI_LIST,
+ DND_TYPE_NETSCAPE_URL,
+ DND_TYPE_TEXT_VCARD,
+ DND_TYPE_TEXT_CALENDAR
+};
+
+static GtkTargetEntry drop_types[] = {
+ { "message/rfc822", 0, DND_TYPE_MESSAGE_RFC822 },
+ { "x-uid-list", 0, DND_TYPE_X_UID_LIST },
+ { "text/uri-list", 0, DND_TYPE_TEXT_URI_LIST },
+ { "_NETSCAPE_URL", 0, DND_TYPE_NETSCAPE_URL },
+ { "text/x-vcard", 0, DND_TYPE_TEXT_VCARD },
+ { "text/calendar", 0, DND_TYPE_TEXT_CALENDAR }
+};
+
+/* The atoms need initialized at runtime. */
+static struct {
+ const gchar *target;
+ GdkAtom atom;
+ GdkDragAction actions;
+} drag_info[] = {
+ { "message/rfc822", NULL, GDK_ACTION_COPY },
+ { "x-uid-list", NULL, GDK_ACTION_COPY |
+ GDK_ACTION_MOVE |
+ GDK_ACTION_ASK },
+ { "text/uri-list", NULL, GDK_ACTION_COPY },
+ { "_NETSCAPE_URL", NULL, GDK_ACTION_COPY },
+ { "text/x-vcard", NULL, GDK_ACTION_COPY },
+ { "text/calendar", NULL, GDK_ACTION_COPY }
+};
+
+static const gchar *ui =
+"<ui>"
+" <popup name='context'>"
+" <menuitem action='save-as'/>"
+" <menuitem action='set-background'/>"
+" <menuitem action='remove'/>"
+" <menuitem action='properties'/>"
+" <placeholder name='custom-actions'/>"
+" <separator/>"
+" <menuitem action='add'/>"
+" <separator/>"
+" <placeholder name='open-actions'/>"
+" </popup>"
+" <popup name='dnd'>"
+" <menuitem action='drag-copy'/>"
+" <menuitem action='drag-move'/>"
+" <separator/>"
+" <menuitem action='drag-cancel'/>"
+" </popup>"
+"</ui>";
+
+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_drag_cancel_cb (GtkAction *action,
+ EAttachmentView *view)
+{
+ EAttachmentViewPrivate *priv;
+
+ priv = e_attachment_view_get_private (view);
+ gtk_drag_finish (priv->drag_context, FALSE, FALSE, priv->time);
+}
+
+static void
+action_drag_copy_cb (GtkAction *action,
+ EAttachmentView *view)
+{
+ e_attachment_view_drag_action (view, GDK_ACTION_COPY);
+}
+
+static void
+action_drag_move_cb (GtkAction *action,
+ EAttachmentView *view)
+{
+ e_attachment_view_drag_action (view, GDK_ACTION_MOVE);
+}
+
+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;
+ gchar *uri;
+
+ chooser = GTK_RECENT_CHOOSER (action);
+ store = e_attachment_view_get_store (view);
+
+ uri = gtk_recent_chooser_get_current_uri (chooser);
+ attachment = e_attachment_new_for_uri (uri);
+ e_attachment_store_add_attachment (store, attachment);
+ g_free (uri);
+}
+
+static void
+action_remove_cb (GtkAction *action,
+ EAttachmentView *view)
+{
+ e_attachment_view_remove_selected (view, FALSE);
+}
+
+static void
+action_save_as_cb (GtkAction *action,
+ EAttachmentView *view)
+{
+}
+
+static void
+action_set_background_cb (GtkAction *action,
+ EAttachmentView *view)
+{
+}
+
+static GtkActionEntry standard_entries[] = {
+
+ { "drag-cancel",
+ NULL,
+ N_("Cancel _Drag"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_drag_cancel_cb) },
+
+ { "drag-copy",
+ NULL,
+ N_("_Copy"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_drag_copy_cb) },
+
+ { "drag-move",
+ NULL,
+ N_("_Move"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_drag_move_cb) },
+
+ { "save-as",
+ GTK_STOCK_SAVE_AS,
+ NULL,
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_save_as_cb) },
+
+ { "set-background",
+ NULL,
+ N_("Set as _Background"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ G_CALLBACK (action_set_background_cb) }
+};
+
+static GtkActionEntry editable_entries[] = {
+
+ { "add",
+ GTK_STOCK_ADD,
+ N_("A_dd Attachment..."),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ 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 void
+drop_message_rfc822 (EAttachmentView *view,
+ GtkSelectionData *selection_data,
+ EAttachmentStore *store,
+ GdkDragAction action)
+{
+ EAttachmentViewPrivate *priv;
+ EAttachment *attachment;
+ CamelMimeMessage *message;
+ CamelDataWrapper *wrapper;
+ CamelStream *stream;
+ const gchar *data;
+ gboolean success = FALSE;
+ gboolean delete = FALSE;
+ gint length;
+
+ priv = e_attachment_view_get_private (view);
+
+ 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;
+
+ attachment = e_attachment_new_for_message (message);
+ e_attachment_store_add_attachment (store, attachment);
+ g_object_unref (attachment);
+
+ success = TRUE;
+ delete = (action == GDK_ACTION_MOVE);
+
+exit:
+ camel_object_unref (message);
+ camel_object_unref (stream);
+
+ gtk_drag_finish (priv->drag_context, success, delete, priv->time);
+}
+
+static void
+drop_netscape_url (EAttachmentView *view,
+ GtkSelectionData *selection_data,
+ EAttachmentStore *store,
+ GdkDragAction action)
+{
+ EAttachmentViewPrivate *priv;
+ EAttachment *attachment;
+ const gchar *data;
+ gchar *copied_data;
+ gchar **strv;
+ gint length;
+
+ /* _NETSCAPE_URL is represented as "URI\nTITLE" */
+
+ priv = e_attachment_view_get_private (view);
+
+ 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);
+
+ attachment = e_attachment_new_for_uri (strv[0]);
+ e_attachment_store_add_attachment (store, attachment);
+ g_object_unref (attachment);
+
+ g_strfreev (strv);
+
+ gtk_drag_finish (priv->drag_context, TRUE, FALSE, priv->time);
+}
+
+static void
+drop_text_uri_list (EAttachmentView *view,
+ GtkSelectionData *selection_data,
+ EAttachmentStore *store,
+ GdkDragAction action)
+{
+ EAttachmentViewPrivate *priv;
+ gchar **uris;
+ gint ii;
+
+ priv = e_attachment_view_get_private (view);
+
+ uris = gtk_selection_data_get_uris (selection_data);
+
+ for (ii = 0; uris[ii] != NULL; ii++) {
+ EAttachment *attachment;
+
+ attachment = e_attachment_new_for_uri (uris[ii]);
+ e_attachment_store_add_attachment (store, attachment);
+ g_object_unref (attachment);
+ }
+
+ g_strfreev (uris);
+
+ gtk_drag_finish (priv->drag_context, TRUE, FALSE, priv->time);
+}
+
+static void
+drop_text_generic (EAttachmentView *view,
+ GtkSelectionData *selection_data,
+ EAttachmentStore *store,
+ GdkDragAction action)
+{
+ EAttachmentViewPrivate *priv;
+ EAttachment *attachment;
+ CamelMimePart *mime_part;
+ GdkAtom atom;
+ const gchar *data;
+ gchar *content_type;
+ gint length;
+
+ priv = e_attachment_view_get_private (view);
+
+ data = (const gchar *) gtk_selection_data_get_data (selection_data);
+ length = gtk_selection_data_get_length (selection_data);
+ atom = gtk_selection_data_get_data_type (selection_data);
+
+ mime_part = camel_mime_part_new ();
+
+ content_type = gdk_atom_name (atom);
+ camel_mime_part_set_content (mime_part, data, length, content_type);
+ camel_mime_part_set_disposition (mime_part, "inline");
+ g_free (content_type);
+
+ attachment = e_attachment_new ();
+ e_attachment_set_mime_part (attachment, mime_part);
+ e_attachment_store_add_attachment (store, attachment);
+ g_object_unref (attachment);
+
+ camel_object_unref (mime_part);
+
+ gtk_drag_finish (priv->drag_context, TRUE, FALSE, priv->time);
+}
+
+static void
+drop_x_uid_list (EAttachmentView *view,
+ GtkSelectionData *selection_data,
+ EAttachmentStore *store,
+ GdkDragAction action)
+{
+ EAttachmentViewPrivate *priv;
+
+ /* FIXME Ugh, this looks painful. Requires mailer stuff. */
+
+ priv = e_attachment_view_get_private (view);
+
+ gtk_drag_finish (priv->drag_context, FALSE, FALSE, priv->time);
+}
+
+static void
+attachment_view_class_init (EAttachmentViewIface *iface)
+{
+ gint ii;
+
+ for (ii = 0; ii < G_N_ELEMENTS (drag_info); ii++) {
+ const gchar *target = drag_info[ii].target;
+ drag_info[ii].atom = gdk_atom_intern (target, FALSE);
+ }
+}
+
+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);
+ }
+
+ return type;
+}
+
+void
+e_attachment_view_init (EAttachmentView *view)
+{
+ EAttachmentViewPrivate *priv;
+ GtkUIManager *ui_manager;
+ GtkActionGroup *action_group;
+ const gchar *domain = GETTEXT_PACKAGE;
+ GError *error = NULL;
+
+ priv = e_attachment_view_get_private (view);
+
+ gtk_drag_dest_set (
+ GTK_WIDGET (view), GTK_DEST_DEFAULT_ALL,
+ drop_types, G_N_ELEMENTS (drop_types),
+ GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK);
+
+ ui_manager = gtk_ui_manager_new ();
+ priv->merge_id = gtk_ui_manager_new_merge_id (ui_manager);
+ priv->ui_manager = ui_manager;
+
+ action_group = gtk_action_group_new ("standard");
+ gtk_action_group_set_translation_domain (action_group, domain);
+ gtk_action_group_add_actions (
+ action_group, standard_entries,
+ G_N_ELEMENTS (standard_entries), view);
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ priv->standard_actions = action_group;
+
+ action_group = gtk_action_group_new ("editable");
+ gtk_action_group_set_translation_domain (action_group, domain);
+ gtk_action_group_add_actions (
+ action_group, editable_entries,
+ G_N_ELEMENTS (editable_entries), view);
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ priv->editable_actions = action_group;
+
+ action_group = gtk_action_group_new ("openwith");
+ gtk_action_group_set_translation_domain (action_group, domain);
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ priv->openwith_actions = action_group;
+
+ /* 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);
+
+ e_plugin_ui_register_manager (ui_manager, "attachment-view", view);
+}
+
+void
+e_attachment_view_dispose (EAttachmentView *view)
+{
+ EAttachmentViewPrivate *priv;
+
+ priv = e_attachment_view_get_private (view);
+
+ if (priv->ui_manager != NULL) {
+ g_object_unref (priv->ui_manager);
+ priv->ui_manager = NULL;
+ }
+
+ if (priv->standard_actions != NULL) {
+ g_object_unref (priv->standard_actions);
+ priv->standard_actions = NULL;
+ }
+
+ if (priv->editable_actions != NULL) {
+ g_object_unref (priv->editable_actions);
+ priv->editable_actions = NULL;
+ }
+
+ if (priv->openwith_actions != NULL) {
+ g_object_unref (priv->openwith_actions);
+ priv->openwith_actions = NULL;
+ }
+
+ if (priv->drag_context != NULL) {
+ g_object_unref (priv->drag_context);
+ priv->drag_context = NULL;
+ }
+}
+
+void
+e_attachment_view_finalize (EAttachmentView *view)
+{
+ EAttachmentViewPrivate *priv;
+
+ priv = e_attachment_view_get_private (view);
+
+ if (priv->selection_data != NULL)
+ gtk_selection_data_free (priv->selection_data);
+}
+
+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);
+}
+
+GList *
+e_attachment_view_get_selected_attachments (EAttachmentView *view)
+{
+ EAttachmentStore *store;
+ GtkTreeModel *model;
+ GList *selected, *item;
+ gint column_id;
+
+ 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_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);
+}
+
+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_action (EAttachmentView *view,
+ GdkDragAction action)
+{
+ EAttachmentViewPrivate *priv;
+ GtkSelectionData *selection_data;
+ EAttachmentStore *store;
+ GdkAtom atom;
+ gchar *name;
+
+ g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+ priv = e_attachment_view_get_private (view);
+
+ selection_data = priv->selection_data;
+ store = e_attachment_view_get_store (view);
+ atom = gtk_selection_data_get_data_type (selection_data);
+
+ switch (priv->info) {
+ case DND_TYPE_MESSAGE_RFC822:
+ drop_message_rfc822 (
+ view, selection_data, store, action);
+ return;
+
+ case DND_TYPE_NETSCAPE_URL:
+ drop_netscape_url (
+ view, selection_data, store, action);
+ return;
+
+ case DND_TYPE_TEXT_URI_LIST:
+ drop_text_uri_list (
+ view, selection_data, store, action);
+ return;
+
+ case DND_TYPE_TEXT_VCARD:
+ case DND_TYPE_TEXT_CALENDAR:
+ drop_text_generic (
+ view, selection_data, store, action);
+ return;
+
+ case DND_TYPE_X_UID_LIST:
+ drop_x_uid_list (
+ view, selection_data, store, action);
+ return;
+
+ default:
+ name = gdk_atom_name (atom);
+ g_warning ("Unknown drag type: %s", name);
+ g_free (name);
+ break;
+ }
+
+ gtk_drag_finish (priv->drag_context, FALSE, FALSE, priv->time);
+}
+
+gboolean
+e_attachment_view_drag_motion (EAttachmentView *view,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ GList *iter;
+ GdkDragAction actions = 0;
+ GdkDragAction chosen_action;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
+
+ for (iter = context->targets; iter != NULL; iter = iter->next) {
+ GdkAtom atom = iter->data;
+ gint ii;
+
+ for (ii = 0; ii < G_N_ELEMENTS (drag_info); ii++)
+ if (atom == drag_info[ii].atom)
+ actions |= drag_info[ii].actions;
+ }
+
+ 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);
+}
+
+void
+e_attachment_view_drag_data_received (EAttachmentView *view,
+ GdkDragContext *drag_context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time)
+{
+ EAttachmentViewPrivate *priv;
+ GtkUIManager *ui_manager;
+ GdkDragAction action;
+
+ g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+ priv = e_attachment_view_get_private (view);
+ ui_manager = e_attachment_view_get_ui_manager (view);
+
+ action = drag_context->action;
+
+ if (gtk_selection_data_get_data (selection_data) == NULL)
+ return;
+
+ if (gtk_selection_data_get_length (selection_data) == -1)
+ return;
+
+ if (priv->drag_context != NULL)
+ g_object_unref (priv->drag_context);
+
+ if (priv->selection_data != NULL)
+ gtk_selection_data_free (priv->selection_data);
+
+ priv->drag_context = g_object_ref (drag_context);
+ priv->selection_data = gtk_selection_data_copy (selection_data);
+ priv->info = info;
+ priv->time = time;
+
+ if (action == GDK_ACTION_ASK) {
+ GtkWidget *menu;
+
+ menu = gtk_ui_manager_get_widget (ui_manager, "/dnd");
+ g_return_if_fail (GTK_IS_MENU (menu));
+
+ gtk_menu_popup (
+ GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, time);
+ } else
+ e_attachment_view_drag_action (view, action);
+}
+
+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_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);
+}
+
+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)
+{
+ GtkUIManager *ui_manager;
+ GtkWidget *menu;
+
+ g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+ if (event != NULL) {
+ GtkTreePath *path;
+
+ path = e_attachment_view_get_path_at_pos (
+ view, event->x, event->y);
+ if (path != NULL) {
+ if (!e_attachment_view_path_is_selected (view, path)) {
+ e_attachment_view_unselect_all (view);
+ e_attachment_view_select_path (view, path);
+ }
+ gtk_tree_path_free (path);
+ } else
+ e_attachment_view_unselect_all (view);
+ }
+
+ e_attachment_view_update_actions (view);
+
+ ui_manager = e_attachment_view_get_ui_manager (view);
+ menu = gtk_ui_manager_get_widget (ui_manager, "/context");
+ g_return_if_fail (GTK_IS_MENU (menu));
+
+ if (event != NULL)
+ gtk_menu_popup (
+ GTK_MENU (menu), NULL, NULL, NULL, NULL,
+ event->button, event->time);
+ else
+ gtk_menu_popup (
+ GTK_MENU (menu), NULL, NULL, NULL, NULL,
+ 0, gtk_get_current_event_time ());
+}
+
+void
+e_attachment_view_update_actions (EAttachmentView *view)
+{
+ EAttachmentViewPrivate *priv;
+ GFileInfo *file_info;
+ GtkAction *action;
+ GList *selected;
+ guint n_selected;
+ gboolean is_image;
+
+ g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+ priv = e_attachment_view_get_private (view);
+ selected = e_attachment_view_get_selected_attachments (view);
+ n_selected = g_list_length (selected);
+
+ is_image = FALSE;
+ file_info = NULL;
+
+ if (n_selected == 1) {
+ EAttachment *attachment = selected->data;
+ file_info = e_attachment_get_file_info (attachment);
+ is_image = e_attachment_is_image (attachment);
+ }
+
+ action = e_attachment_view_get_action (view, "properties");
+ gtk_action_set_visible (action, n_selected == 1);
+
+ action = e_attachment_view_get_action (view, "remove");
+ gtk_action_set_visible (action, n_selected > 0);
+
+ action = e_attachment_view_get_action (view, "save-as");
+ gtk_action_set_visible (action, n_selected > 0);
+
+ action = e_attachment_view_get_action (view, "set-background");
+ gtk_action_set_visible (action, is_image);
+}
diff --git a/widgets/misc/e-attachment-view.h b/widgets/misc/e-attachment-view.h
new file mode 100644
index 0000000000..a00308d2d5
--- /dev/null
+++ b/widgets/misc/e-attachment-view.h
@@ -0,0 +1,163 @@
+/*
+ * 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);
+};
+
+struct _EAttachmentViewPrivate {
+
+ /* Popup Menu Management */
+ GtkUIManager *ui_manager;
+ GtkActionGroup *standard_actions;
+ GtkActionGroup *editable_actions;
+ GtkActionGroup *openwith_actions;
+ guint merge_id;
+
+ /* Drag and Drop State */
+ GdkDragContext *drag_context;
+ GtkSelectionData *selection_data;
+ guint info;
+ guint time;
+};
+
+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);
+GList * e_attachment_view_get_selected_attachments
+ (EAttachmentView *view);
+void e_attachment_view_remove_selected
+ (EAttachmentView *view,
+ gboolean select_next);
+
+/* 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 and Drop Support */
+void e_attachment_view_drag_action (EAttachmentView *view,
+ GdkDragAction action);
+gboolean e_attachment_view_drag_motion (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_get_action_group
+ (EAttachmentView *view,
+ const gchar *group_name);
+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);
+void e_attachment_view_update_actions(EAttachmentView *view);
+
+G_END_DECLS
+
+#endif /* E_ATTACHMENT_VIEW_H */
diff --git a/widgets/misc/e-attachment.c b/widgets/misc/e-attachment.c
index 9bb6f09ade..6f9e80f02b 100644
--- a/widgets/misc/e-attachment.c
+++ b/widgets/misc/e-attachment.c
@@ -1,4 +1,5 @@
/*
+ * e-attachment.c
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -14,81 +15,275 @@
* License along with the program; if not, see <http://www.gnu.org/licenses/>
*
*
- * Authors:
- * Ettore Perazzoli <ettore@ximian.com>
- * Jeffrey Stedfast <fejj@ximian.com>
- * Srinivasa Ragavan <sragavan@novell.com>
- *
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
#include "e-attachment.h"
-#include "e-attachment-dialog.h"
-
-#ifdef G_OS_WIN32
-/* Include <windows.h> early (as the gio stuff below will
- * include it anyway, sigh) to workaround the DATADIR problem.
- * <windows.h> (and the headers it includes) stomps all over the
- * namespace like a baboon on crack, and especially the DATADIR enum
- * in objidl.h causes problems.
- */
-#undef DATADIR
-#define DATADIR crap_DATADIR
-#include <windows.h>
-#undef DATADIR
-#endif
-#include <sys/stat.h>
-#include <string.h>
#include <errno.h>
-
-#include <camel/camel.h>
-
#include <glib/gi18n.h>
-#include <glib/gstdio.h>
-
-#include <libebook/e-vcard.h>
+#include <camel/camel-iconv.h>
+#include <camel/camel-data-wrapper.h>
+#include <camel/camel-mime-message.h>
+#include <camel/camel-stream-filter.h>
+#include <camel/camel-stream-null.h>
+#include <camel/camel-stream-vfs.h>
#include "e-util/e-util.h"
-#include "e-util/e-error.h"
-#include "e-util/e-mktemp.h"
-#include "e-util/e-util-private.h"
#define E_ATTACHMENT_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_ATTACHMENT, EAttachmentPrivate))
+/* Attributes needed by EAttachmentStore, et al. */
+#define ATTACHMENT_QUERY "standard::*,preview::*,thumbnail::*"
+
struct _EAttachmentPrivate {
- gchar *filename;
- gchar *description;
+ GFile *file;
+ GFileInfo *file_info;
+ GCancellable *cancellable;
+ CamelMimePart *mime_part;
gchar *disposition;
- gchar *mime_type;
- GdkPixbuf *thumbnail;
- CamelMimePart *mime_part;
+ /* This is a reference to our row in an EAttachmentStore,
+ * serving as a means of broadcasting "row-changed" signals.
+ * If we are removed from the store, we lazily free the
+ * reference when it is found to be to be invalid. */
+ GtkTreeRowReference *reference;
};
enum {
PROP_0,
- PROP_DESCRIPTION,
PROP_DISPOSITION,
- PROP_FILENAME,
- PROP_THUMBNAIL
-};
-
-enum {
- CHANGED,
- UPDATE,
- LAST_SIGNAL
+ PROP_FILE,
+ PROP_FILE_INFO,
+ PROP_MIME_PART
};
static gpointer parent_class;
-static guint signals[LAST_SIGNAL];
+
+static void
+attachment_notify_model (EAttachment *attachment)
+{
+ GtkTreeRowReference *reference;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ reference = attachment->priv->reference;
+
+ if (reference == NULL)
+ return;
+
+ /* Free the reference if it's no longer valid.
+ * It means we've been removed from the store. */
+ if (!gtk_tree_row_reference_valid (reference)) {
+ gtk_tree_row_reference_free (reference);
+ attachment->priv->reference = NULL;
+ return;
+ }
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_row_changed (model, path, &iter);
+ g_object_notify (G_OBJECT (model), "total-size");
+
+ gtk_tree_path_free (path);
+}
+
+static gchar *
+attachment_get_default_charset (void)
+{
+ GConfClient *client;
+ const gchar *key;
+ gchar *charset;
+
+ /* XXX This function doesn't really belong here. */
+
+ client = gconf_client_get_default ();
+ key = "/apps/evolution/mail/composer/charset";
+ charset = gconf_client_get_string (client, key, NULL);
+ if (charset == NULL || *charset == '\0') {
+ g_free (charset);
+ key = "/apps/evolution/mail/format/charset";
+ charset = gconf_client_get_string (client, key, NULL);
+ if (charset == NULL || *charset == '\0') {
+ g_free (charset);
+ charset = NULL;
+ }
+ }
+ g_object_unref (client);
+
+ if (charset == NULL)
+ charset = g_strdup (camel_iconv_locale_charset ());
+
+ if (charset == NULL)
+ charset = g_strdup ("us-ascii");
+
+ return charset;
+}
+
+static void
+attachment_set_file_info (EAttachment *attachment,
+ GFileInfo *file_info)
+{
+ GCancellable *cancellable;
+
+ cancellable = attachment->priv->cancellable;
+
+ /* Cancel any query operations in progress. */
+ if (!g_cancellable_is_cancelled (cancellable)) {
+ g_cancellable_cancel (cancellable);
+ g_cancellable_reset (cancellable);
+ }
+
+ if (file_info != NULL)
+ g_object_ref (file_info);
+
+ if (attachment->priv->file_info != NULL)
+ g_object_unref (attachment->priv->file_info);
+
+ attachment->priv->file_info = file_info;
+
+ g_object_notify (G_OBJECT (attachment), "file-info");
+
+ attachment_notify_model (attachment);
+}
+
+static void
+attachment_reset (EAttachment *attachment)
+{
+ GCancellable *cancellable;
+
+ cancellable = attachment->priv->cancellable;
+
+ g_object_freeze_notify (G_OBJECT (attachment));
+
+ /* Cancel any query operations in progress. */
+ if (!g_cancellable_is_cancelled (cancellable)) {
+ g_cancellable_cancel (cancellable);
+ g_cancellable_reset (cancellable);
+ }
+
+ if (attachment->priv->file != NULL) {
+ g_object_notify (G_OBJECT (attachment), "file");
+ g_object_unref (attachment->priv->file);
+ attachment->priv->file = NULL;
+ }
+
+ if (attachment->priv->mime_part != NULL) {
+ g_object_notify (G_OBJECT (attachment), "mime-part");
+ g_object_unref (attachment->priv->mime_part);
+ attachment->priv->mime_part = NULL;
+ }
+
+ attachment_set_file_info (attachment, NULL);
+
+ g_object_thaw_notify (G_OBJECT (attachment));
+}
+
+static void
+attachment_file_info_ready_cb (GFile *file,
+ GAsyncResult *result,
+ EAttachment *attachment)
+{
+ GFileInfo *file_info;
+ GError *error = NULL;
+
+ /* Even if we failed to obtain a GFileInfo, we still emit a
+ * "notify::file-info" to signal the async operation finished. */
+ file_info = g_file_query_info_finish (file, result, &error);
+ attachment_set_file_info (attachment, file_info);
+
+ if (file_info != NULL)
+ g_object_unref (file_info);
+ else {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+attachment_file_info_to_mime_part (EAttachment *attachment,
+ CamelMimePart *mime_part)
+{
+ GFileInfo *file_info;
+ const gchar *attribute;
+ const gchar *string;
+ gchar *allocated;
+
+ file_info = e_attachment_get_file_info (attachment);
+
+ if (file_info == NULL || mime_part == NULL)
+ return;
+
+ /* XXX Note that we skip "standard::size" here. */
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE;
+ string = g_file_info_get_attribute_string (file_info, attribute);
+ allocated = g_content_type_get_mime_type (string);
+ camel_mime_part_set_content_type (mime_part, allocated);
+ g_free (allocated);
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME;
+ string = g_file_info_get_attribute_string (file_info, attribute);
+ camel_mime_part_set_filename (mime_part, string);
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
+ string = g_file_info_get_attribute_string (file_info, attribute);
+ camel_mime_part_set_description (mime_part, string);
+
+ string = e_attachment_get_disposition (attachment);
+ camel_mime_part_set_disposition (mime_part, string);
+}
+
+static void
+attachment_mime_part_to_file_info (EAttachment *attachment)
+{
+ CamelContentType *content_type;
+ CamelMimePart *mime_part;
+ GFileInfo *file_info;
+ const gchar *attribute;
+ const gchar *string;
+ gchar *allocated;
+ guint64 v_uint64;
+
+ file_info = e_attachment_get_file_info (attachment);
+ mime_part = e_attachment_get_mime_part (attachment);
+
+ if (file_info == NULL || mime_part == NULL)
+ return;
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE;
+ content_type = camel_mime_part_get_content_type (mime_part);
+ allocated = camel_content_type_simple (content_type);
+ if (allocated != NULL)
+ g_file_info_set_attribute_string (
+ file_info, attribute, allocated);
+ g_free (allocated);
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME;
+ string = camel_mime_part_get_filename (mime_part);
+ if (string != NULL)
+ g_file_info_set_attribute_string (
+ file_info, attribute, string);
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
+ string = camel_mime_part_get_description (mime_part);
+ if (string != NULL)
+ g_file_info_set_attribute_string (
+ file_info, attribute, string);
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_SIZE;
+ v_uint64 = camel_mime_part_get_content_size (mime_part);
+ g_file_info_set_attribute_uint64 (file_info, attribute, v_uint64);
+
+ string = camel_mime_part_get_disposition (mime_part);
+ e_attachment_set_disposition (attachment, string);
+}
static void
attachment_set_property (GObject *object,
@@ -97,28 +292,22 @@ attachment_set_property (GObject *object,
GParamSpec *pspec)
{
switch (property_id) {
- case PROP_DESCRIPTION:
- e_attachment_set_description (
- E_ATTACHMENT (object),
- g_value_get_string (value));
- return;
-
case PROP_DISPOSITION:
e_attachment_set_disposition (
E_ATTACHMENT (object),
g_value_get_string (value));
return;
- case PROP_FILENAME:
- e_attachment_set_filename (
+ case PROP_FILE:
+ e_attachment_set_file (
E_ATTACHMENT (object),
- g_value_get_string (value));
+ g_value_get_object (value));
return;
- case PROP_THUMBNAIL:
- e_attachment_set_thumbnail (
+ case PROP_MIME_PART:
+ e_attachment_set_mime_part (
E_ATTACHMENT (object),
- g_value_get_object (value));
+ g_value_get_boxed (value));
return;
}
@@ -132,27 +321,27 @@ attachment_get_property (GObject *object,
GParamSpec *pspec)
{
switch (property_id) {
- case PROP_DESCRIPTION:
- g_value_set_string (
- value, e_attachment_get_description (
- E_ATTACHMENT (object)));
- return;
-
case PROP_DISPOSITION:
g_value_set_string (
value, e_attachment_get_disposition (
E_ATTACHMENT (object)));
return;
- case PROP_FILENAME:
- g_value_set_string (
- value, e_attachment_get_filename (
+ case PROP_FILE:
+ g_value_set_object (
+ value, e_attachment_get_file (
E_ATTACHMENT (object)));
return;
- case PROP_THUMBNAIL:
+ case PROP_FILE_INFO:
g_value_set_object (
- value, e_attachment_get_thumbnail (
+ value, e_attachment_get_file_info (
+ E_ATTACHMENT (object)));
+ return;
+
+ case PROP_MIME_PART:
+ g_value_set_boxed (
+ value, e_attachment_get_mime_part (
E_ATTACHMENT (object)));
return;
}
@@ -167,9 +356,20 @@ attachment_dispose (GObject *object)
priv = E_ATTACHMENT_GET_PRIVATE (object);
- if (priv->thumbnail != NULL) {
- g_object_unref (priv->thumbnail);
- priv->thumbnail = NULL;
+ if (priv->cancellable != NULL) {
+ g_cancellable_cancel (priv->cancellable);
+ g_object_unref (priv->cancellable);
+ priv->cancellable = NULL;
+ }
+
+ if (priv->file != NULL) {
+ g_object_unref (priv->file);
+ priv->file = NULL;
+ }
+
+ if (priv->file_info != NULL) {
+ g_object_unref (priv->file_info);
+ priv->file_info = NULL;
}
if (priv->mime_part != NULL) {
@@ -177,6 +377,10 @@ attachment_dispose (GObject *object)
priv->mime_part = NULL;
}
+ /* This accepts NULL arguments. */
+ gtk_tree_row_reference_free (priv->reference);
+ priv->reference = NULL;
+
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (parent_class)->dispose (object);
}
@@ -184,20 +388,11 @@ attachment_dispose (GObject *object)
static void
attachment_finalize (GObject *object)
{
- EAttachment *attachment = (EAttachment *) object;
-
- if (attachment->cancellable) {
- /* the operation is still running, so cancel it */
- g_cancellable_cancel (attachment->cancellable);
- attachment->cancellable = NULL;
- }
+ EAttachmentPrivate *priv;
- g_free (attachment->store_uri);
+ priv = E_ATTACHMENT_GET_PRIVATE (object);
- g_free (attachment->priv->filename);
- g_free (attachment->priv->description);
- g_free (attachment->priv->disposition);
- g_free (attachment->priv->mime_type);
+ g_free (priv->disposition);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (parent_class)->finalize (object);
@@ -219,75 +414,52 @@ attachment_class_init (EAttachmentClass *class)
g_object_class_install_property (
object_class,
- PROP_DESCRIPTION,
+ PROP_DISPOSITION,
g_param_spec_string (
- "description",
- "Description",
- NULL,
+ "disposition",
+ "Disposition",
NULL,
+ "attachment",
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (
object_class,
- PROP_DESCRIPTION,
- g_param_spec_string (
- "disposition",
- "Disposition",
- NULL,
+ PROP_FILE,
+ g_param_spec_object (
+ "file",
+ "File",
NULL,
+ G_TYPE_FILE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (
object_class,
- PROP_DESCRIPTION,
- g_param_spec_string (
- "filename",
- "Filename",
- NULL,
+ PROP_FILE_INFO,
+ g_param_spec_object (
+ "file-info",
+ "File Info",
NULL,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT));
+ G_TYPE_FILE_INFO,
+ G_PARAM_READABLE));
g_object_class_install_property (
object_class,
- PROP_THUMBNAIL,
- g_param_spec_object (
- "thumbnail",
- "Thumbnail Image",
+ PROP_MIME_PART,
+ g_param_spec_boxed (
+ "mime-part",
+ "MIME Part",
NULL,
- GDK_TYPE_PIXBUF,
+ E_TYPE_CAMEL_OBJECT,
G_PARAM_READWRITE));
-
- signals[CHANGED] = g_signal_new (
- "changed",
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (EAttachmentClass, changed),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-
- signals[UPDATE] = g_signal_new (
- "update",
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (EAttachmentClass, update),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
}
static void
attachment_init (EAttachment *attachment)
{
attachment->priv = E_ATTACHMENT_GET_PRIVATE (attachment);
-
- attachment->index = -1;
- attachment->percentage = -1;
- attachment->sign = CAMEL_CIPHER_VALIDITY_SIGN_NONE;
- attachment->encrypt = CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE;
+ attachment->priv->cancellable = g_cancellable_new ();
}
GType
@@ -316,711 +488,845 @@ e_attachment_get_type (void)
return type;
}
-/**
- * file_ext_is:
- * @param filename: path for file
- * @param ext: desired extension, with a dot
- * @return if filename has extension ext or not
- **/
-
-static gboolean
-file_ext_is (const char *filename, const char *ext)
+EAttachment *
+e_attachment_new (void)
{
- int i, dot = -1;
+ return g_object_new (E_TYPE_ATTACHMENT, NULL);
+}
- if (!filename || !ext)
- return FALSE;
+EAttachment *
+e_attachment_new_for_path (const gchar *path)
+{
+ EAttachment *attachment;
+ GFile *file;
- for (i = 0; filename[i]; i++) {
- if (filename [i] == '.')
- dot = i;
- }
+ g_return_val_if_fail (path != NULL, NULL);
- if (dot > 0) {
- return 0 == g_ascii_strcasecmp (filename + dot, ext);
- }
+ file = g_file_new_for_path (path);
+ attachment = g_object_new (E_TYPE_ATTACHMENT, "file", file, NULL);
+ g_object_unref (file);
- return FALSE;
+ return attachment;
}
-static char *
-attachment_guess_mime_type (const char *filename)
+EAttachment *
+e_attachment_new_for_uri (const gchar *uri)
{
- char *type;
- gchar *content = NULL;
+ EAttachment *attachment;
+ GFile *file;
- type = e_util_guess_mime_type (filename, TRUE);
+ g_return_val_if_fail (uri != NULL, NULL);
- if (type && strcmp (type, "text/directory") == 0 &&
- file_ext_is (filename, ".vcf") &&
- g_file_get_contents (filename, &content, NULL, NULL) &&
- content) {
- EVCard *vc = e_vcard_new_from_string (content);
+ file = g_file_new_for_uri (uri);
+ attachment = g_object_new (E_TYPE_ATTACHMENT, "file", file, NULL);
+ g_object_unref (file);
- if (vc) {
- g_free (type);
- g_object_unref (G_OBJECT (vc));
+ return attachment;
+}
- type = g_strdup ("text/x-vcard");
- }
+EAttachment *
+e_attachment_new_for_message (CamelMimeMessage *message)
+{
+ CamelDataWrapper *wrapper;
+ CamelMimePart *mime_part;
+ EAttachment *attachment;
+ GString *description;
+ const gchar *subject;
- }
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
- g_free (content);
+ mime_part = camel_mime_part_new ();
+ camel_mime_part_set_disposition (mime_part, "inline");
+ subject = camel_mime_message_get_subject (message);
- if (type) {
- /* Check if returned mime_type is valid */
- CamelContentType *ctype = camel_content_type_decode (type);
+ description = g_string_new (_("Attached message"));
+ if (subject != NULL)
+ g_string_append_printf (description, " - %s", subject);
+ camel_mime_part_set_description (mime_part, description->str);
+ g_string_free (description, TRUE);
- if (!ctype) {
- g_free (type);
- type = NULL;
- } else
- camel_content_type_unref (ctype);
- }
+ wrapper = CAMEL_DATA_WRAPPER (message);
+ camel_medium_set_content_object (CAMEL_MEDIUM (mime_part), wrapper);
+ camel_mime_part_set_content_type (mime_part, "message/rfc822");
- return type;
+ attachment = e_attachment_new ();
+ e_attachment_set_mime_part (attachment, mime_part);
+ camel_object_unref (mime_part);
+
+ return attachment;
}
-
-/**
- * e_attachment_new:
- * @filename: filename to attach
- * @disposition: Content-Disposition of the attachment
- * @ex: exception
- *
- * Return value: the new attachment, or %NULL on error
- **/
-EAttachment *
-e_attachment_new (const char *filename, const char *disposition, CamelException *ex)
+void
+e_attachment_add_to_multipart (EAttachment *attachment,
+ CamelMultipart *multipart,
+ const gchar *default_charset)
{
- EAttachment *new;
- CamelMimePart *part;
+ CamelContentType *content_type;
CamelDataWrapper *wrapper;
- CamelStream *stream;
- struct stat statbuf;
- gchar *mime_type;
- gchar *basename;
- CamelURL *url;
+ CamelMimePart *mime_part;
- g_return_val_if_fail (filename != NULL, NULL);
+ /* XXX EMsgComposer might be a better place for this function. */
- if (g_stat (filename, &statbuf) < 0) {
- camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
- _("Cannot attach file %s: %s"),
- filename, g_strerror (errno));
- return NULL;
- }
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
- /* return if it's not a regular file */
- if (!S_ISREG (statbuf.st_mode)) {
- camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
- _("Cannot attach file %s: not a regular file"),
- filename);
- return NULL;
- }
+ /* Still loading? Too bad. */
+ mime_part = e_attachment_get_mime_part (attachment);
+ if (mime_part == NULL)
+ return;
- if (!(stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0))) {
- camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
- _("Cannot attach file %s: %s"),
- filename, g_strerror (errno));
- return NULL;
- }
+ content_type = camel_mime_part_get_content_type (mime_part);
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+
+ if (CAMEL_IS_MULTIPART (wrapper))
+ goto exit;
- if ((mime_type = attachment_guess_mime_type (filename))) {
- if (!g_ascii_strcasecmp (mime_type, "message/rfc822")) {
- wrapper = (CamelDataWrapper *) camel_mime_message_new ();
- } else {
- wrapper = camel_data_wrapper_new ();
+ /* For text content, determine the best encoding and character set. */
+ if (camel_content_type_is (content_type, "text", "*")) {
+ CamelTransferEncoding encoding;
+ CamelStreamFilter *filtered_stream;
+ CamelMimeFilterBestenc *filter;
+ CamelStream *stream;
+ const gchar *charset;
+
+ charset = camel_content_type_param (content_type, "charset");
+
+ /* Determine the best encoding by writing the MIME
+ * part to a NULL stream with a "bestenc" filter. */
+ stream = camel_stream_null_new ();
+ filtered_stream = camel_stream_filter_new_with_stream (stream);
+ filter = camel_mime_filter_bestenc_new (
+ CAMEL_BESTENC_GET_ENCODING);
+ camel_stream_filter_add (
+ filtered_stream, CAMEL_MIME_FILTER (filter));
+ camel_data_wrapper_decode_to_stream (
+ wrapper, CAMEL_STREAM (filtered_stream));
+ camel_object_unref (filtered_stream);
+ camel_object_unref (stream);
+
+ /* Retrieve the best encoding from the filter. */
+ encoding = camel_mime_filter_bestenc_get_best_encoding (
+ filter, CAMEL_BESTENC_8BIT);
+ camel_mime_part_set_encoding (mime_part, encoding);
+ camel_object_unref (filter);
+
+ if (encoding == CAMEL_TRANSFER_ENCODING_7BIT) {
+ /* The text fits within us-ascii, so this is safe.
+ * FIXME Check that this isn't iso-2022-jp? */
+ default_charset = "us-ascii";
+
+ } else if (charset == NULL && default_charset == NULL) {
+ default_charset = attachment_get_default_charset ();
+ /* FIXME Check that this fits within the
+ * default_charset and if not, find one
+ * that does and/or allow the user to
+ * specify. */
}
- camel_data_wrapper_construct_from_stream (wrapper, stream);
- camel_data_wrapper_set_mime_type (wrapper, mime_type);
- g_free (mime_type);
- } else {
- wrapper = camel_data_wrapper_new ();
- camel_data_wrapper_construct_from_stream (wrapper, stream);
- camel_data_wrapper_set_mime_type (wrapper, "application/octet-stream");
- }
+ if (charset == NULL) {
+ gchar *type;
- camel_object_unref (stream);
+ camel_content_type_set_param (
+ content_type, "charset", default_charset);
+ type = camel_content_type_format (content_type);
+ camel_mime_part_set_content_type (mime_part, type);
+ g_free (type);
+ }
- part = camel_mime_part_new ();
- camel_medium_set_content_object (CAMEL_MEDIUM (part), wrapper);
- camel_object_unref (wrapper);
+ /* Otherwise, unless it's a message/rfc822, Base64 encode it. */
+ } else if (!CAMEL_IS_MIME_MESSAGE (wrapper))
+ camel_mime_part_set_encoding (
+ mime_part, CAMEL_TRANSFER_ENCODING_BASE64);
- camel_mime_part_set_disposition (part, disposition);
- basename = g_path_get_basename (filename);
- camel_mime_part_set_filename (part, basename);
+exit:
+ camel_multipart_add_part (multipart, mime_part);
+}
-#if 0
- /* Note: Outlook 2002 is broken with respect to Content-Ids on
- non-multipart/related parts, so as an interoperability
- workaround, don't set a Content-Id on these parts. Fixes
- bug #10032 */
- /* set the Content-Id */
- content_id = camel_header_msgid_generate ();
- camel_mime_part_set_content_id (part, content_id);
- g_free (content_id);
-#endif
+const gchar *
+e_attachment_get_disposition (EAttachment *attachment)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- new = g_object_new (E_TYPE_ATTACHMENT, "filename", basename, NULL);
- new->priv->mime_part = part;
- new->size = statbuf.st_size;
- new->guessed_type = TRUE;
- new->cancellable = NULL;
- new->is_available_local = TRUE;
+ return attachment->priv->disposition;
+}
+
+void
+e_attachment_set_disposition (EAttachment *attachment,
+ const gchar *disposition)
+{
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
- url = camel_url_new ("file://", NULL);
- camel_url_set_path (url, filename);
- new->store_uri = camel_url_to_string (url, 0);
- camel_url_free (url);
+ g_free (attachment->priv->disposition);
+ attachment->priv->disposition = g_strdup (disposition);
- return new;
+ g_object_notify (G_OBJECT (attachment), "disposition");
}
+GFile *
+e_attachment_get_file (EAttachment *attachment)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
-typedef struct {
- EAttachment *attachment;
- char *filename;
- char *uri;
- GtkWindow *parent; /* for error dialog */
-
- guint64 file_size; /* zero indicates unknown size */
- GInputStream *istream; /* read from here ... */
- GOutputStream *ostream; /* ...and write into this. */
- gboolean was_error;
+ return attachment->priv->file;
+}
+
+void
+e_attachment_set_file (EAttachment *attachment,
+ GFile *file)
+{
GCancellable *cancellable;
- void *buffer; /* read into this, not more than buffer_size bytes */
- gsize buffer_size;
-} DownloadInfo;
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
-static void
-download_info_free (DownloadInfo *download_info)
-{
- /* if there was an error, then free attachment too */
- if (download_info->was_error)
- g_object_unref (download_info->attachment);
+ g_object_freeze_notify (G_OBJECT (attachment));
+
+ if (file != NULL) {
+ g_return_if_fail (G_IS_FILE (file));
+ g_object_ref (file);
+ }
+
+ attachment_reset (attachment);
+ attachment->priv->file = file;
- if (download_info->ostream)
- g_object_unref (download_info->ostream);
+ cancellable = attachment->priv->cancellable;
- if (download_info->istream)
- g_object_unref (download_info->istream);
+ if (file != NULL)
+ g_file_query_info_async (
+ file, ATTACHMENT_QUERY,
+ G_FILE_QUERY_INFO_NONE,
+ G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback)
+ attachment_file_info_ready_cb,
+ attachment);
- if (download_info->cancellable)
- g_object_unref (download_info->cancellable);
+ g_object_notify (G_OBJECT (attachment), "file");
- g_free (download_info->filename);
- g_free (download_info->uri);
- g_free (download_info->buffer);
- g_free (download_info);
+ g_object_thaw_notify (G_OBJECT (attachment));
}
-static void
-data_ready_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+GFileInfo *
+e_attachment_get_file_info (EAttachment *attachment)
{
- DownloadInfo *download_info = (DownloadInfo *)user_data;
- GError *error = NULL;
- gssize read;
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- g_return_if_fail (download_info != NULL);
+ return attachment->priv->file_info;
+}
- if (g_cancellable_is_cancelled (download_info->cancellable)) {
- /* finish the operation and close both streams */
- g_input_stream_read_finish (G_INPUT_STREAM (source_object), res, NULL);
+CamelMimePart *
+e_attachment_get_mime_part (EAttachment *attachment)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- g_output_stream_close (download_info->ostream, NULL, NULL);
- g_input_stream_close (download_info->istream, NULL, NULL);
+ return attachment->priv->mime_part;
+}
- /* The only way how to get this canceled is in EAttachment's finalize method,
- and because the download_info_free free's the attachment on error,
- then do not consider cancellation as an error. */
- download_info_free (download_info);
- return;
+void
+e_attachment_set_mime_part (EAttachment *attachment,
+ CamelMimePart *mime_part)
+{
+ GFileInfo *file_info;
+
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+ g_object_freeze_notify (G_OBJECT (attachment));
+
+ if (mime_part != NULL) {
+ g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
+ camel_object_ref (mime_part);
}
- read = g_input_stream_read_finish (G_INPUT_STREAM (source_object), res, &error);
+ attachment_reset (attachment);
+ attachment->priv->mime_part = mime_part;
- if (!error)
- g_output_stream_write_all (download_info->ostream, download_info->buffer, read, NULL, download_info->cancellable, &error);
+ file_info = g_file_info_new ();
+ attachment_set_file_info (attachment, file_info);
+ attachment_mime_part_to_file_info (attachment);
+ g_object_unref (file_info);
- if (error) {
- download_info->was_error = error->domain != G_IO_ERROR || error->code != G_IO_ERROR_CANCELLED;
- if (download_info->was_error)
- e_error_run (download_info->parent, "mail-composer:no-attach", download_info->uri, error->message, NULL);
+ g_object_notify (G_OBJECT (attachment), "mime-part");
- g_error_free (error);
+ g_object_thaw_notify (G_OBJECT (attachment));
+}
- download_info->attachment->cancellable = NULL;
- download_info_free (download_info);
- return;
- }
+const gchar *
+e_attachment_get_content_type (EAttachment *attachment)
+{
+ GFileInfo *file_info;
+ const gchar *attribute;
- if (read == 0) {
- CamelException ex;
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- /* done with reading */
- g_output_stream_close (download_info->ostream, NULL, NULL);
- g_input_stream_close (download_info->istream, NULL, NULL);
+ attribute = G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE;
+ file_info = e_attachment_get_file_info (attachment);
- download_info->attachment->cancellable = NULL;
+ if (file_info == NULL)
+ return NULL;
- camel_exception_init (&ex);
- e_attachment_build_remote_file (download_info->filename, download_info->attachment, &ex);
+ return g_file_info_get_attribute_string (file_info, attribute);
+}
- if (camel_exception_is_set (&ex)) {
- download_info->was_error = TRUE;
- e_error_run (download_info->parent, "mail-composer:no-attach", download_info->uri, camel_exception_get_description (&ex), NULL);
- camel_exception_clear (&ex);
- }
+const gchar *
+e_attachment_get_display_name (EAttachment *attachment)
+{
+ GFileInfo *file_info;
+ const gchar *attribute;
- download_info->attachment->percentage = -1;
- download_info->attachment->is_available_local = TRUE;
- g_signal_emit (download_info->attachment, signals[UPDATE], 0);
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- download_info_free (download_info);
- return;
- } else if (download_info->file_size) {
- download_info->attachment->percentage = read * 100 / download_info->file_size;
- download_info->file_size -= MIN (download_info->file_size, read);
- g_signal_emit (download_info->attachment, signals[UPDATE], 0);
- } else {
- download_info->attachment->percentage = 0;
- g_signal_emit (download_info->attachment, signals[UPDATE], 0);
- }
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME;
+ file_info = e_attachment_get_file_info (attachment);
- /* read next chunk */
- g_input_stream_read_async (download_info->istream, download_info->buffer, download_info->buffer_size, G_PRIORITY_DEFAULT, download_info->cancellable, data_ready_cb, download_info);
+ if (file_info == NULL)
+ return NULL;
+
+ return g_file_info_get_attribute_string (file_info, attribute);
}
-static gboolean
-download_to_local_path (DownloadInfo *download_info, CamelException *ex)
+const gchar *
+e_attachment_get_description (EAttachment *attachment)
{
- GError *error = NULL;
- GFile *src = g_file_new_for_uri (download_info->uri);
- GFile *des = g_file_new_for_path (download_info->filename);
- gboolean res = FALSE;
+ GFileInfo *file_info;
+ const gchar *attribute;
- g_return_val_if_fail (src != NULL && des != NULL, FALSE);
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- download_info->ostream = G_OUTPUT_STREAM (g_file_replace (des, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error));
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
+ file_info = e_attachment_get_file_info (attachment);
- if (download_info->ostream && !error) {
- GFileInfo *fi;
+ if (file_info == NULL)
+ return NULL;
- fi = g_file_query_info (src, G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, NULL, NULL);
+ return g_file_info_get_attribute_string (file_info, attribute);
+}
- if (fi) {
- download_info->file_size = g_file_info_get_attribute_uint64 (fi, G_FILE_ATTRIBUTE_STANDARD_SIZE);
- g_object_unref (fi);
- } else {
- download_info->file_size = 0;
- }
+GIcon *
+e_attachment_get_icon (EAttachment *attachment)
+{
+ GFileInfo *file_info;
+ const gchar *attribute;
- download_info->istream = G_INPUT_STREAM (g_file_read (src, NULL, &error));
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- if (download_info->istream && !error) {
- download_info->cancellable = g_cancellable_new ();
- download_info->attachment->cancellable = download_info->cancellable;
- download_info->buffer_size = 10240; /* max 10KB chunk */
- download_info->buffer = g_malloc (sizeof (char) * download_info->buffer_size);
+ attribute = G_FILE_ATTRIBUTE_STANDARD_ICON;
+ file_info = e_attachment_get_file_info (attachment);
- g_input_stream_read_async (download_info->istream, download_info->buffer, download_info->buffer_size, G_PRIORITY_DEFAULT, download_info->cancellable, data_ready_cb, download_info);
+ if (file_info == NULL)
+ return NULL;
- res = TRUE;
- }
- }
+ return (GIcon *)
+ g_file_info_get_attribute_object (file_info, attribute);
+}
+
+const gchar *
+e_attachment_get_thumbnail_path (EAttachment *attachment)
+{
+ GFileInfo *file_info;
+ const gchar *attribute;
- if (error) {
- /* propagate error */
- if (ex)
- camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, error->message);
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- g_error_free (error);
- download_info->was_error = TRUE;
- download_info_free (download_info);
- }
+ attribute = G_FILE_ATTRIBUTE_THUMBNAIL_PATH;
+ file_info = e_attachment_get_file_info (attachment);
- g_object_unref (src);
- g_object_unref (des);
+ if (file_info == NULL)
+ return NULL;
- return res;
+ return g_file_info_get_attribute_byte_string (file_info, attribute);
}
-EAttachment *
-e_attachment_new_remote_file (GtkWindow *error_dlg_parent, const char *uri, const char *disposition, const char *path, CamelException *ex)
+guint64
+e_attachment_get_size (EAttachment *attachment)
{
- EAttachment *new;
- DownloadInfo *download_info;
- CamelURL *url;
- char *base;
- gchar *filename;
+ GFileInfo *file_info;
+ const gchar *attribute;
- g_return_val_if_fail (uri != NULL, NULL);
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), 0);
- url = camel_url_new (uri, NULL);
- base = g_path_get_basename (url->path);
- camel_url_free (url);
+ attribute = G_FILE_ATTRIBUTE_STANDARD_SIZE;
+ file_info = e_attachment_get_file_info (attachment);
- filename = g_build_filename (path, base, NULL);
+ if (file_info == NULL)
+ return 0;
- new = g_object_new (E_TYPE_ATTACHMENT, "filename", filename, NULL);
- new->size = 0;
- new->guessed_type = FALSE;
- new->cancellable = NULL;
- new->is_available_local = FALSE;
- new->percentage = 0;
+ return g_file_info_get_attribute_uint64 (file_info, attribute);
+}
- g_free (base);
+gboolean
+e_attachment_is_image (EAttachment *attachment)
+{
+ const gchar *content_type;
- download_info = g_new0 (DownloadInfo, 1);
- download_info->attachment = new;
- download_info->filename = g_strdup (filename);
- download_info->uri = g_strdup (uri);
- download_info->parent = error_dlg_parent;
- download_info->was_error = FALSE;
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
- g_free (filename);
+ content_type = e_attachment_get_content_type (attachment);
- /* it frees all on the error, so do not free it twice */
- if (!download_to_local_path (download_info, ex))
- return NULL;
+ if (content_type == NULL)
+ return FALSE;
- return new;
+ return g_content_type_is_a (content_type, "image");
}
-
-void
-e_attachment_build_remote_file (const gchar *filename,
- EAttachment *attachment,
- CamelException *ex)
+gboolean
+e_attachment_is_rfc822 (EAttachment *attachment)
{
- CamelMimePart *part;
- CamelDataWrapper *wrapper;
- CamelStream *stream;
- struct stat statbuf;
- const gchar *description;
- const gchar *disposition;
- gchar *mime_type;
- gchar *basename;
- CamelURL *url;
+ const gchar *content_type;
- g_return_if_fail (filename != NULL);
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
- if (g_stat (filename, &statbuf) == -1) {
- camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
- _("Cannot attach file %s: %s"),
- filename, g_strerror (errno));
- g_message ("Cannot attach file %s: %s\n", filename, g_strerror (errno));
- return;
- }
+ content_type = e_attachment_get_content_type (attachment);
- /* return if it's not a regular file */
- if (!S_ISREG (statbuf.st_mode)) {
- camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
- _("Cannot attach file %s: not a regular file"),
- filename);
- g_message ("Cannot attach file %s: not a regular file", filename);
- return;
- }
+ if (content_type == NULL)
+ return FALSE;
- if (!(stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0))) {
- camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
- _("Cannot attach file %s: %s"),
- filename, g_strerror (errno));
- return;
- }
+ return g_content_type_equals (content_type, "message/rfc822");
+}
- if ((mime_type = attachment_guess_mime_type (filename))) {
- if (!g_ascii_strcasecmp (mime_type, "message/rfc822")) {
- wrapper = (CamelDataWrapper *) camel_mime_message_new ();
- } else {
- wrapper = camel_data_wrapper_new ();
- }
+static void
+attachment_save_file_cb (GFile *source,
+ GAsyncResult *result,
+ EActivity *activity)
+{
+ GError *error = NULL;
- camel_data_wrapper_construct_from_stream (wrapper, stream);
- camel_data_wrapper_set_mime_type (wrapper, mime_type);
- g_free (mime_type);
- } else {
- wrapper = camel_data_wrapper_new ();
- camel_data_wrapper_construct_from_stream (wrapper, stream);
- camel_data_wrapper_set_mime_type (wrapper, "application/octet-stream");
+ if (!g_file_copy_finish (source, result, &error)) {
+ e_activity_set_error (activity, error);
+ g_error_free (error);
}
- camel_object_unref (stream);
+ e_activity_complete (activity);
+ g_object_unref (activity);
+}
- part = camel_mime_part_new ();
- camel_medium_set_content_object (CAMEL_MEDIUM (part), wrapper);
- camel_object_unref (wrapper);
+static gpointer
+attachment_save_part_thread (EActivity *activity)
+{
+ GObject *object;
+ EAttachment *attachment;
+ GCancellable *cancellable;
+ GOutputStream *output_stream;
+ EFileActivity *file_activity;
+ CamelDataWrapper *wrapper;
+ CamelMimePart *mime_part;
+ CamelStream *stream;
+ GError *error = NULL;
- disposition = e_attachment_get_disposition (attachment);
- camel_mime_part_set_disposition (part, disposition);
+ object = G_OBJECT (activity);
+ attachment = g_object_get_data (object, "attachment");
+ output_stream = g_object_get_data (object, "output-stream");
- if (e_attachment_get_filename (attachment) == NULL)
- basename = g_path_get_basename (filename);
- else
- basename = g_path_get_basename (e_attachment_get_filename (attachment));
+ /* Last chance to cancel. */
+ file_activity = E_FILE_ACTIVITY (activity);
+ cancellable = e_file_activity_get_cancellable (file_activity);
+ if (g_cancellable_set_error_if_cancelled (cancellable, &error))
+ goto exit;
- camel_mime_part_set_filename (part, filename);
+ object = g_object_ref (output_stream);
+ stream = camel_stream_vfs_new_with_stream (object);
+ mime_part = e_attachment_get_mime_part (attachment);
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
- description = e_attachment_get_description (attachment);
- if (description != NULL) {
- camel_mime_part_set_description (part, description);
- e_attachment_set_description (attachment, NULL);
- }
+ if (camel_data_wrapper_decode_to_stream (wrapper, stream) < 0)
+ g_set_error (
+ &error, G_IO_ERROR,
+ g_io_error_from_errno (errno),
+ g_strerror (errno));
- attachment->priv->mime_part = part;
- attachment->size = statbuf.st_size;
- attachment->guessed_type = TRUE;
+ else if (camel_stream_flush (stream) < 0)
+ g_set_error (
+ &error, G_IO_ERROR,
+ g_io_error_from_errno (errno),
+ g_strerror (errno));
- e_attachment_set_filename (attachment, basename);
+ camel_object_unref (stream);
- url = camel_url_new ("file://", NULL);
- camel_url_set_path (url, filename);
- attachment->store_uri = camel_url_to_string (url, 0);
- camel_url_free (url);
+exit:
+ if (error != NULL) {
+ e_activity_set_error (activity, error);
+ g_error_free (error);
+ }
- g_free (basename);
-}
+ e_activity_complete_in_idle (activity);
+ g_object_unref (activity);
+ return NULL;
+}
-/**
- * e_attachment_new_from_mime_part:
- * @part: a CamelMimePart
- *
- * Return value: a new EAttachment based on the mime part
- **/
-EAttachment *
-e_attachment_new_from_mime_part (CamelMimePart *part)
+static void
+attachment_save_part_cb (GFile *destination,
+ GAsyncResult *result,
+ EActivity *activity)
{
- EAttachment *new;
- const gchar *filename;
+ GFileOutputStream *output_stream;
+ GError *error = NULL;
- g_return_val_if_fail (CAMEL_IS_MIME_PART (part), NULL);
+ output_stream = g_file_replace_finish (destination, result, &error);
- filename = camel_mime_part_get_filename (part);
+ if (output_stream != NULL) {
+ g_object_set_data_full (
+ G_OBJECT (activity),
+ "output-stream", output_stream,
+ (GDestroyNotify) g_object_unref);
+ g_thread_create (
+ (GThreadFunc) attachment_save_part_thread,
+ activity, FALSE, &error);
+ }
- new = g_object_new (E_TYPE_ATTACHMENT, "filename", filename, NULL);
- camel_object_ref (part);
- new->priv->mime_part = part;
- new->guessed_type = FALSE;
- new->is_available_local = TRUE;
- new->size = camel_mime_part_get_content_size (part);
+ if (error != NULL) {
+ e_activity_set_error (activity, error);
+ e_activity_complete (activity);
+ g_object_unref (activity);
+ g_error_free (error);
+ }
- return new;
}
void
-e_attachment_edit (EAttachment *attachment,
- GtkWindow *parent)
+e_attachment_save_async (EAttachment *attachment,
+ EFileActivity *file_activity,
+ GFile *destination)
{
- GtkWidget *dialog;
+ GFileProgressCallback progress_callback;
+ GCancellable *cancellable;
+ CamelMimePart *mime_part;
+ GFile *source;
g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ g_return_if_fail (G_IS_FILE (destination));
+ g_return_if_fail (E_IS_FILE_ACTIVITY (file_activity));
+
+ /* The attachment content is either a GFile (on disk) or a
+ * CamelMimePart (in memory). Each is saved differently. */
- dialog = e_attachment_dialog_new (parent, attachment);
- gtk_dialog_run (GTK_DIALOG (dialog));
- gtk_widget_destroy (dialog);
+ source = e_attachment_get_file (attachment);
+ mime_part = e_attachment_get_mime_part (attachment);
+ g_return_if_fail (source != NULL || mime_part != NULL);
+
+ cancellable = e_file_activity_get_cancellable (file_activity);
+ progress_callback = e_file_activity_progress;
+
+ /* GFile is the easier, but probably less common case. The
+ * attachment already references an on-disk file, so we can
+ * just use GIO to copy it asynchronously.
+ *
+ * We use G_FILE_COPY_OVERWRITE because the user should have
+ * already confirmed the overwrite through the save dialog. */
+ if (G_IS_FILE (source))
+ g_file_copy_async (
+ source, destination,
+ G_FILE_COPY_OVERWRITE,
+ G_PRIORITY_DEFAULT, cancellable,
+ progress_callback, file_activity,
+ (GAsyncReadyCallback) attachment_save_file_cb,
+ g_object_ref (file_activity));
+
+ /* CamelMimePart can only be decoded to a file synchronously, so
+ * we do this in two stages. Stage one asynchronously opens the
+ * destination file for writing. Stage two spawns a thread that
+ * decodes the MIME part to the destination file. This stage is
+ * not cancellable, unfortunately. */
+ else if (CAMEL_IS_MIME_PART (mime_part)) {
+ g_object_set_data_full (
+ G_OBJECT (file_activity),
+ "attachment", g_object_ref (attachment),
+ (GDestroyNotify) g_object_unref);
+ g_file_replace_async (
+ destination, NULL, FALSE,
+ G_FILE_CREATE_REPLACE_DESTINATION,
+ G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) attachment_save_part_cb,
+ g_object_ref (file_activity));
+ }
}
-const gchar *
-e_attachment_get_description (EAttachment *attachment)
+#if 0
+typedef struct {
+ gint io_priority;
+ GCancellable *cancellable;
+ GSimpleAsyncResult *simple;
+ GFileInfo *file_info;
+} BuildMimePartData;
+
+static BuildMimePartData *
+attachment_build_mime_part_data_new (EAttachment *attachment,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ gpointer source_tag)
{
- CamelMimePart *mime_part;
- const gchar *description;
+ BuildMimePartData *data;
+ GSimpleAsyncResult *simple;
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+ simple = g_simple_async_result_new (
+ G_OBJECT (attachment), callback, user_data, source_tag);
- mime_part = e_attachment_get_mime_part (attachment);
- if (mime_part != NULL)
- description = camel_mime_part_get_description (mime_part);
- else
- description = attachment->priv->description;
+ if (G_IS_CANCELLABLE (cancellable))
+ g_object_ref (cancellable);
- return description;
+ data = g_slice_new0 (BuildMimePartData);
+ data->io_priority = io_priority;
+ data->cancellable = cancellable;
+ data->simple = simple;
+ return data;
}
-void
-e_attachment_set_description (EAttachment *attachment,
- const gchar *description)
+static void
+attachment_build_mime_part_data_free (BuildMimePartData *data)
{
- CamelMimePart *mime_part;
+ if (data->attachment != NULL)
+ g_object_unref (data->attachment);
- g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ if (data->cancellable != NULL)
+ g_object_unref (data->cancellable);
- g_free (attachment->priv->description);
- attachment->priv->description = g_strdup (description);
+ if (data->simple != NULL)
+ g_object_unref (data->simple);
- mime_part = e_attachment_get_mime_part (attachment);
- if (mime_part != NULL)
- camel_mime_part_set_description (mime_part, description);
+ if (data->file_info != NULL)
+ g_object_unref (data->file_info);
- g_object_notify (G_OBJECT (attachment), "description");
+ g_slice_free (BuildMimePartData, data);
}
-const gchar *
-e_attachment_get_disposition (EAttachment *attachment)
+static void
+attachment_build_mime_part_splice_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
+ GSimpleAsyncResult *final_result;
+ GCancellable *cancellable;
+ EAttachment *attachment;
+ CamelDataWrapper *wrapper;
CamelMimePart *mime_part;
- const gchar *disposition;
+ CamelStream *stream;
+ const gchar *content_type;
+ gchar *mime_type;
+ gssize length;
+ gpointer data;
+ GError *error = NULL;
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+ final_result = G_SIMPLE_ASYNC_RESULT (user_data);
- mime_part = e_attachment_get_mime_part (attachment);
- if (mime_part != NULL)
- disposition = camel_mime_part_get_disposition (mime_part);
- else
- disposition = attachment->priv->disposition;
+ cancellable = g_cancellable_get_current ();
+ g_cancellable_pop_current (cancellable);
+ g_object_unref (cancellable);
- return disposition;
-}
+ length = g_output_stream_splice_finish (
+ G_OUTPUT_STREAM (source), result, &error);
+ if (error != NULL)
+ goto fail;
-void
-e_attachment_set_disposition (EAttachment *attachment,
- const gchar *disposition)
-{
- CamelMimePart *mime_part;
+ data = g_memory_output_stream_get_data (
+ G_MEMORY_OUTPUT_STREAM (source));
- g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ attachment = E_ATTACHMENT (
+ g_async_result_get_source_object (
+ G_ASYNC_RESULT (final_result)));
- g_free (attachment->priv->disposition);
- attachment->priv->disposition = g_strdup (disposition);
+ if (e_attachment_is_rfc822 (attachment))
+ wrapper = (CamelDataWrapper *) camel_mime_message_new ();
+ else
+ wrapper = camel_data_wrapper_new ();
- mime_part = e_attachment_get_mime_part (attachment);
- if (mime_part != NULL)
- camel_mime_part_set_disposition (mime_part, disposition);
+ content_type = e_attachment_get_content_type (attachment);
+ mime_type = g_content_type_get_mime_type (content_type);
- g_object_notify (G_OBJECT (attachment), "disposition");
-}
+ stream = camel_stream_mem_new_with_buffer (data, length);
+ camel_data_wrapper_construct_from_stream (wrapper, stream);
+ camel_data_wrapper_set_mime_type (wrapper, mime_type);
+ camel_object_unref (stream);
-const gchar *
-e_attachment_get_filename (EAttachment *attachment)
-{
- CamelMimePart *mime_part;
- const gchar *filename;
+ mime_part = camel_mime_part_new ();
+ camel_medium_set_content_object (CAMEL_MEDIUM (mime_part), wrapper);
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+ g_simple_async_result_set_op_res_gpointer (
+ final_result, mime_part, camel_object_unref);
- mime_part = e_attachment_get_mime_part (attachment);
- if (mime_part != NULL)
- filename = camel_mime_part_get_filename (mime_part);
- else
- filename = attachment->priv->filename;
+ g_simple_async_result_complete (final_result);
+
+ camel_object_unref (wrapper);
+ g_free (mime_type);
+
+ return;
- return filename;
+fail:
+ g_simple_async_result_set_from_error (final_result, error);
+ g_simple_async_result_complete (final_result);
+ g_error_free (error);
}
-void
-e_attachment_set_filename (EAttachment *attachment,
- const gchar *filename)
+static void
+attachment_build_mime_part_read_cb (GObject *source,
+ GAsyncResult *result,
+ BuildMimePartData *data)
{
- CamelMimePart *mime_part;
+ GFileInputStream *input_stream;
+ GOutputStream *output_stream;
+ GCancellable *cancellable;
+ GError *error = NULL;
- g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ input_stream = g_file_read_finish (G_FILE (source), result, &error);
+ if (error != NULL)
+ goto fail;
- g_free (attachment->priv->filename);
- attachment->priv->filename = g_strdup (filename);
+ output_stream = g_memory_output_stream_new (
+ NULL, 0, g_realloc, g_free);
- mime_part = e_attachment_get_mime_part (attachment);
- if (mime_part != NULL)
- camel_mime_part_set_filename (mime_part, filename);
+ g_output_stream_splice_async (
+ output_stream, G_INPUT_STREAM (input_stream),
+ G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
+ G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+ G_PRIORITY_DEFAULT, cancellable,
+ attachment_build_mime_part_splice_cb, result);
- g_object_notify (G_OBJECT (attachment), "filename");
-}
+ g_cancellable_push_current (cancellable);
-CamelMimePart *
-e_attachment_get_mime_part (EAttachment *attachment)
-{
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+ g_object_unref (input_stream);
+ g_object_unref (output_stream);
- return attachment->priv->mime_part;
+ return;
+
+fail:
+ g_simple_async_result_set_from_error (final_result, error);
+ g_simple_async_result_complete (final_result);
+ g_error_free (error);
}
-const gchar *
-e_attachment_get_mime_type (EAttachment *attachment)
+static gboolean
+attachment_build_mime_part_idle_cb (BuildMimePartData *data)
{
- CamelContentType *content_type;
- CamelMimePart *mime_part;
- const gchar *filename;
- gchar *mime_type;
+ GObject *source;
+ GAsyncResult *result;
+ GFileInfo *file_info;
+ GFile *file;
+ GError *error = NULL;
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+ if (g_cancellable_set_error_if_cancelled (data->cancellable, &error))
+ goto cancelled;
- if (attachment->priv->mime_type != NULL)
- goto exit;
+ result = G_ASYNC_RESULT (data->simple);
+ source = g_async_result_get_source_object (result);
+ file_info = e_attachment_get_file_info (E_ATTACHMENT (source));
- mime_part = e_attachment_get_mime_part (attachment);
- filename = e_attachment_get_filename (attachment);
- content_type = camel_mime_part_get_content_type (mime_part);
+ /* Poll again on the next idle. */
+ if (!G_IS_FILE_INFO (file_info))
+ return TRUE;
- if (mime_part == NULL)
- mime_type = attachment_guess_mime_type (filename);
- else {
- content_type = camel_mime_part_get_content_type (mime_part);
- mime_type = camel_content_type_simple (content_type);
- }
+ /* We have a GFileInfo, so on to step 2. */
- attachment->priv->mime_type = mime_type;
+ data->file_info = g_file_info_dup (file_info);
+ file = e_attachment_get_file (E_ATTACHMENT (source));
-exit:
- return attachment->priv->mime_type;
-}
+ /* Because Camel's stream API is synchronous and not
+ * cancellable, we have to asynchronously read the file
+ * into memory and then encode it to a MIME part. That
+ * means double buffering the file contents in memory,
+ * unfortunately. */
+ g_file_read_async (
+ file, data->io_priority, data->cancellable,
+ attachment_build_mime_part_read_cb, data);
-GdkPixbuf *
-e_attachment_get_thumbnail (EAttachment *attachment)
-{
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+ return FALSE;
+
+cancelled:
+ g_simple_async_result_set_op_res_gboolean (data->simple, FALSE);
+ g_simple_async_result_set_from_error (data->simple, error);
+ g_simple_async_result_complete (data->simple);
- return attachment->priv->thumbnail;
+ build_mime_part_data_free (data);
+ g_error_free (error);
+
+ return FALSE;
}
void
-e_attachment_set_thumbnail (EAttachment *attachment,
- GdkPixbuf *thumbnail)
+e_attachment_build_mime_part_async (EAttachment *attachment,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ CamelMimePart *mime_part;
+ GSimpleAsyncResult *result;
+ GFile *file;
+
g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ g_return_if_fail (callback != NULL);
- if (thumbnail != NULL) {
- g_return_if_fail (GDK_IS_PIXBUF (thumbnail));
- g_object_ref (thumbnail);
+ file = e_attachment_get_file (attachment);
+ mime_part = e_attachment_get_mime_part (attachment);
+ g_return_if_fail (file != NULL || mime_part != NULL);
+
+ result = g_simple_async_result_new (
+ G_OBJECT (attachment), callback, user_data,
+ e_attachment_build_mime_part_async);
+
+ /* First try the easy way out. */
+ if (CAMEL_IS_MIME_PART (mime_part)) {
+ camel_object_ref (mime_part);
+ g_simple_async_result_set_op_res_gpointer (
+ result, mime_part, camel_object_unref);
+ g_simple_async_result_complete_in_idle (result);
+ return;
}
- if (attachment->priv->thumbnail != NULL)
- g_object_unref (attachment->priv->thumbnail);
+ /* XXX g_cancellable_push_current() documentation lies.
+ * The function rejects NULL pointers, so create a
+ * dummy GCancellable if necessary. */
+ if (cancellable == NULL)
+ cancellable = g_cancellable_new ();
+ else
+ g_object_ref (cancellable);
- attachment->priv->thumbnail = thumbnail;
+ /* Because Camel's stream API is synchronous and not
+ * cancellable, we have to asynchronously read the file
+ * into memory and then encode it to a MIME part. That
+ * means it's double buffered, unfortunately. */
+ g_file_read_async (
+ file, G_PRIORITY_DEFAULT, cancellable,
+ attachment_build_mime_part_read_cb, result);
- g_object_notify (G_OBJECT (attachment), "thumbnail");
+ g_cancellable_push_current (cancellable);
}
-gboolean
-e_attachment_is_image (EAttachment *attachment)
+CamelMimePart *
+e_attachment_build_mime_part_finish (EAttachment *attachment,
+ GAsyncResult *result,
+ GError **error)
{
- CamelContentType *content_type;
CamelMimePart *mime_part;
+ GSimpleAsyncResult *simple_result;
+ gboolean async_result_is_valid;
+ gpointer source_tag;
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
- mime_part = e_attachment_get_mime_part (attachment);
- if (mime_part == NULL)
- return FALSE;
+ source_tag = e_attachment_build_mime_part_async;
+ async_result_is_valid = g_simple_async_result_is_valid (
+ result, G_OBJECT (attachment), source_tag);
+ g_return_val_if_fail (async_result_is_valid, NULL);
- content_type = camel_mime_part_get_content_type (mime_part);
+ simple_result = G_SIMPLE_ASYNC_RESULT (result);
+ g_simple_async_result_propagate_error (simple_result, error);
+ mime_part = g_simple_async_result_get_op_res_gpointer (simple_result);
+ attachment_file_info_to_mime_part (attachment, mime_part);
- return camel_content_type_is (content_type, "image", "*");
-}
+ if (CAMEL_IS_MIME_PART (mime_part))
+ camel_object_ref (mime_part);
-gboolean
-e_attachment_is_inline (EAttachment *attachment)
-{
- const gchar *disposition;
+ g_object_unref (result);
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+ return mime_part;
+}
+#endif
- disposition = e_attachment_get_disposition (attachment);
- g_return_val_if_fail (disposition != NULL, FALSE);
+void
+_e_attachment_set_reference (EAttachment *attachment,
+ GtkTreeRowReference *reference)
+{
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ g_return_if_fail (reference != NULL);
- return (g_ascii_strcasecmp (disposition, "inline") == 0);
+ gtk_tree_row_reference_free (attachment->priv->reference);
+ attachment->priv->reference = gtk_tree_row_reference_copy (reference);
}
diff --git a/widgets/misc/e-attachment.h b/widgets/misc/e-attachment.h
index c69189c189..a7bb85dd75 100644
--- a/widgets/misc/e-attachment.h
+++ b/widgets/misc/e-attachment.h
@@ -1,4 +1,6 @@
/*
+ * e-attachment.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
@@ -13,10 +15,6 @@
* License along with the program; if not, see <http://www.gnu.org/licenses/>
*
*
- * Authors:
- * Ettore Perazzoli <ettore@ximian.com>
- * Srinivasa Ragavan <sragavan@novell.com>
- *
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
@@ -25,11 +23,10 @@
#define E_ATTACHMENT_H
#include <gio/gio.h>
-#include <gtk/gtk.h>
-#include <glade/glade-xml.h>
#include <camel/camel-mime-part.h>
-#include <camel/camel-exception.h>
-#include <camel/camel-cipher-context.h>
+#include <camel/camel-mime-message.h>
+#include <camel/camel-multipart.h>
+#include <widgets/misc/e-file-activity.h>
/* Standard GObject macros */
#define E_TYPE_ATTACHMENT \
@@ -45,7 +42,7 @@
((obj), E_TYPE_ATTACHMENT))
#define E_IS_ATTACHMENT_CLASS(cls) \
(G_TYPE_CHECK_CLASS_TYPE \
- ((obj), E_TYPE_ATTACHMENT))
+ ((cls), E_TYPE_ATTACHMENT))
#define E_ATTACHMENT_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS \
((obj), E_TYPE_ATTACHMENT, EAttachmentClass))
@@ -58,63 +55,58 @@ typedef struct _EAttachmentPrivate EAttachmentPrivate;
struct _EAttachment {
GObject parent;
-
- gboolean guessed_type;
- gulong size;
-
- GCancellable *cancellable;
-
- gboolean is_available_local;
- int percentage;
- int index;
- char *store_uri;
-
- /* Status of signed/encrypted attachments */
- camel_cipher_validity_sign_t sign;
- camel_cipher_validity_encrypt_t encrypt;
-
EAttachmentPrivate *priv;
};
struct _EAttachmentClass {
GObjectClass parent_class;
-
- void (*changed) (EAttachment *attachment);
- void (*update) (EAttachment *attachment,
- gchar *message);
};
GType e_attachment_get_type (void);
-EAttachment * e_attachment_new (const gchar *filename,
- const gchar *disposition,
- CamelException *ex);
-EAttachment * e_attachment_new_remote_file (GtkWindow *error_dlg_parent,
- const gchar *url,
- const gchar *disposition,
- const gchar *path,
- CamelException *ex);
-void e_attachment_build_remote_file (const gchar *filename,
- EAttachment *attachment,
- CamelException *ex);
-EAttachment * e_attachment_new_from_mime_part (CamelMimePart *part);
-void e_attachment_edit (EAttachment *attachment,
- GtkWindow *parent);
-const gchar * e_attachment_get_description (EAttachment *attachment);
-void e_attachment_set_description (EAttachment *attachment,
- const gchar *description);
+EAttachment * e_attachment_new (void);
+EAttachment * e_attachment_new_for_path (const gchar *path);
+EAttachment * e_attachment_new_for_uri (const gchar *uri);
+EAttachment * e_attachment_new_for_message (CamelMimeMessage *message);
+void e_attachment_add_to_multipart (EAttachment *attachment,
+ CamelMultipart *multipart,
+ const gchar *default_charset);
const gchar * e_attachment_get_disposition (EAttachment *attachment);
void e_attachment_set_disposition (EAttachment *attachment,
const gchar *disposition);
-const gchar * e_attachment_get_filename (EAttachment *attachment);
-void e_attachment_set_filename (EAttachment *attachment,
- const gchar *filename);
+GFile * e_attachment_get_file (EAttachment *attachment);
+void e_attachment_set_file (EAttachment *attachment,
+ GFile *file);
+GFileInfo * e_attachment_get_file_info (EAttachment *attachment);
CamelMimePart * e_attachment_get_mime_part (EAttachment *attachment);
-const gchar * e_attachment_get_mime_type (EAttachment *attachment);
-GdkPixbuf * e_attachment_get_thumbnail (EAttachment *attachment);
-void e_attachment_set_thumbnail (EAttachment *attachment,
- GdkPixbuf *pixbuf);
+void e_attachment_set_mime_part (EAttachment *attachment,
+ CamelMimePart *mime_part);
+const gchar * e_attachment_get_content_type (EAttachment *attachment);
+const gchar * e_attachment_get_display_name (EAttachment *attachment);
+const gchar * e_attachment_get_description (EAttachment *attachment);
+GIcon * e_attachment_get_icon (EAttachment *attachment);
+const gchar * e_attachment_get_thumbnail_path (EAttachment *attachment);
+guint64 e_attachment_get_size (EAttachment *attachment);
gboolean e_attachment_is_image (EAttachment *attachment);
-gboolean e_attachment_is_inline (EAttachment *attachment);
+gboolean e_attachment_is_rfc822 (EAttachment *attachment);
+void e_attachment_save_async (EAttachment *attachment,
+ EFileActivity *file_activity,
+ GFile *destination);
+
+#if 0
+void e_attachment_build_mime_part_async
+ (EAttachment *attachment,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+CamelMimePart * e_attachment_build_mime_part_finish
+ (EAttachment *attachment,
+ GAsyncResult *result,
+ GError **error);
+#endif
+
+/* For use by EAttachmentStore only. */
+void _e_attachment_set_reference (EAttachment *attachment,
+ GtkTreeRowReference *reference);
G_END_DECLS
diff --git a/widgets/misc/e-file-activity.c b/widgets/misc/e-file-activity.c
index 38dae234df..1957c20282 100644
--- a/widgets/misc/e-file-activity.c
+++ b/widgets/misc/e-file-activity.c
@@ -21,18 +21,25 @@
#include "e-file-activity.h"
+#include <stdarg.h>
+
#define E_FILE_ACTIVITY_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_FILE_ACTIVITY, EFileActivityPrivate))
struct _EFileActivityPrivate {
GCancellable *cancellable;
+ GAsyncResult *result;
+ GFile *file;
+
gulong handler_id;
};
enum {
PROP_0,
- PROP_CANCELLABLE
+ PROP_CANCELLABLE,
+ PROP_FILE,
+ PROP_RESULT
};
static gpointer parent_class;
@@ -49,6 +56,18 @@ file_activity_set_property (GObject *object,
E_FILE_ACTIVITY (object),
g_value_get_object (value));
return;
+
+ case PROP_FILE:
+ e_file_activity_set_file (
+ E_FILE_ACTIVITY (object),
+ g_value_get_object (value));
+ return;
+
+ case PROP_RESULT:
+ e_file_activity_set_result (
+ E_FILE_ACTIVITY (object),
+ g_value_get_object (value));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -66,6 +85,18 @@ file_activity_get_property (GObject *object,
value, e_file_activity_get_cancellable (
E_FILE_ACTIVITY (object)));
return;
+
+ case PROP_FILE:
+ g_value_set_object (
+ value, e_file_activity_get_file (
+ E_FILE_ACTIVITY (object)));
+ return;
+
+ case PROP_RESULT:
+ g_value_set_object (
+ value, e_file_activity_get_result (
+ E_FILE_ACTIVITY (object)));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -85,6 +116,16 @@ file_activity_dispose (GObject *object)
priv->cancellable = NULL;
}
+ if (priv->result != NULL) {
+ g_object_unref (priv->result);
+ priv->result = NULL;
+ }
+
+ if (priv->file != NULL) {
+ g_object_unref (priv->file);
+ priv->file = NULL;
+ }
+
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (parent_class)->dispose (object);
}
@@ -129,6 +170,26 @@ file_activity_class_init (EFileActivityClass *class)
NULL,
G_TYPE_CANCELLABLE,
G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_FILE,
+ g_param_spec_object (
+ "file",
+ "File",
+ NULL,
+ G_TYPE_FILE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_RESULT,
+ g_param_spec_object (
+ "result",
+ "Result",
+ NULL,
+ G_TYPE_ASYNC_RESULT,
+ G_PARAM_READWRITE));
}
static void
@@ -179,6 +240,22 @@ e_file_activity_new (const gchar *primary_text)
"primary-text", primary_text, NULL);
}
+EActivity *
+e_file_activity_newv (const gchar *format, ...)
+{
+ EActivity *activity;
+ gchar *primary_text;
+ va_list args;
+
+ va_start (args, format);
+ primary_text = g_strdup_vprintf (format, args);
+ activity = e_file_activity_new (primary_text);
+ g_free (primary_text);
+ va_end (args);
+
+ return activity;
+}
+
GCancellable *
e_file_activity_get_cancellable (EFileActivity *file_activity)
{
@@ -218,6 +295,60 @@ e_file_activity_set_cancellable (EFileActivity *file_activity,
g_object_notify (G_OBJECT (file_activity), "cancellable");
}
+GFile *
+e_file_activity_get_file (EFileActivity *file_activity)
+{
+ g_return_val_if_fail (E_IS_FILE_ACTIVITY (file_activity), NULL);
+
+ return file_activity->priv->file;
+}
+
+void
+e_file_activity_set_file (EFileActivity *file_activity,
+ GFile *file)
+{
+ g_return_if_fail (E_IS_FILE_ACTIVITY (file_activity));
+
+ if (file != NULL) {
+ g_return_if_fail (G_IS_FILE (file));
+ g_object_ref (file);
+ }
+
+ if (file_activity->priv->file != NULL)
+ g_object_unref (file_activity->priv->file);
+
+ file_activity->priv->file = file;
+
+ g_object_notify (G_OBJECT (file_activity), "file");
+}
+
+GAsyncResult *
+e_file_activity_get_result (EFileActivity *file_activity)
+{
+ g_return_val_if_fail (E_IS_FILE_ACTIVITY (file_activity), NULL);
+
+ return file_activity->priv->result;
+}
+
+void
+e_file_activity_set_result (EFileActivity *file_activity,
+ GAsyncResult *result)
+{
+ g_return_if_fail (E_IS_FILE_ACTIVITY (file_activity));
+
+ if (result != NULL) {
+ g_return_if_fail (G_IS_ASYNC_RESULT (result));
+ g_object_ref (result);
+ }
+
+ if (file_activity->priv->result != NULL)
+ g_object_unref (file_activity->priv->result);
+
+ file_activity->priv->result = result;
+
+ g_object_notify (G_OBJECT (file_activity), "result");
+}
+
void
e_file_activity_progress (goffset current_num_bytes,
goffset total_num_bytes,
diff --git a/widgets/misc/e-file-activity.h b/widgets/misc/e-file-activity.h
index 4a57228872..acd144dec0 100644
--- a/widgets/misc/e-file-activity.h
+++ b/widgets/misc/e-file-activity.h
@@ -23,7 +23,7 @@
#define E_FILE_ACTIVITY_H
#include <gio/gio.h>
-#include <e-activity.h>
+#include <widgets/misc/e-activity.h>
/* Standard GObject macros */
#define E_TYPE_FILE_ACTIVITY \
@@ -61,9 +61,17 @@ struct _EFileActivityClass {
GType e_file_activity_get_type (void);
EActivity * e_file_activity_new (const gchar *primary_text);
+EActivity * e_file_activity_newv (const gchar *format,
+ ...) G_GNUC_PRINTF (1, 2);
GCancellable * e_file_activity_get_cancellable (EFileActivity *file_activity);
void e_file_activity_set_cancellable (EFileActivity *file_activity,
GCancellable *cancellable);
+GFile * e_file_activity_get_file (EFileActivity *file_activity);
+void e_file_activity_set_file (EFileActivity *file_activity,
+ GFile *file);
+GAsyncResult * e_file_activity_get_result (EFileActivity *file_activity);
+void e_file_activity_set_result (EFileActivity *file_activity,
+ GAsyncResult *result);
/* This can be used as a GFileProgressCallback. */
void e_file_activity_progress (goffset current_num_bytes,
diff --git a/widgets/misc/e-timeout-activity.c b/widgets/misc/e-timeout-activity.c
index d02bcf96d2..878d6b87a3 100644
--- a/widgets/misc/e-timeout-activity.c
+++ b/widgets/misc/e-timeout-activity.c
@@ -21,6 +21,8 @@
#include "e-timeout-activity.h"
+#include <stdarg.h>
+
#define E_TIMEOUT_ACTIVITY_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_TIMEOUT_ACTIVITY, ETimeoutActivityPrivate))
@@ -166,6 +168,22 @@ e_timeout_activity_new (const gchar *primary_text)
"primary-text", primary_text, NULL);
}
+EActivity *
+e_timeout_activity_newv (const gchar *format, ...)
+{
+ EActivity *activity;
+ gchar *primary_text;
+ va_list args;
+
+ va_start (args, format);
+ primary_text = g_strdup_vprintf (format, args);
+ activity = e_timeout_activity_new (primary_text);
+ g_free (primary_text);
+ va_end (args);
+
+ return activity;
+}
+
void
e_timeout_activity_set_timeout (ETimeoutActivity *timeout_activity,
guint seconds)
diff --git a/widgets/misc/e-timeout-activity.h b/widgets/misc/e-timeout-activity.h
index 70aaee7dcb..b395f39bde 100644
--- a/widgets/misc/e-timeout-activity.h
+++ b/widgets/misc/e-timeout-activity.h
@@ -63,6 +63,8 @@ struct _ETimeoutActivityClass {
GType e_timeout_activity_get_type (void);
EActivity * e_timeout_activity_new (const gchar *primary_text);
+EActivity * e_timeout_activity_newv (const gchar *format,
+ ...) G_GNUC_PRINTF (1, 2);
void e_timeout_activity_set_timeout (ETimeoutActivity *timeout_activity,
guint seconds);