From 4cec9fc7169dc3b810321555a70cda916720867d Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Fri, 20 Mar 2009 19:06:59 +0000 Subject: Saving progress on a massive attachment handling rewrite. svn path=/branches/kill-bonobo/; revision=37465 --- a11y/e-table/Makefile.am | 5 +- calendar/gui/dialogs/comp-editor.c | 860 +++-------- calendar/gui/e-cal-popup.c | 442 ------ composer/e-composer-actions.c | 72 +- composer/e-composer-private.c | 90 +- composer/e-composer-private.h | 13 +- composer/e-msg-composer.c | 545 ++----- composer/e-msg-composer.h | 14 +- e-util/Makefile.am | 2 +- e-util/e-util.c | 37 +- e-util/e-util.h | 9 +- mail/e-mail-browser.c | 2 +- mail/e-mail-reader.c | 2 +- mail/em-format-html-display.c | 136 +- mail/em-format-html-display.h | 2 - mail/em-popup.c | 12 + plugins/attachment-reminder/attachment-reminder.c | 11 +- plugins/import-ics-attachments/icsimporter.c | 1 - shell/e-shell.c | 14 + shell/main.c | 16 +- widgets/misc/Makefile.am | 12 +- widgets/misc/e-activity.c | 112 +- widgets/misc/e-activity.h | 8 + widgets/misc/e-attachment-bar.c | 445 ------ widgets/misc/e-attachment-dialog.c | 112 +- widgets/misc/e-attachment-icon-view.c | 319 +++++ widgets/misc/e-attachment-icon-view.h | 66 + widgets/misc/e-attachment-paned.c | 563 ++++++++ widgets/misc/e-attachment-paned.h | 79 + widgets/misc/e-attachment-store.c | 1073 ++++++++++++++ widgets/misc/e-attachment-store.h | 110 ++ widgets/misc/e-attachment-tree-view.c | 396 ++++++ widgets/misc/e-attachment-tree-view.h | 66 + widgets/misc/e-attachment-view.c | 1041 ++++++++++++++ widgets/misc/e-attachment-view.h | 163 +++ widgets/misc/e-attachment.c | 1584 ++++++++++++--------- widgets/misc/e-attachment.h | 98 +- widgets/misc/e-file-activity.c | 133 +- widgets/misc/e-file-activity.h | 10 +- widgets/misc/e-timeout-activity.c | 18 + widgets/misc/e-timeout-activity.h | 2 + widgets/table/Makefile.am | 6 +- widgets/text/Makefile.am | 2 + 43 files changed, 5742 insertions(+), 2961 deletions(-) create mode 100644 widgets/misc/e-attachment-icon-view.c create mode 100644 widgets/misc/e-attachment-icon-view.h create mode 100644 widgets/misc/e-attachment-paned.c create mode 100644 widgets/misc/e-attachment-paned.h create mode 100644 widgets/misc/e-attachment-store.c create mode 100644 widgets/misc/e-attachment-store.h create mode 100644 widgets/misc/e-attachment-tree-view.c create mode 100644 widgets/misc/e-attachment-tree-view.h create mode 100644 widgets/misc/e-attachment-view.c create mode 100644 widgets/misc/e-attachment-view.h diff --git a/a11y/e-table/Makefile.am b/a11y/e-table/Makefile.am index 1884c3a1c5..f344d7b8c3 100644 --- a/a11y/e-table/Makefile.am +++ b/a11y/e-table/Makefile.am @@ -1,6 +1,7 @@ INCLUDES = \ - -I$(top_srcdir) \ - -I$(top_srcdir)/widgets \ + -I$(top_srcdir) \ + -I$(top_srcdir)/widgets \ + $(E_UTIL_CFLAGS) \ $(GNOME_PLATFORM_CFLAGS) \ -DG_LOG_DOMAIN=\"e-table\" diff --git a/calendar/gui/dialogs/comp-editor.c b/calendar/gui/dialogs/comp-editor.c index 7be36d043b..e3b1bac053 100644 --- a/calendar/gui/dialogs/comp-editor.c +++ b/calendar/gui/dialogs/comp-editor.c @@ -63,9 +63,8 @@ #include "../e-cal-popup.h" #include "../calendar-config-keys.h" #include "cal-attachment-select-file.h" +#include "widgets/misc/e-attachment-paned.h" -#include "e-attachment-bar.h" -#include "misc/e-expander.h" #include "e-util/e-error.h" #define COMP_EDITOR_GET_PRIVATE(obj) \ @@ -95,12 +94,7 @@ struct _CompEditorPrivate { GtkNotebook *notebook; /* Attachment handling */ - GtkWidget *attachment_bar; - GtkWidget *attachment_scrolled_window; - GtkWidget *attachment_expander; - GtkWidget *attachment_expander_label; - GtkWidget *attachment_expander_icon; - GtkWidget *attachment_expander_num; + GtkWidget *attachment_paned; /* Manages menus and toolbars */ GtkUIManager *manager; @@ -169,7 +163,6 @@ static const gchar *ui = ""; static void comp_editor_show_help (CompEditor *editor); -static void setup_widgets (CompEditor *editor); static void real_edit_comp (CompEditor *editor, ECalComponent *comp); static gboolean real_send_comp (CompEditor *editor, ECalComponentItipMethod method, gboolean strip_alarms); @@ -232,391 +225,82 @@ comp_editor_weak_notify_cb (gpointer unused, } static void -attach_message(CompEditor *editor, CamelMimeMessage *msg) +drag_data_received (CompEditor *editor, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection, + guint info, + guint time) { - CamelMimePart *mime_part; - const char *subject; - guint i; - char *filename = NULL; + EAttachmentPaned *paned; + EAttachmentView *view; - mime_part = camel_mime_part_new(); - camel_mime_part_set_disposition(mime_part, "inline"); - subject = camel_mime_message_get_subject(msg); - if (subject) { - char *desc = g_strdup_printf(_("Attached message - %s"), subject); + paned = E_ATTACHMENT_PANED (editor->priv->attachment_paned); + view = e_attachment_paned_get_view (paned); - camel_mime_part_set_description(mime_part, desc); - g_free(desc); - } else - camel_mime_part_set_description(mime_part, _("Attached message")); - - i = e_attachment_bar_get_num_attachments (E_ATTACHMENT_BAR (editor->priv->attachment_bar)); - i++; - filename = g_strdup_printf ("email%d",i); - camel_mime_part_set_filename (mime_part, filename); - - camel_medium_set_content_object((CamelMedium *)mime_part, (CamelDataWrapper *)msg); - camel_mime_part_set_content_type(mime_part, "message/rfc822"); - e_attachment_bar_attach_mime_part(E_ATTACHMENT_BAR(editor->priv->attachment_bar), mime_part); - camel_object_unref(mime_part); - g_free (filename); -} - -struct _drop_data { - CompEditor *editor; - - GdkDragContext *context; - /* Only selection->data and selection->length are valid */ - GtkSelectionData *selection; - - guint32 action; - guint info; - guint time; - - unsigned int move:1; - unsigned int moved:1; - unsigned int aborted:1; -}; - -static void -drop_action(CompEditor *editor, GdkDragContext *context, guint32 action, GtkSelectionData *selection, guint info, guint time) -{ -#if 0 /* KILL-BONOBO */ - char *tmp, *str, **urls; - CamelMimePart *mime_part; - CamelStream *stream; - CamelURL *url; - CamelMimeMessage *msg; - char *content_type; - int i, success=FALSE, delete=FALSE; - - switch (info) { - case DND_TYPE_MESSAGE_RFC822: - d(printf ("dropping a message/rfc822\n")); - /* write the message(s) out to a CamelStream so we can use it */ - stream = camel_stream_mem_new (); - camel_stream_write (stream, (char *)selection->data, selection->length); - camel_stream_reset (stream); - - msg = camel_mime_message_new (); - if (camel_data_wrapper_construct_from_stream((CamelDataWrapper *)msg, stream) != -1) { - attach_message(editor, msg); - success = TRUE; - delete = action == GDK_ACTION_MOVE; - } - - camel_object_unref(msg); - camel_object_unref(stream); - break; - case DND_TYPE_TEXT_URI_LIST: - case DND_TYPE_NETSCAPE_URL: - d(printf ("dropping a text/uri-list\n")); - tmp = g_strndup ((char *)selection->data, selection->length); - urls = g_strsplit (tmp, "\n", 0); - g_free (tmp); - - for (i = 0; urls[i] != NULL; i++) { - str = g_strstrip (urls[i]); - if (urls[i][0] == '#') - continue; - - if (!g_ascii_strncasecmp (str, "mailto:", 7)) { - /* TODO does not handle mailto now */ - } else { - url = camel_url_new (str, NULL); - - if (url == NULL) - continue; - - if (!g_ascii_strcasecmp (url->protocol, "file")) - e_attachment_bar_attach - (E_ATTACHMENT_BAR (editor->priv->attachment_bar), - url->path, - "attachment"); - else - e_attachment_bar_attach_remote_file - (E_ATTACHMENT_BAR (editor->priv->attachment_bar), - str, "attachment"); - - camel_url_free (url); - } - } - - g_strfreev (urls); - success = TRUE; - break; - case DND_TYPE_TEXT_VCARD: - case DND_TYPE_TEXT_CALENDAR: - content_type = gdk_atom_name (selection->type); - d(printf ("dropping a %s\n", content_type)); - - mime_part = camel_mime_part_new (); - camel_mime_part_set_content (mime_part, (char *)selection->data, selection->length, content_type); - camel_mime_part_set_disposition (mime_part, "inline"); - - e_attachment_bar_attach_mime_part - (E_ATTACHMENT_BAR (editor->priv->attachment_bar), - mime_part); - - camel_object_unref (mime_part); - g_free (content_type); - - success = TRUE; - break; - case DND_TYPE_X_UID_LIST: { - GPtrArray *uids; - char *inptr, *inend; - CamelFolder *folder; - CamelException ex = CAMEL_EXCEPTION_INITIALISER; - - /* NB: This all runs synchronously, could be very slow/hang/block the ui */ - - uids = g_ptr_array_new(); - - inptr = (char *)selection->data; - inend = (char *)(selection->data + selection->length); - while (inptr < inend) { - char *start = inptr; - - while (inptr < inend && *inptr) - inptr++; - - if (start > (char *)selection->data) - g_ptr_array_add(uids, g_strndup(start, inptr-start)); - - inptr++; - } - - if (uids->len > 0) { - folder = mail_tool_uri_to_folder((char *)selection->data, 0, &ex); - if (folder) { - if (uids->len == 1) { - msg = camel_folder_get_message(folder, uids->pdata[0], &ex); - if (msg == NULL) - goto fail; - attach_message(editor, msg); - } else { - CamelMultipart *mp = camel_multipart_new(); - char *desc; - char *filename = NULL; - guint num; - - camel_data_wrapper_set_mime_type((CamelDataWrapper *)mp, "multipart/digest"); - camel_multipart_set_boundary(mp, NULL); - for (i=0;ilen;i++) { - - msg = camel_folder_get_message(folder, uids->pdata[i], &ex); - if (msg) { - mime_part = camel_mime_part_new(); - camel_mime_part_set_disposition(mime_part, "inline"); - camel_medium_set_content_object((CamelMedium *)mime_part, (CamelDataWrapper *)msg); - camel_mime_part_set_content_type(mime_part, "message/rfc822"); - camel_multipart_add_part(mp, mime_part); - camel_object_unref(mime_part); - camel_object_unref(msg); - } else { - camel_object_unref(mp); - goto fail; - } - } - mime_part = camel_mime_part_new(); - camel_medium_set_content_object((CamelMedium *)mime_part, (CamelDataWrapper *)mp); - /* translators, this count will always be >1 */ - desc = g_strdup_printf(ngettext("Attached message", "%d attached messages", uids->len), uids->len); - camel_mime_part_set_description(mime_part, desc); - g_free(desc); - - num = e_attachment_bar_get_num_attachments (E_ATTACHMENT_BAR (editor->priv->attachment_bar)); - num++; - filename = g_strdup_printf ("email%d", num); - camel_mime_part_set_filename (mime_part, filename); - - e_attachment_bar_attach_mime_part - (E_ATTACHMENT_BAR(editor->priv->attachment_bar), mime_part); - camel_object_unref(mime_part); - camel_object_unref(mp); - g_free (filename); - } - success = TRUE; - delete = action == GDK_ACTION_MOVE; - fail: - if (camel_exception_is_set(&ex)) { - char *name; - - camel_object_get(folder, NULL, CAMEL_FOLDER_NAME, &name, NULL); - e_error_run((GtkWindow *)editor, "mail-editor:attach-nomessages", - name?name:(char *)selection->data, camel_exception_get_description(&ex), NULL); - camel_object_free(folder, CAMEL_FOLDER_NAME, name); - } - camel_object_unref(folder); - } else { - e_error_run((GtkWindow *)editor, "mail-editor:attach-nomessages", - (char *)selection->data, camel_exception_get_description(&ex), NULL); - } - - camel_exception_clear(&ex); - } - - g_ptr_array_free(uids, TRUE); - - break; } - default: - d(printf ("dropping an unknown\n")); - break; - } - - printf("Drag finished, success %d delete %d\n", success, delete); - - gtk_drag_finish(context, success, delete, time); -#endif -} - -static void -drop_popup_copy (EPopup *ep, EPopupItem *item, void *data) -{ - struct _drop_data *m = data; - drop_action(m->editor, m->context, GDK_ACTION_COPY, m->selection, m->info, m->time); -} - -static void -drop_popup_move (EPopup *ep, EPopupItem *item, void *data) -{ - struct _drop_data *m = data; - drop_action(m->editor, m->context, GDK_ACTION_MOVE, m->selection, m->info, m->time); -} - -static void -drop_popup_cancel(EPopup *ep, EPopupItem *item, void *data) -{ - struct _drop_data *m = data; - gtk_drag_finish(m->context, FALSE, FALSE, m->time); -} - -static EPopupItem drop_popup_menu[] = { - { E_POPUP_ITEM, "00.emc.02", N_("_Copy"), drop_popup_copy, NULL, "mail-copy", 0 }, - { E_POPUP_ITEM, "00.emc.03", N_("_Move"), drop_popup_move, NULL, "mail-move", 0 }, - { E_POPUP_BAR, "10.emc" }, - { E_POPUP_ITEM, "99.emc.00", N_("Cancel _Drag"), drop_popup_cancel, NULL, NULL, 0 }, -}; - -static void -drop_popup_free(EPopup *ep, GSList *items, void *data) -{ - struct _drop_data *m = data; - - g_slist_free(items); - - g_object_unref(m->context); - g_object_unref(m->editor); - g_free(m->selection->data); - g_free(m->selection); - g_free(m); -} - -static void -drag_data_received (CompEditor *editor, GdkDragContext *context, - int x, int y, GtkSelectionData *selection, - guint info, guint time) -{ - if (selection->data == NULL || selection->length == -1) - return; - - if (context->action == GDK_ACTION_ASK) { - ECalPopup *ecp; - GSList *menus = NULL; - GtkMenu *menu; - int i; - struct _drop_data *m; - - m = g_malloc0(sizeof(*m)); - m->context = context; - g_object_ref(context); - m->editor = editor; - g_object_ref(editor); - m->action = context->action; - m->info = info; - m->time = time; - m->selection = g_malloc0(sizeof(*m->selection)); - m->selection->data = g_malloc(selection->length); - memcpy(m->selection->data, selection->data, selection->length); - m->selection->length = selection->length; - - ecp = e_cal_popup_new("org.gnome.evolution.calendar.editor.popup.drop"); - for (i=0;iaction, selection, info, time); - } + e_attachment_view_drag_data_received ( + view, context, x, y, selection, info, time); } static gboolean -drag_motion(GObject *o, GdkDragContext *context, gint x, gint y, guint time, CompEditor *editor) -{ - GList *targets; - GdkDragAction action, actions = 0; - - for (targets = context->targets; targets; targets = targets->next) { - int i; - - for (i=0;idata == (void *)drag_info[i].atom) - actions |= drag_info[i].actions; - } - - actions &= context->actions; - action = context->suggested_action; - /* we default to copy */ - if (action == GDK_ACTION_ASK && (actions & (GDK_ACTION_MOVE|GDK_ACTION_COPY)) != (GDK_ACTION_MOVE|GDK_ACTION_COPY)) - action = GDK_ACTION_COPY; - - gdk_drag_status(context, action, time); - - return action != 0; -} - -static void -add_to_bar (CompEditor *editor, GPtrArray *file_list, int is_inline) +drag_motion (CompEditor *editor, + GdkDragContext *context, + gint x, + gint y, + guint time) { - CompEditorPrivate *priv = editor->priv; - int i; - - for (i = 0; i < file_list->len; i++) { - CamelURL *url; + EAttachmentPaned *paned; + EAttachmentView *view; - if (!(url = camel_url_new (file_list->pdata[i], NULL))) - continue; + paned = E_ATTACHMENT_PANED (editor->priv->attachment_paned); + view = e_attachment_paned_get_view (paned); - if (!g_ascii_strcasecmp (url->protocol, "file")) { - e_attachment_bar_attach((EAttachmentBar *)priv->attachment_bar, url->path, is_inline ? "inline" : "attachment"); - } else { - e_attachment_bar_attach_remote_file ((EAttachmentBar *)priv->attachment_bar, file_list->pdata[i], is_inline ? "inline" : "attachment"); - } - - camel_url_free (url); - } + return e_attachment_view_drag_motion (view, context, x, y, time); } static GSList * get_attachment_list (CompEditor *editor) { + EAttachmentPaned *paned; + EAttachmentStore *store; + EAttachmentView *view; + GtkTreeModel *model; + GtkTreeIter iter; GSList *parts = NULL, *list = NULL, *p = NULL; const char *comp_uid = NULL; const char *local_store = e_cal_get_local_attachment_store (editor->priv->client); + gboolean valid; int ticker=0; + e_cal_component_get_uid (editor->priv->comp, &comp_uid); - parts = e_attachment_bar_get_parts((EAttachmentBar *)editor->priv->attachment_bar); + paned = E_ATTACHMENT_PANED (editor->priv->attachment_paned); + view = e_attachment_paned_get_view (paned); + store = e_attachment_view_get_store (view); - for (p = parts; p!=NULL ; p = p->next) { + model = GTK_TREE_MODEL (store); + valid = gtk_tree_model_get_iter_first (model, &iter); + + while (valid) { + EAttachment *attachment; CamelDataWrapper *wrapper; + CamelMimePart *mime_part; CamelStream *stream; char *attach_file_url; char *safe_fname, *utf8_safe_fname; char *filename; + gint column_id; + + column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT; + gtk_tree_model_get (model, &iter, column_id, &attachment, -1); + mime_part = e_attachment_get_mime_part (attachment); + g_object_unref (attachment); + + valid = gtk_tree_model_iter_next (model, &iter); + + if (mime_part == NULL) + continue; wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (p->data)); @@ -997,20 +681,15 @@ static void action_attach_cb (GtkAction *action, CompEditor *editor) { - GPtrArray *file_list; - gboolean is_inline = FALSE; - int i; - - file_list = comp_editor_select_file_attachments (editor, &is_inline); + EAttachmentPaned *paned; + EAttachmentStore *store; + EAttachmentView *view; - if (file_list) { - add_to_bar (editor, file_list, is_inline); + paned = E_ATTACHMENT_PANED (editor->priv->attachment_paned); + view = e_attachment_paned_get_view (paned); + store = e_attachment_view_get_store (view); - for (i = 0; i < file_list->len; i++) - g_free (file_list->pdata[i]); - - g_ptr_array_free (file_list, TRUE); - } + e_attachment_store_run_load_dialog (store, GTK_WINDOW (editor)); } static void @@ -1136,12 +815,19 @@ action_save_cb (GtkAction *action, CompEditor *editor) { CompEditorPrivate *priv = editor->priv; + EAttachmentPaned *paned; + EAttachmentStore *store; + EAttachmentView *view; ECalComponentText text; gboolean delegated = FALSE; gboolean read_only, correct = FALSE; ECalComponent *comp; - if (e_attachment_bar_get_download_count (E_ATTACHMENT_BAR (editor->priv->attachment_bar)) ){ + paned = E_ATTACHMENT_PANED (priv->attachment_paned); + view = e_attachment_paned_get_view (paned); + store = e_attachment_view_get_store (view); + + if (e_attachment_store_get_num_downloading (store) > 0) { gboolean response = 1; /*FIXME: Cannot use mail functions from calendar!!!! */ #if 0 @@ -1693,6 +1379,44 @@ comp_editor_map (GtkWidget *widget) GTK_WIDGET_CLASS (comp_editor_parent_class)->map (widget); } +static gboolean +comp_editor_delete_event (GtkWidget *widget, + GdkEventAny *event) +{ + CompEditor *editor; + + editor = COMP_EDITOR (widget); + + commit_all_fields (editor); + + if (prompt_and_save_changes (editor, TRUE)) + close_dialog (editor); + + return TRUE; +} + +static gboolean +comp_editor_key_press_event (GtkWidget *widget, + GdkEventKey *event) +{ + CompEditor *editor; + + editor = COMP_EDITOR (editor); + + if (event->keyval == GDK_Escape) { + commit_all_fields (editor); + + if (prompt_and_save_changes (editor, TRUE)) + close_dialog (editor); + + return TRUE; + } + + /* Chain up to parent's key_press_event() method. */ + return GTK_WIDGET_CLASS (comp_editor_parent_class)-> + key_press_event (widget, event); +} + static void comp_editor_class_init (CompEditorClass *class) { @@ -1713,6 +1437,8 @@ comp_editor_class_init (CompEditorClass *class) widget_class = GTK_WIDGET_CLASS (class); widget_class->map = comp_editor_map; + widget_class->delete_event = comp_editor_delete_event; + widget_class->key_press_event = comp_editor_key_press_event; class->help_section = "usage-calendar"; class->edit_comp = real_edit_comp; @@ -1778,8 +1504,12 @@ static void comp_editor_init (CompEditor *editor) { CompEditorPrivate *priv; + EAttachmentPaned *paned; + EAttachmentView *view; GtkActionGroup *action_group; GtkAction *action; + GtkWidget *container; + GtkWidget *widget; EShell *shell; GError *error = NULL; @@ -1795,17 +1525,18 @@ comp_editor_init (CompEditor *editor) priv->changed = FALSE; priv->needs_send = FALSE; priv->mod = CALOBJ_MOD_ALL; - priv->existing_org = FALSE; - priv->user_org = FALSE; - priv->warned = FALSE; + priv->existing_org = FALSE; + priv->user_org = FALSE; + priv->warned = FALSE; priv->is_group_item = FALSE; - priv->attachment_bar = e_attachment_bar_new (); priv->manager = gtk_ui_manager_new (); - gtk_window_add_accel_group ( - GTK_WINDOW (editor), - gtk_ui_manager_get_accel_group (priv->manager)); + gtk_window_add_accel_group ( + GTK_WINDOW (editor), + gtk_ui_manager_get_accel_group (priv->manager)); + + /* Setup Action Groups */ action_group = gtk_action_group_new ("core"); gtk_action_group_set_translation_domain ( @@ -1831,10 +1562,6 @@ comp_editor_init (CompEditor *editor) G_N_ELEMENTS (classification_radio_entries), E_CAL_COMPONENT_CLASS_PUBLIC, NULL, NULL); /* no callback */ - action = e_attachment_bar_recent_action_new ( - E_ATTACHMENT_BAR (priv->attachment_bar), - "attach-recent", _("Recent _Documents")); - gtk_action_group_add_action (action_group, action); gtk_ui_manager_insert_action_group ( priv->manager, action_group, 0); g_object_unref (action_group); @@ -1864,12 +1591,52 @@ comp_editor_init (CompEditor *editor) g_error_free (error); } - setup_widgets (editor); + /* Setup Widgets */ + + container = GTK_WIDGET (editor); + + widget = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (container), widget); + gtk_widget_show (widget); + + container = widget; + + widget = comp_editor_get_managed_widget (editor, "/main-menu"); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + gtk_widget_show (widget); + + widget = comp_editor_get_managed_widget (editor, "/main-toolbar"); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + gtk_widget_show (widget); + + widget = e_attachment_paned_new (); + gtk_container_set_border_width (GTK_CONTAINER (widget), 6); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); + priv->attachment_paned = g_object_ref (widget); + gtk_widget_show (widget); + + container = e_attachment_paned_get_content_area ( + E_ATTACHMENT_PANED (priv->attachment_paned)); + + widget = gtk_notebook_new (); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); + priv->notebook = GTK_NOTEBOOK (widget); + gtk_widget_show (widget); + + /* Add a GtkRecentAction to the "individual" action group. */ + action_group = comp_editor_get_action_group (editor, "individual"); + paned = E_ATTACHMENT_PANED (priv->attachment_paned); + view = e_attachment_paned_get_view (paned); + action = e_attachment_view_recent_action_new ( + view, "attach-recent", _("Recent _Documents")); + gtk_action_group_add_action (action_group, action); + g_object_unref (action); /* DND support */ gtk_drag_dest_set (GTK_WIDGET (editor), GTK_DEST_DEFAULT_ALL, drop_types, num_drop_types, GDK_ACTION_COPY|GDK_ACTION_ASK|GDK_ACTION_MOVE); g_signal_connect(editor, "drag_data_received", G_CALLBACK (drag_data_received), NULL); - g_signal_connect(editor, "drag-motion", G_CALLBACK(drag_motion), editor); + g_signal_connect(editor, "drag-motion", G_CALLBACK(drag_motion), NULL); gtk_window_set_type_hint (GTK_WINDOW (editor), GDK_WINDOW_TYPE_HINT_NORMAL); @@ -1928,169 +1695,16 @@ prompt_and_save_changes (CompEditor *editor, gboolean send) } } -static int -delete_event_cb (GtkWidget *widget, - GdkEvent *event, - CompEditor *editor) -{ - commit_all_fields (editor); - - if (prompt_and_save_changes (editor, TRUE)) - close_dialog (editor); - - return TRUE; -} - static void -attachment_bar_changed_cb (EAttachmentBar *bar, - CompEditor *editor) +attachment_store_changed_cb (CompEditor *editor) { - guint attachment_num = e_attachment_bar_get_num_attachments ( - E_ATTACHMENT_BAR (editor->priv->attachment_bar)); - if (attachment_num) { - gchar *num_text = g_strdup_printf ( - ngettext ("%d Attachment", "%d Attachments", attachment_num), - attachment_num); - gtk_label_set_markup (GTK_LABEL (editor->priv->attachment_expander_num), - num_text); - g_free (num_text); - - gtk_widget_show (editor->priv->attachment_expander_icon); - e_expander_set_expanded(E_EXPANDER(editor->priv->attachment_expander),TRUE); - - } else { - gtk_label_set_text (GTK_LABEL (editor->priv->attachment_expander_num), ""); - gtk_widget_hide (editor->priv->attachment_expander_icon); - e_expander_set_expanded(E_EXPANDER(editor->priv->attachment_expander),FALSE); - } - - /* Mark the editor as changed so it prompts about unsaved changes on close */ comp_editor_set_changed (editor, TRUE); - -} - -static void -attachment_expander_activate_cb (EExpander *expander, - void *data) -{ - CompEditor *editor = COMP_EDITOR (data); - gboolean show = e_expander_get_expanded (expander); - - /* Update the expander label */ - if (show) - gtk_label_set_text_with_mnemonic (GTK_LABEL (editor->priv->attachment_expander_label), - _("Hide Attachment _Bar")); - else - gtk_label_set_text_with_mnemonic (GTK_LABEL (editor->priv->attachment_expander_label), - _("Show Attachment _Bar")); -} - - -/* GtkWidget methods. */ - -static gint -editor_key_press_event (CompEditor *editor, - GdkEventKey *event) -{ - if (event->keyval == GDK_Escape) { - commit_all_fields (editor); - - if (prompt_and_save_changes (editor, TRUE)) - close_dialog (editor); - - return TRUE; - } - - return FALSE; } /* Menu callbacks */ -static void -setup_widgets (CompEditor *editor) -{ - CompEditorPrivate *priv; - GtkWidget *expander_hbox; - GtkWidget *widget; - GtkWidget *vbox; - - priv = editor->priv; - - /* Useful vbox */ - vbox = gtk_vbox_new (FALSE, 0); - gtk_container_add (GTK_CONTAINER (editor), vbox); - gtk_widget_show (vbox); - - /* Main Menu */ - widget = comp_editor_get_managed_widget (editor, "/main-menu"); - gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0); - gtk_widget_show (widget); - - /* Main Toolbar */ - widget = comp_editor_get_managed_widget (editor, "/main-toolbar"); - gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0); - gtk_widget_show (widget); - - /* Notebook */ - widget = gtk_notebook_new (); - gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE); - gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0); - gtk_widget_show (widget); - priv->notebook = GTK_NOTEBOOK (widget); - - g_signal_connect (editor, "delete_event", G_CALLBACK (delete_event_cb), editor); - g_signal_connect (editor, "key_press_event", G_CALLBACK (editor_key_press_event), editor); - - /*Attachments */ - priv->attachment_scrolled_window = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->attachment_scrolled_window), - GTK_SHADOW_IN); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->attachment_scrolled_window), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - - GTK_WIDGET_SET_FLAGS (priv->attachment_bar, GTK_CAN_FOCUS); - gtk_container_add (GTK_CONTAINER (priv->attachment_scrolled_window), - priv->attachment_bar); - gtk_widget_show (priv->attachment_bar); - g_signal_connect (priv->attachment_bar, "changed", - G_CALLBACK (attachment_bar_changed_cb), editor); - priv->attachment_expander_label = - gtk_label_new_with_mnemonic (_("Show Attachment _Bar")); - priv->attachment_expander_num = gtk_label_new (""); - gtk_label_set_use_markup (GTK_LABEL (priv->attachment_expander_num), TRUE); - gtk_misc_set_alignment (GTK_MISC (priv->attachment_expander_label), 0.0, 0.5); - gtk_misc_set_alignment (GTK_MISC (priv->attachment_expander_num), 1.0, 0.5); - expander_hbox = gtk_hbox_new (FALSE, 0); - - priv->attachment_expander_icon = gtk_image_new_from_icon_name ("mail-attachment", GTK_ICON_SIZE_MENU); - gtk_misc_set_alignment (GTK_MISC (priv->attachment_expander_icon), 1, 0.5); - gtk_widget_set_size_request (priv->attachment_expander_icon, 100, -1); - - gtk_box_pack_start (GTK_BOX (expander_hbox), priv->attachment_expander_label, - TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (expander_hbox), priv->attachment_expander_icon, - TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (expander_hbox), priv->attachment_expander_num, - TRUE, TRUE, 0); - gtk_widget_show_all (expander_hbox); - gtk_widget_hide (priv->attachment_expander_icon); - - priv->attachment_expander = e_expander_new (""); - e_expander_set_label_widget (E_EXPANDER (priv->attachment_expander), expander_hbox); - atk_object_set_name (gtk_widget_get_accessible (priv->attachment_expander), _("Show Attachments")); - atk_object_set_description (gtk_widget_get_accessible (priv->attachment_expander), _("Press space key to toggle attachment bar")); - gtk_container_add (GTK_CONTAINER (priv->attachment_expander), priv->attachment_scrolled_window); - - gtk_box_pack_start (GTK_BOX (vbox), priv->attachment_expander, FALSE, FALSE, 4); - gtk_widget_show (priv->attachment_expander); - e_expander_set_expanded (E_EXPANDER (priv->attachment_expander), FALSE); - g_signal_connect_after (priv->attachment_expander, "activate", - G_CALLBACK (attachment_expander_activate_cb), editor); -} - - static void comp_editor_show_help (CompEditor *editor) { @@ -2637,12 +2251,16 @@ comp_editor_get_client (CompEditor *editor) static void set_attachment_list (CompEditor *editor, GSList *attach_list) { - GSList *p = NULL; - const char *comp_uid= NULL; + EAttachmentPaned *paned; + EAttachmentStore *store; + EAttachmentView *view; + GSList *iter = NULL; - e_cal_component_get_uid (editor->priv->comp, &comp_uid); + paned = E_ATTACHMENT_PANED (editor->priv->attachment_paned); + view = e_attachment_paned_get_view (paned); + store = e_attachment_view_get_store (view); - if (e_attachment_bar_get_num_attachments (E_ATTACHMENT_BAR (editor->priv->attachment_bar))) { + if (e_attachment_store_get_num_attachments (store) > 0) { /* To prevent repopulating the * bar due to redraw functions in fill_widget. * Assumes it can be set only once. @@ -2650,100 +2268,42 @@ set_attachment_list (CompEditor *editor, GSList *attach_list) return; } - for (p = attach_list; p != NULL; p = p->next) { - char *attach_filename; - CamelMimePart *part; - CamelDataWrapper *wrapper; - CamelStream *stream; - struct stat statbuf; - char *mime_type, *file_name; - char *ptr; - - attach_filename = (char *) p->data; - /* should we assert if g_str_has_prefix (attach_filename, "file://")) - * here - */ - /* get url sans protocol and add it to the bar. - * how to set the filename properly */ - file_name = g_filename_from_uri (attach_filename, NULL, NULL); - if (!file_name) - continue; - - if (g_stat (file_name, &statbuf) < 0) { - g_warning ("Cannot attach file %s: %s", file_name, g_strerror (errno)); - g_free (file_name); - continue; - } - - /* return if it's not a regular file */ - if (!S_ISREG (statbuf.st_mode)) { - g_warning ("Cannot attach file %s: not a regular file", file_name); - g_free (file_name); - return; - } + for (iter = attach_list; iter != NULL; iter = iter->next) { + EAttachment *attachment; + const gchar *uri = iter->data; - stream = camel_stream_fs_new_with_name (file_name, O_RDONLY, 0); - if (!stream) { - g_warning ("Cannot attach file %s: %s", file_name, g_strerror (errno)); - g_free (file_name); - return; - } - - mime_type = e_util_guess_mime_type (file_name, TRUE); - if (mime_type) { - if (!g_ascii_strcasecmp (mime_type, "message/rfc822")) { - wrapper = (CamelDataWrapper *) camel_mime_message_new (); - } else { - wrapper = camel_data_wrapper_new (); - } - - 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"); - } - - camel_object_unref (stream); - - part = camel_mime_part_new (); - camel_medium_set_content_object (CAMEL_MEDIUM (part), wrapper); - camel_object_unref (wrapper); - - camel_mime_part_set_disposition (part, "attachment"); - - ptr = strstr (file_name, comp_uid); - if (ptr) { - ptr += strlen(comp_uid); - if (*ptr++ == '-') - camel_mime_part_set_filename (part, ptr); - } - g_free (file_name); - - e_attachment_bar_attach_mime_part ((EAttachmentBar *) editor->priv->attachment_bar, part); - e_expander_set_expanded (E_EXPANDER (editor->priv->attachment_expander), TRUE); - - camel_object_unref (part); + attachment = e_attachment_new_for_uri (uri); + e_attachment_store_add_attachment (store, attachment); + g_object_unref (attachment); } } static void fill_widgets (CompEditor *editor) { + EAttachmentPaned *paned; + EAttachmentStore *store; + EAttachmentView *view; CompEditorPrivate *priv; GList *l; + paned = E_ATTACHMENT_PANED (editor->priv->attachment_paned); + view = e_attachment_paned_get_view (paned); + store = e_attachment_view_get_store (view); + priv = editor->priv; /*Check if attachments are available here and set them*/ if (e_cal_component_has_attachments (priv->comp)) { GSList *attachment_list = NULL; e_cal_component_get_attachment_list (priv->comp, &attachment_list); - g_signal_handlers_block_by_func(priv->attachment_bar, G_CALLBACK (attachment_bar_changed_cb), editor); + g_signal_handlers_block_by_func ( + store, G_CALLBACK (attachment_store_changed_cb), + editor); set_attachment_list (editor, attachment_list); - g_signal_handlers_unblock_by_func(priv->attachment_bar, G_CALLBACK (attachment_bar_changed_cb), editor); + g_signal_handlers_unblock_by_func( + store, G_CALLBACK (attachment_store_changed_cb), + editor); g_slist_foreach (attachment_list, (GFunc)g_free, NULL); g_slist_free (attachment_list); } @@ -3068,20 +2628,43 @@ comp_editor_close (CompEditor *editor) GSList * comp_editor_get_mime_attach_list (CompEditor *editor) { + EAttachmentPaned *paned; + EAttachmentStore *store; + EAttachmentView *view; + GtkTreeModel *model; + GtkTreeIter iter; struct CalMimeAttach *cal_mime_attach; - GSList *attach_list = NULL, *l, *parts; + GSList *attach_list = NULL; + gboolean valid; + + paned = E_ATTACHMENT_PANED (editor->priv->attachment_paned); + view = e_attachment_paned_get_view (paned); + store = e_attachment_view_get_store (view); - /* TODO assert sanity of bar */ - parts = e_attachment_bar_get_parts (E_ATTACHMENT_BAR (editor->priv->attachment_bar)); - for (l = parts; l ; l = l->next) { + model = GTK_TREE_MODEL (store); + valid = gtk_tree_model_get_iter_first (model, &iter); + while (valid) { + EAttachment *attachment; CamelDataWrapper *wrapper; + CamelMimePart *mime_part; CamelStreamMem *mstream; unsigned char *buffer = NULL; const char *desc, *disp; + gint column_id; + + column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT; + gtk_tree_model_get (model, &iter, column_id, &attachment, -1); + mime_part = e_attachment_get_mime_part (attachment); + g_object_unref (attachment); + + valid = gtk_tree_model_iter_next (model, &iter); + + if (mime_part == NULL) + continue; cal_mime_attach = g_malloc0 (sizeof (struct CalMimeAttach)); - wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (l->data)); + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); mstream = (CamelStreamMem *) camel_stream_mem_new (); camel_data_wrapper_decode_to_stream (wrapper, (CamelStream *) mstream); @@ -3089,15 +2672,14 @@ comp_editor_get_mime_attach_list (CompEditor *editor) cal_mime_attach->encoded_data = (char *)buffer; cal_mime_attach->length = mstream->buffer->len; - cal_mime_attach->filename = g_strdup (camel_mime_part_get_filename - ((CamelMimePart *) l->data)); - desc = camel_mime_part_get_description ((CamelMimePart *) l->data); + cal_mime_attach->filename = g_strdup (camel_mime_part_get_filename (mime_part)); + desc = camel_mime_part_get_description (mime_part); if (!desc || *desc == '\0') desc = _("attachment"); cal_mime_attach->description = g_strdup (desc); cal_mime_attach->content_type = g_strdup (camel_data_wrapper_get_mime_type (wrapper)); - disp = camel_mime_part_get_disposition ((CamelMimePart *)l->data); + disp = camel_mime_part_get_disposition (mime_part); if (disp && !g_ascii_strcasecmp(disp, "inline")) cal_mime_attach->disposition = TRUE; @@ -3107,8 +2689,6 @@ comp_editor_get_mime_attach_list (CompEditor *editor) } - g_slist_free (parts); - return attach_list; } diff --git a/calendar/gui/e-cal-popup.c b/calendar/gui/e-cal-popup.c index 1495e10506..5839991060 100644 --- a/calendar/gui/e-cal-popup.c +++ b/calendar/gui/e-cal-popup.c @@ -86,395 +86,11 @@ ecalp_target_free(EPopup *ep, EPopupTarget *t) /* Standard menu code */ -static char * -temp_save_part(CamelMimePart *part, char *path, gboolean file) -{ - const char *filename; - char *tmpdir, *utf8_mfilename = NULL, *mfilename = NULL, *usepath; - CamelStream *stream; - CamelDataWrapper *wrapper; - - if (!path) { - tmpdir = e_mkdtemp("evolution-tmp-XXXXXX"); - if (tmpdir == NULL) { - return NULL; - } - - filename = camel_mime_part_get_filename (part); - if (filename == NULL) { - /* This is the default filename used for temporary file creation */ - filename = _("Unknown"); - } else { - utf8_mfilename = g_strdup (filename); - e_filename_make_safe (utf8_mfilename); - mfilename = g_filename_from_utf8 ((const char *) utf8_mfilename, -1, NULL, NULL, NULL); - g_free (utf8_mfilename); - filename = (const char *) mfilename; - } - - path = g_build_filename(tmpdir, filename, NULL); - g_free(tmpdir); - g_free(mfilename); - } else if (!file) { - tmpdir = path; - filename = camel_mime_part_get_filename (part); - if (filename == NULL) { - /* This is the default filename used for temporary file creation */ - filename = _("Unknown"); - } else { - utf8_mfilename = g_strdup (filename); - e_filename_make_safe (utf8_mfilename); - mfilename = g_filename_from_utf8 ((const char *)utf8_mfilename, -1, NULL, NULL, NULL); - g_free (utf8_mfilename); - filename = (const char *) mfilename; - } - - path = g_build_filename(tmpdir, filename, NULL); - g_free(mfilename); - } - - if (strstr (path, "://")) - usepath = path; - else - usepath = g_strjoin (NULL, "file://", path, NULL); - - wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part)); - stream = camel_stream_vfs_new_with_uri (usepath, CAMEL_STREAM_VFS_CREATE); - - if (usepath != path) - g_free (usepath); - - if (!stream) { - /* TODO handle error conditions */ - g_message ("DEBUG: could not open the file to write\n"); - return NULL; - } - - if (camel_data_wrapper_decode_to_stream (wrapper, (CamelStream *) stream) == -1) { - camel_stream_close (stream); - camel_object_unref (stream); - g_message ("DEBUG: could not write to file\n"); - return NULL; - } - - camel_stream_close(stream); - camel_object_unref(stream); - - return path; -} - -static void -ecalp_part_popup_saveas(EPopup *ep, EPopupItem *item, void *data) -{ - EPopupTarget *t = ep->target; - EAttachment *attachment; - CamelMimePart *part = NULL; - char *file, *mfilename = NULL; - const char *filename; - - attachment = E_ATTACHMENT (((ECalPopupTargetAttachments *) t)->attachments->data); - part = e_attachment_get_mime_part (attachment); - filename = camel_mime_part_get_filename (part); - if (filename == NULL) { - /* This is the default filename used for temporary file creation */ - filename = _("Unknown"); - } else { - mfilename = g_strdup(filename); - e_filename_make_safe(mfilename); - filename = mfilename; - } - file = e_file_dialog_save (_("Save As..."), filename); - - if (file) - temp_save_part (part, file, TRUE); - - g_free (file); - g_free (mfilename); -} - -static void -ecalp_part_popup_save_selected(EPopup *ep, EPopupItem *item, void *data) -{ - GSList *parts; - EPopupTarget *t = ep->target; - char *dir, *path; - - dir = e_file_dialog_save_folder (_("Select folder to save selected attachments...")); - parts = ((ECalPopupTargetAttachments *) t)->attachments; - - for (;parts; parts=parts->next) { - EAttachment *attachment = parts->data; - CamelMimePart *mime_part; - - mime_part = e_attachment_get_mime_part (attachment); - path = temp_save_part (mime_part, dir, FALSE); - /* Probably we 'll do some reporting in next release, like listing the saved files and locations */ - g_free (path); - } -} - -static void -ecalp_part_popup_set_background(EPopup *ep, EPopupItem *item, void *data) -{ - EAttachment *attachment; - EPopupTarget *t = ep->target; - GConfClient *gconf; - char *str, *filename, *path, *extension; - unsigned int i=1; - CamelMimePart *part; - - attachment = E_ATTACHMENT (((ECalPopupTargetAttachments *) t)->attachments->data); - part = e_attachment_get_mime_part (attachment); - - if (!part) - return; - - filename = g_strdup(camel_mime_part_get_filename(part)); - - /* if filename is blank, create a default filename based on MIME type */ - if (!filename || !filename[0]) { - CamelContentType *ct; - - ct = camel_mime_part_get_content_type(part); - g_free (filename); - filename = g_strdup_printf (_("untitled_image.%s"), ct->subtype); - } - - e_filename_make_safe(filename); - - path = g_build_filename(g_get_home_dir(), ".gnome2", "wallpapers", filename, NULL); - - extension = strrchr(filename, '.'); - if (extension) - *extension++ = 0; - - /* if file exists, stick a (number) on the end */ - while (g_file_test(path, G_FILE_TEST_EXISTS)) { - char *name; - name = g_strdup_printf(extension?"%s (%d).%s":"%s (%d)", filename, i++, extension); - g_free(path); - path = g_build_filename(g_get_home_dir(), ".gnome2", "wallpapers", name, NULL); - g_free(name); - } - - g_free(filename); - - if (temp_save_part(part, path, TRUE)) { - gconf = gconf_client_get_default(); - - /* if the filename hasn't changed, blank the filename before - * setting it so that gconf detects a change and updates it */ - if ((str = gconf_client_get_string(gconf, "/desktop/gnome/background/picture_filename", NULL)) != NULL - && strcmp (str, path) == 0) { - gconf_client_set_string(gconf, "/desktop/gnome/background/picture_filename", "", NULL); - } - - g_free (str); - gconf_client_set_string(gconf, "/desktop/gnome/background/picture_filename", path, NULL); - - /* if GNOME currently doesn't display a picture, set to "wallpaper" - * display mode, otherwise leave it alone */ - if ((str = gconf_client_get_string(gconf, "/desktop/gnome/background/picture_options", NULL)) == NULL - || strcmp(str, "none") == 0) { - gconf_client_set_string(gconf, "/desktop/gnome/background/picture_options", "wallpaper", NULL); - } - - gconf_client_suggest_sync(gconf, NULL); - - g_free(str); - g_object_unref(gconf); - } - - g_free(path); -} - -static const EPopupItem ecalp_standard_part_apps_bar = { E_POPUP_BAR, "99.object" }; - -static ECalPopupItem ecalp_attachment_object_popups[] = { - { E_POPUP_ITEM, "00.attach.00", N_("_Save As..."), ecalp_part_popup_saveas, NULL, "document-save-as", E_CAL_POPUP_ATTACHMENTS_ONE }, - { E_POPUP_ITEM, "00.attach.10", N_("Set as _Background"), ecalp_part_popup_set_background, NULL, NULL, E_CAL_POPUP_ATTACHMENTS_IMAGE }, - { E_POPUP_ITEM, "00.attach.20", N_("_Save Selected"), ecalp_part_popup_save_selected, NULL, "document-save-as", E_CAL_POPUP_ATTACHMENTS_MULTIPLE }, - { E_POPUP_BAR, "05.attach", }, -}; - -static void -ecalp_apps_open_in(EPopup *ep, EPopupItem *item, void *data) -{ - char *path; - EAttachment *attachment; - EPopupTarget *target = ep->target; - CamelMimePart *part; - - attachment = E_ATTACHMENT (((ECalPopupTargetAttachments *) target)->attachments->data); - part = e_attachment_get_mime_part (attachment); - - path = temp_save_part(part, NULL, FALSE); - if (path) { - GAppInfo *app = item->user_data; - GList *uris = NULL; - GError *error = NULL; - - if (g_app_info_supports_files (app)) { - GFile *file = g_file_new_for_path (path); - - uris = g_list_append (uris, file); - g_app_info_launch (app, uris, NULL, &error); - g_object_unref (file); - } else { - char *uri; - - uri = e_util_filename_to_uri (path); - uris = g_list_append (uris, uri); - - g_app_info_launch_uris (app, uris, NULL, &error); - g_free (uri); - } - - if (error) { - g_warning ("%s", error->message); - g_error_free (error); - } - - g_list_free (uris); - g_free (path); - } -} - -static void -ecalp_apps_popup_free(EPopup *ep, GSList *free_list, void *data) -{ - while (free_list) { - GSList *n = free_list->next; - EPopupItem *item = free_list->data; - - if (item->user_data && item->activate == ecalp_apps_open_in) - g_object_unref (item->user_data); - - g_free(item->path); - g_free(item->label); - g_free(item); - g_slist_free_1(free_list); - - free_list = n; - } -} - -static void -ecalp_standard_items_free(EPopup *ep, GSList *items, void *data) -{ - g_slist_free(items); -} - -static void -ecalp_standard_menu_factory (EPopup *ecalp, void *data) -{ - int i, len; - EPopupItem *items; - GSList *menus = NULL; - GList *apps = NULL; - char *mime_type = NULL; - const char *filename = NULL; - - switch (ecalp->target->type) { - case E_CAL_POPUP_TARGET_ATTACHMENTS: { - ECalPopupTargetAttachments *t = (ECalPopupTargetAttachments *)ecalp->target; - GSList *list = t->attachments; - EAttachment *attachment; - CamelMimePart *mime_part; - - items = ecalp_attachment_object_popups; - len = G_N_ELEMENTS(ecalp_attachment_object_popups); - - if (g_slist_length(list) != 1 || !((EAttachment *)list->data)->is_available_local) { - break; - } - - /* Only one attachment selected */ - attachment = list->data; - mime_part = e_attachment_get_mime_part (attachment); - mime_type = camel_data_wrapper_get_mime_type (CAMEL_DATA_WRAPPER (mime_part)); - filename = camel_mime_part_get_filename (mime_part); - - - break; } - default: - items = NULL; - len = 0; - } - - if (mime_type) { - gchar *cp; - - /* does gvfs expect lowercase MIME types? */ - for (cp = mime_type; *cp != '\0'; cp++) - *cp = g_ascii_tolower (*cp); - - cp = g_content_type_from_mime_type (mime_type); - apps = g_app_info_get_all_for_type (cp ? cp : mime_type); - g_free (cp); - - if (apps == NULL && strcmp(mime_type, "application/octet-stream") == 0) { - if (filename) { - gchar *name_type; - - name_type = e_util_guess_mime_type (filename, FALSE); - cp = g_content_type_from_mime_type (name_type); - apps = g_app_info_get_all_for_type (cp ? cp : name_type); - g_free (cp); - g_free (name_type); - } - } - g_free (mime_type); - - if (apps) { - GSList *open_menus = NULL; - GList *l; - - menus = g_slist_prepend(menus, (void *)&ecalp_standard_part_apps_bar); - - for (l = apps, i = 0; l; l = l->next, i++) { - GAppInfo *app = l->data; - EPopupItem *item; - - if (!g_app_info_should_show (app)) { - g_object_unref (app); - l->data = NULL; - continue; - } - - item = g_malloc0(sizeof(*item)); - item->type = E_POPUP_ITEM; - item->path = g_strdup_printf("99.object.%02d", i); - item->label = g_strdup_printf(_("Open in %s..."), g_app_info_get_name (app)); - item->activate = ecalp_apps_open_in; - item->user_data = app; - - open_menus = g_slist_prepend(open_menus, item); - } - - if (open_menus) - e_popup_add_items(ecalp, open_menus, NULL, ecalp_apps_popup_free, NULL); - - g_list_free (apps); - } - } - - for (i=0;itarget->mask) == 0) - menus = g_slist_prepend(menus, &items[i]); - } - - if (menus) - e_popup_add_items(ecalp, menus, NULL, ecalp_standard_items_free, NULL); -} - static void ecalp_class_init(GObjectClass *klass) { klass->finalize = ecalp_finalise; ((EPopupClass *)klass)->target_free = ecalp_target_free; - - e_popup_class_add_factory((EPopupClass *)klass, NULL, ecalp_standard_menu_factory, NULL); } GType @@ -763,64 +379,6 @@ e_cal_popup_target_new_source(ECalPopup *eabp, ESourceSelector *selector) return t; } -/** - * e_cal_popup_target_new_attachments: - * @ecp: - * @attachments: A list of CalAttachment objects, reffed for - * the list. Will be unreff'd once finished with. - * - * Owns the list @attachments and their items after they're passed in. - * - * Return value: - **/ -ECalPopupTargetAttachments * -e_cal_popup_target_new_attachments(ECalPopup *ecp, CompEditor *editor, GSList *attachments) -{ - ECalPopupTargetAttachments *t = e_popup_target_new(&ecp->popup, E_CAL_POPUP_TARGET_ATTACHMENTS, sizeof(*t)); - guint32 mask = ~0; - int len = g_slist_length(attachments); - ECal *client = comp_editor_get_client (editor); - CompEditorFlags flags = comp_editor_get_flags (editor); - gboolean read_only = FALSE; - GError *error = NULL; - - if (!e_cal_is_read_only (client, &read_only, &error)) { - if (error->code != E_CALENDAR_STATUS_BUSY) - read_only = TRUE; - g_error_free (error); - } - - if (!read_only && (!(flags & COMP_EDITOR_MEETING) || - (flags & COMP_EDITOR_NEW_ITEM) || - (flags & COMP_EDITOR_USER_ORG))) - mask &= ~ E_CAL_POPUP_ATTACHMENTS_MODIFY; - - t->attachments = attachments; - if (len > 0) - mask &= ~ E_CAL_POPUP_ATTACHMENTS_MANY; - - if (len == 1 && ((EAttachment *)attachments->data)->is_available_local) { - EAttachment *attachment; - CamelMimePart *mime_part; - CamelContentType *mime_type; - - attachment = attachments->data; - mime_part = e_attachment_get_mime_part (attachment); - mime_type = CAMEL_DATA_WRAPPER (mime_part)->mime_type; - - if (camel_content_type_is (mime_type, "image", "*")) - mask &= ~ E_CAL_POPUP_ATTACHMENTS_IMAGE; - mask &= ~ E_CAL_POPUP_ATTACHMENTS_ONE; - } - - if (len > 1) - mask &= ~ E_CAL_POPUP_ATTACHMENTS_MULTIPLE; - - t->target.mask = mask; - - return t; -} - /* ********************************************************************** */ /* Popup menu plugin handler */ diff --git a/composer/e-composer-actions.c b/composer/e-composer-actions.c index 01c05ecc61..2af7a63fcd 100644 --- a/composer/e-composer-actions.c +++ b/composer/e-composer-actions.c @@ -29,69 +29,13 @@ static void action_attach_cb (GtkAction *action, EMsgComposer *composer) { - EAttachmentBar *bar; - GtkWidget *dialog; - GtkWidget *option; - GSList *uris, *iter; - const gchar *disposition; - gboolean active; - gint response; - - bar = E_ATTACHMENT_BAR (composer->priv->attachment_bar); - - dialog = gtk_file_chooser_dialog_new ( - _("Insert Attachment"), - GTK_WINDOW (composer), - 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-message-new"); + EAttachmentView *view; + EAttachmentStore *store; - option = gtk_check_button_new_with_mnemonic ( - _("_Suggest automatic display of attachment")); - gtk_widget_show (option); - gtk_file_chooser_set_extra_widget ( - GTK_FILE_CHOOSER (dialog), option); + view = e_msg_composer_get_attachment_view (composer); + store = e_attachment_view_get_store (view); - response = gtkhtml_editor_file_chooser_dialog_run ( - GTKHTML_EDITOR (composer), 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; - - if (!g_ascii_strcasecmp (url->protocol, "file")) - e_attachment_bar_attach (bar, url->path, disposition); - else - e_attachment_bar_attach_remote_file (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); + e_attachment_store_run_load_dialog (store, GTK_WINDOW (composer)); } static void @@ -327,10 +271,10 @@ static void action_new_message_cb (GtkAction *action, EMsgComposer *composer) { - GtkWidget *widget; + EMsgComposer *new_composer; - widget = e_msg_composer_new (); - gtk_widget_show (widget); + new_composer = e_msg_composer_new (); + gtk_widget_show (GTK_WIDGET (new_composer)); } static void diff --git a/composer/e-composer-private.c b/composer/e-composer-private.c index f910f978a6..aba1fdbe9f 100644 --- a/composer/e-composer-private.c +++ b/composer/e-composer-private.c @@ -51,22 +51,25 @@ composer_setup_charset_menu (EMsgComposer *composer) static void composer_setup_recent_menu (EMsgComposer *composer) { + EAttachmentView *view; GtkUIManager *manager; GtkAction *action = NULL; - const gchar *path, *action_name; + const gchar *action_name; + const gchar *path; guint merge_id; + view = e_msg_composer_get_attachment_view (composer); manager = gtkhtml_editor_get_ui_manager (GTKHTML_EDITOR (composer)); - action_name = "recent-menu"; path = "/main-menu/insert-menu/insert-menu-top/recent-placeholder"; merge_id = gtk_ui_manager_new_merge_id (manager); + action_name = "recent-menu"; - action = e_attachment_bar_recent_action_new ( - e_msg_composer_get_attachment_bar (composer), - action_name, _("Recent _Documents")); + action = e_attachment_view_recent_action_new ( + view, action_name, _("Recent _Documents")); if (action != NULL) { - gtk_action_group_add_action (composer->priv->composer_actions, action); + gtk_action_group_add_action ( + composer->priv->composer_actions, action); gtk_ui_manager_add_ui ( manager, merge_id, path, @@ -85,9 +88,8 @@ e_composer_private_init (EMsgComposer *composer) GtkhtmlEditor *editor; GtkUIManager *manager; - GtkWidget *widget; - GtkWidget *expander; GtkWidget *container; + GtkWidget *widget; GtkWidget *send_widget; const gchar *path; gchar *filename; @@ -138,66 +140,33 @@ e_composer_private_init (EMsgComposer *composer) /* Construct the header table. */ + container = editor->vbox; + widget = e_composer_header_table_new (); gtk_container_set_border_width (GTK_CONTAINER (widget), 6); - gtk_box_pack_start (GTK_BOX (editor->vbox), widget, FALSE, FALSE, 0); - gtk_box_reorder_child (GTK_BOX (editor->vbox), widget, 2); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + gtk_box_reorder_child (GTK_BOX (container), widget, 2); priv->header_table = g_object_ref (widget); gtk_widget_show (widget); - /* Construct attachment widgets. - * XXX Move this stuff into a new custom widget. */ + /* Construct the attachment paned. */ - widget = gtk_expander_new (NULL); - gtk_expander_set_expanded (GTK_EXPANDER (widget), FALSE); + widget = e_attachment_paned_new (); gtk_container_set_border_width (GTK_CONTAINER (widget), 6); - gtk_box_pack_start (GTK_BOX (editor->vbox), widget, FALSE, FALSE, 0); - priv->attachment_expander = g_object_ref (widget); - gtk_widget_show (widget); - expander = widget; - - 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_container_add (GTK_CONTAINER (expander), widget); - priv->attachment_scrolled_window = g_object_ref (widget); - gtk_widget_show (widget); - container = widget; - - widget = e_attachment_bar_new (); - GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS); - gtk_container_add (GTK_CONTAINER (container), widget); - priv->attachment_bar = g_object_ref (widget); - gtk_widget_show (widget); - - widget = gtk_hbox_new (FALSE, 0); - gtk_expander_set_label_widget (GTK_EXPANDER (expander), widget); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); + priv->attachment_paned = g_object_ref (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); - priv->attachment_expander_label = g_object_ref (widget); - gtk_widget_show (widget); + /* Reparent the scrolled window containing the GtkHTML widget + * into the content area of the top attachment pane. */ - 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); - priv->attachment_expander_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); - priv->attachment_expander_num = g_object_ref (widget); - gtk_widget_show (widget); + widget = GTK_WIDGET (gtkhtml_editor_get_html (editor)); + widget = gtk_widget_get_parent (widget); + container = e_attachment_paned_get_content_area ( + E_ATTACHMENT_PANED (priv->attachment_paned)); + gtk_widget_reparent (widget, container); + gtk_box_set_child_packing ( + GTK_BOX (container), widget, TRUE, TRUE, 0, GTK_PACK_START); composer_setup_recent_menu (composer); } @@ -223,6 +192,11 @@ e_composer_private_dispose (EMsgComposer *composer) composer->priv->header_table = NULL; } + if (composer->priv->attachment_paned != NULL) { + g_object_unref (composer->priv->attachment_paned); + composer->priv->attachment_paned = NULL; + } + if (composer->priv->charset_actions != NULL) { g_object_unref (composer->priv->charset_actions); composer->priv->charset_actions = NULL; diff --git a/composer/e-composer-private.h b/composer/e-composer-private.h index 5984d3cb2b..fc30472897 100644 --- a/composer/e-composer-private.h +++ b/composer/e-composer-private.h @@ -25,13 +25,14 @@ #include -#include "e-attachment-bar.h" #include "e-composer-actions.h" #include "e-composer-autosave.h" #include "e-composer-header-table.h" #include "e-util/e-binding.h" #include "e-util/e-util.h" #include "e-util/gconf-bridge.h" +#include "widgets/misc/e-attachment-paned.h" +#include "widgets/misc/e-attachment-store.h" #define E_MSG_COMPOSER_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ @@ -94,6 +95,8 @@ struct _EMsgComposerPrivate { GtkWidget *html_editor; GtkWidget *header_table; + GtkWidget *attachment_paned; + GtkActionGroup *charset_actions; GtkActionGroup *composer_actions; @@ -102,13 +105,6 @@ struct _EMsgComposerPrivate { GtkWidget *focused_entry; - GtkWidget *attachment_bar; - GtkWidget *attachment_scrolled_window; - GtkWidget *attachment_expander; - GtkWidget *attachment_expander_label; - GtkWidget *attachment_expander_icon; - GtkWidget *attachment_expander_num; - GtkWidget *address_dialog; GHashTable *inline_images; @@ -117,7 +113,6 @@ struct _EMsgComposerPrivate { gchar *mime_type, *mime_body, *charset; - guint32 attachment_bar_visible : 1; guint32 is_alternative : 1; guint32 autosaved : 1; guint32 mode_post : 1; diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c index ab2dfc8172..138da07389 100644 --- a/composer/e-msg-composer.c +++ b/composer/e-msg-composer.c @@ -76,13 +76,11 @@ #include #endif -#include "mail/em-popup.h" #include "mail/em-utils.h" #include "mail/mail-tools.h" #include "e-msg-composer.h" #include "e-attachment.h" -#include "e-attachment-bar.h" #include "e-composer-autosave.h" #include "e-composer-private.h" #include "e-composer-header-table.h" @@ -178,7 +176,6 @@ static GSList *all_composers = NULL; static GList *add_recipients (GList *list, const gchar *recips); static void handle_mailto (EMsgComposer *composer, const gchar *mailto); -static void handle_uri (EMsgComposer *composer, const gchar *uri, gboolean html_dnd); /* used by e_msg_composer_add_message_attachments () */ static void add_attachments_from_multipart (EMsgComposer *composer, CamelMultipart *multipart, @@ -510,7 +507,8 @@ build_message (EMsgComposer *composer, GtkhtmlEditor *editor; EMsgComposerPrivate *p = composer->priv; - EAttachmentBar *attachment_bar; + EAttachmentView *view; + EAttachmentStore *store; EComposerHeaderTable *table; GtkToggleAction *action; CamelDataWrapper *plain, *html, *current; @@ -538,7 +536,8 @@ build_message (EMsgComposer *composer, editor = GTKHTML_EDITOR (composer); table = e_msg_composer_get_header_table (composer); account = e_composer_header_table_get_account (table); - attachment_bar = E_ATTACHMENT_BAR (p->attachment_bar); + view = e_msg_composer_get_attachment_view (composer); + store = e_attachment_view_get_store (view); session = e_msg_composer_get_session (composer); /* evil kludgy hack for Redirect */ @@ -699,7 +698,7 @@ build_message (EMsgComposer *composer, } else current = plain; - if (e_attachment_bar_get_num_attachments (attachment_bar)) { + if (e_attachment_store_get_num_attachments (store) > 0) { CamelMultipart *multipart = camel_multipart_new (); if (p->is_alternative) { @@ -718,7 +717,8 @@ build_message (EMsgComposer *composer, camel_multipart_add_part (multipart, part); camel_object_unref (part); - e_attachment_bar_to_multipart (attachment_bar, multipart, p->charset); + e_attachment_store_add_to_multipart ( + store, multipart, p->charset); if (p->is_alternative) { for (i = camel_multipart_get_number (multipart); i > 1; i--) { @@ -1252,64 +1252,16 @@ autosave_load_draft (const gchar *filename) /* Miscellaneous callbacks. */ static void -attachment_bar_changed_cb (EAttachmentBar *attachment_bar, - EMsgComposer *composer) +attachment_store_changed_cb (EMsgComposer *composer) { GtkhtmlEditor *editor; - GtkWidget *widget; - guint attachment_num; - - editor = GTKHTML_EDITOR (composer); - attachment_num = e_attachment_bar_get_num_attachments (attachment_bar); - - if (attachment_num > 0) { - gchar *markup; - - markup = g_strdup_printf ( - "%d %s", attachment_num, ngettext ( - "Attachment", "Attachments", attachment_num)); - widget = composer->priv->attachment_expander_num; - gtk_label_set_markup (GTK_LABEL (widget), markup); - g_free (markup); - - gtk_widget_show (composer->priv->attachment_expander_icon); - - widget = composer->priv->attachment_expander; - gtk_expander_set_expanded (GTK_EXPANDER (widget), TRUE); - } else { - widget = composer->priv->attachment_expander_num; - gtk_label_set_text (GTK_LABEL (widget), ""); - - gtk_widget_hide (composer->priv->attachment_expander_icon); - - widget = composer->priv->attachment_expander; - gtk_expander_set_expanded (GTK_EXPANDER (widget), FALSE); - } /* Mark the editor as changed so it prompts about unsaved changes on close. */ + editor = GTKHTML_EDITOR (composer); gtkhtml_editor_set_changed (editor, TRUE); } -static void -attachment_expander_notify_cb (GtkExpander *expander, - GParamSpec *pspec, - EMsgComposer *composer) -{ - GtkLabel *label; - const gchar *text; - - label = GTK_LABEL (composer->priv->attachment_expander_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 msg_composer_subject_changed_cb (EMsgComposer *composer) { @@ -1505,35 +1457,6 @@ msg_composer_account_list_changed_cb (EMsgComposer *composer) g_object_unref (iterator); } -static void -msg_composer_attach_message (EMsgComposer *composer, - CamelMimeMessage *msg) -{ - CamelMimePart *mime_part; - GString *description; - const gchar *subject; - EMsgComposerPrivate *p = composer->priv; - - mime_part = camel_mime_part_new (); - camel_mime_part_set_disposition (mime_part, "inline"); - subject = camel_mime_message_get_subject (msg); - - 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); - - camel_medium_set_content_object ( - (CamelMedium *) mime_part, (CamelDataWrapper *) msg); - camel_mime_part_set_content_type (mime_part, "message/rfc822"); - - e_attachment_bar_attach_mime_part ( - E_ATTACHMENT_BAR (p->attachment_bar), mime_part); - - camel_object_unref (mime_part); -} - struct _drop_data { EMsgComposer *composer; @@ -1544,23 +1467,9 @@ struct _drop_data { guint32 action; guint info; guint time; - - unsigned int move:1; - unsigned int moved:1; - unsigned int aborted:1; }; -int -e_msg_composer_get_remote_download_count (EMsgComposer *composer) -{ - EAttachmentBar *attachment_bar; - - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), 0); - - attachment_bar = E_ATTACHMENT_BAR (composer->priv->attachment_bar); - return e_attachment_bar_get_download_count (attachment_bar); -} - +#if 0 /* KILL-BONOBO */ static void drop_action (EMsgComposer *composer, GdkDragContext *context, @@ -1570,77 +1479,12 @@ drop_action (EMsgComposer *composer, guint time, gboolean html_dnd) { - char *tmp, *str, **urls; CamelMimePart *mime_part; - CamelStream *stream; CamelMimeMessage *msg; - char *content_type; int i, success = FALSE, delete = FALSE; EMsgComposerPrivate *p = composer->priv; switch (info) { - case DND_TYPE_MESSAGE_RFC822: - d (printf ("dropping a message/rfc822\n")); - /* write the message (s) out to a CamelStream so we can use it */ - stream = camel_stream_mem_new (); - camel_stream_write (stream, (const gchar *)selection->data, selection->length); - camel_stream_reset (stream); - - msg = camel_mime_message_new (); - if (camel_data_wrapper_construct_from_stream ((CamelDataWrapper *)msg, stream) != -1) { - msg_composer_attach_message (composer, msg); - success = TRUE; - delete = action == GDK_ACTION_MOVE; - } - - camel_object_unref (msg); - camel_object_unref (stream); - break; - case DND_TYPE_NETSCAPE_URL: - d (printf ("dropping a _NETSCAPE_URL\n")); - tmp = g_strndup ((const gchar *) selection->data, selection->length); - urls = g_strsplit (tmp, "\n", 2); - g_free (tmp); - - /* _NETSCAPE_URL is represented as "URI\nTITLE" */ - handle_uri (composer, urls[0], html_dnd); - - g_strfreev (urls); - success = TRUE; - break; - case DND_TYPE_TEXT_URI_LIST: - d (printf ("dropping a text/uri-list\n")); - tmp = g_strndup ((const gchar *) selection->data, selection->length); - urls = g_strsplit (tmp, "\n", 0); - g_free (tmp); - - for (i = 0; urls[i] != NULL; i++) { - str = g_strstrip (urls[i]); - if (str[0] == '#' || str[0] == '\0') - continue; - - handle_uri (composer, str, html_dnd); - } - - g_strfreev (urls); - success = TRUE; - break; - case DND_TYPE_TEXT_VCARD: - case DND_TYPE_TEXT_CALENDAR: - content_type = gdk_atom_name (selection->type); - d (printf ("dropping a %s\n", content_type)); - - mime_part = camel_mime_part_new (); - camel_mime_part_set_content (mime_part, (const gchar *)selection->data, selection->length, content_type); - camel_mime_part_set_disposition (mime_part, "inline"); - - e_attachment_bar_attach_mime_part (E_ATTACHMENT_BAR (p->attachment_bar), mime_part); - - camel_object_unref (mime_part); - g_free (content_type); - - success = TRUE; - break; case DND_TYPE_X_UID_LIST: { GPtrArray *uids; char *inptr, *inend; @@ -1734,55 +1578,7 @@ drop_action (EMsgComposer *composer, gtk_drag_finish (context, success, delete, time); } - -static void -drop_popup_copy (EPopup *ep, EPopupItem *item, gpointer data) -{ - struct _drop_data *m = data; - - drop_action ( - m->composer, m->context, GDK_ACTION_COPY, - m->selection, m->info, m->time, FALSE); -} - -static void -drop_popup_move (EPopup *ep, EPopupItem *item, gpointer data) -{ - struct _drop_data *m = data; - - drop_action ( - m->composer, m->context, GDK_ACTION_MOVE, - m->selection, m->info, m->time, FALSE); -} - -static void -drop_popup_cancel (EPopup *ep, EPopupItem *item, gpointer data) -{ - struct _drop_data *m = data; - - gtk_drag_finish (m->context, FALSE, FALSE, m->time); -} - -static EPopupItem drop_popup_menu[] = { - { E_POPUP_ITEM, "00.emc.02", N_("_Copy"), drop_popup_copy, NULL, "mail-copy", 0 }, - { E_POPUP_ITEM, "00.emc.03", N_("_Move"), drop_popup_move, NULL, "mail-move", 0 }, - { E_POPUP_BAR, "10.emc" }, - { E_POPUP_ITEM, "99.emc.00", N_("Cancel _Drag"), drop_popup_cancel, NULL, NULL, 0 }, -}; - -static void -drop_popup_free (EPopup *ep, GSList *items, gpointer data) -{ - struct _drop_data *m = data; - - g_slist_free (items); - - g_object_unref (m->context); - g_object_unref (m->composer); - g_free (m->selection->data); - g_free (m->selection); - g_free (m); -} +#endif static void msg_composer_notify_header_cb (EMsgComposer *composer) @@ -2022,33 +1818,14 @@ msg_composer_drag_motion (GtkWidget *widget, gint y, guint time) { - GList *targets; - GdkDragAction actions = 0; - GdkDragAction chosen_action; - - targets = context->targets; - while (targets != NULL) { - gint ii; - - for (ii = 0; ii < G_N_ELEMENTS (drag_info); ii++) - if (targets->data == (gpointer) drag_info[ii].atom) - actions |= drag_info[ii].actions; - - targets = g_list_next (targets); - } - - actions &= context->actions; - chosen_action = context->suggested_action; - - /* we default to copy */ - if (chosen_action == GDK_ACTION_ASK && - (actions & (GDK_ACTION_MOVE|GDK_ACTION_COPY)) != - (GDK_ACTION_MOVE|GDK_ACTION_COPY)) - chosen_action = GDK_ACTION_COPY; + EMsgComposer *composer; + EAttachmentView *view; - gdk_drag_status (context, chosen_action, time); + /* Widget may be EMsgComposer or GtkHTML. */ + composer = E_MSG_COMPOSER (gtk_widget_get_toplevel (widget)); + view = e_msg_composer_get_attachment_view (composer); - return (chosen_action != 0); + return e_attachment_view_drag_motion (view, context, x, y, time); } static void @@ -2061,46 +1838,14 @@ msg_composer_drag_data_received (GtkWidget *widget, guint time) { EMsgComposer *composer; + EAttachmentView *view; /* Widget may be EMsgComposer or GtkHTML. */ composer = E_MSG_COMPOSER (gtk_widget_get_toplevel (widget)); + view = e_msg_composer_get_attachment_view (composer); - if (selection->data == NULL) - return; - - if (selection->length == -1) - return; - - if (context->action == GDK_ACTION_ASK) { - EMPopup *emp; - GSList *menus = NULL; - GtkMenu *menu; - gint ii; - struct _drop_data *m; - - m = g_malloc0(sizeof (*m)); - m->context = g_object_ref (context); - m->composer = g_object_ref (composer); - m->action = context->action; - m->info = info; - m->time = time; - m->selection = g_malloc0(sizeof (*m->selection)); - m->selection->data = g_malloc (selection->length); - memcpy (m->selection->data, selection->data, selection->length); - m->selection->length = selection->length; - - emp = em_popup_new ("org.gnome.evolution.mail.composer.popup.drop"); - for (ii = 0; ii < G_N_ELEMENTS (drop_popup_menu); ii++) - menus = g_slist_append (menus, &drop_popup_menu[ii]); - - e_popup_add_items ((EPopup *)emp, menus, NULL, drop_popup_free, m); - menu = e_popup_create_menu_once ((EPopup *)emp, NULL, 0); - gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0, time); - } else { - drop_action ( - composer, context, context->action, selection, - info, time, !GTK_WIDGET_TOPLEVEL (widget)); - } + e_attachment_view_drag_data_received ( + view, context, x, y, selection, info, time); } static void @@ -2147,11 +1892,21 @@ static void msg_composer_paste_clipboard (GtkhtmlEditor *editor) { EMsgComposer *composer; + EAttachmentView *view; + EAttachmentStore *store; + GtkClipboard *clipboard; + GdkPixbuf *pixbuf; GtkWidget *parent; GtkWidget *widget; - GtkClipboard *clipboard; + gchar *filename; + gchar *uri; + gint fd; + GError *error = NULL; composer = E_MSG_COMPOSER (editor); + view = e_msg_composer_get_attachment_view (composer); + store = e_attachment_view_get_store (view); + widget = gtk_window_get_focus (GTK_WINDOW (editor)); parent = gtk_widget_get_parent (widget); @@ -2161,36 +1916,61 @@ msg_composer_paste_clipboard (GtkhtmlEditor *editor) } clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD); - if (clipboard && gtk_clipboard_wait_is_image_available (clipboard)) { - GdkPixbuf *pixbuf; - - pixbuf = gtk_clipboard_wait_for_image (clipboard); - if (pixbuf) { - char *tmpl = g_strconcat (_("Image"), "-XXXXXX", NULL); - char *filename = e_mktemp (tmpl); - - g_free (tmpl); - - if (filename && gdk_pixbuf_save (pixbuf, filename, "png", NULL, NULL)) { - if (gtkhtml_editor_get_html_mode (editor)) { - char *uri = g_strconcat ("file://", filename, NULL); - /* this loads image async, thus cannot remove file from this */ - gtkhtml_editor_insert_image (editor, uri); - g_free (uri); - } else { - /* this loads image immediately, remove file from cache to free up disk space */ - e_attachment_bar_attach (E_ATTACHMENT_BAR (composer->priv->attachment_bar), filename, "image/png"); - g_remove (filename); - } - } - g_free (filename); - g_object_unref (pixbuf); - } - } else { - /* Chain up to parent's paste_clipboard() method. */ - GTKHTML_EDITOR_CLASS (parent_class)->paste_clipboard (editor); + /* Assume the clipboard has an image. The return + * value will be NULL if we assumed wrong. */ + pixbuf = gtk_clipboard_wait_for_image (clipboard); + if (!GDK_IS_PIXBUF (pixbuf)) + goto chainup; + + /* Reserve a temporary file. */ + fd = e_file_open_tmp (&filename, &error); + if (error != NULL) { + g_warning ("%s", error->message); + g_object_unref (pixbuf); + g_error_free (error); + return; } + close (fd); + + /* Save the pixbuf as a temporary file in image/png format. */ + if (!gdk_pixbuf_save (pixbuf, filename, "png", &error, NULL)) { + g_warning ("%s", error->message); + g_object_unref (pixbuf); + g_error_free (error); + g_free (filename); + return; + } + + /* Convert the filename to a URI. */ + uri = g_filename_to_uri (filename, NULL, &error); + if (error != NULL) { + g_warning ("%s", error->message); + g_object_unref (pixbuf); + g_error_free (error); + g_free (filename); + return; + } + + if (gtkhtml_editor_get_html_mode (editor)) + gtkhtml_editor_insert_image (editor, uri); + else { + EAttachment *attachment; + + attachment = e_attachment_new_for_uri (uri); + e_attachment_store_add_attachment (store, attachment); + g_object_unref (attachment); + } + + g_object_unref (pixbuf); + g_free (filename); + g_free (uri); + + return; + +chainup: + /* Chain up to parent's paste_clipboard() method. */ + GTKHTML_EDITOR_CLASS (parent_class)->paste_clipboard (editor); } static void @@ -2479,6 +2259,8 @@ msg_composer_class_init (EMsgComposerClass *class) static void msg_composer_init (EMsgComposer *composer) { + EAttachmentView *view; + EAttachmentStore *store; EComposerHeaderTable *table; GtkUIManager *manager; GtkhtmlEditor *editor; @@ -2505,27 +2287,6 @@ msg_composer_init (EMsgComposer *composer) drop_types, G_N_ELEMENTS (drop_types), GDK_ACTION_COPY | GDK_ACTION_ASK | GDK_ACTION_MOVE); - /* XXX I'm not sure why we have to explicitly configure the - * attachment bar as a drag destination when CompEditor - * doesn't and previous Evolution releases (2.22 and - * prior) don't, but this is the only way I could figure - * out how to get drag-and-drop to the attachment bar - * working again. I'm probably overlooking something - * simple... */ - - gtk_drag_dest_set ( - composer->priv->attachment_bar, GTK_DEST_DEFAULT_ALL, - drop_types, G_N_ELEMENTS (drop_types), - GDK_ACTION_COPY | GDK_ACTION_ASK | GDK_ACTION_MOVE); - - g_signal_connect ( - composer->priv->attachment_bar, "drag-motion", - G_CALLBACK (msg_composer_drag_motion), NULL); - - g_signal_connect ( - composer->priv->attachment_bar, "drag-data-received", - G_CALLBACK (msg_composer_drag_data_received), NULL); - g_signal_connect ( html, "drag-data-received", G_CALLBACK (msg_composer_drag_data_received), NULL); @@ -2568,14 +2329,18 @@ msg_composer_init (EMsgComposer *composer) msg_composer_account_changed_cb (composer); msg_composer_account_list_changed_cb (composer); - /* Attachment Bar */ + /* Attachments */ - g_signal_connect ( - composer->priv->attachment_bar, "changed", - G_CALLBACK (attachment_bar_changed_cb), composer); - g_signal_connect_after ( - composer->priv->attachment_expander, "notify::expanded", - G_CALLBACK (attachment_expander_notify_cb), composer); + view = e_msg_composer_get_attachment_view (composer); + store = e_attachment_view_get_store (view); + + g_signal_connect_swapped ( + store, "row-deleted", + G_CALLBACK (attachment_store_changed_cb), composer); + + g_signal_connect_swapped ( + store, "row-inserted", + G_CALLBACK (attachment_store_changed_cb), composer); e_composer_autosave_register (composer); @@ -3445,7 +3210,7 @@ disable_editor (EMsgComposer *composer) action = GTKHTML_EDITOR_ACTION_INSERT_MENU (composer); gtk_action_set_sensitive (action, FALSE); - gtk_widget_set_sensitive (composer->priv->attachment_bar, FALSE); + gtk_widget_set_sensitive (composer->priv->attachment_paned, FALSE); } /** @@ -3589,7 +3354,8 @@ add_recipients (GList *list, const gchar *recips) static void handle_mailto (EMsgComposer *composer, const gchar *mailto) { - EMsgComposerPrivate *priv = composer->priv; + EAttachmentView *view; + EAttachmentStore *store; EComposerHeaderTable *table; GList *to = NULL, *cc = NULL, *bcc = NULL; EDestination **tov, **ccv, **bccv; @@ -3598,9 +3364,10 @@ handle_mailto (EMsgComposer *composer, const gchar *mailto) gsize nread, nwritten; const gchar *p; gint len, clen; - CamelURL *url; table = e_msg_composer_get_header_table (composer); + view = e_msg_composer_get_attachment_view (composer); + store = e_attachment_view_get_store (view); buf = g_strdup (mailto); @@ -3672,20 +3439,11 @@ handle_mailto (EMsgComposer *composer, const gchar *mailto) } } else if (!g_ascii_strcasecmp (header, "attach") || !g_ascii_strcasecmp (header, "attachment")) { - /* Change file url to absolute path */ - if (!g_ascii_strncasecmp (content, "file:", 5)) { - url = camel_url_new (content, NULL); - e_attachment_bar_attach (E_ATTACHMENT_BAR (priv->attachment_bar), - url->path, - "attachment"); - camel_url_free (url); - } else { - e_attachment_bar_attach (E_ATTACHMENT_BAR (priv->attachment_bar), - content, - "attachment"); - } - gtk_widget_show (priv->attachment_expander); - gtk_widget_show (priv->attachment_scrolled_window); + EAttachment *attachment; + + attachment = e_attachment_new_for_uri (content); + e_attachment_store_add_attachment (store, attachment); + g_object_unref (attachment); } else if (!g_ascii_strcasecmp (header, "from")) { /* Ignore */ } else if (!g_ascii_strcasecmp (header, "reply-to")) { @@ -3737,45 +3495,6 @@ handle_mailto (EMsgComposer *composer, const gchar *mailto) } } -static void -handle_uri (EMsgComposer *composer, - const gchar *uri, - gboolean html_dnd) -{ - EMsgComposerPrivate *p = composer->priv; - GtkhtmlEditor *editor; - gboolean html_content; - - editor = GTKHTML_EDITOR (composer); - html_content = gtkhtml_editor_get_html_mode (editor); - - if (!g_ascii_strncasecmp (uri, "mailto:", 7)) { - handle_mailto (composer, uri); - } else { - CamelURL *url = camel_url_new (uri, NULL); - gchar *type; - - if (!url) - return; - - if (!g_ascii_strcasecmp (url->protocol, "file")) { - type = e_util_guess_mime_type (uri + strlen ("file://"), TRUE); - if (!type) - return; - - if (strncmp (type, "image", 5) || !html_dnd || (!html_content && !strncmp (type, "image", 5))) { - e_attachment_bar_attach (E_ATTACHMENT_BAR (p->attachment_bar), - url->path, "attachment"); - } - g_free (type); - } else { - e_attachment_bar_attach_remote_file (E_ATTACHMENT_BAR (p->attachment_bar), - uri, "attachment"); - } - camel_url_free (url); - } -} - /** * e_msg_composer_new_from_url: * @url: a mailto URL @@ -3939,21 +3658,28 @@ e_msg_composer_remove_header (EMsgComposer *composer, /** * e_msg_composer_attach: * @composer: a composer object - * @attachment: the CamelMimePart to attach + * @mime_part: the #CamelMimePart to attach * * Attaches @attachment to the message being composed in the composer. **/ void -e_msg_composer_attach (EMsgComposer *composer, CamelMimePart *attachment) +e_msg_composer_attach (EMsgComposer *composer, + CamelMimePart *mime_part) { - EAttachmentBar *bar; - EMsgComposerPrivate *p = composer->priv; + EAttachmentView *view; + EAttachmentStore *store; + EAttachment *attachment; g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - g_return_if_fail (CAMEL_IS_MIME_PART (attachment)); + g_return_if_fail (CAMEL_IS_MIME_PART (mime_part)); + + view = e_msg_composer_get_attachment_view (composer); + store = e_attachment_view_get_store (view); - bar = E_ATTACHMENT_BAR (p->attachment_bar); - e_attachment_bar_attach_mime_part (bar, attachment); + attachment = e_attachment_new (); + e_attachment_set_mime_part (attachment, mime_part); + e_attachment_store_add_attachment (store, attachment); + g_object_unref (attachment); } /** @@ -4067,12 +3793,17 @@ CamelMimeMessage * e_msg_composer_get_message (EMsgComposer *composer, gboolean save_html_object_data) { + EAttachmentView *view; + EAttachmentStore *store; GtkhtmlEditor *editor; gboolean html_content; g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); - if (e_msg_composer_get_remote_download_count (composer) != 0) { + view = e_msg_composer_get_attachment_view (composer); + store = e_attachment_view_get_store (view); + + if (e_attachment_store_get_num_downloading (store) > 0) { if (!em_utils_prompt_user (GTK_WINDOW (composer), NULL, "mail-composer:ask-send-message-pending-download", NULL)) { return NULL; @@ -4367,14 +4098,6 @@ e_msg_composer_get_raw_message_text (EMsgComposer *composer) return array; } -EAttachmentBar * -e_msg_composer_get_attachment_bar (EMsgComposer *composer) -{ - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); - - return E_ATTACHMENT_BAR (composer->priv->attachment_bar); -} - void e_msg_composer_set_enable_autosave (EMsgComposer *composer, gboolean enabled) @@ -4529,6 +4252,18 @@ e_msg_composer_get_header_table (EMsgComposer *composer) return E_COMPOSER_HEADER_TABLE (composer->priv->header_table); } +EAttachmentView * +e_msg_composer_get_attachment_view (EMsgComposer *composer) +{ + EAttachmentPaned *paned; + + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); + + paned = E_ATTACHMENT_PANED (composer->priv->attachment_paned); + + return e_attachment_paned_get_view (paned); +} + void e_msg_composer_set_send_options (EMsgComposer *composer, gboolean send_enable) diff --git a/composer/e-msg-composer.h b/composer/e-msg-composer.h index 7e0851fb52..42f12bf973 100644 --- a/composer/e-msg-composer.h +++ b/composer/e-msg-composer.h @@ -29,6 +29,7 @@ #include #include #include +#include #include "e-composer-header-table.h" @@ -66,8 +67,6 @@ struct _EMsgComposerClass { GtkhtmlEditorClass parent_class; }; -struct _EAttachmentBar; - #define E_MSG_COMPOSER_MAIL 1 #define E_MSG_COMPOSER_POST 2 #define E_MSG_COMPOSER_MAIL_POST E_MSG_COMPOSER_MAIL|E_MSG_COMPOSER_POST @@ -104,7 +103,7 @@ void e_msg_composer_modify_header (EMsgComposer *composer, void e_msg_composer_remove_header (EMsgComposer *composer, const gchar *name); void e_msg_composer_attach (EMsgComposer *composer, - CamelMimePart *attachment); + CamelMimePart *mime_part); CamelMimePart * e_msg_composer_add_inline_image_from_file (EMsgComposer *composer, const gchar *filename); @@ -140,22 +139,19 @@ void e_msg_composer_add_message_attachments gboolean e_msg_composer_request_close_all(void); EMsgComposer * e_msg_composer_load_from_file (const gchar *filename); void e_msg_composer_check_autosave (GtkWindow *parent); -gint e_msg_composer_get_remote_download_count - (EMsgComposer *composer); void e_msg_composer_reply_indent (EMsgComposer *composer); EComposerHeaderTable * e_msg_composer_get_header_table (EMsgComposer *composer); +EAttachmentView * + e_msg_composer_get_attachment_view + (EMsgComposer *composer); void e_msg_composer_set_send_options (EMsgComposer *composer, gboolean send_enable); GByteArray * e_msg_composer_get_raw_message_text (EMsgComposer *composer); -struct _EAttachmentBar * - e_msg_composer_get_attachment_bar - (EMsgComposer *composer); - gboolean e_msg_composer_is_exiting (EMsgComposer *composer); GList * e_load_spell_languages (void); diff --git a/e-util/Makefile.am b/e-util/Makefile.am index be155b52bd..eb8c2e9bd8 100644 --- a/e-util/Makefile.am +++ b/e-util/Makefile.am @@ -34,7 +34,7 @@ INCLUDES = \ -DSEARCH_RULE_DIR=\"$(ruledir)\" \ -DG_LOG_DOMAIN=\"e-utils\" \ $(GNOME_PILOT_CFLAGS) \ - $(ICONV_CFLAGS) \ + $(ICONV_CFLAGS) \ $(E_UTIL_CFLAGS) privsolib_LTLIBRARIES = libeutil.la libeconduit.la diff --git a/e-util/e-util.c b/e-util/e-util.c index e4e9a3ac32..02c76c3d32 100644 --- a/e-util/e-util.c +++ b/e-util/e-util.c @@ -1529,23 +1529,32 @@ e_util_read_file (const char *filename, gboolean filename_is_uri, char **buffer, return res; } -GSList * -e_util_get_category_filter_options (void) +static gpointer +e_camel_object_copy (gpointer camel_object) { - GSList *res = NULL; - GList *clist, *l; + if (CAMEL_IS_OBJECT (camel_object)) + camel_object_ref (camel_object); - clist = e_categories_get_list (); - for (l = clist; l; l = l->next) { - const char *cname = l->data; - struct _filter_option *fo = g_new0 (struct _filter_option, 1); + return camel_object; +} - fo->title = g_strdup (cname); - fo->value = g_strdup (cname); - res = g_slist_prepend (res, fo); - } +static void +e_camel_object_free (gpointer camel_object) +{ + if (CAMEL_IS_OBJECT (camel_object)) + camel_object_unref (camel_object); +} + +GType +e_camel_object_get_type (void) +{ + static GType type = 0; - g_list_free (clist); + if (G_UNLIKELY (type == 0)) + type = g_boxed_type_register_static ( + "ECamelObject", + (GBoxedCopyFunc) e_camel_object_copy, + (GBoxedFreeFunc) e_camel_object_free); - return g_slist_reverse (res); + return type; } diff --git a/e-util/e-util.h b/e-util/e-util.h index 744b4a7a9d..168b497da7 100644 --- a/e-util/e-util.h +++ b/e-util/e-util.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -131,7 +132,8 @@ gboolean e_file_lock_create (void); void e_file_lock_destroy (void); gboolean e_file_lock_exists (void); -gchar * e_util_guess_mime_type (const gchar *filename, gboolean localfile); +gchar * e_util_guess_mime_type (const gchar *filename, + gboolean localfile); gchar * e_util_filename_to_uri (const gchar *filename); gchar * e_util_uri_to_filename (const gchar *uri); @@ -141,7 +143,10 @@ gboolean e_util_read_file (const gchar *filename, gsize *read, GError **error); -GSList *e_util_get_category_filter_options (void); +/* Camel uses its own object system, so we have to box + * CamelObjects to safely use them as GObject properties. */ +#define E_TYPE_CAMEL_OBJECT (e_camel_object_get_type ()) +GType e_camel_object_get_type (void); G_END_DECLS diff --git a/mail/e-mail-browser.c b/mail/e-mail-browser.c index ac53faeffa..4088538db5 100644 --- a/mail/e-mail-browser.c +++ b/mail/e-mail-browser.c @@ -653,7 +653,7 @@ e_mail_browser_get_type (void) static const GInterfaceInfo iface_info = { (GInterfaceInitFunc) mail_browser_iface_init, (GInterfaceFinalizeFunc) NULL, - NULL /* interface_data */ + NULL /* interface_data */ }; type = g_type_register_static ( diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c index b28221d5e9..a026f6db8d 100644 --- a/mail/e-mail-reader.c +++ b/mail/e-mail-reader.c @@ -2009,7 +2009,7 @@ e_mail_reader_get_type (void) NULL, /* class_data */ 0, /* instance_size */ 0, /* n_preallocs */ - NULL, /* instance_init */ + (GInstanceInitFunc) NULL, NULL /* value_table */ }; diff --git a/mail/em-format-html-display.c b/mail/em-format-html-display.c index 59f63adc6c..242dcdceb8 100644 --- a/mail/em-format-html-display.c +++ b/mail/em-format-html-display.c @@ -79,8 +79,8 @@ #include "em-icon-stream.h" #include "em-utils.h" #include "em-popup.h" -#include "e-attachment.h" -#include "e-attachment-bar.h" +#include "widgets/misc/e-attachment-view.h" +#include "widgets/misc/e-attachment-icon-view.h" #ifdef G_OS_WIN32 /* Undefine the similar macro from ,it doesn't check if @@ -100,7 +100,7 @@ struct _EMFormatHTMLDisplayPrivate { /* for Attachment bar */ - GtkWidget *attachment_bar; + GtkWidget *attachment_view; GtkWidget *attachment_box; GtkWidget *label; GtkWidget *save_txt; @@ -494,8 +494,8 @@ efhd_format_attachment (EMFormat *emf, info->shown = em_format_is_inline ( emf, info->puri.part_id, info->puri.part, handle); info->snoop_mime_type = emf->snoop_mime_type; - info->attachment = e_attachment_new_from_mime_part (info->puri.part); - e_attachment_bar_create_attachment_cache (info->attachment); + info->attachment = e_attachment_new (); + e_attachment_set_mime_part (info->attachment, info->puri.part); if (emf->valid) { info->sign = emf->valid->sign.status; @@ -559,7 +559,8 @@ efhd_format_optional (EMFormat *emf, info->handle = em_format_find_handler (emf, "text/plain"); info->shown = FALSE; info->snoop_mime_type = "text/plain"; - info->attachment = e_attachment_new_from_mime_part (info->puri.part); + info->attachment = e_attachment_new (); + e_attachment_set_mime_part (info->attachment, info->puri.part); info->mstream = (CamelStreamMem *) mstream; if (emf->valid) { info->sign = emf->valid->sign.status; @@ -736,12 +737,6 @@ em_format_html_display_new (void) return g_object_new (EM_TYPE_FORMAT_HTML_DISPLAY, NULL); } -EAttachmentBar * -em_format_html_display_get_bar (EMFormatHTMLDisplay *efhd) -{ - return E_ATTACHMENT_BAR (efhd->priv->attachment_bar); -} - void em_format_html_display_cut (EMFormatHTMLDisplay *efhd) { @@ -1302,6 +1297,7 @@ efhd_attachment_image(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObjec static gboolean efhd_attachment_button(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject) { +#if 0 /* KILL-BONOBO !! FIXME !! */ EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *)efh; EAttachment *new; struct _attach_puri *info; @@ -1324,7 +1320,10 @@ efhd_attachment_button(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObje return TRUE; } - if (efhd->priv->attachment_bar) { + if (efhd->priv->attachment_view) { + EAttachmentView *view; + EAttachmentStore *store; + file = camel_mime_part_get_filename(info->puri.part); new = info->attachment; @@ -1457,6 +1456,7 @@ efhd_attachment_button(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObje gtk_widget_hide(info->down); gtk_container_add((GtkContainer *)eb, mainbox); +#endif return TRUE; } @@ -1500,6 +1500,7 @@ attachment_bar_arrow_clicked(GtkWidget *w, EMFormatHTMLDisplay *efhd) static void attachments_save_all_clicked (GtkWidget *widget, EMFormatHTMLDisplay *efhd) { +#if 0 /* KILL-BONOBO */ GSList *attachment_parts; guint n_attachment_parts; gpointer parent; @@ -1522,31 +1523,10 @@ attachments_save_all_clicked (GtkWidget *widget, EMFormatHTMLDisplay *efhd) attachment_parts); g_slist_free (attachment_parts); +#endif } -static void -efhd_bar_popup_position(GtkMenu *menu, int *x, int *y, gboolean *push_in, gpointer user_data) -{ - EAttachmentBar *bar = user_data; - GnomeIconList *icon_list = user_data; - GList *selection; - GnomeCanvasPixbuf *image; - - gdk_window_get_origin (((GtkWidget*) bar)->window, x, y); - - selection = gnome_icon_list_get_selection (icon_list); - if (selection == NULL) - return; - - image = gnome_icon_list_get_icon_pixbuf_item (icon_list, GPOINTER_TO_INT(selection->data)); - if (image == NULL) - return; - - /* Put menu to the center of icon. */ - *x += (int)(image->item.x1 + image->item.x2) / 2; - *y += (int)(image->item.y1 + image->item.y2) / 2; -} - +#if 0 /* KILL-BONOBO -- Move this to EAttachmentView */ static void efhd_bar_save_selected(EPopup *ep, EPopupItem *item, void *data) { @@ -1582,68 +1562,24 @@ static EPopupItem efhd_bar_menu_items[] = { { E_POPUP_BAR, "05.display", }, { E_POPUP_ITEM, "05.display.01", N_("_Save Selected..."), efhd_bar_save_selected, NULL, NULL, EM_POPUP_ATTACHMENTS_MULTIPLE}, }; - -static gboolean -efhd_bar_button_press_event(EAttachmentBar *bar, GdkEventButton *event, EMFormat *emf) -{ - GtkMenu *menu; - GSList *list=NULL; - EPopupTarget *target; - EMPopup *emp; - GSList *menus = NULL; - int i; - - if (event && event->button != 3) - return FALSE; - - /** @HookPoint-EMPopup: Attachment Bar Context Menu - * @Id: org.gnome.evolution.mail.attachments.popup - * @Class: org.gnome.evolution.mail.popup:1.0 - * @Target: EMPopupTargetPart - * - * This is the drop-down menu shown when a user clicks on the attachment bar - * when attachments are selected. - */ - emp = em_popup_new("org.gnome.evolution.mail.attachments.popup"); - - /* Add something like save-selected, foward selected attachments in a mail etc....*/ - list = e_attachment_bar_get_selected(bar); - - /* Lets not propagate any more the r-click which is intended to us*/ - if ( g_slist_length (list) == 0) - return TRUE; - - target = (EPopupTarget *)em_popup_target_new_attachments(emp, list); - for (i=0; i<2; i++) - menus = g_slist_prepend(menus, &efhd_bar_menu_items[i]); - e_popup_add_items((EPopup *)emp, menus, NULL, efhd_menu_items_free, emf); - - ((EMPopupTargetPart *)target)->target.widget = (GtkWidget *)bar; - menu = e_popup_create_menu_once((EPopup *)emp, (EPopupTarget *)target, 0); - if (event) - gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button, event->time); - else - gtk_menu_popup(menu, NULL, NULL, (GtkMenuPositionFunc)efhd_bar_popup_position, bar, 0, gtk_get_current_event_time()); - - return TRUE; -} - -static gboolean -efhd_bar_popup_menu_event (EAttachmentBar *bar, EMFormat *emf) -{ - return efhd_bar_button_press_event(bar, NULL, emf); -} +#endif static void efhd_attachment_bar_refresh (EMFormatHTMLDisplay *efhd) { - int nattachments; + EAttachmentStore *store; + EAttachmentView *view; + guint nattachments; - if (!efhd->priv->attachment_bar) + if (!efhd->priv->attachment_view) return; - nattachments = e_attachment_bar_get_num_attachments (E_ATTACHMENT_BAR(efhd->priv->attachment_bar)); - if (nattachments) { + view = E_ATTACHMENT_VIEW (efhd->priv->attachment_view); + store = e_attachment_view_get_store (view); + + nattachments = e_attachment_store_get_num_attachments (store); + + if (nattachments > 0) { char *txt; /* Cant i put in the number of attachments here ?*/ @@ -1673,6 +1609,7 @@ efhd_attachment_bar_refresh (EMFormatHTMLDisplay *efhd) static void efhd_bar_resize(GtkWidget *w, GtkAllocation *event, EMFormatHTML *efh) { +#if 0 /* KILL-BONOBO -- Does EAttachmentIconView need a resize method? */ int width; GtkRequisition req; EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *) efh; @@ -1683,17 +1620,20 @@ efhd_bar_resize(GtkWidget *w, GtkAllocation *event, EMFormatHTML *efh) /* Update the width of the bar when the width is greater than 1*/ if (width > 0) e_attachment_bar_set_width(E_ATTACHMENT_BAR(efhd->priv->attachment_bar), width); +#endif } static gboolean efhd_bar_scroll_event(GtkWidget *w, GdkEventScroll *event, EMFormatHTMLDisplay *efhd) { +#if 0 /* KILL-BONOBO -- Do we still need this for a GtkIconView? */ gboolean ret; /* Emulate the scroll over the attachment bar, as if it is scrolled in the window. * It doesnt go automatically since the GnomeIconList is a layout by itself */ g_signal_emit_by_name (gtk_widget_get_parent((GtkWidget *)efhd->parent.html), "scroll_event", event, &ret); +#endif return TRUE; } @@ -1709,11 +1649,13 @@ efhd_mnemonic_show_bar (GtkWidget *widget, gboolean focus, GtkWidget *efhd) static gboolean efhd_update_bar(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject) { +#if 0 /* KILL-BONOBO -- Does EAttachmentIconView need a refresh method? */ EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *)efh; struct _EMFormatHTMLDisplayPrivate *priv = efhd->priv; if (priv->attachment_bar) e_attachment_bar_refresh (E_ATTACHMENT_BAR (priv->attachment_bar)); +#endif return TRUE; } @@ -1726,10 +1668,9 @@ efhd_add_bar(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobjec GtkWidget *hbox1, *hbox2, *hbox3, *vbox, *txt, *image, *save, *scroll; int width, height, bar_width; - priv->attachment_bar = e_attachment_bar_new (); + priv->attachment_view = e_attachment_icon_view_new (); scroll = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - ((EAttachmentBar *)priv->attachment_bar)->expand = TRUE; priv->forward = gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_NONE); priv->down = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE); @@ -1759,14 +1700,14 @@ efhd_add_bar(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobjec priv->attachment_box = scroll; gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN); - gtk_container_add ((GtkContainer *)priv->attachment_box, priv->attachment_bar); + gtk_container_add ((GtkContainer *)priv->attachment_box, priv->attachment_view); - gtk_widget_get_size_request(priv->attachment_bar, &width, &height); + gtk_widget_get_size_request(priv->attachment_view, &width, &height); /* FIXME: What if the text is more?. Should we reduce the text with appending ...? * or resize the bar? How to figure out that, it needs more space? */ bar_width = ((GtkWidget *)efh->html)->parent->allocation.width - /* FIXME */16; - gtk_widget_set_size_request (priv->attachment_bar, + gtk_widget_set_size_request (priv->attachment_view, bar_width > 0 ? bar_width : 0, 84 /* FIXME: Default show only one row, Dont hardcode size*/); @@ -1782,11 +1723,8 @@ efhd_add_bar(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobjec gtk_widget_hide_all (priv->attachment_area); g_signal_connect (priv->arrow, "clicked", G_CALLBACK(attachment_bar_arrow_clicked), efh); - g_signal_connect (priv->attachment_bar, "button_press_event", G_CALLBACK(efhd_bar_button_press_event), efhd); - g_signal_connect (priv->attachment_bar, "popup-menu", G_CALLBACK(efhd_bar_popup_menu_event), efhd); g_signal_connect (save, "clicked", G_CALLBACK(attachments_save_all_clicked), efh); g_signal_connect (eb, "size_allocate", G_CALLBACK (efhd_bar_resize), efh); - g_signal_connect (priv->attachment_bar, "scroll_event", G_CALLBACK(efhd_bar_scroll_event), efhd); return TRUE; } diff --git a/mail/em-format-html-display.h b/mail/em-format-html-display.h index 2577b039e7..9cfdb03780 100644 --- a/mail/em-format-html-display.h +++ b/mail/em-format-html-display.h @@ -27,7 +27,6 @@ #include #include -#include /* Standard GObject macros */ #define EM_TYPE_FORMAT_HTML_DISPLAY \ @@ -76,7 +75,6 @@ void em_format_html_display_zoom_in (EMFormatHTMLDisplay *efhd); void em_format_html_display_zoom_out (EMFormatHTMLDisplay *efhd); void em_format_html_display_zoom_reset (EMFormatHTMLDisplay *efhd); -EAttachmentBar *em_format_html_display_get_bar (EMFormatHTMLDisplay *efhd); gboolean em_format_html_display_popup_menu (EMFormatHTMLDisplay *efhd); diff --git a/mail/em-popup.c b/mail/em-popup.c index f0887f26ce..c9dde270a0 100644 --- a/mail/em-popup.c +++ b/mail/em-popup.c @@ -117,7 +117,9 @@ emp_class_init(GObjectClass *klass) klass->finalize = emp_finalise; ((EPopupClass *)klass)->target_free = emp_target_free; +#if 0 /* KILL-BONOBO */ e_popup_class_add_factory((EPopupClass *)klass, NULL, emp_standard_menu_factory, NULL); +#endif } GType @@ -361,6 +363,7 @@ done: return t; } +#if 0 /* KILL-BONOBO */ /** * em_popup_target_new_attachments: * @emp: @@ -406,9 +409,11 @@ em_popup_target_new_attachments(EMPopup *emp, GSList *attachments) return t; } +#endif /* ********************************************************************** */ +#if 0 /* KILL-BONOBO */ static void emp_part_popup_saveas(EPopup *ep, EPopupItem *item, void *data) { @@ -432,7 +437,9 @@ emp_part_popup_saveas(EPopup *ep, EPopupItem *item, void *data) em_utils_save_part (parent, _("Save As..."), part); } +#endif +#if 0 /* KILL-BONOBO */ static void emp_part_popup_set_background(EPopup *ep, EPopupItem *item, void *data) { @@ -511,6 +518,7 @@ emp_part_popup_set_background(EPopup *ep, EPopupItem *item, void *data) g_free(path); } +#endif static void emp_part_popup_reply_sender(EPopup *ep, EPopupItem *item, void *data) @@ -589,6 +597,7 @@ emp_part_popup_forward (EPopup *ep, EPopupItem *item, void *data) em_utils_forward_message(message, NULL); } +#if 0 /* KILL-BONOBO */ static EMPopupItem emp_standard_object_popups[] = { { E_POPUP_ITEM, "00.part.00", N_("_Save As..."), emp_part_popup_saveas, NULL, "document-save-as", 0 }, { E_POPUP_ITEM, "00.part.10", N_("Set as _Background"), emp_part_popup_set_background, NULL, NULL, EM_POPUP_PART_IMAGE }, @@ -612,6 +621,7 @@ static EMPopupItem emp_attachment_object_popups[] = { }; static const EPopupItem emp_standard_part_apps_bar = { E_POPUP_BAR, "99.object" }; +#endif /* ********************************************************************** */ @@ -771,6 +781,7 @@ emp_add_vcard (EPopup *ep, EPopupItem *item, void *data) camel_object_unref (mem); } +#if 0 /* KILL-BONOBO */ static void emp_standard_menu_factory(EPopup *emp, void *data) { @@ -912,6 +923,7 @@ emp_standard_menu_factory(EPopup *emp, void *data) if (menus) e_popup_add_items(emp, menus, NULL, emp_standard_items_free, NULL); } +#endif /* ********************************************************************** */ diff --git a/plugins/attachment-reminder/attachment-reminder.c b/plugins/attachment-reminder/attachment-reminder.c index 15bac58080..200425041d 100644 --- a/plugins/attachment-reminder/attachment-reminder.c +++ b/plugins/attachment-reminder/attachment-reminder.c @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -43,7 +44,6 @@ #include -#include "widgets/misc/e-attachment-bar.h" #include "composer/e-msg-composer.h" #include "composer/e-composer-actions.h" @@ -182,12 +182,13 @@ check_for_attachment_clues (gchar *msg) static gboolean check_for_attachment (EMsgComposer *composer) { - EAttachmentBar* bar = (EAttachmentBar*)e_msg_composer_get_attachment_bar (composer); + EAttachmentView *view; + EAttachmentStore *store; - if (e_attachment_bar_get_num_attachments (bar)) - return TRUE; + view = e_msg_composer_get_attachment_view (composer); + store = e_attachment_view_get_store (view); - return FALSE; + return (e_attachment_store_get_num_attachments (store) > 0); } static gchar* diff --git a/plugins/import-ics-attachments/icsimporter.c b/plugins/import-ics-attachments/icsimporter.c index fbd5e46518..096ccdec9a 100644 --- a/plugins/import-ics-attachments/icsimporter.c +++ b/plugins/import-ics-attachments/icsimporter.c @@ -36,7 +36,6 @@ #include #include #include -#include "e-attachment-bar.h" #include #include "e-util/e-error.h" #include diff --git a/shell/e-shell.c b/shell/e-shell.c index 6e52d8bbef..5de46bcc65 100644 --- a/shell/e-shell.c +++ b/shell/e-shell.c @@ -170,12 +170,19 @@ shell_ready_for_offline (EShell *shell, if (!is_last_ref) return; + /* Increment the reference count so we can safely emit + * a signal without triggering the toggle reference. */ + g_object_ref (activity); + e_activity_complete (activity); g_object_remove_toggle_ref ( G_OBJECT (activity), (GToggleNotify) shell_ready_for_offline, shell); + /* Finalize the activity. */ + g_object_unref (activity); + shell->priv->online = FALSE; g_object_notify (G_OBJECT (shell), "online"); @@ -217,12 +224,19 @@ shell_ready_for_online (EShell *shell, if (!is_last_ref) return; + /* Increment the reference count so we can safely emit + * a signal without triggering the toggle reference. */ + g_object_ref (activity); + e_activity_complete (activity); g_object_remove_toggle_ref ( G_OBJECT (activity), (GToggleNotify) shell_ready_for_online, shell); + /* Finalize the activity. */ + g_object_unref (activity); + shell->priv->online = TRUE; g_object_notify (G_OBJECT (shell), "online"); diff --git a/shell/main.c b/shell/main.c index 6b15768e07..bf0a8c47c6 100644 --- a/shell/main.c +++ b/shell/main.c @@ -618,13 +618,15 @@ create_default_shell (void) shell, "window-destroyed", G_CALLBACK (shell_window_destroyed_cb), NULL); - g_signal_connect ( - master_client, "save_yourself", - G_CALLBACK (master_client_save_yourself_cb), shell); - - g_signal_connect ( - master_client, "die", - G_CALLBACK (master_client_die_cb), shell); + if (master_client != NULL) { + g_signal_connect ( + master_client, "save_yourself", + G_CALLBACK (master_client_save_yourself_cb), shell); + + g_signal_connect ( + master_client, "die", + G_CALLBACK (master_client_die_cb), shell); + } g_object_unref (conf_client); 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 #include #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; @@ -416,6 +471,18 @@ e_activity_cancel (EActivity *activity) g_signal_emit (activity, signals[CANCELLED], 0); } +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) { @@ -430,6 +497,18 @@ e_activity_complete (EActivity *activity) g_signal_emit (activity, signals[COMPLETED], 0); } +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) { @@ -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 }; @@ -128,78 +121,6 @@ static const gchar *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) @@ -232,33 +153,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 @@ -1426,39 +1224,6 @@ attachment_bar_class_init (EAttachmentBarClass *class) class->update_actions = attachment_bar_update_actions; - 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, @@ -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 + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-attachment-icon-view.h" + +#include + +#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 + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_ATTACHMENT_ICON_VIEW_H +#define E_ATTACHMENT_ICON_VIEW_H + +#include + +/* 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 + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-attachment-paned.h" + +#include +#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 ( + "%d %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 + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_ATTACHMENT_PANED_H +#define E_ATTACHMENT_PANED_H + +#include +#include + +/* 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 + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-attachment-store.h" + +#include + +#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 + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_ATTACHMENT_STORE_H +#define E_ATTACHMENT_STORE_H + +#include +#include + +/* 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 + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-attachment-tree-view.h" + +#include +#include + +#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 + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_ATTACHMENT_TREE_VIEW_H +#define E_ATTACHMENT_TREE_VIEW_H + +#include + +/* 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 + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-attachment-view.h" + +#include +#include +#include + +#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 = +"" +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +""; + +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 + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_ATTACHMENT_VIEW_H +#define E_ATTACHMENT_VIEW_H + +#include +#include + +/* 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 * * - * Authors: - * Ettore Perazzoli - * Jeffrey Stedfast - * Srinivasa Ragavan - * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ -#ifdef HAVE_CONFIG_H -#include -#endif - #include "e-attachment.h" -#include "e-attachment-dialog.h" - -#ifdef G_OS_WIN32 -/* Include early (as the gio stuff below will - * include it anyway, sigh) to workaround the DATADIR problem. - * (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 -#undef DATADIR -#endif -#include -#include #include - -#include - #include -#include - -#include +#include +#include +#include +#include +#include +#include #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 * * - * Authors: - * Ettore Perazzoli - * Srinivasa Ragavan - * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ @@ -25,11 +23,10 @@ #define E_ATTACHMENT_H #include -#include -#include #include -#include -#include +#include +#include +#include /* 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 + #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 -#include +#include /* 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 + #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) -- cgit v1.2.3