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