7 * This software is released under the GNU General Public License (GNU GPL).
8 * Please read the included file COPYING for more information.
9 * This software comes with no warranty of any kind, use at your own risk!
20 #include "fullscreen.h"
22 #include "image-load.h"
26 #include "pixbuf_util.h"
29 #include "ui_bookmark.h"
30 #include "ui_fileops.h"
33 #include "ui_tabcomp.h"
35 #include <gdk/gdkkeysyms.h> /* for keyboard values */
39 #define PAN_WINDOW_DEFAULT_WIDTH 720
40 #define PAN_WINDOW_DEFAULT_HEIGHT 500
42 #define PAN_TILE_SIZE 512
44 #define PAN_THUMB_SIZE_DOTS 4
45 #define PAN_THUMB_SIZE_NONE 24
46 #define PAN_THUMB_SIZE_SMALL 64
47 #define PAN_THUMB_SIZE_NORMAL 128
48 #define PAN_THUMB_SIZE_LARGE 256
49 #define PAN_THUMB_SIZE pw->thumb_size
51 #define PAN_THUMB_GAP_DOTS 2
52 #define PAN_THUMB_GAP_SMALL 14
53 #define PAN_THUMB_GAP_NORMAL 30
54 #define PAN_THUMB_GAP_LARGE 40
55 #define PAN_THUMB_GAP_HUGE 50
56 #define PAN_THUMB_GAP pw->thumb_gap
58 #define PAN_SHADOW_OFFSET 6
59 #define PAN_SHADOW_FADE 5
60 #define PAN_SHADOW_COLOR 0, 0, 0
61 #define PAN_SHADOW_ALPHA 64
63 #define PAN_OUTLINE_THICKNESS 1
64 #define PAN_OUTLINE_COLOR_1 255, 255, 255
65 #define PAN_OUTLINE_COLOR_2 64, 64, 64
66 #define PAN_OUTLINE_ALPHA 180
68 #define PAN_BACKGROUND_COLOR 255, 255, 230
70 #define PAN_GRID_SIZE 10
71 #define PAN_GRID_COLOR 0, 0, 0
72 #define PAN_GRID_ALPHA 20
74 #define PAN_FOLDER_BOX_COLOR 0, 0, 255
75 #define PAN_FOLDER_BOX_ALPHA 10
76 #define PAN_FOLDER_BOX_BORDER 20
78 #define PAN_FOLDER_BOX_OUTLINE_THICKNESS 4
79 #define PAN_FOLDER_BOX_OUTLINE_COLOR 0, 0, 255
80 #define PAN_FOLDER_BOX_OUTLINE_ALPHA 64
82 #define PAN_TEXT_BORDER_SIZE 4
83 #define PAN_TEXT_COLOR 0, 0, 0
85 #define PAN_POPUP_COLOR 255, 255, 220
86 #define PAN_POPUP_ALPHA 255
87 #define PAN_POPUP_BORDER 1
88 #define PAN_POPUP_BORDER_COLOR 0, 0, 0
89 #define PAN_POPUP_TEXT_COLOR 0, 0, 0
91 #define PAN_GROUP_MAX 16
93 #define ZOOM_INCREMENT 1.0
94 #define ZOOM_LABEL_WIDTH 64
97 #define PAN_PREF_GROUP "pan_view_options"
98 #define PAN_PREF_HIDE_WARNING "hide_performance_warning"
104 LAYOUT_FOLDERS_LINEAR,
105 LAYOUT_FOLDERS_FLOWER,
110 LAYOUT_SIZE_THUMB_DOTS = 0,
111 LAYOUT_SIZE_THUMB_NONE,
112 LAYOUT_SIZE_THUMB_SMALL,
113 LAYOUT_SIZE_THUMB_NORMAL,
114 LAYOUT_SIZE_THUMB_LARGE,
133 TEXT_ATTR_BOLD = 1 << 0,
134 TEXT_ATTR_HEADING = 1 << 1,
135 TEXT_ATTR_MARKUP = 1 << 2
146 typedef struct _PanItem PanItem;
161 TextAttrType text_attr;
179 typedef struct _PanWindow PanWindow;
184 ImageWindow *imd_normal;
187 GtkWidget *path_entry;
189 GtkWidget *label_message;
190 GtkWidget *label_zoom;
192 GtkWidget *search_box;
193 GtkWidget *search_entry;
194 GtkWidget *search_label;
195 GtkWidget *search_button;
196 GtkWidget *search_button_arrow;
198 GtkWidget *scrollbar_h;
199 GtkWidget *scrollbar_v;
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);
245 static void pan_overlay_toggle(PanWindow *pw);
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 void pixbuf_draw_line(GdkPixbuf *pb,
488 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
489 gint x1, gint y1, gint x2, gint y2,
490 guint8 r, guint8 g, guint8 b, guint8 a)
495 gint fx1, fy1, fx2, fy2;
501 gdouble xstep, ystep;
508 pw = gdk_pixbuf_get_width(pb);
509 ph = gdk_pixbuf_get_height(pb);
511 if (!util_clip_region(0, 0, pw, ph,
512 clip_x, clip_y, clip_w, clip_h,
513 &rx, &ry, &rw, &rh)) return;
525 if (xa == 0 && ya == 0) return;
527 nt = sqrt(xd * xd + yd * yd);
529 nt = (xa > ya) ? xa : ya;
530 xstep = (double)xd / nt;
531 ystep = (double)yd / nt;
533 p_alpha = gdk_pixbuf_get_has_alpha(pb);
534 prs = gdk_pixbuf_get_rowstride(pb);
535 p_pix = gdk_pixbuf_get_pixels(pb);
537 p_step = (p_alpha) ? 4 : 3;
541 for (n = 0; n < nt; n++)
546 if (x >= fx1 && x < fx2 &&
549 pp = p_pix + y * prs + x * p_step;
550 *pp = (r * a + *pp * (256-a)) >> 8;
552 *pp = (g * a + *pp * (256-a)) >> 8;
554 *pp = (b * a + *pp * (256-a)) >> 8;
561 static void pixbuf_draw_fade_linear(guchar *p_pix, gint prs, gint p_alpha,
562 gint s, gint vertical, gint border,
563 gint x1, gint y1, gint x2, gint y2,
564 guint8 r, guint8 g, guint8 b, guint8 a)
571 p_step = (p_alpha) ? 4 : 3;
572 for (j = y1; j < y2; j++)
574 pp = p_pix + j * prs + x1 * p_step;
575 if (!vertical) n = a - a * abs(j - s) / border;
576 for (i = x1; i < x2; i++)
578 if (vertical) n = a - a * abs(i - s) / border;
579 *pp = (r * n + *pp * (256-n)) >> 8;
581 *pp = (g * n + *pp * (256-n)) >> 8;
583 *pp = (b * n + *pp * (256-n)) >> 8;
590 static void pixbuf_draw_fade_radius(guchar *p_pix, gint prs, gint p_alpha,
591 gint sx, gint sy, gint border,
592 gint x1, gint y1, gint x2, gint y2,
593 guint8 r, guint8 g, guint8 b, guint8 a)
599 p_step = (p_alpha) ? 4 : 3;
600 for (j = y1; j < y2; j++)
602 pp = p_pix + j * prs + x1 * p_step;
603 for (i = x1; i < x2; i++)
608 r = MIN(border, (gint)sqrt((i-sx)*(i-sx) + (j-sy)*(j-sy)));
609 n = a - a * r / border;
610 *pp = (r * n + *pp * (256-n)) >> 8;
612 *pp = (g * n + *pp * (256-n)) >> 8;
614 *pp = (b * n + *pp * (256-n)) >> 8;
621 static void pixbuf_draw_shadow(GdkPixbuf *pb,
622 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
623 gint x, gint y, gint w, gint h, gint border,
624 guint8 r, guint8 g, guint8 b, guint8 a)
634 pw = gdk_pixbuf_get_width(pb);
635 ph = gdk_pixbuf_get_height(pb);
637 if (!util_clip_region(0, 0, pw, ph,
638 clip_x, clip_y, clip_w, clip_h,
639 &rx, &ry, &rw, &rh)) return;
641 p_alpha = gdk_pixbuf_get_has_alpha(pb);
642 prs = gdk_pixbuf_get_rowstride(pb);
643 p_pix = gdk_pixbuf_get_pixels(pb);
645 if (util_clip_region(x + border, y + border, w - border * 2, h - border * 2,
649 pixbuf_draw_rect_fill(pb, fx, fy, fw, fh, r, g, b, a);
652 if (border < 1) return;
654 if (util_clip_region(x, y + border, border, h - border * 2,
658 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
659 x + border, TRUE, border,
660 fx, fy, fx + fw, fy + fh,
663 if (util_clip_region(x + w - border, y + border, border, h - border * 2,
667 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
668 x + w - border, TRUE, border,
669 fx, fy, fx + fw, fy + fh,
672 if (util_clip_region(x + border, y, w - border * 2, border,
676 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
677 y + border, FALSE, border,
678 fx, fy, fx + fw, fy + fh,
681 if (util_clip_region(x + border, y + h - border, w - border * 2, border,
685 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
686 y + h - border, FALSE, border,
687 fx, fy, fx + fw, fy + fh,
690 if (util_clip_region(x, y, border, border,
694 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
695 x + border, y + border, border,
696 fx, fy, fx + fw, fy + fh,
699 if (util_clip_region(x + w - border, y, border, border,
703 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
704 x + w - border, y + border, border,
705 fx, fy, fx + fw, fy + fh,
708 if (util_clip_region(x, y + h - border, border, border,
712 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
713 x + border, y + h - border, border,
714 fx, fy, fx + fw, fy + fh,
717 if (util_clip_region(x + w - border, y + h - border, border, border,
721 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
722 x + w - border, y + h - border, border,
723 fx, fy, fx + fw, fy + fh,
730 *-----------------------------------------------------------------------------
732 *-----------------------------------------------------------------------------
735 static void pan_cache_free(PanWindow *pw)
739 work = pw->cache_list;
747 cache_sim_data_free(pc->cd);
748 file_data_free((FileData *)pc);
751 g_list_free(pw->cache_list);
752 pw->cache_list = NULL;
754 filelist_free(pw->cache_todo);
755 pw->cache_todo = NULL;
762 static void pan_cache_fill(PanWindow *pw, const gchar *path)
768 list = pan_window_layout_list(path, SORT_NAME, TRUE);
769 pw->cache_todo = g_list_reverse(list);
771 pw->cache_total = g_list_length(pw->cache_todo);
774 static gint pan_cache_step(PanWindow *pw)
778 CacheData *cd = NULL;
780 if (!pw->cache_todo) return FALSE;
782 fd = pw->cache_todo->data;
783 pw->cache_todo = g_list_remove(pw->cache_todo, fd);
785 if (enable_thumb_caching)
789 found = cache_find_location(CACHE_TYPE_SIM, fd->path);
790 if (found && filetime(found) == fd->date)
792 cd = cache_sim_data_load(found);
797 if (!cd) cd = cache_sim_data_new();
801 cd->dimensions = image_load_dimensions(fd->path, &cd->width, &cd->height);
802 if (enable_thumb_caching &&
808 base = cache_get_location(CACHE_TYPE_SIM, fd->path, FALSE, &mode);
809 if (cache_ensure_dir_exists(base, mode))
812 cd->path = cache_get_location(CACHE_TYPE_SIM, fd->path, TRUE, NULL);
813 if (cache_sim_data_save(cd))
815 filetime_set(cd->path, filetime(fd->path));
824 pc = g_new0(PanCacheData, 1);
825 memcpy(pc, fd, sizeof(FileData));
830 pw->cache_list = g_list_prepend(pw->cache_list, pc);
837 *-----------------------------------------------------------------------------
839 *-----------------------------------------------------------------------------
842 static void pan_item_free(PanItem *pi)
846 if (pi->pixbuf) g_object_unref(pi->pixbuf);
847 if (pi->fd) file_data_free(pi->fd);
855 static void pan_window_items_free(PanWindow *pw)
862 PanItem *pi = work->data;
868 g_list_free(pw->list);
871 g_list_free(pw->queue);
875 image_loader_free(pw->il);
878 thumb_loader_free(pw->tl);
882 pw->search_pi = NULL;
885 static PanItem *pan_item_new_thumb(PanWindow *pw, FileData *fd, gint x, gint y)
889 pi = g_new0(PanItem, 1);
890 pi->type = ITEM_THUMB;
894 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
895 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
901 pw->list = g_list_prepend(pw->list, pi);
906 static PanItem *pan_item_new_box(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
908 guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
909 guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
913 pi = g_new0(PanItem, 1);
921 pi->color_r = base_r;
922 pi->color_g = base_g;
923 pi->color_b = base_b;
924 pi->color_a = base_a;
926 pi->color2_r = bord_r;
927 pi->color2_g = bord_g;
928 pi->color2_b = bord_b;
929 pi->color2_a = bord_a;
930 pi->border = border_size;
932 pw->list = g_list_prepend(pw->list, pi);
937 static void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
941 if (!pi || pi->type != ITEM_BOX) return;
946 pi->width -= shadow[0];
947 pi->height -= shadow[0];
950 shadow = g_new0(gint, 2);
955 pi->height += offset;
961 static PanItem *pan_item_new_tri(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
962 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
963 guint8 r, guint8 g, guint8 b, guint8 a)
968 pi = g_new0(PanItem, 1);
969 pi->type = ITEM_TRIANGLE;
980 coord = g_new0(gint, 6);
990 pi->border = BORDER_NONE;
992 pw->list = g_list_prepend(pw->list, pi);
997 static void pan_item_tri_border(PanItem *pi, gint borders,
998 guint8 r, guint8 g, guint8 b, guint8 a)
1000 if (!pi || pi->type != ITEM_TRIANGLE) return;
1002 pi->border = borders;
1010 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
1012 PangoLayout *layout;
1014 layout = gtk_widget_create_pango_layout(widget, NULL);
1016 if (pi->text_attr & TEXT_ATTR_MARKUP)
1018 pango_layout_set_markup(layout, pi->text, -1);
1022 if (pi->text_attr & TEXT_ATTR_BOLD ||
1023 pi->text_attr & TEXT_ATTR_HEADING)
1028 pal = pango_attr_list_new();
1029 if (pi->text_attr & TEXT_ATTR_BOLD)
1031 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
1032 pa->start_index = 0;
1033 pa->end_index = G_MAXINT;
1034 pango_attr_list_insert(pal, pa);
1036 if (pi->text_attr & TEXT_ATTR_HEADING)
1038 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
1039 pa->start_index = 0;
1040 pa->end_index = G_MAXINT;
1041 pango_attr_list_insert(pal, pa);
1043 pango_layout_set_attributes(layout, pal);
1044 pango_attr_list_unref(pal);
1047 pango_layout_set_text(layout, pi->text, -1);
1051 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
1053 PangoLayout *layout;
1055 if (!pi || !pi->text || !widget) return;
1057 layout = pan_item_text_layout(pi, widget);
1058 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
1059 g_object_unref(G_OBJECT(layout));
1061 pi->width += PAN_TEXT_BORDER_SIZE * 2;
1062 pi->height += PAN_TEXT_BORDER_SIZE * 2;
1065 static PanItem *pan_item_new_text(PanWindow *pw, gint x, gint y, const gchar *text, TextAttrType attr,
1066 guint8 r, guint8 g, guint8 b, guint8 a)
1070 pi = g_new0(PanItem, 1);
1071 pi->type = ITEM_TEXT;
1074 pi->text = g_strdup(text);
1075 pi->text_attr = attr;
1082 pan_item_text_compute_size(pi, pw->imd->widget);
1084 pw->list = g_list_prepend(pw->list, pi);
1089 static void pan_item_set_key(PanItem *pi, const gchar *key)
1096 pi->key = g_strdup(key);
1100 static void pan_item_added(PanWindow *pw, PanItem *pi)
1103 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1106 static void pan_item_remove(PanWindow *pw, PanItem *pi)
1110 if (pw->click_pi == pi) pw->click_pi = NULL;
1111 if (pw->queue_pi == pi) pw->queue_pi = NULL;
1112 if (pw->search_pi == pi) pw->search_pi = NULL;
1113 pw->queue = g_list_remove(pw->queue, pi);
1115 pw->list = g_list_remove(pw->list, pi);
1116 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1120 static void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
1122 if (!pi || !child) return;
1124 if (pi->x + pi->width < child->x + child->width + border)
1125 pi->width = child->x + child->width + border - pi->x;
1127 if (pi->y + pi->height < child->y + child->height + border)
1128 pi->height = child->y + child->height + border - pi->y;
1131 static void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
1135 if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
1136 if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
1139 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
1146 if (!pi->fd) return;
1148 work = pw->cache_list;
1157 path = ((FileData *)pc)->path;
1159 if (pc->cd && pc->cd->dimensions &&
1160 path && strcmp(path, pi->fd->path) == 0)
1162 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
1163 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
1165 pw->cache_list = g_list_remove(pw->cache_list, pc);
1166 cache_sim_data_free(pc->cd);
1167 file_data_free((FileData *)pc);
1173 static PanItem *pan_item_new_image(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
1177 pi = g_new0(PanItem, 1);
1178 pi->type = ITEM_IMAGE;
1183 pan_item_image_find_size(pw, pi, w, h);
1185 pw->list = g_list_prepend(pw->list, pi);
1190 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
1194 if (!key) return NULL;
1196 work = g_list_last(pw->list);
1202 if ((pi->type == type || type == ITEM_NONE) &&
1203 pi->key && strcmp(pi->key, key) == 0)
1213 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1214 static GList *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1215 gint ignore_case, gint partial)
1220 if (!path) return NULL;
1221 if (partial && path[0] == '/') return NULL;
1223 work = g_list_last(pw->list);
1229 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1235 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
1237 else if (pi->fd->name)
1245 haystack = g_utf8_strdown(pi->fd->name, -1);
1246 match = (strstr(haystack, path) != NULL);
1251 if (strstr(pi->fd->name, path)) match = TRUE;
1254 else if (ignore_case)
1256 if (strcasecmp(path, pi->fd->name) == 0) match = TRUE;
1260 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
1264 if (match) list = g_list_prepend(list, pi);
1269 return g_list_reverse(list);
1272 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y, const gchar *key)
1276 if (x < 0 || x >= pw->imd->image_width ||
1277 y < 0 || y >= pw->imd->image_height) return NULL;
1285 if ((pi->type == type || type == ITEM_NONE) &&
1286 x >= pi->x && x < pi->x + pi->width &&
1287 y >= pi->y && y < pi->y + pi->height &&
1288 (!key || (pi->key && strcmp(pi->key, key) == 0)))
1299 *-----------------------------------------------------------------------------
1301 *-----------------------------------------------------------------------------
1304 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend)
1306 GList *flist = NULL;
1307 GList *dlist = NULL;
1311 filelist_read(path, &flist, &dlist);
1312 if (sort != SORT_NONE)
1314 flist = filelist_sort(flist, sort, ascend);
1315 dlist = filelist_sort(dlist, sort, ascend);
1325 folders = g_list_remove(folders, fd);
1327 if (filelist_read(fd->path, &flist, &dlist))
1329 if (sort != SORT_NONE)
1331 flist = filelist_sort(flist, sort, ascend);
1332 dlist = filelist_sort(dlist, sort, ascend);
1335 result = g_list_concat(result, flist);
1336 folders = g_list_concat(dlist, folders);
1345 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1353 list = pan_window_layout_list(path, SORT_NAME, TRUE);
1355 grid_size = (gint)sqrt((double)g_list_length(list));
1356 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1358 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1362 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1367 *width = PAN_FOLDER_BOX_BORDER * 2;
1368 *height = PAN_FOLDER_BOX_BORDER * 2;
1381 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1383 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1385 x += pi->width + PAN_THUMB_GAP;
1386 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1395 pi = pan_item_new_thumb(pw, fd, x, y);
1397 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1401 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1404 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1410 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1413 gint x1, y1, x2, y2;
1428 if (x1 > pi->x) x1 = pi->x;
1429 if (y1 > pi->y) y1 = pi->y;
1430 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1431 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1434 x1 -= PAN_FOLDER_BOX_BORDER;
1435 y1 -= PAN_FOLDER_BOX_BORDER;
1436 x2 += PAN_FOLDER_BOX_BORDER;
1437 y2 += PAN_FOLDER_BOX_BORDER;
1450 if (pi->type == ITEM_TRIANGLE && pi->data)
1464 if (width) *width = x2 - x1;
1465 if (height) *height = y2 - y1;
1468 typedef struct _FlowerGroup FlowerGroup;
1469 struct _FlowerGroup {
1482 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1486 work = group->items;
1504 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1505 gint *result_x, gint *result_y)
1511 radius = parent->circumference / (2*PI);
1512 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1514 a = 2*PI * group->diameter / parent->circumference;
1516 x = (gint)((double)radius * cos(parent->angle + a / 2));
1517 y = (gint)((double)radius * sin(parent->angle + a / 2));
1524 x += parent->width / 2;
1525 y += parent->height / 2;
1527 x -= group->width / 2;
1528 y -= group->height / 2;
1534 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1541 if (parent && parent->children)
1543 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1551 pan_window_layout_compute_folder_flower_move(group, x, y);
1556 gint px, py, gx, gy;
1557 gint x1, y1, x2, y2;
1559 px = parent->x + parent->width / 2;
1560 py = parent->y + parent->height / 2;
1562 gx = group->x + group->width / 2;
1563 gy = group->y + group->height / 2;
1568 x2 = MAX(px, gx + 5);
1569 y2 = MAX(py, gy + 5);
1571 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1572 px, py, gx, gy, gx + 5, gy + 5,
1574 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1578 pw->list = g_list_concat(group->items, pw->list);
1579 group->items = NULL;
1581 group->circumference = 0;
1582 work = group->children;
1590 group->circumference += child->diameter;
1593 work = g_list_last(group->children);
1601 pan_window_layout_compute_folder_flower_build(pw, child, group);
1604 g_list_free(group->children);
1608 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1621 if (!filelist_read(path, &f, &d)) return NULL;
1622 if (!f && !d) return NULL;
1624 f = filelist_sort(f, SORT_NAME, TRUE);
1625 d = filelist_sort(d, SORT_NAME, TRUE);
1627 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1628 PAN_TEXT_COLOR, 255);
1630 y += pi_box->height;
1632 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1634 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1635 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1636 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1637 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1639 x += PAN_FOLDER_BOX_BORDER;
1640 y += PAN_FOLDER_BOX_BORDER;
1642 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1656 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1658 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1659 x += pi->width + PAN_THUMB_GAP;
1660 if (pi->height > y_height) y_height = pi->height;
1664 pi = pan_item_new_thumb(pw, fd, x, y);
1665 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1666 y_height = PAN_THUMB_SIZE;
1670 if (grid_count >= grid_size)
1674 y += y_height + PAN_THUMB_GAP;
1678 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1683 group = g_new0(FlowerGroup, 1);
1684 group->items = pw->list;
1687 group->width = pi_box->width;
1688 group->height = pi_box->y + pi_box->height;
1689 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1691 group->children = NULL;
1702 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1703 if (child) group->children = g_list_prepend(group->children, child);
1711 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1712 gint *width, gint *height,
1713 gint *scroll_x, gint *scroll_y)
1718 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1719 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1721 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1723 list = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1726 PanItem *pi = list->data;
1727 *scroll_x = pi->x + pi->width / 2;
1728 *scroll_y = pi->y + pi->height / 2;
1733 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1734 gint *x, gint *y, gint *level,
1736 gint *width, gint *height)
1744 if (!filelist_read(path, &f, &d)) return;
1745 if (!f && !d) return;
1747 f = filelist_sort(f, SORT_NAME, TRUE);
1748 d = filelist_sort(d, SORT_NAME, TRUE);
1750 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1752 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1753 PAN_TEXT_COLOR, 255);
1755 *y += pi_box->height;
1757 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1759 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1760 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1761 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1762 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1764 *x += PAN_FOLDER_BOX_BORDER;
1765 *y += PAN_FOLDER_BOX_BORDER;
1776 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1778 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1779 *x += pi->width + PAN_THUMB_GAP;
1780 if (pi->height > y_height) y_height = pi->height;
1784 pi = pan_item_new_thumb(pw, fd, *x, *y);
1785 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1786 y_height = PAN_THUMB_SIZE;
1789 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1792 if (f) *y = pi_box->y + pi_box->height;
1804 *level = *level + 1;
1805 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1806 pi_box, width, height);
1807 *level = *level - 1;
1812 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1814 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1815 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1817 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1820 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1827 x = PAN_FOLDER_BOX_BORDER;
1828 y = PAN_FOLDER_BOX_BORDER;
1829 w = PAN_FOLDER_BOX_BORDER * 2;
1830 h = PAN_FOLDER_BOX_BORDER * 2;
1832 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1834 if (width) *width = w;
1835 if (height) *height = h;
1839 *-----------------------------------------------------------------------------
1841 *-----------------------------------------------------------------------------
1844 #define PAN_CAL_DAY_WIDTH 100
1845 #define PAN_CAL_DAY_HEIGHT 80
1846 #define PAN_CAL_DOT_SIZE 3
1847 #define PAN_CAL_DOT_GAP 2
1848 #define PAN_CAL_DOT_COLOR 0, 0, 0
1849 #define PAN_CAL_DOT_ALPHA 32
1851 static void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
1857 gint x1, y1, x2, y2, x3, y3;
1862 while ((pi = pan_item_find_by_key(pw, ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
1864 if (!pi_day || pi_day->type != ITEM_BOX ||
1865 !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
1867 list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
1879 if (dot->type != ITEM_BOX || !dot->fd ||
1880 !dot->key || strcmp(dot->key, "dot") != 0)
1882 list = g_list_delete_link(list, node);
1890 grid = (gint)(sqrt(g_list_length(list)) + 0.5);
1892 x = pi_day->x + pi_day->width + 4;
1896 if (y + grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4 > pw->imd->image_height)
1898 y = pw->imd->image_height - (grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4);
1902 pbox = pan_item_new_box(pw, NULL, x, y, PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1904 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
1905 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
1906 pan_item_set_key(pbox, "day_bubble");
1913 buf = date_value_string(pi_day->fd->date, DATE_LENGTH_WEEK);
1914 plabel = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1915 PAN_POPUP_TEXT_COLOR, 255);
1916 pan_item_set_key(plabel, "day_bubble");
1919 pan_item_size_by_item(pbox, plabel, 0);
1921 y += plabel->height;
1928 x += PAN_FOLDER_BOX_BORDER;
1929 y += PAN_FOLDER_BOX_BORDER;
1943 pimg = pan_item_new_thumb(pw, file_data_new_simple(dot->fd->path), x, y);
1944 pan_item_set_key(pimg, "day_bubble");
1946 pan_item_size_by_item(pbox, pimg, PAN_FOLDER_BOX_BORDER);
1951 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1956 x = pbox->x + PAN_FOLDER_BOX_BORDER;
1957 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1963 x1 = pi_day->x + pi_day->width - 8;
1966 y2 = pbox->y + MIN(42, pbox->height);
1968 y3 = MAX(pbox->y, y2 - 30);
1969 triangle_rect_region(x1, y1, x2, y2, x3, y3,
1972 pi = pan_item_new_tri(pw, NULL, x, y, w, h,
1973 x1, y1, x2, y2, x3, y3,
1974 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
1975 pan_item_tri_border(pi, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
1976 pan_item_set_key(pi, "day_bubble");
1977 pan_item_added(pw, pi);
1979 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
1980 pan_item_added(pw, pbox);
1983 static void pan_window_layout_compute_calendar(PanWindow *pw, const gchar *path, gint *width, gint *height)
1999 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2001 list = pan_window_layout_list(path, SORT_NONE, TRUE);
2002 list = filelist_sort(list, SORT_TIME, TRUE);
2015 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2023 if (day_max < count) day_max = count;
2027 printf("biggest day contains %d images\n", day_max);
2029 grid = (gint)(sqrt((double)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
2030 day_width = MAX(PAN_CAL_DAY_WIDTH, grid);
2031 day_height = MAX(PAN_CAL_DAY_HEIGHT, grid);
2035 FileData *fd = list->data;
2037 year = date_value(fd->date, DATE_LENGTH_YEAR);
2038 month = date_value(fd->date, DATE_LENGTH_MONTH);
2041 work = g_list_last(list);
2044 FileData *fd = work->data;
2045 end_year = date_value(fd->date, DATE_LENGTH_YEAR);
2046 end_month = date_value(fd->date, DATE_LENGTH_MONTH);
2049 *width = PAN_FOLDER_BOX_BORDER * 2;
2050 *height = PAN_FOLDER_BOX_BORDER * 2;
2052 x = PAN_FOLDER_BOX_BORDER;
2053 y = PAN_FOLDER_BOX_BORDER;
2056 while (work && (year < end_year || (year == end_year && month <= end_month)))
2067 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
2069 days = date_value(dt, DATE_LENGTH_DAY);
2070 dt = date_to_time(year, month, 1);
2071 col = date_value(dt, DATE_LENGTH_WEEK);
2074 x = PAN_FOLDER_BOX_BORDER;
2076 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
2077 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2078 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2079 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2080 buf = date_value_string(dt, DATE_LENGTH_MONTH);
2081 pi_text = pan_item_new_text(pw, x, y, buf,
2082 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2083 PAN_TEXT_COLOR, 255);
2085 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
2087 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
2089 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
2090 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
2092 for (day = 1; day <= days; day++)
2099 dt = date_to_time(year, month, day);
2101 fd = g_new0(FileData, 1);
2102 /* path and name must be non NULL, so make them an invalid filename */
2103 fd->path = g_strdup("//");
2106 pi_day = pan_item_new_box(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
2107 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2108 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2109 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2110 pan_item_set_key(pi_day, "day");
2112 dx = x + PAN_CAL_DOT_GAP * 2;
2113 dy = y + PAN_CAL_DOT_GAP * 2;
2115 fd = (work) ? work->data : NULL;
2116 while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
2120 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
2122 PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
2124 pan_item_set_key(pi, "dot");
2126 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2127 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
2129 dx = x + PAN_CAL_DOT_GAP * 2;
2130 dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2132 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
2134 /* must keep all dots within respective day even if it gets ugly */
2135 dy = y + PAN_CAL_DOT_GAP * 2;
2138 pi_day->color_a = MIN(PAN_FOLDER_BOX_ALPHA + 64 + n, 255);
2142 fd = (work) ? work->data : NULL;
2149 buf = g_strdup_printf("( %d )", n);
2150 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2151 PAN_TEXT_COLOR, 255);
2154 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
2155 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
2158 buf = g_strdup_printf("%d", day);
2159 pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2160 PAN_TEXT_COLOR, 255);
2164 pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
2171 x = PAN_FOLDER_BOX_BORDER;
2172 y += PAN_CAL_DAY_HEIGHT;
2176 x += PAN_CAL_DAY_WIDTH;
2180 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
2181 y += PAN_FOLDER_BOX_BORDER * 2;
2192 *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
2197 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
2205 PanItem *pi_month = NULL;
2206 PanItem *pi_day = NULL;
2212 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2214 list = pan_window_layout_list(path, SORT_NONE, TRUE);
2215 list = filelist_sort(list, SORT_TIME, TRUE);
2217 *width = PAN_FOLDER_BOX_BORDER * 2;
2218 *height = PAN_FOLDER_BOX_BORDER * 2;
2223 day_start = month_start;
2238 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2243 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
2249 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
2253 x = PAN_FOLDER_BOX_BORDER;
2256 y = PAN_FOLDER_BOX_BORDER;
2258 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
2259 pi = pan_item_new_text(pw, x, y, buf,
2260 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2261 PAN_TEXT_COLOR, 255);
2264 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
2266 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2267 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2268 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2270 x += PAN_FOLDER_BOX_BORDER;
2271 y += PAN_FOLDER_BOX_BORDER;
2275 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
2287 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
2289 needle = needle->next;
2298 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
2299 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2300 PAN_TEXT_COLOR, 255);
2305 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
2306 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2307 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2308 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2310 x += PAN_FOLDER_BOX_BORDER;
2311 y += PAN_FOLDER_BOX_BORDER;
2315 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2317 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
2318 if (pi->width > x_width) x_width = pi->width;
2319 y_height = pi->height;
2323 pi = pan_item_new_thumb(pw, fd, x, y);
2324 x_width = PAN_THUMB_SIZE;
2325 y_height = PAN_THUMB_SIZE;
2328 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
2329 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
2334 if (total > 0 && count < PAN_GROUP_MAX)
2336 y += y_height + PAN_THUMB_GAP;
2340 x += x_width + PAN_THUMB_GAP;
2350 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
2356 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
2357 gint *width, gint *height,
2358 gint *scroll_x, gint *scroll_y)
2360 pan_window_items_free(pw);
2364 case LAYOUT_SIZE_THUMB_DOTS:
2365 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2366 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2368 case LAYOUT_SIZE_THUMB_NONE:
2369 pw->thumb_size = PAN_THUMB_SIZE_NONE;
2370 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2372 case LAYOUT_SIZE_THUMB_SMALL:
2373 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2374 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2376 case LAYOUT_SIZE_THUMB_NORMAL:
2378 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2379 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2381 case LAYOUT_SIZE_THUMB_LARGE:
2382 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2383 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2385 case LAYOUT_SIZE_10:
2386 pw->image_size = 10;
2387 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2389 case LAYOUT_SIZE_25:
2390 pw->image_size = 25;
2391 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2393 case LAYOUT_SIZE_33:
2394 pw->image_size = 33;
2395 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2397 case LAYOUT_SIZE_50:
2398 pw->image_size = 50;
2399 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2401 case LAYOUT_SIZE_100:
2402 pw->image_size = 100;
2403 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2416 pan_window_layout_compute_grid(pw, path, width, height);
2418 case LAYOUT_FOLDERS_LINEAR:
2419 pan_window_layout_compute_folders_linear(pw, path, width, height);
2421 case LAYOUT_FOLDERS_FLOWER:
2422 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2424 case LAYOUT_CALENDAR:
2425 pan_window_layout_compute_calendar(pw, path, width, height);
2427 case LAYOUT_TIMELINE:
2428 pan_window_layout_compute_timeline(pw, path, width, height);
2434 printf("computed %d objects\n", g_list_length(pw->list));
2437 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2450 if (util_clip_region_test(x, y, width, height,
2451 pi->x, pi->y, pi->width, pi->height))
2453 list = g_list_prepend(list, pi);
2463 *-----------------------------------------------------------------------------
2465 *-----------------------------------------------------------------------------
2468 static gint pan_layout_queue_step(PanWindow *pw);
2471 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2473 PanWindow *pw = data;
2481 pw->queue_pi = NULL;
2485 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2486 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2489 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2493 thumb_loader_free(pw->tl);
2496 while (pan_layout_queue_step(pw));
2499 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2501 PanWindow *pw = data;
2509 pw->queue_pi = NULL;
2513 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2514 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2515 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2517 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2518 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2519 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2524 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2525 (GdkInterpType)zoom_quality);
2526 g_object_unref(tmp);
2530 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2534 image_loader_free(pw->il);
2537 while (pan_layout_queue_step(pw));
2541 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2542 guint width, guint height, gpointer data)
2544 PanWindow *pw = data;
2555 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2556 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2560 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2566 static gint pan_layout_queue_step(PanWindow *pw)
2570 if (!pw->queue) return FALSE;
2572 pi = pw->queue->data;
2573 pw->queue = g_list_remove(pw->queue, pi);
2576 if (!pw->queue_pi->fd)
2578 pw->queue_pi->queued = FALSE;
2579 pw->queue_pi = NULL;
2583 image_loader_free(pw->il);
2585 thumb_loader_free(pw->tl);
2588 if (pi->type == ITEM_IMAGE)
2590 pw->il = image_loader_new(pi->fd->path);
2592 if (pw->size != LAYOUT_SIZE_100)
2594 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2598 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2600 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2602 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2604 image_loader_free(pw->il);
2607 else if (pi->type == ITEM_THUMB)
2609 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2611 if (!pw->tl->standard_loader)
2613 /* The classic loader will recreate a thumbnail any time we
2614 * request a different size than what exists. This view will
2615 * almost never use the user configured sizes so disable cache.
2617 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2620 thumb_loader_set_callbacks(pw->tl,
2621 pan_layout_queue_thumb_done_cb,
2622 pan_layout_queue_thumb_done_cb,
2625 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2627 thumb_loader_free(pw->tl);
2631 pw->queue_pi->queued = FALSE;
2632 pw->queue_pi = NULL;
2636 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2638 if (!pi || pi->queued || pi->pixbuf) return;
2639 if (pw->size <= LAYOUT_SIZE_THUMB_NONE) return;
2642 pw->queue = g_list_prepend(pw->queue, pi);
2644 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2647 static gint pan_window_request_tile_cb(ImageWindow *imd, gint x, gint y, gint width, gint height,
2648 GdkPixbuf *pixbuf, gpointer data)
2650 PanWindow *pw = data;
2655 pixbuf_draw_rect_fill(pixbuf,
2656 0, 0, width, height,
2657 PAN_BACKGROUND_COLOR, 255);
2659 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2661 gint rx, ry, rw, rh;
2663 if (util_clip_region(x, y, width, height,
2665 &rx, &ry, &rw, &rh))
2667 pixbuf_draw_rect_fill(pixbuf,
2668 rx - x, ry - y, rw, rh,
2669 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2672 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2674 gint rx, ry, rw, rh;
2676 if (util_clip_region(x, y, width, height,
2678 &rx, &ry, &rw, &rh))
2680 pixbuf_draw_rect_fill(pixbuf,
2681 rx - x, ry - y, rw, rh,
2682 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2686 list = pan_layout_intersect(pw, x, y, width, height);
2691 gint tx, ty, tw, th;
2692 gint rx, ry, rw, rh;
2699 if (pi->type == ITEM_THUMB && pi->pixbuf)
2701 tw = gdk_pixbuf_get_width(pi->pixbuf);
2702 th = gdk_pixbuf_get_height(pi->pixbuf);
2704 tx = pi->x + (pi->width - tw) / 2;
2705 ty = pi->y + (pi->height - th) / 2;
2707 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2709 if (util_clip_region(x, y, width, height,
2710 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2711 &rx, &ry, &rw, &rh))
2713 pixbuf_draw_shadow(pixbuf,
2714 rx - x, ry - y, rw, rh,
2715 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2717 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2722 if (util_clip_region(x, y, width, height,
2723 tx + tw, ty + PAN_SHADOW_OFFSET,
2724 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2725 &rx, &ry, &rw, &rh))
2727 pixbuf_draw_shadow(pixbuf,
2728 rx - x, ry - y, rw, rh,
2729 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2731 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2733 if (util_clip_region(x, y, width, height,
2734 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2735 &rx, &ry, &rw, &rh))
2737 pixbuf_draw_shadow(pixbuf,
2738 rx - x, ry - y, rw, rh,
2739 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2741 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2745 if (util_clip_region(x, y, width, height,
2747 &rx, &ry, &rw, &rh))
2749 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2752 1.0, 1.0, GDK_INTERP_NEAREST,
2756 if (util_clip_region(x, y, width, height,
2757 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2758 &rx, &ry, &rw, &rh))
2760 pixbuf_draw_rect_fill(pixbuf,
2761 rx - x, ry - y, rw, rh,
2762 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2764 if (util_clip_region(x, y, width, height,
2765 tx, ty, PAN_OUTLINE_THICKNESS, th,
2766 &rx, &ry, &rw, &rh))
2768 pixbuf_draw_rect_fill(pixbuf,
2769 rx - x, ry - y, rw, rh,
2770 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2772 if (util_clip_region(x, y, width, height,
2773 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2774 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2775 &rx, &ry, &rw, &rh))
2777 pixbuf_draw_rect_fill(pixbuf,
2778 rx - x, ry - y, rw, rh,
2779 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2781 if (util_clip_region(x, y, width, height,
2782 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2783 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2784 &rx, &ry, &rw, &rh))
2786 pixbuf_draw_rect_fill(pixbuf,
2787 rx - x, ry - y, rw, rh,
2788 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2791 else if (pi->type == ITEM_THUMB)
2793 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2794 th = pi->height - PAN_SHADOW_OFFSET * 2;
2795 tx = pi->x + PAN_SHADOW_OFFSET;
2796 ty = pi->y + PAN_SHADOW_OFFSET;
2798 if (util_clip_region(x, y, width, height,
2800 &rx, &ry, &rw, &rh))
2804 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2805 pixbuf_draw_rect_fill(pixbuf,
2806 rx - x, ry - y, rw, rh,
2808 PAN_SHADOW_ALPHA / d);
2811 pan_layout_queue(pw, pi);
2813 else if (pi->type == ITEM_IMAGE)
2815 if (util_clip_region(x, y, width, height,
2816 pi->x, pi->y, pi->width, pi->height,
2817 &rx, &ry, &rw, &rh))
2821 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2824 1.0, 1.0, GDK_INTERP_NEAREST,
2829 pixbuf_draw_rect_fill(pixbuf,
2830 rx - x, ry - y, rw, rh,
2831 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2832 pan_layout_queue(pw, pi);
2836 else if (pi->type == ITEM_BOX)
2850 if (pi->color_a > 254)
2852 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2853 shadow[0], bh - shadow[0],
2854 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2856 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2857 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2859 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2861 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2866 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2867 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2869 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2871 PAN_SHADOW_COLOR, a);
2875 if (util_clip_region(x, y, width, height,
2876 pi->x, pi->y, bw, bh,
2877 &rx, &ry, &rw, &rh))
2879 pixbuf_draw_rect_fill(pixbuf,
2880 rx - x, ry - y, rw, rh,
2881 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2883 if (util_clip_region(x, y, width, height,
2884 pi->x, pi->y, bw, pi->border,
2885 &rx, &ry, &rw, &rh))
2887 pixbuf_draw_rect_fill(pixbuf,
2888 rx - x, ry - y, rw, rh,
2889 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2891 if (util_clip_region(x, y, width, height,
2892 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
2893 &rx, &ry, &rw, &rh))
2895 pixbuf_draw_rect_fill(pixbuf,
2896 rx - x, ry - y, rw, rh,
2897 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2899 if (util_clip_region(x, y, width, height,
2900 pi->x + bw - pi->border, pi->y + pi->border,
2901 pi->border, bh - pi->border * 2,
2902 &rx, &ry, &rw, &rh))
2904 pixbuf_draw_rect_fill(pixbuf,
2905 rx - x, ry - y, rw, rh,
2906 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2908 if (util_clip_region(x, y, width, height,
2909 pi->x, pi->y + bh - pi->border,
2911 &rx, &ry, &rw, &rh))
2913 pixbuf_draw_rect_fill(pixbuf,
2914 rx - x, ry - y, rw, rh,
2915 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2918 else if (pi->type == ITEM_TRIANGLE)
2920 if (util_clip_region(x, y, width, height,
2921 pi->x, pi->y, pi->width, pi->height,
2922 &rx, &ry, &rw, &rh) && pi->data)
2924 gint *coord = pi->data;
2925 pixbuf_draw_triangle(pixbuf,
2926 rx - x, ry - y, rw, rh,
2927 coord[0] - x, coord[1] - y,
2928 coord[2] - x, coord[3] - y,
2929 coord[4] - x, coord[5] - y,
2930 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2932 if (pi->border & BORDER_1)
2934 pixbuf_draw_line(pixbuf,
2935 rx - x, ry - y, rw, rh,
2936 coord[0] - x, coord[1] - y,
2937 coord[2] - x, coord[3] - y,
2938 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2940 if (pi->border & BORDER_2)
2942 pixbuf_draw_line(pixbuf,
2943 rx - x, ry - y, rw, rh,
2944 coord[2] - x, coord[3] - y,
2945 coord[4] - x, coord[5] - y,
2946 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2948 if (pi->border & BORDER_3)
2950 pixbuf_draw_line(pixbuf,
2951 rx - x, ry - y, rw, rh,
2952 coord[4] - x, coord[5] - y,
2953 coord[0] - x, coord[1] - y,
2954 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2958 else if (pi->type == ITEM_TEXT && pi->text)
2960 PangoLayout *layout;
2962 layout = pan_item_text_layout(pi, imd->image);
2963 pixbuf_draw_layout(pixbuf, layout, imd->image,
2964 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
2965 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2966 g_object_unref(G_OBJECT(layout));
2973 static gint count = 0;
2974 PangoLayout *layout;
2980 layout = gtk_widget_create_pango_layout(imd->image, NULL);
2982 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
2983 (x / imd->source_tile_width) +
2984 (y / imd->source_tile_height * (imd->image_width/imd->source_tile_width + 1)));
2985 pango_layout_set_text(layout, buf, -1);
2988 pango_layout_get_pixel_size(layout, &lw, &lh);
2990 pixmap = gdk_pixmap_new(imd->widget->window, lw, lh, -1);
2991 gdk_draw_rectangle(pixmap, imd->widget->style->black_gc, TRUE, 0, 0, lw, lh);
2992 gdk_draw_layout(pixmap, imd->widget->style->white_gc, 0, 0, layout);
2993 g_object_unref(G_OBJECT(layout));
2995 lx = MAX(0, width / 2 - lw / 2);
2996 ly = MAX(0, height / 2 - lh / 2);
2997 lw = MIN(lw, width - lx);
2998 lh = MIN(lh, height - ly);
2999 gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_drawable_get_colormap(imd->image->window),
3000 0, 0, lx, ly, lw, lh);
3001 g_object_unref(pixmap);
3009 static void pan_window_dispose_tile_cb(ImageWindow *imd, gint x, gint y, gint width, gint height,
3010 GdkPixbuf *pixbuf, gpointer data)
3012 PanWindow *pw = data;
3016 list = pan_layout_intersect(pw, x, y, width, height);
3025 if (pi->refcount > 0)
3029 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
3034 pw->queue = g_list_remove(pw->queue, pi);
3037 if (pw->queue_pi == pi) pw->queue_pi = NULL;
3040 g_object_unref(pi->pixbuf);
3052 *-----------------------------------------------------------------------------
3054 *-----------------------------------------------------------------------------
3057 static void pan_window_message(PanWindow *pw, const gchar *text)
3067 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
3072 if (pw->layout == LAYOUT_CALENDAR)
3082 pi->type == ITEM_BOX &&
3083 pi->key && strcmp(pi->key, "dot") == 0)
3085 size += pi->fd->size;
3100 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
3102 size += pi->fd->size;
3108 ss = text_from_size_abrev(size);
3109 buf = g_strdup_printf(_("%d images, %s"), count, ss);
3111 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
3115 static ImageWindow *pan_window_active_image(PanWindow *pw)
3117 if (pw->fs) return pw->fs->imd;
3122 static void pan_window_zoom_limit(PanWindow *pw)
3128 case LAYOUT_SIZE_THUMB_DOTS:
3129 case LAYOUT_SIZE_THUMB_NONE:
3130 case LAYOUT_SIZE_THUMB_SMALL:
3131 case LAYOUT_SIZE_THUMB_NORMAL:
3133 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
3137 case LAYOUT_SIZE_THUMB_LARGE:
3140 case LAYOUT_SIZE_10:
3141 case LAYOUT_SIZE_25:
3144 case LAYOUT_SIZE_33:
3145 case LAYOUT_SIZE_50:
3146 case LAYOUT_SIZE_100:
3152 image_zoom_set_limits(pw->imd, min, 32.0);
3155 static gint pan_window_layout_update_idle_cb(gpointer data)
3157 PanWindow *pw = data;
3163 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
3165 if (!pw->cache_list && !pw->cache_todo)
3167 pan_cache_fill(pw, pw->path);
3170 pan_window_message(pw, _("Reading dimensions..."));
3174 if (pan_cache_step(pw))
3178 if (pw->cache_count == pw->cache_total)
3180 pan_window_message(pw, _("Sorting images..."));
3182 else if (pw->cache_tick > 9)
3186 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
3187 pw->cache_total - pw->cache_count);
3188 pan_window_message(pw, buf);
3198 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3200 pan_window_zoom_limit(pw);
3202 if (width > 0 && height > 0)
3206 image_set_image_as_tiles(pw->imd, width, height,
3207 PAN_TILE_SIZE, PAN_TILE_SIZE, 8,
3208 pan_window_request_tile_cb,
3209 pan_window_dispose_tile_cb, pw, 1.0);
3211 if (scroll_x == 0 && scroll_y == 0)
3219 image_scroll_to_point(pw->imd, scroll_x, scroll_y, align, align);
3222 pan_window_message(pw, NULL);
3229 static void pan_window_layout_update_idle(PanWindow *pw)
3231 if (pw->idle_id == -1)
3233 pan_window_message(pw, _("Sorting images..."));
3234 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3239 *-----------------------------------------------------------------------------
3240 * pan window keyboard
3241 *-----------------------------------------------------------------------------
3244 static const gchar *pan_menu_click_path(PanWindow *pw)
3246 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3250 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3252 PanWindow *pw = data;
3255 imd = pan_window_active_image(pw);
3256 gdk_window_get_origin(imd->image->window, x, y);
3257 popup_menu_position_clamp(menu, x, y, 0);
3260 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3262 PanWindow *pw = data;
3265 gint stop_signal = FALSE;
3271 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(pw->imd->widget));
3273 imd = pan_window_active_image(pw);
3274 path = pan_menu_click_path(pw);
3278 switch (event->keyval)
3280 case GDK_Left: case GDK_KP_Left:
3284 case GDK_Right: case GDK_KP_Right:
3288 case GDK_Up: case GDK_KP_Up:
3292 case GDK_Down: case GDK_KP_Down:
3296 case GDK_Page_Up: case GDK_KP_Page_Up:
3297 image_scroll(imd, 0, 0-imd->vis_height / 2);
3299 case GDK_Page_Down: case GDK_KP_Page_Down:
3300 image_scroll(imd, 0, imd->vis_height / 2);
3302 case GDK_Home: case GDK_KP_Home:
3303 image_scroll(imd, 0-imd->vis_width / 2, 0);
3305 case GDK_End: case GDK_KP_End:
3306 image_scroll(imd, imd->vis_width / 2, 0);
3311 if (focused && !(event->state & GDK_CONTROL_MASK) )
3312 switch (event->keyval)
3314 case '+': case '=': case GDK_KP_Add:
3315 image_zoom_adjust(imd, ZOOM_INCREMENT);
3317 case '-': case GDK_KP_Subtract:
3318 image_zoom_adjust(imd, -ZOOM_INCREMENT);
3320 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3321 image_zoom_set(imd, 1.0);
3324 image_zoom_set(imd, 2.0);
3327 image_zoom_set(imd, 3.0);
3330 image_zoom_set(imd, 4.0);
3333 image_zoom_set(imd, -4.0);
3336 image_zoom_set(imd, -3.0);
3339 image_zoom_set(imd, -2.0);
3343 pan_fullscreen_toggle(pw, FALSE);
3347 pan_overlay_toggle(pw);
3349 case GDK_Delete: case GDK_KP_Delete:
3354 if (GTK_WIDGET_VISIBLE(pw->search_box))
3356 gtk_widget_grab_focus(pw->search_entry);
3360 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3368 pan_fullscreen_toggle(pw, TRUE);
3371 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3373 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3379 menu = pan_popup_menu(pw);
3380 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3385 if (event->state & GDK_CONTROL_MASK)
3388 switch (event->keyval)
3421 if (path) file_util_copy(path, NULL, NULL, imd->widget);
3424 if (path) file_util_move(path, NULL, NULL, imd->widget);
3427 if (path) file_util_rename(path, NULL, imd->widget);
3430 if (path) file_util_delete(path, NULL, imd->widget);
3433 if (path) info_window_new(path, NULL);
3436 pan_window_close(pw);
3439 if (n != -1 && path)
3441 pan_fullscreen_toggle(pw, TRUE);
3442 start_editor_from_file(n, path);
3446 else if (event->state & GDK_SHIFT_MASK)
3453 switch (event->keyval)
3458 pan_fullscreen_toggle(pw, TRUE);
3461 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3463 gtk_widget_grab_focus(pw->imd->widget);
3464 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3473 if (x != 0 || y!= 0)
3475 keyboard_scroll_calc(&x, &y, event);
3476 image_scroll(imd, x, y);
3483 *-----------------------------------------------------------------------------
3485 *-----------------------------------------------------------------------------
3488 static void pan_info_update(PanWindow *pw, PanItem *pi)
3494 gint x1, y1, x2, y2, x3, y3;
3497 if (pw->click_pi == pi) return;
3498 if (pi && !pi->fd) pi = NULL;
3500 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3505 printf("info set to %s\n", pi->fd->path);
3507 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3509 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3510 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3511 pan_item_set_key(pbox, "info");
3513 if (pi->type == ITEM_THUMB && pi->pixbuf)
3515 w = gdk_pixbuf_get_width(pi->pixbuf);
3516 h = gdk_pixbuf_get_height(pi->pixbuf);
3518 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3519 y1 = pi->y + (pi->height - h) / 2 + 8;
3523 x1 = pi->x + pi->width - 8;
3531 triangle_rect_region(x1, y1, x2, y2, x3, y3,
3534 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3535 x1, y1, x2, y2, x3, y3,
3536 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3537 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3538 pan_item_set_key(p, "info");
3539 pan_item_added(pw, p);
3541 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3542 _("Filename:"), TEXT_ATTR_BOLD,
3543 PAN_POPUP_TEXT_COLOR, 255);
3544 pan_item_set_key(plabel, "info");
3545 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3546 pi->fd->name, TEXT_ATTR_NONE,
3547 PAN_POPUP_TEXT_COLOR, 255);
3548 pan_item_set_key(p, "info");
3549 pan_item_size_by_item(pbox, p, 0);
3551 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3552 _("Date:"), TEXT_ATTR_BOLD,
3553 PAN_POPUP_TEXT_COLOR, 255);
3554 pan_item_set_key(plabel, "info");
3555 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3556 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3557 PAN_POPUP_TEXT_COLOR, 255);
3558 pan_item_set_key(p, "info");
3559 pan_item_size_by_item(pbox, p, 0);
3561 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3562 _("Size:"), TEXT_ATTR_BOLD,
3563 PAN_POPUP_TEXT_COLOR, 255);
3564 pan_item_set_key(plabel, "info");
3565 buf = text_from_size(pi->fd->size);
3566 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3567 buf, TEXT_ATTR_NONE,
3568 PAN_POPUP_TEXT_COLOR, 255);
3570 pan_item_set_key(p, "info");
3571 pan_item_size_by_item(pbox, p, 0);
3573 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3574 pan_item_added(pw, pbox);
3579 *-----------------------------------------------------------------------------
3581 *-----------------------------------------------------------------------------
3584 static void pan_search_status(PanWindow *pw, const gchar *text)
3586 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3589 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3597 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3599 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3600 if (!list) return FALSE;
3602 found = g_list_find(list, pw->click_pi);
3603 if (found && found->next)
3605 found = found->next;
3613 pan_info_update(pw, pi);
3614 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3616 buf = g_strdup_printf("%s ( %d / %d )",
3617 (path[0] == '/') ? _("path found") : _("filename found"),
3618 g_list_index(list, pi) + 1,
3619 g_list_length(list));
3620 pan_search_status(pw, buf);
3628 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3636 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3638 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3639 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3644 needle = g_utf8_strdown(text, -1);
3645 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3648 if (!list) return FALSE;
3650 found = g_list_find(list, pw->click_pi);
3651 if (found && found->next)
3653 found = found->next;
3661 pan_info_update(pw, pi);
3662 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3664 buf = g_strdup_printf("%s ( %d / %d )",
3666 g_list_index(list, pi) + 1,
3667 g_list_length(list));
3668 pan_search_status(pw, buf);
3676 static gint valid_date_separator(gchar c)
3678 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3681 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
3682 gint year, gint month, gint day,
3688 work = g_list_last(pw->list);
3696 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
3697 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
3701 tl = localtime(&pi->fd->date);
3706 match = (tl->tm_year == year - 1900);
3707 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3708 if (match && day > 0) match = (tl->tm_mday == day);
3710 if (match) list = g_list_prepend(list, pi);
3715 return g_list_reverse(list);
3718 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3734 if (!text) return FALSE;
3736 ptr = (gchar *)text;
3737 while (*ptr != '\0')
3739 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3744 if (t == -1) return FALSE;
3746 if (!lt) return FALSE;
3748 if (valid_date_separator(*text))
3751 mptr = (gchar *)text;
3755 year = (gint)strtol(text, &mptr, 10);
3756 if (mptr == text) return FALSE;
3759 if (*mptr != '\0' && valid_date_separator(*mptr))
3764 month = strtol(mptr, &dptr, 10);
3767 if (valid_date_separator(*dptr))
3769 month = lt->tm_mon + 1;
3777 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3781 day = strtol(dptr, &eptr, 10);
3791 year = lt->tm_year + 1900;
3793 else if (year < 100)
3802 month < -1 || month == 0 || month > 12 ||
3803 day < -1 || day == 0 || day > 31) return FALSE;
3805 t = date_to_time(year, month, day);
3806 if (t < 0) return FALSE;
3808 if (pw->layout == LAYOUT_CALENDAR)
3810 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
3816 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3817 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
3822 found = g_list_find(list, pw->search_pi);
3823 if (found && found->next)
3825 found = found->next;
3836 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
3838 pan_info_update(pw, NULL);
3839 pan_calendar_update(pw, pi);
3840 image_scroll_to_point(pw->imd,
3841 pi->x + pi->width / 2,
3842 pi->y + pi->height / 2, 0.5, 0.5);
3846 pan_info_update(pw, pi);
3847 image_scroll_to_point(pw->imd,
3848 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3854 buf = date_value_string(t, DATE_LENGTH_MONTH);
3859 buf = g_strdup_printf("%d %s", day, tmp);
3865 buf = date_value_string(t, DATE_LENGTH_YEAR);
3870 buf_count = g_strdup_printf("( %d / %d )",
3871 g_list_index(list, pi) + 1,
3872 g_list_length(list));
3876 buf_count = g_strdup_printf("(%s)", _("no match"));
3879 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
3882 pan_search_status(pw, message);
3890 static void pan_search_activate_cb(const gchar *text, gpointer data)
3892 PanWindow *pw = data;
3896 tab_completion_append_to_history(pw->search_entry, text);
3898 if (pan_search_by_path(pw, text)) return;
3900 if ((pw->layout == LAYOUT_TIMELINE ||
3901 pw->layout == LAYOUT_CALENDAR) &&
3902 pan_search_by_date(pw, text))
3907 if (pan_search_by_partial(pw, text)) return;
3909 pan_search_status(pw, _("no match"));
3912 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3914 PanWindow *pw = data;
3917 visible = GTK_WIDGET_VISIBLE(pw->search_box);
3918 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3922 gtk_widget_hide(pw->search_box);
3923 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3927 gtk_widget_show(pw->search_box);
3928 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3929 gtk_widget_grab_focus(pw->search_entry);
3935 *-----------------------------------------------------------------------------
3936 * view window main routines
3937 *-----------------------------------------------------------------------------
3940 static void button_cb(ImageWindow *imd, gint button, guint32 time,
3941 gdouble x, gdouble y, guint state, gpointer data)
3943 PanWindow *pw = data;
3951 rx = (double)(pw->imd->x_scroll + x - pw->imd->x_offset) / pw->imd->scale;
3952 ry = (double)(pw->imd->y_scroll + y - pw->imd->y_offset) / pw->imd->scale;
3955 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3961 pan_info_update(pw, pi);
3963 if (!pi && pw->layout == LAYOUT_CALENDAR)
3965 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
3966 pan_calendar_update(pw, pi);
3972 pan_info_update(pw, pi);
3973 menu = pan_popup_menu(pw);
3974 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, time);
3981 static void scroll_cb(ImageWindow *imd, GdkScrollDirection direction, guint32 time,
3982 gdouble x, gdouble y, guint state, gpointer data)
3985 PanWindow *pw = data;
3990 h = imd->vis_height;
3992 if (!(state & GDK_SHIFT_MASK))
3998 if (state & GDK_CONTROL_MASK)
4003 image_zoom_adjust_at_point(imd, ZOOM_INCREMENT, x, y);
4005 case GDK_SCROLL_DOWN:
4006 image_zoom_adjust_at_point(imd, -ZOOM_INCREMENT, x, y);
4017 image_scroll(imd, 0, -h);
4019 case GDK_SCROLL_DOWN:
4020 image_scroll(imd, 0, h);
4022 case GDK_SCROLL_LEFT:
4023 image_scroll(imd, -w, 0);
4025 case GDK_SCROLL_RIGHT:
4026 image_scroll(imd, w, 0);
4034 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
4036 image_set_button_func(imd, button_cb, pw);
4037 image_set_scroll_func(imd, scroll_cb, pw);
4040 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
4042 PanWindow *pw = data;
4047 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
4049 if (force_off && !pw->fs) return;
4053 fullscreen_stop(pw->fs);
4054 pw->imd = pw->imd_normal;
4058 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
4060 pan_image_set_buttons(pw, pw->fs->imd);
4061 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
4062 G_CALLBACK(pan_window_key_press_cb), pw);
4064 pw->imd = pw->fs->imd;
4068 static void pan_overlay_toggle(PanWindow *pw)
4072 imd = pan_window_active_image(pw);
4074 if (pw->overlay_id == -1)
4076 pw->overlay_id = image_overlay_info_enable(imd);
4080 image_overlay_info_disable(imd, pw->overlay_id);
4081 pw->overlay_id = -1;
4086 static void pan_window_image_update_cb(ImageWindow *imd, gpointer data)
4088 PanWindow *pw = data;
4091 text = image_zoom_get_as_text(imd);
4092 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4096 static void pan_window_image_scroll_notify_cb(ImageWindow *imd, gint x, gint y,
4097 gint width, gint height, gpointer data)
4099 PanWindow *pw = data;
4102 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4103 adj->page_size = (gdouble)imd->vis_width / imd->scale;
4104 adj->page_increment = adj->page_size / 2.0;
4105 adj->step_increment = 48.0 / imd->scale;
4107 adj->upper = MAX((gdouble)width + adj->page_size, 1.0);
4108 adj->value = (gdouble)x;
4110 pref_signal_block_data(pw->scrollbar_h, pw);
4111 gtk_adjustment_changed(adj);
4112 pref_signal_unblock_data(pw->scrollbar_h, pw);
4114 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4115 adj->page_size = (gdouble)imd->vis_height / imd->scale;
4116 adj->page_increment = adj->page_size / 2.0;
4117 adj->step_increment = 48.0 / imd->scale;
4119 adj->upper = MAX((gdouble)height + adj->page_size, 1.0);
4120 adj->value = (gdouble)y;
4122 pref_signal_block_data(pw->scrollbar_v, pw);
4123 gtk_adjustment_changed(adj);
4124 pref_signal_unblock_data(pw->scrollbar_v, pw);
4126 // printf("scrolled to %d,%d @ %d x %d\n", x, y, width, height);
4129 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4131 PanWindow *pw = data;
4134 if (!pw->imd->scale) return;
4136 x = (gint)gtk_range_get_value(range);
4138 image_scroll_to_point(pw->imd, x, (gint)((gdouble)pw->imd->y_scroll / pw->imd->scale), 0.0, 0.0);
4141 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4143 PanWindow *pw = data;
4146 if (!pw->imd->scale) return;
4148 y = (gint)gtk_range_get_value(range);
4150 image_scroll_to_point(pw->imd, (gint)((gdouble)pw->imd->x_scroll / pw->imd->scale), y, 0.0, 0.0);
4153 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4155 PanWindow *pw = data;
4157 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4158 pan_window_layout_update_idle(pw);
4161 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4163 PanWindow *pw = data;
4165 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4166 pan_window_layout_update_idle(pw);
4169 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4171 PanWindow *pw = data;
4174 path = remove_trailing_slash(new_text);
4175 parse_out_relatives(path);
4179 warning_dialog(_("Folder not found"),
4180 _("The entered path is not a folder"),
4181 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4185 tab_completion_append_to_history(pw->path_entry, path);
4188 pw->path = g_strdup(path);
4190 pan_window_layout_update_idle(pw);
4193 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4195 PanWindow *pw = data;
4198 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4200 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4201 pan_window_entry_activate_cb(text, pw);
4205 static void pan_window_close(PanWindow *pw)
4207 pan_window_list = g_list_remove(pan_window_list, pw);
4209 if (pw->idle_id != -1)
4211 g_source_remove(pw->idle_id);
4214 pan_fullscreen_toggle(pw, TRUE);
4215 gtk_widget_destroy(pw->window);
4217 pan_window_items_free(pw);
4224 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4226 PanWindow *pw = data;
4228 pan_window_close(pw);
4232 static void pan_window_new_real(const gchar *path)
4241 GdkGeometry geometry;
4243 pw = g_new0(PanWindow, 1);
4245 pw->path = g_strdup(path);
4246 pw->layout = LAYOUT_TIMELINE;
4247 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4248 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4249 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4253 pw->overlay_id = -1;
4256 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4258 geometry.min_width = 8;
4259 geometry.min_height = 8;
4260 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4262 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4263 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
4264 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4265 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4267 window_set_icon(pw->window, NULL, NULL);
4269 vbox = gtk_vbox_new(FALSE, 0);
4270 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4271 gtk_widget_show(vbox);
4273 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4275 pref_spacer(box, 0);
4276 pref_label_new(box, _("Location:"));
4277 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
4278 pan_window_entry_activate_cb, pw);
4279 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4280 G_CALLBACK(pan_window_entry_change_cb), pw);
4281 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4282 gtk_widget_show(combo);
4284 combo = gtk_combo_box_new_text();
4285 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4286 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4287 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4288 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4289 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4291 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4292 g_signal_connect(G_OBJECT(combo), "changed",
4293 G_CALLBACK(pan_window_layout_change_cb), pw);
4294 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4295 gtk_widget_show(combo);
4297 combo = gtk_combo_box_new_text();
4298 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4299 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4300 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4301 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4302 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4303 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4304 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4305 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4306 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4307 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4309 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4310 g_signal_connect(G_OBJECT(combo), "changed",
4311 G_CALLBACK(pan_window_layout_size_cb), pw);
4312 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4313 gtk_widget_show(combo);
4315 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4316 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4317 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4319 pw->imd = image_new(TRUE);
4320 pw->imd_normal = pw->imd;
4322 if (black_window_background) image_background_set_black(pw->imd, TRUE);
4323 image_set_update_func(pw->imd, pan_window_image_update_cb, pw);
4325 image_set_scroll_notify_func(pw->imd, pan_window_image_scroll_notify_cb, pw);
4328 gtk_box_pack_start(GTK_BOX(vbox), pw->imd->widget, TRUE, TRUE, 0);
4330 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4331 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4332 gtk_widget_show(pw->imd->widget);
4334 pan_window_dnd_init(pw);
4336 pan_image_set_buttons(pw, pw->imd);
4338 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4339 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4340 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4341 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4342 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4343 gtk_widget_show(pw->scrollbar_h);
4345 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4346 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4347 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4348 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4349 0, GTK_FILL | GTK_EXPAND, 0, 0);
4350 gtk_widget_show(pw->scrollbar_v);
4354 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4355 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4357 pref_spacer(pw->search_box, 0);
4358 pref_label_new(pw->search_box, _("Find:"));
4360 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4361 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4362 gtk_widget_show(hbox);
4364 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4365 pan_search_activate_cb, pw);
4366 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4367 gtk_widget_show(combo);
4369 pw->search_label = gtk_label_new("");
4370 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4371 gtk_widget_show(pw->search_label);
4375 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4377 frame = gtk_frame_new(NULL);
4378 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4379 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4380 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4381 gtk_widget_show(frame);
4383 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4384 gtk_container_add(GTK_CONTAINER(frame), hbox);
4385 gtk_widget_show(hbox);
4387 pref_spacer(hbox, 0);
4388 pw->label_message = pref_label_new(hbox, "");
4390 frame = gtk_frame_new(NULL);
4391 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4392 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4393 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4394 gtk_widget_show(frame);
4396 pw->label_zoom = gtk_label_new("");
4397 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4398 gtk_widget_show(pw->label_zoom);
4400 pw->search_button = gtk_toggle_button_new();
4401 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4402 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4403 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4404 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4405 gtk_widget_show(hbox);
4406 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4407 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4408 gtk_widget_show(pw->search_button_arrow);
4409 pref_label_new(hbox, _("Find"));
4411 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4412 gtk_widget_show(pw->search_button);
4413 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4414 G_CALLBACK(pan_search_toggle_cb), pw);
4416 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4417 G_CALLBACK(pan_window_delete_cb), pw);
4418 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4419 G_CALLBACK(pan_window_key_press_cb), pw);
4421 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4423 pan_window_layout_update_idle(pw);
4425 gtk_widget_grab_focus(pw->imd->widget);
4426 gtk_widget_show(pw->window);
4428 pan_window_list = g_list_append(pan_window_list, pw);
4432 *-----------------------------------------------------------------------------
4433 * peformance warnings
4434 *-----------------------------------------------------------------------------
4437 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4441 generic_dialog_close(gd);
4443 pan_window_new_real(path);
4447 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4451 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4452 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4455 static gint pan_warning(const gchar *path)
4461 GtkWidget *ct_button;
4464 if (enable_thumb_caching &&
4465 thumbnail_spec_standard) return FALSE;
4467 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4468 if (hide_dlg) return FALSE;
4470 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4472 gd->data = g_strdup(path);
4473 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4474 pan_warning_ok_cb, TRUE);
4476 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4477 _("Pan view performance may be poor."),
4478 _("To improve performance of thumbnails in the pan view the"
4479 " following options can be enabled. Note that both options"
4480 " must be enabled to notice a change in performance."));
4482 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4483 pref_spacer(group, PREF_PAD_INDENT);
4484 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4486 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4487 enable_thumb_caching, &enable_thumb_caching);
4488 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4489 thumbnail_spec_standard, &thumbnail_spec_standard);
4490 pref_checkbox_link_sensitivity(ct_button, button);
4494 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4495 G_CALLBACK(pan_warning_hide_cb), NULL);
4497 gtk_widget_show(gd->dialog);
4504 *-----------------------------------------------------------------------------
4506 *-----------------------------------------------------------------------------
4509 void pan_window_new(const gchar *path)
4511 if (pan_warning(path)) return;
4513 pan_window_new_real(path);
4517 *-----------------------------------------------------------------------------
4519 *-----------------------------------------------------------------------------
4522 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4524 PanWindow *pw = data;
4527 path = pan_menu_click_path(pw);
4530 pan_fullscreen_toggle(pw, TRUE);
4531 view_window_new(path);
4535 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4541 pw = submenu_item_get_data(widget);
4542 n = GPOINTER_TO_INT(data);
4545 path = pan_menu_click_path(pw);
4548 pan_fullscreen_toggle(pw, TRUE);
4549 start_editor_from_file(n, path);
4553 static void pan_info_cb(GtkWidget *widget, gpointer data)
4555 PanWindow *pw = data;
4558 path = pan_menu_click_path(pw);
4559 if (path) info_window_new(path, NULL);
4562 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4564 PanWindow *pw = data;
4566 image_zoom_adjust(pan_window_active_image(pw), ZOOM_INCREMENT);
4569 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4571 PanWindow *pw = data;
4573 image_zoom_adjust(pan_window_active_image(pw), -ZOOM_INCREMENT);
4576 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4578 PanWindow *pw = data;
4580 image_zoom_set(pan_window_active_image(pw), 1.0);
4583 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4585 PanWindow *pw = data;
4588 path = pan_menu_click_path(pw);
4589 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4592 static void pan_move_cb(GtkWidget *widget, gpointer data)
4594 PanWindow *pw = data;
4597 path = pan_menu_click_path(pw);
4598 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4601 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4603 PanWindow *pw = data;
4606 path = pan_menu_click_path(pw);
4607 if (path) file_util_rename(path, NULL, pw->imd->widget);
4610 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4612 PanWindow *pw = data;
4615 path = pan_menu_click_path(pw);
4616 if (path) file_util_delete(path, NULL, pw->imd->widget);
4619 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4621 PanWindow *pw = data;
4623 pan_fullscreen_toggle(pw, FALSE);
4626 static void pan_close_cb(GtkWidget *widget, gpointer data)
4628 PanWindow *pw = data;
4630 pan_window_close(pw);
4633 static GtkWidget *pan_popup_menu(PanWindow *pw)
4639 active = (pw->click_pi != NULL);
4641 menu = popup_menu_short_lived();
4643 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4644 G_CALLBACK(pan_zoom_in_cb), pw);
4645 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4646 G_CALLBACK(pan_zoom_out_cb), pw);
4647 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4648 G_CALLBACK(pan_zoom_1_1_cb), pw);
4649 menu_item_add_divider(menu);
4651 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4652 gtk_widget_set_sensitive(item, active);
4654 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4655 G_CALLBACK(pan_info_cb), pw);
4657 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4658 G_CALLBACK(pan_new_window_cb), pw);
4660 menu_item_add_divider(menu);
4661 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4662 G_CALLBACK(pan_copy_cb), pw);
4663 menu_item_add_sensitive(menu, _("_Move..."), active,
4664 G_CALLBACK(pan_move_cb), pw);
4665 menu_item_add_sensitive(menu, _("_Rename..."), active,
4666 G_CALLBACK(pan_rename_cb), pw);
4667 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4668 G_CALLBACK(pan_delete_cb), pw);
4670 menu_item_add_divider(menu);
4674 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4678 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4681 menu_item_add_divider(menu);
4682 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4688 *-----------------------------------------------------------------------------
4690 *-----------------------------------------------------------------------------
4693 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4695 GtkSelectionData *selection_data, guint info,
4696 guint time, gpointer data)
4698 PanWindow *pw = data;
4701 if (gtk_drag_get_source_widget(context) == pw->imd->image) return;
4705 if (info == TARGET_URI_LIST)
4709 list = uri_list_from_text(selection_data->data, TRUE);
4710 if (list && isdir((gchar *)list->data))
4712 gchar *path = list->data;
4715 pw->path = g_strdup(path);
4717 pan_window_layout_update_idle(pw);
4720 path_list_free(list);
4724 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4725 GtkSelectionData *selection_data, guint info,
4726 guint time, gpointer data)
4728 PanWindow *pw = data;
4731 path = pan_menu_click_path(pw);
4741 case TARGET_URI_LIST:
4744 case TARGET_TEXT_PLAIN:
4749 list = g_list_append(NULL, (gchar *)path);
4750 text = uri_text_from_list(list, &len, plain_text);
4754 gtk_selection_data_set (selection_data, selection_data->target,
4761 gtk_selection_data_set (selection_data, selection_data->target,
4766 static void pan_window_dnd_init(PanWindow *pw)
4772 gtk_drag_source_set(imd->image, GDK_BUTTON2_MASK,
4773 dnd_file_drag_types, dnd_file_drag_types_count,
4774 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4775 g_signal_connect(G_OBJECT(imd->image), "drag_data_get",
4776 G_CALLBACK(pan_window_set_dnd_data), pw);
4778 gtk_drag_dest_set(imd->image,
4779 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4780 dnd_file_drop_types, dnd_file_drop_types_count,
4781 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4782 g_signal_connect(G_OBJECT(imd->image), "drag_data_received",
4783 G_CALLBACK(pan_window_get_dnd_data), pw);
4787 *-----------------------------------------------------------------------------
4788 * maintenance (for rename, move, remove)
4789 *-----------------------------------------------------------------------------