gqview.h -> main.h
[geeqie.git] / src / image.c
1 /*
2  * Geeqie
3  * (C) 2006 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 "main.h"
14 #include "image.h"
15
16
17 #include "image-load.h"
18 #include "collect.h"
19 #include "color-man.h"
20 #include "exif.h"
21 #include "pixbuf-renderer.h"
22 #include "pixbuf_util.h"
23 #include "ui_fileops.h"
24
25 #include "filelist.h"
26
27 #include <math.h>
28
29
30 /* size of the image loader buffer (512 bytes x defined number) */
31 #define IMAGE_LOAD_BUFFER_COUNT 8
32
33 /* define this so that more bytes are read per idle loop on larger images (> 1MB) */
34 #define IMAGE_THROTTLE_LARGER_IMAGES 1
35
36 /* throttle factor to increase read bytes by (2 is double, 3 is triple, etc.) */
37 #define IMAGE_THROTTLE_FACTOR 4
38
39 /* the file size at which throttling take place */
40 #define IMAGE_THROTTLE_THRESHOLD 1048576
41
42 #define IMAGE_AUTO_REFRESH_TIME 3000
43
44
45 static GList *image_list = NULL;
46
47
48 static void image_update_title(ImageWindow *imd);
49 static void image_post_process(ImageWindow *imd, gint clamp);
50 static void image_read_ahead_start(ImageWindow *imd);
51
52 /*
53  *-------------------------------------------------------------------
54  * 'signals'
55  *-------------------------------------------------------------------
56  */
57
58 static void image_click_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
59 {
60         ImageWindow *imd = data;
61
62         if (imd->func_button)
63                 {
64                 imd->func_button(imd, event->button, event->time,
65                                  event->x, event->y, event->state, imd->data_button);
66                 }
67 }
68
69 static void image_drag_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
70 {
71         ImageWindow *imd = data;
72         gint width, height;
73
74         pixbuf_renderer_get_scaled_size(pr, &width, &height);
75         
76         if (imd->func_drag)
77                 {
78                 imd->func_drag(imd, event->button, event->time,
79                                  event->x, event->y, event->state,
80                                  (gfloat)(pr->drag_last_x - event->x) / width, (gfloat)(pr->drag_last_y - event->y) / height,
81                                  imd->data_button);
82                 }
83 }
84
85 static void image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
86 {
87         ImageWindow *imd = data;
88
89         if (imd->func_scroll_notify && pr->scale)
90                 {
91                 imd->func_scroll_notify(imd,
92                                         (gint)((gdouble)pr->x_scroll / pr->scale),
93                                         (gint)((gdouble)pr->y_scroll / pr->scale),
94                                         (gint)((gdouble)pr->image_width - pr->vis_width / pr->scale),
95                                         (gint)((gdouble)pr->image_height - pr->vis_height / pr->scale),
96                                         imd->data_scroll_notify);
97                 }
98 }
99
100 static void image_update_util(ImageWindow *imd)
101 {
102         if (imd->func_update) imd->func_update(imd, imd->data_update);
103 }
104
105 static void image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
106 {
107         ImageWindow *imd = data;
108
109         if (imd->title_show_zoom) image_update_title(imd);
110         image_update_util(imd);
111 }
112
113 static void image_complete_util(ImageWindow *imd, gint preload)
114 {
115         if (imd->il && image_get_pixbuf(imd) != image_loader_get_pixbuf(imd->il)) return;
116
117         if (debug) printf("image load completed \"%s\" (%s)\n",
118                           (preload) ? (imd->read_ahead_fd ? imd->read_ahead_fd->path : "null") : 
119                                       (imd->image_fd ? imd->image_fd->path : "null"),
120                           (preload) ? "preload" : "current");
121
122         if (!preload) imd->completed = TRUE;
123         if (imd->func_complete) imd->func_complete(imd, preload, imd->data_complete);
124 }
125
126 static void image_render_complete_cb(PixbufRenderer *pr, gpointer data)
127 {
128         ImageWindow *imd = data;
129
130         image_complete_util(imd, FALSE);
131 }
132
133 static void image_state_set(ImageWindow *imd, ImageState state)
134 {
135         if (state == IMAGE_STATE_NONE)
136                 {
137                 imd->state = state;
138                 }
139         else
140                 {
141                 imd->state |= state;
142                 }
143         if (imd->func_state) imd->func_state(imd, state, imd->data_state);
144 }
145
146 static void image_state_unset(ImageWindow *imd, ImageState state)
147 {
148         imd->state &= ~state;
149         if (imd->func_state) imd->func_state(imd, state, imd->data_state);
150 }
151
152 /*
153  *-------------------------------------------------------------------
154  * misc
155  *-------------------------------------------------------------------
156  */
157
158 static void image_update_title(ImageWindow *imd)
159 {
160         gchar *title = NULL;
161         gchar *zoom = NULL;
162         gchar *collection = NULL;
163
164         if (!imd->top_window) return;
165
166         if (imd->collection && collection_to_number(imd->collection) >= 0)
167                 {
168                 const gchar *name;
169                 name = imd->collection->name;
170                 if (!name) name = _("Untitled");
171                 collection = g_strdup_printf(" (Collection %s)", name);
172                 }
173
174         if (imd->title_show_zoom)
175                 {
176                 gchar *buf = image_zoom_get_as_text(imd);
177                 zoom = g_strconcat(" [", buf, "]", NULL);
178                 g_free(buf);
179                 }
180
181         title = g_strdup_printf("%s%s%s%s%s%s",
182                 imd->title ? imd->title : "",
183                 imd->image_fd ? imd->image_fd->name : "",
184                 zoom ? zoom : "",
185                 collection ? collection : "",
186                 imd->image_fd ? " - " : "",
187                 imd->title_right ? imd->title_right : "");
188
189         gtk_window_set_title(GTK_WINDOW(imd->top_window), title);
190
191         g_free(title);
192         g_free(zoom);
193         g_free(collection);
194 }
195
196 /*
197  *-------------------------------------------------------------------
198  * rotation, flip, etc.
199  *-------------------------------------------------------------------
200  */
201
202 static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp)
203 {
204         PixbufRenderer *pr;
205         GdkPixbuf *new = NULL;
206         gint exif_rotate;
207         gint x, y;
208         gint t;
209
210         pr = (PixbufRenderer *)imd->pr;
211
212         exif_rotate = (imd->delay_alter_type != ALTER_NONE && (imd->state & IMAGE_STATE_ROTATE_AUTO));
213         imd->delay_alter_type = ALTER_NONE;
214
215         if (!pr->pixbuf) return;
216
217         x = pr->x_scroll + (pr->vis_width / 2);
218         y = pr->y_scroll + (pr->vis_height / 2);
219
220         switch (type)
221                 {
222                 case ALTER_ROTATE_90:
223                         new = pixbuf_copy_rotate_90(pr->pixbuf, FALSE);
224                         t = x;
225                         x = pr->height - y;
226                         y = t;
227                         break;
228                 case ALTER_ROTATE_90_CC:
229                         new = pixbuf_copy_rotate_90(pr->pixbuf, TRUE);
230                         t = x;
231                         x = y;
232                         y = pr->width - t;
233                         break;
234                 case ALTER_ROTATE_180:
235                         new = pixbuf_copy_mirror(pr->pixbuf, TRUE, TRUE);
236                         x = pr->width - x;
237                         y = pr->height - y;
238                         break;
239                 case ALTER_MIRROR:
240                         new = pixbuf_copy_mirror(pr->pixbuf, TRUE, FALSE);
241                         x = pr->width - x;
242                         break;
243                 case ALTER_FLIP:
244                         new = pixbuf_copy_mirror(pr->pixbuf, FALSE, TRUE);
245                         y = pr->height - y;
246                         break;
247                 case ALTER_DESATURATE:
248                         pixbuf_desaturate_rect(pr->pixbuf,
249                                                0, 0,  pr->image_width, pr->image_height);
250                         image_area_changed(imd, 0, 0, pr->image_width, pr->image_height);
251                         layout_image_overlay_update(layout_find_by_image(imd));
252                         break;
253                 case ALTER_NONE:
254                 default:
255                         return;
256                         break;
257                 }
258
259         if (!new) return;
260
261         pixbuf_renderer_set_pixbuf(pr, new, pr->zoom);
262         g_object_unref(new);
263
264         if (clamp && pr->zoom != 0.0 && pr->scale != 0.0)
265                 {
266                 if (exif_rotate)
267                         {
268                         switch (pr->scroll_reset)
269                                 {
270                                 case PR_SCROLL_RESET_NOCHANGE:
271                                         break;
272                                 case PR_SCROLL_RESET_CENTER:
273                                         x = (gint)((gdouble)pr->image_width / 2.0 * pr->scale);
274                                         y = (gint)((gdouble)pr->image_height / 2.0 * pr->scale);
275                                         break;
276                                 case PR_SCROLL_RESET_TOPLEFT:
277                                 default:
278                                         x = 0;
279                                         y = 0;
280                                         break;
281                                 }
282                         }
283                 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)x / pr->scale),
284                                                     (gint)((gdouble)y / pr->scale),
285                                                     0.50, 0.50);
286                 }
287
288         if (exif_rotate) image_state_set(imd, IMAGE_STATE_ROTATE_AUTO);
289         layout_image_overlay_update(layout_find_by_image(imd));
290 }
291
292 static void image_post_process_alter(ImageWindow *imd, gint clamp)
293 {
294         if (imd->delay_alter_type != ALTER_NONE)
295                 {
296                 image_alter_real(imd, imd->delay_alter_type, clamp);
297                 }
298 }
299
300 static void image_post_process_color_cb(ColorMan *cm, ColorManReturnType type, gpointer data)
301 {
302         ImageWindow *imd = data;
303
304         color_man_free(cm);
305         if (type == COLOR_RETURN_IMAGE_CHANGED)
306                 {
307                 if (cm == imd->cm) imd->cm = NULL;
308                 return;
309                 }
310
311         imd->cm = NULL;
312         image_state_set(imd, IMAGE_STATE_COLOR_ADJ);
313
314         image_post_process_alter(imd, FALSE);
315
316         image_read_ahead_start(imd);
317 }
318
319 static gint image_post_process_color(ImageWindow *imd, gint start_row, ExifData *exif)
320 {
321         ColorMan *cm;
322         ColorManProfileType input_type;
323         ColorManProfileType screen_type;
324         const gchar *input_file;
325         const gchar *screen_file;
326         ExifItem *item = NULL;
327
328         if (imd->cm) return FALSE;
329
330         if (imd->color_profile_input >= 1 &&
331             imd->color_profile_input <= COLOR_PROFILE_INPUTS)
332                 {
333                 gint n;
334
335                 n = imd->color_profile_input - 1;
336                 if (!color_profile_input_file[n]) return FALSE;
337
338                 input_type = COLOR_PROFILE_FILE;
339                 input_file = color_profile_input_file[n];
340                 }
341         else if (imd->color_profile_input == 0)
342                 {
343                 input_type = COLOR_PROFILE_SRGB;
344                 input_file = NULL;
345                 }
346         else
347                 {
348                 return FALSE;
349                 }
350
351         if (imd->color_profile_screen == 1 &&
352             color_profile_screen_file)
353                 {
354                 screen_type = COLOR_PROFILE_FILE;
355                 screen_file = color_profile_screen_file;
356                 }
357         else if (imd->color_profile_screen == 0)
358                 {
359                 screen_type = COLOR_PROFILE_SRGB;
360                 screen_file = NULL;
361                 }
362         else
363                 {
364                 return FALSE;
365                 }
366
367         if (imd->color_profile_use_image && exif)
368                 {
369                 item = exif_get_item(exif, "Exif.Image.InterColorProfile");
370                 if (!item)
371                         {
372                         gint cs;
373
374                         /* ColorSpace == 1 specifies sRGB per EXIF 2.2 */
375                         if (exif_get_integer(exif, "Exif.Photo.ColorSpace", &cs) &&
376                             cs == 1)
377                                 {
378                                 input_type = COLOR_PROFILE_SRGB;
379                                 input_file = NULL;
380
381                                 if (debug) printf("Found EXIF ColorSpace of sRGB\n");
382                                 }
383                         }
384                 }
385
386         if (item && exif_item_get_format_id(item) == EXIF_FORMAT_UNDEFINED)
387                 {
388                 unsigned char *data;
389                 guint data_len;
390                 if (debug) printf("Found embedded color profile\n");
391                 
392                 data = (unsigned char *) exif_item_get_data(item, &data_len);
393
394                 cm = color_man_new_embedded(imd, NULL,
395                                             data, data_len,
396                                             screen_type, screen_file,
397                                             image_post_process_color_cb, imd);
398                 }
399         else 
400                 {
401                 cm = color_man_new(imd, NULL,
402                                    input_type, input_file,
403                                    screen_type, screen_file,
404                                    image_post_process_color_cb, imd);
405                 }
406
407         if (cm)
408                 {
409                 if (start_row > 0)
410                         {
411                         cm->row = start_row;
412                         cm->incremental_sync = TRUE;
413                         }
414
415                 imd->cm = (gpointer)cm;
416                 return TRUE;
417                 }
418
419         return FALSE;
420 }
421
422 static void image_post_process(ImageWindow *imd, gint clamp)
423 {
424         ExifData *exif = NULL;
425
426         if (!image_get_pixbuf(imd)) return;
427
428         if (exif_rotate_enable ||
429             (imd->color_profile_enable && imd->color_profile_use_image) )
430                 {
431                 exif = exif_read_fd(imd->image_fd, (imd->color_profile_enable && imd->color_profile_use_image));
432                 }
433
434         if (exif_rotate_enable && exif)
435                 {
436                 gint orientation;
437
438                 if (exif_get_integer(exif, "Exif.Image.Orientation", &orientation))
439                         {
440                         gint rotate = TRUE;
441
442                         /* see http://jpegclub.org/exif_orientation.html 
443                           1        2       3      4         5            6           7          8
444
445                         888888  888888      88  88      8888888888  88                  88  8888888888
446                         88          88      88  88      88  88      88  88          88  88      88  88
447                         8888      8888    8888  8888    88          8888888888  8888888888          88
448                         88          88      88  88
449                         88          88  888888  888888
450                         */
451                         switch (orientation)
452                                 {
453                                 case EXIF_ORIENTATION_TOP_LEFT:
454                                         /* normal -- nothing to do */
455                                         rotate = FALSE;
456                                         break;
457                                 case EXIF_ORIENTATION_TOP_RIGHT:
458                                         /* mirrored */
459                                         imd->delay_alter_type = ALTER_MIRROR;
460                                         break;
461                                 case EXIF_ORIENTATION_BOTTOM_RIGHT:
462                                         /* upside down */
463                                         imd->delay_alter_type = ALTER_ROTATE_180;
464                                         break;
465                                 case EXIF_ORIENTATION_BOTTOM_LEFT:
466                                         /* flipped */
467                                         imd->delay_alter_type = ALTER_FLIP;
468                                         break;
469                                 case EXIF_ORIENTATION_LEFT_TOP:
470                                         /* not implemented -- too wacky to fix in one step */
471                                         rotate = FALSE;
472                                         break;
473                                 case EXIF_ORIENTATION_RIGHT_TOP:
474                                         /* rotated -90 (270) */
475                                         imd->delay_alter_type = ALTER_ROTATE_90;
476                                         break;
477                                 case EXIF_ORIENTATION_RIGHT_BOTTOM:
478                                         /* not implemented -- too wacky to fix in one step */
479                                         rotate = FALSE;
480                                         break;
481                                 case EXIF_ORIENTATION_LEFT_BOTTOM:
482                                         /* rotated 90 */
483                                         imd->delay_alter_type = ALTER_ROTATE_90_CC;
484                                         break;
485                                 default:
486                                         /* The other values are out of range */
487                                         rotate = FALSE;
488                                         break;
489                                 }
490
491                         if (rotate) image_state_set(imd, IMAGE_STATE_ROTATE_AUTO);
492                         }
493                 }
494
495         if (imd->color_profile_enable)
496                 {
497                 if (!image_post_process_color(imd, 0, exif))
498                         {
499                         /* fixme: note error to user */
500                         image_state_set(imd, IMAGE_STATE_COLOR_ADJ);
501                         }
502                 }
503
504         if (!imd->cm) image_post_process_alter(imd, clamp);
505
506         exif_free(exif);
507 }
508
509 /*
510  *-------------------------------------------------------------------
511  * read ahead (prebuffer)
512  *-------------------------------------------------------------------
513  */
514
515 static void image_read_ahead_cancel(ImageWindow *imd)
516 {
517         if (debug) printf("read ahead cancelled for :%s\n", imd->read_ahead_fd ? imd->read_ahead_fd->path : "null");
518
519         image_loader_free(imd->read_ahead_il);
520         imd->read_ahead_il = NULL;
521
522         if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
523         imd->read_ahead_pixbuf = NULL;
524
525         file_data_unref(imd->read_ahead_fd);
526         imd->read_ahead_fd = NULL;
527 }
528
529 static void image_read_ahead_done_cb(ImageLoader *il, gpointer data)
530 {
531         ImageWindow *imd = data;
532
533         if (debug) printf("read ahead done for :%s\n", imd->read_ahead_fd->path);
534
535         imd->read_ahead_pixbuf = image_loader_get_pixbuf(imd->read_ahead_il);
536         if (imd->read_ahead_pixbuf)
537                 {
538                 g_object_ref(imd->read_ahead_pixbuf);
539                 }
540         else
541                 {
542                 imd->read_ahead_pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
543                 }
544         image_loader_free(imd->read_ahead_il);
545         imd->read_ahead_il = NULL;
546
547         image_complete_util(imd, TRUE);
548 }
549
550 static void image_read_ahead_error_cb(ImageLoader *il, gpointer data)
551 {
552         /* we even treat errors as success, maybe at least some of the file was ok */
553         image_read_ahead_done_cb(il, data);
554 }
555
556 static void image_read_ahead_start(ImageWindow *imd)
557 {
558         /* already started ? */
559         if (!imd->read_ahead_fd || imd->read_ahead_il || imd->read_ahead_pixbuf) return;
560
561         /* still loading ?, do later */
562         if (imd->il || imd->cm) return;
563
564         if (debug) printf("read ahead started for :%s\n", imd->read_ahead_fd->path);
565
566         imd->read_ahead_il = image_loader_new(imd->read_ahead_fd);
567
568         image_loader_set_error_func(imd->read_ahead_il, image_read_ahead_error_cb, imd);
569         if (!image_loader_start(imd->read_ahead_il, image_read_ahead_done_cb, imd))
570                 {
571                 image_read_ahead_cancel(imd);
572                 image_complete_util(imd, TRUE);
573                 }
574 }
575
576 static void image_read_ahead_set(ImageWindow *imd, FileData *fd)
577 {
578         if (imd->read_ahead_fd && fd && imd->read_ahead_fd == fd) return;
579
580         image_read_ahead_cancel(imd);
581
582         imd->read_ahead_fd = file_data_ref(fd);
583
584         if (debug) printf("read ahead set to :%s\n", imd->read_ahead_fd->path);
585
586         image_read_ahead_start(imd);
587 }
588
589 /*
590  *-------------------------------------------------------------------
591  * post buffering
592  *-------------------------------------------------------------------
593  */
594
595 static void image_post_buffer_set(ImageWindow *imd, FileData *fd, GdkPixbuf *pixbuf, gint color_row)
596 {
597         file_data_unref(imd->prev_fd);
598         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
599
600         if (fd && pixbuf)
601                 {
602                 imd->prev_fd = file_data_ref(fd);
603                         
604                 g_object_ref(pixbuf);
605                 imd->prev_pixbuf = pixbuf;
606                 imd->prev_color_row = color_row;
607                 }
608         else
609                 {
610                 imd->prev_fd = NULL;
611                 imd->prev_pixbuf = NULL;
612                 imd->prev_color_row = -1;
613                 }
614
615         if (debug) printf("post buffer set: %s\n", fd ? fd->path : "null");
616 }
617
618 static gint image_post_buffer_get(ImageWindow *imd)
619 {
620         gint success;
621
622         if (imd->prev_pixbuf &&
623             imd->image_fd && imd->prev_fd && imd->image_fd == imd->prev_fd)
624                 {
625                 image_change_pixbuf(imd, imd->prev_pixbuf, image_zoom_get(imd));
626                 if (imd->prev_color_row >= 0)
627                         {
628                         ExifData *exif = NULL;
629
630                         if (imd->color_profile_use_image) exif = exif_read_fd(imd->image_fd, TRUE);
631                         image_post_process_color(imd, imd->prev_color_row, exif);
632                         exif_free(exif);
633                         }
634                 success = TRUE;
635                 }
636         else
637                 {
638                 success = FALSE;
639                 }
640
641         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
642         imd->prev_pixbuf = NULL;
643
644         file_data_unref(imd->prev_fd);
645         imd->prev_fd = NULL;
646
647         return success;
648 }
649
650 /*
651  *-------------------------------------------------------------------
652  * loading
653  *-------------------------------------------------------------------
654  */
655
656 static void image_load_pixbuf_ready(ImageWindow *imd)
657 {
658         if (image_get_pixbuf(imd) || !imd->il) return;
659
660         image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
661 }
662
663 static void image_load_area_cb(ImageLoader *il, guint x, guint y, guint w, guint h, gpointer data)
664 {
665         ImageWindow *imd = data;
666         PixbufRenderer *pr;
667
668         pr = (PixbufRenderer *)imd->pr;
669
670         if (imd->delay_flip &&
671             pr->pixbuf != image_loader_get_pixbuf(il))
672                 {
673                 return;
674                 }
675
676         if (!pr->pixbuf) image_load_pixbuf_ready(imd);
677
678         pixbuf_renderer_area_changed(pr, x, y, w, h);
679 }
680
681 static void image_load_done_cb(ImageLoader *il, gpointer data)
682 {
683         ImageWindow *imd = data;
684
685         if (debug) printf ("image done\n");
686
687         g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
688         image_state_unset(imd, IMAGE_STATE_LOADING);
689
690         if (imd->delay_flip &&
691             image_get_pixbuf(imd) != image_loader_get_pixbuf(imd->il))
692                 {
693                 g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
694                 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
695                 }
696
697         image_loader_free(imd->il);
698         imd->il = NULL;
699
700         image_post_process(imd, TRUE);
701
702         image_read_ahead_start(imd);
703 }
704
705 static void image_load_error_cb(ImageLoader *il, gpointer data)
706 {
707         if (debug) printf ("image error\n");
708
709         /* even on error handle it like it was done,
710          * since we have a pixbuf with _something_ */
711
712         image_load_done_cb(il, data);
713 }
714
715 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
716 static void image_load_buffer_throttle(ImageLoader *il)
717 {
718         if (!il || il->bytes_total < IMAGE_THROTTLE_THRESHOLD) return;
719
720         /* Larger image files usually have larger chunks of data per pixel...
721          * So increase the buffer read size so that the rendering chunks called
722          * are also larger.
723          */
724
725         image_loader_set_buffer_size(il, IMAGE_LOAD_BUFFER_COUNT * IMAGE_THROTTLE_FACTOR);
726 }
727 #endif
728
729 /* this read ahead is located here merely for the callbacks, above */
730
731 static gint image_read_ahead_check(ImageWindow *imd)
732 {
733         if (!imd->read_ahead_fd) return FALSE;
734         if (imd->il) return FALSE;
735
736         if (!imd->image_fd || imd->read_ahead_fd != imd->image_fd)
737                 {
738                 image_read_ahead_cancel(imd);
739                 return FALSE;
740                 }
741
742         if (imd->read_ahead_il)
743                 {
744                 imd->il = imd->read_ahead_il;
745                 imd->read_ahead_il = NULL;
746
747                 /* override the old signals */
748                 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
749                 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
750                 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
751
752 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
753                 image_load_buffer_throttle(imd->il);
754 #endif
755
756                 /* do this one directly (probably should add a set func) */
757                 imd->il->func_done = image_load_done_cb;
758
759                 g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
760                 image_state_set(imd, IMAGE_STATE_LOADING);
761
762                 if (!imd->delay_flip)
763                         {
764                         image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
765                         }
766
767                 image_read_ahead_cancel(imd);
768                 return TRUE;
769                 }
770         else if (imd->read_ahead_pixbuf)
771                 {
772                 image_change_pixbuf(imd, imd->read_ahead_pixbuf, image_zoom_get(imd));
773                 g_object_unref(imd->read_ahead_pixbuf);
774                 imd->read_ahead_pixbuf = NULL;
775
776                 image_read_ahead_cancel(imd);
777
778                 image_post_process(imd, FALSE);
779                 return TRUE;
780                 }
781
782         image_read_ahead_cancel(imd);
783         return FALSE;
784 }
785
786 static gint image_load_begin(ImageWindow *imd, FileData *fd)
787 {
788         if (debug) printf ("image begin \n");
789
790         if (imd->il) return FALSE;
791
792         imd->completed = FALSE;
793         g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
794
795         if (image_post_buffer_get(imd))
796                 {
797                 if (debug) printf("from post buffer: %s\n", imd->image_fd->path);
798                 return TRUE;
799                 }
800
801         if (image_read_ahead_check(imd))
802                 {
803                 if (debug) printf("from read ahead buffer: %s\n", imd->image_fd->path);
804                 return TRUE;
805                 }
806
807         if (!imd->delay_flip && image_get_pixbuf(imd))
808                 {
809                 PixbufRenderer *pr;
810
811                 pr = PIXBUF_RENDERER(imd->pr);
812                 if (pr->pixbuf) g_object_unref(pr->pixbuf);
813                 pr->pixbuf = NULL;
814                 }
815
816         g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
817
818         imd->il = image_loader_new(fd);
819
820         image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
821         image_loader_set_error_func(imd->il, image_load_error_cb, imd);
822         image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
823
824         if (!image_loader_start(imd->il, image_load_done_cb, imd))
825                 {
826                 if (debug) printf("image start error\n");
827
828                 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
829
830                 image_loader_free(imd->il);
831                 imd->il = NULL;
832
833                 image_complete_util(imd, FALSE);
834
835                 return FALSE;
836                 }
837
838         image_state_set(imd, IMAGE_STATE_LOADING);
839
840 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
841         image_load_buffer_throttle(imd->il);
842 #endif
843
844         if (!imd->delay_flip && !image_get_pixbuf(imd)) image_load_pixbuf_ready(imd);
845
846         return TRUE;
847 }
848
849 static void image_reset(ImageWindow *imd)
850 {
851         /* stops anything currently being done */
852
853         if (debug) printf("image reset\n");
854
855         g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
856
857         image_loader_free(imd->il);
858         imd->il = NULL;
859
860         color_man_free((ColorMan *)imd->cm);
861         imd->cm = NULL;
862
863         imd->delay_alter_type = ALTER_NONE;
864
865         image_state_set(imd, IMAGE_STATE_NONE);
866 }
867
868 /*
869  *-------------------------------------------------------------------
870  * image changer
871  *-------------------------------------------------------------------
872  */
873
874 static void image_change_complete(ImageWindow *imd, gdouble zoom, gint new)
875 {
876         image_reset(imd);
877
878         if (imd->image_fd && isfile(imd->image_fd->path))
879                 {
880                 PixbufRenderer *pr;
881
882                 pr = PIXBUF_RENDERER(imd->pr);
883                 pr->zoom = zoom;        /* store the zoom, needed by the loader */
884
885                 if (image_load_begin(imd, imd->image_fd))
886                         {
887                         imd->unknown = FALSE;
888                         }
889                 else
890                         {
891                         GdkPixbuf *pixbuf;
892
893                         pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
894                         image_change_pixbuf(imd, pixbuf, zoom);
895                         g_object_unref(pixbuf);
896
897                         imd->unknown = TRUE;
898                         }
899                 imd->size = filesize(imd->image_fd->path);
900                 imd->mtime = filetime(imd->image_fd->path);
901                 }
902         else
903                 {
904                 if (imd->image_fd)
905                         {
906                         GdkPixbuf *pixbuf;
907
908                         pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
909                         image_change_pixbuf(imd, pixbuf, zoom);
910                         g_object_unref(pixbuf);
911                         imd->mtime = filetime(imd->image_fd->path);
912                         }
913                 else
914                         {
915                         image_change_pixbuf(imd, NULL, zoom);
916                         imd->mtime = 0;
917                         }
918                 imd->unknown = TRUE;
919                 imd->size = 0;
920                 }
921
922         image_update_util(imd);
923 }
924
925 static void image_change_real(ImageWindow *imd, FileData *fd,
926                               CollectionData *cd, CollectInfo *info, gdouble zoom)
927 {
928         GdkPixbuf *pixbuf;
929         GdkPixbuf *prev_pixbuf = NULL;
930         FileData *prev_fd = NULL;
931         gint prev_clear = FALSE;
932         gint prev_color_row = -1;
933
934         imd->collection = cd;
935         imd->collection_info = info;
936
937         pixbuf = image_get_pixbuf(imd);
938
939         if (enable_read_ahead && imd->image_fd && pixbuf)
940                 {
941                 if (imd->il)
942                         {
943                         /* current image is not finished */
944                         prev_clear = TRUE;
945                         }
946                 else
947                         {
948                         prev_fd = file_data_ref(imd->image_fd);
949                         prev_pixbuf = pixbuf;
950                         g_object_ref(prev_pixbuf);
951
952                         if (imd->cm)
953                                 {
954                                 ColorMan *cm;
955
956                                 cm = (ColorMan *)imd->cm;
957                                 prev_color_row = cm->row;
958                                 }
959                         }
960                 }
961
962         file_data_unref(imd->image_fd);
963         imd->image_fd = file_data_ref(fd);
964
965         image_change_complete(imd, zoom, TRUE);
966
967         if (prev_pixbuf)
968                 {
969                 image_post_buffer_set(imd, prev_fd, prev_pixbuf, prev_color_row);
970                 file_data_unref(prev_fd);
971                 g_object_unref(prev_pixbuf);
972                 }
973         else if (prev_clear)
974                 {
975                 image_post_buffer_set(imd, NULL, NULL, -1);
976                 }
977
978         image_update_title(imd);
979         image_state_set(imd, IMAGE_STATE_IMAGE);
980 }
981
982 /*
983  *-------------------------------------------------------------------
984  * focus stuff
985  *-------------------------------------------------------------------
986  */
987
988 static void image_focus_paint(ImageWindow *imd, gint has_focus, GdkRectangle *area)
989 {
990         GtkWidget *widget;
991
992         widget = imd->widget;
993         if (!widget->window) return;
994
995         if (has_focus)
996                 {
997                 gtk_paint_focus (widget->style, widget->window, GTK_STATE_ACTIVE,
998                                  area, widget, "image_window",
999                                  widget->allocation.x, widget->allocation.y,
1000                                  widget->allocation.width - 1, widget->allocation.height - 1);  
1001                 }
1002         else
1003                 {
1004                 gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
1005                                   area, widget, "image_window",
1006                                   widget->allocation.x, widget->allocation.y,
1007                                   widget->allocation.width - 1, widget->allocation.height - 1);
1008                 }
1009 }
1010
1011 static gint image_focus_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
1012 {
1013         ImageWindow *imd = data;
1014
1015         image_focus_paint(imd, GTK_WIDGET_HAS_FOCUS(widget), &event->area);
1016         return TRUE;
1017 }
1018
1019 static gint image_focus_in_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
1020 {
1021         ImageWindow *imd = data;
1022
1023         GTK_WIDGET_SET_FLAGS(imd->widget, GTK_HAS_FOCUS);
1024         image_focus_paint(imd, TRUE, NULL);
1025
1026         return TRUE;
1027 }
1028
1029 static gint image_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
1030 {
1031         ImageWindow *imd = data;
1032
1033         GTK_WIDGET_UNSET_FLAGS(imd->widget, GTK_HAS_FOCUS);
1034         image_focus_paint(imd, FALSE, NULL);
1035
1036         return TRUE;
1037 }
1038
1039 gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
1040                        gint relative, gint always)
1041 {
1042         return pixbuf_renderer_overlay_add((PixbufRenderer *)imd->pr, pixbuf, x, y, relative, always);
1043 }
1044
1045 void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
1046 {
1047         pixbuf_renderer_overlay_set((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
1048 }
1049
1050 gint image_overlay_get(ImageWindow *imd, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
1051 {
1052         return pixbuf_renderer_overlay_get((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
1053 }
1054
1055 void image_overlay_remove(ImageWindow *imd, gint id)
1056 {
1057         pixbuf_renderer_overlay_remove((PixbufRenderer *)imd->pr, id);
1058 }
1059
1060 static gint image_scroll_cb(GtkWidget *widget, GdkEventScroll *event, gpointer data)
1061 {
1062         ImageWindow *imd = data;
1063
1064         if (imd->func_scroll &&
1065             event && event->type == GDK_SCROLL)
1066                 {
1067                 imd->func_scroll(imd, event->direction, event->time,
1068                                  event->x, event->y, event->state, imd->data_scroll);
1069                 return TRUE;
1070                 }
1071
1072         return FALSE;
1073 }
1074
1075 /*
1076  *-------------------------------------------------------------------
1077  * public interface
1078  *-------------------------------------------------------------------
1079  */
1080
1081 void image_attach_window(ImageWindow *imd, GtkWidget *window,
1082                          const gchar *title, const gchar *title_right, gint show_zoom)
1083 {
1084         imd->top_window = window;
1085         g_free(imd->title);
1086         imd->title = g_strdup(title);
1087         g_free(imd->title_right);
1088         imd->title_right = g_strdup(title_right);
1089         imd->title_show_zoom = show_zoom;
1090
1091         if (!fit_window) window = NULL;
1092
1093         pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)window);
1094
1095         image_update_title(imd);
1096 }
1097
1098 void image_set_update_func(ImageWindow *imd,
1099                            void (*func)(ImageWindow *imd, gpointer data),
1100                            gpointer data)
1101 {
1102         imd->func_update = func;
1103         imd->data_update = data;
1104 }
1105
1106 void image_set_complete_func(ImageWindow *imd,
1107                              void (*func)(ImageWindow *imd, gint preload, gpointer data),
1108                              gpointer data)
1109 {
1110         imd->func_complete = func;
1111         imd->data_complete = data;
1112 }
1113
1114 void image_set_state_func(ImageWindow *imd,
1115                         void (*func)(ImageWindow *imd, ImageState state, gpointer data),
1116                         gpointer data)
1117 {
1118         imd->func_state = func;
1119         imd->data_state = data;
1120 }
1121
1122
1123 void image_set_button_func(ImageWindow *imd,
1124                            void (*func)(ImageWindow *, gint button, guint32 time, gdouble x, gdouble y, guint state, gpointer),
1125                            gpointer data)
1126 {
1127         imd->func_button = func;
1128         imd->data_button = data;
1129 }
1130
1131 void image_set_drag_func(ImageWindow *imd,
1132                            void (*func)(ImageWindow *, gint button, guint32 time, gdouble x, gdouble y, guint state, gdouble dx, gdouble dy, gpointer),
1133                            gpointer data)
1134 {
1135         imd->func_drag = func;
1136         imd->data_drag = data;
1137 }
1138
1139 void image_set_scroll_func(ImageWindow *imd,
1140                            void (*func)(ImageWindow *, GdkScrollDirection direction, guint32 time, gdouble x, gdouble y, guint state, gpointer),
1141                            gpointer data)
1142 {
1143         imd->func_scroll = func;
1144         imd->data_scroll = data;
1145 }
1146
1147 void image_set_scroll_notify_func(ImageWindow *imd,
1148                                   void (*func)(ImageWindow *imd, gint x, gint y, gint width, gint height, gpointer data),
1149                                   gpointer data)
1150 {
1151         imd->func_scroll_notify = func;
1152         imd->data_scroll_notify = data;
1153 }
1154
1155 /* path, name */
1156
1157 const gchar *image_get_path(ImageWindow *imd)
1158 {
1159         if (imd->image_fd == NULL) return NULL;
1160         return imd->image_fd->path;
1161 }
1162
1163 const gchar *image_get_name(ImageWindow *imd)
1164 {
1165         if (imd->image_fd == NULL) return NULL;
1166         return imd->image_fd->name;
1167 }
1168
1169 FileData *image_get_fd(ImageWindow *imd)
1170 {
1171         return imd->image_fd;
1172 }
1173
1174 /* merely changes path string, does not change the image! */
1175 void image_set_fd(ImageWindow *imd, FileData *fd)
1176 {
1177         file_data_unref(imd->image_fd);
1178         imd->image_fd = file_data_ref(fd);
1179
1180         image_update_title(imd);
1181         image_state_set(imd, IMAGE_STATE_IMAGE);
1182 }
1183
1184 /* load a new image */
1185
1186 void image_change_fd(ImageWindow *imd, FileData *fd, gdouble zoom)
1187 {
1188         if (imd->image_fd == fd) return;
1189
1190         image_change_real(imd, fd, NULL, NULL, zoom);
1191 }
1192
1193 GdkPixbuf *image_get_pixbuf(ImageWindow *imd)
1194 {
1195         return pixbuf_renderer_get_pixbuf((PixbufRenderer *)imd->pr);
1196 }
1197
1198 void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom)
1199 {
1200         pixbuf_renderer_set_pixbuf((PixbufRenderer *)imd->pr, pixbuf, zoom);
1201         image_state_set(imd, IMAGE_STATE_IMAGE);
1202 }
1203
1204 void image_change_from_collection(ImageWindow *imd, CollectionData *cd, CollectInfo *info, gdouble zoom)
1205 {
1206         if (!cd || !info || !g_list_find(cd->list, info)) return;
1207
1208         image_change_real(imd, info->fd, cd, info, zoom);
1209 }
1210
1211 CollectionData *image_get_collection(ImageWindow *imd, CollectInfo **info)
1212 {
1213         if (collection_to_number(imd->collection) >= 0)
1214                 {
1215                 if (g_list_find(imd->collection->list, imd->collection_info) != NULL)
1216                         {
1217                         if (info) *info = imd->collection_info;
1218                         }
1219                 else
1220                         {
1221                         if (info) *info = NULL;
1222                         }
1223                 return imd->collection;
1224                 }
1225
1226         if (info) *info = NULL;
1227         return NULL;
1228 }
1229
1230 static void image_loader_sync_data(ImageLoader *il, gpointer data)
1231 {
1232         /* change data for the callbacks directly */
1233
1234         il->data_area_ready = data;
1235         il->data_error = data;
1236         il->data_done = data;
1237         il->data_percent = data;
1238 }
1239
1240 /* this is more like a move function
1241  * it moves most data from source to imd
1242  */
1243 void image_change_from_image(ImageWindow *imd, ImageWindow *source)
1244 {
1245         if (imd == source) return;
1246
1247         imd->unknown = source->unknown;
1248
1249         imd->collection = source->collection;
1250         imd->collection_info = source->collection_info;
1251         imd->size = source->size;
1252         imd->mtime = source->mtime;
1253
1254         image_set_fd(imd, image_get_fd(source));
1255
1256         image_loader_free(imd->il);
1257         imd->il = NULL;
1258
1259         if (source->il)
1260                 {
1261                 imd->il = source->il;
1262                 source->il = NULL;
1263
1264                 image_loader_sync_data(imd->il, imd);
1265
1266                 imd->delay_alter_type = source->delay_alter_type;
1267                 source->delay_alter_type = ALTER_NONE;
1268                 }
1269
1270         imd->color_profile_enable = source->color_profile_enable;
1271         imd->color_profile_input = source->color_profile_input;
1272         imd->color_profile_screen = source->color_profile_screen;
1273         imd->color_profile_use_image = source->color_profile_use_image;
1274         color_man_free((ColorMan *)imd->cm);
1275         imd->cm = NULL;
1276         if (source->cm)
1277                 {
1278                 ColorMan *cm;
1279
1280                 imd->cm = source->cm;
1281                 source->cm = NULL;
1282
1283                 cm = (ColorMan *)imd->cm;
1284                 cm->imd = imd;
1285                 cm->func_done_data = imd;
1286                 }
1287
1288         image_loader_free(imd->read_ahead_il);
1289         imd->read_ahead_il = source->read_ahead_il;
1290         source->read_ahead_il = NULL;
1291         if (imd->read_ahead_il) image_loader_sync_data(imd->read_ahead_il, imd);
1292
1293         if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
1294         imd->read_ahead_pixbuf = source->read_ahead_pixbuf;
1295         source->read_ahead_pixbuf = NULL;
1296
1297         file_data_unref(imd->read_ahead_fd);
1298         imd->read_ahead_fd = source->read_ahead_fd;
1299         source->read_ahead_fd = NULL;
1300
1301         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
1302         imd->prev_pixbuf = source->prev_pixbuf;
1303         source->prev_pixbuf = NULL;
1304         imd->prev_color_row = source->prev_color_row;
1305         source->prev_color_row = -1;
1306
1307         file_data_unref(imd->prev_fd);
1308         imd->prev_fd = source->prev_fd;
1309         source->prev_fd = NULL;
1310
1311         imd->completed = source->completed;
1312         imd->state = source->state;
1313         source->state = IMAGE_STATE_NONE;
1314
1315         pixbuf_renderer_move(PIXBUF_RENDERER(imd->pr), PIXBUF_RENDERER(source->pr));
1316 }
1317
1318 /* manipulation */
1319
1320 void image_area_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
1321 {
1322         pixbuf_renderer_area_changed((PixbufRenderer *)imd->pr, x, y, width, height);
1323 }
1324
1325 void image_reload(ImageWindow *imd)
1326 {
1327         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1328
1329         image_change_complete(imd, image_zoom_get(imd), FALSE);
1330 }
1331
1332 void image_scroll(ImageWindow *imd, gint x, gint y)
1333 {
1334         pixbuf_renderer_scroll((PixbufRenderer *)imd->pr, x, y);
1335 }
1336
1337 void image_scroll_to_point(ImageWindow *imd, gint x, gint y,
1338                            gdouble x_align, gdouble y_align)
1339 {
1340         pixbuf_renderer_scroll_to_point((PixbufRenderer *)imd->pr, x, y, x_align, y_align);
1341 }
1342
1343 void image_get_scroll_center(ImageWindow *imd, gdouble *x, gdouble *y)
1344 {
1345         pixbuf_renderer_get_scroll_center(PIXBUF_RENDERER(imd->pr), x, y);
1346 }
1347
1348 void image_set_scroll_center(ImageWindow *imd, gdouble x, gdouble y)
1349 {
1350         pixbuf_renderer_set_scroll_center(PIXBUF_RENDERER(imd->pr), x, y);
1351 }
1352
1353
1354
1355 void image_alter(ImageWindow *imd, AlterType type)
1356 {
1357         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1358
1359         if (imd->il || imd->cm)
1360                 {
1361                 /* still loading, wait till done */
1362                 imd->delay_alter_type = type;
1363                 image_state_set(imd, IMAGE_STATE_ROTATE_USER);
1364
1365                 if (imd->cm && (imd->state & IMAGE_STATE_ROTATE_AUTO))
1366                         {
1367                         image_state_unset(imd, IMAGE_STATE_ROTATE_AUTO);
1368                         }
1369                 return;
1370                 }
1371
1372         image_alter_real(imd, type, TRUE);
1373 }
1374
1375 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
1376 {
1377         pixbuf_renderer_zoom_adjust((PixbufRenderer *)imd->pr, increment);
1378 }
1379
1380 void image_zoom_adjust_at_point(ImageWindow *imd, gdouble increment, gint x, gint y)
1381 {
1382         pixbuf_renderer_zoom_adjust_at_point((PixbufRenderer *)imd->pr, increment, x, y);
1383 }
1384
1385 void image_zoom_set_limits(ImageWindow *imd, gdouble min, gdouble max)
1386 {
1387         pixbuf_renderer_zoom_set_limits((PixbufRenderer *)imd->pr, min, max);
1388 }
1389
1390 void image_zoom_set(ImageWindow *imd, gdouble zoom)
1391 {
1392         pixbuf_renderer_zoom_set((PixbufRenderer *)imd->pr, zoom);
1393 }
1394
1395 void image_zoom_set_fill_geometry(ImageWindow *imd, gint vertical)
1396 {
1397         PixbufRenderer *pr;
1398         gdouble zoom;
1399         gint width, height;
1400
1401         pr = (PixbufRenderer *)imd->pr;
1402
1403         if (!pixbuf_renderer_get_pixbuf(pr) ||
1404             !pixbuf_renderer_get_image_size(pr, &width, &height)) return;
1405
1406         if (vertical)
1407                 {
1408                 zoom = (gdouble)pr->window_height / height;
1409                 }
1410         else
1411                 {
1412                 zoom = (gdouble)pr->window_width / width;
1413                 }
1414
1415         if (zoom < 1.0)
1416                 {
1417                 zoom = 0.0 - 1.0 / zoom;
1418                 }
1419
1420         pixbuf_renderer_zoom_set(pr, zoom);
1421 }
1422
1423 gdouble image_zoom_get(ImageWindow *imd)
1424 {
1425         return pixbuf_renderer_zoom_get((PixbufRenderer *)imd->pr);
1426 }
1427
1428 gdouble image_zoom_get_real(ImageWindow *imd)
1429 {
1430         return pixbuf_renderer_zoom_get_scale((PixbufRenderer *)imd->pr);
1431 }
1432
1433 gchar *image_zoom_get_as_text(ImageWindow *imd)
1434 {
1435         gdouble zoom;
1436         gdouble scale;
1437         gdouble l = 1.0;
1438         gdouble r = 1.0;
1439         gint pl = 0;
1440         gint pr = 0;
1441         gchar *approx = " ";
1442
1443         zoom = image_zoom_get(imd);
1444         scale = image_zoom_get_real(imd);
1445
1446         if (zoom > 0.0)
1447                 {
1448                 l = zoom;
1449                 }
1450         else if (zoom < 0.0)
1451                 {
1452                 r = 0.0 - zoom;
1453                 }
1454         else if (zoom == 0.0 && scale != 0.0)
1455                 {
1456                 if (scale >= 1.0)
1457                         {
1458                         l = scale;
1459                         }
1460                 else
1461                         {
1462                         r = 1.0 / scale;
1463                         }
1464                 approx = " ~";
1465                 }
1466
1467         if (rint(l) != l) pl = 1;
1468         if (rint(r) != r) pr = 1;
1469
1470         return g_strdup_printf("%.*f :%s%.*f", pl, l, approx, pr, r);
1471 }
1472
1473 gdouble image_zoom_get_default(ImageWindow *imd, gint mode)
1474 {
1475         gdouble zoom;
1476
1477         if (mode == ZOOM_RESET_ORIGINAL)
1478                 {
1479                 zoom = 1.0;
1480                 }
1481         else if (mode == ZOOM_RESET_FIT_WINDOW)
1482                 {
1483                 zoom = 0.0;
1484                 }
1485         else
1486                 {
1487                 if (imd)
1488                         {
1489                         zoom = image_zoom_get(imd);
1490                         }
1491                 else
1492                         {
1493                         zoom = 1.0;
1494                         }
1495                 }
1496
1497         return zoom;
1498 }
1499
1500 /* read ahead */
1501
1502 void image_prebuffer_set(ImageWindow *imd, FileData *fd)
1503 {
1504         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1505
1506         if (fd)
1507                 {
1508                 image_read_ahead_set(imd, fd);
1509                 }
1510         else
1511                 {
1512                 image_read_ahead_cancel(imd);
1513                 }
1514 }
1515
1516 static gint image_auto_refresh_cb(gpointer data)
1517 {
1518         ImageWindow *imd = data;
1519         time_t newtime;
1520         
1521         if (!imd || !image_get_pixbuf(imd) ||
1522             imd->il || !imd->image_fd ||
1523             !update_on_time_change) return TRUE;
1524
1525         newtime = filetime(imd->image_fd->path);
1526         if (newtime > 0 && newtime != imd->mtime)
1527                 {
1528                 imd->mtime = newtime;
1529                 image_reload(imd);
1530                 }
1531
1532         return TRUE;
1533 }
1534
1535 /* image auto refresh on time stamp change, in 1/1000's second, -1 disables */
1536
1537 void image_auto_refresh(ImageWindow *imd, gint interval)
1538 {
1539         if (!imd) return;
1540         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1541
1542         if (imd->auto_refresh_id > -1)
1543                 {
1544                 g_source_remove(imd->auto_refresh_id);
1545                 imd->auto_refresh_id = -1;
1546                 imd->auto_refresh_interval = -1;
1547                 }
1548
1549         if (interval < 0) return;
1550
1551         if (interval == 0) interval = IMAGE_AUTO_REFRESH_TIME;
1552
1553         imd->auto_refresh_id = g_timeout_add((guint32)interval, image_auto_refresh_cb, imd);
1554         imd->auto_refresh_interval = interval;
1555 }
1556
1557 void image_top_window_set_sync(ImageWindow *imd, gint allow_sync)
1558 {
1559         imd->top_window_sync = allow_sync;
1560
1561         g_object_set(G_OBJECT(imd->pr), "window_fit", allow_sync, NULL);
1562 }
1563
1564 void image_background_set_color(ImageWindow *imd, GdkColor *color)
1565 {
1566         pixbuf_renderer_set_color((PixbufRenderer *)imd->pr, color);
1567 }
1568
1569 void image_color_profile_set(ImageWindow *imd,
1570                              gint input_type, gint screen_type,
1571                              gint use_image)
1572 {
1573         if (!imd) return;
1574
1575         if (input_type < 0 || input_type > COLOR_PROFILE_INPUTS ||
1576             screen_type < 0 || screen_type > 1)
1577                 {
1578                 return;
1579                 }
1580
1581         imd->color_profile_input = input_type;
1582         imd->color_profile_screen = screen_type;
1583         imd->color_profile_use_image = use_image;
1584 }
1585
1586 gint image_color_profile_get(ImageWindow *imd,
1587                              gint *input_type, gint *screen_type,
1588                              gint *use_image)
1589 {
1590         if (!imd) return FALSE;
1591
1592         if (input_type) *input_type = imd->color_profile_input;
1593         if (screen_type) *screen_type = imd->color_profile_screen;
1594         if (use_image) *use_image = imd->color_profile_use_image;
1595
1596         return TRUE;
1597 }
1598
1599 void image_color_profile_set_use(ImageWindow *imd, gint enable)
1600 {
1601         if (!imd) return;
1602
1603         if (imd->color_profile_enable == enable) return;
1604
1605         imd->color_profile_enable = enable;
1606 }
1607
1608 gint image_color_profile_get_use(ImageWindow *imd)
1609 {
1610         if (!imd) return FALSE;
1611
1612         return imd->color_profile_enable;
1613 }
1614
1615 void image_set_delay_flip(ImageWindow *imd, gint delay)
1616 {
1617         if (!imd ||
1618             imd->delay_flip == delay) return;
1619
1620         imd->delay_flip = delay;
1621
1622         g_object_set(G_OBJECT(imd->pr), "delay_flip", delay, NULL);
1623
1624         if (!imd->delay_flip && imd->il)
1625                 {
1626                 PixbufRenderer *pr;
1627
1628                 pr = PIXBUF_RENDERER(imd->pr);
1629                 if (pr->pixbuf) g_object_unref(pr->pixbuf);
1630                 pr->pixbuf = NULL;
1631
1632                 image_load_pixbuf_ready(imd);
1633                 }
1634 }
1635
1636 void image_to_root_window(ImageWindow *imd, gint scaled)
1637 {
1638         GdkScreen *screen;
1639         GdkWindow *rootwindow;
1640         GdkPixmap *pixmap;
1641         GdkPixbuf *pixbuf;
1642         GdkPixbuf *pb;
1643         gint width, height;
1644
1645         if (!imd) return;
1646
1647         pixbuf = image_get_pixbuf(imd);
1648         if (!pixbuf) return;
1649
1650         screen = gtk_widget_get_screen(imd->widget);
1651         rootwindow = gdk_screen_get_root_window(screen);
1652         if (gdk_drawable_get_visual(rootwindow) != gdk_visual_get_system()) return;
1653
1654         if (scaled)
1655                 {
1656                 width = gdk_screen_width();
1657                 height = gdk_screen_height();
1658                 }
1659         else
1660                 {
1661                 pixbuf_renderer_get_scaled_size((PixbufRenderer *)imd->pr, &width, &height);
1662                 }
1663
1664         pb = gdk_pixbuf_scale_simple(pixbuf, width, height, (GdkInterpType)zoom_quality);
1665
1666         gdk_pixbuf_render_pixmap_and_mask (pb, &pixmap, NULL, 128);
1667         gdk_window_set_back_pixmap(rootwindow, pixmap, FALSE);
1668         gdk_window_clear(rootwindow);
1669         g_object_unref(pb);
1670         g_object_unref(pixmap);
1671
1672         gdk_flush();
1673 }
1674
1675 void image_select(ImageWindow *imd, gboolean select)
1676 {
1677         if (imd->has_frame)
1678                 {
1679                 if (select) 
1680                         {
1681                         gtk_widget_set_state(imd->widget, GTK_STATE_SELECTED);
1682                         gtk_widget_set_state(imd->pr, GTK_STATE_NORMAL); /* do not propagate */
1683                         }
1684                 else
1685                         gtk_widget_set_state(imd->widget, GTK_STATE_NORMAL);
1686                 }
1687 }
1688
1689
1690
1691 void image_set_selectable(ImageWindow *imd, gboolean selectable)
1692 {
1693         if (imd->has_frame)
1694                 {
1695                 if (selectable) 
1696                         {
1697                         gtk_frame_set_shadow_type(GTK_FRAME(imd->frame), GTK_SHADOW_NONE);
1698                         gtk_container_set_border_width (GTK_CONTAINER (imd->frame), 4);
1699                         }
1700                 else
1701                         {
1702                         gtk_frame_set_shadow_type(GTK_FRAME(imd->frame), GTK_SHADOW_NONE);
1703                         gtk_container_set_border_width (GTK_CONTAINER (imd->frame), 0);
1704                         }
1705                 }
1706 }
1707
1708 /*
1709  *-------------------------------------------------------------------
1710  * prefs sync
1711  *-------------------------------------------------------------------
1712  */
1713
1714 static void image_options_set(ImageWindow *imd)
1715 {
1716         g_object_set(G_OBJECT(imd->pr), "zoom_quality", zoom_quality,
1717                                         "zoom_2pass", two_pass_zoom,
1718                                         "zoom_expand", zoom_to_fit_expands,
1719                                         "dither_quality", dither_quality,
1720                                         "scroll_reset", scroll_reset_method,
1721                                         "cache_display", tile_cache_max,
1722                                         "window_fit", (imd->top_window_sync && fit_window),
1723                                         "window_limit", limit_window_size,
1724                                         "window_limit_value", max_window_size,
1725                                         "autofit_limit", limit_autofit_size,
1726                                         "autofit_limit_value", max_autofit_size,
1727
1728                                         NULL);
1729
1730         pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)imd->top_window);
1731 }
1732
1733 void image_options_sync(void)
1734 {
1735         GList *work;
1736
1737         work = image_list;
1738         while (work)
1739                 {
1740                 ImageWindow *imd;
1741
1742                 imd = work->data;
1743                 work = work->next;
1744
1745                 image_options_set(imd);
1746                 }
1747 }
1748
1749 /*
1750  *-------------------------------------------------------------------
1751  * init / destroy
1752  *-------------------------------------------------------------------
1753  */
1754
1755 static void image_free(ImageWindow *imd)
1756 {
1757         image_list = g_list_remove(image_list, imd);
1758
1759         image_reset(imd);
1760
1761         image_read_ahead_cancel(imd);
1762         image_post_buffer_set(imd, NULL, NULL, -1);
1763         image_auto_refresh(imd, -1);
1764
1765         file_data_unref(imd->image_fd);
1766         g_free(imd->title);
1767         g_free(imd->title_right);
1768
1769         g_free(imd);
1770 }
1771
1772 static void image_destroy_cb(GtkObject *widget, gpointer data)
1773 {
1774         ImageWindow *imd = data;
1775         image_free(imd);
1776 }
1777
1778 gboolean selectable_frame_expose_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data)
1779 {
1780         gtk_paint_flat_box(widget->style,
1781                            widget->window,
1782                            widget->state,
1783                            GTK_FRAME (widget)->shadow_type,
1784                            NULL,
1785                            widget,
1786                            NULL,
1787                            widget->allocation.x + 3, widget->allocation.y + 3, 
1788                            widget->allocation.width - 6, widget->allocation.height - 6); 
1789  
1790  
1791         return FALSE;
1792 }
1793
1794
1795 void image_set_frame(ImageWindow *imd, gboolean frame)
1796 {
1797         frame = !!frame;
1798         
1799         if (frame == imd->has_frame) return;
1800         
1801         gtk_widget_hide(imd->pr);
1802         
1803         if (frame)
1804                 {
1805                 imd->frame = gtk_frame_new(NULL);
1806                 gtk_widget_ref(imd->pr);
1807                 if (imd->has_frame != -1) gtk_container_remove(GTK_CONTAINER(imd->widget), imd->pr);
1808                 gtk_container_add(GTK_CONTAINER(imd->frame), imd->pr);
1809                 gtk_widget_unref(imd->pr);
1810                 g_signal_connect (G_OBJECT (imd->frame), "expose_event",  
1811                     G_CALLBACK (selectable_frame_expose_cb), NULL);
1812
1813                 GTK_WIDGET_SET_FLAGS(imd->frame, GTK_CAN_FOCUS);
1814                 g_signal_connect(G_OBJECT(imd->frame), "focus_in_event",
1815                                  G_CALLBACK(image_focus_in_cb), imd);
1816                 g_signal_connect(G_OBJECT(imd->frame), "focus_out_event",
1817                                  G_CALLBACK(image_focus_out_cb), imd);
1818
1819                 g_signal_connect_after(G_OBJECT(imd->frame), "expose_event",
1820                                        G_CALLBACK(image_focus_expose), imd);
1821
1822
1823                 gtk_box_pack_start_defaults(GTK_BOX(imd->widget), imd->frame);
1824                 gtk_widget_show(imd->frame);
1825                 }
1826         else
1827                 {
1828                 gtk_widget_ref(imd->pr);
1829                 if (imd->frame) 
1830                         {
1831                         gtk_container_remove(GTK_CONTAINER(imd->frame), imd->pr);
1832                         gtk_widget_destroy(imd->frame);
1833                         imd->frame = NULL;
1834                         }
1835                 gtk_box_pack_start_defaults(GTK_BOX(imd->widget), imd->pr);
1836                 gtk_widget_unref(imd->pr);
1837                 }
1838
1839         gtk_widget_show(imd->pr);
1840         
1841         imd->has_frame = frame;
1842 }
1843
1844 ImageWindow *image_new(gint frame)
1845 {
1846         ImageWindow *imd;
1847
1848         imd = g_new0(ImageWindow, 1);
1849
1850         imd->top_window = NULL;
1851         imd->title = NULL;
1852         imd->title_right = NULL;
1853         imd->title_show_zoom = FALSE;
1854
1855         imd->unknown = TRUE;
1856
1857         imd->has_frame = -1; /* not initialized; for image_set_frame */
1858         imd->top_window_sync = FALSE;
1859
1860         imd->delay_alter_type = ALTER_NONE;
1861
1862         imd->read_ahead_il = NULL;
1863         imd->read_ahead_pixbuf = NULL;
1864         imd->read_ahead_fd = NULL;
1865
1866         imd->completed = FALSE;
1867         imd->state = IMAGE_STATE_NONE;
1868
1869         imd->color_profile_enable = FALSE;
1870         imd->color_profile_input = 0;
1871         imd->color_profile_screen = 0;
1872         imd->color_profile_use_image = FALSE;
1873
1874         imd->auto_refresh_id = -1;
1875         imd->auto_refresh_interval = -1;
1876
1877         imd->delay_flip = FALSE;
1878
1879         imd->func_update = NULL;
1880         imd->func_complete = NULL;
1881         imd->func_tile_request = NULL;
1882         imd->func_tile_dispose = NULL;
1883
1884         imd->func_button = NULL;
1885         imd->func_scroll = NULL;
1886
1887         imd->pr = GTK_WIDGET(pixbuf_renderer_new());
1888
1889         image_options_set(imd);
1890
1891         
1892         imd->widget = gtk_vbox_new(0, 0);
1893
1894         image_set_frame(imd, frame);
1895
1896         image_set_selectable(imd, 0);
1897
1898         g_signal_connect(G_OBJECT(imd->pr), "clicked",
1899                          G_CALLBACK(image_click_cb), imd);
1900         g_signal_connect(G_OBJECT(imd->pr), "scroll_notify",
1901                          G_CALLBACK(image_scroll_notify_cb), imd);
1902
1903         g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
1904                          G_CALLBACK(image_scroll_cb), imd);
1905
1906         g_signal_connect(G_OBJECT(imd->pr), "destroy",
1907                          G_CALLBACK(image_destroy_cb), imd);
1908
1909         g_signal_connect(G_OBJECT(imd->pr), "zoom",
1910                          G_CALLBACK(image_zoom_cb), imd);
1911         g_signal_connect(G_OBJECT(imd->pr), "render_complete",
1912                          G_CALLBACK(image_render_complete_cb), imd);
1913         g_signal_connect(G_OBJECT(imd->pr), "drag",
1914                          G_CALLBACK(image_drag_cb), imd);
1915
1916         image_list = g_list_append(image_list, imd);
1917
1918         return imd;
1919 }
1920