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));
2970 static gint count = 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));
2992 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
2993 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2995 PanWindow *pw = data;
2999 list = pan_layout_intersect(pw, x, y, width, height);
3008 if (pi->refcount > 0)
3012 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
3017 pw->queue = g_list_remove(pw->queue, pi);
3020 if (pw->queue_pi == pi) pw->queue_pi = NULL;
3023 g_object_unref(pi->pixbuf);
3035 *-----------------------------------------------------------------------------
3037 *-----------------------------------------------------------------------------
3040 static void pan_window_message(PanWindow *pw, const gchar *text)
3050 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
3055 if (pw->layout == LAYOUT_CALENDAR)
3065 pi->type == ITEM_BOX &&
3066 pi->key && strcmp(pi->key, "dot") == 0)
3068 size += pi->fd->size;
3083 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
3085 size += pi->fd->size;
3091 ss = text_from_size_abrev(size);
3092 buf = g_strdup_printf(_("%d images, %s"), count, ss);
3094 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
3098 static void pan_window_zoom_limit(PanWindow *pw)
3104 case LAYOUT_SIZE_THUMB_DOTS:
3105 case LAYOUT_SIZE_THUMB_NONE:
3106 case LAYOUT_SIZE_THUMB_SMALL:
3107 case LAYOUT_SIZE_THUMB_NORMAL:
3109 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
3113 case LAYOUT_SIZE_THUMB_LARGE:
3116 case LAYOUT_SIZE_10:
3117 case LAYOUT_SIZE_25:
3120 case LAYOUT_SIZE_33:
3121 case LAYOUT_SIZE_50:
3122 case LAYOUT_SIZE_100:
3128 image_zoom_set_limits(pw->imd, min, 32.0);
3131 static gint pan_window_layout_update_idle_cb(gpointer data)
3133 PanWindow *pw = data;
3139 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
3141 if (!pw->cache_list && !pw->cache_todo)
3143 pan_cache_fill(pw, pw->path);
3146 pan_window_message(pw, _("Reading dimensions..."));
3150 if (pan_cache_step(pw))
3154 if (pw->cache_count == pw->cache_total)
3156 pan_window_message(pw, _("Sorting images..."));
3158 else if (pw->cache_tick > 9)
3162 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
3163 pw->cache_total - pw->cache_count);
3164 pan_window_message(pw, buf);
3174 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3176 pan_window_zoom_limit(pw);
3178 if (width > 0 && height > 0)
3182 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
3183 PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
3184 pan_window_request_tile_cb,
3185 pan_window_dispose_tile_cb, pw, 1.0);
3187 if (scroll_x == 0 && scroll_y == 0)
3195 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
3198 pan_window_message(pw, NULL);
3205 static void pan_window_layout_update_idle(PanWindow *pw)
3207 if (pw->idle_id == -1)
3209 pan_window_message(pw, _("Sorting images..."));
3210 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3215 *-----------------------------------------------------------------------------
3216 * pan window keyboard
3217 *-----------------------------------------------------------------------------
3220 static const gchar *pan_menu_click_path(PanWindow *pw)
3222 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3226 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3228 PanWindow *pw = data;
3230 gdk_window_get_origin(pw->imd->pr->window, x, y);
3231 popup_menu_position_clamp(menu, x, y, 0);
3234 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3236 PanWindow *pw = data;
3239 gint stop_signal = FALSE;
3245 pr = PIXBUF_RENDERER(pw->imd->pr);
3246 path = pan_menu_click_path(pw);
3248 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pw->imd->widget)));
3252 switch (event->keyval)
3254 case GDK_Left: case GDK_KP_Left:
3258 case GDK_Right: case GDK_KP_Right:
3262 case GDK_Up: case GDK_KP_Up:
3266 case GDK_Down: case GDK_KP_Down:
3270 case GDK_Page_Up: case GDK_KP_Page_Up:
3271 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
3273 case GDK_Page_Down: case GDK_KP_Page_Down:
3274 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
3276 case GDK_Home: case GDK_KP_Home:
3277 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
3279 case GDK_End: case GDK_KP_End:
3280 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
3285 if (focused && !(event->state & GDK_CONTROL_MASK) )
3286 switch (event->keyval)
3288 case '+': case '=': case GDK_KP_Add:
3289 pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
3291 case '-': case GDK_KP_Subtract:
3292 pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
3294 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3295 pixbuf_renderer_zoom_set(pr, 1.0);
3298 pixbuf_renderer_zoom_set(pr, 2.0);
3301 pixbuf_renderer_zoom_set(pr, 3.0);
3304 pixbuf_renderer_zoom_set(pr, 4.0);
3307 pixbuf_renderer_zoom_set(pr, -4.0);
3310 pixbuf_renderer_zoom_set(pr, -3.0);
3313 pixbuf_renderer_zoom_set(pr, -2.0);
3317 pan_fullscreen_toggle(pw, FALSE);
3322 pan_overlay_toggle(pw);
3325 case GDK_Delete: case GDK_KP_Delete:
3330 if (GTK_WIDGET_VISIBLE(pw->search_box))
3332 gtk_widget_grab_focus(pw->search_entry);
3336 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3344 pan_fullscreen_toggle(pw, TRUE);
3347 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3349 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3355 menu = pan_popup_menu(pw);
3356 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3361 if (event->state & GDK_CONTROL_MASK)
3364 switch (event->keyval)
3397 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3400 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3403 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3406 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3409 if (path) info_window_new(path, NULL);
3412 pan_window_close(pw);
3415 if (n != -1 && path)
3417 pan_fullscreen_toggle(pw, TRUE);
3418 start_editor_from_file(n, path);
3422 else if (event->state & GDK_SHIFT_MASK)
3429 switch (event->keyval)
3434 pan_fullscreen_toggle(pw, TRUE);
3437 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3439 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
3440 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3449 if (x != 0 || y!= 0)
3451 keyboard_scroll_calc(&x, &y, event);
3452 pixbuf_renderer_scroll(pr, x, y);
3459 *-----------------------------------------------------------------------------
3461 *-----------------------------------------------------------------------------
3464 static void pan_info_update(PanWindow *pw, PanItem *pi)
3470 gint x1, y1, x2, y2, x3, y3;
3473 if (pw->click_pi == pi) return;
3474 if (pi && !pi->fd) pi = NULL;
3476 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3481 printf("info set to %s\n", pi->fd->path);
3483 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3485 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3486 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3487 pan_item_set_key(pbox, "info");
3489 if (pi->type == ITEM_THUMB && pi->pixbuf)
3491 w = gdk_pixbuf_get_width(pi->pixbuf);
3492 h = gdk_pixbuf_get_height(pi->pixbuf);
3494 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3495 y1 = pi->y + (pi->height - h) / 2 + 8;
3499 x1 = pi->x + pi->width - 8;
3507 triangle_rect_region(x1, y1, x2, y2, x3, y3,
3510 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3511 x1, y1, x2, y2, x3, y3,
3512 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3513 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3514 pan_item_set_key(p, "info");
3515 pan_item_added(pw, p);
3517 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3518 _("Filename:"), TEXT_ATTR_BOLD,
3519 PAN_POPUP_TEXT_COLOR, 255);
3520 pan_item_set_key(plabel, "info");
3521 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3522 pi->fd->name, TEXT_ATTR_NONE,
3523 PAN_POPUP_TEXT_COLOR, 255);
3524 pan_item_set_key(p, "info");
3525 pan_item_size_by_item(pbox, p, 0);
3527 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3528 _("Date:"), TEXT_ATTR_BOLD,
3529 PAN_POPUP_TEXT_COLOR, 255);
3530 pan_item_set_key(plabel, "info");
3531 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3532 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3533 PAN_POPUP_TEXT_COLOR, 255);
3534 pan_item_set_key(p, "info");
3535 pan_item_size_by_item(pbox, p, 0);
3537 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3538 _("Size:"), TEXT_ATTR_BOLD,
3539 PAN_POPUP_TEXT_COLOR, 255);
3540 pan_item_set_key(plabel, "info");
3541 buf = text_from_size(pi->fd->size);
3542 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3543 buf, TEXT_ATTR_NONE,
3544 PAN_POPUP_TEXT_COLOR, 255);
3546 pan_item_set_key(p, "info");
3547 pan_item_size_by_item(pbox, p, 0);
3549 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3550 pan_item_added(pw, pbox);
3555 *-----------------------------------------------------------------------------
3557 *-----------------------------------------------------------------------------
3560 static void pan_search_status(PanWindow *pw, const gchar *text)
3562 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3565 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3573 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3575 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3576 if (!list) return FALSE;
3578 found = g_list_find(list, pw->click_pi);
3579 if (found && found->next)
3581 found = found->next;
3589 pan_info_update(pw, pi);
3590 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3592 buf = g_strdup_printf("%s ( %d / %d )",
3593 (path[0] == '/') ? _("path found") : _("filename found"),
3594 g_list_index(list, pi) + 1,
3595 g_list_length(list));
3596 pan_search_status(pw, buf);
3604 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3612 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3614 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3615 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3620 needle = g_utf8_strdown(text, -1);
3621 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3624 if (!list) return FALSE;
3626 found = g_list_find(list, pw->click_pi);
3627 if (found && found->next)
3629 found = found->next;
3637 pan_info_update(pw, pi);
3638 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3640 buf = g_strdup_printf("%s ( %d / %d )",
3642 g_list_index(list, pi) + 1,
3643 g_list_length(list));
3644 pan_search_status(pw, buf);
3652 static gint valid_date_separator(gchar c)
3654 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3657 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
3658 gint year, gint month, gint day,
3664 work = g_list_last(pw->list);
3672 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
3673 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
3677 tl = localtime(&pi->fd->date);
3682 match = (tl->tm_year == year - 1900);
3683 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3684 if (match && day > 0) match = (tl->tm_mday == day);
3686 if (match) list = g_list_prepend(list, pi);
3691 return g_list_reverse(list);
3694 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3710 if (!text) return FALSE;
3712 ptr = (gchar *)text;
3713 while (*ptr != '\0')
3715 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3720 if (t == -1) return FALSE;
3722 if (!lt) return FALSE;
3724 if (valid_date_separator(*text))
3727 mptr = (gchar *)text;
3731 year = (gint)strtol(text, &mptr, 10);
3732 if (mptr == text) return FALSE;
3735 if (*mptr != '\0' && valid_date_separator(*mptr))
3740 month = strtol(mptr, &dptr, 10);
3743 if (valid_date_separator(*dptr))
3745 month = lt->tm_mon + 1;
3753 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3757 day = strtol(dptr, &eptr, 10);
3767 year = lt->tm_year + 1900;
3769 else if (year < 100)
3778 month < -1 || month == 0 || month > 12 ||
3779 day < -1 || day == 0 || day > 31) return FALSE;
3781 t = date_to_time(year, month, day);
3782 if (t < 0) return FALSE;
3784 if (pw->layout == LAYOUT_CALENDAR)
3786 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
3792 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3793 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
3798 found = g_list_find(list, pw->search_pi);
3799 if (found && found->next)
3801 found = found->next;
3812 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
3814 pan_info_update(pw, NULL);
3815 pan_calendar_update(pw, pi);
3816 image_scroll_to_point(pw->imd,
3817 pi->x + pi->width / 2,
3818 pi->y + pi->height / 2, 0.5, 0.5);
3822 pan_info_update(pw, pi);
3823 image_scroll_to_point(pw->imd,
3824 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3830 buf = date_value_string(t, DATE_LENGTH_MONTH);
3835 buf = g_strdup_printf("%d %s", day, tmp);
3841 buf = date_value_string(t, DATE_LENGTH_YEAR);
3846 buf_count = g_strdup_printf("( %d / %d )",
3847 g_list_index(list, pi) + 1,
3848 g_list_length(list));
3852 buf_count = g_strdup_printf("(%s)", _("no match"));
3855 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
3858 pan_search_status(pw, message);
3866 static void pan_search_activate_cb(const gchar *text, gpointer data)
3868 PanWindow *pw = data;
3872 tab_completion_append_to_history(pw->search_entry, text);
3874 if (pan_search_by_path(pw, text)) return;
3876 if ((pw->layout == LAYOUT_TIMELINE ||
3877 pw->layout == LAYOUT_CALENDAR) &&
3878 pan_search_by_date(pw, text))
3883 if (pan_search_by_partial(pw, text)) return;
3885 pan_search_status(pw, _("no match"));
3888 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3890 PanWindow *pw = data;
3893 visible = GTK_WIDGET_VISIBLE(pw->search_box);
3894 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3898 gtk_widget_hide(pw->search_box);
3899 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3903 gtk_widget_show(pw->search_box);
3904 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3905 gtk_widget_grab_focus(pw->search_entry);
3911 *-----------------------------------------------------------------------------
3912 * view window main routines
3913 *-----------------------------------------------------------------------------
3916 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
3918 PanWindow *pw = data;
3926 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
3927 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
3930 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3933 switch (event->button)
3936 pan_info_update(pw, pi);
3938 if (!pi && pw->layout == LAYOUT_CALENDAR)
3940 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
3941 pan_calendar_update(pw, pi);
3947 pan_info_update(pw, pi);
3948 menu = pan_popup_menu(pw);
3949 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
3956 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
3959 PanWindow *pw = data;
3966 if (!(event->state & GDK_SHIFT_MASK))
3972 if (event->state & GDK_CONTROL_MASK)
3974 switch (event->direction)
3977 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
3978 (gint)event->x, (gint)event->y);
3980 case GDK_SCROLL_DOWN:
3981 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
3982 (gint)event->x, (gint)event->y);
3990 switch (event->direction)
3993 pixbuf_renderer_scroll(pr, 0, -h);
3995 case GDK_SCROLL_DOWN:
3996 pixbuf_renderer_scroll(pr, 0, h);
3998 case GDK_SCROLL_LEFT:
3999 pixbuf_renderer_scroll(pr, -w, 0);
4001 case GDK_SCROLL_RIGHT:
4002 pixbuf_renderer_scroll(pr, w, 0);
4010 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
4012 g_signal_connect(G_OBJECT(imd->pr), "clicked",
4013 G_CALLBACK(button_cb), pw);
4014 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
4015 G_CALLBACK(scroll_cb), pw);
4018 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
4020 PanWindow *pw = data;
4023 pw->imd = pw->imd_normal;
4026 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
4028 if (force_off && !pw->fs) return;
4032 fullscreen_stop(pw->fs);
4036 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
4037 pan_image_set_buttons(pw, pw->fs->imd);
4038 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
4039 G_CALLBACK(pan_window_key_press_cb), pw);
4041 pw->imd = pw->fs->imd;
4045 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
4047 PanWindow *pw = data;
4051 text = image_zoom_get_as_text(imd);
4053 text = g_strdup_printf("%.2f", zoom);
4054 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4058 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
4060 PanWindow *pw = data;
4065 pixbuf_renderer_get_visible_rect(pr, &rect);
4066 pixbuf_renderer_get_image_size(pr, &width, &height);
4068 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4069 adj->page_size = (gdouble)rect.width;
4070 adj->page_increment = adj->page_size / 2.0;
4071 adj->step_increment = 48.0 / pr->scale;
4073 adj->upper = MAX((gdouble)width, 1.0);
4074 adj->value = (gdouble)rect.x;
4076 pref_signal_block_data(pw->scrollbar_h, pw);
4077 gtk_adjustment_changed(adj);
4078 pref_signal_unblock_data(pw->scrollbar_h, pw);
4080 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4081 adj->page_size = (gdouble)rect.height;
4082 adj->page_increment = adj->page_size / 2.0;
4083 adj->step_increment = 48.0 / pr->scale;
4085 adj->upper = MAX((gdouble)height, 1.0);
4086 adj->value = (gdouble)rect.y;
4088 pref_signal_block_data(pw->scrollbar_v, pw);
4089 gtk_adjustment_changed(adj);
4090 pref_signal_unblock_data(pw->scrollbar_v, pw);
4092 // printf("scrolled to %d,%d @ %d x %d\n", x, y, width, height);
4095 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4097 PanWindow *pw = data;
4101 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4103 if (!pr->scale) return;
4105 x = (gint)gtk_range_get_value(range);
4107 pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
4110 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4112 PanWindow *pw = data;
4116 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4118 if (!pr->scale) return;
4120 y = (gint)gtk_range_get_value(range);
4122 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
4125 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4127 PanWindow *pw = data;
4129 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4130 pan_window_layout_update_idle(pw);
4133 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4135 PanWindow *pw = data;
4137 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4138 pan_window_layout_update_idle(pw);
4141 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4143 PanWindow *pw = data;
4146 path = remove_trailing_slash(new_text);
4147 parse_out_relatives(path);
4151 warning_dialog(_("Folder not found"),
4152 _("The entered path is not a folder"),
4153 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4157 tab_completion_append_to_history(pw->path_entry, path);
4160 pw->path = g_strdup(path);
4162 pan_window_layout_update_idle(pw);
4165 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4167 PanWindow *pw = data;
4170 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4172 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4173 pan_window_entry_activate_cb(text, pw);
4177 static void pan_window_close(PanWindow *pw)
4179 pan_window_list = g_list_remove(pan_window_list, pw);
4181 if (pw->idle_id != -1)
4183 g_source_remove(pw->idle_id);
4186 pan_fullscreen_toggle(pw, TRUE);
4187 gtk_widget_destroy(pw->window);
4189 pan_window_items_free(pw);
4196 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4198 PanWindow *pw = data;
4200 pan_window_close(pw);
4204 static void pan_window_new_real(const gchar *path)
4213 GdkGeometry geometry;
4215 pw = g_new0(PanWindow, 1);
4217 pw->path = g_strdup(path);
4218 pw->layout = LAYOUT_TIMELINE;
4219 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4220 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4221 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4225 pw->overlay_id = -1;
4228 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4230 geometry.min_width = 8;
4231 geometry.min_height = 8;
4232 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4234 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4235 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
4236 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4237 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4239 window_set_icon(pw->window, NULL, NULL);
4241 vbox = gtk_vbox_new(FALSE, 0);
4242 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4243 gtk_widget_show(vbox);
4245 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4247 pref_spacer(box, 0);
4248 pref_label_new(box, _("Location:"));
4249 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
4250 pan_window_entry_activate_cb, pw);
4251 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4252 G_CALLBACK(pan_window_entry_change_cb), pw);
4253 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4254 gtk_widget_show(combo);
4256 combo = gtk_combo_box_new_text();
4257 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4258 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4259 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4260 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4261 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4263 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4264 g_signal_connect(G_OBJECT(combo), "changed",
4265 G_CALLBACK(pan_window_layout_change_cb), pw);
4266 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4267 gtk_widget_show(combo);
4269 combo = gtk_combo_box_new_text();
4270 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4271 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4272 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4273 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4274 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4275 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4276 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4277 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4278 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4279 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4281 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4282 g_signal_connect(G_OBJECT(combo), "changed",
4283 G_CALLBACK(pan_window_layout_size_cb), pw);
4284 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4285 gtk_widget_show(combo);
4287 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4288 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4289 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4291 pw->imd = image_new(TRUE);
4292 pw->imd_normal = pw->imd;
4294 g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
4295 G_CALLBACK(pan_window_image_zoom_cb), pw);
4296 g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
4297 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4299 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4300 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4301 gtk_widget_show(GTK_WIDGET(pw->imd->widget));
4303 pan_window_dnd_init(pw);
4305 pan_image_set_buttons(pw, pw->imd);
4307 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4308 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4309 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4310 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4311 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4312 gtk_widget_show(pw->scrollbar_h);
4314 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4315 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4316 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4317 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4318 0, GTK_FILL | GTK_EXPAND, 0, 0);
4319 gtk_widget_show(pw->scrollbar_v);
4323 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4324 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4326 pref_spacer(pw->search_box, 0);
4327 pref_label_new(pw->search_box, _("Find:"));
4329 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4330 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4331 gtk_widget_show(hbox);
4333 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4334 pan_search_activate_cb, pw);
4335 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4336 gtk_widget_show(combo);
4338 pw->search_label = gtk_label_new("");
4339 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4340 gtk_widget_show(pw->search_label);
4344 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4346 frame = gtk_frame_new(NULL);
4347 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4348 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4349 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4350 gtk_widget_show(frame);
4352 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4353 gtk_container_add(GTK_CONTAINER(frame), hbox);
4354 gtk_widget_show(hbox);
4356 pref_spacer(hbox, 0);
4357 pw->label_message = pref_label_new(hbox, "");
4359 frame = gtk_frame_new(NULL);
4360 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4361 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4362 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4363 gtk_widget_show(frame);
4365 pw->label_zoom = gtk_label_new("");
4366 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4367 gtk_widget_show(pw->label_zoom);
4369 pw->search_button = gtk_toggle_button_new();
4370 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4371 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4372 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4373 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4374 gtk_widget_show(hbox);
4375 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4376 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4377 gtk_widget_show(pw->search_button_arrow);
4378 pref_label_new(hbox, _("Find"));
4380 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4381 gtk_widget_show(pw->search_button);
4382 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4383 G_CALLBACK(pan_search_toggle_cb), pw);
4385 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4386 G_CALLBACK(pan_window_delete_cb), pw);
4387 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4388 G_CALLBACK(pan_window_key_press_cb), pw);
4390 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4392 pan_window_layout_update_idle(pw);
4394 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4395 gtk_widget_show(pw->window);
4397 pan_window_list = g_list_append(pan_window_list, pw);
4401 *-----------------------------------------------------------------------------
4402 * peformance warnings
4403 *-----------------------------------------------------------------------------
4406 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4410 generic_dialog_close(gd);
4412 pan_window_new_real(path);
4416 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4420 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4421 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4424 static gint pan_warning(const gchar *path)
4430 GtkWidget *ct_button;
4433 if (enable_thumb_caching &&
4434 thumbnail_spec_standard) return FALSE;
4436 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4437 if (hide_dlg) return FALSE;
4439 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4441 gd->data = g_strdup(path);
4442 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4443 pan_warning_ok_cb, TRUE);
4445 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4446 _("Pan view performance may be poor."),
4447 _("To improve performance of thumbnails in the pan view the"
4448 " following options can be enabled. Note that both options"
4449 " must be enabled to notice a change in performance."));
4451 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4452 pref_spacer(group, PREF_PAD_INDENT);
4453 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4455 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4456 enable_thumb_caching, &enable_thumb_caching);
4457 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4458 thumbnail_spec_standard, &thumbnail_spec_standard);
4459 pref_checkbox_link_sensitivity(ct_button, button);
4463 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4464 G_CALLBACK(pan_warning_hide_cb), NULL);
4466 gtk_widget_show(gd->dialog);
4473 *-----------------------------------------------------------------------------
4475 *-----------------------------------------------------------------------------
4478 void pan_window_new(const gchar *path)
4480 if (pan_warning(path)) return;
4482 pan_window_new_real(path);
4486 *-----------------------------------------------------------------------------
4488 *-----------------------------------------------------------------------------
4491 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4493 PanWindow *pw = data;
4496 path = pan_menu_click_path(pw);
4499 pan_fullscreen_toggle(pw, TRUE);
4500 view_window_new(path);
4504 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4510 pw = submenu_item_get_data(widget);
4511 n = GPOINTER_TO_INT(data);
4514 path = pan_menu_click_path(pw);
4517 pan_fullscreen_toggle(pw, TRUE);
4518 start_editor_from_file(n, path);
4522 static void pan_info_cb(GtkWidget *widget, gpointer data)
4524 PanWindow *pw = data;
4527 path = pan_menu_click_path(pw);
4528 if (path) info_window_new(path, NULL);
4531 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4533 PanWindow *pw = data;
4535 image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
4538 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4540 PanWindow *pw = data;
4542 image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
4545 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4547 PanWindow *pw = data;
4549 image_zoom_set(pw->imd, 1.0);
4552 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4554 PanWindow *pw = data;
4557 path = pan_menu_click_path(pw);
4558 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4561 static void pan_move_cb(GtkWidget *widget, gpointer data)
4563 PanWindow *pw = data;
4566 path = pan_menu_click_path(pw);
4567 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4570 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4572 PanWindow *pw = data;
4575 path = pan_menu_click_path(pw);
4576 if (path) file_util_rename(path, NULL, pw->imd->widget);
4579 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4581 PanWindow *pw = data;
4584 path = pan_menu_click_path(pw);
4585 if (path) file_util_delete(path, NULL, pw->imd->widget);
4588 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4590 PanWindow *pw = data;
4592 pan_fullscreen_toggle(pw, FALSE);
4595 static void pan_close_cb(GtkWidget *widget, gpointer data)
4597 PanWindow *pw = data;
4599 pan_window_close(pw);
4602 static GtkWidget *pan_popup_menu(PanWindow *pw)
4608 active = (pw->click_pi != NULL);
4610 menu = popup_menu_short_lived();
4612 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4613 G_CALLBACK(pan_zoom_in_cb), pw);
4614 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4615 G_CALLBACK(pan_zoom_out_cb), pw);
4616 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4617 G_CALLBACK(pan_zoom_1_1_cb), pw);
4618 menu_item_add_divider(menu);
4620 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4621 gtk_widget_set_sensitive(item, active);
4623 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4624 G_CALLBACK(pan_info_cb), pw);
4626 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4627 G_CALLBACK(pan_new_window_cb), pw);
4629 menu_item_add_divider(menu);
4630 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4631 G_CALLBACK(pan_copy_cb), pw);
4632 menu_item_add_sensitive(menu, _("_Move..."), active,
4633 G_CALLBACK(pan_move_cb), pw);
4634 menu_item_add_sensitive(menu, _("_Rename..."), active,
4635 G_CALLBACK(pan_rename_cb), pw);
4636 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4637 G_CALLBACK(pan_delete_cb), pw);
4639 menu_item_add_divider(menu);
4643 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4647 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4650 menu_item_add_divider(menu);
4651 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4657 *-----------------------------------------------------------------------------
4659 *-----------------------------------------------------------------------------
4662 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4664 GtkSelectionData *selection_data, guint info,
4665 guint time, gpointer data)
4667 PanWindow *pw = data;
4669 if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
4671 if (info == TARGET_URI_LIST)
4675 list = uri_list_from_text(selection_data->data, TRUE);
4676 if (list && isdir((gchar *)list->data))
4678 gchar *path = list->data;
4681 pw->path = g_strdup(path);
4683 pan_window_layout_update_idle(pw);
4686 path_list_free(list);
4690 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4691 GtkSelectionData *selection_data, guint info,
4692 guint time, gpointer data)
4694 PanWindow *pw = data;
4697 path = pan_menu_click_path(pw);
4707 case TARGET_URI_LIST:
4710 case TARGET_TEXT_PLAIN:
4715 list = g_list_append(NULL, (gchar *)path);
4716 text = uri_text_from_list(list, &len, plain_text);
4720 gtk_selection_data_set (selection_data, selection_data->target,
4727 gtk_selection_data_set (selection_data, selection_data->target,
4732 static void pan_window_dnd_init(PanWindow *pw)
4736 widget = pw->imd->pr;
4738 gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
4739 dnd_file_drag_types, dnd_file_drag_types_count,
4740 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4741 g_signal_connect(G_OBJECT(widget), "drag_data_get",
4742 G_CALLBACK(pan_window_set_dnd_data), pw);
4744 gtk_drag_dest_set(widget,
4745 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4746 dnd_file_drop_types, dnd_file_drop_types_count,
4747 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4748 g_signal_connect(G_OBJECT(widget), "drag_data_received",
4749 G_CALLBACK(pan_window_get_dnd_data), pw);
4753 *-----------------------------------------------------------------------------
4754 * maintenance (for rename, move, remove)
4755 *-----------------------------------------------------------------------------