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