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 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->pr);
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)
1282 if ((pi->type == type || type == ITEM_NONE) &&
1283 x >= pi->x && x < pi->x + pi->width &&
1284 y >= pi->y && y < pi->y + pi->height &&
1285 (!key || (pi->key && strcmp(pi->key, key) == 0)))
1296 *-----------------------------------------------------------------------------
1298 *-----------------------------------------------------------------------------
1301 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend)
1303 GList *flist = NULL;
1304 GList *dlist = NULL;
1308 filelist_read(path, &flist, &dlist);
1309 if (sort != SORT_NONE)
1311 flist = filelist_sort(flist, sort, ascend);
1312 dlist = filelist_sort(dlist, sort, ascend);
1322 folders = g_list_remove(folders, fd);
1324 if (filelist_read(fd->path, &flist, &dlist))
1326 if (sort != SORT_NONE)
1328 flist = filelist_sort(flist, sort, ascend);
1329 dlist = filelist_sort(dlist, sort, ascend);
1332 result = g_list_concat(result, flist);
1333 folders = g_list_concat(dlist, folders);
1342 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1350 list = pan_window_layout_list(path, SORT_NAME, TRUE);
1352 grid_size = (gint)sqrt((double)g_list_length(list));
1353 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1355 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1359 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1364 *width = PAN_FOLDER_BOX_BORDER * 2;
1365 *height = PAN_FOLDER_BOX_BORDER * 2;
1378 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1380 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1382 x += pi->width + PAN_THUMB_GAP;
1383 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1392 pi = pan_item_new_thumb(pw, fd, x, y);
1394 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1398 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1401 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1407 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1410 gint x1, y1, x2, y2;
1425 if (x1 > pi->x) x1 = pi->x;
1426 if (y1 > pi->y) y1 = pi->y;
1427 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1428 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1431 x1 -= PAN_FOLDER_BOX_BORDER;
1432 y1 -= PAN_FOLDER_BOX_BORDER;
1433 x2 += PAN_FOLDER_BOX_BORDER;
1434 y2 += PAN_FOLDER_BOX_BORDER;
1447 if (pi->type == ITEM_TRIANGLE && pi->data)
1461 if (width) *width = x2 - x1;
1462 if (height) *height = y2 - y1;
1465 typedef struct _FlowerGroup FlowerGroup;
1466 struct _FlowerGroup {
1479 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1483 work = group->items;
1501 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1502 gint *result_x, gint *result_y)
1508 radius = parent->circumference / (2*PI);
1509 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1511 a = 2*PI * group->diameter / parent->circumference;
1513 x = (gint)((double)radius * cos(parent->angle + a / 2));
1514 y = (gint)((double)radius * sin(parent->angle + a / 2));
1521 x += parent->width / 2;
1522 y += parent->height / 2;
1524 x -= group->width / 2;
1525 y -= group->height / 2;
1531 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1538 if (parent && parent->children)
1540 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1548 pan_window_layout_compute_folder_flower_move(group, x, y);
1553 gint px, py, gx, gy;
1554 gint x1, y1, x2, y2;
1556 px = parent->x + parent->width / 2;
1557 py = parent->y + parent->height / 2;
1559 gx = group->x + group->width / 2;
1560 gy = group->y + group->height / 2;
1565 x2 = MAX(px, gx + 5);
1566 y2 = MAX(py, gy + 5);
1568 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1569 px, py, gx, gy, gx + 5, gy + 5,
1571 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1575 pw->list = g_list_concat(group->items, pw->list);
1576 group->items = NULL;
1578 group->circumference = 0;
1579 work = group->children;
1587 group->circumference += child->diameter;
1590 work = g_list_last(group->children);
1598 pan_window_layout_compute_folder_flower_build(pw, child, group);
1601 g_list_free(group->children);
1605 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1618 if (!filelist_read(path, &f, &d)) return NULL;
1619 if (!f && !d) return NULL;
1621 f = filelist_sort(f, SORT_NAME, TRUE);
1622 d = filelist_sort(d, SORT_NAME, TRUE);
1624 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1625 PAN_TEXT_COLOR, 255);
1627 y += pi_box->height;
1629 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1631 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1632 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1633 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1634 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1636 x += PAN_FOLDER_BOX_BORDER;
1637 y += PAN_FOLDER_BOX_BORDER;
1639 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1653 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1655 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1656 x += pi->width + PAN_THUMB_GAP;
1657 if (pi->height > y_height) y_height = pi->height;
1661 pi = pan_item_new_thumb(pw, fd, x, y);
1662 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1663 y_height = PAN_THUMB_SIZE;
1667 if (grid_count >= grid_size)
1671 y += y_height + PAN_THUMB_GAP;
1675 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1680 group = g_new0(FlowerGroup, 1);
1681 group->items = pw->list;
1684 group->width = pi_box->width;
1685 group->height = pi_box->y + pi_box->height;
1686 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1688 group->children = NULL;
1699 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1700 if (child) group->children = g_list_prepend(group->children, child);
1708 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1709 gint *width, gint *height,
1710 gint *scroll_x, gint *scroll_y)
1715 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1716 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1718 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1720 list = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1723 PanItem *pi = list->data;
1724 *scroll_x = pi->x + pi->width / 2;
1725 *scroll_y = pi->y + pi->height / 2;
1730 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1731 gint *x, gint *y, gint *level,
1733 gint *width, gint *height)
1741 if (!filelist_read(path, &f, &d)) return;
1742 if (!f && !d) return;
1744 f = filelist_sort(f, SORT_NAME, TRUE);
1745 d = filelist_sort(d, SORT_NAME, TRUE);
1747 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1749 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1750 PAN_TEXT_COLOR, 255);
1752 *y += pi_box->height;
1754 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1756 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1757 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1758 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1759 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1761 *x += PAN_FOLDER_BOX_BORDER;
1762 *y += PAN_FOLDER_BOX_BORDER;
1773 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1775 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1776 *x += pi->width + PAN_THUMB_GAP;
1777 if (pi->height > y_height) y_height = pi->height;
1781 pi = pan_item_new_thumb(pw, fd, *x, *y);
1782 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1783 y_height = PAN_THUMB_SIZE;
1786 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1789 if (f) *y = pi_box->y + pi_box->height;
1801 *level = *level + 1;
1802 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1803 pi_box, width, height);
1804 *level = *level - 1;
1809 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1811 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1812 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1814 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1817 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1824 x = PAN_FOLDER_BOX_BORDER;
1825 y = PAN_FOLDER_BOX_BORDER;
1826 w = PAN_FOLDER_BOX_BORDER * 2;
1827 h = PAN_FOLDER_BOX_BORDER * 2;
1829 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1831 if (width) *width = w;
1832 if (height) *height = h;
1836 *-----------------------------------------------------------------------------
1838 *-----------------------------------------------------------------------------
1841 #define PAN_CAL_DAY_WIDTH 100
1842 #define PAN_CAL_DAY_HEIGHT 80
1843 #define PAN_CAL_DOT_SIZE 3
1844 #define PAN_CAL_DOT_GAP 2
1845 #define PAN_CAL_DOT_COLOR 0, 0, 0
1846 #define PAN_CAL_DOT_ALPHA 32
1848 static void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
1854 gint x1, y1, x2, y2, x3, y3;
1859 while ((pi = pan_item_find_by_key(pw, ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
1861 if (!pi_day || pi_day->type != ITEM_BOX ||
1862 !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
1864 list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
1876 if (dot->type != ITEM_BOX || !dot->fd ||
1877 !dot->key || strcmp(dot->key, "dot") != 0)
1879 list = g_list_delete_link(list, node);
1887 grid = (gint)(sqrt(g_list_length(list)) + 0.5);
1889 x = pi_day->x + pi_day->width + 4;
1893 if (y + grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4 > pw->pr->image_height)
1895 y = pw->pr->image_height - (grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4);
1899 pbox = pan_item_new_box(pw, NULL, x, y, PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1901 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
1902 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
1903 pan_item_set_key(pbox, "day_bubble");
1910 buf = date_value_string(pi_day->fd->date, DATE_LENGTH_WEEK);
1911 plabel = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1912 PAN_POPUP_TEXT_COLOR, 255);
1913 pan_item_set_key(plabel, "day_bubble");
1916 pan_item_size_by_item(pbox, plabel, 0);
1918 y += plabel->height;
1925 x += PAN_FOLDER_BOX_BORDER;
1926 y += PAN_FOLDER_BOX_BORDER;
1940 pimg = pan_item_new_thumb(pw, file_data_new_simple(dot->fd->path), x, y);
1941 pan_item_set_key(pimg, "day_bubble");
1943 pan_item_size_by_item(pbox, pimg, PAN_FOLDER_BOX_BORDER);
1948 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1953 x = pbox->x + PAN_FOLDER_BOX_BORDER;
1954 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1960 x1 = pi_day->x + pi_day->width - 8;
1963 y2 = pbox->y + MIN(42, pbox->height);
1965 y3 = MAX(pbox->y, y2 - 30);
1966 triangle_rect_region(x1, y1, x2, y2, x3, y3,
1969 pi = pan_item_new_tri(pw, NULL, x, y, w, h,
1970 x1, y1, x2, y2, x3, y3,
1971 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
1972 pan_item_tri_border(pi, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
1973 pan_item_set_key(pi, "day_bubble");
1974 pan_item_added(pw, pi);
1976 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
1977 pan_item_added(pw, pbox);
1980 static void pan_window_layout_compute_calendar(PanWindow *pw, const gchar *path, gint *width, gint *height)
1996 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
1998 list = pan_window_layout_list(path, SORT_NONE, TRUE);
1999 list = filelist_sort(list, SORT_TIME, TRUE);
2012 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2020 if (day_max < count) day_max = count;
2024 printf("biggest day contains %d images\n", day_max);
2026 grid = (gint)(sqrt((double)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
2027 day_width = MAX(PAN_CAL_DAY_WIDTH, grid);
2028 day_height = MAX(PAN_CAL_DAY_HEIGHT, grid);
2032 FileData *fd = list->data;
2034 year = date_value(fd->date, DATE_LENGTH_YEAR);
2035 month = date_value(fd->date, DATE_LENGTH_MONTH);
2038 work = g_list_last(list);
2041 FileData *fd = work->data;
2042 end_year = date_value(fd->date, DATE_LENGTH_YEAR);
2043 end_month = date_value(fd->date, DATE_LENGTH_MONTH);
2046 *width = PAN_FOLDER_BOX_BORDER * 2;
2047 *height = PAN_FOLDER_BOX_BORDER * 2;
2049 x = PAN_FOLDER_BOX_BORDER;
2050 y = PAN_FOLDER_BOX_BORDER;
2053 while (work && (year < end_year || (year == end_year && month <= end_month)))
2064 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
2066 days = date_value(dt, DATE_LENGTH_DAY);
2067 dt = date_to_time(year, month, 1);
2068 col = date_value(dt, DATE_LENGTH_WEEK);
2071 x = PAN_FOLDER_BOX_BORDER;
2073 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
2074 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2075 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2076 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2077 buf = date_value_string(dt, DATE_LENGTH_MONTH);
2078 pi_text = pan_item_new_text(pw, x, y, buf,
2079 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2080 PAN_TEXT_COLOR, 255);
2082 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
2084 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
2086 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
2087 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
2089 for (day = 1; day <= days; day++)
2096 dt = date_to_time(year, month, day);
2098 fd = g_new0(FileData, 1);
2099 /* path and name must be non NULL, so make them an invalid filename */
2100 fd->path = g_strdup("//");
2103 pi_day = pan_item_new_box(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
2104 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2105 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2106 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2107 pan_item_set_key(pi_day, "day");
2109 dx = x + PAN_CAL_DOT_GAP * 2;
2110 dy = y + PAN_CAL_DOT_GAP * 2;
2112 fd = (work) ? work->data : NULL;
2113 while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
2117 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
2119 PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
2121 pan_item_set_key(pi, "dot");
2123 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2124 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
2126 dx = x + PAN_CAL_DOT_GAP * 2;
2127 dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2129 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
2131 /* must keep all dots within respective day even if it gets ugly */
2132 dy = y + PAN_CAL_DOT_GAP * 2;
2135 pi_day->color_a = MIN(PAN_FOLDER_BOX_ALPHA + 64 + n, 255);
2139 fd = (work) ? work->data : NULL;
2146 buf = g_strdup_printf("( %d )", n);
2147 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2148 PAN_TEXT_COLOR, 255);
2151 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
2152 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
2155 buf = g_strdup_printf("%d", day);
2156 pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2157 PAN_TEXT_COLOR, 255);
2161 pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
2168 x = PAN_FOLDER_BOX_BORDER;
2169 y += PAN_CAL_DAY_HEIGHT;
2173 x += PAN_CAL_DAY_WIDTH;
2177 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
2178 y += PAN_FOLDER_BOX_BORDER * 2;
2189 *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
2194 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
2202 PanItem *pi_month = NULL;
2203 PanItem *pi_day = NULL;
2209 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2211 list = pan_window_layout_list(path, SORT_NONE, TRUE);
2212 list = filelist_sort(list, SORT_TIME, TRUE);
2214 *width = PAN_FOLDER_BOX_BORDER * 2;
2215 *height = PAN_FOLDER_BOX_BORDER * 2;
2220 day_start = month_start;
2235 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2240 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
2246 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
2250 x = PAN_FOLDER_BOX_BORDER;
2253 y = PAN_FOLDER_BOX_BORDER;
2255 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
2256 pi = pan_item_new_text(pw, x, y, buf,
2257 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2258 PAN_TEXT_COLOR, 255);
2261 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
2263 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2264 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2265 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2267 x += PAN_FOLDER_BOX_BORDER;
2268 y += PAN_FOLDER_BOX_BORDER;
2272 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
2284 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
2286 needle = needle->next;
2295 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
2296 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2297 PAN_TEXT_COLOR, 255);
2302 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
2303 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2304 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2305 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2307 x += PAN_FOLDER_BOX_BORDER;
2308 y += PAN_FOLDER_BOX_BORDER;
2312 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2314 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
2315 if (pi->width > x_width) x_width = pi->width;
2316 y_height = pi->height;
2320 pi = pan_item_new_thumb(pw, fd, x, y);
2321 x_width = PAN_THUMB_SIZE;
2322 y_height = PAN_THUMB_SIZE;
2325 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
2326 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
2331 if (total > 0 && count < PAN_GROUP_MAX)
2333 y += y_height + PAN_THUMB_GAP;
2337 x += x_width + PAN_THUMB_GAP;
2347 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
2353 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
2354 gint *width, gint *height,
2355 gint *scroll_x, gint *scroll_y)
2357 pan_window_items_free(pw);
2361 case LAYOUT_SIZE_THUMB_DOTS:
2362 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2363 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2365 case LAYOUT_SIZE_THUMB_NONE:
2366 pw->thumb_size = PAN_THUMB_SIZE_NONE;
2367 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2369 case LAYOUT_SIZE_THUMB_SMALL:
2370 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2371 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2373 case LAYOUT_SIZE_THUMB_NORMAL:
2375 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2376 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2378 case LAYOUT_SIZE_THUMB_LARGE:
2379 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2380 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2382 case LAYOUT_SIZE_10:
2383 pw->image_size = 10;
2384 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2386 case LAYOUT_SIZE_25:
2387 pw->image_size = 25;
2388 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2390 case LAYOUT_SIZE_33:
2391 pw->image_size = 33;
2392 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2394 case LAYOUT_SIZE_50:
2395 pw->image_size = 50;
2396 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2398 case LAYOUT_SIZE_100:
2399 pw->image_size = 100;
2400 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2413 pan_window_layout_compute_grid(pw, path, width, height);
2415 case LAYOUT_FOLDERS_LINEAR:
2416 pan_window_layout_compute_folders_linear(pw, path, width, height);
2418 case LAYOUT_FOLDERS_FLOWER:
2419 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2421 case LAYOUT_CALENDAR:
2422 pan_window_layout_compute_calendar(pw, path, width, height);
2424 case LAYOUT_TIMELINE:
2425 pan_window_layout_compute_timeline(pw, path, width, height);
2431 printf("computed %d objects\n", g_list_length(pw->list));
2434 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2447 if (util_clip_region_test(x, y, width, height,
2448 pi->x, pi->y, pi->width, pi->height))
2450 list = g_list_prepend(list, pi);
2460 *-----------------------------------------------------------------------------
2462 *-----------------------------------------------------------------------------
2465 static gint pan_layout_queue_step(PanWindow *pw);
2468 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2470 PanWindow *pw = data;
2478 pw->queue_pi = NULL;
2482 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2483 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2486 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2490 thumb_loader_free(pw->tl);
2493 while (pan_layout_queue_step(pw));
2496 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2498 PanWindow *pw = data;
2506 pw->queue_pi = NULL;
2510 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2511 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2512 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2514 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2515 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2516 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2521 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2522 (GdkInterpType)zoom_quality);
2523 g_object_unref(tmp);
2527 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2531 image_loader_free(pw->il);
2534 while (pan_layout_queue_step(pw));
2538 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2539 guint width, guint height, gpointer data)
2541 PanWindow *pw = data;
2552 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2553 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2557 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2563 static gint pan_layout_queue_step(PanWindow *pw)
2567 if (!pw->queue) return FALSE;
2569 pi = pw->queue->data;
2570 pw->queue = g_list_remove(pw->queue, pi);
2573 if (!pw->queue_pi->fd)
2575 pw->queue_pi->queued = FALSE;
2576 pw->queue_pi = NULL;
2580 image_loader_free(pw->il);
2582 thumb_loader_free(pw->tl);
2585 if (pi->type == ITEM_IMAGE)
2587 pw->il = image_loader_new(pi->fd->path);
2589 if (pw->size != LAYOUT_SIZE_100)
2591 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2595 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2597 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2599 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2601 image_loader_free(pw->il);
2604 else if (pi->type == ITEM_THUMB)
2606 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2608 if (!pw->tl->standard_loader)
2610 /* The classic loader will recreate a thumbnail any time we
2611 * request a different size than what exists. This view will
2612 * almost never use the user configured sizes so disable cache.
2614 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2617 thumb_loader_set_callbacks(pw->tl,
2618 pan_layout_queue_thumb_done_cb,
2619 pan_layout_queue_thumb_done_cb,
2622 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2624 thumb_loader_free(pw->tl);
2628 pw->queue_pi->queued = FALSE;
2629 pw->queue_pi = NULL;
2633 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2635 if (!pi || pi->queued || pi->pixbuf) return;
2636 if (pw->size <= LAYOUT_SIZE_THUMB_NONE) return;
2639 pw->queue = g_list_prepend(pw->queue, pi);
2641 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2644 static gint pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
2645 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2647 PanWindow *pw = data;
2652 pixbuf_draw_rect_fill(pixbuf,
2653 0, 0, width, height,
2654 PAN_BACKGROUND_COLOR, 255);
2656 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2658 gint rx, ry, rw, rh;
2660 if (util_clip_region(x, y, width, height,
2662 &rx, &ry, &rw, &rh))
2664 pixbuf_draw_rect_fill(pixbuf,
2665 rx - x, ry - y, rw, rh,
2666 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2669 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2671 gint rx, ry, rw, rh;
2673 if (util_clip_region(x, y, width, height,
2675 &rx, &ry, &rw, &rh))
2677 pixbuf_draw_rect_fill(pixbuf,
2678 rx - x, ry - y, rw, rh,
2679 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2683 list = pan_layout_intersect(pw, x, y, width, height);
2688 gint tx, ty, tw, th;
2689 gint rx, ry, rw, rh;
2696 if (pi->type == ITEM_THUMB && pi->pixbuf)
2698 tw = gdk_pixbuf_get_width(pi->pixbuf);
2699 th = gdk_pixbuf_get_height(pi->pixbuf);
2701 tx = pi->x + (pi->width - tw) / 2;
2702 ty = pi->y + (pi->height - th) / 2;
2704 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2706 if (util_clip_region(x, y, width, height,
2707 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2708 &rx, &ry, &rw, &rh))
2710 pixbuf_draw_shadow(pixbuf,
2711 rx - x, ry - y, rw, rh,
2712 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2714 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2719 if (util_clip_region(x, y, width, height,
2720 tx + tw, ty + PAN_SHADOW_OFFSET,
2721 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2722 &rx, &ry, &rw, &rh))
2724 pixbuf_draw_shadow(pixbuf,
2725 rx - x, ry - y, rw, rh,
2726 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2728 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2730 if (util_clip_region(x, y, width, height,
2731 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2732 &rx, &ry, &rw, &rh))
2734 pixbuf_draw_shadow(pixbuf,
2735 rx - x, ry - y, rw, rh,
2736 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2738 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2742 if (util_clip_region(x, y, width, height,
2744 &rx, &ry, &rw, &rh))
2746 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2749 1.0, 1.0, GDK_INTERP_NEAREST,
2753 if (util_clip_region(x, y, width, height,
2754 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2755 &rx, &ry, &rw, &rh))
2757 pixbuf_draw_rect_fill(pixbuf,
2758 rx - x, ry - y, rw, rh,
2759 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2761 if (util_clip_region(x, y, width, height,
2762 tx, ty, PAN_OUTLINE_THICKNESS, th,
2763 &rx, &ry, &rw, &rh))
2765 pixbuf_draw_rect_fill(pixbuf,
2766 rx - x, ry - y, rw, rh,
2767 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2769 if (util_clip_region(x, y, width, height,
2770 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2771 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2772 &rx, &ry, &rw, &rh))
2774 pixbuf_draw_rect_fill(pixbuf,
2775 rx - x, ry - y, rw, rh,
2776 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2778 if (util_clip_region(x, y, width, height,
2779 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2780 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2781 &rx, &ry, &rw, &rh))
2783 pixbuf_draw_rect_fill(pixbuf,
2784 rx - x, ry - y, rw, rh,
2785 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2788 else if (pi->type == ITEM_THUMB)
2790 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2791 th = pi->height - PAN_SHADOW_OFFSET * 2;
2792 tx = pi->x + PAN_SHADOW_OFFSET;
2793 ty = pi->y + PAN_SHADOW_OFFSET;
2795 if (util_clip_region(x, y, width, height,
2797 &rx, &ry, &rw, &rh))
2801 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2802 pixbuf_draw_rect_fill(pixbuf,
2803 rx - x, ry - y, rw, rh,
2805 PAN_SHADOW_ALPHA / d);
2808 pan_layout_queue(pw, pi);
2810 else if (pi->type == ITEM_IMAGE)
2812 if (util_clip_region(x, y, width, height,
2813 pi->x, pi->y, pi->width, pi->height,
2814 &rx, &ry, &rw, &rh))
2818 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2821 1.0, 1.0, GDK_INTERP_NEAREST,
2826 pixbuf_draw_rect_fill(pixbuf,
2827 rx - x, ry - y, rw, rh,
2828 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2829 pan_layout_queue(pw, pi);
2833 else if (pi->type == ITEM_BOX)
2847 if (pi->color_a > 254)
2849 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2850 shadow[0], bh - shadow[0],
2851 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2853 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2854 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2856 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2858 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2863 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2864 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2866 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2868 PAN_SHADOW_COLOR, a);
2872 if (util_clip_region(x, y, width, height,
2873 pi->x, pi->y, bw, bh,
2874 &rx, &ry, &rw, &rh))
2876 pixbuf_draw_rect_fill(pixbuf,
2877 rx - x, ry - y, rw, rh,
2878 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2880 if (util_clip_region(x, y, width, height,
2881 pi->x, pi->y, bw, pi->border,
2882 &rx, &ry, &rw, &rh))
2884 pixbuf_draw_rect_fill(pixbuf,
2885 rx - x, ry - y, rw, rh,
2886 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2888 if (util_clip_region(x, y, width, height,
2889 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
2890 &rx, &ry, &rw, &rh))
2892 pixbuf_draw_rect_fill(pixbuf,
2893 rx - x, ry - y, rw, rh,
2894 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2896 if (util_clip_region(x, y, width, height,
2897 pi->x + bw - pi->border, pi->y + pi->border,
2898 pi->border, bh - pi->border * 2,
2899 &rx, &ry, &rw, &rh))
2901 pixbuf_draw_rect_fill(pixbuf,
2902 rx - x, ry - y, rw, rh,
2903 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2905 if (util_clip_region(x, y, width, height,
2906 pi->x, pi->y + bh - pi->border,
2908 &rx, &ry, &rw, &rh))
2910 pixbuf_draw_rect_fill(pixbuf,
2911 rx - x, ry - y, rw, rh,
2912 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2915 else if (pi->type == ITEM_TRIANGLE)
2917 if (util_clip_region(x, y, width, height,
2918 pi->x, pi->y, pi->width, pi->height,
2919 &rx, &ry, &rw, &rh) && pi->data)
2921 gint *coord = pi->data;
2922 pixbuf_draw_triangle(pixbuf,
2923 rx - x, ry - y, rw, rh,
2924 coord[0] - x, coord[1] - y,
2925 coord[2] - x, coord[3] - y,
2926 coord[4] - x, coord[5] - y,
2927 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2929 if (pi->border & BORDER_1)
2931 pixbuf_draw_line(pixbuf,
2932 rx - x, ry - y, rw, rh,
2933 coord[0] - x, coord[1] - y,
2934 coord[2] - x, coord[3] - y,
2935 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2937 if (pi->border & BORDER_2)
2939 pixbuf_draw_line(pixbuf,
2940 rx - x, ry - y, rw, rh,
2941 coord[2] - x, coord[3] - y,
2942 coord[4] - x, coord[5] - y,
2943 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2945 if (pi->border & BORDER_3)
2947 pixbuf_draw_line(pixbuf,
2948 rx - x, ry - y, rw, rh,
2949 coord[4] - x, coord[5] - y,
2950 coord[0] - x, coord[1] - y,
2951 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2955 else if (pi->type == ITEM_TEXT && pi->text)
2957 PangoLayout *layout;
2959 layout = pan_item_text_layout(pi, (GtkWidget *)pr);
2960 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
2961 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
2962 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2963 g_object_unref(G_OBJECT(layout));
2969 if (x%512 == 0 && y%512 == 0)
2971 PangoLayout *layout;
2974 layout = gtk_widget_create_pango_layout((GtkWidget *)pr, NULL);
2976 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
2977 (x / pr->source_tile_width) +
2978 (y / pr->source_tile_height * (pr->image_width/pr->source_tile_width + 1)));
2979 pango_layout_set_text(layout, buf, -1);
2982 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr, 0, 0, 0, 0, 0, 255);
2984 g_object_unref(G_OBJECT(layout));
2991 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
2992 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2994 PanWindow *pw = data;
2998 list = pan_layout_intersect(pw, x, y, width, height);
3007 if (pi->refcount > 0)
3011 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
3016 pw->queue = g_list_remove(pw->queue, pi);
3019 if (pw->queue_pi == pi) pw->queue_pi = NULL;
3022 g_object_unref(pi->pixbuf);
3034 *-----------------------------------------------------------------------------
3036 *-----------------------------------------------------------------------------
3039 static void pan_window_message(PanWindow *pw, const gchar *text)
3049 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
3054 if (pw->layout == LAYOUT_CALENDAR)
3064 pi->type == ITEM_BOX &&
3065 pi->key && strcmp(pi->key, "dot") == 0)
3067 size += pi->fd->size;
3082 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
3084 size += pi->fd->size;
3090 ss = text_from_size_abrev(size);
3091 buf = g_strdup_printf(_("%d images, %s"), count, ss);
3093 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
3097 static void pan_window_zoom_limit(PanWindow *pw)
3103 case LAYOUT_SIZE_THUMB_DOTS:
3104 case LAYOUT_SIZE_THUMB_NONE:
3105 case LAYOUT_SIZE_THUMB_SMALL:
3106 case LAYOUT_SIZE_THUMB_NORMAL:
3108 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
3112 case LAYOUT_SIZE_THUMB_LARGE:
3115 case LAYOUT_SIZE_10:
3116 case LAYOUT_SIZE_25:
3119 case LAYOUT_SIZE_33:
3120 case LAYOUT_SIZE_50:
3121 case LAYOUT_SIZE_100:
3127 image_zoom_set_limits(pw->imd, min, 32.0);
3130 static gint pan_window_layout_update_idle_cb(gpointer data)
3132 PanWindow *pw = data;
3138 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
3140 if (!pw->cache_list && !pw->cache_todo)
3142 pan_cache_fill(pw, pw->path);
3145 pan_window_message(pw, _("Reading dimensions..."));
3149 if (pan_cache_step(pw))
3153 if (pw->cache_count == pw->cache_total)
3155 pan_window_message(pw, _("Sorting images..."));
3157 else if (pw->cache_tick > 9)
3161 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
3162 pw->cache_total - pw->cache_count);
3163 pan_window_message(pw, buf);
3173 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3175 pan_window_zoom_limit(pw);
3177 if (width > 0 && height > 0)
3181 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
3182 PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
3183 pan_window_request_tile_cb,
3184 pan_window_dispose_tile_cb, pw, 1.0);
3186 if (scroll_x == 0 && scroll_y == 0)
3194 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
3197 pan_window_message(pw, NULL);
3204 static void pan_window_layout_update_idle(PanWindow *pw)
3206 if (pw->idle_id == -1)
3208 pan_window_message(pw, _("Sorting images..."));
3209 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3214 *-----------------------------------------------------------------------------
3215 * pan window keyboard
3216 *-----------------------------------------------------------------------------
3219 static const gchar *pan_menu_click_path(PanWindow *pw)
3221 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3225 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3227 PanWindow *pw = data;
3229 gdk_window_get_origin(pw->imd->pr->window, x, y);
3230 popup_menu_position_clamp(menu, x, y, 0);
3233 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3235 PanWindow *pw = data;
3238 gint stop_signal = FALSE;
3244 pr = PIXBUF_RENDERER(pw->imd->pr);
3245 path = pan_menu_click_path(pw);
3247 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pw->imd->widget)));
3251 switch (event->keyval)
3253 case GDK_Left: case GDK_KP_Left:
3257 case GDK_Right: case GDK_KP_Right:
3261 case GDK_Up: case GDK_KP_Up:
3265 case GDK_Down: case GDK_KP_Down:
3269 case GDK_Page_Up: case GDK_KP_Page_Up:
3270 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
3272 case GDK_Page_Down: case GDK_KP_Page_Down:
3273 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
3275 case GDK_Home: case GDK_KP_Home:
3276 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
3278 case GDK_End: case GDK_KP_End:
3279 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
3284 if (focused && !(event->state & GDK_CONTROL_MASK) )
3285 switch (event->keyval)
3287 case '+': case '=': case GDK_KP_Add:
3288 pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
3290 case '-': case GDK_KP_Subtract:
3291 pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
3293 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3294 pixbuf_renderer_zoom_set(pr, 1.0);
3297 pixbuf_renderer_zoom_set(pr, 2.0);
3300 pixbuf_renderer_zoom_set(pr, 3.0);
3303 pixbuf_renderer_zoom_set(pr, 4.0);
3306 pixbuf_renderer_zoom_set(pr, -4.0);
3309 pixbuf_renderer_zoom_set(pr, -3.0);
3312 pixbuf_renderer_zoom_set(pr, -2.0);
3316 pan_fullscreen_toggle(pw, FALSE);
3321 pan_overlay_toggle(pw);
3324 case GDK_Delete: case GDK_KP_Delete:
3329 if (GTK_WIDGET_VISIBLE(pw->search_box))
3331 gtk_widget_grab_focus(pw->search_entry);
3335 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3343 pan_fullscreen_toggle(pw, TRUE);
3346 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3348 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3354 menu = pan_popup_menu(pw);
3355 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3360 if (event->state & GDK_CONTROL_MASK)
3363 switch (event->keyval)
3396 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3399 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3402 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3405 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3408 if (path) info_window_new(path, NULL);
3411 pan_window_close(pw);
3414 if (n != -1 && path)
3416 pan_fullscreen_toggle(pw, TRUE);
3417 start_editor_from_file(n, path);
3421 else if (event->state & GDK_SHIFT_MASK)
3428 switch (event->keyval)
3433 pan_fullscreen_toggle(pw, TRUE);
3436 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3438 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
3439 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3448 if (x != 0 || y!= 0)
3450 keyboard_scroll_calc(&x, &y, event);
3451 pixbuf_renderer_scroll(pr, x, y);
3458 *-----------------------------------------------------------------------------
3460 *-----------------------------------------------------------------------------
3463 static void pan_info_update(PanWindow *pw, PanItem *pi)
3469 gint x1, y1, x2, y2, x3, y3;
3472 if (pw->click_pi == pi) return;
3473 if (pi && !pi->fd) pi = NULL;
3475 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3480 printf("info set to %s\n", pi->fd->path);
3482 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3484 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3485 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3486 pan_item_set_key(pbox, "info");
3488 if (pi->type == ITEM_THUMB && pi->pixbuf)
3490 w = gdk_pixbuf_get_width(pi->pixbuf);
3491 h = gdk_pixbuf_get_height(pi->pixbuf);
3493 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3494 y1 = pi->y + (pi->height - h) / 2 + 8;
3498 x1 = pi->x + pi->width - 8;
3506 triangle_rect_region(x1, y1, x2, y2, x3, y3,
3509 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3510 x1, y1, x2, y2, x3, y3,
3511 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3512 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3513 pan_item_set_key(p, "info");
3514 pan_item_added(pw, p);
3516 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3517 _("Filename:"), TEXT_ATTR_BOLD,
3518 PAN_POPUP_TEXT_COLOR, 255);
3519 pan_item_set_key(plabel, "info");
3520 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3521 pi->fd->name, TEXT_ATTR_NONE,
3522 PAN_POPUP_TEXT_COLOR, 255);
3523 pan_item_set_key(p, "info");
3524 pan_item_size_by_item(pbox, p, 0);
3526 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3527 _("Date:"), TEXT_ATTR_BOLD,
3528 PAN_POPUP_TEXT_COLOR, 255);
3529 pan_item_set_key(plabel, "info");
3530 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3531 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3532 PAN_POPUP_TEXT_COLOR, 255);
3533 pan_item_set_key(p, "info");
3534 pan_item_size_by_item(pbox, p, 0);
3536 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3537 _("Size:"), TEXT_ATTR_BOLD,
3538 PAN_POPUP_TEXT_COLOR, 255);
3539 pan_item_set_key(plabel, "info");
3540 buf = text_from_size(pi->fd->size);
3541 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3542 buf, TEXT_ATTR_NONE,
3543 PAN_POPUP_TEXT_COLOR, 255);
3545 pan_item_set_key(p, "info");
3546 pan_item_size_by_item(pbox, p, 0);
3548 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3549 pan_item_added(pw, pbox);
3554 *-----------------------------------------------------------------------------
3556 *-----------------------------------------------------------------------------
3559 static void pan_search_status(PanWindow *pw, const gchar *text)
3561 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3564 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3572 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3574 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3575 if (!list) return FALSE;
3577 found = g_list_find(list, pw->click_pi);
3578 if (found && found->next)
3580 found = found->next;
3588 pan_info_update(pw, pi);
3589 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3591 buf = g_strdup_printf("%s ( %d / %d )",
3592 (path[0] == '/') ? _("path found") : _("filename found"),
3593 g_list_index(list, pi) + 1,
3594 g_list_length(list));
3595 pan_search_status(pw, buf);
3603 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3611 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3613 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3614 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3619 needle = g_utf8_strdown(text, -1);
3620 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3623 if (!list) return FALSE;
3625 found = g_list_find(list, pw->click_pi);
3626 if (found && found->next)
3628 found = found->next;
3636 pan_info_update(pw, pi);
3637 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3639 buf = g_strdup_printf("%s ( %d / %d )",
3641 g_list_index(list, pi) + 1,
3642 g_list_length(list));
3643 pan_search_status(pw, buf);
3651 static gint valid_date_separator(gchar c)
3653 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3656 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
3657 gint year, gint month, gint day,
3663 work = g_list_last(pw->list);
3671 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
3672 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
3676 tl = localtime(&pi->fd->date);
3681 match = (tl->tm_year == year - 1900);
3682 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3683 if (match && day > 0) match = (tl->tm_mday == day);
3685 if (match) list = g_list_prepend(list, pi);
3690 return g_list_reverse(list);
3693 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3709 if (!text) return FALSE;
3711 ptr = (gchar *)text;
3712 while (*ptr != '\0')
3714 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3719 if (t == -1) return FALSE;
3721 if (!lt) return FALSE;
3723 if (valid_date_separator(*text))
3726 mptr = (gchar *)text;
3730 year = (gint)strtol(text, &mptr, 10);
3731 if (mptr == text) return FALSE;
3734 if (*mptr != '\0' && valid_date_separator(*mptr))
3739 month = strtol(mptr, &dptr, 10);
3742 if (valid_date_separator(*dptr))
3744 month = lt->tm_mon + 1;
3752 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3756 day = strtol(dptr, &eptr, 10);
3766 year = lt->tm_year + 1900;
3768 else if (year < 100)
3777 month < -1 || month == 0 || month > 12 ||
3778 day < -1 || day == 0 || day > 31) return FALSE;
3780 t = date_to_time(year, month, day);
3781 if (t < 0) return FALSE;
3783 if (pw->layout == LAYOUT_CALENDAR)
3785 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
3791 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3792 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
3797 found = g_list_find(list, pw->search_pi);
3798 if (found && found->next)
3800 found = found->next;
3811 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
3813 pan_info_update(pw, NULL);
3814 pan_calendar_update(pw, pi);
3815 image_scroll_to_point(pw->imd,
3816 pi->x + pi->width / 2,
3817 pi->y + pi->height / 2, 0.5, 0.5);
3821 pan_info_update(pw, pi);
3822 image_scroll_to_point(pw->imd,
3823 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3829 buf = date_value_string(t, DATE_LENGTH_MONTH);
3834 buf = g_strdup_printf("%d %s", day, tmp);
3840 buf = date_value_string(t, DATE_LENGTH_YEAR);
3845 buf_count = g_strdup_printf("( %d / %d )",
3846 g_list_index(list, pi) + 1,
3847 g_list_length(list));
3851 buf_count = g_strdup_printf("(%s)", _("no match"));
3854 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
3857 pan_search_status(pw, message);
3865 static void pan_search_activate_cb(const gchar *text, gpointer data)
3867 PanWindow *pw = data;
3871 tab_completion_append_to_history(pw->search_entry, text);
3873 if (pan_search_by_path(pw, text)) return;
3875 if ((pw->layout == LAYOUT_TIMELINE ||
3876 pw->layout == LAYOUT_CALENDAR) &&
3877 pan_search_by_date(pw, text))
3882 if (pan_search_by_partial(pw, text)) return;
3884 pan_search_status(pw, _("no match"));
3887 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3889 PanWindow *pw = data;
3892 visible = GTK_WIDGET_VISIBLE(pw->search_box);
3893 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3897 gtk_widget_hide(pw->search_box);
3898 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3902 gtk_widget_show(pw->search_box);
3903 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3904 gtk_widget_grab_focus(pw->search_entry);
3910 *-----------------------------------------------------------------------------
3911 * view window main routines
3912 *-----------------------------------------------------------------------------
3915 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
3917 PanWindow *pw = data;
3925 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
3926 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
3929 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3932 switch (event->button)
3935 pan_info_update(pw, pi);
3937 if (!pi && pw->layout == LAYOUT_CALENDAR)
3939 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
3940 pan_calendar_update(pw, pi);
3946 pan_info_update(pw, pi);
3947 menu = pan_popup_menu(pw);
3948 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
3955 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
3958 PanWindow *pw = data;
3965 if (!(event->state & GDK_SHIFT_MASK))
3971 if (event->state & GDK_CONTROL_MASK)
3973 switch (event->direction)
3976 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
3977 (gint)event->x, (gint)event->y);
3979 case GDK_SCROLL_DOWN:
3980 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
3981 (gint)event->x, (gint)event->y);
3989 switch (event->direction)
3992 pixbuf_renderer_scroll(pr, 0, -h);
3994 case GDK_SCROLL_DOWN:
3995 pixbuf_renderer_scroll(pr, 0, h);
3997 case GDK_SCROLL_LEFT:
3998 pixbuf_renderer_scroll(pr, -w, 0);
4000 case GDK_SCROLL_RIGHT:
4001 pixbuf_renderer_scroll(pr, w, 0);
4009 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
4011 g_signal_connect(G_OBJECT(imd->pr), "clicked",
4012 G_CALLBACK(button_cb), pw);
4013 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
4014 G_CALLBACK(scroll_cb), pw);
4017 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
4019 PanWindow *pw = data;
4022 pw->imd = pw->imd_normal;
4025 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
4027 if (force_off && !pw->fs) return;
4031 fullscreen_stop(pw->fs);
4035 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
4036 pan_image_set_buttons(pw, pw->fs->imd);
4037 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
4038 G_CALLBACK(pan_window_key_press_cb), pw);
4040 pw->imd = pw->fs->imd;
4044 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
4046 PanWindow *pw = data;
4049 text = image_zoom_get_as_text(pw->imd);
4050 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4054 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
4056 PanWindow *pw = data;
4061 if (pr->scale == 0.0) return;
4063 pixbuf_renderer_get_visible_rect(pr, &rect);
4064 pixbuf_renderer_get_image_size(pr, &width, &height);
4066 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4067 adj->page_size = (gdouble)rect.width;
4068 adj->page_increment = adj->page_size / 2.0;
4069 adj->step_increment = 48.0 / pr->scale;
4071 adj->upper = MAX((gdouble)width, 1.0);
4072 adj->value = (gdouble)rect.x;
4074 pref_signal_block_data(pw->scrollbar_h, pw);
4075 gtk_adjustment_changed(adj);
4076 gtk_adjustment_value_changed(adj);
4077 pref_signal_unblock_data(pw->scrollbar_h, pw);
4079 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4080 adj->page_size = (gdouble)rect.height;
4081 adj->page_increment = adj->page_size / 2.0;
4082 adj->step_increment = 48.0 / pr->scale;
4084 adj->upper = MAX((gdouble)height, 1.0);
4085 adj->value = (gdouble)rect.y;
4087 pref_signal_block_data(pw->scrollbar_v, pw);
4088 gtk_adjustment_changed(adj);
4089 gtk_adjustment_value_changed(adj);
4090 pref_signal_unblock_data(pw->scrollbar_v, pw);
4093 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4095 PanWindow *pw = data;
4099 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4101 if (!pr->scale) return;
4103 x = (gint)gtk_range_get_value(range);
4105 pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
4108 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4110 PanWindow *pw = data;
4114 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4116 if (!pr->scale) return;
4118 y = (gint)gtk_range_get_value(range);
4120 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
4123 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4125 PanWindow *pw = data;
4127 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4128 pan_window_layout_update_idle(pw);
4131 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4133 PanWindow *pw = data;
4135 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4136 pan_window_layout_update_idle(pw);
4139 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4141 PanWindow *pw = data;
4144 path = remove_trailing_slash(new_text);
4145 parse_out_relatives(path);
4149 warning_dialog(_("Folder not found"),
4150 _("The entered path is not a folder"),
4151 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4155 tab_completion_append_to_history(pw->path_entry, path);
4158 pw->path = g_strdup(path);
4160 pan_window_layout_update_idle(pw);
4163 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4165 PanWindow *pw = data;
4168 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4170 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4171 pan_window_entry_activate_cb(text, pw);
4175 static void pan_window_close(PanWindow *pw)
4177 pan_window_list = g_list_remove(pan_window_list, pw);
4179 if (pw->idle_id != -1)
4181 g_source_remove(pw->idle_id);
4184 pan_fullscreen_toggle(pw, TRUE);
4185 gtk_widget_destroy(pw->window);
4187 pan_window_items_free(pw);
4194 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4196 PanWindow *pw = data;
4198 pan_window_close(pw);
4202 static void pan_window_new_real(const gchar *path)
4211 GdkGeometry geometry;
4213 pw = g_new0(PanWindow, 1);
4215 pw->path = g_strdup(path);
4216 pw->layout = LAYOUT_TIMELINE;
4217 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4218 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4219 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4223 pw->overlay_id = -1;
4226 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4228 geometry.min_width = 8;
4229 geometry.min_height = 8;
4230 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4232 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4233 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
4234 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4235 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4237 window_set_icon(pw->window, NULL, NULL);
4239 vbox = gtk_vbox_new(FALSE, 0);
4240 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4241 gtk_widget_show(vbox);
4243 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4245 pref_spacer(box, 0);
4246 pref_label_new(box, _("Location:"));
4247 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
4248 pan_window_entry_activate_cb, pw);
4249 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4250 G_CALLBACK(pan_window_entry_change_cb), pw);
4251 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4252 gtk_widget_show(combo);
4254 combo = gtk_combo_box_new_text();
4255 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4256 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4257 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4258 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4259 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4261 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4262 g_signal_connect(G_OBJECT(combo), "changed",
4263 G_CALLBACK(pan_window_layout_change_cb), pw);
4264 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4265 gtk_widget_show(combo);
4267 combo = gtk_combo_box_new_text();
4268 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4269 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4270 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4271 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4272 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4273 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4274 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4275 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4276 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4277 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4279 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4280 g_signal_connect(G_OBJECT(combo), "changed",
4281 G_CALLBACK(pan_window_layout_size_cb), pw);
4282 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4283 gtk_widget_show(combo);
4285 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4286 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4287 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4289 pw->imd = image_new(TRUE);
4290 pw->imd_normal = pw->imd;
4292 g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
4293 G_CALLBACK(pan_window_image_zoom_cb), pw);
4294 g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
4295 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4297 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4298 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4299 gtk_widget_show(GTK_WIDGET(pw->imd->widget));
4301 pan_window_dnd_init(pw);
4303 pan_image_set_buttons(pw, pw->imd);
4305 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4306 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4307 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4308 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4309 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4310 gtk_widget_show(pw->scrollbar_h);
4312 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4313 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4314 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4315 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4316 0, GTK_FILL | GTK_EXPAND, 0, 0);
4317 gtk_widget_show(pw->scrollbar_v);
4321 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4322 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4324 pref_spacer(pw->search_box, 0);
4325 pref_label_new(pw->search_box, _("Find:"));
4327 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4328 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4329 gtk_widget_show(hbox);
4331 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4332 pan_search_activate_cb, pw);
4333 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4334 gtk_widget_show(combo);
4336 pw->search_label = gtk_label_new("");
4337 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4338 gtk_widget_show(pw->search_label);
4342 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4344 frame = gtk_frame_new(NULL);
4345 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4346 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4347 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4348 gtk_widget_show(frame);
4350 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4351 gtk_container_add(GTK_CONTAINER(frame), hbox);
4352 gtk_widget_show(hbox);
4354 pref_spacer(hbox, 0);
4355 pw->label_message = pref_label_new(hbox, "");
4357 frame = gtk_frame_new(NULL);
4358 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4359 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4360 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4361 gtk_widget_show(frame);
4363 pw->label_zoom = gtk_label_new("");
4364 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4365 gtk_widget_show(pw->label_zoom);
4367 pw->search_button = gtk_toggle_button_new();
4368 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4369 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4370 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4371 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4372 gtk_widget_show(hbox);
4373 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4374 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4375 gtk_widget_show(pw->search_button_arrow);
4376 pref_label_new(hbox, _("Find"));
4378 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4379 gtk_widget_show(pw->search_button);
4380 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4381 G_CALLBACK(pan_search_toggle_cb), pw);
4383 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4384 G_CALLBACK(pan_window_delete_cb), pw);
4385 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4386 G_CALLBACK(pan_window_key_press_cb), pw);
4388 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4390 pan_window_layout_update_idle(pw);
4392 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4393 gtk_widget_show(pw->window);
4395 pan_window_list = g_list_append(pan_window_list, pw);
4399 *-----------------------------------------------------------------------------
4400 * peformance warnings
4401 *-----------------------------------------------------------------------------
4404 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4408 generic_dialog_close(gd);
4410 pan_window_new_real(path);
4414 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4418 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4419 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4422 static gint pan_warning(const gchar *path)
4428 GtkWidget *ct_button;
4431 if (enable_thumb_caching &&
4432 thumbnail_spec_standard) return FALSE;
4434 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4435 if (hide_dlg) return FALSE;
4437 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4439 gd->data = g_strdup(path);
4440 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4441 pan_warning_ok_cb, TRUE);
4443 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4444 _("Pan view performance may be poor."),
4445 _("To improve performance of thumbnails in the pan view the"
4446 " following options can be enabled. Note that both options"
4447 " must be enabled to notice a change in performance."));
4449 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4450 pref_spacer(group, PREF_PAD_INDENT);
4451 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4453 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4454 enable_thumb_caching, &enable_thumb_caching);
4455 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4456 thumbnail_spec_standard, &thumbnail_spec_standard);
4457 pref_checkbox_link_sensitivity(ct_button, button);
4461 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4462 G_CALLBACK(pan_warning_hide_cb), NULL);
4464 gtk_widget_show(gd->dialog);
4471 *-----------------------------------------------------------------------------
4473 *-----------------------------------------------------------------------------
4476 void pan_window_new(const gchar *path)
4478 if (pan_warning(path)) return;
4480 pan_window_new_real(path);
4484 *-----------------------------------------------------------------------------
4486 *-----------------------------------------------------------------------------
4489 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4491 PanWindow *pw = data;
4494 path = pan_menu_click_path(pw);
4497 pan_fullscreen_toggle(pw, TRUE);
4498 view_window_new(path);
4502 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4508 pw = submenu_item_get_data(widget);
4509 n = GPOINTER_TO_INT(data);
4512 path = pan_menu_click_path(pw);
4515 pan_fullscreen_toggle(pw, TRUE);
4516 start_editor_from_file(n, path);
4520 static void pan_info_cb(GtkWidget *widget, gpointer data)
4522 PanWindow *pw = data;
4525 path = pan_menu_click_path(pw);
4526 if (path) info_window_new(path, NULL);
4529 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4531 PanWindow *pw = data;
4533 image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
4536 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4538 PanWindow *pw = data;
4540 image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
4543 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4545 PanWindow *pw = data;
4547 image_zoom_set(pw->imd, 1.0);
4550 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4552 PanWindow *pw = data;
4555 path = pan_menu_click_path(pw);
4556 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4559 static void pan_move_cb(GtkWidget *widget, gpointer data)
4561 PanWindow *pw = data;
4564 path = pan_menu_click_path(pw);
4565 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4568 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4570 PanWindow *pw = data;
4573 path = pan_menu_click_path(pw);
4574 if (path) file_util_rename(path, NULL, pw->imd->widget);
4577 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4579 PanWindow *pw = data;
4582 path = pan_menu_click_path(pw);
4583 if (path) file_util_delete(path, NULL, pw->imd->widget);
4586 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4588 PanWindow *pw = data;
4590 pan_fullscreen_toggle(pw, FALSE);
4593 static void pan_close_cb(GtkWidget *widget, gpointer data)
4595 PanWindow *pw = data;
4597 pan_window_close(pw);
4600 static GtkWidget *pan_popup_menu(PanWindow *pw)
4606 active = (pw->click_pi != NULL);
4608 menu = popup_menu_short_lived();
4610 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4611 G_CALLBACK(pan_zoom_in_cb), pw);
4612 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4613 G_CALLBACK(pan_zoom_out_cb), pw);
4614 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4615 G_CALLBACK(pan_zoom_1_1_cb), pw);
4616 menu_item_add_divider(menu);
4618 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4619 gtk_widget_set_sensitive(item, active);
4621 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4622 G_CALLBACK(pan_info_cb), pw);
4624 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4625 G_CALLBACK(pan_new_window_cb), pw);
4627 menu_item_add_divider(menu);
4628 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4629 G_CALLBACK(pan_copy_cb), pw);
4630 menu_item_add_sensitive(menu, _("_Move..."), active,
4631 G_CALLBACK(pan_move_cb), pw);
4632 menu_item_add_sensitive(menu, _("_Rename..."), active,
4633 G_CALLBACK(pan_rename_cb), pw);
4634 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4635 G_CALLBACK(pan_delete_cb), pw);
4637 menu_item_add_divider(menu);
4641 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4645 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4648 menu_item_add_divider(menu);
4649 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4655 *-----------------------------------------------------------------------------
4657 *-----------------------------------------------------------------------------
4660 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4662 GtkSelectionData *selection_data, guint info,
4663 guint time, gpointer data)
4665 PanWindow *pw = data;
4667 if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
4669 if (info == TARGET_URI_LIST)
4673 list = uri_list_from_text(selection_data->data, TRUE);
4674 if (list && isdir((gchar *)list->data))
4676 gchar *path = list->data;
4679 pw->path = g_strdup(path);
4681 pan_window_layout_update_idle(pw);
4684 path_list_free(list);
4688 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4689 GtkSelectionData *selection_data, guint info,
4690 guint time, gpointer data)
4692 PanWindow *pw = data;
4695 path = pan_menu_click_path(pw);
4705 case TARGET_URI_LIST:
4708 case TARGET_TEXT_PLAIN:
4713 list = g_list_append(NULL, (gchar *)path);
4714 text = uri_text_from_list(list, &len, plain_text);
4718 gtk_selection_data_set (selection_data, selection_data->target,
4725 gtk_selection_data_set (selection_data, selection_data->target,
4730 static void pan_window_dnd_init(PanWindow *pw)
4734 widget = pw->imd->pr;
4736 gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
4737 dnd_file_drag_types, dnd_file_drag_types_count,
4738 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4739 g_signal_connect(G_OBJECT(widget), "drag_data_get",
4740 G_CALLBACK(pan_window_set_dnd_data), pw);
4742 gtk_drag_dest_set(widget,
4743 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4744 dnd_file_drop_types, dnd_file_drop_types_count,
4745 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4746 g_signal_connect(G_OBJECT(widget), "drag_data_received",
4747 G_CALLBACK(pan_window_get_dnd_data), pw);
4751 *-----------------------------------------------------------------------------
4752 * maintenance (for rename, move, remove)
4753 *-----------------------------------------------------------------------------