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