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!
20 #include "fullscreen.h"
22 #include "image-load.h"
26 #include "pixbuf_util.h"
29 #include "ui_bookmark.h"
30 #include "ui_fileops.h"
33 #include "ui_tabcomp.h"
35 #include <gdk/gdkkeysyms.h> /* for keyboard values */
39 #define PAN_WINDOW_DEFAULT_WIDTH 720
40 #define PAN_WINDOW_DEFAULT_HEIGHT 500
42 #define PAN_TILE_SIZE 512
44 #define PAN_THUMB_SIZE_DOTS 4
45 #define PAN_THUMB_SIZE_NONE 24
46 #define PAN_THUMB_SIZE_SMALL 64
47 #define PAN_THUMB_SIZE_NORMAL 128
48 #define PAN_THUMB_SIZE_LARGE 256
49 #define PAN_THUMB_SIZE pw->thumb_size
51 #define PAN_THUMB_GAP_DOTS 2
52 #define PAN_THUMB_GAP_SMALL 14
53 #define PAN_THUMB_GAP_NORMAL 30
54 #define PAN_THUMB_GAP_LARGE 40
55 #define PAN_THUMB_GAP_HUGE 50
56 #define PAN_THUMB_GAP pw->thumb_gap
58 #define PAN_SHADOW_OFFSET 6
59 #define PAN_SHADOW_FADE 5
60 #define PAN_SHADOW_COLOR 0, 0, 0
61 #define PAN_SHADOW_ALPHA 64
63 #define PAN_OUTLINE_THICKNESS 1
64 #define PAN_OUTLINE_COLOR_1 255, 255, 255
65 #define PAN_OUTLINE_COLOR_2 64, 64, 64
66 #define PAN_OUTLINE_ALPHA 180
68 #define PAN_BACKGROUND_COLOR 255, 255, 230
70 #define PAN_GRID_SIZE 10
71 #define PAN_GRID_COLOR 0, 0, 0
72 #define PAN_GRID_ALPHA 20
74 #define PAN_FOLDER_BOX_COLOR 0, 0, 255
75 #define PAN_FOLDER_BOX_ALPHA 10
76 #define PAN_FOLDER_BOX_BORDER 20
78 #define PAN_FOLDER_BOX_OUTLINE_THICKNESS 4
79 #define PAN_FOLDER_BOX_OUTLINE_COLOR 0, 0, 255
80 #define PAN_FOLDER_BOX_OUTLINE_ALPHA 64
82 #define PAN_TEXT_BORDER_SIZE 4
83 #define PAN_TEXT_COLOR 0, 0, 0
85 #define PAN_POPUP_COLOR 255, 255, 220
86 #define PAN_POPUP_ALPHA 255
87 #define PAN_POPUP_BORDER 1
88 #define PAN_POPUP_BORDER_COLOR 0, 0, 0
89 #define PAN_POPUP_TEXT_COLOR 0, 0, 0
91 #define PAN_GROUP_MAX 16
93 #define ZOOM_INCREMENT 1.0
94 #define ZOOM_LABEL_WIDTH 64
97 #define PAN_PREF_GROUP "pan_view_options"
98 #define PAN_PREF_HIDE_WARNING "hide_performance_warning"
104 LAYOUT_FOLDERS_LINEAR,
105 LAYOUT_FOLDERS_FLOWER,
110 LAYOUT_SIZE_THUMB_DOTS = 0,
111 LAYOUT_SIZE_THUMB_NONE,
112 LAYOUT_SIZE_THUMB_SMALL,
113 LAYOUT_SIZE_THUMB_NORMAL,
114 LAYOUT_SIZE_THUMB_LARGE,
133 TEXT_ATTR_BOLD = 1 << 0,
134 TEXT_ATTR_HEADING = 1 << 1,
135 TEXT_ATTR_MARKUP = 1 << 2
146 typedef struct _PanItem PanItem;
161 TextAttrType text_attr;
179 typedef struct _PanWindow PanWindow;
184 ImageWindow *imd_normal;
187 GtkWidget *path_entry;
189 GtkWidget *label_message;
190 GtkWidget *label_zoom;
192 GtkWidget *search_box;
193 GtkWidget *search_entry;
194 GtkWidget *search_label;
195 GtkWidget *search_button;
196 GtkWidget *search_button_arrow;
198 GtkWidget *scrollbar_h;
199 GtkWidget *scrollbar_v;
228 typedef struct _PanCacheData PanCacheData;
229 struct _PanCacheData {
235 static GList *pan_window_list = NULL;
238 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend);
240 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
242 static GtkWidget *pan_popup_menu(PanWindow *pw);
243 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off);
244 static void pan_overlay_toggle(PanWindow *pw);
246 static void pan_window_close(PanWindow *pw);
248 static void pan_window_dnd_init(PanWindow *pw);
251 static gint util_clip_region(gint x, gint y, gint w, gint h,
252 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
253 gint *rx, gint *ry, gint *rw, gint *rh)
255 if (clip_x + clip_w <= x ||
257 clip_y + clip_h <= y ||
263 *rx = MAX(x, clip_x);
264 *rw = MIN((x + w), (clip_x + clip_w)) - *rx;
266 *ry = MAX(y, clip_y);
267 *rh = MIN((y + h), (clip_y + clip_h)) - *ry;
272 static gint util_clip_region_test(gint x, gint y, gint w, gint h,
273 gint clip_x, gint clip_y, gint clip_w, gint clip_h)
277 return util_clip_region(x, y, w, h,
278 clip_x, clip_y, clip_w, clip_h,
291 static gint date_compare(time_t a, time_t b, DateLengthType length)
296 if (length == DATE_LENGTH_EXACT) return (a == b);
298 if (!localtime_r(&a, &ta) ||
299 !localtime_r(&b, &tb)) return FALSE;
301 if (ta.tm_year != tb.tm_year) return FALSE;
302 if (length == DATE_LENGTH_YEAR) return TRUE;
304 if (ta.tm_mon != tb.tm_mon) return FALSE;
305 if (length == DATE_LENGTH_MONTH) return TRUE;
307 if (length == DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
309 if (ta.tm_mday != tb.tm_mday) return FALSE;
310 if (length == DATE_LENGTH_DAY) return TRUE;
312 return (ta.tm_hour == tb.tm_hour);
315 static gint date_value(time_t d, DateLengthType length)
319 if (!localtime_r(&d, &td)) return -1;
323 case DATE_LENGTH_DAY:
326 case DATE_LENGTH_WEEK:
329 case DATE_LENGTH_MONTH:
330 return td.tm_mon + 1;
332 case DATE_LENGTH_YEAR:
333 return td.tm_year + 1900;
335 case DATE_LENGTH_EXACT:
343 static gchar *date_value_string(time_t d, DateLengthType length)
347 gchar *format = NULL;
349 if (!localtime_r(&d, &td)) return g_strdup("");
353 case DATE_LENGTH_DAY:
354 return g_strdup_printf("%d", td.tm_mday);
356 case DATE_LENGTH_WEEK:
359 case DATE_LENGTH_MONTH:
362 case DATE_LENGTH_YEAR:
363 return g_strdup_printf("%d", td.tm_year + 1900);
365 case DATE_LENGTH_EXACT:
367 return g_strdup(text_from_time(d));
372 if (format && strftime(buf, sizeof(buf), format, &td) > 0)
374 gchar *ret = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
381 static time_t date_to_time(gint year, gint month, gint day)
388 lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
389 lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
390 lt.tm_year = year - 1900;
397 *-----------------------------------------------------------------------------
399 *-----------------------------------------------------------------------------
402 static void triangle_rect_region(gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
403 gint *rx, gint *ry, gint *rw, gint *rh)
411 tw = MAX(abs(x1 - x2), abs(x2 - x3));
412 tw = MAX(tw, abs(x3 - x1));
413 th = MAX(abs(y1 - y2), abs(y2 - y3));
414 th = MAX(th, abs(y3 - y1));
422 static void pixbuf_draw_triangle(GdkPixbuf *pb,
423 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
424 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
425 guint8 r, guint8 g, guint8 b, guint8 a)
441 pw = gdk_pixbuf_get_width(pb);
442 ph = gdk_pixbuf_get_height(pb);
444 if (!util_clip_region(0, 0, pw, ph,
445 clip_x, clip_y, clip_w, clip_h,
446 &rx, &ry, &rw, &rh)) return;
448 triangle_rect_region(x1, y1, x2, y2, x3, y3,
451 if (!util_clip_region(rx, ry, rw, rh,
453 &fx1, &fy1, &fw, &fh)) return;
457 p_alpha = gdk_pixbuf_get_has_alpha(pb);
458 prs = gdk_pixbuf_get_rowstride(pb);
459 p_pix = gdk_pixbuf_get_pixels(pb);
461 p_step = (p_alpha) ? 4 : 3;
462 for (i = fy1; i < fy2; i++)
464 pp = p_pix + i * prs + (fx1 * p_step);
465 for (j = fx1; j < fx2; j++)
469 z1 = (y1 - y2)*(j - x2) + (x2 - x1)*(i - y2);
470 z2 = (y2 - y3)*(j - x3) + (x3 - x2)*(i - y3);
473 z2 = (y3 - y1)*(j - x1) + (x1 - x3)*(i - y1);
476 pp[0] = (r * a + pp[0] * (256-a)) >> 8;
477 pp[1] = (g * a + pp[1] * (256-a)) >> 8;
478 pp[2] = (b * a + pp[2] * (256-a)) >> 8;
486 static void pixbuf_draw_line(GdkPixbuf *pb,
487 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
488 gint x1, gint y1, gint x2, gint y2,
489 guint8 r, guint8 g, guint8 b, guint8 a)
494 gint fx1, fy1, fx2, fy2;
500 gdouble xstep, ystep;
507 pw = gdk_pixbuf_get_width(pb);
508 ph = gdk_pixbuf_get_height(pb);
510 if (!util_clip_region(0, 0, pw, ph,
511 clip_x, clip_y, clip_w, clip_h,
512 &rx, &ry, &rw, &rh)) return;
524 if (xa == 0 && ya == 0) return;
526 nt = sqrt(xd * xd + yd * yd);
528 nt = (xa > ya) ? xa : ya;
529 xstep = (double)xd / nt;
530 ystep = (double)yd / nt;
532 p_alpha = gdk_pixbuf_get_has_alpha(pb);
533 prs = gdk_pixbuf_get_rowstride(pb);
534 p_pix = gdk_pixbuf_get_pixels(pb);
536 p_step = (p_alpha) ? 4 : 3;
540 for (n = 0; n < nt; n++)
545 if (x >= fx1 && x < fx2 &&
548 pp = p_pix + y * prs + x * p_step;
549 *pp = (r * a + *pp * (256-a)) >> 8;
551 *pp = (g * a + *pp * (256-a)) >> 8;
553 *pp = (b * a + *pp * (256-a)) >> 8;
560 static void pixbuf_draw_fade_linear(guchar *p_pix, gint prs, gint p_alpha,
561 gint s, gint vertical, gint border,
562 gint x1, gint y1, gint x2, gint y2,
563 guint8 r, guint8 g, guint8 b, guint8 a)
570 p_step = (p_alpha) ? 4 : 3;
571 for (j = y1; j < y2; j++)
573 pp = p_pix + j * prs + x1 * p_step;
574 if (!vertical) n = a - a * abs(j - s) / border;
575 for (i = x1; i < x2; i++)
577 if (vertical) n = a - a * abs(i - s) / border;
578 *pp = (r * n + *pp * (256-n)) >> 8;
580 *pp = (g * n + *pp * (256-n)) >> 8;
582 *pp = (b * n + *pp * (256-n)) >> 8;
589 static void pixbuf_draw_fade_radius(guchar *p_pix, gint prs, gint p_alpha,
590 gint sx, gint sy, gint border,
591 gint x1, gint y1, gint x2, gint y2,
592 guint8 r, guint8 g, guint8 b, guint8 a)
598 p_step = (p_alpha) ? 4 : 3;
599 for (j = y1; j < y2; j++)
601 pp = p_pix + j * prs + x1 * p_step;
602 for (i = x1; i < x2; i++)
607 r = MIN(border, (gint)sqrt((i-sx)*(i-sx) + (j-sy)*(j-sy)));
608 n = a - a * r / border;
609 *pp = (r * n + *pp * (256-n)) >> 8;
611 *pp = (g * n + *pp * (256-n)) >> 8;
613 *pp = (b * n + *pp * (256-n)) >> 8;
620 static void pixbuf_draw_shadow(GdkPixbuf *pb,
621 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
622 gint x, gint y, gint w, gint h, gint border,
623 guint8 r, guint8 g, guint8 b, guint8 a)
633 pw = gdk_pixbuf_get_width(pb);
634 ph = gdk_pixbuf_get_height(pb);
636 if (!util_clip_region(0, 0, pw, ph,
637 clip_x, clip_y, clip_w, clip_h,
638 &rx, &ry, &rw, &rh)) return;
640 p_alpha = gdk_pixbuf_get_has_alpha(pb);
641 prs = gdk_pixbuf_get_rowstride(pb);
642 p_pix = gdk_pixbuf_get_pixels(pb);
644 if (util_clip_region(x + border, y + border, w - border * 2, h - border * 2,
648 pixbuf_draw_rect_fill(pb, fx, fy, fw, fh, r, g, b, a);
651 if (border < 1) return;
653 if (util_clip_region(x, y + border, border, h - border * 2,
657 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
658 x + border, TRUE, border,
659 fx, fy, fx + fw, fy + fh,
662 if (util_clip_region(x + w - border, y + border, border, h - border * 2,
666 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
667 x + w - border, TRUE, border,
668 fx, fy, fx + fw, fy + fh,
671 if (util_clip_region(x + border, y, w - border * 2, border,
675 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
676 y + border, FALSE, border,
677 fx, fy, fx + fw, fy + fh,
680 if (util_clip_region(x + border, y + h - border, w - border * 2, border,
684 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
685 y + h - border, FALSE, border,
686 fx, fy, fx + fw, fy + fh,
689 if (util_clip_region(x, y, border, border,
693 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
694 x + border, y + border, border,
695 fx, fy, fx + fw, fy + fh,
698 if (util_clip_region(x + w - border, y, border, border,
702 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
703 x + w - border, y + border, border,
704 fx, fy, fx + fw, fy + fh,
707 if (util_clip_region(x, y + h - border, border, border,
711 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
712 x + border, y + h - border, border,
713 fx, fy, fx + fw, fy + fh,
716 if (util_clip_region(x + w - border, y + h - border, border, border,
720 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
721 x + w - border, y + h - border, border,
722 fx, fy, fx + fw, fy + fh,
729 *-----------------------------------------------------------------------------
731 *-----------------------------------------------------------------------------
734 static void pan_cache_free(PanWindow *pw)
738 work = pw->cache_list;
746 cache_sim_data_free(pc->cd);
747 file_data_free((FileData *)pc);
750 g_list_free(pw->cache_list);
751 pw->cache_list = NULL;
753 filelist_free(pw->cache_todo);
754 pw->cache_todo = NULL;
761 static void pan_cache_fill(PanWindow *pw, const gchar *path)
767 list = pan_window_layout_list(path, SORT_NAME, TRUE);
768 pw->cache_todo = g_list_reverse(list);
770 pw->cache_total = g_list_length(pw->cache_todo);
773 static gint pan_cache_step(PanWindow *pw)
777 CacheData *cd = NULL;
779 if (!pw->cache_todo) return FALSE;
781 fd = pw->cache_todo->data;
782 pw->cache_todo = g_list_remove(pw->cache_todo, fd);
784 if (enable_thumb_caching)
788 found = cache_find_location(CACHE_TYPE_SIM, fd->path);
789 if (found && filetime(found) == fd->date)
791 cd = cache_sim_data_load(found);
796 if (!cd) cd = cache_sim_data_new();
800 cd->dimensions = image_load_dimensions(fd->path, &cd->width, &cd->height);
801 if (enable_thumb_caching &&
807 base = cache_get_location(CACHE_TYPE_SIM, fd->path, FALSE, &mode);
808 if (cache_ensure_dir_exists(base, mode))
811 cd->path = cache_get_location(CACHE_TYPE_SIM, fd->path, TRUE, NULL);
812 if (cache_sim_data_save(cd))
814 filetime_set(cd->path, filetime(fd->path));
823 pc = g_new0(PanCacheData, 1);
824 memcpy(pc, fd, sizeof(FileData));
829 pw->cache_list = g_list_prepend(pw->cache_list, pc);
836 *-----------------------------------------------------------------------------
838 *-----------------------------------------------------------------------------
841 static void pan_item_free(PanItem *pi)
845 if (pi->pixbuf) g_object_unref(pi->pixbuf);
846 if (pi->fd) file_data_free(pi->fd);
854 static void pan_window_items_free(PanWindow *pw)
861 PanItem *pi = work->data;
867 g_list_free(pw->list);
870 g_list_free(pw->queue);
874 image_loader_free(pw->il);
877 thumb_loader_free(pw->tl);
883 static PanItem *pan_item_new_thumb(PanWindow *pw, FileData *fd, gint x, gint y)
887 pi = g_new0(PanItem, 1);
888 pi->type = ITEM_THUMB;
892 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
893 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
899 pw->list = g_list_prepend(pw->list, pi);
904 static PanItem *pan_item_new_box(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
906 guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
907 guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
911 pi = g_new0(PanItem, 1);
919 pi->color_r = base_r;
920 pi->color_g = base_g;
921 pi->color_b = base_b;
922 pi->color_a = base_a;
924 pi->color2_r = bord_r;
925 pi->color2_g = bord_g;
926 pi->color2_b = bord_b;
927 pi->color2_a = bord_a;
928 pi->border = border_size;
930 pw->list = g_list_prepend(pw->list, pi);
935 static void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
939 if (!pi || pi->type != ITEM_BOX) return;
944 pi->width -= shadow[0];
945 pi->height -= shadow[0];
948 shadow = g_new0(gint, 2);
953 pi->height += offset;
959 static PanItem *pan_item_new_tri(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
960 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
961 guint8 r, guint8 g, guint8 b, guint8 a)
966 pi = g_new0(PanItem, 1);
967 pi->type = ITEM_TRIANGLE;
978 coord = g_new0(gint, 6);
988 pi->border = BORDER_NONE;
990 pw->list = g_list_prepend(pw->list, pi);
995 static void pan_item_tri_border(PanItem *pi, gint borders,
996 guint8 r, guint8 g, guint8 b, guint8 a)
998 if (!pi || pi->type != ITEM_TRIANGLE) return;
1000 pi->border = borders;
1008 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
1010 PangoLayout *layout;
1012 layout = gtk_widget_create_pango_layout(widget, NULL);
1014 if (pi->text_attr & TEXT_ATTR_MARKUP)
1016 pango_layout_set_markup(layout, pi->text, -1);
1020 if (pi->text_attr & TEXT_ATTR_BOLD ||
1021 pi->text_attr & TEXT_ATTR_HEADING)
1026 pal = pango_attr_list_new();
1027 if (pi->text_attr & TEXT_ATTR_BOLD)
1029 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
1030 pa->start_index = 0;
1031 pa->end_index = G_MAXINT;
1032 pango_attr_list_insert(pal, pa);
1034 if (pi->text_attr & TEXT_ATTR_HEADING)
1036 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
1037 pa->start_index = 0;
1038 pa->end_index = G_MAXINT;
1039 pango_attr_list_insert(pal, pa);
1041 pango_layout_set_attributes(layout, pal);
1042 pango_attr_list_unref(pal);
1045 pango_layout_set_text(layout, pi->text, -1);
1049 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
1051 PangoLayout *layout;
1053 if (!pi || !pi->text || !widget) return;
1055 layout = pan_item_text_layout(pi, widget);
1056 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
1057 g_object_unref(G_OBJECT(layout));
1059 pi->width += PAN_TEXT_BORDER_SIZE * 2;
1060 pi->height += PAN_TEXT_BORDER_SIZE * 2;
1063 static PanItem *pan_item_new_text(PanWindow *pw, gint x, gint y, const gchar *text, TextAttrType attr,
1064 guint8 r, guint8 g, guint8 b, guint8 a)
1068 pi = g_new0(PanItem, 1);
1069 pi->type = ITEM_TEXT;
1072 pi->text = g_strdup(text);
1073 pi->text_attr = attr;
1080 pan_item_text_compute_size(pi, pw->imd->widget);
1082 pw->list = g_list_prepend(pw->list, pi);
1087 static void pan_item_set_key(PanItem *pi, const gchar *key)
1094 pi->key = g_strdup(key);
1098 static void pan_item_added(PanWindow *pw, PanItem *pi)
1101 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1104 static void pan_item_remove(PanWindow *pw, PanItem *pi)
1108 if (pw->click_pi == pi) pw->click_pi = NULL;
1109 if (pw->queue_pi == pi) pw->queue_pi = NULL;
1110 pw->queue = g_list_remove(pw->queue, pi);
1112 pw->list = g_list_remove(pw->list, pi);
1113 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1117 static void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
1119 if (!pi || !child) return;
1121 if (pi->x + pi->width < child->x + child->width + border)
1122 pi->width = child->x + child->width + border - pi->x;
1124 if (pi->y + pi->height < child->y + child->height + border)
1125 pi->height = child->y + child->height + border - pi->y;
1128 static void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
1132 if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
1133 if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
1136 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
1143 if (!pi->fd) return;
1145 work = pw->cache_list;
1154 path = ((FileData *)pc)->path;
1156 if (pc->cd && pc->cd->dimensions &&
1157 path && strcmp(path, pi->fd->path) == 0)
1159 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
1160 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
1162 pw->cache_list = g_list_remove(pw->cache_list, pc);
1163 cache_sim_data_free(pc->cd);
1164 file_data_free((FileData *)pc);
1170 static PanItem *pan_item_new_image(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
1174 pi = g_new0(PanItem, 1);
1175 pi->type = ITEM_IMAGE;
1180 pan_item_image_find_size(pw, pi, w, h);
1182 pw->list = g_list_prepend(pw->list, pi);
1187 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
1191 if (!key) return NULL;
1193 work = g_list_last(pw->list);
1199 if ((pi->type == type || type == ITEM_NONE) &&
1200 pi->key && strcmp(pi->key, key) == 0)
1210 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1211 static GList *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1212 gint ignore_case, gint partial)
1217 if (!path) return NULL;
1218 if (partial && path[0] == '/') return NULL;
1220 work = g_list_last(pw->list);
1226 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1232 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
1234 else if (pi->fd->name)
1242 haystack = g_utf8_strdown(pi->fd->name, -1);
1243 match = (strstr(haystack, path) != NULL);
1248 if (strstr(pi->fd->name, path)) match = TRUE;
1251 else if (ignore_case)
1253 if (strcasecmp(path, pi->fd->name) == 0) match = TRUE;
1257 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
1261 if (match) list = g_list_prepend(list, pi);
1266 return g_list_reverse(list);
1269 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y, const gchar *key)
1273 if (x < 0 || x >= pw->imd->image_width ||
1274 y < 0 || y >= pw->imd->image_height) return NULL;
1282 if ((pi->type == type || type == ITEM_NONE) &&
1283 x >= pi->x && x < pi->x + pi->width &&
1284 y >= pi->y && y < pi->y + pi->height &&
1285 (!key || (pi->key && strcmp(pi->key, key) == 0)))
1296 *-----------------------------------------------------------------------------
1298 *-----------------------------------------------------------------------------
1301 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend)
1303 GList *flist = NULL;
1304 GList *dlist = NULL;
1308 filelist_read(path, &flist, &dlist);
1309 if (sort != SORT_NONE)
1311 flist = filelist_sort(flist, sort, ascend);
1312 dlist = filelist_sort(dlist, sort, ascend);
1322 folders = g_list_remove(folders, fd);
1324 if (filelist_read(fd->path, &flist, &dlist))
1326 if (sort != SORT_NONE)
1328 flist = filelist_sort(flist, sort, ascend);
1329 dlist = filelist_sort(dlist, sort, ascend);
1332 result = g_list_concat(result, flist);
1333 folders = g_list_concat(dlist, folders);
1342 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1350 list = pan_window_layout_list(path, SORT_NAME, TRUE);
1352 grid_size = (gint)sqrt((double)g_list_length(list));
1353 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1355 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1359 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1364 *width = PAN_FOLDER_BOX_BORDER * 2;
1365 *height = PAN_FOLDER_BOX_BORDER * 2;
1378 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1380 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1382 x += pi->width + PAN_THUMB_GAP;
1383 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1392 pi = pan_item_new_thumb(pw, fd, x, y);
1394 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1398 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1401 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1407 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1410 gint x1, y1, x2, y2;
1425 if (x1 > pi->x) x1 = pi->x;
1426 if (y1 > pi->y) y1 = pi->y;
1427 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1428 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1431 x1 -= PAN_FOLDER_BOX_BORDER;
1432 y1 -= PAN_FOLDER_BOX_BORDER;
1433 x2 += PAN_FOLDER_BOX_BORDER;
1434 y2 += PAN_FOLDER_BOX_BORDER;
1447 if (pi->type == ITEM_TRIANGLE && pi->data)
1461 if (width) *width = x2 - x1;
1462 if (height) *height = y2 - y1;
1465 typedef struct _FlowerGroup FlowerGroup;
1466 struct _FlowerGroup {
1479 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1483 work = group->items;
1501 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1502 gint *result_x, gint *result_y)
1508 radius = parent->circumference / (2*PI);
1509 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1511 a = 2*PI * group->diameter / parent->circumference;
1513 x = (gint)((double)radius * cos(parent->angle + a / 2));
1514 y = (gint)((double)radius * sin(parent->angle + a / 2));
1521 x += parent->width / 2;
1522 y += parent->height / 2;
1524 x -= group->width / 2;
1525 y -= group->height / 2;
1531 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1538 if (parent && parent->children)
1540 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1548 pan_window_layout_compute_folder_flower_move(group, x, y);
1553 gint px, py, gx, gy;
1554 gint x1, y1, x2, y2;
1556 px = parent->x + parent->width / 2;
1557 py = parent->y + parent->height / 2;
1559 gx = group->x + group->width / 2;
1560 gy = group->y + group->height / 2;
1565 x2 = MAX(px, gx + 5);
1566 y2 = MAX(py, gy + 5);
1568 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1569 px, py, gx, gy, gx + 5, gy + 5,
1571 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1575 pw->list = g_list_concat(group->items, pw->list);
1576 group->items = NULL;
1578 group->circumference = 0;
1579 work = group->children;
1587 group->circumference += child->diameter;
1590 work = g_list_last(group->children);
1598 pan_window_layout_compute_folder_flower_build(pw, child, group);
1601 g_list_free(group->children);
1605 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1618 if (!filelist_read(path, &f, &d)) return NULL;
1619 if (!f && !d) return NULL;
1621 f = filelist_sort(f, SORT_NAME, TRUE);
1622 d = filelist_sort(d, SORT_NAME, TRUE);
1624 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1625 PAN_TEXT_COLOR, 255);
1627 y += pi_box->height;
1629 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1631 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1632 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1633 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1634 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1636 x += PAN_FOLDER_BOX_BORDER;
1637 y += PAN_FOLDER_BOX_BORDER;
1639 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1653 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1655 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1656 x += pi->width + PAN_THUMB_GAP;
1657 if (pi->height > y_height) y_height = pi->height;
1661 pi = pan_item_new_thumb(pw, fd, x, y);
1662 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1663 y_height = PAN_THUMB_SIZE;
1667 if (grid_count >= grid_size)
1671 y += y_height + PAN_THUMB_GAP;
1675 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1680 group = g_new0(FlowerGroup, 1);
1681 group->items = pw->list;
1684 group->width = pi_box->width;
1685 group->height = pi_box->y + pi_box->height;
1686 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1688 group->children = NULL;
1699 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1700 if (child) group->children = g_list_prepend(group->children, child);
1708 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1709 gint *width, gint *height,
1710 gint *scroll_x, gint *scroll_y)
1715 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1716 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1718 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1720 list = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1723 PanItem *pi = list->data;
1724 *scroll_x = pi->x + pi->width / 2;
1725 *scroll_y = pi->y + pi->height / 2;
1730 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1731 gint *x, gint *y, gint *level,
1733 gint *width, gint *height)
1741 if (!filelist_read(path, &f, &d)) return;
1742 if (!f && !d) return;
1744 f = filelist_sort(f, SORT_NAME, TRUE);
1745 d = filelist_sort(d, SORT_NAME, TRUE);
1747 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1749 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1750 PAN_TEXT_COLOR, 255);
1752 *y += pi_box->height;
1754 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1756 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1757 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1758 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1759 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1761 *x += PAN_FOLDER_BOX_BORDER;
1762 *y += PAN_FOLDER_BOX_BORDER;
1773 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1775 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1776 *x += pi->width + PAN_THUMB_GAP;
1777 if (pi->height > y_height) y_height = pi->height;
1781 pi = pan_item_new_thumb(pw, fd, *x, *y);
1782 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1783 y_height = PAN_THUMB_SIZE;
1786 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1789 if (f) *y = pi_box->y + pi_box->height;
1801 *level = *level + 1;
1802 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1803 pi_box, width, height);
1804 *level = *level - 1;
1809 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1811 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1812 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1814 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1817 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1824 x = PAN_FOLDER_BOX_BORDER;
1825 y = PAN_FOLDER_BOX_BORDER;
1826 w = PAN_FOLDER_BOX_BORDER * 2;
1827 h = PAN_FOLDER_BOX_BORDER * 2;
1829 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1831 if (width) *width = w;
1832 if (height) *height = h;
1836 *-----------------------------------------------------------------------------
1838 *-----------------------------------------------------------------------------
1841 #define PAN_CAL_DAY_WIDTH 100
1842 #define PAN_CAL_DAY_HEIGHT 80
1843 #define PAN_CAL_DOT_SIZE 3
1844 #define PAN_CAL_DOT_GAP 2
1845 #define PAN_CAL_DOT_COLOR 0, 0, 0
1846 #define PAN_CAL_DOT_ALPHA 32
1848 static void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
1854 gint x1, y1, x2, y2, x3, y3;
1859 while ((pi = pan_item_find_by_key(pw, ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
1861 if (!pi_day || pi_day->type != ITEM_BOX ||
1862 !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
1864 list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
1876 if (dot->type != ITEM_BOX || !dot->fd ||
1877 !dot->key || strcmp(dot->key, "dot") != 0)
1879 list = g_list_delete_link(list, node);
1885 grid = (gint)(sqrt(g_list_length(list)) + 0.5);
1887 x = pi_day->x + pi_day->width + 4;
1891 if (y + grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4 > pw->imd->image_height)
1893 y = pw->imd->image_height - (grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4);
1897 pbox = pan_item_new_box(pw, NULL, x, y, PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1899 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
1900 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
1901 pan_item_set_key(pbox, "day_bubble");
1909 buf = date_value_string(pi->fd->date, DATE_LENGTH_WEEK);
1910 plabel = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1911 PAN_POPUP_TEXT_COLOR, 255);
1912 pan_item_set_key(plabel, "day_bubble");
1915 pan_item_size_by_item(pbox, plabel, 0);
1917 y += plabel->height;
1922 x += PAN_FOLDER_BOX_BORDER;
1923 y += PAN_FOLDER_BOX_BORDER;
1937 pimg = pan_item_new_thumb(pw, file_data_new_simple(dot->fd->path), x, y);
1938 pan_item_set_key(pimg, "day_bubble");
1940 pan_item_size_by_item(pbox, pimg, PAN_FOLDER_BOX_BORDER);
1945 x += pimg->width + PAN_THUMB_GAP;
1950 x = pbox->x + PAN_FOLDER_BOX_BORDER;
1951 y += pimg->height + PAN_THUMB_GAP;
1956 x1 = pi_day->x + pi_day->width - 8;
1962 triangle_rect_region(x1, y1, x2, y2, x3, y3,
1965 pi = pan_item_new_tri(pw, NULL, x, y, w, h,
1966 x1, y1, x2, y2, x3, y3,
1967 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
1968 pan_item_tri_border(pi, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
1969 pan_item_set_key(pi, "day_bubble");
1970 pan_item_added(pw, pi);
1972 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
1973 pan_item_added(pw, pbox);
1976 static void pan_window_layout_compute_calendar(PanWindow *pw, const gchar *path, gint *width, gint *height)
1992 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
1994 list = pan_window_layout_list(path, SORT_NONE, TRUE);
1995 list = filelist_sort(list, SORT_TIME, TRUE);
2008 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2016 if (day_max < count) day_max = count;
2020 printf("biggest day contains %d images\n", day_max);
2022 grid = (gint)(sqrt((double)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
2023 day_width = MAX(PAN_CAL_DAY_WIDTH, grid);
2024 day_height = MAX(PAN_CAL_DAY_HEIGHT, grid);
2028 FileData *fd = list->data;
2030 year = date_value(fd->date, DATE_LENGTH_YEAR);
2031 month = date_value(fd->date, DATE_LENGTH_MONTH);
2034 work = g_list_last(list);
2037 FileData *fd = work->data;
2038 end_year = date_value(fd->date, DATE_LENGTH_YEAR);
2039 end_month = date_value(fd->date, DATE_LENGTH_MONTH);
2042 *width = PAN_FOLDER_BOX_BORDER * 2;
2043 *height = PAN_FOLDER_BOX_BORDER * 2;
2045 x = PAN_FOLDER_BOX_BORDER;
2046 y = PAN_FOLDER_BOX_BORDER;
2049 while (work && (year < end_year || (year == end_year && month <= end_month)))
2060 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
2062 days = date_value(dt, DATE_LENGTH_DAY);
2063 dt = date_to_time(year, month, 1);
2064 col = date_value(dt, DATE_LENGTH_WEEK);
2067 x = PAN_FOLDER_BOX_BORDER;
2069 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
2070 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2071 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2072 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2073 buf = date_value_string(dt, DATE_LENGTH_MONTH);
2074 pi_text = pan_item_new_text(pw, x, y, buf,
2075 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2076 PAN_TEXT_COLOR, 255);
2078 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
2080 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
2082 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
2083 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
2085 for (day = 1; day <= days; day++)
2092 dt = date_to_time(year, month, day);
2094 pi_day = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
2095 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2096 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2097 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2098 pan_item_set_key(pi_day, "day");
2100 dx = x + PAN_CAL_DOT_GAP * 2;
2101 dy = y + PAN_CAL_DOT_GAP * 2;
2103 fd = (work) ? work->data : NULL;
2104 while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
2108 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
2110 PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
2112 pan_item_set_key(pi, "dot");
2114 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2115 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
2117 dx = x + PAN_CAL_DOT_GAP * 2;
2118 dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2120 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
2122 /* must keep all dots within respective day even if it gets ugly */
2123 dy = y + PAN_CAL_DOT_GAP * 2;
2126 pi_day->color_a = MIN(PAN_FOLDER_BOX_ALPHA + 64 + n, 255);
2130 fd = (work) ? work->data : NULL;
2133 buf = g_strdup_printf("%d", day);
2134 pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2135 PAN_TEXT_COLOR, 255);
2139 pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
2146 x = PAN_FOLDER_BOX_BORDER;
2147 y += PAN_CAL_DAY_HEIGHT;
2151 x += PAN_CAL_DAY_WIDTH;
2155 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
2156 y += PAN_FOLDER_BOX_BORDER * 2;
2167 *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
2172 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
2180 PanItem *pi_month = NULL;
2181 PanItem *pi_day = NULL;
2187 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2189 list = pan_window_layout_list(path, SORT_NONE, TRUE);
2190 list = filelist_sort(list, SORT_TIME, TRUE);
2192 *width = PAN_FOLDER_BOX_BORDER * 2;
2193 *height = PAN_FOLDER_BOX_BORDER * 2;
2198 day_start = month_start;
2213 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2218 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
2224 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
2228 x = PAN_FOLDER_BOX_BORDER;
2231 y = PAN_FOLDER_BOX_BORDER;
2233 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
2234 pi = pan_item_new_text(pw, x, y, buf,
2235 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2236 PAN_TEXT_COLOR, 255);
2239 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
2241 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2242 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2243 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2245 x += PAN_FOLDER_BOX_BORDER;
2246 y += PAN_FOLDER_BOX_BORDER;
2250 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
2262 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
2264 needle = needle->next;
2273 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
2274 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2275 PAN_TEXT_COLOR, 255);
2280 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
2281 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2282 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2283 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2285 x += PAN_FOLDER_BOX_BORDER;
2286 y += PAN_FOLDER_BOX_BORDER;
2290 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2292 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
2293 if (pi->width > x_width) x_width = pi->width;
2294 y_height = pi->height;
2298 pi = pan_item_new_thumb(pw, fd, x, y);
2299 x_width = PAN_THUMB_SIZE;
2300 y_height = PAN_THUMB_SIZE;
2303 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
2304 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
2309 if (total > 0 && count < PAN_GROUP_MAX)
2311 y += y_height + PAN_THUMB_GAP;
2315 x += x_width + PAN_THUMB_GAP;
2325 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
2331 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
2332 gint *width, gint *height,
2333 gint *scroll_x, gint *scroll_y)
2335 pan_window_items_free(pw);
2339 case LAYOUT_SIZE_THUMB_DOTS:
2340 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2341 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2343 case LAYOUT_SIZE_THUMB_NONE:
2344 pw->thumb_size = PAN_THUMB_SIZE_NONE;
2345 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2347 case LAYOUT_SIZE_THUMB_SMALL:
2348 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2349 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2351 case LAYOUT_SIZE_THUMB_NORMAL:
2353 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2354 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2356 case LAYOUT_SIZE_THUMB_LARGE:
2357 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2358 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2360 case LAYOUT_SIZE_10:
2361 pw->image_size = 10;
2362 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2364 case LAYOUT_SIZE_25:
2365 pw->image_size = 25;
2366 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2368 case LAYOUT_SIZE_33:
2369 pw->image_size = 33;
2370 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2372 case LAYOUT_SIZE_50:
2373 pw->image_size = 50;
2374 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2376 case LAYOUT_SIZE_100:
2377 pw->image_size = 100;
2378 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2391 pan_window_layout_compute_grid(pw, path, width, height);
2393 case LAYOUT_FOLDERS_LINEAR:
2394 pan_window_layout_compute_folders_linear(pw, path, width, height);
2396 case LAYOUT_FOLDERS_FLOWER:
2397 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2399 case LAYOUT_CALENDAR:
2400 pan_window_layout_compute_calendar(pw, path, width, height);
2402 case LAYOUT_TIMELINE:
2403 pan_window_layout_compute_timeline(pw, path, width, height);
2409 printf("computed %d objects\n", g_list_length(pw->list));
2412 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2425 if (util_clip_region_test(x, y, width, height,
2426 pi->x, pi->y, pi->width, pi->height))
2428 list = g_list_prepend(list, pi);
2438 *-----------------------------------------------------------------------------
2440 *-----------------------------------------------------------------------------
2443 static gint pan_layout_queue_step(PanWindow *pw);
2446 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2448 PanWindow *pw = data;
2456 pw->queue_pi = NULL;
2460 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2461 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2464 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2468 thumb_loader_free(pw->tl);
2471 while (pan_layout_queue_step(pw));
2474 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2476 PanWindow *pw = data;
2484 pw->queue_pi = NULL;
2488 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2489 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2490 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2492 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2493 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2494 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2499 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2500 (GdkInterpType)zoom_quality);
2501 g_object_unref(tmp);
2505 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2509 image_loader_free(pw->il);
2512 while (pan_layout_queue_step(pw));
2516 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2517 guint width, guint height, gpointer data)
2519 PanWindow *pw = data;
2530 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2531 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2535 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2541 static gint pan_layout_queue_step(PanWindow *pw)
2545 if (!pw->queue) return FALSE;
2547 pi = pw->queue->data;
2548 pw->queue = g_list_remove(pw->queue, pi);
2551 if (!pw->queue_pi->fd)
2553 pw->queue_pi->queued = FALSE;
2554 pw->queue_pi = NULL;
2558 image_loader_free(pw->il);
2560 thumb_loader_free(pw->tl);
2563 if (pi->type == ITEM_IMAGE)
2565 pw->il = image_loader_new(pi->fd->path);
2567 if (pw->size != LAYOUT_SIZE_100)
2569 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2573 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2575 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2577 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2579 image_loader_free(pw->il);
2582 else if (pi->type == ITEM_THUMB)
2584 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2586 if (!pw->tl->standard_loader)
2588 /* The classic loader will recreate a thumbnail any time we
2589 * request a different size than what exists. This view will
2590 * almost never use the user configured sizes so disable cache.
2592 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2595 thumb_loader_set_callbacks(pw->tl,
2596 pan_layout_queue_thumb_done_cb,
2597 pan_layout_queue_thumb_done_cb,
2600 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2602 thumb_loader_free(pw->tl);
2606 pw->queue_pi->queued = FALSE;
2607 pw->queue_pi = NULL;
2611 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2613 if (!pi || pi->queued || pi->pixbuf) return;
2614 if (pw->size <= LAYOUT_SIZE_THUMB_NONE) return;
2617 pw->queue = g_list_prepend(pw->queue, pi);
2619 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2622 static gint pan_window_request_tile_cb(ImageWindow *imd, gint x, gint y, gint width, gint height,
2623 GdkPixbuf *pixbuf, gpointer data)
2625 PanWindow *pw = data;
2630 pixbuf_draw_rect_fill(pixbuf,
2631 0, 0, width, height,
2632 PAN_BACKGROUND_COLOR, 255);
2634 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2636 gint rx, ry, rw, rh;
2638 if (util_clip_region(x, y, width, height,
2640 &rx, &ry, &rw, &rh))
2642 pixbuf_draw_rect_fill(pixbuf,
2643 rx - x, ry - y, rw, rh,
2644 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2647 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2649 gint rx, ry, rw, rh;
2651 if (util_clip_region(x, y, width, height,
2653 &rx, &ry, &rw, &rh))
2655 pixbuf_draw_rect_fill(pixbuf,
2656 rx - x, ry - y, rw, rh,
2657 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2661 list = pan_layout_intersect(pw, x, y, width, height);
2666 gint tx, ty, tw, th;
2667 gint rx, ry, rw, rh;
2674 if (pi->type == ITEM_THUMB && pi->pixbuf)
2676 tw = gdk_pixbuf_get_width(pi->pixbuf);
2677 th = gdk_pixbuf_get_height(pi->pixbuf);
2679 tx = pi->x + (pi->width - tw) / 2;
2680 ty = pi->y + (pi->height - th) / 2;
2682 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2684 if (util_clip_region(x, y, width, height,
2685 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2686 &rx, &ry, &rw, &rh))
2688 pixbuf_draw_shadow(pixbuf,
2689 rx - x, ry - y, rw, rh,
2690 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2692 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2697 if (util_clip_region(x, y, width, height,
2698 tx + tw, ty + PAN_SHADOW_OFFSET,
2699 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2700 &rx, &ry, &rw, &rh))
2702 pixbuf_draw_shadow(pixbuf,
2703 rx - x, ry - y, rw, rh,
2704 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2706 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2708 if (util_clip_region(x, y, width, height,
2709 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2710 &rx, &ry, &rw, &rh))
2712 pixbuf_draw_shadow(pixbuf,
2713 rx - x, ry - y, rw, rh,
2714 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2716 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2720 if (util_clip_region(x, y, width, height,
2722 &rx, &ry, &rw, &rh))
2724 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2727 1.0, 1.0, GDK_INTERP_NEAREST,
2731 if (util_clip_region(x, y, width, height,
2732 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2733 &rx, &ry, &rw, &rh))
2735 pixbuf_draw_rect_fill(pixbuf,
2736 rx - x, ry - y, rw, rh,
2737 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2739 if (util_clip_region(x, y, width, height,
2740 tx, ty, PAN_OUTLINE_THICKNESS, th,
2741 &rx, &ry, &rw, &rh))
2743 pixbuf_draw_rect_fill(pixbuf,
2744 rx - x, ry - y, rw, rh,
2745 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2747 if (util_clip_region(x, y, width, height,
2748 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2749 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2750 &rx, &ry, &rw, &rh))
2752 pixbuf_draw_rect_fill(pixbuf,
2753 rx - x, ry - y, rw, rh,
2754 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2756 if (util_clip_region(x, y, width, height,
2757 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2758 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2759 &rx, &ry, &rw, &rh))
2761 pixbuf_draw_rect_fill(pixbuf,
2762 rx - x, ry - y, rw, rh,
2763 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2766 else if (pi->type == ITEM_THUMB)
2768 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2769 th = pi->height - PAN_SHADOW_OFFSET * 2;
2770 tx = pi->x + PAN_SHADOW_OFFSET;
2771 ty = pi->y + PAN_SHADOW_OFFSET;
2773 if (util_clip_region(x, y, width, height,
2775 &rx, &ry, &rw, &rh))
2779 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2780 pixbuf_draw_rect_fill(pixbuf,
2781 rx - x, ry - y, rw, rh,
2783 PAN_SHADOW_ALPHA / d);
2786 pan_layout_queue(pw, pi);
2788 else if (pi->type == ITEM_IMAGE)
2790 if (util_clip_region(x, y, width, height,
2791 pi->x, pi->y, pi->width, pi->height,
2792 &rx, &ry, &rw, &rh))
2796 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2799 1.0, 1.0, GDK_INTERP_NEAREST,
2804 pixbuf_draw_rect_fill(pixbuf,
2805 rx - x, ry - y, rw, rh,
2806 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2807 pan_layout_queue(pw, pi);
2811 else if (pi->type == ITEM_BOX)
2825 if (pi->color_a > 254)
2827 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2828 shadow[0], bh - shadow[0],
2829 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2831 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2832 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2834 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2836 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2841 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2842 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2844 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2846 PAN_SHADOW_COLOR, a);
2850 if (util_clip_region(x, y, width, height,
2851 pi->x, pi->y, bw, bh,
2852 &rx, &ry, &rw, &rh))
2854 pixbuf_draw_rect_fill(pixbuf,
2855 rx - x, ry - y, rw, rh,
2856 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2858 if (util_clip_region(x, y, width, height,
2859 pi->x, pi->y, bw, pi->border,
2860 &rx, &ry, &rw, &rh))
2862 pixbuf_draw_rect_fill(pixbuf,
2863 rx - x, ry - y, rw, rh,
2864 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2866 if (util_clip_region(x, y, width, height,
2867 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
2868 &rx, &ry, &rw, &rh))
2870 pixbuf_draw_rect_fill(pixbuf,
2871 rx - x, ry - y, rw, rh,
2872 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2874 if (util_clip_region(x, y, width, height,
2875 pi->x + bw - pi->border, pi->y + pi->border,
2876 pi->border, bh - pi->border * 2,
2877 &rx, &ry, &rw, &rh))
2879 pixbuf_draw_rect_fill(pixbuf,
2880 rx - x, ry - y, rw, rh,
2881 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2883 if (util_clip_region(x, y, width, height,
2884 pi->x, pi->y + bh - pi->border,
2886 &rx, &ry, &rw, &rh))
2888 pixbuf_draw_rect_fill(pixbuf,
2889 rx - x, ry - y, rw, rh,
2890 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2893 else if (pi->type == ITEM_TRIANGLE)
2895 if (util_clip_region(x, y, width, height,
2896 pi->x, pi->y, pi->width, pi->height,
2897 &rx, &ry, &rw, &rh) && pi->data)
2899 gint *coord = pi->data;
2900 pixbuf_draw_triangle(pixbuf,
2901 rx - x, ry - y, rw, rh,
2902 coord[0] - x, coord[1] - y,
2903 coord[2] - x, coord[3] - y,
2904 coord[4] - x, coord[5] - y,
2905 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2907 if (pi->border & BORDER_1)
2909 pixbuf_draw_line(pixbuf,
2910 rx - x, ry - y, rw, rh,
2911 coord[0] - x, coord[1] - y,
2912 coord[2] - x, coord[3] - y,
2913 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2915 if (pi->border & BORDER_2)
2917 pixbuf_draw_line(pixbuf,
2918 rx - x, ry - y, rw, rh,
2919 coord[2] - x, coord[3] - y,
2920 coord[4] - x, coord[5] - y,
2921 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2923 if (pi->border & BORDER_3)
2925 pixbuf_draw_line(pixbuf,
2926 rx - x, ry - y, rw, rh,
2927 coord[4] - x, coord[5] - y,
2928 coord[0] - x, coord[1] - y,
2929 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2933 else if (pi->type == ITEM_TEXT && pi->text)
2935 PangoLayout *layout;
2937 layout = pan_item_text_layout(pi, imd->image);
2938 pixbuf_draw_layout(pixbuf, layout, imd->image,
2939 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
2940 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2941 g_object_unref(G_OBJECT(layout));
2948 static gint count = 0;
2949 PangoLayout *layout;
2955 layout = gtk_widget_create_pango_layout(imd->image, NULL);
2957 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
2958 (x / imd->source_tile_width) +
2959 (y / imd->source_tile_height * (imd->image_width/imd->source_tile_width + 1)));
2960 pango_layout_set_text(layout, buf, -1);
2963 pango_layout_get_pixel_size(layout, &lw, &lh);
2965 pixmap = gdk_pixmap_new(imd->widget->window, lw, lh, -1);
2966 gdk_draw_rectangle(pixmap, imd->widget->style->black_gc, TRUE, 0, 0, lw, lh);
2967 gdk_draw_layout(pixmap, imd->widget->style->white_gc, 0, 0, layout);
2968 g_object_unref(G_OBJECT(layout));
2970 lx = MAX(0, width / 2 - lw / 2);
2971 ly = MAX(0, height / 2 - lh / 2);
2972 lw = MIN(lw, width - lx);
2973 lh = MIN(lh, height - ly);
2974 gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_drawable_get_colormap(imd->image->window),
2975 0, 0, lx, ly, lw, lh);
2976 g_object_unref(pixmap);
2984 static void pan_window_dispose_tile_cb(ImageWindow *imd, gint x, gint y, gint width, gint height,
2985 GdkPixbuf *pixbuf, gpointer data)
2987 PanWindow *pw = data;
2991 list = pan_layout_intersect(pw, x, y, width, height);
3000 if (pi->refcount > 0)
3004 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
3009 pw->queue = g_list_remove(pw->queue, pi);
3012 if (pw->queue_pi == pi) pw->queue_pi = NULL;
3015 g_object_unref(pi->pixbuf);
3027 *-----------------------------------------------------------------------------
3029 *-----------------------------------------------------------------------------
3032 static void pan_window_message(PanWindow *pw, const gchar *text)
3042 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
3055 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
3057 size += pi->fd->size;
3062 ss = text_from_size_abrev(size);
3063 buf = g_strdup_printf(_("%d images, %s"), count, ss);
3065 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
3069 static ImageWindow *pan_window_active_image(PanWindow *pw)
3071 if (pw->fs) return pw->fs->imd;
3076 static void pan_window_zoom_limit(PanWindow *pw)
3082 case LAYOUT_SIZE_THUMB_DOTS:
3083 case LAYOUT_SIZE_THUMB_NONE:
3084 case LAYOUT_SIZE_THUMB_SMALL:
3085 case LAYOUT_SIZE_THUMB_NORMAL:
3087 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
3091 case LAYOUT_SIZE_THUMB_LARGE:
3094 case LAYOUT_SIZE_10:
3095 case LAYOUT_SIZE_25:
3098 case LAYOUT_SIZE_33:
3099 case LAYOUT_SIZE_50:
3100 case LAYOUT_SIZE_100:
3106 image_zoom_set_limits(pw->imd, min, 32.0);
3109 static gint pan_window_layout_update_idle_cb(gpointer data)
3111 PanWindow *pw = data;
3117 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
3119 if (!pw->cache_list && !pw->cache_todo)
3121 pan_cache_fill(pw, pw->path);
3124 pan_window_message(pw, _("Reading dimensions..."));
3128 if (pan_cache_step(pw))
3132 if (pw->cache_count == pw->cache_total)
3134 pan_window_message(pw, _("Sorting images..."));
3136 else if (pw->cache_tick > 9)
3140 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
3141 pw->cache_total - pw->cache_count);
3142 pan_window_message(pw, buf);
3152 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3154 pan_window_zoom_limit(pw);
3156 if (width > 0 && height > 0)
3160 image_set_image_as_tiles(pw->imd, width, height,
3161 PAN_TILE_SIZE, PAN_TILE_SIZE, 8,
3162 pan_window_request_tile_cb,
3163 pan_window_dispose_tile_cb, pw, 1.0);
3165 if (scroll_x == 0 && scroll_y == 0)
3173 image_scroll_to_point(pw->imd, scroll_x, scroll_y, align, align);
3176 pan_window_message(pw, NULL);
3183 static void pan_window_layout_update_idle(PanWindow *pw)
3185 if (pw->idle_id == -1)
3187 pan_window_message(pw, _("Sorting images..."));
3188 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3193 *-----------------------------------------------------------------------------
3194 * pan window keyboard
3195 *-----------------------------------------------------------------------------
3198 static const gchar *pan_menu_click_path(PanWindow *pw)
3200 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3204 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3206 PanWindow *pw = data;
3209 imd = pan_window_active_image(pw);
3210 gdk_window_get_origin(imd->image->window, x, y);
3211 popup_menu_position_clamp(menu, x, y, 0);
3214 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3216 PanWindow *pw = data;
3219 gint stop_signal = FALSE;
3225 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(pw->imd->widget));
3227 imd = pan_window_active_image(pw);
3228 path = pan_menu_click_path(pw);
3232 switch (event->keyval)
3234 case GDK_Left: case GDK_KP_Left:
3238 case GDK_Right: case GDK_KP_Right:
3242 case GDK_Up: case GDK_KP_Up:
3246 case GDK_Down: case GDK_KP_Down:
3250 case GDK_Page_Up: case GDK_KP_Page_Up:
3251 image_scroll(imd, 0, 0-imd->vis_height / 2);
3253 case GDK_Page_Down: case GDK_KP_Page_Down:
3254 image_scroll(imd, 0, imd->vis_height / 2);
3256 case GDK_Home: case GDK_KP_Home:
3257 image_scroll(imd, 0-imd->vis_width / 2, 0);
3259 case GDK_End: case GDK_KP_End:
3260 image_scroll(imd, imd->vis_width / 2, 0);
3265 if (focused && !(event->state & GDK_CONTROL_MASK) )
3266 switch (event->keyval)
3268 case '+': case '=': case GDK_KP_Add:
3269 image_zoom_adjust(imd, ZOOM_INCREMENT);
3271 case '-': case GDK_KP_Subtract:
3272 image_zoom_adjust(imd, -ZOOM_INCREMENT);
3274 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3275 image_zoom_set(imd, 1.0);
3278 image_zoom_set(imd, 2.0);
3281 image_zoom_set(imd, 3.0);
3284 image_zoom_set(imd, 4.0);
3287 image_zoom_set(imd, -4.0);
3290 image_zoom_set(imd, -3.0);
3293 image_zoom_set(imd, -2.0);
3297 pan_fullscreen_toggle(pw, FALSE);
3301 pan_overlay_toggle(pw);
3303 case GDK_Delete: case GDK_KP_Delete:
3308 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3315 pan_fullscreen_toggle(pw, TRUE);
3318 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3320 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3326 menu = pan_popup_menu(pw);
3327 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3332 if (event->state & GDK_CONTROL_MASK)
3335 switch (event->keyval)
3368 if (path) file_util_copy(path, NULL, NULL, imd->widget);
3371 if (path) file_util_move(path, NULL, NULL, imd->widget);
3374 if (path) file_util_rename(path, NULL, imd->widget);
3377 if (path) file_util_delete(path, NULL, imd->widget);
3380 if (path) info_window_new(path, NULL);
3383 pan_window_close(pw);
3386 if (n != -1 && path)
3388 pan_fullscreen_toggle(pw, TRUE);
3389 start_editor_from_file(n, path);
3393 else if (event->state & GDK_SHIFT_MASK)
3400 switch (event->keyval)
3405 pan_fullscreen_toggle(pw, TRUE);
3408 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3410 gtk_widget_grab_focus(pw->imd->widget);
3411 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3420 if (x != 0 || y!= 0)
3422 keyboard_scroll_calc(&x, &y, event);
3423 image_scroll(imd, x, y);
3430 *-----------------------------------------------------------------------------
3432 *-----------------------------------------------------------------------------
3435 static void pan_info_update(PanWindow *pw, PanItem *pi)
3441 gint x1, y1, x2, y2, x3, y3;
3444 if (pw->click_pi == pi) return;
3445 if (pi && !pi->fd) pi = NULL;
3447 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3452 printf("info set to %s\n", pi->fd->path);
3454 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3456 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3457 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3458 pan_item_set_key(pbox, "info");
3460 if (pi->type == ITEM_THUMB && pi->pixbuf)
3462 w = gdk_pixbuf_get_width(pi->pixbuf);
3463 h = gdk_pixbuf_get_height(pi->pixbuf);
3465 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3466 y1 = pi->y + (pi->height - h) / 2 + 8;
3470 x1 = pi->x + pi->width - 8;
3478 triangle_rect_region(x1, y1, x2, y2, x3, y3,
3481 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3482 x1, y1, x2, y2, x3, y3,
3483 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3484 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3485 pan_item_set_key(p, "info");
3486 pan_item_added(pw, p);
3488 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3489 _("Filename:"), TEXT_ATTR_BOLD,
3490 PAN_POPUP_TEXT_COLOR, 255);
3491 pan_item_set_key(plabel, "info");
3492 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3493 pi->fd->name, TEXT_ATTR_NONE,
3494 PAN_POPUP_TEXT_COLOR, 255);
3495 pan_item_set_key(p, "info");
3496 pan_item_size_by_item(pbox, p, 0);
3498 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3499 _("Date:"), TEXT_ATTR_BOLD,
3500 PAN_POPUP_TEXT_COLOR, 255);
3501 pan_item_set_key(plabel, "info");
3502 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3503 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3504 PAN_POPUP_TEXT_COLOR, 255);
3505 pan_item_set_key(p, "info");
3506 pan_item_size_by_item(pbox, p, 0);
3508 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3509 _("Size:"), TEXT_ATTR_BOLD,
3510 PAN_POPUP_TEXT_COLOR, 255);
3511 pan_item_set_key(plabel, "info");
3512 buf = text_from_size(pi->fd->size);
3513 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3514 buf, TEXT_ATTR_NONE,
3515 PAN_POPUP_TEXT_COLOR, 255);
3517 pan_item_set_key(p, "info");
3518 pan_item_size_by_item(pbox, p, 0);
3520 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3521 pan_item_added(pw, pbox);
3526 *-----------------------------------------------------------------------------
3528 *-----------------------------------------------------------------------------
3531 static void pan_search_status(PanWindow *pw, const gchar *text)
3533 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3536 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3544 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3546 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3547 if (!list) return FALSE;
3549 found = g_list_find(list, pw->click_pi);
3550 if (found && found->next)
3552 found = found->next;
3560 pan_info_update(pw, pi);
3561 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3563 buf = g_strdup_printf("%s ( %d / %d )",
3564 (path[0] == '/') ? _("path found") : _("filename found"),
3565 g_list_index(list, pi) + 1,
3566 g_list_length(list));
3567 pan_search_status(pw, buf);
3575 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3583 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3585 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3586 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3591 needle = g_utf8_strdown(text, -1);
3592 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3595 if (!list) return FALSE;
3597 found = g_list_find(list, pw->click_pi);
3598 if (found && found->next)
3600 found = found->next;
3608 pan_info_update(pw, pi);
3609 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3611 buf = g_strdup_printf("%s ( %d / %d )",
3613 g_list_index(list, pi) + 1,
3614 g_list_length(list));
3615 pan_search_status(pw, buf);
3623 static gint valid_date_separator(gchar c)
3625 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3628 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type, gint year, gint month, gint day)
3633 work = g_list_last(pw->list);
3641 if (pi->fd && (pi->type == type || type == ITEM_NONE))
3645 tl = localtime(&pi->fd->date);
3650 match = (tl->tm_year == year - 1900);
3651 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3652 if (match && day > 0) match = (tl->tm_mday == day);
3654 if (match) list = g_list_prepend(list, pi);
3659 return g_list_reverse(list);
3662 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3679 if (!text) return FALSE;
3681 ptr = (gchar *)text;
3682 while (*ptr != '\0')
3684 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3689 if (t == -1) return FALSE;
3691 if (!lt) return FALSE;
3693 if (valid_date_separator(*text))
3696 mptr = (gchar *)text;
3700 year = (gint)strtol(text, &mptr, 10);
3701 if (mptr == text) return FALSE;
3704 if (*mptr != '\0' && valid_date_separator(*mptr))
3709 month = strtol(mptr, &dptr, 10);
3712 if (valid_date_separator(*dptr))
3714 month = lt->tm_mon + 1;
3722 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3726 day = strtol(dptr, &eptr, 10);
3736 year = lt->tm_year + 1900;
3738 else if (year < 100)
3747 month < -1 || month == 0 || month > 12 ||
3748 day < -1 || day == 0 || day > 31) return FALSE;
3750 t = date_to_time(year, month, day);
3751 if (t < 0) return FALSE;
3753 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3755 list = pan_search_by_date_val(pw, type, year, month, day);
3758 found = g_list_find(list, pw->click_pi);
3759 if (found && found->next)
3761 found = found->next;
3772 pan_info_update(pw, pi);
3773 image_scroll_to_point(pw->imd,
3774 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3780 buf = date_value_string(t, DATE_LENGTH_MONTH);
3785 buf = g_strdup_printf("%d %s", day, tmp);
3791 buf = date_value_string(t, DATE_LENGTH_YEAR);
3796 buf_count = g_strdup_printf("( %d / %d )",
3797 g_list_index(list, pi) + 1,
3798 g_list_length(list));
3802 buf_count = g_strdup_printf("(%s)", _("no match"));
3805 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
3808 pan_search_status(pw, message);
3816 static void pan_search_activate_cb(const gchar *text, gpointer data)
3818 PanWindow *pw = data;
3822 tab_completion_append_to_history(pw->search_entry, text);
3824 if (pan_search_by_path(pw, text)) return;
3826 if ((pw->layout == LAYOUT_TIMELINE ||
3827 pw->layout == LAYOUT_CALENDAR) &&
3828 pan_search_by_date(pw, text))
3833 if (pan_search_by_partial(pw, text)) return;
3835 pan_search_status(pw, _("no match"));
3838 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3840 PanWindow *pw = data;
3843 visible = GTK_WIDGET_VISIBLE(pw->search_box);
3844 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3848 gtk_widget_hide(pw->search_box);
3849 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3853 gtk_widget_show(pw->search_box);
3854 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3855 gtk_widget_grab_focus(pw->search_entry);
3861 *-----------------------------------------------------------------------------
3862 * view window main routines
3863 *-----------------------------------------------------------------------------
3866 static void button_cb(ImageWindow *imd, gint button, guint32 time,
3867 gdouble x, gdouble y, guint state, gpointer data)
3869 PanWindow *pw = data;
3877 rx = (double)(pw->imd->x_scroll + x - pw->imd->x_offset) / pw->imd->scale;
3878 ry = (double)(pw->imd->y_scroll + y - pw->imd->y_offset) / pw->imd->scale;
3881 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3887 pan_info_update(pw, pi);
3889 if (!pi && pw->layout == LAYOUT_CALENDAR)
3891 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
3892 pan_calendar_update(pw, pi);
3898 pan_info_update(pw, pi);
3899 menu = pan_popup_menu(pw);
3900 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, time);
3907 static void scroll_cb(ImageWindow *imd, GdkScrollDirection direction, guint32 time,
3908 gdouble x, gdouble y, guint state, gpointer data)
3911 PanWindow *pw = data;
3916 h = imd->vis_height;
3918 if (!(state & GDK_SHIFT_MASK))
3924 if (state & GDK_CONTROL_MASK)
3929 image_zoom_adjust_at_point(imd, ZOOM_INCREMENT, x, y);
3931 case GDK_SCROLL_DOWN:
3932 image_zoom_adjust_at_point(imd, -ZOOM_INCREMENT, x, y);
3943 image_scroll(imd, 0, -h);
3945 case GDK_SCROLL_DOWN:
3946 image_scroll(imd, 0, h);
3948 case GDK_SCROLL_LEFT:
3949 image_scroll(imd, -w, 0);
3951 case GDK_SCROLL_RIGHT:
3952 image_scroll(imd, w, 0);
3960 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
3962 image_set_button_func(imd, button_cb, pw);
3963 image_set_scroll_func(imd, scroll_cb, pw);
3966 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
3968 PanWindow *pw = data;
3973 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
3975 if (force_off && !pw->fs) return;
3979 fullscreen_stop(pw->fs);
3980 pw->imd = pw->imd_normal;
3984 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
3986 pan_image_set_buttons(pw, pw->fs->imd);
3987 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
3988 G_CALLBACK(pan_window_key_press_cb), pw);
3990 pw->imd = pw->fs->imd;
3994 static void pan_overlay_toggle(PanWindow *pw)
3998 imd = pan_window_active_image(pw);
4000 if (pw->overlay_id == -1)
4002 pw->overlay_id = image_overlay_info_enable(imd);
4006 image_overlay_info_disable(imd, pw->overlay_id);
4007 pw->overlay_id = -1;
4012 static void pan_window_image_update_cb(ImageWindow *imd, gpointer data)
4014 PanWindow *pw = data;
4017 text = image_zoom_get_as_text(imd);
4018 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4022 static void pan_window_image_scroll_notify_cb(ImageWindow *imd, gint x, gint y,
4023 gint width, gint height, gpointer data)
4025 PanWindow *pw = data;
4028 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4029 adj->page_size = (gdouble)imd->vis_width / imd->scale;
4030 adj->page_increment = adj->page_size / 2.0;
4031 adj->step_increment = 48.0 / imd->scale;
4033 adj->upper = MAX((gdouble)width + adj->page_size, 1.0);
4034 adj->value = (gdouble)x;
4036 pref_signal_block_data(pw->scrollbar_h, pw);
4037 gtk_adjustment_changed(adj);
4038 pref_signal_unblock_data(pw->scrollbar_h, pw);
4040 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4041 adj->page_size = (gdouble)imd->vis_height / imd->scale;
4042 adj->page_increment = adj->page_size / 2.0;
4043 adj->step_increment = 48.0 / imd->scale;
4045 adj->upper = MAX((gdouble)height + adj->page_size, 1.0);
4046 adj->value = (gdouble)y;
4048 pref_signal_block_data(pw->scrollbar_v, pw);
4049 gtk_adjustment_changed(adj);
4050 pref_signal_unblock_data(pw->scrollbar_v, pw);
4052 // printf("scrolled to %d,%d @ %d x %d\n", x, y, width, height);
4055 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4057 PanWindow *pw = data;
4060 if (!pw->imd->scale) return;
4062 x = (gint)gtk_range_get_value(range);
4064 image_scroll_to_point(pw->imd, x, (gint)((gdouble)pw->imd->y_scroll / pw->imd->scale), 0.0, 0.0);
4067 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4069 PanWindow *pw = data;
4072 if (!pw->imd->scale) return;
4074 y = (gint)gtk_range_get_value(range);
4076 image_scroll_to_point(pw->imd, (gint)((gdouble)pw->imd->x_scroll / pw->imd->scale), y, 0.0, 0.0);
4079 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4081 PanWindow *pw = data;
4083 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4084 pan_window_layout_update_idle(pw);
4087 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4089 PanWindow *pw = data;
4091 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4092 pan_window_layout_update_idle(pw);
4095 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4097 PanWindow *pw = data;
4100 path = remove_trailing_slash(new_text);
4101 parse_out_relatives(path);
4105 warning_dialog(_("Folder not found"),
4106 _("The entered path is not a folder"),
4107 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4111 tab_completion_append_to_history(pw->path_entry, path);
4114 pw->path = g_strdup(path);
4116 pan_window_layout_update_idle(pw);
4119 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4121 PanWindow *pw = data;
4124 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4126 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4127 pan_window_entry_activate_cb(text, pw);
4131 static void pan_window_close(PanWindow *pw)
4133 pan_window_list = g_list_remove(pan_window_list, pw);
4135 if (pw->idle_id != -1)
4137 g_source_remove(pw->idle_id);
4140 pan_fullscreen_toggle(pw, TRUE);
4141 gtk_widget_destroy(pw->window);
4143 pan_window_items_free(pw);
4150 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4152 PanWindow *pw = data;
4154 pan_window_close(pw);
4158 static void pan_window_new_real(const gchar *path)
4167 GdkGeometry geometry;
4169 pw = g_new0(PanWindow, 1);
4171 pw->path = g_strdup(path);
4172 pw->layout = LAYOUT_TIMELINE;
4173 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4174 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4175 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4179 pw->overlay_id = -1;
4182 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4184 geometry.min_width = 8;
4185 geometry.min_height = 8;
4186 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4188 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4189 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
4190 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4191 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4193 window_set_icon(pw->window, NULL, NULL);
4195 vbox = gtk_vbox_new(FALSE, 0);
4196 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4197 gtk_widget_show(vbox);
4199 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4201 pref_spacer(box, 0);
4202 pref_label_new(box, _("Location:"));
4203 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
4204 pan_window_entry_activate_cb, pw);
4205 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4206 G_CALLBACK(pan_window_entry_change_cb), pw);
4207 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4208 gtk_widget_show(combo);
4210 combo = gtk_combo_box_new_text();
4211 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4212 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4213 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4214 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4215 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4217 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4218 g_signal_connect(G_OBJECT(combo), "changed",
4219 G_CALLBACK(pan_window_layout_change_cb), pw);
4220 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4221 gtk_widget_show(combo);
4223 combo = gtk_combo_box_new_text();
4224 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4225 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4226 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4227 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4228 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4229 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4230 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4231 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4232 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4233 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4235 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4236 g_signal_connect(G_OBJECT(combo), "changed",
4237 G_CALLBACK(pan_window_layout_size_cb), pw);
4238 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4239 gtk_widget_show(combo);
4241 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4242 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4243 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4245 pw->imd = image_new(TRUE);
4246 pw->imd_normal = pw->imd;
4248 if (black_window_background) image_background_set_black(pw->imd, TRUE);
4249 image_set_update_func(pw->imd, pan_window_image_update_cb, pw);
4251 image_set_scroll_notify_func(pw->imd, pan_window_image_scroll_notify_cb, pw);
4254 gtk_box_pack_start(GTK_BOX(vbox), pw->imd->widget, TRUE, TRUE, 0);
4256 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4257 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4258 gtk_widget_show(pw->imd->widget);
4260 pan_window_dnd_init(pw);
4262 pan_image_set_buttons(pw, pw->imd);
4264 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4265 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4266 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4267 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4268 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4269 gtk_widget_show(pw->scrollbar_h);
4271 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4272 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4273 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4274 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4275 0, GTK_FILL | GTK_EXPAND, 0, 0);
4276 gtk_widget_show(pw->scrollbar_v);
4280 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4281 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4283 pref_spacer(pw->search_box, 0);
4284 pref_label_new(pw->search_box, _("Find:"));
4286 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4287 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4288 gtk_widget_show(hbox);
4290 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4291 pan_search_activate_cb, pw);
4292 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4293 gtk_widget_show(combo);
4295 pw->search_label = gtk_label_new("");
4296 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4297 gtk_widget_show(pw->search_label);
4301 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4303 frame = gtk_frame_new(NULL);
4304 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4305 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4306 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4307 gtk_widget_show(frame);
4309 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4310 gtk_container_add(GTK_CONTAINER(frame), hbox);
4311 gtk_widget_show(hbox);
4313 pref_spacer(hbox, 0);
4314 pw->label_message = pref_label_new(hbox, "");
4316 frame = gtk_frame_new(NULL);
4317 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4318 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4319 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4320 gtk_widget_show(frame);
4322 pw->label_zoom = gtk_label_new("");
4323 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4324 gtk_widget_show(pw->label_zoom);
4326 pw->search_button = gtk_toggle_button_new();
4327 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4328 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4329 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4330 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4331 gtk_widget_show(hbox);
4332 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4333 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4334 gtk_widget_show(pw->search_button_arrow);
4335 pref_label_new(hbox, _("Find"));
4337 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4338 gtk_widget_show(pw->search_button);
4339 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4340 G_CALLBACK(pan_search_toggle_cb), pw);
4342 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4343 G_CALLBACK(pan_window_delete_cb), pw);
4344 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4345 G_CALLBACK(pan_window_key_press_cb), pw);
4347 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4349 pan_window_layout_update_idle(pw);
4351 gtk_widget_grab_focus(pw->imd->widget);
4352 gtk_widget_show(pw->window);
4354 pan_window_list = g_list_append(pan_window_list, pw);
4358 *-----------------------------------------------------------------------------
4359 * peformance warnings
4360 *-----------------------------------------------------------------------------
4363 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4367 generic_dialog_close(gd);
4369 pan_window_new_real(path);
4373 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4377 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4378 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4381 static gint pan_warning(const gchar *path)
4387 GtkWidget *ct_button;
4390 if (enable_thumb_caching &&
4391 thumbnail_spec_standard) return FALSE;
4393 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4394 if (hide_dlg) return FALSE;
4396 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4398 gd->data = g_strdup(path);
4399 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4400 pan_warning_ok_cb, TRUE);
4402 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4403 _("Pan view performance may be poor."),
4404 _("To improve performance of thumbnails in the pan view the"
4405 " following options can be enabled. Note that both options"
4406 " must be enabled to notice a change in performance."));
4408 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4409 pref_spacer(group, PREF_PAD_INDENT);
4410 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4412 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4413 enable_thumb_caching, &enable_thumb_caching);
4414 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4415 thumbnail_spec_standard, &thumbnail_spec_standard);
4416 pref_checkbox_link_sensitivity(ct_button, button);
4420 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4421 G_CALLBACK(pan_warning_hide_cb), NULL);
4423 gtk_widget_show(gd->dialog);
4430 *-----------------------------------------------------------------------------
4432 *-----------------------------------------------------------------------------
4435 void pan_window_new(const gchar *path)
4437 if (pan_warning(path)) return;
4439 pan_window_new_real(path);
4443 *-----------------------------------------------------------------------------
4445 *-----------------------------------------------------------------------------
4448 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4450 PanWindow *pw = data;
4453 path = pan_menu_click_path(pw);
4456 pan_fullscreen_toggle(pw, TRUE);
4457 view_window_new(path);
4461 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4467 pw = submenu_item_get_data(widget);
4468 n = GPOINTER_TO_INT(data);
4471 path = pan_menu_click_path(pw);
4474 pan_fullscreen_toggle(pw, TRUE);
4475 start_editor_from_file(n, path);
4479 static void pan_info_cb(GtkWidget *widget, gpointer data)
4481 PanWindow *pw = data;
4484 path = pan_menu_click_path(pw);
4485 if (path) info_window_new(path, NULL);
4488 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4490 PanWindow *pw = data;
4492 image_zoom_adjust(pan_window_active_image(pw), ZOOM_INCREMENT);
4495 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4497 PanWindow *pw = data;
4499 image_zoom_adjust(pan_window_active_image(pw), -ZOOM_INCREMENT);
4502 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4504 PanWindow *pw = data;
4506 image_zoom_set(pan_window_active_image(pw), 1.0);
4509 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4511 PanWindow *pw = data;
4514 path = pan_menu_click_path(pw);
4515 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4518 static void pan_move_cb(GtkWidget *widget, gpointer data)
4520 PanWindow *pw = data;
4523 path = pan_menu_click_path(pw);
4524 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4527 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4529 PanWindow *pw = data;
4532 path = pan_menu_click_path(pw);
4533 if (path) file_util_rename(path, NULL, pw->imd->widget);
4536 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4538 PanWindow *pw = data;
4541 path = pan_menu_click_path(pw);
4542 if (path) file_util_delete(path, NULL, pw->imd->widget);
4545 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4547 PanWindow *pw = data;
4549 pan_fullscreen_toggle(pw, FALSE);
4552 static void pan_close_cb(GtkWidget *widget, gpointer data)
4554 PanWindow *pw = data;
4556 pan_window_close(pw);
4559 static GtkWidget *pan_popup_menu(PanWindow *pw)
4565 active = (pw->click_pi != NULL);
4567 menu = popup_menu_short_lived();
4569 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4570 G_CALLBACK(pan_zoom_in_cb), pw);
4571 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4572 G_CALLBACK(pan_zoom_out_cb), pw);
4573 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4574 G_CALLBACK(pan_zoom_1_1_cb), pw);
4575 menu_item_add_divider(menu);
4577 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4578 gtk_widget_set_sensitive(item, active);
4580 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4581 G_CALLBACK(pan_info_cb), pw);
4583 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4584 G_CALLBACK(pan_new_window_cb), pw);
4586 menu_item_add_divider(menu);
4587 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4588 G_CALLBACK(pan_copy_cb), pw);
4589 menu_item_add_sensitive(menu, _("_Move..."), active,
4590 G_CALLBACK(pan_move_cb), pw);
4591 menu_item_add_sensitive(menu, _("_Rename..."), active,
4592 G_CALLBACK(pan_rename_cb), pw);
4593 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4594 G_CALLBACK(pan_delete_cb), pw);
4596 menu_item_add_divider(menu);
4600 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4604 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4607 menu_item_add_divider(menu);
4608 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4614 *-----------------------------------------------------------------------------
4616 *-----------------------------------------------------------------------------
4619 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4621 GtkSelectionData *selection_data, guint info,
4622 guint time, gpointer data)
4624 PanWindow *pw = data;
4627 if (gtk_drag_get_source_widget(context) == pw->imd->image) return;
4631 if (info == TARGET_URI_LIST)
4635 list = uri_list_from_text(selection_data->data, TRUE);
4636 if (list && isdir((gchar *)list->data))
4638 gchar *path = list->data;
4641 pw->path = g_strdup(path);
4643 pan_window_layout_update_idle(pw);
4646 path_list_free(list);
4650 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4651 GtkSelectionData *selection_data, guint info,
4652 guint time, gpointer data)
4654 PanWindow *pw = data;
4657 path = pan_menu_click_path(pw);
4667 case TARGET_URI_LIST:
4670 case TARGET_TEXT_PLAIN:
4675 list = g_list_append(NULL, (gchar *)path);
4676 text = uri_text_from_list(list, &len, plain_text);
4680 gtk_selection_data_set (selection_data, selection_data->target,
4687 gtk_selection_data_set (selection_data, selection_data->target,
4692 static void pan_window_dnd_init(PanWindow *pw)
4698 gtk_drag_source_set(imd->image, GDK_BUTTON2_MASK,
4699 dnd_file_drag_types, dnd_file_drag_types_count,
4700 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4701 g_signal_connect(G_OBJECT(imd->image), "drag_data_get",
4702 G_CALLBACK(pan_window_set_dnd_data), pw);
4704 gtk_drag_dest_set(imd->image,
4705 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4706 dnd_file_drop_types, dnd_file_drop_types_count,
4707 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4708 g_signal_connect(G_OBJECT(imd->image), "drag_data_received",
4709 G_CALLBACK(pan_window_get_dnd_data), pw);
4713 *-----------------------------------------------------------------------------
4714 * maintenance (for rename, move, remove)
4715 *-----------------------------------------------------------------------------