Thu Oct 19 15:20:51 2006 John Ellis <johne@verizon.net>
[geeqie.git] / src / image.c
1 /*
2  * GQview
3  * (C) 2005 John Ellis
4  *
5  * Author: John Ellis
6  *
7  * This software is released under the GNU General Public License (GNU GPL).
8  * Please read the included file COPYING for more information.
9  * This software comes with no warranty of any kind, use at your own risk!
10  */
11
12
13 #include "gqview.h"
14 #include "image.h"
15
16
17 #include "image-load.h"
18 #include "collect.h"
19 #include "exif.h"
20 #include "pixbuf-renderer.h"
21 #include "pixbuf_util.h"
22 #include "ui_fileops.h"
23
24 #include <math.h>
25
26
27 /* size of the image loader buffer (512 bytes x defined number) */
28 #define IMAGE_LOAD_BUFFER_COUNT 8
29
30 /* define this so that more bytes are read per idle loop on larger images (> 1MB) */
31 #define IMAGE_THROTTLE_LARGER_IMAGES 1
32
33 /* throttle factor to increase read bytes by (2 is double, 3 is triple, etc.) */
34 #define IMAGE_THROTTLE_FACTOR 4
35
36 /* the file size at which throttling take place */
37 #define IMAGE_THROTTLE_THRESHOLD 1048576
38
39 #define IMAGE_AUTO_REFRESH_TIME 3000
40
41
42 static GList *image_list = NULL;
43
44
45 static void image_update_title(ImageWindow *imd);
46
47 /*
48  *-------------------------------------------------------------------
49  * 'signals'
50  *-------------------------------------------------------------------
51  */
52
53 static void image_click_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
54 {
55         ImageWindow *imd = data;
56
57         if (imd->func_button)
58                 {
59                 imd->func_button(imd, event->button, event->time,
60                                  event->x, event->y, event->state, imd->data_button);
61                 }
62 }
63
64 static void image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
65 {
66         ImageWindow *imd = data;
67
68         if (imd->func_scroll_notify && pr->scale)
69                 {
70                 imd->func_scroll_notify(imd,
71                                         (gint)((gdouble)pr->x_scroll / pr->scale),
72                                         (gint)((gdouble)pr->y_scroll / pr->scale),
73                                         (gint)((gdouble)pr->image_width - pr->vis_width / pr->scale),
74                                         (gint)((gdouble)pr->image_height - pr->vis_height / pr->scale),
75                                         imd->data_scroll_notify);
76                 }
77 }
78
79 static void image_update_util(ImageWindow *imd)
80 {
81         if (imd->func_update) imd->func_update(imd, imd->data_update);
82 }
83
84 static void image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
85 {
86         ImageWindow *imd = data;
87
88         if (imd->title_show_zoom) image_update_title(imd);
89         image_update_util(imd);
90 }
91
92 static void image_complete_util(ImageWindow *imd, gint preload)
93 {
94         if (imd->il && image_get_pixbuf(imd) != image_loader_get_pixbuf(imd->il)) return;
95
96         if (debug) printf("image load completed \"%s\" (%s)\n",
97                           (preload) ? imd->read_ahead_path : imd->image_path,
98                           (preload) ? "preload" : "current");
99
100         if (!preload) imd->completed = TRUE;
101         if (imd->func_complete) imd->func_complete(imd, preload, imd->data_complete);
102 }
103
104 static void image_render_complete_cb(PixbufRenderer *pr, gpointer data)
105 {
106         ImageWindow *imd = data;
107
108         image_complete_util(imd, FALSE);
109 }
110
111 static void image_new_util(ImageWindow *imd)
112 {
113         if (imd->func_new) imd->func_new(imd, imd->data_new);
114 }
115
116 /*
117  *-------------------------------------------------------------------
118  * misc
119  *-------------------------------------------------------------------
120  */
121
122 static void image_update_title(ImageWindow *imd)
123 {
124         gchar *title = NULL;
125         gchar *zoom = NULL;
126         gchar *collection = NULL;
127
128         if (!imd->top_window) return;
129
130         if (imd->collection && collection_to_number(imd->collection) >= 0)
131                 {
132                 const gchar *name;
133                 name = imd->collection->name;
134                 if (!name) name = _("Untitled");
135                 collection = g_strdup_printf(" (Collection %s)", name);
136                 }
137
138         if (imd->title_show_zoom)
139                 {
140                 gchar *buf = image_zoom_get_as_text(imd);
141                 zoom = g_strconcat(" [", buf, "]", NULL);
142                 g_free(buf);
143                 }
144
145         title = g_strdup_printf("%s%s%s%s%s%s",
146                 imd->title ? imd->title : "",
147                 imd->image_name ? imd->image_name : "",
148                 zoom ? zoom : "",
149                 collection ? collection : "",
150                 imd->image_name ? " - " : "",
151                 imd->title_right ? imd->title_right : "");
152
153         gtk_window_set_title(GTK_WINDOW(imd->top_window), title);
154
155         g_free(title);
156         g_free(zoom);
157         g_free(collection);
158 }
159
160 /*
161  *-------------------------------------------------------------------
162  * rotation, flip, etc.
163  *-------------------------------------------------------------------
164  */
165
166 static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp)
167 {
168         PixbufRenderer *pr;
169         GdkPixbuf *new = NULL;
170         gint x, y;
171         gint t;
172
173         pr = (PixbufRenderer *)imd->pr;
174
175         imd->delay_alter_type = ALTER_NONE;
176
177         if (!pr->pixbuf) return;
178
179         x = pr->x_scroll + (pr->vis_width / 2);
180         y = pr->y_scroll + (pr->vis_height / 2);
181
182         switch (type)
183                 {
184                 case ALTER_ROTATE_90:
185                         new = pixbuf_copy_rotate_90(pr->pixbuf, FALSE);
186                         t = x;
187                         x = pr->height - y;
188                         y = t;
189                         break;
190                 case ALTER_ROTATE_90_CC:
191                         new = pixbuf_copy_rotate_90(pr->pixbuf, TRUE);
192                         t = x;
193                         x = y;
194                         y = pr->width - t;
195                         break;
196                 case ALTER_ROTATE_180:
197                         new = pixbuf_copy_mirror(pr->pixbuf, TRUE, TRUE);
198                         x = pr->width - x;
199                         y = pr->height - y;
200                         break;
201                 case ALTER_MIRROR:
202                         new = pixbuf_copy_mirror(pr->pixbuf, TRUE, FALSE);
203                         x = pr->width - x;
204                         break;
205                 case ALTER_FLIP:
206                         new = pixbuf_copy_mirror(pr->pixbuf, FALSE, TRUE);
207                         y = pr->height - y;
208                         break;
209                 case ALTER_DESATURATE:
210                         pixbuf_desaturate_rect(pr->pixbuf,
211                                                0, 0,  pr->image_width, pr->image_height);
212                         image_area_changed(imd, 0, 0, pr->image_width, pr->image_height);
213                         break;
214                 case ALTER_NONE:
215                 default:
216                         return;
217                         break;
218                 }
219
220         if (!new) return;
221
222         pixbuf_renderer_set_pixbuf(pr, new, pr->zoom);
223         g_object_unref(new);
224
225         if (clamp && pr->zoom != 0.0)
226                 {
227                 pixbuf_renderer_scroll(pr, x - (pr->vis_width / 2), y - (pr->vis_height / 2));
228                 }
229 }
230
231 static void image_post_process(ImageWindow *imd, gint clamp)
232 {
233         if (exif_rotate_enable && image_get_pixbuf(imd))
234                 {
235                 ExifData *ed;
236                 gint orientation;
237
238                 ed = exif_read(imd->image_path);
239                 if (ed && exif_get_integer(ed, "Orientation", &orientation))
240                         {
241                         /* see http://jpegclub.org/exif_orientation.html 
242                           1        2       3      4         5            6           7          8
243
244                         888888  888888      88  88      8888888888  88                  88  8888888888
245                         88          88      88  88      88  88      88  88          88  88      88  88
246                         8888      8888    8888  8888    88          8888888888  8888888888          88
247                         88          88      88  88
248                         88          88  888888  888888
249                         */
250
251                         switch (orientation)
252                                 {
253                                 case EXIF_ORIENTATION_TOP_LEFT:
254                                         /* normal -- nothing to do */
255                                         break;
256                                 case EXIF_ORIENTATION_TOP_RIGHT:
257                                         /* mirrored */
258                                         imd->delay_alter_type = ALTER_MIRROR;
259                                         break;
260                                 case EXIF_ORIENTATION_BOTTOM_RIGHT:
261                                         /* upside down */
262                                         imd->delay_alter_type = ALTER_ROTATE_180;
263                                         break;
264                                 case EXIF_ORIENTATION_BOTTOM_LEFT:
265                                         /* flipped */
266                                         imd->delay_alter_type = ALTER_FLIP;
267                                         break;
268                                 case EXIF_ORIENTATION_LEFT_TOP:
269                                         /* not implemented -- too wacky to fix in one step */
270                                         break;
271                                 case EXIF_ORIENTATION_RIGHT_TOP:
272                                         /* rotated -90 (270) */
273                                         imd->delay_alter_type = ALTER_ROTATE_90;
274                                         break;
275                                 case EXIF_ORIENTATION_RIGHT_BOTTOM:
276                                         /* not implemented -- too wacky to fix in one step */
277                                         break;
278                                 case EXIF_ORIENTATION_LEFT_BOTTOM:
279                                         /* rotated 90 */
280                                         imd->delay_alter_type = ALTER_ROTATE_90_CC;
281                                         break;
282                                 default:
283                                         /* The other values are out of range */
284                                         break;
285                                 }
286                         }
287                 exif_free(ed);
288                 }
289
290         if (imd->delay_alter_type != ALTER_NONE)
291                 {
292                 image_alter_real(imd, imd->delay_alter_type, clamp);
293                 }
294 }
295
296 /*
297  *-------------------------------------------------------------------
298  * read ahead (prebuffer)
299  *-------------------------------------------------------------------
300  */
301
302 static void image_read_ahead_cancel(ImageWindow *imd)
303 {
304         if (debug) printf("read ahead cancelled for :%s\n", imd->read_ahead_path);
305
306         image_loader_free(imd->read_ahead_il);
307         imd->read_ahead_il = NULL;
308
309         if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
310         imd->read_ahead_pixbuf = NULL;
311
312         g_free(imd->read_ahead_path);
313         imd->read_ahead_path = NULL;
314 }
315
316 static void image_read_ahead_done_cb(ImageLoader *il, gpointer data)
317 {
318         ImageWindow *imd = data;
319
320         if (debug) printf("read ahead done for :%s\n", imd->read_ahead_path);
321
322         imd->read_ahead_pixbuf = image_loader_get_pixbuf(imd->read_ahead_il);
323         if (imd->read_ahead_pixbuf)
324                 {
325                 g_object_ref(imd->read_ahead_pixbuf);
326                 }
327         else
328                 {
329                 imd->read_ahead_pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
330                 }
331         image_loader_free(imd->read_ahead_il);
332         imd->read_ahead_il = NULL;
333
334         image_complete_util(imd, TRUE);
335 }
336
337 static void image_read_ahead_error_cb(ImageLoader *il, gpointer data)
338 {
339         /* we even treat errors as success, maybe at least some of the file was ok */
340         image_read_ahead_done_cb(il, data);
341 }
342
343 static void image_read_ahead_start(ImageWindow *imd)
344 {
345         /* already started ? */
346         if (!imd->read_ahead_path || imd->read_ahead_il || imd->read_ahead_pixbuf) return;
347
348         /* still loading ?, do later */
349         if (imd->il) return;
350
351         if (debug) printf("read ahead started for :%s\n", imd->read_ahead_path);
352
353         imd->read_ahead_il = image_loader_new(imd->read_ahead_path);
354
355         image_loader_set_error_func(imd->read_ahead_il, image_read_ahead_error_cb, imd);
356         if (!image_loader_start(imd->read_ahead_il, image_read_ahead_done_cb, imd))
357                 {
358                 image_read_ahead_cancel(imd);
359                 image_complete_util(imd, TRUE);
360                 }
361 }
362
363 static void image_read_ahead_set(ImageWindow *imd, const gchar *path)
364 {
365         if (imd->read_ahead_path && path && strcmp(imd->read_ahead_path, path) == 0) return;
366
367         image_read_ahead_cancel(imd);
368
369         imd->read_ahead_path = g_strdup(path);
370
371         if (debug) printf("read ahead set to :%s\n", imd->read_ahead_path);
372
373         image_read_ahead_start(imd);
374 }
375
376 /*
377  *-------------------------------------------------------------------
378  * post buffering
379  *-------------------------------------------------------------------
380  */
381
382 static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf *pixbuf)
383 {
384         g_free(imd->prev_path);
385         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
386
387         if (path && pixbuf)
388                 {
389                 imd->prev_path = g_strdup(path);
390                         
391                 g_object_ref(pixbuf);
392                 imd->prev_pixbuf = pixbuf;
393                 }
394         else
395                 {
396                 imd->prev_path = NULL;
397                 imd->prev_pixbuf = NULL;
398                 }
399
400         if (debug) printf("post buffer set: %s\n", path);
401 }
402
403 static gint image_post_buffer_get(ImageWindow *imd)
404 {
405         gint success;
406
407         if (imd->prev_pixbuf &&
408             imd->image_path && imd->prev_path && strcmp(imd->image_path, imd->prev_path) == 0)
409                 {
410                 image_change_pixbuf(imd, imd->prev_pixbuf, image_zoom_get(imd));
411                 success = TRUE;
412                 }
413         else
414                 {
415                 success = FALSE;
416                 }
417
418         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
419         imd->prev_pixbuf = NULL;
420
421         g_free(imd->prev_path);
422         imd->prev_path = NULL;
423
424         return success;
425 }
426
427 /*
428  *-------------------------------------------------------------------
429  * loading
430  *-------------------------------------------------------------------
431  */
432
433 static void image_load_pixbuf_ready(ImageWindow *imd)
434 {
435         if (image_get_pixbuf(imd) || !imd->il) return;
436
437         image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
438 }
439
440 static void image_load_area_cb(ImageLoader *il, guint x, guint y, guint w, guint h, gpointer data)
441 {
442         ImageWindow *imd = data;
443         PixbufRenderer *pr;
444
445         pr = (PixbufRenderer *)imd->pr;
446
447         if (imd->delay_flip &&
448             pr->pixbuf != image_loader_get_pixbuf(il))
449                 {
450                 return;
451                 }
452
453         if (!pr->pixbuf) image_load_pixbuf_ready(imd);
454
455         pixbuf_renderer_area_changed(pr, x, y, w, h);
456 }
457
458 static void image_load_done_cb(ImageLoader *il, gpointer data)
459 {
460         ImageWindow *imd = data;
461
462         if (debug) printf ("image done\n");
463
464         g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
465
466         if (imd->delay_flip &&
467             image_get_pixbuf(imd) != image_loader_get_pixbuf(imd->il))
468                 {
469                 g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
470                 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
471                 }
472
473         image_loader_free(imd->il);
474         imd->il = NULL;
475
476         image_post_process(imd, TRUE);
477
478         image_read_ahead_start(imd);
479 }
480
481 static void image_load_error_cb(ImageLoader *il, gpointer data)
482 {
483         if (debug) printf ("image error\n");
484
485         /* even on error handle it like it was done,
486          * since we have a pixbuf with _something_ */
487
488         image_load_done_cb(il, data);
489 }
490
491 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
492 static void image_load_buffer_throttle(ImageLoader *il)
493 {
494         if (!il || il->bytes_total < IMAGE_THROTTLE_THRESHOLD) return;
495
496         /* Larger image files usually have larger chunks of data per pixel...
497          * So increase the buffer read size so that the rendering chunks called
498          * are also larger.
499          */
500
501         image_loader_set_buffer_size(il, IMAGE_LOAD_BUFFER_COUNT * IMAGE_THROTTLE_FACTOR);
502 }
503 #endif
504
505 /* this read ahead is located here merely for the callbacks, above */
506
507 static gint image_read_ahead_check(ImageWindow *imd)
508 {
509         if (!imd->read_ahead_path) return FALSE;
510         if (imd->il) return FALSE;
511
512         if (!imd->image_path || strcmp(imd->read_ahead_path, imd->image_path) != 0)
513                 {
514                 image_read_ahead_cancel(imd);
515                 return FALSE;
516                 }
517
518         if (imd->read_ahead_il)
519                 {
520                 imd->il = imd->read_ahead_il;
521                 imd->read_ahead_il = NULL;
522
523                 /* override the old signals */
524                 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
525                 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
526                 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
527
528 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
529                 image_load_buffer_throttle(imd->il);
530 #endif
531
532                 /* do this one directly (probably should add a set func) */
533                 imd->il->func_done = image_load_done_cb;
534
535                 g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
536
537                 if (!imd->delay_flip)
538                         {
539                         image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
540                         }
541
542                 image_read_ahead_cancel(imd);
543                 return TRUE;
544                 }
545         else if (imd->read_ahead_pixbuf)
546                 {
547                 image_change_pixbuf(imd, imd->read_ahead_pixbuf, image_zoom_get(imd));
548                 g_object_unref(imd->read_ahead_pixbuf);
549                 imd->read_ahead_pixbuf = NULL;
550
551                 image_read_ahead_cancel(imd);
552
553                 image_post_process(imd, FALSE);
554                 return TRUE;
555                 }
556
557         image_read_ahead_cancel(imd);
558         return FALSE;
559 }
560
561 static gint image_load_begin(ImageWindow *imd, const gchar *path)
562 {
563         if (debug) printf ("image begin \n");
564
565         if (imd->il) return FALSE;
566
567         imd->completed = FALSE;
568         g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
569
570         if (image_post_buffer_get(imd))
571                 {
572                 if (debug) printf("from post buffer: %s\n", imd->image_path);
573                 return TRUE;
574                 }
575
576         if (image_read_ahead_check(imd))
577                 {
578                 if (debug) printf("from read ahead buffer: %s\n", imd->image_path);
579                 return TRUE;
580                 }
581
582         if (!imd->delay_flip && image_get_pixbuf(imd))
583                 {
584                 PixbufRenderer *pr;
585
586                 pr = PIXBUF_RENDERER(imd->pr);
587                 if (pr->pixbuf) g_object_unref(pr->pixbuf);
588                 pr->pixbuf = NULL;
589                 }
590
591         g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
592
593         imd->il = image_loader_new(path);
594
595         image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
596         image_loader_set_error_func(imd->il, image_load_error_cb, imd);
597         image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
598
599         if (!image_loader_start(imd->il, image_load_done_cb, imd))
600                 {
601                 if (debug) printf("image start error\n");
602
603                 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
604
605                 image_loader_free(imd->il);
606                 imd->il = NULL;
607
608                 image_complete_util(imd, FALSE);
609
610                 return FALSE;
611                 }
612
613 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
614         image_load_buffer_throttle(imd->il);
615 #endif
616
617         if (!imd->delay_flip && !image_get_pixbuf(imd)) image_load_pixbuf_ready(imd);
618
619         return TRUE;
620 }
621
622 static void image_reset(ImageWindow *imd)
623 {
624         /* stops anything currently being done */
625
626         if (debug) printf("image reset\n");
627
628         g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
629
630         image_loader_free(imd->il);
631         imd->il = NULL;
632
633         imd->delay_alter_type = ALTER_NONE;
634 }
635
636 /*
637  *-------------------------------------------------------------------
638  * image changer
639  *-------------------------------------------------------------------
640  */
641
642 static void image_change_complete(ImageWindow *imd, gdouble zoom, gint new)
643 {
644         image_reset(imd);
645
646         if (imd->image_path && isfile(imd->image_path))
647                 {
648                 PixbufRenderer *pr;
649
650                 pr = PIXBUF_RENDERER(imd->pr);
651                 pr->zoom = zoom;        /* store the zoom, needed by the loader */
652
653                 if (image_load_begin(imd, imd->image_path))
654                         {
655                         imd->unknown = FALSE;
656                         }
657                 else
658                         {
659                         GdkPixbuf *pixbuf;
660
661                         pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
662                         image_change_pixbuf(imd, pixbuf, zoom);
663                         g_object_unref(pixbuf);
664
665                         imd->unknown = TRUE;
666                         }
667                 imd->size = filesize(imd->image_path);
668                 imd->mtime = filetime(imd->image_path);
669                 }
670         else
671                 {
672                 if (imd->image_path)
673                         {
674                         GdkPixbuf *pixbuf;
675
676                         pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
677                         image_change_pixbuf(imd, pixbuf, zoom);
678                         g_object_unref(pixbuf);
679                         imd->mtime = filetime(imd->image_path);
680                         }
681                 else
682                         {
683                         image_change_pixbuf(imd, NULL, zoom);
684                         imd->mtime = 0;
685                         }
686                 imd->unknown = TRUE;
687                 imd->size = 0;
688                 }
689
690         image_update_util(imd);
691 }
692
693 static void image_change_real(ImageWindow *imd, const gchar *path,
694                               CollectionData *cd, CollectInfo *info, gdouble zoom)
695 {
696         GdkPixbuf *pixbuf;
697         GdkPixbuf *prev_pixbuf = NULL;
698         gchar *prev_path = NULL;
699         gint prev_clear = FALSE;
700
701         imd->collection = cd;
702         imd->collection_info = info;
703
704         pixbuf = image_get_pixbuf(imd);
705
706         if (enable_read_ahead && imd->image_path && pixbuf)
707                 {
708                 if (imd->il)
709                         {
710                         /* current image is not finished */
711                         prev_clear = TRUE;
712                         }
713                 else
714                         {
715                         prev_path = g_strdup(imd->image_path);
716                         prev_pixbuf = pixbuf;
717                         g_object_ref(prev_pixbuf);
718                         }
719                 }
720
721         g_free(imd->image_path);
722         imd->image_path = g_strdup(path);
723         imd->image_name = filename_from_path(imd->image_path);
724
725         image_change_complete(imd, zoom, TRUE);
726
727         if (prev_pixbuf)
728                 {
729                 image_post_buffer_set(imd, prev_path, prev_pixbuf);
730                 g_free(prev_path);
731                 g_object_unref(prev_pixbuf);
732                 }
733         else if (prev_clear)
734                 {
735                 image_post_buffer_set(imd, NULL, NULL);
736                 }
737
738         image_update_title(imd);
739         image_new_util(imd);
740 }
741
742 /*
743  *-------------------------------------------------------------------
744  * focus stuff
745  *-------------------------------------------------------------------
746  */
747
748 static void image_focus_paint(ImageWindow *imd, gint has_focus, GdkRectangle *area)
749 {
750         GtkWidget *widget;
751
752         widget = imd->widget;
753         if (!widget->window) return;
754
755         if (has_focus)
756                 {
757                 gtk_paint_focus (widget->style, widget->window, GTK_STATE_ACTIVE,
758                                  area, widget, "image_window",
759                                  widget->allocation.x, widget->allocation.y,
760                                  widget->allocation.width - 1, widget->allocation.height - 1);  
761                 }
762         else
763                 {
764                 gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
765                                   area, widget, "image_window",
766                                   widget->allocation.x, widget->allocation.y,
767                                   widget->allocation.width - 1, widget->allocation.height - 1);
768                 }
769 }
770
771 static gint image_focus_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
772 {
773         ImageWindow *imd = data;
774
775         image_focus_paint(imd, GTK_WIDGET_HAS_FOCUS(widget), &event->area);
776         return TRUE;
777 }
778
779 static gint image_focus_in_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
780 {
781         ImageWindow *imd = data;
782
783         GTK_WIDGET_SET_FLAGS(imd->widget, GTK_HAS_FOCUS);
784         image_focus_paint(imd, TRUE, NULL);
785
786         return TRUE;
787 }
788
789 static gint image_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
790 {
791         ImageWindow *imd = data;
792
793         GTK_WIDGET_UNSET_FLAGS(imd->widget, GTK_HAS_FOCUS);
794         image_focus_paint(imd, FALSE, NULL);
795
796         return TRUE;
797 }
798
799 gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
800                        gint relative, gint always)
801 {
802         return pixbuf_renderer_overlay_add((PixbufRenderer *)imd->pr, pixbuf, x, y, relative, always);
803 }
804
805 void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
806 {
807         pixbuf_renderer_overlay_set((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
808 }
809
810 gint image_overlay_get(ImageWindow *imd, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
811 {
812         return pixbuf_renderer_overlay_get((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
813 }
814
815 void image_overlay_remove(ImageWindow *imd, gint id)
816 {
817         pixbuf_renderer_overlay_remove((PixbufRenderer *)imd->pr, id);
818 }
819
820 static gint image_scroll_cb(GtkWidget *widget, GdkEventScroll *event, gpointer data)
821 {
822         ImageWindow *imd = data;
823
824         if (imd->func_scroll &&
825             event && event->type == GDK_SCROLL)
826                 {
827                 imd->func_scroll(imd, event->direction, event->time,
828                                  event->x, event->y, event->state, imd->data_scroll);
829                 return TRUE;
830                 }
831
832         return FALSE;
833 }
834
835 /*
836  *-------------------------------------------------------------------
837  * public interface
838  *-------------------------------------------------------------------
839  */
840
841 void image_attach_window(ImageWindow *imd, GtkWidget *window,
842                          const gchar *title, const gchar *title_right, gint show_zoom)
843 {
844         imd->top_window = window;
845         g_free(imd->title);
846         imd->title = g_strdup(title);
847         g_free(imd->title_right);
848         imd->title_right = g_strdup(title_right);
849         imd->title_show_zoom = show_zoom;
850
851         if (!fit_window) window = NULL;
852
853         pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)window);
854
855         image_update_title(imd);
856 }
857
858 void image_set_update_func(ImageWindow *imd,
859                            void (*func)(ImageWindow *imd, gpointer data),
860                            gpointer data)
861 {
862         imd->func_update = func;
863         imd->data_update = data;
864 }
865
866 void image_set_complete_func(ImageWindow *imd,
867                              void (*func)(ImageWindow *, gint preload, gpointer),
868                              gpointer data)
869 {
870         imd->func_complete = func;
871         imd->data_complete = data;
872 }
873
874 void image_set_new_func(ImageWindow *imd,
875                         void (*func)(ImageWindow *, gpointer),
876                         gpointer data)
877 {
878         imd->func_new = func;
879         imd->data_new = data;
880 }
881
882
883 void image_set_button_func(ImageWindow *imd,
884                            void (*func)(ImageWindow *, gint button, guint32 time, gdouble x, gdouble y, guint state, gpointer),
885                            gpointer data)
886 {
887         imd->func_button = func;
888         imd->data_button = data;
889 }
890
891 void image_set_scroll_func(ImageWindow *imd,
892                            void (*func)(ImageWindow *, GdkScrollDirection direction, guint32 time, gdouble x, gdouble y, guint state, gpointer),
893                            gpointer data)
894 {
895         imd->func_scroll = func;
896         imd->data_scroll = data;
897 }
898
899 void image_set_scroll_notify_func(ImageWindow *imd,
900                                   void (*func)(ImageWindow *imd, gint x, gint y, gint width, gint height, gpointer data),
901                                   gpointer data)
902 {
903         imd->func_scroll_notify = func;
904         imd->data_scroll_notify = data;
905 }
906
907 /* path, name */
908
909 const gchar *image_get_path(ImageWindow *imd)
910 {
911         return imd->image_path;
912 }
913
914 const gchar *image_get_name(ImageWindow *imd)
915 {
916         return imd->image_name;
917 }
918
919 /* merely changes path string, does not change the image! */
920 void image_set_path(ImageWindow *imd, const gchar *newpath)
921 {
922         g_free(imd->image_path);
923         imd->image_path = g_strdup(newpath);
924         imd->image_name = filename_from_path(imd->image_path);
925
926         image_update_title(imd);
927         image_new_util(imd);
928 }
929
930 /* load a new image */
931
932 void image_change_path(ImageWindow *imd, const gchar *path, gdouble zoom)
933 {
934         if (imd->image_path == path ||
935             (path && imd->image_path && !strcmp(path, imd->image_path)) ) return;
936
937         image_change_real(imd, path, NULL, NULL, zoom);
938 }
939
940 GdkPixbuf *image_get_pixbuf(ImageWindow *imd)
941 {
942         return pixbuf_renderer_get_pixbuf((PixbufRenderer *)imd->pr);
943 }
944
945 void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom)
946 {
947         pixbuf_renderer_set_pixbuf((PixbufRenderer *)imd->pr, pixbuf, zoom);
948         image_new_util(imd);
949 }
950
951 void image_change_from_collection(ImageWindow *imd, CollectionData *cd, CollectInfo *info, gdouble zoom)
952 {
953         if (!cd || !info || !g_list_find(cd->list, info)) return;
954
955         image_change_real(imd, info->path, cd, info, zoom);
956 }
957
958 CollectionData *image_get_collection(ImageWindow *imd, CollectInfo **info)
959 {
960         if (collection_to_number(imd->collection) >= 0)
961                 {
962                 if (g_list_find(imd->collection->list, imd->collection_info) != NULL)
963                         {
964                         if (info) *info = imd->collection_info;
965                         }
966                 else
967                         {
968                         if (info) *info = NULL;
969                         }
970                 return imd->collection;
971                 }
972
973         if (info) *info = NULL;
974         return NULL;
975 }
976
977 static void image_loader_sync_data(ImageLoader *il, gpointer data)
978 {
979         /* change data for the callbacks directly */
980
981         il->data_area_ready = data;
982         il->data_error = data;
983         il->data_done = data;
984         il->data_percent = data;
985 }
986
987 /* this is more like a move function
988  * it moves most data from source to imd
989  */
990 void image_change_from_image(ImageWindow *imd, ImageWindow *source)
991 {
992         if (imd == source) return;
993
994         imd->unknown = source->unknown;
995
996         imd->collection = source->collection;
997         imd->collection_info = source->collection_info;
998         imd->size = source->size;
999         imd->mtime = source->mtime;
1000
1001         image_set_path(imd, image_get_path(source));
1002
1003         image_loader_free(imd->il);
1004         imd->il = NULL;
1005
1006         if (source->il)
1007                 {
1008                 imd->il = source->il;
1009                 source->il = NULL;
1010
1011                 image_loader_sync_data(imd->il, imd);
1012
1013                 imd->delay_alter_type = source->delay_alter_type;
1014                 source->delay_alter_type = ALTER_NONE;
1015                 }
1016
1017         image_loader_free(imd->read_ahead_il);
1018         imd->read_ahead_il = source->read_ahead_il;
1019         source->read_ahead_il = NULL;
1020         if (imd->read_ahead_il) image_loader_sync_data(imd->read_ahead_il, imd);
1021
1022         if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
1023         imd->read_ahead_pixbuf = source->read_ahead_pixbuf;
1024         source->read_ahead_pixbuf = NULL;
1025
1026         g_free(imd->read_ahead_path);
1027         imd->read_ahead_path = source->read_ahead_path;
1028         source->read_ahead_path = NULL;
1029
1030         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
1031         imd->prev_pixbuf = source->prev_pixbuf;
1032         source->prev_pixbuf = NULL;
1033
1034         g_free(imd->prev_path);
1035         imd->prev_path = source->prev_path;
1036         source->prev_path = NULL;
1037
1038         imd->completed = source->completed;
1039
1040         pixbuf_renderer_move(PIXBUF_RENDERER(imd->pr), PIXBUF_RENDERER(source->pr));
1041 }
1042
1043 /* manipulation */
1044
1045 void image_area_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
1046 {
1047         pixbuf_renderer_area_changed((PixbufRenderer *)imd->pr, x, y, width, height);
1048 }
1049
1050 void image_reload(ImageWindow *imd)
1051 {
1052         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1053
1054         image_change_complete(imd, image_zoom_get(imd), FALSE);
1055 }
1056
1057 void image_scroll(ImageWindow *imd, gint x, gint y)
1058 {
1059         pixbuf_renderer_scroll((PixbufRenderer *)imd->pr, x, y);
1060 }
1061
1062 void image_scroll_to_point(ImageWindow *imd, gint x, gint y,
1063                            gdouble x_align, gdouble y_align)
1064 {
1065         pixbuf_renderer_scroll_to_point((PixbufRenderer *)imd->pr, x, y, x_align, y_align);
1066 }
1067
1068 void image_alter(ImageWindow *imd, AlterType type)
1069 {
1070         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1071
1072         if (imd->il)
1073                 {
1074                 /* still loading, wait till done */
1075                 imd->delay_alter_type = type;
1076                 return;
1077                 }
1078
1079         image_alter_real(imd, type, TRUE);
1080 }
1081
1082 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
1083 {
1084         pixbuf_renderer_zoom_adjust((PixbufRenderer *)imd->pr, increment);
1085 }
1086
1087 void image_zoom_adjust_at_point(ImageWindow *imd, gdouble increment, gint x, gint y)
1088 {
1089         pixbuf_renderer_zoom_adjust_at_point((PixbufRenderer *)imd->pr, increment, x, y);
1090 }
1091
1092 void image_zoom_set_limits(ImageWindow *imd, gdouble min, gdouble max)
1093 {
1094         pixbuf_renderer_zoom_set_limits((PixbufRenderer *)imd->pr, min, max);
1095 }
1096
1097 void image_zoom_set(ImageWindow *imd, gdouble zoom)
1098 {
1099         pixbuf_renderer_zoom_set((PixbufRenderer *)imd->pr, zoom);
1100 }
1101
1102 void image_zoom_set_fill_geometry(ImageWindow *imd, gint vertical)
1103 {
1104         PixbufRenderer *pr;
1105         gdouble zoom;
1106         gint width, height;
1107
1108         pr = (PixbufRenderer *)imd->pr;
1109
1110         if (!pixbuf_renderer_get_pixbuf(pr) ||
1111             !pixbuf_renderer_get_image_size(pr, &width, &height)) return;
1112
1113         if (vertical)
1114                 {
1115                 zoom = (gdouble)pr->window_height / height;
1116                 }
1117         else
1118                 {
1119                 zoom = (gdouble)pr->window_width / width;
1120                 }
1121
1122         if (zoom < 1.0)
1123                 {
1124                 zoom = 0.0 - 1.0 / zoom;
1125                 }
1126
1127         pixbuf_renderer_zoom_set(pr, zoom);
1128 }
1129
1130 gdouble image_zoom_get(ImageWindow *imd)
1131 {
1132         return pixbuf_renderer_zoom_get((PixbufRenderer *)imd->pr);
1133 }
1134
1135 gdouble image_zoom_get_real(ImageWindow *imd)
1136 {
1137         return pixbuf_renderer_zoom_get_scale((PixbufRenderer *)imd->pr);
1138 }
1139
1140 gchar *image_zoom_get_as_text(ImageWindow *imd)
1141 {
1142         gdouble zoom;
1143         gdouble scale;
1144         gdouble l = 1.0;
1145         gdouble r = 1.0;
1146         gint pl = 0;
1147         gint pr = 0;
1148         gchar *approx = " ";
1149
1150         zoom = image_zoom_get(imd);
1151         scale = image_zoom_get_real(imd);
1152
1153         if (zoom > 0.0)
1154                 {
1155                 l = zoom;
1156                 }
1157         else if (zoom < 0.0)
1158                 {
1159                 r = 0.0 - zoom;
1160                 }
1161         else if (zoom == 0.0 && scale != 0.0)
1162                 {
1163                 if (scale >= 1.0)
1164                         {
1165                         l = scale;
1166                         }
1167                 else
1168                         {
1169                         r = 1.0 / scale;
1170                         }
1171                 approx = " ~";
1172                 }
1173
1174         if (rint(l) != l) pl = 1;
1175         if (rint(r) != r) pr = 1;
1176
1177         return g_strdup_printf("%.*f :%s%.*f", pl, l, approx, pr, r);
1178 }
1179
1180 gdouble image_zoom_get_default(ImageWindow *imd, gint mode)
1181 {
1182         gdouble zoom;
1183
1184         if (mode == ZOOM_RESET_ORIGINAL)
1185                 {
1186                 zoom = 1.0;
1187                 }
1188         else if (mode == ZOOM_RESET_FIT_WINDOW)
1189                 {
1190                 zoom = 0.0;
1191                 }
1192         else
1193                 {
1194                 if (imd)
1195                         {
1196                         zoom = image_zoom_get(imd);
1197                         }
1198                 else
1199                         {
1200                         zoom = 1.0;
1201                         }
1202                 }
1203
1204         return zoom;
1205 }
1206
1207 /* read ahead */
1208
1209 void image_prebuffer_set(ImageWindow *imd, const gchar *path)
1210 {
1211         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1212
1213         if (path)
1214                 {
1215                 image_read_ahead_set(imd, path);
1216                 }
1217         else
1218                 {
1219                 image_read_ahead_cancel(imd);
1220                 }
1221 }
1222
1223 static gint image_auto_refresh_cb(gpointer data)
1224 {
1225         ImageWindow *imd = data;
1226         time_t newtime;
1227         
1228         if (!imd || !image_get_pixbuf(imd) ||
1229             imd->il || !imd->image_path ||
1230             !update_on_time_change) return TRUE;
1231
1232         newtime = filetime(imd->image_path);
1233         if (newtime > 0 && newtime != imd->mtime)
1234                 {
1235                 imd->mtime = newtime;
1236                 image_reload(imd);
1237                 }
1238
1239         return TRUE;
1240 }
1241
1242 /* image auto refresh on time stamp change, in 1/1000's second, -1 disables */
1243
1244 void image_auto_refresh(ImageWindow *imd, gint interval)
1245 {
1246         if (!imd) return;
1247         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1248
1249         if (imd->auto_refresh_id > -1)
1250                 {
1251                 g_source_remove(imd->auto_refresh_id);
1252                 imd->auto_refresh_id = -1;
1253                 imd->auto_refresh_interval = -1;
1254                 }
1255
1256         if (interval < 0) return;
1257
1258         if (interval == 0) interval = IMAGE_AUTO_REFRESH_TIME;
1259
1260         imd->auto_refresh_id = g_timeout_add((guint32)interval, image_auto_refresh_cb, imd);
1261         imd->auto_refresh_interval = interval;
1262 }
1263
1264 void image_top_window_set_sync(ImageWindow *imd, gint allow_sync)
1265 {
1266         imd->top_window_sync = allow_sync;
1267
1268         g_object_set(G_OBJECT(imd->pr), "window_fit", allow_sync, NULL);
1269 }
1270
1271 void image_background_set_black(ImageWindow *imd, gint black)
1272 {
1273         pixbuf_renderer_set_black((PixbufRenderer *)imd->pr, black);
1274 }
1275
1276 void image_background_set_color(ImageWindow *imd, GdkColor *color)
1277 {
1278         pixbuf_renderer_set_color((PixbufRenderer *)imd->pr, color);
1279 }
1280
1281 void image_set_delay_flip(ImageWindow *imd, gint delay)
1282 {
1283         if (!imd ||
1284             imd->delay_flip == delay) return;
1285
1286         imd->delay_flip = delay;
1287
1288         g_object_set(G_OBJECT(imd->pr), "delay_flip", delay, NULL);
1289
1290         if (!imd->delay_flip && imd->il)
1291                 {
1292                 PixbufRenderer *pr;
1293
1294                 pr = PIXBUF_RENDERER(imd->pr);
1295                 if (pr->pixbuf) g_object_unref(pr->pixbuf);
1296                 pr->pixbuf = NULL;
1297
1298                 image_load_pixbuf_ready(imd);
1299                 }
1300 }
1301
1302 void image_to_root_window(ImageWindow *imd, gint scaled)
1303 {
1304         GdkScreen *screen;
1305         GdkWindow *rootwindow;
1306         GdkPixmap *pixmap;
1307         GdkPixbuf *pixbuf;
1308         GdkPixbuf *pb;
1309         gint width, height;
1310
1311         if (!imd) return;
1312
1313         pixbuf = image_get_pixbuf(imd);
1314         if (!pixbuf) return;
1315
1316         screen = gtk_widget_get_screen(imd->widget);
1317         rootwindow = gdk_screen_get_root_window(screen);
1318         if (gdk_drawable_get_visual(rootwindow) != gdk_visual_get_system()) return;
1319
1320         if (scaled)
1321                 {
1322                 width = gdk_screen_width();
1323                 height = gdk_screen_height();
1324                 }
1325         else
1326                 {
1327                 pixbuf_renderer_get_scaled_size((PixbufRenderer *)imd->pr, &width, &height);
1328                 }
1329
1330         pb = gdk_pixbuf_scale_simple(pixbuf, width, height, (GdkInterpType)zoom_quality);
1331
1332         gdk_pixbuf_render_pixmap_and_mask (pb, &pixmap, NULL, 128);
1333         gdk_window_set_back_pixmap(rootwindow, pixmap, FALSE);
1334         gdk_window_clear(rootwindow);
1335         g_object_unref(pb);
1336         g_object_unref(pixmap);
1337
1338         gdk_flush();
1339 }
1340
1341 /*
1342  *-------------------------------------------------------------------
1343  * prefs sync
1344  *-------------------------------------------------------------------
1345  */
1346
1347 static void image_options_set(ImageWindow *imd)
1348 {
1349         g_object_set(G_OBJECT(imd->pr), "zoom_quality", zoom_quality,
1350                                         "zoom_2pass", two_pass_zoom,
1351                                         "zoom_expand", zoom_to_fit_expands,
1352                                         "dither_quality", dither_quality,
1353                                         "scroll_reset", scroll_reset_method,
1354                                         "cache_display", tile_cache_max,
1355                                         "window_fit", (imd->top_window_sync && fit_window),
1356                                         "window_limit", limit_window_size,
1357                                         "window_limit_value", max_window_size,
1358                                         NULL);
1359
1360         pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)imd->top_window);
1361 }
1362
1363 void image_options_sync(void)
1364 {
1365         GList *work;
1366
1367         work = image_list;
1368         while (work)
1369                 {
1370                 ImageWindow *imd;
1371
1372                 imd = work->data;
1373                 work = work->next;
1374
1375                 image_options_set(imd);
1376                 }
1377 }
1378
1379 /*
1380  *-------------------------------------------------------------------
1381  * init / destroy
1382  *-------------------------------------------------------------------
1383  */
1384
1385 static void image_free(ImageWindow *imd)
1386 {
1387         image_list = g_list_remove(image_list, imd);
1388
1389         image_reset(imd);
1390
1391         image_read_ahead_cancel(imd);
1392         image_post_buffer_set(imd, NULL, NULL);
1393         image_auto_refresh(imd, -1);
1394
1395         g_free(imd->image_path);
1396         g_free(imd->title);
1397         g_free(imd->title_right);
1398
1399         g_free(imd);
1400 }
1401
1402 static void image_destroy_cb(GtkObject *widget, gpointer data)
1403 {
1404         ImageWindow *imd = data;
1405         image_free(imd);
1406 }
1407
1408 ImageWindow *image_new(gint frame)
1409 {
1410         ImageWindow *imd;
1411
1412         imd = g_new0(ImageWindow, 1);
1413
1414         imd->top_window = NULL;
1415         imd->title = NULL;
1416         imd->title_right = NULL;
1417         imd->title_show_zoom = FALSE;
1418
1419         imd->unknown = TRUE;
1420
1421         imd->has_frame = frame;
1422         imd->top_window_sync = FALSE;
1423
1424         imd->delay_alter_type = ALTER_NONE;
1425
1426         imd->read_ahead_il = NULL;
1427         imd->read_ahead_pixbuf = NULL;
1428         imd->read_ahead_path = NULL;
1429
1430         imd->completed = FALSE;
1431
1432         imd->auto_refresh_id = -1;
1433         imd->auto_refresh_interval = -1;
1434
1435         imd->delay_flip = FALSE;
1436
1437         imd->func_update = NULL;
1438         imd->func_complete = NULL;
1439         imd->func_tile_request = NULL;
1440         imd->func_tile_dispose = NULL;
1441
1442         imd->func_button = NULL;
1443         imd->func_scroll = NULL;
1444
1445         imd->pr = GTK_WIDGET(pixbuf_renderer_new());
1446
1447         image_options_set(imd);
1448
1449         if (imd->has_frame)
1450                 {
1451                 imd->widget = gtk_frame_new(NULL);
1452                 gtk_frame_set_shadow_type(GTK_FRAME(imd->widget), GTK_SHADOW_IN);
1453                 gtk_container_add(GTK_CONTAINER(imd->widget), imd->pr);
1454                 gtk_widget_show(imd->pr);
1455
1456                 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_CAN_FOCUS);
1457                 g_signal_connect(G_OBJECT(imd->widget), "focus_in_event",
1458                                  G_CALLBACK(image_focus_in_cb), imd);
1459                 g_signal_connect(G_OBJECT(imd->widget), "focus_out_event",
1460                                  G_CALLBACK(image_focus_out_cb), imd);
1461
1462                 g_signal_connect_after(G_OBJECT(imd->widget), "expose_event",
1463                                        G_CALLBACK(image_focus_expose), imd);
1464                 }
1465         else
1466                 {
1467                 imd->widget = imd->pr;
1468                 }
1469
1470         g_signal_connect(G_OBJECT(imd->pr), "clicked",
1471                          G_CALLBACK(image_click_cb), imd);
1472         g_signal_connect(G_OBJECT(imd->pr), "scroll_notify",
1473                          G_CALLBACK(image_scroll_notify_cb), imd);
1474
1475         g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
1476                          G_CALLBACK(image_scroll_cb), imd);
1477
1478         g_signal_connect(G_OBJECT(imd->pr), "destroy",
1479                          G_CALLBACK(image_destroy_cb), imd);
1480
1481         g_signal_connect(G_OBJECT(imd->pr), "zoom",
1482                          G_CALLBACK(image_zoom_cb), imd);
1483         g_signal_connect(G_OBJECT(imd->pr), "render_complete",
1484                          G_CALLBACK(image_render_complete_cb), imd);
1485
1486         image_list = g_list_append(image_list, imd);
1487
1488         return imd;
1489 }
1490