diff options
-rw-r--r-- | mail/ChangeLog | 26 | ||||
-rw-r--r-- | mail/em-format-html-display.c | 166 | ||||
-rw-r--r-- | mail/em-icon-stream.c | 151 | ||||
-rw-r--r-- | mail/em-icon-stream.h | 8 |
4 files changed, 306 insertions, 45 deletions
diff --git a/mail/ChangeLog b/mail/ChangeLog index 0e005ad0c1..732ecb35af 100644 --- a/mail/ChangeLog +++ b/mail/ChangeLog @@ -1,3 +1,29 @@ +2005-07-05 Not Zed <NotZed@Ximian.com> + + * em-format-html-display.c (efhd_attachment_image): use the cache, + since we set it up. + (efhd_image): added a (private!) format handler for all the image + types so we intercept them and handle them directly. + (efhd_image_fit, efhd_image_unfit): replace the resize callback + with two much simpler ones. + + * em-icon-stream.c (em_icon_stream_get_image): added 'fit to' + arguments. Changed dramatically to get approximate fit-to image, + update cache, etc. + (em_icon_stream_is_resized): added 'fit to' arguments. changed to + manipulate the cache properly. + (emis_fit): helper to fit an image to a size. + (em_icon_stream_new): added 'fit to' arguments rather than poking + structures. + + * em-format-html-display.c (efhd_attachment_image): fixed a memory + leak, various style issues. Removed all scaling code. + (efhd_attachment_popup): show menu's appropriately. add back the + hide/show menu always. + (efhd_image_popup): fix formatting. + + ** Applied patch from Srini for scaling images to fit by default. + 2005-07-04 Veerapuram Varadhan <vvaradhan@novell.com> * mail-component.c: (handleuri_got_folder): Added "forward" diff --git a/mail/em-format-html-display.c b/mail/em-format-html-display.c index f04b7ee416..25dc7622a6 100644 --- a/mail/em-format-html-display.c +++ b/mail/em-format-html-display.c @@ -30,6 +30,7 @@ #include <gtkhtml/gtkhtml-embedded.h> #include <gtkhtml/gtkhtml-search.h> +#include <gtk/gtkeventbox.h> #include <gtk/gtkvbox.h> #include <gtk/gtkhbox.h> #include <gtk/gtkbutton.h> @@ -106,6 +107,9 @@ static int efhd_html_button_press_event (GtkWidget *widget, GdkEventButton *even static void efhd_html_link_clicked (GtkHTML *html, const char *url, EMFormatHTMLDisplay *efhd); static void efhd_html_on_url (GtkHTML *html, const char *url, EMFormatHTMLDisplay *efhd); +static void efhd_attachment_frame(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri); +static gboolean efhd_attachment_image(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject); + struct _attach_puri { EMFormatPURI puri; @@ -119,8 +123,14 @@ struct _attach_puri { GtkHTML *frame; CamelStream *output; unsigned int shown:1; + + /* image stuff */ + int fit_width; + int fit_height; + GtkImage *image; }; + static void efhd_iframe_created(GtkHTML *html, GtkHTML *iframe, EMFormatHTMLDisplay *efh); /*static void efhd_url_requested(GtkHTML *html, const char *url, GtkHTMLStream *handle, EMFormatHTMLDisplay *efh); static gboolean efhd_object_requested(GtkHTML *html, GtkHTMLEmbedded *eb, EMFormatHTMLDisplay *efh);*/ @@ -903,9 +913,51 @@ efhd_format_secure(EMFormat *emf, CamelStream *stream, CamelMimePart *part, Came } } +static void +efhd_image(EMFormatHTML *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *handle) +{ + char *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; + 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[] = { + { "image/gif", (EMFormatFunc)efhd_image }, + { "image/jpeg", (EMFormatFunc)efhd_image }, + { "image/png", (EMFormatFunc)efhd_image }, + { "image/x-png", (EMFormatFunc)efhd_image }, + { "image/tiff", (EMFormatFunc)efhd_image }, + { "image/x-bmp", (EMFormatFunc)efhd_image }, + { "image/bmp", (EMFormatFunc)efhd_image }, + { "image/svg", (EMFormatFunc)efhd_image }, + { "image/x-cmu-raster", (EMFormatFunc)efhd_image }, + { "image/x-ico", (EMFormatFunc)efhd_image }, + { "image/x-portable-anymap", (EMFormatFunc)efhd_image }, + { "image/x-portable-bitmap", (EMFormatFunc)efhd_image }, + { "image/x-portable-graymap", (EMFormatFunc)efhd_image }, + { "image/x-portable-pixmap", (EMFormatFunc)efhd_image }, + { "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. */ + + { "image/jpg", (EMFormatFunc)efhd_image }, + { "image/pjpeg", (EMFormatFunc)efhd_image }, + { "x-evolution/message/prefix", (EMFormatFunc)efhd_message_prefix }, }; @@ -1059,10 +1111,30 @@ efhd_attachment_button_show(GtkWidget *w, void *data) efhd_attachment_show(NULL, NULL, data); } +static void +efhd_image_fit(EPopup *ep, EPopupItem *item, void *data) +{ + struct _attach_puri *info = data; + + info->fit_width = ((GtkWidget *)((EMFormatHTML *)info->puri.format)->html)->allocation.width - 12; + gtk_image_set_from_pixbuf(info->image, em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height)); +} + +static void +efhd_image_unfit(EPopup *ep, EPopupItem *item, void *data) +{ + struct _attach_puri *info = data; + + info->fit_width = 0; + gtk_image_set_from_pixbuf((GtkImage *)info->image, em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height)); +} + static EPopupItem efhd_menu_items[] = { { E_POPUP_BAR, "05.display", }, { E_POPUP_ITEM, "05.display.00", N_("_View Inline"), efhd_attachment_show }, { E_POPUP_ITEM, "05.display.00", N_("_Hide"), efhd_attachment_show }, + { E_POPUP_ITEM, "05.display.01", N_("_Fit to Width"), efhd_image_fit, NULL, NULL, EM_POPUP_PART_IMAGE }, + { E_POPUP_ITEM, "05.display.01", N_("Show _Original Size"), efhd_image_unfit, NULL, NULL, EM_POPUP_PART_IMAGE }, }; static void @@ -1088,7 +1160,6 @@ efhd_attachment_popup(GtkWidget *w, GdkEventButton *event, struct _attach_puri * GSList *menus = NULL; EMPopup *emp; EMPopupTargetPart *target; - EPopupItem *item; d(printf("attachment popup, button %d\n", event->button)); @@ -1113,8 +1184,14 @@ efhd_attachment_popup(GtkWidget *w, GdkEventButton *event, struct _attach_puri * if (info->handle) { /* show/hide menus, only if we have an inline handler */ menus = g_slist_prepend(menus, &efhd_menu_items[0]); - item = &efhd_menu_items[info->shown?2:1]; - menus = g_slist_prepend(menus, item); + menus = g_slist_prepend(menus, &efhd_menu_items[info->shown?2:1]); + 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)) + menus = g_slist_prepend(menus, &efhd_menu_items[4]); + } else + menus = g_slist_prepend(menus, &efhd_menu_items[3]); + } } e_popup_add_items((EPopup *)emp, menus, NULL, efhd_menu_items_free, info); @@ -1129,6 +1206,15 @@ efhd_attachment_popup(GtkWidget *w, GdkEventButton *event, struct _attach_puri * } static gboolean +efhd_image_popup(GtkWidget *w, GdkEventButton *event, struct _attach_puri *info) +{ + if (event && event->button != 3) + return FALSE; + + return efhd_attachment_popup(w, event, info); +} + +static gboolean efhd_attachment_popup_menu(GtkWidget *w, struct _attach_puri *info) { return efhd_attachment_popup(w, NULL, info); @@ -1213,6 +1299,73 @@ efhd_write_icon_job(struct _EMFormatHTMLJob *job, int cancelled) camel_stream_close(job->stream); } +static void +efhd_image_resized(GtkWidget *w, GtkAllocation *event, struct _attach_puri *info) +{ + GdkPixbuf *pb; + int 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); + gtk_image_set_from_pixbuf(info->image, pb); + g_object_unref(pb); +} + +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 }, + { "text/uri-list", 0, 1 }, + }; + char *simple_type; + + info = (struct _attach_puri *)em_format_find_puri((EMFormat *)efh, pobject->classid); + + info->image = (GtkImage *)gtk_image_new(); + 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(); + 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, sizeof(drag_types)/sizeof(drag_types[0]), 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, "button_press_event", G_CALLBACK(efhd_image_popup), info); + g_signal_connect(box, "popup_menu", G_CALLBACK(efhd_attachment_popup_menu), info); + + return TRUE; +} + /* attachment button callback */ static gboolean efhd_attachment_button(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject) @@ -1266,13 +1419,13 @@ efhd_attachment_button(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObje camel_strdown(simple_type); /* FIXME: offline parts, just get icon */ - if (camel_content_type_is (((CamelDataWrapper *)pobject->part)->mime_type, "image", "*")) { + if (camel_content_type_is(((CamelDataWrapper *)pobject->part)->mime_type, "image", "*")) { EMFormatHTMLJob *job; GdkPixbuf *mini; char *key; key = pobject->classid; - mini = em_icon_stream_get_image(key); + mini = em_icon_stream_get_image(key, 24, 24); if (mini) { d(printf("got image from cache '%s'\n", key)); gtk_image_set_from_pixbuf((GtkImage *)w, mini); @@ -1280,7 +1433,7 @@ efhd_attachment_button(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObje } else { d(printf("need to create icon image '%s'\n", key)); job = em_format_html_job_new(efh, efhd_write_icon_job, pobject); - job->stream = (CamelStream *)em_icon_stream_new((GtkImage *)w, key); + job->stream = (CamelStream *)em_icon_stream_new((GtkImage *)w, key, 24, 24, FALSE); em_format_html_job_queue(efh, job); } } else { @@ -1308,7 +1461,6 @@ efhd_attachment_button(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObje a11y = gtk_widget_get_accessible (button); atk_object_set_name (a11y, _("Attachment Button")); - g_signal_connect(button, "button_press_event", G_CALLBACK(efhd_attachment_popup), info); g_signal_connect(button, "popup_menu", G_CALLBACK(efhd_attachment_popup_menu), info); g_signal_connect(button, "clicked", G_CALLBACK(efhd_attachment_popup_menu), info); diff --git a/mail/em-icon-stream.c b/mail/em-icon-stream.c index 565eacc34e..b9079e8c02 100644 --- a/mail/em-icon-stream.c +++ b/mail/em-icon-stream.c @@ -26,6 +26,8 @@ #endif #include <stdio.h> +#include <string.h> + #include <gdk-pixbuf/gdk-pixbuf.h> #include <gdk-pixbuf/gdk-pixbuf-loader.h> #ifdef HAVE_LIBGNOMEUI_GNOME_THUMBNAIL_H @@ -38,6 +40,9 @@ #define d(x) +/* fixed-point scale factor for scaled images in cache */ +#define EMIS_SCALE (1024) + struct _emis_cache_node { EMCacheNode node; @@ -98,8 +103,7 @@ em_icon_stream_init (CamelObject *object) { EMIconStream *emis = (EMIconStream *)object; - emis->width = 24; - emis->height = 24; + emis = emis; } static void @@ -153,13 +157,47 @@ emis_sync_flush(CamelStream *stream) return 0; } +static GdkPixbuf * +emis_fit(GdkPixbuf *pixbuf, int maxwidth, int maxheight, int *scale) +{ + GdkPixbuf *mini = NULL; + int width, height; + + width = gdk_pixbuf_get_width(pixbuf); + height = gdk_pixbuf_get_height(pixbuf); + + if ((maxwidth && width > maxwidth) + || (maxheight && height > maxheight)) { + if (width >= height) { + 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; + } + +#ifdef HAVE_LIBGNOMEUI_GNOME_THUMBNAIL_H + mini = gnome_thumbnail_scale_down_pixbuf(pixbuf, width, height); +#else + mini = gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR); +#endif + } + + return mini; +} + static int emis_sync_close(CamelStream *stream) { EMIconStream *emis = (EMIconStream *)stream; - int width, height, ratio; GdkPixbuf *pixbuf, *mini; struct _emis_cache_node *node; + char *scalekey; + int scale; if (emis->loader == NULL) return -1; @@ -173,39 +211,22 @@ emis_sync_close(CamelStream *stream) return -1; } - width = gdk_pixbuf_get_width(pixbuf); - height = gdk_pixbuf_get_height(pixbuf); + mini = emis_fit(pixbuf, emis->width, emis->height, &scale); + gtk_image_set_from_pixbuf(emis->image, mini?mini:pixbuf); - if (width != emis->width || height != emis->height) { - if (width >= height) { - if (width > emis->width) { - ratio = width / emis->width; - width = emis->width; - height /= ratio; - } - } else { - if (height > emis->height) { - ratio = height / emis->height; - height = emis->height; - width /= ratio; - } - } - -#ifdef HAVE_LIBGNOMEUI_GNOME_THUMBNAIL_H - mini = gnome_thumbnail_scale_down_pixbuf (pixbuf, width, height); -#else - mini = gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR); -#endif - gtk_image_set_from_pixbuf(emis->image, mini); - pixbuf = mini; - } else { - g_object_ref(pixbuf); - gtk_image_set_from_pixbuf(emis->image, 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); } - node = (struct _emis_cache_node *)em_cache_node_new(emis_cache, emis->key); - node->pixbuf = 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; @@ -223,12 +244,15 @@ emis_image_destroy(struct _GtkImage *image, EMIconStream *emis) } CamelStream * -em_icon_stream_new(GtkImage *image, const char *key) +em_icon_stream_new(GtkImage *image, const char *key, unsigned int maxwidth, unsigned int maxheight, int 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); @@ -237,24 +261,79 @@ em_icon_stream_new(GtkImage *image, const char *key) } GdkPixbuf * -em_icon_stream_get_image(const char *key) +em_icon_stream_get_image(const char *key, unsigned int maxwidth, unsigned int maxheight) { struct _emis_cache_node *node; GdkPixbuf *pb = NULL; /* forces the cache to be setup if not */ - em_icon_stream_get_type(); + em_icon_stream_get_type(); node = (struct _emis_cache_node *)em_cache_lookup(emis_cache, key); if (node) { + int 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)) { + unsigned int scale; + char *realkey; + + if (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; } +int +em_icon_stream_is_resized(const char *key, unsigned int maxwidth, unsigned int maxheight) +{ + int res = FALSE; + struct _emis_cache_node *node; + + /* 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) { diff --git a/mail/em-icon-stream.h b/mail/em-icon-stream.h index 124259b3df..4b048f3428 100644 --- a/mail/em-icon-stream.h +++ b/mail/em-icon-stream.h @@ -46,6 +46,8 @@ typedef struct _EMIconStream { struct _GdkPixbufLoader *loader; struct _GtkImage *image; char *key; + + int keep:1; } EMIconStream; typedef struct { @@ -53,9 +55,11 @@ typedef struct { } EMIconStreamClass; CamelType em_icon_stream_get_type (void); +CamelStream *em_icon_stream_new(GtkImage *image, const char *key, unsigned int maxwidth, unsigned int maxheight, int keep); + +struct _GdkPixbuf *em_icon_stream_get_image(const char *key, unsigned int maxwidth, unsigned int maxheight); +int em_icon_stream_is_resized(const char *key, unsigned int maxwidth, unsigned int maxheight); -CamelStream *em_icon_stream_new(GtkImage *image, const char *key); -struct _GdkPixbuf *em_icon_stream_get_image(const char *key); void em_icon_stream_clear_cache(void); #ifdef __cplusplus |