ae3850566ee7d2e87bb2ef89484e970c85670278
[geeqie.git] / src / image.c
1 /*
2  * GQview
3  * (C) 2004 John Ellis
4  *
5  * Author: John Ellis
6  *
7  * This software is released under the GNU General Public License (GNU GPL).
8  * Please read the included file COPYING for more information.
9  * This software comes with no warranty of any kind, use at your own risk!
10  */
11
12
13 #include "gqview.h"
14 #include "image.h"
15
16
17 #include "image-load.h"
18 #include "collect.h"
19 #include "exif.h"
20 #include "pixbuf_util.h"
21 #include "ui_fileops.h"
22
23 #include <math.h>
24
25
26 #define IMAGE_TILE_SIZE 128
27 #define IMAGE_ZOOM_MIN -32.0
28 #define IMAGE_ZOOM_MAX 32.0
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 /* distance to drag mouse to disable image flip */
43 #define IMAGE_DRAG_SCROLL_THRESHHOLD 4
44
45 /* alpha channel checkerboard background (same as gimp) */
46 #define IMAGE_ALPHA_CHECK1 0x00999999
47 #define IMAGE_ALPHA_CHECK2 0x00666666
48 #define IMAGE_ALPHA_CHECK_SIZE 16
49
50 #define IMAGE_AUTO_REFRESH_TIME 3000
51
52 /* when scaling image to below this size, use nearest pixel for scaling
53  * (below about 4, the other scale types become slow generating their conversion tables)
54  */
55 #define IMAGE_MIN_SCALE_SIZE 8
56
57
58 typedef enum {
59         TILE_RENDER_NONE = 0,   /* do nothing */
60         TILE_RENDER_AREA,       /* render an area of the tile */
61         TILE_RENDER_ALL         /* render the whole tile */
62 } TileRenderType;
63
64 typedef struct _ImageTile ImageTile;
65 struct _ImageTile
66 {
67         GdkPixmap *pixmap;      /* off screen buffer */
68         GdkPixbuf *pixbuf;      /* pixbuf area for zooming */
69         gint x;                 /* x offset into image */
70         gint y;                 /* y offset into image */
71         gint w;                 /* width that is visible (may be less if at edge of image) */
72         gint h;                 /* height '' */
73
74         gint blank;
75
76 /* render_todo: (explanation)
77         NONE    do nothing
78         AREA    render area of tile, usually only used when loading an image
79                 note: will jump to an ALL if render_done is not ALL.
80         ALL     render entire tile, if never done before w/ ALL, for expose events *only*
81 */
82
83         TileRenderType render_todo;     /* what to do (see above) */
84         TileRenderType render_done;     /* highest that has been done before on tile */
85 };
86
87 typedef struct _QueueData QueueData;
88 struct _QueueData
89 {
90         ImageTile *it;
91         gint x;
92         gint y;
93         gint w;
94         gint h;
95         gint new_data;
96 };
97
98 typedef struct _CacheData CacheData;
99 struct _CacheData
100 {
101         GdkPixmap *pixmap;
102         GdkPixbuf *pixbuf;
103         ImageTile *it;
104         guint size;
105 };
106
107 typedef struct _OverlayData OverlayData;
108 struct _OverlayData
109 {
110         gint id;
111
112         GdkPixbuf *pixbuf;
113
114         gint x;
115         gint y;
116         gint relative;  /* x,y coordinates are relative, negative values start bottom right */
117
118         gint visible;
119         gint always;    /* hide temporarily when scrolling */
120 };
121
122
123 static void image_queue_clear(ImageWindow *imd);
124
125 static void image_update_title(ImageWindow *imd);
126 static void image_update_util(ImageWindow *imd);
127 static void image_complete_util(ImageWindow *imd, gint preload);
128
129 static void image_button_do(ImageWindow *imd, GdkEventButton *bevent);
130
131 static void image_overlay_draw(ImageWindow *imd, gint x, gint y, gint w, gint h);
132 static void image_overlay_queue_all(ImageWindow *imd);
133
134 static void image_scroller_timer_set(ImageWindow *imd, gint start);
135
136
137 static gint util_clip_region(gint x, gint y, gint w, gint h,
138                              gint clip_x, gint clip_y, gint clip_w, gint clip_h,
139                              gint *rx, gint *ry, gint *rw, gint *rh)
140 {
141         if (clip_x + clip_w <= x ||
142             clip_x >= x + w ||
143             clip_y + clip_h <= y ||
144             clip_y >= y + h)
145                 {
146                 return FALSE;
147                 }
148
149         *rx = MAX(x, clip_x);
150         *rw = MIN((x + w), (clip_x + clip_w)) - *rx;
151
152         *ry = MAX(y, clip_y);
153         *rh = MIN((y + h), (clip_y + clip_h)) - *ry;
154
155         return TRUE;
156 }
157
158
159 /*
160  *-------------------------------------------------------------------
161  * tile cache
162  *-------------------------------------------------------------------
163  */
164
165 static gint pixmap_calc_size(GdkPixmap *pixmap)
166 {
167         gint w, h, d;
168
169         d = gdk_drawable_get_depth(pixmap);
170         gdk_drawable_get_size(pixmap, &w, &h);
171         return w * h * (d / 8);
172 }
173
174 static void image_tile_cache_remove(ImageWindow *imd, ImageTile *it)
175 {
176         GList *work;
177
178         work = imd->tile_cache;
179         while(work)
180                 {
181                 CacheData *cd = work->data;
182                 work = work->next;
183
184                 if (cd->it == it)
185                         {
186                         imd->tile_cache = g_list_remove(imd->tile_cache, cd);
187                         imd->tile_cache_size -= cd->size;
188                         g_free(cd);
189                         }
190                 }
191 }
192
193 static void image_tile_cache_free(ImageWindow *imd, CacheData *cd)
194 {
195         imd->tile_cache = g_list_remove(imd->tile_cache, cd);
196         if (cd->pixmap)
197                 {
198                 g_object_unref(cd->it->pixmap);
199                 cd->it->pixmap = NULL;
200                 cd->it->render_done = TILE_RENDER_NONE;
201                 }
202         if (cd->pixbuf)
203                 {
204                 gdk_pixbuf_unref(cd->it->pixbuf);
205                 cd->it->pixbuf = NULL;
206                 }
207         imd->tile_cache_size -= cd->size;
208         g_free(cd);
209 }
210
211 static void image_tile_cache_free_space(ImageWindow *imd, gint space, ImageTile *it)
212 {
213         GList *work = g_list_last(imd->tile_cache);
214
215         while (work && imd->tile_cache_size > 0 && imd->tile_cache_size + space > tile_cache_max * 1048576)
216                 {
217                 CacheData *cd = work->data;
218                 work = work->prev;
219                 if (cd->it != it) image_tile_cache_free(imd, cd);
220                 }
221 }
222
223 static void image_tile_cache_add(ImageWindow *imd, ImageTile *it,
224                                  GdkPixmap *pixmap, GdkPixbuf *pixbuf, guint size)
225 {
226         CacheData *cd;
227
228         cd = g_new(CacheData, 1);
229         cd->pixmap = pixmap;
230         cd->pixbuf = pixbuf;
231         cd->it = it;
232         cd->size = size;
233
234         imd->tile_cache = g_list_prepend(imd->tile_cache, cd);
235
236         imd->tile_cache_size += cd->size;
237 }
238
239 static void image_tile_prepare(ImageWindow *imd, ImageTile *it)
240 {
241         if (!it->pixmap)
242                 {
243                 GdkPixmap *pixmap;
244                 guint size;
245
246                 pixmap = gdk_pixmap_new(imd->image->window, imd->tile_width, imd->tile_height, -1);
247
248                 size = pixmap_calc_size(pixmap);
249                 image_tile_cache_free_space(imd, size, it);
250
251                 it->pixmap = pixmap;
252                 image_tile_cache_add(imd, it, pixmap, NULL, size);
253                 }
254         
255         if ((imd->zoom != 1.0 || gdk_pixbuf_get_has_alpha(imd->pixbuf)) &&
256             !it->pixbuf)
257                 {
258                 GdkPixbuf *pixbuf;
259                 guint size;
260
261                 pixbuf = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(imd->pixbuf),
262                                         gdk_pixbuf_get_has_alpha(imd->pixbuf),
263                                         gdk_pixbuf_get_bits_per_sample(imd->pixbuf),
264                                         imd->tile_width, imd->tile_height);
265
266                 size = gdk_pixbuf_get_rowstride(pixbuf) * imd->tile_height;
267                 image_tile_cache_free_space(imd, size, it);
268
269                 it->pixbuf = pixbuf;
270                 image_tile_cache_add(imd, it, NULL, pixbuf, size);
271                 }
272 }
273
274 /*
275  *-------------------------------------------------------------------
276  * tiles
277  *-------------------------------------------------------------------
278  */
279
280 static ImageTile *image_tile_new(gint w, gint h)
281 {
282         ImageTile *it;
283
284         it = g_new0(ImageTile, 1);
285         it->w = w;
286         it->h = h;
287         it->pixmap = NULL;
288         it->pixbuf = NULL;
289         it->blank = TRUE;
290         it->render_todo = TILE_RENDER_NONE;
291         it->render_done = TILE_RENDER_NONE;
292
293         return it;
294 }
295
296 static void image_tile_free(ImageTile *it)
297 {
298         if (!it) return;
299
300         if (it->pixbuf) gdk_pixbuf_unref(it->pixbuf);
301         if (it->pixmap) g_object_unref(it->pixmap);
302
303         g_free(it);
304 }
305
306 static void image_tile_sync_count(ImageWindow *imd, gint n)
307 {
308         gint l;
309
310         l = g_list_length(imd->tiles);
311
312         if (l == n) return;
313
314         if (l < n)
315                 {
316                 while (l < n)
317                         {
318                         imd->tiles = g_list_prepend(imd->tiles, image_tile_new(imd->tile_width, imd->tile_height));
319                         l++;
320                         }
321                 }
322         else
323                 {
324                 /* This should remove from the tail of the GList, but with large images there are many tiles,
325                  * making this significantly faster for those cases.
326                  */
327                 while (l > n && imd->tiles)
328                         {
329                         ImageTile *it = imd->tiles->data;
330                         imd->tiles = g_list_remove(imd->tiles, it);
331                         image_tile_cache_remove(imd, it);
332                         image_tile_free(it);
333                         l--;
334                         }
335                 }
336 }
337
338 static void image_tile_sync(ImageWindow *imd, gint width, gint height, gint blank)
339 {
340         gint rows;
341         gint x, y;
342         GList *work;
343
344         imd->tile_cols = (width + imd->tile_width - 1) / imd->tile_width;
345
346         rows = (height + imd->tile_height - 1) / imd->tile_height;
347
348         image_tile_sync_count(imd, imd->tile_cols * rows);
349
350         x = y = 0;
351         work = imd->tiles;
352         while(work)
353                 {
354                 ImageTile *it = work->data;
355                 work = work->next;
356
357                 it->x = x;
358                 it->y = y;
359                 if (x + imd->tile_width > width)
360                         {
361                         it->w = width - x;
362                         }       
363                 else
364                         {
365                         it->w = imd->tile_width;
366                         }
367                 if (y + imd->tile_height > height)
368                         {
369                         it->h = height - y;
370                         }
371                 else
372                         {
373                         it->h = imd->tile_height;
374                         }
375
376                 it->blank = blank;
377                 it->render_todo = TILE_RENDER_NONE;
378                 it->render_done = TILE_RENDER_NONE;
379
380                 x += imd->tile_width;
381                 if (x >= width)
382                         {
383                         x = 0;
384                         y += imd->tile_height;
385                         }
386                 }
387
388         /* all it's are now useless in queue */
389         image_queue_clear(imd);
390 }
391
392 static void image_tile_render(ImageWindow *imd, ImageTile *it,
393                               gint x, gint y, gint w, gint h,
394                               gint new_data, gint fast)
395 {
396         gint has_alpha;
397         gint draw = FALSE;
398
399         if (it->render_todo == TILE_RENDER_NONE && it->pixmap) return;
400
401         if (it->render_done != TILE_RENDER_ALL)
402                 {
403                 x = 0;
404                 y = 0;
405                 w = it->w;
406                 h = it->h;
407                 if (!fast) it->render_done = TILE_RENDER_ALL;
408                 }
409         else if (it->render_todo != TILE_RENDER_AREA)
410                 {
411                 if (!fast) it->render_todo = TILE_RENDER_NONE;
412                 return;
413                 }
414
415         if (!fast) it->render_todo = TILE_RENDER_NONE;
416
417         if (new_data) it->blank = FALSE;
418
419         image_tile_prepare(imd, it);
420         has_alpha = gdk_pixbuf_get_has_alpha(imd->pixbuf);
421
422         /* FIXME checker colors for alpha should be configurable,
423          * also should be drawn for blank = TRUE
424          */
425
426         if (it->blank)
427                 {
428                 /* no data, do fast rect fill */
429                 gdk_draw_rectangle(it->pixmap, imd->image->style->black_gc, TRUE,
430                                    0, 0, it->w, it->h);
431                 }
432         else if (imd->zoom == 1.0 || imd->scale == 1.0)
433                 {
434                 if (has_alpha)
435                         {
436                         gdk_pixbuf_composite_color(imd->pixbuf, it->pixbuf, x, y, w, h,
437                                          (double) 0.0 - it->x,
438                                          (double) 0.0 - it->y,
439                                          1.0, 1.0, GDK_INTERP_NEAREST,
440                                          255, it->x + x, it->y + y,
441                                          IMAGE_ALPHA_CHECK_SIZE, IMAGE_ALPHA_CHECK1, IMAGE_ALPHA_CHECK2);
442                         draw = TRUE;
443                         }
444                 else
445                         {
446                         /* faster, simple */
447                         gdk_draw_pixbuf(it->pixmap,
448                                         imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
449                                         imd->pixbuf,
450                                         it->x + x, it->y + y,
451                                         x, y,
452                                         w, h,
453                                         (GdkRgbDither)dither_quality, it->x + x, it->y + y);
454                         }
455                 }
456         else
457                 {
458                 double scale_x, scale_y;
459
460                 if (imd->image_width == 0 || imd->image_height == 0) return;
461                 scale_x = (double)imd->width / imd->image_width;
462                 scale_y = (double)imd->height / imd->image_height;
463
464                 /* HACK: The pixbuf scalers get kinda buggy(crash) with extremely
465                  * small sizes for anything but GDK_INTERP_NEAREST
466                  */
467                 if (imd->width < IMAGE_MIN_SCALE_SIZE || imd->height < IMAGE_MIN_SCALE_SIZE) fast = TRUE;
468
469                 if (!has_alpha)
470                         {
471                         gdk_pixbuf_scale(imd->pixbuf, it->pixbuf, x, y, w, h,
472                                          (double) 0.0 - it->x,
473                                          (double) 0.0 - it->y,
474                                          scale_x, scale_y,
475                                          (fast) ? GDK_INTERP_NEAREST : (GdkInterpType)zoom_quality);
476                         }
477                 else
478                         {
479                         gdk_pixbuf_composite_color(imd->pixbuf, it->pixbuf, x, y, w, h,
480                                          (double) 0.0 - it->x,
481                                          (double) 0.0 - it->y,
482                                          scale_x, scale_y,
483                                          (fast) ? GDK_INTERP_NEAREST : (GdkInterpType)zoom_quality,
484                                          255, it->x + x, it->y + y,
485                                          IMAGE_ALPHA_CHECK_SIZE, IMAGE_ALPHA_CHECK1, IMAGE_ALPHA_CHECK2);
486                         }
487                 draw = TRUE;
488                 }
489
490         if (draw && it->pixbuf && !it->blank)
491                 {
492                 gdk_draw_pixbuf(it->pixmap,
493                                 imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
494                                 it->pixbuf,
495                                 x, y,
496                                 x, y,
497                                 w, h,
498                                 (GdkRgbDither)dither_quality, it->x + x, it->y + y);
499                 }
500 }
501
502 static void image_tile_expose(ImageWindow *imd, ImageTile *it,
503                               gint x, gint y, gint w, gint h,
504                               gint new_data, gint fast)
505 {
506         image_tile_render(imd, it, x, y, w, h, new_data, fast);
507
508         gdk_draw_drawable(imd->image->window, imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
509                           it->pixmap, x, y,
510                           imd->x_offset + (it->x - imd->x_scroll) + x, imd->y_offset + (it->y - imd->y_scroll) + y, w, h);
511
512         if (imd->overlay_list)
513                 {
514                 image_overlay_draw(imd, imd->x_offset + (it->x - imd->x_scroll) + x,
515                                         imd->y_offset + (it->y - imd->y_scroll) + y,
516                                         w, h);
517                 }
518 }
519
520 static gint image_tile_is_visible(ImageWindow *imd, ImageTile *it)
521 {
522         return (it->x + it->w >= imd->x_scroll && it->x <= imd->x_scroll + imd->window_width - imd->x_offset * 2 &&
523                 it->y + it->h >= imd->y_scroll && it->y <= imd->y_scroll + imd->window_height - imd->y_offset * 2);
524 }
525
526 /*
527  *-------------------------------------------------------------------
528  * render queue
529  *-------------------------------------------------------------------
530  */
531
532
533 static gint image_queue_draw_idle_cb(gpointer data)
534 {
535         ImageWindow *imd = data;
536         QueueData *qd;
537         gint fast;
538
539         if (!imd->pixbuf || (!imd->draw_queue && !imd->draw_queue_2pass) || imd->draw_idle_id == -1)
540                 {
541                 if (!imd->completed) image_complete_util(imd, FALSE);
542
543                 imd->draw_idle_id = -1;
544                 return FALSE;
545                 }
546
547         if (imd->draw_queue)
548                 {
549                 qd = imd->draw_queue->data;
550                 fast = (two_pass_zoom && (GdkInterpType)zoom_quality != GDK_INTERP_NEAREST && imd->scale != 1.0);
551                 }
552         else
553                 {
554                 if (imd->il)
555                         {
556                         /* still loading, wait till done (also drops the higher priority) */
557
558                         imd->draw_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
559                                                             image_queue_draw_idle_cb, imd, NULL);
560                         imd->draw_idle_high = FALSE;
561                         return FALSE;
562                         }
563                 qd = imd->draw_queue_2pass->data;
564                 fast = FALSE;
565                 }
566
567         if (GTK_WIDGET_REALIZED(imd->image))
568                 {
569                 if (image_tile_is_visible(imd, qd->it))
570                         {
571                         image_tile_expose(imd, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
572                         }
573                 else if (qd->new_data)
574                         {
575                         /* if new pixel data, and we already have a pixmap, update the tile */
576                         qd->it->blank = FALSE;
577                         if (qd->it->pixmap && qd->it->render_done == TILE_RENDER_ALL)
578                                 {
579                                 image_tile_render(imd, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
580                                 }
581                         }
582                 }
583
584         if (imd->draw_queue)
585                 {
586                 imd->draw_queue = g_list_remove(imd->draw_queue, qd);
587                 if (fast)
588                         {
589                         imd->draw_queue_2pass = g_list_append(imd->draw_queue_2pass, qd);
590                         }
591                 else
592                         {
593                         g_free(qd);
594                         }
595                 }
596         else
597                 {
598                 imd->draw_queue_2pass = g_list_remove(imd->draw_queue_2pass, qd);
599                 g_free(qd);
600                 }
601
602         if (!imd->draw_queue && !imd->draw_queue_2pass)
603                 {
604                 if (!imd->completed) image_complete_util(imd, FALSE);
605
606                 imd->draw_idle_id = -1;
607                 return FALSE;
608                 }
609
610         return TRUE;
611 }
612
613 static QueueData *image_queue_combine(ImageWindow *imd, QueueData *qd)
614 {
615         QueueData *found = NULL;
616         GList *work;
617
618         work = imd->draw_queue;
619         while (work && !found)
620                 {
621                 found = work->data;
622                 work = work->next;
623
624                 if (found->it != qd->it) found = NULL;
625                 }
626
627         if (found)
628                 {
629                 if (found->x + found->w < qd->x + qd->w) found->w += (qd->x + qd->w) - (found->x + found->w);
630                 if (found->x > qd->x)
631                         {
632                         found->w += found->x - qd->x;
633                         found->x = qd->x;
634                         }
635
636                 if (found->y + found->h < qd->y + qd->h) found->h += (qd->y + qd->h) - (found->y + found->h);
637                 if (found->y > qd->y)
638                         {
639                         found->h += found->y - qd->y;
640                         found->y = qd->y;
641                         }
642                 found->new_data |= qd->new_data;
643                 }
644
645         return found;
646 }
647
648 static gint image_clamp_to_visible(ImageWindow *imd, gint *x, gint *y, gint *w, gint *h)
649 {
650         gint nx, ny;
651         gint nw, nh;
652         gint vx, vy;
653         gint vw, vh;
654
655         vw = imd->vis_width;
656         vh = imd->vis_height;
657
658         vx = imd->x_scroll;
659         vy = imd->y_scroll;
660
661         if (*x + *w < vx || *x > vx + vw || *y + *h < vy || *y > vy + vh) return FALSE;
662
663         /* now clamp it */
664         nx = CLAMP(*x, vx, vx + vw);
665         nw = CLAMP(*w - (nx - *x), 1, vw);
666
667         ny = CLAMP(*y, vy, vy + vh);
668         nh = CLAMP(*h - (ny - *y), 1, vh);
669
670         *x = nx;
671         *y = ny;
672         *w = nw;
673         *h = nh;
674
675         return TRUE;
676 }
677
678 static gint image_queue_to_tiles(ImageWindow *imd, gint x, gint y, gint w, gint h,
679                                  gint clamp, TileRenderType render, gint new_data)
680 {
681         gint i, j;
682         gint x1, x2;
683         gint y1, y2;
684         GList *work;
685
686         if (clamp && !image_clamp_to_visible(imd, &x, &y, &w, &h)) return FALSE;
687
688         x1 = (gint)floor(x / imd->tile_width) * imd->tile_width;
689         x2 = (gint)ceil((x + w) / imd->tile_width) * imd->tile_width;
690
691         y1 = (gint)floor(y / imd->tile_height) * imd->tile_height;
692         y2 = (gint)ceil((y + h) / imd->tile_height) * imd->tile_height;
693
694         work = g_list_nth(imd->tiles, y1 / imd->tile_height * imd->tile_cols + (x1 / imd->tile_width));
695         for (j = y1; j <= y2; j += imd->tile_height)
696                 {
697                 GList *tmp;
698                 tmp = work;
699                 for (i = x1; i <= x2; i += imd->tile_width)
700                         {
701                         if (tmp)
702                                 {
703                                 ImageTile *it = tmp->data;
704                                 QueueData *qd;
705
706                                 if ((render == TILE_RENDER_ALL && it->render_done != TILE_RENDER_ALL) ||
707                                     (render == TILE_RENDER_AREA && it->render_todo != TILE_RENDER_ALL))
708                                         {
709                                         it->render_todo = render;
710                                         }
711
712                                 qd = g_new(QueueData, 1);
713                                 qd->it = it;
714                                 qd->new_data = new_data;
715
716                                 if (i < x)
717                                         {
718                                         qd->x = x - i;
719                                         }
720                                 else
721                                         {
722                                         qd->x = 0;
723                                         }
724                                 qd->w = x + w - i - qd->x;
725                                 if (qd->x + qd->w > imd->tile_width) qd->w = imd->tile_width - qd->x;
726
727
728                                 if (j < y)
729                                         {
730                                         qd->y = y - j;
731                                         }
732                                 else
733                                         {
734                                         qd->y = 0;
735                                         }
736                                 qd->h = y + h - j - qd->y;
737                                 if (qd->y + qd->h > imd->tile_height) qd->h = imd->tile_height - qd->y;
738
739                                 if (qd->w < 1 || qd->h < 1 || /* <--- sanity checks, rare cases cause this */
740                                     image_queue_combine(imd, qd))
741                                         {
742                                         g_free(qd);
743                                         }
744                                 else
745                                         {
746                                         imd->draw_queue = g_list_append(imd->draw_queue, qd);
747                                         }
748
749                                 tmp = tmp->next;
750                                 }
751                         }
752                 work = g_list_nth(work, imd->tile_cols);        /* step 1 row */
753                 }
754
755         return TRUE;
756 }
757
758 static void image_queue(ImageWindow *imd, gint x, gint y, gint w, gint h,
759                         gint clamp, TileRenderType render, gint new_data)
760 {
761         gint nx, ny;
762
763         nx = CLAMP(x, 0, imd->width - 1);
764         ny = CLAMP(y, 0, imd->height - 1);
765         w -= (nx - x);
766         h -= (ny - y);
767         w = CLAMP(w, 0, imd->width - nx);
768         h = CLAMP(h, 0, imd->height - ny);
769         if (w < 1 || h < 1) return;
770
771         if (image_queue_to_tiles(imd, nx, ny, w, h, clamp, render, new_data) &&
772             ((!imd->draw_queue && !imd->draw_queue_2pass) || imd->draw_idle_id == -1 || !imd->draw_idle_high))
773                 {
774                 if (imd->draw_idle_id != -1) g_source_remove(imd->draw_idle_id);
775                 imd->draw_idle_id = g_idle_add_full(GDK_PRIORITY_REDRAW,
776                                                     image_queue_draw_idle_cb, imd, NULL);
777                 imd->draw_idle_high = TRUE;
778                 }
779 }
780
781 static void image_queue_list_free(GList *list)
782 {
783         GList *work;
784
785         work = list;
786         while (work)
787                 {
788                 QueueData *qd;
789
790                 qd = work->data;
791                 work = work->next;
792                 g_free(qd);
793                 }
794
795         g_list_free(list);
796 }
797
798 static void image_queue_clear(ImageWindow *imd)
799 {
800         image_queue_list_free(imd->draw_queue);
801         imd->draw_queue = NULL;
802
803         image_queue_list_free(imd->draw_queue_2pass);
804         imd->draw_queue_2pass = NULL;
805
806         if (imd->draw_idle_id != -1) g_source_remove(imd->draw_idle_id);
807         imd->draw_idle_id = -1;
808 }
809
810 /*
811  *-------------------------------------------------------------------
812  * core calculations
813  *-------------------------------------------------------------------
814  */
815
816 static gint image_top_window_sizable(ImageWindow *imd)
817 {
818         if (!imd->top_window) return FALSE;
819         if (!fit_window) return FALSE;
820         if (!imd->top_window_sync) return FALSE;
821         if (!imd->image->window) return FALSE;
822         if (window_maximized(imd->top_window)) return FALSE;
823
824         return TRUE;
825 }
826
827 static gint image_size_top_window(ImageWindow *imd, gint w, gint h)
828 {
829         gint ww, wh;
830
831         if (!image_top_window_sizable(imd)) return FALSE;
832
833         if (limit_window_size)
834                 {
835                 gint sw = gdk_screen_width() * max_window_size / 100;
836                 gint sh = gdk_screen_height() * max_window_size / 100;
837
838                 if (w > sw) w = sw;
839                 if (h > sh) h = sh;
840                 }
841
842         w += (imd->top_window->allocation.width - imd->image->allocation.width);
843         h += (imd->top_window->allocation.height - imd->image->allocation.height);
844
845         gdk_drawable_get_size(imd->top_window->window, &ww, &wh);
846         if (w == ww && h == wh) return FALSE;
847
848         gdk_window_resize(imd->top_window->window, w, h);
849
850         return TRUE;
851 }
852
853 static void image_redraw(ImageWindow *imd, gint new_data)
854 {
855         image_queue_clear(imd);
856         image_queue(imd, 0, 0, imd->width, imd->height, TRUE, TILE_RENDER_ALL, new_data);
857 }
858
859 static void image_border_draw(ImageWindow *imd, gint x, gint y, gint w, gint h)
860 {
861         gint rx, ry, rw, rh;
862
863         if (!imd->image->window) return;
864
865         if (!imd->pixbuf)
866                 {
867                 if (util_clip_region(x, y, w, h,
868                         0, 0,
869                         imd->window_width, imd->window_height,
870                         &rx, &ry, &rw, &rh))
871                         {
872                         gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
873                         image_overlay_draw(imd, rx, ry, rw, rh);
874                         }
875                 return;
876                 }
877
878         if (imd->vis_width < imd->window_width)
879                 {
880                 if (imd->x_offset > 0 &&
881                     util_clip_region(x, y, w, h,
882                         0, 0,
883                         imd->x_offset, imd->window_height,
884                         &rx, &ry, &rw, &rh))
885                         {
886                         gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
887                         image_overlay_draw(imd, rx, ry, rw, rh);
888                         }
889                 if (imd->window_width - imd->vis_width - imd->x_offset > 0 &&
890                     util_clip_region(x, y, w, h,
891                         imd->x_offset + imd->vis_width, 0,
892                         imd->window_width - imd->vis_width - imd->x_offset, imd->window_height,
893                         &rx, &ry, &rw, &rh))
894                         {
895                         gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
896                         image_overlay_draw(imd, rx, ry, rw, rh);
897                         }
898                 }
899         if (imd->vis_height < imd->window_height)
900                 {
901                 if (imd->y_offset > 0 &&
902                     util_clip_region(x, y, w, h,
903                         imd->x_offset, 0,
904                         imd->vis_width, imd->y_offset,
905                         &rx, &ry, &rw, &rh))
906                         {
907                         gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
908                         image_overlay_draw(imd, rx, ry, rw, rh);
909                         }
910                 if (imd->window_height - imd->vis_height - imd->y_offset > 0 &&
911                     util_clip_region(x, y, w, h,
912                         imd->x_offset, imd->y_offset + imd->vis_height,
913                         imd->vis_width, imd->window_height - imd->vis_height - imd->y_offset,
914                         &rx, &ry, &rw, &rh))
915                         {
916                         gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
917                         image_overlay_draw(imd, rx, ry, rw, rh);
918                         }
919                 }
920 }
921
922 static void image_border_clear(ImageWindow *imd)
923 {
924         image_border_draw(imd, 0, 0, imd->window_width, imd->window_height);
925 }
926
927 static gint image_scroll_clamp(ImageWindow *imd)
928 {
929         gint old_xs;
930         gint old_ys;
931
932         if (imd->zoom == 0.0)
933                 {
934                 imd->x_scroll = 0;
935                 imd->y_scroll = 0;
936                 return FALSE;
937                 }
938
939         old_xs = imd->x_scroll;
940         old_ys = imd->y_scroll;
941
942         if (imd->x_offset > 0)
943                 {
944                 imd->x_scroll = 0;
945                 }
946         else
947                 {
948                 imd->x_scroll = CLAMP(imd->x_scroll, 0, imd->width - imd->vis_width);
949                 }
950
951         if (imd->y_offset > 0)
952                 {
953                 imd->y_scroll = 0;
954                 }
955         else
956                 {
957                 imd->y_scroll = CLAMP(imd->y_scroll, 0, imd->height - imd->vis_height);
958                 }
959
960         return (old_xs != imd->x_scroll || old_ys != imd->y_scroll);
961 }
962
963 static gint image_zoom_clamp(ImageWindow *imd, gdouble zoom, gint force, gint new)
964 {
965         gint w, h;
966         gdouble scale;
967
968         zoom = CLAMP(zoom, IMAGE_ZOOM_MIN, IMAGE_ZOOM_MAX);
969
970         if (imd->zoom == zoom && !force) return FALSE;
971
972         w = imd->image_width;
973         h = imd->image_height;
974
975         if (zoom == 0.0 && !imd->pixbuf)
976                 {
977                 scale = 1.0;
978                 }
979         else if (zoom == 0.0)
980                 {
981                 gint max_w;
982                 gint max_h;
983                 gint sizeable;
984
985                 sizeable = (new && image_top_window_sizable(imd));
986
987                 if (sizeable)
988                         {
989                         max_w = gdk_screen_width();
990                         max_h = gdk_screen_height();
991
992                         if (limit_window_size)
993                                 {
994                                 max_w = max_w * max_window_size / 100;
995                                 max_h = max_h * max_window_size / 100;
996                                 }
997                         }
998                 else
999                         {
1000                         max_w = imd->window_width;
1001                         max_h = imd->window_height;
1002                         }
1003
1004                 if ((zoom_to_fit_expands && !sizeable) || w > max_w || h > max_h)
1005                         {
1006                         if ((gdouble)max_w / w > (gdouble)max_h / h)
1007                                 {
1008                                 scale = (gdouble)max_h / h;
1009                                 h = max_h;
1010                                 w = w * scale + 0.5;
1011                                 if (w > max_w) w = max_w;
1012                                 }
1013                         else
1014                                 {
1015                                 scale = (gdouble)max_w / w;
1016                                 w = max_w;
1017                                 h = h * scale + 0.5;
1018                                 if (h > max_h) h = max_h;
1019                                 }
1020                         if (w < 1) w = 1;
1021                         if (h < 1) h = 1;
1022                         }
1023                 else
1024                         {
1025                         scale = 1.0;
1026                         }
1027                 }
1028         else if (zoom > 0.0) /* zoom orig, in */
1029                 {
1030                 scale = zoom;
1031                 w = w * scale;
1032                 h = h * scale;
1033                 }
1034         else /* zoom out */
1035                 {
1036                 scale = 1.0 / (0.0 - zoom);
1037                 w = w * scale;
1038                 h = h * scale;
1039                 }
1040
1041         imd->zoom = zoom;
1042         imd->width = w;
1043         imd->height = h;
1044         imd->scale = scale;
1045
1046         return TRUE;
1047 }
1048
1049 static gint image_size_clamp(ImageWindow *imd)
1050 {
1051         gint old_vw, old_vh;
1052
1053         old_vw = imd->vis_width;
1054         old_vh = imd->vis_height;
1055
1056         if (imd->width < imd->window_width)
1057                 {
1058                 imd->vis_width = imd->width;
1059                 imd->x_offset = (imd->window_width - imd->width) / 2;
1060                 }
1061         else
1062                 {
1063                 imd->vis_width = imd->window_width;
1064                 imd->x_offset = 0;
1065                 }
1066
1067         if (imd->height < imd->window_height)
1068                 {
1069                 imd->vis_height = imd->height;
1070                 imd->y_offset = (imd->window_height - imd->height) / 2;
1071                 }
1072         else
1073                 {
1074                 imd->vis_height = imd->window_height;
1075                 imd->y_offset = 0;
1076                 }
1077
1078         return (old_vw != imd->vis_width || old_vh != imd->vis_height);
1079 }
1080
1081 static void image_size_sync(ImageWindow *imd, gint new_width, gint new_height)
1082 {
1083         if (imd->window_width == new_width && imd->window_height == new_height) return;
1084
1085         imd->window_width = new_width;
1086         imd->window_height = new_height;
1087
1088         if (imd->zoom == 0.0) image_zoom_clamp(imd, 0.0, TRUE, FALSE);
1089
1090         image_size_clamp(imd);
1091         image_scroll_clamp(imd);
1092
1093         gtk_widget_set_size_request(imd->image, imd->window_width, imd->window_height);
1094
1095         /* ensure scroller remains visible */
1096         if (imd->scroller_overlay != -1)
1097                 {
1098                 gint update = FALSE;
1099
1100                 if (imd->scroller_x > new_width)
1101                         {
1102                         imd->scroller_x = new_width;
1103                         imd->scroller_xpos = new_width;
1104                         update = TRUE;
1105                         }
1106                 if (imd->scroller_y > new_height)
1107                         {
1108                         imd->scroller_y = new_height;
1109                         imd->scroller_ypos = new_height;
1110                         update = TRUE;
1111                         }
1112
1113                 if (update)
1114                         {
1115                         GdkPixbuf *pixbuf;
1116
1117                         if (image_overlay_get(imd, imd->scroller_overlay, &pixbuf, NULL, NULL))
1118                                 {
1119                                 gint w, h;
1120
1121                                 w = gdk_pixbuf_get_width(pixbuf);
1122                                 h = gdk_pixbuf_get_height(pixbuf);
1123                                 image_overlay_set(imd, imd->scroller_overlay, pixbuf,
1124                                                   imd->scroller_x - w / 2, imd->scroller_y - h / 2);
1125                                 }
1126                         }
1127                 }
1128
1129         /* clear any borders */
1130         image_border_clear(imd);
1131         
1132         image_tile_sync(imd, imd->width, imd->height, FALSE);
1133 #if 0
1134         /* no longer needed? (expose event should be doing this for us) */
1135         image_redraw(imd, FALSE);
1136 #endif
1137
1138         if (imd->title_show_zoom) image_update_title(imd);
1139         image_update_util(imd);
1140 }
1141
1142 /*
1143  *-------------------------------------------------------------------
1144  * misc
1145  *-------------------------------------------------------------------
1146  */
1147
1148 static void image_update_title(ImageWindow *imd)
1149 {
1150         gchar *title = NULL;
1151         gchar *zoom = NULL;
1152         gchar *collection = NULL;
1153
1154         if (!imd->top_window) return;
1155
1156         if (imd->collection && collection_to_number(imd->collection) >= 0)
1157                 {
1158                 const gchar *name;
1159                 name = imd->collection->name;
1160                 if (!name) name = _("Untitled");
1161                 collection = g_strdup_printf(" (Collection %s)", name);
1162                 }
1163
1164         if (imd->title_show_zoom)
1165                 {
1166                 gchar *buf = image_zoom_get_as_text(imd);
1167                 zoom = g_strconcat(" [", buf, "]", NULL);
1168                 g_free(buf);
1169                 }
1170
1171         title = g_strdup_printf("%s%s%s%s%s%s",
1172                 imd->title ? imd->title : "",
1173                 imd->image_name ? imd->image_name : "",
1174                 zoom ? zoom : "",
1175                 collection ? collection : "",
1176                 imd->image_name ? " - " : "",
1177                 imd->title_right ? imd->title_right : "");
1178
1179         gtk_window_set_title(GTK_WINDOW(imd->top_window), title);
1180
1181         g_free(title);
1182         g_free(zoom);
1183         g_free(collection);
1184 }
1185
1186 static void image_update_util(ImageWindow *imd)
1187 {
1188         if (imd->func_update) imd->func_update(imd, imd->data_update);
1189 }
1190
1191 static void image_complete_util(ImageWindow *imd, gint preload)
1192 {
1193         if (imd->il && imd->pixbuf != image_loader_get_pixbuf(imd->il)) return;
1194
1195         if (debug) printf("image load completed \"%s\" (%s)\n",
1196                           (preload) ? imd->read_ahead_path : imd->image_path,
1197                           (preload) ? "preload" : "current");
1198
1199         if (!preload) imd->completed = TRUE;
1200         if (imd->func_complete) imd->func_complete(imd, preload, imd->data_complete);
1201 }
1202
1203 static void image_new_util(ImageWindow *imd)
1204 {
1205         if (imd->func_new) imd->func_new(imd, imd->data_new);
1206 }
1207
1208 static void image_scroll_real(ImageWindow *imd, gint x, gint y)
1209 {
1210         gint old_x, old_y;
1211         gint x_off, y_off;
1212         gint w, h;
1213
1214         if (!imd->pixbuf) return;
1215
1216         old_x = imd->x_scroll;
1217         old_y = imd->y_scroll;
1218
1219         imd->x_scroll += x;
1220         imd->y_scroll += y;
1221
1222         image_scroll_clamp(imd);
1223         if (imd->x_scroll == old_x && imd->y_scroll == old_y) return;
1224
1225         if (imd->overlay_list)
1226                 {
1227                 gint new_x, new_y;
1228
1229                 new_x = imd->x_scroll;
1230                 new_y = imd->y_scroll;
1231                 imd->x_scroll = old_x;
1232                 imd->y_scroll = old_y;
1233                 image_overlay_queue_all(imd);
1234                 imd->x_scroll = new_x;
1235                 imd->y_scroll = new_y;
1236                 }
1237
1238         x_off = imd->x_scroll - old_x;
1239         y_off = imd->y_scroll - old_y;
1240
1241         w = imd->vis_width - abs(x_off);
1242         h = imd->vis_height - abs(y_off);
1243
1244         if (w < 1 || h < 1)
1245                 {
1246                 /* scrolled completely to new material */
1247                 image_queue(imd, 0, 0, imd->width, imd->height, TRUE, TILE_RENDER_ALL, FALSE);
1248                 return;
1249                 }
1250         else
1251                 {
1252                 gint x1, y1;
1253                 gint x2, y2;
1254                 GdkGC *gc;
1255
1256                 if (x_off < 0)
1257                         {
1258                         x1 = abs(x_off);
1259                         x2 = 0;
1260                         }
1261                 else
1262                         {
1263                         x1 = 0;
1264                         x2 = abs(x_off);
1265                         }
1266
1267                 if (y_off < 0)
1268                         {
1269                         y1 = abs(y_off);
1270                         y2 = 0;
1271                         }
1272                 else
1273                         {
1274                         y1 = 0;
1275                         y2 = abs(y_off);
1276                         }
1277
1278                 gc = gdk_gc_new(imd->image->window);
1279                 gdk_gc_set_exposures(gc, TRUE);
1280                 gdk_draw_drawable(imd->image->window, gc,
1281                                   imd->image->window,
1282                                   x2 + imd->x_offset, y2 + imd->y_offset,
1283                                   x1 + imd->x_offset, y1 + imd->y_offset, w, h);
1284                 g_object_unref(gc);
1285
1286                 if (imd->overlay_list)
1287                         {
1288                         image_overlay_queue_all(imd);
1289                         }
1290
1291                 w = imd->vis_width - w;
1292                 h = imd->vis_height - h;
1293
1294                 if (w > 0)
1295                         {
1296                         image_queue(imd,
1297                                     x_off > 0 ? imd->x_scroll + (imd->vis_width - w) : imd->x_scroll, imd->y_scroll,
1298                                     w, imd->vis_height, TRUE, TILE_RENDER_ALL, FALSE);
1299                         }
1300                 if (h > 0)
1301                         {
1302                         /* FIXME, to optimize this, remove overlap */
1303                         image_queue(imd,
1304                                     imd->x_scroll, y_off > 0 ? imd->y_scroll + (imd->vis_height - h) : imd->y_scroll,
1305                                     imd->vis_width, h, TRUE, TILE_RENDER_ALL, FALSE);
1306                         }
1307                 }
1308 }
1309
1310 static void widget_set_cursor(GtkWidget *widget, gint icon)
1311 {
1312         GdkCursor *cursor;
1313
1314         if (!widget->window) return;
1315
1316         if (icon == -1)
1317                 {
1318                 cursor = NULL;
1319                 }
1320         else
1321                 {
1322                 cursor = gdk_cursor_new (icon);
1323                 }
1324
1325         gdk_window_set_cursor(widget->window, cursor);
1326
1327         if (cursor) gdk_cursor_unref(cursor);
1328 }
1329
1330 /*
1331  *-------------------------------------------------------------------
1332  * image pixbuf handling
1333  *-------------------------------------------------------------------
1334  */
1335
1336 static void image_zoom_sync(ImageWindow *imd, gdouble zoom,
1337                             gint force, gint blank, gint new,
1338                             gint center_point, gint px, gint py)
1339 {
1340         gdouble old_scale;
1341         gint old_cx, old_cy;
1342         gint clamped;
1343         gint sized;
1344
1345         old_scale = imd->scale;
1346         if (center_point)
1347                 {
1348                 px = CLAMP(px, 0, imd->width);
1349                 py = CLAMP(py, 0, imd->height);
1350                 old_cx = imd->x_scroll + (px - imd->x_offset);
1351                 old_cy = imd->y_scroll + (py - imd->y_offset);
1352                 }
1353         else
1354                 {
1355                 px = py = 0;
1356                 old_cx = imd->x_scroll + imd->vis_width / 2;
1357                 old_cy = imd->y_scroll + imd->vis_height / 2;
1358                 }
1359
1360         if (!image_zoom_clamp(imd, zoom, force, new)) return;
1361
1362         clamped = image_size_clamp(imd);
1363         sized = image_size_top_window(imd, imd->width, imd->height);
1364
1365         if (force)
1366                 {
1367                 /* force means new image, so update scroll offset per options */
1368                 switch (scroll_reset_method)
1369                         {
1370                         case SCROLL_RESET_NOCHANGE:
1371                                 /* maintain old scroll position, do nothing */
1372                                 break;
1373                         case SCROLL_RESET_CENTER:
1374                                 /* center new image */
1375                                 imd->x_scroll = ((double)imd->image_width / 2.0 * imd->scale) - imd->vis_width / 2;
1376                                 imd->y_scroll = ((double)imd->image_height / 2.0 * imd->scale) - imd->vis_height / 2;
1377                                 break;
1378                         case SCROLL_RESET_TOPLEFT:
1379                         default:
1380                                 /* reset to upper left */
1381                                 imd->x_scroll = 0;
1382                                 imd->y_scroll = 0;
1383                                 break;
1384                         }
1385                 }
1386         else
1387                 {
1388                 /* user zoom does not force, so keep visible center point */
1389                 if (center_point)
1390                         {
1391                         imd->x_scroll = old_cx / old_scale * imd->scale - (px - imd->x_offset);
1392                         imd->y_scroll = old_cy / old_scale * imd->scale - (py - imd->y_offset);
1393                         }
1394                 else
1395                         {
1396                         imd->x_scroll = old_cx / old_scale * imd->scale - (imd->vis_width / 2);
1397                         imd->y_scroll = old_cy / old_scale * imd->scale - (imd->vis_height / 2);
1398                         }
1399                 }
1400         image_scroll_clamp(imd);
1401
1402         image_tile_sync(imd, imd->width, imd->height, blank);
1403
1404         /* If the window was not sized, redraw the image - we know there will be no size/expose signal.
1405          * But even if a size is claimed, there is no guarantee that the window manager will allow it,
1406          * so redraw the window anyway :/
1407          */
1408         if (sized || clamped) image_border_clear(imd);
1409         image_redraw(imd, FALSE);
1410
1411         if (imd->title_show_zoom) image_update_title(imd);
1412         image_update_util(imd);
1413 }
1414
1415 static void image_pixbuf_sync(ImageWindow *imd, gdouble zoom, gint blank, gint new)
1416 {
1417         if (!imd->pixbuf)
1418                 {
1419                 /* no pixbuf so just clear the window */
1420                 imd->image_width = 0;
1421                 imd->image_height = 0;
1422                 imd->scale = 1.0;
1423
1424                 if (imd->image->window)
1425                         {
1426                         gdk_window_clear(imd->image->window);
1427                         image_overlay_draw(imd, 0, 0, imd->window_width, imd->window_height);
1428                         }
1429
1430                 image_update_util(imd);
1431                 
1432                 return;
1433                 }
1434
1435         imd->image_width = gdk_pixbuf_get_width(imd->pixbuf);
1436         imd->image_height = gdk_pixbuf_get_height(imd->pixbuf);
1437
1438 #if 0
1439         /* reset scrolling */
1440         imd->x_scroll = 0;
1441         imd->y_scroll = 0;
1442 #endif
1443
1444         image_zoom_sync(imd, zoom, TRUE, blank, new, FALSE, 0, 0);
1445 }
1446
1447 static void image_set_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom, gint new)
1448 {
1449         if (pixbuf) g_object_ref(pixbuf);
1450         if (imd->pixbuf) g_object_unref(imd->pixbuf);
1451         imd->pixbuf = pixbuf;
1452
1453         image_pixbuf_sync(imd, zoom, FALSE, new);
1454 }
1455
1456 static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp)
1457 {
1458         GdkPixbuf *new = NULL;
1459         gint x, y;
1460         gint t;
1461
1462         imd->delay_alter_type = ALTER_NONE;
1463
1464         if (!imd->pixbuf) return;
1465
1466         x = imd->x_scroll + (imd->vis_width / 2);
1467         y = imd->y_scroll + (imd->vis_height / 2);
1468
1469         switch (type)
1470                 {
1471                 case ALTER_ROTATE_90:
1472                         new = pixbuf_copy_rotate_90(imd->pixbuf, FALSE);
1473                         t = x;
1474                         x = imd->height - y;
1475                         y = t;
1476                         break;
1477                 case ALTER_ROTATE_90_CC:
1478                         new = pixbuf_copy_rotate_90(imd->pixbuf, TRUE);
1479                         t = x;
1480                         x = y;
1481                         y = imd->width - t;
1482                         break;
1483                 case ALTER_ROTATE_180:
1484                         new = pixbuf_copy_mirror(imd->pixbuf, TRUE, TRUE);
1485                         x = imd->width - x;
1486                         y = imd->height - y;
1487                         break;
1488                 case ALTER_MIRROR:
1489                         new = pixbuf_copy_mirror(imd->pixbuf, TRUE, FALSE);
1490                         x = imd->width - x;
1491                         break;
1492                 case ALTER_FLIP:
1493                         new = pixbuf_copy_mirror(imd->pixbuf, FALSE, TRUE);
1494                         y = imd->height - y;
1495                         break;
1496                 case ALTER_NONE:
1497                 default:
1498                         return;
1499                         break;
1500                 }
1501
1502         if (!new) return;
1503
1504         if (clamp)
1505                 {
1506                 image_set_pixbuf(imd, new, imd->zoom, TRUE);
1507                 g_object_unref(new);
1508
1509                 if (imd->zoom != 0.0)
1510                         {
1511                         image_scroll(imd, x - (imd->vis_width / 2), y - (imd->vis_height / 2));
1512                         }
1513                 }
1514         else
1515                 {
1516                 g_object_unref(imd->pixbuf);
1517                 imd->pixbuf = new;
1518                 }
1519 }
1520
1521 static void image_post_process(ImageWindow *imd, gint clamp)
1522 {
1523         if (exif_rotate_enable && imd->pixbuf)
1524                 {
1525                 ExifData *ed;
1526                 gint orientation;
1527
1528                 ed = exif_read(imd->image_path);
1529                 if (ed && exif_get_integer(ed, "Orientation", &orientation))
1530                         {
1531                         /* see http://jpegclub.org/exif_orientation.html 
1532                           1        2       3      4         5            6           7          8
1533
1534                         888888  888888      88  88      8888888888  88                  88  8888888888
1535                         88          88      88  88      88  88      88  88          88  88      88  88
1536                         8888      8888    8888  8888    88          8888888888  8888888888          88
1537                         88          88      88  88
1538                         88          88  888888  888888
1539                         */
1540
1541                         switch (orientation)
1542                                 {
1543                                 case EXIF_ORIENTATION_TOP_LEFT:
1544                                         /* normal -- nothing to do */
1545                                         break;
1546                                 case EXIF_ORIENTATION_TOP_RIGHT:
1547                                         /* mirrored */
1548                                         imd->delay_alter_type = ALTER_MIRROR;
1549                                         break;
1550                                 case EXIF_ORIENTATION_BOTTOM_RIGHT:
1551                                         /* upside down */
1552                                         imd->delay_alter_type = ALTER_ROTATE_180;
1553                                         break;
1554                                 case EXIF_ORIENTATION_BOTTOM_LEFT:
1555                                         /* flipped */
1556                                         imd->delay_alter_type = ALTER_FLIP;
1557                                         break;
1558                                 case EXIF_ORIENTATION_LEFT_TOP:
1559                                         /* not implemented -- too wacky to fix in one step */
1560                                         break;
1561                                 case EXIF_ORIENTATION_RIGHT_TOP:
1562                                         /* rotated -90 (270) */
1563                                         imd->delay_alter_type = ALTER_ROTATE_90;
1564                                         break;
1565                                 case EXIF_ORIENTATION_RIGHT_BOTTOM:
1566                                         /* not implemented -- too wacky to fix in one step */
1567                                         break;
1568                                 case EXIF_ORIENTATION_LEFT_BOTTOM:
1569                                         /* rotated 90 */
1570                                         imd->delay_alter_type = ALTER_ROTATE_90_CC;
1571                                         break;
1572                                 default:
1573                                         /* The other values are out of range */
1574                                         break;
1575                                 }
1576                         }
1577                 exif_free(ed);
1578                 }
1579
1580         if (imd->delay_alter_type != ALTER_NONE)
1581                 {
1582                 image_alter_real(imd, imd->delay_alter_type, clamp);
1583                 }
1584 }
1585
1586 /*
1587  *-------------------------------------------------------------------
1588  * read ahead (prebuffer)
1589  *-------------------------------------------------------------------
1590  */
1591
1592 static void image_read_ahead_cancel(ImageWindow *imd)
1593 {
1594         if (debug) printf("read ahead cancelled for :%s\n", imd->read_ahead_path);
1595
1596         image_loader_free(imd->read_ahead_il);
1597         imd->read_ahead_il = NULL;
1598
1599         if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
1600         imd->read_ahead_pixbuf = NULL;
1601
1602         g_free(imd->read_ahead_path);
1603         imd->read_ahead_path = NULL;
1604 }
1605
1606 static void image_read_ahead_done_cb(ImageLoader *il, gpointer data)
1607 {
1608         ImageWindow *imd = data;
1609
1610         if (debug) printf("read ahead done for :%s\n", imd->read_ahead_path);
1611
1612         imd->read_ahead_pixbuf = image_loader_get_pixbuf(imd->read_ahead_il);
1613         if (imd->read_ahead_pixbuf)
1614                 {
1615                 g_object_ref(imd->read_ahead_pixbuf);
1616                 }
1617         else
1618                 {
1619                 imd->read_ahead_pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
1620                 }
1621         image_loader_free(imd->read_ahead_il);
1622         imd->read_ahead_il = NULL;
1623
1624         image_complete_util(imd, TRUE);
1625 }
1626
1627 static void image_read_ahead_error_cb(ImageLoader *il, gpointer data)
1628 {
1629         /* we even treat errors as success, maybe at least some of the file was ok */
1630         image_read_ahead_done_cb(il, data);
1631 }
1632
1633 static void image_read_ahead_start(ImageWindow *imd)
1634 {
1635         /* already started ? */
1636         if (!imd->read_ahead_path || imd->read_ahead_il || imd->read_ahead_pixbuf) return;
1637
1638         /* still loading ?, do later */
1639         if (imd->il) return;
1640
1641         if (debug) printf("read ahead started for :%s\n", imd->read_ahead_path);
1642
1643         imd->read_ahead_il = image_loader_new(imd->read_ahead_path);
1644
1645         image_loader_set_error_func(imd->read_ahead_il, image_read_ahead_error_cb, imd);
1646         if (!image_loader_start(imd->read_ahead_il, image_read_ahead_done_cb, imd))
1647                 {
1648                 image_read_ahead_cancel(imd);
1649                 image_complete_util(imd, TRUE);
1650                 }
1651 }
1652
1653 static void image_read_ahead_set(ImageWindow *imd, const gchar *path)
1654 {
1655         if (imd->read_ahead_path && path && strcmp(imd->read_ahead_path, path) == 0) return;
1656
1657         image_read_ahead_cancel(imd);
1658
1659         imd->read_ahead_path = g_strdup(path);
1660
1661         if (debug) printf("read ahead set to :%s\n", imd->read_ahead_path);
1662
1663         image_read_ahead_start(imd);
1664 }
1665
1666 /*
1667  *-------------------------------------------------------------------
1668  * post buffering
1669  *-------------------------------------------------------------------
1670  */
1671
1672 static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf *pixbuf)
1673 {
1674         g_free(imd->prev_path);
1675         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
1676
1677         if (path && pixbuf)
1678                 {
1679                 imd->prev_path = g_strdup(path);
1680                         
1681                 g_object_ref(pixbuf);
1682                 imd->prev_pixbuf = pixbuf;
1683                 }
1684         else
1685                 {
1686                 imd->prev_path = NULL;
1687                 imd->prev_pixbuf = NULL;
1688                 }
1689
1690         if (debug) printf("post buffer set: %s\n", path);
1691 }
1692
1693 static gint image_post_buffer_get(ImageWindow *imd)
1694 {
1695         gint success;
1696
1697         if (imd->prev_pixbuf &&
1698             imd->image_path && imd->prev_path && strcmp(imd->image_path, imd->prev_path) == 0)
1699                 {
1700                 if (imd->pixbuf) g_object_unref(imd->pixbuf);
1701                 imd->pixbuf = imd->prev_pixbuf;
1702                 success = TRUE;
1703                 }
1704         else
1705                 {
1706                 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
1707                 success = FALSE;
1708                 }
1709
1710         imd->prev_pixbuf = NULL;
1711
1712         g_free(imd->prev_path);
1713         imd->prev_path = NULL;
1714
1715         return success;
1716 }
1717
1718 /*
1719  *-------------------------------------------------------------------
1720  * loading
1721  *-------------------------------------------------------------------
1722  */
1723
1724 static void image_load_pixbuf_ready(ImageWindow *imd)
1725 {
1726         if (imd->pixbuf || !imd->il) return;
1727
1728         imd->pixbuf = image_loader_get_pixbuf(imd->il);
1729
1730         if (imd->pixbuf) g_object_ref(imd->pixbuf);
1731
1732         image_pixbuf_sync(imd, imd->zoom, TRUE, TRUE);
1733 }
1734
1735 static void image_load_area_cb(ImageLoader *il, guint x, guint y, guint w, guint h, gpointer data)
1736 {
1737         ImageWindow *imd = data;
1738
1739         if (imd->delay_flip &&
1740             imd->pixbuf != image_loader_get_pixbuf(il))
1741                 {
1742                 return;
1743                 }
1744
1745         if (!imd->pixbuf) image_load_pixbuf_ready(imd);
1746
1747         if (imd->scale != 1.0)
1748                 {
1749                 x = (guint) floor((double)x * imd->scale);
1750                 y = (guint) floor((double)y * imd->scale);
1751                 w = (guint) ceil((double)w * imd->scale);
1752                 h = (guint) ceil((double)h * imd->scale);
1753
1754                 if (w == 0) w = 1;
1755                 if (h == 0) h = 1;
1756
1757                 if ((GdkInterpType)zoom_quality != GDK_INTERP_NEAREST)
1758                         {
1759                         /* some scaling types use surrounding pixels to smooth the image,
1760                          * this will expand the new area to cover up for faint black
1761                          * lines caused by previous renders with non-complete image
1762                          */
1763                         y -= 1;
1764                         h += 2;
1765                         }
1766
1767                 }
1768
1769         image_queue(imd, (gint) x, (gint) y, (gint) w, (gint) h, FALSE, TILE_RENDER_AREA, TRUE);
1770 }
1771
1772 static void image_load_done_cb(ImageLoader *il, gpointer data)
1773 {
1774         ImageWindow *imd = data;
1775
1776         if (debug) printf ("image done\n");
1777
1778         if (imd->delay_flip &&
1779             imd->pixbuf != image_loader_get_pixbuf(imd->il))
1780                 {
1781                 if (imd->pixbuf) g_object_unref(imd->pixbuf);
1782                 imd->pixbuf = image_loader_get_pixbuf(imd->il);
1783                 if (imd->pixbuf) g_object_ref(imd->pixbuf);
1784                 image_pixbuf_sync(imd, imd->zoom, FALSE, TRUE);
1785                 }
1786
1787         image_loader_free(imd->il);
1788         imd->il = NULL;
1789
1790         image_post_process(imd, TRUE);
1791
1792         image_read_ahead_start(imd);
1793 }
1794
1795 static void image_load_error_cb(ImageLoader *il, gpointer data)
1796 {
1797         if (debug) printf ("image error\n");
1798
1799         /* even on error handle it like it was done,
1800          * since we have a pixbuf with _something_ */
1801
1802         image_load_done_cb(il, data);
1803 }
1804
1805 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
1806 static void image_load_buffer_throttle(ImageLoader *il)
1807 {
1808         if (!il || il->bytes_total < IMAGE_THROTTLE_THRESHOLD) return;
1809
1810         /* Larger image files usually have larger chunks of data per pixel...
1811          * So increase the buffer read size so that the rendering chunks called
1812          * are also larger.
1813          */
1814
1815         image_loader_set_buffer_size(il, IMAGE_LOAD_BUFFER_COUNT * IMAGE_THROTTLE_FACTOR);
1816 }
1817 #endif
1818
1819 /* this read ahead is located here merely for the callbacks, above */
1820
1821 static gint image_read_ahead_check(ImageWindow *imd)
1822 {
1823         if (!imd->read_ahead_path) return FALSE;
1824         if (imd->il) return FALSE;
1825
1826         if (!imd->image_path || strcmp(imd->read_ahead_path, imd->image_path) != 0)
1827                 {
1828                 image_read_ahead_cancel(imd);
1829                 return FALSE;
1830                 }
1831
1832         if (imd->read_ahead_il)
1833                 {
1834                 imd->il = imd->read_ahead_il;
1835                 imd->read_ahead_il = NULL;
1836
1837                 /* override the old signals */
1838                 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
1839                 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
1840                 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
1841
1842 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
1843                 image_load_buffer_throttle(imd->il);
1844 #endif
1845
1846                 /* do this one directly (probably should add a set func) */
1847                 imd->il->func_done = image_load_done_cb;
1848
1849                 if (!imd->delay_flip)
1850                         {
1851                         if (imd->pixbuf) g_object_unref(imd->pixbuf);
1852                         imd->pixbuf = image_loader_get_pixbuf(imd->il);
1853                         if (imd->pixbuf) g_object_ref(imd->pixbuf);
1854                         }
1855
1856                 image_read_ahead_cancel(imd);
1857                 return TRUE;
1858                 }
1859         else if (imd->read_ahead_pixbuf)
1860                 {
1861                 if (imd->pixbuf) g_object_unref(imd->pixbuf);
1862                 imd->pixbuf = imd->read_ahead_pixbuf;
1863                 imd->read_ahead_pixbuf = NULL;
1864
1865                 image_read_ahead_cancel(imd);
1866
1867                 image_post_process(imd, FALSE);
1868                 return TRUE;
1869                 }
1870
1871         image_read_ahead_cancel(imd);
1872         return FALSE;
1873 }
1874
1875 static gint image_load_begin(ImageWindow *imd, const gchar *path)
1876 {
1877         if (debug) printf ("image begin \n");
1878
1879         if (imd->il) return FALSE;
1880
1881         imd->completed = FALSE;
1882
1883         if (image_post_buffer_get(imd))
1884                 {
1885                 if (debug) printf("from post buffer: %s\n", imd->image_path);
1886
1887                 image_pixbuf_sync(imd, imd->zoom, FALSE, TRUE);
1888                 return TRUE;
1889                 }
1890
1891         if (image_read_ahead_check(imd))
1892                 {
1893                 if (debug) printf("from read ahead buffer: %s\n", imd->image_path);
1894
1895                 if (!imd->delay_flip || !imd->il) image_pixbuf_sync(imd, imd->zoom, FALSE, TRUE);
1896                 return TRUE;
1897                 }
1898
1899         if (!imd->delay_flip && imd->pixbuf)
1900                 {
1901                 g_object_unref(imd->pixbuf);
1902                 imd->pixbuf = NULL;
1903                 }
1904
1905         imd->il = image_loader_new(path);
1906
1907         image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
1908         image_loader_set_error_func(imd->il, image_load_error_cb, imd);
1909         image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
1910
1911         if (!image_loader_start(imd->il, image_load_done_cb, imd))
1912                 {
1913                 if (debug) printf("image start error\n");
1914
1915                 image_loader_free(imd->il);
1916                 imd->il = NULL;
1917
1918                 image_complete_util(imd, FALSE);
1919
1920                 return FALSE;
1921                 }
1922
1923 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
1924         image_load_buffer_throttle(imd->il);
1925 #endif
1926
1927         if (!imd->delay_flip && !imd->pixbuf) image_load_pixbuf_ready(imd);
1928
1929         return TRUE;
1930 }
1931
1932 static void image_reset(ImageWindow *imd)
1933 {
1934         /* stops anything currently being done */
1935
1936         if (debug) printf("image reset\n");
1937
1938         image_loader_free(imd->il);
1939         imd->il = NULL;
1940
1941         image_queue_clear(imd);
1942         imd->delay_alter_type = ALTER_NONE;
1943 }
1944
1945 /*
1946  *-------------------------------------------------------------------
1947  * image changer
1948  *-------------------------------------------------------------------
1949  */
1950
1951 static void image_change_complete(ImageWindow *imd, gdouble zoom, gint new)
1952 {
1953         gint sync = TRUE;
1954
1955         imd->zoom = zoom;       /* store the zoom, needed by the loader */
1956
1957         image_reset(imd);
1958
1959         if (imd->image_path && isfile(imd->image_path))
1960                 {
1961                 if (image_load_begin(imd, imd->image_path))
1962                         {
1963                         imd->unknown = FALSE;
1964                         sync = FALSE;
1965                         }
1966                 else
1967                         {
1968                         if (imd->pixbuf) g_object_unref(imd->pixbuf);
1969                         imd->pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
1970                         imd->unknown = TRUE;
1971                         }
1972                 imd->size = filesize(imd->image_path);
1973                 imd->mtime = filetime(imd->image_path);
1974                 }
1975         else
1976                 {
1977                 if (imd->pixbuf) g_object_unref(imd->pixbuf);
1978                 imd->pixbuf = NULL;
1979
1980                 if (imd->image_path)
1981                         {
1982                         imd->pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
1983                         imd->mtime = filetime(imd->image_path);
1984                         }
1985                 else
1986                         {
1987                         imd->pixbuf = NULL;
1988                         imd->mtime = 0;
1989                         }
1990                 imd->unknown = TRUE;
1991                 imd->size = 0;
1992                 }
1993
1994         if (sync)
1995                 {
1996                 image_pixbuf_sync(imd, zoom, FALSE, new);
1997                 }
1998         else
1999                 {
2000                 image_update_util(imd);
2001                 }
2002 }
2003
2004 static void image_change_real(ImageWindow *imd, const gchar *path,
2005                               CollectionData *cd, CollectInfo *info, gdouble zoom)
2006 {
2007         GdkPixbuf *prev_pixbuf = NULL;
2008         gchar *prev_path = NULL;
2009         gint prev_clear = FALSE;
2010
2011         imd->collection = cd;
2012         imd->collection_info = info;
2013
2014         if (enable_read_ahead && imd->image_path && imd->pixbuf)
2015                 {
2016                 if (imd->il)
2017                         {
2018                         /* current image is not finished */
2019                         prev_clear = TRUE;
2020                         }
2021                 else
2022                         {
2023                         prev_path = g_strdup(imd->image_path);
2024                         prev_pixbuf = imd->pixbuf;
2025                         g_object_ref(prev_pixbuf);
2026                         }
2027                 }
2028
2029         g_free(imd->image_path);
2030         imd->image_path = g_strdup(path);
2031         imd->image_name = filename_from_path(imd->image_path);
2032
2033         image_change_complete(imd, zoom, TRUE);
2034
2035         if (prev_pixbuf)
2036                 {
2037                 image_post_buffer_set(imd, prev_path, prev_pixbuf);
2038                 g_free(prev_path);
2039                 g_object_unref(prev_pixbuf);
2040                 }
2041         else if (prev_clear)
2042                 {
2043                 image_post_buffer_set(imd, NULL, NULL);
2044                 }
2045
2046         image_update_title(imd);
2047         image_new_util(imd);
2048 }
2049
2050 /*
2051  *-------------------------------------------------------------------
2052  * callbacks
2053  *-------------------------------------------------------------------
2054  */
2055
2056 static gint image_expose_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data)
2057 {
2058         gint x, y;
2059
2060         ImageWindow *imd = data;
2061
2062         image_border_draw(imd, event->area.x, event->area.y,
2063                           event->area.width, event->area.height);
2064
2065         /* image */
2066         x = MAX(0, (gint)event->area.x - imd->x_offset + imd->x_scroll);
2067         y = MAX(0, (gint)event->area.y - imd->y_offset + imd->y_scroll);
2068
2069         image_queue(imd, x, y,
2070                     MIN((gint)event->area.width, imd->width - x),
2071                     MIN((gint)event->area.height, imd->height - y),
2072                     FALSE, TILE_RENDER_ALL, FALSE);
2073
2074         return TRUE;
2075 }
2076
2077 static void image_size_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data)
2078 {
2079         ImageWindow *imd = data;
2080
2081         image_size_sync(imd, allocation->width, allocation->height);
2082 }
2083
2084 /*
2085  *-------------------------------------------------------------------
2086  * focus stuff
2087  *-------------------------------------------------------------------
2088  */
2089
2090 static void image_focus_paint(ImageWindow *imd, gint has_focus, GdkRectangle *area)
2091 {
2092         GtkWidget *widget;
2093
2094         widget = imd->widget;
2095         if (!widget->window) return;
2096
2097         if (has_focus)
2098                 {
2099                 gtk_paint_focus (widget->style, widget->window, GTK_STATE_ACTIVE,
2100                                  area, widget, "image_window",
2101                                  widget->allocation.x, widget->allocation.y,
2102                                  widget->allocation.width - 1, widget->allocation.height - 1);  
2103                 }
2104         else
2105                 {
2106                 gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
2107                                   area, widget, "image_window",
2108                                   widget->allocation.x, widget->allocation.y,
2109                                   widget->allocation.width - 1, widget->allocation.height - 1);
2110                 }
2111 }
2112
2113 static gint image_focus_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
2114 {
2115         ImageWindow *imd = data;
2116
2117         image_focus_paint(imd, GTK_WIDGET_HAS_FOCUS(widget), &event->area);
2118         return TRUE;
2119 }
2120
2121 static gint image_focus_in_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
2122 {
2123         ImageWindow *imd = data;
2124
2125         GTK_WIDGET_SET_FLAGS(imd->widget, GTK_HAS_FOCUS);
2126         image_focus_paint(imd, TRUE, NULL);
2127
2128         return TRUE;
2129 }
2130
2131 static gint image_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
2132 {
2133         ImageWindow *imd = data;
2134
2135         GTK_WIDGET_UNSET_FLAGS(imd->widget, GTK_HAS_FOCUS);
2136         image_focus_paint(imd, FALSE, NULL);
2137
2138         return TRUE;
2139 }
2140
2141
2142 /*
2143  *-------------------------------------------------------------------
2144  * overlays
2145  *-------------------------------------------------------------------
2146  */
2147
2148 static void image_overlay_draw(ImageWindow *imd, gint x, gint y, gint w, gint h)
2149 {
2150         GList *work;
2151
2152         work = imd->overlay_list;
2153         while (work)
2154                 {
2155                 OverlayData *od;
2156                 gint px, py, pw, ph;
2157                 gint rx, ry, rw, rh;
2158
2159                 od = work->data;
2160                 work = work->next;
2161
2162                 if (!od->visible) continue;
2163
2164                 pw = gdk_pixbuf_get_width(od->pixbuf);
2165                 ph = gdk_pixbuf_get_height(od->pixbuf);
2166                 px = od->x;
2167                 py = od->y;
2168
2169                 if (od->relative)
2170                         {
2171                         if (px < 0) px = imd->window_width - pw + px;
2172                         if (py < 0) py = imd->window_height - ph + py;
2173                         }
2174
2175                 if (util_clip_region(x, y, w, h, px, py, pw, ph, &rx, &ry, &rw, &rh))
2176                         {
2177                         gdk_draw_pixbuf(imd->image->window,
2178                                         imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
2179                                         od->pixbuf,
2180                                         rx - px, ry - py,
2181                                         rx, ry, rw, rh,
2182                                         (GdkRgbDither)dither_quality, rx, ry);
2183                         }
2184                 }
2185 }
2186
2187 static void image_overlay_queue_draw(ImageWindow *imd, OverlayData *od, gint hidden)
2188 {
2189         gint x, y, w, h;
2190         gint old_vis;
2191
2192         w = gdk_pixbuf_get_width(od->pixbuf);
2193         h = gdk_pixbuf_get_height(od->pixbuf);
2194         x = od->x;
2195         y = od->y;
2196
2197         if (od->relative)
2198                 {
2199                 if (x < 0) x = imd->window_width - w + x;
2200                 if (y < 0) y = imd->window_height - h + y;
2201                 }
2202
2203         image_queue(imd, imd->x_scroll - imd->x_offset + x,
2204                          imd->y_scroll - imd->y_offset + y,
2205                          w, h,
2206                          FALSE, TILE_RENDER_ALL, FALSE);
2207
2208         old_vis = od->visible;
2209         if (hidden) od->visible = FALSE;
2210         image_border_draw(imd, x, y, w, h);
2211         od->visible = old_vis;
2212 }
2213
2214 static void image_overlay_queue_all(ImageWindow *imd)
2215 {
2216         GList *work;
2217
2218         work = imd->overlay_list;
2219         while (work)
2220                 {
2221                 OverlayData *od = work->data;
2222                 work = work->next;
2223
2224                 image_overlay_queue_draw(imd, od, FALSE);
2225                 }
2226 }
2227
2228 static OverlayData *image_overlay_find(ImageWindow *imd, gint id)
2229 {
2230         GList *work;
2231
2232         work = imd->overlay_list;
2233         while (work)
2234                 {
2235                 OverlayData *od = work->data;
2236                 work = work->next;
2237
2238                 if (od->id == id) return od;
2239                 }
2240
2241         return NULL;
2242 }
2243
2244 gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
2245                        gint relative, gint always)
2246 {
2247         OverlayData *od;
2248         gint id;
2249
2250         if (!imd || !pixbuf) return -1;
2251
2252         id = 1;
2253         while (image_overlay_find(imd, id)) id++;
2254
2255         od = g_new0(OverlayData, 1);
2256         od->id = id;
2257         od->pixbuf = pixbuf;
2258         g_object_ref(G_OBJECT(od->pixbuf));
2259         od->x = x;
2260         od->y = y;
2261         od->relative = relative;
2262         od->visible = TRUE;
2263         od->always = always;
2264
2265         imd->overlay_list = g_list_append(imd->overlay_list, od);
2266
2267         image_overlay_queue_draw(imd, od, FALSE);
2268
2269         return od->id;
2270 }
2271
2272 static void image_overlay_free(ImageWindow *imd, OverlayData *od)
2273 {
2274         imd->overlay_list = g_list_remove(imd->overlay_list, od);
2275
2276         if (od->pixbuf) g_object_unref(G_OBJECT(od->pixbuf));
2277         g_free(od);
2278 }
2279
2280 static void image_overlay_list_clear(ImageWindow *imd)
2281 {
2282         while (imd->overlay_list)
2283                 {
2284                 OverlayData *od;
2285
2286                 od = imd->overlay_list->data;
2287                 image_overlay_free(imd, od);
2288                 }
2289 }
2290
2291 void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
2292 {
2293         OverlayData *od;
2294
2295         if (!imd) return;
2296
2297         od = image_overlay_find(imd, id);
2298         if (!od) return;
2299
2300         if (pixbuf)
2301                 {
2302                 image_overlay_queue_draw(imd, od, TRUE);
2303
2304                 g_object_ref(G_OBJECT(pixbuf));
2305                 g_object_unref(G_OBJECT(od->pixbuf));
2306                 od->pixbuf = pixbuf;
2307
2308                 od->x = x;
2309                 od->y = y;
2310
2311                 image_overlay_queue_draw(imd, od, FALSE);
2312                 }
2313         else
2314                 {
2315                 image_overlay_queue_draw(imd, od, TRUE);
2316                 image_overlay_free(imd, od);
2317                 }
2318 }
2319
2320 gint image_overlay_get(ImageWindow *imd, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
2321 {
2322         OverlayData *od;
2323
2324         if (!imd) return FALSE;
2325
2326         od = image_overlay_find(imd, id);
2327         if (!od) return FALSE;
2328
2329         if (pixbuf) *pixbuf = od->pixbuf;
2330         if (x) *x = od->x;
2331         if (y) *y = od->y;
2332
2333         return TRUE;
2334 }
2335
2336 void image_overlay_remove(ImageWindow *imd, gint id)
2337 {
2338         image_overlay_set(imd, id, NULL, 0, 0);
2339 }
2340
2341 /*
2342  *-------------------------------------------------------------------
2343  * scroller
2344  *-------------------------------------------------------------------
2345  */
2346
2347 #define SCROLLER_UPDATES_PER_SEC 30
2348 #define SCROLLER_DEAD_ZONE 6
2349
2350
2351 static gboolean image_scroller_update_cb(gpointer data)
2352 {
2353         ImageWindow *imd = data;
2354         gint x, y;
2355         gint xinc, yinc;
2356
2357         /* this was a simple scroll by difference between scroller and mouse position,
2358          * but all this math results in a smoother result and accounts for a dead zone.
2359          */
2360
2361         if (abs(imd->scroller_xpos - imd->scroller_x) < SCROLLER_DEAD_ZONE)
2362                 {
2363                 x = 0;
2364                 }
2365         else
2366                 {
2367                 gint shift = SCROLLER_DEAD_ZONE / 2 * SCROLLER_UPDATES_PER_SEC;
2368                 x = (imd->scroller_xpos - imd->scroller_x) / 2 * SCROLLER_UPDATES_PER_SEC;
2369                 x += (x > 0) ? -shift : shift;
2370                 }
2371
2372         if (abs(imd->scroller_ypos - imd->scroller_y) < SCROLLER_DEAD_ZONE)
2373                 {
2374                 y = 0;
2375                 }
2376         else
2377                 {
2378                 gint shift = SCROLLER_DEAD_ZONE / 2 * SCROLLER_UPDATES_PER_SEC;
2379                 y = (imd->scroller_ypos - imd->scroller_y) / 2 * SCROLLER_UPDATES_PER_SEC;
2380                 y += (y > 0) ? -shift : shift;
2381                 }
2382
2383         if (abs(x) < SCROLLER_DEAD_ZONE * SCROLLER_UPDATES_PER_SEC)
2384                 {
2385                 xinc = x;
2386                 }
2387         else
2388                 {
2389                 xinc = imd->scroller_xinc;
2390
2391                 if (x >= 0)
2392                         {
2393                         if (xinc < 0) xinc = 0;
2394                         if (x < xinc) xinc = x;
2395                         if (x > xinc) xinc = MIN(xinc + x / SCROLLER_UPDATES_PER_SEC, x);
2396                         }
2397                 else
2398                         {
2399                         if (xinc > 0) xinc = 0;
2400                         if (x > xinc) xinc = x;
2401                         if (x < xinc) xinc = MAX(xinc + x / SCROLLER_UPDATES_PER_SEC, x);
2402                         }
2403                 }
2404
2405         if (abs(y) < SCROLLER_DEAD_ZONE * SCROLLER_UPDATES_PER_SEC)
2406                 {
2407                 yinc = y;
2408                 }
2409         else
2410                 {
2411                 yinc = imd->scroller_yinc;
2412
2413                 if (y >= 0)
2414                         {
2415                         if (yinc < 0) yinc = 0;
2416                         if (y < yinc) yinc = y;
2417                         if (y > yinc) yinc = MIN(yinc + y / SCROLLER_UPDATES_PER_SEC, y);
2418                         }
2419                 else
2420                         {
2421                         if (yinc > 0) yinc = 0;
2422                         if (y > yinc) yinc = y;
2423                         if (y < yinc) yinc = MAX(yinc + y / SCROLLER_UPDATES_PER_SEC, y);
2424                         }
2425                 }
2426
2427         imd->scroller_xinc = xinc;
2428         imd->scroller_yinc = yinc;
2429
2430         xinc = xinc / SCROLLER_UPDATES_PER_SEC;
2431         yinc = yinc / SCROLLER_UPDATES_PER_SEC;
2432
2433         image_scroll(imd, xinc, yinc);
2434
2435         return TRUE;
2436 }
2437
2438 static void image_scroller_timer_set(ImageWindow *imd, gint start)
2439 {
2440         if (imd->scroller_id != -1)
2441                 {
2442                 g_source_remove(imd->scroller_id);
2443                 imd->scroller_id = -1;
2444                 }
2445
2446         if (start)
2447                 {
2448                 imd->scroller_id = g_timeout_add(1000 / SCROLLER_UPDATES_PER_SEC,
2449                                                    image_scroller_update_cb, imd);
2450                 }
2451 }
2452
2453 static void image_scroller_start(ImageWindow *imd, gint x, gint y)
2454 {
2455         if (imd->scroller_overlay == -1)
2456                 {
2457                 GdkPixbuf *pixbuf;
2458                 gint w, h;
2459
2460                 pixbuf = pixbuf_inline(PIXBUF_INLINE_SCROLLER);
2461                 w = gdk_pixbuf_get_width(pixbuf);
2462                 h = gdk_pixbuf_get_height(pixbuf);
2463
2464                 imd->scroller_overlay = image_overlay_add(imd, pixbuf, x - w / 2, y - h / 2, FALSE, TRUE);
2465                 }
2466
2467         imd->scroller_x = x;
2468         imd->scroller_y = y;
2469         imd->scroller_xpos = x;
2470         imd->scroller_ypos = y;
2471
2472         image_scroller_timer_set(imd, TRUE);
2473 }
2474
2475 static void image_scroller_stop(ImageWindow *imd)
2476 {
2477         if (imd->scroller_id == -1) return;
2478
2479         image_overlay_remove(imd, imd->scroller_overlay);
2480         imd->scroller_overlay = -1;
2481
2482         image_scroller_timer_set(imd, FALSE);
2483 }
2484
2485 /*
2486  *-------------------------------------------------------------------
2487  * mouse stuff
2488  *-------------------------------------------------------------------
2489  */
2490
2491 static gint image_mouse_motion_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
2492 {
2493         ImageWindow *imd = data;
2494
2495         if (imd->scroller_id != -1)
2496                 {
2497                 imd->scroller_xpos = bevent->x;
2498                 imd->scroller_ypos = bevent->y;
2499                 }
2500
2501         if (!imd->in_drag || !gdk_pointer_is_grabbed()) return FALSE;
2502
2503         if (imd->drag_moved < IMAGE_DRAG_SCROLL_THRESHHOLD)
2504                 {
2505                 imd->drag_moved++;
2506                 }
2507         else
2508                 {
2509                 widget_set_cursor (imd->image, GDK_FLEUR);
2510                 }
2511
2512         /* do the scroll */
2513         image_scroll_real(imd, imd->drag_last_x - bevent->x, imd->drag_last_y - bevent->y);
2514
2515         imd->drag_last_x = bevent->x;
2516         imd->drag_last_y = bevent->y;
2517
2518         return FALSE;
2519 }
2520
2521 static gint image_mouse_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
2522 {
2523         ImageWindow *imd = data;
2524
2525         if (imd->scroller_id != -1) return TRUE;
2526
2527         switch (bevent->button)
2528                 {
2529                 case 1:
2530                         imd->in_drag = TRUE;
2531                         imd->drag_last_x = bevent->x;
2532                         imd->drag_last_y = bevent->y;
2533                         imd->drag_moved = 0;
2534                         gdk_pointer_grab(imd->image->window, FALSE,
2535                                 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
2536                                 NULL, NULL, bevent->time);
2537                         gtk_grab_add(imd->image);
2538                         break;
2539                 case 2:
2540                         imd->drag_moved = 0;
2541                         break;
2542                 case 3:
2543                         image_button_do(imd, bevent);
2544                         break;
2545                 default:
2546                         break;
2547                 }
2548
2549         gtk_widget_grab_focus(imd->widget);
2550
2551         return FALSE;
2552 }
2553
2554 static gint image_mouse_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
2555 {
2556         ImageWindow *imd = data;
2557
2558         if (imd->scroller_id != -1)
2559                 {
2560                 image_scroller_stop(imd);
2561                 return TRUE;
2562                 }
2563
2564         if (gdk_pointer_is_grabbed() && GTK_WIDGET_HAS_GRAB(imd->image))
2565                 {
2566                 gtk_grab_remove(imd->image);
2567                 gdk_pointer_ungrab(bevent->time);
2568                 widget_set_cursor(imd->image, -1);
2569                 }
2570
2571         if (bevent->button == 1 && (bevent->state & GDK_SHIFT_MASK))
2572                 {
2573                 image_scroller_start(imd, bevent->x, bevent->y);
2574                 }
2575         else if (bevent->button == 1 || bevent->button == 2)
2576                 {
2577                 if (imd->drag_moved < IMAGE_DRAG_SCROLL_THRESHHOLD) image_button_do(imd, bevent);
2578                 }
2579
2580         imd->in_drag = FALSE;
2581
2582         return FALSE;
2583 }
2584
2585 static gint image_mouse_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
2586 {
2587         ImageWindow *imd = data;
2588
2589         if (imd->scroller_id != -1)
2590                 {
2591                 imd->scroller_xpos = imd->scroller_x;
2592                 imd->scroller_ypos = imd->scroller_y;
2593                 imd->scroller_xinc = 0;
2594                 imd->scroller_yinc = 0;
2595                 }
2596
2597         return FALSE;
2598 }
2599
2600 static gint image_scroll_cb(GtkWidget *widget, GdkEventScroll *event, gpointer data)
2601 {
2602         ImageWindow *imd = data;
2603
2604         if (imd->func_scroll &&
2605             event && event->type == GDK_SCROLL)
2606                 {
2607                 imd->func_scroll(imd, event->direction, event->time,
2608                                  event->x, event->y, event->state, imd->data_scroll);
2609                 return TRUE;
2610                 }
2611
2612         return FALSE;
2613 }
2614
2615 static void image_mouse_drag_cb(GtkWidget *widget, GdkDragContext *context, gpointer data)
2616 {
2617         ImageWindow *imd = data;
2618
2619         imd->drag_moved = IMAGE_DRAG_SCROLL_THRESHHOLD;
2620 }
2621
2622 /*
2623  *-------------------------------------------------------------------
2624  * drag and drop
2625  *-------------------------------------------------------------------
2626  */
2627
2628 /*
2629  *-------------------------------------------------------------------
2630  * public interface
2631  *-------------------------------------------------------------------
2632  */
2633
2634 void image_attach_window(ImageWindow *imd, GtkWidget *window,
2635                          const gchar *title, const gchar *title_right, gint show_zoom)
2636 {
2637         imd->top_window = window;
2638         g_free(imd->title);
2639         imd->title = g_strdup(title);
2640         g_free(imd->title_right);
2641         imd->title_right = g_strdup(title_right);
2642         imd->title_show_zoom = show_zoom;
2643
2644         image_update_title(imd);
2645 }
2646
2647 void image_set_update_func(ImageWindow *imd,
2648                            void (*func)(ImageWindow *imd, gpointer data),
2649                            gpointer data)
2650 {
2651         imd->func_update = func;
2652         imd->data_update = data;
2653 }
2654
2655 void image_set_complete_func(ImageWindow *imd,
2656                              void (*func)(ImageWindow *, gint preload, gpointer),
2657                              gpointer data)
2658 {
2659         imd->func_complete = func;
2660         imd->data_complete = data;
2661 }
2662
2663 void image_set_new_func(ImageWindow *imd,
2664                         void (*func)(ImageWindow *, gpointer),
2665                         gpointer data)
2666 {
2667         imd->func_new = func;
2668         imd->data_new = data;
2669 }
2670
2671
2672 static void image_button_do(ImageWindow *imd, GdkEventButton *bevent)
2673 {
2674         if (imd->func_button &&
2675             bevent &&
2676             (bevent->type == GDK_BUTTON_PRESS || bevent->type == GDK_BUTTON_RELEASE))
2677                 {
2678                 imd->func_button(imd, bevent->button, bevent->time,
2679                                  bevent->x, bevent->y, bevent->state, imd->data_button);
2680                 }
2681 }
2682
2683 void image_set_button_func(ImageWindow *imd,
2684                            void (*func)(ImageWindow *, gint button, guint32 time, gdouble x, gdouble y, guint state, gpointer),
2685                            gpointer data)
2686 {
2687         imd->func_button = func;
2688         imd->data_button = data;
2689 }
2690
2691 void image_set_scroll_func(ImageWindow *imd,
2692                            void (*func)(ImageWindow *, GdkScrollDirection direction, guint32 time, gdouble x, gdouble y, guint state, gpointer),
2693                            gpointer data)
2694 {
2695         imd->func_scroll = func;
2696         imd->data_scroll = data;
2697 }
2698
2699 /* path, name */
2700
2701 const gchar *image_get_path(ImageWindow *imd)
2702 {
2703         return imd->image_path;
2704 }
2705
2706 const gchar *image_get_name(ImageWindow *imd)
2707 {
2708         return imd->image_name;
2709 }
2710
2711 /* merely changes path string, does not change the image! */
2712 void image_set_path(ImageWindow *imd, const gchar *newpath)
2713 {
2714         g_free(imd->image_path);
2715         imd->image_path = g_strdup(newpath);
2716         imd->image_name = filename_from_path(imd->image_path);
2717
2718         image_update_title(imd);
2719         image_new_util(imd);
2720 }
2721
2722 /* load a new image */
2723
2724 void image_change_path(ImageWindow *imd, const gchar *path, gdouble zoom)
2725 {
2726         if (imd->image_path == path ||
2727             (path && imd->image_path && !strcmp(path, imd->image_path)) ) return;
2728
2729         image_change_real(imd, path, NULL, NULL, zoom);
2730 }
2731
2732 void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom)
2733 {
2734         image_set_pixbuf(imd, pixbuf, zoom, TRUE);
2735         image_new_util(imd);
2736 }
2737
2738 void image_change_from_collection(ImageWindow *imd, CollectionData *cd, CollectInfo *info, gdouble zoom)
2739 {
2740         if (!cd || !info || !g_list_find(cd->list, info)) return;
2741
2742         image_change_real(imd, info->path, cd, info, zoom);
2743 }
2744
2745 CollectionData *image_get_collection(ImageWindow *imd, CollectInfo **info)
2746 {
2747         if (collection_to_number(imd->collection) >= 0)
2748                 {
2749                 if (g_list_find(imd->collection->list, imd->collection_info) != NULL)
2750                         {
2751                         if (info) *info = imd->collection_info;
2752                         }
2753                 else
2754                         {
2755                         if (info) *info = NULL;
2756                         }
2757                 return imd->collection;
2758                 }
2759
2760         if (info) *info = NULL;
2761         return NULL;
2762 }
2763
2764 static void image_loader_sync_data(ImageLoader *il, gpointer data)
2765 {
2766         /* change data for the callbacks directly */
2767
2768         il->data_area_ready = data;
2769         il->data_error = data;
2770         il->data_done = data;
2771         il->data_percent = data;
2772 }
2773
2774 /* this is more like a move function
2775  * it moves most data from source to imd, source does keep a ref on the pixbuf
2776  */
2777 void image_change_from_image(ImageWindow *imd, ImageWindow *source)
2778 {
2779         if (imd == source) return;
2780
2781         imd->unknown = source->unknown;
2782
2783         image_set_pixbuf(imd, source->pixbuf, image_zoom_get(source), TRUE);
2784
2785         imd->collection = source->collection;
2786         imd->collection_info = source->collection_info;
2787         imd->size = source->size;
2788         imd->mtime = source->mtime;
2789
2790         image_set_path(imd, image_get_path(source));
2791
2792         image_loader_free(imd->il);
2793         imd->il = NULL;
2794
2795         if (imd->pixbuf && source->il)
2796                 {
2797                 imd->il = source->il;
2798                 source->il = NULL;
2799
2800                 image_loader_sync_data(imd->il, imd);
2801
2802                 imd->delay_alter_type = source->delay_alter_type;
2803                 source->delay_alter_type = ALTER_NONE;
2804                 }
2805
2806         image_loader_free(imd->read_ahead_il);
2807         imd->read_ahead_il = source->read_ahead_il;
2808         source->read_ahead_il = NULL;
2809         if (imd->read_ahead_il) image_loader_sync_data(imd->read_ahead_il, imd);
2810
2811         if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
2812         imd->read_ahead_pixbuf = source->read_ahead_pixbuf;
2813         source->read_ahead_pixbuf = NULL;
2814
2815         g_free(imd->read_ahead_path);
2816         imd->read_ahead_path = source->read_ahead_path;
2817         source->read_ahead_path = NULL;
2818
2819         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
2820         imd->prev_pixbuf = source->prev_pixbuf;
2821         source->prev_pixbuf = NULL;
2822
2823         g_free(imd->prev_path);
2824         imd->prev_path = source->prev_path;
2825         source->prev_path = NULL;
2826
2827         imd->completed = source->completed;
2828
2829         imd->x_scroll = source->x_scroll;
2830         imd->y_scroll = source->y_scroll;
2831
2832         image_scroll_clamp(imd);
2833 }
2834
2835 /* manipulation */
2836
2837 void image_area_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
2838 {
2839         gint sx, sy, sw, sh;
2840
2841         sx = (gint)floor((double)x * imd->scale);
2842         sy = (gint)floor((double)y * imd->scale);
2843         sw = (gint)ceil((double)width * imd->scale);
2844         sh = (gint)ceil((double)height * imd->scale);
2845
2846         image_queue(imd, sx, sy, sw, sh, FALSE, TILE_RENDER_AREA, TRUE);
2847 }
2848
2849 void image_reload(ImageWindow *imd)
2850 {
2851         image_change_complete(imd, imd->zoom, FALSE);
2852 }
2853
2854 void image_scroll(ImageWindow *imd, gint x, gint y)
2855 {
2856         image_scroll_real(imd, x, y);
2857 }
2858
2859 void image_alter(ImageWindow *imd, AlterType type)
2860 {
2861         if (imd->il)
2862                 {
2863                 /* still loading, wait till done */
2864                 imd->delay_alter_type = type;
2865                 return;
2866                 }
2867
2868         image_alter_real(imd, type, TRUE);
2869 }
2870
2871 /* zoom */
2872
2873 static void image_zoom_adjust_real(ImageWindow *imd, gdouble increment,
2874                                    gint center_point, gint x, gint y)
2875 {
2876         gdouble zoom = imd->zoom;
2877
2878         if (increment == 0.0) return; /* avoid possible div by zero, a no-op anyway... */
2879
2880         if (zoom == 0.0)
2881                 {
2882                 if (imd->scale < 1.0)
2883                         {
2884                         zoom = 0.0 - 1.0 / imd->scale;
2885                         }
2886                 else
2887                         {
2888                         zoom = imd->scale;
2889                         }
2890                 }
2891
2892         if (increment < 0.0)
2893                 {
2894                 if (zoom >= 1.0 && zoom + increment < 1.0)
2895                         {
2896                         zoom = zoom + increment - 2.0;
2897                         }
2898                 else
2899                         {
2900                         zoom = zoom + increment;
2901                         }
2902                 }
2903         else
2904                 {
2905                 if (zoom <= -1.0 && zoom + increment > -1.0)
2906                         {
2907                         zoom = zoom + increment + 2.0;
2908                         }
2909                 else
2910                         {
2911                         zoom = zoom + increment;
2912                         }
2913                 }
2914
2915         image_zoom_sync(imd, zoom, FALSE, FALSE, FALSE, center_point, x, y);
2916 }
2917
2918 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
2919 {
2920         image_zoom_adjust_real(imd, increment, FALSE, 0, 0);
2921 }
2922
2923 void image_zoom_adjust_at_point(ImageWindow *imd, gdouble increment, gint x, gint y)
2924 {
2925         image_zoom_adjust_real(imd, increment, TRUE, x, y);
2926 }
2927
2928 void image_zoom_set(ImageWindow *imd, gdouble zoom)
2929 {
2930         image_zoom_sync(imd, zoom, FALSE, FALSE, FALSE, FALSE, 0, 0);
2931 }
2932
2933 void image_zoom_set_fill_geometry(ImageWindow *imd, gint vertical)
2934 {
2935         gdouble zoom;
2936
2937         if (!imd->pixbuf || imd->image_width < 1 || imd->image_height < 1) return;
2938
2939         if (vertical)
2940                 {
2941                 zoom = (gdouble)imd->window_height / imd->image_height;
2942                 }
2943         else
2944                 {
2945                 zoom = (gdouble)imd->window_width / imd->image_width;
2946                 }
2947
2948         if (zoom < 1.0)
2949                 {
2950                 zoom = 0.0 - 1.0 / zoom;
2951                 }
2952
2953         image_zoom_set(imd, zoom);
2954 }
2955
2956 gdouble image_zoom_get(ImageWindow *imd)
2957 {
2958         return imd->zoom;
2959 }
2960
2961 gdouble image_zoom_get_real(ImageWindow *imd)
2962 {
2963         return imd->scale;
2964 }
2965
2966 gchar *image_zoom_get_as_text(ImageWindow *imd)
2967 {
2968         gdouble l = 1.0;
2969         gdouble r = 1.0;
2970         gint pl = 0;
2971         gint pr = 0;
2972         gchar *approx = " ";
2973
2974         if (imd->zoom > 0.0)
2975                 {
2976                 l = imd->zoom;
2977                 }
2978         else if (imd->zoom < 0.0)
2979                 {
2980                 r = 0.0 - imd->zoom;
2981                 }
2982         else if (imd->zoom == 0.0 && imd->scale != 0.0)
2983                 {
2984                 if (imd->scale >= 1.0)
2985                         {
2986                         l = imd->scale;
2987                         }
2988                 else
2989                         {
2990                         r = 1.0 / imd->scale;
2991                         }
2992                 approx = " ~";
2993                 }
2994
2995         if (rint(l) != l) pl = 1;
2996         if (rint(r) != r) pr = 1;
2997
2998         return g_strdup_printf("%.*f :%s%.*f", pl, l, approx, pr, r);
2999 }
3000
3001 gdouble image_zoom_get_default(ImageWindow *imd, gint mode)
3002 {
3003         gdouble zoom;
3004
3005         if (mode == ZOOM_RESET_ORIGINAL)
3006                 {
3007                 zoom = 1.0;
3008                 }
3009         else if (mode == ZOOM_RESET_FIT_WINDOW)
3010                 {
3011                 zoom = 0.0;
3012                 }
3013         else
3014                 {
3015                 if (imd)
3016                         {
3017                         zoom = image_zoom_get(imd);
3018                         }
3019                 else
3020                         {
3021                         zoom = 1.0;
3022                         }
3023                 }
3024
3025         return zoom;
3026 }
3027
3028 /* read ahead */
3029
3030 void image_prebuffer_set(ImageWindow *imd, const gchar *path)
3031 {
3032         if (path)
3033                 {
3034                 image_read_ahead_set(imd, path);
3035                 }
3036         else
3037                 {
3038                 image_read_ahead_cancel(imd);
3039                 }
3040 }
3041
3042 static gint image_auto_refresh_cb(gpointer data)
3043 {
3044         ImageWindow *imd = data;
3045         time_t newtime;
3046         
3047         if (!imd || !imd->pixbuf ||
3048             imd->il || !imd->image_path ||
3049             !update_on_time_change) return TRUE;
3050
3051         newtime = filetime(imd->image_path);
3052         if (newtime > 0 && newtime != imd->mtime)
3053                 {
3054                 imd->mtime = newtime;
3055                 image_reload(imd);
3056                 }
3057
3058         return TRUE;
3059 }
3060
3061 /* image auto refresh on time stamp change, in 1/1000's second, -1 disables */
3062
3063 void image_auto_refresh(ImageWindow *imd, gint interval)
3064 {
3065         if (!imd) return;
3066
3067         if (imd->auto_refresh_id > -1)
3068                 {
3069                 g_source_remove(imd->auto_refresh_id);
3070                 imd->auto_refresh_id = -1;
3071                 imd->auto_refresh_interval = -1;
3072                 }
3073
3074         if (interval < 0) return;
3075
3076         if (interval == 0) interval = IMAGE_AUTO_REFRESH_TIME;
3077
3078         imd->auto_refresh_id = g_timeout_add((guint32)interval, image_auto_refresh_cb, imd);
3079         imd->auto_refresh_interval = interval;
3080 }
3081
3082 /* allow top window to be resized ? */
3083
3084 void image_top_window_set_sync(ImageWindow *imd, gint allow_sync)
3085 {
3086         imd->top_window_sync = allow_sync;
3087 }
3088
3089 /* background colors */
3090
3091 void image_background_set_black(ImageWindow *imd, gint black)
3092 {
3093         GtkStyle *style;
3094
3095         if (!imd) return;
3096
3097         style = gtk_style_copy(gtk_widget_get_style(imd->widget));
3098         g_object_ref(G_OBJECT(style));
3099
3100         if (black)
3101                 {
3102                 style->bg[GTK_STATE_NORMAL] = style->black;
3103                 }
3104
3105         gtk_widget_set_style(imd->image, style);
3106         g_object_unref(G_OBJECT(style));
3107
3108         if (GTK_WIDGET_VISIBLE(imd->widget)) image_border_clear(imd);
3109 }
3110
3111 void image_background_set_color(ImageWindow *imd, GdkColor *color)
3112 {
3113         GtkStyle *style;
3114
3115         if (!imd) return;
3116
3117         style = gtk_style_copy(gtk_widget_get_style(imd->widget));
3118         g_object_ref(G_OBJECT(style));
3119
3120         if (color)
3121                 {
3122                 GdkColor *slot;
3123
3124                 slot = &style->bg[GTK_STATE_NORMAL];
3125
3126                 slot->red = color->red;
3127                 slot->green = color->green;
3128                 slot->blue = color->blue;
3129                 }
3130
3131         gtk_widget_set_style(imd->image, style);
3132         g_object_unref(G_OBJECT(style));
3133
3134         if (GTK_WIDGET_VISIBLE(imd->widget)) image_border_clear(imd);
3135 }
3136
3137 void image_set_delay_flip(ImageWindow *imd, gint delay)
3138 {
3139         if (!imd ||
3140             imd->delay_flip == delay) return;
3141
3142         imd->delay_flip = delay;
3143         if (!imd->delay_flip && imd->il)
3144                 {
3145                 if (imd->pixbuf) g_object_unref(imd->pixbuf);
3146                 imd->pixbuf = NULL;
3147                 image_load_pixbuf_ready(imd);
3148
3149                 image_queue_clear(imd);
3150                 image_queue(imd, 0, 0, imd->width, imd->height, FALSE, TILE_RENDER_AREA, TRUE);
3151                 }
3152 }
3153
3154 /* wallpaper util */
3155
3156 void image_to_root_window(ImageWindow *imd, gint scaled)
3157 {
3158         GdkScreen *screen;
3159         GdkWindow *rootwindow;
3160         GdkPixmap *pixmap;
3161         GdkPixbuf *pb;
3162
3163         if (!imd || !imd->pixbuf) return;
3164
3165
3166         screen = gtk_widget_get_screen(imd->image);
3167         rootwindow = gdk_screen_get_root_window(screen);
3168         if (gdk_drawable_get_visual(rootwindow) != gdk_visual_get_system()) return;
3169
3170         if (scaled)
3171                 {
3172                 pb = gdk_pixbuf_scale_simple(imd->pixbuf, gdk_screen_width(), gdk_screen_height(), (GdkInterpType)zoom_quality);
3173                 }
3174         else
3175                 {
3176                 pb = gdk_pixbuf_scale_simple(imd->pixbuf, imd->width, imd->height, (GdkInterpType)zoom_quality);
3177                 }
3178
3179         gdk_pixbuf_render_pixmap_and_mask (pb, &pixmap, NULL, 128);
3180         gdk_window_set_back_pixmap(rootwindow, pixmap, FALSE);
3181         gdk_window_clear(rootwindow);
3182         g_object_unref(pb);
3183         g_object_unref(pixmap);
3184
3185         gdk_flush();
3186 }
3187
3188
3189 /*
3190  *-------------------------------------------------------------------
3191  * init / destroy
3192  *-------------------------------------------------------------------
3193  */
3194
3195 static void image_free(ImageWindow *imd)
3196 {
3197         image_read_ahead_cancel(imd);
3198         image_post_buffer_set(imd, NULL, NULL);
3199         image_auto_refresh(imd, -1);
3200
3201         g_free(imd->image_path);
3202         g_free(imd->title);
3203         g_free(imd->title_right);
3204
3205         image_reset(imd);
3206         image_tile_sync_count(imd, 0);
3207         if (imd->pixbuf) g_object_unref(imd->pixbuf);
3208
3209         image_scroller_timer_set(imd, FALSE);
3210
3211         image_overlay_list_clear(imd);
3212
3213         g_free(imd);
3214 }
3215
3216 static void image_destroy_cb(GtkObject *widget, gpointer data)
3217 {
3218         ImageWindow *imd = data;
3219         image_free(imd);
3220 }
3221
3222 ImageWindow *image_new(gint frame)
3223 {
3224         ImageWindow *imd;
3225
3226         imd = g_new0(ImageWindow, 1);
3227         imd->zoom = 1.0;
3228         imd->scale = 1.0;
3229
3230         imd->draw_idle_id = -1;
3231
3232         imd->tile_width = IMAGE_TILE_SIZE;
3233         imd->tile_height = IMAGE_TILE_SIZE;
3234
3235         imd->top_window = NULL;
3236         imd->title = NULL;
3237         imd->title_right = NULL;
3238         imd->title_show_zoom = FALSE;
3239
3240         imd->unknown = TRUE;
3241
3242         imd->pixbuf = NULL;
3243
3244         imd->has_frame = frame;
3245         imd->top_window_sync = FALSE;
3246
3247         imd->tile_cache = NULL;
3248         imd->tile_cache_size = 0;
3249
3250         imd->delay_alter_type = ALTER_NONE;
3251
3252         imd->read_ahead_il = NULL;
3253         imd->read_ahead_pixbuf = NULL;
3254         imd->read_ahead_path = NULL;
3255
3256         imd->completed = FALSE;
3257
3258         imd->auto_refresh_id = -1;
3259         imd->auto_refresh_interval = -1;
3260
3261         imd->delay_flip = FALSE;
3262
3263         imd->func_update = NULL;
3264         imd->func_complete = NULL;
3265
3266         imd->func_button = NULL;
3267         imd->func_scroll = NULL;
3268
3269         imd->scroller_id = -1;
3270         imd->scroller_overlay = -1;
3271
3272         imd->image = gtk_drawing_area_new();
3273         gtk_widget_set_double_buffered(imd->image, FALSE);
3274
3275         if (imd->has_frame)
3276                 {
3277                 imd->widget = gtk_frame_new(NULL);
3278                 gtk_frame_set_shadow_type(GTK_FRAME(imd->widget), GTK_SHADOW_IN);
3279                 gtk_container_add(GTK_CONTAINER(imd->widget), imd->image);
3280                 gtk_widget_show(imd->image);
3281
3282                 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_CAN_FOCUS);
3283                 g_signal_connect(G_OBJECT(imd->widget), "focus_in_event",
3284                                  G_CALLBACK(image_focus_in_cb), imd);
3285                 g_signal_connect(G_OBJECT(imd->widget), "focus_out_event",
3286                                  G_CALLBACK(image_focus_out_cb), imd);
3287
3288                 g_signal_connect_after(G_OBJECT(imd->widget), "expose_event",
3289                                        G_CALLBACK(image_focus_expose), imd);
3290                 }
3291         else
3292                 {
3293                 imd->widget = imd->image;
3294                 }
3295
3296         g_signal_connect(G_OBJECT(imd->image), "motion_notify_event",
3297                          G_CALLBACK(image_mouse_motion_cb), imd);
3298         g_signal_connect(G_OBJECT(imd->image), "button_press_event",
3299                          G_CALLBACK(image_mouse_press_cb), imd);
3300         g_signal_connect(G_OBJECT(imd->image), "button_release_event",
3301                          G_CALLBACK(image_mouse_release_cb), imd);
3302         g_signal_connect(G_OBJECT(imd->image), "leave_notify_event",
3303                          G_CALLBACK(image_mouse_leave_cb), imd);
3304         gtk_widget_set_events(imd->image, GDK_POINTER_MOTION_MASK |
3305                                           GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK |
3306                                           GDK_LEAVE_NOTIFY_MASK);
3307
3308         g_signal_connect(G_OBJECT(imd->image), "expose_event",
3309                          G_CALLBACK(image_expose_cb), imd);
3310         g_signal_connect_after(G_OBJECT(imd->image), "size_allocate",
3311                          G_CALLBACK(image_size_cb), imd);
3312
3313         g_signal_connect(G_OBJECT(imd->image), "drag_begin",
3314                          G_CALLBACK(image_mouse_drag_cb), imd);
3315         g_signal_connect(G_OBJECT(imd->image), "scroll_event",
3316                          G_CALLBACK(image_scroll_cb), imd);
3317
3318         g_signal_connect(G_OBJECT(imd->widget), "destroy",
3319                          G_CALLBACK(image_destroy_cb), imd);
3320
3321         return imd;
3322 }
3323