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