aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mail/ChangeLog26
-rw-r--r--mail/em-format-html-display.c166
-rw-r--r--mail/em-icon-stream.c151
-rw-r--r--mail/em-icon-stream.h8
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