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