##### Note: GQview CVS on sourceforge is not always up to date, please use #####
[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 #if 0
951 static void image_loader_sync_data(ImageLoader *il, gpointer data)
952 {
953         /* change data for the callbacks directly */
954
955         il->data_area_ready = data;
956         il->data_error = data;
957         il->data_done = data;
958         il->data_percent = data;
959 }
960 #endif
961
962 /* this is more like a move function
963  * it moves most data from source to imd, source does keep a ref on the pixbuf
964  */
965 void image_change_from_image(ImageWindow *imd, ImageWindow *source)
966 {
967         if (imd == source) return;
968
969         printf("FIXME: enable set from image\n");
970 #if 0
971         imd->zoom_min = source->zoom_min;
972         imd->zoom_max = source->zoom_max;
973
974         imd->unknown = source->unknown;
975
976         image_set_pixbuf(imd, source->pixbuf, image_zoom_get(source), TRUE);
977
978         imd->collection = source->collection;
979         imd->collection_info = source->collection_info;
980         imd->size = source->size;
981         imd->mtime = source->mtime;
982
983         image_set_path(imd, image_get_path(source));
984
985         image_loader_free(imd->il);
986         imd->il = NULL;
987
988         if (imd->pixbuf && source->il)
989                 {
990                 imd->il = source->il;
991                 source->il = NULL;
992
993                 image_loader_sync_data(imd->il, imd);
994
995                 imd->delay_alter_type = source->delay_alter_type;
996                 source->delay_alter_type = ALTER_NONE;
997                 }
998
999         image_loader_free(imd->read_ahead_il);
1000         imd->read_ahead_il = source->read_ahead_il;
1001         source->read_ahead_il = NULL;
1002         if (imd->read_ahead_il) image_loader_sync_data(imd->read_ahead_il, imd);
1003
1004         if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
1005         imd->read_ahead_pixbuf = source->read_ahead_pixbuf;
1006         source->read_ahead_pixbuf = NULL;
1007
1008         g_free(imd->read_ahead_path);
1009         imd->read_ahead_path = source->read_ahead_path;
1010         source->read_ahead_path = NULL;
1011
1012         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
1013         imd->prev_pixbuf = source->prev_pixbuf;
1014         source->prev_pixbuf = NULL;
1015
1016         g_free(imd->prev_path);
1017         imd->prev_path = source->prev_path;
1018         source->prev_path = NULL;
1019
1020         imd->completed = source->completed;
1021
1022         imd->x_scroll = source->x_scroll;
1023         imd->y_scroll = source->y_scroll;
1024
1025         if (imd->source_tiles_enabled)
1026                 {
1027                 image_source_tile_unset(imd);
1028                 }
1029
1030         if (source->source_tiles_enabled)
1031                 {
1032                 imd->source_tiles_enabled = source->source_tiles_enabled;
1033                 imd->source_tiles_cache_size = source->source_tiles_cache_size;
1034                 imd->source_tiles = source->source_tiles;
1035                 imd->source_tile_width = source->source_tile_width;
1036                 imd->source_tile_height = source->source_tile_height;
1037
1038                 source->source_tiles_enabled = FALSE;
1039                 source->source_tiles = NULL;
1040
1041                 imd->func_tile_request = source->func_tile_request;
1042                 imd->func_tile_dispose = source->func_tile_dispose;
1043                 imd->data_tile = source->data_tile;
1044
1045                 source->func_tile_request = NULL;
1046                 source->func_tile_dispose = NULL;
1047                 source->data_tile = NULL;
1048
1049                 imd->image_width = source->image_width;
1050                 imd->image_height = source->image_height;
1051
1052                 if (image_zoom_clamp(imd, source->zoom, TRUE, TRUE))
1053                         {
1054                         image_size_clamp(imd);
1055                         image_scroll_clamp(imd);
1056                         image_tile_sync(imd, imd->width, imd->height, FALSE);
1057                         image_redraw(imd, FALSE);
1058                         }
1059                 return;
1060                 }
1061
1062         image_scroll_clamp(imd);
1063 #endif
1064 }
1065
1066 /* manipulation */
1067
1068 void image_area_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
1069 {
1070         pixbuf_renderer_area_changed((PixbufRenderer *)imd->pr, x, y, width, height);
1071 }
1072
1073 void image_reload(ImageWindow *imd)
1074 {
1075         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1076
1077         image_change_complete(imd, image_zoom_get(imd), FALSE);
1078 }
1079
1080 void image_scroll(ImageWindow *imd, gint x, gint y)
1081 {
1082         pixbuf_renderer_scroll((PixbufRenderer *)imd->pr, x, y);
1083 }
1084
1085 void image_scroll_to_point(ImageWindow *imd, gint x, gint y,
1086                            gdouble x_align, gdouble y_align)
1087 {
1088         pixbuf_renderer_scroll_to_point((PixbufRenderer *)imd->pr, x, y, x_align, y_align);
1089 }
1090
1091 void image_alter(ImageWindow *imd, AlterType type)
1092 {
1093         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1094
1095         if (imd->il)
1096                 {
1097                 /* still loading, wait till done */
1098                 imd->delay_alter_type = type;
1099                 return;
1100                 }
1101
1102         image_alter_real(imd, type, TRUE);
1103 }
1104
1105 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
1106 {
1107         pixbuf_renderer_zoom_adjust((PixbufRenderer *)imd->pr, increment);
1108 }
1109
1110 void image_zoom_adjust_at_point(ImageWindow *imd, gdouble increment, gint x, gint y)
1111 {
1112         pixbuf_renderer_zoom_adjust_at_point((PixbufRenderer *)imd->pr, increment, x, y);
1113 }
1114
1115 void image_zoom_set_limits(ImageWindow *imd, gdouble min, gdouble max)
1116 {
1117         pixbuf_renderer_zoom_set_limits((PixbufRenderer *)imd->pr, min, max);
1118 }
1119
1120 void image_zoom_set(ImageWindow *imd, gdouble zoom)
1121 {
1122         pixbuf_renderer_zoom_set((PixbufRenderer *)imd->pr, zoom);
1123 }
1124
1125 void image_zoom_set_fill_geometry(ImageWindow *imd, gint vertical)
1126 {
1127         PixbufRenderer *pr;
1128         gdouble zoom;
1129         gint width, height;
1130
1131         pr = (PixbufRenderer *)imd->pr;
1132
1133         if (!pixbuf_renderer_get_pixbuf(pr) ||
1134             !pixbuf_renderer_get_image_size(pr, &width, &height)) return;
1135
1136         if (vertical)
1137                 {
1138                 zoom = (gdouble)pr->window_height / height;
1139                 }
1140         else
1141                 {
1142                 zoom = (gdouble)pr->window_width / width;
1143                 }
1144
1145         if (zoom < 1.0)
1146                 {
1147                 zoom = 0.0 - 1.0 / zoom;
1148                 }
1149
1150         pixbuf_renderer_zoom_set(pr, zoom);
1151 }
1152
1153 gdouble image_zoom_get(ImageWindow *imd)
1154 {
1155         return pixbuf_renderer_zoom_get((PixbufRenderer *)imd->pr);
1156 }
1157
1158 gdouble image_zoom_get_real(ImageWindow *imd)
1159 {
1160         return pixbuf_renderer_zoom_get_scale((PixbufRenderer *)imd->pr);
1161 }
1162
1163 gchar *image_zoom_get_as_text(ImageWindow *imd)
1164 {
1165         gdouble zoom;
1166         gdouble scale;
1167         gdouble l = 1.0;
1168         gdouble r = 1.0;
1169         gint pl = 0;
1170         gint pr = 0;
1171         gchar *approx = " ";
1172
1173         zoom = image_zoom_get(imd);
1174         scale = image_zoom_get_real(imd);
1175
1176         if (zoom > 0.0)
1177                 {
1178                 l = zoom;
1179                 }
1180         else if (zoom < 0.0)
1181                 {
1182                 r = 0.0 - zoom;
1183                 }
1184         else if (zoom == 0.0 && scale != 0.0)
1185                 {
1186                 if (scale >= 1.0)
1187                         {
1188                         l = scale;
1189                         }
1190                 else
1191                         {
1192                         r = 1.0 / scale;
1193                         }
1194                 approx = " ~";
1195                 }
1196
1197         if (rint(l) != l) pl = 1;
1198         if (rint(r) != r) pr = 1;
1199
1200         return g_strdup_printf("%.*f :%s%.*f", pl, l, approx, pr, r);
1201 }
1202
1203 gdouble image_zoom_get_default(ImageWindow *imd, gint mode)
1204 {
1205         gdouble zoom;
1206
1207         if (mode == ZOOM_RESET_ORIGINAL)
1208                 {
1209                 zoom = 1.0;
1210                 }
1211         else if (mode == ZOOM_RESET_FIT_WINDOW)
1212                 {
1213                 zoom = 0.0;
1214                 }
1215         else
1216                 {
1217                 if (imd)
1218                         {
1219                         zoom = image_zoom_get(imd);
1220                         }
1221                 else
1222                         {
1223                         zoom = 1.0;
1224                         }
1225                 }
1226
1227         return zoom;
1228 }
1229
1230 /* read ahead */
1231
1232 void image_prebuffer_set(ImageWindow *imd, const gchar *path)
1233 {
1234         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1235
1236         if (path)
1237                 {
1238                 image_read_ahead_set(imd, path);
1239                 }
1240         else
1241                 {
1242                 image_read_ahead_cancel(imd);
1243                 }
1244 }
1245
1246 static gint image_auto_refresh_cb(gpointer data)
1247 {
1248         ImageWindow *imd = data;
1249         time_t newtime;
1250         
1251         if (!imd || !image_get_pixbuf(imd) ||
1252             imd->il || !imd->image_path ||
1253             !update_on_time_change) return TRUE;
1254
1255         newtime = filetime(imd->image_path);
1256         if (newtime > 0 && newtime != imd->mtime)
1257                 {
1258                 imd->mtime = newtime;
1259                 image_reload(imd);
1260                 }
1261
1262         return TRUE;
1263 }
1264
1265 /* image auto refresh on time stamp change, in 1/1000's second, -1 disables */
1266
1267 void image_auto_refresh(ImageWindow *imd, gint interval)
1268 {
1269         if (!imd) return;
1270         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1271
1272         if (imd->auto_refresh_id > -1)
1273                 {
1274                 g_source_remove(imd->auto_refresh_id);
1275                 imd->auto_refresh_id = -1;
1276                 imd->auto_refresh_interval = -1;
1277                 }
1278
1279         if (interval < 0) return;
1280
1281         if (interval == 0) interval = IMAGE_AUTO_REFRESH_TIME;
1282
1283         imd->auto_refresh_id = g_timeout_add((guint32)interval, image_auto_refresh_cb, imd);
1284         imd->auto_refresh_interval = interval;
1285 }
1286
1287 void image_top_window_set_sync(ImageWindow *imd, gint allow_sync)
1288 {
1289         imd->top_window_sync = allow_sync;
1290
1291         g_object_set(G_OBJECT(imd->pr), "window_fit", allow_sync, NULL);
1292 }
1293
1294 void image_background_set_black(ImageWindow *imd, gint black)
1295 {
1296         pixbuf_renderer_set_black((PixbufRenderer *)imd->pr, black);
1297 }
1298
1299 void image_background_set_color(ImageWindow *imd, GdkColor *color)
1300 {
1301         pixbuf_renderer_set_color((PixbufRenderer *)imd->pr, color);
1302 }
1303
1304 void image_set_delay_flip(ImageWindow *imd, gint delay)
1305 {
1306         if (!imd ||
1307             imd->delay_flip == delay) return;
1308
1309         imd->delay_flip = delay;
1310
1311         g_object_set(G_OBJECT(imd->pr), "delay_flip", delay, NULL);
1312
1313         if (!imd->delay_flip && imd->il)
1314                 {
1315                 PixbufRenderer *pr;
1316
1317                 pr = PIXBUF_RENDERER(imd->pr);
1318                 if (pr->pixbuf) g_object_unref(pr->pixbuf);
1319                 pr->pixbuf = NULL;
1320
1321                 image_load_pixbuf_ready(imd);
1322                 }
1323 }
1324
1325 void image_to_root_window(ImageWindow *imd, gint scaled)
1326 {
1327         GdkScreen *screen;
1328         GdkWindow *rootwindow;
1329         GdkPixmap *pixmap;
1330         GdkPixbuf *pixbuf;
1331         GdkPixbuf *pb;
1332         gint width, height;
1333
1334         if (!imd) return;
1335
1336         pixbuf = image_get_pixbuf(imd);
1337         if (!pixbuf) return;
1338
1339         screen = gtk_widget_get_screen(imd->widget);
1340         rootwindow = gdk_screen_get_root_window(screen);
1341         if (gdk_drawable_get_visual(rootwindow) != gdk_visual_get_system()) return;
1342
1343         if (scaled)
1344                 {
1345                 width = gdk_screen_width();
1346                 height = gdk_screen_height();
1347                 }
1348         else
1349                 {
1350                 pixbuf_renderer_get_scaled_size((PixbufRenderer *)imd->pr, &width, &height);
1351                 }
1352
1353         pb = gdk_pixbuf_scale_simple(pixbuf, width, height, (GdkInterpType)zoom_quality);
1354
1355         gdk_pixbuf_render_pixmap_and_mask (pb, &pixmap, NULL, 128);
1356         gdk_window_set_back_pixmap(rootwindow, pixmap, FALSE);
1357         gdk_window_clear(rootwindow);
1358         g_object_unref(pb);
1359         g_object_unref(pixmap);
1360
1361         gdk_flush();
1362 }
1363
1364 /*
1365  *-------------------------------------------------------------------
1366  * init / destroy
1367  *-------------------------------------------------------------------
1368  */
1369
1370 static void image_free(ImageWindow *imd)
1371 {
1372         image_reset(imd);
1373
1374         image_read_ahead_cancel(imd);
1375         image_post_buffer_set(imd, NULL, NULL);
1376         image_auto_refresh(imd, -1);
1377
1378         g_free(imd->image_path);
1379         g_free(imd->title);
1380         g_free(imd->title_right);
1381
1382         g_free(imd);
1383 }
1384
1385 static void image_destroy_cb(GtkObject *widget, gpointer data)
1386 {
1387         ImageWindow *imd = data;
1388         image_free(imd);
1389 }
1390
1391 ImageWindow *image_new(gint frame)
1392 {
1393         ImageWindow *imd;
1394
1395         imd = g_new0(ImageWindow, 1);
1396
1397         imd->top_window = NULL;
1398         imd->title = NULL;
1399         imd->title_right = NULL;
1400         imd->title_show_zoom = FALSE;
1401
1402         imd->unknown = TRUE;
1403
1404         imd->has_frame = frame;
1405         imd->top_window_sync = FALSE;
1406
1407         imd->delay_alter_type = ALTER_NONE;
1408
1409         imd->read_ahead_il = NULL;
1410         imd->read_ahead_pixbuf = NULL;
1411         imd->read_ahead_path = NULL;
1412
1413         imd->completed = FALSE;
1414
1415         imd->auto_refresh_id = -1;
1416         imd->auto_refresh_interval = -1;
1417
1418         imd->delay_flip = FALSE;
1419
1420         imd->func_update = NULL;
1421         imd->func_complete = NULL;
1422         imd->func_tile_request = NULL;
1423         imd->func_tile_dispose = NULL;
1424
1425         imd->func_button = NULL;
1426         imd->func_scroll = NULL;
1427
1428         imd->pr = GTK_WIDGET(pixbuf_renderer_new());
1429
1430         g_object_set(G_OBJECT(imd->pr), "zoom_2pass", TRUE, NULL);
1431
1432         if (imd->has_frame)
1433                 {
1434                 imd->widget = gtk_frame_new(NULL);
1435                 gtk_frame_set_shadow_type(GTK_FRAME(imd->widget), GTK_SHADOW_IN);
1436                 gtk_container_add(GTK_CONTAINER(imd->widget), imd->pr);
1437                 gtk_widget_show(imd->pr);
1438
1439                 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_CAN_FOCUS);
1440                 g_signal_connect(G_OBJECT(imd->widget), "focus_in_event",
1441                                  G_CALLBACK(image_focus_in_cb), imd);
1442                 g_signal_connect(G_OBJECT(imd->widget), "focus_out_event",
1443                                  G_CALLBACK(image_focus_out_cb), imd);
1444
1445                 g_signal_connect_after(G_OBJECT(imd->widget), "expose_event",
1446                                        G_CALLBACK(image_focus_expose), imd);
1447                 }
1448         else
1449                 {
1450                 imd->widget = imd->pr;
1451                 }
1452
1453         g_signal_connect(G_OBJECT(imd->pr), "clicked",
1454                          G_CALLBACK(image_click_cb), imd);
1455         g_signal_connect(G_OBJECT(imd->pr), "scroll_notify",
1456                          G_CALLBACK(image_scroll_notify_cb), imd);
1457
1458         g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
1459                          G_CALLBACK(image_scroll_cb), imd);
1460
1461         g_signal_connect(G_OBJECT(imd->pr), "destroy",
1462                          G_CALLBACK(image_destroy_cb), imd);
1463
1464         return imd;
1465 }
1466