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