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"
21 #include "image-load.h"
25 #include "pixbuf-renderer.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 PixbufRenderer *pr_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;
229 typedef struct _PanCacheData PanCacheData;
230 struct _PanCacheData {
236 static GList *pan_window_list = NULL;
239 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend);
241 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
243 static GtkWidget *pan_popup_menu(PanWindow *pw);
244 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off);
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);
881 pw->search_pi = NULL;
884 static PanItem *pan_item_new_thumb(PanWindow *pw, FileData *fd, gint x, gint y)
888 pi = g_new0(PanItem, 1);
889 pi->type = ITEM_THUMB;
893 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
894 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
900 pw->list = g_list_prepend(pw->list, pi);
905 static PanItem *pan_item_new_box(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
907 guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
908 guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
912 pi = g_new0(PanItem, 1);
920 pi->color_r = base_r;
921 pi->color_g = base_g;
922 pi->color_b = base_b;
923 pi->color_a = base_a;
925 pi->color2_r = bord_r;
926 pi->color2_g = bord_g;
927 pi->color2_b = bord_b;
928 pi->color2_a = bord_a;
929 pi->border = border_size;
931 pw->list = g_list_prepend(pw->list, pi);
936 static void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
940 if (!pi || pi->type != ITEM_BOX) return;
945 pi->width -= shadow[0];
946 pi->height -= shadow[0];
949 shadow = g_new0(gint, 2);
954 pi->height += offset;
960 static PanItem *pan_item_new_tri(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
961 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
962 guint8 r, guint8 g, guint8 b, guint8 a)
967 pi = g_new0(PanItem, 1);
968 pi->type = ITEM_TRIANGLE;
979 coord = g_new0(gint, 6);
989 pi->border = BORDER_NONE;
991 pw->list = g_list_prepend(pw->list, pi);
996 static void pan_item_tri_border(PanItem *pi, gint borders,
997 guint8 r, guint8 g, guint8 b, guint8 a)
999 if (!pi || pi->type != ITEM_TRIANGLE) return;
1001 pi->border = borders;
1009 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
1011 PangoLayout *layout;
1013 layout = gtk_widget_create_pango_layout(widget, NULL);
1015 if (pi->text_attr & TEXT_ATTR_MARKUP)
1017 pango_layout_set_markup(layout, pi->text, -1);
1021 if (pi->text_attr & TEXT_ATTR_BOLD ||
1022 pi->text_attr & TEXT_ATTR_HEADING)
1027 pal = pango_attr_list_new();
1028 if (pi->text_attr & TEXT_ATTR_BOLD)
1030 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
1031 pa->start_index = 0;
1032 pa->end_index = G_MAXINT;
1033 pango_attr_list_insert(pal, pa);
1035 if (pi->text_attr & TEXT_ATTR_HEADING)
1037 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
1038 pa->start_index = 0;
1039 pa->end_index = G_MAXINT;
1040 pango_attr_list_insert(pal, pa);
1042 pango_layout_set_attributes(layout, pal);
1043 pango_attr_list_unref(pal);
1046 pango_layout_set_text(layout, pi->text, -1);
1050 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
1052 PangoLayout *layout;
1054 if (!pi || !pi->text || !widget) return;
1056 layout = pan_item_text_layout(pi, widget);
1057 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
1058 g_object_unref(G_OBJECT(layout));
1060 pi->width += PAN_TEXT_BORDER_SIZE * 2;
1061 pi->height += PAN_TEXT_BORDER_SIZE * 2;
1064 static PanItem *pan_item_new_text(PanWindow *pw, gint x, gint y, const gchar *text, TextAttrType attr,
1065 guint8 r, guint8 g, guint8 b, guint8 a)
1069 pi = g_new0(PanItem, 1);
1070 pi->type = ITEM_TEXT;
1073 pi->text = g_strdup(text);
1074 pi->text_attr = attr;
1081 pan_item_text_compute_size(pi, (GtkWidget *)pw->pr);
1083 pw->list = g_list_prepend(pw->list, pi);
1088 static void pan_item_set_key(PanItem *pi, const gchar *key)
1095 pi->key = g_strdup(key);
1099 static void pan_item_added(PanWindow *pw, PanItem *pi)
1102 pixbuf_renderer_area_changed(pw->pr, pi->x, pi->y, pi->width, pi->height);
1105 static void pan_item_remove(PanWindow *pw, PanItem *pi)
1109 if (pw->click_pi == pi) pw->click_pi = NULL;
1110 if (pw->queue_pi == pi) pw->queue_pi = NULL;
1111 if (pw->search_pi == pi) pw->search_pi = NULL;
1112 pw->queue = g_list_remove(pw->queue, pi);
1114 pw->list = g_list_remove(pw->list, pi);
1115 pixbuf_renderer_area_changed(pw->pr, pi->x, pi->y, pi->width, pi->height);
1119 static void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
1121 if (!pi || !child) return;
1123 if (pi->x + pi->width < child->x + child->width + border)
1124 pi->width = child->x + child->width + border - pi->x;
1126 if (pi->y + pi->height < child->y + child->height + border)
1127 pi->height = child->y + child->height + border - pi->y;
1130 static void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
1134 if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
1135 if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
1138 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
1145 if (!pi->fd) return;
1147 work = pw->cache_list;
1156 path = ((FileData *)pc)->path;
1158 if (pc->cd && pc->cd->dimensions &&
1159 path && strcmp(path, pi->fd->path) == 0)
1161 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
1162 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
1164 pw->cache_list = g_list_remove(pw->cache_list, pc);
1165 cache_sim_data_free(pc->cd);
1166 file_data_free((FileData *)pc);
1172 static PanItem *pan_item_new_image(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
1176 pi = g_new0(PanItem, 1);
1177 pi->type = ITEM_IMAGE;
1182 pan_item_image_find_size(pw, pi, w, h);
1184 pw->list = g_list_prepend(pw->list, pi);
1189 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
1193 if (!key) return NULL;
1195 work = g_list_last(pw->list);
1201 if ((pi->type == type || type == ITEM_NONE) &&
1202 pi->key && strcmp(pi->key, key) == 0)
1212 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1213 static GList *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1214 gint ignore_case, gint partial)
1219 if (!path) return NULL;
1220 if (partial && path[0] == '/') return NULL;
1222 work = g_list_last(pw->list);
1228 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1234 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
1236 else if (pi->fd->name)
1244 haystack = g_utf8_strdown(pi->fd->name, -1);
1245 match = (strstr(haystack, path) != NULL);
1250 if (strstr(pi->fd->name, path)) match = TRUE;
1253 else if (ignore_case)
1255 if (strcasecmp(path, pi->fd->name) == 0) match = TRUE;
1259 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
1263 if (match) list = g_list_prepend(list, pi);
1268 return g_list_reverse(list);
1271 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y, const gchar *key)
1281 if ((pi->type == type || type == ITEM_NONE) &&
1282 x >= pi->x && x < pi->x + pi->width &&
1283 y >= pi->y && y < pi->y + pi->height &&
1284 (!key || (pi->key && strcmp(pi->key, key) == 0)))
1295 *-----------------------------------------------------------------------------
1297 *-----------------------------------------------------------------------------
1300 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend)
1302 GList *flist = NULL;
1303 GList *dlist = NULL;
1307 filelist_read(path, &flist, &dlist);
1308 if (sort != SORT_NONE)
1310 flist = filelist_sort(flist, sort, ascend);
1311 dlist = filelist_sort(dlist, sort, ascend);
1321 folders = g_list_remove(folders, fd);
1323 if (filelist_read(fd->path, &flist, &dlist))
1325 if (sort != SORT_NONE)
1327 flist = filelist_sort(flist, sort, ascend);
1328 dlist = filelist_sort(dlist, sort, ascend);
1331 result = g_list_concat(result, flist);
1332 folders = g_list_concat(dlist, folders);
1341 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1349 list = pan_window_layout_list(path, SORT_NAME, TRUE);
1351 grid_size = (gint)sqrt((double)g_list_length(list));
1352 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1354 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1358 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1363 *width = PAN_FOLDER_BOX_BORDER * 2;
1364 *height = PAN_FOLDER_BOX_BORDER * 2;
1377 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1379 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1381 x += pi->width + PAN_THUMB_GAP;
1382 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1391 pi = pan_item_new_thumb(pw, fd, x, y);
1393 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1397 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1400 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1406 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1409 gint x1, y1, x2, y2;
1424 if (x1 > pi->x) x1 = pi->x;
1425 if (y1 > pi->y) y1 = pi->y;
1426 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1427 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1430 x1 -= PAN_FOLDER_BOX_BORDER;
1431 y1 -= PAN_FOLDER_BOX_BORDER;
1432 x2 += PAN_FOLDER_BOX_BORDER;
1433 y2 += PAN_FOLDER_BOX_BORDER;
1446 if (pi->type == ITEM_TRIANGLE && pi->data)
1460 if (width) *width = x2 - x1;
1461 if (height) *height = y2 - y1;
1464 typedef struct _FlowerGroup FlowerGroup;
1465 struct _FlowerGroup {
1478 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1482 work = group->items;
1500 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1501 gint *result_x, gint *result_y)
1507 radius = parent->circumference / (2*PI);
1508 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1510 a = 2*PI * group->diameter / parent->circumference;
1512 x = (gint)((double)radius * cos(parent->angle + a / 2));
1513 y = (gint)((double)radius * sin(parent->angle + a / 2));
1520 x += parent->width / 2;
1521 y += parent->height / 2;
1523 x -= group->width / 2;
1524 y -= group->height / 2;
1530 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1537 if (parent && parent->children)
1539 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1547 pan_window_layout_compute_folder_flower_move(group, x, y);
1552 gint px, py, gx, gy;
1553 gint x1, y1, x2, y2;
1555 px = parent->x + parent->width / 2;
1556 py = parent->y + parent->height / 2;
1558 gx = group->x + group->width / 2;
1559 gy = group->y + group->height / 2;
1564 x2 = MAX(px, gx + 5);
1565 y2 = MAX(py, gy + 5);
1567 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1568 px, py, gx, gy, gx + 5, gy + 5,
1570 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1574 pw->list = g_list_concat(group->items, pw->list);
1575 group->items = NULL;
1577 group->circumference = 0;
1578 work = group->children;
1586 group->circumference += child->diameter;
1589 work = g_list_last(group->children);
1597 pan_window_layout_compute_folder_flower_build(pw, child, group);
1600 g_list_free(group->children);
1604 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1617 if (!filelist_read(path, &f, &d)) return NULL;
1618 if (!f && !d) return NULL;
1620 f = filelist_sort(f, SORT_NAME, TRUE);
1621 d = filelist_sort(d, SORT_NAME, TRUE);
1623 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1624 PAN_TEXT_COLOR, 255);
1626 y += pi_box->height;
1628 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1630 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1631 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1632 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1633 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1635 x += PAN_FOLDER_BOX_BORDER;
1636 y += PAN_FOLDER_BOX_BORDER;
1638 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1652 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1654 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1655 x += pi->width + PAN_THUMB_GAP;
1656 if (pi->height > y_height) y_height = pi->height;
1660 pi = pan_item_new_thumb(pw, fd, x, y);
1661 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1662 y_height = PAN_THUMB_SIZE;
1666 if (grid_count >= grid_size)
1670 y += y_height + PAN_THUMB_GAP;
1674 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1679 group = g_new0(FlowerGroup, 1);
1680 group->items = pw->list;
1683 group->width = pi_box->width;
1684 group->height = pi_box->y + pi_box->height;
1685 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1687 group->children = NULL;
1698 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1699 if (child) group->children = g_list_prepend(group->children, child);
1707 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1708 gint *width, gint *height,
1709 gint *scroll_x, gint *scroll_y)
1714 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1715 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1717 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1719 list = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1722 PanItem *pi = list->data;
1723 *scroll_x = pi->x + pi->width / 2;
1724 *scroll_y = pi->y + pi->height / 2;
1729 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1730 gint *x, gint *y, gint *level,
1732 gint *width, gint *height)
1740 if (!filelist_read(path, &f, &d)) return;
1741 if (!f && !d) return;
1743 f = filelist_sort(f, SORT_NAME, TRUE);
1744 d = filelist_sort(d, SORT_NAME, TRUE);
1746 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1748 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1749 PAN_TEXT_COLOR, 255);
1751 *y += pi_box->height;
1753 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1755 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1756 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1757 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1758 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1760 *x += PAN_FOLDER_BOX_BORDER;
1761 *y += PAN_FOLDER_BOX_BORDER;
1772 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1774 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1775 *x += pi->width + PAN_THUMB_GAP;
1776 if (pi->height > y_height) y_height = pi->height;
1780 pi = pan_item_new_thumb(pw, fd, *x, *y);
1781 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1782 y_height = PAN_THUMB_SIZE;
1785 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1788 if (f) *y = pi_box->y + pi_box->height;
1800 *level = *level + 1;
1801 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1802 pi_box, width, height);
1803 *level = *level - 1;
1808 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1810 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1811 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1813 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1816 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1823 x = PAN_FOLDER_BOX_BORDER;
1824 y = PAN_FOLDER_BOX_BORDER;
1825 w = PAN_FOLDER_BOX_BORDER * 2;
1826 h = PAN_FOLDER_BOX_BORDER * 2;
1828 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1830 if (width) *width = w;
1831 if (height) *height = h;
1835 *-----------------------------------------------------------------------------
1837 *-----------------------------------------------------------------------------
1840 #define PAN_CAL_DAY_WIDTH 100
1841 #define PAN_CAL_DAY_HEIGHT 80
1842 #define PAN_CAL_DOT_SIZE 3
1843 #define PAN_CAL_DOT_GAP 2
1844 #define PAN_CAL_DOT_COLOR 0, 0, 0
1845 #define PAN_CAL_DOT_ALPHA 32
1847 static void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
1853 gint x1, y1, x2, y2, x3, y3;
1858 while ((pi = pan_item_find_by_key(pw, ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
1860 if (!pi_day || pi_day->type != ITEM_BOX ||
1861 !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
1863 list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
1875 if (dot->type != ITEM_BOX || !dot->fd ||
1876 !dot->key || strcmp(dot->key, "dot") != 0)
1878 list = g_list_delete_link(list, node);
1886 grid = (gint)(sqrt(g_list_length(list)) + 0.5);
1888 x = pi_day->x + pi_day->width + 4;
1892 if (y + grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4 > pw->pr->image_height)
1894 y = pw->pr->image_height - (grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4);
1898 pbox = pan_item_new_box(pw, NULL, x, y, PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1900 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
1901 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
1902 pan_item_set_key(pbox, "day_bubble");
1909 buf = date_value_string(pi_day->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;
1924 x += PAN_FOLDER_BOX_BORDER;
1925 y += PAN_FOLDER_BOX_BORDER;
1939 pimg = pan_item_new_thumb(pw, file_data_new_simple(dot->fd->path), x, y);
1940 pan_item_set_key(pimg, "day_bubble");
1942 pan_item_size_by_item(pbox, pimg, PAN_FOLDER_BOX_BORDER);
1947 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1952 x = pbox->x + PAN_FOLDER_BOX_BORDER;
1953 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1959 x1 = pi_day->x + pi_day->width - 8;
1962 y2 = pbox->y + MIN(42, pbox->height);
1964 y3 = MAX(pbox->y, y2 - 30);
1965 triangle_rect_region(x1, y1, x2, y2, x3, y3,
1968 pi = pan_item_new_tri(pw, NULL, x, y, w, h,
1969 x1, y1, x2, y2, x3, y3,
1970 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
1971 pan_item_tri_border(pi, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
1972 pan_item_set_key(pi, "day_bubble");
1973 pan_item_added(pw, pi);
1975 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
1976 pan_item_added(pw, pbox);
1979 static void pan_window_layout_compute_calendar(PanWindow *pw, const gchar *path, gint *width, gint *height)
1995 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
1997 list = pan_window_layout_list(path, SORT_NONE, TRUE);
1998 list = filelist_sort(list, SORT_TIME, TRUE);
2011 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2019 if (day_max < count) day_max = count;
2023 printf("biggest day contains %d images\n", day_max);
2025 grid = (gint)(sqrt((double)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
2026 day_width = MAX(PAN_CAL_DAY_WIDTH, grid);
2027 day_height = MAX(PAN_CAL_DAY_HEIGHT, grid);
2031 FileData *fd = list->data;
2033 year = date_value(fd->date, DATE_LENGTH_YEAR);
2034 month = date_value(fd->date, DATE_LENGTH_MONTH);
2037 work = g_list_last(list);
2040 FileData *fd = work->data;
2041 end_year = date_value(fd->date, DATE_LENGTH_YEAR);
2042 end_month = date_value(fd->date, DATE_LENGTH_MONTH);
2045 *width = PAN_FOLDER_BOX_BORDER * 2;
2046 *height = PAN_FOLDER_BOX_BORDER * 2;
2048 x = PAN_FOLDER_BOX_BORDER;
2049 y = PAN_FOLDER_BOX_BORDER;
2052 while (work && (year < end_year || (year == end_year && month <= end_month)))
2063 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
2065 days = date_value(dt, DATE_LENGTH_DAY);
2066 dt = date_to_time(year, month, 1);
2067 col = date_value(dt, DATE_LENGTH_WEEK);
2070 x = PAN_FOLDER_BOX_BORDER;
2072 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
2073 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2074 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2075 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2076 buf = date_value_string(dt, DATE_LENGTH_MONTH);
2077 pi_text = pan_item_new_text(pw, x, y, buf,
2078 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2079 PAN_TEXT_COLOR, 255);
2081 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
2083 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
2085 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
2086 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
2088 for (day = 1; day <= days; day++)
2095 dt = date_to_time(year, month, day);
2097 fd = g_new0(FileData, 1);
2098 /* path and name must be non NULL, so make them an invalid filename */
2099 fd->path = g_strdup("//");
2102 pi_day = pan_item_new_box(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
2103 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2104 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2105 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2106 pan_item_set_key(pi_day, "day");
2108 dx = x + PAN_CAL_DOT_GAP * 2;
2109 dy = y + PAN_CAL_DOT_GAP * 2;
2111 fd = (work) ? work->data : NULL;
2112 while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
2116 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
2118 PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
2120 pan_item_set_key(pi, "dot");
2122 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2123 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
2125 dx = x + PAN_CAL_DOT_GAP * 2;
2126 dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2128 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
2130 /* must keep all dots within respective day even if it gets ugly */
2131 dy = y + PAN_CAL_DOT_GAP * 2;
2134 pi_day->color_a = MIN(PAN_FOLDER_BOX_ALPHA + 64 + n, 255);
2138 fd = (work) ? work->data : NULL;
2145 buf = g_strdup_printf("( %d )", n);
2146 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2147 PAN_TEXT_COLOR, 255);
2150 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
2151 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
2154 buf = g_strdup_printf("%d", day);
2155 pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2156 PAN_TEXT_COLOR, 255);
2160 pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
2167 x = PAN_FOLDER_BOX_BORDER;
2168 y += PAN_CAL_DAY_HEIGHT;
2172 x += PAN_CAL_DAY_WIDTH;
2176 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
2177 y += PAN_FOLDER_BOX_BORDER * 2;
2188 *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
2193 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
2201 PanItem *pi_month = NULL;
2202 PanItem *pi_day = NULL;
2208 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2210 list = pan_window_layout_list(path, SORT_NONE, TRUE);
2211 list = filelist_sort(list, SORT_TIME, TRUE);
2213 *width = PAN_FOLDER_BOX_BORDER * 2;
2214 *height = PAN_FOLDER_BOX_BORDER * 2;
2219 day_start = month_start;
2234 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2239 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
2245 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
2249 x = PAN_FOLDER_BOX_BORDER;
2252 y = PAN_FOLDER_BOX_BORDER;
2254 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
2255 pi = pan_item_new_text(pw, x, y, buf,
2256 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2257 PAN_TEXT_COLOR, 255);
2260 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
2262 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2263 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2264 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2266 x += PAN_FOLDER_BOX_BORDER;
2267 y += PAN_FOLDER_BOX_BORDER;
2271 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
2283 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
2285 needle = needle->next;
2294 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
2295 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2296 PAN_TEXT_COLOR, 255);
2301 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
2302 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2303 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2304 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2306 x += PAN_FOLDER_BOX_BORDER;
2307 y += PAN_FOLDER_BOX_BORDER;
2311 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2313 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
2314 if (pi->width > x_width) x_width = pi->width;
2315 y_height = pi->height;
2319 pi = pan_item_new_thumb(pw, fd, x, y);
2320 x_width = PAN_THUMB_SIZE;
2321 y_height = PAN_THUMB_SIZE;
2324 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
2325 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
2330 if (total > 0 && count < PAN_GROUP_MAX)
2332 y += y_height + PAN_THUMB_GAP;
2336 x += x_width + PAN_THUMB_GAP;
2346 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
2352 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
2353 gint *width, gint *height,
2354 gint *scroll_x, gint *scroll_y)
2356 pan_window_items_free(pw);
2360 case LAYOUT_SIZE_THUMB_DOTS:
2361 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2362 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2364 case LAYOUT_SIZE_THUMB_NONE:
2365 pw->thumb_size = PAN_THUMB_SIZE_NONE;
2366 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2368 case LAYOUT_SIZE_THUMB_SMALL:
2369 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2370 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2372 case LAYOUT_SIZE_THUMB_NORMAL:
2374 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2375 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2377 case LAYOUT_SIZE_THUMB_LARGE:
2378 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2379 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2381 case LAYOUT_SIZE_10:
2382 pw->image_size = 10;
2383 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2385 case LAYOUT_SIZE_25:
2386 pw->image_size = 25;
2387 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2389 case LAYOUT_SIZE_33:
2390 pw->image_size = 33;
2391 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2393 case LAYOUT_SIZE_50:
2394 pw->image_size = 50;
2395 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2397 case LAYOUT_SIZE_100:
2398 pw->image_size = 100;
2399 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2412 pan_window_layout_compute_grid(pw, path, width, height);
2414 case LAYOUT_FOLDERS_LINEAR:
2415 pan_window_layout_compute_folders_linear(pw, path, width, height);
2417 case LAYOUT_FOLDERS_FLOWER:
2418 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2420 case LAYOUT_CALENDAR:
2421 pan_window_layout_compute_calendar(pw, path, width, height);
2423 case LAYOUT_TIMELINE:
2424 pan_window_layout_compute_timeline(pw, path, width, height);
2430 printf("computed %d objects\n", g_list_length(pw->list));
2433 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2446 if (util_clip_region_test(x, y, width, height,
2447 pi->x, pi->y, pi->width, pi->height))
2449 list = g_list_prepend(list, pi);
2459 *-----------------------------------------------------------------------------
2461 *-----------------------------------------------------------------------------
2464 static gint pan_layout_queue_step(PanWindow *pw);
2467 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2469 PanWindow *pw = data;
2477 pw->queue_pi = NULL;
2481 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2482 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2485 pixbuf_renderer_area_changed(pw->pr, pi->x, pi->y, pi->width, pi->height);
2489 thumb_loader_free(pw->tl);
2492 while (pan_layout_queue_step(pw));
2495 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2497 PanWindow *pw = data;
2505 pw->queue_pi = NULL;
2509 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2510 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2511 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2513 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2514 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2515 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2520 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2521 (GdkInterpType)zoom_quality);
2522 g_object_unref(tmp);
2526 pixbuf_renderer_area_changed(pw->pr, pi->x, pi->y, pi->width, pi->height);
2530 image_loader_free(pw->il);
2533 while (pan_layout_queue_step(pw));
2537 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2538 guint width, guint height, gpointer data)
2540 PanWindow *pw = data;
2551 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2552 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2556 pixbuf_renderer_area_changed(pw->pr, pi->x + x, pi->y + y, width, height);
2562 static gint pan_layout_queue_step(PanWindow *pw)
2566 if (!pw->queue) return FALSE;
2568 pi = pw->queue->data;
2569 pw->queue = g_list_remove(pw->queue, pi);
2572 if (!pw->queue_pi->fd)
2574 pw->queue_pi->queued = FALSE;
2575 pw->queue_pi = NULL;
2579 image_loader_free(pw->il);
2581 thumb_loader_free(pw->tl);
2584 if (pi->type == ITEM_IMAGE)
2586 pw->il = image_loader_new(pi->fd->path);
2588 if (pw->size != LAYOUT_SIZE_100)
2590 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2594 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2596 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2598 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2600 image_loader_free(pw->il);
2603 else if (pi->type == ITEM_THUMB)
2605 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2607 if (!pw->tl->standard_loader)
2609 /* The classic loader will recreate a thumbnail any time we
2610 * request a different size than what exists. This view will
2611 * almost never use the user configured sizes so disable cache.
2613 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2616 thumb_loader_set_callbacks(pw->tl,
2617 pan_layout_queue_thumb_done_cb,
2618 pan_layout_queue_thumb_done_cb,
2621 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2623 thumb_loader_free(pw->tl);
2627 pw->queue_pi->queued = FALSE;
2628 pw->queue_pi = NULL;
2632 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2634 if (!pi || pi->queued || pi->pixbuf) return;
2635 if (pw->size <= LAYOUT_SIZE_THUMB_NONE) return;
2638 pw->queue = g_list_prepend(pw->queue, pi);
2640 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2643 static gint pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
2644 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2646 PanWindow *pw = data;
2651 pixbuf_draw_rect_fill(pixbuf,
2652 0, 0, width, height,
2653 PAN_BACKGROUND_COLOR, 255);
2655 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2657 gint rx, ry, rw, rh;
2659 if (util_clip_region(x, y, width, height,
2661 &rx, &ry, &rw, &rh))
2663 pixbuf_draw_rect_fill(pixbuf,
2664 rx - x, ry - y, rw, rh,
2665 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2668 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2670 gint rx, ry, rw, rh;
2672 if (util_clip_region(x, y, width, height,
2674 &rx, &ry, &rw, &rh))
2676 pixbuf_draw_rect_fill(pixbuf,
2677 rx - x, ry - y, rw, rh,
2678 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2682 list = pan_layout_intersect(pw, x, y, width, height);
2687 gint tx, ty, tw, th;
2688 gint rx, ry, rw, rh;
2695 if (pi->type == ITEM_THUMB && pi->pixbuf)
2697 tw = gdk_pixbuf_get_width(pi->pixbuf);
2698 th = gdk_pixbuf_get_height(pi->pixbuf);
2700 tx = pi->x + (pi->width - tw) / 2;
2701 ty = pi->y + (pi->height - th) / 2;
2703 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2705 if (util_clip_region(x, y, width, height,
2706 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2707 &rx, &ry, &rw, &rh))
2709 pixbuf_draw_shadow(pixbuf,
2710 rx - x, ry - y, rw, rh,
2711 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2713 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2718 if (util_clip_region(x, y, width, height,
2719 tx + tw, ty + PAN_SHADOW_OFFSET,
2720 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2721 &rx, &ry, &rw, &rh))
2723 pixbuf_draw_shadow(pixbuf,
2724 rx - x, ry - y, rw, rh,
2725 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2727 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2729 if (util_clip_region(x, y, width, height,
2730 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2731 &rx, &ry, &rw, &rh))
2733 pixbuf_draw_shadow(pixbuf,
2734 rx - x, ry - y, rw, rh,
2735 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2737 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2741 if (util_clip_region(x, y, width, height,
2743 &rx, &ry, &rw, &rh))
2745 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2748 1.0, 1.0, GDK_INTERP_NEAREST,
2752 if (util_clip_region(x, y, width, height,
2753 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2754 &rx, &ry, &rw, &rh))
2756 pixbuf_draw_rect_fill(pixbuf,
2757 rx - x, ry - y, rw, rh,
2758 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2760 if (util_clip_region(x, y, width, height,
2761 tx, ty, PAN_OUTLINE_THICKNESS, th,
2762 &rx, &ry, &rw, &rh))
2764 pixbuf_draw_rect_fill(pixbuf,
2765 rx - x, ry - y, rw, rh,
2766 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2768 if (util_clip_region(x, y, width, height,
2769 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2770 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2771 &rx, &ry, &rw, &rh))
2773 pixbuf_draw_rect_fill(pixbuf,
2774 rx - x, ry - y, rw, rh,
2775 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2777 if (util_clip_region(x, y, width, height,
2778 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2779 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2780 &rx, &ry, &rw, &rh))
2782 pixbuf_draw_rect_fill(pixbuf,
2783 rx - x, ry - y, rw, rh,
2784 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2787 else if (pi->type == ITEM_THUMB)
2789 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2790 th = pi->height - PAN_SHADOW_OFFSET * 2;
2791 tx = pi->x + PAN_SHADOW_OFFSET;
2792 ty = pi->y + PAN_SHADOW_OFFSET;
2794 if (util_clip_region(x, y, width, height,
2796 &rx, &ry, &rw, &rh))
2800 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2801 pixbuf_draw_rect_fill(pixbuf,
2802 rx - x, ry - y, rw, rh,
2804 PAN_SHADOW_ALPHA / d);
2807 pan_layout_queue(pw, pi);
2809 else if (pi->type == ITEM_IMAGE)
2811 if (util_clip_region(x, y, width, height,
2812 pi->x, pi->y, pi->width, pi->height,
2813 &rx, &ry, &rw, &rh))
2817 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2820 1.0, 1.0, GDK_INTERP_NEAREST,
2825 pixbuf_draw_rect_fill(pixbuf,
2826 rx - x, ry - y, rw, rh,
2827 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2828 pan_layout_queue(pw, pi);
2832 else if (pi->type == ITEM_BOX)
2846 if (pi->color_a > 254)
2848 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2849 shadow[0], bh - shadow[0],
2850 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2852 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2853 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2855 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2857 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2862 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2863 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2865 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2867 PAN_SHADOW_COLOR, a);
2871 if (util_clip_region(x, y, width, height,
2872 pi->x, pi->y, bw, bh,
2873 &rx, &ry, &rw, &rh))
2875 pixbuf_draw_rect_fill(pixbuf,
2876 rx - x, ry - y, rw, rh,
2877 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2879 if (util_clip_region(x, y, width, height,
2880 pi->x, pi->y, bw, pi->border,
2881 &rx, &ry, &rw, &rh))
2883 pixbuf_draw_rect_fill(pixbuf,
2884 rx - x, ry - y, rw, rh,
2885 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2887 if (util_clip_region(x, y, width, height,
2888 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
2889 &rx, &ry, &rw, &rh))
2891 pixbuf_draw_rect_fill(pixbuf,
2892 rx - x, ry - y, rw, rh,
2893 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2895 if (util_clip_region(x, y, width, height,
2896 pi->x + bw - pi->border, pi->y + pi->border,
2897 pi->border, bh - pi->border * 2,
2898 &rx, &ry, &rw, &rh))
2900 pixbuf_draw_rect_fill(pixbuf,
2901 rx - x, ry - y, rw, rh,
2902 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2904 if (util_clip_region(x, y, width, height,
2905 pi->x, pi->y + bh - pi->border,
2907 &rx, &ry, &rw, &rh))
2909 pixbuf_draw_rect_fill(pixbuf,
2910 rx - x, ry - y, rw, rh,
2911 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2914 else if (pi->type == ITEM_TRIANGLE)
2916 if (util_clip_region(x, y, width, height,
2917 pi->x, pi->y, pi->width, pi->height,
2918 &rx, &ry, &rw, &rh) && pi->data)
2920 gint *coord = pi->data;
2921 pixbuf_draw_triangle(pixbuf,
2922 rx - x, ry - y, rw, rh,
2923 coord[0] - x, coord[1] - y,
2924 coord[2] - x, coord[3] - y,
2925 coord[4] - x, coord[5] - y,
2926 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2928 if (pi->border & BORDER_1)
2930 pixbuf_draw_line(pixbuf,
2931 rx - x, ry - y, rw, rh,
2932 coord[0] - x, coord[1] - y,
2933 coord[2] - x, coord[3] - y,
2934 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2936 if (pi->border & BORDER_2)
2938 pixbuf_draw_line(pixbuf,
2939 rx - x, ry - y, rw, rh,
2940 coord[2] - x, coord[3] - y,
2941 coord[4] - x, coord[5] - y,
2942 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2944 if (pi->border & BORDER_3)
2946 pixbuf_draw_line(pixbuf,
2947 rx - x, ry - y, rw, rh,
2948 coord[4] - x, coord[5] - y,
2949 coord[0] - x, coord[1] - y,
2950 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2954 else if (pi->type == ITEM_TEXT && pi->text)
2956 PangoLayout *layout;
2958 layout = pan_item_text_layout(pi, (GtkWidget *)pr);
2959 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
2960 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
2961 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2962 g_object_unref(G_OBJECT(layout));
2969 static gint count = 0;
2970 PangoLayout *layout;
2973 layout = gtk_widget_create_pango_layout((GtkWidget *)pr, NULL);
2975 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
2976 (x / pr->source_tile_width) +
2977 (y / pr->source_tile_height * (pr->image_width/pr->source_tile_width + 1)));
2978 pango_layout_set_text(layout, buf, -1);
2981 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr, 0, 0, 0, 0, 0, 255);
2983 g_object_unref(G_OBJECT(layout));
2991 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
2992 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2994 PanWindow *pw = data;
2998 list = pan_layout_intersect(pw, x, y, width, height);
3007 if (pi->refcount > 0)
3011 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
3016 pw->queue = g_list_remove(pw->queue, pi);
3019 if (pw->queue_pi == pi) pw->queue_pi = NULL;
3022 g_object_unref(pi->pixbuf);
3034 *-----------------------------------------------------------------------------
3036 *-----------------------------------------------------------------------------
3039 static void pan_window_message(PanWindow *pw, const gchar *text)
3049 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
3054 if (pw->layout == LAYOUT_CALENDAR)
3064 pi->type == ITEM_BOX &&
3065 pi->key && strcmp(pi->key, "dot") == 0)
3067 size += pi->fd->size;
3082 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
3084 size += pi->fd->size;
3090 ss = text_from_size_abrev(size);
3091 buf = g_strdup_printf(_("%d images, %s"), count, ss);
3093 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
3097 static PixbufRenderer *pan_window_active_image(PanWindow *pw)
3100 if (pw->fs) return pw->fs->imd;
3106 static void pan_window_zoom_limit(PanWindow *pw)
3112 case LAYOUT_SIZE_THUMB_DOTS:
3113 case LAYOUT_SIZE_THUMB_NONE:
3114 case LAYOUT_SIZE_THUMB_SMALL:
3115 case LAYOUT_SIZE_THUMB_NORMAL:
3117 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
3121 case LAYOUT_SIZE_THUMB_LARGE:
3124 case LAYOUT_SIZE_10:
3125 case LAYOUT_SIZE_25:
3128 case LAYOUT_SIZE_33:
3129 case LAYOUT_SIZE_50:
3130 case LAYOUT_SIZE_100:
3136 pixbuf_renderer_zoom_set_limits(pw->pr, min, 32.0);
3139 static gint pan_window_layout_update_idle_cb(gpointer data)
3141 PanWindow *pw = data;
3147 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
3149 if (!pw->cache_list && !pw->cache_todo)
3151 pan_cache_fill(pw, pw->path);
3154 pan_window_message(pw, _("Reading dimensions..."));
3158 if (pan_cache_step(pw))
3162 if (pw->cache_count == pw->cache_total)
3164 pan_window_message(pw, _("Sorting images..."));
3166 else if (pw->cache_tick > 9)
3170 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
3171 pw->cache_total - pw->cache_count);
3172 pan_window_message(pw, buf);
3182 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3184 pan_window_zoom_limit(pw);
3186 if (width > 0 && height > 0)
3190 pixbuf_renderer_set_tiles(pw->pr, width, height,
3191 PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
3192 pan_window_request_tile_cb,
3193 pan_window_dispose_tile_cb, pw, 1.0);
3195 if (scroll_x == 0 && scroll_y == 0)
3203 pixbuf_renderer_scroll_to_point(pw->pr, scroll_x, scroll_y, align, align);
3206 pan_window_message(pw, NULL);
3213 static void pan_window_layout_update_idle(PanWindow *pw)
3215 if (pw->idle_id == -1)
3217 pan_window_message(pw, _("Sorting images..."));
3218 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3223 *-----------------------------------------------------------------------------
3224 * pan window keyboard
3225 *-----------------------------------------------------------------------------
3228 static const gchar *pan_menu_click_path(PanWindow *pw)
3230 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3234 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3236 PanWindow *pw = data;
3239 pr = pan_window_active_image(pw);
3240 gdk_window_get_origin(GTK_WIDGET(pr)->window, x, y);
3241 popup_menu_position_clamp(menu, x, y, 0);
3244 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3246 PanWindow *pw = data;
3249 gint stop_signal = FALSE;
3255 pr = pan_window_active_image(pw);
3256 path = pan_menu_click_path(pw);
3258 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pr)));
3262 switch (event->keyval)
3264 case GDK_Left: case GDK_KP_Left:
3268 case GDK_Right: case GDK_KP_Right:
3272 case GDK_Up: case GDK_KP_Up:
3276 case GDK_Down: case GDK_KP_Down:
3280 case GDK_Page_Up: case GDK_KP_Page_Up:
3281 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
3283 case GDK_Page_Down: case GDK_KP_Page_Down:
3284 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
3286 case GDK_Home: case GDK_KP_Home:
3287 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
3289 case GDK_End: case GDK_KP_End:
3290 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
3295 if (focused && !(event->state & GDK_CONTROL_MASK) )
3296 switch (event->keyval)
3298 case '+': case '=': case GDK_KP_Add:
3299 pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
3301 case '-': case GDK_KP_Subtract:
3302 pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
3304 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3305 pixbuf_renderer_zoom_set(pr, 1.0);
3308 pixbuf_renderer_zoom_set(pr, 2.0);
3311 pixbuf_renderer_zoom_set(pr, 3.0);
3314 pixbuf_renderer_zoom_set(pr, 4.0);
3317 pixbuf_renderer_zoom_set(pr, -4.0);
3320 pixbuf_renderer_zoom_set(pr, -3.0);
3323 pixbuf_renderer_zoom_set(pr, -2.0);
3327 pan_fullscreen_toggle(pw, FALSE);
3332 pan_overlay_toggle(pw);
3335 case GDK_Delete: case GDK_KP_Delete:
3340 if (GTK_WIDGET_VISIBLE(pw->search_box))
3342 gtk_widget_grab_focus(pw->search_entry);
3346 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3354 pan_fullscreen_toggle(pw, TRUE);
3357 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3359 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3365 menu = pan_popup_menu(pw);
3366 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3371 if (event->state & GDK_CONTROL_MASK)
3374 switch (event->keyval)
3407 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3410 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3413 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3416 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3419 if (path) info_window_new(path, NULL);
3422 pan_window_close(pw);
3425 if (n != -1 && path)
3427 pan_fullscreen_toggle(pw, TRUE);
3428 start_editor_from_file(n, path);
3432 else if (event->state & GDK_SHIFT_MASK)
3439 switch (event->keyval)
3444 pan_fullscreen_toggle(pw, TRUE);
3447 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3449 gtk_widget_grab_focus(GTK_WIDGET(pw->pr));
3450 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3459 if (x != 0 || y!= 0)
3461 keyboard_scroll_calc(&x, &y, event);
3462 pixbuf_renderer_scroll(pr, x, y);
3469 *-----------------------------------------------------------------------------
3471 *-----------------------------------------------------------------------------
3474 static void pan_info_update(PanWindow *pw, PanItem *pi)
3480 gint x1, y1, x2, y2, x3, y3;
3483 if (pw->click_pi == pi) return;
3484 if (pi && !pi->fd) pi = NULL;
3486 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3491 printf("info set to %s\n", pi->fd->path);
3493 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3495 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3496 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3497 pan_item_set_key(pbox, "info");
3499 if (pi->type == ITEM_THUMB && pi->pixbuf)
3501 w = gdk_pixbuf_get_width(pi->pixbuf);
3502 h = gdk_pixbuf_get_height(pi->pixbuf);
3504 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3505 y1 = pi->y + (pi->height - h) / 2 + 8;
3509 x1 = pi->x + pi->width - 8;
3517 triangle_rect_region(x1, y1, x2, y2, x3, y3,
3520 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3521 x1, y1, x2, y2, x3, y3,
3522 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3523 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3524 pan_item_set_key(p, "info");
3525 pan_item_added(pw, p);
3527 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3528 _("Filename:"), TEXT_ATTR_BOLD,
3529 PAN_POPUP_TEXT_COLOR, 255);
3530 pan_item_set_key(plabel, "info");
3531 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3532 pi->fd->name, TEXT_ATTR_NONE,
3533 PAN_POPUP_TEXT_COLOR, 255);
3534 pan_item_set_key(p, "info");
3535 pan_item_size_by_item(pbox, p, 0);
3537 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3538 _("Date:"), TEXT_ATTR_BOLD,
3539 PAN_POPUP_TEXT_COLOR, 255);
3540 pan_item_set_key(plabel, "info");
3541 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3542 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3543 PAN_POPUP_TEXT_COLOR, 255);
3544 pan_item_set_key(p, "info");
3545 pan_item_size_by_item(pbox, p, 0);
3547 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3548 _("Size:"), TEXT_ATTR_BOLD,
3549 PAN_POPUP_TEXT_COLOR, 255);
3550 pan_item_set_key(plabel, "info");
3551 buf = text_from_size(pi->fd->size);
3552 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3553 buf, TEXT_ATTR_NONE,
3554 PAN_POPUP_TEXT_COLOR, 255);
3556 pan_item_set_key(p, "info");
3557 pan_item_size_by_item(pbox, p, 0);
3559 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3560 pan_item_added(pw, pbox);
3565 *-----------------------------------------------------------------------------
3567 *-----------------------------------------------------------------------------
3570 static void pan_search_status(PanWindow *pw, const gchar *text)
3572 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3575 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3583 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3585 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3586 if (!list) return FALSE;
3588 found = g_list_find(list, pw->click_pi);
3589 if (found && found->next)
3591 found = found->next;
3599 pan_info_update(pw, pi);
3600 pixbuf_renderer_scroll_to_point(pw->pr, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3602 buf = g_strdup_printf("%s ( %d / %d )",
3603 (path[0] == '/') ? _("path found") : _("filename found"),
3604 g_list_index(list, pi) + 1,
3605 g_list_length(list));
3606 pan_search_status(pw, buf);
3614 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3622 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3624 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3625 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3630 needle = g_utf8_strdown(text, -1);
3631 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3634 if (!list) return FALSE;
3636 found = g_list_find(list, pw->click_pi);
3637 if (found && found->next)
3639 found = found->next;
3647 pan_info_update(pw, pi);
3648 pixbuf_renderer_scroll_to_point(pw->pr, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3650 buf = g_strdup_printf("%s ( %d / %d )",
3652 g_list_index(list, pi) + 1,
3653 g_list_length(list));
3654 pan_search_status(pw, buf);
3662 static gint valid_date_separator(gchar c)
3664 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3667 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
3668 gint year, gint month, gint day,
3674 work = g_list_last(pw->list);
3682 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
3683 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
3687 tl = localtime(&pi->fd->date);
3692 match = (tl->tm_year == year - 1900);
3693 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3694 if (match && day > 0) match = (tl->tm_mday == day);
3696 if (match) list = g_list_prepend(list, pi);
3701 return g_list_reverse(list);
3704 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3720 if (!text) return FALSE;
3722 ptr = (gchar *)text;
3723 while (*ptr != '\0')
3725 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3730 if (t == -1) return FALSE;
3732 if (!lt) return FALSE;
3734 if (valid_date_separator(*text))
3737 mptr = (gchar *)text;
3741 year = (gint)strtol(text, &mptr, 10);
3742 if (mptr == text) return FALSE;
3745 if (*mptr != '\0' && valid_date_separator(*mptr))
3750 month = strtol(mptr, &dptr, 10);
3753 if (valid_date_separator(*dptr))
3755 month = lt->tm_mon + 1;
3763 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3767 day = strtol(dptr, &eptr, 10);
3777 year = lt->tm_year + 1900;
3779 else if (year < 100)
3788 month < -1 || month == 0 || month > 12 ||
3789 day < -1 || day == 0 || day > 31) return FALSE;
3791 t = date_to_time(year, month, day);
3792 if (t < 0) return FALSE;
3794 if (pw->layout == LAYOUT_CALENDAR)
3796 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
3802 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3803 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
3808 found = g_list_find(list, pw->search_pi);
3809 if (found && found->next)
3811 found = found->next;
3822 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
3824 pan_info_update(pw, NULL);
3825 pan_calendar_update(pw, pi);
3826 pixbuf_renderer_scroll_to_point(pw->pr,
3827 pi->x + pi->width / 2,
3828 pi->y + pi->height / 2, 0.5, 0.5);
3832 pan_info_update(pw, pi);
3833 pixbuf_renderer_scroll_to_point(pw->pr,
3834 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3840 buf = date_value_string(t, DATE_LENGTH_MONTH);
3845 buf = g_strdup_printf("%d %s", day, tmp);
3851 buf = date_value_string(t, DATE_LENGTH_YEAR);
3856 buf_count = g_strdup_printf("( %d / %d )",
3857 g_list_index(list, pi) + 1,
3858 g_list_length(list));
3862 buf_count = g_strdup_printf("(%s)", _("no match"));
3865 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
3868 pan_search_status(pw, message);
3876 static void pan_search_activate_cb(const gchar *text, gpointer data)
3878 PanWindow *pw = data;
3882 tab_completion_append_to_history(pw->search_entry, text);
3884 if (pan_search_by_path(pw, text)) return;
3886 if ((pw->layout == LAYOUT_TIMELINE ||
3887 pw->layout == LAYOUT_CALENDAR) &&
3888 pan_search_by_date(pw, text))
3893 if (pan_search_by_partial(pw, text)) return;
3895 pan_search_status(pw, _("no match"));
3898 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3900 PanWindow *pw = data;
3903 visible = GTK_WIDGET_VISIBLE(pw->search_box);
3904 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3908 gtk_widget_hide(pw->search_box);
3909 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3913 gtk_widget_show(pw->search_box);
3914 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3915 gtk_widget_grab_focus(pw->search_entry);
3921 *-----------------------------------------------------------------------------
3922 * view window main routines
3923 *-----------------------------------------------------------------------------
3926 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
3928 PanWindow *pw = data;
3936 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
3937 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
3940 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3943 switch (event->button)
3946 pan_info_update(pw, pi);
3948 if (!pi && pw->layout == LAYOUT_CALENDAR)
3950 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
3951 pan_calendar_update(pw, pi);
3957 pan_info_update(pw, pi);
3958 menu = pan_popup_menu(pw);
3959 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
3966 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
3969 PanWindow *pw = data;
3976 if (!(event->state & GDK_SHIFT_MASK))
3982 if (event->state & GDK_CONTROL_MASK)
3984 switch (event->direction)
3987 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
3988 (gint)event->x, (gint)event->y);
3990 case GDK_SCROLL_DOWN:
3991 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
3992 (gint)event->x, (gint)event->y);
4000 switch (event->direction)
4003 pixbuf_renderer_scroll(pr, 0, -h);
4005 case GDK_SCROLL_DOWN:
4006 pixbuf_renderer_scroll(pr, 0, h);
4008 case GDK_SCROLL_LEFT:
4009 pixbuf_renderer_scroll(pr, -w, 0);
4011 case GDK_SCROLL_RIGHT:
4012 pixbuf_renderer_scroll(pr, w, 0);
4020 static void pan_image_set_buttons(PanWindow *pw, PixbufRenderer *pr)
4022 g_signal_connect(G_OBJECT(pr), "clicked",
4023 G_CALLBACK(button_cb), pw);
4024 g_signal_connect(G_OBJECT(pr), "scroll_event",
4025 G_CALLBACK(scroll_cb), pw);
4029 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
4031 PanWindow *pw = data;
4037 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
4039 if (force_off && !pw->fs) return;
4043 fullscreen_stop(pw->fs);
4044 pw->pr = pw->pr_normal;
4048 printf("FIXME: fullscreen\n");
4050 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
4052 pan_image_set_buttons(pw, pw->fs->imd);
4053 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
4054 G_CALLBACK(pan_window_key_press_cb), pw);
4056 pw->imd = pw->fs->imd;
4061 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
4063 PanWindow *pw = data;
4067 text = image_zoom_get_as_text(imd);
4069 text = g_strdup_printf("%.2f", zoom);
4070 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4074 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
4076 PanWindow *pw = data;
4081 pixbuf_renderer_get_visible_rect(pr, &rect);
4082 pixbuf_renderer_get_image_size(pr, &width, &height);
4084 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4085 adj->page_size = (gdouble)rect.width;
4086 adj->page_increment = adj->page_size / 2.0;
4087 adj->step_increment = 48.0 / pr->scale;
4089 adj->upper = MAX((gdouble)width, 1.0);
4090 adj->value = (gdouble)rect.x;
4092 pref_signal_block_data(pw->scrollbar_h, pw);
4093 gtk_adjustment_changed(adj);
4094 pref_signal_unblock_data(pw->scrollbar_h, pw);
4096 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4097 adj->page_size = (gdouble)rect.height;
4098 adj->page_increment = adj->page_size / 2.0;
4099 adj->step_increment = 48.0 / pr->scale;
4101 adj->upper = MAX((gdouble)height, 1.0);
4102 adj->value = (gdouble)rect.y;
4104 pref_signal_block_data(pw->scrollbar_v, pw);
4105 gtk_adjustment_changed(adj);
4106 pref_signal_unblock_data(pw->scrollbar_v, pw);
4108 // printf("scrolled to %d,%d @ %d x %d\n", x, y, width, height);
4111 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4113 PanWindow *pw = data;
4116 if (!pw->pr->scale) return;
4118 x = (gint)gtk_range_get_value(range);
4120 pixbuf_renderer_scroll_to_point(pw->pr, x, (gint)((gdouble)pw->pr->y_scroll / pw->pr->scale), 0.0, 0.0);
4123 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4125 PanWindow *pw = data;
4128 if (!pw->pr->scale) return;
4130 y = (gint)gtk_range_get_value(range);
4132 pixbuf_renderer_scroll_to_point(pw->pr, (gint)((gdouble)pw->pr->x_scroll / pw->pr->scale), y, 0.0, 0.0);
4135 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4137 PanWindow *pw = data;
4139 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4140 pan_window_layout_update_idle(pw);
4143 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4145 PanWindow *pw = data;
4147 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4148 pan_window_layout_update_idle(pw);
4151 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4153 PanWindow *pw = data;
4156 path = remove_trailing_slash(new_text);
4157 parse_out_relatives(path);
4161 warning_dialog(_("Folder not found"),
4162 _("The entered path is not a folder"),
4163 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4167 tab_completion_append_to_history(pw->path_entry, path);
4170 pw->path = g_strdup(path);
4172 pan_window_layout_update_idle(pw);
4175 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4177 PanWindow *pw = data;
4180 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4182 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4183 pan_window_entry_activate_cb(text, pw);
4187 static void pan_window_close(PanWindow *pw)
4189 pan_window_list = g_list_remove(pan_window_list, pw);
4191 if (pw->idle_id != -1)
4193 g_source_remove(pw->idle_id);
4196 pan_fullscreen_toggle(pw, TRUE);
4197 gtk_widget_destroy(pw->window);
4199 pan_window_items_free(pw);
4206 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4208 PanWindow *pw = data;
4210 pan_window_close(pw);
4214 static void pan_window_new_real(const gchar *path)
4223 GdkGeometry geometry;
4225 pw = g_new0(PanWindow, 1);
4227 pw->path = g_strdup(path);
4228 pw->layout = LAYOUT_TIMELINE;
4229 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4230 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4231 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4235 pw->overlay_id = -1;
4238 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4240 geometry.min_width = 8;
4241 geometry.min_height = 8;
4242 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4244 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4245 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
4246 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4247 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4249 window_set_icon(pw->window, NULL, NULL);
4251 vbox = gtk_vbox_new(FALSE, 0);
4252 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4253 gtk_widget_show(vbox);
4255 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4257 pref_spacer(box, 0);
4258 pref_label_new(box, _("Location:"));
4259 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
4260 pan_window_entry_activate_cb, pw);
4261 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4262 G_CALLBACK(pan_window_entry_change_cb), pw);
4263 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4264 gtk_widget_show(combo);
4266 combo = gtk_combo_box_new_text();
4267 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4268 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4269 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4270 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4271 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4273 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4274 g_signal_connect(G_OBJECT(combo), "changed",
4275 G_CALLBACK(pan_window_layout_change_cb), pw);
4276 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4277 gtk_widget_show(combo);
4279 combo = gtk_combo_box_new_text();
4280 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4281 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4282 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4283 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4284 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4285 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4286 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4287 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4288 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4289 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4291 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4292 g_signal_connect(G_OBJECT(combo), "changed",
4293 G_CALLBACK(pan_window_layout_size_cb), pw);
4294 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4295 gtk_widget_show(combo);
4297 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4298 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4299 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4301 pw->pr = pixbuf_renderer_new();
4302 pw->pr_normal = pw->pr;
4304 if (black_window_background) pixbuf_renderer_set_black(pw->pr, TRUE);
4306 g_object_set(G_OBJECT(pw->pr), "zoom_2pass", TRUE, NULL);
4308 g_signal_connect(G_OBJECT(pw->pr), "zoom",
4309 G_CALLBACK(pan_window_image_zoom_cb), pw);
4310 g_signal_connect(G_OBJECT(pw->pr), "scroll_notify",
4311 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4313 gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(pw->pr), 0, 1, 0, 1,
4314 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4315 gtk_widget_show(GTK_WIDGET(pw->pr));
4317 pan_window_dnd_init(pw);
4319 pan_image_set_buttons(pw, pw->pr);
4321 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4322 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4323 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4324 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4325 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4326 gtk_widget_show(pw->scrollbar_h);
4328 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4329 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4330 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4331 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4332 0, GTK_FILL | GTK_EXPAND, 0, 0);
4333 gtk_widget_show(pw->scrollbar_v);
4337 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4338 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4340 pref_spacer(pw->search_box, 0);
4341 pref_label_new(pw->search_box, _("Find:"));
4343 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4344 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4345 gtk_widget_show(hbox);
4347 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4348 pan_search_activate_cb, pw);
4349 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4350 gtk_widget_show(combo);
4352 pw->search_label = gtk_label_new("");
4353 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4354 gtk_widget_show(pw->search_label);
4358 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4360 frame = gtk_frame_new(NULL);
4361 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4362 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4363 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4364 gtk_widget_show(frame);
4366 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4367 gtk_container_add(GTK_CONTAINER(frame), hbox);
4368 gtk_widget_show(hbox);
4370 pref_spacer(hbox, 0);
4371 pw->label_message = pref_label_new(hbox, "");
4373 frame = gtk_frame_new(NULL);
4374 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4375 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4376 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4377 gtk_widget_show(frame);
4379 pw->label_zoom = gtk_label_new("");
4380 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4381 gtk_widget_show(pw->label_zoom);
4383 pw->search_button = gtk_toggle_button_new();
4384 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4385 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4386 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4387 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4388 gtk_widget_show(hbox);
4389 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4390 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4391 gtk_widget_show(pw->search_button_arrow);
4392 pref_label_new(hbox, _("Find"));
4394 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4395 gtk_widget_show(pw->search_button);
4396 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4397 G_CALLBACK(pan_search_toggle_cb), pw);
4399 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4400 G_CALLBACK(pan_window_delete_cb), pw);
4401 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4402 G_CALLBACK(pan_window_key_press_cb), pw);
4404 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4406 pan_window_layout_update_idle(pw);
4409 gtk_widget_grab_focus(GTK_WIDGET(pw->pr));
4411 gtk_widget_show(pw->window);
4413 pan_window_list = g_list_append(pan_window_list, pw);
4417 *-----------------------------------------------------------------------------
4418 * peformance warnings
4419 *-----------------------------------------------------------------------------
4422 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4426 generic_dialog_close(gd);
4428 pan_window_new_real(path);
4432 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4436 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4437 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4440 static gint pan_warning(const gchar *path)
4446 GtkWidget *ct_button;
4449 if (enable_thumb_caching &&
4450 thumbnail_spec_standard) return FALSE;
4452 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4453 if (hide_dlg) return FALSE;
4455 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4457 gd->data = g_strdup(path);
4458 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4459 pan_warning_ok_cb, TRUE);
4461 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4462 _("Pan view performance may be poor."),
4463 _("To improve performance of thumbnails in the pan view the"
4464 " following options can be enabled. Note that both options"
4465 " must be enabled to notice a change in performance."));
4467 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4468 pref_spacer(group, PREF_PAD_INDENT);
4469 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4471 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4472 enable_thumb_caching, &enable_thumb_caching);
4473 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4474 thumbnail_spec_standard, &thumbnail_spec_standard);
4475 pref_checkbox_link_sensitivity(ct_button, button);
4479 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4480 G_CALLBACK(pan_warning_hide_cb), NULL);
4482 gtk_widget_show(gd->dialog);
4489 *-----------------------------------------------------------------------------
4491 *-----------------------------------------------------------------------------
4494 void pan_window_new(const gchar *path)
4496 if (pan_warning(path)) return;
4498 pan_window_new_real(path);
4502 *-----------------------------------------------------------------------------
4504 *-----------------------------------------------------------------------------
4507 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4509 PanWindow *pw = data;
4512 path = pan_menu_click_path(pw);
4515 pan_fullscreen_toggle(pw, TRUE);
4516 view_window_new(path);
4520 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4526 pw = submenu_item_get_data(widget);
4527 n = GPOINTER_TO_INT(data);
4530 path = pan_menu_click_path(pw);
4533 pan_fullscreen_toggle(pw, TRUE);
4534 start_editor_from_file(n, path);
4538 static void pan_info_cb(GtkWidget *widget, gpointer data)
4540 PanWindow *pw = data;
4543 path = pan_menu_click_path(pw);
4544 if (path) info_window_new(path, NULL);
4547 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4549 PanWindow *pw = data;
4551 pixbuf_renderer_zoom_adjust(pan_window_active_image(pw), ZOOM_INCREMENT);
4554 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4556 PanWindow *pw = data;
4558 pixbuf_renderer_zoom_adjust(pan_window_active_image(pw), -ZOOM_INCREMENT);
4561 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4563 PanWindow *pw = data;
4565 pixbuf_renderer_zoom_set(pan_window_active_image(pw), 1.0);
4568 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4570 PanWindow *pw = data;
4573 path = pan_menu_click_path(pw);
4574 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pw->pr));
4577 static void pan_move_cb(GtkWidget *widget, gpointer data)
4579 PanWindow *pw = data;
4582 path = pan_menu_click_path(pw);
4583 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pw->pr));
4586 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4588 PanWindow *pw = data;
4591 path = pan_menu_click_path(pw);
4592 if (path) file_util_rename(path, NULL, GTK_WIDGET(pw->pr));
4595 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4597 PanWindow *pw = data;
4600 path = pan_menu_click_path(pw);
4601 if (path) file_util_delete(path, NULL, GTK_WIDGET(pw->pr));
4604 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4606 PanWindow *pw = data;
4608 pan_fullscreen_toggle(pw, FALSE);
4611 static void pan_close_cb(GtkWidget *widget, gpointer data)
4613 PanWindow *pw = data;
4615 pan_window_close(pw);
4618 static GtkWidget *pan_popup_menu(PanWindow *pw)
4624 active = (pw->click_pi != NULL);
4626 menu = popup_menu_short_lived();
4628 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4629 G_CALLBACK(pan_zoom_in_cb), pw);
4630 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4631 G_CALLBACK(pan_zoom_out_cb), pw);
4632 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4633 G_CALLBACK(pan_zoom_1_1_cb), pw);
4634 menu_item_add_divider(menu);
4636 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4637 gtk_widget_set_sensitive(item, active);
4639 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4640 G_CALLBACK(pan_info_cb), pw);
4642 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4643 G_CALLBACK(pan_new_window_cb), pw);
4645 menu_item_add_divider(menu);
4646 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4647 G_CALLBACK(pan_copy_cb), pw);
4648 menu_item_add_sensitive(menu, _("_Move..."), active,
4649 G_CALLBACK(pan_move_cb), pw);
4650 menu_item_add_sensitive(menu, _("_Rename..."), active,
4651 G_CALLBACK(pan_rename_cb), pw);
4652 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4653 G_CALLBACK(pan_delete_cb), pw);
4655 menu_item_add_divider(menu);
4659 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4663 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4666 menu_item_add_divider(menu);
4667 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4673 *-----------------------------------------------------------------------------
4675 *-----------------------------------------------------------------------------
4678 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4680 GtkSelectionData *selection_data, guint info,
4681 guint time, gpointer data)
4683 PanWindow *pw = data;
4685 if (gtk_drag_get_source_widget(context) == GTK_WIDGET(pw->pr)) return;
4687 if (info == TARGET_URI_LIST)
4691 list = uri_list_from_text(selection_data->data, TRUE);
4692 if (list && isdir((gchar *)list->data))
4694 gchar *path = list->data;
4697 pw->path = g_strdup(path);
4699 pan_window_layout_update_idle(pw);
4702 path_list_free(list);
4706 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4707 GtkSelectionData *selection_data, guint info,
4708 guint time, gpointer data)
4710 PanWindow *pw = data;
4713 path = pan_menu_click_path(pw);
4723 case TARGET_URI_LIST:
4726 case TARGET_TEXT_PLAIN:
4731 list = g_list_append(NULL, (gchar *)path);
4732 text = uri_text_from_list(list, &len, plain_text);
4736 gtk_selection_data_set (selection_data, selection_data->target,
4743 gtk_selection_data_set (selection_data, selection_data->target,
4748 static void pan_window_dnd_init(PanWindow *pw)
4752 widget = GTK_WIDGET(pw->pr);
4754 gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
4755 dnd_file_drag_types, dnd_file_drag_types_count,
4756 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4757 g_signal_connect(G_OBJECT(widget), "drag_data_get",
4758 G_CALLBACK(pan_window_set_dnd_data), pw);
4760 gtk_drag_dest_set(widget,
4761 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4762 dnd_file_drop_types, dnd_file_drop_types_count,
4763 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4764 g_signal_connect(G_OBJECT(widget), "drag_data_received",
4765 G_CALLBACK(pan_window_get_dnd_data), pw);
4769 *-----------------------------------------------------------------------------
4770 * maintenance (for rename, move, remove)
4771 *-----------------------------------------------------------------------------