comparison src/pan-view.c @ 12:147f4c4b9025

##### Note: GQview CVS on sourceforge is not always up to date, please use ##### ##### an offical release when making enhancements and translation updates. ##### Tue Mar 1 11:32:26 2005 John Ellis <johne@verizon.net> * src/Makefile.am: Add pan-view.[ch]: * image.[ch]: Add support for using a grid of tiles as soource image. Added scroll_notify callback for when the viewable regionis scrolled. Added ability to set min and max for the zoom range. Removed unnecessary gtk_widget_size_request from image_size_sync. Added image_scroll_to_point. * layout_util.c: Add menu item and callback for the new 'Pan view'. * pixbuf_util.c (pixbuf_draw_layout): Fix for when offset is non-zero. * typedefs.h: Add source tile stuff for ImageWindow. * ui_tabcomp.c: Fix tab completion pop-up menu placement. * pan-view.[ch]: New files for the pan view - 2.1 is officially started :)
author gqview
date Tue, 01 Mar 2005 17:16:34 +0000
parents
children ef790149ae21
comparison
equal deleted inserted replaced
11:3c3b40dbde11 12:147f4c4b9025
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 - PAN_FOLDER_BOX_BORDER;
1678 *scroll_y = pi->y - PAN_FOLDER_BOX_BORDER;
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 image_set_image_as_tiles(pw->imd, width, height,
2787 PAN_TILE_SIZE, PAN_TILE_SIZE, 8,
2788 pan_window_request_tile_cb,
2789 pan_window_dispose_tile_cb, pw, 1.0);
2790 image_scroll_to_point(pw->imd, scroll_x, scroll_y);
2791 }
2792
2793 pan_window_message(pw, NULL);
2794
2795 pw->idle_id = -1;
2796
2797 return FALSE;
2798 }
2799
2800 static void pan_window_layout_update_idle(PanWindow *pw)
2801 {
2802 if (pw->idle_id == -1)
2803 {
2804 pan_window_message(pw, _("Sorting images..."));
2805 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
2806 }
2807 }
2808
2809 /*
2810 *-----------------------------------------------------------------------------
2811 * pan window keyboard
2812 *-----------------------------------------------------------------------------
2813 */
2814
2815 static const gchar *pan_menu_click_path(PanWindow *pw)
2816 {
2817 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
2818 return NULL;
2819 }
2820
2821 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
2822 {
2823 PanWindow *pw = data;
2824 ImageWindow *imd;
2825
2826 imd = pan_window_active_image(pw);
2827 gdk_window_get_origin(imd->image->window, x, y);
2828 popup_menu_position_clamp(menu, x, y, 0);
2829 }
2830
2831 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
2832 {
2833 PanWindow *pw = data;
2834 ImageWindow *imd;
2835 const gchar *path;
2836 gint stop_signal = FALSE;
2837 GtkWidget *menu;
2838 gint x = 0;
2839 gint y = 0;
2840 gint focused;
2841
2842 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(pw->imd->widget));
2843
2844 imd = pan_window_active_image(pw);
2845 path = pan_menu_click_path(pw);
2846
2847 if (focused)
2848 {
2849 switch (event->keyval)
2850 {
2851 case GDK_Left: case GDK_KP_Left:
2852 x -= 1;
2853 stop_signal = TRUE;
2854 break;
2855 case GDK_Right: case GDK_KP_Right:
2856 x += 1;
2857 stop_signal = TRUE;
2858 break;
2859 case GDK_Up: case GDK_KP_Up:
2860 y -= 1;
2861 stop_signal = TRUE;
2862 break;
2863 case GDK_Down: case GDK_KP_Down:
2864 y += 1;
2865 stop_signal = TRUE;
2866 break;
2867 case GDK_Page_Up: case GDK_KP_Page_Up:
2868 image_scroll(imd, 0, 0-imd->vis_height / 2);
2869 break;
2870 case GDK_Page_Down: case GDK_KP_Page_Down:
2871 image_scroll(imd, 0, imd->vis_height / 2);
2872 break;
2873 case GDK_Home: case GDK_KP_Home:
2874 image_scroll(imd, 0-imd->vis_width / 2, 0);
2875 break;
2876 case GDK_End: case GDK_KP_End:
2877 image_scroll(imd, imd->vis_width / 2, 0);
2878 break;
2879 }
2880 }
2881
2882 if (focused && !(event->state & GDK_CONTROL_MASK) )
2883 switch (event->keyval)
2884 {
2885 case '+': case '=': case GDK_KP_Add:
2886 image_zoom_adjust(imd, ZOOM_INCREMENT);
2887 break;
2888 case '-': case GDK_KP_Subtract:
2889 image_zoom_adjust(imd, -ZOOM_INCREMENT);
2890 break;
2891 case 'Z': case 'z': case GDK_KP_Divide: case '1':
2892 image_zoom_set(imd, 1.0);
2893 break;
2894 case '2':
2895 image_zoom_set(imd, 2.0);
2896 break;
2897 case '3':
2898 image_zoom_set(imd, 3.0);
2899 break;
2900 case '4':
2901 image_zoom_set(imd, 4.0);
2902 break;
2903 case '7':
2904 image_zoom_set(imd, -4.0);
2905 break;
2906 case '8':
2907 image_zoom_set(imd, -3.0);
2908 break;
2909 case '9':
2910 image_zoom_set(imd, -2.0);
2911 break;
2912 case 'F': case 'f':
2913 case 'V': case 'v':
2914 pan_fullscreen_toggle(pw, FALSE);
2915 stop_signal = TRUE;
2916 break;
2917 case 'I': case 'i':
2918 pan_overlay_toggle(pw);
2919 break;
2920 case GDK_Delete: case GDK_KP_Delete:
2921 break;
2922 case '/':
2923 if (!pw->fs)
2924 {
2925 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
2926 stop_signal = TRUE;
2927 }
2928 break;
2929 case GDK_Escape:
2930 if (pw->fs)
2931 {
2932 pan_fullscreen_toggle(pw, TRUE);
2933 stop_signal = TRUE;
2934 }
2935 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
2936 {
2937 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
2938 stop_signal = TRUE;
2939 }
2940 break;
2941 case GDK_Menu:
2942 case GDK_F10:
2943 menu = pan_popup_menu(pw);
2944 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
2945 stop_signal = TRUE;
2946 break;
2947 }
2948
2949 if (event->state & GDK_CONTROL_MASK)
2950 {
2951 gint n = -1;
2952 switch (event->keyval)
2953 {
2954 case '1':
2955 n = 0;
2956 break;
2957 case '2':
2958 n = 1;
2959 break;
2960 case '3':
2961 n = 2;
2962 break;
2963 case '4':
2964 n = 3;
2965 break;
2966 case '5':
2967 n = 4;
2968 break;
2969 case '6':
2970 n = 5;
2971 break;
2972 case '7':
2973 n = 6;
2974 break;
2975 case '8':
2976 n = 7;
2977 break;
2978 case '9':
2979 n = 8;
2980 break;
2981 case '0':
2982 n = 9;
2983 break;
2984 case 'C': case 'c':
2985 if (path) file_util_copy(path, NULL, NULL, imd->widget);
2986 break;
2987 case 'M': case 'm':
2988 if (path) file_util_move(path, NULL, NULL, imd->widget);
2989 break;
2990 case 'R': case 'r':
2991 if (path) file_util_rename(path, NULL, imd->widget);
2992 break;
2993 case 'D': case 'd':
2994 if (path) file_util_delete(path, NULL, imd->widget);
2995 break;
2996 case 'P': case 'p':
2997 if (path) info_window_new(path, NULL);
2998 break;
2999 case 'W': case 'w':
3000 pan_window_close(pw);
3001 break;
3002 }
3003 if (n != -1 && path)
3004 {
3005 pan_fullscreen_toggle(pw, TRUE);
3006 start_editor_from_file(n, path);
3007 stop_signal = TRUE;
3008 }
3009 }
3010 else if (event->state & GDK_SHIFT_MASK)
3011 {
3012 x *= 3;
3013 y *= 3;
3014 }
3015 else if (!focused)
3016 {
3017 switch (event->keyval)
3018 {
3019 case GDK_Escape:
3020 if (pw->fs)
3021 {
3022 pan_fullscreen_toggle(pw, TRUE);
3023 stop_signal = TRUE;
3024 }
3025 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3026 {
3027 gtk_widget_grab_focus(pw->imd->widget);
3028 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3029 stop_signal = TRUE;
3030 }
3031 break;
3032 default:
3033 break;
3034 }
3035 }
3036
3037 if (x != 0 || y!= 0)
3038 {
3039 keyboard_scroll_calc(&x, &y, event);
3040 image_scroll(imd, x, y);
3041 }
3042
3043 return stop_signal;
3044 }
3045
3046 /*
3047 *-----------------------------------------------------------------------------
3048 * info popup
3049 *-----------------------------------------------------------------------------
3050 */
3051
3052 static void pan_info_update(PanWindow *pw, PanItem *pi)
3053 {
3054 PanItem *pbox;
3055 PanItem *plabel;
3056 PanItem *p;
3057 gchar *buf;
3058 gint x1, y1, x2, y2, x3, y3;
3059 gint x, y, w, h;
3060
3061 if (pw->click_pi == pi) return;
3062 if (pi && !pi->fd) pi = NULL;
3063
3064 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3065 pw->click_pi = pi;
3066
3067 if (!pi) return;
3068
3069 printf("info set to %s\n", pi->fd->path);
3070
3071 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3072 PAN_POPUP_BORDER,
3073 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3074 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3075 pan_item_set_key(pbox, "info");
3076
3077 if (pi->type == ITEM_THUMB && pi->pixbuf)
3078 {
3079 w = gdk_pixbuf_get_width(pi->pixbuf);
3080 h = gdk_pixbuf_get_height(pi->pixbuf);
3081
3082 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3083 y1 = pi->y + (pi->height - h) / 2 + 8;
3084 }
3085 else
3086 {
3087 x1 = pi->x + pi->width - 8;
3088 y1 = pi->y + 8;
3089 }
3090
3091 x2 = pbox->x + 1;
3092 y2 = pbox->y + 36;
3093 x3 = pbox->x + 1;
3094 y3 = pbox->y + 12;
3095 triangle_rect_region(x1, y1, x2, y2, x3, y3,
3096 &x, &y, &w, &h);
3097
3098 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3099 x1, y1, x2, y2, x3, y3,
3100 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3101 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3102 pan_item_set_key(p, "info");
3103 pan_item_added(pw, p);
3104
3105 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3106 _("Filename:"), TEXT_ATTR_BOLD,
3107 PAN_POPUP_TEXT_COLOR, 255);
3108 pan_item_set_key(plabel, "info");
3109 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3110 pi->fd->name, TEXT_ATTR_NONE,
3111 PAN_POPUP_TEXT_COLOR, 255);
3112 pan_item_set_key(p, "info");
3113 pan_item_size_by_item(pbox, p, 0);
3114
3115 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3116 _("Date:"), TEXT_ATTR_BOLD,
3117 PAN_POPUP_TEXT_COLOR, 255);
3118 pan_item_set_key(plabel, "info");
3119 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3120 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3121 PAN_POPUP_TEXT_COLOR, 255);
3122 pan_item_set_key(p, "info");
3123 pan_item_size_by_item(pbox, p, 0);
3124
3125 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3126 _("Size:"), TEXT_ATTR_BOLD,
3127 PAN_POPUP_TEXT_COLOR, 255);
3128 pan_item_set_key(plabel, "info");
3129 buf = text_from_size(pi->fd->size);
3130 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3131 buf, TEXT_ATTR_NONE,
3132 PAN_POPUP_TEXT_COLOR, 255);
3133 g_free(buf);
3134 pan_item_set_key(p, "info");
3135 pan_item_size_by_item(pbox, p, 0);
3136
3137 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3138 pan_item_added(pw, pbox);
3139 }
3140
3141
3142 /*
3143 *-----------------------------------------------------------------------------
3144 * search
3145 *-----------------------------------------------------------------------------
3146 */
3147
3148 static void pan_search_status(PanWindow *pw, const gchar *text)
3149 {
3150 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3151 }
3152
3153 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3154 {
3155 PanItem *pi;
3156 ItemType type;
3157
3158 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3159
3160 pi = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3161 if (!pi) return FALSE;
3162
3163 pan_info_update(pw, pi);
3164 image_scroll_to_point(pw->imd, pi->x - PAN_THUMB_GAP, pi->y - PAN_THUMB_GAP);
3165
3166 pan_search_status(pw, (path[0] == '/') ? _("path found") : _("filename found"));
3167
3168 return TRUE;
3169 }
3170
3171 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3172 {
3173 PanItem *pi;
3174 ItemType type;
3175
3176 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3177
3178 pi = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3179 if (!pi) pi = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3180 if (!pi)
3181 {
3182 gchar *needle;
3183
3184 needle = g_utf8_strdown(text, -1);
3185 pi = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3186 g_free(needle);
3187 }
3188 if (!pi) return FALSE;
3189
3190 pan_info_update(pw, pi);
3191 image_scroll_to_point(pw->imd, pi->x - PAN_THUMB_GAP, pi->y - PAN_THUMB_GAP);
3192
3193 pan_search_status(pw, _("partial match"));
3194
3195 return TRUE;
3196 }
3197
3198 static gint valid_date_separator(gchar c)
3199 {
3200 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3201 }
3202
3203 static PanItem *pan_search_by_date_val(PanWindow *pw, gint year, gint month, gint day)
3204 {
3205 GList *work;
3206
3207 work = g_list_last(pw->list);
3208 while (work)
3209 {
3210 PanItem *pi;
3211
3212 pi = work->data;
3213 work = work->prev;
3214
3215 if (pi->fd)
3216 {
3217 struct tm *tl;
3218
3219 tl = localtime(&pi->fd->date);
3220 if (tl)
3221 {
3222 gint match;
3223
3224 match = (tl->tm_year == year - 1900);
3225 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3226 if (match && day > 0) match = (tl->tm_mday == day);
3227
3228 if (match) return pi;
3229 }
3230 }
3231 }
3232
3233 return NULL;
3234 }
3235
3236 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3237 {
3238 PanItem *pi;
3239 gint year;
3240 gint month = -1;
3241 gint day = -1;
3242 gchar *ptr;
3243 gchar *mptr;
3244 struct tm *lt;
3245 time_t t;
3246 gchar *message;
3247 gchar *buf;
3248
3249 if (!text) return FALSE;
3250
3251 ptr = (gchar *)text;
3252 while (*ptr != '\0')
3253 {
3254 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3255 ptr++;
3256 }
3257
3258 t = time(NULL);
3259 if (t == -1) return FALSE;
3260 lt = localtime(&t);
3261 if (!lt) return FALSE;
3262
3263 if (valid_date_separator(*text))
3264 {
3265 year = -1;
3266 mptr = (gchar *)text;
3267 }
3268 else
3269 {
3270 year = (gint)strtol(text, &mptr, 10);
3271 if (mptr == text) return FALSE;
3272 }
3273
3274 if (*mptr != '\0' && valid_date_separator(*mptr))
3275 {
3276 gchar *dptr;
3277
3278 mptr++;
3279 month = strtol(mptr, &dptr, 10);
3280 if (dptr == mptr)
3281 {
3282 if (valid_date_separator(*dptr))
3283 {
3284 month = lt->tm_mon + 1;
3285 dptr++;
3286 }
3287 else
3288 {
3289 month = -1;
3290 }
3291 }
3292 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3293 {
3294 gchar *eptr;
3295 dptr++;
3296 day = strtol(dptr, &eptr, 10);
3297 if (dptr == eptr)
3298 {
3299 day = lt->tm_mday;
3300 }
3301 }
3302 }
3303
3304 if (year == -1)
3305 {
3306 year = lt->tm_year + 1900;
3307 }
3308 else if (year < 100)
3309 {
3310 if (year > 70)
3311 year+= 1900;
3312 else
3313 year+= 2000;
3314 }
3315
3316 if (year < 1970 ||
3317 month < -1 || month == 0 || month > 12 ||
3318 day < -1 || day == 0 || day > 31) return FALSE;
3319
3320 t = date_to_time(year, month, day);
3321 if (t < 0) return FALSE;
3322
3323 pi = pan_search_by_date_val(pw, year, month, day);
3324 if (pi)
3325 {
3326 pan_info_update(pw, pi);
3327 image_scroll_to_point(pw->imd, pi->x - PAN_THUMB_GAP, pi->y - PAN_THUMB_GAP);
3328 }
3329
3330 if (month > 0)
3331 {
3332 buf = date_value_string(t, DATE_LENGTH_MONTH);
3333 if (day > 0)
3334 {
3335 gchar *tmp;
3336 tmp = buf;
3337 buf = g_strdup_printf("%d %s", day, tmp);
3338 g_free(tmp);
3339 }
3340 }
3341 else
3342 {
3343 buf = date_value_string(t, DATE_LENGTH_YEAR);
3344 }
3345 message = g_strdup_printf("%s%s%s%s %s",
3346 (pi) ? "" : "(", (pi) ? "" : _("no match"), (pi) ? "" : ") " ,
3347 _("Date:"), buf);
3348 g_free(buf);
3349 pan_search_status(pw, message);
3350 g_free(message);
3351
3352 return TRUE;
3353 }
3354
3355 static void pan_search_activate_cb(const gchar *text, gpointer data)
3356 {
3357 PanWindow *pw = data;
3358
3359 if (!text) return;
3360
3361 tab_completion_append_to_history(pw->search_entry, text);
3362
3363 if (pan_search_by_path(pw, text)) return;
3364
3365 if (pw->layout == LAYOUT_TIMELINE && pan_search_by_date(pw, text)) return;
3366
3367 if (pan_search_by_partial(pw, text)) return;
3368
3369 pan_search_status(pw, _("no match"));
3370 }
3371
3372 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3373 {
3374 PanWindow *pw = data;
3375 gint visible;
3376
3377 visible = GTK_WIDGET_VISIBLE(pw->search_box);
3378 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3379
3380 if (visible)
3381 {
3382 gtk_widget_hide(pw->search_box);
3383 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3384 }
3385 else
3386 {
3387 gtk_widget_show(pw->search_box);
3388 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3389 gtk_widget_grab_focus(pw->search_entry);
3390 }
3391 }
3392
3393
3394 /*
3395 *-----------------------------------------------------------------------------
3396 * view window main routines
3397 *-----------------------------------------------------------------------------
3398 */
3399
3400 static void button_cb(ImageWindow *imd, gint button, guint32 time,
3401 gdouble x, gdouble y, guint state, gpointer data)
3402 {
3403 PanWindow *pw = data;
3404 PanItem *pi = NULL;
3405 GtkWidget *menu;
3406
3407 if (pw->imd->scale)
3408 {
3409 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3410 (gint)((double)(pw->imd->x_scroll + x - pw->imd->x_offset) / pw->imd->scale),
3411 (gint)((double)(pw->imd->y_scroll + y - pw->imd->y_offset) / pw->imd->scale));
3412 }
3413
3414 switch (button)
3415 {
3416 case 1:
3417 pan_info_update(pw, pi);
3418 break;
3419 case 2:
3420 break;
3421 case 3:
3422 pan_info_update(pw, pi);
3423 menu = pan_popup_menu(pw);
3424 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, time);
3425 break;
3426 default:
3427 break;
3428 }
3429 }
3430
3431 static void scroll_cb(ImageWindow *imd, GdkScrollDirection direction, guint32 time,
3432 gdouble x, gdouble y, guint state, gpointer data)
3433 {
3434 #if 0
3435 PanWindow *pw = data;
3436 #endif
3437
3438 if (state & GDK_CONTROL_MASK)
3439 {
3440 switch (direction)
3441 {
3442 case GDK_SCROLL_UP:
3443 image_zoom_adjust_at_point(imd, ZOOM_INCREMENT, x, y);
3444 break;
3445 case GDK_SCROLL_DOWN:
3446 image_zoom_adjust_at_point(imd, -ZOOM_INCREMENT, x, y);
3447 break;
3448 default:
3449 break;
3450 }
3451 }
3452 else if ( (state & GDK_SHIFT_MASK) != (mousewheel_scrolls))
3453 {
3454 switch (direction)
3455 {
3456 case GDK_SCROLL_UP:
3457 image_scroll(imd, 0, -MOUSEWHEEL_SCROLL_SIZE);
3458 break;
3459 case GDK_SCROLL_DOWN:
3460 image_scroll(imd, 0, MOUSEWHEEL_SCROLL_SIZE);
3461 break;
3462 case GDK_SCROLL_LEFT:
3463 image_scroll(imd, -MOUSEWHEEL_SCROLL_SIZE, 0);
3464 break;
3465 case GDK_SCROLL_RIGHT:
3466 image_scroll(imd, MOUSEWHEEL_SCROLL_SIZE, 0);
3467 break;
3468 default:
3469 break;
3470 }
3471 }
3472 else
3473 {
3474 switch (direction)
3475 {
3476 case GDK_SCROLL_UP:
3477 break;
3478 case GDK_SCROLL_DOWN:
3479 break;
3480 default:
3481 break;
3482 }
3483 }
3484 }
3485
3486 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
3487 {
3488 image_set_button_func(imd, button_cb, pw);
3489 image_set_scroll_func(imd, scroll_cb, pw);
3490 }
3491
3492 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
3493 {
3494 PanWindow *pw = data;
3495
3496 pw->fs = NULL;
3497 }
3498
3499 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
3500 {
3501 if (force_off && !pw->fs) return;
3502
3503 if (pw->fs)
3504 {
3505 fullscreen_stop(pw->fs);
3506 pw->imd = pw->imd_normal;
3507 }
3508 else
3509 {
3510 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
3511
3512 pan_image_set_buttons(pw, pw->fs->imd);
3513 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
3514 G_CALLBACK(pan_window_key_press_cb), pw);
3515
3516 pw->imd = pw->fs->imd;
3517 }
3518 }
3519
3520 static void pan_overlay_toggle(PanWindow *pw)
3521 {
3522 ImageWindow *imd;
3523
3524 imd = pan_window_active_image(pw);
3525 #if 0
3526 if (pw->overlay_id == -1)
3527 {
3528 pw->overlay_id = image_overlay_info_enable(imd);
3529 }
3530 else
3531 {
3532 image_overlay_info_disable(imd, pw->overlay_id);
3533 pw->overlay_id = -1;
3534 }
3535 #endif
3536 }
3537
3538 static void pan_window_image_update_cb(ImageWindow *imd, gpointer data)
3539 {
3540 PanWindow *pw = data;
3541 gchar *text;
3542
3543 text = image_zoom_get_as_text(imd);
3544 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
3545 g_free(text);
3546 }
3547
3548 static void pan_window_image_scroll_notify_cb(ImageWindow *imd, gint x, gint y,
3549 gint width, gint height, gpointer data)
3550 {
3551 PanWindow *pw = data;
3552 GtkAdjustment *adj;
3553
3554 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
3555 adj->page_size = (gdouble)imd->vis_width / imd->scale;
3556 adj->page_increment = adj->page_size / 2.0;
3557 adj->step_increment = 48.0 / imd->scale;
3558 adj->lower = 0.0;
3559 adj->upper = MAX((gdouble)width + adj->page_size, 1.0);
3560 adj->value = (gdouble)x;
3561
3562 pref_signal_block_data(pw->scrollbar_h, pw);
3563 gtk_adjustment_changed(adj);
3564 pref_signal_unblock_data(pw->scrollbar_h, pw);
3565
3566 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
3567 adj->page_size = (gdouble)imd->vis_height / imd->scale;
3568 adj->page_increment = adj->page_size / 2.0;
3569 adj->step_increment = 48.0 / imd->scale;
3570 adj->lower = 0.0;
3571 adj->upper = MAX((gdouble)height + adj->page_size, 1.0);
3572 adj->value = (gdouble)y;
3573
3574 pref_signal_block_data(pw->scrollbar_v, pw);
3575 gtk_adjustment_changed(adj);
3576 pref_signal_unblock_data(pw->scrollbar_v, pw);
3577
3578 // printf("scrolled to %d,%d @ %d x %d\n", x, y, width, height);
3579 }
3580
3581 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
3582 {
3583 PanWindow *pw = data;
3584 gint x;
3585
3586 if (!pw->imd->scale) return;
3587
3588 x = (gint)gtk_range_get_value(range);
3589
3590 image_scroll_to_point(pw->imd, x, (gint)((gdouble)pw->imd->y_scroll / pw->imd->scale));
3591 }
3592
3593 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
3594 {
3595 PanWindow *pw = data;
3596 gint y;
3597
3598 if (!pw->imd->scale) return;
3599
3600 y = (gint)gtk_range_get_value(range);
3601
3602 image_scroll_to_point(pw->imd, (gint)((gdouble)pw->imd->x_scroll / pw->imd->scale), y);
3603 }
3604
3605 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
3606 {
3607 PanWindow *pw = data;
3608
3609 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3610 pan_window_layout_update_idle(pw);
3611 }
3612
3613 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
3614 {
3615 PanWindow *pw = data;
3616
3617 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3618 pan_window_layout_update_idle(pw);
3619 }
3620
3621 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
3622 {
3623 PanWindow *pw = data;
3624 gchar *path;
3625
3626 path = remove_trailing_slash(new_text);
3627 parse_out_relatives(path);
3628
3629 if (!isdir(path))
3630 {
3631 warning_dialog(_("Folder not found"),
3632 _("The entered path is not a folder"),
3633 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
3634 return;
3635 }
3636
3637 tab_completion_append_to_history(pw->path_entry, path);
3638
3639 g_free(pw->path);
3640 pw->path = g_strdup(path);
3641
3642 pan_window_layout_update_idle(pw);
3643 }
3644
3645 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
3646 {
3647 PanWindow *pw = data;
3648 gchar *text;
3649
3650 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
3651
3652 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
3653 pan_window_entry_activate_cb(text, pw);
3654 g_free(text);
3655 }
3656
3657 static void pan_window_close(PanWindow *pw)
3658 {
3659 pan_window_list = g_list_remove(pan_window_list, pw);
3660
3661 if (pw->idle_id != -1)
3662 {
3663 g_source_remove(pw->idle_id);
3664 }
3665
3666 pan_fullscreen_toggle(pw, TRUE);
3667 gtk_widget_destroy(pw->window);
3668
3669 pan_window_items_free(pw);
3670
3671 g_free(pw->path);
3672
3673 g_free(pw);
3674 }
3675
3676 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
3677 {
3678 PanWindow *pw = data;
3679
3680 pan_window_close(pw);
3681 return TRUE;
3682 }
3683
3684 void pan_window_new(const gchar *path)
3685 {
3686 PanWindow *pw;
3687 GtkWidget *vbox;
3688 GtkWidget *box;
3689 GtkWidget *combo;
3690 GtkWidget *hbox;
3691 GtkWidget *frame;
3692 GtkWidget *table;
3693 GdkGeometry geometry;
3694
3695 pw = g_new0(PanWindow, 1);
3696
3697 pw->path = g_strdup(path);
3698 pw->layout = LAYOUT_TIMELINE;
3699 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
3700 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
3701 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
3702 pw->list = NULL;
3703
3704 pw->fs = NULL;
3705 pw->overlay_id = -1;
3706 pw->idle_id = -1;
3707
3708 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3709
3710 geometry.min_width = 8;
3711 geometry.min_height = 8;
3712 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
3713
3714 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
3715 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
3716 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
3717 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
3718
3719 window_set_icon(pw->window, NULL, NULL);
3720
3721 vbox = gtk_vbox_new(FALSE, 0);
3722 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
3723 gtk_widget_show(vbox);
3724
3725 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3726
3727 pref_spacer(box, 0);
3728 pref_label_new(box, _("Location:"));
3729 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
3730 pan_window_entry_activate_cb, pw);
3731 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
3732 G_CALLBACK(pan_window_entry_change_cb), pw);
3733 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
3734 gtk_widget_show(combo);
3735
3736 combo = gtk_combo_box_new_text();
3737 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
3738 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
3739 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
3740 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
3741
3742 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
3743 g_signal_connect(G_OBJECT(combo), "changed",
3744 G_CALLBACK(pan_window_layout_change_cb), pw);
3745 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
3746 gtk_widget_show(combo);
3747
3748 combo = gtk_combo_box_new_text();
3749 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
3750 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
3751 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
3752 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
3753 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
3754 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
3755 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
3756 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
3757 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
3758
3759 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
3760 g_signal_connect(G_OBJECT(combo), "changed",
3761 G_CALLBACK(pan_window_layout_size_cb), pw);
3762 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
3763 gtk_widget_show(combo);
3764
3765 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
3766 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
3767 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
3768
3769 pw->imd = image_new(TRUE);
3770 pw->imd_normal = pw->imd;
3771
3772 if (black_window_background) image_background_set_black(pw->imd, TRUE);
3773 image_set_update_func(pw->imd, pan_window_image_update_cb, pw);
3774
3775 image_set_scroll_notify_func(pw->imd, pan_window_image_scroll_notify_cb, pw);
3776
3777 #if 0
3778 gtk_box_pack_start(GTK_BOX(vbox), pw->imd->widget, TRUE, TRUE, 0);
3779 #endif
3780 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
3781 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
3782 gtk_widget_show(pw->imd->widget);
3783
3784 pan_window_dnd_init(pw);
3785
3786 pan_image_set_buttons(pw, pw->imd);
3787
3788 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
3789 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
3790 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
3791 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
3792 GTK_FILL | GTK_EXPAND, 0, 0, 0);
3793 gtk_widget_show(pw->scrollbar_h);
3794
3795 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
3796 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
3797 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
3798 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
3799 0, GTK_FILL | GTK_EXPAND, 0, 0);
3800 gtk_widget_show(pw->scrollbar_v);
3801
3802 /* find bar */
3803
3804 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
3805 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
3806
3807 pref_spacer(pw->search_box, 0);
3808 pref_label_new(pw->search_box, _("Find:"));
3809
3810 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
3811 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
3812 gtk_widget_show(hbox);
3813
3814 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
3815 pan_search_activate_cb, pw);
3816 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
3817 gtk_widget_show(combo);
3818
3819 pw->search_label = gtk_label_new("");
3820 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
3821 gtk_widget_show(pw->search_label);
3822
3823 /* status bar */
3824
3825 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3826
3827 frame = gtk_frame_new(NULL);
3828 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
3829 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
3830 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
3831 gtk_widget_show(frame);
3832
3833 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
3834 gtk_container_add(GTK_CONTAINER(frame), hbox);
3835 gtk_widget_show(hbox);
3836
3837 pref_spacer(hbox, PREF_PAD_SPACE);
3838 pw->label_message = pref_label_new(hbox, "");
3839
3840 frame = gtk_frame_new(NULL);
3841 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
3842 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
3843 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
3844 gtk_widget_show(frame);
3845
3846 pw->label_zoom = gtk_label_new("");
3847 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
3848 gtk_widget_show(pw->label_zoom);
3849
3850 pw->search_button = gtk_toggle_button_new();
3851 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
3852 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
3853 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
3854 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
3855 gtk_widget_show(hbox);
3856 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
3857 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
3858 gtk_widget_show(pw->search_button_arrow);
3859 pref_label_new(hbox, _("Find"));
3860
3861 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
3862 gtk_widget_show(pw->search_button);
3863 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
3864 G_CALLBACK(pan_search_toggle_cb), pw);
3865
3866 g_signal_connect(G_OBJECT(pw->window), "delete_event",
3867 G_CALLBACK(pan_window_delete_cb), pw);
3868 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
3869 G_CALLBACK(pan_window_key_press_cb), pw);
3870
3871 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
3872
3873 pan_window_layout_update_idle(pw);
3874
3875 gtk_widget_grab_focus(pw->imd->widget);
3876 gtk_widget_show(pw->window);
3877
3878 pan_window_list = g_list_append(pan_window_list, pw);
3879 }
3880
3881 /*
3882 *-----------------------------------------------------------------------------
3883 * public
3884 *-----------------------------------------------------------------------------
3885 */
3886
3887 /*
3888 *-----------------------------------------------------------------------------
3889 * view window menu routines and callbacks
3890 *-----------------------------------------------------------------------------
3891 */
3892
3893 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
3894 {
3895 PanWindow *pw = data;
3896 const gchar *path;
3897
3898 path = pan_menu_click_path(pw);
3899 if (path)
3900 {
3901 pan_fullscreen_toggle(pw, TRUE);
3902 view_window_new(path);
3903 }
3904 }
3905
3906 static void pan_edit_cb(GtkWidget *widget, gpointer data)
3907 {
3908 PanWindow *pw;
3909 const gchar *path;
3910 gint n;
3911
3912 pw = submenu_item_get_data(widget);
3913 n = GPOINTER_TO_INT(data);
3914 if (!pw) return;
3915
3916 path = pan_menu_click_path(pw);
3917 if (path)
3918 {
3919 pan_fullscreen_toggle(pw, TRUE);
3920 start_editor_from_file(n, path);
3921 }
3922 }
3923
3924 static void pan_info_cb(GtkWidget *widget, gpointer data)
3925 {
3926 PanWindow *pw = data;
3927 const gchar *path;
3928
3929 path = pan_menu_click_path(pw);
3930 if (path) info_window_new(path, NULL);
3931 }
3932
3933 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
3934 {
3935 PanWindow *pw = data;
3936
3937 image_zoom_adjust(pan_window_active_image(pw), ZOOM_INCREMENT);
3938 }
3939
3940 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
3941 {
3942 PanWindow *pw = data;
3943
3944 image_zoom_adjust(pan_window_active_image(pw), -ZOOM_INCREMENT);
3945 }
3946
3947 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
3948 {
3949 PanWindow *pw = data;
3950
3951 image_zoom_set(pan_window_active_image(pw), 1.0);
3952 }
3953
3954 static void pan_copy_cb(GtkWidget *widget, gpointer data)
3955 {
3956 PanWindow *pw = data;
3957 const gchar *path;
3958
3959 path = pan_menu_click_path(pw);
3960 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
3961 }
3962
3963 static void pan_move_cb(GtkWidget *widget, gpointer data)
3964 {
3965 PanWindow *pw = data;
3966 const gchar *path;
3967
3968 path = pan_menu_click_path(pw);
3969 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
3970 }
3971
3972 static void pan_rename_cb(GtkWidget *widget, gpointer data)
3973 {
3974 PanWindow *pw = data;
3975 const gchar *path;
3976
3977 path = pan_menu_click_path(pw);
3978 if (path) file_util_rename(path, NULL, pw->imd->widget);
3979 }
3980
3981 static void pan_delete_cb(GtkWidget *widget, gpointer data)
3982 {
3983 PanWindow *pw = data;
3984 const gchar *path;
3985
3986 path = pan_menu_click_path(pw);
3987 if (path) file_util_delete(path, NULL, pw->imd->widget);
3988 }
3989
3990 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
3991 {
3992 PanWindow *pw = data;
3993
3994 pan_fullscreen_toggle(pw, FALSE);
3995 }
3996
3997 static void pan_close_cb(GtkWidget *widget, gpointer data)
3998 {
3999 PanWindow *pw = data;
4000
4001 pan_window_close(pw);
4002 }
4003
4004 static GtkWidget *pan_popup_menu(PanWindow *pw)
4005 {
4006 GtkWidget *menu;
4007 GtkWidget *item;
4008 gint active;
4009
4010 active = (pw->click_pi != NULL);
4011
4012 menu = popup_menu_short_lived();
4013
4014 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4015 G_CALLBACK(pan_zoom_in_cb), pw);
4016 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4017 G_CALLBACK(pan_zoom_out_cb), pw);
4018 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4019 G_CALLBACK(pan_zoom_1_1_cb), pw);
4020 menu_item_add_divider(menu);
4021
4022 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4023 gtk_widget_set_sensitive(item, active);
4024
4025 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4026 G_CALLBACK(pan_info_cb), pw);
4027
4028 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4029 G_CALLBACK(pan_new_window_cb), pw);
4030
4031 menu_item_add_divider(menu);
4032 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4033 G_CALLBACK(pan_copy_cb), pw);
4034 menu_item_add_sensitive(menu, _("_Move..."), active,
4035 G_CALLBACK(pan_move_cb), pw);
4036 menu_item_add_sensitive(menu, _("_Rename..."), active,
4037 G_CALLBACK(pan_rename_cb), pw);
4038 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4039 G_CALLBACK(pan_delete_cb), pw);
4040
4041 menu_item_add_divider(menu);
4042
4043 if (pw->fs)
4044 {
4045 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4046 }
4047 else
4048 {
4049 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4050 }
4051
4052 menu_item_add_divider(menu);
4053 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4054
4055 return menu;
4056 }
4057
4058 /*
4059 *-----------------------------------------------------------------------------
4060 * image drag and drop routines
4061 *-----------------------------------------------------------------------------
4062 */
4063
4064 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4065 gint x, gint y,
4066 GtkSelectionData *selection_data, guint info,
4067 guint time, gpointer data)
4068 {
4069 PanWindow *pw = data;
4070 ImageWindow *imd;
4071
4072 if (gtk_drag_get_source_widget(context) == pw->imd->image) return;
4073
4074 imd = pw->imd;
4075
4076 if (info == TARGET_URI_LIST)
4077 {
4078 GList *list;
4079
4080 list = uri_list_from_text(selection_data->data, TRUE);
4081 if (list && isdir((gchar *)list->data))
4082 {
4083 printf("FIXME: change to this folder: %s\n", (gchar *)list->data);
4084 }
4085
4086 path_list_free(list);
4087 }
4088 }
4089
4090 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4091 GtkSelectionData *selection_data, guint info,
4092 guint time, gpointer data)
4093 {
4094 printf("FIXME: set dnd data\n");
4095 }
4096
4097 static void pan_window_dnd_init(PanWindow *pw)
4098 {
4099 ImageWindow *imd;
4100
4101 imd = pw->imd;
4102
4103 gtk_drag_source_set(imd->image, GDK_BUTTON2_MASK,
4104 dnd_file_drag_types, dnd_file_drag_types_count,
4105 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4106 g_signal_connect(G_OBJECT(imd->image), "drag_data_get",
4107 G_CALLBACK(pan_window_set_dnd_data), pw);
4108
4109 gtk_drag_dest_set(imd->image,
4110 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4111 dnd_file_drop_types, dnd_file_drop_types_count,
4112 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4113 g_signal_connect(G_OBJECT(imd->image), "drag_data_received",
4114 G_CALLBACK(pan_window_get_dnd_data), pw);
4115 }
4116
4117 /*
4118 *-----------------------------------------------------------------------------
4119 * maintenance (for rename, move, remove)
4120 *-----------------------------------------------------------------------------
4121 */
4122