Sat Apr 2 17:28:16 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         if (x%512 == 0 && y%512 == 0)
2970                 {
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 #endif
2987
2988         return TRUE;
2989 }
2990
2991 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
2992                                        gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2993 {
2994         PanWindow *pw = data;
2995         GList *list;
2996         GList *work;
2997
2998         list = pan_layout_intersect(pw, x, y, width, height);
2999         work = list;
3000         while (work)
3001                 {
3002                 PanItem *pi;
3003
3004                 pi = work->data;
3005                 work = work->next;
3006
3007                 if (pi->refcount > 0)
3008                         {
3009                         pi->refcount--;
3010
3011                         if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
3012                             pi->refcount == 0)
3013                                 {
3014                                 if (pi->queued)
3015                                         {
3016                                         pw->queue = g_list_remove(pw->queue, pi);
3017                                         pi->queued = FALSE;
3018                                         }
3019                                 if (pw->queue_pi == pi) pw->queue_pi = NULL;
3020                                 if (pi->pixbuf)
3021                                         {
3022                                         g_object_unref(pi->pixbuf);
3023                                         pi->pixbuf = NULL;
3024                                         }
3025                                 }
3026                         }
3027                 }
3028
3029         g_list_free(list);
3030 }
3031
3032
3033 /*
3034  *-----------------------------------------------------------------------------
3035  * misc
3036  *-----------------------------------------------------------------------------
3037  */ 
3038
3039 static void pan_window_message(PanWindow *pw, const gchar *text)
3040 {
3041         GList *work;
3042         gint count = 0;
3043         gint64 size = 0;
3044         gchar *ss;
3045         gchar *buf;
3046
3047         if (text)
3048                 {
3049                 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
3050                 return;
3051                 }
3052
3053         work = pw->list;
3054         if (pw->layout == LAYOUT_CALENDAR)
3055                 {
3056                 while (work)
3057                         {
3058                         PanItem *pi;
3059
3060                         pi = work->data;
3061                         work = work->next;
3062
3063                         if (pi->fd &&
3064                             pi->type == ITEM_BOX &&
3065                             pi->key && strcmp(pi->key, "dot") == 0)
3066                                 {
3067                                 size += pi->fd->size;
3068                                 count++;
3069                                 }
3070                         }
3071                 }
3072         else
3073                 {
3074                 while (work)
3075                         {
3076                         PanItem *pi;
3077
3078                         pi = work->data;
3079                         work = work->next;
3080
3081                         if (pi->fd &&
3082                             (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
3083                                 {
3084                                 size += pi->fd->size;
3085                                 count++;
3086                                 }
3087                         }
3088                 }
3089
3090         ss = text_from_size_abrev(size);
3091         buf = g_strdup_printf(_("%d images, %s"), count, ss);
3092         g_free(ss);
3093         gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
3094         g_free(buf);
3095 }
3096
3097 static void pan_window_zoom_limit(PanWindow *pw)
3098 {
3099         gdouble min;
3100
3101         switch (pw->size)
3102                 {
3103                 case LAYOUT_SIZE_THUMB_DOTS:
3104                 case LAYOUT_SIZE_THUMB_NONE:
3105                 case LAYOUT_SIZE_THUMB_SMALL:
3106                 case LAYOUT_SIZE_THUMB_NORMAL:
3107 #if 0
3108                         /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
3109                         min = -16.0;
3110                         break;
3111 #endif
3112                 case LAYOUT_SIZE_THUMB_LARGE:
3113                         min = -6.0;
3114                         break;
3115                 case LAYOUT_SIZE_10:
3116                 case LAYOUT_SIZE_25:
3117                         min = -4.0;
3118                         break;
3119                 case LAYOUT_SIZE_33:
3120                 case LAYOUT_SIZE_50:
3121                 case LAYOUT_SIZE_100:
3122                 default:
3123                         min = -2.0;
3124                         break;
3125                 }
3126
3127         image_zoom_set_limits(pw->imd, min, 32.0);
3128 }
3129
3130 static gint pan_window_layout_update_idle_cb(gpointer data)
3131 {
3132         PanWindow *pw = data;
3133         gint width;
3134         gint height;
3135         gint scroll_x;
3136         gint scroll_y;
3137
3138         if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
3139                 {
3140                 if (!pw->cache_list && !pw->cache_todo)
3141                         {
3142                         pan_cache_fill(pw, pw->path);
3143                         if (pw->cache_todo)
3144                                 {
3145                                 pan_window_message(pw, _("Reading dimensions..."));
3146                                 return TRUE;
3147                                 }
3148                         }
3149                 if (pan_cache_step(pw))
3150                         {
3151                         pw->cache_count++;
3152                         pw->cache_tick++;
3153                         if (pw->cache_count == pw->cache_total)
3154                                 {
3155                                 pan_window_message(pw, _("Sorting images..."));
3156                                 }
3157                         else if (pw->cache_tick > 9)
3158                                 {
3159                                 gchar *buf;
3160
3161                                 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
3162                                                       pw->cache_total - pw->cache_count);
3163                                 pan_window_message(pw, buf);
3164                                 g_free(buf);
3165
3166                                 pw->cache_tick = 0;
3167                                 }
3168
3169                         return TRUE;
3170                         }
3171                 }
3172
3173         pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3174
3175         pan_window_zoom_limit(pw);
3176
3177         if (width > 0 && height > 0)
3178                 {
3179                 gdouble align;
3180
3181                 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
3182                                           PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
3183                                           pan_window_request_tile_cb,
3184                                           pan_window_dispose_tile_cb, pw, 1.0);
3185
3186                 if (scroll_x == 0 && scroll_y == 0)
3187                         {
3188                         align = 0.0;
3189                         }
3190                 else
3191                         {
3192                         align = 0.5;
3193                         }
3194                 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
3195                 }
3196
3197         pan_window_message(pw, NULL);
3198
3199         pw->idle_id = -1;
3200
3201         return FALSE;
3202 }
3203
3204 static void pan_window_layout_update_idle(PanWindow *pw)
3205 {
3206         if (pw->idle_id == -1)
3207                 {
3208                 pan_window_message(pw, _("Sorting images..."));
3209                 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3210                 }
3211 }
3212
3213 /*
3214  *-----------------------------------------------------------------------------
3215  * pan window keyboard
3216  *-----------------------------------------------------------------------------
3217  */
3218
3219 static const gchar *pan_menu_click_path(PanWindow *pw)
3220 {
3221         if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3222         return NULL;
3223 }
3224
3225 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3226 {
3227         PanWindow *pw = data;
3228
3229         gdk_window_get_origin(pw->imd->pr->window, x, y);
3230         popup_menu_position_clamp(menu, x, y, 0);
3231 }
3232
3233 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3234 {
3235         PanWindow *pw = data;
3236         PixbufRenderer *pr;
3237         const gchar *path;
3238         gint stop_signal = FALSE;
3239         GtkWidget *menu;
3240         gint x = 0;
3241         gint y = 0;
3242         gint focused;
3243
3244         pr = PIXBUF_RENDERER(pw->imd->pr);
3245         path = pan_menu_click_path(pw);
3246
3247         focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pw->imd->widget)));
3248
3249         if (focused)
3250                 {
3251                 switch (event->keyval)
3252                         {
3253                         case GDK_Left: case GDK_KP_Left:
3254                                 x -= 1;
3255                                 stop_signal = TRUE;
3256                                 break;
3257                         case GDK_Right: case GDK_KP_Right:
3258                                 x += 1;
3259                                 stop_signal = TRUE;
3260                                 break;
3261                         case GDK_Up: case GDK_KP_Up:
3262                                 y -= 1;
3263                                 stop_signal = TRUE;
3264                                 break;
3265                         case GDK_Down: case GDK_KP_Down:
3266                                 y += 1;
3267                                 stop_signal = TRUE;
3268                                 break;
3269                         case GDK_Page_Up: case GDK_KP_Page_Up:
3270                                 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
3271                                 break;
3272                         case GDK_Page_Down: case GDK_KP_Page_Down:
3273                                 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
3274                                 break;
3275                         case GDK_Home: case GDK_KP_Home:
3276                                 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
3277                                 break;
3278                         case GDK_End: case GDK_KP_End:
3279                                 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
3280                                 break;
3281                         }
3282                 }
3283
3284         if (focused && !(event->state & GDK_CONTROL_MASK) )
3285             switch (event->keyval)
3286                 {
3287                 case '+': case '=': case GDK_KP_Add:
3288                         pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
3289                         break;
3290                 case '-': case GDK_KP_Subtract:
3291                         pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
3292                         break;
3293                 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3294                         pixbuf_renderer_zoom_set(pr, 1.0);
3295                         break;
3296                 case '2':
3297                         pixbuf_renderer_zoom_set(pr, 2.0);
3298                         break;
3299                 case '3':
3300                         pixbuf_renderer_zoom_set(pr, 3.0);
3301                         break;
3302                 case '4':
3303                         pixbuf_renderer_zoom_set(pr, 4.0);
3304                         break;
3305                 case '7':
3306                         pixbuf_renderer_zoom_set(pr, -4.0);
3307                         break;
3308                 case '8':
3309                         pixbuf_renderer_zoom_set(pr, -3.0);
3310                         break;
3311                 case '9':
3312                         pixbuf_renderer_zoom_set(pr, -2.0);
3313                         break;
3314                 case 'F': case 'f':
3315                 case 'V': case 'v':
3316                         pan_fullscreen_toggle(pw, FALSE);
3317                         stop_signal = TRUE;
3318                         break;
3319                 case 'I': case 'i':
3320 #if 0
3321                         pan_overlay_toggle(pw);
3322 #endif
3323                         break;
3324                 case GDK_Delete: case GDK_KP_Delete:
3325                         break;
3326                 case '/':
3327                         if (!pw->fs)
3328                                 {
3329                                 if (GTK_WIDGET_VISIBLE(pw->search_box))
3330                                         {
3331                                         gtk_widget_grab_focus(pw->search_entry);
3332                                         }
3333                                 else
3334                                         {
3335                                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3336                                         }
3337                                 stop_signal = TRUE;
3338                                 }
3339                         break;
3340                 case GDK_Escape:
3341                         if (pw->fs)
3342                                 {
3343                                 pan_fullscreen_toggle(pw, TRUE);
3344                                 stop_signal = TRUE;
3345                                 }
3346                         else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3347                                 {
3348                                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3349                                 stop_signal = TRUE;
3350                                 }
3351                         break;
3352                 case GDK_Menu:
3353                 case GDK_F10:
3354                         menu = pan_popup_menu(pw);
3355                         gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3356                         stop_signal = TRUE;
3357                         break;
3358                 }
3359
3360         if (event->state & GDK_CONTROL_MASK)
3361                 {
3362                 gint n = -1;
3363                 switch (event->keyval)
3364                         {
3365                         case '1':
3366                                 n = 0;
3367                                 break;
3368                         case '2':
3369                                 n = 1;
3370                                 break;
3371                         case '3':
3372                                 n = 2;
3373                                 break;
3374                         case '4':
3375                                 n = 3;
3376                                 break;
3377                         case '5':
3378                                 n = 4;
3379                                 break;
3380                         case '6':
3381                                 n = 5;
3382                                 break;
3383                         case '7':
3384                                 n = 6;
3385                                 break;
3386                         case '8':
3387                                 n = 7;
3388                                 break;
3389                         case '9':
3390                                 n = 8;
3391                                 break;
3392                         case '0':
3393                                 n = 9;
3394                                 break;
3395                         case 'C': case 'c':
3396                                 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3397                                 break;
3398                         case 'M': case 'm':
3399                                 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3400                                 break;
3401                         case 'R': case 'r':
3402                                 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3403                                 break;
3404                         case 'D': case 'd':
3405                                 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3406                                 break;
3407                         case 'P': case 'p':
3408                                 if (path) info_window_new(path, NULL);
3409                                 break;
3410                         case 'W': case 'w':
3411                                 pan_window_close(pw);
3412                                 break;
3413                         }
3414                 if (n != -1 && path)
3415                         {
3416                         pan_fullscreen_toggle(pw, TRUE);
3417                         start_editor_from_file(n, path);
3418                         stop_signal = TRUE;
3419                         }
3420                 }
3421         else if (event->state & GDK_SHIFT_MASK)
3422                 {
3423                 x *= 3;
3424                 y *= 3;
3425                 }
3426         else if (!focused)
3427                 {
3428                 switch (event->keyval)
3429                         {
3430                         case GDK_Escape:
3431                                 if (pw->fs)
3432                                         {
3433                                         pan_fullscreen_toggle(pw, TRUE);
3434                                         stop_signal = TRUE;
3435                                         }
3436                                 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3437                                         {
3438                                         gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
3439                                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3440                                         stop_signal = TRUE;
3441                                         }
3442                         break;
3443                         default:
3444                                 break;
3445                         }
3446                 }
3447
3448         if (x != 0 || y!= 0)
3449                 {
3450                 keyboard_scroll_calc(&x, &y, event);
3451                 pixbuf_renderer_scroll(pr, x, y);
3452                 }
3453
3454         return stop_signal;
3455 }
3456
3457 /*
3458  *-----------------------------------------------------------------------------
3459  * info popup
3460  *-----------------------------------------------------------------------------
3461  */
3462
3463 static void pan_info_update(PanWindow *pw, PanItem *pi)
3464 {
3465         PanItem *pbox;
3466         PanItem *plabel;
3467         PanItem *p;
3468         gchar *buf;
3469         gint x1, y1, x2, y2, x3, y3;
3470         gint x, y, w, h;
3471
3472         if (pw->click_pi == pi) return;
3473         if (pi && !pi->fd) pi = NULL;
3474
3475         while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3476         pw->click_pi = pi;
3477
3478         if (!pi) return;
3479
3480         printf("info set to %s\n", pi->fd->path);
3481
3482         pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3483                              PAN_POPUP_BORDER,
3484                              PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3485                              PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3486         pan_item_set_key(pbox, "info");
3487
3488         if (pi->type == ITEM_THUMB && pi->pixbuf)
3489                 {
3490                 w = gdk_pixbuf_get_width(pi->pixbuf);
3491                 h = gdk_pixbuf_get_height(pi->pixbuf);
3492
3493                 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3494                 y1 = pi->y + (pi->height - h) / 2 + 8;
3495                 }
3496         else
3497                 {
3498                 x1 = pi->x + pi->width - 8;
3499                 y1 = pi->y + 8;
3500                 }
3501
3502         x2 = pbox->x + 1;
3503         y2 = pbox->y + 36;
3504         x3 = pbox->x + 1;
3505         y3 = pbox->y + 12;
3506         triangle_rect_region(x1, y1, x2, y2, x3, y3,
3507                              &x, &y, &w, &h);
3508
3509         p = pan_item_new_tri(pw, NULL, x, y, w, h,
3510                              x1, y1, x2, y2, x3, y3,
3511                              PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3512         pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3513         pan_item_set_key(p, "info");
3514         pan_item_added(pw, p);
3515
3516         plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3517                                    _("Filename:"), TEXT_ATTR_BOLD,
3518                                    PAN_POPUP_TEXT_COLOR, 255);
3519         pan_item_set_key(plabel, "info");
3520         p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3521                               pi->fd->name, TEXT_ATTR_NONE,
3522                               PAN_POPUP_TEXT_COLOR, 255);
3523         pan_item_set_key(p, "info");
3524         pan_item_size_by_item(pbox, p, 0);
3525
3526         plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3527                                    _("Date:"), TEXT_ATTR_BOLD,
3528                                    PAN_POPUP_TEXT_COLOR, 255);
3529         pan_item_set_key(plabel, "info");
3530         p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3531                               text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3532                               PAN_POPUP_TEXT_COLOR, 255);
3533         pan_item_set_key(p, "info");
3534         pan_item_size_by_item(pbox, p, 0);
3535
3536         plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3537                                    _("Size:"), TEXT_ATTR_BOLD,
3538                                    PAN_POPUP_TEXT_COLOR, 255);
3539         pan_item_set_key(plabel, "info");
3540         buf = text_from_size(pi->fd->size);
3541         p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3542                               buf, TEXT_ATTR_NONE,
3543                               PAN_POPUP_TEXT_COLOR, 255);
3544         g_free(buf);
3545         pan_item_set_key(p, "info");
3546         pan_item_size_by_item(pbox, p, 0);
3547
3548         pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3549         pan_item_added(pw, pbox);
3550 }
3551
3552
3553 /*
3554  *-----------------------------------------------------------------------------
3555  * search
3556  *-----------------------------------------------------------------------------
3557  */
3558
3559 static void pan_search_status(PanWindow *pw, const gchar *text)
3560 {
3561         gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3562 }
3563
3564 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3565 {
3566         PanItem *pi;
3567         GList *list;
3568         GList *found;
3569         ItemType type;
3570         gchar *buf;
3571
3572         type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3573
3574         list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3575         if (!list) return FALSE;
3576
3577         found = g_list_find(list, pw->click_pi);
3578         if (found && found->next)
3579                 {
3580                 found = found->next;
3581                 pi = found->data;
3582                 }
3583         else
3584                 {
3585                 pi = list->data;
3586                 }
3587
3588         pan_info_update(pw, pi);
3589         image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3590
3591         buf = g_strdup_printf("%s ( %d / %d )",
3592                               (path[0] == '/') ? _("path found") : _("filename found"),
3593                               g_list_index(list, pi) + 1,
3594                               g_list_length(list));
3595         pan_search_status(pw, buf);
3596         g_free(buf);
3597
3598         g_list_free(list);
3599
3600         return TRUE;
3601 }
3602
3603 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3604 {
3605         PanItem *pi;
3606         GList *list;
3607         GList *found;
3608         ItemType type;
3609         gchar *buf;
3610
3611         type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3612
3613         list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3614         if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3615         if (!list)
3616                 {
3617                 gchar *needle;
3618
3619                 needle = g_utf8_strdown(text, -1);
3620                 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3621                 g_free(needle);
3622                 }
3623         if (!list) return FALSE;
3624
3625         found = g_list_find(list, pw->click_pi);
3626         if (found && found->next)
3627                 {
3628                 found = found->next;
3629                 pi = found->data;
3630                 }
3631         else
3632                 {
3633                 pi = list->data;
3634                 }
3635
3636         pan_info_update(pw, pi);
3637         image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3638
3639         buf = g_strdup_printf("%s ( %d / %d )",
3640                               _("partial match"),
3641                               g_list_index(list, pi) + 1,
3642                               g_list_length(list));
3643         pan_search_status(pw, buf);
3644         g_free(buf);
3645
3646         g_list_free(list);
3647
3648         return TRUE;
3649 }
3650
3651 static gint valid_date_separator(gchar c)
3652 {
3653         return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3654 }
3655
3656 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
3657                                      gint year, gint month, gint day,
3658                                      const gchar *key)
3659 {
3660         GList *list = NULL;
3661         GList *work;
3662
3663         work = g_list_last(pw->list);
3664         while (work)
3665                 {
3666                 PanItem *pi;
3667
3668                 pi = work->data;
3669                 work = work->prev;
3670
3671                 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
3672                     ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
3673                         {
3674                         struct tm *tl;
3675
3676                         tl = localtime(&pi->fd->date);
3677                         if (tl)
3678                                 {
3679                                 gint match;
3680
3681                                 match = (tl->tm_year == year - 1900);
3682                                 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3683                                 if (match && day > 0) match = (tl->tm_mday == day);
3684
3685                                 if (match) list = g_list_prepend(list, pi);
3686                                 }
3687                         }
3688                 }
3689
3690         return g_list_reverse(list);
3691 }
3692
3693 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3694 {
3695         PanItem *pi = NULL;
3696         GList *list = NULL;
3697         GList *found;
3698         gint year;
3699         gint month = -1;
3700         gint day = -1;
3701         gchar *ptr;
3702         gchar *mptr;
3703         struct tm *lt;
3704         time_t t;
3705         gchar *message;
3706         gchar *buf;
3707         gchar *buf_count;
3708
3709         if (!text) return FALSE;
3710
3711         ptr = (gchar *)text;
3712         while (*ptr != '\0')
3713                 {
3714                 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3715                 ptr++;
3716                 }
3717
3718         t = time(NULL);
3719         if (t == -1) return FALSE;
3720         lt = localtime(&t);
3721         if (!lt) return FALSE;
3722
3723         if (valid_date_separator(*text))
3724                 {
3725                 year = -1;
3726                 mptr = (gchar *)text;
3727                 }
3728         else
3729                 {
3730                 year = (gint)strtol(text, &mptr, 10);
3731                 if (mptr == text) return FALSE;
3732                 }
3733
3734         if (*mptr != '\0' && valid_date_separator(*mptr))
3735                 {
3736                 gchar *dptr;
3737
3738                 mptr++;
3739                 month = strtol(mptr, &dptr, 10);
3740                 if (dptr == mptr)
3741                         {
3742                         if (valid_date_separator(*dptr))
3743                                 {
3744                                 month = lt->tm_mon + 1;
3745                                 dptr++;
3746                                 }
3747                         else
3748                                 {
3749                                 month = -1;
3750                                 }
3751                         }
3752                 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3753                         {
3754                         gchar *eptr;
3755                         dptr++;
3756                         day = strtol(dptr, &eptr, 10);
3757                         if (dptr == eptr)
3758                                 {
3759                                 day = lt->tm_mday;
3760                                 }
3761                         }
3762                 }
3763
3764         if (year == -1)
3765                 {
3766                 year = lt->tm_year + 1900;
3767                 }
3768         else if (year < 100)
3769                 {
3770                 if (year > 70)
3771                         year+= 1900;
3772                 else
3773                         year+= 2000;
3774                 }
3775
3776         if (year < 1970 ||
3777             month < -1 || month == 0 || month > 12 ||
3778             day < -1 || day == 0 || day > 31) return FALSE;
3779
3780         t = date_to_time(year, month, day);
3781         if (t < 0) return FALSE;
3782
3783         if (pw->layout == LAYOUT_CALENDAR)
3784                 {
3785                 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
3786                 }
3787         else
3788                 {
3789                 ItemType type;
3790
3791                 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3792                 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
3793                 }
3794
3795         if (list)
3796                 {
3797                 found = g_list_find(list, pw->search_pi);
3798                 if (found && found->next)
3799                         {
3800                         found = found->next;
3801                         pi = found->data;
3802                         }
3803                 else
3804                         {
3805                         pi = list->data;
3806                         }
3807                 }
3808
3809         pw->search_pi = pi;
3810
3811         if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
3812                 {
3813                 pan_info_update(pw, NULL);
3814                 pan_calendar_update(pw, pi);
3815                 image_scroll_to_point(pw->imd,
3816                                       pi->x + pi->width / 2,
3817                                       pi->y + pi->height / 2, 0.5, 0.5);
3818                 }
3819         else if (pi)
3820                 {
3821                 pan_info_update(pw, pi);
3822                 image_scroll_to_point(pw->imd,
3823                                       pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3824                                       pi->y, 0.0, 0.5);
3825                 }
3826
3827         if (month > 0)
3828                 {
3829                 buf = date_value_string(t, DATE_LENGTH_MONTH);
3830                 if (day > 0)
3831                         {
3832                         gchar *tmp;
3833                         tmp = buf;
3834                         buf = g_strdup_printf("%d %s", day, tmp);
3835                         g_free(tmp);
3836                         }
3837                 }
3838         else
3839                 {
3840                 buf = date_value_string(t, DATE_LENGTH_YEAR);
3841                 }
3842
3843         if (pi)
3844                 {
3845                 buf_count = g_strdup_printf("( %d / %d )",
3846                                             g_list_index(list, pi) + 1,
3847                                             g_list_length(list));
3848                 }
3849         else
3850                 {
3851                 buf_count = g_strdup_printf("(%s)", _("no match"));
3852                 }
3853
3854         message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
3855         g_free(buf);
3856         g_free(buf_count);
3857         pan_search_status(pw, message);
3858         g_free(message);
3859
3860         g_list_free(list);
3861
3862         return TRUE;
3863 }
3864
3865 static void pan_search_activate_cb(const gchar *text, gpointer data)
3866 {
3867         PanWindow *pw = data;
3868
3869         if (!text) return;
3870
3871         tab_completion_append_to_history(pw->search_entry, text);
3872
3873         if (pan_search_by_path(pw, text)) return;
3874
3875         if ((pw->layout == LAYOUT_TIMELINE ||
3876              pw->layout == LAYOUT_CALENDAR) &&
3877             pan_search_by_date(pw, text))
3878                 {
3879                 return;
3880                 }
3881
3882         if (pan_search_by_partial(pw, text)) return;
3883
3884         pan_search_status(pw, _("no match"));
3885 }
3886
3887 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3888 {
3889         PanWindow *pw = data;
3890         gint visible;
3891
3892         visible = GTK_WIDGET_VISIBLE(pw->search_box);
3893         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3894
3895         if (visible)
3896                 {
3897                 gtk_widget_hide(pw->search_box);
3898                 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3899                 }
3900         else
3901                 {
3902                 gtk_widget_show(pw->search_box);
3903                 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3904                 gtk_widget_grab_focus(pw->search_entry);
3905                 }
3906 }
3907
3908
3909 /*
3910  *-----------------------------------------------------------------------------
3911  * view window main routines
3912  *-----------------------------------------------------------------------------
3913  */ 
3914
3915 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
3916 {
3917         PanWindow *pw = data;
3918         PanItem *pi = NULL;
3919         GtkWidget *menu;
3920         gint rx, ry;
3921
3922         rx = ry = 0;
3923         if (pr->scale)
3924                 {
3925                 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
3926                 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
3927                 }
3928
3929         pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3930                                     rx, ry, NULL);
3931
3932         switch (event->button)
3933                 {
3934                 case 1:
3935                         pan_info_update(pw, pi);
3936
3937                         if (!pi && pw->layout == LAYOUT_CALENDAR)
3938                                 {
3939                                 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
3940                                 pan_calendar_update(pw, pi);
3941                                 }
3942                         break;
3943                 case 2:
3944                         break;
3945                 case 3:
3946                         pan_info_update(pw, pi);
3947                         menu = pan_popup_menu(pw);
3948                         gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
3949                         break;
3950                 default:
3951                         break;
3952                 }
3953 }
3954
3955 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
3956 {
3957 #if 0
3958         PanWindow *pw = data;
3959 #endif
3960         gint w, h;
3961
3962         w = pr->vis_width;
3963         h = pr->vis_height;
3964
3965         if (!(event->state & GDK_SHIFT_MASK))
3966                 {
3967                 w /= 3;
3968                 h /= 3;
3969                 }
3970
3971         if (event->state & GDK_CONTROL_MASK)
3972                 {
3973                 switch (event->direction)
3974                         {
3975                         case GDK_SCROLL_UP:
3976                                 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
3977                                                                      (gint)event->x, (gint)event->y);
3978                                 break;
3979                         case GDK_SCROLL_DOWN:
3980                                 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
3981                                                                      (gint)event->x, (gint)event->y);
3982                                 break;
3983                         default:
3984                                 break;
3985                         }
3986                 }
3987         else
3988                 {
3989                 switch (event->direction)
3990                         {
3991                         case GDK_SCROLL_UP:
3992                                 pixbuf_renderer_scroll(pr, 0, -h);
3993                                 break;
3994                         case GDK_SCROLL_DOWN:
3995                                 pixbuf_renderer_scroll(pr, 0, h);
3996                                 break;
3997                         case GDK_SCROLL_LEFT:
3998                                 pixbuf_renderer_scroll(pr, -w, 0);
3999                                 break;
4000                         case GDK_SCROLL_RIGHT:
4001                                 pixbuf_renderer_scroll(pr, w, 0);
4002                                 break;
4003                         default:
4004                                 break;
4005                         }
4006                 }
4007 }
4008
4009 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
4010 {
4011         g_signal_connect(G_OBJECT(imd->pr), "clicked",
4012                          G_CALLBACK(button_cb), pw);
4013         g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
4014                          G_CALLBACK(scroll_cb), pw);
4015 }
4016
4017 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
4018 {
4019         PanWindow *pw = data;
4020
4021         pw->fs = NULL;
4022         pw->imd = pw->imd_normal;
4023 }
4024
4025 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
4026 {
4027         if (force_off && !pw->fs) return;
4028
4029         if (pw->fs)
4030                 {
4031                 fullscreen_stop(pw->fs);
4032                 }
4033         else
4034                 {
4035                 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
4036                 pan_image_set_buttons(pw, pw->fs->imd);
4037                 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
4038                                  G_CALLBACK(pan_window_key_press_cb), pw);
4039
4040                 pw->imd = pw->fs->imd;
4041                 }
4042 }
4043
4044 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
4045 {
4046         PanWindow *pw = data;
4047         gchar *text;
4048
4049         text = image_zoom_get_as_text(pw->imd);
4050         gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4051         g_free(text);
4052 }
4053
4054 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
4055 {
4056         PanWindow *pw = data;
4057         GtkAdjustment *adj;
4058         GdkRectangle rect;
4059         gint width, height;
4060
4061         pixbuf_renderer_get_visible_rect(pr, &rect);
4062         pixbuf_renderer_get_image_size(pr, &width, &height);
4063
4064         adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4065         adj->page_size = (gdouble)rect.width;
4066         adj->page_increment = adj->page_size / 2.0;
4067         adj->step_increment = 48.0 / pr->scale;
4068         adj->lower = 0.0;
4069         adj->upper = MAX((gdouble)width, 1.0);
4070         adj->value = (gdouble)rect.x;
4071
4072         pref_signal_block_data(pw->scrollbar_h, pw);
4073         gtk_adjustment_changed(adj);
4074         pref_signal_unblock_data(pw->scrollbar_h, pw);
4075
4076         adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4077         adj->page_size = (gdouble)rect.height;
4078         adj->page_increment = adj->page_size / 2.0;
4079         adj->step_increment = 48.0 / pr->scale;
4080         adj->lower = 0.0;
4081         adj->upper = MAX((gdouble)height, 1.0);
4082         adj->value = (gdouble)rect.y;
4083
4084         pref_signal_block_data(pw->scrollbar_v, pw);
4085         gtk_adjustment_changed(adj);
4086         pref_signal_unblock_data(pw->scrollbar_v, pw);
4087
4088 //      printf("scrolled to %d,%d @ %d x %d\n", x, y, width, height);
4089 }
4090
4091 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4092 {
4093         PanWindow *pw = data;
4094         PixbufRenderer *pr;
4095         gint x;
4096
4097         pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4098
4099         if (!pr->scale) return;
4100
4101         x = (gint)gtk_range_get_value(range);
4102
4103         pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
4104 }
4105
4106 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4107 {
4108         PanWindow *pw = data;
4109         PixbufRenderer *pr;
4110         gint y;
4111
4112         pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4113
4114         if (!pr->scale) return;
4115
4116         y = (gint)gtk_range_get_value(range);
4117
4118         pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
4119 }
4120
4121 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4122 {
4123         PanWindow *pw = data;
4124
4125         pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4126         pan_window_layout_update_idle(pw);
4127 }
4128
4129 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4130 {
4131         PanWindow *pw = data;
4132
4133         pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4134         pan_window_layout_update_idle(pw);
4135 }
4136
4137 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4138 {
4139         PanWindow *pw = data;
4140         gchar *path;
4141
4142         path = remove_trailing_slash(new_text);
4143         parse_out_relatives(path);
4144
4145         if (!isdir(path))
4146                 {
4147                 warning_dialog(_("Folder not found"),
4148                                _("The entered path is not a folder"),
4149                                GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4150                 return;
4151                 }
4152
4153         tab_completion_append_to_history(pw->path_entry, path);
4154
4155         g_free(pw->path);
4156         pw->path = g_strdup(path);
4157
4158         pan_window_layout_update_idle(pw);
4159 }
4160
4161 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4162 {
4163         PanWindow *pw = data;
4164         gchar *text;
4165
4166         if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4167
4168         text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4169         pan_window_entry_activate_cb(text, pw);
4170         g_free(text);
4171 }
4172
4173 static void pan_window_close(PanWindow *pw)
4174 {
4175         pan_window_list = g_list_remove(pan_window_list, pw);
4176
4177         if (pw->idle_id != -1)
4178                 {
4179                 g_source_remove(pw->idle_id);
4180                 }
4181
4182         pan_fullscreen_toggle(pw, TRUE);
4183         gtk_widget_destroy(pw->window);
4184
4185         pan_window_items_free(pw);
4186
4187         g_free(pw->path);
4188
4189         g_free(pw);
4190 }
4191
4192 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4193 {
4194         PanWindow *pw = data;
4195
4196         pan_window_close(pw);
4197         return TRUE;
4198 }
4199
4200 static void pan_window_new_real(const gchar *path)
4201 {
4202         PanWindow *pw;
4203         GtkWidget *vbox;
4204         GtkWidget *box;
4205         GtkWidget *combo;
4206         GtkWidget *hbox;
4207         GtkWidget *frame;
4208         GtkWidget *table;
4209         GdkGeometry geometry;
4210
4211         pw = g_new0(PanWindow, 1);
4212
4213         pw->path = g_strdup(path);
4214         pw->layout = LAYOUT_TIMELINE;
4215         pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4216         pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4217         pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4218         pw->list = NULL;
4219
4220         pw->fs = NULL;
4221         pw->overlay_id = -1;
4222         pw->idle_id = -1;
4223
4224         pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4225
4226         geometry.min_width = 8;
4227         geometry.min_height = 8;
4228         gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4229
4230         gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4231         gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
4232         gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4233         gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4234
4235         window_set_icon(pw->window, NULL, NULL);
4236
4237         vbox = gtk_vbox_new(FALSE, 0);
4238         gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4239         gtk_widget_show(vbox);
4240
4241         box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4242
4243         pref_spacer(box, 0);
4244         pref_label_new(box, _("Location:"));
4245         combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
4246                                                 pan_window_entry_activate_cb, pw);
4247         g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4248                          G_CALLBACK(pan_window_entry_change_cb), pw);
4249         gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4250         gtk_widget_show(combo);
4251
4252         combo = gtk_combo_box_new_text();
4253         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4254         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4255         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4256         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4257         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4258
4259         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4260         g_signal_connect(G_OBJECT(combo), "changed",
4261                          G_CALLBACK(pan_window_layout_change_cb), pw);
4262         gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4263         gtk_widget_show(combo);
4264
4265         combo = gtk_combo_box_new_text();
4266         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4267         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4268         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4269         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4270         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4271         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4272         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4273         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4274         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4275         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4276
4277         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4278         g_signal_connect(G_OBJECT(combo), "changed",
4279                          G_CALLBACK(pan_window_layout_size_cb), pw);
4280         gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4281         gtk_widget_show(combo);
4282
4283         table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4284         gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4285         gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4286
4287         pw->imd = image_new(TRUE);
4288         pw->imd_normal = pw->imd;
4289
4290         g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
4291                          G_CALLBACK(pan_window_image_zoom_cb), pw);
4292         g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
4293                          G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4294
4295         gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4296                          GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4297         gtk_widget_show(GTK_WIDGET(pw->imd->widget));
4298
4299         pan_window_dnd_init(pw);
4300
4301         pan_image_set_buttons(pw, pw->imd);
4302
4303         pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4304         g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4305                          G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4306         gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4307                          GTK_FILL | GTK_EXPAND, 0, 0, 0);
4308         gtk_widget_show(pw->scrollbar_h);
4309
4310         pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4311         g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4312                          G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4313         gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4314                          0, GTK_FILL | GTK_EXPAND, 0, 0);
4315         gtk_widget_show(pw->scrollbar_v);
4316
4317         /* find bar */
4318
4319         pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4320         gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4321
4322         pref_spacer(pw->search_box, 0);
4323         pref_label_new(pw->search_box, _("Find:"));
4324
4325         hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4326         gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4327         gtk_widget_show(hbox);
4328
4329         combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4330                                                 pan_search_activate_cb, pw);
4331         gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4332         gtk_widget_show(combo);
4333
4334         pw->search_label = gtk_label_new("");
4335         gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4336         gtk_widget_show(pw->search_label);
4337
4338         /* status bar */
4339
4340         box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4341
4342         frame = gtk_frame_new(NULL);
4343         gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4344         gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4345         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4346         gtk_widget_show(frame);
4347
4348         hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4349         gtk_container_add(GTK_CONTAINER(frame), hbox);
4350         gtk_widget_show(hbox);
4351
4352         pref_spacer(hbox, 0);
4353         pw->label_message = pref_label_new(hbox, "");
4354
4355         frame = gtk_frame_new(NULL);
4356         gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4357         gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4358         gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4359         gtk_widget_show(frame);
4360
4361         pw->label_zoom = gtk_label_new("");
4362         gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4363         gtk_widget_show(pw->label_zoom);
4364
4365         pw->search_button = gtk_toggle_button_new();
4366         gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4367         gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4368         hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4369         gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4370         gtk_widget_show(hbox);
4371         pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4372         gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4373         gtk_widget_show(pw->search_button_arrow);
4374         pref_label_new(hbox, _("Find"));
4375
4376         gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4377         gtk_widget_show(pw->search_button);
4378         g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4379                          G_CALLBACK(pan_search_toggle_cb), pw);
4380
4381         g_signal_connect(G_OBJECT(pw->window), "delete_event",
4382                          G_CALLBACK(pan_window_delete_cb), pw);
4383         g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4384                          G_CALLBACK(pan_window_key_press_cb), pw);
4385
4386         gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4387
4388         pan_window_layout_update_idle(pw);
4389
4390         gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4391         gtk_widget_show(pw->window);
4392
4393         pan_window_list = g_list_append(pan_window_list, pw);
4394 }
4395
4396 /*
4397  *-----------------------------------------------------------------------------
4398  * peformance warnings
4399  *-----------------------------------------------------------------------------
4400  */
4401
4402 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4403 {
4404         gchar *path = data;
4405
4406         generic_dialog_close(gd);
4407
4408         pan_window_new_real(path);
4409         g_free(path);
4410 }
4411
4412 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4413 {
4414         gint hide_dlg;
4415
4416         hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4417         pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4418 }
4419
4420 static gint pan_warning(const gchar *path)
4421 {
4422         GenericDialog *gd;
4423         GtkWidget *box;
4424         GtkWidget *group;
4425         GtkWidget *button;
4426         GtkWidget *ct_button;
4427         gint hide_dlg;
4428
4429         if (enable_thumb_caching &&
4430             thumbnail_spec_standard) return FALSE;
4431
4432         if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4433         if (hide_dlg) return FALSE;
4434
4435         gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4436                                 NULL, NULL);
4437         gd->data = g_strdup(path);
4438         generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4439                                   pan_warning_ok_cb, TRUE);
4440
4441         box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4442                                          _("Pan view performance may be poor."),
4443                                          _("To improve performance of thumbnails in the pan view the"
4444                                            " following options can be enabled. Note that both options"
4445                                            " must be enabled to notice a change in performance."));
4446
4447         group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4448         pref_spacer(group, PREF_PAD_INDENT);
4449         group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4450
4451         ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4452                                           enable_thumb_caching, &enable_thumb_caching);
4453         button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4454                                        thumbnail_spec_standard, &thumbnail_spec_standard);
4455         pref_checkbox_link_sensitivity(ct_button, button);
4456
4457         pref_line(box, 0);
4458
4459         pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4460                           G_CALLBACK(pan_warning_hide_cb), NULL);
4461
4462         gtk_widget_show(gd->dialog);
4463
4464         return TRUE;
4465 }
4466
4467
4468 /*
4469  *-----------------------------------------------------------------------------
4470  * public
4471  *-----------------------------------------------------------------------------
4472  */
4473
4474 void pan_window_new(const gchar *path)
4475 {
4476         if (pan_warning(path)) return;
4477
4478         pan_window_new_real(path);
4479 }
4480
4481 /*
4482  *-----------------------------------------------------------------------------
4483  * menus
4484  *-----------------------------------------------------------------------------
4485  */
4486
4487 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4488 {
4489         PanWindow *pw = data;
4490         const gchar *path;
4491
4492         path = pan_menu_click_path(pw);
4493         if (path)
4494                 {
4495                 pan_fullscreen_toggle(pw, TRUE);
4496                 view_window_new(path);
4497                 }
4498 }
4499
4500 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4501 {
4502         PanWindow *pw;
4503         const gchar *path;
4504         gint n;
4505
4506         pw = submenu_item_get_data(widget);
4507         n = GPOINTER_TO_INT(data);
4508         if (!pw) return;
4509
4510         path = pan_menu_click_path(pw);
4511         if (path)
4512                 {
4513                 pan_fullscreen_toggle(pw, TRUE);
4514                 start_editor_from_file(n, path);
4515                 }
4516 }
4517
4518 static void pan_info_cb(GtkWidget *widget, gpointer data)
4519 {
4520         PanWindow *pw = data;
4521         const gchar *path;
4522
4523         path = pan_menu_click_path(pw);
4524         if (path) info_window_new(path, NULL);
4525 }
4526
4527 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4528 {
4529         PanWindow *pw = data;
4530
4531         image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
4532 }
4533
4534 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4535 {
4536         PanWindow *pw = data;
4537
4538         image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
4539 }
4540
4541 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4542 {
4543         PanWindow *pw = data;
4544
4545         image_zoom_set(pw->imd, 1.0);
4546 }
4547
4548 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4549 {
4550         PanWindow *pw = data;
4551         const gchar *path;
4552
4553         path = pan_menu_click_path(pw);
4554         if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4555 }
4556
4557 static void pan_move_cb(GtkWidget *widget, gpointer data)
4558 {
4559         PanWindow *pw = data;
4560         const gchar *path;
4561
4562         path = pan_menu_click_path(pw);
4563         if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4564 }
4565
4566 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4567 {
4568         PanWindow *pw = data;
4569         const gchar *path;
4570
4571         path = pan_menu_click_path(pw);
4572         if (path) file_util_rename(path, NULL, pw->imd->widget);
4573 }
4574
4575 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4576 {
4577         PanWindow *pw = data;
4578         const gchar *path;
4579
4580         path = pan_menu_click_path(pw);
4581         if (path) file_util_delete(path, NULL, pw->imd->widget);
4582 }
4583
4584 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4585 {
4586         PanWindow *pw = data;
4587
4588         pan_fullscreen_toggle(pw, FALSE);
4589 }
4590
4591 static void pan_close_cb(GtkWidget *widget, gpointer data)
4592 {
4593         PanWindow *pw = data;
4594
4595         pan_window_close(pw);
4596 }
4597
4598 static GtkWidget *pan_popup_menu(PanWindow *pw)
4599 {
4600         GtkWidget *menu;
4601         GtkWidget *item;
4602         gint active;
4603
4604         active = (pw->click_pi != NULL);
4605
4606         menu = popup_menu_short_lived();
4607
4608         menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4609                             G_CALLBACK(pan_zoom_in_cb), pw);
4610         menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4611                             G_CALLBACK(pan_zoom_out_cb), pw);
4612         menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4613                             G_CALLBACK(pan_zoom_1_1_cb), pw);
4614         menu_item_add_divider(menu);
4615
4616         submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4617         gtk_widget_set_sensitive(item, active);
4618
4619         menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4620                                       G_CALLBACK(pan_info_cb), pw);
4621
4622         menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4623                                       G_CALLBACK(pan_new_window_cb), pw);
4624
4625         menu_item_add_divider(menu);
4626         menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4627                                       G_CALLBACK(pan_copy_cb), pw);
4628         menu_item_add_sensitive(menu, _("_Move..."), active,
4629                                 G_CALLBACK(pan_move_cb), pw);
4630         menu_item_add_sensitive(menu, _("_Rename..."), active,
4631                                 G_CALLBACK(pan_rename_cb), pw);
4632         menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4633                                       G_CALLBACK(pan_delete_cb), pw);
4634
4635         menu_item_add_divider(menu);
4636
4637         if (pw->fs)
4638                 {
4639                 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4640                 }
4641         else
4642                 {
4643                 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4644                 }
4645
4646         menu_item_add_divider(menu);
4647         menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4648
4649         return menu;
4650 }
4651
4652 /*
4653  *-----------------------------------------------------------------------------
4654  * drag and drop
4655  *-----------------------------------------------------------------------------
4656  */
4657
4658 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4659                                     gint x, gint y,
4660                                     GtkSelectionData *selection_data, guint info,
4661                                     guint time, gpointer data)
4662 {
4663         PanWindow *pw = data;
4664
4665         if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
4666
4667         if (info == TARGET_URI_LIST)
4668                 {
4669                 GList *list;
4670
4671                 list = uri_list_from_text(selection_data->data, TRUE);
4672                 if (list && isdir((gchar *)list->data))
4673                         {
4674                         gchar *path = list->data;
4675
4676                         g_free(pw->path);
4677                         pw->path = g_strdup(path);
4678
4679                         pan_window_layout_update_idle(pw);
4680                         }
4681
4682                 path_list_free(list);
4683                 }
4684 }
4685
4686 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4687                                     GtkSelectionData *selection_data, guint info,
4688                                     guint time, gpointer data)
4689 {
4690         PanWindow *pw = data;
4691         const gchar *path;
4692
4693         path = pan_menu_click_path(pw);
4694         if (path)
4695                 {
4696                 gchar *text = NULL;
4697                 gint len;
4698                 gint plain_text;
4699                 GList *list;
4700
4701                 switch (info)
4702                         {
4703                         case TARGET_URI_LIST:
4704                                 plain_text = FALSE;
4705                                 break;
4706                         case TARGET_TEXT_PLAIN:
4707                         default:
4708                                 plain_text = TRUE;
4709                                 break;
4710                         }
4711                 list = g_list_append(NULL, (gchar *)path);
4712                 text = uri_text_from_list(list, &len, plain_text);
4713                 g_list_free(list);
4714                 if (text)
4715                         {
4716                         gtk_selection_data_set (selection_data, selection_data->target,
4717                                                 8, text, len);
4718                         g_free(text);
4719                         }
4720                 }
4721         else
4722                 {
4723                 gtk_selection_data_set (selection_data, selection_data->target,
4724                                         8, NULL, 0);
4725                 }
4726 }
4727
4728 static void pan_window_dnd_init(PanWindow *pw)
4729 {
4730         GtkWidget *widget;
4731
4732         widget = pw->imd->pr;
4733
4734         gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
4735                             dnd_file_drag_types, dnd_file_drag_types_count,
4736                             GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4737         g_signal_connect(G_OBJECT(widget), "drag_data_get",
4738                          G_CALLBACK(pan_window_set_dnd_data), pw);
4739
4740         gtk_drag_dest_set(widget,
4741                           GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4742                           dnd_file_drop_types, dnd_file_drop_types_count,
4743                           GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4744         g_signal_connect(G_OBJECT(widget), "drag_data_received",
4745                          G_CALLBACK(pan_window_get_dnd_data), pw);
4746 }
4747
4748 /*
4749  *-----------------------------------------------------------------------------
4750  * maintenance (for rename, move, remove)
4751  *-----------------------------------------------------------------------------
4752  */
4753