##### Note: GQview CVS on sourceforge is not always up to date, please use #####
[geeqie.git] / src / pan-view.c
1 /*
2  * GQview
3  * (C) 2005 John Ellis
4  *
5  * Author: John Ellis
6  *
7  * This software is released under the GNU General Public License (GNU GPL).
8  * Please read the included file COPYING for more information.
9  * This software comes with no warranty of any kind, use at your own risk!
10  */
11
12
13 #include "gqview.h"
14 #include "pan-view.h"
15
16 #include "cache.h"
17 #include "dnd.h"
18 #include "editors.h"
19 #include "filelist.h"
20 #include "fullscreen.h"
21 #include "image.h"
22 #include "image-load.h"
23 #include "img-view.h"
24 #include "info.h"
25 #include "menu.h"
26 #include "pixbuf_util.h"
27 #include "thumb.h"
28 #include "utilops.h"
29 #include "ui_bookmark.h"
30 #include "ui_fileops.h"
31 #include "ui_menu.h"
32 #include "ui_misc.h"
33 #include "ui_tabcomp.h"
34
35 #include <gdk/gdkkeysyms.h> /* for keyboard values */
36 #include <math.h>
37
38
39 #define PAN_WINDOW_DEFAULT_WIDTH 720
40 #define PAN_WINDOW_DEFAULT_HEIGHT 500
41
42 #define PAN_TILE_SIZE 512
43
44 #define PAN_THUMB_SIZE_NONE 24
45 #define PAN_THUMB_SIZE_SMALL 64
46 #define PAN_THUMB_SIZE_NORMAL 128
47 #define PAN_THUMB_SIZE_LARGE 256
48 #define PAN_THUMB_SIZE pw->thumb_size
49
50 #define PAN_THUMB_GAP_SMALL 14
51 #define PAN_THUMB_GAP_NORMAL 30
52 #define PAN_THUMB_GAP_LARGE 40
53 #define PAN_THUMB_GAP_HUGE 50
54 #define PAN_THUMB_GAP pw->thumb_gap
55
56 #define PAN_SHADOW_OFFSET 6
57 #define PAN_SHADOW_FADE 5
58 #define PAN_SHADOW_COLOR 0, 0, 0
59 #define PAN_SHADOW_ALPHA 64
60
61 #define PAN_OUTLINE_THICKNESS 1
62 #define PAN_OUTLINE_COLOR_1 255, 255, 255
63 #define PAN_OUTLINE_COLOR_2 64, 64, 64
64 #define PAN_OUTLINE_ALPHA 180
65
66 #define PAN_BACKGROUND_COLOR 255, 255, 230
67
68 #define PAN_GRID_SIZE 10
69 #define PAN_GRID_COLOR 0, 0, 0
70 #define PAN_GRID_ALPHA 20
71
72 #define PAN_FOLDER_BOX_COLOR 0, 0, 255
73 #define PAN_FOLDER_BOX_ALPHA 10
74 #define PAN_FOLDER_BOX_BORDER 20
75
76 #define PAN_FOLDER_BOX_OUTLINE_THICKNESS 4
77 #define PAN_FOLDER_BOX_OUTLINE_COLOR 0, 0, 255
78 #define PAN_FOLDER_BOX_OUTLINE_ALPHA 64
79
80 #define PAN_TEXT_BORDER_SIZE 4
81 #define PAN_TEXT_COLOR 0, 0, 0
82
83 #define PAN_POPUP_COLOR 255, 255, 220
84 #define PAN_POPUP_ALPHA 255
85 #define PAN_POPUP_BORDER 1
86 #define PAN_POPUP_BORDER_COLOR 0, 0, 0
87 #define PAN_POPUP_TEXT_COLOR 0, 0, 0
88
89 #define PAN_GROUP_MAX 16
90
91 #define ZOOM_INCREMENT 1.0
92 #define ZOOM_LABEL_WIDTH 64
93
94
95 typedef enum {
96         LAYOUT_TIMELINE = 0,
97         LAYOUT_FOLDERS_LINEAR,
98         LAYOUT_FOLDERS_FLOWER,
99         LAYOUT_GRID,
100 } LayoutType;
101
102 typedef enum {
103         LAYOUT_SIZE_THUMB_NONE = 0,
104         LAYOUT_SIZE_THUMB_SMALL,
105         LAYOUT_SIZE_THUMB_NORMAL,
106         LAYOUT_SIZE_THUMB_LARGE,
107         LAYOUT_SIZE_10,
108         LAYOUT_SIZE_25,
109         LAYOUT_SIZE_33,
110         LAYOUT_SIZE_50,
111         LAYOUT_SIZE_100
112 } LayoutSize;
113
114 typedef enum {
115         ITEM_NONE,
116         ITEM_THUMB,
117         ITEM_BOX,
118         ITEM_TRIANGLE,
119         ITEM_TEXT,
120         ITEM_IMAGE
121 } ItemType;
122
123 typedef enum {
124         TEXT_ATTR_NONE = 0,
125         TEXT_ATTR_BOLD = 1 << 0,
126         TEXT_ATTR_HEADING = 1 << 1,
127         TEXT_ATTR_MARKUP = 1 << 2
128 } TextAttrType;
129
130 enum {
131         BORDER_NONE = 0,
132         BORDER_1 = 1 << 0,
133         BORDER_2 = 1 << 1,
134         BORDER_3 = 1 << 2,
135         BORDER_4 = 1 << 3
136 };
137
138 typedef struct _PanItem PanItem;
139 struct _PanItem {
140         ItemType type;
141         gint x;
142         gint y;
143         gint width;
144         gint height;
145         gchar *key;
146
147         FileData *fd;
148
149         GdkPixbuf *pixbuf;
150         gint refcount;
151
152         gchar *text;
153         TextAttrType text_attr;
154
155         guint8 color_r;
156         guint8 color_g;
157         guint8 color_b;
158         guint8 color_a;
159
160         guint8 color2_r;
161         guint8 color2_g;
162         guint8 color2_b;
163         guint8 color2_a;
164         gint border;
165
166         gpointer data;
167
168         gint queued;
169 };
170
171 typedef struct _PanWindow PanWindow;
172 struct _PanWindow
173 {
174         GtkWidget *window;
175         ImageWindow *imd;
176         ImageWindow *imd_normal;
177         FullScreenData *fs;
178
179         GtkWidget *path_entry;
180
181         GtkWidget *label_message;
182         GtkWidget *label_zoom;
183
184         GtkWidget *search_box;
185         GtkWidget *search_entry;
186         GtkWidget *search_label;
187         GtkWidget *search_button;
188         GtkWidget *search_button_arrow;
189
190         GtkWidget *scrollbar_h;
191         GtkWidget *scrollbar_v;
192
193         gint overlay_id;
194
195         gchar *path;
196         LayoutType layout;
197         LayoutSize size;
198         gint thumb_size;
199         gint thumb_gap;
200         gint image_size;
201
202         GList *list;
203
204         GList *cache_list;
205         GList *cache_todo;
206         gint cache_count;
207         gint cache_total;
208         gint cache_tick;
209
210         ImageLoader *il;
211         ThumbLoader *tl;
212         PanItem *queue_pi;
213         GList *queue;
214
215         PanItem *click_pi;
216
217         gint idle_id;
218 };
219
220 typedef struct _PanCacheData PanCacheData;
221 struct _PanCacheData {
222         FileData fd;
223         CacheData *cd;
224 };
225
226
227 static GList *pan_window_list = NULL;
228
229
230 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend);
231
232 static GtkWidget *pan_popup_menu(PanWindow *pw);
233 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off);
234 static void pan_overlay_toggle(PanWindow *pw);
235
236 static void pan_window_close(PanWindow *pw);
237
238 static void pan_window_dnd_init(PanWindow *pw);
239
240
241 static gint util_clip_region(gint x, gint y, gint w, gint h,
242                              gint clip_x, gint clip_y, gint clip_w, gint clip_h,
243                              gint *rx, gint *ry, gint *rw, gint *rh)
244 {
245         if (clip_x + clip_w <= x ||
246             clip_x >= x + w ||
247             clip_y + clip_h <= y ||
248             clip_y >= y + h)
249                 {
250                 return FALSE;
251                 }
252
253         *rx = MAX(x, clip_x);
254         *rw = MIN((x + w), (clip_x + clip_w)) - *rx;
255
256         *ry = MAX(y, clip_y);
257         *rh = MIN((y + h), (clip_y + clip_h)) - *ry;
258
259         return TRUE;
260 }
261
262 static gint util_clip_region_test(gint x, gint y, gint w, gint h,
263                                   gint clip_x, gint clip_y, gint clip_w, gint clip_h)
264 {
265         gint rx, ry, rw, rh;
266
267         return util_clip_region(x, y, w, h,
268                                 clip_x, clip_y, clip_w, clip_h,
269                                 &rx, &ry, &rw, &rh);
270 }
271
272 typedef enum {
273         DATE_LENGTH_EXACT,
274         DATE_LENGTH_HOUR,
275         DATE_LENGTH_DAY,
276         DATE_LENGTH_WEEK,
277         DATE_LENGTH_MONTH,
278         DATE_LENGTH_YEAR
279 } DateLengthType;
280
281 static gint date_compare(time_t a, time_t b, DateLengthType length)
282 {
283         struct tm ta;
284         struct tm tb;
285
286         if (length == DATE_LENGTH_EXACT) return (a == b);
287
288         if (!localtime_r(&a, &ta) ||
289             !localtime_r(&b, &tb)) return FALSE;
290
291         if (ta.tm_year != tb.tm_year) return FALSE;
292         if (length == DATE_LENGTH_YEAR) return TRUE;
293
294         if (ta.tm_mon != tb.tm_mon) return FALSE;
295         if (length == DATE_LENGTH_MONTH) return TRUE;
296
297         if (length == DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
298
299         if (ta.tm_mday != tb.tm_mday) return FALSE;
300         if (length == DATE_LENGTH_DAY) return TRUE;
301
302         return (ta.tm_hour == tb.tm_hour);
303 }
304
305 static gchar *date_value_string(time_t d, DateLengthType length)
306 {
307         struct tm td;
308         gchar buf[128];
309         gchar *format = NULL;
310
311         if (!localtime_r(&d, &td)) return g_strdup("");
312
313         switch (length)
314                 {
315                 case DATE_LENGTH_DAY:
316                         return g_strdup_printf("%d", td.tm_mday);
317                         break;
318                 case DATE_LENGTH_WEEK:
319                         format = "%A %e";
320                         break;
321                 case DATE_LENGTH_MONTH:
322                         format = "%B %Y";
323                         break;
324                 case DATE_LENGTH_YEAR:
325                         return g_strdup_printf("%d", td.tm_year + 1900);
326                         break;
327                 case DATE_LENGTH_EXACT:
328                 default:
329                         return g_strdup(text_from_time(d));
330                         break;
331                 }
332
333
334         if (format && strftime(buf, sizeof(buf), format, &td) > 0)
335                 {
336                 gchar *ret = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
337                 if (ret) return ret;
338                 }
339
340         return g_strdup("");
341 }
342
343 static time_t date_to_time(gint year, gint month, gint day)
344 {
345         struct tm lt;
346
347         lt.tm_sec = 0;
348         lt.tm_min = 0;
349         lt.tm_hour = 0;
350         lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
351         lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
352         lt.tm_year = year - 1900;
353         lt.tm_isdst = 0;
354
355         return mktime(&lt);
356 }
357
358 /*
359  *-----------------------------------------------------------------------------
360  * drawing utils
361  *-----------------------------------------------------------------------------
362  */
363
364 static void triangle_rect_region(gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
365                                  gint *rx, gint *ry, gint *rw, gint *rh)
366 {
367         gint tx, ty, tw, th;
368
369         tx = MIN(x1, x2);
370         tx = MIN(tx, x3);
371         ty = MIN(y1, y2);
372         ty = MIN(ty, y3);
373         tw = MAX(abs(x1 - x2), abs(x2 - x3));
374         tw = MAX(tw, abs(x3 - x1));
375         th = MAX(abs(y1 - y2), abs(y2 - y3));
376         th = MAX(th, abs(y3 - y1));
377
378         *rx = tx;
379         *ry = ty;
380         *rw = tw;
381         *rh = th;
382 }
383
384 static void pixbuf_draw_triangle(GdkPixbuf *pb,
385                                  gint clip_x, gint clip_y, gint clip_w, gint clip_h,
386                                  gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
387                                  guint8 r, guint8 g, guint8 b, guint8 a)
388 {
389         gint p_alpha;
390         gint pw, ph, prs;
391         gint rx, ry, rw, rh;
392         gint tx, ty, tw, th;
393         gint fx1, fy1;
394         gint fx2, fy2;
395         gint fw, fh;
396         guchar *p_pix;
397         guchar *pp;
398         gint p_step;
399         gint i, j;
400
401         if (!pb) return;
402
403         pw = gdk_pixbuf_get_width(pb);
404         ph = gdk_pixbuf_get_height(pb);
405
406         if (!util_clip_region(0, 0, pw, ph,
407                               clip_x, clip_y, clip_w, clip_h,
408                               &rx, &ry, &rw, &rh)) return;
409
410         triangle_rect_region(x1, y1, x2, y2, x3, y3,
411                              &tx, &ty, &tw, &th);
412
413         if (!util_clip_region(rx, ry, rw, rh,
414                               tx, ty, tw, th,
415                               &fx1, &fy1, &fw, &fh)) return;
416         fx2 = fx1 + fw;
417         fy2 = fy1 + fh;
418
419         p_alpha = gdk_pixbuf_get_has_alpha(pb);
420         prs = gdk_pixbuf_get_rowstride(pb);
421         p_pix = gdk_pixbuf_get_pixels(pb);
422
423         p_step = (p_alpha) ? 4 : 3;
424         for (i = fy1; i < fy2; i++)
425                 {
426                 pp = p_pix + i * prs + (fx1 * p_step);
427                 for (j = fx1; j < fx2; j++)
428                         {
429                         gint z1, z2;
430
431                         z1 = (y1 - y2)*(j - x2) + (x2 - x1)*(i - y2);
432                         z2 = (y2 - y3)*(j - x3) + (x3 - x2)*(i - y3);
433                         if ((z1 ^ z2) >= 0)
434                                 {
435                                 z2 = (y3 - y1)*(j - x1) + (x1 - x3)*(i - y1);
436                                 if ((z1 ^ z2) >= 0)
437                                         {
438                                         pp[0] = (r * a + pp[0] * (256-a)) >> 8;
439                                         pp[1] = (g * a + pp[1] * (256-a)) >> 8;
440                                         pp[2] = (b * a + pp[2] * (256-a)) >> 8;
441                                         }
442                                 }
443                         pp += p_step;
444                         }
445                 }
446 }
447
448 static void pixbuf_draw_line(GdkPixbuf *pb,
449                              gint clip_x, gint clip_y, gint clip_w, gint clip_h,
450                              gint x1, gint y1, gint x2, gint y2,
451                              guint8 r, guint8 g, guint8 b, guint8 a)
452 {
453         gint p_alpha;
454         gint pw, ph, prs;
455         gint rx, ry, rw, rh;
456         gint fx1, fy1, fx2, fy2;
457         guchar *p_pix;
458         guchar *pp;
459         gint p_step;
460         gint xd, yd;
461         gint xa, ya;
462         gdouble xstep, ystep;
463         gdouble i, j;
464         gint n, nt;
465         gint x, y;
466
467         if (!pb) return;
468
469         pw = gdk_pixbuf_get_width(pb);
470         ph = gdk_pixbuf_get_height(pb);
471
472         if (!util_clip_region(0, 0, pw, ph,
473                               clip_x, clip_y, clip_w, clip_h,
474                               &rx, &ry, &rw, &rh)) return;
475
476         fx1 = rx;
477         fy1 = ry;
478         fx2 = rx + rw;
479         fy2 = ry + rh;
480
481         xd = x2 - x1;
482         yd = y2 - y1;
483         xa = abs(xd);
484         ya = abs(yd);
485
486         if (xa == 0 && ya == 0) return;
487 #if 0
488         nt = sqrt(xd * xd + yd * yd);
489 #endif
490         nt = (xa > ya) ? xa : ya;
491         xstep = (double)xd / nt;
492         ystep = (double)yd / nt;
493
494         p_alpha = gdk_pixbuf_get_has_alpha(pb);
495         prs = gdk_pixbuf_get_rowstride(pb);
496         p_pix = gdk_pixbuf_get_pixels(pb);
497
498         p_step = (p_alpha) ? 4 : 3;
499
500         i = (double)y1;
501         j = (double)x1;
502         for (n = 0; n < nt; n++)
503                 {
504                 x = (gint)(j + 0.5);
505                 y = (gint)(i + 0.5);
506
507                 if (x >= fx1 && x < fx2 &&
508                     y >= fy1 && y < fy2)
509                         {
510                         pp = p_pix + y * prs + x * p_step;
511                         *pp = (r * a + *pp * (256-a)) >> 8;
512                         pp++;
513                         *pp = (g * a + *pp * (256-a)) >> 8;
514                         pp++;
515                         *pp = (b * a + *pp * (256-a)) >> 8;
516                         }
517                 i += ystep;
518                 j += xstep;
519                 }
520 }
521
522 static void pixbuf_draw_fade_linear(guchar *p_pix, gint prs, gint p_alpha,
523                                     gint s, gint vertical, gint border,
524                                     gint x1, gint y1, gint x2, gint y2,
525                                     guint8 r, guint8 g, guint8 b, guint8 a)
526 {
527         guchar *pp;
528         gint p_step;
529         guint8 n = a;
530         gint i, j;
531
532         p_step = (p_alpha) ? 4 : 3;
533         for (j = y1; j < y2; j++)
534                 {
535                 pp = p_pix + j * prs + x1 * p_step;
536                 if (!vertical) n = a - a * abs(j - s) / border;
537                 for (i = x1; i < x2; i++)
538                         {
539                         if (vertical) n = a - a * abs(i - s) / border;
540                         *pp = (r * n + *pp * (256-n)) >> 8;
541                         pp++;
542                         *pp = (g * n + *pp * (256-n)) >> 8;
543                         pp++;
544                         *pp = (b * n + *pp * (256-n)) >> 8;
545                         pp++;
546                         if (p_alpha) pp++;
547                         }
548                 }
549 }
550
551 static void pixbuf_draw_fade_radius(guchar *p_pix, gint prs, gint p_alpha,
552                                     gint sx, gint sy, gint border,
553                                     gint x1, gint y1, gint x2, gint y2,
554                                     guint8 r, guint8 g, guint8 b, guint8 a)
555 {
556         guchar *pp;
557         gint p_step;
558         gint i, j;
559
560         p_step = (p_alpha) ? 4 : 3;
561         for (j = y1; j < y2; j++)
562                 {
563                 pp = p_pix + j * prs + x1 * p_step;
564                 for (i = x1; i < x2; i++)
565                         {
566                         guint8 n;
567                         gint r;
568
569                         r = MIN(border, (gint)sqrt((i-sx)*(i-sx) + (j-sy)*(j-sy)));
570                         n = a - a * r / border;
571                         *pp = (r * n + *pp * (256-n)) >> 8;
572                         pp++;
573                         *pp = (g * n + *pp * (256-n)) >> 8;
574                         pp++;
575                         *pp = (b * n + *pp * (256-n)) >> 8;
576                         pp++;
577                         if (p_alpha) pp++;
578                         }
579                 }
580 }
581
582 static void pixbuf_draw_shadow(GdkPixbuf *pb,
583                                gint clip_x, gint clip_y, gint clip_w, gint clip_h,
584                                gint x, gint y, gint w, gint h, gint border,
585                                guint8 r, guint8 g, guint8 b, guint8 a)
586 {
587         gint p_alpha;
588         gint pw, ph, prs;
589         gint rx, ry, rw, rh;
590         gint fx, fy, fw, fh;
591         guchar *p_pix;
592
593         if (!pb) return;
594
595         pw = gdk_pixbuf_get_width(pb);
596         ph = gdk_pixbuf_get_height(pb);
597
598         if (!util_clip_region(0, 0, pw, ph,
599                               clip_x, clip_y, clip_w, clip_h,
600                               &rx, &ry, &rw, &rh)) return;
601
602         p_alpha = gdk_pixbuf_get_has_alpha(pb);
603         prs = gdk_pixbuf_get_rowstride(pb);
604         p_pix = gdk_pixbuf_get_pixels(pb);
605
606         if (util_clip_region(x + border, y + border, w - border * 2, h - border * 2,
607                              rx, ry, rw, rh,
608                              &fx, &fy, &fw, &fh))
609                 {
610                 pixbuf_draw_rect_fill(pb, fx, fy, fw, fh, r, g, b, a);
611                 }
612
613         if (border < 1) return;
614
615         if (util_clip_region(x, y + border, border, h - border * 2,
616                              rx, ry, rw, rh,
617                              &fx, &fy, &fw, &fh))
618                 {
619                 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
620                                         x + border, TRUE, border,
621                                         fx, fy, fx + fw, fy + fh,
622                                         r, g, b, a);
623                 }
624         if (util_clip_region(x + w - border, y + border, border, h - border * 2,
625                              rx, ry, rw, rh,
626                              &fx, &fy, &fw, &fh))
627                 {
628                 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
629                                         x + w - border, TRUE, border,
630                                         fx, fy, fx + fw, fy + fh,
631                                         r, g, b, a);
632                 }
633         if (util_clip_region(x + border, y, w - border * 2, border,
634                              rx, ry, rw, rh,
635                              &fx, &fy, &fw, &fh))
636                 {
637                 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
638                                         y + border, FALSE, border,
639                                         fx, fy, fx + fw, fy + fh,
640                                         r, g, b, a);
641                 }
642         if (util_clip_region(x + border, y + h - border, w - border * 2, border,
643                              rx, ry, rw, rh,
644                              &fx, &fy, &fw, &fh))
645                 {
646                 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
647                                         y + h - border, FALSE, border,
648                                         fx, fy, fx + fw, fy + fh,
649                                         r, g, b, a);
650                 }
651         if (util_clip_region(x, y, border, border,
652                              rx, ry, rw, rh,
653                              &fx, &fy, &fw, &fh))
654                 {
655                 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
656                                         x + border, y + border, border,
657                                         fx, fy, fx + fw, fy + fh,
658                                         r, g, b, a);
659                 }
660         if (util_clip_region(x + w - border, y, border, border,
661                              rx, ry, rw, rh,
662                              &fx, &fy, &fw, &fh))
663                 {
664                 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
665                                         x + w - border, y + border, border,
666                                         fx, fy, fx + fw, fy + fh,
667                                         r, g, b, a);
668                 }
669         if (util_clip_region(x, y + h - border, border, border,
670                              rx, ry, rw, rh,
671                              &fx, &fy, &fw, &fh))
672                 {
673                 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
674                                         x + border, y + h - border, border,
675                                         fx, fy, fx + fw, fy + fh,
676                                         r, g, b, a);
677                 }
678         if (util_clip_region(x + w - border, y + h - border, border, border,
679                              rx, ry, rw, rh,
680                              &fx, &fy, &fw, &fh))
681                 {
682                 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
683                                         x + w - border, y + h - border, border,
684                                         fx, fy, fx + fw, fy + fh,
685                                         r, g, b, a);
686                 }
687 }
688                                 
689
690 /*
691  *-----------------------------------------------------------------------------
692  * cache
693  *-----------------------------------------------------------------------------
694  */
695
696 static void pan_cache_free(PanWindow *pw)
697 {
698         GList *work;
699
700         work = pw->cache_list;
701         while (work)
702                 {
703                 PanCacheData *pc;
704
705                 pc = work->data;
706                 work = work->next;
707
708                 cache_sim_data_free(pc->cd);
709                 file_data_free((FileData *)pc);
710                 }
711
712         g_list_free(pw->cache_list);
713         pw->cache_list = NULL;
714
715         filelist_free(pw->cache_todo);
716         pw->cache_todo = NULL;
717
718         pw->cache_count = 0;
719         pw->cache_total = 0;
720         pw->cache_tick = 0;
721 }
722
723 static void pan_cache_fill(PanWindow *pw, const gchar *path)
724 {
725         GList *list;
726
727         pan_cache_free(pw);
728
729         list = pan_window_layout_list(path, SORT_NAME, TRUE);
730         pw->cache_todo = g_list_reverse(list);
731
732         pw->cache_total = g_list_length(pw->cache_todo);
733 }
734
735 static gint pan_cache_step(PanWindow *pw)
736 {
737         FileData *fd;
738         PanCacheData *pc;
739         CacheData *cd = NULL;
740
741         if (!pw->cache_todo) return FALSE;
742
743         fd = pw->cache_todo->data;
744         pw->cache_todo = g_list_remove(pw->cache_todo, fd);
745
746         if (enable_thumb_caching)
747                 {
748                 gchar *found;
749
750                 found = cache_find_location(CACHE_TYPE_SIM, fd->path);
751                 if (found && filetime(found) == fd->date)
752                         {
753                         cd = cache_sim_data_load(found);
754                         }
755                 g_free(found);
756                 }
757
758         if (!cd) cd = cache_sim_data_new();
759
760         if (!cd->dimensions)
761                 {
762                 cd->dimensions = image_load_dimensions(fd->path, &cd->width, &cd->height);
763                 if (enable_thumb_caching &&
764                     cd->dimensions)
765                         {
766                         gchar *base;
767                         mode_t mode = 0755;
768
769                         base = cache_get_location(CACHE_TYPE_SIM, fd->path, FALSE, &mode);
770                         if (cache_ensure_dir_exists(base, mode))
771                                 {
772                                 g_free(cd->path);
773                                 cd->path = cache_get_location(CACHE_TYPE_SIM, fd->path, TRUE, NULL);
774                                 if (cache_sim_data_save(cd))
775                                         {
776                                         filetime_set(cd->path, filetime(fd->path));
777                                         }
778                                 }
779                         g_free(base);
780                         }
781
782                 pw->cache_tick = 9;
783                 }
784
785         pc = g_new0(PanCacheData, 1);
786         memcpy(pc, fd, sizeof(FileData));
787         g_free(fd);
788
789         pc->cd = cd;
790
791         pw->cache_list = g_list_prepend(pw->cache_list, pc);
792
793         return TRUE;
794 }
795
796
797 /*
798  *-----------------------------------------------------------------------------
799  * item objects
800  *-----------------------------------------------------------------------------
801  */
802
803 static void pan_item_free(PanItem *pi)
804 {
805         if (!pi) return;
806
807         if (pi->pixbuf) g_object_unref(pi->pixbuf);
808         if (pi->fd) file_data_free(pi->fd);
809         g_free(pi->text);
810         g_free(pi->key);
811         g_free(pi->data);
812
813         g_free(pi);
814 }
815
816 static void pan_window_items_free(PanWindow *pw)
817 {
818         GList *work;
819
820         work = pw->list;
821         while (work)
822                 {
823                 PanItem *pi = work->data;
824                 work = work->next;
825
826                 pan_item_free(pi);
827                 }
828
829         g_list_free(pw->list);
830         pw->list = NULL;
831
832         g_list_free(pw->queue);
833         pw->queue = NULL;
834         pw->queue_pi = NULL;
835
836         image_loader_free(pw->il);
837         pw->il = NULL;
838
839         thumb_loader_free(pw->tl);
840         pw->tl = NULL;
841
842         pw->click_pi = NULL;
843 }
844
845 static PanItem *pan_item_new_thumb(PanWindow *pw, FileData *fd, gint x, gint y)
846 {
847         PanItem *pi;
848
849         pi = g_new0(PanItem, 1);
850         pi->type = ITEM_THUMB;
851         pi->fd = fd;
852         pi->x = x;
853         pi->y = y;
854         pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
855         pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
856
857         pi->pixbuf = NULL;
858
859         pi->queued = FALSE;
860
861         pw->list = g_list_prepend(pw->list, pi);
862
863         return pi;
864 }
865
866 static PanItem *pan_item_new_box(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
867                                  gint border_size,
868                                  guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
869                                  guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
870 {
871         PanItem *pi;
872
873         pi = g_new0(PanItem, 1);
874         pi->type = ITEM_BOX;
875         pi->fd = fd;
876         pi->x = x;
877         pi->y = y;
878         pi->width = width;
879         pi->height = height;
880
881         pi->color_r = base_r;
882         pi->color_g = base_g;
883         pi->color_b = base_b;
884         pi->color_a = base_a;
885
886         pi->color2_r = bord_r;
887         pi->color2_g = bord_g;
888         pi->color2_b = bord_b;
889         pi->color2_a = bord_a;
890         pi->border = border_size;
891
892         pw->list = g_list_prepend(pw->list, pi);
893
894         return pi;
895 }
896
897 static void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
898 {
899         gint *shadow;
900
901         if (!pi || pi->type != ITEM_BOX) return;
902
903         shadow = pi->data;
904         if (shadow)
905                 {
906                 pi->width -= shadow[0];
907                 pi->height -= shadow[0];
908                 }
909
910         shadow = g_new0(gint, 2);
911         shadow[0] = offset;
912         shadow[1] = fade;
913
914         pi->width += offset;
915         pi->height += offset;
916
917         g_free(pi->data);
918         pi->data = shadow;
919 }
920
921 static PanItem *pan_item_new_tri(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
922                                  gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
923                                  guint8 r, guint8 g, guint8 b, guint8 a)
924 {
925         PanItem *pi;
926         gint *coord;
927
928         pi = g_new0(PanItem, 1);
929         pi->type = ITEM_TRIANGLE;
930         pi->x = x;
931         pi->y = y;
932         pi->width = width;
933         pi->height = height;
934
935         pi->color_r = r;
936         pi->color_g = g;
937         pi->color_b = b;
938         pi->color_a = a;
939
940         coord = g_new0(gint, 6);
941         coord[0] = x1;
942         coord[1] = y1;
943         coord[2] = x2;
944         coord[3] = y2;
945         coord[4] = x3;
946         coord[5] = y3;
947
948         pi->data = coord;
949
950         pi->border = BORDER_NONE;
951
952         pw->list = g_list_prepend(pw->list, pi);
953
954         return pi;
955 }
956
957 static void pan_item_tri_border(PanItem *pi, gint borders,
958                                 guint8 r, guint8 g, guint8 b, guint8 a)
959 {
960         if (!pi || pi->type != ITEM_TRIANGLE) return;
961
962         pi->border = borders;
963
964         pi->color2_r = r;
965         pi->color2_g = g;
966         pi->color2_b = b;
967         pi->color2_a = a;
968 }
969
970 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
971 {
972         PangoLayout *layout;
973
974         layout = gtk_widget_create_pango_layout(widget, NULL);
975
976         if (pi->text_attr & TEXT_ATTR_MARKUP)
977                 {
978                 pango_layout_set_markup(layout, pi->text, -1);
979                 return layout;
980                 }
981
982         if (pi->text_attr & TEXT_ATTR_BOLD ||
983             pi->text_attr & TEXT_ATTR_HEADING)
984                 {
985                 PangoAttrList *pal;
986                 PangoAttribute *pa;
987                 
988                 pal = pango_attr_list_new();
989                 if (pi->text_attr & TEXT_ATTR_BOLD)
990                         {
991                         pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
992                         pa->start_index = 0;
993                         pa->end_index = G_MAXINT;
994                         pango_attr_list_insert(pal, pa);
995                         }
996                 if (pi->text_attr & TEXT_ATTR_HEADING)
997                         {
998                         pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
999                         pa->start_index = 0;
1000                         pa->end_index = G_MAXINT;
1001                         pango_attr_list_insert(pal, pa);
1002                         }
1003                 pango_layout_set_attributes(layout, pal);
1004                 pango_attr_list_unref(pal);
1005                 }
1006
1007         pango_layout_set_text(layout, pi->text, -1);
1008         return layout;
1009 }
1010
1011 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
1012 {
1013         PangoLayout *layout;
1014
1015         if (!pi || !pi->text || !widget) return;
1016
1017         layout = pan_item_text_layout(pi, widget);
1018         pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
1019         g_object_unref(G_OBJECT(layout));
1020
1021         pi->width += PAN_TEXT_BORDER_SIZE * 2;
1022         pi->height += PAN_TEXT_BORDER_SIZE * 2;
1023 }
1024
1025 static PanItem *pan_item_new_text(PanWindow *pw, gint x, gint y, const gchar *text, TextAttrType attr,
1026                                   guint8 r, guint8 g, guint8 b, guint8 a)
1027 {
1028         PanItem *pi;
1029
1030         pi = g_new0(PanItem, 1);
1031         pi->type = ITEM_TEXT;
1032         pi->x = x;
1033         pi->y = y;
1034         pi->text = g_strdup(text);
1035         pi->text_attr = attr;
1036
1037         pi->color_r = r;
1038         pi->color_g = g;
1039         pi->color_b = b;
1040         pi->color_a = a;
1041
1042         pan_item_text_compute_size(pi, pw->imd->widget);
1043
1044         pw->list = g_list_prepend(pw->list, pi);
1045
1046         return pi;
1047 }
1048
1049 static void pan_item_set_key(PanItem *pi, const gchar *key)
1050 {
1051         gchar *tmp;
1052
1053         if (!pi) return;
1054
1055         tmp = pi->key;
1056         pi->key = g_strdup(key);
1057         g_free(tmp);
1058 }
1059
1060 static void pan_item_added(PanWindow *pw, PanItem *pi)
1061 {
1062         if (!pi) return;
1063         image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1064 }
1065
1066 static void pan_item_remove(PanWindow *pw, PanItem *pi)
1067 {
1068         if (!pi) return;
1069
1070         if (pw->click_pi == pi) pw->click_pi = NULL;
1071
1072         pw->list = g_list_remove(pw->list, pi);
1073         image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1074         pan_item_free(pi);
1075 }
1076
1077 static void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
1078 {
1079         if (!pi || !child) return;
1080
1081         if (pi->x + pi->width < child->x + child->width + border)
1082                 pi->width = child->x + child->width + border - pi->x;
1083
1084         if (pi->y + pi->height < child->y + child->height + border)
1085                 pi->height = child->y + child->height + border - pi->y;
1086 }
1087
1088 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
1089 {
1090         GList *work;
1091
1092         pi->width = w;
1093         pi->height = h;
1094
1095         if (!pi->fd) return;
1096
1097         work = pw->cache_list;
1098         while (work)
1099                 {
1100                 PanCacheData *pc;
1101                 gchar *path;
1102
1103                 pc = work->data;
1104                 work = work->next;
1105
1106                 path = ((FileData *)pc)->path;
1107
1108                 if (pc->cd && pc->cd->dimensions &&
1109                     path && strcmp(path, pi->fd->path) == 0)
1110                         {
1111                         pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
1112                         pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
1113
1114                         pw->cache_list = g_list_remove(pw->cache_list, pc);
1115                         cache_sim_data_free(pc->cd);
1116                         file_data_free((FileData *)pc);
1117                         return;
1118                         }
1119                 }
1120 }
1121
1122 static PanItem *pan_item_new_image(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
1123 {
1124         PanItem *pi;
1125
1126         pi = g_new0(PanItem, 1);
1127         pi->type = ITEM_IMAGE;
1128         pi->fd = fd;
1129         pi->x = x;
1130         pi->y = y;
1131
1132         pan_item_image_find_size(pw, pi, w, h);
1133
1134         pw->list = g_list_prepend(pw->list, pi);
1135
1136         return pi;
1137 }
1138
1139 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
1140 {
1141         GList *work;
1142
1143         if (!key) return NULL;
1144
1145         work = g_list_last(pw->list);
1146         while (work)
1147                 {
1148                 PanItem *pi;
1149
1150                 pi = work->data;
1151                 if ((pi->type == type || type == ITEM_NONE) &&
1152                      pi->key && strcmp(pi->key, key) == 0)
1153                         {
1154                         return pi;
1155                         }
1156                 work = work->prev;
1157                 }
1158
1159         return NULL;
1160 }
1161
1162 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1163 static PanItem *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1164                                       gint ignore_case, gint partial)
1165 {
1166         GList *work;
1167
1168         if (!path) return NULL;
1169         if (partial && path[0] == '/') return NULL;
1170
1171         work = g_list_last(pw->list);
1172         while (work)
1173                 {
1174                 PanItem *pi;
1175
1176                 pi = work->data;
1177                 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1178                         {
1179                         if (path[0] == '/')
1180                                 {
1181                                 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) return pi;
1182                                 }
1183                         else if (pi->fd->name)
1184                                 {
1185                                 if (partial)
1186                                         {
1187                                         if (ignore_case)
1188                                                 {
1189                                                 gchar *haystack;
1190                                                 gint match;
1191
1192                                                 haystack = g_utf8_strdown(pi->fd->name, -1);
1193                                                 match = (strstr(haystack, path) != NULL);
1194                                                 g_free(haystack);
1195                                                 if (match) return pi;
1196                                                 }
1197                                         else
1198                                                 {
1199                                                 if (strstr(pi->fd->name, path)) return pi;
1200                                                 }
1201                                         }
1202                                 else if (ignore_case)
1203                                         {
1204                                         if (strcasecmp(path, pi->fd->name) == 0) return pi;
1205                                         }
1206                                 else
1207                                         {
1208                                         if (strcmp(path, pi->fd->name) == 0) return pi;
1209                                         }
1210                                 }
1211                         }
1212                 work = work->prev;
1213                 }
1214
1215         return NULL;
1216 }
1217
1218 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y)
1219 {
1220         GList *work;
1221
1222         if (x < 0 || x >= pw->imd->image_width ||
1223             y < 0 || y >= pw->imd->image_height) return  NULL;
1224
1225         work = pw->list;
1226         while (work)
1227                 {
1228                 PanItem *pi;
1229
1230                 pi = work->data;
1231                 if ((pi->type == type || type == ITEM_NONE) &&
1232                      x >= pi->x && x < pi->x + pi->width &&
1233                      y >= pi->y && y < pi->y + pi->height)
1234                         {
1235                         return pi;
1236                         }
1237                 work = work->next;
1238                 }
1239
1240         return NULL;
1241 }
1242
1243 /*
1244  *-----------------------------------------------------------------------------
1245  * layout generation
1246  *-----------------------------------------------------------------------------
1247  */
1248
1249 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend)
1250 {
1251         GList *flist = NULL;
1252         GList *dlist = NULL;
1253         GList *result;
1254         GList *folders;
1255
1256         filelist_read(path, &flist, &dlist);
1257         if (sort != SORT_NONE)
1258                 {
1259                 flist = filelist_sort(flist, sort, ascend);
1260                 dlist = filelist_sort(dlist, sort, ascend);
1261                 }
1262
1263         result = flist;
1264         folders = dlist;
1265         while (folders)
1266                 {
1267                 FileData *fd;
1268
1269                 fd = folders->data;
1270                 folders = g_list_remove(folders, fd);
1271
1272                 if (filelist_read(fd->path, &flist, &dlist))
1273                         {
1274                         if (sort != SORT_NONE)
1275                                 {
1276                                 flist = filelist_sort(flist, sort, ascend);
1277                                 dlist = filelist_sort(dlist, sort, ascend);
1278                                 }
1279
1280                         result = g_list_concat(result, flist);
1281                         folders = g_list_concat(dlist, folders);
1282                         }
1283
1284                 file_data_free(fd);
1285                 }
1286
1287         return result;
1288 }
1289
1290 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1291 {
1292         GList *list;
1293         GList *work;
1294         gint x, y;
1295         gint w, h;
1296         gint grid_size;
1297         gint next_y;
1298
1299         list = pan_window_layout_list(path, SORT_NAME, TRUE);
1300
1301         grid_size = (gint)sqrt((double)g_list_length(list));
1302         if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1303                 {
1304                 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1305                 }
1306         else
1307                 {
1308                 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1309                 }
1310
1311         next_y = 0;
1312
1313         w = PAN_THUMB_GAP * 2;
1314         h = PAN_THUMB_GAP * 2;
1315
1316         x = PAN_THUMB_GAP;
1317         y = PAN_THUMB_GAP;
1318         work = list;
1319         while (work)
1320                 {
1321                 FileData *fd;
1322                 PanItem *pi;
1323
1324                 fd = work->data;
1325                 work = work->next;
1326
1327                 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1328                         {
1329                         pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1330
1331                         x += pi->width + PAN_THUMB_GAP;
1332                         if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1333                         if (x > grid_size)
1334                                 {
1335                                 x = PAN_THUMB_GAP;
1336                                 y = next_y;
1337                                 }
1338                         }
1339                 else
1340                         {
1341                         pi = pan_item_new_thumb(pw, fd, x, y);
1342
1343                         x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1344                         if (x > grid_size)
1345                                 {
1346                                 x = PAN_THUMB_GAP;
1347                                 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1348                                 }
1349                         }
1350
1351                 if (w < pi->x + pi->width + PAN_THUMB_GAP) w  = pi->x + pi->width + PAN_THUMB_GAP;
1352                 if (h < pi->y + pi->height + PAN_THUMB_GAP) h = pi->y + pi->height + PAN_THUMB_GAP;
1353                 }
1354
1355         if (width) *width = w;
1356         if (height) *height = h;
1357
1358         g_list_free(list);
1359 }
1360
1361 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1362 {
1363         GList *work;
1364         gint x1, y1, x2, y2;
1365
1366         x1 = 0;
1367         y1 = 0;
1368         x2 = 0;
1369         y2 = 0;
1370
1371         work = pw->list;
1372         while (work)
1373                 {
1374                 PanItem *pi;
1375
1376                 pi = work->data;
1377                 work = work->next;
1378
1379                 if (x1 > pi->x) x1 = pi->x;
1380                 if (y1 > pi->y) y1 = pi->y;
1381                 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1382                 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1383                 }
1384
1385         x1 -= PAN_FOLDER_BOX_BORDER;
1386         y1 -= PAN_FOLDER_BOX_BORDER;
1387         x2 += PAN_FOLDER_BOX_BORDER;
1388         y2 += PAN_FOLDER_BOX_BORDER;
1389
1390         work = pw->list;
1391         while (work)
1392                 {
1393                 PanItem *pi;
1394
1395                 pi = work->data;
1396                 work = work->next;
1397
1398                 pi->x -= x1;
1399                 pi->y -= y1;
1400
1401                 if (pi->type == ITEM_TRIANGLE && pi->data)
1402                         {
1403                         gint *coord;
1404
1405                         coord = pi->data;
1406                         coord[0] -= x1;
1407                         coord[1] -= y1;
1408                         coord[2] -= x1;
1409                         coord[3] -= y1;
1410                         coord[4] -= x1;
1411                         coord[5] -= y1;
1412                         }
1413                 }
1414
1415         if (width) *width = x2 - x1;
1416         if (height) *height = y2 - y1;
1417 }
1418
1419 typedef struct _FlowerGroup FlowerGroup;
1420 struct _FlowerGroup {
1421         GList *items;
1422         GList *children;
1423         gint x;
1424         gint y;
1425         gint width;
1426         gint height;
1427
1428         gdouble angle;
1429         gint circumference;
1430         gint diameter;
1431 };
1432
1433 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1434 {
1435         GList *work;
1436
1437         work = group->items;
1438         while (work)
1439                 {
1440                 PanItem *pi;
1441
1442                 pi = work->data;
1443                 work = work->next;
1444
1445                 pi->x += x;
1446                 pi->y += y;
1447                 }
1448
1449         group->x += x;
1450         group->y += y;
1451 }
1452
1453 #define PI 3.14159
1454
1455 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1456                                                              gint *result_x, gint *result_y)
1457 {
1458         gint x, y;
1459         gint radius;
1460         gdouble a;
1461
1462         radius = parent->circumference / (2*PI);
1463         radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1464
1465         a = 2*PI * group->diameter / parent->circumference;
1466
1467         x = (gint)((double)radius * cos(parent->angle + a / 2));
1468         y = (gint)((double)radius * sin(parent->angle + a / 2));
1469
1470         parent->angle += a;
1471
1472         x += parent->x;
1473         y += parent->y;
1474
1475         x += parent->width / 2;
1476         y += parent->height / 2;
1477
1478         x -= group->width / 2;
1479         y -= group->height / 2;
1480
1481         *result_x = x;
1482         *result_y = y;
1483 }
1484
1485 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1486 {
1487         GList *work;
1488         gint x, y;
1489
1490         if (!group) return;
1491
1492         if (parent && parent->children)
1493                 {
1494                 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1495                 }
1496         else
1497                 {
1498                 x = 0;
1499                 y = 0;
1500                 }
1501
1502         pan_window_layout_compute_folder_flower_move(group, x, y);
1503
1504         if (parent)
1505                 {
1506                 PanItem *pi;
1507                 gint px, py, gx, gy;
1508                 gint x1, y1, x2, y2;
1509
1510                 px = parent->x + parent->width / 2;
1511                 py = parent->y + parent->height / 2;
1512
1513                 gx = group->x + group->width / 2;
1514                 gy = group->y + group->height / 2;
1515
1516                 x1 = MIN(px, gx);
1517                 y1 = MIN(py, gy);
1518
1519                 x2 = MAX(px, gx + 5);
1520                 y2 = MAX(py, gy + 5);
1521
1522                 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1523                                       px, py, gx, gy, gx + 5, gy + 5,
1524                                       255, 40, 40, 128);
1525                 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1526                                     255, 0, 0, 128);
1527                 }
1528
1529         pw->list = g_list_concat(group->items, pw->list);
1530         group->items = NULL;
1531
1532         group->circumference = 0;
1533         work = group->children;
1534         while (work)
1535                 {
1536                 FlowerGroup *child;
1537
1538                 child = work->data;
1539                 work = work->next;
1540
1541                 group->circumference += child->diameter;
1542                 }
1543
1544         work = g_list_last(group->children);
1545         while (work)
1546                 {
1547                 FlowerGroup *child;
1548
1549                 child = work->data;
1550                 work = work->prev;
1551
1552                 pan_window_layout_compute_folder_flower_build(pw, child, group);
1553                 }
1554
1555         g_list_free(group->children);
1556         g_free(group);
1557 }
1558
1559 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1560                                                                   gint x, gint y)
1561 {
1562         FlowerGroup *group;
1563         GList *f;
1564         GList *d;
1565         GList *work;
1566         PanItem *pi_box;
1567         gint x_start;
1568         gint y_height;
1569         gint grid_size;
1570         gint grid_count;
1571
1572         if (!filelist_read(path, &f, &d)) return NULL;
1573         if (!f && !d) return NULL;
1574
1575         f = filelist_sort(f, SORT_NAME, TRUE);
1576         d = filelist_sort(d, SORT_NAME, TRUE);
1577
1578         pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1579                                    PAN_TEXT_COLOR, 255);
1580
1581         y += pi_box->height;
1582
1583         pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1584                                   x, y,
1585                                   PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1586                                   PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1587                                   PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1588                                   PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1589
1590         x += PAN_FOLDER_BOX_BORDER;
1591         y += PAN_FOLDER_BOX_BORDER;
1592
1593         grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1594         grid_count = 0;
1595         x_start = x;
1596         y_height = y;
1597
1598         work = f;
1599         while (work)
1600                 {
1601                 FileData *fd;
1602                 PanItem *pi;
1603
1604                 fd = work->data;
1605                 work = work->next;
1606
1607                 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1608                         {
1609                         pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1610                         x += pi->width + PAN_THUMB_GAP;
1611                         if (pi->height > y_height) y_height = pi->height;
1612                         }
1613                 else
1614                         {
1615                         pi = pan_item_new_thumb(pw, fd, x, y);
1616                         x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1617                         y_height = PAN_THUMB_SIZE;
1618                         }
1619
1620                 grid_count++;
1621                 if (grid_count >= grid_size)
1622                         {
1623                         grid_count = 0;
1624                         x = x_start;
1625                         y += y_height + PAN_THUMB_GAP;
1626                         y_height = 0;
1627                         }
1628
1629                 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1630                 }
1631
1632         g_list_free(f);
1633
1634         group = g_new0(FlowerGroup, 1);
1635         group->items = pw->list;
1636         pw->list = NULL;
1637
1638         group->width = pi_box->width;
1639         group->height = pi_box->y + pi_box->height;
1640         group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1641
1642         group->children = NULL;
1643
1644         work = d;
1645         while (work)
1646                 {
1647                 FileData *fd;
1648                 FlowerGroup *child;
1649
1650                 fd = work->data;
1651                 work = work->next;
1652
1653                 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1654                 if (child) group->children = g_list_prepend(group->children, child);
1655                 }
1656
1657         filelist_free(d);
1658
1659         return group;
1660 }
1661
1662 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1663                                                      gint *width, gint *height,
1664                                                      gint *scroll_x, gint *scroll_y)
1665 {
1666         FlowerGroup *group;
1667         PanItem *pi;
1668
1669         group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1670         pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1671
1672         pan_window_Layout_compute_folders_flower_size(pw, width, height);
1673
1674         pi = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1675         if (pi)
1676                 {
1677                 *scroll_x = pi->x - PAN_FOLDER_BOX_BORDER;
1678                 *scroll_y = pi->y - PAN_FOLDER_BOX_BORDER;
1679                 }
1680 }
1681
1682 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1683                                                           gint *x, gint *y, gint *level,
1684                                                           PanItem *parent,
1685                                                           gint *width, gint *height)
1686 {
1687         GList *f;
1688         GList *d;
1689         GList *work;
1690         PanItem *pi_box;
1691         gint y_height = 0;
1692
1693         if (!filelist_read(path, &f, &d)) return;
1694         if (!f && !d) return;
1695
1696         f = filelist_sort(f, SORT_NAME, TRUE);
1697         d = filelist_sort(d, SORT_NAME, TRUE);
1698
1699         *x = PAN_THUMB_GAP + ((*level) * (PAN_THUMB_GAP * 2));
1700
1701         pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1702                                    PAN_TEXT_COLOR, 255);
1703
1704         *y += pi_box->height;
1705
1706         pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1707                                   *x, *y,
1708                                   PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1709                                   PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1710                                   PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1711                                   PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1712
1713         *x += PAN_FOLDER_BOX_BORDER;
1714         *y += PAN_FOLDER_BOX_BORDER;
1715
1716         work = f;
1717         while (work)
1718                 {
1719                 FileData *fd;
1720                 PanItem *pi;
1721
1722                 fd = work->data;
1723                 work = work->next;
1724
1725                 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1726                         {
1727                         pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1728                         *x += pi->width + PAN_THUMB_GAP;
1729                         if (pi->height > y_height) y_height = pi->height;
1730                         }
1731                 else
1732                         {
1733                         pi = pan_item_new_thumb(pw, fd, *x, *y);
1734                         *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1735                         y_height = PAN_THUMB_SIZE;
1736                         }
1737
1738                 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1739                 }
1740
1741         if (f) *y = pi_box->y + pi_box->height;
1742
1743         g_list_free(f);
1744
1745         work = d;
1746         while (work)
1747                 {
1748                 FileData *fd;
1749
1750                 fd = work->data;
1751                 work = work->next;
1752
1753                 *level = *level + 1;
1754                 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1755                                                               pi_box, width, height);
1756                 *level = *level - 1;
1757                 }
1758
1759         filelist_free(d);
1760
1761         pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1762
1763         if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1764                 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1765
1766         if (*width < pi_box->x + pi_box->width + PAN_FOLDER_BOX_BORDER)
1767                 *width  = pi_box->x + pi_box->width + PAN_FOLDER_BOX_BORDER;
1768         if (*height < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1769                 *height = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1770 }
1771
1772 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1773 {
1774         gint x, y;
1775         gint level;
1776         gint w, h;
1777
1778         level = 0;
1779         x = PAN_FOLDER_BOX_BORDER;
1780         y = PAN_FOLDER_BOX_BORDER;
1781         w = PAN_FOLDER_BOX_BORDER * 2;
1782         h = PAN_FOLDER_BOX_BORDER * 2;
1783
1784         pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1785
1786         if (width) *width = w;
1787         if (height) *height = h;
1788 }
1789
1790 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
1791 {
1792         GList *list;
1793         GList *work;
1794         gint x, y;
1795         gint w, h;
1796         time_t tc;
1797         gint total;
1798         gint count;
1799         PanItem *pi_month = NULL;
1800         PanItem *pi_day = NULL;
1801         gint month_start;
1802         gint day_start;
1803         gint x_width;
1804         gint y_height;
1805
1806         pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
1807
1808         list = pan_window_layout_list(path, SORT_NONE, TRUE);
1809         list = filelist_sort(list, SORT_TIME, TRUE);
1810
1811         w = PAN_FOLDER_BOX_BORDER * 2;
1812         h = PAN_FOLDER_BOX_BORDER * 2;
1813
1814         x = 0;
1815         y = 0;
1816         month_start = y;
1817         day_start = month_start;
1818         x_width = 0;
1819         y_height = 0;
1820         tc = 0;
1821         total = 0;
1822         count = 0;
1823         work = list;
1824         while (work)
1825                 {
1826                 FileData *fd;
1827                 PanItem *pi;
1828
1829                 fd = work->data;
1830                 work = work->next;
1831
1832                 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
1833                         {
1834                         GList *needle;
1835                         gchar *buf;
1836
1837                         if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
1838                                 {
1839                                 pi_day = NULL;
1840
1841                                 if (pi_month)
1842                                         {
1843                                         x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
1844                                         }
1845                                 else
1846                                         {
1847                                         x = PAN_FOLDER_BOX_BORDER;
1848                                         }
1849
1850                                 y = PAN_FOLDER_BOX_BORDER;
1851
1852                                 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
1853                                 pi = pan_item_new_text(pw, x, y, buf,
1854                                                        TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1855                                                        PAN_TEXT_COLOR, 255);
1856                                 y += pi->height;
1857
1858                                 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
1859                                                             x, y, 0, 0,
1860                                                             PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1861                                                             PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1862                                                             PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1863
1864                                 x += PAN_FOLDER_BOX_BORDER;
1865                                 y += PAN_FOLDER_BOX_BORDER;
1866                                 month_start = y;
1867                                 }
1868
1869                         if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
1870
1871                         tc = fd->date;
1872                         total = 1;
1873                         count = 0;
1874
1875                         needle = work;
1876                         while (needle)
1877                                 {
1878                                 FileData *nfd;
1879
1880                                 nfd = needle->data;
1881                                 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
1882                                         {
1883                                         needle = needle->next;
1884                                         total++;
1885                                         }
1886                                 else
1887                                         {
1888                                         needle = NULL;
1889                                         }
1890                                 }
1891
1892                         buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
1893                         pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
1894                                                PAN_TEXT_COLOR, 255);
1895                         g_free(buf);
1896
1897                         y += pi->height;
1898
1899                         pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
1900                                                   PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1901                                                   PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1902                                                   PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1903
1904                         x += PAN_FOLDER_BOX_BORDER;
1905                         y += PAN_FOLDER_BOX_BORDER;
1906                         day_start = y;
1907                         }
1908
1909                 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1910                         {
1911                         pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1912                         if (pi->width > x_width) x_width = pi->width;
1913                         y_height = pi->height;
1914                         }
1915                 else
1916                         {
1917                         pi = pan_item_new_thumb(pw, fd, x, y);
1918                         x_width = PAN_THUMB_SIZE;
1919                         y_height = PAN_THUMB_SIZE;
1920                         }
1921
1922                 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
1923                 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
1924 #if 0
1925                 if (pi_day)
1926                         {
1927                         if (pi->x + pi->width + PAN_FOLDER_BOX_BORDER > pi_day->x + pi_day->width)
1928                                 pi_day->width = pi->x + pi->width +  PAN_FOLDER_BOX_BORDER - pi_day->x;
1929                         if (pi->y + pi->height + PAN_FOLDER_BOX_BORDER > pi_day->y + pi_day->height)
1930                                 pi_day->height = pi->y + pi->height +  PAN_FOLDER_BOX_BORDER - pi_day->y;
1931                         }
1932
1933                 if (pi_month && pi_day)
1934                         {
1935                         if (pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER > pi_month->x + pi_month->width)
1936                                 pi_month->width = pi_day->x + pi_day->width +  PAN_FOLDER_BOX_BORDER - pi_month->x;
1937                         if (pi_day->y + pi_day->height + PAN_FOLDER_BOX_BORDER > pi_month->y + pi_month->height)
1938                                 pi_month->height = pi_day->y + pi_day->height +  PAN_FOLDER_BOX_BORDER - pi_month->y;
1939                         }
1940 #endif
1941
1942                 total--;
1943                 count++;
1944
1945                 if (total > 0 && count < PAN_GROUP_MAX)
1946                         {
1947                         y += y_height + PAN_THUMB_GAP;
1948                         }
1949                 else
1950                         {
1951                         x += x_width + PAN_THUMB_GAP;
1952                         x_width = 0;
1953                         count = 0;
1954
1955                         if (total > 0)
1956                                 y = day_start;
1957                         else
1958                                 y = month_start;
1959                         }
1960
1961                 if (w < pi->x + pi->width + PAN_THUMB_GAP) w  = pi->x + pi->width + PAN_THUMB_GAP;
1962                 if (h < pi->y + pi->height + PAN_THUMB_GAP) h = pi->y + pi->height + PAN_THUMB_GAP;
1963                 }
1964
1965         w += PAN_FOLDER_BOX_BORDER;
1966         h += PAN_FOLDER_BOX_BORDER;
1967
1968         if (width) *width = w;
1969         if (height) *height = h;
1970
1971         g_list_free(list);
1972 }
1973
1974 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
1975                                       gint *width, gint *height,
1976                                       gint *scroll_x, gint *scroll_y)
1977 {
1978         pan_window_items_free(pw);
1979
1980         switch (pw->size)
1981                 {
1982                 case LAYOUT_SIZE_THUMB_NONE:
1983                         pw->thumb_size = PAN_THUMB_SIZE_NONE;
1984                         pw->thumb_gap = PAN_THUMB_GAP_SMALL;
1985                         break;
1986                 case LAYOUT_SIZE_THUMB_SMALL:
1987                         pw->thumb_size = PAN_THUMB_SIZE_SMALL;
1988                         pw->thumb_gap = PAN_THUMB_GAP_SMALL;
1989                         break;
1990                 case LAYOUT_SIZE_THUMB_NORMAL:
1991                 default:
1992                         pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
1993                         pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
1994                         break;
1995                 case LAYOUT_SIZE_THUMB_LARGE:
1996                         pw->thumb_size = PAN_THUMB_SIZE_LARGE;
1997                         pw->thumb_gap = PAN_THUMB_GAP_LARGE;
1998                         break;
1999                 case LAYOUT_SIZE_10:
2000                         pw->image_size = 10;
2001                         pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2002                         break;
2003                 case LAYOUT_SIZE_25:
2004                         pw->image_size = 25;
2005                         pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2006                         break;
2007                 case LAYOUT_SIZE_33:
2008                         pw->image_size = 33;
2009                         pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2010                         break;
2011                 case LAYOUT_SIZE_50:
2012                         pw->image_size = 50;
2013                         pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2014                         break;
2015                 case LAYOUT_SIZE_100:
2016                         pw->image_size = 100;
2017                         pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2018                         break;
2019                 }
2020
2021         *width = 0;
2022         *height = 0;
2023         *scroll_x = 0;
2024         *scroll_y = 0;
2025
2026         switch (pw->layout)
2027                 {
2028                 case LAYOUT_GRID:
2029                 default:
2030                         pan_window_layout_compute_grid(pw, path, width, height);
2031                         break;
2032                 case LAYOUT_FOLDERS_LINEAR:
2033                         pan_window_layout_compute_folders_linear(pw, path, width, height);
2034                         break;
2035                 case LAYOUT_FOLDERS_FLOWER:
2036                         pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2037                         break;
2038                 case LAYOUT_TIMELINE:
2039                         pan_window_layout_compute_timeline(pw, path, width, height);
2040                         break;
2041                 }
2042
2043         pan_cache_free(pw);
2044
2045         printf("computed %d objects\n", g_list_length(pw->list));
2046 }
2047
2048 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2049 {
2050         GList *list = NULL;
2051         GList *work;
2052
2053         work = pw->list;
2054         while (work)
2055                 {
2056                 PanItem *pi;
2057
2058                 pi = work->data;
2059                 work = work->next;
2060
2061                 if (util_clip_region_test(x, y, width, height,
2062                                           pi->x, pi->y, pi->width, pi->height))
2063                         {
2064                         list = g_list_prepend(list, pi);
2065                         }
2066                 }
2067
2068         return list;
2069 }
2070
2071
2072
2073 /*
2074  *-----------------------------------------------------------------------------
2075  * tile generation
2076  *-----------------------------------------------------------------------------
2077  */
2078
2079 static gint pan_layout_queue_step(PanWindow *pw);
2080
2081
2082 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2083 {
2084         PanWindow *pw = data;
2085
2086         if (pw->queue_pi)
2087                 {
2088                 PanItem *pi;
2089                 gint rc;
2090
2091                 pi = pw->queue_pi;
2092                 pw->queue_pi = NULL;
2093
2094                 pi->queued = FALSE;
2095
2096                 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2097                 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2098
2099                 rc = pi->refcount;
2100                 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2101                 pi->refcount = rc;
2102                 }
2103
2104         thumb_loader_free(pw->tl);
2105         pw->tl = NULL;
2106
2107         while (pan_layout_queue_step(pw));
2108 }
2109
2110 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2111 {
2112         PanWindow *pw = data;
2113
2114         if (pw->queue_pi)
2115                 {
2116                 PanItem *pi;
2117                 gint rc;
2118
2119                 pi = pw->queue_pi;
2120                 pw->queue_pi = NULL;
2121
2122                 pi->queued = FALSE;
2123
2124                 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2125                 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2126                 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2127
2128                 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2129                     (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2130                      gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2131                         {
2132                         GdkPixbuf *tmp;
2133
2134                         tmp = pi->pixbuf;
2135                         pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2136                                                              (GdkInterpType)zoom_quality);
2137                         g_object_unref(tmp);
2138                         }
2139
2140                 rc = pi->refcount;
2141                 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2142                 pi->refcount = rc;
2143                 }
2144
2145         image_loader_free(pw->il);
2146         pw->il = NULL;
2147
2148         while (pan_layout_queue_step(pw));
2149 }
2150
2151 #if 0
2152 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2153                                            guint width, guint height, gpointer data)
2154 {
2155         PanWindow *pw = data;
2156
2157         if (pw->queue_pi)
2158                 {
2159                 PanItem *pi;
2160                 gint rc;
2161
2162                 pi = pw->queue_pi;
2163
2164                 if (!pi->pixbuf)
2165                         {
2166                         pi->pixbuf = image_loader_get_pixbuf(pw->il);
2167                         if (pi->pixbuf) g_object_ref(pi->pixbuf);
2168                         }
2169
2170                 rc = pi->refcount;
2171                 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2172                 pi->refcount = rc;
2173                 }
2174 }
2175 #endif
2176
2177 static gint pan_layout_queue_step(PanWindow *pw)
2178 {
2179         PanItem *pi;
2180
2181         if (!pw->queue) return FALSE;
2182
2183         pi = pw->queue->data;
2184         pw->queue = g_list_remove(pw->queue, pi);
2185         pw->queue_pi = pi;
2186
2187         image_loader_free(pw->il);
2188         pw->il = NULL;
2189         thumb_loader_free(pw->tl);
2190         pw->tl = NULL;
2191
2192         if (pi->type == ITEM_IMAGE)
2193                 {
2194                 pw->il = image_loader_new(pi->fd->path);
2195
2196                 if (pw->size != LAYOUT_SIZE_100)
2197                         {
2198                         image_loader_set_requested_size(pw->il, pi->width, pi->height);
2199                         }
2200
2201 #if 0
2202                 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2203 #endif
2204                 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2205
2206                 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2207
2208                 image_loader_free(pw->il);
2209                 pw->il = NULL;
2210                 }
2211         else if (pi->type == ITEM_THUMB)
2212                 {
2213                 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2214
2215                 if (!pw->tl->standard_loader)
2216                         {
2217                         /* The classic loader will recreate a thumbnail any time we
2218                          * request a different size than what exists. This view will
2219                          * almost never use the user configured sizes so disable cache.
2220                          */
2221                         thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2222                         }
2223
2224                 thumb_loader_set_callbacks(pw->tl,
2225                                            pan_layout_queue_thumb_done_cb,
2226                                            pan_layout_queue_thumb_done_cb,
2227                                            NULL, pw);
2228
2229                 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2230
2231                 thumb_loader_free(pw->tl);
2232                 pw->tl = NULL;
2233                 }
2234
2235         pw->queue_pi->queued = FALSE;
2236         pw->queue_pi = NULL;
2237         return TRUE;
2238 }
2239
2240 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2241 {
2242         if (!pi || pi->queued || pi->pixbuf) return;
2243         if (pw->size == LAYOUT_SIZE_THUMB_NONE) return;
2244
2245         pi->queued = TRUE;
2246         pw->queue = g_list_prepend(pw->queue, pi);
2247
2248         if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2249 }
2250
2251 static gint pan_window_request_tile_cb(ImageWindow *imd, gint x, gint y, gint width, gint height,
2252                                        GdkPixbuf *pixbuf, gpointer data)
2253 {
2254         PanWindow *pw = data;
2255         GList *list;
2256         GList *work;
2257         gint i;
2258
2259         pixbuf_draw_rect_fill(pixbuf,
2260                          0, 0, width, height,
2261                          PAN_BACKGROUND_COLOR, 255);
2262
2263         for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2264                 {
2265                 gint rx, ry, rw, rh;
2266
2267                 if (util_clip_region(x, y, width, height,
2268                                      i, y, 1, height,
2269                                      &rx, &ry, &rw, &rh))
2270                         {
2271                         pixbuf_draw_rect_fill(pixbuf,
2272                                               rx - x, ry - y, rw, rh,
2273                                               PAN_GRID_COLOR, PAN_GRID_ALPHA);
2274                         }
2275                 }
2276         for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2277                 {
2278                 gint rx, ry, rw, rh;
2279
2280                 if (util_clip_region(x, y, width, height,
2281                                      x, i, width, 1,
2282                                      &rx, &ry, &rw, &rh))
2283                         {
2284                         pixbuf_draw_rect_fill(pixbuf,
2285                                               rx - x, ry - y, rw, rh,
2286                                               PAN_GRID_COLOR, PAN_GRID_ALPHA);
2287                         }
2288                 }
2289
2290         list = pan_layout_intersect(pw, x, y, width, height);
2291         work = list;
2292         while (work)
2293                 {
2294                 PanItem *pi;
2295                 gint tx, ty, tw, th;
2296                 gint rx, ry, rw, rh;
2297
2298                 pi = work->data;
2299                 work = work->next;
2300
2301                 pi->refcount++;
2302
2303                 if (pi->type == ITEM_THUMB && pi->pixbuf)
2304                         {
2305                         tw = gdk_pixbuf_get_width(pi->pixbuf);
2306                         th = gdk_pixbuf_get_height(pi->pixbuf);
2307
2308                         tx = pi->x + (pi->width - tw) / 2;
2309                         ty = pi->y + (pi->height - th) / 2;
2310
2311                         if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2312                                 {
2313                                 if (util_clip_region(x, y, width, height,
2314                                                      tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2315                                                      &rx, &ry, &rw, &rh))
2316                                         {
2317                                         pixbuf_draw_shadow(pixbuf,
2318                                                            rx - x, ry - y, rw, rh,
2319                                                            tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2320                                                            PAN_SHADOW_FADE,
2321                                                            PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2322                                         }
2323                                 }
2324                         else
2325                                 {
2326                                 if (util_clip_region(x, y, width, height,
2327                                                      tx + tw, ty + PAN_SHADOW_OFFSET,
2328                                                      PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2329                                                      &rx, &ry, &rw, &rh))
2330                                         {
2331                                         pixbuf_draw_shadow(pixbuf,
2332                                                            rx - x, ry - y, rw, rh,
2333                                                            tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2334                                                            PAN_SHADOW_FADE,
2335                                                            PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2336                                         }
2337                                 if (util_clip_region(x, y, width, height,
2338                                                      tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2339                                                      &rx, &ry, &rw, &rh))
2340                                         {
2341                                         pixbuf_draw_shadow(pixbuf,
2342                                                            rx - x, ry - y, rw, rh,
2343                                                            tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2344                                                            PAN_SHADOW_FADE,
2345                                                            PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2346                                         }
2347                                 }
2348
2349                         if (util_clip_region(x, y, width, height,
2350                                              tx, ty, tw, th,
2351                                              &rx, &ry, &rw, &rh))
2352                                 {
2353                                 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2354                                                      (double) tx - x,
2355                                                      (double) ty - y,
2356                                                      1.0, 1.0, GDK_INTERP_NEAREST,
2357                                                      255);
2358                                 }
2359
2360                         if (util_clip_region(x, y, width, height,
2361                                              tx, ty, tw, PAN_OUTLINE_THICKNESS,
2362                                              &rx, &ry, &rw, &rh))
2363                                 {
2364                                 pixbuf_draw_rect_fill(pixbuf,
2365                                                       rx - x, ry - y, rw, rh,
2366                                                       PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2367                                 }
2368                         if (util_clip_region(x, y, width, height,
2369                                              tx, ty, PAN_OUTLINE_THICKNESS, th,
2370                                              &rx, &ry, &rw, &rh))
2371                                 {
2372                                 pixbuf_draw_rect_fill(pixbuf,
2373                                                       rx - x, ry - y, rw, rh,
2374                                                       PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2375                                 }
2376                         if (util_clip_region(x, y, width, height,
2377                                              tx + tw - PAN_OUTLINE_THICKNESS, ty +  PAN_OUTLINE_THICKNESS,
2378                                              PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2379                                              &rx, &ry, &rw, &rh))
2380                                 {
2381                                 pixbuf_draw_rect_fill(pixbuf,
2382                                                       rx - x, ry - y, rw, rh,
2383                                                       PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2384                                 }
2385                         if (util_clip_region(x, y, width, height,
2386                                              tx +  PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2387                                              tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2388                                              &rx, &ry, &rw, &rh))
2389                                 {
2390                                 pixbuf_draw_rect_fill(pixbuf,
2391                                                       rx - x, ry - y, rw, rh,
2392                                                       PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2393                                 }
2394                         }
2395                 else if (pi->type == ITEM_THUMB)
2396                         {
2397                         tw = pi->width - PAN_SHADOW_OFFSET * 2;
2398                         th = pi->height - PAN_SHADOW_OFFSET * 2;
2399                         tx = pi->x + PAN_SHADOW_OFFSET;
2400                         ty = pi->y + PAN_SHADOW_OFFSET;
2401
2402                         if (util_clip_region(x, y, width, height,
2403                                              tx, ty, tw, th,
2404                                              &rx, &ry, &rw, &rh))
2405                                 {
2406                                 gint d;
2407
2408                                 d = (pw->size == LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2409                                 pixbuf_draw_rect_fill(pixbuf,
2410                                                       rx - x, ry - y, rw, rh,
2411                                                       PAN_SHADOW_COLOR,
2412                                                       PAN_SHADOW_ALPHA / d);
2413                                 }
2414
2415                         pan_layout_queue(pw, pi);
2416                         }
2417                 else if (pi->type == ITEM_IMAGE)
2418                         {
2419                         if (util_clip_region(x, y, width, height,
2420                                              pi->x, pi->y, pi->width, pi->height,
2421                                              &rx, &ry, &rw, &rh))
2422                                 {
2423                                 if (pi->pixbuf)
2424                                         {
2425                                         gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2426                                                              (double) pi->x - x,
2427                                                              (double) pi->y - y,
2428                                                              1.0, 1.0, GDK_INTERP_NEAREST,
2429                                                              255);
2430                                         }
2431                                 else
2432                                         {
2433                                         pixbuf_draw_rect_fill(pixbuf,
2434                                                               rx - x, ry - y, rw, rh,
2435                                                               PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2436                                         pan_layout_queue(pw, pi);
2437                                         }
2438                                 }
2439                         }
2440                 else if (pi->type == ITEM_BOX)
2441                         {
2442                         gint bw, bh;
2443                         gint *shadow;
2444
2445                         bw = pi->width;
2446                         bh = pi->height;
2447
2448                         shadow = pi->data;
2449                         if (shadow)
2450                                 {
2451                                 bw -= shadow[0];
2452                                 bh -= shadow[0];
2453
2454                                 if (pi->color_a > 254)
2455                                         {
2456                                         pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2457                                                            shadow[0], bh - shadow[0],
2458                                                            pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2459                                                            shadow[1],
2460                                                            PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2461                                         pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2462                                                            bw, shadow[0],
2463                                                            pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2464                                                            shadow[1],
2465                                                            PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2466                                         }
2467                                 else
2468                                         {
2469                                         gint a;
2470                                         a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2471                                         pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2472                                                            bw, bh,
2473                                                            pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2474                                                            shadow[1],
2475                                                            PAN_SHADOW_COLOR, a);
2476                                         }
2477                                 }
2478
2479                         if (util_clip_region(x, y, width, height,
2480                                              pi->x, pi->y, bw, bh,
2481                                              &rx, &ry, &rw, &rh))
2482                                 {
2483                                 pixbuf_draw_rect_fill(pixbuf,
2484                                                       rx - x, ry - y, rw, rh,
2485                                                       pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2486                                 }
2487                         if (util_clip_region(x, y, width, height,
2488                                              pi->x, pi->y, bw, pi->border,
2489                                              &rx, &ry, &rw, &rh))
2490                                 {
2491                                 pixbuf_draw_rect_fill(pixbuf,
2492                                                       rx - x, ry - y, rw, rh,
2493                                                       pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2494                                 }
2495                         if (util_clip_region(x, y, width, height,
2496                                              pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
2497                                              &rx, &ry, &rw, &rh))
2498                                 {
2499                                 pixbuf_draw_rect_fill(pixbuf,
2500                                                       rx - x, ry - y, rw, rh,
2501                                                       pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2502                                 }
2503                         if (util_clip_region(x, y, width, height,
2504                                              pi->x + bw - pi->border, pi->y + pi->border,
2505                                              pi->border, bh - pi->border * 2,
2506                                              &rx, &ry, &rw, &rh))
2507                                 {
2508                                 pixbuf_draw_rect_fill(pixbuf,
2509                                                       rx - x, ry - y, rw, rh,
2510                                                       pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2511                                 }
2512                         if (util_clip_region(x, y, width, height,
2513                                              pi->x, pi->y + bh - pi->border,
2514                                              bw,  pi->border,
2515                                              &rx, &ry, &rw, &rh))
2516                                 {
2517                                 pixbuf_draw_rect_fill(pixbuf,
2518                                                       rx - x, ry - y, rw, rh,
2519                                                       pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2520                                 }
2521                         }
2522                 else if (pi->type == ITEM_TRIANGLE)
2523                         {
2524                         if (util_clip_region(x, y, width, height,
2525                                              pi->x, pi->y, pi->width, pi->height,
2526                                              &rx, &ry, &rw, &rh) && pi->data)
2527                                 {
2528                                 gint *coord = pi->data;
2529                                 pixbuf_draw_triangle(pixbuf,
2530                                                      rx - x, ry - y, rw, rh,
2531                                                      coord[0] - x, coord[1] - y,
2532                                                      coord[2] - x, coord[3] - y,
2533                                                      coord[4] - x, coord[5] - y,
2534                                                      pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2535
2536                                 if (pi->border & BORDER_1)
2537                                         {
2538                                         pixbuf_draw_line(pixbuf,
2539                                                          rx - x, ry - y, rw, rh,
2540                                                          coord[0] - x, coord[1] - y,
2541                                                          coord[2] - x, coord[3] - y,
2542                                                          pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2543                                         }
2544                                 if (pi->border & BORDER_2)
2545                                         {
2546                                         pixbuf_draw_line(pixbuf,
2547                                                          rx - x, ry - y, rw, rh,
2548                                                          coord[2] - x, coord[3] - y,
2549                                                          coord[4] - x, coord[5] - y,
2550                                                          pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2551                                         }
2552                                 if (pi->border & BORDER_3)
2553                                         {
2554                                         pixbuf_draw_line(pixbuf,
2555                                                          rx - x, ry - y, rw, rh,
2556                                                          coord[4] - x, coord[5] - y,
2557                                                          coord[0] - x, coord[1] - y,
2558                                                          pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2559                                         }
2560                                 }
2561                         }
2562                 else if (pi->type == ITEM_TEXT && pi->text)
2563                         {
2564                         PangoLayout *layout;
2565
2566                         layout = pan_item_text_layout(pi, imd->image);
2567                         pixbuf_draw_layout(pixbuf, layout, imd->image,
2568                                            pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
2569                                            pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2570                         g_object_unref(G_OBJECT(layout));
2571                         }
2572                 }
2573         g_list_free(list);
2574
2575         if (0)
2576                 {
2577                 static gint count = 0;
2578                 PangoLayout *layout;
2579                 gint lx, ly;
2580                 gint lw, lh;
2581                 GdkPixmap *pixmap;
2582                 gchar *buf;
2583
2584                 layout = gtk_widget_create_pango_layout(imd->image, NULL);
2585
2586                 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
2587                                       (x / imd->source_tile_width) +
2588                                       (y / imd->source_tile_height * (imd->image_width/imd->source_tile_width + 1)));
2589                 pango_layout_set_text(layout, buf, -1);
2590                 g_free(buf);
2591
2592                 pango_layout_get_pixel_size(layout, &lw, &lh);
2593
2594                 pixmap = gdk_pixmap_new(imd->widget->window, lw, lh, -1);
2595                 gdk_draw_rectangle(pixmap, imd->widget->style->black_gc, TRUE, 0, 0, lw, lh);
2596                 gdk_draw_layout(pixmap, imd->widget->style->white_gc, 0, 0, layout);
2597                 g_object_unref(G_OBJECT(layout));
2598
2599                 lx = MAX(0, width / 2 - lw / 2);
2600                 ly = MAX(0, height / 2 - lh / 2);
2601                 lw = MIN(lw, width - lx);
2602                 lh = MIN(lh, height - ly);
2603                 gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_drawable_get_colormap(imd->image->window),
2604                                              0, 0, lx, ly, lw, lh);
2605                 g_object_unref(pixmap);
2606
2607                 count++;
2608                 }
2609
2610         return TRUE;
2611 }
2612
2613 static void pan_window_dispose_tile_cb(ImageWindow *imd, gint x, gint y, gint width, gint height,
2614                                        GdkPixbuf *pixbuf, gpointer data)
2615 {
2616         PanWindow *pw = data;
2617         GList *list;
2618         GList *work;
2619
2620         list = pan_layout_intersect(pw, x, y, width, height);
2621         work = list;
2622         while (work)
2623                 {
2624                 PanItem *pi;
2625
2626                 pi = work->data;
2627                 work = work->next;
2628
2629                 if (pi->refcount > 0)
2630                         {
2631                         pi->refcount--;
2632
2633                         if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
2634                             pi->refcount == 0)
2635                                 {
2636                                 if (pi->queued)
2637                                         {
2638                                         pw->queue = g_list_remove(pw->queue, pi);
2639                                         pi->queued = FALSE;
2640                                         }
2641                                 if (pw->queue_pi == pi) pw->queue_pi = NULL;
2642                                 if (pi->pixbuf)
2643                                         {
2644                                         g_object_unref(pi->pixbuf);
2645                                         pi->pixbuf = NULL;
2646                                         }
2647                                 }
2648                         }
2649                 }
2650
2651         g_list_free(list);
2652 }
2653
2654
2655 /*
2656  *-----------------------------------------------------------------------------
2657  * misc
2658  *-----------------------------------------------------------------------------
2659  */ 
2660
2661 static void pan_window_message(PanWindow *pw, const gchar *text)
2662 {
2663         GList *work;
2664         gint count = 0;
2665         gint64 size = 0;
2666         gchar *ss;
2667         gchar *buf;
2668
2669         if (text)
2670                 {
2671                 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
2672                 return;
2673                 }
2674
2675         work = pw->list;
2676         while (work)
2677                 {
2678                 PanItem *pi;
2679
2680                 pi = work->data;
2681                 work = work->next;
2682
2683                 if (pi->fd &&
2684                     (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
2685                         {
2686                         size += pi->fd->size;
2687                         count++;
2688                         }
2689                 }
2690
2691         ss = text_from_size_abrev(size);
2692         buf = g_strdup_printf(_("%d images, %s"), count, ss);
2693         g_free(ss);
2694         gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
2695         g_free(buf);
2696 }
2697
2698 static ImageWindow *pan_window_active_image(PanWindow *pw)
2699 {
2700         if (pw->fs) return pw->fs->imd;
2701
2702         return pw->imd;
2703 }
2704
2705 static void pan_window_zoom_limit(PanWindow *pw)
2706 {
2707         gdouble min;
2708
2709         switch (pw->size)
2710                 {
2711                 case LAYOUT_SIZE_THUMB_NONE:
2712                 case LAYOUT_SIZE_THUMB_SMALL:
2713                 case LAYOUT_SIZE_THUMB_NORMAL:
2714 #if 0
2715                         /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
2716                         min = -16.0;
2717                         break;
2718 #endif
2719                 case LAYOUT_SIZE_THUMB_LARGE:
2720                         min = -6.0;
2721                         break;
2722                 case LAYOUT_SIZE_10:
2723                 case LAYOUT_SIZE_25:
2724                         min = -4.0;
2725                         break;
2726                 case LAYOUT_SIZE_33:
2727                 case LAYOUT_SIZE_50:
2728                 case LAYOUT_SIZE_100:
2729                 default:
2730                         min = -2.0;
2731                         break;
2732                 }
2733
2734         image_zoom_set_limits(pw->imd, min, 32.0);
2735 }
2736
2737 static gint pan_window_layout_update_idle_cb(gpointer data)
2738 {
2739         PanWindow *pw = data;
2740         gint width;
2741         gint height;
2742         gint scroll_x;
2743         gint scroll_y;
2744
2745         if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2746                 {
2747                 if (!pw->cache_list && !pw->cache_todo)
2748                         {
2749                         pan_cache_fill(pw, pw->path);
2750                         if (pw->cache_todo)
2751                                 {
2752                                 pan_window_message(pw, _("Reading dimensions..."));
2753                                 return TRUE;
2754                                 }
2755                         }
2756                 if (pan_cache_step(pw))
2757                         {
2758                         pw->cache_count++;
2759                         pw->cache_tick++;
2760                         if (pw->cache_count == pw->cache_total)
2761                                 {
2762                                 pan_window_message(pw, _("Sorting images..."));
2763                                 }
2764                         else if (pw->cache_tick > 9)
2765                                 {
2766                                 gchar *buf;
2767
2768                                 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
2769                                                       pw->cache_total - pw->cache_count);
2770                                 pan_window_message(pw, buf);
2771                                 g_free(buf);
2772
2773                                 pw->cache_tick = 0;
2774                                 }
2775
2776                         return TRUE;
2777                         }
2778                 }
2779
2780         pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
2781
2782         pan_window_zoom_limit(pw);
2783
2784         if (width > 0 && height > 0)
2785                 {
2786                 image_set_image_as_tiles(pw->imd, width, height,
2787                                          PAN_TILE_SIZE, PAN_TILE_SIZE, 8,
2788                                          pan_window_request_tile_cb,
2789                                          pan_window_dispose_tile_cb, pw, 1.0);
2790                 image_scroll_to_point(pw->imd, scroll_x, scroll_y);
2791                 }
2792
2793         pan_window_message(pw, NULL);
2794
2795         pw->idle_id = -1;
2796
2797         return FALSE;
2798 }
2799
2800 static void pan_window_layout_update_idle(PanWindow *pw)
2801 {
2802         if (pw->idle_id == -1)
2803                 {
2804                 pan_window_message(pw, _("Sorting images..."));
2805                 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
2806                 }
2807 }
2808
2809 /*
2810  *-----------------------------------------------------------------------------
2811  * pan window keyboard
2812  *-----------------------------------------------------------------------------
2813  */
2814
2815 static const gchar *pan_menu_click_path(PanWindow *pw)
2816 {
2817         if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
2818         return NULL;
2819 }
2820
2821 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
2822 {
2823         PanWindow *pw = data;
2824         ImageWindow *imd;
2825
2826         imd = pan_window_active_image(pw);
2827         gdk_window_get_origin(imd->image->window, x, y);
2828         popup_menu_position_clamp(menu, x, y, 0);
2829 }
2830
2831 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
2832 {
2833         PanWindow *pw = data;
2834         ImageWindow *imd;
2835         const gchar *path;
2836         gint stop_signal = FALSE;
2837         GtkWidget *menu;
2838         gint x = 0;
2839         gint y = 0;
2840         gint focused;
2841
2842         focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(pw->imd->widget));
2843
2844         imd = pan_window_active_image(pw);
2845         path = pan_menu_click_path(pw);
2846
2847         if (focused)
2848                 {
2849                 switch (event->keyval)
2850                         {
2851                         case GDK_Left: case GDK_KP_Left:
2852                                 x -= 1;
2853                                 stop_signal = TRUE;
2854                                 break;
2855                         case GDK_Right: case GDK_KP_Right:
2856                                 x += 1;
2857                                 stop_signal = TRUE;
2858                                 break;
2859                         case GDK_Up: case GDK_KP_Up:
2860                                 y -= 1;
2861                                 stop_signal = TRUE;
2862                                 break;
2863                         case GDK_Down: case GDK_KP_Down:
2864                                 y += 1;
2865                                 stop_signal = TRUE;
2866                                 break;
2867                         case GDK_Page_Up: case GDK_KP_Page_Up:
2868                                 image_scroll(imd, 0, 0-imd->vis_height / 2);
2869                                 break;
2870                         case GDK_Page_Down: case GDK_KP_Page_Down:
2871                                 image_scroll(imd, 0, imd->vis_height / 2);
2872                                 break;
2873                         case GDK_Home: case GDK_KP_Home:
2874                                 image_scroll(imd, 0-imd->vis_width / 2, 0);
2875                                 break;
2876                         case GDK_End: case GDK_KP_End:
2877                                 image_scroll(imd, imd->vis_width / 2, 0);
2878                                 break;
2879                         }
2880                 }
2881
2882         if (focused && !(event->state & GDK_CONTROL_MASK) )
2883             switch (event->keyval)
2884                 {
2885                 case '+': case '=': case GDK_KP_Add:
2886                         image_zoom_adjust(imd, ZOOM_INCREMENT);
2887                         break;
2888                 case '-': case GDK_KP_Subtract:
2889                         image_zoom_adjust(imd, -ZOOM_INCREMENT);
2890                         break;
2891                 case 'Z': case 'z': case GDK_KP_Divide: case '1':
2892                         image_zoom_set(imd, 1.0);
2893                         break;
2894                 case '2':
2895                         image_zoom_set(imd, 2.0);
2896                         break;
2897                 case '3':
2898                         image_zoom_set(imd, 3.0);
2899                         break;
2900                 case '4':
2901                         image_zoom_set(imd, 4.0);
2902                         break;
2903                 case '7':
2904                         image_zoom_set(imd, -4.0);
2905                         break;
2906                 case '8':
2907                         image_zoom_set(imd, -3.0);
2908                         break;
2909                 case '9':
2910                         image_zoom_set(imd, -2.0);
2911                         break;
2912                 case 'F': case 'f':
2913                 case 'V': case 'v':
2914                         pan_fullscreen_toggle(pw, FALSE);
2915                         stop_signal = TRUE;
2916                         break;
2917                 case 'I': case 'i':
2918                         pan_overlay_toggle(pw);
2919                         break;
2920                 case GDK_Delete: case GDK_KP_Delete:
2921                         break;
2922                 case '/':
2923                         if (!pw->fs)
2924                                 {
2925                                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
2926                                 stop_signal = TRUE;
2927                                 }
2928                         break;
2929                 case GDK_Escape:
2930                         if (pw->fs)
2931                                 {
2932                                 pan_fullscreen_toggle(pw, TRUE);
2933                                 stop_signal = TRUE;
2934                                 }
2935                         else if (GTK_WIDGET_VISIBLE(pw->search_entry))
2936                                 {
2937                                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
2938                                 stop_signal = TRUE;
2939                                 }
2940                         break;
2941                 case GDK_Menu:
2942                 case GDK_F10:
2943                         menu = pan_popup_menu(pw);
2944                         gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
2945                         stop_signal = TRUE;
2946                         break;
2947                 }
2948
2949         if (event->state & GDK_CONTROL_MASK)
2950                 {
2951                 gint n = -1;
2952                 switch (event->keyval)
2953                         {
2954                         case '1':
2955                                 n = 0;
2956                                 break;
2957                         case '2':
2958                                 n = 1;
2959                                 break;
2960                         case '3':
2961                                 n = 2;
2962                                 break;
2963                         case '4':
2964                                 n = 3;
2965                                 break;
2966                         case '5':
2967                                 n = 4;
2968                                 break;
2969                         case '6':
2970                                 n = 5;
2971                                 break;
2972                         case '7':
2973                                 n = 6;
2974                                 break;
2975                         case '8':
2976                                 n = 7;
2977                                 break;
2978                         case '9':
2979                                 n = 8;
2980                                 break;
2981                         case '0':
2982                                 n = 9;
2983                                 break;
2984                         case 'C': case 'c':
2985                                 if (path) file_util_copy(path, NULL, NULL, imd->widget);
2986                                 break;
2987                         case 'M': case 'm':
2988                                 if (path) file_util_move(path, NULL, NULL, imd->widget);
2989                                 break;
2990                         case 'R': case 'r':
2991                                 if (path) file_util_rename(path, NULL, imd->widget);
2992                                 break;
2993                         case 'D': case 'd':
2994                                 if (path) file_util_delete(path, NULL, imd->widget);
2995                                 break;
2996                         case 'P': case 'p':
2997                                 if (path) info_window_new(path, NULL);
2998                                 break;
2999                         case 'W': case 'w':
3000                                 pan_window_close(pw);
3001                                 break;
3002                         }
3003                 if (n != -1 && path)
3004                         {
3005                         pan_fullscreen_toggle(pw, TRUE);
3006                         start_editor_from_file(n, path);
3007                         stop_signal = TRUE;
3008                         }
3009                 }
3010         else if (event->state & GDK_SHIFT_MASK)
3011                 {
3012                 x *= 3;
3013                 y *= 3;
3014                 }
3015         else if (!focused)
3016                 {
3017                 switch (event->keyval)
3018                         {
3019                         case GDK_Escape:
3020                                 if (pw->fs)
3021                                         {
3022                                         pan_fullscreen_toggle(pw, TRUE);
3023                                         stop_signal = TRUE;
3024                                         }
3025                                 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3026                                         {
3027                                         gtk_widget_grab_focus(pw->imd->widget);
3028                                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3029                                         stop_signal = TRUE;
3030                                         }
3031                         break;
3032                         default:
3033                                 break;
3034                         }
3035                 }
3036
3037         if (x != 0 || y!= 0)
3038                 {
3039                 keyboard_scroll_calc(&x, &y, event);
3040                 image_scroll(imd, x, y);
3041                 }
3042
3043         return stop_signal;
3044 }
3045
3046 /*
3047  *-----------------------------------------------------------------------------
3048  * info popup
3049  *-----------------------------------------------------------------------------
3050  */
3051
3052 static void pan_info_update(PanWindow *pw, PanItem *pi)
3053 {
3054         PanItem *pbox;
3055         PanItem *plabel;
3056         PanItem *p;
3057         gchar *buf;
3058         gint x1, y1, x2, y2, x3, y3;
3059         gint x, y, w, h;
3060
3061         if (pw->click_pi == pi) return;
3062         if (pi && !pi->fd) pi = NULL;
3063
3064         while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3065         pw->click_pi = pi;
3066
3067         if (!pi) return;
3068
3069         printf("info set to %s\n", pi->fd->path);
3070
3071         pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3072                              PAN_POPUP_BORDER,
3073                              PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3074                              PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3075         pan_item_set_key(pbox, "info");
3076
3077         if (pi->type == ITEM_THUMB && pi->pixbuf)
3078                 {
3079                 w = gdk_pixbuf_get_width(pi->pixbuf);
3080                 h = gdk_pixbuf_get_height(pi->pixbuf);
3081
3082                 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3083                 y1 = pi->y + (pi->height - h) / 2 + 8;
3084                 }
3085         else
3086                 {
3087                 x1 = pi->x + pi->width - 8;
3088                 y1 = pi->y + 8;
3089                 }
3090
3091         x2 = pbox->x + 1;
3092         y2 = pbox->y + 36;
3093         x3 = pbox->x + 1;
3094         y3 = pbox->y + 12;
3095         triangle_rect_region(x1, y1, x2, y2, x3, y3,
3096                              &x, &y, &w, &h);
3097
3098         p = pan_item_new_tri(pw, NULL, x, y, w, h,
3099                              x1, y1, x2, y2, x3, y3,
3100                              PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3101         pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3102         pan_item_set_key(p, "info");
3103         pan_item_added(pw, p);
3104
3105         plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3106                                    _("Filename:"), TEXT_ATTR_BOLD,
3107                                    PAN_POPUP_TEXT_COLOR, 255);
3108         pan_item_set_key(plabel, "info");
3109         p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3110                               pi->fd->name, TEXT_ATTR_NONE,
3111                               PAN_POPUP_TEXT_COLOR, 255);
3112         pan_item_set_key(p, "info");
3113         pan_item_size_by_item(pbox, p, 0);
3114
3115         plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3116                                    _("Date:"), TEXT_ATTR_BOLD,
3117                                    PAN_POPUP_TEXT_COLOR, 255);
3118         pan_item_set_key(plabel, "info");
3119         p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3120                               text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3121                               PAN_POPUP_TEXT_COLOR, 255);
3122         pan_item_set_key(p, "info");
3123         pan_item_size_by_item(pbox, p, 0);
3124
3125         plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3126                                    _("Size:"), TEXT_ATTR_BOLD,
3127                                    PAN_POPUP_TEXT_COLOR, 255);
3128         pan_item_set_key(plabel, "info");
3129         buf = text_from_size(pi->fd->size);
3130         p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3131                               buf, TEXT_ATTR_NONE,
3132                               PAN_POPUP_TEXT_COLOR, 255);
3133         g_free(buf);
3134         pan_item_set_key(p, "info");
3135         pan_item_size_by_item(pbox, p, 0);
3136
3137         pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3138         pan_item_added(pw, pbox);
3139 }
3140
3141
3142 /*
3143  *-----------------------------------------------------------------------------
3144  * search
3145  *-----------------------------------------------------------------------------
3146  */
3147
3148 static void pan_search_status(PanWindow *pw, const gchar *text)
3149 {
3150         gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3151 }
3152
3153 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3154 {
3155         PanItem *pi;
3156         ItemType type;
3157
3158         type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3159
3160         pi = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3161         if (!pi) return FALSE;
3162
3163         pan_info_update(pw, pi);
3164         image_scroll_to_point(pw->imd, pi->x - PAN_THUMB_GAP, pi->y - PAN_THUMB_GAP);
3165
3166         pan_search_status(pw, (path[0] == '/') ? _("path found") : _("filename found"));
3167
3168         return TRUE;
3169 }
3170
3171 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3172 {
3173         PanItem *pi;
3174         ItemType type;
3175
3176         type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3177
3178         pi = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3179         if (!pi) pi = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3180         if (!pi)
3181                 {
3182                 gchar *needle;
3183
3184                 needle = g_utf8_strdown(text, -1);
3185                 pi = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3186                 g_free(needle);
3187                 }
3188         if (!pi) return FALSE;
3189
3190         pan_info_update(pw, pi);
3191         image_scroll_to_point(pw->imd, pi->x - PAN_THUMB_GAP, pi->y - PAN_THUMB_GAP);
3192
3193         pan_search_status(pw, _("partial match"));
3194
3195         return TRUE;
3196 }
3197
3198 static gint valid_date_separator(gchar c)
3199 {
3200         return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3201 }
3202
3203 static PanItem *pan_search_by_date_val(PanWindow *pw, gint year, gint month, gint day)
3204 {
3205         GList *work;
3206
3207         work = g_list_last(pw->list);
3208         while (work)
3209                 {
3210                 PanItem *pi;
3211
3212                 pi = work->data;
3213                 work = work->prev;
3214
3215                 if (pi->fd)
3216                         {
3217                         struct tm *tl;
3218
3219                         tl = localtime(&pi->fd->date);
3220                         if (tl)
3221                                 {
3222                                 gint match;
3223
3224                                 match = (tl->tm_year == year - 1900);
3225                                 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3226                                 if (match && day > 0) match = (tl->tm_mday == day);
3227
3228                                 if (match) return pi;
3229                                 }
3230                         }
3231                 }
3232
3233         return NULL;
3234 }
3235
3236 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3237 {
3238         PanItem *pi;
3239         gint year;
3240         gint month = -1;
3241         gint day = -1;
3242         gchar *ptr;
3243         gchar *mptr;
3244         struct tm *lt;
3245         time_t t;
3246         gchar *message;
3247         gchar *buf;
3248
3249         if (!text) return FALSE;
3250
3251         ptr = (gchar *)text;
3252         while (*ptr != '\0')
3253                 {
3254                 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3255                 ptr++;
3256                 }
3257
3258         t = time(NULL);
3259         if (t == -1) return FALSE;
3260         lt = localtime(&t);
3261         if (!lt) return FALSE;
3262
3263         if (valid_date_separator(*text))
3264                 {
3265                 year = -1;
3266                 mptr = (gchar *)text;
3267                 }
3268         else
3269                 {
3270                 year = (gint)strtol(text, &mptr, 10);
3271                 if (mptr == text) return FALSE;
3272                 }
3273
3274         if (*mptr != '\0' && valid_date_separator(*mptr))
3275                 {
3276                 gchar *dptr;
3277
3278                 mptr++;
3279                 month = strtol(mptr, &dptr, 10);
3280                 if (dptr == mptr)
3281                         {
3282                         if (valid_date_separator(*dptr))
3283                                 {
3284                                 month = lt->tm_mon + 1;
3285                                 dptr++;
3286                                 }
3287                         else
3288                                 {
3289                                 month = -1;
3290                                 }
3291                         }
3292                 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3293                         {
3294                         gchar *eptr;
3295                         dptr++;
3296                         day = strtol(dptr, &eptr, 10);
3297                         if (dptr == eptr)
3298                                 {
3299                                 day = lt->tm_mday;
3300                                 }
3301                         }
3302                 }
3303
3304         if (year == -1)
3305                 {
3306                 year = lt->tm_year + 1900;
3307                 }
3308         else if (year < 100)
3309                 {
3310                 if (year > 70)
3311                         year+= 1900;
3312                 else
3313                         year+= 2000;
3314                 }
3315
3316         if (year < 1970 ||
3317             month < -1 || month == 0 || month > 12 ||
3318             day < -1 || day == 0 || day > 31) return FALSE;
3319
3320         t = date_to_time(year, month, day);
3321         if (t < 0) return FALSE;
3322
3323         pi = pan_search_by_date_val(pw, year, month, day);
3324         if (pi)
3325                 {
3326                 pan_info_update(pw, pi);
3327                 image_scroll_to_point(pw->imd, pi->x - PAN_THUMB_GAP, pi->y - PAN_THUMB_GAP);
3328                 }
3329
3330         if (month > 0)
3331                 {
3332                 buf = date_value_string(t, DATE_LENGTH_MONTH);
3333                 if (day > 0)
3334                         {
3335                         gchar *tmp;
3336                         tmp = buf;
3337                         buf = g_strdup_printf("%d %s", day, tmp);
3338                         g_free(tmp);
3339                         }
3340                 }
3341         else
3342                 {
3343                 buf = date_value_string(t, DATE_LENGTH_YEAR);
3344                 }
3345         message = g_strdup_printf("%s%s%s%s %s",
3346                                   (pi) ? "" : "(", (pi) ? "" : _("no match"), (pi) ? "" : ") " ,
3347                                   _("Date:"), buf);
3348         g_free(buf);
3349         pan_search_status(pw, message);
3350         g_free(message);
3351
3352         return TRUE;
3353 }
3354
3355 static void pan_search_activate_cb(const gchar *text, gpointer data)
3356 {
3357         PanWindow *pw = data;
3358
3359         if (!text) return;
3360
3361         tab_completion_append_to_history(pw->search_entry, text);
3362
3363         if (pan_search_by_path(pw, text)) return;
3364
3365         if (pw->layout == LAYOUT_TIMELINE && pan_search_by_date(pw, text)) return;
3366
3367         if (pan_search_by_partial(pw, text)) return;
3368
3369         pan_search_status(pw, _("no match"));
3370 }
3371
3372 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3373 {
3374         PanWindow *pw = data;
3375         gint visible;
3376
3377         visible = GTK_WIDGET_VISIBLE(pw->search_box);
3378         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3379
3380         if (visible)
3381                 {
3382                 gtk_widget_hide(pw->search_box);
3383                 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3384                 }
3385         else
3386                 {
3387                 gtk_widget_show(pw->search_box);
3388                 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3389                 gtk_widget_grab_focus(pw->search_entry);
3390                 }
3391 }
3392
3393
3394 /*
3395  *-----------------------------------------------------------------------------
3396  * view window main routines
3397  *-----------------------------------------------------------------------------
3398  */ 
3399
3400 static void button_cb(ImageWindow *imd, gint button, guint32 time,
3401                       gdouble x, gdouble y, guint state, gpointer data)
3402 {
3403         PanWindow *pw = data;
3404         PanItem *pi = NULL;
3405         GtkWidget *menu;
3406
3407         if (pw->imd->scale)
3408                 {
3409                 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3410                             (gint)((double)(pw->imd->x_scroll + x - pw->imd->x_offset) / pw->imd->scale),
3411                             (gint)((double)(pw->imd->y_scroll + y - pw->imd->y_offset) / pw->imd->scale));
3412                 }
3413
3414         switch (button)
3415                 {
3416                 case 1:
3417                         pan_info_update(pw, pi);
3418                         break;
3419                 case 2:
3420                         break;
3421                 case 3:
3422                         pan_info_update(pw, pi);
3423                         menu = pan_popup_menu(pw);
3424                         gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, time);
3425                         break;
3426                 default:
3427                         break;
3428                 }
3429 }
3430
3431 static void scroll_cb(ImageWindow *imd, GdkScrollDirection direction, guint32 time,
3432                       gdouble x, gdouble y, guint state, gpointer data)
3433 {
3434 #if 0
3435         PanWindow *pw = data;
3436 #endif
3437
3438         if (state & GDK_CONTROL_MASK)
3439                 {
3440                 switch (direction)
3441                         {
3442                         case GDK_SCROLL_UP:
3443                                 image_zoom_adjust_at_point(imd, ZOOM_INCREMENT, x, y);
3444                                 break;
3445                         case GDK_SCROLL_DOWN:
3446                                 image_zoom_adjust_at_point(imd, -ZOOM_INCREMENT, x, y);
3447                                 break;
3448                         default:
3449                                 break;
3450                         }
3451                 }
3452         else if ( (state & GDK_SHIFT_MASK) != (mousewheel_scrolls))
3453                 {
3454                 switch (direction)
3455                         {
3456                         case GDK_SCROLL_UP:
3457                                 image_scroll(imd, 0, -MOUSEWHEEL_SCROLL_SIZE);
3458                                 break;
3459                         case GDK_SCROLL_DOWN:
3460                                 image_scroll(imd, 0, MOUSEWHEEL_SCROLL_SIZE);
3461                                 break;
3462                         case GDK_SCROLL_LEFT:
3463                                 image_scroll(imd, -MOUSEWHEEL_SCROLL_SIZE, 0);
3464                                 break;
3465                         case GDK_SCROLL_RIGHT:
3466                                 image_scroll(imd, MOUSEWHEEL_SCROLL_SIZE, 0);
3467                                 break;
3468                         default:
3469                                 break;
3470                         }
3471                 }
3472         else
3473                 {
3474                 switch (direction)
3475                         {
3476                         case GDK_SCROLL_UP:
3477                                 break;
3478                         case GDK_SCROLL_DOWN:
3479                                 break;
3480                         default:
3481                                 break;
3482                         }
3483                 }
3484 }
3485
3486 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
3487 {
3488         image_set_button_func(imd, button_cb, pw);
3489         image_set_scroll_func(imd, scroll_cb, pw);
3490 }
3491
3492 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
3493 {
3494         PanWindow *pw = data;
3495
3496         pw->fs = NULL;
3497 }
3498
3499 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
3500 {
3501         if (force_off && !pw->fs) return;
3502
3503         if (pw->fs)
3504                 {
3505                 fullscreen_stop(pw->fs);
3506                 pw->imd = pw->imd_normal;
3507                 }
3508         else
3509                 {
3510                 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
3511
3512                 pan_image_set_buttons(pw, pw->fs->imd);
3513                 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
3514                                  G_CALLBACK(pan_window_key_press_cb), pw);
3515
3516                 pw->imd = pw->fs->imd;
3517                 }
3518 }
3519
3520 static void pan_overlay_toggle(PanWindow *pw)
3521 {
3522         ImageWindow *imd;
3523
3524         imd = pan_window_active_image(pw);
3525 #if 0
3526         if (pw->overlay_id == -1)
3527                 {
3528                 pw->overlay_id = image_overlay_info_enable(imd);
3529                 }
3530         else
3531                 {
3532                 image_overlay_info_disable(imd, pw->overlay_id);
3533                 pw->overlay_id = -1;
3534                 }
3535 #endif
3536 }
3537
3538 static void pan_window_image_update_cb(ImageWindow *imd, gpointer data)
3539 {
3540         PanWindow *pw = data;
3541         gchar *text;
3542
3543         text = image_zoom_get_as_text(imd);
3544         gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
3545         g_free(text);
3546 }
3547
3548 static void pan_window_image_scroll_notify_cb(ImageWindow *imd, gint x, gint y,
3549                                               gint width, gint height, gpointer data)
3550 {
3551         PanWindow *pw = data;
3552         GtkAdjustment *adj;
3553
3554         adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
3555         adj->page_size = (gdouble)imd->vis_width / imd->scale;
3556         adj->page_increment = adj->page_size / 2.0;
3557         adj->step_increment = 48.0 / imd->scale;
3558         adj->lower = 0.0;
3559         adj->upper = MAX((gdouble)width + adj->page_size, 1.0);
3560         adj->value = (gdouble)x;
3561
3562         pref_signal_block_data(pw->scrollbar_h, pw);
3563         gtk_adjustment_changed(adj);
3564         pref_signal_unblock_data(pw->scrollbar_h, pw);
3565
3566         adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
3567         adj->page_size = (gdouble)imd->vis_height / imd->scale;
3568         adj->page_increment = adj->page_size / 2.0;
3569         adj->step_increment = 48.0 / imd->scale;
3570         adj->lower = 0.0;
3571         adj->upper = MAX((gdouble)height + adj->page_size, 1.0);
3572         adj->value = (gdouble)y;
3573
3574         pref_signal_block_data(pw->scrollbar_v, pw);
3575         gtk_adjustment_changed(adj);
3576         pref_signal_unblock_data(pw->scrollbar_v, pw);
3577
3578 //      printf("scrolled to %d,%d @ %d x %d\n", x, y, width, height);
3579 }
3580
3581 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
3582 {
3583         PanWindow *pw = data;
3584         gint x;
3585
3586         if (!pw->imd->scale) return;
3587
3588         x = (gint)gtk_range_get_value(range);
3589
3590         image_scroll_to_point(pw->imd, x, (gint)((gdouble)pw->imd->y_scroll / pw->imd->scale));
3591 }
3592
3593 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
3594 {
3595         PanWindow *pw = data;
3596         gint y;
3597
3598         if (!pw->imd->scale) return;
3599
3600         y = (gint)gtk_range_get_value(range);
3601
3602         image_scroll_to_point(pw->imd, (gint)((gdouble)pw->imd->x_scroll / pw->imd->scale), y);
3603 }
3604
3605 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
3606 {
3607         PanWindow *pw = data;
3608
3609         pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3610         pan_window_layout_update_idle(pw);
3611 }
3612
3613 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
3614 {
3615         PanWindow *pw = data;
3616
3617         pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3618         pan_window_layout_update_idle(pw);
3619 }
3620
3621 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
3622 {
3623         PanWindow *pw = data;
3624         gchar *path;
3625
3626         path = remove_trailing_slash(new_text);
3627         parse_out_relatives(path);
3628
3629         if (!isdir(path))
3630                 {
3631                 warning_dialog(_("Folder not found"),
3632                                _("The entered path is not a folder"),
3633                                GTK_STOCK_DIALOG_WARNING, pw->path_entry);
3634                 return;
3635                 }
3636
3637         tab_completion_append_to_history(pw->path_entry, path);
3638
3639         g_free(pw->path);
3640         pw->path = g_strdup(path);
3641
3642         pan_window_layout_update_idle(pw);
3643 }
3644
3645 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
3646 {
3647         PanWindow *pw = data;
3648         gchar *text;
3649
3650         if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
3651
3652         text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
3653         pan_window_entry_activate_cb(text, pw);
3654         g_free(text);
3655 }
3656
3657 static void pan_window_close(PanWindow *pw)
3658 {
3659         pan_window_list = g_list_remove(pan_window_list, pw);
3660
3661         if (pw->idle_id != -1)
3662                 {
3663                 g_source_remove(pw->idle_id);
3664                 }
3665
3666         pan_fullscreen_toggle(pw, TRUE);
3667         gtk_widget_destroy(pw->window);
3668
3669         pan_window_items_free(pw);
3670
3671         g_free(pw->path);
3672
3673         g_free(pw);
3674 }
3675
3676 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
3677 {
3678         PanWindow *pw = data;
3679
3680         pan_window_close(pw);
3681         return TRUE;
3682 }
3683
3684 void pan_window_new(const gchar *path)
3685 {
3686         PanWindow *pw;
3687         GtkWidget *vbox;
3688         GtkWidget *box;
3689         GtkWidget *combo;
3690         GtkWidget *hbox;
3691         GtkWidget *frame;
3692         GtkWidget *table;
3693         GdkGeometry geometry;
3694
3695         pw = g_new0(PanWindow, 1);
3696
3697         pw->path = g_strdup(path);
3698         pw->layout = LAYOUT_TIMELINE;
3699         pw->size = LAYOUT_SIZE_THUMB_NORMAL;
3700         pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
3701         pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
3702         pw->list = NULL;
3703
3704         pw->fs = NULL;
3705         pw->overlay_id = -1;
3706         pw->idle_id = -1;
3707
3708         pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3709
3710         geometry.min_width = 8;
3711         geometry.min_height = 8;
3712         gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
3713
3714         gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
3715         gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
3716         gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
3717         gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
3718
3719         window_set_icon(pw->window, NULL, NULL);
3720
3721         vbox = gtk_vbox_new(FALSE, 0);
3722         gtk_container_add(GTK_CONTAINER(pw->window), vbox);
3723         gtk_widget_show(vbox);
3724
3725         box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3726
3727         pref_spacer(box, 0);
3728         pref_label_new(box, _("Location:"));
3729         combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
3730                                                 pan_window_entry_activate_cb, pw);
3731         g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
3732                          G_CALLBACK(pan_window_entry_change_cb), pw);
3733         gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
3734         gtk_widget_show(combo);
3735
3736         combo = gtk_combo_box_new_text();
3737         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
3738         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
3739         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
3740         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
3741
3742         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
3743         g_signal_connect(G_OBJECT(combo), "changed",
3744                          G_CALLBACK(pan_window_layout_change_cb), pw);
3745         gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
3746         gtk_widget_show(combo);
3747
3748         combo = gtk_combo_box_new_text();
3749         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
3750         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
3751         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
3752         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
3753         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
3754         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
3755         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
3756         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
3757         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
3758
3759         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
3760         g_signal_connect(G_OBJECT(combo), "changed",
3761                          G_CALLBACK(pan_window_layout_size_cb), pw);
3762         gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
3763         gtk_widget_show(combo);
3764
3765         table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
3766         gtk_table_set_row_spacings(GTK_TABLE(table), 2);
3767         gtk_table_set_col_spacings(GTK_TABLE(table), 2);
3768
3769         pw->imd = image_new(TRUE);
3770         pw->imd_normal = pw->imd;
3771
3772         if (black_window_background) image_background_set_black(pw->imd, TRUE);
3773         image_set_update_func(pw->imd, pan_window_image_update_cb, pw);
3774
3775         image_set_scroll_notify_func(pw->imd, pan_window_image_scroll_notify_cb, pw);
3776
3777 #if 0
3778         gtk_box_pack_start(GTK_BOX(vbox), pw->imd->widget, TRUE, TRUE, 0);
3779 #endif
3780         gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
3781                          GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
3782         gtk_widget_show(pw->imd->widget);
3783
3784         pan_window_dnd_init(pw);
3785
3786         pan_image_set_buttons(pw, pw->imd);
3787
3788         pw->scrollbar_h = gtk_hscrollbar_new(NULL);
3789         g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
3790                          G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
3791         gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
3792                          GTK_FILL | GTK_EXPAND, 0, 0, 0);
3793         gtk_widget_show(pw->scrollbar_h);
3794
3795         pw->scrollbar_v = gtk_vscrollbar_new(NULL);
3796         g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
3797                          G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
3798         gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
3799                          0, GTK_FILL | GTK_EXPAND, 0, 0);
3800         gtk_widget_show(pw->scrollbar_v);
3801
3802         /* find bar */
3803
3804         pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
3805         gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
3806
3807         pref_spacer(pw->search_box, 0);
3808         pref_label_new(pw->search_box, _("Find:"));
3809
3810         hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
3811         gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
3812         gtk_widget_show(hbox);
3813
3814         combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
3815                                                 pan_search_activate_cb, pw);
3816         gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
3817         gtk_widget_show(combo);
3818
3819         pw->search_label = gtk_label_new("");
3820         gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
3821         gtk_widget_show(pw->search_label);
3822
3823         /* status bar */
3824
3825         box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3826
3827         frame = gtk_frame_new(NULL);
3828         gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
3829         gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
3830         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
3831         gtk_widget_show(frame);
3832
3833         hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
3834         gtk_container_add(GTK_CONTAINER(frame), hbox);
3835         gtk_widget_show(hbox);
3836
3837         pref_spacer(hbox, PREF_PAD_SPACE);
3838         pw->label_message = pref_label_new(hbox, "");
3839
3840         frame = gtk_frame_new(NULL);
3841         gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
3842         gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
3843         gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
3844         gtk_widget_show(frame);
3845
3846         pw->label_zoom = gtk_label_new("");
3847         gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
3848         gtk_widget_show(pw->label_zoom);
3849
3850         pw->search_button = gtk_toggle_button_new();
3851         gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
3852         gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
3853         hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
3854         gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
3855         gtk_widget_show(hbox);
3856         pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
3857         gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
3858         gtk_widget_show(pw->search_button_arrow);
3859         pref_label_new(hbox, _("Find"));
3860
3861         gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
3862         gtk_widget_show(pw->search_button);
3863         g_signal_connect(G_OBJECT(pw->search_button), "clicked",
3864                          G_CALLBACK(pan_search_toggle_cb), pw);
3865
3866         g_signal_connect(G_OBJECT(pw->window), "delete_event",
3867                          G_CALLBACK(pan_window_delete_cb), pw);
3868         g_signal_connect(G_OBJECT(pw->window), "key_press_event",
3869                          G_CALLBACK(pan_window_key_press_cb), pw);
3870
3871         gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
3872
3873         pan_window_layout_update_idle(pw);
3874
3875         gtk_widget_grab_focus(pw->imd->widget);
3876         gtk_widget_show(pw->window);
3877
3878         pan_window_list = g_list_append(pan_window_list, pw);
3879 }
3880
3881 /*
3882  *-----------------------------------------------------------------------------
3883  * public
3884  *-----------------------------------------------------------------------------
3885  */
3886
3887 /*
3888  *-----------------------------------------------------------------------------
3889  * view window menu routines and callbacks
3890  *-----------------------------------------------------------------------------
3891  */
3892
3893 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
3894 {
3895         PanWindow *pw = data;
3896         const gchar *path;
3897
3898         path = pan_menu_click_path(pw);
3899         if (path)
3900                 {
3901                 pan_fullscreen_toggle(pw, TRUE);
3902                 view_window_new(path);
3903                 }
3904 }
3905
3906 static void pan_edit_cb(GtkWidget *widget, gpointer data)
3907 {
3908         PanWindow *pw;
3909         const gchar *path;
3910         gint n;
3911
3912         pw = submenu_item_get_data(widget);
3913         n = GPOINTER_TO_INT(data);
3914         if (!pw) return;
3915
3916         path = pan_menu_click_path(pw);
3917         if (path)
3918                 {
3919                 pan_fullscreen_toggle(pw, TRUE);
3920                 start_editor_from_file(n, path);
3921                 }
3922 }
3923
3924 static void pan_info_cb(GtkWidget *widget, gpointer data)
3925 {
3926         PanWindow *pw = data;
3927         const gchar *path;
3928
3929         path = pan_menu_click_path(pw);
3930         if (path) info_window_new(path, NULL);
3931 }
3932
3933 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
3934 {
3935         PanWindow *pw = data;
3936
3937         image_zoom_adjust(pan_window_active_image(pw), ZOOM_INCREMENT);
3938 }
3939
3940 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
3941 {
3942         PanWindow *pw = data;
3943
3944         image_zoom_adjust(pan_window_active_image(pw), -ZOOM_INCREMENT);
3945 }
3946
3947 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
3948 {
3949         PanWindow *pw = data;
3950
3951         image_zoom_set(pan_window_active_image(pw), 1.0);
3952 }
3953
3954 static void pan_copy_cb(GtkWidget *widget, gpointer data)
3955 {
3956         PanWindow *pw = data;
3957         const gchar *path;
3958
3959         path = pan_menu_click_path(pw);
3960         if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
3961 }
3962
3963 static void pan_move_cb(GtkWidget *widget, gpointer data)
3964 {
3965         PanWindow *pw = data;
3966         const gchar *path;
3967
3968         path = pan_menu_click_path(pw);
3969         if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
3970 }
3971
3972 static void pan_rename_cb(GtkWidget *widget, gpointer data)
3973 {
3974         PanWindow *pw = data;
3975         const gchar *path;
3976
3977         path = pan_menu_click_path(pw);
3978         if (path) file_util_rename(path, NULL, pw->imd->widget);
3979 }
3980
3981 static void pan_delete_cb(GtkWidget *widget, gpointer data)
3982 {
3983         PanWindow *pw = data;
3984         const gchar *path;
3985
3986         path = pan_menu_click_path(pw);
3987         if (path) file_util_delete(path, NULL, pw->imd->widget);
3988 }
3989
3990 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
3991 {
3992         PanWindow *pw = data;
3993
3994         pan_fullscreen_toggle(pw, FALSE);
3995 }
3996
3997 static void pan_close_cb(GtkWidget *widget, gpointer data)
3998 {
3999         PanWindow *pw = data;
4000
4001         pan_window_close(pw);
4002 }
4003
4004 static GtkWidget *pan_popup_menu(PanWindow *pw)
4005 {
4006         GtkWidget *menu;
4007         GtkWidget *item;
4008         gint active;
4009
4010         active = (pw->click_pi != NULL);
4011
4012         menu = popup_menu_short_lived();
4013
4014         menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4015                             G_CALLBACK(pan_zoom_in_cb), pw);
4016         menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4017                             G_CALLBACK(pan_zoom_out_cb), pw);
4018         menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4019                             G_CALLBACK(pan_zoom_1_1_cb), pw);
4020         menu_item_add_divider(menu);
4021
4022         submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4023         gtk_widget_set_sensitive(item, active);
4024
4025         menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4026                                       G_CALLBACK(pan_info_cb), pw);
4027
4028         menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4029                                       G_CALLBACK(pan_new_window_cb), pw);
4030
4031         menu_item_add_divider(menu);
4032         menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4033                                       G_CALLBACK(pan_copy_cb), pw);
4034         menu_item_add_sensitive(menu, _("_Move..."), active,
4035                                 G_CALLBACK(pan_move_cb), pw);
4036         menu_item_add_sensitive(menu, _("_Rename..."), active,
4037                                 G_CALLBACK(pan_rename_cb), pw);
4038         menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4039                                       G_CALLBACK(pan_delete_cb), pw);
4040
4041         menu_item_add_divider(menu);
4042
4043         if (pw->fs)
4044                 {
4045                 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4046                 }
4047         else
4048                 {
4049                 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4050                 }
4051
4052         menu_item_add_divider(menu);
4053         menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4054
4055         return menu;
4056 }
4057
4058 /*
4059  *-----------------------------------------------------------------------------
4060  * image drag and drop routines
4061  *-----------------------------------------------------------------------------
4062  */
4063
4064 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4065                                      gint x, gint y,
4066                                      GtkSelectionData *selection_data, guint info,
4067                                      guint time, gpointer data)
4068 {
4069         PanWindow *pw = data;
4070         ImageWindow *imd;
4071
4072         if (gtk_drag_get_source_widget(context) == pw->imd->image) return;
4073
4074         imd = pw->imd;
4075
4076         if (info == TARGET_URI_LIST)
4077                 {
4078                 GList *list;
4079
4080                 list = uri_list_from_text(selection_data->data, TRUE);
4081                 if (list && isdir((gchar *)list->data))
4082                         {
4083                         printf("FIXME: change to this folder: %s\n", (gchar *)list->data);
4084                         }
4085
4086                 path_list_free(list);
4087                 }
4088 }
4089
4090 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4091                                      GtkSelectionData *selection_data, guint info,
4092                                      guint time, gpointer data)
4093 {
4094         printf("FIXME: set dnd data\n");
4095 }
4096
4097 static void pan_window_dnd_init(PanWindow *pw)
4098 {
4099         ImageWindow *imd;
4100
4101         imd = pw->imd;
4102
4103         gtk_drag_source_set(imd->image, GDK_BUTTON2_MASK,
4104                             dnd_file_drag_types, dnd_file_drag_types_count,
4105                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4106         g_signal_connect(G_OBJECT(imd->image), "drag_data_get",
4107                          G_CALLBACK(pan_window_set_dnd_data), pw);
4108
4109         gtk_drag_dest_set(imd->image,
4110                           GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4111                           dnd_file_drop_types, dnd_file_drop_types_count,
4112                           GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4113         g_signal_connect(G_OBJECT(imd->image), "drag_data_received",
4114                          G_CALLBACK(pan_window_get_dnd_data), pw);
4115 }
4116
4117 /*
4118  *-----------------------------------------------------------------------------
4119  * maintenance (for rename, move, remove)
4120  *-----------------------------------------------------------------------------
4121  */
4122