1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* This file is part of the GtkHTML library.
3  *
4  * Copyright (C) 1997 Martin Jones (mjones@kde.org)
5  * Copyright (C) 1997 Torben Weis (weis@kde.org)
6  * Copyright (C) 1999 Red Hat Software
7  * Copyright (C) 1999, 2000 Helix Code, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  *
24 */
25 
26 #include <config.h>
27 #include <string.h>
28 
29 #include "gtkhtml.h"
30 #include "gtkhtml-properties.h"
31 #include "gtkhtml-stream.h"
32 
33 #include "htmlclueflow.h"
34 #include "htmlcolor.h"
35 #include "htmlcolorset.h"
36 #include "htmldrawqueue.h"
37 #include "htmlengine.h"
38 #include "htmlengine-save.h"
39 #include "htmlenumutils.h"
40 #include "htmlimage.h"
41 #include "htmlobject.h"
42 #include "htmlmap.h"
43 #include "htmlprinter.h"
44 #include "htmlgdkpainter.h"
45 #include "htmlplainpainter.h"
46 #include "htmlsettings.h"
47 
48 /* HTMLImageFactory stuff.  */
49 
50 struct _HTMLImageFactory {
51 	HTMLEngine *engine;
52 	GHashTable *loaded_images;
53 	GdkPixbuf  *missing;
54 	gboolean    animate;
55 };
56 
57 
58 #define DEFAULT_SIZE 48
59 #define STRDUP_HELPER(i,j) if (i != j) {char *tmp = g_strdup (j); g_free(i); i = tmp;}
60 
61 #define DA(x)
62 
63 static HTMLImageClass html_image_class;
64 static HTMLObjectClass *parent_class = NULL;
65 
66 static HTMLImagePointer   *html_image_pointer_new               (const gchar *filename, HTMLImageFactory *factory);
67 static void                html_image_pointer_ref               (HTMLImagePointer *ip);
68 static void                html_image_pointer_unref             (HTMLImagePointer *ip);
69 static gboolean            html_image_pointer_timeout           (HTMLImagePointer *ip);
70 static gint                html_image_pointer_update            (HTMLImagePointer *ip);
71 static void                html_image_pointer_start_animation   (HTMLImagePointer *ip);
72 
73 static GdkPixbuf *         html_image_factory_get_missing       (HTMLImageFactory *factory);
74 
75 guint
html_image_get_actual_width(HTMLImage * image,HTMLPainter * painter)76 html_image_get_actual_width (HTMLImage *image,
77                              HTMLPainter *painter)
78 {
79 	GdkPixbufAnimation *anim = image->image_ptr->animation;
80 	gint pixel_size = painter ? html_painter_get_pixel_size (painter) : 1;
81 	gint width;
82 
83 	if (image->percent_width) {
84 		/* The cast to `gdouble' is to avoid overflow (eg. when
85 		 * printing).  */
86 		width = ((gdouble) HTML_OBJECT (image)->max_width
87 			 * image->specified_width) / 100;
88 	} else if (image->specified_width > 0) {
89 		width = image->specified_width * pixel_size;
90 	} else if (image->image_ptr == NULL || anim == NULL) {
91 		width = DEFAULT_SIZE * pixel_size;
92 	} else {
93 		width = gdk_pixbuf_animation_get_width (anim) * pixel_size;
94 
95 		if (image->specified_height > 0 || image->percent_height) {
96 			gdouble scale;
97 
98 			scale =  ((double) html_image_get_actual_height (image, painter))
99 				/ (gdk_pixbuf_animation_get_height (anim) * pixel_size);
100 
101 			width *= scale;
102 		}
103 
104 	}
105 
106 	return width;
107 }
108 
109 guint
html_image_get_actual_height(HTMLImage * image,HTMLPainter * painter)110 html_image_get_actual_height (HTMLImage *image,
111                               HTMLPainter *painter)
112 {
113 	GdkPixbufAnimation *anim = image->image_ptr->animation;
114 	gint pixel_size = painter ? html_painter_get_pixel_size (painter) : 1;
115 	gint height;
116 
117 	if (image->percent_height) {
118 		/* The cast to `gdouble' is to avoid overflow (eg. when
119 		 * printing).  */
120 		height = ((gdouble) html_engine_get_view_height (image->image_ptr->factory->engine)
121 			  * image->specified_height) / 100;
122 	} else if (image->specified_height > 0) {
123 		height = image->specified_height * pixel_size;
124 	} else if (image->image_ptr == NULL || anim == NULL) {
125 		height = DEFAULT_SIZE * pixel_size;
126 	} else {
127 		height = gdk_pixbuf_animation_get_height (anim) * pixel_size;
128 
129 		if (image->specified_width > 0 || image->percent_width) {
130 			gdouble scale;
131 
132 			scale = ((double) html_image_get_actual_width (image, painter))
133 				/ (gdk_pixbuf_animation_get_width (anim) * pixel_size);
134 
135 			height *= scale;
136 		}
137 	}
138 
139 	return height;
140 }
141 
142 
143 /* HTMLObject methods.  */
144 
145 /* FIXME: We should close the stream here, too.  But in practice we cannot
146  * because the stream pointer might be invalid at this point, and there is no
147  * way to set it to NULL when the stream is closed.  This clearly sucks and
148  * must be fixed.  */
149 static void
destroy(HTMLObject * o)150 destroy (HTMLObject *o)
151 {
152 	HTMLImage *image = HTML_IMAGE (o);
153 
154 	if (image->image_ptr->factory)
155 		html_image_factory_unregister (image->image_ptr->factory,
156 					       image->image_ptr, HTML_IMAGE (image));
157 
158 	g_free (image->url);
159 	g_free (image->target);
160 	g_free (image->alt);
161 	g_free (image->usemap);
162 	g_free (image->final_url);
163 
164 	if (image->color)
165 		html_color_unref (image->color);
166 
167 	HTML_OBJECT_CLASS (parent_class)->destroy (o);
168 }
169 
170 static void
copy(HTMLObject * self,HTMLObject * dest)171 copy (HTMLObject *self,
172       HTMLObject *dest)
173 {
174 	HTMLImage *dimg = HTML_IMAGE (dest);
175 	HTMLImage *simg = HTML_IMAGE (self);
176 
177 	/* FIXME not sure this is all correct.  */
178 
179 	(* HTML_OBJECT_CLASS (parent_class)->copy) (self, dest);
180 
181 	dimg->image_ptr = simg->image_ptr;
182 	dimg->color = simg->color;
183 	if (simg->color)
184 		html_color_ref (dimg->color);
185 
186 	dimg->have_color = simg->have_color;
187 
188 	dimg->border = simg->border;
189 
190 	dimg->specified_width = simg->specified_width;
191 	dimg->specified_height = simg->specified_height;
192 	dimg->percent_width = simg->percent_width;
193 	dimg->percent_height = simg->percent_height;
194 	dimg->ismap = simg->ismap;
195 
196 	dimg->hspace = simg->hspace;
197 	dimg->vspace = simg->vspace;
198 
199 	dimg->valign = simg->valign;
200 
201 	dimg->url = g_strdup (simg->url);
202 	dimg->target = g_strdup (simg->target);
203 	dimg->alt = g_strdup (simg->alt);
204 	dimg->usemap = g_strdup (simg->usemap);
205 	dimg->final_url = NULL;
206 	dimg->animation_active = FALSE;
207 
208 	/* add dest to image_ptr interests */
209 	dimg->image_ptr->interests = g_slist_prepend (dimg->image_ptr->interests, dimg);
210 	html_image_pointer_ref (dimg->image_ptr);
211 }
212 
213 static void
image_update_url(HTMLImage * image,gint x,gint y)214 image_update_url (HTMLImage *image,
215                   gint x,
216                   gint y)
217 {
218 	HTMLMap *map;
219 	HTMLObject *o = HTML_OBJECT (image);
220 	gchar *url = NULL;
221 
222 	/*
223 	 * FIXME this is a huge hack waiting until we implement events for now we write
224 	 * over the image->url for every point since we always call point before get_url
225 	 * it is sick, I know.
226 	 */
227 	if (image->usemap != NULL) {
228 		map = html_engine_get_map (image->image_ptr->factory->engine,
229 					   image->usemap + 1);
230 
231 		if (map) {
232 			url = html_map_calc_point (map, x - o->x , y - (o->y - o->ascent));
233 
234 			if (url)
235 				url = g_strdup (url);
236 		}
237 	} else if (image->ismap) {
238 		if (image->url)
239 			url = g_strdup_printf ("%s?%d,%d", image->url, x - o->x, y - (o->y - o->ascent));
240 	} else {
241 		return;
242 	}
243 
244 	g_free (image->final_url);
245 	image->final_url = url;
246 }
247 
248 static HTMLObject *
check_point(HTMLObject * self,HTMLPainter * painter,gint x,gint y,guint * offset_return,gboolean for_cursor)249 check_point (HTMLObject *self,
250              HTMLPainter *painter,
251              gint x,
252              gint y,
253              guint *offset_return,
254              gboolean for_cursor)
255 {
256 	if ((x >= self->x)
257 	    && (x < (self->x + self->width))
258 	    && (y >= (self->y - self->ascent))
259 	    && (y < (self->y + self->descent))) {
260 		if (offset_return != NULL)
261 			*offset_return = x - self->x < self->width / 2 ? 0 : 1;
262 
263 		image_update_url (HTML_IMAGE (self), x, y);
264 		return self;
265 	}
266 
267 	return NULL;
268 }
269 
270 static gint
calc_min_width(HTMLObject * o,HTMLPainter * painter)271 calc_min_width (HTMLObject *o,
272                 HTMLPainter *painter)
273 {
274 	HTMLImage *image = HTML_IMAGE (o);
275 	guint pixel_size;
276 	guint min_width;
277 
278 	pixel_size = html_painter_get_pixel_size (painter);
279 
280 	if (image->percent_width || image->percent_height)
281 		min_width = pixel_size;
282 	else
283 		min_width = html_image_get_actual_width (HTML_IMAGE (o), painter);
284 
285 	min_width += (image->border * 2 + 2 * image->hspace) * pixel_size;
286 
287 	return min_width;
288 }
289 
290 static gint
calc_preferred_width(HTMLObject * o,HTMLPainter * painter)291 calc_preferred_width (HTMLObject *o,
292                       HTMLPainter *painter)
293 {
294 	HTMLImage *image = HTML_IMAGE (o);
295 	guint width;
296 
297 	width = html_image_get_actual_width (HTML_IMAGE (o), painter)
298 		+ (image->border * 2 + 2 * image->hspace) * html_painter_get_pixel_size (painter);
299 
300 	return width;
301 }
302 
303 static gboolean
html_image_real_calc_size(HTMLObject * o,HTMLPainter * painter,GList ** changed_objs)304 html_image_real_calc_size (HTMLObject *o,
305                            HTMLPainter *painter,
306                            GList **changed_objs)
307 {
308 	HTMLImage *image;
309 	guint pixel_size;
310 	guint width, height;
311 	gint old_width, old_ascent, old_descent;
312 
313 	old_width = o->width;
314 	old_ascent = o->ascent;
315 	old_descent = o->descent;
316 
317 	image = HTML_IMAGE (o);
318 
319 	pixel_size = html_painter_get_pixel_size (painter);
320 
321 	if (o->parent && HTML_IS_PLAIN_PAINTER (painter) && image->alt && *image->alt) {
322 		HTMLClueFlow *cf = html_object_get_flow (o);
323 
324 		if (cf)
325 			html_painter_set_font_style (painter, html_clueflow_get_default_font_style (cf));
326 
327 		html_painter_set_font_face (painter, NULL);
328 		/* FIXME: cache items and glyphs? */
329 		html_painter_calc_text_size (painter, image->alt, g_utf8_strlen (image->alt, -1),
330 					     &o->width, &o->ascent, &o->descent);
331 	} else {
332 		width = html_image_get_actual_width (image, painter);
333 		height = html_image_get_actual_height (image, painter);
334 
335 		o->width  = width + (image->border + image->hspace) * 2 * pixel_size;
336 		o->ascent = height + (image->border + image->vspace) * 2 * pixel_size;
337 		o->descent = 0;
338 	}
339 
340 	if (o->descent != old_descent
341 	    || o->ascent != old_ascent
342 	    || o->width != old_width)
343 		return TRUE;
344 
345 	return FALSE;
346 }
347 
348 static void
draw_plain(HTMLObject * o,HTMLPainter * p,gint x,gint y,gint width,gint height,gint tx,gint ty)349 draw_plain (HTMLObject *o,
350             HTMLPainter *p,
351             gint x,
352             gint y,
353             gint width,
354             gint height,
355             gint tx,
356             gint ty)
357 {
358 	HTMLImage *img = HTML_IMAGE (o);
359 	HTMLEngine *e;
360 
361 	if (p->widget && GTK_IS_HTML (p->widget))
362 		e = html_object_engine (o, GTK_HTML (p->widget)->engine);
363 	else
364 		return;
365 
366 	if (img->alt && *img->alt) {
367 		HTMLClueFlow *cf = html_object_get_flow (o);
368 
369 		/* FIXME: cache items and glyphs? */
370 		if (o->selected) {
371 			html_painter_set_pen (p, &html_colorset_get_color_allocated
372 					      (e->settings->color_set, p,
373 					       p->focus ? HTMLHighlightColor : HTMLHighlightNFColor)->color);
374 			html_painter_fill_rect (p, o->x + tx, o->y + ty - o->ascent, o->width, o->ascent + o->descent);
375 			html_painter_set_pen (p, &html_colorset_get_color_allocated
376 					      (e->settings->color_set, p,
377 					       p->focus ? HTMLHighlightTextColor : HTMLHighlightTextNFColor)->color);
378 		} else {
379 			html_painter_set_pen (p, &html_colorset_get_color_allocated (e->settings->color_set, p,
380 										     HTMLTextColor)->color);
381 		}
382 
383 		if (cf)
384 			html_painter_set_font_style (p, html_clueflow_get_default_font_style (cf));
385 
386 		html_painter_set_font_face (p, NULL);
387 		html_painter_draw_text (p, o->x + tx, o->y + ty, img->alt, g_utf8_strlen (img->alt, -1));
388 	}
389 }
390 
391 static void
draw_focus(HTMLImage * image,HTMLPainter * painter,GdkRectangle * box)392 draw_focus (HTMLImage *image,
393              HTMLPainter *painter,
394              GdkRectangle *box)
395 {
396 	HTMLGdkPainter *p;
397 	const double dashes[] = { 1, 1 };
398 	gint ndash = G_N_ELEMENTS (dashes);
399 	HTMLEngine *e;
400 
401 	if (painter->widget && GTK_IS_HTML (painter->widget))
402 		e = html_object_engine (HTML_OBJECT (image), GTK_HTML (painter->widget)->engine);
403 	else
404 		return;
405 
406 	if (HTML_IS_PRINTER (painter))
407 		return;
408 
409 	p = HTML_GDK_PAINTER (painter);
410 	/* printf ("draw_image_focus\n"); */
411 
412 	cairo_save (p->cr);
413 	gdk_cairo_set_source_color (p->cr,
414 				    &html_colorset_get_color_allocated (e->settings->color_set,
415 									painter, HTMLTextColor)->color);
416 	cairo_set_line_cap (p->cr, CAIRO_LINE_CAP_ROUND);
417 	cairo_set_dash (p->cr, dashes, ndash, 2);
418 	cairo_rectangle (p->cr, box->x - p->x1, box->y - p->y1, box->width - 1, box->height - 1);
419 	cairo_stroke (p->cr);
420 	cairo_restore (p->cr);
421 }
422 
423 static void
draw(HTMLObject * o,HTMLPainter * painter,gint x,gint y,gint width,gint height,gint tx,gint ty)424 draw (HTMLObject *o,
425       HTMLPainter *painter,
426       gint x,
427       gint y,
428       gint width,
429       gint height,
430       gint tx,
431       gint ty)
432 {
433 	HTMLImage *image;
434 	HTMLImagePointer *ip;
435 	GdkPixbuf *pixbuf;
436 	gint base_x, base_y;
437 	gint scale_width, scale_height;
438 	GdkColor *highlight_color;
439 	guint pixel_size;
440 	GdkRectangle paint;
441 	HTMLEngine *e;
442 
443 	if (painter->widget && GTK_IS_HTML (painter->widget))
444 		e = html_object_engine (o, GTK_HTML (painter->widget)->engine);
445 	else
446 		return;
447 
448 	/* printf ("Image::draw\n"); */
449 
450 	if (!html_object_intersect (o, &paint, x, y, width, height))
451 		return;
452 
453 	if (HTML_IS_PLAIN_PAINTER (painter)) {
454 		draw_plain (o, painter, x, y, width, height, tx, ty);
455 		return;
456 	}
457 
458 	image = HTML_IMAGE (o);
459 	ip = image->image_ptr;
460 
461 	image->animation_active = TRUE;
462 
463 	if (ip->animation) {
464 		if (HTML_IS_GDK_PAINTER (painter) && !gdk_pixbuf_animation_is_static_image (ip->animation)) {
465 			pixbuf = gdk_pixbuf_animation_iter_get_pixbuf (ip->iter);
466 		} else {
467 			pixbuf = gdk_pixbuf_animation_get_static_image (ip->animation);
468 		}
469 	} else {
470 		pixbuf = NULL;
471 	}
472 
473 	pixel_size = html_painter_get_pixel_size (painter);
474 
475 	if (o->selected) {
476 		highlight_color = &html_colorset_get_color_allocated
477 			(e->settings->color_set, painter,
478 			 painter->focus ? HTMLHighlightColor : HTMLHighlightNFColor)->color;
479 	} else
480 		highlight_color = NULL;
481 
482 	base_x = o->x + tx + (image->border + image->hspace) * pixel_size;
483 	base_y = o->y + ty + (image->border + image->vspace) * pixel_size - o->ascent;
484 
485 	if (pixbuf == NULL) {
486 		gint vspace, hspace;
487 
488 		hspace = image->hspace * pixel_size;
489 		vspace = image->vspace * pixel_size;
490 
491 		if (image->image_ptr->loader && !image->image_ptr->stall)
492 			return;
493 
494 		if (o->selected) {
495 			html_painter_set_pen (painter, highlight_color);
496 			html_painter_fill_rect (painter,
497 						o->x + tx + hspace,
498 						o->y + ty - o->ascent + vspace,
499 						o->width - 2 * hspace,
500 						o->ascent + o->descent - 2 * vspace);
501 		}
502 		html_painter_draw_border (painter,
503 					  &((html_colorset_get_color (e->settings->color_set, HTMLBgColor))->color),
504 					  o->x + tx + hspace,
505 					  o->y + ty - o->ascent + vspace,
506 					  o->width - 2 * hspace,
507 					  o->ascent + o->descent - 2 * vspace,
508 					  HTML_BORDER_INSET, 1);
509 
510 		if (ip->factory)
511 			pixbuf = html_image_factory_get_missing (ip->factory);
512 
513 		if (pixbuf &&
514 		    (o->width > gdk_pixbuf_get_width (pixbuf)) &&
515 		    (o->ascent  + o->descent > gdk_pixbuf_get_height (pixbuf)))
516 			html_painter_draw_pixmap (painter, pixbuf,
517 						  base_x, base_y,
518 						  gdk_pixbuf_get_width (pixbuf) * pixel_size,
519 						  gdk_pixbuf_get_height (pixbuf) * pixel_size,
520 						  highlight_color);
521 
522 		if (o->draw_focused) {
523 			GdkRectangle rect;
524 
525 			scale_width = html_image_get_actual_width (image, painter);
526 			scale_height = html_image_get_actual_height (image, painter);
527 
528 			rect.x = base_x - image->border * pixel_size;
529 			rect.y = base_y - image->border * pixel_size;
530 			rect.width = scale_width + (2 * image->border) * pixel_size;
531 			rect.height = scale_height + (2 * image->border) * pixel_size;
532 
533 			draw_focus (image, painter, &rect);
534 		}
535 
536 		return;
537 	}
538 
539 	scale_width = html_image_get_actual_width (image, painter);
540 	scale_height = html_image_get_actual_height (image, painter);
541 
542 	if (image->border) {
543 		if (image->have_color) {
544 			html_color_alloc (image->color, painter);
545 			html_painter_set_pen (painter, &image->color->color);
546 		}
547 
548 		html_painter_draw_border (painter,
549 					  &((html_colorset_get_color (e->settings->color_set, HTMLBgColor))->color),
550 					  base_x - image->border * pixel_size,
551 					  base_y - image->border * pixel_size,
552 					  scale_width + (2 * image->border) * pixel_size,
553 					  scale_height + (2 * image->border) * pixel_size,
554 					  HTML_BORDER_SOLID, image->border);
555 
556 	}
557 
558 	html_painter_draw_pixmap (painter, pixbuf,
559 				  base_x, base_y,
560 				  scale_width, scale_height,
561 				  highlight_color);
562 
563 	if (o->draw_focused) {
564 		GdkRectangle rect;
565 		rect.x = base_x - image->border * pixel_size;
566 		rect.y = base_y - image->border * pixel_size;
567 		rect.width = scale_width + (2 * image->border) * pixel_size;
568 		rect.height = scale_height + (2 * image->border) * pixel_size;
569 
570 		draw_focus (image, painter, &rect);
571 	}
572 }
573 
574 gchar *
html_image_resolve_image_url(GtkHTML * html,gchar * image_url)575 html_image_resolve_image_url (GtkHTML *html,
576                               gchar *image_url)
577 {
578 	gchar *url = NULL;
579 
580 	/* printf ("html_image_resolve_image_url %p\n", html->editor_api); */
581 	if (html->editor_api) {
582 		GValue  *iarg = g_new0 (GValue, 1);
583 		GValue  *oarg;
584 
585 		g_value_init (iarg, G_TYPE_STRING);
586 		g_value_set_string (iarg, image_url);
587 
588 		oarg = (* html->editor_api->event) (html, GTK_HTML_EDITOR_EVENT_IMAGE_URL, iarg, html->editor_data);
589 
590 		if (oarg) {
591 			if (G_VALUE_TYPE (oarg) == G_TYPE_STRING)
592 				url = (gchar *) g_strdup (g_value_get_string (oarg));
593 
594 			g_value_unset (oarg);
595 			g_free (oarg);
596 		}
597 		g_value_unset (iarg);
598 		g_free (iarg);
599 	}
600 	if (!url)
601 		url = g_strdup (image_url);
602 	/* printf ("image URL resolved to: %s (from: %s)\n", url, image_url); */
603 
604 	return url;
605 }
606 
607 static gboolean
save(HTMLObject * self,HTMLEngineSaveState * state)608 save (HTMLObject *self,
609       HTMLEngineSaveState *state)
610 {
611 	HTMLImage *image;
612 	gchar *url;
613 	gboolean result, link = FALSE;
614 
615 	g_return_val_if_fail (self != NULL, FALSE);
616 	g_return_val_if_fail (state != NULL, FALSE);
617 
618 	image  = HTML_IMAGE (self);
619 
620 	if (image->url && *image->url) {
621 		url  = g_strconcat (image->url, image->target ? "#" : "", image->target, NULL);
622 		link = TRUE;
623 		result = html_engine_save_delims_and_vals (state, "<A HREF=\"", url, "\">", NULL);
624 		g_free (url);
625 		if (!result)
626 			return FALSE;
627 	}
628 
629 	url    = html_image_resolve_image_url (state->engine->widget, image->image_ptr->url);
630 	result = html_engine_save_delims_and_vals (state, "<IMG SRC=\"", url, "\"", NULL);
631 	g_free (url);
632 	if (!result)
633 		return FALSE;
634 
635 	if (image->percent_width) {
636 		if (!html_engine_save_output_string (state, " WIDTH=\"%d%%\"", image->specified_width))
637 			return FALSE;
638 	} else if (image->specified_width > 0) {
639 		if (!html_engine_save_output_string (state, " WIDTH=\"%d\"", image->specified_width))
640 			return FALSE;
641 	}
642 
643 	if (image->percent_height) {
644 		if (!html_engine_save_output_string (state, " HEIGHT=\"%d%%\"", image->specified_height))
645 			return FALSE;
646 	} else if (image->specified_height > 0) {
647 		if (!html_engine_save_output_string (state, " HEIGHT=\"%d\"", image->specified_height))
648 			return FALSE;
649 	}
650 
651 	if (image->vspace) {
652 		if (!html_engine_save_output_string (state, " VSPACE=\"%d\"", image->vspace))
653 			return FALSE;
654 	}
655 
656 	if (image->hspace) {
657 		if (!html_engine_save_output_string (state, " HSPACE=\"%d\"", image->hspace))
658 			return FALSE;
659 	}
660 
661 	if (image->vspace) {
662 		if (!html_engine_save_output_string (state, " VSPACE=\"%d\"", image->vspace))
663 			return FALSE;
664 	}
665 
666 	if (image->valign != HTML_VALIGN_NONE) {
667 		if (!html_engine_save_output_string (state, " ALIGN=\"%s\"", html_valign_name (image->valign)))
668 			return FALSE;
669 	}
670 
671 	if (image->alt) {
672 		if (!html_engine_save_delims_and_vals (state, " ALT=\"", image->alt, "\"", NULL))
673 			return FALSE;
674 	}
675 
676 	/* FIXME this is the default set in htmlengine.c but there is no real way to tell
677 	 * if the usr specified it directly
678 	 */
679 	if (image->border != 2) {
680 		if (!html_engine_save_output_string (state, " BORDER=\"%d\"", image->border))
681 			return FALSE;
682 	}
683 
684 	if (!html_engine_save_output_string (state, ">"))
685 		return FALSE;
686 	if (link && !html_engine_save_output_string (state, "</A>"))
687 		return FALSE;
688 
689 	return TRUE;
690 }
691 
692 static gboolean
save_plain(HTMLObject * self,HTMLEngineSaveState * state,gint requested_width)693 save_plain (HTMLObject *self,
694             HTMLEngineSaveState *state,
695             gint requested_width)
696 {
697 	HTMLImage *image;
698 	gboolean rv = TRUE;
699 
700 	image = HTML_IMAGE (self);
701 
702 	if (image->alt)
703 		rv = html_engine_save_output_string (state, "%s", image->alt);
704 
705 	return rv;
706 }
707 
708 static const gchar *
get_url(HTMLObject * o,gint offset)709 get_url (HTMLObject *o,
710          gint offset)
711 {
712 	HTMLImage *image;
713 
714 	image = HTML_IMAGE (o);
715 	return image->final_url ? image->final_url : image->url;
716 }
717 
718 static const gchar *
get_target(HTMLObject * o,gint offset)719 get_target (HTMLObject *o,
720             gint offset)
721 {
722 	HTMLImage *image;
723 
724 	image = HTML_IMAGE (o);
725 	return image->target;
726 }
727 
728 static const gchar *
get_src(HTMLObject * o)729 get_src (HTMLObject *o)
730 {
731 	HTMLImage *image;
732 
733 	image = HTML_IMAGE (o);
734 	return image->image_ptr->url;
735 }
736 
737 static HTMLObject *
set_link(HTMLObject * self,HTMLColor * color,const gchar * url,const gchar * target)738 set_link (HTMLObject *self,
739           HTMLColor *color,
740           const gchar *url,
741           const gchar *target)
742 {
743 	HTMLImage *image = HTML_IMAGE (self);
744 
745 	STRDUP_HELPER (image->url, url);
746 	STRDUP_HELPER (image->target, target);
747 	if (image->have_color)
748 		html_color_unref (image->color);
749 	image->color = color;
750 	if (color) {
751 		html_color_ref (color);
752 		image->have_color = TRUE;
753 	} else {
754 		image->have_color = FALSE;
755 	}
756 
757 	return NULL;
758 }
759 
760 static gboolean
accepts_cursor(HTMLObject * o)761 accepts_cursor (HTMLObject *o)
762 {
763 	return TRUE;
764 }
765 
766 static HTMLVAlignType
get_valign(HTMLObject * self)767 get_valign (HTMLObject *self)
768 {
769 	HTMLImage *image;
770 
771 	image = HTML_IMAGE (self);
772 
773 	return image->valign;
774 }
775 
776 static gboolean
select_range(HTMLObject * self,HTMLEngine * engine,guint offset,gint length,gboolean queue_draw)777 select_range (HTMLObject *self,
778               HTMLEngine *engine,
779               guint offset,
780               gint length,
781               gboolean queue_draw)
782 {
783 	/* printf ("IMAGE: select range\n"); */
784 	if ((*parent_class->select_range) (self, engine, offset, length, queue_draw)) {
785 		if (queue_draw) {
786 			html_engine_queue_draw (engine, self);
787 			/* printf ("IMAGE: draw queued\n"); */
788 		}
789 		return TRUE;
790 	} else
791 		return FALSE;
792 }
793 
794 
795 void
html_image_type_init(void)796 html_image_type_init (void)
797 {
798 	html_image_class_init (&html_image_class, HTML_TYPE_IMAGE, sizeof (HTMLImage));
799 }
800 
801 void
html_image_class_init(HTMLImageClass * image_class,HTMLType type,guint size)802 html_image_class_init (HTMLImageClass *image_class,
803                        HTMLType type,
804                        guint size)
805 {
806 	HTMLObjectClass *object_class;
807 
808 	object_class = HTML_OBJECT_CLASS (image_class);
809 
810 	html_object_class_init (object_class, type, size);
811 
812 	object_class->copy = copy;
813 	object_class->draw = draw;
814 	object_class->destroy = destroy;
815 	object_class->calc_min_width = calc_min_width;
816 	object_class->calc_preferred_width = calc_preferred_width;
817 	object_class->calc_size = html_image_real_calc_size;
818 	object_class->check_point = check_point;
819 	object_class->get_url = get_url;
820 	object_class->get_target = get_target;
821 	object_class->get_src = get_src;
822 	object_class->set_link = set_link;
823 	object_class->accepts_cursor = accepts_cursor;
824 	object_class->get_valign = get_valign;
825 	object_class->save = save;
826 	object_class->save_plain = save_plain;
827 	object_class->select_range = select_range;
828 
829 	parent_class = &html_object_class;
830 }
831 
832 void
html_image_init(HTMLImage * image,HTMLImageClass * klass,HTMLImageFactory * imf,const gchar * filename,const gchar * url,const gchar * target,gint16 width,gint16 height,gboolean percent_width,gboolean percent_height,gint8 border,HTMLColor * color,HTMLVAlignType valign,gboolean reload)833 html_image_init (HTMLImage *image,
834                  HTMLImageClass *klass,
835                  HTMLImageFactory *imf,
836                  const gchar *filename,
837                  const gchar *url,
838                  const gchar *target,
839                  gint16 width,
840                  gint16 height,
841                  gboolean percent_width,
842                  gboolean percent_height,
843                  gint8 border,
844                  HTMLColor *color,
845                  HTMLVAlignType valign,
846                  gboolean reload)
847 {
848 	HTMLObject *object;
849 
850 	g_assert (filename);
851 
852 	object = HTML_OBJECT (image);
853 
854 	html_object_init (object, HTML_OBJECT_CLASS (klass));
855 
856 	image->animation_active = FALSE;
857 	image->url = g_strdup (url);
858 	image->target = g_strdup (target);
859 	image->usemap = NULL;
860 	image->final_url = NULL;
861 	image->ismap = FALSE;
862 
863 	image->specified_width  = width;
864 	image->specified_height = height;
865 	image->percent_width    = percent_width;
866 	image->percent_height   = percent_height;
867 	image->border           = border;
868 
869 	if (color) {
870 		image->color = color;
871 		image->have_color = TRUE;
872 		html_color_ref (color);
873 	} else {
874 		image->color = NULL;
875 		image->have_color = FALSE;
876 	}
877 
878 	image->alt = NULL;
879 
880 	image->hspace = 0;
881 	image->vspace = 0;
882 
883 	if (valign == HTML_VALIGN_NONE)
884 		valign = HTML_VALIGN_BOTTOM;
885 	image->valign = valign;
886 
887 	image->image_ptr = html_image_factory_register (imf, image, filename, reload);
888 }
889 
890 HTMLObject *
html_image_new(HTMLImageFactory * imf,const gchar * filename,const gchar * url,const gchar * target,gint16 width,gint16 height,gboolean percent_width,gboolean percent_height,gint8 border,HTMLColor * color,HTMLVAlignType valign,gboolean reload)891 html_image_new (HTMLImageFactory *imf,
892                 const gchar *filename,
893                 const gchar *url,
894                 const gchar *target,
895                 gint16 width,
896                 gint16 height,
897                 gboolean percent_width,
898                 gboolean percent_height,
899                 gint8 border,
900                 HTMLColor *color,
901                 HTMLVAlignType valign,
902                 gboolean reload)
903 {
904 	HTMLImage *image;
905 
906 	image = g_new (HTMLImage, 1);
907 
908 	html_image_init (image, &html_image_class,
909 			 imf,
910 			 filename,
911 			 url,
912 			 target,
913 			 width, height,
914 			 percent_width, percent_height,
915 			 border,
916 			 color,
917 			 valign,
918 			 reload);
919 
920 	return HTML_OBJECT (image);
921 }
922 
923 void
html_image_set_spacing(HTMLImage * image,gint hspace,gint vspace)924 html_image_set_spacing (HTMLImage *image,
925                         gint hspace,
926                         gint vspace)
927 {
928 	gboolean changed = FALSE;
929 
930 	if (image->hspace != hspace) {
931 		image->hspace = hspace;
932 		changed = TRUE;
933 	}
934 
935 	if (image->vspace != vspace) {
936 		image->vspace = vspace;
937 		changed = TRUE;
938 	}
939 
940 	if (changed) {
941 		html_object_change_set (HTML_OBJECT (image), HTML_CHANGE_ALL_CALC);
942 		html_engine_schedule_update (image->image_ptr->factory->engine);
943 	}
944 }
945 
946 void
html_image_edit_set_url(HTMLImage * image,const gchar * url)947 html_image_edit_set_url (HTMLImage *image,
948                          const gchar *url)
949 {
950 	if (url) {
951 		HTMLImageFactory *imf = image->image_ptr->factory;
952 
953 		html_object_change_set (HTML_OBJECT (image), HTML_CHANGE_ALL_CALC);
954 		html_image_factory_unregister (imf, image->image_ptr, HTML_IMAGE (image));
955 		image->image_ptr = html_image_factory_register (imf, image, url, TRUE);
956 		html_object_change_set (HTML_OBJECT (image), HTML_CHANGE_ALL_CALC);
957 		html_engine_schedule_update (imf->engine);
958 	}
959 }
960 
961 void
html_image_set_url(HTMLImage * image,const gchar * url)962 html_image_set_url (HTMLImage *image,
963                     const gchar *url)
964 {
965 	if (url && strcmp (image->image_ptr->url, url)) {
966 		HTMLImageFactory *imf = image->image_ptr->factory;
967 
968 		html_image_factory_unregister (imf, image->image_ptr, HTML_IMAGE (image));
969 		image->image_ptr = html_image_factory_register (imf, image, url, FALSE);
970 	}
971 }
972 
973 void
html_image_set_valign(HTMLImage * image,HTMLVAlignType valign)974 html_image_set_valign (HTMLImage *image,
975                        HTMLVAlignType valign)
976 {
977 	if (image->valign != valign) {
978 		image->valign = valign;
979 		html_engine_schedule_update (image->image_ptr->factory->engine);
980 	}
981 }
982 
983 void
html_image_set_border(HTMLImage * image,gint border)984 html_image_set_border (HTMLImage *image,
985                        gint border)
986 {
987 	if (image->border != border) {
988 		image->border = border;
989 		html_object_change_set (HTML_OBJECT (image), HTML_CHANGE_ALL_CALC);
990 		html_engine_schedule_update (image->image_ptr->factory->engine);
991 	}
992 }
993 
994 void
html_image_set_alt(HTMLImage * image,const gchar * alt)995 html_image_set_alt (HTMLImage *image,
996                     const gchar *alt)
997 {
998 	g_free (image->alt);
999 	image->alt = g_strdup (alt);
1000 }
1001 
1002 void
html_image_set_map(HTMLImage * image,gchar * usemap,gboolean ismap)1003 html_image_set_map (HTMLImage *image,
1004                     gchar *usemap,
1005                     gboolean ismap)
1006 {
1007 	gchar *url = NULL;
1008 
1009 	g_free (image->usemap);
1010 
1011 	if (usemap != NULL) {
1012 		image->ismap = FALSE;
1013 		url = g_strdup (usemap);
1014 	} else {
1015 		image->ismap = ismap;
1016 	}
1017 	image->usemap = url;
1018 }
1019 
1020 void
html_image_set_size(HTMLImage * image,gint w,gint h,gboolean pw,gboolean ph)1021 html_image_set_size (HTMLImage *image,
1022                      gint w,
1023                      gint h,
1024                      gboolean pw,
1025                      gboolean ph)
1026 {
1027 	gboolean changed = FALSE;
1028 
1029 	if (pw != image->percent_width) {
1030 		image->percent_width = pw;
1031 		changed = TRUE;
1032 	}
1033 
1034 	if (ph != image->percent_height) {
1035 		image->percent_height = ph;
1036 		changed = TRUE;
1037 	}
1038 
1039 	if (w != image->specified_width) {
1040 		image->specified_width = w;
1041 		changed = TRUE;
1042 	}
1043 
1044 	if (h != image->specified_height) {
1045 		image->specified_height = h;
1046 		changed = TRUE;
1047 	}
1048 
1049 	if (changed) {
1050 		html_object_change_set (HTML_OBJECT (image), HTML_CHANGE_ALL_CALC);
1051 		html_engine_schedule_update (image->image_ptr->factory->engine);
1052 	}
1053 }
1054 
1055 static gchar *fallback_image_content_types[] = { (gchar *) "image/*", NULL};
1056 
1057 static gchar **
html_image_factory_types(GtkHTMLStream * stream,gpointer user_data)1058 html_image_factory_types (GtkHTMLStream *stream,
1059                           gpointer user_data)
1060 {
1061 	static gchar **image_content_types = NULL;
1062 
1063 #if 0
1064 	/* this code should work in gtk+-2.2 but it is untested */
1065 	if (image_content_types == NULL) {
1066 		GSList *formats;
1067 		GSList *cur;
1068 		GSList *types = NULL;
1069 		gint i;
1070 
1071 		formats = gdk_pixbuf_get_formats ();
1072 
1073 		for (cur = formats; cur; cur = cur->next) {
1074 			GdkPixbufFormat *format = cur->data;
1075 			gchar **mime;
1076 
1077 			mime = gdk_pixbuf_formats_get_mime_types ();
1078 			for (i = 0; mime && mime[i]; i++)
1079 				g_slist_prepend (types, g_strdup (mime[i]));
1080 
1081 		}
1082 		g_slist_free (formats);
1083 
1084 		if (types) {
1085 			image_content_types = g_new0 (gchar *, g_slist_length (types) + 1);
1086 
1087 			for (cur = types, i = 0; cur; cur = cur->next, i++) {
1088 				image_content_types[i] = cur->data;
1089 			}
1090 			g_slist_free (types);
1091 		} else {
1092 			image_content_types = fallback_image_content_types;
1093 		}
1094 	}
1095 #else
1096 	image_content_types = fallback_image_content_types;
1097 #endif
1098 
1099 	return image_content_types;
1100 }
1101 
1102 static void
update_or_redraw(HTMLImagePointer * ip)1103 update_or_redraw (HTMLImagePointer *ip)
1104 {
1105 	GSList *list;
1106 	gboolean update = FALSE;
1107 
1108 	/* do nothing when factory gone; maybe the message drawing has been cancelled */
1109 	if (!ip->factory)
1110 		return;
1111 
1112 	for (list = ip->interests; list; list = list->next) {
1113 		if (list->data == NULL)
1114 			update = TRUE;
1115 		else {
1116 			HTMLImage *image = HTML_IMAGE (list->data);
1117 			gint pixel_size = html_painter_get_pixel_size (ip->factory->engine->painter);
1118 			gint w, h;
1119 
1120 			w = html_image_get_actual_width (image, ip->factory->engine->painter)
1121 				+ (image->border * 2 + 2 * image->hspace) * pixel_size;
1122 			h = html_image_get_actual_height (image, ip->factory->engine->painter)
1123 				+ (image->border * 2 + 2 * image->vspace) * pixel_size;
1124 
1125 			/* printf ("%dx%d  <-->  %dx%d\n", w, h, HTML_OBJECT (list->data)->width,
1126 			 * HTML_OBJECT (list->data)->ascent + HTML_OBJECT (list->data)->descent); */
1127 
1128 			if (w != HTML_OBJECT (list->data)->width
1129 			    || h != HTML_OBJECT (list->data)->ascent + HTML_OBJECT (list->data)->descent) {
1130 				html_object_change_set (HTML_OBJECT (list->data), HTML_CHANGE_ALL_CALC);
1131 				update = TRUE;
1132 			}
1133 		}
1134 	}
1135 
1136 	if (ip->factory->engine->block && ip->factory->engine->opened_streams)
1137 		return;
1138 
1139 	if (!update) {
1140 		/* printf ("REDRAW\n"); */
1141 		for (list = ip->interests; list; list = list->next)
1142 			if (list->data) /* && html_object_is_visible (HTML_OBJECT (list->data))) */
1143 				html_engine_queue_draw (ip->factory->engine, HTML_OBJECT (list->data));
1144 		if (ip->interests)
1145 			html_engine_flush_draw_queue (ip->factory->engine);
1146 	} else {
1147 		/* printf ("UPDATE\n"); */
1148 		html_engine_schedule_update (ip->factory->engine);
1149 	}
1150 }
1151 
1152 static void
html_image_factory_end_pixbuf(GtkHTMLStream * stream,GtkHTMLStreamStatus status,gpointer user_data)1153 html_image_factory_end_pixbuf (GtkHTMLStream *stream,
1154                                GtkHTMLStreamStatus status,
1155                                gpointer user_data)
1156 {
1157 	HTMLImagePointer *ip = user_data;
1158 
1159 	gdk_pixbuf_loader_close (ip->loader, NULL);
1160 
1161 	if (!ip->animation) {
1162 		ip->animation = gdk_pixbuf_loader_get_animation (ip->loader);
1163 
1164 		if (ip->animation)
1165 			g_object_ref (ip->animation);
1166 	}
1167 	html_image_pointer_start_animation (ip);
1168 
1169 	g_object_unref (ip->loader);
1170 	ip->loader = NULL;
1171 
1172 	/* if no ip->factory is set, then the image loading has been cancelled meanwhile, probably. */
1173 	if (ip->factory) {
1174 		update_or_redraw (ip);
1175 		if (ip->factory->engine->opened_streams && ip->factory->engine->block_images)
1176 			html_engine_opened_streams_decrement (ip->factory->engine);
1177 		/* printf ("IMAGE(%p) opened streams: %d\n", ip->factory->engine, ip->factory->engine->opened_streams); */
1178 		if (ip->factory->engine->opened_streams == 0 && ip->factory->engine->block && ip->factory->engine->block_images)
1179 			html_engine_schedule_update (ip->factory->engine);
1180 	}
1181 
1182 	html_image_pointer_unref (ip);
1183 }
1184 
1185 static void
html_image_factory_write_pixbuf(GtkHTMLStream * stream,const gchar * buffer,gsize size,gpointer user_data)1186 html_image_factory_write_pixbuf (GtkHTMLStream *stream,
1187                                  const gchar *buffer,
1188                                  gsize size,
1189                                  gpointer user_data)
1190 {
1191 	HTMLImagePointer *p = user_data;
1192 
1193 	/* FIXME !Check return value */
1194 	gdk_pixbuf_loader_write (p->loader, (const guchar *) buffer, size, NULL);
1195 }
1196 
1197 static void
html_image_pointer_queue_animation(HTMLImagePointer * ip)1198 html_image_pointer_queue_animation (HTMLImagePointer *ip)
1199 {
1200 	if (!ip->animation_timeout && ip->factory && ip->factory->animate) {
1201 		gint delay;
1202 
1203 		gdk_pixbuf_animation_iter_advance (ip->iter, NULL);
1204 		delay = gdk_pixbuf_animation_iter_get_delay_time (ip->iter);
1205 
1206 		ip->animation_timeout = g_timeout_add (delay,
1207 						       (GSourceFunc) html_image_pointer_update,
1208 						       (gpointer) ip);
1209 	}
1210 
1211 }
1212 
1213 static gboolean
html_image_pointer_update(HTMLImagePointer * ip)1214 html_image_pointer_update (HTMLImagePointer *ip)
1215 {
1216 	HTMLEngine *engine;
1217 	GSList *cur;
1218 
1219 	g_return_val_if_fail (ip->factory != NULL, FALSE);
1220 
1221 	engine = ip->factory->engine;
1222 	ip->animation_timeout = 0;
1223 
1224 	DA (printf ("animation_timeout (%p)\n", ip);)
1225 	for (cur = ip->interests; cur; cur = cur->next) {
1226 		HTMLImage           *image = cur->data;
1227 
1228 		if (image && image->animation_active && html_object_is_parent (engine->clue, HTML_OBJECT (image))) {
1229 			DA (printf ("queue draw (%p)\n", image);)
1230 
1231 			image->animation_active = FALSE;
1232 			html_engine_queue_draw (engine, HTML_OBJECT (image));
1233 		}
1234 	}
1235 
1236 	html_image_pointer_start_animation (ip);
1237 	return FALSE;
1238 }
1239 
1240 static void
html_image_pointer_start_animation(HTMLImagePointer * ip)1241 html_image_pointer_start_animation (HTMLImagePointer *ip)
1242 {
1243 	if (ip->animation && !gdk_pixbuf_animation_is_static_image (ip->animation)) {
1244 		if (!ip->iter)
1245 			ip->iter = gdk_pixbuf_animation_get_iter (ip->animation, NULL);
1246 
1247 		html_image_pointer_queue_animation (ip);
1248 	}
1249 }
1250 
1251 static void
html_image_pointer_stop_animation(HTMLImagePointer * ip)1252 html_image_pointer_stop_animation (HTMLImagePointer *ip)
1253 {
1254 	if (ip->animation_timeout) {
1255 		g_source_remove (ip->animation_timeout);
1256 		ip->animation_timeout = 0;
1257 	}
1258 }
1259 
1260 static void
html_image_factory_area_updated(GdkPixbufLoader * loader,guint x,guint y,guint width,guint height,HTMLImagePointer * ip)1261 html_image_factory_area_updated (GdkPixbufLoader *loader,
1262                                  guint x,
1263                                  guint y,
1264                                  guint width,
1265                                  guint height,
1266                                  HTMLImagePointer *ip)
1267 {
1268 	html_image_pointer_stop_animation (ip);
1269 	/* update will requeue */
1270 	html_image_pointer_update (ip);
1271 }
1272 
1273 static void
html_image_factory_area_prepared(GdkPixbufLoader * loader,HTMLImagePointer * ip)1274 html_image_factory_area_prepared (GdkPixbufLoader *loader,
1275                                   HTMLImagePointer *ip)
1276 {
1277 	if (!ip->animation) {
1278 		ip->animation = gdk_pixbuf_loader_get_animation (loader);
1279 		g_object_ref (ip->animation);
1280 
1281 		html_image_pointer_start_animation (ip);
1282 	}
1283 	update_or_redraw (ip);
1284 }
1285 
1286 static GdkPixbuf *
html_image_factory_get_missing(HTMLImageFactory * factory)1287 html_image_factory_get_missing (HTMLImageFactory *factory)
1288 {
1289 	if (!factory->missing)
1290 		factory->missing = gtk_widget_render_icon (GTK_WIDGET (factory->engine->widget),
1291 							  GTK_STOCK_MISSING_IMAGE,
1292 							  GTK_ICON_SIZE_BUTTON, "GtkHTML.ImageMissing");
1293 	return factory->missing;
1294 }
1295 
1296 HTMLImageFactory *
html_image_factory_new(HTMLEngine * e)1297 html_image_factory_new (HTMLEngine *e)
1298 {
1299 	HTMLImageFactory *retval;
1300 	retval = g_new (HTMLImageFactory, 1);
1301 	retval->engine = e;
1302 	retval->loaded_images = g_hash_table_new (g_str_hash, g_str_equal);
1303 	retval->missing = NULL;
1304 	retval->animate = TRUE;
1305 
1306 	return retval;
1307 }
1308 
1309 static gboolean
cleanup_images(gpointer key,gpointer value,gpointer free_everything)1310 cleanup_images (gpointer key,
1311                 gpointer value,
1312                 gpointer free_everything)
1313 {
1314 	HTMLImagePointer *ip = value;
1315 
1316 	/* free_everything means: NULL only clean, non-NULL free */
1317 	if (free_everything) {
1318 		if (ip->interests != NULL) {
1319 			g_slist_free (ip->interests);
1320 			ip->interests = NULL;
1321 		}
1322 	}
1323 
1324 	/* clean only if this image is not used anymore */
1325 	if (!ip->interests) {
1326 		html_image_pointer_unref (ip);
1327 		ip->factory = NULL;
1328 		return TRUE;
1329 	}
1330 
1331 	return FALSE;
1332 }
1333 
1334 void
html_image_factory_cleanup(HTMLImageFactory * factory)1335 html_image_factory_cleanup (HTMLImageFactory *factory)
1336 {
1337 	g_return_if_fail (factory);
1338 	g_hash_table_foreach_remove (factory->loaded_images, cleanup_images, NULL);
1339 }
1340 
1341 void
html_image_factory_free(HTMLImageFactory * factory)1342 html_image_factory_free (HTMLImageFactory *factory)
1343 {
1344 	g_return_if_fail (factory);
1345 
1346 	g_hash_table_foreach_remove (factory->loaded_images, cleanup_images, factory);
1347 	g_hash_table_destroy (factory->loaded_images);
1348 
1349 	if (factory->missing)
1350 		g_object_unref (factory->missing);
1351 
1352 	g_free (factory);
1353 }
1354 
1355 #define STALL_INTERVAL 1000
1356 
1357 static HTMLImagePointer *
html_image_pointer_new(const gchar * filename,HTMLImageFactory * factory)1358 html_image_pointer_new (const gchar *filename,
1359                         HTMLImageFactory *factory)
1360 {
1361 	HTMLImagePointer *retval;
1362 
1363 	retval = g_new (HTMLImagePointer, 1);
1364 	retval->refcount = 1;
1365 	retval->url = g_strdup (filename);
1366 	retval->loader = gdk_pixbuf_loader_new ();
1367 	retval->iter = NULL;
1368 	retval->animation = NULL;
1369 	retval->interests = NULL;
1370 	retval->factory = factory;
1371 	retval->stall = FALSE;
1372 	retval->stall_timeout = g_timeout_add (STALL_INTERVAL,
1373 					       (GSourceFunc) html_image_pointer_timeout,
1374 					       retval);
1375 	retval->animation_timeout = 0;
1376 	return retval;
1377 }
1378 
1379 static gboolean
html_image_pointer_timeout(HTMLImagePointer * ip)1380 html_image_pointer_timeout (HTMLImagePointer *ip)
1381 {
1382 	GSList *list;
1383 	HTMLImage *image;
1384 
1385 	ip->stall_timeout = 0;
1386 
1387 	g_return_val_if_fail (ip->factory != NULL, FALSE);
1388 
1389 	ip->stall = TRUE;
1390 
1391 	list = ip->interests;
1392 	/*
1393 	 * draw the frame now that we decided they've had enough time to
1394 	 * load the image
1395 	 */
1396 	if (ip->animation == NULL) {
1397 		while (list) {
1398 			image = (HTMLImage *) list->data;
1399 
1400 			if (image)
1401 				html_engine_queue_draw (ip->factory->engine,
1402 							HTML_OBJECT (image));
1403 
1404 			list = list->next;
1405 		}
1406 	}
1407 	return FALSE;
1408 }
1409 
1410 static void
html_image_pointer_ref(HTMLImagePointer * ip)1411 html_image_pointer_ref (HTMLImagePointer *ip)
1412 {
1413 	ip->refcount++;
1414 }
1415 
1416 static void
free_image_ptr_data(HTMLImagePointer * ip)1417 free_image_ptr_data (HTMLImagePointer *ip)
1418 {
1419 	if (ip->loader) {
1420 		gdk_pixbuf_loader_close (ip->loader, NULL);
1421 		g_object_unref (ip->loader);
1422 		ip->loader = NULL;
1423 	}
1424 	if (ip->animation) {
1425 		g_object_unref (ip->animation);
1426 		ip->animation = NULL;
1427 	}
1428 	if (ip->iter) {
1429 		g_object_unref (ip->iter);
1430 		ip->iter = NULL;
1431 	}
1432 }
1433 
1434 static void
html_image_pointer_remove_stall(HTMLImagePointer * ip)1435 html_image_pointer_remove_stall (HTMLImagePointer *ip)
1436 {
1437 	if (ip->stall_timeout) {
1438 		g_source_remove (ip->stall_timeout);
1439 		ip->stall_timeout = 0;
1440 	}
1441 }
1442 
1443 static void
html_image_pointer_unref(HTMLImagePointer * ip)1444 html_image_pointer_unref (HTMLImagePointer *ip)
1445 {
1446 	g_return_if_fail (ip != NULL);
1447 
1448 	ip->refcount--;
1449 	/* printf ("unref(%p) %s --> %d\n", ip, ip->url, ip->refcount); */
1450 	if (ip->refcount < 1) {
1451 		/* printf ("freeing %s\n", ip->url); */
1452 		html_image_pointer_remove_stall (ip);
1453 		html_image_pointer_stop_animation (ip);
1454 		g_free (ip->url);
1455 		free_image_ptr_data (ip);
1456 		g_free (ip);
1457 	}
1458 }
1459 
1460 static GtkHTMLStream *
html_image_pointer_load(HTMLImagePointer * ip)1461 html_image_pointer_load (HTMLImagePointer *ip)
1462 {
1463 	if (!ip->factory || ip->factory->engine->stopped)
1464 		return NULL;
1465 
1466 	html_image_pointer_ref (ip);
1467 
1468 	if (ip->factory->engine->block_images)
1469 		html_engine_opened_streams_increment (ip->factory->engine);
1470 	return gtk_html_stream_new (GTK_HTML (ip->factory->engine->widget),
1471 				    html_image_factory_types,
1472 				    html_image_factory_write_pixbuf,
1473 				    html_image_factory_end_pixbuf,
1474 				    ip);
1475 }
1476 
1477 HTMLImagePointer *
html_image_factory_register(HTMLImageFactory * factory,HTMLImage * i,const gchar * url,gboolean reload)1478 html_image_factory_register (HTMLImageFactory *factory,
1479                              HTMLImage *i,
1480                              const gchar *url,
1481                              gboolean reload)
1482 {
1483 	HTMLImagePointer *ip;
1484 	GtkHTMLStream *stream = NULL;
1485 
1486 	g_return_val_if_fail (factory, NULL);
1487 	g_return_val_if_fail (url, NULL);
1488 
1489 	ip = g_hash_table_lookup (factory->loaded_images, url);
1490 
1491 	if (!ip) {
1492 		ip = html_image_pointer_new (url, factory);
1493 		g_hash_table_insert (factory->loaded_images, ip->url, ip);
1494 		if (*url) {
1495 			g_signal_connect (G_OBJECT (ip->loader), "area_prepared",
1496 					  G_CALLBACK (html_image_factory_area_prepared),
1497 					  ip);
1498 
1499 			g_signal_connect (G_OBJECT (ip->loader), "area_updated",
1500 					  G_CALLBACK (html_image_factory_area_updated),
1501 					  ip);
1502 			stream = html_image_pointer_load (ip);
1503 		}
1504 	} else {
1505 		if (reload) {
1506 			free_image_ptr_data (ip);
1507 			ip->loader = gdk_pixbuf_loader_new ();
1508 			stream = html_image_pointer_load (ip);
1509 		}
1510 	}
1511 
1512 	if (stream)
1513 		g_signal_emit_by_name (factory->engine, "url_requested", ip->url, stream);
1514 
1515 	html_image_pointer_ref (ip);
1516 
1517 	/* we add also NULL ptrs, as we dont want these to be cleaned out */
1518 	ip->interests = g_slist_prepend (ip->interests, i);
1519 
1520 	if (i) {
1521 		i->image_ptr = ip;
1522 	}
1523 
1524 	return ip;
1525 }
1526 
1527 #if 0
1528 HTMLEngine *
1529 html_image_factory_get_engine (HTMLImageFactory *factory)
1530 {
1531 	return factory->engine;
1532 }
1533 #endif
1534 
1535 void
html_image_factory_unregister(HTMLImageFactory * factory,HTMLImagePointer * pointer,HTMLImage * i)1536 html_image_factory_unregister (HTMLImageFactory *factory,
1537                                HTMLImagePointer *pointer,
1538                                HTMLImage *i)
1539 {
1540 	pointer->interests = g_slist_remove (pointer->interests, i);
1541 
1542 	html_image_pointer_unref (pointer);
1543 
1544 	if (pointer->refcount == 1) {
1545 		g_assert (pointer->interests == NULL);
1546 		/*
1547 		 * FIXME The factory can be NULL if the image was from an iframe
1548 		 * that has been destroyed and the image is living in the cut buffer
1549 		 * this isn't particularly clean and should be refactored.
1550 		 *
1551 		 * We really need a way to let cut objects know they are living outside
1552 		 * the normal flow.
1553 		 */
1554 		if (factory)
1555 			g_hash_table_remove (factory->loaded_images, pointer->url);
1556 		pointer->factory = NULL;
1557 		html_image_pointer_unref (pointer);
1558 	}
1559 }
1560 
1561 static void
stop_anim(gpointer key,gpointer value,gpointer user_data)1562 stop_anim (gpointer key,
1563            gpointer value,
1564            gpointer user_data)
1565 {
1566 	HTMLImagePointer *ip = value;
1567 	html_image_pointer_remove_stall (ip);
1568 	html_image_pointer_stop_animation (ip);
1569 }
1570 
1571 void
html_image_factory_stop_animations(HTMLImageFactory * factory)1572 html_image_factory_stop_animations (HTMLImageFactory *factory)
1573 {
1574 	DA (g_warning ("stop animations");)
1575 	g_hash_table_foreach (factory->loaded_images, stop_anim, NULL);
1576 }
1577 
1578 static void
start_anim(gpointer key,gpointer value,gpointer user_data)1579 start_anim (gpointer key,
1580             gpointer value,
1581             gpointer user_data)
1582 {
1583 	HTMLImagePointer *ip = value;
1584 	html_image_pointer_start_animation (ip);
1585 }
1586 
1587 void
html_image_factory_start_animations(HTMLImageFactory * factory)1588 html_image_factory_start_animations (HTMLImageFactory *factory)
1589 {
1590 	DA (g_warning ("start animations");)
1591 	g_hash_table_foreach (factory->loaded_images, start_anim, NULL);
1592 }
1593 
1594 gboolean
html_image_factory_get_animate(HTMLImageFactory * factory)1595 html_image_factory_get_animate (HTMLImageFactory *factory)
1596 {
1597 	return factory->animate;
1598 }
1599 
1600 void
html_image_factory_set_animate(HTMLImageFactory * factory,gboolean animate)1601 html_image_factory_set_animate (HTMLImageFactory *factory,
1602                                 gboolean animate)
1603 {
1604 	if (animate != factory->animate) {
1605 		factory->animate = animate;
1606 
1607 		if (animate)
1608 			html_image_factory_start_animations (factory);
1609 		else
1610 			html_image_factory_stop_animations (factory);
1611 	}
1612 }
1613 
1614 static gboolean
move_image_pointers(gpointer key,gpointer value,gpointer data)1615 move_image_pointers (gpointer key,
1616                      gpointer value,
1617                      gpointer data)
1618 {
1619 	HTMLImageFactory *dst = HTML_IMAGE_FACTORY (data);
1620 	HTMLImagePointer *ip  = HTML_IMAGE_POINTER (value);
1621 
1622 	ip->factory = dst;
1623 
1624 	g_hash_table_insert (dst->loaded_images, ip->url, ip);
1625 	if (!ip->factory->engine->stopped)
1626 		g_signal_emit_by_name (ip->factory->engine, "url_requested", ip->url, html_image_pointer_load (ip));
1627 
1628 	return TRUE;
1629 }
1630 
1631 void
html_image_factory_move_images(HTMLImageFactory * dst,HTMLImageFactory * src)1632 html_image_factory_move_images (HTMLImageFactory *dst,
1633                                 HTMLImageFactory *src)
1634 {
1635 	g_hash_table_foreach_remove (src->loaded_images, move_image_pointers, dst);
1636 }
1637 
1638 static void
deactivate_anim(gpointer key,gpointer value,gpointer user_data)1639 deactivate_anim (gpointer key,
1640                  gpointer value,
1641                  gpointer user_data)
1642 {
1643 	HTMLImagePointer *ip = value;
1644 	GSList *cur = ip->interests;
1645 	HTMLImage *image;
1646 
1647 	while (cur) {
1648 		if (cur->data) {
1649 			image = (HTMLImage *) cur->data;
1650 			image->animation_active = FALSE;
1651 		}
1652 		cur = cur->next;
1653 	}
1654 }
1655 
1656 void
html_image_factory_deactivate_animations(HTMLImageFactory * factory)1657 html_image_factory_deactivate_animations (HTMLImageFactory *factory)
1658 {
1659 	g_hash_table_foreach (factory->loaded_images, deactivate_anim, NULL);
1660 }
1661 
1662 static void
ref_image_ptr(gpointer key,gpointer val,gpointer data)1663 ref_image_ptr (gpointer key,
1664                gpointer val,
1665                gpointer data)
1666 {
1667 	if (HTML_IMAGE_POINTER (val)->animation)
1668 		html_image_pointer_ref (HTML_IMAGE_POINTER (val));
1669 	/* printf ("ref(%p) %s --> %d\n", val, HTML_IMAGE_POINTER (val)->url, HTML_IMAGE_POINTER (val)->refcount); */
1670 }
1671 
1672 static void
unref_image_ptr(gpointer key,gpointer val,gpointer data)1673 unref_image_ptr (gpointer key,
1674                  gpointer val,
1675                  gpointer data)
1676 {
1677 	if (HTML_IMAGE_POINTER (val)->animation)
1678 		html_image_pointer_unref (HTML_IMAGE_POINTER (val));
1679 }
1680 
1681 void
html_image_factory_ref_all_images(HTMLImageFactory * factory)1682 html_image_factory_ref_all_images (HTMLImageFactory *factory)
1683 {
1684 	if (!factory->loaded_images)
1685 		return;
1686 
1687 	g_hash_table_foreach (factory->loaded_images, ref_image_ptr, NULL);
1688 }
1689 
1690 void
html_image_factory_unref_all_images(HTMLImageFactory * factory)1691 html_image_factory_unref_all_images (HTMLImageFactory *factory)
1692 {
1693 	if (!factory->loaded_images)
1694 		return;
1695 
1696 	g_hash_table_foreach (factory->loaded_images, unref_image_ptr, NULL);
1697 }
1698 
1699 void
html_image_factory_ref_image_ptr(HTMLImageFactory * factory,const gchar * url)1700 html_image_factory_ref_image_ptr (HTMLImageFactory *factory,
1701                                   const gchar *url)
1702 {
1703 	HTMLImagePointer *ptr;
1704 
1705 	if (!factory->loaded_images)
1706 		return;
1707 
1708 	ptr = HTML_IMAGE_POINTER (g_hash_table_lookup (factory->loaded_images, url));
1709 	if (ptr)
1710 		html_image_pointer_ref (ptr);
1711 }
1712 
1713 void
html_image_factory_unref_image_ptr(HTMLImageFactory * factory,const gchar * url)1714 html_image_factory_unref_image_ptr (HTMLImageFactory *factory,
1715                                     const gchar *url)
1716 {
1717 	HTMLImagePointer *ptr;
1718 
1719 	if (!factory->loaded_images)
1720 		return;
1721 
1722 	ptr = HTML_IMAGE_POINTER (g_hash_table_lookup (factory->loaded_images, url));
1723 	if (ptr)
1724 		html_image_pointer_unref (ptr);
1725 }
1726