Sat May 14 13:04:23 2005 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 /*
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                 g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
462                 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
463                 }
464
465         image_loader_free(imd->il);
466         imd->il = NULL;
467
468         image_post_process(imd, TRUE);
469
470         image_read_ahead_start(imd);
471 }
472
473 static void image_load_error_cb(ImageLoader *il, gpointer data)
474 {
475         if (debug) printf ("image error\n");
476
477         /* even on error handle it like it was done,
478          * since we have a pixbuf with _something_ */
479
480         image_load_done_cb(il, data);
481 }
482
483 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
484 static void image_load_buffer_throttle(ImageLoader *il)
485 {
486         if (!il || il->bytes_total < IMAGE_THROTTLE_THRESHOLD) return;
487
488         /* Larger image files usually have larger chunks of data per pixel...
489          * So increase the buffer read size so that the rendering chunks called
490          * are also larger.
491          */
492
493         image_loader_set_buffer_size(il, IMAGE_LOAD_BUFFER_COUNT * IMAGE_THROTTLE_FACTOR);
494 }
495 #endif
496
497 /* this read ahead is located here merely for the callbacks, above */
498
499 static gint image_read_ahead_check(ImageWindow *imd)
500 {
501         if (!imd->read_ahead_path) return FALSE;
502         if (imd->il) return FALSE;
503
504         if (!imd->image_path || strcmp(imd->read_ahead_path, imd->image_path) != 0)
505                 {
506                 image_read_ahead_cancel(imd);
507                 return FALSE;
508                 }
509
510         if (imd->read_ahead_il)
511                 {
512                 imd->il = imd->read_ahead_il;
513                 imd->read_ahead_il = NULL;
514
515                 /* override the old signals */
516                 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
517                 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
518                 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
519
520 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
521                 image_load_buffer_throttle(imd->il);
522 #endif
523
524                 /* do this one directly (probably should add a set func) */
525                 imd->il->func_done = image_load_done_cb;
526
527                 g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
528
529                 if (!imd->delay_flip)
530                         {
531                         image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
532                         }
533
534                 image_read_ahead_cancel(imd);
535                 return TRUE;
536                 }
537         else if (imd->read_ahead_pixbuf)
538                 {
539                 image_change_pixbuf(imd, imd->read_ahead_pixbuf, image_zoom_get(imd));
540                 g_object_unref(imd->read_ahead_pixbuf);
541                 imd->read_ahead_pixbuf = NULL;
542
543                 image_read_ahead_cancel(imd);
544
545                 image_post_process(imd, FALSE);
546                 return TRUE;
547                 }
548
549         image_read_ahead_cancel(imd);
550         return FALSE;
551 }
552
553 static gint image_load_begin(ImageWindow *imd, const gchar *path)
554 {
555         if (debug) printf ("image begin \n");
556
557         if (imd->il) return FALSE;
558
559         imd->completed = FALSE;
560         g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
561
562         if (image_post_buffer_get(imd))
563                 {
564                 if (debug) printf("from post buffer: %s\n", imd->image_path);
565                 return TRUE;
566                 }
567
568         if (image_read_ahead_check(imd))
569                 {
570                 if (debug) printf("from read ahead buffer: %s\n", imd->image_path);
571                 return TRUE;
572                 }
573
574         if (!imd->delay_flip && image_get_pixbuf(imd))
575                 {
576                 PixbufRenderer *pr;
577
578                 pr = PIXBUF_RENDERER(imd->pr);
579                 if (pr->pixbuf) g_object_unref(pr->pixbuf);
580                 pr->pixbuf = NULL;
581                 }
582
583         g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
584
585         imd->il = image_loader_new(path);
586
587         image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
588         image_loader_set_error_func(imd->il, image_load_error_cb, imd);
589         image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
590
591         if (!image_loader_start(imd->il, image_load_done_cb, imd))
592                 {
593                 if (debug) printf("image start error\n");
594
595                 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
596
597                 image_loader_free(imd->il);
598                 imd->il = NULL;
599
600                 image_complete_util(imd, FALSE);
601
602                 return FALSE;
603                 }
604
605 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
606         image_load_buffer_throttle(imd->il);
607 #endif
608
609         if (!imd->delay_flip && !image_get_pixbuf(imd)) image_load_pixbuf_ready(imd);
610
611         return TRUE;
612 }
613
614 static void image_reset(ImageWindow *imd)
615 {
616         /* stops anything currently being done */
617
618         if (debug) printf("image reset\n");
619
620         g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
621
622         image_loader_free(imd->il);
623         imd->il = NULL;
624
625         imd->delay_alter_type = ALTER_NONE;
626 }
627
628 /*
629  *-------------------------------------------------------------------
630  * image changer
631  *-------------------------------------------------------------------
632  */
633
634 static void image_change_complete(ImageWindow *imd, gdouble zoom, gint new)
635 {
636         image_reset(imd);
637
638         if (imd->image_path && isfile(imd->image_path))
639                 {
640                 PixbufRenderer *pr;
641
642                 pr = PIXBUF_RENDERER(imd->pr);
643                 pr->zoom = zoom;        /* store the zoom, needed by the loader */
644
645                 if (image_load_begin(imd, imd->image_path))
646                         {
647                         imd->unknown = FALSE;
648                         }
649                 else
650                         {
651                         GdkPixbuf *pixbuf;
652
653                         pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
654                         image_change_pixbuf(imd, pixbuf, zoom);
655                         g_object_unref(pixbuf);
656
657                         imd->unknown = TRUE;
658                         }
659                 imd->size = filesize(imd->image_path);
660                 imd->mtime = filetime(imd->image_path);
661                 }
662         else
663                 {
664                 if (imd->image_path)
665                         {
666                         GdkPixbuf *pixbuf;
667
668                         pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
669                         image_change_pixbuf(imd, pixbuf, zoom);
670                         g_object_unref(pixbuf);
671                         imd->mtime = filetime(imd->image_path);
672                         }
673                 else
674                         {
675                         image_change_pixbuf(imd, NULL, zoom);
676                         imd->mtime = 0;
677                         }
678                 imd->unknown = TRUE;
679                 imd->size = 0;
680                 }
681
682         image_update_util(imd);
683 }
684
685 static void image_change_real(ImageWindow *imd, const gchar *path,
686                               CollectionData *cd, CollectInfo *info, gdouble zoom)
687 {
688         GdkPixbuf *pixbuf;
689         GdkPixbuf *prev_pixbuf = NULL;
690         gchar *prev_path = NULL;
691         gint prev_clear = FALSE;
692
693         imd->collection = cd;
694         imd->collection_info = info;
695
696         pixbuf = image_get_pixbuf(imd);
697
698         if (enable_read_ahead && imd->image_path && pixbuf)
699                 {
700                 if (imd->il)
701                         {
702                         /* current image is not finished */
703                         prev_clear = TRUE;
704                         }
705                 else
706                         {
707                         prev_path = g_strdup(imd->image_path);
708                         prev_pixbuf = pixbuf;
709                         g_object_ref(prev_pixbuf);
710                         }
711                 }
712
713         g_free(imd->image_path);
714         imd->image_path = g_strdup(path);
715         imd->image_name = filename_from_path(imd->image_path);
716
717         image_change_complete(imd, zoom, TRUE);
718
719         if (prev_pixbuf)
720                 {
721                 image_post_buffer_set(imd, prev_path, prev_pixbuf);
722                 g_free(prev_path);
723                 g_object_unref(prev_pixbuf);
724                 }
725         else if (prev_clear)
726                 {
727                 image_post_buffer_set(imd, NULL, NULL);
728                 }
729
730         image_update_title(imd);
731         image_new_util(imd);
732 }
733
734 /*
735  *-------------------------------------------------------------------
736  * focus stuff
737  *-------------------------------------------------------------------
738  */
739
740 static void image_focus_paint(ImageWindow *imd, gint has_focus, GdkRectangle *area)
741 {
742         GtkWidget *widget;
743
744         widget = imd->widget;
745         if (!widget->window) return;
746
747         if (has_focus)
748                 {
749                 gtk_paint_focus (widget->style, widget->window, GTK_STATE_ACTIVE,
750                                  area, widget, "image_window",
751                                  widget->allocation.x, widget->allocation.y,
752                                  widget->allocation.width - 1, widget->allocation.height - 1);  
753                 }
754         else
755                 {
756                 gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
757                                   area, widget, "image_window",
758                                   widget->allocation.x, widget->allocation.y,
759                                   widget->allocation.width - 1, widget->allocation.height - 1);
760                 }
761 }
762
763 static gint image_focus_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
764 {
765         ImageWindow *imd = data;
766
767         image_focus_paint(imd, GTK_WIDGET_HAS_FOCUS(widget), &event->area);
768         return TRUE;
769 }
770
771 static gint image_focus_in_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
772 {
773         ImageWindow *imd = data;
774
775         GTK_WIDGET_SET_FLAGS(imd->widget, GTK_HAS_FOCUS);
776         image_focus_paint(imd, TRUE, NULL);
777
778         return TRUE;
779 }
780
781 static gint image_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
782 {
783         ImageWindow *imd = data;
784
785         GTK_WIDGET_UNSET_FLAGS(imd->widget, GTK_HAS_FOCUS);
786         image_focus_paint(imd, FALSE, NULL);
787
788         return TRUE;
789 }
790
791 gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
792                        gint relative, gint always)
793 {
794         return pixbuf_renderer_overlay_add((PixbufRenderer *)imd->pr, pixbuf, x, y, relative, always);
795 }
796
797 void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
798 {
799         pixbuf_renderer_overlay_set((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
800 }
801
802 gint image_overlay_get(ImageWindow *imd, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
803 {
804         return pixbuf_renderer_overlay_get((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
805 }
806
807 void image_overlay_remove(ImageWindow *imd, gint id)
808 {
809         pixbuf_renderer_overlay_remove((PixbufRenderer *)imd->pr, id);
810 }
811
812 static gint image_scroll_cb(GtkWidget *widget, GdkEventScroll *event, gpointer data)
813 {
814         ImageWindow *imd = data;
815
816         if (imd->func_scroll &&
817             event && event->type == GDK_SCROLL)
818                 {
819                 imd->func_scroll(imd, event->direction, event->time,
820                                  event->x, event->y, event->state, imd->data_scroll);
821                 return TRUE;
822                 }
823
824         return FALSE;
825 }
826
827 /*
828  *-------------------------------------------------------------------
829  * public interface
830  *-------------------------------------------------------------------
831  */
832
833 void image_attach_window(ImageWindow *imd, GtkWidget *window,
834                          const gchar *title, const gchar *title_right, gint show_zoom)
835 {
836         imd->top_window = window;
837         g_free(imd->title);
838         imd->title = g_strdup(title);
839         g_free(imd->title_right);
840         imd->title_right = g_strdup(title_right);
841         imd->title_show_zoom = show_zoom;
842
843         if (!fit_window) window = NULL;
844
845         pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)window);
846
847         image_update_title(imd);
848 }
849
850 void image_set_update_func(ImageWindow *imd,
851                            void (*func)(ImageWindow *imd, gpointer data),
852                            gpointer data)
853 {
854         imd->func_update = func;
855         imd->data_update = data;
856 }
857
858 void image_set_complete_func(ImageWindow *imd,
859                              void (*func)(ImageWindow *, gint preload, gpointer),
860                              gpointer data)
861 {
862         imd->func_complete = func;
863         imd->data_complete = data;
864 }
865
866 void image_set_new_func(ImageWindow *imd,
867                         void (*func)(ImageWindow *, gpointer),
868                         gpointer data)
869 {
870         imd->func_new = func;
871         imd->data_new = data;
872 }
873
874
875 void image_set_button_func(ImageWindow *imd,
876                            void (*func)(ImageWindow *, gint button, guint32 time, gdouble x, gdouble y, guint state, gpointer),
877                            gpointer data)
878 {
879         imd->func_button = func;
880         imd->data_button = data;
881 }
882
883 void image_set_scroll_func(ImageWindow *imd,
884                            void (*func)(ImageWindow *, GdkScrollDirection direction, guint32 time, gdouble x, gdouble y, guint state, gpointer),
885                            gpointer data)
886 {
887         imd->func_scroll = func;
888         imd->data_scroll = data;
889 }
890
891 void image_set_scroll_notify_func(ImageWindow *imd,
892                                   void (*func)(ImageWindow *imd, gint x, gint y, gint width, gint height, gpointer data),
893                                   gpointer data)
894 {
895         imd->func_scroll_notify = func;
896         imd->data_scroll_notify = data;
897 }
898
899 /* path, name */
900
901 const gchar *image_get_path(ImageWindow *imd)
902 {
903         return imd->image_path;
904 }
905
906 const gchar *image_get_name(ImageWindow *imd)
907 {
908         return imd->image_name;
909 }
910
911 /* merely changes path string, does not change the image! */
912 void image_set_path(ImageWindow *imd, const gchar *newpath)
913 {
914         g_free(imd->image_path);
915         imd->image_path = g_strdup(newpath);
916         imd->image_name = filename_from_path(imd->image_path);
917
918         image_update_title(imd);
919         image_new_util(imd);
920 }
921
922 /* load a new image */
923
924 void image_change_path(ImageWindow *imd, const gchar *path, gdouble zoom)
925 {
926         if (imd->image_path == path ||
927             (path && imd->image_path && !strcmp(path, imd->image_path)) ) return;
928
929         image_change_real(imd, path, NULL, NULL, zoom);
930 }
931
932 GdkPixbuf *image_get_pixbuf(ImageWindow *imd)
933 {
934         return pixbuf_renderer_get_pixbuf((PixbufRenderer *)imd->pr);
935 }
936
937 void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom)
938 {
939         pixbuf_renderer_set_pixbuf((PixbufRenderer *)imd->pr, pixbuf, zoom);
940         image_new_util(imd);
941 }
942
943 void image_change_from_collection(ImageWindow *imd, CollectionData *cd, CollectInfo *info, gdouble zoom)
944 {
945         if (!cd || !info || !g_list_find(cd->list, info)) return;
946
947         image_change_real(imd, info->path, cd, info, zoom);
948 }
949
950 CollectionData *image_get_collection(ImageWindow *imd, CollectInfo **info)
951 {
952         if (collection_to_number(imd->collection) >= 0)
953                 {
954                 if (g_list_find(imd->collection->list, imd->collection_info) != NULL)
955                         {
956                         if (info) *info = imd->collection_info;
957                         }
958                 else
959                         {
960                         if (info) *info = NULL;
961                         }
962                 return imd->collection;
963                 }
964
965         if (info) *info = NULL;
966         return NULL;
967 }
968
969 static void image_loader_sync_data(ImageLoader *il, gpointer data)
970 {
971         /* change data for the callbacks directly */
972
973         il->data_area_ready = data;
974         il->data_error = data;
975         il->data_done = data;
976         il->data_percent = data;
977 }
978
979 /* this is more like a move function
980  * it moves most data from source to imd
981  */
982 void image_change_from_image(ImageWindow *imd, ImageWindow *source)
983 {
984         if (imd == source) return;
985
986         imd->unknown = source->unknown;
987
988         imd->collection = source->collection;
989         imd->collection_info = source->collection_info;
990         imd->size = source->size;
991         imd->mtime = source->mtime;
992
993         image_set_path(imd, image_get_path(source));
994
995         image_loader_free(imd->il);
996         imd->il = NULL;
997
998         if (source->il)
999                 {
1000                 imd->il = source->il;
1001                 source->il = NULL;
1002
1003                 image_loader_sync_data(imd->il, imd);
1004
1005                 imd->delay_alter_type = source->delay_alter_type;
1006                 source->delay_alter_type = ALTER_NONE;
1007                 }
1008
1009         image_loader_free(imd->read_ahead_il);
1010         imd->read_ahead_il = source->read_ahead_il;
1011         source->read_ahead_il = NULL;
1012         if (imd->read_ahead_il) image_loader_sync_data(imd->read_ahead_il, imd);
1013
1014         if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
1015         imd->read_ahead_pixbuf = source->read_ahead_pixbuf;
1016         source->read_ahead_pixbuf = NULL;
1017
1018         g_free(imd->read_ahead_path);
1019         imd->read_ahead_path = source->read_ahead_path;
1020         source->read_ahead_path = NULL;
1021
1022         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
1023         imd->prev_pixbuf = source->prev_pixbuf;
1024         source->prev_pixbuf = NULL;
1025
1026         g_free(imd->prev_path);
1027         imd->prev_path = source->prev_path;
1028         source->prev_path = NULL;
1029
1030         imd->completed = source->completed;
1031
1032         pixbuf_renderer_move(PIXBUF_RENDERER(imd->pr), PIXBUF_RENDERER(source->pr));
1033 }
1034
1035 /* manipulation */
1036
1037 void image_area_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
1038 {
1039         pixbuf_renderer_area_changed((PixbufRenderer *)imd->pr, x, y, width, height);
1040 }
1041
1042 void image_reload(ImageWindow *imd)
1043 {
1044         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1045
1046         image_change_complete(imd, image_zoom_get(imd), FALSE);
1047 }
1048
1049 void image_scroll(ImageWindow *imd, gint x, gint y)
1050 {
1051         pixbuf_renderer_scroll((PixbufRenderer *)imd->pr, x, y);
1052 }
1053
1054 void image_scroll_to_point(ImageWindow *imd, gint x, gint y,
1055                            gdouble x_align, gdouble y_align)
1056 {
1057         pixbuf_renderer_scroll_to_point((PixbufRenderer *)imd->pr, x, y, x_align, y_align);
1058 }
1059
1060 void image_alter(ImageWindow *imd, AlterType type)
1061 {
1062         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1063
1064         if (imd->il)
1065                 {
1066                 /* still loading, wait till done */
1067                 imd->delay_alter_type = type;
1068                 return;
1069                 }
1070
1071         image_alter_real(imd, type, TRUE);
1072 }
1073
1074 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
1075 {
1076         pixbuf_renderer_zoom_adjust((PixbufRenderer *)imd->pr, increment);
1077 }
1078
1079 void image_zoom_adjust_at_point(ImageWindow *imd, gdouble increment, gint x, gint y)
1080 {
1081         pixbuf_renderer_zoom_adjust_at_point((PixbufRenderer *)imd->pr, increment, x, y);
1082 }
1083
1084 void image_zoom_set_limits(ImageWindow *imd, gdouble min, gdouble max)
1085 {
1086         pixbuf_renderer_zoom_set_limits((PixbufRenderer *)imd->pr, min, max);
1087 }
1088
1089 void image_zoom_set(ImageWindow *imd, gdouble zoom)
1090 {
1091         pixbuf_renderer_zoom_set((PixbufRenderer *)imd->pr, zoom);
1092 }
1093
1094 void image_zoom_set_fill_geometry(ImageWindow *imd, gint vertical)
1095 {
1096         PixbufRenderer *pr;
1097         gdouble zoom;
1098         gint width, height;
1099
1100         pr = (PixbufRenderer *)imd->pr;
1101
1102         if (!pixbuf_renderer_get_pixbuf(pr) ||
1103             !pixbuf_renderer_get_image_size(pr, &width, &height)) return;
1104
1105         if (vertical)
1106                 {
1107                 zoom = (gdouble)pr->window_height / height;
1108                 }
1109         else
1110                 {
1111                 zoom = (gdouble)pr->window_width / width;
1112                 }
1113
1114         if (zoom < 1.0)
1115                 {
1116                 zoom = 0.0 - 1.0 / zoom;
1117                 }
1118
1119         pixbuf_renderer_zoom_set(pr, zoom);
1120 }
1121
1122 gdouble image_zoom_get(ImageWindow *imd)
1123 {
1124         return pixbuf_renderer_zoom_get((PixbufRenderer *)imd->pr);
1125 }
1126
1127 gdouble image_zoom_get_real(ImageWindow *imd)
1128 {
1129         return pixbuf_renderer_zoom_get_scale((PixbufRenderer *)imd->pr);
1130 }
1131
1132 gchar *image_zoom_get_as_text(ImageWindow *imd)
1133 {
1134         gdouble zoom;
1135         gdouble scale;
1136         gdouble l = 1.0;
1137         gdouble r = 1.0;
1138         gint pl = 0;
1139         gint pr = 0;
1140         gchar *approx = " ";
1141
1142         zoom = image_zoom_get(imd);
1143         scale = image_zoom_get_real(imd);
1144
1145         if (zoom > 0.0)
1146                 {
1147                 l = zoom;
1148                 }
1149         else if (zoom < 0.0)
1150                 {
1151                 r = 0.0 - zoom;
1152                 }
1153         else if (zoom == 0.0 && scale != 0.0)
1154                 {
1155                 if (scale >= 1.0)
1156                         {
1157                         l = scale;
1158                         }
1159                 else
1160                         {
1161                         r = 1.0 / scale;
1162                         }
1163                 approx = " ~";
1164                 }
1165
1166         if (rint(l) != l) pl = 1;
1167         if (rint(r) != r) pr = 1;
1168
1169         return g_strdup_printf("%.*f :%s%.*f", pl, l, approx, pr, r);
1170 }
1171
1172 gdouble image_zoom_get_default(ImageWindow *imd, gint mode)
1173 {
1174         gdouble zoom;
1175
1176         if (mode == ZOOM_RESET_ORIGINAL)
1177                 {
1178                 zoom = 1.0;
1179                 }
1180         else if (mode == ZOOM_RESET_FIT_WINDOW)
1181                 {
1182                 zoom = 0.0;
1183                 }
1184         else
1185                 {
1186                 if (imd)
1187                         {
1188                         zoom = image_zoom_get(imd);
1189                         }
1190                 else
1191                         {
1192                         zoom = 1.0;
1193                         }
1194                 }
1195
1196         return zoom;
1197 }
1198
1199 /* read ahead */
1200
1201 void image_prebuffer_set(ImageWindow *imd, const gchar *path)
1202 {
1203         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1204
1205         if (path)
1206                 {
1207                 image_read_ahead_set(imd, path);
1208                 }
1209         else
1210                 {
1211                 image_read_ahead_cancel(imd);
1212                 }
1213 }
1214
1215 static gint image_auto_refresh_cb(gpointer data)
1216 {
1217         ImageWindow *imd = data;
1218         time_t newtime;
1219         
1220         if (!imd || !image_get_pixbuf(imd) ||
1221             imd->il || !imd->image_path ||
1222             !update_on_time_change) return TRUE;
1223
1224         newtime = filetime(imd->image_path);
1225         if (newtime > 0 && newtime != imd->mtime)
1226                 {
1227                 imd->mtime = newtime;
1228                 image_reload(imd);
1229                 }
1230
1231         return TRUE;
1232 }
1233
1234 /* image auto refresh on time stamp change, in 1/1000's second, -1 disables */
1235
1236 void image_auto_refresh(ImageWindow *imd, gint interval)
1237 {
1238         if (!imd) return;
1239         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1240
1241         if (imd->auto_refresh_id > -1)
1242                 {
1243                 g_source_remove(imd->auto_refresh_id);
1244                 imd->auto_refresh_id = -1;
1245                 imd->auto_refresh_interval = -1;
1246                 }
1247
1248         if (interval < 0) return;
1249
1250         if (interval == 0) interval = IMAGE_AUTO_REFRESH_TIME;
1251
1252         imd->auto_refresh_id = g_timeout_add((guint32)interval, image_auto_refresh_cb, imd);
1253         imd->auto_refresh_interval = interval;
1254 }
1255
1256 void image_top_window_set_sync(ImageWindow *imd, gint allow_sync)
1257 {
1258         imd->top_window_sync = allow_sync;
1259
1260         g_object_set(G_OBJECT(imd->pr), "window_fit", allow_sync, NULL);
1261 }
1262
1263 void image_background_set_black(ImageWindow *imd, gint black)
1264 {
1265         pixbuf_renderer_set_black((PixbufRenderer *)imd->pr, black);
1266 }
1267
1268 void image_background_set_color(ImageWindow *imd, GdkColor *color)
1269 {
1270         pixbuf_renderer_set_color((PixbufRenderer *)imd->pr, color);
1271 }
1272
1273 void image_set_delay_flip(ImageWindow *imd, gint delay)
1274 {
1275         if (!imd ||
1276             imd->delay_flip == delay) return;
1277
1278         imd->delay_flip = delay;
1279
1280         g_object_set(G_OBJECT(imd->pr), "delay_flip", delay, NULL);
1281
1282         if (!imd->delay_flip && imd->il)
1283                 {
1284                 PixbufRenderer *pr;
1285
1286                 pr = PIXBUF_RENDERER(imd->pr);
1287                 if (pr->pixbuf) g_object_unref(pr->pixbuf);
1288                 pr->pixbuf = NULL;
1289
1290                 image_load_pixbuf_ready(imd);
1291                 }
1292 }
1293
1294 void image_to_root_window(ImageWindow *imd, gint scaled)
1295 {
1296         GdkScreen *screen;
1297         GdkWindow *rootwindow;
1298         GdkPixmap *pixmap;
1299         GdkPixbuf *pixbuf;
1300         GdkPixbuf *pb;
1301         gint width, height;
1302
1303         if (!imd) return;
1304
1305         pixbuf = image_get_pixbuf(imd);
1306         if (!pixbuf) return;
1307
1308         screen = gtk_widget_get_screen(imd->widget);
1309         rootwindow = gdk_screen_get_root_window(screen);
1310         if (gdk_drawable_get_visual(rootwindow) != gdk_visual_get_system()) return;
1311
1312         if (scaled)
1313                 {
1314                 width = gdk_screen_width();
1315                 height = gdk_screen_height();
1316                 }
1317         else
1318                 {
1319                 pixbuf_renderer_get_scaled_size((PixbufRenderer *)imd->pr, &width, &height);
1320                 }
1321
1322         pb = gdk_pixbuf_scale_simple(pixbuf, width, height, (GdkInterpType)zoom_quality);
1323
1324         gdk_pixbuf_render_pixmap_and_mask (pb, &pixmap, NULL, 128);
1325         gdk_window_set_back_pixmap(rootwindow, pixmap, FALSE);
1326         gdk_window_clear(rootwindow);
1327         g_object_unref(pb);
1328         g_object_unref(pixmap);
1329
1330         gdk_flush();
1331 }
1332
1333 /*
1334  *-------------------------------------------------------------------
1335  * prefs sync
1336  *-------------------------------------------------------------------
1337  */
1338
1339 static void image_options_set(ImageWindow *imd)
1340 {
1341         g_object_set(G_OBJECT(imd->pr), "zoom_quality", zoom_quality,
1342                                         "zoom_2pass", two_pass_zoom,
1343                                         "zoom_expand", zoom_to_fit_expands,
1344                                         "dither_quality", dither_quality,
1345                                         "scroll_reset", scroll_reset_method,
1346                                         "cache_display", tile_cache_max,
1347                                         "window_fit", (imd->top_window_sync && fit_window),
1348                                         "window_limit", limit_window_size,
1349                                         "window_limit_value", max_window_size,
1350                                         NULL);
1351
1352         pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)imd->top_window);
1353 }
1354
1355 void image_options_sync(void)
1356 {
1357         GList *work;
1358
1359         work = image_list;
1360         while (work)
1361                 {
1362                 ImageWindow *imd;
1363
1364                 imd = work->data;
1365                 work = work->next;
1366
1367                 image_options_set(imd);
1368                 }
1369 }
1370
1371 /*
1372  *-------------------------------------------------------------------
1373  * init / destroy
1374  *-------------------------------------------------------------------
1375  */
1376
1377 static void image_free(ImageWindow *imd)
1378 {
1379         image_list = g_list_remove(image_list, imd);
1380
1381         image_reset(imd);
1382
1383         image_read_ahead_cancel(imd);
1384         image_post_buffer_set(imd, NULL, NULL);
1385         image_auto_refresh(imd, -1);
1386
1387         g_free(imd->image_path);
1388         g_free(imd->title);
1389         g_free(imd->title_right);
1390
1391         g_free(imd);
1392 }
1393
1394 static void image_destroy_cb(GtkObject *widget, gpointer data)
1395 {
1396         ImageWindow *imd = data;
1397         image_free(imd);
1398 }
1399
1400 ImageWindow *image_new(gint frame)
1401 {
1402         ImageWindow *imd;
1403
1404         imd = g_new0(ImageWindow, 1);
1405
1406         imd->top_window = NULL;
1407         imd->title = NULL;
1408         imd->title_right = NULL;
1409         imd->title_show_zoom = FALSE;
1410
1411         imd->unknown = TRUE;
1412
1413         imd->has_frame = frame;
1414         imd->top_window_sync = FALSE;
1415
1416         imd->delay_alter_type = ALTER_NONE;
1417
1418         imd->read_ahead_il = NULL;
1419         imd->read_ahead_pixbuf = NULL;
1420         imd->read_ahead_path = NULL;
1421
1422         imd->completed = FALSE;
1423
1424         imd->auto_refresh_id = -1;
1425         imd->auto_refresh_interval = -1;
1426
1427         imd->delay_flip = FALSE;
1428
1429         imd->func_update = NULL;
1430         imd->func_complete = NULL;
1431         imd->func_tile_request = NULL;
1432         imd->func_tile_dispose = NULL;
1433
1434         imd->func_button = NULL;
1435         imd->func_scroll = NULL;
1436
1437         imd->pr = GTK_WIDGET(pixbuf_renderer_new());
1438
1439         image_options_set(imd);
1440
1441         if (imd->has_frame)
1442                 {
1443                 imd->widget = gtk_frame_new(NULL);
1444                 gtk_frame_set_shadow_type(GTK_FRAME(imd->widget), GTK_SHADOW_IN);
1445                 gtk_container_add(GTK_CONTAINER(imd->widget), imd->pr);
1446                 gtk_widget_show(imd->pr);
1447
1448                 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_CAN_FOCUS);
1449                 g_signal_connect(G_OBJECT(imd->widget), "focus_in_event",
1450                                  G_CALLBACK(image_focus_in_cb), imd);
1451                 g_signal_connect(G_OBJECT(imd->widget), "focus_out_event",
1452                                  G_CALLBACK(image_focus_out_cb), imd);
1453
1454                 g_signal_connect_after(G_OBJECT(imd->widget), "expose_event",
1455                                        G_CALLBACK(image_focus_expose), imd);
1456                 }
1457         else
1458                 {
1459                 imd->widget = imd->pr;
1460                 }
1461
1462         g_signal_connect(G_OBJECT(imd->pr), "clicked",
1463                          G_CALLBACK(image_click_cb), imd);
1464         g_signal_connect(G_OBJECT(imd->pr), "scroll_notify",
1465                          G_CALLBACK(image_scroll_notify_cb), imd);
1466
1467         g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
1468                          G_CALLBACK(image_scroll_cb), imd);
1469
1470         g_signal_connect(G_OBJECT(imd->pr), "destroy",
1471                          G_CALLBACK(image_destroy_cb), imd);
1472
1473         g_signal_connect(G_OBJECT(imd->pr), "zoom",
1474                          G_CALLBACK(image_zoom_cb), imd);
1475         g_signal_connect(G_OBJECT(imd->pr), "render_complete",
1476                          G_CALLBACK(image_render_complete_cb), imd);
1477
1478         image_list = g_list_append(image_list, imd);
1479
1480         return imd;
1481 }
1482