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