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