diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2009-11-08 14:49:48 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2009-11-10 08:57:12 +0800 |
commit | e13eb8a5b52c0b95f5b235c61245277230d565dc (patch) | |
tree | 8cf00d8bb9927c301029725e8e4009ba84c6df1f | |
parent | 6db0e254190a80cf1cccb3629442b78e76c55b36 (diff) | |
download | gsoc2013-evolution-e13eb8a5b52c0b95f5b235c61245277230d565dc.tar.gz gsoc2013-evolution-e13eb8a5b52c0b95f5b235c61245277230d565dc.tar.zst gsoc2013-evolution-e13eb8a5b52c0b95f5b235c61245277230d565dc.zip |
Add zooming and drag-and-drop to the plugin.
A bunch of crusty old code in em-format-html-display.c falls out.
-rw-r--r-- | mail/Makefile.am | 2 | ||||
-rw-r--r-- | mail/em-format-html-display.c | 277 | ||||
-rw-r--r-- | mail/em-format-html-display.h | 4 | ||||
-rw-r--r-- | mail/em-format-html.c | 4 | ||||
-rw-r--r-- | mail/em-icon-stream.c | 347 | ||||
-rw-r--r-- | mail/em-icon-stream.h | 61 | ||||
-rw-r--r-- | plugins/image-inline/Makefile.am | 1 | ||||
-rw-r--r-- | plugins/image-inline/image-inline.c | 165 |
8 files changed, 151 insertions, 710 deletions
diff --git a/mail/Makefile.am b/mail/Makefile.am index 7f284e96e2..01d41aaaa6 100644 --- a/mail/Makefile.am +++ b/mail/Makefile.am @@ -70,7 +70,6 @@ mailinclude_HEADERS = \ em-format-html-display.h \ em-format-html-print.h \ em-html-stream.h \ - em-icon-stream.h \ em-inline-filter.h \ em-junk.h \ em-search-context.h \ @@ -127,7 +126,6 @@ libevolution_mail_la_SOURCES = \ em-format-html-display.c \ em-format-html-print.c \ em-html-stream.c \ - em-icon-stream.c \ em-inline-filter.c \ em-junk.c \ em-search-context.c \ diff --git a/mail/em-format-html-display.c b/mail/em-format-html-display.c index de64e2b15c..34c146e05f 100644 --- a/mail/em-format-html-display.c +++ b/mail/em-format-html-display.c @@ -76,7 +76,6 @@ #include "e-mail-display.h" #include "e-mail-attachment-bar.h" #include "em-format-html-display.h" -#include "em-icon-stream.h" #include "em-utils.h" #include "widgets/misc/e-attachment-button.h" #include "widgets/misc/e-attachment-view.h" @@ -126,7 +125,6 @@ static const gchar *smime_sign_colour[5] = { }; static void efhd_attachment_frame(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri); -static gboolean efhd_attachment_image(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject); static void efhd_message_add_bar(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info); static gboolean efhd_attachment_button (EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject); static gboolean efhd_attachment_optional (EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *object); @@ -672,58 +670,7 @@ em_format_html_display_new (void) /* ********************************************************************** */ -static void -efhd_image(EMFormatHTML *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *handle) -{ - gchar *classid; - struct _attach_puri *info; - - classid = g_strdup_printf("image%s", ((EMFormat *)efh)->part_id->str); - info = (struct _attach_puri *)em_format_add_puri((EMFormat *)efh, sizeof(*info), classid, part, efhd_attachment_frame); - em_format_html_add_pobject(efh, sizeof(EMFormatHTMLPObject), classid, part, efhd_attachment_image); - - info->handle = handle; - info->shown = TRUE; - info->snoop_mime_type = ((EMFormat *) efh)->snoop_mime_type; - if (camel_operation_cancel_check (NULL) || !info->puri.format || !((EMFormatHTML *)info->puri.format)->html) { - /* some fake value, we are cancelled anyway, thus doesn't matter */ - info->fit_width = 256; - } else { - info->fit_width = ((GtkWidget *)((EMFormatHTML *)info->puri.format)->html)->allocation.width - 12; - } - - camel_stream_printf(stream, "<td><object classid=\"%s\"></object></td>", classid); - g_free(classid); -} - -/* ********************************************************************** */ - static EMFormatHandler type_builtin_table[] = { -#if 0 - { (gchar *) "image/gif", (EMFormatFunc)efhd_image }, - { (gchar *) "image/jpeg", (EMFormatFunc)efhd_image }, - { (gchar *) "image/png", (EMFormatFunc)efhd_image }, - { (gchar *) "image/x-png", (EMFormatFunc)efhd_image }, - { (gchar *) "image/tiff", (EMFormatFunc)efhd_image }, - { (gchar *) "image/x-bmp", (EMFormatFunc)efhd_image }, - { (gchar *) "image/bmp", (EMFormatFunc)efhd_image }, - { (gchar *) "image/svg", (EMFormatFunc)efhd_image }, - { (gchar *) "image/x-cmu-raster", (EMFormatFunc)efhd_image }, - { (gchar *) "image/x-ico", (EMFormatFunc)efhd_image }, - { (gchar *) "image/x-portable-anymap", (EMFormatFunc)efhd_image }, - { (gchar *) "image/x-portable-bitmap", (EMFormatFunc)efhd_image }, - { (gchar *) "image/x-portable-graymap", (EMFormatFunc)efhd_image }, - { (gchar *) "image/x-portable-pixmap", (EMFormatFunc)efhd_image }, - { (gchar *) "image/x-xpixmap", (EMFormatFunc)efhd_image }, - - /* This is where one adds those busted, non-registered types, - that some idiot mailer writers out there decide to pull out - of their proverbials at random. */ - - { (gchar *) "image/jpg", (EMFormatFunc)efhd_image }, - { (gchar *) "image/pjpeg", (EMFormatFunc)efhd_image }, -#endif - { (gchar *) "x-evolution/message/prefix", (EMFormatFunc)efhd_message_prefix }, { (gchar *) "x-evolution/message/post-header", (EMFormatFunc)efhd_message_add_bar } }; @@ -839,219 +786,6 @@ efhd_attachment_button_expanded (GtkWidget *widget, /* ********************************************************************** */ -static void -efhd_drag_data_get(GtkWidget *w, GdkDragContext *drag, GtkSelectionData *data, guint info, guint time, EMFormatHTMLPObject *pobject) -{ - CamelMimePart *part = pobject->part; - gchar *uri, *uri_crlf, *path; - CamelStream *stream; - - switch (info) { - case 0: /* mime/type request */ - stream = camel_stream_mem_new(); - /* TODO: shoudl format_format_text run on the content-object? */ - /* TODO: should we just do format_content? */ - if (camel_content_type_is (((CamelDataWrapper *)part)->mime_type, "text", "*")) { - /* FIXME: this should be an em_utils method, it only needs a default charset param */ - em_format_format_text((EMFormat *)pobject->format, stream, (CamelDataWrapper *)part); - } else { - CamelDataWrapper *dw = camel_medium_get_content_object((CamelMedium *)part); - - camel_data_wrapper_decode_to_stream(dw, stream); - } - - gtk_selection_data_set(data, data->target, 8, - ((CamelStreamMem *)stream)->buffer->data, - ((CamelStreamMem *)stream)->buffer->len); - camel_object_unref(stream); - break; - case 1: /* text-uri-list request */ - /* Kludge around Nautilus requesting the same data many times */ - uri = g_object_get_data((GObject *)w, "e-drag-uri"); - if (uri) { - gtk_selection_data_set(data, data->target, 8, (guchar *)uri, strlen(uri)); - return; - } - - path = em_utils_temp_save_part(w, part, FALSE); - if (path == NULL) - return; - - uri = g_filename_to_uri(path, NULL, NULL); - g_free(path); - uri_crlf = g_strconcat(uri, "\r\n", NULL); - g_free(uri); - gtk_selection_data_set(data, data->target, 8, (guchar *)uri_crlf, strlen(uri_crlf)); - g_object_set_data_full((GObject *)w, "e-drag-uri", uri_crlf, g_free); - break; - default: - abort(); - } -} - -static void -efhd_drag_data_delete(GtkWidget *w, GdkDragContext *drag, EMFormatHTMLPObject *pobject) -{ - gchar *uri; - - uri = g_object_get_data((GObject *)w, "e-drag-uri"); - if (uri) { - /* NB: this doesn't kill the dnd directory */ - /* NB: is this ever called? */ - /* NB even more: doesn't the e-drag-uri have \r\n - * appended? (see efhd_drag_data_get()) - */ - gchar *filename = g_filename_from_uri (uri, NULL, NULL); - g_unlink(filename); - g_free(filename); - g_object_set_data((GObject *)w, "e-drag-uri", NULL); - } -} - -static void -efhd_write_icon_job(struct _EMFormatHTMLJob *job, gint cancelled) -{ - EMFormatHTMLPObject *pobject; - CamelDataWrapper *dw; - - if (cancelled) - return; - - pobject = job->u.data; - dw = camel_medium_get_content_object((CamelMedium *)pobject->part); - camel_data_wrapper_decode_to_stream(dw, job->stream); - camel_stream_close(job->stream); -} - -static void -efhd_image_resized(GtkWidget *w, GtkAllocation *event, struct _attach_puri *info) -{ - GdkPixbuf *pb; - gint width; - - if (info->fit_width == 0) - return; - - width = ((GtkWidget *)((EMFormatHTML *)info->puri.format)->html)->allocation.width - 12; - if (info->fit_width == width) - return; - info->fit_width = width; - - pb = em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height); - if (pb) { - gtk_image_set_from_pixbuf(info->image, pb); - g_object_unref(pb); - } -} - -static void -efhd_change_cursor(GtkWidget *w, GdkEventCrossing *event, struct _attach_puri *info) -{ - if (info->shown && info->image) { - if (info->fit_width != 0) { - if (em_icon_stream_is_resized(info->puri.cid, info->fit_width, info->fit_height)) - e_cursor_set(w->window, E_CURSOR_ZOOM_IN); - - } - } -} - -static void -efhd_image_fit_width(GtkWidget *widget, GdkEventButton *event, struct _attach_puri *info) -{ - gint width; - - width = ((GtkWidget *)((EMFormatHTML *)info->puri.format)->html)->allocation.width - 12; - - if (info->shown && info->image) { - if (info->fit_width != 0) { - if (em_icon_stream_is_resized(info->puri.cid, info->fit_width, info->fit_height)) { - if (info->fit_width != width) { - info->fit_width = width; - e_cursor_set (widget->window, E_CURSOR_ZOOM_IN); - } else { - info->fit_width = 0; - e_cursor_set(widget->window, E_CURSOR_ZOOM_OUT); - } - } - } else { - info->fit_width = width; - e_cursor_set (widget->window, E_CURSOR_ZOOM_IN); - } - } - - gtk_image_set_from_pixbuf(info->image, em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height)); -} - -/* When the puri gets freed in the formatter thread and if the image is resized, crash will happen - See bug #333864 So while freeing the puri, we disconnect the image attach resize attached with - the puri */ - -static void -efhd_image_unallocate (struct _EMFormatPURI * puri) -{ - struct _attach_puri *info = (struct _attach_puri *) puri; - g_signal_handlers_disconnect_by_func(info->html, efhd_image_resized, info); - - g_signal_handlers_disconnect_by_func(info->event_box, efhd_change_cursor, info); - g_signal_handlers_disconnect_by_func(info->event_box, efhd_image_fit_width, info); -} - -static gboolean -efhd_attachment_image(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject) -{ - GtkWidget *box; - EMFormatHTMLJob *job; - struct _attach_puri *info; - GdkPixbuf *pixbuf; - GtkTargetEntry drag_types[] = { - { NULL, 0, 0 }, - { (gchar *) "text/uri-list", 0, 1 }, - }; - gchar *simple_type; - - info = (struct _attach_puri *)em_format_find_puri((EMFormat *)efh, pobject->classid); - - info->image = (GtkImage *)gtk_image_new(); - info->html = eb; - info->puri.free = efhd_image_unallocate; - - pixbuf = em_icon_stream_get_image(pobject->classid, info->fit_width, info->fit_height); - if (pixbuf) { - gtk_image_set_from_pixbuf(info->image, pixbuf); - g_object_unref(pixbuf); - } else { - job = em_format_html_job_new(efh, efhd_write_icon_job, pobject); - job->stream = (CamelStream *)em_icon_stream_new((GtkImage *)info->image, pobject->classid, info->fit_width, info->fit_height, TRUE); - em_format_html_job_queue(efh, job); - } - - box = gtk_event_box_new(); - info->event_box = box; - gtk_container_add((GtkContainer *)box, (GtkWidget *)info->image); - gtk_widget_show_all(box); - gtk_container_add((GtkContainer *)eb, box); - - g_signal_connect(eb, "size_allocate", G_CALLBACK(efhd_image_resized), info); - - simple_type = camel_content_type_simple(((CamelDataWrapper *)pobject->part)->mime_type); - camel_strdown(simple_type); - - drag_types[0].target = simple_type; - gtk_drag_source_set(box, GDK_BUTTON1_MASK, drag_types, G_N_ELEMENTS (drag_types), GDK_ACTION_COPY); - g_free(simple_type); - - g_signal_connect(box, "drag-data-get", G_CALLBACK(efhd_drag_data_get), pobject); - g_signal_connect (box, "drag-data-delete", G_CALLBACK(efhd_drag_data_delete), pobject); - - g_signal_connect(box, "enter-notify-event", G_CALLBACK(efhd_change_cursor), info); - g_signal_connect(box, "button-press-event", G_CALLBACK(efhd_image_fit_width), info); - - g_object_set_data (G_OBJECT (box), "efh", efh); - - return TRUE; -} - /* attachment button callback */ static gboolean efhd_attachment_button(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject) @@ -1083,8 +817,8 @@ efhd_attachment_button(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObje parent = gtk_widget_get_toplevel (GTK_WIDGET (efh->html)); parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL; - view = E_ATTACHMENT_VIEW (efhd->priv->attachment_view); - gtk_widget_show (efhd->priv->attachment_view); + view = em_format_html_display_get_attachment_view (efhd); + gtk_widget_show (GTK_WIDGET (view)); store = e_attachment_view_get_store (view); e_attachment_store_add_attachment (store, info->attachment); @@ -1298,3 +1032,10 @@ efhd_attachment_optional(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPOb return TRUE; } +EAttachmentView * +em_format_html_display_get_attachment_view (EMFormatHTMLDisplay *html_display) +{ + g_return_val_if_fail (EM_IS_FORMAT_HTML_DISPLAY (html_display), NULL); + + return E_ATTACHMENT_VIEW (html_display->priv->attachment_view); +} diff --git a/mail/em-format-html-display.h b/mail/em-format-html-display.h index 4626598cb5..089b361592 100644 --- a/mail/em-format-html-display.h +++ b/mail/em-format-html-display.h @@ -26,6 +26,7 @@ #define EM_FORMAT_HTML_DISPLAY_H #include <mail/em-format-html.h> +#include <misc/e-attachment-view.h> /* Standard GObject macros */ #define EM_TYPE_FORMAT_HTML_DISPLAY \ @@ -66,6 +67,9 @@ struct _EMFormatHTMLDisplayClass { GType em_format_html_display_get_type (void); EMFormatHTMLDisplay * em_format_html_display_new (void); +EAttachmentView * + em_format_html_display_get_attachment_view + (EMFormatHTMLDisplay *html_display); G_END_DECLS diff --git a/mail/em-format-html.c b/mail/em-format-html.c index 29bb47653f..47213dc5d5 100644 --- a/mail/em-format-html.c +++ b/mail/em-format-html.c @@ -2105,7 +2105,6 @@ efh_image(EMFormatHTML *efh, CamelStream *stream, CamelMimePart *part, EMFormatH } static EMFormatHandler type_builtin_table[] = { -#if 0 { (gchar *) "image/gif", (EMFormatFunc)efh_image }, { (gchar *) "image/jpeg", (EMFormatFunc)efh_image }, { (gchar *) "image/png", (EMFormatFunc)efh_image }, @@ -2121,7 +2120,6 @@ static EMFormatHandler type_builtin_table[] = { { (gchar *) "image/x-portable-graymap", (EMFormatFunc)efh_image }, { (gchar *) "image/x-portable-pixmap", (EMFormatFunc)efh_image }, { (gchar *) "image/x-xpixmap", (EMFormatFunc)efh_image }, -#endif { (gchar *) "text/enriched", (EMFormatFunc)efh_text_enriched }, { (gchar *) "text/plain", (EMFormatFunc)efh_text_plain }, { (gchar *) "text/html", (EMFormatFunc)efh_text_html }, @@ -2135,10 +2133,8 @@ static EMFormatHandler type_builtin_table[] = { that some idiot mailer writers out there decide to pull out of their proverbials at random. */ -#if 0 { (gchar *) "image/jpg", (EMFormatFunc)efh_image }, { (gchar *) "image/pjpeg", (EMFormatFunc)efh_image }, -#endif /* special internal types */ diff --git a/mail/em-icon-stream.c b/mail/em-icon-stream.c deleted file mode 100644 index 563853adcf..0000000000 --- a/mail/em-icon-stream.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with the program; if not, see <http://www.gnu.org/licenses/> - * - * - * Authors: - * Jeffrey Stedfast <fejj@ximian.com> - * Michael Zucchi <notzed@ximian.com> - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <stdio.h> -#include <string.h> - -#include <gdk-pixbuf/gdk-pixbuf.h> -#include <gtk/gtk.h> -#include "em-icon-stream.h" -#include "e-util/e-icon-factory.h" - -#include "libedataserver/e-msgport.h" - -#define d(x) - -/* fixed-point scale factor for scaled images in cache */ -#define EMIS_SCALE (1024) - -struct _emis_cache_node { - EMCacheNode node; - - GdkPixbuf *pixbuf; -}; - -static void em_icon_stream_class_init (EMIconStreamClass *klass); -static void em_icon_stream_init (CamelObject *object); -static void em_icon_stream_finalize (CamelObject *object); - -static gssize emis_sync_write(CamelStream *stream, const gchar *buffer, gsize n); -static gint emis_sync_close(CamelStream *stream); -static gint emis_sync_flush(CamelStream *stream); - -static EMSyncStreamClass *parent_class = NULL; -static EMCache *emis_cache; - -static void -emis_cache_free(gpointer data) -{ - struct _emis_cache_node *node = data; - - g_object_unref(node->pixbuf); -} - -CamelType -em_icon_stream_get_type (void) -{ - static CamelType type = CAMEL_INVALID_TYPE; - - if (type == CAMEL_INVALID_TYPE) { - parent_class = (EMSyncStreamClass *)em_sync_stream_get_type(); - type = camel_type_register (em_sync_stream_get_type(), - "EMIconStream", - sizeof (EMIconStream), - sizeof (EMIconStreamClass), - (CamelObjectClassInitFunc) em_icon_stream_class_init, - NULL, - (CamelObjectInitFunc) em_icon_stream_init, - (CamelObjectFinalizeFunc) em_icon_stream_finalize); - - emis_cache = em_cache_new(60, sizeof(struct _emis_cache_node), emis_cache_free); - } - - return type; -} - -static void -em_icon_stream_class_init (EMIconStreamClass *klass) -{ - ((EMSyncStreamClass *)klass)->sync_write = emis_sync_write; - ((EMSyncStreamClass *)klass)->sync_flush = emis_sync_flush; - ((EMSyncStreamClass *)klass)->sync_close = emis_sync_close; -} - -static void -em_icon_stream_init (CamelObject *object) -{ - EMIconStream *emis = (EMIconStream *)object; - - emis = emis; -} - -static void -emis_cleanup(EMIconStream *emis) -{ - if (emis->loader) { - gdk_pixbuf_loader_close(emis->loader, NULL); - g_object_unref(emis->loader); - emis->loader = NULL; - } - - if (emis->destroy_id) { - g_signal_handler_disconnect(emis->image, emis->destroy_id); - emis->destroy_id = 0; - } - - g_free(emis->key); - emis->key = NULL; - - emis->image = NULL; - emis->sync.cancel = TRUE; -} - -static void -em_icon_stream_finalize(CamelObject *object) -{ - EMIconStream *emis = (EMIconStream *)object; - - emis_cleanup(emis); -} - -static gssize -emis_sync_write(CamelStream *stream, const gchar *buffer, gsize n) -{ - EMIconStream *emis = EM_ICON_STREAM (stream); - - if (emis->loader == NULL) - return -1; - - if (!gdk_pixbuf_loader_write(emis->loader, (const guchar *)buffer, n, NULL)) { - emis_cleanup(emis); - return -1; - } - - return (gssize) n; -} - -static gint -emis_sync_flush(CamelStream *stream) -{ - return 0; -} - -static GdkPixbuf * -emis_fit(GdkPixbuf *pixbuf, gint maxwidth, gint maxheight, gint *scale) -{ - GdkPixbuf *mini = NULL; - gint width, height; - - width = gdk_pixbuf_get_width(pixbuf); - height = gdk_pixbuf_get_height(pixbuf); - - if ((maxwidth && width > maxwidth) - || (maxheight && height > maxheight)) { - if (width >= height || maxheight == 0) { - if (scale) - *scale = maxwidth * EMIS_SCALE / width; - height = height * maxwidth / width; - width = maxwidth; - } else { - if (scale) - *scale = maxheight * EMIS_SCALE / height; - width = width * maxheight / height; - height = maxheight; - } - - /* check if we don't want to scale down too much, if so, do 1 pixel width/height */ - if (width <= 0) - width = 1; - - if (height <= 0) - height = 1; - - mini = e_icon_factory_pixbuf_scale (pixbuf, width, height); - } - - return mini; -} - -static gint -emis_sync_close(CamelStream *stream) -{ - EMIconStream *emis = (EMIconStream *)stream; - GdkPixbuf *pixbuf, *mini; - struct _emis_cache_node *node; - gchar *scalekey; - gint scale; - - if (emis->loader == NULL) - return -1; - - gdk_pixbuf_loader_close(emis->loader, NULL); - - pixbuf = gdk_pixbuf_loader_get_pixbuf(emis->loader); - if (pixbuf == NULL) { - d(printf("couldn't get pixbuf from loader\n")); - emis_cleanup(emis); - return -1; - } - - mini = emis_fit(pixbuf, emis->width, emis->height, &scale); - gtk_image_set_from_pixbuf(emis->image, mini?mini:pixbuf); - - if (emis->keep) { - node = (struct _emis_cache_node *)em_cache_node_new(emis_cache, emis->key); - node->pixbuf = g_object_ref(pixbuf); - em_cache_add(emis_cache, (EMCacheNode *)node); - } - - if (!emis->keep || mini) { - scalekey = g_alloca(strlen(emis->key) + 20); - sprintf(scalekey, "%s.%x", emis->key, scale); - node = (struct _emis_cache_node *)em_cache_node_new(emis_cache, scalekey); - node->pixbuf = mini?mini:g_object_ref(pixbuf); - em_cache_add(emis_cache, (EMCacheNode *)node); - } - - g_object_unref(emis->loader); - emis->loader = NULL; - - g_signal_handler_disconnect(emis->image, emis->destroy_id); - emis->destroy_id = 0; - - return 0; -} - -static void -emis_image_destroy(GtkImage *image, EMIconStream *emis) -{ - emis_cleanup(emis); -} - -CamelStream * -em_icon_stream_new(GtkImage *image, const gchar *key, guint maxwidth, guint maxheight, gint keep) -{ - EMIconStream *new; - - new = EM_ICON_STREAM(camel_object_new(EM_ICON_STREAM_TYPE)); - new->width = maxwidth; - new->height = maxheight; - new->image = image; - new->keep = keep; - new->destroy_id = g_signal_connect(image, "destroy", G_CALLBACK(emis_image_destroy), new); - new->loader = gdk_pixbuf_loader_new(); - new->key = g_strdup(key); - - return (CamelStream *)new; -} - -GdkPixbuf * -em_icon_stream_get_image(const gchar *tkey, guint maxwidth, guint maxheight) -{ - struct _emis_cache_node *node; - GdkPixbuf *pb = NULL; - const gchar *key; - - key = tkey ? tkey : ""; - - /* forces the cache to be setup if not */ - em_icon_stream_get_type(); - - node = (struct _emis_cache_node *)em_cache_lookup(emis_cache, key); - if (node) { - gint width, height; - - pb = node->pixbuf; - g_object_ref(pb); - em_cache_node_unref(emis_cache, (EMCacheNode *)node); - - width = gdk_pixbuf_get_width(pb); - height = gdk_pixbuf_get_height(pb); - - if ((maxwidth && width > maxwidth) - || (maxheight && height > maxheight)) { - guint scale; - gchar *realkey; - - if (maxheight == 0 || width >= height) - scale = width * EMIS_SCALE / maxwidth; - else - scale = height * EMIS_SCALE / maxheight; - - realkey = g_alloca(strlen(key)+20); - sprintf(realkey, "%s.%x", key, scale); - node = (struct _emis_cache_node *)em_cache_lookup(emis_cache, realkey); - if (node) { - g_object_unref(pb); - pb = node->pixbuf; - g_object_ref(pb); - em_cache_node_unref(emis_cache, (EMCacheNode *)node); - } else { - GdkPixbuf *mini = emis_fit(pb, maxwidth, maxheight, NULL); - - g_object_unref(pb); - pb = mini; - node = (struct _emis_cache_node *)em_cache_node_new(emis_cache, realkey); - node->pixbuf = pb; - g_object_ref(pb); - em_cache_add(emis_cache, (EMCacheNode *)node); - } - } - } - - return pb; -} - -gint -em_icon_stream_is_resized(const gchar *tkey, guint maxwidth, guint maxheight) -{ - gint res = FALSE; - struct _emis_cache_node *node; - const gchar *key; - - key = tkey ? tkey : ""; - - /* forces the cache to be setup if not */ - em_icon_stream_get_type(); - - node = (struct _emis_cache_node *)em_cache_lookup(emis_cache, key); - if (node) { - res = (maxwidth && gdk_pixbuf_get_width(node->pixbuf) > maxwidth) - || (maxheight && gdk_pixbuf_get_width(node->pixbuf) > maxheight); - - em_cache_node_unref(emis_cache, (EMCacheNode *)node); - } - - return res; -} - -void -em_icon_stream_clear_cache(void) -{ - em_cache_clear(emis_cache); -} diff --git a/mail/em-icon-stream.h b/mail/em-icon-stream.h deleted file mode 100644 index a06b30d4de..0000000000 --- a/mail/em-icon-stream.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with the program; if not, see <http://www.gnu.org/licenses/> - * - * - * Authors: - * Michael Zucchi <notzed@ximian.com> - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifndef EM_ICON_STREAM_H -#define EM_ICON_STREAM_H - -#include "mail/em-sync-stream.h" - -#define EM_ICON_STREAM_TYPE (em_icon_stream_get_type ()) -#define EM_ICON_STREAM(obj) (CAMEL_CHECK_CAST((obj), EM_ICON_STREAM_TYPE, EMIconStream)) -#define EM_ICON_STREAM_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), EM_ICON_STREAM_TYPE, EMIconStreamClass)) -#define EM_IS_ICON_STREAM(o) (CAMEL_CHECK_TYPE((o), EM_ICON_STREAM_TYPE)) - -G_BEGIN_DECLS - -typedef struct _EMIconStream { - EMSyncStream sync; - - guint width, height; - guint destroy_id; - struct _GdkPixbufLoader *loader; - GtkImage *image; - gchar *key; - - guint keep:1; -} EMIconStream; - -typedef struct { - EMSyncStreamClass parent_class; -} EMIconStreamClass; - -CamelType em_icon_stream_get_type (void); -CamelStream *em_icon_stream_new(GtkImage *image, const gchar *key, guint maxwidth, guint maxheight, gint keep); - -struct _GdkPixbuf *em_icon_stream_get_image(const gchar *key, guint maxwidth, guint maxheight); -gint em_icon_stream_is_resized(const gchar *key, guint maxwidth, guint maxheight); - -void em_icon_stream_clear_cache(void); - -G_END_DECLS - -#endif /* EM_ICON_STREAM_H */ diff --git a/plugins/image-inline/Makefile.am b/plugins/image-inline/Makefile.am index 5ff7c260eb..d59203a79b 100644 --- a/plugins/image-inline/Makefile.am +++ b/plugins/image-inline/Makefile.am @@ -7,6 +7,7 @@ plugin_LTLIBRARIES = liborg-gnome-image-inline.la liborg_gnome_image_inline_la_CPPFLAGS = \ $(AM_CPPFLAGS) \ -I$(top_srcdir) \ + -I$(top_srcdir)/widgets \ $(GNOME_PLATFORM_CFLAGS) \ $(EVOLUTION_MAIL_CFLAGS) \ $(GTKIMAGEVIEW_CFLAGS) diff --git a/plugins/image-inline/image-inline.c b/plugins/image-inline/image-inline.c index 81e5d92c5e..688a7d7d17 100644 --- a/plugins/image-inline/image-inline.c +++ b/plugins/image-inline/image-inline.c @@ -31,8 +31,8 @@ #include <gtkhtml/gtkhtml-embedded.h> #include <gtkimageview/gtkimagescrollwin.h> -#include "mail/em-format-hook.h" -#include "mail/em-format-html.h" +#include <mail/em-format-hook.h> +#include <mail/em-format-html-display.h> static gint org_gnome_image_inline_classid; @@ -44,13 +44,118 @@ typedef struct _ImageInlinePObject ImageInlinePObject; struct _ImageInlinePObject { EMFormatHTMLPObject object; + CamelMimePart *mime_part; GdkPixbuf *pixbuf; GtkWidget *widget; }; static void +set_drag_source (GtkImageView *image_view) +{ + GtkTargetEntry *targets; + GtkTargetList *list; + gint n_targets; + + list = gtk_target_list_new (NULL, 0); + gtk_target_list_add_uri_targets (list, 0); + targets = gtk_target_table_new_from_list (list, &n_targets); + + gtk_drag_source_set ( + GTK_WIDGET (image_view), GDK_BUTTON1_MASK, + targets, n_targets, GDK_ACTION_COPY); + + gtk_target_table_free (targets, n_targets); + gtk_target_list_unref (list); +} + +static gboolean +button_press_press_cb (GtkImageView *image_view, + GdkEventButton *event, + ImageInlinePObject *image_object) +{ + if (event->type != GDK_2BUTTON_PRESS) + return FALSE; + + if (gtk_image_view_get_zoom (image_view) < 1.0) { + gtk_image_view_set_zoom (image_view, 1.0); + gtk_drag_source_unset (GTK_WIDGET (image_view)); + } else { + gtk_image_view_set_fitting (image_view, TRUE); + set_drag_source (image_view); + } + + return TRUE; +} + +static void +drag_data_get_cb (GtkImageView *image_view, + GdkDragContext *context, + GtkSelectionData *selection, + guint info, + guint time, + ImageInlinePObject *image_object) +{ + EMFormatHTMLDisplay *html_display; + EAttachmentStore *store; + EAttachmentView *view; + EAttachment *attachment = NULL; + GtkTreeRowReference *reference; + GtkTreePath *path; + GList *list, *iter; + + /* XXX This illustrates the lack of integration between EMFormat + * and EAttachment, in that we now have to search through the + * attachment store to find an attachment whose CamelMimePart + * matches ours. This allows us to defer to EAttachmentView + * for the drag-data-get implementation. */ + + html_display = EM_FORMAT_HTML_DISPLAY (image_object->object.format); + view = em_format_html_display_get_attachment_view (html_display); + + store = e_attachment_view_get_store (view); + list = e_attachment_store_get_attachments (store); + + for (iter = list; iter != NULL; iter = iter->next) { + CamelMimePart *mime_part; + + attachment = E_ATTACHMENT (iter->data); + mime_part = e_attachment_get_mime_part (attachment); + + if (mime_part == image_object->mime_part) { + g_object_ref (attachment); + break; + } + + attachment = NULL; + } + + g_list_foreach (list, (GFunc) g_object_unref, NULL); + g_list_free (list); + + /* Make sure we found an EAttachment to select. */ + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + + /* Now select its path in the attachment store. */ + + reference = e_attachment_get_reference (attachment); + g_return_if_fail (gtk_tree_row_reference_valid (reference)); + + path = gtk_tree_row_reference_get_path (reference); + + e_attachment_view_unselect_all (view); + e_attachment_view_select_path (view, path); + + gtk_tree_path_free (path); + + /* Let EAttachmentView handle the rest. */ + + e_attachment_view_drag_data_get ( + view, context, selection, info, time); +} + +static void size_allocate_cb (GtkHTMLEmbedded *embedded, - GtkAllocation *event, + GtkAllocation *allocation, ImageInlinePObject *image_object) { GtkWidget *widget; @@ -58,7 +163,7 @@ size_allocate_cb (GtkHTMLEmbedded *embedded, gint pixbuf_height; gint widget_width; gint widget_height; - gdouble zoom; + gdouble zoom = 1.0; widget = GTK_WIDGET (image_object->object.format->html); widget_width = widget->allocation.width - 12; @@ -66,9 +171,7 @@ size_allocate_cb (GtkHTMLEmbedded *embedded, pixbuf_width = gdk_pixbuf_get_width (image_object->pixbuf); pixbuf_height = gdk_pixbuf_get_height (image_object->pixbuf); - if (pixbuf_width <= widget_width) - zoom = 1.0; - else + if (pixbuf_width > widget_width) zoom = (gdouble) widget_width / pixbuf_width; widget_width = MIN (widget_width, pixbuf_width); @@ -85,6 +188,11 @@ org_gnome_image_inline_pobject_free (EMFormatHTMLPObject *object) image_object = (ImageInlinePObject *) object; + if (image_object->mime_part != NULL) { + camel_object_unref (image_object->mime_part); + image_object->mime_part = NULL; + } + if (image_object->pixbuf != NULL) { g_object_unref (image_object->pixbuf); image_object->pixbuf = NULL; @@ -97,20 +205,19 @@ org_gnome_image_inline_pobject_free (EMFormatHTMLPObject *object) } static void -org_gnome_image_inline_decode (ImageInlinePObject *image_object, - CamelMimePart *mime_part) +org_gnome_image_inline_decode (ImageInlinePObject *image_object) { GdkPixbuf *pixbuf; GdkPixbufLoader *loader; - CamelContentType *content_type; CamelDataWrapper *data_wrapper; + CamelMimePart *mime_part; CamelMedium *medium; CamelStream *stream; GByteArray *array; - gchar *mime_type; GError *error = NULL; array = g_byte_array_new (); + mime_part = image_object->mime_part; medium = CAMEL_MEDIUM (mime_part); /* Stream takes ownership of the byte array. */ @@ -118,17 +225,10 @@ org_gnome_image_inline_decode (ImageInlinePObject *image_object, data_wrapper = camel_medium_get_content_object (medium); camel_data_wrapper_decode_to_stream (data_wrapper, stream); - content_type = camel_mime_part_get_content_type (mime_part); - mime_type = camel_content_type_simple (content_type); - loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, &error); - g_free (mime_type); - - if (error != NULL) { - g_warning ("%s", error->message); - g_error_free (error); - goto exit; - } - + /* Don't trust the content type in the MIME part. It could + * be lying or it could be "application/octet-stream". Let + * the GtkPixbufLoader figure it out. */ + loader = gdk_pixbuf_loader_new (); gdk_pixbuf_loader_write (loader, array->data, array->len, &error); if (error != NULL) { @@ -150,8 +250,8 @@ org_gnome_image_inline_decode (ImageInlinePObject *image_object, } exit: - camel_object_unref (mime_part); camel_object_unref (stream); + g_object_unref (loader); } static gboolean @@ -172,17 +272,25 @@ org_gnome_image_inline_embed (EMFormatHTML *format, container = GTK_WIDGET (embedded); widget = gtk_image_view_new (); - image_view = GTK_IMAGE_VIEW (widget); - gtk_widget_show (widget); - - widget = gtk_image_scroll_win_new (image_view); gtk_container_add (GTK_CONTAINER (container), widget); image_object->widget = g_object_ref (widget); gtk_widget_show (widget); + image_view = GTK_IMAGE_VIEW (widget); + gtk_image_view_set_pixbuf ( image_view, image_object->pixbuf, TRUE); + set_drag_source (image_view); + + g_signal_connect ( + image_view, "button-press-event", + G_CALLBACK (button_press_press_cb), image_object); + + g_signal_connect ( + image_view, "drag-data-get", + G_CALLBACK (drag_data_get_cb), image_object); + g_signal_connect ( embedded, "size-allocate", G_CALLBACK (size_allocate_cb), image_object); @@ -208,9 +316,10 @@ org_gnome_image_inline_format (gpointer ep, EMFormatHookTarget *target) org_gnome_image_inline_embed); camel_object_ref (target->part); + image_object->mime_part = target->part; image_object->object.free = org_gnome_image_inline_pobject_free; - org_gnome_image_inline_decode (image_object, target->part); + org_gnome_image_inline_decode (image_object); camel_stream_printf ( target->stream, "<object classid=%s></object>", classid); |