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