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-renderer.h"
27 #include "pixbuf_util.h"
30 #include "ui_bookmark.h"
31 #include "ui_fileops.h"
34 #include "ui_tabcomp.h"
36 #include <gdk/gdkkeysyms.h> /* for keyboard values */
40 #define PAN_WINDOW_DEFAULT_WIDTH 720
41 #define PAN_WINDOW_DEFAULT_HEIGHT 500
43 #define PAN_TILE_SIZE 512
45 #define PAN_THUMB_SIZE_DOTS 4
46 #define PAN_THUMB_SIZE_NONE 24
47 #define PAN_THUMB_SIZE_SMALL 64
48 #define PAN_THUMB_SIZE_NORMAL 128
49 #define PAN_THUMB_SIZE_LARGE 256
50 #define PAN_THUMB_SIZE pw->thumb_size
52 #define PAN_THUMB_GAP_DOTS 2
53 #define PAN_THUMB_GAP_SMALL 14
54 #define PAN_THUMB_GAP_NORMAL 30
55 #define PAN_THUMB_GAP_LARGE 40
56 #define PAN_THUMB_GAP_HUGE 50
57 #define PAN_THUMB_GAP pw->thumb_gap
59 #define PAN_SHADOW_OFFSET 6
60 #define PAN_SHADOW_FADE 5
61 #define PAN_SHADOW_COLOR 0, 0, 0
62 #define PAN_SHADOW_ALPHA 64
64 #define PAN_OUTLINE_THICKNESS 1
65 #define PAN_OUTLINE_COLOR_1 255, 255, 255
66 #define PAN_OUTLINE_COLOR_2 64, 64, 64
67 #define PAN_OUTLINE_ALPHA 180
69 #define PAN_BACKGROUND_COLOR 255, 255, 230
71 #define PAN_GRID_SIZE 10
72 #define PAN_GRID_COLOR 0, 0, 0
73 #define PAN_GRID_ALPHA 20
75 #define PAN_FOLDER_BOX_COLOR 0, 0, 255
76 #define PAN_FOLDER_BOX_ALPHA 10
77 #define PAN_FOLDER_BOX_BORDER 20
79 #define PAN_FOLDER_BOX_OUTLINE_THICKNESS 4
80 #define PAN_FOLDER_BOX_OUTLINE_COLOR 0, 0, 255
81 #define PAN_FOLDER_BOX_OUTLINE_ALPHA 64
83 #define PAN_TEXT_BORDER_SIZE 4
84 #define PAN_TEXT_COLOR 0, 0, 0
86 #define PAN_POPUP_COLOR 255, 255, 220
87 #define PAN_POPUP_ALPHA 255
88 #define PAN_POPUP_BORDER 1
89 #define PAN_POPUP_BORDER_COLOR 0, 0, 0
90 #define PAN_POPUP_TEXT_COLOR 0, 0, 0
92 #define PAN_GROUP_MAX 16
94 #define ZOOM_INCREMENT 1.0
95 #define ZOOM_LABEL_WIDTH 64
98 #define PAN_PREF_GROUP "pan_view_options"
99 #define PAN_PREF_HIDE_WARNING "hide_performance_warning"
105 LAYOUT_FOLDERS_LINEAR,
106 LAYOUT_FOLDERS_FLOWER,
111 LAYOUT_SIZE_THUMB_DOTS = 0,
112 LAYOUT_SIZE_THUMB_NONE,
113 LAYOUT_SIZE_THUMB_SMALL,
114 LAYOUT_SIZE_THUMB_NORMAL,
115 LAYOUT_SIZE_THUMB_LARGE,
134 TEXT_ATTR_BOLD = 1 << 0,
135 TEXT_ATTR_HEADING = 1 << 1,
136 TEXT_ATTR_MARKUP = 1 << 2
147 typedef struct _PanItem PanItem;
162 TextAttrType text_attr;
180 typedef struct _PanWindow PanWindow;
185 ImageWindow *imd_normal;
188 GtkWidget *path_entry;
190 GtkWidget *label_message;
191 GtkWidget *label_zoom;
193 GtkWidget *search_box;
194 GtkWidget *search_entry;
195 GtkWidget *search_label;
196 GtkWidget *search_button;
197 GtkWidget *search_button_arrow;
199 GtkWidget *scrollbar_h;
200 GtkWidget *scrollbar_v;
230 typedef struct _PanCacheData PanCacheData;
231 struct _PanCacheData {
237 static GList *pan_window_list = NULL;
240 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend);
242 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
244 static GtkWidget *pan_popup_menu(PanWindow *pw);
245 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off);
247 static void pan_window_close(PanWindow *pw);
249 static void pan_window_dnd_init(PanWindow *pw);
252 static gint util_clip_region(gint x, gint y, gint w, gint h,
253 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
254 gint *rx, gint *ry, gint *rw, gint *rh)
256 if (clip_x + clip_w <= x ||
258 clip_y + clip_h <= y ||
264 *rx = MAX(x, clip_x);
265 *rw = MIN((x + w), (clip_x + clip_w)) - *rx;
267 *ry = MAX(y, clip_y);
268 *rh = MIN((y + h), (clip_y + clip_h)) - *ry;
273 static gint util_clip_region_test(gint x, gint y, gint w, gint h,
274 gint clip_x, gint clip_y, gint clip_w, gint clip_h)
278 return util_clip_region(x, y, w, h,
279 clip_x, clip_y, clip_w, clip_h,
292 static gint date_compare(time_t a, time_t b, DateLengthType length)
297 if (length == DATE_LENGTH_EXACT) return (a == b);
299 if (!localtime_r(&a, &ta) ||
300 !localtime_r(&b, &tb)) return FALSE;
302 if (ta.tm_year != tb.tm_year) return FALSE;
303 if (length == DATE_LENGTH_YEAR) return TRUE;
305 if (ta.tm_mon != tb.tm_mon) return FALSE;
306 if (length == DATE_LENGTH_MONTH) return TRUE;
308 if (length == DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
310 if (ta.tm_mday != tb.tm_mday) return FALSE;
311 if (length == DATE_LENGTH_DAY) return TRUE;
313 return (ta.tm_hour == tb.tm_hour);
316 static gint date_value(time_t d, DateLengthType length)
320 if (!localtime_r(&d, &td)) return -1;
324 case DATE_LENGTH_DAY:
327 case DATE_LENGTH_WEEK:
330 case DATE_LENGTH_MONTH:
331 return td.tm_mon + 1;
333 case DATE_LENGTH_YEAR:
334 return td.tm_year + 1900;
336 case DATE_LENGTH_EXACT:
344 static gchar *date_value_string(time_t d, DateLengthType length)
348 gchar *format = NULL;
350 if (!localtime_r(&d, &td)) return g_strdup("");
354 case DATE_LENGTH_DAY:
355 return g_strdup_printf("%d", td.tm_mday);
357 case DATE_LENGTH_WEEK:
360 case DATE_LENGTH_MONTH:
363 case DATE_LENGTH_YEAR:
364 return g_strdup_printf("%d", td.tm_year + 1900);
366 case DATE_LENGTH_EXACT:
368 return g_strdup(text_from_time(d));
373 if (format && strftime(buf, sizeof(buf), format, &td) > 0)
375 gchar *ret = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
382 static time_t date_to_time(gint year, gint month, gint day)
389 lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
390 lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
391 lt.tm_year = year - 1900;
398 *-----------------------------------------------------------------------------
400 *-----------------------------------------------------------------------------
403 static void triangle_rect_region(gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
404 gint *rx, gint *ry, gint *rw, gint *rh)
412 tw = MAX(abs(x1 - x2), abs(x2 - x3));
413 tw = MAX(tw, abs(x3 - x1));
414 th = MAX(abs(y1 - y2), abs(y2 - y3));
415 th = MAX(th, abs(y3 - y1));
423 static void pixbuf_draw_triangle(GdkPixbuf *pb,
424 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
425 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
426 guint8 r, guint8 g, guint8 b, guint8 a)
442 pw = gdk_pixbuf_get_width(pb);
443 ph = gdk_pixbuf_get_height(pb);
445 if (!util_clip_region(0, 0, pw, ph,
446 clip_x, clip_y, clip_w, clip_h,
447 &rx, &ry, &rw, &rh)) return;
449 triangle_rect_region(x1, y1, x2, y2, x3, y3,
452 if (!util_clip_region(rx, ry, rw, rh,
454 &fx1, &fy1, &fw, &fh)) return;
458 p_alpha = gdk_pixbuf_get_has_alpha(pb);
459 prs = gdk_pixbuf_get_rowstride(pb);
460 p_pix = gdk_pixbuf_get_pixels(pb);
462 p_step = (p_alpha) ? 4 : 3;
463 for (i = fy1; i < fy2; i++)
465 pp = p_pix + i * prs + (fx1 * p_step);
466 for (j = fx1; j < fx2; j++)
470 z1 = (y1 - y2)*(j - x2) + (x2 - x1)*(i - y2);
471 z2 = (y2 - y3)*(j - x3) + (x3 - x2)*(i - y3);
474 z2 = (y3 - y1)*(j - x1) + (x1 - x3)*(i - y1);
477 pp[0] = (r * a + pp[0] * (256-a)) >> 8;
478 pp[1] = (g * a + pp[1] * (256-a)) >> 8;
479 pp[2] = (b * a + pp[2] * (256-a)) >> 8;
487 static gint util_clip_line(gdouble clip_x, gdouble clip_y, gdouble clip_w, gdouble clip_h,
488 gdouble x1, gdouble y1, gdouble x2, gdouble y2,
489 gdouble *rx1, gdouble *ry1, gdouble *rx2, gdouble *ry2)
509 if (x2 < clip_x || x1 > clip_x + clip_w) return FALSE;
513 if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
517 if (y1 < clip_y || y2 > clip_y + clip_h) return FALSE;
521 if (x1 >= clip_x && x2 <= clip_x + clip_w)
525 if (y1 >= clip_y && y2 <= clip_y + clip_h) return TRUE;
529 if (y2 >= clip_y && y1 <= clip_y + clip_h) return TRUE;
539 slope = (y2 - y1) / d;
542 y1 = y1 + slope * (clip_x - x1);
545 if (x2 > clip_x + clip_w)
547 y2 = y2 + slope * (clip_x + clip_w - x2);
548 x2 = clip_x + clip_w;
554 if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
560 if (y1 < clip_y || y2 > clip_y + clip_h) return FALSE;
578 slope = (x2 - x1) / d;
581 x1 = x1 + slope * (clip_y - y1);
584 if (y2 > clip_y + clip_h)
586 x2 = x2 + slope * (clip_y + clip_h - y2);
587 y2 = clip_y + clip_h;
609 static void pixbuf_draw_line(GdkPixbuf *pb,
610 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
611 gint x1, gint y1, gint x2, gint y2,
612 guint8 r, guint8 g, guint8 b, guint8 a)
617 gdouble rx1, ry1, rx2, ry2;
624 gint cx1, cy1, cx2, cy2;
628 pw = gdk_pixbuf_get_width(pb);
629 ph = gdk_pixbuf_get_height(pb);
631 if (!util_clip_region(0, 0, pw, ph,
632 clip_x, clip_y, clip_w, clip_h,
633 &rx, &ry, &rw, &rh)) return;
634 if (!util_clip_line((gdouble)rx, (gdouble)ry, (gdouble)rw, (gdouble)rh,
635 (gdouble)x1, (gdouble)y1, (gdouble)x2, (gdouble)y2,
636 &rx1, &ry1, &rx2, &ry2)) return;
643 p_alpha = gdk_pixbuf_get_has_alpha(pb);
644 prs = gdk_pixbuf_get_rowstride(pb);
645 p_pix = gdk_pixbuf_get_pixels(pb);
647 p_step = (p_alpha) ? 4 : 3;
649 if (fabs(rx2 - rx1) > fabs(ry2 - ry1))
654 t = rx1; rx1 = rx2; rx2 = t;
655 t = ry1; ry1 = ry2; ry2 = t;
659 if (slope != 0.0) slope = (ry2 - ry1) / slope;
660 for (x = rx1; x < rx2; x += 1.0)
662 px = (gint)(x + 0.5);
663 py = (gint)(ry1 + (x - rx1) * slope + 0.5);
665 if (px >= cx1 && px < cx2 && py >= cy1 && py < cy2)
667 pp = p_pix + py * prs + px * p_step;
668 *pp = (r * a + *pp * (256-a)) >> 8;
670 *pp = (g * a + *pp * (256-a)) >> 8;
672 *pp = (b * a + *pp * (256-a)) >> 8;
681 t = rx1; rx1 = rx2; rx2 = t;
682 t = ry1; ry1 = ry2; ry2 = t;
686 if (slope != 0.0) slope = (rx2 - rx1) / slope;
687 for (y = ry1; y < ry2; y += 1.0)
689 px = (gint)(rx1 + (y - ry1) * slope + 0.5);
690 py = (gint)(y + 0.5);
692 if (px >= cx1 && px < cx2 && py >= cy1 && py < cy2)
694 pp = p_pix + py * prs + px * p_step;
695 *pp = (r * a + *pp * (256-a)) >> 8;
697 *pp = (g * a + *pp * (256-a)) >> 8;
699 *pp = (b * a + *pp * (256-a)) >> 8;
705 static void pixbuf_draw_fade_linear(guchar *p_pix, gint prs, gint p_alpha,
706 gint s, gint vertical, gint border,
707 gint x1, gint y1, gint x2, gint y2,
708 guint8 r, guint8 g, guint8 b, guint8 a)
715 p_step = (p_alpha) ? 4 : 3;
716 for (j = y1; j < y2; j++)
718 pp = p_pix + j * prs + x1 * p_step;
719 if (!vertical) n = a - a * abs(j - s) / border;
720 for (i = x1; i < x2; i++)
722 if (vertical) n = a - a * abs(i - s) / border;
723 *pp = (r * n + *pp * (256-n)) >> 8;
725 *pp = (g * n + *pp * (256-n)) >> 8;
727 *pp = (b * n + *pp * (256-n)) >> 8;
734 static void pixbuf_draw_fade_radius(guchar *p_pix, gint prs, gint p_alpha,
735 gint sx, gint sy, gint border,
736 gint x1, gint y1, gint x2, gint y2,
737 guint8 r, guint8 g, guint8 b, guint8 a)
743 p_step = (p_alpha) ? 4 : 3;
744 for (j = y1; j < y2; j++)
746 pp = p_pix + j * prs + x1 * p_step;
747 for (i = x1; i < x2; i++)
752 r = MIN(border, (gint)sqrt((i-sx)*(i-sx) + (j-sy)*(j-sy)));
753 n = a - a * r / border;
754 *pp = (r * n + *pp * (256-n)) >> 8;
756 *pp = (g * n + *pp * (256-n)) >> 8;
758 *pp = (b * n + *pp * (256-n)) >> 8;
765 static void pixbuf_draw_shadow(GdkPixbuf *pb,
766 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
767 gint x, gint y, gint w, gint h, gint border,
768 guint8 r, guint8 g, guint8 b, guint8 a)
778 pw = gdk_pixbuf_get_width(pb);
779 ph = gdk_pixbuf_get_height(pb);
781 if (!util_clip_region(0, 0, pw, ph,
782 clip_x, clip_y, clip_w, clip_h,
783 &rx, &ry, &rw, &rh)) return;
785 p_alpha = gdk_pixbuf_get_has_alpha(pb);
786 prs = gdk_pixbuf_get_rowstride(pb);
787 p_pix = gdk_pixbuf_get_pixels(pb);
789 if (util_clip_region(x + border, y + border, w - border * 2, h - border * 2,
793 pixbuf_draw_rect_fill(pb, fx, fy, fw, fh, r, g, b, a);
796 if (border < 1) return;
798 if (util_clip_region(x, y + border, border, h - border * 2,
802 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
803 x + border, TRUE, border,
804 fx, fy, fx + fw, fy + fh,
807 if (util_clip_region(x + w - border, y + border, border, h - border * 2,
811 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
812 x + w - border, TRUE, border,
813 fx, fy, fx + fw, fy + fh,
816 if (util_clip_region(x + border, y, w - border * 2, border,
820 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
821 y + border, FALSE, border,
822 fx, fy, fx + fw, fy + fh,
825 if (util_clip_region(x + border, y + h - border, w - border * 2, border,
829 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
830 y + h - border, FALSE, border,
831 fx, fy, fx + fw, fy + fh,
834 if (util_clip_region(x, y, border, border,
838 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
839 x + border, y + border, border,
840 fx, fy, fx + fw, fy + fh,
843 if (util_clip_region(x + w - border, y, border, border,
847 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
848 x + w - border, y + border, border,
849 fx, fy, fx + fw, fy + fh,
852 if (util_clip_region(x, y + h - border, border, border,
856 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
857 x + border, y + h - border, border,
858 fx, fy, fx + fw, fy + fh,
861 if (util_clip_region(x + w - border, y + h - border, border, border,
865 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
866 x + w - border, y + h - border, border,
867 fx, fy, fx + fw, fy + fh,
874 *-----------------------------------------------------------------------------
876 *-----------------------------------------------------------------------------
879 static void pan_cache_free(PanWindow *pw)
883 work = pw->cache_list;
891 cache_sim_data_free(pc->cd);
892 file_data_free((FileData *)pc);
895 g_list_free(pw->cache_list);
896 pw->cache_list = NULL;
898 filelist_free(pw->cache_todo);
899 pw->cache_todo = NULL;
906 static void pan_cache_fill(PanWindow *pw, const gchar *path)
912 list = pan_window_layout_list(path, SORT_NAME, TRUE);
913 pw->cache_todo = g_list_reverse(list);
915 pw->cache_total = g_list_length(pw->cache_todo);
918 static gint pan_cache_step(PanWindow *pw)
922 CacheData *cd = NULL;
924 if (!pw->cache_todo) return FALSE;
926 fd = pw->cache_todo->data;
927 pw->cache_todo = g_list_remove(pw->cache_todo, fd);
929 if (enable_thumb_caching)
933 found = cache_find_location(CACHE_TYPE_SIM, fd->path);
934 if (found && filetime(found) == fd->date)
936 cd = cache_sim_data_load(found);
941 if (!cd) cd = cache_sim_data_new();
945 cd->dimensions = image_load_dimensions(fd->path, &cd->width, &cd->height);
946 if (enable_thumb_caching &&
952 base = cache_get_location(CACHE_TYPE_SIM, fd->path, FALSE, &mode);
953 if (cache_ensure_dir_exists(base, mode))
956 cd->path = cache_get_location(CACHE_TYPE_SIM, fd->path, TRUE, NULL);
957 if (cache_sim_data_save(cd))
959 filetime_set(cd->path, filetime(fd->path));
968 pc = g_new0(PanCacheData, 1);
969 memcpy(pc, fd, sizeof(FileData));
974 pw->cache_list = g_list_prepend(pw->cache_list, pc);
981 *-----------------------------------------------------------------------------
983 *-----------------------------------------------------------------------------
986 static void pan_item_free(PanItem *pi)
990 if (pi->pixbuf) g_object_unref(pi->pixbuf);
991 if (pi->fd) file_data_free(pi->fd);
999 static void pan_window_items_free(PanWindow *pw)
1006 PanItem *pi = work->data;
1012 g_list_free(pw->list);
1015 g_list_free(pw->queue);
1017 pw->queue_pi = NULL;
1019 image_loader_free(pw->il);
1022 thumb_loader_free(pw->tl);
1025 pw->click_pi = NULL;
1026 pw->search_pi = NULL;
1029 static PanItem *pan_item_new_thumb(PanWindow *pw, FileData *fd, gint x, gint y)
1033 pi = g_new0(PanItem, 1);
1034 pi->type = ITEM_THUMB;
1038 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
1039 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
1045 pw->list = g_list_prepend(pw->list, pi);
1050 static PanItem *pan_item_new_box(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
1052 guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
1053 guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
1057 pi = g_new0(PanItem, 1);
1058 pi->type = ITEM_BOX;
1063 pi->height = height;
1065 pi->color_r = base_r;
1066 pi->color_g = base_g;
1067 pi->color_b = base_b;
1068 pi->color_a = base_a;
1070 pi->color2_r = bord_r;
1071 pi->color2_g = bord_g;
1072 pi->color2_b = bord_b;
1073 pi->color2_a = bord_a;
1074 pi->border = border_size;
1076 pw->list = g_list_prepend(pw->list, pi);
1081 static void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
1085 if (!pi || pi->type != ITEM_BOX) return;
1090 pi->width -= shadow[0];
1091 pi->height -= shadow[0];
1094 shadow = g_new0(gint, 2);
1098 pi->width += offset;
1099 pi->height += offset;
1105 static PanItem *pan_item_new_tri(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
1106 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
1107 guint8 r, guint8 g, guint8 b, guint8 a)
1112 pi = g_new0(PanItem, 1);
1113 pi->type = ITEM_TRIANGLE;
1117 pi->height = height;
1124 coord = g_new0(gint, 6);
1134 pi->border = BORDER_NONE;
1136 pw->list = g_list_prepend(pw->list, pi);
1141 static void pan_item_tri_border(PanItem *pi, gint borders,
1142 guint8 r, guint8 g, guint8 b, guint8 a)
1144 if (!pi || pi->type != ITEM_TRIANGLE) return;
1146 pi->border = borders;
1154 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
1156 PangoLayout *layout;
1158 layout = gtk_widget_create_pango_layout(widget, NULL);
1160 if (pi->text_attr & TEXT_ATTR_MARKUP)
1162 pango_layout_set_markup(layout, pi->text, -1);
1166 if (pi->text_attr & TEXT_ATTR_BOLD ||
1167 pi->text_attr & TEXT_ATTR_HEADING)
1172 pal = pango_attr_list_new();
1173 if (pi->text_attr & TEXT_ATTR_BOLD)
1175 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
1176 pa->start_index = 0;
1177 pa->end_index = G_MAXINT;
1178 pango_attr_list_insert(pal, pa);
1180 if (pi->text_attr & TEXT_ATTR_HEADING)
1182 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
1183 pa->start_index = 0;
1184 pa->end_index = G_MAXINT;
1185 pango_attr_list_insert(pal, pa);
1187 pango_layout_set_attributes(layout, pal);
1188 pango_attr_list_unref(pal);
1191 pango_layout_set_text(layout, pi->text, -1);
1195 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
1197 PangoLayout *layout;
1199 if (!pi || !pi->text || !widget) return;
1201 layout = pan_item_text_layout(pi, widget);
1202 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
1203 g_object_unref(G_OBJECT(layout));
1205 pi->width += PAN_TEXT_BORDER_SIZE * 2;
1206 pi->height += PAN_TEXT_BORDER_SIZE * 2;
1209 static PanItem *pan_item_new_text(PanWindow *pw, gint x, gint y, const gchar *text, TextAttrType attr,
1210 guint8 r, guint8 g, guint8 b, guint8 a)
1214 pi = g_new0(PanItem, 1);
1215 pi->type = ITEM_TEXT;
1218 pi->text = g_strdup(text);
1219 pi->text_attr = attr;
1226 pan_item_text_compute_size(pi, pw->imd->pr);
1228 pw->list = g_list_prepend(pw->list, pi);
1233 static void pan_item_set_key(PanItem *pi, const gchar *key)
1240 pi->key = g_strdup(key);
1244 static void pan_item_added(PanWindow *pw, PanItem *pi)
1247 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1250 static void pan_item_remove(PanWindow *pw, PanItem *pi)
1254 if (pw->click_pi == pi) pw->click_pi = NULL;
1255 if (pw->queue_pi == pi) pw->queue_pi = NULL;
1256 if (pw->search_pi == pi) pw->search_pi = NULL;
1257 pw->queue = g_list_remove(pw->queue, pi);
1259 pw->list = g_list_remove(pw->list, pi);
1260 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1264 static void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
1266 if (!pi || !child) return;
1268 if (pi->x + pi->width < child->x + child->width + border)
1269 pi->width = child->x + child->width + border - pi->x;
1271 if (pi->y + pi->height < child->y + child->height + border)
1272 pi->height = child->y + child->height + border - pi->y;
1275 static void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
1279 if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
1280 if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
1283 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
1290 if (!pi->fd) return;
1292 work = pw->cache_list;
1301 path = ((FileData *)pc)->path;
1303 if (pc->cd && pc->cd->dimensions &&
1304 path && strcmp(path, pi->fd->path) == 0)
1306 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
1307 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
1309 pw->cache_list = g_list_remove(pw->cache_list, pc);
1310 cache_sim_data_free(pc->cd);
1311 file_data_free((FileData *)pc);
1317 static PanItem *pan_item_new_image(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
1321 pi = g_new0(PanItem, 1);
1322 pi->type = ITEM_IMAGE;
1327 pan_item_image_find_size(pw, pi, w, h);
1329 pw->list = g_list_prepend(pw->list, pi);
1334 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
1338 if (!key) return NULL;
1340 work = g_list_last(pw->list);
1346 if ((pi->type == type || type == ITEM_NONE) &&
1347 pi->key && strcmp(pi->key, key) == 0)
1357 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1358 static GList *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1359 gint ignore_case, gint partial)
1364 if (!path) return NULL;
1365 if (partial && path[0] == '/') return NULL;
1367 work = g_list_last(pw->list);
1373 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1379 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
1381 else if (pi->fd->name)
1389 haystack = g_utf8_strdown(pi->fd->name, -1);
1390 match = (strstr(haystack, path) != NULL);
1395 if (strstr(pi->fd->name, path)) match = TRUE;
1398 else if (ignore_case)
1400 if (strcasecmp(path, pi->fd->name) == 0) match = TRUE;
1404 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
1408 if (match) list = g_list_prepend(list, pi);
1413 return g_list_reverse(list);
1416 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y, const gchar *key)
1426 if ((pi->type == type || type == ITEM_NONE) &&
1427 x >= pi->x && x < pi->x + pi->width &&
1428 y >= pi->y && y < pi->y + pi->height &&
1429 (!key || (pi->key && strcmp(pi->key, key) == 0)))
1440 *-----------------------------------------------------------------------------
1442 *-----------------------------------------------------------------------------
1445 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend)
1447 GList *flist = NULL;
1448 GList *dlist = NULL;
1452 filelist_read(path, &flist, &dlist);
1453 if (sort != SORT_NONE)
1455 flist = filelist_sort(flist, sort, ascend);
1456 dlist = filelist_sort(dlist, sort, ascend);
1466 folders = g_list_remove(folders, fd);
1468 if (filelist_read(fd->path, &flist, &dlist))
1470 if (sort != SORT_NONE)
1472 flist = filelist_sort(flist, sort, ascend);
1473 dlist = filelist_sort(dlist, sort, ascend);
1476 result = g_list_concat(result, flist);
1477 folders = g_list_concat(dlist, folders);
1486 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1494 list = pan_window_layout_list(path, SORT_NAME, TRUE);
1496 grid_size = (gint)sqrt((double)g_list_length(list));
1497 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1499 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1503 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1508 *width = PAN_FOLDER_BOX_BORDER * 2;
1509 *height = PAN_FOLDER_BOX_BORDER * 2;
1522 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1524 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1526 x += pi->width + PAN_THUMB_GAP;
1527 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1536 pi = pan_item_new_thumb(pw, fd, x, y);
1538 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1542 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1545 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1551 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1554 gint x1, y1, x2, y2;
1569 if (x1 > pi->x) x1 = pi->x;
1570 if (y1 > pi->y) y1 = pi->y;
1571 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1572 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1575 x1 -= PAN_FOLDER_BOX_BORDER;
1576 y1 -= PAN_FOLDER_BOX_BORDER;
1577 x2 += PAN_FOLDER_BOX_BORDER;
1578 y2 += PAN_FOLDER_BOX_BORDER;
1591 if (pi->type == ITEM_TRIANGLE && pi->data)
1605 if (width) *width = x2 - x1;
1606 if (height) *height = y2 - y1;
1609 typedef struct _FlowerGroup FlowerGroup;
1610 struct _FlowerGroup {
1623 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1627 work = group->items;
1645 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1646 gint *result_x, gint *result_y)
1652 radius = parent->circumference / (2*PI);
1653 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1655 a = 2*PI * group->diameter / parent->circumference;
1657 x = (gint)((double)radius * cos(parent->angle + a / 2));
1658 y = (gint)((double)radius * sin(parent->angle + a / 2));
1665 x += parent->width / 2;
1666 y += parent->height / 2;
1668 x -= group->width / 2;
1669 y -= group->height / 2;
1675 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1682 if (parent && parent->children)
1684 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1692 pan_window_layout_compute_folder_flower_move(group, x, y);
1697 gint px, py, gx, gy;
1698 gint x1, y1, x2, y2;
1700 px = parent->x + parent->width / 2;
1701 py = parent->y + parent->height / 2;
1703 gx = group->x + group->width / 2;
1704 gy = group->y + group->height / 2;
1709 x2 = MAX(px, gx + 5);
1710 y2 = MAX(py, gy + 5);
1712 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1713 px, py, gx, gy, gx + 5, gy + 5,
1715 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1719 pw->list = g_list_concat(group->items, pw->list);
1720 group->items = NULL;
1722 group->circumference = 0;
1723 work = group->children;
1731 group->circumference += child->diameter;
1734 work = g_list_last(group->children);
1742 pan_window_layout_compute_folder_flower_build(pw, child, group);
1745 g_list_free(group->children);
1749 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1762 if (!filelist_read(path, &f, &d)) return NULL;
1763 if (!f && !d) return NULL;
1765 f = filelist_sort(f, SORT_NAME, TRUE);
1766 d = filelist_sort(d, SORT_NAME, TRUE);
1768 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1769 PAN_TEXT_COLOR, 255);
1771 y += pi_box->height;
1773 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1775 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1776 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1777 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1778 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1780 x += PAN_FOLDER_BOX_BORDER;
1781 y += PAN_FOLDER_BOX_BORDER;
1783 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1797 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1799 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1800 x += pi->width + PAN_THUMB_GAP;
1801 if (pi->height > y_height) y_height = pi->height;
1805 pi = pan_item_new_thumb(pw, fd, x, y);
1806 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1807 y_height = PAN_THUMB_SIZE;
1811 if (grid_count >= grid_size)
1815 y += y_height + PAN_THUMB_GAP;
1819 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1824 group = g_new0(FlowerGroup, 1);
1825 group->items = pw->list;
1828 group->width = pi_box->width;
1829 group->height = pi_box->y + pi_box->height;
1830 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1832 group->children = NULL;
1843 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1844 if (child) group->children = g_list_prepend(group->children, child);
1852 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1853 gint *width, gint *height,
1854 gint *scroll_x, gint *scroll_y)
1859 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1860 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1862 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1864 list = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1867 PanItem *pi = list->data;
1868 *scroll_x = pi->x + pi->width / 2;
1869 *scroll_y = pi->y + pi->height / 2;
1874 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1875 gint *x, gint *y, gint *level,
1877 gint *width, gint *height)
1885 if (!filelist_read(path, &f, &d)) return;
1886 if (!f && !d) return;
1888 f = filelist_sort(f, SORT_NAME, TRUE);
1889 d = filelist_sort(d, SORT_NAME, TRUE);
1891 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1893 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1894 PAN_TEXT_COLOR, 255);
1896 *y += pi_box->height;
1898 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1900 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1901 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1902 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1903 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1905 *x += PAN_FOLDER_BOX_BORDER;
1906 *y += PAN_FOLDER_BOX_BORDER;
1917 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1919 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1920 *x += pi->width + PAN_THUMB_GAP;
1921 if (pi->height > y_height) y_height = pi->height;
1925 pi = pan_item_new_thumb(pw, fd, *x, *y);
1926 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1927 y_height = PAN_THUMB_SIZE;
1930 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1933 if (f) *y = pi_box->y + pi_box->height;
1945 *level = *level + 1;
1946 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1947 pi_box, width, height);
1948 *level = *level - 1;
1953 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1955 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1956 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1958 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1961 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1968 x = PAN_FOLDER_BOX_BORDER;
1969 y = PAN_FOLDER_BOX_BORDER;
1970 w = PAN_FOLDER_BOX_BORDER * 2;
1971 h = PAN_FOLDER_BOX_BORDER * 2;
1973 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1975 if (width) *width = w;
1976 if (height) *height = h;
1980 *-----------------------------------------------------------------------------
1982 *-----------------------------------------------------------------------------
1985 #define PAN_CAL_DAY_WIDTH 100
1986 #define PAN_CAL_DAY_HEIGHT 80
1987 #define PAN_CAL_DOT_SIZE 3
1988 #define PAN_CAL_DOT_GAP 2
1989 #define PAN_CAL_DOT_COLOR 0, 0, 0
1990 #define PAN_CAL_DOT_ALPHA 32
1992 static void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
1998 gint x1, y1, x2, y2, x3, y3;
2003 while ((pi = pan_item_find_by_key(pw, ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
2005 if (!pi_day || pi_day->type != ITEM_BOX ||
2006 !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
2008 list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
2020 if (dot->type != ITEM_BOX || !dot->fd ||
2021 !dot->key || strcmp(dot->key, "dot") != 0)
2023 list = g_list_delete_link(list, node);
2031 grid = (gint)(sqrt(g_list_length(list)) + 0.5);
2033 x = pi_day->x + pi_day->width + 4;
2037 if (y + grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4 > pw->pr->image_height)
2039 y = pw->pr->image_height - (grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4);
2043 pbox = pan_item_new_box(pw, NULL, x, y, PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
2045 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
2046 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
2047 pan_item_set_key(pbox, "day_bubble");
2054 buf = date_value_string(pi_day->fd->date, DATE_LENGTH_WEEK);
2055 plabel = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2056 PAN_POPUP_TEXT_COLOR, 255);
2057 pan_item_set_key(plabel, "day_bubble");
2060 pan_item_size_by_item(pbox, plabel, 0);
2062 y += plabel->height;
2069 x += PAN_FOLDER_BOX_BORDER;
2070 y += PAN_FOLDER_BOX_BORDER;
2084 pimg = pan_item_new_thumb(pw, file_data_new_simple(dot->fd->path), x, y);
2085 pan_item_set_key(pimg, "day_bubble");
2087 pan_item_size_by_item(pbox, pimg, PAN_FOLDER_BOX_BORDER);
2092 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
2097 x = pbox->x + PAN_FOLDER_BOX_BORDER;
2098 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
2104 x1 = pi_day->x + pi_day->width - 8;
2107 y2 = pbox->y + MIN(42, pbox->height);
2109 y3 = MAX(pbox->y, y2 - 30);
2110 triangle_rect_region(x1, y1, x2, y2, x3, y3,
2113 pi = pan_item_new_tri(pw, NULL, x, y, w, h,
2114 x1, y1, x2, y2, x3, y3,
2115 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
2116 pan_item_tri_border(pi, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
2117 pan_item_set_key(pi, "day_bubble");
2118 pan_item_added(pw, pi);
2120 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
2121 pan_item_added(pw, pbox);
2124 static void pan_window_layout_compute_calendar(PanWindow *pw, const gchar *path, gint *width, gint *height)
2140 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2142 list = pan_window_layout_list(path, SORT_NONE, TRUE);
2143 list = filelist_sort(list, SORT_TIME, TRUE);
2156 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2164 if (day_max < count) day_max = count;
2168 printf("biggest day contains %d images\n", day_max);
2170 grid = (gint)(sqrt((double)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
2171 day_width = MAX(PAN_CAL_DAY_WIDTH, grid);
2172 day_height = MAX(PAN_CAL_DAY_HEIGHT, grid);
2176 FileData *fd = list->data;
2178 year = date_value(fd->date, DATE_LENGTH_YEAR);
2179 month = date_value(fd->date, DATE_LENGTH_MONTH);
2182 work = g_list_last(list);
2185 FileData *fd = work->data;
2186 end_year = date_value(fd->date, DATE_LENGTH_YEAR);
2187 end_month = date_value(fd->date, DATE_LENGTH_MONTH);
2190 *width = PAN_FOLDER_BOX_BORDER * 2;
2191 *height = PAN_FOLDER_BOX_BORDER * 2;
2193 x = PAN_FOLDER_BOX_BORDER;
2194 y = PAN_FOLDER_BOX_BORDER;
2197 while (work && (year < end_year || (year == end_year && month <= end_month)))
2208 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
2210 days = date_value(dt, DATE_LENGTH_DAY);
2211 dt = date_to_time(year, month, 1);
2212 col = date_value(dt, DATE_LENGTH_WEEK);
2215 x = PAN_FOLDER_BOX_BORDER;
2217 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
2218 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2219 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2220 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2221 buf = date_value_string(dt, DATE_LENGTH_MONTH);
2222 pi_text = pan_item_new_text(pw, x, y, buf,
2223 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2224 PAN_TEXT_COLOR, 255);
2226 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
2228 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
2230 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
2231 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
2233 for (day = 1; day <= days; day++)
2240 dt = date_to_time(year, month, day);
2242 fd = g_new0(FileData, 1);
2243 /* path and name must be non NULL, so make them an invalid filename */
2244 fd->path = g_strdup("//");
2247 pi_day = pan_item_new_box(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
2248 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2249 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2250 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2251 pan_item_set_key(pi_day, "day");
2253 dx = x + PAN_CAL_DOT_GAP * 2;
2254 dy = y + PAN_CAL_DOT_GAP * 2;
2256 fd = (work) ? work->data : NULL;
2257 while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
2261 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
2263 PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
2265 pan_item_set_key(pi, "dot");
2267 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2268 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
2270 dx = x + PAN_CAL_DOT_GAP * 2;
2271 dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2273 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
2275 /* must keep all dots within respective day even if it gets ugly */
2276 dy = y + PAN_CAL_DOT_GAP * 2;
2279 pi_day->color_a = MIN(PAN_FOLDER_BOX_ALPHA + 64 + n, 255);
2283 fd = (work) ? work->data : NULL;
2290 buf = g_strdup_printf("( %d )", n);
2291 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2292 PAN_TEXT_COLOR, 255);
2295 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
2296 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
2299 buf = g_strdup_printf("%d", day);
2300 pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2301 PAN_TEXT_COLOR, 255);
2305 pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
2312 x = PAN_FOLDER_BOX_BORDER;
2313 y += PAN_CAL_DAY_HEIGHT;
2317 x += PAN_CAL_DAY_WIDTH;
2321 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
2322 y += PAN_FOLDER_BOX_BORDER * 2;
2333 *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
2338 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
2346 PanItem *pi_month = NULL;
2347 PanItem *pi_day = NULL;
2353 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2355 list = pan_window_layout_list(path, SORT_NONE, TRUE);
2356 list = filelist_sort(list, SORT_TIME, TRUE);
2358 *width = PAN_FOLDER_BOX_BORDER * 2;
2359 *height = PAN_FOLDER_BOX_BORDER * 2;
2364 day_start = month_start;
2379 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2384 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
2390 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
2394 x = PAN_FOLDER_BOX_BORDER;
2397 y = PAN_FOLDER_BOX_BORDER;
2399 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
2400 pi = pan_item_new_text(pw, x, y, buf,
2401 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2402 PAN_TEXT_COLOR, 255);
2405 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
2407 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2408 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2409 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2411 x += PAN_FOLDER_BOX_BORDER;
2412 y += PAN_FOLDER_BOX_BORDER;
2416 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
2428 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
2430 needle = needle->next;
2439 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
2440 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2441 PAN_TEXT_COLOR, 255);
2446 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
2447 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2448 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2449 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2451 x += PAN_FOLDER_BOX_BORDER;
2452 y += PAN_FOLDER_BOX_BORDER;
2456 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2458 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
2459 if (pi->width > x_width) x_width = pi->width;
2460 y_height = pi->height;
2464 pi = pan_item_new_thumb(pw, fd, x, y);
2465 x_width = PAN_THUMB_SIZE;
2466 y_height = PAN_THUMB_SIZE;
2469 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
2470 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
2475 if (total > 0 && count < PAN_GROUP_MAX)
2477 y += y_height + PAN_THUMB_GAP;
2481 x += x_width + PAN_THUMB_GAP;
2491 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
2497 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
2498 gint *width, gint *height,
2499 gint *scroll_x, gint *scroll_y)
2501 pan_window_items_free(pw);
2505 case LAYOUT_SIZE_THUMB_DOTS:
2506 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2507 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2509 case LAYOUT_SIZE_THUMB_NONE:
2510 pw->thumb_size = PAN_THUMB_SIZE_NONE;
2511 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2513 case LAYOUT_SIZE_THUMB_SMALL:
2514 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2515 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2517 case LAYOUT_SIZE_THUMB_NORMAL:
2519 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2520 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2522 case LAYOUT_SIZE_THUMB_LARGE:
2523 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2524 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2526 case LAYOUT_SIZE_10:
2527 pw->image_size = 10;
2528 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2530 case LAYOUT_SIZE_25:
2531 pw->image_size = 25;
2532 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2534 case LAYOUT_SIZE_33:
2535 pw->image_size = 33;
2536 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2538 case LAYOUT_SIZE_50:
2539 pw->image_size = 50;
2540 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2542 case LAYOUT_SIZE_100:
2543 pw->image_size = 100;
2544 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2557 pan_window_layout_compute_grid(pw, path, width, height);
2559 case LAYOUT_FOLDERS_LINEAR:
2560 pan_window_layout_compute_folders_linear(pw, path, width, height);
2562 case LAYOUT_FOLDERS_FLOWER:
2563 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2565 case LAYOUT_CALENDAR:
2566 pan_window_layout_compute_calendar(pw, path, width, height);
2568 case LAYOUT_TIMELINE:
2569 pan_window_layout_compute_timeline(pw, path, width, height);
2575 printf("computed %d objects\n", g_list_length(pw->list));
2578 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2591 if (util_clip_region_test(x, y, width, height,
2592 pi->x, pi->y, pi->width, pi->height))
2594 list = g_list_prepend(list, pi);
2604 *-----------------------------------------------------------------------------
2606 *-----------------------------------------------------------------------------
2609 static gint pan_layout_queue_step(PanWindow *pw);
2612 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2614 PanWindow *pw = data;
2622 pw->queue_pi = NULL;
2626 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2627 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2630 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2634 thumb_loader_free(pw->tl);
2637 while (pan_layout_queue_step(pw));
2640 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2642 PanWindow *pw = data;
2650 pw->queue_pi = NULL;
2654 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2655 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2656 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2658 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2659 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2660 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2665 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2666 (GdkInterpType)zoom_quality);
2667 g_object_unref(tmp);
2671 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2675 image_loader_free(pw->il);
2678 while (pan_layout_queue_step(pw));
2682 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2683 guint width, guint height, gpointer data)
2685 PanWindow *pw = data;
2696 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2697 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2701 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2707 static gint pan_layout_queue_step(PanWindow *pw)
2711 if (!pw->queue) return FALSE;
2713 pi = pw->queue->data;
2714 pw->queue = g_list_remove(pw->queue, pi);
2717 if (!pw->queue_pi->fd)
2719 pw->queue_pi->queued = FALSE;
2720 pw->queue_pi = NULL;
2724 image_loader_free(pw->il);
2726 thumb_loader_free(pw->tl);
2729 if (pi->type == ITEM_IMAGE)
2731 pw->il = image_loader_new(pi->fd->path);
2733 if (pw->size != LAYOUT_SIZE_100)
2735 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2739 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2741 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2743 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2745 image_loader_free(pw->il);
2748 else if (pi->type == ITEM_THUMB)
2750 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2752 if (!pw->tl->standard_loader)
2754 /* The classic loader will recreate a thumbnail any time we
2755 * request a different size than what exists. This view will
2756 * almost never use the user configured sizes so disable cache.
2758 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2761 thumb_loader_set_callbacks(pw->tl,
2762 pan_layout_queue_thumb_done_cb,
2763 pan_layout_queue_thumb_done_cb,
2766 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2768 thumb_loader_free(pw->tl);
2772 pw->queue_pi->queued = FALSE;
2773 pw->queue_pi = NULL;
2777 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2779 if (!pi || pi->queued || pi->pixbuf) return;
2780 if (pw->size <= LAYOUT_SIZE_THUMB_NONE) return;
2783 pw->queue = g_list_prepend(pw->queue, pi);
2785 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2788 static gint pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
2789 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2791 PanWindow *pw = data;
2796 pixbuf_set_rect_fill(pixbuf,
2797 0, 0, width, height,
2798 PAN_BACKGROUND_COLOR, 255);
2800 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2802 gint rx, ry, rw, rh;
2804 if (util_clip_region(x, y, width, height,
2806 &rx, &ry, &rw, &rh))
2808 pixbuf_draw_rect_fill(pixbuf,
2809 rx - x, ry - y, rw, rh,
2810 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2813 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2815 gint rx, ry, rw, rh;
2817 if (util_clip_region(x, y, width, height,
2819 &rx, &ry, &rw, &rh))
2821 pixbuf_draw_rect_fill(pixbuf,
2822 rx - x, ry - y, rw, rh,
2823 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2827 list = pan_layout_intersect(pw, x, y, width, height);
2832 gint tx, ty, tw, th;
2833 gint rx, ry, rw, rh;
2840 if (pi->type == ITEM_THUMB && pi->pixbuf)
2842 tw = gdk_pixbuf_get_width(pi->pixbuf);
2843 th = gdk_pixbuf_get_height(pi->pixbuf);
2845 tx = pi->x + (pi->width - tw) / 2;
2846 ty = pi->y + (pi->height - th) / 2;
2848 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2850 if (util_clip_region(x, y, width, height,
2851 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2852 &rx, &ry, &rw, &rh))
2854 pixbuf_draw_shadow(pixbuf,
2855 rx - x, ry - y, rw, rh,
2856 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2858 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2863 if (util_clip_region(x, y, width, height,
2864 tx + tw, ty + PAN_SHADOW_OFFSET,
2865 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2866 &rx, &ry, &rw, &rh))
2868 pixbuf_draw_shadow(pixbuf,
2869 rx - x, ry - y, rw, rh,
2870 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2872 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2874 if (util_clip_region(x, y, width, height,
2875 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2876 &rx, &ry, &rw, &rh))
2878 pixbuf_draw_shadow(pixbuf,
2879 rx - x, ry - y, rw, rh,
2880 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2882 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2886 if (util_clip_region(x, y, width, height,
2888 &rx, &ry, &rw, &rh))
2890 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2893 1.0, 1.0, GDK_INTERP_NEAREST,
2897 if (util_clip_region(x, y, width, height,
2898 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2899 &rx, &ry, &rw, &rh))
2901 pixbuf_draw_rect_fill(pixbuf,
2902 rx - x, ry - y, rw, rh,
2903 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2905 if (util_clip_region(x, y, width, height,
2906 tx, ty, PAN_OUTLINE_THICKNESS, th,
2907 &rx, &ry, &rw, &rh))
2909 pixbuf_draw_rect_fill(pixbuf,
2910 rx - x, ry - y, rw, rh,
2911 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2913 if (util_clip_region(x, y, width, height,
2914 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2915 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2916 &rx, &ry, &rw, &rh))
2918 pixbuf_draw_rect_fill(pixbuf,
2919 rx - x, ry - y, rw, rh,
2920 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2922 if (util_clip_region(x, y, width, height,
2923 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2924 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2925 &rx, &ry, &rw, &rh))
2927 pixbuf_draw_rect_fill(pixbuf,
2928 rx - x, ry - y, rw, rh,
2929 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2932 else if (pi->type == ITEM_THUMB)
2934 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2935 th = pi->height - PAN_SHADOW_OFFSET * 2;
2936 tx = pi->x + PAN_SHADOW_OFFSET;
2937 ty = pi->y + PAN_SHADOW_OFFSET;
2939 if (util_clip_region(x, y, width, height,
2941 &rx, &ry, &rw, &rh))
2945 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2946 pixbuf_draw_rect_fill(pixbuf,
2947 rx - x, ry - y, rw, rh,
2949 PAN_SHADOW_ALPHA / d);
2952 pan_layout_queue(pw, pi);
2954 else if (pi->type == ITEM_IMAGE)
2956 if (util_clip_region(x, y, width, height,
2957 pi->x, pi->y, pi->width, pi->height,
2958 &rx, &ry, &rw, &rh))
2962 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2965 1.0, 1.0, GDK_INTERP_NEAREST,
2970 pixbuf_draw_rect_fill(pixbuf,
2971 rx - x, ry - y, rw, rh,
2972 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2973 pan_layout_queue(pw, pi);
2977 else if (pi->type == ITEM_BOX)
2991 if (pi->color_a > 254)
2993 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2994 shadow[0], bh - shadow[0],
2995 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2997 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2998 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
3000 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
3002 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
3007 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
3008 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
3010 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
3012 PAN_SHADOW_COLOR, a);
3016 if (util_clip_region(x, y, width, height,
3017 pi->x, pi->y, bw, bh,
3018 &rx, &ry, &rw, &rh))
3020 pixbuf_draw_rect_fill(pixbuf,
3021 rx - x, ry - y, rw, rh,
3022 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3024 if (util_clip_region(x, y, width, height,
3025 pi->x, pi->y, bw, pi->border,
3026 &rx, &ry, &rw, &rh))
3028 pixbuf_draw_rect_fill(pixbuf,
3029 rx - x, ry - y, rw, rh,
3030 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3032 if (util_clip_region(x, y, width, height,
3033 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
3034 &rx, &ry, &rw, &rh))
3036 pixbuf_draw_rect_fill(pixbuf,
3037 rx - x, ry - y, rw, rh,
3038 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3040 if (util_clip_region(x, y, width, height,
3041 pi->x + bw - pi->border, pi->y + pi->border,
3042 pi->border, bh - pi->border * 2,
3043 &rx, &ry, &rw, &rh))
3045 pixbuf_draw_rect_fill(pixbuf,
3046 rx - x, ry - y, rw, rh,
3047 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3049 if (util_clip_region(x, y, width, height,
3050 pi->x, pi->y + bh - pi->border,
3052 &rx, &ry, &rw, &rh))
3054 pixbuf_draw_rect_fill(pixbuf,
3055 rx - x, ry - y, rw, rh,
3056 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3059 else if (pi->type == ITEM_TRIANGLE)
3061 if (util_clip_region(x, y, width, height,
3062 pi->x, pi->y, pi->width, pi->height,
3063 &rx, &ry, &rw, &rh) && pi->data)
3065 gint *coord = pi->data;
3066 pixbuf_draw_triangle(pixbuf,
3067 rx - x, ry - y, rw, rh,
3068 coord[0] - x, coord[1] - y,
3069 coord[2] - x, coord[3] - y,
3070 coord[4] - x, coord[5] - y,
3071 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3073 if (pi->border & BORDER_1)
3075 pixbuf_draw_line(pixbuf,
3076 rx - x, ry - y, rw, rh,
3077 coord[0] - x, coord[1] - y,
3078 coord[2] - x, coord[3] - y,
3079 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3081 if (pi->border & BORDER_2)
3083 pixbuf_draw_line(pixbuf,
3084 rx - x, ry - y, rw, rh,
3085 coord[2] - x, coord[3] - y,
3086 coord[4] - x, coord[5] - y,
3087 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3089 if (pi->border & BORDER_3)
3091 pixbuf_draw_line(pixbuf,
3092 rx - x, ry - y, rw, rh,
3093 coord[4] - x, coord[5] - y,
3094 coord[0] - x, coord[1] - y,
3095 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3099 else if (pi->type == ITEM_TEXT && pi->text)
3101 PangoLayout *layout;
3103 layout = pan_item_text_layout(pi, (GtkWidget *)pr);
3104 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
3105 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
3106 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3107 g_object_unref(G_OBJECT(layout));
3113 if (x%512 == 0 && y%512 == 0)
3115 PangoLayout *layout;
3118 layout = gtk_widget_create_pango_layout((GtkWidget *)pr, NULL);
3120 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
3121 (x / pr->source_tile_width) +
3122 (y / pr->source_tile_height * (pr->image_width/pr->source_tile_width + 1)));
3123 pango_layout_set_text(layout, buf, -1);
3126 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr, 0, 0, 0, 0, 0, 255);
3128 g_object_unref(G_OBJECT(layout));
3135 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
3136 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
3138 PanWindow *pw = data;
3142 list = pan_layout_intersect(pw, x, y, width, height);
3151 if (pi->refcount > 0)
3155 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
3160 pw->queue = g_list_remove(pw->queue, pi);
3163 if (pw->queue_pi == pi) pw->queue_pi = NULL;
3166 g_object_unref(pi->pixbuf);
3178 *-----------------------------------------------------------------------------
3180 *-----------------------------------------------------------------------------
3183 static void pan_window_message(PanWindow *pw, const gchar *text)
3193 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
3198 if (pw->layout == LAYOUT_CALENDAR)
3208 pi->type == ITEM_BOX &&
3209 pi->key && strcmp(pi->key, "dot") == 0)
3211 size += pi->fd->size;
3226 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
3228 size += pi->fd->size;
3234 ss = text_from_size_abrev(size);
3235 buf = g_strdup_printf(_("%d images, %s"), count, ss);
3237 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
3241 static void pan_window_zoom_limit(PanWindow *pw)
3247 case LAYOUT_SIZE_THUMB_DOTS:
3248 case LAYOUT_SIZE_THUMB_NONE:
3249 case LAYOUT_SIZE_THUMB_SMALL:
3250 case LAYOUT_SIZE_THUMB_NORMAL:
3252 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
3256 case LAYOUT_SIZE_THUMB_LARGE:
3259 case LAYOUT_SIZE_10:
3260 case LAYOUT_SIZE_25:
3263 case LAYOUT_SIZE_33:
3264 case LAYOUT_SIZE_50:
3265 case LAYOUT_SIZE_100:
3271 image_zoom_set_limits(pw->imd, min, 32.0);
3274 static gint pan_window_layout_update_idle_cb(gpointer data)
3276 PanWindow *pw = data;
3282 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
3284 if (!pw->cache_list && !pw->cache_todo)
3286 pan_cache_fill(pw, pw->path);
3289 pan_window_message(pw, _("Reading dimensions..."));
3293 if (pan_cache_step(pw))
3297 if (pw->cache_count == pw->cache_total)
3299 pan_window_message(pw, _("Sorting images..."));
3301 else if (pw->cache_tick > 9)
3305 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
3306 pw->cache_total - pw->cache_count);
3307 pan_window_message(pw, buf);
3317 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3319 pan_window_zoom_limit(pw);
3321 if (width > 0 && height > 0)
3325 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
3326 PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
3327 pan_window_request_tile_cb,
3328 pan_window_dispose_tile_cb, pw, 1.0);
3330 if (scroll_x == 0 && scroll_y == 0)
3338 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
3341 pan_window_message(pw, NULL);
3348 static void pan_window_layout_update_idle(PanWindow *pw)
3350 if (pw->idle_id == -1)
3352 pan_window_message(pw, _("Sorting images..."));
3353 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3358 *-----------------------------------------------------------------------------
3359 * pan window keyboard
3360 *-----------------------------------------------------------------------------
3363 static const gchar *pan_menu_click_path(PanWindow *pw)
3365 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3369 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3371 PanWindow *pw = data;
3373 gdk_window_get_origin(pw->imd->pr->window, x, y);
3374 popup_menu_position_clamp(menu, x, y, 0);
3377 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3379 PanWindow *pw = data;
3382 gint stop_signal = FALSE;
3388 pr = PIXBUF_RENDERER(pw->imd->pr);
3389 path = pan_menu_click_path(pw);
3391 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pw->imd->widget)));
3395 switch (event->keyval)
3397 case GDK_Left: case GDK_KP_Left:
3401 case GDK_Right: case GDK_KP_Right:
3405 case GDK_Up: case GDK_KP_Up:
3409 case GDK_Down: case GDK_KP_Down:
3413 case GDK_Page_Up: case GDK_KP_Page_Up:
3414 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
3416 case GDK_Page_Down: case GDK_KP_Page_Down:
3417 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
3419 case GDK_Home: case GDK_KP_Home:
3420 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
3422 case GDK_End: case GDK_KP_End:
3423 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
3428 if (focused && !(event->state & GDK_CONTROL_MASK) )
3429 switch (event->keyval)
3431 case '+': case '=': case GDK_KP_Add:
3432 pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
3434 case '-': case GDK_KP_Subtract:
3435 pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
3437 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3438 pixbuf_renderer_zoom_set(pr, 1.0);
3441 pixbuf_renderer_zoom_set(pr, 2.0);
3444 pixbuf_renderer_zoom_set(pr, 3.0);
3447 pixbuf_renderer_zoom_set(pr, 4.0);
3450 pixbuf_renderer_zoom_set(pr, -4.0);
3453 pixbuf_renderer_zoom_set(pr, -3.0);
3456 pixbuf_renderer_zoom_set(pr, -2.0);
3460 pan_fullscreen_toggle(pw, FALSE);
3465 pan_overlay_toggle(pw);
3468 case GDK_Delete: case GDK_KP_Delete:
3473 if (GTK_WIDGET_VISIBLE(pw->search_box))
3475 gtk_widget_grab_focus(pw->search_entry);
3479 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3487 pan_fullscreen_toggle(pw, TRUE);
3490 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3492 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3498 menu = pan_popup_menu(pw);
3499 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3504 if (event->state & GDK_CONTROL_MASK)
3507 switch (event->keyval)
3540 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3543 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3546 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3549 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3552 if (path) info_window_new(path, NULL);
3555 pan_window_close(pw);
3558 if (n != -1 && path)
3560 pan_fullscreen_toggle(pw, TRUE);
3561 start_editor_from_file(n, path);
3565 else if (event->state & GDK_SHIFT_MASK)
3572 switch (event->keyval)
3577 pan_fullscreen_toggle(pw, TRUE);
3580 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3582 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
3583 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3592 if (x != 0 || y!= 0)
3594 keyboard_scroll_calc(&x, &y, event);
3595 pixbuf_renderer_scroll(pr, x, y);
3602 *-----------------------------------------------------------------------------
3604 *-----------------------------------------------------------------------------
3607 static void pan_info_update(PanWindow *pw, PanItem *pi)
3613 gint x1, y1, x2, y2, x3, y3;
3616 if (pw->click_pi == pi) return;
3617 if (pi && !pi->fd) pi = NULL;
3619 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3624 printf("info set to %s\n", pi->fd->path);
3626 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3628 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3629 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3630 pan_item_set_key(pbox, "info");
3632 if (pi->type == ITEM_THUMB && pi->pixbuf)
3634 w = gdk_pixbuf_get_width(pi->pixbuf);
3635 h = gdk_pixbuf_get_height(pi->pixbuf);
3637 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3638 y1 = pi->y + (pi->height - h) / 2 + 8;
3642 x1 = pi->x + pi->width - 8;
3650 triangle_rect_region(x1, y1, x2, y2, x3, y3,
3653 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3654 x1, y1, x2, y2, x3, y3,
3655 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3656 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3657 pan_item_set_key(p, "info");
3658 pan_item_added(pw, p);
3660 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3661 _("Filename:"), TEXT_ATTR_BOLD,
3662 PAN_POPUP_TEXT_COLOR, 255);
3663 pan_item_set_key(plabel, "info");
3664 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3665 pi->fd->name, TEXT_ATTR_NONE,
3666 PAN_POPUP_TEXT_COLOR, 255);
3667 pan_item_set_key(p, "info");
3668 pan_item_size_by_item(pbox, p, 0);
3670 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3671 _("Date:"), TEXT_ATTR_BOLD,
3672 PAN_POPUP_TEXT_COLOR, 255);
3673 pan_item_set_key(plabel, "info");
3674 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3675 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3676 PAN_POPUP_TEXT_COLOR, 255);
3677 pan_item_set_key(p, "info");
3678 pan_item_size_by_item(pbox, p, 0);
3680 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3681 _("Size:"), TEXT_ATTR_BOLD,
3682 PAN_POPUP_TEXT_COLOR, 255);
3683 pan_item_set_key(plabel, "info");
3684 buf = text_from_size(pi->fd->size);
3685 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3686 buf, TEXT_ATTR_NONE,
3687 PAN_POPUP_TEXT_COLOR, 255);
3689 pan_item_set_key(p, "info");
3690 pan_item_size_by_item(pbox, p, 0);
3692 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3693 pan_item_added(pw, pbox);
3698 *-----------------------------------------------------------------------------
3700 *-----------------------------------------------------------------------------
3703 static void pan_search_status(PanWindow *pw, const gchar *text)
3705 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3708 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3716 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3718 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3719 if (!list) return FALSE;
3721 found = g_list_find(list, pw->click_pi);
3722 if (found && found->next)
3724 found = found->next;
3732 pan_info_update(pw, pi);
3733 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3735 buf = g_strdup_printf("%s ( %d / %d )",
3736 (path[0] == '/') ? _("path found") : _("filename found"),
3737 g_list_index(list, pi) + 1,
3738 g_list_length(list));
3739 pan_search_status(pw, buf);
3747 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3755 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3757 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3758 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3763 needle = g_utf8_strdown(text, -1);
3764 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3767 if (!list) return FALSE;
3769 found = g_list_find(list, pw->click_pi);
3770 if (found && found->next)
3772 found = found->next;
3780 pan_info_update(pw, pi);
3781 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3783 buf = g_strdup_printf("%s ( %d / %d )",
3785 g_list_index(list, pi) + 1,
3786 g_list_length(list));
3787 pan_search_status(pw, buf);
3795 static gint valid_date_separator(gchar c)
3797 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3800 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
3801 gint year, gint month, gint day,
3807 work = g_list_last(pw->list);
3815 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
3816 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
3820 tl = localtime(&pi->fd->date);
3825 match = (tl->tm_year == year - 1900);
3826 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3827 if (match && day > 0) match = (tl->tm_mday == day);
3829 if (match) list = g_list_prepend(list, pi);
3834 return g_list_reverse(list);
3837 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3853 if (!text) return FALSE;
3855 ptr = (gchar *)text;
3856 while (*ptr != '\0')
3858 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3863 if (t == -1) return FALSE;
3865 if (!lt) return FALSE;
3867 if (valid_date_separator(*text))
3870 mptr = (gchar *)text;
3874 year = (gint)strtol(text, &mptr, 10);
3875 if (mptr == text) return FALSE;
3878 if (*mptr != '\0' && valid_date_separator(*mptr))
3883 month = strtol(mptr, &dptr, 10);
3886 if (valid_date_separator(*dptr))
3888 month = lt->tm_mon + 1;
3896 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3900 day = strtol(dptr, &eptr, 10);
3910 year = lt->tm_year + 1900;
3912 else if (year < 100)
3921 month < -1 || month == 0 || month > 12 ||
3922 day < -1 || day == 0 || day > 31) return FALSE;
3924 t = date_to_time(year, month, day);
3925 if (t < 0) return FALSE;
3927 if (pw->layout == LAYOUT_CALENDAR)
3929 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
3935 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3936 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
3941 found = g_list_find(list, pw->search_pi);
3942 if (found && found->next)
3944 found = found->next;
3955 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
3957 pan_info_update(pw, NULL);
3958 pan_calendar_update(pw, pi);
3959 image_scroll_to_point(pw->imd,
3960 pi->x + pi->width / 2,
3961 pi->y + pi->height / 2, 0.5, 0.5);
3965 pan_info_update(pw, pi);
3966 image_scroll_to_point(pw->imd,
3967 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3973 buf = date_value_string(t, DATE_LENGTH_MONTH);
3978 buf = g_strdup_printf("%d %s", day, tmp);
3984 buf = date_value_string(t, DATE_LENGTH_YEAR);
3989 buf_count = g_strdup_printf("( %d / %d )",
3990 g_list_index(list, pi) + 1,
3991 g_list_length(list));
3995 buf_count = g_strdup_printf("(%s)", _("no match"));
3998 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
4001 pan_search_status(pw, message);
4009 static void pan_search_activate_cb(const gchar *text, gpointer data)
4011 PanWindow *pw = data;
4015 tab_completion_append_to_history(pw->search_entry, text);
4017 if (pan_search_by_path(pw, text)) return;
4019 if ((pw->layout == LAYOUT_TIMELINE ||
4020 pw->layout == LAYOUT_CALENDAR) &&
4021 pan_search_by_date(pw, text))
4026 if (pan_search_by_partial(pw, text)) return;
4028 pan_search_status(pw, _("no match"));
4031 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
4033 PanWindow *pw = data;
4036 visible = GTK_WIDGET_VISIBLE(pw->search_box);
4037 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
4041 gtk_widget_hide(pw->search_box);
4042 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
4046 gtk_widget_show(pw->search_box);
4047 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
4048 gtk_widget_grab_focus(pw->search_entry);
4054 *-----------------------------------------------------------------------------
4055 * view window main routines
4056 *-----------------------------------------------------------------------------
4059 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
4061 PanWindow *pw = data;
4069 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
4070 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
4073 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
4076 switch (event->button)
4079 pan_info_update(pw, pi);
4081 if (!pi && pw->layout == LAYOUT_CALENDAR)
4083 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
4084 pan_calendar_update(pw, pi);
4090 pan_info_update(pw, pi);
4091 menu = pan_popup_menu(pw);
4092 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
4099 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
4102 PanWindow *pw = data;
4109 if (!(event->state & GDK_SHIFT_MASK))
4115 if (event->state & GDK_CONTROL_MASK)
4117 switch (event->direction)
4120 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
4121 (gint)event->x, (gint)event->y);
4123 case GDK_SCROLL_DOWN:
4124 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
4125 (gint)event->x, (gint)event->y);
4133 switch (event->direction)
4136 pixbuf_renderer_scroll(pr, 0, -h);
4138 case GDK_SCROLL_DOWN:
4139 pixbuf_renderer_scroll(pr, 0, h);
4141 case GDK_SCROLL_LEFT:
4142 pixbuf_renderer_scroll(pr, -w, 0);
4144 case GDK_SCROLL_RIGHT:
4145 pixbuf_renderer_scroll(pr, w, 0);
4153 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
4155 g_signal_connect(G_OBJECT(imd->pr), "clicked",
4156 G_CALLBACK(button_cb), pw);
4157 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
4158 G_CALLBACK(scroll_cb), pw);
4161 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
4163 PanWindow *pw = data;
4166 pw->imd = pw->imd_normal;
4169 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
4171 if (force_off && !pw->fs) return;
4175 fullscreen_stop(pw->fs);
4179 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
4180 pan_image_set_buttons(pw, pw->fs->imd);
4181 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
4182 G_CALLBACK(pan_window_key_press_cb), pw);
4184 pw->imd = pw->fs->imd;
4188 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
4190 PanWindow *pw = data;
4193 text = image_zoom_get_as_text(pw->imd);
4194 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4198 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
4200 PanWindow *pw = data;
4205 if (pr->scale == 0.0) return;
4207 pixbuf_renderer_get_visible_rect(pr, &rect);
4208 pixbuf_renderer_get_image_size(pr, &width, &height);
4210 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4211 adj->page_size = (gdouble)rect.width;
4212 adj->page_increment = adj->page_size / 2.0;
4213 adj->step_increment = 48.0 / pr->scale;
4215 adj->upper = MAX((gdouble)width, 1.0);
4216 adj->value = (gdouble)rect.x;
4218 pref_signal_block_data(pw->scrollbar_h, pw);
4219 gtk_adjustment_changed(adj);
4220 gtk_adjustment_value_changed(adj);
4221 pref_signal_unblock_data(pw->scrollbar_h, pw);
4223 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4224 adj->page_size = (gdouble)rect.height;
4225 adj->page_increment = adj->page_size / 2.0;
4226 adj->step_increment = 48.0 / pr->scale;
4228 adj->upper = MAX((gdouble)height, 1.0);
4229 adj->value = (gdouble)rect.y;
4231 pref_signal_block_data(pw->scrollbar_v, pw);
4232 gtk_adjustment_changed(adj);
4233 gtk_adjustment_value_changed(adj);
4234 pref_signal_unblock_data(pw->scrollbar_v, pw);
4237 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4239 PanWindow *pw = data;
4243 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4245 if (!pr->scale) return;
4247 x = (gint)gtk_range_get_value(range);
4249 pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
4252 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4254 PanWindow *pw = data;
4258 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4260 if (!pr->scale) return;
4262 y = (gint)gtk_range_get_value(range);
4264 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
4267 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4269 PanWindow *pw = data;
4271 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4272 pan_window_layout_update_idle(pw);
4275 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4277 PanWindow *pw = data;
4279 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4280 pan_window_layout_update_idle(pw);
4283 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4285 PanWindow *pw = data;
4288 path = remove_trailing_slash(new_text);
4289 parse_out_relatives(path);
4293 warning_dialog(_("Folder not found"),
4294 _("The entered path is not a folder"),
4295 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4299 tab_completion_append_to_history(pw->path_entry, path);
4302 pw->path = g_strdup(path);
4304 pan_window_layout_update_idle(pw);
4307 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4309 PanWindow *pw = data;
4312 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4314 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4315 pan_window_entry_activate_cb(text, pw);
4319 static void pan_window_close(PanWindow *pw)
4321 pan_window_list = g_list_remove(pan_window_list, pw);
4323 if (pw->idle_id != -1)
4325 g_source_remove(pw->idle_id);
4328 pan_fullscreen_toggle(pw, TRUE);
4329 gtk_widget_destroy(pw->window);
4331 pan_window_items_free(pw);
4338 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4340 PanWindow *pw = data;
4342 pan_window_close(pw);
4346 static void pan_window_new_real(const gchar *path)
4355 GdkGeometry geometry;
4357 pw = g_new0(PanWindow, 1);
4359 pw->path = g_strdup(path);
4360 pw->layout = LAYOUT_TIMELINE;
4361 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4362 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4363 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4367 pw->overlay_id = -1;
4370 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4372 geometry.min_width = 8;
4373 geometry.min_height = 8;
4374 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4376 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4377 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
4378 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4379 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4381 window_set_icon(pw->window, NULL, NULL);
4383 vbox = gtk_vbox_new(FALSE, 0);
4384 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4385 gtk_widget_show(vbox);
4387 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4389 pref_spacer(box, 0);
4390 pref_label_new(box, _("Location:"));
4391 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
4392 pan_window_entry_activate_cb, pw);
4393 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4394 G_CALLBACK(pan_window_entry_change_cb), pw);
4395 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4396 gtk_widget_show(combo);
4398 combo = gtk_combo_box_new_text();
4399 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4400 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4401 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4402 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4403 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4405 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4406 g_signal_connect(G_OBJECT(combo), "changed",
4407 G_CALLBACK(pan_window_layout_change_cb), pw);
4408 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4409 gtk_widget_show(combo);
4411 combo = gtk_combo_box_new_text();
4412 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4413 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4414 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4415 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4416 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4417 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4418 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4419 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4420 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4421 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4423 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4424 g_signal_connect(G_OBJECT(combo), "changed",
4425 G_CALLBACK(pan_window_layout_size_cb), pw);
4426 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4427 gtk_widget_show(combo);
4429 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4430 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4431 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4433 pw->imd = image_new(TRUE);
4434 pw->imd_normal = pw->imd;
4436 g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
4437 G_CALLBACK(pan_window_image_zoom_cb), pw);
4438 g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
4439 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4441 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4442 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4443 gtk_widget_show(GTK_WIDGET(pw->imd->widget));
4445 pan_window_dnd_init(pw);
4447 pan_image_set_buttons(pw, pw->imd);
4449 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4450 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4451 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4452 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4453 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4454 gtk_widget_show(pw->scrollbar_h);
4456 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4457 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4458 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4459 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4460 0, GTK_FILL | GTK_EXPAND, 0, 0);
4461 gtk_widget_show(pw->scrollbar_v);
4465 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4466 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4468 pref_spacer(pw->search_box, 0);
4469 pref_label_new(pw->search_box, _("Find:"));
4471 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4472 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4473 gtk_widget_show(hbox);
4475 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4476 pan_search_activate_cb, pw);
4477 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4478 gtk_widget_show(combo);
4480 pw->search_label = gtk_label_new("");
4481 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4482 gtk_widget_show(pw->search_label);
4486 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4488 frame = gtk_frame_new(NULL);
4489 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4490 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4491 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4492 gtk_widget_show(frame);
4494 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4495 gtk_container_add(GTK_CONTAINER(frame), hbox);
4496 gtk_widget_show(hbox);
4498 pref_spacer(hbox, 0);
4499 pw->label_message = pref_label_new(hbox, "");
4501 frame = gtk_frame_new(NULL);
4502 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4503 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4504 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4505 gtk_widget_show(frame);
4507 pw->label_zoom = gtk_label_new("");
4508 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4509 gtk_widget_show(pw->label_zoom);
4511 pw->search_button = gtk_toggle_button_new();
4512 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4513 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4514 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4515 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4516 gtk_widget_show(hbox);
4517 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4518 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4519 gtk_widget_show(pw->search_button_arrow);
4520 pref_label_new(hbox, _("Find"));
4522 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4523 gtk_widget_show(pw->search_button);
4524 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4525 G_CALLBACK(pan_search_toggle_cb), pw);
4527 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4528 G_CALLBACK(pan_window_delete_cb), pw);
4529 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4530 G_CALLBACK(pan_window_key_press_cb), pw);
4532 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4534 pan_window_layout_update_idle(pw);
4536 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4537 gtk_widget_show(pw->window);
4539 pan_window_list = g_list_append(pan_window_list, pw);
4543 *-----------------------------------------------------------------------------
4544 * peformance warnings
4545 *-----------------------------------------------------------------------------
4548 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4552 generic_dialog_close(gd);
4554 pan_window_new_real(path);
4558 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4562 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4563 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4566 static gint pan_warning(const gchar *path)
4572 GtkWidget *ct_button;
4575 if (enable_thumb_caching &&
4576 thumbnail_spec_standard) return FALSE;
4578 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4579 if (hide_dlg) return FALSE;
4581 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4583 gd->data = g_strdup(path);
4584 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4585 pan_warning_ok_cb, TRUE);
4587 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4588 _("Pan view performance may be poor."),
4589 _("To improve performance of thumbnails in the pan view the"
4590 " following options can be enabled. Note that both options"
4591 " must be enabled to notice a change in performance."));
4593 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4594 pref_spacer(group, PREF_PAD_INDENT);
4595 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4597 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4598 enable_thumb_caching, &enable_thumb_caching);
4599 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4600 thumbnail_spec_standard, &thumbnail_spec_standard);
4601 pref_checkbox_link_sensitivity(ct_button, button);
4605 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4606 G_CALLBACK(pan_warning_hide_cb), NULL);
4608 gtk_widget_show(gd->dialog);
4615 *-----------------------------------------------------------------------------
4617 *-----------------------------------------------------------------------------
4620 void pan_window_new(const gchar *path)
4622 if (pan_warning(path)) return;
4624 pan_window_new_real(path);
4628 *-----------------------------------------------------------------------------
4630 *-----------------------------------------------------------------------------
4633 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4635 PanWindow *pw = data;
4638 path = pan_menu_click_path(pw);
4641 pan_fullscreen_toggle(pw, TRUE);
4642 view_window_new(path);
4646 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4652 pw = submenu_item_get_data(widget);
4653 n = GPOINTER_TO_INT(data);
4656 path = pan_menu_click_path(pw);
4659 pan_fullscreen_toggle(pw, TRUE);
4660 start_editor_from_file(n, path);
4664 static void pan_info_cb(GtkWidget *widget, gpointer data)
4666 PanWindow *pw = data;
4669 path = pan_menu_click_path(pw);
4670 if (path) info_window_new(path, NULL);
4673 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4675 PanWindow *pw = data;
4677 image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
4680 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4682 PanWindow *pw = data;
4684 image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
4687 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4689 PanWindow *pw = data;
4691 image_zoom_set(pw->imd, 1.0);
4694 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4696 PanWindow *pw = data;
4699 path = pan_menu_click_path(pw);
4700 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4703 static void pan_move_cb(GtkWidget *widget, gpointer data)
4705 PanWindow *pw = data;
4708 path = pan_menu_click_path(pw);
4709 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4712 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4714 PanWindow *pw = data;
4717 path = pan_menu_click_path(pw);
4718 if (path) file_util_rename(path, NULL, pw->imd->widget);
4721 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4723 PanWindow *pw = data;
4726 path = pan_menu_click_path(pw);
4727 if (path) file_util_delete(path, NULL, pw->imd->widget);
4730 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4732 PanWindow *pw = data;
4734 pan_fullscreen_toggle(pw, FALSE);
4737 static void pan_close_cb(GtkWidget *widget, gpointer data)
4739 PanWindow *pw = data;
4741 pan_window_close(pw);
4744 static GtkWidget *pan_popup_menu(PanWindow *pw)
4750 active = (pw->click_pi != NULL);
4752 menu = popup_menu_short_lived();
4754 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4755 G_CALLBACK(pan_zoom_in_cb), pw);
4756 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4757 G_CALLBACK(pan_zoom_out_cb), pw);
4758 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4759 G_CALLBACK(pan_zoom_1_1_cb), pw);
4760 menu_item_add_divider(menu);
4762 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4763 gtk_widget_set_sensitive(item, active);
4765 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4766 G_CALLBACK(pan_info_cb), pw);
4768 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4769 G_CALLBACK(pan_new_window_cb), pw);
4771 menu_item_add_divider(menu);
4772 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4773 G_CALLBACK(pan_copy_cb), pw);
4774 menu_item_add_sensitive(menu, _("_Move..."), active,
4775 G_CALLBACK(pan_move_cb), pw);
4776 menu_item_add_sensitive(menu, _("_Rename..."), active,
4777 G_CALLBACK(pan_rename_cb), pw);
4778 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4779 G_CALLBACK(pan_delete_cb), pw);
4781 menu_item_add_divider(menu);
4785 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4789 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4792 menu_item_add_divider(menu);
4793 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4799 *-----------------------------------------------------------------------------
4801 *-----------------------------------------------------------------------------
4804 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4806 GtkSelectionData *selection_data, guint info,
4807 guint time, gpointer data)
4809 PanWindow *pw = data;
4811 if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
4813 if (info == TARGET_URI_LIST)
4817 list = uri_list_from_text(selection_data->data, TRUE);
4818 if (list && isdir((gchar *)list->data))
4820 gchar *path = list->data;
4823 pw->path = g_strdup(path);
4825 pan_window_layout_update_idle(pw);
4828 path_list_free(list);
4832 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4833 GtkSelectionData *selection_data, guint info,
4834 guint time, gpointer data)
4836 PanWindow *pw = data;
4839 path = pan_menu_click_path(pw);
4849 case TARGET_URI_LIST:
4852 case TARGET_TEXT_PLAIN:
4857 list = g_list_append(NULL, (gchar *)path);
4858 text = uri_text_from_list(list, &len, plain_text);
4862 gtk_selection_data_set (selection_data, selection_data->target,
4869 gtk_selection_data_set (selection_data, selection_data->target,
4874 static void pan_window_dnd_init(PanWindow *pw)
4878 widget = pw->imd->pr;
4880 gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
4881 dnd_file_drag_types, dnd_file_drag_types_count,
4882 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4883 g_signal_connect(G_OBJECT(widget), "drag_data_get",
4884 G_CALLBACK(pan_window_set_dnd_data), pw);
4886 gtk_drag_dest_set(widget,
4887 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4888 dnd_file_drop_types, dnd_file_drop_types_count,
4889 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4890 g_signal_connect(G_OBJECT(widget), "drag_data_received",
4891 G_CALLBACK(pan_window_get_dnd_data), pw);
4895 *-----------------------------------------------------------------------------
4896 * maintenance (for rename, move, remove)
4897 *-----------------------------------------------------------------------------