diff options
author | Matthew Barnes <mbarnes@src.gnome.org> | 2009-03-21 03:06:59 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@src.gnome.org> | 2009-03-21 03:06:59 +0800 |
commit | 4cec9fc7169dc3b810321555a70cda916720867d (patch) | |
tree | 8fe739ab0d249fb35bedc572bd34b3717512283b /widgets | |
parent | 7a92d9cc82b7775a0f5cb1fde233119d435a79b6 (diff) | |
download | gsoc2013-evolution-4cec9fc7169dc3b810321555a70cda916720867d.tar gsoc2013-evolution-4cec9fc7169dc3b810321555a70cda916720867d.tar.gz gsoc2013-evolution-4cec9fc7169dc3b810321555a70cda916720867d.tar.bz2 gsoc2013-evolution-4cec9fc7169dc3b810321555a70cda916720867d.tar.lz gsoc2013-evolution-4cec9fc7169dc3b810321555a70cda916720867d.tar.xz 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')
23 files changed, 5219 insertions, 1199 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); diff --git a/widgets/table/Makefile.am b/widgets/table/Makefile.am index 836a1dd570..c507770290 100644 --- a/widgets/table/Makefile.am +++ b/widgets/table/Makefile.am @@ -9,8 +9,9 @@ glade_DATA = \ e-table-field-chooser.glade INCLUDES = \ - -I$(top_srcdir) \ - -I$(top_srcdir)/widgets \ + -I$(top_srcdir) \ + -I$(top_srcdir)/widgets \ + $(E_UTIL_CFLAGS) \ $(E_WIDGETS_CFLAGS) \ $(GNOME_PLATFORM_CFLAGS) \ -DEVOLUTION_GLADEDIR=\"$(gladedir)\" \ @@ -147,6 +148,7 @@ libetable_la_LIBADD = \ $(WIN32_BOOTSTRAP_LIBS) \ $(top_builddir)/e-util/libeutil.la \ $(top_builddir)/a11y/libevolution-a11y.la \ + $(E_UTIL_LIBS) \ $(E_WIDGETS_LIBS) \ $(GNOME_PLATFORM_LIBS) diff --git a/widgets/text/Makefile.am b/widgets/text/Makefile.am index df2b02c781..565ec4e521 100644 --- a/widgets/text/Makefile.am +++ b/widgets/text/Makefile.am @@ -5,6 +5,7 @@ endif INCLUDES = \ -I$(top_srcdir) \ -I$(top_srcdir)/widgets \ + $(E_UTIL_CFLAGS) \ $(GNOME_PLATFORM_CFLAGS) \ -DG_LOG_DOMAIN=\"e-text\" @@ -30,5 +31,6 @@ libetext_la_LIBADD = \ $(top_builddir)/e-util/libeutil.la \ $(top_builddir)/a11y/libevolution-a11y.la \ $(top_builddir)/widgets/table/libetable.la \ + $(E_UTIL_LIBS) \ $(GNOME_PLATFORM_LIBS) \ $(REGEX_LIBS) |