use the cache, since we set it up. (efhd_image): added a (private!) format

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.

svn path=/trunk/; revision=29640
This commit is contained in:
Not Zed
2005-07-06 03:56:48 +00:00
committed by Michael Zucci
parent 76e2be74ff
commit e7faa99582
4 changed files with 308 additions and 47 deletions

View File

@ -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"

View File

@ -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);
@ -1128,6 +1205,15 @@ efhd_attachment_popup(GtkWidget *w, GdkEventButton *event, struct _attach_puri *
return TRUE;
}
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)
{
@ -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);

View File

@ -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,22 +261,77 @@ 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();
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) {
pb = node->pixbuf;
g_object_ref(pb);
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 pb;
return res;
}
void

View File

@ -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