/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * 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-composer-private.h" #include "e-util/e-util-private.h" /* Initial height of the picture gallery. */ #define GALLERY_INITIAL_HEIGHT 150 static void composer_setup_charset_menu (EMsgComposer *composer) { GtkUIManager *ui_manager; const gchar *path; GList *list; guint merge_id; ui_manager = gtkhtml_editor_get_ui_manager (GTKHTML_EDITOR (composer)); path = "/main-menu/options-menu/charset-menu"; merge_id = gtk_ui_manager_new_merge_id (ui_manager); list = gtk_action_group_list_actions (composer->priv->charset_actions); list = g_list_sort (list, (GCompareFunc) e_action_compare_by_label); while (list != NULL) { GtkAction *action = list->data; gtk_ui_manager_add_ui ( ui_manager, merge_id, path, gtk_action_get_name (action), gtk_action_get_name (action), GTK_UI_MANAGER_AUTO, FALSE); list = g_list_delete_link (list, list); } gtk_ui_manager_ensure_update (ui_manager); } static void composer_setup_recent_menu (EMsgComposer *composer) { EAttachmentView *view; GtkUIManager *ui_manager; GtkAction *action; const gchar *action_name; const gchar *path; guint merge_id; view = e_msg_composer_get_attachment_view (composer); ui_manager = gtkhtml_editor_get_ui_manager (GTKHTML_EDITOR (composer)); path = "/main-menu/insert-menu/insert-menu-top/recent-placeholder"; merge_id = gtk_ui_manager_new_merge_id (ui_manager); action_name = "recent-menu"; 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_ui_manager_add_ui ( ui_manager, merge_id, path, action_name, action_name, GTK_UI_MANAGER_AUTO, FALSE); } gtk_ui_manager_ensure_update (ui_manager); } static void msg_composer_url_requested_cb (GtkHTML *html, const gchar *uri, GtkHTMLStream *stream, EMsgComposer *composer) { GByteArray *array; GHashTable *hash_table; CamelDataWrapper *wrapper; CamelStream *camel_stream; CamelMimePart *mime_part; hash_table = composer->priv->inline_images_by_url; mime_part = g_hash_table_lookup (hash_table, uri); if (mime_part == NULL) { hash_table = composer->priv->inline_images; mime_part = g_hash_table_lookup (hash_table, uri); } /* If this is not an inline image request, * allow the signal emission to continue. */ if (mime_part == NULL) return; array = g_byte_array_new (); camel_stream = camel_stream_mem_new_with_byte_array (array); wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part)); camel_data_wrapper_decode_to_stream_sync ( wrapper, camel_stream, NULL, NULL); gtk_html_write (html, stream, (gchar *) array->data, array->len); gtk_html_end (html, stream, GTK_HTML_STREAM_OK); g_object_unref (camel_stream); /* gtk_html_end() destroys the GtkHTMLStream, so we need to * stop the signal emission so nothing else tries to use it. */ g_signal_stop_emission_by_name (html, "url-requested"); } static void composer_update_gallery_visibility (EMsgComposer *composer) { GtkhtmlEditor *editor; GtkToggleAction *toggle_action; gboolean gallery_active; gboolean html_mode; editor = GTKHTML_EDITOR (composer); html_mode = gtkhtml_editor_get_html_mode (editor); toggle_action = GTK_TOGGLE_ACTION (ACTION (PICTURE_GALLERY)); gallery_active = gtk_toggle_action_get_active (toggle_action); if (html_mode && gallery_active) { gtk_widget_show (composer->priv->gallery_scrolled_window); gtk_widget_show (composer->priv->gallery_icon_view); } else { gtk_widget_hide (composer->priv->gallery_scrolled_window); gtk_widget_hide (composer->priv->gallery_icon_view); } } void e_composer_private_constructed (EMsgComposer *composer) { EMsgComposerPrivate *priv = composer->priv; EFocusTracker *focus_tracker; EShell *shell; EShellSettings *shell_settings; EWebView *web_view; GtkhtmlEditor *editor; GtkUIManager *ui_manager; GtkAction *action; GtkWidget *container; GtkWidget *widget; GtkWidget *send_widget; GtkWindow *window; const gchar *path; gboolean small_screen_mode; gchar *filename, *gallery_path; gint ii; GError *error = NULL; editor = GTKHTML_EDITOR (composer); ui_manager = gtkhtml_editor_get_ui_manager (editor); shell = e_msg_composer_get_shell (composer); shell_settings = e_shell_get_shell_settings (shell); web_view = e_msg_composer_get_web_view (composer); small_screen_mode = e_shell_get_small_screen_mode (shell); if (small_screen_mode) { #if 0 /* In the lite composer, for small screens, we are not * ready yet to hide the menubar. It still has useful * items like the ones to show/hide the various header * fields, plus the security options. * * When we move those options out of the menu and into * the composer's toplevel, we can probably get rid of * the menu. */ widget = gtkhtml_editor_get_managed_widget (editor, "/main-menu"); gtk_widget_hide (widget); #endif widget = gtkhtml_editor_get_managed_widget (editor, "/main-toolbar"); gtk_toolbar_set_style (GTK_TOOLBAR (widget), GTK_TOOLBAR_BOTH_HORIZ); gtk_widget_hide (widget); } /* Each composer window gets its own window group. */ window = GTK_WINDOW (composer); priv->window_group = gtk_window_group_new (); gtk_window_group_add_window (priv->window_group, window); priv->async_actions = gtk_action_group_new ("async"); priv->charset_actions = gtk_action_group_new ("charset"); priv->composer_actions = gtk_action_group_new ("composer"); priv->extra_hdr_names = g_ptr_array_new (); priv->extra_hdr_values = g_ptr_array_new (); priv->gconf_bridge_binding_ids = g_array_new ( FALSE, FALSE, sizeof (guint)); priv->inline_images = g_hash_table_new_full ( g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) NULL); priv->inline_images_by_url = g_hash_table_new_full ( g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref); priv->charset = e_composer_get_default_charset (); priv->is_from_message = FALSE; e_composer_actions_init (composer); filename = e_composer_find_data_file ("evolution-composer.ui"); gtk_ui_manager_add_ui_from_file (ui_manager, filename, &error); g_free (filename); /* We set the send button as important to have a label */ path = "/main-toolbar/pre-main-toolbar/send"; send_widget = gtk_ui_manager_get_widget (ui_manager, path); gtk_tool_item_set_is_important (GTK_TOOL_ITEM (send_widget), TRUE); /* Tooltips for all buttons in toolbar that don't yet have one */ path = "/main-toolbar/undo"; widget = gtk_ui_manager_get_widget (ui_manager, path); gtk_widget_set_tooltip_text (widget, _("Undo the last action")); path = "/main-toolbar/redo"; widget = gtk_ui_manager_get_widget (ui_manager, path); gtk_widget_set_tooltip_text (widget, _("Redo the last undone action")); path = "/main-toolbar/show-find"; widget = gtk_ui_manager_get_widget (ui_manager, path); gtk_widget_set_tooltip_text (widget, _("Search for text")); path = "/main-toolbar/show-replace"; widget = gtk_ui_manager_get_widget (ui_manager, path); gtk_widget_set_tooltip_text (widget, _("Search for and replace text")); composer_setup_charset_menu (composer); if (error != NULL) { /* Henceforth, bad things start happening. */ g_critical ("%s", error->message); g_clear_error (&error); } /* Configure an EFocusTracker to manage selection actions. */ focus_tracker = e_focus_tracker_new (GTK_WINDOW (composer)); action = gtkhtml_editor_get_action (editor, "cut"); e_focus_tracker_set_cut_clipboard_action (focus_tracker, action); action = gtkhtml_editor_get_action (editor, "copy"); e_focus_tracker_set_copy_clipboard_action (focus_tracker, action); action = gtkhtml_editor_get_action (editor, "paste"); e_focus_tracker_set_paste_clipboard_action (focus_tracker, action); action = gtkhtml_editor_get_action (editor, "select-all"); e_focus_tracker_set_select_all_action (focus_tracker, action); priv->focus_tracker = focus_tracker; container = editor->vbox; /* Construct the activity bar. */ widget = e_activity_bar_new (); gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); priv->activity_bar = g_object_ref (widget); /* EActivityBar controls its own visibility. */ /* Construct the alert bar for errors. */ widget = e_alert_bar_new (); gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); priv->alert_bar = g_object_ref (widget); /* EAlertBar controls its own visibility. */ /* Construct the header table. */ widget = e_composer_header_table_new (shell); gtk_container_set_border_width (GTK_CONTAINER (widget), 6); gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); if (small_screen_mode) gtk_box_reorder_child (GTK_BOX (container), widget, 1); else gtk_box_reorder_child (GTK_BOX (container), widget, 2); priv->header_table = g_object_ref (widget); gtk_widget_show (widget); /* Construct the attachment paned. */ if (small_screen_mode) { e_attachment_paned_set_default_height (75); /* short attachment bar for Anjal */ e_attachment_icon_view_set_default_icon_size (GTK_ICON_SIZE_BUTTON); } widget = e_attachment_paned_new (); gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); priv->attachment_paned = g_object_ref (widget); gtk_widget_show (widget); g_object_bind_property ( web_view, "editable", widget, "editable", G_BINDING_SYNC_CREATE); if (small_screen_mode) { GtkWidget *tmp, *tmp1, *tmp_box, *container; GtkWidget *combo; combo = e_attachment_paned_get_view_combo ( E_ATTACHMENT_PANED (widget)); gtk_widget_hide (combo); container = e_attachment_paned_get_controls_container ( E_ATTACHMENT_PANED (widget)); tmp_box = gtk_hbox_new (FALSE, 0); tmp = gtk_hbox_new (FALSE, 0); tmp1 = gtk_image_new_from_icon_name ( "mail-send", GTK_ICON_SIZE_BUTTON); gtk_box_pack_start ((GtkBox *) tmp, tmp1, FALSE, FALSE, 0); tmp1 = gtk_label_new_with_mnemonic (_("S_end")); gtk_box_pack_start ((GtkBox *) tmp, tmp1, FALSE, FALSE, 6); gtk_widget_show_all (tmp); gtk_widget_reparent (send_widget, tmp_box); gtk_box_set_child_packing ( GTK_BOX (tmp_box), send_widget, FALSE, FALSE, 6, GTK_PACK_END); gtk_tool_item_set_is_important (GTK_TOOL_ITEM (send_widget), TRUE); send_widget = gtk_bin_get_child ((GtkBin *) send_widget); gtk_container_remove ( GTK_CONTAINER (send_widget), gtk_bin_get_child (GTK_BIN (send_widget))); gtk_container_add ((GtkContainer *) send_widget, tmp); gtk_button_set_relief ((GtkButton *) send_widget, GTK_RELIEF_NORMAL); path = "/main-toolbar/pre-main-toolbar/save-draft"; send_widget = gtk_ui_manager_get_widget (ui_manager, path); tmp = gtk_hbox_new (FALSE, 0); tmp1 = gtk_image_new_from_stock ( GTK_STOCK_SAVE, GTK_ICON_SIZE_BUTTON); gtk_box_pack_start ((GtkBox *) tmp, tmp1, FALSE, FALSE, 0); tmp1 = gtk_label_new_with_mnemonic (_("Save draft")); gtk_box_pack_start ((GtkBox *) tmp, tmp1, FALSE, FALSE, 3); gtk_widget_show_all (tmp); gtk_widget_reparent (send_widget, tmp_box); gtk_box_set_child_packing ( GTK_BOX (tmp_box), send_widget, FALSE, FALSE, 6, GTK_PACK_END); gtk_tool_item_set_is_important (GTK_TOOL_ITEM (send_widget), TRUE); send_widget = gtk_bin_get_child ((GtkBin *) send_widget); gtk_container_remove ( GTK_CONTAINER (send_widget), gtk_bin_get_child (GTK_BIN (send_widget))); gtk_container_add ((GtkContainer *) send_widget, tmp); gtk_button_set_relief ((GtkButton *) send_widget, GTK_RELIEF_NORMAL); gtk_widget_show (tmp_box); gtk_box_pack_end (GTK_BOX (container), tmp_box, FALSE, FALSE, 3); } container = e_attachment_paned_get_content_area ( E_ATTACHMENT_PANED (priv->attachment_paned)); widget = gtk_vpaned_new (); gtk_container_add (GTK_CONTAINER (container), widget); gtk_widget_show (widget); container = 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_widget_set_size_request (widget, -1, GALLERY_INITIAL_HEIGHT); gtk_paned_pack1 (GTK_PANED (container), widget, FALSE, FALSE); priv->gallery_scrolled_window = 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_WIDGET (web_view); widget = gtk_widget_get_parent (widget); gtk_widget_reparent (widget, container); /* Construct the picture gallery. */ container = priv->gallery_scrolled_window; gallery_path = e_shell_settings_get_string ( shell_settings, "composer-gallery-path"); widget = e_picture_gallery_new (gallery_path); gtk_container_add (GTK_CONTAINER (container), widget); priv->gallery_icon_view = g_object_ref (widget); g_free (gallery_path); g_signal_connect ( composer, "notify::html-mode", G_CALLBACK (composer_update_gallery_visibility), NULL); g_signal_connect_swapped ( ACTION (PICTURE_GALLERY), "toggled", G_CALLBACK (composer_update_gallery_visibility), composer); /* XXX What is this for? */ g_object_set_data (G_OBJECT (composer), "vbox", editor->vbox); composer_setup_recent_menu (composer); /* Bind headers to their corresponding actions. */ for (ii = 0; ii < E_COMPOSER_NUM_HEADERS; ii++) { EComposerHeaderTable *table; EComposerHeader *header; GtkAction *action; table = E_COMPOSER_HEADER_TABLE (priv->header_table); header = e_composer_header_table_get_header (table, ii); switch (ii) { case E_COMPOSER_HEADER_BCC: action = ACTION (VIEW_BCC); break; case E_COMPOSER_HEADER_CC: action = ACTION (VIEW_CC); break; case E_COMPOSER_HEADER_REPLY_TO: action = ACTION (VIEW_REPLY_TO); break; default: continue; } g_object_bind_property ( header, "sensitive", action, "sensitive", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); g_object_bind_property ( header, "visible", action, "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); } /* Install a handler for inline images. */ /* XXX We no longer use GtkhtmlEditor::uri-requested because it * conflicts with EWebView's url_requested() method, which * unconditionally launches an async operation. I changed * GtkHTML::url-requested to be a G_SIGNAL_RUN_LAST so that * our handler runs first. If we can handle the request * we'll stop the signal emission to prevent EWebView from * launching an async operation. Messy, but works until we * switch to WebKit. --mbarnes */ g_signal_connect ( web_view, "url-requested", G_CALLBACK (msg_composer_url_requested_cb), composer); } void e_composer_private_dispose (EMsgComposer *composer) { if (composer->priv->gconf_bridge_binding_ids) { GConfBridge *bridge; GArray *array; guint binding_id; bridge = gconf_bridge_get (); array = composer->priv->gconf_bridge_binding_ids; while (array->len > 0) { binding_id = g_array_index (array, guint, 0); gconf_bridge_unbind (bridge, binding_id); g_array_remove_index_fast (array, 0); } g_array_free (composer->priv->gconf_bridge_binding_ids, TRUE); composer->priv->gconf_bridge_binding_ids = NULL; } if (composer->priv->shell != NULL) { g_object_remove_weak_pointer ( G_OBJECT (composer->priv->shell), &composer->priv->shell); composer->priv->shell = NULL; } if (composer->priv->header_table != NULL) { g_object_unref (composer->priv->header_table); composer->priv->header_table = NULL; } if (composer->priv->activity_bar != NULL) { g_object_unref (composer->priv->activity_bar); composer->priv->activity_bar = NULL; } if (composer->priv->alert_bar != NULL) { g_object_unref (composer->priv->alert_bar); composer->priv->alert_bar = NULL; } if (composer->priv->attachment_paned != NULL) { g_object_unref (composer->priv->attachment_paned); composer->priv->attachment_paned = NULL; } if (composer->priv->focus_tracker != NULL) { g_object_unref (composer->priv->focus_tracker); composer->priv->focus_tracker = NULL; } if (composer->priv->window_group != NULL) { g_object_unref (composer->priv->window_group); composer->priv->window_group = NULL; } if (composer->priv->async_actions != NULL) { g_object_unref (composer->priv->async_actions); composer->priv->async_actions = NULL; } if (composer->priv->charset_actions != NULL) { g_object_unref (composer->priv->charset_actions); composer->priv->charset_actions = NULL; } if (composer->priv->composer_actions != NULL) { g_object_unref (composer->priv->composer_actions); composer->priv->composer_actions = NULL; } if (composer->priv->gallery_scrolled_window != NULL) { g_object_unref (composer->priv->gallery_scrolled_window); composer->priv->gallery_scrolled_window = NULL; } g_hash_table_remove_all (composer->priv->inline_images); g_hash_table_remove_all (composer->priv->inline_images_by_url); if (composer->priv->redirect != NULL) { g_object_unref (composer->priv->redirect); composer->priv->redirect = NULL; } } void e_composer_private_finalize (EMsgComposer *composer) { GPtrArray *array; array = composer->priv->extra_hdr_names; g_ptr_array_foreach (array, (GFunc) g_free, NULL); g_ptr_array_free (array, TRUE); array = composer->priv->extra_hdr_values; g_ptr_array_foreach (array, (GFunc) g_free, NULL); g_ptr_array_free (array, TRUE); g_free (composer->priv->charset); g_free (composer->priv->mime_type); g_free (composer->priv->mime_body); g_hash_table_destroy (composer->priv->inline_images); g_hash_table_destroy (composer->priv->inline_images_by_url); } gchar * e_composer_find_data_file (const gchar *basename) { gchar *filename; g_return_val_if_fail (basename != NULL, NULL); /* Support running directly from the source tree. */ filename = g_build_filename (".", basename, NULL); if (g_file_test (filename, G_FILE_TEST_EXISTS)) return filename; g_free (filename); /* XXX This is kinda broken. */ filename = g_build_filename (EVOLUTION_UIDIR, basename, NULL); if (g_file_test (filename, G_FILE_TEST_EXISTS)) return filename; g_free (filename); g_critical ("Could not locate '%s'", basename); return NULL; } gchar * e_composer_get_default_charset (void) { GConfClient *client; gchar *charset; GError *error = NULL; client = gconf_client_get_default (); charset = gconf_client_get_string ( client, COMPOSER_GCONF_CHARSET_KEY, &error); if (error != NULL) { g_warning ("%s", error->message); g_clear_error (&error); } /* See what charset the mailer is using. * XXX We should not have to know where this lives in GConf. * Need a mail_config_get_default_charset() that does this. */ if (!charset || charset[0] == '\0') { g_free (charset); charset = gconf_client_get_string ( client, MAIL_GCONF_CHARSET_KEY, NULL); if (charset != NULL && *charset == '\0') { g_free (charset); charset = NULL; } else if (error != NULL) { g_warning ("%s", error->message); g_clear_error (&error); } } g_object_unref (client); if (charset == NULL) charset = g_strdup (camel_iconv_locale_charset ()); if (charset == NULL) charset = g_strdup ("us-ascii"); return charset; } gboolean e_composer_paste_html (EMsgComposer *composer, GtkClipboard *clipboard) { GtkhtmlEditor *editor; gchar *html; g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); g_return_val_if_fail (GTK_IS_CLIPBOARD (clipboard), FALSE); html = e_clipboard_wait_for_html (clipboard); g_return_val_if_fail (html != NULL, FALSE); editor = GTKHTML_EDITOR (composer); gtkhtml_editor_insert_html (editor, html); g_free (html); return TRUE; } gboolean e_composer_paste_image (EMsgComposer *composer, GtkClipboard *clipboard) { GtkhtmlEditor *editor; EAttachmentStore *store; EAttachmentView *view; GdkPixbuf *pixbuf = NULL; gchar *filename = NULL; gchar *uri = NULL; gboolean success = FALSE; GError *error = NULL; g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); g_return_val_if_fail (GTK_IS_CLIPBOARD (clipboard), FALSE); editor = GTKHTML_EDITOR (composer); view = e_msg_composer_get_attachment_view (composer); store = e_attachment_view_get_store (view); /* Extract the image data from the clipboard. */ pixbuf = gtk_clipboard_wait_for_image (clipboard); g_return_val_if_fail (pixbuf != NULL, FALSE); /* Reserve a temporary file. */ filename = e_mktemp (NULL); if (filename == NULL) { g_set_error ( &error, G_FILE_ERROR, g_file_error_from_errno (errno), "Could not create temporary file: %s", g_strerror (errno)); goto exit; } /* Save the pixbuf as a temporary file in image/png format. */ if (!gdk_pixbuf_save (pixbuf, filename, "png", &error, NULL)) goto exit; /* Convert the filename to a URI. */ uri = g_filename_to_uri (filename, NULL, &error); if (uri == NULL) goto exit; /* In HTML mode, paste the image into the message body. * In text mode, add the image to the attachment store. */ 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); e_attachment_load_async ( attachment, (GAsyncReadyCallback) e_attachment_load_handle_error, composer); g_object_unref (attachment); } success = TRUE; exit: if (error != NULL) { g_warning ("%s", error->message); g_error_free (error); } g_object_unref (pixbuf); g_free (filename); g_free (uri); return success; } gboolean e_composer_paste_text (EMsgComposer *composer, GtkClipboard *clipboard) { GtkhtmlEditor *editor; gchar *text; g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); g_return_val_if_fail (GTK_IS_CLIPBOARD (clipboard), FALSE); text = gtk_clipboard_wait_for_text (clipboard); g_return_val_if_fail (text != NULL, FALSE); editor = GTKHTML_EDITOR (composer); gtkhtml_editor_insert_text (editor, text); g_free (text); return TRUE; } gboolean e_composer_paste_uris (EMsgComposer *composer, GtkClipboard *clipboard) { EAttachmentStore *store; EAttachmentView *view; gchar **uris; gint ii; g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); g_return_val_if_fail (GTK_IS_CLIPBOARD (clipboard), FALSE); view = e_msg_composer_get_attachment_view (composer); store = e_attachment_view_get_store (view); /* Extract the URI data from the clipboard. */ uris = gtk_clipboard_wait_for_uris (clipboard); g_return_val_if_fail (uris != NULL, FALSE); /* Add the URIs to the attachment store. */ for (ii = 0; uris[ii] != NULL; ii++) { EAttachment *attachment; attachment = e_attachment_new_for_uri (uris[ii]); e_attachment_store_add_attachment (store, attachment); e_attachment_load_async ( attachment, (GAsyncReadyCallback) e_attachment_load_handle_error, composer); g_object_unref (attachment); } return TRUE; } gboolean e_composer_selection_is_image_uris (EMsgComposer *composer, GtkSelectionData *selection) { gboolean all_image_uris = TRUE; gchar **uris; guint ii; g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); g_return_val_if_fail (selection != NULL, FALSE); uris = gtk_selection_data_get_uris (selection); if (uris == NULL) return FALSE; for (ii = 0; uris[ii] != NULL; ii++) { GFile *file; GFileInfo *file_info; GdkPixbufLoader *loader; const gchar *attribute; const gchar *content_type; gchar *mime_type = NULL; file = g_file_new_for_uri (uris[ii]); attribute = G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE; /* XXX This blocks, but we're requesting the fast content * type (which only inspects filenames), so hopefully * it won't be noticeable. Also, this is best effort * so we don't really care if it fails. */ file_info = g_file_query_info ( file, attribute, G_FILE_QUERY_INFO_NONE, NULL, NULL); if (file_info == NULL) { g_object_unref (file); all_image_uris = FALSE; break; } content_type = g_file_info_get_attribute_string ( file_info, attribute); mime_type = g_content_type_get_mime_type (content_type); g_object_unref (file_info); g_object_unref (file); if (mime_type == NULL) { all_image_uris = FALSE; break; } /* Easy way to determine if a MIME type is a supported * image format: try creating a GdkPixbufLoader for it. */ loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, NULL); g_free (mime_type); if (loader == NULL) { all_image_uris = FALSE; break; } gdk_pixbuf_loader_close (loader, NULL); g_object_unref (loader); } g_strfreev (uris); return all_image_uris; }