Wed Mar 23 00:22:28 2005 John Ellis <johne@verizon.net>
[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-renderer.h"
27 #include "pixbuf_util.h"
28 #include "thumb.h"
29 #include "utilops.h"
30 #include "ui_bookmark.h"
31 #include "ui_fileops.h"
32 #include "ui_menu.h"
33 #include "ui_misc.h"
34 #include "ui_tabcomp.h"
35
36 #include <gdk/gdkkeysyms.h> /* for keyboard values */
37 #include <math.h>
38
39
40 #define PAN_WINDOW_DEFAULT_WIDTH 720
41 #define PAN_WINDOW_DEFAULT_HEIGHT 500
42
43 #define PAN_TILE_SIZE 512
44
45 #define PAN_THUMB_SIZE_DOTS 4
46 #define PAN_THUMB_SIZE_NONE 24
47 #define PAN_THUMB_SIZE_SMALL 64
48 #define PAN_THUMB_SIZE_NORMAL 128
49 #define PAN_THUMB_SIZE_LARGE 256
50 #define PAN_THUMB_SIZE pw->thumb_size
51
52 #define PAN_THUMB_GAP_DOTS 2
53 #define PAN_THUMB_GAP_SMALL 14
54 #define PAN_THUMB_GAP_NORMAL 30
55 #define PAN_THUMB_GAP_LARGE 40
56 #define PAN_THUMB_GAP_HUGE 50
57 #define PAN_THUMB_GAP pw->thumb_gap
58
59 #define PAN_SHADOW_OFFSET 6
60 #define PAN_SHADOW_FADE 5
61 #define PAN_SHADOW_COLOR 0, 0, 0
62 #define PAN_SHADOW_ALPHA 64
63
64 #define PAN_OUTLINE_THICKNESS 1
65 #define PAN_OUTLINE_COLOR_1 255, 255, 255
66 #define PAN_OUTLINE_COLOR_2 64, 64, 64
67 #define PAN_OUTLINE_ALPHA 180
68
69 #define PAN_BACKGROUND_COLOR 255, 255, 230
70
71 #define PAN_GRID_SIZE 10
72 #define PAN_GRID_COLOR 0, 0, 0
73 #define PAN_GRID_ALPHA 20
74
75 #define PAN_FOLDER_BOX_COLOR 0, 0, 255
76 #define PAN_FOLDER_BOX_ALPHA 10
77 #define PAN_FOLDER_BOX_BORDER 20
78
79 #define PAN_FOLDER_BOX_OUTLINE_THICKNESS 4
80 #define PAN_FOLDER_BOX_OUTLINE_COLOR 0, 0, 255
81 #define PAN_FOLDER_BOX_OUTLINE_ALPHA 64
82
83 #define PAN_TEXT_BORDER_SIZE 4
84 #define PAN_TEXT_COLOR 0, 0, 0
85
86 #define PAN_POPUP_COLOR 255, 255, 220
87 #define PAN_POPUP_ALPHA 255
88 #define PAN_POPUP_BORDER 1
89 #define PAN_POPUP_BORDER_COLOR 0, 0, 0
90 #define PAN_POPUP_TEXT_COLOR 0, 0, 0
91
92 #define PAN_GROUP_MAX 16
93
94 #define ZOOM_INCREMENT 1.0
95 #define ZOOM_LABEL_WIDTH 64
96
97
98 #define PAN_PREF_GROUP "pan_view_options"
99 #define PAN_PREF_HIDE_WARNING "hide_performance_warning"
100
101
102 typedef enum {
103         LAYOUT_TIMELINE = 0,
104         LAYOUT_CALENDAR,
105         LAYOUT_FOLDERS_LINEAR,
106         LAYOUT_FOLDERS_FLOWER,
107         LAYOUT_GRID,
108 } LayoutType;
109
110 typedef enum {
111         LAYOUT_SIZE_THUMB_DOTS = 0,
112         LAYOUT_SIZE_THUMB_NONE,
113         LAYOUT_SIZE_THUMB_SMALL,
114         LAYOUT_SIZE_THUMB_NORMAL,
115         LAYOUT_SIZE_THUMB_LARGE,
116         LAYOUT_SIZE_10,
117         LAYOUT_SIZE_25,
118         LAYOUT_SIZE_33,
119         LAYOUT_SIZE_50,
120         LAYOUT_SIZE_100
121 } LayoutSize;
122
123 typedef enum {
124         ITEM_NONE,
125         ITEM_THUMB,
126         ITEM_BOX,
127         ITEM_TRIANGLE,
128         ITEM_TEXT,
129         ITEM_IMAGE
130 } ItemType;
131
132 typedef enum {
133         TEXT_ATTR_NONE = 0,
134         TEXT_ATTR_BOLD = 1 << 0,
135         TEXT_ATTR_HEADING = 1 << 1,
136         TEXT_ATTR_MARKUP = 1 << 2
137 } TextAttrType;
138
139 enum {
140         BORDER_NONE = 0,
141         BORDER_1 = 1 << 0,
142         BORDER_2 = 1 << 1,
143         BORDER_3 = 1 << 2,
144         BORDER_4 = 1 << 3
145 };
146
147 typedef struct _PanItem PanItem;
148 struct _PanItem {
149         ItemType type;
150         gint x;
151         gint y;
152         gint width;
153         gint height;
154         gchar *key;
155
156         FileData *fd;
157
158         GdkPixbuf *pixbuf;
159         gint refcount;
160
161         gchar *text;
162         TextAttrType text_attr;
163
164         guint8 color_r;
165         guint8 color_g;
166         guint8 color_b;
167         guint8 color_a;
168
169         guint8 color2_r;
170         guint8 color2_g;
171         guint8 color2_b;
172         guint8 color2_a;
173         gint border;
174
175         gpointer data;
176
177         gint queued;
178 };
179
180 typedef struct _PanWindow PanWindow;
181 struct _PanWindow
182 {
183         GtkWidget *window;
184         ImageWindow *imd;
185         ImageWindow *imd_normal;
186         FullScreenData *fs;
187
188         GtkWidget *path_entry;
189
190         GtkWidget *label_message;
191         GtkWidget *label_zoom;
192
193         GtkWidget *search_box;
194         GtkWidget *search_entry;
195         GtkWidget *search_label;
196         GtkWidget *search_button;
197         GtkWidget *search_button_arrow;
198
199         GtkWidget *scrollbar_h;
200         GtkWidget *scrollbar_v;
201
202         gint overlay_id;
203
204         gchar *path;
205         LayoutType layout;
206         LayoutSize size;
207         gint thumb_size;
208         gint thumb_gap;
209         gint image_size;
210
211         GList *list;
212
213         GList *cache_list;
214         GList *cache_todo;
215         gint cache_count;
216         gint cache_total;
217         gint cache_tick;
218
219         ImageLoader *il;
220         ThumbLoader *tl;
221         PanItem *queue_pi;
222         GList *queue;
223
224         PanItem *click_pi;
225         PanItem *search_pi;
226
227         gint idle_id;
228 };
229
230 typedef struct _PanCacheData PanCacheData;
231 struct _PanCacheData {
232         FileData fd;
233         CacheData *cd;
234 };
235
236
237 static GList *pan_window_list = NULL;
238
239
240 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend);
241
242 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
243
244 static GtkWidget *pan_popup_menu(PanWindow *pw);
245 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off);
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->pr);
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         work = pw->list;
1277         while (work)
1278                 {
1279                 PanItem *pi;
1280
1281                 pi = work->data;
1282                 if ((pi->type == type || type == ITEM_NONE) &&
1283                      x >= pi->x && x < pi->x + pi->width &&
1284                      y >= pi->y && y < pi->y + pi->height &&
1285                     (!key || (pi->key && strcmp(pi->key, key) == 0)))
1286                         {
1287                         return pi;
1288                         }
1289                 work = work->next;
1290                 }
1291
1292         return NULL;
1293 }
1294
1295 /*
1296  *-----------------------------------------------------------------------------
1297  * layout generation
1298  *-----------------------------------------------------------------------------
1299  */
1300
1301 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend)
1302 {
1303         GList *flist = NULL;
1304         GList *dlist = NULL;
1305         GList *result;
1306         GList *folders;
1307
1308         filelist_read(path, &flist, &dlist);
1309         if (sort != SORT_NONE)
1310                 {
1311                 flist = filelist_sort(flist, sort, ascend);
1312                 dlist = filelist_sort(dlist, sort, ascend);
1313                 }
1314
1315         result = flist;
1316         folders = dlist;
1317         while (folders)
1318                 {
1319                 FileData *fd;
1320
1321                 fd = folders->data;
1322                 folders = g_list_remove(folders, fd);
1323
1324                 if (filelist_read(fd->path, &flist, &dlist))
1325                         {
1326                         if (sort != SORT_NONE)
1327                                 {
1328                                 flist = filelist_sort(flist, sort, ascend);
1329                                 dlist = filelist_sort(dlist, sort, ascend);
1330                                 }
1331
1332                         result = g_list_concat(result, flist);
1333                         folders = g_list_concat(dlist, folders);
1334                         }
1335
1336                 file_data_free(fd);
1337                 }
1338
1339         return result;
1340 }
1341
1342 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1343 {
1344         GList *list;
1345         GList *work;
1346         gint x, y;
1347         gint grid_size;
1348         gint next_y;
1349
1350         list = pan_window_layout_list(path, SORT_NAME, TRUE);
1351
1352         grid_size = (gint)sqrt((double)g_list_length(list));
1353         if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1354                 {
1355                 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1356                 }
1357         else
1358                 {
1359                 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1360                 }
1361
1362         next_y = 0;
1363
1364         *width = PAN_FOLDER_BOX_BORDER * 2;
1365         *height = PAN_FOLDER_BOX_BORDER * 2;
1366
1367         x = PAN_THUMB_GAP;
1368         y = PAN_THUMB_GAP;
1369         work = list;
1370         while (work)
1371                 {
1372                 FileData *fd;
1373                 PanItem *pi;
1374
1375                 fd = work->data;
1376                 work = work->next;
1377
1378                 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1379                         {
1380                         pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1381
1382                         x += pi->width + PAN_THUMB_GAP;
1383                         if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1384                         if (x > grid_size)
1385                                 {
1386                                 x = PAN_THUMB_GAP;
1387                                 y = next_y;
1388                                 }
1389                         }
1390                 else
1391                         {
1392                         pi = pan_item_new_thumb(pw, fd, x, y);
1393
1394                         x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1395                         if (x > grid_size)
1396                                 {
1397                                 x = PAN_THUMB_GAP;
1398                                 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1399                                 }
1400                         }
1401                 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1402                 }
1403
1404         g_list_free(list);
1405 }
1406
1407 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1408 {
1409         GList *work;
1410         gint x1, y1, x2, y2;
1411
1412         x1 = 0;
1413         y1 = 0;
1414         x2 = 0;
1415         y2 = 0;
1416
1417         work = pw->list;
1418         while (work)
1419                 {
1420                 PanItem *pi;
1421
1422                 pi = work->data;
1423                 work = work->next;
1424
1425                 if (x1 > pi->x) x1 = pi->x;
1426                 if (y1 > pi->y) y1 = pi->y;
1427                 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1428                 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1429                 }
1430
1431         x1 -= PAN_FOLDER_BOX_BORDER;
1432         y1 -= PAN_FOLDER_BOX_BORDER;
1433         x2 += PAN_FOLDER_BOX_BORDER;
1434         y2 += PAN_FOLDER_BOX_BORDER;
1435
1436         work = pw->list;
1437         while (work)
1438                 {
1439                 PanItem *pi;
1440
1441                 pi = work->data;
1442                 work = work->next;
1443
1444                 pi->x -= x1;
1445                 pi->y -= y1;
1446
1447                 if (pi->type == ITEM_TRIANGLE && pi->data)
1448                         {
1449                         gint *coord;
1450
1451                         coord = pi->data;
1452                         coord[0] -= x1;
1453                         coord[1] -= y1;
1454                         coord[2] -= x1;
1455                         coord[3] -= y1;
1456                         coord[4] -= x1;
1457                         coord[5] -= y1;
1458                         }
1459                 }
1460
1461         if (width) *width = x2 - x1;
1462         if (height) *height = y2 - y1;
1463 }
1464
1465 typedef struct _FlowerGroup FlowerGroup;
1466 struct _FlowerGroup {
1467         GList *items;
1468         GList *children;
1469         gint x;
1470         gint y;
1471         gint width;
1472         gint height;
1473
1474         gdouble angle;
1475         gint circumference;
1476         gint diameter;
1477 };
1478
1479 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1480 {
1481         GList *work;
1482
1483         work = group->items;
1484         while (work)
1485                 {
1486                 PanItem *pi;
1487
1488                 pi = work->data;
1489                 work = work->next;
1490
1491                 pi->x += x;
1492                 pi->y += y;
1493                 }
1494
1495         group->x += x;
1496         group->y += y;
1497 }
1498
1499 #define PI 3.14159
1500
1501 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1502                                                              gint *result_x, gint *result_y)
1503 {
1504         gint x, y;
1505         gint radius;
1506         gdouble a;
1507
1508         radius = parent->circumference / (2*PI);
1509         radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1510
1511         a = 2*PI * group->diameter / parent->circumference;
1512
1513         x = (gint)((double)radius * cos(parent->angle + a / 2));
1514         y = (gint)((double)radius * sin(parent->angle + a / 2));
1515
1516         parent->angle += a;
1517
1518         x += parent->x;
1519         y += parent->y;
1520
1521         x += parent->width / 2;
1522         y += parent->height / 2;
1523
1524         x -= group->width / 2;
1525         y -= group->height / 2;
1526
1527         *result_x = x;
1528         *result_y = y;
1529 }
1530
1531 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1532 {
1533         GList *work;
1534         gint x, y;
1535
1536         if (!group) return;
1537
1538         if (parent && parent->children)
1539                 {
1540                 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1541                 }
1542         else
1543                 {
1544                 x = 0;
1545                 y = 0;
1546                 }
1547
1548         pan_window_layout_compute_folder_flower_move(group, x, y);
1549
1550         if (parent)
1551                 {
1552                 PanItem *pi;
1553                 gint px, py, gx, gy;
1554                 gint x1, y1, x2, y2;
1555
1556                 px = parent->x + parent->width / 2;
1557                 py = parent->y + parent->height / 2;
1558
1559                 gx = group->x + group->width / 2;
1560                 gy = group->y + group->height / 2;
1561
1562                 x1 = MIN(px, gx);
1563                 y1 = MIN(py, gy);
1564
1565                 x2 = MAX(px, gx + 5);
1566                 y2 = MAX(py, gy + 5);
1567
1568                 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1569                                       px, py, gx, gy, gx + 5, gy + 5,
1570                                       255, 40, 40, 128);
1571                 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1572                                     255, 0, 0, 128);
1573                 }
1574
1575         pw->list = g_list_concat(group->items, pw->list);
1576         group->items = NULL;
1577
1578         group->circumference = 0;
1579         work = group->children;
1580         while (work)
1581                 {
1582                 FlowerGroup *child;
1583
1584                 child = work->data;
1585                 work = work->next;
1586
1587                 group->circumference += child->diameter;
1588                 }
1589
1590         work = g_list_last(group->children);
1591         while (work)
1592                 {
1593                 FlowerGroup *child;
1594
1595                 child = work->data;
1596                 work = work->prev;
1597
1598                 pan_window_layout_compute_folder_flower_build(pw, child, group);
1599                 }
1600
1601         g_list_free(group->children);
1602         g_free(group);
1603 }
1604
1605 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1606                                                                   gint x, gint y)
1607 {
1608         FlowerGroup *group;
1609         GList *f;
1610         GList *d;
1611         GList *work;
1612         PanItem *pi_box;
1613         gint x_start;
1614         gint y_height;
1615         gint grid_size;
1616         gint grid_count;
1617
1618         if (!filelist_read(path, &f, &d)) return NULL;
1619         if (!f && !d) return NULL;
1620
1621         f = filelist_sort(f, SORT_NAME, TRUE);
1622         d = filelist_sort(d, SORT_NAME, TRUE);
1623
1624         pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1625                                    PAN_TEXT_COLOR, 255);
1626
1627         y += pi_box->height;
1628
1629         pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1630                                   x, y,
1631                                   PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1632                                   PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1633                                   PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1634                                   PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1635
1636         x += PAN_FOLDER_BOX_BORDER;
1637         y += PAN_FOLDER_BOX_BORDER;
1638
1639         grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1640         grid_count = 0;
1641         x_start = x;
1642         y_height = y;
1643
1644         work = f;
1645         while (work)
1646                 {
1647                 FileData *fd;
1648                 PanItem *pi;
1649
1650                 fd = work->data;
1651                 work = work->next;
1652
1653                 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1654                         {
1655                         pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1656                         x += pi->width + PAN_THUMB_GAP;
1657                         if (pi->height > y_height) y_height = pi->height;
1658                         }
1659                 else
1660                         {
1661                         pi = pan_item_new_thumb(pw, fd, x, y);
1662                         x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1663                         y_height = PAN_THUMB_SIZE;
1664                         }
1665
1666                 grid_count++;
1667                 if (grid_count >= grid_size)
1668                         {
1669                         grid_count = 0;
1670                         x = x_start;
1671                         y += y_height + PAN_THUMB_GAP;
1672                         y_height = 0;
1673                         }
1674
1675                 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1676                 }
1677
1678         g_list_free(f);
1679
1680         group = g_new0(FlowerGroup, 1);
1681         group->items = pw->list;
1682         pw->list = NULL;
1683
1684         group->width = pi_box->width;
1685         group->height = pi_box->y + pi_box->height;
1686         group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1687
1688         group->children = NULL;
1689
1690         work = d;
1691         while (work)
1692                 {
1693                 FileData *fd;
1694                 FlowerGroup *child;
1695
1696                 fd = work->data;
1697                 work = work->next;
1698
1699                 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1700                 if (child) group->children = g_list_prepend(group->children, child);
1701                 }
1702
1703         filelist_free(d);
1704
1705         return group;
1706 }
1707
1708 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1709                                                      gint *width, gint *height,
1710                                                      gint *scroll_x, gint *scroll_y)
1711 {
1712         FlowerGroup *group;
1713         GList *list;
1714
1715         group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1716         pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1717
1718         pan_window_Layout_compute_folders_flower_size(pw, width, height);
1719
1720         list = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1721         if (list)
1722                 {
1723                 PanItem *pi = list->data;
1724                 *scroll_x = pi->x + pi->width / 2;
1725                 *scroll_y = pi->y + pi->height / 2;
1726                 }
1727         g_list_free(list);
1728 }
1729
1730 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1731                                                           gint *x, gint *y, gint *level,
1732                                                           PanItem *parent,
1733                                                           gint *width, gint *height)
1734 {
1735         GList *f;
1736         GList *d;
1737         GList *work;
1738         PanItem *pi_box;
1739         gint y_height = 0;
1740
1741         if (!filelist_read(path, &f, &d)) return;
1742         if (!f && !d) return;
1743
1744         f = filelist_sort(f, SORT_NAME, TRUE);
1745         d = filelist_sort(d, SORT_NAME, TRUE);
1746
1747         *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1748
1749         pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1750                                    PAN_TEXT_COLOR, 255);
1751
1752         *y += pi_box->height;
1753
1754         pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1755                                   *x, *y,
1756                                   PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1757                                   PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1758                                   PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1759                                   PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1760
1761         *x += PAN_FOLDER_BOX_BORDER;
1762         *y += PAN_FOLDER_BOX_BORDER;
1763
1764         work = f;
1765         while (work)
1766                 {
1767                 FileData *fd;
1768                 PanItem *pi;
1769
1770                 fd = work->data;
1771                 work = work->next;
1772
1773                 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1774                         {
1775                         pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1776                         *x += pi->width + PAN_THUMB_GAP;
1777                         if (pi->height > y_height) y_height = pi->height;
1778                         }
1779                 else
1780                         {
1781                         pi = pan_item_new_thumb(pw, fd, *x, *y);
1782                         *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1783                         y_height = PAN_THUMB_SIZE;
1784                         }
1785
1786                 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1787                 }
1788
1789         if (f) *y = pi_box->y + pi_box->height;
1790
1791         g_list_free(f);
1792
1793         work = d;
1794         while (work)
1795                 {
1796                 FileData *fd;
1797
1798                 fd = work->data;
1799                 work = work->next;
1800
1801                 *level = *level + 1;
1802                 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1803                                                               pi_box, width, height);
1804                 *level = *level - 1;
1805                 }
1806
1807         filelist_free(d);
1808
1809         pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1810
1811         if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1812                 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1813
1814         pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1815 }
1816
1817 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1818 {
1819         gint x, y;
1820         gint level;
1821         gint w, h;
1822
1823         level = 0;
1824         x = PAN_FOLDER_BOX_BORDER;
1825         y = PAN_FOLDER_BOX_BORDER;
1826         w = PAN_FOLDER_BOX_BORDER * 2;
1827         h = PAN_FOLDER_BOX_BORDER * 2;
1828
1829         pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1830
1831         if (width) *width = w;
1832         if (height) *height = h;
1833 }
1834
1835 /*
1836  *-----------------------------------------------------------------------------
1837  * calendar
1838  *-----------------------------------------------------------------------------
1839  */
1840
1841 #define PAN_CAL_DAY_WIDTH 100
1842 #define PAN_CAL_DAY_HEIGHT 80
1843 #define PAN_CAL_DOT_SIZE 3
1844 #define PAN_CAL_DOT_GAP 2
1845 #define PAN_CAL_DOT_COLOR 0, 0, 0
1846 #define PAN_CAL_DOT_ALPHA 32
1847
1848 static void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
1849 {
1850         PanItem *pbox;
1851         PanItem *pi;
1852         GList *list;
1853         GList *work;
1854         gint x1, y1, x2, y2, x3, y3;
1855         gint x, y, w, h;
1856         gint grid;
1857         gint column;
1858         
1859         while ((pi = pan_item_find_by_key(pw, ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
1860
1861         if (!pi_day || pi_day->type != ITEM_BOX ||
1862             !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
1863
1864         list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
1865
1866         work = list;
1867         while (work)
1868                 {
1869                 PanItem *dot;
1870                 GList *node;
1871
1872                 dot = work->data;
1873                 node = work;
1874                 work = work->next;
1875
1876                 if (dot->type != ITEM_BOX || !dot->fd ||
1877                     !dot->key || strcmp(dot->key, "dot") != 0)
1878                         {
1879                         list = g_list_delete_link(list, node);
1880                         }
1881                 }
1882
1883 #if 0
1884         if (!list) return;
1885 #endif
1886
1887         grid = (gint)(sqrt(g_list_length(list)) + 0.5);
1888
1889         x = pi_day->x + pi_day->width + 4;
1890         y = pi_day->y;
1891
1892 #if 0
1893         if (y + grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4 > pw->pr->image_height)
1894                 {
1895                 y = pw->pr->image_height - (grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4);
1896                 }
1897 #endif
1898
1899         pbox = pan_item_new_box(pw, NULL, x, y, PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1900                                 PAN_POPUP_BORDER,
1901                                 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
1902                                 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
1903         pan_item_set_key(pbox, "day_bubble");
1904
1905         if (pi_day->fd)
1906                 {
1907                 PanItem *plabel;
1908                 gchar *buf;
1909
1910                 buf = date_value_string(pi_day->fd->date, DATE_LENGTH_WEEK);
1911                 plabel = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1912                                            PAN_POPUP_TEXT_COLOR, 255);
1913                 pan_item_set_key(plabel, "day_bubble");
1914                 g_free(buf);
1915
1916                 pan_item_size_by_item(pbox, plabel, 0);
1917
1918                 y += plabel->height;
1919                 }
1920
1921         if (list)
1922                 {
1923                 column = 0;
1924
1925                 x += PAN_FOLDER_BOX_BORDER;
1926                 y += PAN_FOLDER_BOX_BORDER;
1927
1928                 work = list;
1929                 while (work)
1930                         {
1931                         PanItem *dot;
1932
1933                         dot = work->data;
1934                         work = work->next;
1935
1936                         if (dot->fd)
1937                                 {
1938                                 PanItem *pimg;
1939
1940                                 pimg = pan_item_new_thumb(pw, file_data_new_simple(dot->fd->path), x, y);
1941                                 pan_item_set_key(pimg, "day_bubble");
1942
1943                                 pan_item_size_by_item(pbox, pimg, PAN_FOLDER_BOX_BORDER);
1944
1945                                 column++;
1946                                 if (column < grid)
1947                                         {
1948                                         x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1949                                         }
1950                                 else
1951                                         {
1952                                         column = 0;
1953                                         x = pbox->x + PAN_FOLDER_BOX_BORDER;
1954                                         y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1955                                         }
1956                                 }
1957                         }
1958                 }
1959
1960         x1 = pi_day->x + pi_day->width - 8;
1961         y1 = pi_day->y + 8;
1962         x2 = pbox->x + 1;
1963         y2 = pbox->y + MIN(42, pbox->height);
1964         x3 = pbox->x + 1;
1965         y3 = MAX(pbox->y, y2 - 30);
1966         triangle_rect_region(x1, y1, x2, y2, x3, y3,
1967                              &x, &y, &w, &h);
1968
1969         pi = pan_item_new_tri(pw, NULL, x, y, w, h,
1970                               x1, y1, x2, y2, x3, y3,
1971                               PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
1972         pan_item_tri_border(pi, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
1973         pan_item_set_key(pi, "day_bubble");
1974         pan_item_added(pw, pi);
1975
1976         pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
1977         pan_item_added(pw, pbox);
1978 }
1979
1980 static void pan_window_layout_compute_calendar(PanWindow *pw, const gchar *path, gint *width, gint *height)
1981 {
1982         GList *list;
1983         GList *work;
1984         gint x, y;
1985         time_t tc;
1986         gint count;
1987         gint day_max;
1988         gint day_width;
1989         gint day_height;
1990         gint grid;
1991         gint year = 0;
1992         gint month = 0;
1993         gint end_year = 0;
1994         gint end_month = 0;
1995
1996         pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
1997
1998         list = pan_window_layout_list(path, SORT_NONE, TRUE);
1999         list = filelist_sort(list, SORT_TIME, TRUE);
2000
2001         day_max = 0;
2002         count = 0;
2003         tc = 0;
2004         work = list;
2005         while (work)
2006                 {
2007                 FileData *fd;
2008
2009                 fd = work->data;
2010                 work = work->next;
2011
2012                 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2013                         {
2014                         count = 0;
2015                         tc = fd->date;
2016                         }
2017                 else
2018                         {
2019                         count++;
2020                         if (day_max < count) day_max = count;
2021                         }
2022                 }
2023
2024         printf("biggest day contains %d images\n", day_max);
2025
2026         grid = (gint)(sqrt((double)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
2027         day_width = MAX(PAN_CAL_DAY_WIDTH, grid);
2028         day_height = MAX(PAN_CAL_DAY_HEIGHT, grid);
2029
2030         if (list)
2031                 {
2032                 FileData *fd = list->data;
2033
2034                 year = date_value(fd->date, DATE_LENGTH_YEAR);
2035                 month = date_value(fd->date, DATE_LENGTH_MONTH);
2036                 }
2037
2038         work = g_list_last(list);
2039         if (work)
2040                 {
2041                 FileData *fd = work->data;
2042                 end_year = date_value(fd->date, DATE_LENGTH_YEAR);
2043                 end_month = date_value(fd->date, DATE_LENGTH_MONTH);
2044                 }
2045
2046         *width = PAN_FOLDER_BOX_BORDER * 2;
2047         *height = PAN_FOLDER_BOX_BORDER * 2;
2048
2049         x = PAN_FOLDER_BOX_BORDER;
2050         y = PAN_FOLDER_BOX_BORDER;
2051
2052         work = list;
2053         while (work && (year < end_year || (year == end_year && month <= end_month)))
2054                 {
2055                 PanItem *pi_month;
2056                 PanItem *pi_text;
2057                 gint day;
2058                 gint days;
2059                 gint col;
2060                 gint row;
2061                 time_t dt;
2062                 gchar *buf;
2063
2064                 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
2065                 dt -= 60 * 60 * 24;
2066                 days = date_value(dt, DATE_LENGTH_DAY);
2067                 dt = date_to_time(year, month, 1);
2068                 col = date_value(dt, DATE_LENGTH_WEEK);
2069                 row = 1;
2070
2071                 x = PAN_FOLDER_BOX_BORDER;
2072
2073                 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
2074                                             PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2075                                             PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2076                                             PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2077                 buf = date_value_string(dt, DATE_LENGTH_MONTH);
2078                 pi_text = pan_item_new_text(pw, x, y, buf,
2079                                              TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2080                                              PAN_TEXT_COLOR, 255);
2081                 g_free(buf);
2082                 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
2083
2084                 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
2085
2086                 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
2087                 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
2088
2089                 for (day = 1; day <= days; day++)
2090                         {
2091                         FileData *fd;
2092                         PanItem *pi_day;
2093                         gint dx, dy;
2094                         gint n = 0;
2095
2096                         dt = date_to_time(year, month, day);
2097
2098                         fd = g_new0(FileData, 1);
2099                         /* path and name must be non NULL, so make them an invalid filename */
2100                         fd->path = g_strdup("//");
2101                         fd->name = path;
2102                         fd->date = dt;
2103                         pi_day = pan_item_new_box(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
2104                                                   PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2105                                                   PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2106                                                   PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2107                         pan_item_set_key(pi_day, "day");
2108
2109                         dx = x + PAN_CAL_DOT_GAP * 2;
2110                         dy = y + PAN_CAL_DOT_GAP * 2;
2111
2112                         fd = (work) ? work->data : NULL;
2113                         while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
2114                                 {
2115                                 PanItem *pi;
2116
2117                                 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
2118                                                       0,
2119                                                       PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
2120                                                       0, 0, 0, 0);
2121                                 pan_item_set_key(pi, "dot");
2122
2123                                 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2124                                 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
2125                                         {
2126                                         dx = x + PAN_CAL_DOT_GAP * 2;
2127                                         dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2128                                         }
2129                                 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
2130                                         {
2131                                         /* must keep all dots within respective day even if it gets ugly */
2132                                         dy = y + PAN_CAL_DOT_GAP * 2;
2133                                         }
2134
2135                                 pi_day->color_a = MIN(PAN_FOLDER_BOX_ALPHA + 64 + n, 255);
2136                                 n++;
2137
2138                                 work = work->next;
2139                                 fd = (work) ? work->data : NULL;
2140                                 }
2141
2142                         if (n > 0)
2143                                 {
2144                                 PanItem *pi;
2145
2146                                 buf = g_strdup_printf("( %d )", n);
2147                                 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2148                                                        PAN_TEXT_COLOR, 255);
2149                                 g_free(buf);
2150
2151                                 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
2152                                 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
2153                                 }
2154
2155                         buf = g_strdup_printf("%d", day);
2156                         pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2157                                           PAN_TEXT_COLOR, 255);
2158                         g_free(buf);
2159
2160
2161                         pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
2162
2163                         col++;
2164                         if (col > 6)
2165                                 {
2166                                 col = 0;
2167                                 row++;
2168                                 x = PAN_FOLDER_BOX_BORDER;
2169                                 y += PAN_CAL_DAY_HEIGHT;
2170                                 }
2171                         else
2172                                 {
2173                                 x += PAN_CAL_DAY_WIDTH;
2174                                 }
2175                         }
2176
2177                 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
2178                 y += PAN_FOLDER_BOX_BORDER * 2;
2179
2180                 month ++;
2181                 if (month > 12)
2182                         {
2183                         year++;
2184                         month = 1;
2185                         }
2186                 }
2187
2188         *width += grid;
2189         *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
2190
2191         g_list_free(list);
2192 }
2193
2194 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
2195 {
2196         GList *list;
2197         GList *work;
2198         gint x, y;
2199         time_t tc;
2200         gint total;
2201         gint count;
2202         PanItem *pi_month = NULL;
2203         PanItem *pi_day = NULL;
2204         gint month_start;
2205         gint day_start;
2206         gint x_width;
2207         gint y_height;
2208
2209         pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2210
2211         list = pan_window_layout_list(path, SORT_NONE, TRUE);
2212         list = filelist_sort(list, SORT_TIME, TRUE);
2213
2214         *width = PAN_FOLDER_BOX_BORDER * 2;
2215         *height = PAN_FOLDER_BOX_BORDER * 2;
2216
2217         x = 0;
2218         y = 0;
2219         month_start = y;
2220         day_start = month_start;
2221         x_width = 0;
2222         y_height = 0;
2223         tc = 0;
2224         total = 0;
2225         count = 0;
2226         work = list;
2227         while (work)
2228                 {
2229                 FileData *fd;
2230                 PanItem *pi;
2231
2232                 fd = work->data;
2233                 work = work->next;
2234
2235                 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2236                         {
2237                         GList *needle;
2238                         gchar *buf;
2239
2240                         if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
2241                                 {
2242                                 pi_day = NULL;
2243
2244                                 if (pi_month)
2245                                         {
2246                                         x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
2247                                         }
2248                                 else
2249                                         {
2250                                         x = PAN_FOLDER_BOX_BORDER;
2251                                         }
2252
2253                                 y = PAN_FOLDER_BOX_BORDER;
2254
2255                                 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
2256                                 pi = pan_item_new_text(pw, x, y, buf,
2257                                                        TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2258                                                        PAN_TEXT_COLOR, 255);
2259                                 y += pi->height;
2260
2261                                 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
2262                                                             x, y, 0, 0,
2263                                                             PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2264                                                             PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2265                                                             PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2266
2267                                 x += PAN_FOLDER_BOX_BORDER;
2268                                 y += PAN_FOLDER_BOX_BORDER;
2269                                 month_start = y;
2270                                 }
2271
2272                         if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
2273
2274                         tc = fd->date;
2275                         total = 1;
2276                         count = 0;
2277
2278                         needle = work;
2279                         while (needle)
2280                                 {
2281                                 FileData *nfd;
2282
2283                                 nfd = needle->data;
2284                                 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
2285                                         {
2286                                         needle = needle->next;
2287                                         total++;
2288                                         }
2289                                 else
2290                                         {
2291                                         needle = NULL;
2292                                         }
2293                                 }
2294
2295                         buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
2296                         pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2297                                                PAN_TEXT_COLOR, 255);
2298                         g_free(buf);
2299
2300                         y += pi->height;
2301
2302                         pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
2303                                                   PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2304                                                   PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2305                                                   PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2306
2307                         x += PAN_FOLDER_BOX_BORDER;
2308                         y += PAN_FOLDER_BOX_BORDER;
2309                         day_start = y;
2310                         }
2311
2312                 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2313                         {
2314                         pi = pan_item_new_image(pw, fd, x, y, 10, 10);
2315                         if (pi->width > x_width) x_width = pi->width;
2316                         y_height = pi->height;
2317                         }
2318                 else
2319                         {
2320                         pi = pan_item_new_thumb(pw, fd, x, y);
2321                         x_width = PAN_THUMB_SIZE;
2322                         y_height = PAN_THUMB_SIZE;
2323                         }
2324
2325                 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
2326                 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
2327
2328                 total--;
2329                 count++;
2330
2331                 if (total > 0 && count < PAN_GROUP_MAX)
2332                         {
2333                         y += y_height + PAN_THUMB_GAP;
2334                         }
2335                 else
2336                         {
2337                         x += x_width + PAN_THUMB_GAP;
2338                         x_width = 0;
2339                         count = 0;
2340
2341                         if (total > 0)
2342                                 y = day_start;
2343                         else
2344                                 y = month_start;
2345                         }
2346
2347                 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
2348                 }
2349
2350         g_list_free(list);
2351 }
2352
2353 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
2354                                       gint *width, gint *height,
2355                                       gint *scroll_x, gint *scroll_y)
2356 {
2357         pan_window_items_free(pw);
2358
2359         switch (pw->size)
2360                 {
2361                 case LAYOUT_SIZE_THUMB_DOTS:
2362                         pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2363                         pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2364                         break;
2365                 case LAYOUT_SIZE_THUMB_NONE:
2366                         pw->thumb_size = PAN_THUMB_SIZE_NONE;
2367                         pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2368                         break;
2369                 case LAYOUT_SIZE_THUMB_SMALL:
2370                         pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2371                         pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2372                         break;
2373                 case LAYOUT_SIZE_THUMB_NORMAL:
2374                 default:
2375                         pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2376                         pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2377                         break;
2378                 case LAYOUT_SIZE_THUMB_LARGE:
2379                         pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2380                         pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2381                         break;
2382                 case LAYOUT_SIZE_10:
2383                         pw->image_size = 10;
2384                         pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2385                         break;
2386                 case LAYOUT_SIZE_25:
2387                         pw->image_size = 25;
2388                         pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2389                         break;
2390                 case LAYOUT_SIZE_33:
2391                         pw->image_size = 33;
2392                         pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2393                         break;
2394                 case LAYOUT_SIZE_50:
2395                         pw->image_size = 50;
2396                         pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2397                         break;
2398                 case LAYOUT_SIZE_100:
2399                         pw->image_size = 100;
2400                         pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2401                         break;
2402                 }
2403
2404         *width = 0;
2405         *height = 0;
2406         *scroll_x = 0;
2407         *scroll_y = 0;
2408
2409         switch (pw->layout)
2410                 {
2411                 case LAYOUT_GRID:
2412                 default:
2413                         pan_window_layout_compute_grid(pw, path, width, height);
2414                         break;
2415                 case LAYOUT_FOLDERS_LINEAR:
2416                         pan_window_layout_compute_folders_linear(pw, path, width, height);
2417                         break;
2418                 case LAYOUT_FOLDERS_FLOWER:
2419                         pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2420                         break;
2421                 case LAYOUT_CALENDAR:
2422                         pan_window_layout_compute_calendar(pw, path, width, height);
2423                         break;
2424                 case LAYOUT_TIMELINE:
2425                         pan_window_layout_compute_timeline(pw, path, width, height);
2426                         break;
2427                 }
2428
2429         pan_cache_free(pw);
2430
2431         printf("computed %d objects\n", g_list_length(pw->list));
2432 }
2433
2434 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2435 {
2436         GList *list = NULL;
2437         GList *work;
2438
2439         work = pw->list;
2440         while (work)
2441                 {
2442                 PanItem *pi;
2443
2444                 pi = work->data;
2445                 work = work->next;
2446
2447                 if (util_clip_region_test(x, y, width, height,
2448                                           pi->x, pi->y, pi->width, pi->height))
2449                         {
2450                         list = g_list_prepend(list, pi);
2451                         }
2452                 }
2453
2454         return list;
2455 }
2456
2457
2458
2459 /*
2460  *-----------------------------------------------------------------------------
2461  * tile generation
2462  *-----------------------------------------------------------------------------
2463  */
2464
2465 static gint pan_layout_queue_step(PanWindow *pw);
2466
2467
2468 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2469 {
2470         PanWindow *pw = data;
2471
2472         if (pw->queue_pi)
2473                 {
2474                 PanItem *pi;
2475                 gint rc;
2476
2477                 pi = pw->queue_pi;
2478                 pw->queue_pi = NULL;
2479
2480                 pi->queued = FALSE;
2481
2482                 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2483                 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2484
2485                 rc = pi->refcount;
2486                 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2487                 pi->refcount = rc;
2488                 }
2489
2490         thumb_loader_free(pw->tl);
2491         pw->tl = NULL;
2492
2493         while (pan_layout_queue_step(pw));
2494 }
2495
2496 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2497 {
2498         PanWindow *pw = data;
2499
2500         if (pw->queue_pi)
2501                 {
2502                 PanItem *pi;
2503                 gint rc;
2504
2505                 pi = pw->queue_pi;
2506                 pw->queue_pi = NULL;
2507
2508                 pi->queued = FALSE;
2509
2510                 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2511                 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2512                 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2513
2514                 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2515                     (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2516                      gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2517                         {
2518                         GdkPixbuf *tmp;
2519
2520                         tmp = pi->pixbuf;
2521                         pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2522                                                              (GdkInterpType)zoom_quality);
2523                         g_object_unref(tmp);
2524                         }
2525
2526                 rc = pi->refcount;
2527                 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2528                 pi->refcount = rc;
2529                 }
2530
2531         image_loader_free(pw->il);
2532         pw->il = NULL;
2533
2534         while (pan_layout_queue_step(pw));
2535 }
2536
2537 #if 0
2538 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2539                                            guint width, guint height, gpointer data)
2540 {
2541         PanWindow *pw = data;
2542
2543         if (pw->queue_pi)
2544                 {
2545                 PanItem *pi;
2546                 gint rc;
2547
2548                 pi = pw->queue_pi;
2549
2550                 if (!pi->pixbuf)
2551                         {
2552                         pi->pixbuf = image_loader_get_pixbuf(pw->il);
2553                         if (pi->pixbuf) g_object_ref(pi->pixbuf);
2554                         }
2555
2556                 rc = pi->refcount;
2557                 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2558                 pi->refcount = rc;
2559                 }
2560 }
2561 #endif
2562
2563 static gint pan_layout_queue_step(PanWindow *pw)
2564 {
2565         PanItem *pi;
2566
2567         if (!pw->queue) return FALSE;
2568
2569         pi = pw->queue->data;
2570         pw->queue = g_list_remove(pw->queue, pi);
2571         pw->queue_pi = pi;
2572
2573         if (!pw->queue_pi->fd)
2574                 {
2575                 pw->queue_pi->queued = FALSE;
2576                 pw->queue_pi = NULL;
2577                 return TRUE;
2578                 }
2579
2580         image_loader_free(pw->il);
2581         pw->il = NULL;
2582         thumb_loader_free(pw->tl);
2583         pw->tl = NULL;
2584
2585         if (pi->type == ITEM_IMAGE)
2586                 {
2587                 pw->il = image_loader_new(pi->fd->path);
2588
2589                 if (pw->size != LAYOUT_SIZE_100)
2590                         {
2591                         image_loader_set_requested_size(pw->il, pi->width, pi->height);
2592                         }
2593
2594 #if 0
2595                 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2596 #endif
2597                 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2598
2599                 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2600
2601                 image_loader_free(pw->il);
2602                 pw->il = NULL;
2603                 }
2604         else if (pi->type == ITEM_THUMB)
2605                 {
2606                 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2607
2608                 if (!pw->tl->standard_loader)
2609                         {
2610                         /* The classic loader will recreate a thumbnail any time we
2611                          * request a different size than what exists. This view will
2612                          * almost never use the user configured sizes so disable cache.
2613                          */
2614                         thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2615                         }
2616
2617                 thumb_loader_set_callbacks(pw->tl,
2618                                            pan_layout_queue_thumb_done_cb,
2619                                            pan_layout_queue_thumb_done_cb,
2620                                            NULL, pw);
2621
2622                 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2623
2624                 thumb_loader_free(pw->tl);
2625                 pw->tl = NULL;
2626                 }
2627
2628         pw->queue_pi->queued = FALSE;
2629         pw->queue_pi = NULL;
2630         return TRUE;
2631 }
2632
2633 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2634 {
2635         if (!pi || pi->queued || pi->pixbuf) return;
2636         if (pw->size <= LAYOUT_SIZE_THUMB_NONE) return;
2637
2638         pi->queued = TRUE;
2639         pw->queue = g_list_prepend(pw->queue, pi);
2640
2641         if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2642 }
2643
2644 static gint pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
2645                                        gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2646 {
2647         PanWindow *pw = data;
2648         GList *list;
2649         GList *work;
2650         gint i;
2651
2652         pixbuf_draw_rect_fill(pixbuf,
2653                          0, 0, width, height,
2654                          PAN_BACKGROUND_COLOR, 255);
2655
2656         for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2657                 {
2658                 gint rx, ry, rw, rh;
2659
2660                 if (util_clip_region(x, y, width, height,
2661                                      i, y, 1, height,
2662                                      &rx, &ry, &rw, &rh))
2663                         {
2664                         pixbuf_draw_rect_fill(pixbuf,
2665                                               rx - x, ry - y, rw, rh,
2666                                               PAN_GRID_COLOR, PAN_GRID_ALPHA);
2667                         }
2668                 }
2669         for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2670                 {
2671                 gint rx, ry, rw, rh;
2672
2673                 if (util_clip_region(x, y, width, height,
2674                                      x, i, width, 1,
2675                                      &rx, &ry, &rw, &rh))
2676                         {
2677                         pixbuf_draw_rect_fill(pixbuf,
2678                                               rx - x, ry - y, rw, rh,
2679                                               PAN_GRID_COLOR, PAN_GRID_ALPHA);
2680                         }
2681                 }
2682
2683         list = pan_layout_intersect(pw, x, y, width, height);
2684         work = list;
2685         while (work)
2686                 {
2687                 PanItem *pi;
2688                 gint tx, ty, tw, th;
2689                 gint rx, ry, rw, rh;
2690
2691                 pi = work->data;
2692                 work = work->next;
2693
2694                 pi->refcount++;
2695
2696                 if (pi->type == ITEM_THUMB && pi->pixbuf)
2697                         {
2698                         tw = gdk_pixbuf_get_width(pi->pixbuf);
2699                         th = gdk_pixbuf_get_height(pi->pixbuf);
2700
2701                         tx = pi->x + (pi->width - tw) / 2;
2702                         ty = pi->y + (pi->height - th) / 2;
2703
2704                         if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2705                                 {
2706                                 if (util_clip_region(x, y, width, height,
2707                                                      tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2708                                                      &rx, &ry, &rw, &rh))
2709                                         {
2710                                         pixbuf_draw_shadow(pixbuf,
2711                                                            rx - x, ry - y, rw, rh,
2712                                                            tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2713                                                            PAN_SHADOW_FADE,
2714                                                            PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2715                                         }
2716                                 }
2717                         else
2718                                 {
2719                                 if (util_clip_region(x, y, width, height,
2720                                                      tx + tw, ty + PAN_SHADOW_OFFSET,
2721                                                      PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2722                                                      &rx, &ry, &rw, &rh))
2723                                         {
2724                                         pixbuf_draw_shadow(pixbuf,
2725                                                            rx - x, ry - y, rw, rh,
2726                                                            tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2727                                                            PAN_SHADOW_FADE,
2728                                                            PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2729                                         }
2730                                 if (util_clip_region(x, y, width, height,
2731                                                      tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2732                                                      &rx, &ry, &rw, &rh))
2733                                         {
2734                                         pixbuf_draw_shadow(pixbuf,
2735                                                            rx - x, ry - y, rw, rh,
2736                                                            tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2737                                                            PAN_SHADOW_FADE,
2738                                                            PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2739                                         }
2740                                 }
2741
2742                         if (util_clip_region(x, y, width, height,
2743                                              tx, ty, tw, th,
2744                                              &rx, &ry, &rw, &rh))
2745                                 {
2746                                 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2747                                                      (double) tx - x,
2748                                                      (double) ty - y,
2749                                                      1.0, 1.0, GDK_INTERP_NEAREST,
2750                                                      255);
2751                                 }
2752
2753                         if (util_clip_region(x, y, width, height,
2754                                              tx, ty, tw, PAN_OUTLINE_THICKNESS,
2755                                              &rx, &ry, &rw, &rh))
2756                                 {
2757                                 pixbuf_draw_rect_fill(pixbuf,
2758                                                       rx - x, ry - y, rw, rh,
2759                                                       PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2760                                 }
2761                         if (util_clip_region(x, y, width, height,
2762                                              tx, ty, PAN_OUTLINE_THICKNESS, th,
2763                                              &rx, &ry, &rw, &rh))
2764                                 {
2765                                 pixbuf_draw_rect_fill(pixbuf,
2766                                                       rx - x, ry - y, rw, rh,
2767                                                       PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2768                                 }
2769                         if (util_clip_region(x, y, width, height,
2770                                              tx + tw - PAN_OUTLINE_THICKNESS, ty +  PAN_OUTLINE_THICKNESS,
2771                                              PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2772                                              &rx, &ry, &rw, &rh))
2773                                 {
2774                                 pixbuf_draw_rect_fill(pixbuf,
2775                                                       rx - x, ry - y, rw, rh,
2776                                                       PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2777                                 }
2778                         if (util_clip_region(x, y, width, height,
2779                                              tx +  PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2780                                              tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2781                                              &rx, &ry, &rw, &rh))
2782                                 {
2783                                 pixbuf_draw_rect_fill(pixbuf,
2784                                                       rx - x, ry - y, rw, rh,
2785                                                       PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2786                                 }
2787                         }
2788                 else if (pi->type == ITEM_THUMB)
2789                         {
2790                         tw = pi->width - PAN_SHADOW_OFFSET * 2;
2791                         th = pi->height - PAN_SHADOW_OFFSET * 2;
2792                         tx = pi->x + PAN_SHADOW_OFFSET;
2793                         ty = pi->y + PAN_SHADOW_OFFSET;
2794
2795                         if (util_clip_region(x, y, width, height,
2796                                              tx, ty, tw, th,
2797                                              &rx, &ry, &rw, &rh))
2798                                 {
2799                                 gint d;
2800
2801                                 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2802                                 pixbuf_draw_rect_fill(pixbuf,
2803                                                       rx - x, ry - y, rw, rh,
2804                                                       PAN_SHADOW_COLOR,
2805                                                       PAN_SHADOW_ALPHA / d);
2806                                 }
2807
2808                         pan_layout_queue(pw, pi);
2809                         }
2810                 else if (pi->type == ITEM_IMAGE)
2811                         {
2812                         if (util_clip_region(x, y, width, height,
2813                                              pi->x, pi->y, pi->width, pi->height,
2814                                              &rx, &ry, &rw, &rh))
2815                                 {
2816                                 if (pi->pixbuf)
2817                                         {
2818                                         gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2819                                                              (double) pi->x - x,
2820                                                              (double) pi->y - y,
2821                                                              1.0, 1.0, GDK_INTERP_NEAREST,
2822                                                              255);
2823                                         }
2824                                 else
2825                                         {
2826                                         pixbuf_draw_rect_fill(pixbuf,
2827                                                               rx - x, ry - y, rw, rh,
2828                                                               PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2829                                         pan_layout_queue(pw, pi);
2830                                         }
2831                                 }
2832                         }
2833                 else if (pi->type == ITEM_BOX)
2834                         {
2835                         gint bw, bh;
2836                         gint *shadow;
2837
2838                         bw = pi->width;
2839                         bh = pi->height;
2840
2841                         shadow = pi->data;
2842                         if (shadow)
2843                                 {
2844                                 bw -= shadow[0];
2845                                 bh -= shadow[0];
2846
2847                                 if (pi->color_a > 254)
2848                                         {
2849                                         pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2850                                                            shadow[0], bh - shadow[0],
2851                                                            pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2852                                                            shadow[1],
2853                                                            PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2854                                         pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2855                                                            bw, shadow[0],
2856                                                            pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2857                                                            shadow[1],
2858                                                            PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2859                                         }
2860                                 else
2861                                         {
2862                                         gint a;
2863                                         a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2864                                         pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2865                                                            bw, bh,
2866                                                            pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2867                                                            shadow[1],
2868                                                            PAN_SHADOW_COLOR, a);
2869                                         }
2870                                 }
2871
2872                         if (util_clip_region(x, y, width, height,
2873                                              pi->x, pi->y, bw, bh,
2874                                              &rx, &ry, &rw, &rh))
2875                                 {
2876                                 pixbuf_draw_rect_fill(pixbuf,
2877                                                       rx - x, ry - y, rw, rh,
2878                                                       pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2879                                 }
2880                         if (util_clip_region(x, y, width, height,
2881                                              pi->x, pi->y, bw, pi->border,
2882                                              &rx, &ry, &rw, &rh))
2883                                 {
2884                                 pixbuf_draw_rect_fill(pixbuf,
2885                                                       rx - x, ry - y, rw, rh,
2886                                                       pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2887                                 }
2888                         if (util_clip_region(x, y, width, height,
2889                                              pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
2890                                              &rx, &ry, &rw, &rh))
2891                                 {
2892                                 pixbuf_draw_rect_fill(pixbuf,
2893                                                       rx - x, ry - y, rw, rh,
2894                                                       pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2895                                 }
2896                         if (util_clip_region(x, y, width, height,
2897                                              pi->x + bw - pi->border, pi->y + pi->border,
2898                                              pi->border, bh - pi->border * 2,
2899                                              &rx, &ry, &rw, &rh))
2900                                 {
2901                                 pixbuf_draw_rect_fill(pixbuf,
2902                                                       rx - x, ry - y, rw, rh,
2903                                                       pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2904                                 }
2905                         if (util_clip_region(x, y, width, height,
2906                                              pi->x, pi->y + bh - pi->border,
2907                                              bw,  pi->border,
2908                                              &rx, &ry, &rw, &rh))
2909                                 {
2910                                 pixbuf_draw_rect_fill(pixbuf,
2911                                                       rx - x, ry - y, rw, rh,
2912                                                       pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2913                                 }
2914                         }
2915                 else if (pi->type == ITEM_TRIANGLE)
2916                         {
2917                         if (util_clip_region(x, y, width, height,
2918                                              pi->x, pi->y, pi->width, pi->height,
2919                                              &rx, &ry, &rw, &rh) && pi->data)
2920                                 {
2921                                 gint *coord = pi->data;
2922                                 pixbuf_draw_triangle(pixbuf,
2923                                                      rx - x, ry - y, rw, rh,
2924                                                      coord[0] - x, coord[1] - y,
2925                                                      coord[2] - x, coord[3] - y,
2926                                                      coord[4] - x, coord[5] - y,
2927                                                      pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2928
2929                                 if (pi->border & BORDER_1)
2930                                         {
2931                                         pixbuf_draw_line(pixbuf,
2932                                                          rx - x, ry - y, rw, rh,
2933                                                          coord[0] - x, coord[1] - y,
2934                                                          coord[2] - x, coord[3] - y,
2935                                                          pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2936                                         }
2937                                 if (pi->border & BORDER_2)
2938                                         {
2939                                         pixbuf_draw_line(pixbuf,
2940                                                          rx - x, ry - y, rw, rh,
2941                                                          coord[2] - x, coord[3] - y,
2942                                                          coord[4] - x, coord[5] - y,
2943                                                          pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2944                                         }
2945                                 if (pi->border & BORDER_3)
2946                                         {
2947                                         pixbuf_draw_line(pixbuf,
2948                                                          rx - x, ry - y, rw, rh,
2949                                                          coord[4] - x, coord[5] - y,
2950                                                          coord[0] - x, coord[1] - y,
2951                                                          pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2952                                         }
2953                                 }
2954                         }
2955                 else if (pi->type == ITEM_TEXT && pi->text)
2956                         {
2957                         PangoLayout *layout;
2958
2959                         layout = pan_item_text_layout(pi, (GtkWidget *)pr);
2960                         pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
2961                                            pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
2962                                            pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2963                         g_object_unref(G_OBJECT(layout));
2964                         }
2965                 }
2966         g_list_free(list);
2967
2968         if (0)
2969                 {
2970                 static gint count = 0;
2971                 PangoLayout *layout;
2972                 gchar *buf;
2973
2974                 layout = gtk_widget_create_pango_layout((GtkWidget *)pr, NULL);
2975
2976                 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
2977                                       (x / pr->source_tile_width) +
2978                                       (y / pr->source_tile_height * (pr->image_width/pr->source_tile_width + 1)));
2979                 pango_layout_set_text(layout, buf, -1);
2980                 g_free(buf);
2981
2982                 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr, 0, 0, 0, 0, 0, 255);
2983
2984                 g_object_unref(G_OBJECT(layout));
2985
2986                 count++;
2987                 }
2988
2989         return TRUE;
2990 }
2991
2992 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
2993                                        gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2994 {
2995         PanWindow *pw = data;
2996         GList *list;
2997         GList *work;
2998
2999         list = pan_layout_intersect(pw, x, y, width, height);
3000         work = list;
3001         while (work)
3002                 {
3003                 PanItem *pi;
3004
3005                 pi = work->data;
3006                 work = work->next;
3007
3008                 if (pi->refcount > 0)
3009                         {
3010                         pi->refcount--;
3011
3012                         if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
3013                             pi->refcount == 0)
3014                                 {
3015                                 if (pi->queued)
3016                                         {
3017                                         pw->queue = g_list_remove(pw->queue, pi);
3018                                         pi->queued = FALSE;
3019                                         }
3020                                 if (pw->queue_pi == pi) pw->queue_pi = NULL;
3021                                 if (pi->pixbuf)
3022                                         {
3023                                         g_object_unref(pi->pixbuf);
3024                                         pi->pixbuf = NULL;
3025                                         }
3026                                 }
3027                         }
3028                 }
3029
3030         g_list_free(list);
3031 }
3032
3033
3034 /*
3035  *-----------------------------------------------------------------------------
3036  * misc
3037  *-----------------------------------------------------------------------------
3038  */ 
3039
3040 static void pan_window_message(PanWindow *pw, const gchar *text)
3041 {
3042         GList *work;
3043         gint count = 0;
3044         gint64 size = 0;
3045         gchar *ss;
3046         gchar *buf;
3047
3048         if (text)
3049                 {
3050                 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
3051                 return;
3052                 }
3053
3054         work = pw->list;
3055         if (pw->layout == LAYOUT_CALENDAR)
3056                 {
3057                 while (work)
3058                         {
3059                         PanItem *pi;
3060
3061                         pi = work->data;
3062                         work = work->next;
3063
3064                         if (pi->fd &&
3065                             pi->type == ITEM_BOX &&
3066                             pi->key && strcmp(pi->key, "dot") == 0)
3067                                 {
3068                                 size += pi->fd->size;
3069                                 count++;
3070                                 }
3071                         }
3072                 }
3073         else
3074                 {
3075                 while (work)
3076                         {
3077                         PanItem *pi;
3078
3079                         pi = work->data;
3080                         work = work->next;
3081
3082                         if (pi->fd &&
3083                             (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
3084                                 {
3085                                 size += pi->fd->size;
3086                                 count++;
3087                                 }
3088                         }
3089                 }
3090
3091         ss = text_from_size_abrev(size);
3092         buf = g_strdup_printf(_("%d images, %s"), count, ss);
3093         g_free(ss);
3094         gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
3095         g_free(buf);
3096 }
3097
3098 static void pan_window_zoom_limit(PanWindow *pw)
3099 {
3100         gdouble min;
3101
3102         switch (pw->size)
3103                 {
3104                 case LAYOUT_SIZE_THUMB_DOTS:
3105                 case LAYOUT_SIZE_THUMB_NONE:
3106                 case LAYOUT_SIZE_THUMB_SMALL:
3107                 case LAYOUT_SIZE_THUMB_NORMAL:
3108 #if 0
3109                         /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
3110                         min = -16.0;
3111                         break;
3112 #endif
3113                 case LAYOUT_SIZE_THUMB_LARGE:
3114                         min = -6.0;
3115                         break;
3116                 case LAYOUT_SIZE_10:
3117                 case LAYOUT_SIZE_25:
3118                         min = -4.0;
3119                         break;
3120                 case LAYOUT_SIZE_33:
3121                 case LAYOUT_SIZE_50:
3122                 case LAYOUT_SIZE_100:
3123                 default:
3124                         min = -2.0;
3125                         break;
3126                 }
3127
3128         image_zoom_set_limits(pw->imd, min, 32.0);
3129 }
3130
3131 static gint pan_window_layout_update_idle_cb(gpointer data)
3132 {
3133         PanWindow *pw = data;
3134         gint width;
3135         gint height;
3136         gint scroll_x;
3137         gint scroll_y;
3138
3139         if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
3140                 {
3141                 if (!pw->cache_list && !pw->cache_todo)
3142                         {
3143                         pan_cache_fill(pw, pw->path);
3144                         if (pw->cache_todo)
3145                                 {
3146                                 pan_window_message(pw, _("Reading dimensions..."));
3147                                 return TRUE;
3148                                 }
3149                         }
3150                 if (pan_cache_step(pw))
3151                         {
3152                         pw->cache_count++;
3153                         pw->cache_tick++;
3154                         if (pw->cache_count == pw->cache_total)
3155                                 {
3156                                 pan_window_message(pw, _("Sorting images..."));
3157                                 }
3158                         else if (pw->cache_tick > 9)
3159                                 {
3160                                 gchar *buf;
3161
3162                                 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
3163                                                       pw->cache_total - pw->cache_count);
3164                                 pan_window_message(pw, buf);
3165                                 g_free(buf);
3166
3167                                 pw->cache_tick = 0;
3168                                 }
3169
3170                         return TRUE;
3171                         }
3172                 }
3173
3174         pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3175
3176         pan_window_zoom_limit(pw);
3177
3178         if (width > 0 && height > 0)
3179                 {
3180                 gdouble align;
3181
3182                 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
3183                                           PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
3184                                           pan_window_request_tile_cb,
3185                                           pan_window_dispose_tile_cb, pw, 1.0);
3186
3187                 if (scroll_x == 0 && scroll_y == 0)
3188                         {
3189                         align = 0.0;
3190                         }
3191                 else
3192                         {
3193                         align = 0.5;
3194                         }
3195                 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
3196                 }
3197
3198         pan_window_message(pw, NULL);
3199
3200         pw->idle_id = -1;
3201
3202         return FALSE;
3203 }
3204
3205 static void pan_window_layout_update_idle(PanWindow *pw)
3206 {
3207         if (pw->idle_id == -1)
3208                 {
3209                 pan_window_message(pw, _("Sorting images..."));
3210                 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3211                 }
3212 }
3213
3214 /*
3215  *-----------------------------------------------------------------------------
3216  * pan window keyboard
3217  *-----------------------------------------------------------------------------
3218  */
3219
3220 static const gchar *pan_menu_click_path(PanWindow *pw)
3221 {
3222         if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3223         return NULL;
3224 }
3225
3226 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3227 {
3228         PanWindow *pw = data;
3229
3230         gdk_window_get_origin(pw->imd->pr->window, x, y);
3231         popup_menu_position_clamp(menu, x, y, 0);
3232 }
3233
3234 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3235 {
3236         PanWindow *pw = data;
3237         PixbufRenderer *pr;
3238         const gchar *path;
3239         gint stop_signal = FALSE;
3240         GtkWidget *menu;
3241         gint x = 0;
3242         gint y = 0;
3243         gint focused;
3244
3245         pr = PIXBUF_RENDERER(pw->imd->pr);
3246         path = pan_menu_click_path(pw);
3247
3248         focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pw->imd->widget)));
3249
3250         if (focused)
3251                 {
3252                 switch (event->keyval)
3253                         {
3254                         case GDK_Left: case GDK_KP_Left:
3255                                 x -= 1;
3256                                 stop_signal = TRUE;
3257                                 break;
3258                         case GDK_Right: case GDK_KP_Right:
3259                                 x += 1;
3260                                 stop_signal = TRUE;
3261                                 break;
3262                         case GDK_Up: case GDK_KP_Up:
3263                                 y -= 1;
3264                                 stop_signal = TRUE;
3265                                 break;
3266                         case GDK_Down: case GDK_KP_Down:
3267                                 y += 1;
3268                                 stop_signal = TRUE;
3269                                 break;
3270                         case GDK_Page_Up: case GDK_KP_Page_Up:
3271                                 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
3272                                 break;
3273                         case GDK_Page_Down: case GDK_KP_Page_Down:
3274                                 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
3275                                 break;
3276                         case GDK_Home: case GDK_KP_Home:
3277                                 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
3278                                 break;
3279                         case GDK_End: case GDK_KP_End:
3280                                 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
3281                                 break;
3282                         }
3283                 }
3284
3285         if (focused && !(event->state & GDK_CONTROL_MASK) )
3286             switch (event->keyval)
3287                 {
3288                 case '+': case '=': case GDK_KP_Add:
3289                         pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
3290                         break;
3291                 case '-': case GDK_KP_Subtract:
3292                         pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
3293                         break;
3294                 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3295                         pixbuf_renderer_zoom_set(pr, 1.0);
3296                         break;
3297                 case '2':
3298                         pixbuf_renderer_zoom_set(pr, 2.0);
3299                         break;
3300                 case '3':
3301                         pixbuf_renderer_zoom_set(pr, 3.0);
3302                         break;
3303                 case '4':
3304                         pixbuf_renderer_zoom_set(pr, 4.0);
3305                         break;
3306                 case '7':
3307                         pixbuf_renderer_zoom_set(pr, -4.0);
3308                         break;
3309                 case '8':
3310                         pixbuf_renderer_zoom_set(pr, -3.0);
3311                         break;
3312                 case '9':
3313                         pixbuf_renderer_zoom_set(pr, -2.0);
3314                         break;
3315                 case 'F': case 'f':
3316                 case 'V': case 'v':
3317                         pan_fullscreen_toggle(pw, FALSE);
3318                         stop_signal = TRUE;
3319                         break;
3320                 case 'I': case 'i':
3321 #if 0
3322                         pan_overlay_toggle(pw);
3323 #endif
3324                         break;
3325                 case GDK_Delete: case GDK_KP_Delete:
3326                         break;
3327                 case '/':
3328                         if (!pw->fs)
3329                                 {
3330                                 if (GTK_WIDGET_VISIBLE(pw->search_box))
3331                                         {
3332                                         gtk_widget_grab_focus(pw->search_entry);
3333                                         }
3334                                 else
3335                                         {
3336                                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3337                                         }
3338                                 stop_signal = TRUE;
3339                                 }
3340                         break;
3341                 case GDK_Escape:
3342                         if (pw->fs)
3343                                 {
3344                                 pan_fullscreen_toggle(pw, TRUE);
3345                                 stop_signal = TRUE;
3346                                 }
3347                         else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3348                                 {
3349                                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3350                                 stop_signal = TRUE;
3351                                 }
3352                         break;
3353                 case GDK_Menu:
3354                 case GDK_F10:
3355                         menu = pan_popup_menu(pw);
3356                         gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3357                         stop_signal = TRUE;
3358                         break;
3359                 }
3360
3361         if (event->state & GDK_CONTROL_MASK)
3362                 {
3363                 gint n = -1;
3364                 switch (event->keyval)
3365                         {
3366                         case '1':
3367                                 n = 0;
3368                                 break;
3369                         case '2':
3370                                 n = 1;
3371                                 break;
3372                         case '3':
3373                                 n = 2;
3374                                 break;
3375                         case '4':
3376                                 n = 3;
3377                                 break;
3378                         case '5':
3379                                 n = 4;
3380                                 break;
3381                         case '6':
3382                                 n = 5;
3383                                 break;
3384                         case '7':
3385                                 n = 6;
3386                                 break;
3387                         case '8':
3388                                 n = 7;
3389                                 break;
3390                         case '9':
3391                                 n = 8;
3392                                 break;
3393                         case '0':
3394                                 n = 9;
3395                                 break;
3396                         case 'C': case 'c':
3397                                 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3398                                 break;
3399                         case 'M': case 'm':
3400                                 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3401                                 break;
3402                         case 'R': case 'r':
3403                                 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3404                                 break;
3405                         case 'D': case 'd':
3406                                 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3407                                 break;
3408                         case 'P': case 'p':
3409                                 if (path) info_window_new(path, NULL);
3410                                 break;
3411                         case 'W': case 'w':
3412                                 pan_window_close(pw);
3413                                 break;
3414                         }
3415                 if (n != -1 && path)
3416                         {
3417                         pan_fullscreen_toggle(pw, TRUE);
3418                         start_editor_from_file(n, path);
3419                         stop_signal = TRUE;
3420                         }
3421                 }
3422         else if (event->state & GDK_SHIFT_MASK)
3423                 {
3424                 x *= 3;
3425                 y *= 3;
3426                 }
3427         else if (!focused)
3428                 {
3429                 switch (event->keyval)
3430                         {
3431                         case GDK_Escape:
3432                                 if (pw->fs)
3433                                         {
3434                                         pan_fullscreen_toggle(pw, TRUE);
3435                                         stop_signal = TRUE;
3436                                         }
3437                                 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3438                                         {
3439                                         gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
3440                                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3441                                         stop_signal = TRUE;
3442                                         }
3443                         break;
3444                         default:
3445                                 break;
3446                         }
3447                 }
3448
3449         if (x != 0 || y!= 0)
3450                 {
3451                 keyboard_scroll_calc(&x, &y, event);
3452                 pixbuf_renderer_scroll(pr, x, y);
3453                 }
3454
3455         return stop_signal;
3456 }
3457
3458 /*
3459  *-----------------------------------------------------------------------------
3460  * info popup
3461  *-----------------------------------------------------------------------------
3462  */
3463
3464 static void pan_info_update(PanWindow *pw, PanItem *pi)
3465 {
3466         PanItem *pbox;
3467         PanItem *plabel;
3468         PanItem *p;
3469         gchar *buf;
3470         gint x1, y1, x2, y2, x3, y3;
3471         gint x, y, w, h;
3472
3473         if (pw->click_pi == pi) return;
3474         if (pi && !pi->fd) pi = NULL;
3475
3476         while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3477         pw->click_pi = pi;
3478
3479         if (!pi) return;
3480
3481         printf("info set to %s\n", pi->fd->path);
3482
3483         pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3484                              PAN_POPUP_BORDER,
3485                              PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3486                              PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3487         pan_item_set_key(pbox, "info");
3488
3489         if (pi->type == ITEM_THUMB && pi->pixbuf)
3490                 {
3491                 w = gdk_pixbuf_get_width(pi->pixbuf);
3492                 h = gdk_pixbuf_get_height(pi->pixbuf);
3493
3494                 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3495                 y1 = pi->y + (pi->height - h) / 2 + 8;
3496                 }
3497         else
3498                 {
3499                 x1 = pi->x + pi->width - 8;
3500                 y1 = pi->y + 8;
3501                 }
3502
3503         x2 = pbox->x + 1;
3504         y2 = pbox->y + 36;
3505         x3 = pbox->x + 1;
3506         y3 = pbox->y + 12;
3507         triangle_rect_region(x1, y1, x2, y2, x3, y3,
3508                              &x, &y, &w, &h);
3509
3510         p = pan_item_new_tri(pw, NULL, x, y, w, h,
3511                              x1, y1, x2, y2, x3, y3,
3512                              PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3513         pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3514         pan_item_set_key(p, "info");
3515         pan_item_added(pw, p);
3516
3517         plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3518                                    _("Filename:"), TEXT_ATTR_BOLD,
3519                                    PAN_POPUP_TEXT_COLOR, 255);
3520         pan_item_set_key(plabel, "info");
3521         p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3522                               pi->fd->name, TEXT_ATTR_NONE,
3523                               PAN_POPUP_TEXT_COLOR, 255);
3524         pan_item_set_key(p, "info");
3525         pan_item_size_by_item(pbox, p, 0);
3526
3527         plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3528                                    _("Date:"), TEXT_ATTR_BOLD,
3529                                    PAN_POPUP_TEXT_COLOR, 255);
3530         pan_item_set_key(plabel, "info");
3531         p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3532                               text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3533                               PAN_POPUP_TEXT_COLOR, 255);
3534         pan_item_set_key(p, "info");
3535         pan_item_size_by_item(pbox, p, 0);
3536
3537         plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3538                                    _("Size:"), TEXT_ATTR_BOLD,
3539                                    PAN_POPUP_TEXT_COLOR, 255);
3540         pan_item_set_key(plabel, "info");
3541         buf = text_from_size(pi->fd->size);
3542         p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3543                               buf, TEXT_ATTR_NONE,
3544                               PAN_POPUP_TEXT_COLOR, 255);
3545         g_free(buf);
3546         pan_item_set_key(p, "info");
3547         pan_item_size_by_item(pbox, p, 0);
3548
3549         pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3550         pan_item_added(pw, pbox);
3551 }
3552
3553
3554 /*
3555  *-----------------------------------------------------------------------------
3556  * search
3557  *-----------------------------------------------------------------------------
3558  */
3559
3560 static void pan_search_status(PanWindow *pw, const gchar *text)
3561 {
3562         gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3563 }
3564
3565 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3566 {
3567         PanItem *pi;
3568         GList *list;
3569         GList *found;
3570         ItemType type;
3571         gchar *buf;
3572
3573         type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3574
3575         list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3576         if (!list) return FALSE;
3577
3578         found = g_list_find(list, pw->click_pi);
3579         if (found && found->next)
3580                 {
3581                 found = found->next;
3582                 pi = found->data;
3583                 }
3584         else
3585                 {
3586                 pi = list->data;
3587                 }
3588
3589         pan_info_update(pw, pi);
3590         image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3591
3592         buf = g_strdup_printf("%s ( %d / %d )",
3593                               (path[0] == '/') ? _("path found") : _("filename found"),
3594                               g_list_index(list, pi) + 1,
3595                               g_list_length(list));
3596         pan_search_status(pw, buf);
3597         g_free(buf);
3598
3599         g_list_free(list);
3600
3601         return TRUE;
3602 }
3603
3604 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3605 {
3606         PanItem *pi;
3607         GList *list;
3608         GList *found;
3609         ItemType type;
3610         gchar *buf;
3611
3612         type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3613
3614         list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3615         if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3616         if (!list)
3617                 {
3618                 gchar *needle;
3619
3620                 needle = g_utf8_strdown(text, -1);
3621                 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3622                 g_free(needle);
3623                 }
3624         if (!list) return FALSE;
3625
3626         found = g_list_find(list, pw->click_pi);
3627         if (found && found->next)
3628                 {
3629                 found = found->next;
3630                 pi = found->data;
3631                 }
3632         else
3633                 {
3634                 pi = list->data;
3635                 }
3636
3637         pan_info_update(pw, pi);
3638         image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3639
3640         buf = g_strdup_printf("%s ( %d / %d )",
3641                               _("partial match"),
3642                               g_list_index(list, pi) + 1,
3643                               g_list_length(list));
3644         pan_search_status(pw, buf);
3645         g_free(buf);
3646
3647         g_list_free(list);
3648
3649         return TRUE;
3650 }
3651
3652 static gint valid_date_separator(gchar c)
3653 {
3654         return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3655 }
3656
3657 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
3658                                      gint year, gint month, gint day,
3659                                      const gchar *key)
3660 {
3661         GList *list = NULL;
3662         GList *work;
3663
3664         work = g_list_last(pw->list);
3665         while (work)
3666                 {
3667                 PanItem *pi;
3668
3669                 pi = work->data;
3670                 work = work->prev;
3671
3672                 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
3673                     ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
3674                         {
3675                         struct tm *tl;
3676
3677                         tl = localtime(&pi->fd->date);
3678                         if (tl)
3679                                 {
3680                                 gint match;
3681
3682                                 match = (tl->tm_year == year - 1900);
3683                                 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3684                                 if (match && day > 0) match = (tl->tm_mday == day);
3685
3686                                 if (match) list = g_list_prepend(list, pi);
3687                                 }
3688                         }
3689                 }
3690
3691         return g_list_reverse(list);
3692 }
3693
3694 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3695 {
3696         PanItem *pi = NULL;
3697         GList *list = NULL;
3698         GList *found;
3699         gint year;
3700         gint month = -1;
3701         gint day = -1;
3702         gchar *ptr;
3703         gchar *mptr;
3704         struct tm *lt;
3705         time_t t;
3706         gchar *message;
3707         gchar *buf;
3708         gchar *buf_count;
3709
3710         if (!text) return FALSE;
3711
3712         ptr = (gchar *)text;
3713         while (*ptr != '\0')
3714                 {
3715                 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3716                 ptr++;
3717                 }
3718
3719         t = time(NULL);
3720         if (t == -1) return FALSE;
3721         lt = localtime(&t);
3722         if (!lt) return FALSE;
3723
3724         if (valid_date_separator(*text))
3725                 {
3726                 year = -1;
3727                 mptr = (gchar *)text;
3728                 }
3729         else
3730                 {
3731                 year = (gint)strtol(text, &mptr, 10);
3732                 if (mptr == text) return FALSE;
3733                 }
3734
3735         if (*mptr != '\0' && valid_date_separator(*mptr))
3736                 {
3737                 gchar *dptr;
3738
3739                 mptr++;
3740                 month = strtol(mptr, &dptr, 10);
3741                 if (dptr == mptr)
3742                         {
3743                         if (valid_date_separator(*dptr))
3744                                 {
3745                                 month = lt->tm_mon + 1;
3746                                 dptr++;
3747                                 }
3748                         else
3749                                 {
3750                                 month = -1;
3751                                 }
3752                         }
3753                 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3754                         {
3755                         gchar *eptr;
3756                         dptr++;
3757                         day = strtol(dptr, &eptr, 10);
3758                         if (dptr == eptr)
3759                                 {
3760                                 day = lt->tm_mday;
3761                                 }
3762                         }
3763                 }
3764
3765         if (year == -1)
3766                 {
3767                 year = lt->tm_year + 1900;
3768                 }
3769         else if (year < 100)
3770                 {
3771                 if (year > 70)
3772                         year+= 1900;
3773                 else
3774                         year+= 2000;
3775                 }
3776
3777         if (year < 1970 ||
3778             month < -1 || month == 0 || month > 12 ||
3779             day < -1 || day == 0 || day > 31) return FALSE;
3780
3781         t = date_to_time(year, month, day);
3782         if (t < 0) return FALSE;
3783
3784         if (pw->layout == LAYOUT_CALENDAR)
3785                 {
3786                 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
3787                 }
3788         else
3789                 {
3790                 ItemType type;
3791
3792                 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3793                 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
3794                 }
3795
3796         if (list)
3797                 {
3798                 found = g_list_find(list, pw->search_pi);
3799                 if (found && found->next)
3800                         {
3801                         found = found->next;
3802                         pi = found->data;
3803                         }
3804                 else
3805                         {
3806                         pi = list->data;
3807                         }
3808                 }
3809
3810         pw->search_pi = pi;
3811
3812         if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
3813                 {
3814                 pan_info_update(pw, NULL);
3815                 pan_calendar_update(pw, pi);
3816                 image_scroll_to_point(pw->imd,
3817                                       pi->x + pi->width / 2,
3818                                       pi->y + pi->height / 2, 0.5, 0.5);
3819                 }
3820         else if (pi)
3821                 {
3822                 pan_info_update(pw, pi);
3823                 image_scroll_to_point(pw->imd,
3824                                       pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3825                                       pi->y, 0.0, 0.5);
3826                 }
3827
3828         if (month > 0)
3829                 {
3830                 buf = date_value_string(t, DATE_LENGTH_MONTH);
3831                 if (day > 0)
3832                         {
3833                         gchar *tmp;
3834                         tmp = buf;
3835                         buf = g_strdup_printf("%d %s", day, tmp);
3836                         g_free(tmp);
3837                         }
3838                 }
3839         else
3840                 {
3841                 buf = date_value_string(t, DATE_LENGTH_YEAR);
3842                 }
3843
3844         if (pi)
3845                 {
3846                 buf_count = g_strdup_printf("( %d / %d )",
3847                                             g_list_index(list, pi) + 1,
3848                                             g_list_length(list));
3849                 }
3850         else
3851                 {
3852                 buf_count = g_strdup_printf("(%s)", _("no match"));
3853                 }
3854
3855         message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
3856         g_free(buf);
3857         g_free(buf_count);
3858         pan_search_status(pw, message);
3859         g_free(message);
3860
3861         g_list_free(list);
3862
3863         return TRUE;
3864 }
3865
3866 static void pan_search_activate_cb(const gchar *text, gpointer data)
3867 {
3868         PanWindow *pw = data;
3869
3870         if (!text) return;
3871
3872         tab_completion_append_to_history(pw->search_entry, text);
3873
3874         if (pan_search_by_path(pw, text)) return;
3875
3876         if ((pw->layout == LAYOUT_TIMELINE ||
3877              pw->layout == LAYOUT_CALENDAR) &&
3878             pan_search_by_date(pw, text))
3879                 {
3880                 return;
3881                 }
3882
3883         if (pan_search_by_partial(pw, text)) return;
3884
3885         pan_search_status(pw, _("no match"));
3886 }
3887
3888 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3889 {
3890         PanWindow *pw = data;
3891         gint visible;
3892
3893         visible = GTK_WIDGET_VISIBLE(pw->search_box);
3894         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3895
3896         if (visible)
3897                 {
3898                 gtk_widget_hide(pw->search_box);
3899                 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3900                 }
3901         else
3902                 {
3903                 gtk_widget_show(pw->search_box);
3904                 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3905                 gtk_widget_grab_focus(pw->search_entry);
3906                 }
3907 }
3908
3909
3910 /*
3911  *-----------------------------------------------------------------------------
3912  * view window main routines
3913  *-----------------------------------------------------------------------------
3914  */ 
3915
3916 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
3917 {
3918         PanWindow *pw = data;
3919         PanItem *pi = NULL;
3920         GtkWidget *menu;
3921         gint rx, ry;
3922
3923         rx = ry = 0;
3924         if (pr->scale)
3925                 {
3926                 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
3927                 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
3928                 }
3929
3930         pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3931                                     rx, ry, NULL);
3932
3933         switch (event->button)
3934                 {
3935                 case 1:
3936                         pan_info_update(pw, pi);
3937
3938                         if (!pi && pw->layout == LAYOUT_CALENDAR)
3939                                 {
3940                                 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
3941                                 pan_calendar_update(pw, pi);
3942                                 }
3943                         break;
3944                 case 2:
3945                         break;
3946                 case 3:
3947                         pan_info_update(pw, pi);
3948                         menu = pan_popup_menu(pw);
3949                         gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
3950                         break;
3951                 default:
3952                         break;
3953                 }
3954 }
3955
3956 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
3957 {
3958 #if 0
3959         PanWindow *pw = data;
3960 #endif
3961         gint w, h;
3962
3963         w = pr->vis_width;
3964         h = pr->vis_height;
3965
3966         if (!(event->state & GDK_SHIFT_MASK))
3967                 {
3968                 w /= 3;
3969                 h /= 3;
3970                 }
3971
3972         if (event->state & GDK_CONTROL_MASK)
3973                 {
3974                 switch (event->direction)
3975                         {
3976                         case GDK_SCROLL_UP:
3977                                 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
3978                                                                      (gint)event->x, (gint)event->y);
3979                                 break;
3980                         case GDK_SCROLL_DOWN:
3981                                 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
3982                                                                      (gint)event->x, (gint)event->y);
3983                                 break;
3984                         default:
3985                                 break;
3986                         }
3987                 }
3988         else
3989                 {
3990                 switch (event->direction)
3991                         {
3992                         case GDK_SCROLL_UP:
3993                                 pixbuf_renderer_scroll(pr, 0, -h);
3994                                 break;
3995                         case GDK_SCROLL_DOWN:
3996                                 pixbuf_renderer_scroll(pr, 0, h);
3997                                 break;
3998                         case GDK_SCROLL_LEFT:
3999                                 pixbuf_renderer_scroll(pr, -w, 0);
4000                                 break;
4001                         case GDK_SCROLL_RIGHT:
4002                                 pixbuf_renderer_scroll(pr, w, 0);
4003                                 break;
4004                         default:
4005                                 break;
4006                         }
4007                 }
4008 }
4009
4010 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
4011 {
4012         g_signal_connect(G_OBJECT(imd->pr), "clicked",
4013                          G_CALLBACK(button_cb), pw);
4014         g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
4015                          G_CALLBACK(scroll_cb), pw);
4016 }
4017
4018 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
4019 {
4020         PanWindow *pw = data;
4021
4022         pw->fs = NULL;
4023         pw->imd = pw->imd_normal;
4024 }
4025
4026 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
4027 {
4028         if (force_off && !pw->fs) return;
4029
4030         if (pw->fs)
4031                 {
4032                 fullscreen_stop(pw->fs);
4033                 }
4034         else
4035                 {
4036                 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
4037                 pan_image_set_buttons(pw, pw->fs->imd);
4038                 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
4039                                  G_CALLBACK(pan_window_key_press_cb), pw);
4040
4041                 pw->imd = pw->fs->imd;
4042                 }
4043 }
4044
4045 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
4046 {
4047         PanWindow *pw = data;
4048         gchar *text;
4049
4050 #if 0
4051         text = image_zoom_get_as_text(imd);
4052 #endif
4053         text = g_strdup_printf("%.2f", zoom);
4054         gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4055         g_free(text);
4056 }
4057
4058 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
4059 {
4060         PanWindow *pw = data;
4061         GtkAdjustment *adj;
4062         GdkRectangle rect;
4063         gint width, height;
4064
4065         pixbuf_renderer_get_visible_rect(pr, &rect);
4066         pixbuf_renderer_get_image_size(pr, &width, &height);
4067
4068         adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4069         adj->page_size = (gdouble)rect.width;
4070         adj->page_increment = adj->page_size / 2.0;
4071         adj->step_increment = 48.0 / pr->scale;
4072         adj->lower = 0.0;
4073         adj->upper = MAX((gdouble)width, 1.0);
4074         adj->value = (gdouble)rect.x;
4075
4076         pref_signal_block_data(pw->scrollbar_h, pw);
4077         gtk_adjustment_changed(adj);
4078         pref_signal_unblock_data(pw->scrollbar_h, pw);
4079
4080         adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4081         adj->page_size = (gdouble)rect.height;
4082         adj->page_increment = adj->page_size / 2.0;
4083         adj->step_increment = 48.0 / pr->scale;
4084         adj->lower = 0.0;
4085         adj->upper = MAX((gdouble)height, 1.0);
4086         adj->value = (gdouble)rect.y;
4087
4088         pref_signal_block_data(pw->scrollbar_v, pw);
4089         gtk_adjustment_changed(adj);
4090         pref_signal_unblock_data(pw->scrollbar_v, pw);
4091
4092 //      printf("scrolled to %d,%d @ %d x %d\n", x, y, width, height);
4093 }
4094
4095 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4096 {
4097         PanWindow *pw = data;
4098         PixbufRenderer *pr;
4099         gint x;
4100
4101         pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4102
4103         if (!pr->scale) return;
4104
4105         x = (gint)gtk_range_get_value(range);
4106
4107         pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
4108 }
4109
4110 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4111 {
4112         PanWindow *pw = data;
4113         PixbufRenderer *pr;
4114         gint y;
4115
4116         pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4117
4118         if (!pr->scale) return;
4119
4120         y = (gint)gtk_range_get_value(range);
4121
4122         pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
4123 }
4124
4125 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4126 {
4127         PanWindow *pw = data;
4128
4129         pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4130         pan_window_layout_update_idle(pw);
4131 }
4132
4133 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4134 {
4135         PanWindow *pw = data;
4136
4137         pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4138         pan_window_layout_update_idle(pw);
4139 }
4140
4141 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4142 {
4143         PanWindow *pw = data;
4144         gchar *path;
4145
4146         path = remove_trailing_slash(new_text);
4147         parse_out_relatives(path);
4148
4149         if (!isdir(path))
4150                 {
4151                 warning_dialog(_("Folder not found"),
4152                                _("The entered path is not a folder"),
4153                                GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4154                 return;
4155                 }
4156
4157         tab_completion_append_to_history(pw->path_entry, path);
4158
4159         g_free(pw->path);
4160         pw->path = g_strdup(path);
4161
4162         pan_window_layout_update_idle(pw);
4163 }
4164
4165 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4166 {
4167         PanWindow *pw = data;
4168         gchar *text;
4169
4170         if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4171
4172         text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4173         pan_window_entry_activate_cb(text, pw);
4174         g_free(text);
4175 }
4176
4177 static void pan_window_close(PanWindow *pw)
4178 {
4179         pan_window_list = g_list_remove(pan_window_list, pw);
4180
4181         if (pw->idle_id != -1)
4182                 {
4183                 g_source_remove(pw->idle_id);
4184                 }
4185
4186         pan_fullscreen_toggle(pw, TRUE);
4187         gtk_widget_destroy(pw->window);
4188
4189         pan_window_items_free(pw);
4190
4191         g_free(pw->path);
4192
4193         g_free(pw);
4194 }
4195
4196 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4197 {
4198         PanWindow *pw = data;
4199
4200         pan_window_close(pw);
4201         return TRUE;
4202 }
4203
4204 static void pan_window_new_real(const gchar *path)
4205 {
4206         PanWindow *pw;
4207         GtkWidget *vbox;
4208         GtkWidget *box;
4209         GtkWidget *combo;
4210         GtkWidget *hbox;
4211         GtkWidget *frame;
4212         GtkWidget *table;
4213         GdkGeometry geometry;
4214
4215         pw = g_new0(PanWindow, 1);
4216
4217         pw->path = g_strdup(path);
4218         pw->layout = LAYOUT_TIMELINE;
4219         pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4220         pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4221         pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4222         pw->list = NULL;
4223
4224         pw->fs = NULL;
4225         pw->overlay_id = -1;
4226         pw->idle_id = -1;
4227
4228         pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4229
4230         geometry.min_width = 8;
4231         geometry.min_height = 8;
4232         gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4233
4234         gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4235         gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
4236         gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4237         gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4238
4239         window_set_icon(pw->window, NULL, NULL);
4240
4241         vbox = gtk_vbox_new(FALSE, 0);
4242         gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4243         gtk_widget_show(vbox);
4244
4245         box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4246
4247         pref_spacer(box, 0);
4248         pref_label_new(box, _("Location:"));
4249         combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
4250                                                 pan_window_entry_activate_cb, pw);
4251         g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4252                          G_CALLBACK(pan_window_entry_change_cb), pw);
4253         gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4254         gtk_widget_show(combo);
4255
4256         combo = gtk_combo_box_new_text();
4257         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4258         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4259         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4260         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4261         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4262
4263         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4264         g_signal_connect(G_OBJECT(combo), "changed",
4265                          G_CALLBACK(pan_window_layout_change_cb), pw);
4266         gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4267         gtk_widget_show(combo);
4268
4269         combo = gtk_combo_box_new_text();
4270         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4271         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4272         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4273         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4274         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4275         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4276         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4277         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4278         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4279         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4280
4281         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4282         g_signal_connect(G_OBJECT(combo), "changed",
4283                          G_CALLBACK(pan_window_layout_size_cb), pw);
4284         gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4285         gtk_widget_show(combo);
4286
4287         table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4288         gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4289         gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4290
4291         pw->imd = image_new(TRUE);
4292         pw->imd_normal = pw->imd;
4293
4294         g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
4295                          G_CALLBACK(pan_window_image_zoom_cb), pw);
4296         g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
4297                          G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4298
4299         gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4300                          GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4301         gtk_widget_show(GTK_WIDGET(pw->imd->widget));
4302
4303         pan_window_dnd_init(pw);
4304
4305         pan_image_set_buttons(pw, pw->imd);
4306
4307         pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4308         g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4309                          G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4310         gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4311                          GTK_FILL | GTK_EXPAND, 0, 0, 0);
4312         gtk_widget_show(pw->scrollbar_h);
4313
4314         pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4315         g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4316                          G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4317         gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4318                          0, GTK_FILL | GTK_EXPAND, 0, 0);
4319         gtk_widget_show(pw->scrollbar_v);
4320
4321         /* find bar */
4322
4323         pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4324         gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4325
4326         pref_spacer(pw->search_box, 0);
4327         pref_label_new(pw->search_box, _("Find:"));
4328
4329         hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4330         gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4331         gtk_widget_show(hbox);
4332
4333         combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4334                                                 pan_search_activate_cb, pw);
4335         gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4336         gtk_widget_show(combo);
4337
4338         pw->search_label = gtk_label_new("");
4339         gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4340         gtk_widget_show(pw->search_label);
4341
4342         /* status bar */
4343
4344         box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4345
4346         frame = gtk_frame_new(NULL);
4347         gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4348         gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4349         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4350         gtk_widget_show(frame);
4351
4352         hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4353         gtk_container_add(GTK_CONTAINER(frame), hbox);
4354         gtk_widget_show(hbox);
4355
4356         pref_spacer(hbox, 0);
4357         pw->label_message = pref_label_new(hbox, "");
4358
4359         frame = gtk_frame_new(NULL);
4360         gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4361         gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4362         gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4363         gtk_widget_show(frame);
4364
4365         pw->label_zoom = gtk_label_new("");
4366         gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4367         gtk_widget_show(pw->label_zoom);
4368
4369         pw->search_button = gtk_toggle_button_new();
4370         gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4371         gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4372         hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4373         gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4374         gtk_widget_show(hbox);
4375         pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4376         gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4377         gtk_widget_show(pw->search_button_arrow);
4378         pref_label_new(hbox, _("Find"));
4379
4380         gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4381         gtk_widget_show(pw->search_button);
4382         g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4383                          G_CALLBACK(pan_search_toggle_cb), pw);
4384
4385         g_signal_connect(G_OBJECT(pw->window), "delete_event",
4386                          G_CALLBACK(pan_window_delete_cb), pw);
4387         g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4388                          G_CALLBACK(pan_window_key_press_cb), pw);
4389
4390         gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4391
4392         pan_window_layout_update_idle(pw);
4393
4394         gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4395         gtk_widget_show(pw->window);
4396
4397         pan_window_list = g_list_append(pan_window_list, pw);
4398 }
4399
4400 /*
4401  *-----------------------------------------------------------------------------
4402  * peformance warnings
4403  *-----------------------------------------------------------------------------
4404  */
4405
4406 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4407 {
4408         gchar *path = data;
4409
4410         generic_dialog_close(gd);
4411
4412         pan_window_new_real(path);
4413         g_free(path);
4414 }
4415
4416 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4417 {
4418         gint hide_dlg;
4419
4420         hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4421         pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4422 }
4423
4424 static gint pan_warning(const gchar *path)
4425 {
4426         GenericDialog *gd;
4427         GtkWidget *box;
4428         GtkWidget *group;
4429         GtkWidget *button;
4430         GtkWidget *ct_button;
4431         gint hide_dlg;
4432
4433         if (enable_thumb_caching &&
4434             thumbnail_spec_standard) return FALSE;
4435
4436         if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4437         if (hide_dlg) return FALSE;
4438
4439         gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4440                                 NULL, NULL);
4441         gd->data = g_strdup(path);
4442         generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4443                                   pan_warning_ok_cb, TRUE);
4444
4445         box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4446                                          _("Pan view performance may be poor."),
4447                                          _("To improve performance of thumbnails in the pan view the"
4448                                            " following options can be enabled. Note that both options"
4449                                            " must be enabled to notice a change in performance."));
4450
4451         group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4452         pref_spacer(group, PREF_PAD_INDENT);
4453         group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4454
4455         ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4456                                           enable_thumb_caching, &enable_thumb_caching);
4457         button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4458                                        thumbnail_spec_standard, &thumbnail_spec_standard);
4459         pref_checkbox_link_sensitivity(ct_button, button);
4460
4461         pref_line(box, 0);
4462
4463         pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4464                           G_CALLBACK(pan_warning_hide_cb), NULL);
4465
4466         gtk_widget_show(gd->dialog);
4467
4468         return TRUE;
4469 }
4470
4471
4472 /*
4473  *-----------------------------------------------------------------------------
4474  * public
4475  *-----------------------------------------------------------------------------
4476  */
4477
4478 void pan_window_new(const gchar *path)
4479 {
4480         if (pan_warning(path)) return;
4481
4482         pan_window_new_real(path);
4483 }
4484
4485 /*
4486  *-----------------------------------------------------------------------------
4487  * menus
4488  *-----------------------------------------------------------------------------
4489  */
4490
4491 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4492 {
4493         PanWindow *pw = data;
4494         const gchar *path;
4495
4496         path = pan_menu_click_path(pw);
4497         if (path)
4498                 {
4499                 pan_fullscreen_toggle(pw, TRUE);
4500                 view_window_new(path);
4501                 }
4502 }
4503
4504 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4505 {
4506         PanWindow *pw;
4507         const gchar *path;
4508         gint n;
4509
4510         pw = submenu_item_get_data(widget);
4511         n = GPOINTER_TO_INT(data);
4512         if (!pw) return;
4513
4514         path = pan_menu_click_path(pw);
4515         if (path)
4516                 {
4517                 pan_fullscreen_toggle(pw, TRUE);
4518                 start_editor_from_file(n, path);
4519                 }
4520 }
4521
4522 static void pan_info_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) info_window_new(path, NULL);
4529 }
4530
4531 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4532 {
4533         PanWindow *pw = data;
4534
4535         image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
4536 }
4537
4538 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4539 {
4540         PanWindow *pw = data;
4541
4542         image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
4543 }
4544
4545 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4546 {
4547         PanWindow *pw = data;
4548
4549         image_zoom_set(pw->imd, 1.0);
4550 }
4551
4552 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4553 {
4554         PanWindow *pw = data;
4555         const gchar *path;
4556
4557         path = pan_menu_click_path(pw);
4558         if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4559 }
4560
4561 static void pan_move_cb(GtkWidget *widget, gpointer data)
4562 {
4563         PanWindow *pw = data;
4564         const gchar *path;
4565
4566         path = pan_menu_click_path(pw);
4567         if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4568 }
4569
4570 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4571 {
4572         PanWindow *pw = data;
4573         const gchar *path;
4574
4575         path = pan_menu_click_path(pw);
4576         if (path) file_util_rename(path, NULL, pw->imd->widget);
4577 }
4578
4579 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4580 {
4581         PanWindow *pw = data;
4582         const gchar *path;
4583
4584         path = pan_menu_click_path(pw);
4585         if (path) file_util_delete(path, NULL, pw->imd->widget);
4586 }
4587
4588 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4589 {
4590         PanWindow *pw = data;
4591
4592         pan_fullscreen_toggle(pw, FALSE);
4593 }
4594
4595 static void pan_close_cb(GtkWidget *widget, gpointer data)
4596 {
4597         PanWindow *pw = data;
4598
4599         pan_window_close(pw);
4600 }
4601
4602 static GtkWidget *pan_popup_menu(PanWindow *pw)
4603 {
4604         GtkWidget *menu;
4605         GtkWidget *item;
4606         gint active;
4607
4608         active = (pw->click_pi != NULL);
4609
4610         menu = popup_menu_short_lived();
4611
4612         menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4613                             G_CALLBACK(pan_zoom_in_cb), pw);
4614         menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4615                             G_CALLBACK(pan_zoom_out_cb), pw);
4616         menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4617                             G_CALLBACK(pan_zoom_1_1_cb), pw);
4618         menu_item_add_divider(menu);
4619
4620         submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4621         gtk_widget_set_sensitive(item, active);
4622
4623         menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4624                                       G_CALLBACK(pan_info_cb), pw);
4625
4626         menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4627                                       G_CALLBACK(pan_new_window_cb), pw);
4628
4629         menu_item_add_divider(menu);
4630         menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4631                                       G_CALLBACK(pan_copy_cb), pw);
4632         menu_item_add_sensitive(menu, _("_Move..."), active,
4633                                 G_CALLBACK(pan_move_cb), pw);
4634         menu_item_add_sensitive(menu, _("_Rename..."), active,
4635                                 G_CALLBACK(pan_rename_cb), pw);
4636         menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4637                                       G_CALLBACK(pan_delete_cb), pw);
4638
4639         menu_item_add_divider(menu);
4640
4641         if (pw->fs)
4642                 {
4643                 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4644                 }
4645         else
4646                 {
4647                 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4648                 }
4649
4650         menu_item_add_divider(menu);
4651         menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4652
4653         return menu;
4654 }
4655
4656 /*
4657  *-----------------------------------------------------------------------------
4658  * drag and drop
4659  *-----------------------------------------------------------------------------
4660  */
4661
4662 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4663                                     gint x, gint y,
4664                                     GtkSelectionData *selection_data, guint info,
4665                                     guint time, gpointer data)
4666 {
4667         PanWindow *pw = data;
4668
4669         if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
4670
4671         if (info == TARGET_URI_LIST)
4672                 {
4673                 GList *list;
4674
4675                 list = uri_list_from_text(selection_data->data, TRUE);
4676                 if (list && isdir((gchar *)list->data))
4677                         {
4678                         gchar *path = list->data;
4679
4680                         g_free(pw->path);
4681                         pw->path = g_strdup(path);
4682
4683                         pan_window_layout_update_idle(pw);
4684                         }
4685
4686                 path_list_free(list);
4687                 }
4688 }
4689
4690 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4691                                     GtkSelectionData *selection_data, guint info,
4692                                     guint time, gpointer data)
4693 {
4694         PanWindow *pw = data;
4695         const gchar *path;
4696
4697         path = pan_menu_click_path(pw);
4698         if (path)
4699                 {
4700                 gchar *text = NULL;
4701                 gint len;
4702                 gint plain_text;
4703                 GList *list;
4704
4705                 switch (info)
4706                         {
4707                         case TARGET_URI_LIST:
4708                                 plain_text = FALSE;
4709                                 break;
4710                         case TARGET_TEXT_PLAIN:
4711                         default:
4712                                 plain_text = TRUE;
4713                                 break;
4714                         }
4715                 list = g_list_append(NULL, (gchar *)path);
4716                 text = uri_text_from_list(list, &len, plain_text);
4717                 g_list_free(list);
4718                 if (text)
4719                         {
4720                         gtk_selection_data_set (selection_data, selection_data->target,
4721                                                 8, text, len);
4722                         g_free(text);
4723                         }
4724                 }
4725         else
4726                 {
4727                 gtk_selection_data_set (selection_data, selection_data->target,
4728                                         8, NULL, 0);
4729                 }
4730 }
4731
4732 static void pan_window_dnd_init(PanWindow *pw)
4733 {
4734         GtkWidget *widget;
4735
4736         widget = pw->imd->pr;
4737
4738         gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
4739                             dnd_file_drag_types, dnd_file_drag_types_count,
4740                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4741         g_signal_connect(G_OBJECT(widget), "drag_data_get",
4742                          G_CALLBACK(pan_window_set_dnd_data), pw);
4743
4744         gtk_drag_dest_set(widget,
4745                           GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4746                           dnd_file_drop_types, dnd_file_drop_types_count,
4747                           GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4748         g_signal_connect(G_OBJECT(widget), "drag_data_received",
4749                          G_CALLBACK(pan_window_get_dnd_data), pw);
4750 }
4751
4752 /*
4753  *-----------------------------------------------------------------------------
4754  * maintenance (for rename, move, remove)
4755  *-----------------------------------------------------------------------------
4756  */
4757