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