From 85dbfb5b2586e248ed9cef94d0e43f8e24865caf Mon Sep 17 00:00:00 2001 From: Jose Dapena Paz Date: Wed, 25 Jun 2008 10:53:39 +0000 Subject: [PATCH] * src/widgets/modest-gtkhtml-msg-view.c: * Add implementation of external images fetching. We show a button for letting user accept external images when they're available. * Rework all the external images fetching code to use the new tinymail stream cache. * src/widgets/modest-gtkhtml-mime-part-view.c: * Fix some issues in the way we created the streams to avoid some crashes. * Now images retrieval should happen in msg view window layer, not on msg or mime part view. * Implement new ModestMimePartView methods to know if we can view images. Also use the TnyMsg information to show if we can fetch external images. * src/widgets/modest-msg-view.c: * New signal fetch-image to let upper layers handle retrieving images from external sources. * New methods set view images and get view images to set if the user can view external images. * src/widgets/modest-mime-part-view.c: * New methods to get/set view images state, and to know if a message has external images. * src/widgets/modest-tny-stream-gtkthml.[ch]: * Keep a reference to the GtkHTML to keep it alive while stream is alive. * src/maemo/modest-msg-view-window.c: * Handle new ModestMsgView signal "fetch-image", using the new stream cache. Now all the image retrieval happens here, using that cache as backstore. * src/maemo/modest-account-settings-dialog.c: * Fix leaks in sizegroups. * src/modest-utils.[ch]: * Added a new method to get the cache id we'll use to store the images in image cache. * src/modest-runtime.[ch]: * Added method to retrieve the images cache singleton. * src/modest-singletons.[ch]: * Provide a image cache using the TnyFsStreamCache. pmo-trunk-r4773 --- src/maemo/modest-account-settings-dialog.c | 8 ++ src/maemo/modest-msg-view-window.c | 120 ++++++++++++++++++ src/modest-init.c | 2 +- src/modest-runtime.c | 7 ++ src/modest-runtime.h | 10 ++ src/modest-singletons.c | 25 ++++ src/modest-singletons.h | 9 ++ src/modest-utils.c | 16 +++ src/modest-utils.h | 12 ++ src/widgets/modest-gtkhtml-mime-part-view.c | 180 +++++++++++++++------------ src/widgets/modest-gtkhtml-mime-part-view.h | 3 + src/widgets/modest-gtkhtml-msg-view.c | 77 +++++++++++- src/widgets/modest-mime-part-view.c | 41 ++++++ src/widgets/modest-mime-part-view.h | 6 + src/widgets/modest-msg-view.c | 17 +++ src/widgets/modest-msg-view.h | 4 + src/widgets/modest-tny-stream-gtkhtml.c | 22 +++- src/widgets/modest-tny-stream-gtkhtml.h | 2 +- 18 files changed, 473 insertions(+), 88 deletions(-) diff --git a/src/maemo/modest-account-settings-dialog.c b/src/maemo/modest-account-settings-dialog.c index 67bec94..0fdc76b 100644 --- a/src/maemo/modest-account-settings-dialog.c +++ b/src/maemo/modest-account-settings-dialog.c @@ -388,6 +388,8 @@ create_page_account_details (ModestAccountSettingsDialog *self) connect_for_modified (self, self->checkbox_leave_messages); gtk_box_pack_start (GTK_BOX (box), self->caption_leave_messages, FALSE, FALSE, MODEST_MARGIN_HALF); gtk_widget_show (self->caption_leave_messages); + + g_object_unref (sizegroup); gtk_widget_show (GTK_WIDGET (box)); @@ -572,6 +574,8 @@ create_page_user_details (ModestAccountSettingsDialog *self) gtk_widget_show (self->button_signature); gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); gtk_widget_show (caption); + + g_object_unref (sizegroup); g_signal_connect (G_OBJECT (self->button_signature), "clicked", G_CALLBACK (on_button_signature), self); @@ -683,6 +687,8 @@ static GtkWidget* create_page_incoming (ModestAccountSettingsDialog *self) gtk_widget_show (self->checkbox_incoming_auth); connect_for_modified (self, self->checkbox_incoming_auth); gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + + g_object_unref (sizegroup); gtk_widget_show (caption); gtk_widget_show (GTK_WIDGET (box)); @@ -915,6 +921,8 @@ static GtkWidget* create_page_outgoing (ModestAccountSettingsDialog *self) /* Only enable the button when the checkbox is checked: */ enable_widget_for_togglebutton (self->button_outgoing_smtp_servers, GTK_TOGGLE_BUTTON (self->checkbox_outgoing_smtp_specific)); + + g_object_unref (sizegroup); g_signal_connect (G_OBJECT (self->button_outgoing_smtp_servers), "clicked", G_CALLBACK (on_button_outgoing_smtp_servers), self); diff --git a/src/maemo/modest-msg-view-window.c b/src/maemo/modest-msg-view-window.c index 05ae61e..4cc78e0 100644 --- a/src/maemo/modest-msg-view-window.c +++ b/src/maemo/modest-msg-view-window.c @@ -155,6 +155,11 @@ static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox); static void check_dimming_rules_after_change (ModestMsgViewWindow *window); +static gboolean on_fetch_image (ModestMsgView *msgview, + const gchar *uri, + TnyStream *stream, + ModestMsgViewWindow *window); + /* list my signals */ enum { MSG_CHANGED_SIGNAL, @@ -823,6 +828,8 @@ modest_msg_view_window_construct (ModestMsgViewWindow *self, G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj); g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual", G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj); + g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image", + G_CALLBACK (on_fetch_image), obj); g_signal_connect (G_OBJECT (obj), "key-release-event", G_CALLBACK (modest_msg_view_window_key_event), @@ -2979,3 +2986,116 @@ static void on_move_focus (GtkWidget *widget, g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus"); } +static TnyStream * +fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri) +{ + GnomeVFSResult result; + GnomeVFSHandle *handle = NULL; + GnomeVFSFileInfo *info = NULL; + TnyStream *stream; + + result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ); + if (result != GNOME_VFS_OK) { + *expected_size = 0; + return NULL; + } + + info = gnome_vfs_file_info_new (); + result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT); + if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) { + /* We put a "safe" default size for going to cache */ + *expected_size = (300*1024); + } else { + *expected_size = info->size; + } + gnome_vfs_file_info_unref (info); + + stream = tny_vfs_stream_new (handle); + + return stream; + +} + +typedef struct { + gchar *uri; + gchar *cache_id; + TnyStream *output_stream; + GtkWidget *msg_view; +} FetchImageData; + +gboolean +on_fetch_image_idle_refresh_view (gpointer userdata) +{ + + FetchImageData *fidata = (FetchImageData *) userdata; + g_message ("REFRESH VIEW"); + if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) { + g_message ("QUEUING DRAW"); + gtk_widget_queue_draw (fidata->msg_view); + } + g_object_unref (fidata->msg_view); + g_slice_free (FetchImageData, fidata); + return FALSE; +} + +static gpointer +on_fetch_image_thread (gpointer userdata) +{ + FetchImageData *fidata = (FetchImageData *) userdata; + TnyStreamCache *cache; + TnyStream *cache_stream; + + cache = modest_runtime_get_images_cache (); + cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri); + g_free (fidata->cache_id); + g_free (fidata->uri); + + if (cache_stream != NULL) { + tny_stream_write_to_stream (cache_stream, fidata->output_stream); + tny_stream_close (cache_stream); + g_object_unref (cache_stream); + } + + tny_stream_close (fidata->output_stream); + g_object_unref (fidata->output_stream); + + + gdk_threads_enter (); + g_idle_add (on_fetch_image_idle_refresh_view, fidata); + gdk_threads_leave (); + + return NULL; +} + +static gboolean +on_fetch_image (ModestMsgView *msgview, + const gchar *uri, + TnyStream *stream, + ModestMsgViewWindow *window) +{ + const gchar *current_account; + ModestMsgViewWindowPrivate *priv; + FetchImageData *fidata; + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + current_account = modest_window_get_active_account (MODEST_WINDOW (window)); + + fidata = g_slice_new0 (FetchImageData); + fidata->msg_view = g_object_ref (msgview); + fidata->uri = g_strdup (uri); + fidata->cache_id = modest_images_cache_get_id (current_account, uri); + fidata->output_stream = g_object_ref (stream); + + if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) { + g_object_unref (fidata->output_stream); + g_free (fidata->cache_id); + g_free (fidata->uri); + g_object_unref (fidata->msg_view); + g_slice_free (FetchImageData, fidata); + tny_stream_close (stream); + return FALSE; + } + + return TRUE;; +} diff --git a/src/modest-init.c b/src/modest-init.c index 8a3b868..7839b57 100644 --- a/src/modest-init.c +++ b/src/modest-init.c @@ -306,7 +306,7 @@ modest_init_uninit (void) if (gnome_vfs_initialized()) /* apparently, this returns TRUE, even after a shutdown */ gnome_vfs_shutdown (); - + _is_initialized = FALSE; return TRUE; } diff --git a/src/modest-runtime.c b/src/modest-runtime.c index 487f9a7..568fb46 100644 --- a/src/modest-runtime.c +++ b/src/modest-runtime.c @@ -126,6 +126,13 @@ modest_runtime_get_account_mgr (void) return modest_singletons_get_account_mgr (_singletons); } +TnyStreamCache* +modest_runtime_get_images_cache (void) +{ + g_return_val_if_fail (_singletons, NULL); + return modest_singletons_get_images_cache (_singletons); +} + ModestEmailClipboard* modest_runtime_get_email_clipboard (void) { diff --git a/src/modest-runtime.h b/src/modest-runtime.h index 5726a3d..abd4cbf 100644 --- a/src/modest-runtime.h +++ b/src/modest-runtime.h @@ -42,6 +42,7 @@ #include #include #include "widgets/modest-window-mgr.h" +#include G_BEGIN_DECLS @@ -122,6 +123,15 @@ ModestConf* modest_runtime_get_conf (void); ModestAccountMgr* modest_runtime_get_account_mgr (void); /** + * modest_runtime_get_images_cache: + * + * get the images #TnyStreamCache singleton instance + * + * Returns: the images #TnyStreamCache singleton. This should NOT be unref'd. + **/ +TnyStreamCache* modest_runtime_get_images_cache (void); + +/** * modest_runtime_get_email_clipboard: * * get the #ModestEmailClipboard singleton instance diff --git a/src/modest-singletons.c b/src/modest-singletons.c index 3084cbd..32ada6b 100644 --- a/src/modest-singletons.c +++ b/src/modest-singletons.c @@ -30,6 +30,7 @@ #include "modest-singletons.h" #include "modest-runtime.h" #include "modest-debug.h" +#include /* 'private'/'protected' functions */ static void modest_singletons_class_init (ModestSingletonsClass *klass); @@ -46,6 +47,7 @@ struct _ModestSingletonsPrivate { TnyPlatformFactory *platform_fact; TnyDevice *device; ModestWindowMgr *window_mgr; + TnyStreamCache *images_cache; }; #define MODEST_SINGLETONS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ MODEST_TYPE_SINGLETONS, \ @@ -93,6 +95,7 @@ static void modest_singletons_init (ModestSingletons *obj) { ModestSingletonsPrivate *priv; + gchar *images_cache_path; priv = MODEST_SINGLETONS_GET_PRIVATE(obj); priv->conf = NULL; @@ -103,6 +106,7 @@ modest_singletons_init (ModestSingletons *obj) priv->platform_fact = NULL; priv->device = NULL; priv->window_mgr = NULL; + priv->images_cache = NULL; priv->conf = modest_conf_new (); if (!priv->conf) { @@ -151,6 +155,14 @@ modest_singletons_init (ModestSingletons *obj) g_printerr ("modest: cannot create modest window manager instance\n"); return; } + + images_cache_path = g_build_filename (g_get_home_dir (), MODEST_DIR, MODEST_IMAGES_CACHE_DIR, NULL); + priv->images_cache = tny_fs_stream_cache_new (images_cache_path, MODEST_IMAGES_CACHE_SIZE); + g_free (images_cache_path); + if (!priv->images_cache) { + g_printerr ("modest: cannot create images cache instance\n"); + return; + } } static void @@ -159,6 +171,12 @@ modest_singletons_finalize (GObject *obj) ModestSingletonsPrivate *priv; priv = MODEST_SINGLETONS_GET_PRIVATE(obj); + + if (priv->images_cache) { + MODEST_DEBUG_VERIFY_OBJECT_LAST_REF (priv->images_cache, ""); + g_object_unref (G_OBJECT (priv->images_cache)); + priv->images_cache = NULL; + } if (priv->window_mgr) { MODEST_DEBUG_VERIFY_OBJECT_LAST_REF(priv->window_mgr,""); @@ -301,3 +319,10 @@ modest_singletons_get_window_mgr (ModestSingletons *self) g_return_val_if_fail (self, NULL); return MODEST_SINGLETONS_GET_PRIVATE(self)->window_mgr; } + +TnyStreamCache* +modest_singletons_get_images_cache (ModestSingletons *self) +{ + g_return_val_if_fail (self, NULL); + return MODEST_SINGLETONS_GET_PRIVATE(self)->images_cache; +} diff --git a/src/modest-singletons.h b/src/modest-singletons.h index bc3c8a9..cfa6ca3 100644 --- a/src/modest-singletons.h +++ b/src/modest-singletons.h @@ -40,6 +40,7 @@ #include #include #include "widgets/modest-window-mgr.h" +#include G_BEGIN_DECLS @@ -187,6 +188,14 @@ ModestMailOperationQueue* modest_singletons_get_mail_operation_queue (ModestSing **/ ModestWindowMgr* modest_singletons_get_window_mgr (ModestSingletons *self); +/** + * modest_singletons_get_images_cache: + * @self: a #ModestSingletons + * + * Gets the #TnyStreamCache used to store the external images cache. + */ +TnyStreamCache* modest_singletons_get_images_cache (ModestSingletons *self); + G_END_DECLS #endif /* __MODEST_SINGLETONS_H__ */ diff --git a/src/modest-utils.c b/src/modest-utils.c index c2a1e6e..d598b05 100644 --- a/src/modest-utils.c +++ b/src/modest-utils.c @@ -719,3 +719,19 @@ modest_utils_run_sort_dialog (GtkWindow *parent_window, on_destroy_dialog (GTK_DIALOG(dialog)); } + +gchar * +modest_images_cache_get_id (const gchar *account, const gchar *uri) +{ + GnomeVFSURI *vfs_uri; + gchar *result; + + vfs_uri = gnome_vfs_uri_new (uri); + if (vfs_uri == NULL) + return NULL; + + result = g_strdup_printf ("%s__%x", account, gnome_vfs_uri_hash (vfs_uri)); + gnome_vfs_uri_unref (vfs_uri); + + return result; +} diff --git a/src/modest-utils.h b/src/modest-utils.h index 05f42a9..9934396 100644 --- a/src/modest-utils.h +++ b/src/modest-utils.h @@ -160,4 +160,16 @@ gint modest_list_index (TnyList *list, GObject *object); */ guint64 modest_folder_available_space (const gchar *maildir_path); +/** + * modest_images_cache_get_id: + * @account: a #TnyAccount + * @uri: an uri string + * + * obtains the hash corresponding to an image external resource to be + * stored in image cache. + * + * Returns: a newly allocated string containing the hash key + */ +gchar *modest_images_cache_get_id (const gchar *account, const gchar *uri); + #endif /*__MODEST_MAEMO_UTILS_H__*/ diff --git a/src/widgets/modest-gtkhtml-mime-part-view.c b/src/widgets/modest-gtkhtml-mime-part-view.c index 28031b9..22c60cd 100644 --- a/src/widgets/modest-gtkhtml-mime-part-view.c +++ b/src/widgets/modest-gtkhtml-mime-part-view.c @@ -67,6 +67,12 @@ static TnyMimePart* modest_gtkhtml_mime_part_view_get_part_default (TnyMimePartV /* ModestMimePartView implementation */ static gboolean modest_gtkhtml_mime_part_view_is_empty (ModestMimePartView *self); static gboolean modest_gtkhtml_mime_part_view_is_empty_default (ModestMimePartView *self); +static gboolean modest_gtkhtml_mime_part_view_get_view_images (ModestMimePartView *self); +static gboolean modest_gtkhtml_mime_part_view_get_view_images_default (ModestMimePartView *self); +static void modest_gtkhtml_mime_part_view_set_view_images (ModestMimePartView *self, gboolean view_images); +static void modest_gtkhtml_mime_part_view_set_view_images_default (ModestMimePartView *self, gboolean view_images); +static gboolean modest_gtkhtml_mime_part_view_has_external_images (ModestMimePartView *self); +static gboolean modest_gtkhtml_mime_part_view_has_external_images_default (ModestMimePartView *self); /* ModestZoomable implementation */ static gdouble modest_gtkhtml_mime_part_view_get_zoom (ModestZoomable *self); static void modest_gtkhtml_mime_part_view_set_zoom (ModestZoomable *self, gdouble value); @@ -94,6 +100,9 @@ static void set_text_part (ModestGtkhtmlMimePartView *self, TnyMimePa static void set_empty_part (ModestGtkhtmlMimePartView *self); static void set_part (ModestGtkhtmlMimePartView *self, TnyMimePart *part); static gboolean is_empty (ModestGtkhtmlMimePartView *self); +static gboolean get_view_images (ModestGtkhtmlMimePartView *self); +static void set_view_images (ModestGtkhtmlMimePartView *self, gboolean view_images); +static gboolean has_external_images (ModestGtkhtmlMimePartView *self); static void set_zoom (ModestGtkhtmlMimePartView *self, gdouble zoom); static gdouble get_zoom (ModestGtkhtmlMimePartView *self); static gboolean has_contents_receiver (gpointer engine, const gchar *data, @@ -107,6 +116,8 @@ typedef struct _ModestGtkhtmlMimePartViewPrivate ModestGtkhtmlMimePartViewPrivat struct _ModestGtkhtmlMimePartViewPrivate { TnyMimePart *part; gdouble current_zoom; + gboolean view_images; + gboolean has_external_images; }; #define MODEST_GTKHTML_MIME_PART_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ @@ -199,6 +210,9 @@ modest_gtkhtml_mime_part_view_class_init (ModestGtkhtmlMimePartViewClass *klass) klass->set_part_func = modest_gtkhtml_mime_part_view_set_part_default; klass->clear_func = modest_gtkhtml_mime_part_view_clear_default; klass->is_empty_func = modest_gtkhtml_mime_part_view_is_empty_default; + klass->get_view_images_func = modest_gtkhtml_mime_part_view_get_view_images_default; + klass->set_view_images_func = modest_gtkhtml_mime_part_view_set_view_images_default; + klass->has_external_images_func = modest_gtkhtml_mime_part_view_has_external_images_default; klass->get_zoom_func = modest_gtkhtml_mime_part_view_get_zoom_default; klass->set_zoom_func = modest_gtkhtml_mime_part_view_set_zoom_default; klass->zoom_minus_func = modest_gtkhtml_mime_part_view_zoom_minus_default; @@ -219,8 +233,8 @@ modest_gtkhtml_mime_part_view_init (ModestGtkhtmlMimePartView *self) gtk_html_set_editable (GTK_HTML(self), FALSE); gtk_html_allow_selection (GTK_HTML(self), TRUE); gtk_html_set_caret_mode (GTK_HTML(self), FALSE); - gtk_html_set_blocking (GTK_HTML(self), FALSE); - gtk_html_set_images_blocking (GTK_HTML(self), FALSE); + gtk_html_set_blocking (GTK_HTML(self), TRUE); + gtk_html_set_images_blocking (GTK_HTML(self), TRUE); g_signal_connect (G_OBJECT(self), "link_clicked", G_CALLBACK(on_link_clicked), self); @@ -231,6 +245,8 @@ modest_gtkhtml_mime_part_view_init (ModestGtkhtmlMimePartView *self) priv->part = NULL; priv->current_zoom = 1.0; + priv->view_images = FALSE; + priv->has_external_images = FALSE; } static void @@ -265,66 +281,8 @@ typedef struct { gpointer buffer; GtkHTML *html; GtkHTMLStream *stream; - gboolean html_finalized; } ImageFetcherInfo; -static void -html_finalized_notify (ImageFetcherInfo *ifinfo, - GObject *destroyed) -{ - ifinfo->html_finalized = TRUE; -} - -static void -image_fetcher_close (GnomeVFSAsyncHandle *handle, - GnomeVFSResult result, - gpointer data) -{ -} - -static void -image_fetcher_read (GnomeVFSAsyncHandle *handle, - GnomeVFSResult result, - gpointer buffer, - GnomeVFSFileSize bytes_requested, - GnomeVFSFileSize bytes_read, - ImageFetcherInfo *ifinfo) -{ - - if (ifinfo->html_finalized || result != GNOME_VFS_OK) { - gnome_vfs_async_close (handle, (GnomeVFSAsyncCloseCallback) image_fetcher_close, (gpointer) NULL); - if (!ifinfo->html_finalized) { - gtk_html_stream_close (ifinfo->stream, GTK_HTML_STREAM_OK); - g_object_weak_unref ((GObject *) ifinfo->html, (GWeakNotify) html_finalized_notify, (gpointer) ifinfo); - } - g_slice_free1 (128, ifinfo->buffer); - g_slice_free (ImageFetcherInfo, ifinfo); - return; - } - gtk_html_stream_write (ifinfo->stream, buffer, bytes_read); - gnome_vfs_async_read (handle, ifinfo->buffer, 128, - (GnomeVFSAsyncReadCallback)image_fetcher_read, ifinfo); - return; -} - -static void -image_fetcher_open (GnomeVFSAsyncHandle *handle, - GnomeVFSResult result, - ImageFetcherInfo *ifinfo) -{ - if (!ifinfo->html_finalized && result == GNOME_VFS_OK) { - ifinfo->buffer = g_slice_alloc (128); - gnome_vfs_async_read (handle, ifinfo->buffer, 128, - (GnomeVFSAsyncReadCallback) image_fetcher_read, ifinfo); - } else { - if (!ifinfo->html_finalized) { - gtk_html_stream_close (ifinfo->stream, GTK_HTML_STREAM_OK); - g_object_weak_unref ((GObject *) ifinfo->html, (GWeakNotify) html_finalized_notify, (gpointer) ifinfo); - } - g_slice_free (ImageFetcherInfo, ifinfo); - } -} - static gboolean on_url_requested (GtkWidget *widget, const gchar *uri, GtkHTMLStream *stream, ModestGtkhtmlMimePartView *self) @@ -333,26 +291,15 @@ on_url_requested (GtkWidget *widget, const gchar *uri, GtkHTMLStream *stream, TnyStream *tny_stream; g_return_val_if_fail (MODEST_IS_GTKHTML_MIME_PART_VIEW (self), FALSE); - if (g_str_has_prefix (uri, "http:") && - modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_FETCH_HTML_EXTERNAL_IMAGES, NULL)) { - GnomeVFSAsyncHandle *handle; - ImageFetcherInfo *ifinfo; - - ifinfo = g_slice_new (ImageFetcherInfo); - ifinfo->html_finalized = FALSE; - ifinfo->html = (GtkHTML *) self; - ifinfo->buffer = NULL; - ifinfo->stream = stream; - g_object_weak_ref ((GObject *) self, (GWeakNotify) html_finalized_notify, (gpointer) ifinfo); - gnome_vfs_async_open (&handle, uri, GNOME_VFS_OPEN_READ, - GNOME_VFS_PRIORITY_DEFAULT, - (GnomeVFSAsyncOpenCallback) image_fetcher_open, ifinfo); - return FALSE; + if (g_str_has_prefix (uri, "http:")) { + ModestGtkhtmlMimePartViewPrivate *priv = MODEST_GTKHTML_MIME_PART_VIEW_GET_PRIVATE (self); + + if (!priv->view_images) + priv->has_external_images = TRUE; } - - tny_stream = TNY_STREAM (modest_tny_stream_gtkhtml_new (stream)); + + tny_stream = TNY_STREAM (modest_tny_stream_gtkhtml_new (stream, GTK_HTML (widget))); g_signal_emit_by_name (MODEST_MIME_PART_VIEW (self), "fetch-url", uri, tny_stream, &result); - tny_stream_close (tny_stream); g_object_unref (tny_stream); return result; } @@ -370,7 +317,7 @@ set_html_part (ModestGtkhtmlMimePartView *self, TnyMimePart *part) gtkhtml_stream = gtk_html_begin(GTK_HTML(self)); - tny_stream = TNY_STREAM(modest_tny_stream_gtkhtml_new (gtkhtml_stream)); + tny_stream = TNY_STREAM(modest_tny_stream_gtkhtml_new (gtkhtml_stream, GTK_HTML (self))); tny_stream_reset (tny_stream); tny_mime_part_decode_to_stream ((TnyMimePart*)part, tny_stream, NULL); @@ -388,7 +335,7 @@ set_text_part (ModestGtkhtmlMimePartView *self, TnyMimePart *part) g_return_if_fail (part); gtkhtml_stream = gtk_html_begin(GTK_HTML(self)); - tny_stream = TNY_STREAM(modest_tny_stream_gtkhtml_new (gtkhtml_stream)); + tny_stream = TNY_STREAM(modest_tny_stream_gtkhtml_new (gtkhtml_stream, GTK_HTML (self))); text_to_html_stream = TNY_STREAM (modest_stream_text_to_html_new (tny_stream)); modest_stream_text_to_html_set_linkify_limit (MODEST_STREAM_TEXT_TO_HTML (text_to_html_stream), 64*1024); modest_stream_text_to_html_set_full_limit (MODEST_STREAM_TEXT_TO_HTML (text_to_html_stream), 640*1024); @@ -421,6 +368,7 @@ set_part (ModestGtkhtmlMimePartView *self, TnyMimePart *part) g_return_if_fail (self); priv = MODEST_GTKHTML_MIME_PART_VIEW_GET_PRIVATE(self); + priv->has_external_images = FALSE; if (part != priv->part) { if (priv->part) @@ -482,6 +430,39 @@ is_empty (ModestGtkhtmlMimePartView *self) return !has_contents; } +static gboolean +get_view_images (ModestGtkhtmlMimePartView *self) +{ + ModestGtkhtmlMimePartViewPrivate *priv; + + g_return_val_if_fail (MODEST_IS_GTKHTML_MIME_PART_VIEW (self), FALSE); + + priv = MODEST_GTKHTML_MIME_PART_VIEW_GET_PRIVATE (self); + return priv->view_images; +} + +static void +set_view_images (ModestGtkhtmlMimePartView *self, gboolean view_images) +{ + ModestGtkhtmlMimePartViewPrivate *priv; + + g_return_if_fail (MODEST_IS_GTKHTML_MIME_PART_VIEW (self)); + + priv = MODEST_GTKHTML_MIME_PART_VIEW_GET_PRIVATE (self); + priv->view_images = view_images; +} + +static gboolean +has_external_images (ModestGtkhtmlMimePartView *self) +{ + ModestGtkhtmlMimePartViewPrivate *priv; + + g_return_val_if_fail (MODEST_IS_GTKHTML_MIME_PART_VIEW (self), FALSE); + + priv = MODEST_GTKHTML_MIME_PART_VIEW_GET_PRIVATE (self); + return priv->has_external_images; +} + static void set_zoom (ModestGtkhtmlMimePartView *self, gdouble zoom) { @@ -602,6 +583,9 @@ modest_mime_part_view_init (gpointer g, gpointer iface_data) ModestMimePartViewIface *klass = (ModestMimePartViewIface *)g; klass->is_empty_func = modest_gtkhtml_mime_part_view_is_empty; + klass->get_view_images_func = modest_gtkhtml_mime_part_view_get_view_images; + klass->set_view_images_func = modest_gtkhtml_mime_part_view_set_view_images; + klass->has_external_images_func = modest_gtkhtml_mime_part_view_has_external_images; return; } @@ -613,11 +597,47 @@ modest_gtkhtml_mime_part_view_is_empty (ModestMimePartView *self) } static gboolean +modest_gtkhtml_mime_part_view_get_view_images (ModestMimePartView *self) +{ + return MODEST_GTKHTML_MIME_PART_VIEW_GET_CLASS (self)->get_view_images_func (self); +} + +static void +modest_gtkhtml_mime_part_view_set_view_images (ModestMimePartView *self, gboolean view_images) +{ + MODEST_GTKHTML_MIME_PART_VIEW_GET_CLASS (self)->set_view_images_func (self, view_images); +} + +static gboolean +modest_gtkhtml_mime_part_view_has_external_images (ModestMimePartView *self) +{ + return MODEST_GTKHTML_MIME_PART_VIEW_GET_CLASS (self)->has_external_images_func (self); +} + +static gboolean modest_gtkhtml_mime_part_view_is_empty_default (ModestMimePartView *self) { return is_empty (MODEST_GTKHTML_MIME_PART_VIEW (self)); } +static gboolean +modest_gtkhtml_mime_part_view_get_view_images_default (ModestMimePartView *self) +{ + return get_view_images (MODEST_GTKHTML_MIME_PART_VIEW (self)); +} + +static void +modest_gtkhtml_mime_part_view_set_view_images_default (ModestMimePartView *self, gboolean view_images) +{ + set_view_images (MODEST_GTKHTML_MIME_PART_VIEW (self), view_images); +} + +static gboolean +modest_gtkhtml_mime_part_view_has_external_images_default (ModestMimePartView *self) +{ + return has_external_images (MODEST_GTKHTML_MIME_PART_VIEW (self)); +} + /* MODEST ZOOMABLE IMPLEMENTATION */ static void diff --git a/src/widgets/modest-gtkhtml-mime-part-view.h b/src/widgets/modest-gtkhtml-mime-part-view.h index 510f071..8a134cf 100644 --- a/src/widgets/modest-gtkhtml-mime-part-view.h +++ b/src/widgets/modest-gtkhtml-mime-part-view.h @@ -65,6 +65,9 @@ struct _ModestGtkhtmlMimePartViewClass { void (*clear_func) (TnyMimePartView *self); /* ModestMimePartView interface methods */ gboolean (*is_empty_func) (ModestMimePartView *self); + gboolean (*get_view_images_func) (ModestMimePartView *self); + void (*set_view_images_func) (ModestMimePartView *self, gboolean view_images); + gboolean (*has_external_images_func) (ModestMimePartView *self); /* ModestZoomable interface methods */ gdouble (*get_zoom_func) (ModestZoomable *self); void (*set_zoom_func) (ModestZoomable *self, gdouble value); diff --git a/src/widgets/modest-gtkhtml-msg-view.c b/src/widgets/modest-gtkhtml-msg-view.c index 4532119..b752eb5 100644 --- a/src/widgets/modest-gtkhtml-msg-view.c +++ b/src/widgets/modest-gtkhtml-msg-view.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -70,6 +71,7 @@ static void get_property (GObject *object, guint prop_id, GValue *value, GPa /* headers signals */ static void on_recpt_activated (ModestMailHeaderView *header_view, const gchar *address, ModestGtkhtmlMsgView *msg_view); static void on_attachment_activated (ModestAttachmentsView * att_view, TnyMimePart *mime_part, gpointer userdata); +static void on_view_images_clicked (GtkButton * button, gpointer self); /* body view signals */ static gboolean on_activate_link (GtkWidget *widget, const gchar *uri, ModestGtkhtmlMsgView *msg_view); @@ -213,6 +215,7 @@ struct _ModestGtkhtmlMsgViewPrivate { GtkWidget *headers_box; GtkWidget *html_scroll; GtkWidget *attachments_box; + GtkWidget *view_images_button; /* internal adjustments for set_scroll_adjustments */ GtkAdjustment *hadj; @@ -1040,8 +1043,9 @@ modest_gtkhtml_msg_view_init (ModestGtkhtmlMsgView *obj) priv->body_view = GTK_WIDGET (g_object_new (MODEST_TYPE_GTKHTML_MIME_PART_VIEW, NULL)); priv->mail_header_view = GTK_WIDGET(modest_mail_header_view_new (TRUE)); + priv->view_images_button = gtk_button_new_with_label (_("TODO: view images")); gtk_widget_set_no_show_all (priv->mail_header_view, TRUE); - + gtk_widget_set_no_show_all (priv->view_images_button, TRUE); priv->attachments_view = GTK_WIDGET(modest_attachments_view_new (NULL)); g_signal_connect (G_OBJECT(priv->body_view), "activate_link", @@ -1061,6 +1065,9 @@ modest_gtkhtml_msg_view_init (ModestGtkhtmlMsgView *obj) g_signal_connect (G_OBJECT (priv->attachments_view), "activate", G_CALLBACK (on_attachment_activated), obj); + g_signal_connect (G_OBJECT (priv->view_images_button), "clicked", + G_CALLBACK (on_view_images_clicked), obj); + html_vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW(priv->html_scroll)); g_signal_connect (G_OBJECT (html_vadj), "changed", @@ -1073,6 +1080,10 @@ modest_gtkhtml_msg_view_init (ModestGtkhtmlMsgView *obj) if (priv->mail_header_view) gtk_box_pack_start (GTK_BOX(priv->headers_box), priv->mail_header_view, FALSE, FALSE, 0); + if (priv->view_images_button) { + gtk_box_pack_start (GTK_BOX (priv->headers_box), priv->view_images_button, FALSE, FALSE, 0); + gtk_widget_hide (priv->view_images_button); + } if (priv->attachments_view) { gchar *att_label = g_strconcat (_("mcen_me_viewer_attachments"), ":", NULL); @@ -1318,6 +1329,22 @@ on_attachment_activated (ModestAttachmentsView * att_view, TnyMimePart *mime_par g_signal_emit_by_name (G_OBJECT(self), "attachment_clicked", mime_part); } +static void +on_view_images_clicked (GtkButton * button, gpointer self) +{ + ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self); + TnyMimePart *part; + + modest_mime_part_view_set_view_images (MODEST_MIME_PART_VIEW (priv->body_view), TRUE); + gtk_widget_hide (priv->view_images_button); + part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (priv->body_view)); + tny_mime_part_view_set_part (TNY_MIME_PART_VIEW (priv->body_view), part); + tny_msg_set_allow_external_images (TNY_MSG (priv->msg), TRUE); + g_object_unref (part); + + +} + static gboolean on_activate_link (GtkWidget *widget, const gchar *uri, ModestGtkhtmlMsgView *self) { @@ -1426,8 +1453,12 @@ on_fetch_url (GtkWidget *widget, const gchar *uri, const gchar* my_uri; TnyMimePart *part = NULL; + + priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self); + if (modest_mime_part_view_has_external_images (MODEST_MIME_PART_VIEW (priv->body_view))) + gtk_widget_show (priv->view_images_button); /* * we search for either something starting with cid:, or something * with no prefix at all; this latter case occurs when sending mails @@ -1440,12 +1471,42 @@ on_fetch_url (GtkWidget *widget, const gchar *uri, /* now try to find the embedded image */ part = find_cid_image (priv->msg, my_uri); + if (!part) { - g_printerr ("modest: %s: '%s' not found\n", __FUNCTION__, my_uri); - return FALSE; + GtkIconTheme *current_theme; + GtkIconInfo *icon_info; + + if (g_str_has_prefix (uri, "http:")) { + if (modest_mime_part_view_get_view_images (MODEST_MIME_PART_VIEW (priv->body_view))) { + gboolean result = FALSE; + g_signal_emit_by_name (self, "fetch-image", uri, stream, &result); + return result; + } else { + current_theme = gtk_icon_theme_get_default (); + icon_info = gtk_icon_theme_lookup_icon (current_theme, "qgn_indi_messagin_nullcmas", 26, + GTK_ICON_LOOKUP_NO_SVG); + if (icon_info != NULL) { + const gchar *filename; + TnyStream *vfs_stream; + GnomeVFSHandle *handle; + filename = gtk_icon_info_get_filename (icon_info); + gnome_vfs_open (&handle, filename, GNOME_VFS_OPEN_READ); + vfs_stream = tny_vfs_stream_new (handle); + while (tny_stream_write_to_stream (vfs_stream, stream) > 0); + tny_stream_close (vfs_stream); + g_object_unref (vfs_stream); + gtk_icon_info_free (icon_info); + } + tny_stream_close (stream); + return TRUE; + } + } else { + return FALSE; + } } tny_mime_part_decode_to_stream ((TnyMimePart*)part, stream, NULL); + tny_stream_close (stream); g_object_unref (G_OBJECT(part)); return TRUE; } @@ -1462,6 +1523,7 @@ set_message (ModestGtkhtmlMsgView *self, TnyMsg *msg) priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE(self); gtk_widget_set_no_show_all (priv->mail_header_view, FALSE); + modest_mime_part_view_set_view_images (MODEST_MIME_PART_VIEW (priv->body_view), FALSE); html_vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->html_scroll)); html_vadj->upper = 0; @@ -1495,6 +1557,8 @@ set_message (ModestGtkhtmlMsgView *self, TnyMsg *msg) modest_attachments_view_set_message (MODEST_ATTACHMENTS_VIEW(priv->attachments_view), msg); + + modest_mime_part_view_set_view_images (MODEST_MIME_PART_VIEW (priv->body_view), tny_msg_get_allow_external_images (msg)); body = modest_tny_msg_find_body_part (msg, TRUE); if (body) { @@ -1509,6 +1573,13 @@ set_message (ModestGtkhtmlMsgView *self, TnyMsg *msg) } else tny_mime_part_view_clear (TNY_MIME_PART_VIEW (priv->body_view)); + if (modest_mime_part_view_has_external_images (MODEST_MIME_PART_VIEW (priv->body_view)) && + !modest_mime_part_view_get_view_images (MODEST_MIME_PART_VIEW (priv->body_view))) { + gtk_widget_show (priv->view_images_button); + } else { + gtk_widget_hide (priv->view_images_button); + } + gtk_widget_show (priv->body_view); gtk_widget_set_no_show_all (priv->attachments_box, TRUE); gtk_widget_show_all (priv->mail_header_view); diff --git a/src/widgets/modest-mime-part-view.c b/src/widgets/modest-mime-part-view.c index a276c6a..692428a 100644 --- a/src/widgets/modest-mime-part-view.c +++ b/src/widgets/modest-mime-part-view.c @@ -55,6 +55,47 @@ modest_mime_part_view_is_empty (ModestMimePartView *self) return MODEST_MIME_PART_VIEW_GET_IFACE (self)->is_empty_func (self); } +/** + * modest_mime_part_view_get_view_images: + * @self: a #ModestMimePartView + * + * checks if we have enabled capability to view contained images. + * + * Returns: %TRUE if we show images, %FALSE otherwise. + */ +gboolean +modest_mime_part_view_get_view_images (ModestMimePartView *self) +{ + return MODEST_MIME_PART_VIEW_GET_IFACE (self)->get_view_images_func (self); +} + +/** + * modest_mime_part_view_set_view_images: + * @self: a #ModestMimePartView + * @view_images: a #gboolean + * + * set if we want to show images or not. + */ +void +modest_mime_part_view_set_view_images (ModestMimePartView *self, gboolean view_images) +{ + MODEST_MIME_PART_VIEW_GET_IFACE (self)->set_view_images_func (self, view_images); +} + +/** + * modest_mime_part_view_has_external_images: + * @self: a #ModestMimePartView + * + * checks if there are external images in the mime part. + * + * Returns: %TRUE if there are external images, %FALSE otherwise. + */ +gboolean +modest_mime_part_view_has_external_images (ModestMimePartView *self) +{ + return MODEST_MIME_PART_VIEW_GET_IFACE (self)->has_external_images_func (self); +} + static void modest_mime_part_view_base_init (gpointer g_class) { diff --git a/src/widgets/modest-mime-part-view.h b/src/widgets/modest-mime-part-view.h index b559861..95d7ea8 100644 --- a/src/widgets/modest-mime-part-view.h +++ b/src/widgets/modest-mime-part-view.h @@ -55,11 +55,17 @@ struct _ModestMimePartViewIface /* virtuals */ gboolean (*is_empty_func) (ModestMimePartView *self); + gboolean (*get_view_images_func) (ModestMimePartView *self); + void (*set_view_images_func) (ModestMimePartView *self, gboolean view_images); + gboolean (*has_external_images_func) (ModestMimePartView *self); }; GType modest_mime_part_view_get_type (void); gboolean modest_mime_part_view_is_empty (ModestMimePartView *self); +gboolean modest_mime_part_view_get_view_images (ModestMimePartView *self); +void modest_mime_part_view_set_view_images (ModestMimePartView *self, gboolean view_images); +gboolean modest_mime_part_view_has_external_images (ModestMimePartView *self); G_END_DECLS diff --git a/src/widgets/modest-msg-view.c b/src/widgets/modest-msg-view.c index cfb5b6d..d79ad62 100644 --- a/src/widgets/modest-msg-view.c +++ b/src/widgets/modest-msg-view.c @@ -31,11 +31,13 @@ #include #include +#include enum { ATTACHMENT_CLICKED_SIGNAL, RECPT_ACTIVATED_SIGNAL, LINK_CONTEXTUAL_SIGNAL, + FETCH_IMAGE_SIGNAL, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; @@ -88,6 +90,12 @@ modest_msg_view_set_priority (ModestMsgView *self, TnyHeaderFlags flags) MODEST_MSG_VIEW_GET_IFACE (self)->set_priority_func (self, flags); } +void +modest_msg_view_set_view_images (ModestMsgView *self, gboolean view_images) +{ + MODEST_MSG_VIEW_GET_IFACE (self)->set_view_images_func (self, view_images); +} + TnyList* modest_msg_view_get_selected_attachments (ModestMsgView *self) { @@ -146,6 +154,15 @@ modest_msg_view_base_init (gpointer g_class) g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); + signals[FETCH_IMAGE_SIGNAL] = + g_signal_new ("fetch_image", + MODEST_TYPE_MSG_VIEW, + G_SIGNAL_ACTION | G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(ModestMsgViewIface, fetch_image), + NULL, NULL, + modest_marshal_BOOLEAN__STRING_OBJECT, + G_TYPE_BOOLEAN, 2, G_TYPE_STRING, G_TYPE_OBJECT); + initialized = TRUE; } } diff --git a/src/widgets/modest-msg-view.h b/src/widgets/modest-msg-view.h index 241ac51..02d819a 100644 --- a/src/widgets/modest-msg-view.h +++ b/src/widgets/modest-msg-view.h @@ -61,6 +61,7 @@ struct _ModestMsgViewIface { GtkShadowType (*get_shadow_type_func) (ModestMsgView *self); TnyHeaderFlags (*get_priority_func) (ModestMsgView *self); void (*set_priority_func) (ModestMsgView *self, TnyHeaderFlags flags); + void (*set_view_images_func) (ModestMsgView *self, gboolean view_images); TnyList * (*get_selected_attachments_func) (ModestMsgView *self); TnyList * (*get_attachments_func) (ModestMsgView *self); void (*grab_focus_func) (ModestMsgView *self); @@ -77,6 +78,8 @@ struct _ModestMsgViewIface { gpointer user_data); void (*recpt_activated) (ModestMsgView *msgview, const gchar *address, gpointer user_data); + gboolean (*fetch_image) (ModestMsgView *msgview, const gchar *uri, + TnyStream *stream); }; @@ -104,6 +107,7 @@ TnyList *modest_msg_view_get_selected_attachments (ModestMsgView *self); TnyList *modest_msg_view_get_attachments (ModestMsgView *self); void modest_msg_view_grab_focus (ModestMsgView *self); void modest_msg_view_remove_attachment (ModestMsgView *view, TnyMimePart *attachment); +void modest_msg_view_set_view_images (ModestMsgView *view, gboolean view_images); G_END_DECLS diff --git a/src/widgets/modest-tny-stream-gtkhtml.c b/src/widgets/modest-tny-stream-gtkhtml.c index af05333..943a9e1 100644 --- a/src/widgets/modest-tny-stream-gtkhtml.c +++ b/src/widgets/modest-tny-stream-gtkhtml.c @@ -52,6 +52,7 @@ enum { typedef struct _ModestTnyStreamGtkhtmlPrivate ModestTnyStreamGtkhtmlPrivate; struct _ModestTnyStreamGtkhtmlPrivate { GtkHTMLStream *stream; + GtkHTML *html; }; #define MODEST_TNY_STREAM_GTKHTML_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ MODEST_TYPE_TNY_STREAM_GTKHTML, \ @@ -116,6 +117,7 @@ modest_tny_stream_gtkhtml_init (ModestTnyStreamGtkhtml *obj) priv = MODEST_TNY_STREAM_GTKHTML_GET_PRIVATE(obj); priv->stream = NULL; + priv->html = NULL; } static void @@ -125,10 +127,14 @@ modest_tny_stream_gtkhtml_finalize (GObject *obj) priv = MODEST_TNY_STREAM_GTKHTML_GET_PRIVATE(obj); priv->stream = NULL; + if (priv->html) { + g_object_unref (priv->html); + priv->html = NULL; + } } GObject* -modest_tny_stream_gtkhtml_new (GtkHTMLStream *stream) +modest_tny_stream_gtkhtml_new (GtkHTMLStream *stream, GtkHTML *html) { GObject *obj; ModestTnyStreamGtkhtmlPrivate *priv; @@ -139,6 +145,7 @@ modest_tny_stream_gtkhtml_new (GtkHTMLStream *stream) g_return_val_if_fail (stream, NULL); priv->stream = stream; + priv->html = g_object_ref (html); return obj; } @@ -168,7 +175,10 @@ gtkhtml_write (TnyStream *self, const char *buffer, size_t n) if (n == 0 || !buffer) return 0; - + + if (!GTK_WIDGET_VISIBLE (priv->html)) + return -1; + gtk_html_stream_write (priv->stream, buffer, n); return n; /* hmmm */ } @@ -188,8 +198,14 @@ gtkhtml_close (TnyStream *self) g_return_val_if_fail (self, 0); priv = MODEST_TNY_STREAM_GTKHTML_GET_PRIVATE(self); - gtk_html_stream_close (priv->stream, GTK_HTML_STREAM_OK); + if (GTK_WIDGET_VISIBLE (priv->html)) { + gtk_html_stream_close (priv->stream, GTK_HTML_STREAM_OK); + } priv->stream = NULL; + if (priv->html) { + g_object_unref (priv->html); + priv->html = NULL; + } return 0; } diff --git a/src/widgets/modest-tny-stream-gtkhtml.h b/src/widgets/modest-tny-stream-gtkhtml.h index 42fd05a..7bc15a5 100644 --- a/src/widgets/modest-tny-stream-gtkhtml.h +++ b/src/widgets/modest-tny-stream-gtkhtml.h @@ -69,7 +69,7 @@ GType modest_tny_stream_gtkhtml_get_type (void) G_GNUC_CONST; * * Returns: a new #ModestTnyStreamGtkhtml **/ -GObject* modest_tny_stream_gtkhtml_new (GtkHTMLStream* stream); +GObject* modest_tny_stream_gtkhtml_new (GtkHTMLStream* stream, GtkHTML *html); G_END_DECLS -- 1.7.9.5