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)
438 gdouble slope1, slope2;
439 gint slope1_x, slope1_y;
446 pw = gdk_pixbuf_get_width(pb);
447 ph = gdk_pixbuf_get_height(pb);
449 if (!util_clip_region(0, 0, pw, ph,
450 clip_x, clip_y, clip_w, clip_h,
451 &rx, &ry, &rw, &rh)) return;
453 triangle_rect_region(x1, y1, x2, y2, x3, y3,
456 if (!util_clip_region(rx, ry, rw, rh,
458 &fx1, &fy1, &fw, &fh)) return;
462 p_alpha = gdk_pixbuf_get_has_alpha(pb);
463 prs = gdk_pixbuf_get_rowstride(pb);
464 p_pix = gdk_pixbuf_get_pixels(pb);
466 p_step = (p_alpha) ? 4 : 3;
470 t = x1; x1 = x2; x2 = t;
471 t = y1; y1 = y2; y2 = t;
475 t = x2; x2 = x3; x3 = t;
476 t = y2; y2 = y3; y3 = t;
480 t = x1; x1 = x2; x2 = t;
481 t = y1; y1 = y2; y2 = t;
484 slope1 = (gdouble)(y2 - y1);
485 if (slope1) slope1 = (gdouble)(x2 - x1) / slope1;
488 slope2 = (gdouble)(y3 - y1);
489 if (slope2) slope2 = (gdouble)(x3 - x1) / slope2;
491 for (y = fy1; y < fy2; y++)
495 if (!middle && y > y2)
497 slope1 = (gdouble)(y3 - y2);
498 if (slope1) slope1 = (gdouble)(x3 - x2) / slope1;
505 xa = slope1_x + ((gdouble)slope1 * (y - slope1_y) + 0.5);
506 xb = x1 + ((gdouble)slope2 * (y - y1) + 0.5);
510 t = xa; xa = xb; xb = t;
513 xa = CLAMP(xa, fx1, fx2);
514 xb = CLAMP(xb, fx1, fx2);
516 pp = p_pix + y * prs + xa * p_step;
520 *pp = (r * a + *pp * (256-a)) >> 8;
522 *pp = (g * a + *pp * (256-a)) >> 8;
524 *pp = (b * a + *pp * (256-a)) >> 8;
533 static gint util_clip_line(gdouble clip_x, gdouble clip_y, gdouble clip_w, gdouble clip_h,
534 gdouble x1, gdouble y1, gdouble x2, gdouble y2,
535 gdouble *rx1, gdouble *ry1, gdouble *rx2, gdouble *ry2)
544 t = x1; x1 = x2; x2 = t;
545 t = y1; y1 = y2; y2 = t;
549 if (x2 < clip_x || x1 > clip_x + clip_w) return FALSE;
553 if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
557 if (y1 < clip_y || y2 > clip_y + clip_h) return FALSE;
561 if (x1 >= clip_x && x2 <= clip_x + clip_w)
565 if (y1 >= clip_y && y2 <= clip_y + clip_h) return TRUE;
569 if (y2 >= clip_y && y1 <= clip_y + clip_h) return TRUE;
579 slope = (y2 - y1) / d;
582 y1 = y1 + slope * (clip_x - x1);
585 if (x2 > clip_x + clip_w)
587 y2 = y2 + slope * (clip_x + clip_w - x2);
588 x2 = clip_x + clip_w;
594 if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
600 if (y1 < clip_y || y2 > clip_y + clip_h) return FALSE;
602 t = x1; x1 = x2; x2 = t;
603 t = y1; y1 = y2; y2 = t;
612 slope = (x2 - x1) / d;
615 x1 = x1 + slope * (clip_y - y1);
618 if (y2 > clip_y + clip_h)
620 x2 = x2 + slope * (clip_y + clip_h - y2);
621 y2 = clip_y + clip_h;
643 static void pixbuf_draw_line(GdkPixbuf *pb,
644 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
645 gint x1, gint y1, gint x2, gint y2,
646 guint8 r, guint8 g, guint8 b, guint8 a)
651 gdouble rx1, ry1, rx2, ry2;
658 gint cx1, cy1, cx2, cy2;
662 pw = gdk_pixbuf_get_width(pb);
663 ph = gdk_pixbuf_get_height(pb);
665 if (!util_clip_region(0, 0, pw, ph,
666 clip_x, clip_y, clip_w, clip_h,
667 &rx, &ry, &rw, &rh)) return;
668 if (!util_clip_line((gdouble)rx, (gdouble)ry, (gdouble)rw, (gdouble)rh,
669 (gdouble)x1, (gdouble)y1, (gdouble)x2, (gdouble)y2,
670 &rx1, &ry1, &rx2, &ry2)) return;
677 p_alpha = gdk_pixbuf_get_has_alpha(pb);
678 prs = gdk_pixbuf_get_rowstride(pb);
679 p_pix = gdk_pixbuf_get_pixels(pb);
681 p_step = (p_alpha) ? 4 : 3;
683 if (fabs(rx2 - rx1) > fabs(ry2 - ry1))
688 t = rx1; rx1 = rx2; rx2 = t;
689 t = ry1; ry1 = ry2; ry2 = t;
693 if (slope != 0.0) slope = (ry2 - ry1) / slope;
694 for (x = rx1; x < rx2; x += 1.0)
696 px = (gint)(x + 0.5);
697 py = (gint)(ry1 + (x - rx1) * slope + 0.5);
699 if (px >= cx1 && px < cx2 && py >= cy1 && py < cy2)
701 pp = p_pix + py * prs + px * p_step;
702 *pp = (r * a + *pp * (256-a)) >> 8;
704 *pp = (g * a + *pp * (256-a)) >> 8;
706 *pp = (b * a + *pp * (256-a)) >> 8;
715 t = rx1; rx1 = rx2; rx2 = t;
716 t = ry1; ry1 = ry2; ry2 = t;
720 if (slope != 0.0) slope = (rx2 - rx1) / slope;
721 for (y = ry1; y < ry2; y += 1.0)
723 px = (gint)(rx1 + (y - ry1) * slope + 0.5);
724 py = (gint)(y + 0.5);
726 if (px >= cx1 && px < cx2 && py >= cy1 && py < cy2)
728 pp = p_pix + py * prs + px * p_step;
729 *pp = (r * a + *pp * (256-a)) >> 8;
731 *pp = (g * a + *pp * (256-a)) >> 8;
733 *pp = (b * a + *pp * (256-a)) >> 8;
739 static void pixbuf_draw_fade_linear(guchar *p_pix, gint prs, gint p_alpha,
740 gint s, gint vertical, gint border,
741 gint x1, gint y1, gint x2, gint y2,
742 guint8 r, guint8 g, guint8 b, guint8 a)
749 p_step = (p_alpha) ? 4 : 3;
750 for (j = y1; j < y2; j++)
752 pp = p_pix + j * prs + x1 * p_step;
753 if (!vertical) n = a - a * abs(j - s) / border;
754 for (i = x1; i < x2; i++)
756 if (vertical) n = a - a * abs(i - s) / border;
757 *pp = (r * n + *pp * (256-n)) >> 8;
759 *pp = (g * n + *pp * (256-n)) >> 8;
761 *pp = (b * n + *pp * (256-n)) >> 8;
768 static void pixbuf_draw_fade_radius(guchar *p_pix, gint prs, gint p_alpha,
769 gint sx, gint sy, gint border,
770 gint x1, gint y1, gint x2, gint y2,
771 guint8 r, guint8 g, guint8 b, guint8 a)
777 p_step = (p_alpha) ? 4 : 3;
778 for (j = y1; j < y2; j++)
780 pp = p_pix + j * prs + x1 * p_step;
781 for (i = x1; i < x2; i++)
786 r = MIN(border, (gint)sqrt((i-sx)*(i-sx) + (j-sy)*(j-sy)));
787 n = a - a * r / border;
788 *pp = (r * n + *pp * (256-n)) >> 8;
790 *pp = (g * n + *pp * (256-n)) >> 8;
792 *pp = (b * n + *pp * (256-n)) >> 8;
799 static void pixbuf_draw_shadow(GdkPixbuf *pb,
800 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
801 gint x, gint y, gint w, gint h, gint border,
802 guint8 r, guint8 g, guint8 b, guint8 a)
812 pw = gdk_pixbuf_get_width(pb);
813 ph = gdk_pixbuf_get_height(pb);
815 if (!util_clip_region(0, 0, pw, ph,
816 clip_x, clip_y, clip_w, clip_h,
817 &rx, &ry, &rw, &rh)) return;
819 p_alpha = gdk_pixbuf_get_has_alpha(pb);
820 prs = gdk_pixbuf_get_rowstride(pb);
821 p_pix = gdk_pixbuf_get_pixels(pb);
823 if (util_clip_region(x + border, y + border, w - border * 2, h - border * 2,
827 pixbuf_draw_rect_fill(pb, fx, fy, fw, fh, r, g, b, a);
830 if (border < 1) return;
832 if (util_clip_region(x, y + border, border, h - border * 2,
836 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
837 x + border, TRUE, border,
838 fx, fy, fx + fw, fy + fh,
841 if (util_clip_region(x + w - border, y + border, border, h - border * 2,
845 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
846 x + w - border, TRUE, border,
847 fx, fy, fx + fw, fy + fh,
850 if (util_clip_region(x + border, y, w - border * 2, border,
854 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
855 y + border, FALSE, border,
856 fx, fy, fx + fw, fy + fh,
859 if (util_clip_region(x + border, y + h - border, w - border * 2, border,
863 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
864 y + h - border, FALSE, border,
865 fx, fy, fx + fw, fy + fh,
868 if (util_clip_region(x, y, border, border,
872 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
873 x + border, y + border, border,
874 fx, fy, fx + fw, fy + fh,
877 if (util_clip_region(x + w - border, y, border, border,
881 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
882 x + w - border, y + border, border,
883 fx, fy, fx + fw, fy + fh,
886 if (util_clip_region(x, y + h - border, border, border,
890 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
891 x + border, y + h - border, border,
892 fx, fy, fx + fw, fy + fh,
895 if (util_clip_region(x + w - border, y + h - border, border, border,
899 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
900 x + w - border, y + h - border, border,
901 fx, fy, fx + fw, fy + fh,
908 *-----------------------------------------------------------------------------
910 *-----------------------------------------------------------------------------
913 static void pan_cache_free(PanWindow *pw)
917 work = pw->cache_list;
925 cache_sim_data_free(pc->cd);
926 file_data_free((FileData *)pc);
929 g_list_free(pw->cache_list);
930 pw->cache_list = NULL;
932 filelist_free(pw->cache_todo);
933 pw->cache_todo = NULL;
940 static void pan_cache_fill(PanWindow *pw, const gchar *path)
946 list = pan_window_layout_list(path, SORT_NAME, TRUE);
947 pw->cache_todo = g_list_reverse(list);
949 pw->cache_total = g_list_length(pw->cache_todo);
952 static gint pan_cache_step(PanWindow *pw)
956 CacheData *cd = NULL;
958 if (!pw->cache_todo) return FALSE;
960 fd = pw->cache_todo->data;
961 pw->cache_todo = g_list_remove(pw->cache_todo, fd);
963 if (enable_thumb_caching)
967 found = cache_find_location(CACHE_TYPE_SIM, fd->path);
968 if (found && filetime(found) == fd->date)
970 cd = cache_sim_data_load(found);
975 if (!cd) cd = cache_sim_data_new();
979 cd->dimensions = image_load_dimensions(fd->path, &cd->width, &cd->height);
980 if (enable_thumb_caching &&
986 base = cache_get_location(CACHE_TYPE_SIM, fd->path, FALSE, &mode);
987 if (cache_ensure_dir_exists(base, mode))
990 cd->path = cache_get_location(CACHE_TYPE_SIM, fd->path, TRUE, NULL);
991 if (cache_sim_data_save(cd))
993 filetime_set(cd->path, filetime(fd->path));
1002 pc = g_new0(PanCacheData, 1);
1003 memcpy(pc, fd, sizeof(FileData));
1008 pw->cache_list = g_list_prepend(pw->cache_list, pc);
1015 *-----------------------------------------------------------------------------
1017 *-----------------------------------------------------------------------------
1020 static void pan_item_free(PanItem *pi)
1024 if (pi->pixbuf) g_object_unref(pi->pixbuf);
1025 if (pi->fd) file_data_free(pi->fd);
1033 static void pan_window_items_free(PanWindow *pw)
1040 PanItem *pi = work->data;
1046 g_list_free(pw->list);
1049 g_list_free(pw->queue);
1051 pw->queue_pi = NULL;
1053 image_loader_free(pw->il);
1056 thumb_loader_free(pw->tl);
1059 pw->click_pi = NULL;
1060 pw->search_pi = NULL;
1063 static PanItem *pan_item_new_thumb(PanWindow *pw, FileData *fd, gint x, gint y)
1067 pi = g_new0(PanItem, 1);
1068 pi->type = ITEM_THUMB;
1072 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
1073 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
1079 pw->list = g_list_prepend(pw->list, pi);
1084 static PanItem *pan_item_new_box(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
1086 guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
1087 guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
1091 pi = g_new0(PanItem, 1);
1092 pi->type = ITEM_BOX;
1097 pi->height = height;
1099 pi->color_r = base_r;
1100 pi->color_g = base_g;
1101 pi->color_b = base_b;
1102 pi->color_a = base_a;
1104 pi->color2_r = bord_r;
1105 pi->color2_g = bord_g;
1106 pi->color2_b = bord_b;
1107 pi->color2_a = bord_a;
1108 pi->border = border_size;
1110 pw->list = g_list_prepend(pw->list, pi);
1115 static void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
1119 if (!pi || pi->type != ITEM_BOX) return;
1124 pi->width -= shadow[0];
1125 pi->height -= shadow[0];
1128 shadow = g_new0(gint, 2);
1132 pi->width += offset;
1133 pi->height += offset;
1139 static PanItem *pan_item_new_tri(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
1140 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
1141 guint8 r, guint8 g, guint8 b, guint8 a)
1146 pi = g_new0(PanItem, 1);
1147 pi->type = ITEM_TRIANGLE;
1151 pi->height = height;
1158 coord = g_new0(gint, 6);
1168 pi->border = BORDER_NONE;
1170 pw->list = g_list_prepend(pw->list, pi);
1175 static void pan_item_tri_border(PanItem *pi, gint borders,
1176 guint8 r, guint8 g, guint8 b, guint8 a)
1178 if (!pi || pi->type != ITEM_TRIANGLE) return;
1180 pi->border = borders;
1188 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
1190 PangoLayout *layout;
1192 layout = gtk_widget_create_pango_layout(widget, NULL);
1194 if (pi->text_attr & TEXT_ATTR_MARKUP)
1196 pango_layout_set_markup(layout, pi->text, -1);
1200 if (pi->text_attr & TEXT_ATTR_BOLD ||
1201 pi->text_attr & TEXT_ATTR_HEADING)
1206 pal = pango_attr_list_new();
1207 if (pi->text_attr & TEXT_ATTR_BOLD)
1209 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
1210 pa->start_index = 0;
1211 pa->end_index = G_MAXINT;
1212 pango_attr_list_insert(pal, pa);
1214 if (pi->text_attr & TEXT_ATTR_HEADING)
1216 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
1217 pa->start_index = 0;
1218 pa->end_index = G_MAXINT;
1219 pango_attr_list_insert(pal, pa);
1221 pango_layout_set_attributes(layout, pal);
1222 pango_attr_list_unref(pal);
1225 pango_layout_set_text(layout, pi->text, -1);
1229 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
1231 PangoLayout *layout;
1233 if (!pi || !pi->text || !widget) return;
1235 layout = pan_item_text_layout(pi, widget);
1236 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
1237 g_object_unref(G_OBJECT(layout));
1239 pi->width += PAN_TEXT_BORDER_SIZE * 2;
1240 pi->height += PAN_TEXT_BORDER_SIZE * 2;
1243 static PanItem *pan_item_new_text(PanWindow *pw, gint x, gint y, const gchar *text, TextAttrType attr,
1244 guint8 r, guint8 g, guint8 b, guint8 a)
1248 pi = g_new0(PanItem, 1);
1249 pi->type = ITEM_TEXT;
1252 pi->text = g_strdup(text);
1253 pi->text_attr = attr;
1260 pan_item_text_compute_size(pi, pw->imd->pr);
1262 pw->list = g_list_prepend(pw->list, pi);
1267 static void pan_item_set_key(PanItem *pi, const gchar *key)
1274 pi->key = g_strdup(key);
1278 static void pan_item_added(PanWindow *pw, PanItem *pi)
1281 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1284 static void pan_item_remove(PanWindow *pw, PanItem *pi)
1288 if (pw->click_pi == pi) pw->click_pi = NULL;
1289 if (pw->queue_pi == pi) pw->queue_pi = NULL;
1290 if (pw->search_pi == pi) pw->search_pi = NULL;
1291 pw->queue = g_list_remove(pw->queue, pi);
1293 pw->list = g_list_remove(pw->list, pi);
1294 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1298 static void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
1300 if (!pi || !child) return;
1302 if (pi->x + pi->width < child->x + child->width + border)
1303 pi->width = child->x + child->width + border - pi->x;
1305 if (pi->y + pi->height < child->y + child->height + border)
1306 pi->height = child->y + child->height + border - pi->y;
1309 static void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
1313 if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
1314 if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
1317 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
1324 if (!pi->fd) return;
1326 work = pw->cache_list;
1335 path = ((FileData *)pc)->path;
1337 if (pc->cd && pc->cd->dimensions &&
1338 path && strcmp(path, pi->fd->path) == 0)
1340 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
1341 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
1343 pw->cache_list = g_list_remove(pw->cache_list, pc);
1344 cache_sim_data_free(pc->cd);
1345 file_data_free((FileData *)pc);
1351 static PanItem *pan_item_new_image(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
1355 pi = g_new0(PanItem, 1);
1356 pi->type = ITEM_IMAGE;
1361 pan_item_image_find_size(pw, pi, w, h);
1363 pw->list = g_list_prepend(pw->list, pi);
1368 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
1372 if (!key) return NULL;
1374 work = g_list_last(pw->list);
1380 if ((pi->type == type || type == ITEM_NONE) &&
1381 pi->key && strcmp(pi->key, key) == 0)
1391 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1392 static GList *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1393 gint ignore_case, gint partial)
1398 if (!path) return NULL;
1399 if (partial && path[0] == '/') return NULL;
1401 work = g_list_last(pw->list);
1407 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1413 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
1415 else if (pi->fd->name)
1423 haystack = g_utf8_strdown(pi->fd->name, -1);
1424 match = (strstr(haystack, path) != NULL);
1429 if (strstr(pi->fd->name, path)) match = TRUE;
1432 else if (ignore_case)
1434 if (strcasecmp(path, pi->fd->name) == 0) match = TRUE;
1438 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
1442 if (match) list = g_list_prepend(list, pi);
1447 return g_list_reverse(list);
1450 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y, const gchar *key)
1460 if ((pi->type == type || type == ITEM_NONE) &&
1461 x >= pi->x && x < pi->x + pi->width &&
1462 y >= pi->y && y < pi->y + pi->height &&
1463 (!key || (pi->key && strcmp(pi->key, key) == 0)))
1474 *-----------------------------------------------------------------------------
1476 *-----------------------------------------------------------------------------
1479 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend)
1481 GList *flist = NULL;
1482 GList *dlist = NULL;
1486 filelist_read(path, &flist, &dlist);
1487 if (sort != SORT_NONE)
1489 flist = filelist_sort(flist, sort, ascend);
1490 dlist = filelist_sort(dlist, sort, ascend);
1500 folders = g_list_remove(folders, fd);
1502 if (filelist_read(fd->path, &flist, &dlist))
1504 if (sort != SORT_NONE)
1506 flist = filelist_sort(flist, sort, ascend);
1507 dlist = filelist_sort(dlist, sort, ascend);
1510 result = g_list_concat(result, flist);
1511 folders = g_list_concat(dlist, folders);
1520 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1528 list = pan_window_layout_list(path, SORT_NAME, TRUE);
1530 grid_size = (gint)sqrt((double)g_list_length(list));
1531 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1533 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1537 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1542 *width = PAN_FOLDER_BOX_BORDER * 2;
1543 *height = PAN_FOLDER_BOX_BORDER * 2;
1556 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1558 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1560 x += pi->width + PAN_THUMB_GAP;
1561 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1570 pi = pan_item_new_thumb(pw, fd, x, y);
1572 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1576 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1579 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1585 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1588 gint x1, y1, x2, y2;
1603 if (x1 > pi->x) x1 = pi->x;
1604 if (y1 > pi->y) y1 = pi->y;
1605 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1606 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1609 x1 -= PAN_FOLDER_BOX_BORDER;
1610 y1 -= PAN_FOLDER_BOX_BORDER;
1611 x2 += PAN_FOLDER_BOX_BORDER;
1612 y2 += PAN_FOLDER_BOX_BORDER;
1625 if (pi->type == ITEM_TRIANGLE && pi->data)
1639 if (width) *width = x2 - x1;
1640 if (height) *height = y2 - y1;
1643 typedef struct _FlowerGroup FlowerGroup;
1644 struct _FlowerGroup {
1657 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1661 work = group->items;
1679 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1680 gint *result_x, gint *result_y)
1686 radius = parent->circumference / (2*PI);
1687 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1689 a = 2*PI * group->diameter / parent->circumference;
1691 x = (gint)((double)radius * cos(parent->angle + a / 2));
1692 y = (gint)((double)radius * sin(parent->angle + a / 2));
1699 x += parent->width / 2;
1700 y += parent->height / 2;
1702 x -= group->width / 2;
1703 y -= group->height / 2;
1709 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1716 if (parent && parent->children)
1718 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1726 pan_window_layout_compute_folder_flower_move(group, x, y);
1731 gint px, py, gx, gy;
1732 gint x1, y1, x2, y2;
1734 px = parent->x + parent->width / 2;
1735 py = parent->y + parent->height / 2;
1737 gx = group->x + group->width / 2;
1738 gy = group->y + group->height / 2;
1743 x2 = MAX(px, gx + 5);
1744 y2 = MAX(py, gy + 5);
1746 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1747 px, py, gx, gy, gx + 5, gy + 5,
1749 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1753 pw->list = g_list_concat(group->items, pw->list);
1754 group->items = NULL;
1756 group->circumference = 0;
1757 work = group->children;
1765 group->circumference += child->diameter;
1768 work = g_list_last(group->children);
1776 pan_window_layout_compute_folder_flower_build(pw, child, group);
1779 g_list_free(group->children);
1783 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1796 if (!filelist_read(path, &f, &d)) return NULL;
1797 if (!f && !d) return NULL;
1799 f = filelist_sort(f, SORT_NAME, TRUE);
1800 d = filelist_sort(d, SORT_NAME, TRUE);
1802 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1803 PAN_TEXT_COLOR, 255);
1805 y += pi_box->height;
1807 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1809 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1810 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1811 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1812 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1814 x += PAN_FOLDER_BOX_BORDER;
1815 y += PAN_FOLDER_BOX_BORDER;
1817 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1831 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1833 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1834 x += pi->width + PAN_THUMB_GAP;
1835 if (pi->height > y_height) y_height = pi->height;
1839 pi = pan_item_new_thumb(pw, fd, x, y);
1840 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1841 y_height = PAN_THUMB_SIZE;
1845 if (grid_count >= grid_size)
1849 y += y_height + PAN_THUMB_GAP;
1853 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1858 group = g_new0(FlowerGroup, 1);
1859 group->items = pw->list;
1862 group->width = pi_box->width;
1863 group->height = pi_box->y + pi_box->height;
1864 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1866 group->children = NULL;
1877 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1878 if (child) group->children = g_list_prepend(group->children, child);
1886 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1887 gint *width, gint *height,
1888 gint *scroll_x, gint *scroll_y)
1893 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1894 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1896 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1898 list = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1901 PanItem *pi = list->data;
1902 *scroll_x = pi->x + pi->width / 2;
1903 *scroll_y = pi->y + pi->height / 2;
1908 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1909 gint *x, gint *y, gint *level,
1911 gint *width, gint *height)
1919 if (!filelist_read(path, &f, &d)) return;
1920 if (!f && !d) return;
1922 f = filelist_sort(f, SORT_NAME, TRUE);
1923 d = filelist_sort(d, SORT_NAME, TRUE);
1925 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1927 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1928 PAN_TEXT_COLOR, 255);
1930 *y += pi_box->height;
1932 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1934 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1935 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1936 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1937 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1939 *x += PAN_FOLDER_BOX_BORDER;
1940 *y += PAN_FOLDER_BOX_BORDER;
1951 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1953 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1954 *x += pi->width + PAN_THUMB_GAP;
1955 if (pi->height > y_height) y_height = pi->height;
1959 pi = pan_item_new_thumb(pw, fd, *x, *y);
1960 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1961 y_height = PAN_THUMB_SIZE;
1964 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1967 if (f) *y = pi_box->y + pi_box->height;
1979 *level = *level + 1;
1980 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1981 pi_box, width, height);
1982 *level = *level - 1;
1987 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1989 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1990 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1992 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1995 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
2002 x = PAN_FOLDER_BOX_BORDER;
2003 y = PAN_FOLDER_BOX_BORDER;
2004 w = PAN_FOLDER_BOX_BORDER * 2;
2005 h = PAN_FOLDER_BOX_BORDER * 2;
2007 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
2009 if (width) *width = w;
2010 if (height) *height = h;
2014 *-----------------------------------------------------------------------------
2016 *-----------------------------------------------------------------------------
2019 #define PAN_CAL_DAY_WIDTH 100
2020 #define PAN_CAL_DAY_HEIGHT 80
2021 #define PAN_CAL_DOT_SIZE 3
2022 #define PAN_CAL_DOT_GAP 2
2023 #define PAN_CAL_DOT_COLOR 0, 0, 0
2024 #define PAN_CAL_DOT_ALPHA 32
2026 static void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
2032 gint x1, y1, x2, y2, x3, y3;
2037 while ((pi = pan_item_find_by_key(pw, ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
2039 if (!pi_day || pi_day->type != ITEM_BOX ||
2040 !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
2042 list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
2054 if (dot->type != ITEM_BOX || !dot->fd ||
2055 !dot->key || strcmp(dot->key, "dot") != 0)
2057 list = g_list_delete_link(list, node);
2065 grid = (gint)(sqrt(g_list_length(list)) + 0.5);
2067 x = pi_day->x + pi_day->width + 4;
2071 if (y + grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4 > pw->pr->image_height)
2073 y = pw->pr->image_height - (grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4);
2077 pbox = pan_item_new_box(pw, NULL, x, y, PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
2079 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
2080 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
2081 pan_item_set_key(pbox, "day_bubble");
2088 buf = date_value_string(pi_day->fd->date, DATE_LENGTH_WEEK);
2089 plabel = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2090 PAN_POPUP_TEXT_COLOR, 255);
2091 pan_item_set_key(plabel, "day_bubble");
2094 pan_item_size_by_item(pbox, plabel, 0);
2096 y += plabel->height;
2103 x += PAN_FOLDER_BOX_BORDER;
2104 y += PAN_FOLDER_BOX_BORDER;
2118 pimg = pan_item_new_thumb(pw, file_data_new_simple(dot->fd->path), x, y);
2119 pan_item_set_key(pimg, "day_bubble");
2121 pan_item_size_by_item(pbox, pimg, PAN_FOLDER_BOX_BORDER);
2126 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
2131 x = pbox->x + PAN_FOLDER_BOX_BORDER;
2132 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
2138 x1 = pi_day->x + pi_day->width - 8;
2141 y2 = pbox->y + MIN(42, pbox->height);
2143 y3 = MAX(pbox->y, y2 - 30);
2144 triangle_rect_region(x1, y1, x2, y2, x3, y3,
2147 pi = pan_item_new_tri(pw, NULL, x, y, w, h,
2148 x1, y1, x2, y2, x3, y3,
2149 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
2150 pan_item_tri_border(pi, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
2151 pan_item_set_key(pi, "day_bubble");
2152 pan_item_added(pw, pi);
2154 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
2155 pan_item_added(pw, pbox);
2158 static void pan_window_layout_compute_calendar(PanWindow *pw, const gchar *path, gint *width, gint *height)
2174 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2176 list = pan_window_layout_list(path, SORT_NONE, TRUE);
2177 list = filelist_sort(list, SORT_TIME, TRUE);
2190 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2198 if (day_max < count) day_max = count;
2202 printf("biggest day contains %d images\n", day_max);
2204 grid = (gint)(sqrt((double)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
2205 day_width = MAX(PAN_CAL_DAY_WIDTH, grid);
2206 day_height = MAX(PAN_CAL_DAY_HEIGHT, grid);
2210 FileData *fd = list->data;
2212 year = date_value(fd->date, DATE_LENGTH_YEAR);
2213 month = date_value(fd->date, DATE_LENGTH_MONTH);
2216 work = g_list_last(list);
2219 FileData *fd = work->data;
2220 end_year = date_value(fd->date, DATE_LENGTH_YEAR);
2221 end_month = date_value(fd->date, DATE_LENGTH_MONTH);
2224 *width = PAN_FOLDER_BOX_BORDER * 2;
2225 *height = PAN_FOLDER_BOX_BORDER * 2;
2227 x = PAN_FOLDER_BOX_BORDER;
2228 y = PAN_FOLDER_BOX_BORDER;
2231 while (work && (year < end_year || (year == end_year && month <= end_month)))
2242 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
2244 days = date_value(dt, DATE_LENGTH_DAY);
2245 dt = date_to_time(year, month, 1);
2246 col = date_value(dt, DATE_LENGTH_WEEK);
2249 x = PAN_FOLDER_BOX_BORDER;
2251 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
2252 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2253 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2254 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2255 buf = date_value_string(dt, DATE_LENGTH_MONTH);
2256 pi_text = pan_item_new_text(pw, x, y, buf,
2257 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2258 PAN_TEXT_COLOR, 255);
2260 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
2262 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
2264 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
2265 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
2267 for (day = 1; day <= days; day++)
2274 dt = date_to_time(year, month, day);
2276 fd = g_new0(FileData, 1);
2277 /* path and name must be non NULL, so make them an invalid filename */
2278 fd->path = g_strdup("//");
2281 pi_day = pan_item_new_box(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
2282 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2283 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2284 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2285 pan_item_set_key(pi_day, "day");
2287 dx = x + PAN_CAL_DOT_GAP * 2;
2288 dy = y + PAN_CAL_DOT_GAP * 2;
2290 fd = (work) ? work->data : NULL;
2291 while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
2295 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
2297 PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
2299 pan_item_set_key(pi, "dot");
2301 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2302 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
2304 dx = x + PAN_CAL_DOT_GAP * 2;
2305 dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2307 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
2309 /* must keep all dots within respective day even if it gets ugly */
2310 dy = y + PAN_CAL_DOT_GAP * 2;
2313 pi_day->color_a = MIN(PAN_FOLDER_BOX_ALPHA + 64 + n, 255);
2317 fd = (work) ? work->data : NULL;
2324 buf = g_strdup_printf("( %d )", n);
2325 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2326 PAN_TEXT_COLOR, 255);
2329 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
2330 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
2333 buf = g_strdup_printf("%d", day);
2334 pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2335 PAN_TEXT_COLOR, 255);
2339 pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
2346 x = PAN_FOLDER_BOX_BORDER;
2347 y += PAN_CAL_DAY_HEIGHT;
2351 x += PAN_CAL_DAY_WIDTH;
2355 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
2356 y += PAN_FOLDER_BOX_BORDER * 2;
2367 *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
2372 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
2380 PanItem *pi_month = NULL;
2381 PanItem *pi_day = NULL;
2387 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2389 list = pan_window_layout_list(path, SORT_NONE, TRUE);
2390 list = filelist_sort(list, SORT_TIME, TRUE);
2392 *width = PAN_FOLDER_BOX_BORDER * 2;
2393 *height = PAN_FOLDER_BOX_BORDER * 2;
2398 day_start = month_start;
2413 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2418 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
2424 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
2428 x = PAN_FOLDER_BOX_BORDER;
2431 y = PAN_FOLDER_BOX_BORDER;
2433 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
2434 pi = pan_item_new_text(pw, x, y, buf,
2435 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2436 PAN_TEXT_COLOR, 255);
2439 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
2441 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2442 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2443 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2445 x += PAN_FOLDER_BOX_BORDER;
2446 y += PAN_FOLDER_BOX_BORDER;
2450 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
2462 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
2464 needle = needle->next;
2473 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
2474 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2475 PAN_TEXT_COLOR, 255);
2480 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
2481 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2482 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2483 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2485 x += PAN_FOLDER_BOX_BORDER;
2486 y += PAN_FOLDER_BOX_BORDER;
2490 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2492 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
2493 if (pi->width > x_width) x_width = pi->width;
2494 y_height = pi->height;
2498 pi = pan_item_new_thumb(pw, fd, x, y);
2499 x_width = PAN_THUMB_SIZE;
2500 y_height = PAN_THUMB_SIZE;
2503 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
2504 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
2509 if (total > 0 && count < PAN_GROUP_MAX)
2511 y += y_height + PAN_THUMB_GAP;
2515 x += x_width + PAN_THUMB_GAP;
2525 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
2531 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
2532 gint *width, gint *height,
2533 gint *scroll_x, gint *scroll_y)
2535 pan_window_items_free(pw);
2539 case LAYOUT_SIZE_THUMB_DOTS:
2540 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2541 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2543 case LAYOUT_SIZE_THUMB_NONE:
2544 pw->thumb_size = PAN_THUMB_SIZE_NONE;
2545 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2547 case LAYOUT_SIZE_THUMB_SMALL:
2548 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2549 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2551 case LAYOUT_SIZE_THUMB_NORMAL:
2553 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2554 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2556 case LAYOUT_SIZE_THUMB_LARGE:
2557 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2558 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2560 case LAYOUT_SIZE_10:
2561 pw->image_size = 10;
2562 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2564 case LAYOUT_SIZE_25:
2565 pw->image_size = 25;
2566 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2568 case LAYOUT_SIZE_33:
2569 pw->image_size = 33;
2570 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2572 case LAYOUT_SIZE_50:
2573 pw->image_size = 50;
2574 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2576 case LAYOUT_SIZE_100:
2577 pw->image_size = 100;
2578 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2591 pan_window_layout_compute_grid(pw, path, width, height);
2593 case LAYOUT_FOLDERS_LINEAR:
2594 pan_window_layout_compute_folders_linear(pw, path, width, height);
2596 case LAYOUT_FOLDERS_FLOWER:
2597 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2599 case LAYOUT_CALENDAR:
2600 pan_window_layout_compute_calendar(pw, path, width, height);
2602 case LAYOUT_TIMELINE:
2603 pan_window_layout_compute_timeline(pw, path, width, height);
2609 printf("computed %d objects\n", g_list_length(pw->list));
2612 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2625 if (util_clip_region_test(x, y, width, height,
2626 pi->x, pi->y, pi->width, pi->height))
2628 list = g_list_prepend(list, pi);
2638 *-----------------------------------------------------------------------------
2640 *-----------------------------------------------------------------------------
2643 static gint pan_layout_queue_step(PanWindow *pw);
2646 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2648 PanWindow *pw = data;
2656 pw->queue_pi = NULL;
2660 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2661 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2664 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2668 thumb_loader_free(pw->tl);
2671 while (pan_layout_queue_step(pw));
2674 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2676 PanWindow *pw = data;
2684 pw->queue_pi = NULL;
2688 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2689 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2690 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2692 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2693 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2694 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2699 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2700 (GdkInterpType)zoom_quality);
2701 g_object_unref(tmp);
2705 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2709 image_loader_free(pw->il);
2712 while (pan_layout_queue_step(pw));
2716 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2717 guint width, guint height, gpointer data)
2719 PanWindow *pw = data;
2730 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2731 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2735 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2741 static gint pan_layout_queue_step(PanWindow *pw)
2745 if (!pw->queue) return FALSE;
2747 pi = pw->queue->data;
2748 pw->queue = g_list_remove(pw->queue, pi);
2751 if (!pw->queue_pi->fd)
2753 pw->queue_pi->queued = FALSE;
2754 pw->queue_pi = NULL;
2758 image_loader_free(pw->il);
2760 thumb_loader_free(pw->tl);
2763 if (pi->type == ITEM_IMAGE)
2765 pw->il = image_loader_new(pi->fd->path);
2767 if (pw->size != LAYOUT_SIZE_100)
2769 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2773 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2775 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2777 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2779 image_loader_free(pw->il);
2782 else if (pi->type == ITEM_THUMB)
2784 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2786 if (!pw->tl->standard_loader)
2788 /* The classic loader will recreate a thumbnail any time we
2789 * request a different size than what exists. This view will
2790 * almost never use the user configured sizes so disable cache.
2792 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2795 thumb_loader_set_callbacks(pw->tl,
2796 pan_layout_queue_thumb_done_cb,
2797 pan_layout_queue_thumb_done_cb,
2800 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2802 thumb_loader_free(pw->tl);
2806 pw->queue_pi->queued = FALSE;
2807 pw->queue_pi = NULL;
2811 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2813 if (!pi || pi->queued || pi->pixbuf) return;
2814 if (pw->size <= LAYOUT_SIZE_THUMB_NONE) return;
2817 pw->queue = g_list_prepend(pw->queue, pi);
2819 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2822 static gint pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
2823 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2825 PanWindow *pw = data;
2830 pixbuf_set_rect_fill(pixbuf,
2831 0, 0, width, height,
2832 PAN_BACKGROUND_COLOR, 255);
2834 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2836 gint rx, ry, rw, rh;
2838 if (util_clip_region(x, y, width, height,
2840 &rx, &ry, &rw, &rh))
2842 pixbuf_draw_rect_fill(pixbuf,
2843 rx - x, ry - y, rw, rh,
2844 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2847 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2849 gint rx, ry, rw, rh;
2851 if (util_clip_region(x, y, width, height,
2853 &rx, &ry, &rw, &rh))
2855 pixbuf_draw_rect_fill(pixbuf,
2856 rx - x, ry - y, rw, rh,
2857 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2861 list = pan_layout_intersect(pw, x, y, width, height);
2866 gint tx, ty, tw, th;
2867 gint rx, ry, rw, rh;
2874 if (pi->type == ITEM_THUMB && pi->pixbuf)
2876 tw = gdk_pixbuf_get_width(pi->pixbuf);
2877 th = gdk_pixbuf_get_height(pi->pixbuf);
2879 tx = pi->x + (pi->width - tw) / 2;
2880 ty = pi->y + (pi->height - th) / 2;
2882 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2884 if (util_clip_region(x, y, width, height,
2885 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2886 &rx, &ry, &rw, &rh))
2888 pixbuf_draw_shadow(pixbuf,
2889 rx - x, ry - y, rw, rh,
2890 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2892 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2897 if (util_clip_region(x, y, width, height,
2898 tx + tw, ty + PAN_SHADOW_OFFSET,
2899 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2900 &rx, &ry, &rw, &rh))
2902 pixbuf_draw_shadow(pixbuf,
2903 rx - x, ry - y, rw, rh,
2904 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2906 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2908 if (util_clip_region(x, y, width, height,
2909 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2910 &rx, &ry, &rw, &rh))
2912 pixbuf_draw_shadow(pixbuf,
2913 rx - x, ry - y, rw, rh,
2914 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2916 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2920 if (util_clip_region(x, y, width, height,
2922 &rx, &ry, &rw, &rh))
2924 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2927 1.0, 1.0, GDK_INTERP_NEAREST,
2931 if (util_clip_region(x, y, width, height,
2932 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2933 &rx, &ry, &rw, &rh))
2935 pixbuf_draw_rect_fill(pixbuf,
2936 rx - x, ry - y, rw, rh,
2937 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2939 if (util_clip_region(x, y, width, height,
2940 tx, ty, PAN_OUTLINE_THICKNESS, th,
2941 &rx, &ry, &rw, &rh))
2943 pixbuf_draw_rect_fill(pixbuf,
2944 rx - x, ry - y, rw, rh,
2945 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2947 if (util_clip_region(x, y, width, height,
2948 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2949 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2950 &rx, &ry, &rw, &rh))
2952 pixbuf_draw_rect_fill(pixbuf,
2953 rx - x, ry - y, rw, rh,
2954 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2956 if (util_clip_region(x, y, width, height,
2957 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2958 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2959 &rx, &ry, &rw, &rh))
2961 pixbuf_draw_rect_fill(pixbuf,
2962 rx - x, ry - y, rw, rh,
2963 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2966 else if (pi->type == ITEM_THUMB)
2968 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2969 th = pi->height - PAN_SHADOW_OFFSET * 2;
2970 tx = pi->x + PAN_SHADOW_OFFSET;
2971 ty = pi->y + PAN_SHADOW_OFFSET;
2973 if (util_clip_region(x, y, width, height,
2975 &rx, &ry, &rw, &rh))
2979 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2980 pixbuf_draw_rect_fill(pixbuf,
2981 rx - x, ry - y, rw, rh,
2983 PAN_SHADOW_ALPHA / d);
2986 pan_layout_queue(pw, pi);
2988 else if (pi->type == ITEM_IMAGE)
2990 if (util_clip_region(x, y, width, height,
2991 pi->x, pi->y, pi->width, pi->height,
2992 &rx, &ry, &rw, &rh))
2996 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2999 1.0, 1.0, GDK_INTERP_NEAREST,
3004 pixbuf_draw_rect_fill(pixbuf,
3005 rx - x, ry - y, rw, rh,
3006 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
3007 pan_layout_queue(pw, pi);
3011 else if (pi->type == ITEM_BOX)
3025 if (pi->color_a > 254)
3027 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
3028 shadow[0], bh - shadow[0],
3029 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
3031 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
3032 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
3034 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
3036 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
3041 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
3042 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
3044 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
3046 PAN_SHADOW_COLOR, a);
3050 if (util_clip_region(x, y, width, height,
3051 pi->x, pi->y, bw, bh,
3052 &rx, &ry, &rw, &rh))
3054 pixbuf_draw_rect_fill(pixbuf,
3055 rx - x, ry - y, rw, rh,
3056 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3058 if (util_clip_region(x, y, width, height,
3059 pi->x, pi->y, bw, pi->border,
3060 &rx, &ry, &rw, &rh))
3062 pixbuf_draw_rect_fill(pixbuf,
3063 rx - x, ry - y, rw, rh,
3064 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3066 if (util_clip_region(x, y, width, height,
3067 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
3068 &rx, &ry, &rw, &rh))
3070 pixbuf_draw_rect_fill(pixbuf,
3071 rx - x, ry - y, rw, rh,
3072 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3074 if (util_clip_region(x, y, width, height,
3075 pi->x + bw - pi->border, pi->y + pi->border,
3076 pi->border, bh - pi->border * 2,
3077 &rx, &ry, &rw, &rh))
3079 pixbuf_draw_rect_fill(pixbuf,
3080 rx - x, ry - y, rw, rh,
3081 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3083 if (util_clip_region(x, y, width, height,
3084 pi->x, pi->y + bh - pi->border,
3086 &rx, &ry, &rw, &rh))
3088 pixbuf_draw_rect_fill(pixbuf,
3089 rx - x, ry - y, rw, rh,
3090 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3093 else if (pi->type == ITEM_TRIANGLE)
3095 if (util_clip_region(x, y, width, height,
3096 pi->x, pi->y, pi->width, pi->height,
3097 &rx, &ry, &rw, &rh) && pi->data)
3099 gint *coord = pi->data;
3100 pixbuf_draw_triangle(pixbuf,
3101 rx - x, ry - y, rw, rh,
3102 coord[0] - x, coord[1] - y,
3103 coord[2] - x, coord[3] - y,
3104 coord[4] - x, coord[5] - y,
3105 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3107 if (pi->border & BORDER_1)
3109 pixbuf_draw_line(pixbuf,
3110 rx - x, ry - y, rw, rh,
3111 coord[0] - x, coord[1] - y,
3112 coord[2] - x, coord[3] - y,
3113 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3115 if (pi->border & BORDER_2)
3117 pixbuf_draw_line(pixbuf,
3118 rx - x, ry - y, rw, rh,
3119 coord[2] - x, coord[3] - y,
3120 coord[4] - x, coord[5] - y,
3121 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3123 if (pi->border & BORDER_3)
3125 pixbuf_draw_line(pixbuf,
3126 rx - x, ry - y, rw, rh,
3127 coord[4] - x, coord[5] - y,
3128 coord[0] - x, coord[1] - y,
3129 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3133 else if (pi->type == ITEM_TEXT && pi->text)
3135 PangoLayout *layout;
3137 layout = pan_item_text_layout(pi, (GtkWidget *)pr);
3138 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
3139 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
3140 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3141 g_object_unref(G_OBJECT(layout));
3147 if (x%512 == 0 && y%512 == 0)
3149 PangoLayout *layout;
3152 layout = gtk_widget_create_pango_layout((GtkWidget *)pr, NULL);
3154 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
3155 (x / pr->source_tile_width) +
3156 (y / pr->source_tile_height * (pr->image_width/pr->source_tile_width + 1)));
3157 pango_layout_set_text(layout, buf, -1);
3160 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr, 0, 0, 0, 0, 0, 255);
3162 g_object_unref(G_OBJECT(layout));
3169 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
3170 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
3172 PanWindow *pw = data;
3176 list = pan_layout_intersect(pw, x, y, width, height);
3185 if (pi->refcount > 0)
3189 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
3194 pw->queue = g_list_remove(pw->queue, pi);
3197 if (pw->queue_pi == pi) pw->queue_pi = NULL;
3200 g_object_unref(pi->pixbuf);
3212 *-----------------------------------------------------------------------------
3214 *-----------------------------------------------------------------------------
3217 static void pan_window_message(PanWindow *pw, const gchar *text)
3227 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
3232 if (pw->layout == LAYOUT_CALENDAR)
3242 pi->type == ITEM_BOX &&
3243 pi->key && strcmp(pi->key, "dot") == 0)
3245 size += pi->fd->size;
3260 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
3262 size += pi->fd->size;
3268 ss = text_from_size_abrev(size);
3269 buf = g_strdup_printf(_("%d images, %s"), count, ss);
3271 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
3275 static void pan_window_zoom_limit(PanWindow *pw)
3281 case LAYOUT_SIZE_THUMB_DOTS:
3282 case LAYOUT_SIZE_THUMB_NONE:
3283 case LAYOUT_SIZE_THUMB_SMALL:
3284 case LAYOUT_SIZE_THUMB_NORMAL:
3286 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
3290 case LAYOUT_SIZE_THUMB_LARGE:
3293 case LAYOUT_SIZE_10:
3294 case LAYOUT_SIZE_25:
3297 case LAYOUT_SIZE_33:
3298 case LAYOUT_SIZE_50:
3299 case LAYOUT_SIZE_100:
3305 image_zoom_set_limits(pw->imd, min, 32.0);
3308 static gint pan_window_layout_update_idle_cb(gpointer data)
3310 PanWindow *pw = data;
3316 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
3318 if (!pw->cache_list && !pw->cache_todo)
3320 pan_cache_fill(pw, pw->path);
3323 pan_window_message(pw, _("Reading dimensions..."));
3327 if (pan_cache_step(pw))
3331 if (pw->cache_count == pw->cache_total)
3333 pan_window_message(pw, _("Sorting images..."));
3335 else if (pw->cache_tick > 9)
3339 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
3340 pw->cache_total - pw->cache_count);
3341 pan_window_message(pw, buf);
3351 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3353 pan_window_zoom_limit(pw);
3355 if (width > 0 && height > 0)
3359 printf("Canvas size is %d x %d\n", width, height);
3361 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
3362 PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
3363 pan_window_request_tile_cb,
3364 pan_window_dispose_tile_cb, pw, 1.0);
3366 if (scroll_x == 0 && scroll_y == 0)
3374 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
3377 pan_window_message(pw, NULL);
3384 static void pan_window_layout_update_idle(PanWindow *pw)
3386 if (pw->idle_id == -1)
3388 pan_window_message(pw, _("Sorting images..."));
3389 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3394 *-----------------------------------------------------------------------------
3395 * pan window keyboard
3396 *-----------------------------------------------------------------------------
3399 static const gchar *pan_menu_click_path(PanWindow *pw)
3401 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3405 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3407 PanWindow *pw = data;
3409 gdk_window_get_origin(pw->imd->pr->window, x, y);
3410 popup_menu_position_clamp(menu, x, y, 0);
3413 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3415 PanWindow *pw = data;
3418 gint stop_signal = FALSE;
3424 pr = PIXBUF_RENDERER(pw->imd->pr);
3425 path = pan_menu_click_path(pw);
3427 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pw->imd->widget)));
3431 switch (event->keyval)
3433 case GDK_Left: case GDK_KP_Left:
3437 case GDK_Right: case GDK_KP_Right:
3441 case GDK_Up: case GDK_KP_Up:
3445 case GDK_Down: case GDK_KP_Down:
3449 case GDK_Page_Up: case GDK_KP_Page_Up:
3450 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
3452 case GDK_Page_Down: case GDK_KP_Page_Down:
3453 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
3455 case GDK_Home: case GDK_KP_Home:
3456 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
3458 case GDK_End: case GDK_KP_End:
3459 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
3464 if (focused && !(event->state & GDK_CONTROL_MASK) )
3465 switch (event->keyval)
3467 case '+': case '=': case GDK_KP_Add:
3468 pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
3470 case '-': case GDK_KP_Subtract:
3471 pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
3473 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3474 pixbuf_renderer_zoom_set(pr, 1.0);
3477 pixbuf_renderer_zoom_set(pr, 2.0);
3480 pixbuf_renderer_zoom_set(pr, 3.0);
3483 pixbuf_renderer_zoom_set(pr, 4.0);
3486 pixbuf_renderer_zoom_set(pr, -4.0);
3489 pixbuf_renderer_zoom_set(pr, -3.0);
3492 pixbuf_renderer_zoom_set(pr, -2.0);
3496 pan_fullscreen_toggle(pw, FALSE);
3501 pan_overlay_toggle(pw);
3504 case GDK_Delete: case GDK_KP_Delete:
3509 if (GTK_WIDGET_VISIBLE(pw->search_box))
3511 gtk_widget_grab_focus(pw->search_entry);
3515 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3523 pan_fullscreen_toggle(pw, TRUE);
3526 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3528 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3534 menu = pan_popup_menu(pw);
3535 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3540 if (event->state & GDK_CONTROL_MASK)
3543 switch (event->keyval)
3576 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3579 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3582 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3585 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3588 if (path) info_window_new(path, NULL);
3591 pan_window_close(pw);
3594 if (n != -1 && path)
3596 pan_fullscreen_toggle(pw, TRUE);
3597 start_editor_from_file(n, path);
3601 else if (event->state & GDK_SHIFT_MASK)
3608 switch (event->keyval)
3613 pan_fullscreen_toggle(pw, TRUE);
3616 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3618 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
3619 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3628 if (x != 0 || y!= 0)
3630 keyboard_scroll_calc(&x, &y, event);
3631 pixbuf_renderer_scroll(pr, x, y);
3638 *-----------------------------------------------------------------------------
3640 *-----------------------------------------------------------------------------
3643 static void pan_info_update(PanWindow *pw, PanItem *pi)
3649 gint x1, y1, x2, y2, x3, y3;
3652 if (pw->click_pi == pi) return;
3653 if (pi && !pi->fd) pi = NULL;
3655 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3660 printf("info set to %s\n", pi->fd->path);
3662 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3664 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3665 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3666 pan_item_set_key(pbox, "info");
3668 if (pi->type == ITEM_THUMB && pi->pixbuf)
3670 w = gdk_pixbuf_get_width(pi->pixbuf);
3671 h = gdk_pixbuf_get_height(pi->pixbuf);
3673 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3674 y1 = pi->y + (pi->height - h) / 2 + 8;
3678 x1 = pi->x + pi->width - 8;
3686 triangle_rect_region(x1, y1, x2, y2, x3, y3,
3689 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3690 x1, y1, x2, y2, x3, y3,
3691 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3692 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3693 pan_item_set_key(p, "info");
3694 pan_item_added(pw, p);
3696 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3697 _("Filename:"), TEXT_ATTR_BOLD,
3698 PAN_POPUP_TEXT_COLOR, 255);
3699 pan_item_set_key(plabel, "info");
3700 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3701 pi->fd->name, TEXT_ATTR_NONE,
3702 PAN_POPUP_TEXT_COLOR, 255);
3703 pan_item_set_key(p, "info");
3704 pan_item_size_by_item(pbox, p, 0);
3706 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3707 _("Date:"), TEXT_ATTR_BOLD,
3708 PAN_POPUP_TEXT_COLOR, 255);
3709 pan_item_set_key(plabel, "info");
3710 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3711 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3712 PAN_POPUP_TEXT_COLOR, 255);
3713 pan_item_set_key(p, "info");
3714 pan_item_size_by_item(pbox, p, 0);
3716 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3717 _("Size:"), TEXT_ATTR_BOLD,
3718 PAN_POPUP_TEXT_COLOR, 255);
3719 pan_item_set_key(plabel, "info");
3720 buf = text_from_size(pi->fd->size);
3721 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3722 buf, TEXT_ATTR_NONE,
3723 PAN_POPUP_TEXT_COLOR, 255);
3725 pan_item_set_key(p, "info");
3726 pan_item_size_by_item(pbox, p, 0);
3728 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3729 pan_item_added(pw, pbox);
3734 *-----------------------------------------------------------------------------
3736 *-----------------------------------------------------------------------------
3739 static void pan_search_status(PanWindow *pw, const gchar *text)
3741 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3744 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3752 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3754 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3755 if (!list) return FALSE;
3757 found = g_list_find(list, pw->click_pi);
3758 if (found && found->next)
3760 found = found->next;
3768 pan_info_update(pw, pi);
3769 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3771 buf = g_strdup_printf("%s ( %d / %d )",
3772 (path[0] == '/') ? _("path found") : _("filename found"),
3773 g_list_index(list, pi) + 1,
3774 g_list_length(list));
3775 pan_search_status(pw, buf);
3783 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3791 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3793 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3794 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3799 needle = g_utf8_strdown(text, -1);
3800 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3803 if (!list) return FALSE;
3805 found = g_list_find(list, pw->click_pi);
3806 if (found && found->next)
3808 found = found->next;
3816 pan_info_update(pw, pi);
3817 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3819 buf = g_strdup_printf("%s ( %d / %d )",
3821 g_list_index(list, pi) + 1,
3822 g_list_length(list));
3823 pan_search_status(pw, buf);
3831 static gint valid_date_separator(gchar c)
3833 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3836 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
3837 gint year, gint month, gint day,
3843 work = g_list_last(pw->list);
3851 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
3852 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
3856 tl = localtime(&pi->fd->date);
3861 match = (tl->tm_year == year - 1900);
3862 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3863 if (match && day > 0) match = (tl->tm_mday == day);
3865 if (match) list = g_list_prepend(list, pi);
3870 return g_list_reverse(list);
3873 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3889 if (!text) return FALSE;
3891 ptr = (gchar *)text;
3892 while (*ptr != '\0')
3894 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3899 if (t == -1) return FALSE;
3901 if (!lt) return FALSE;
3903 if (valid_date_separator(*text))
3906 mptr = (gchar *)text;
3910 year = (gint)strtol(text, &mptr, 10);
3911 if (mptr == text) return FALSE;
3914 if (*mptr != '\0' && valid_date_separator(*mptr))
3919 month = strtol(mptr, &dptr, 10);
3922 if (valid_date_separator(*dptr))
3924 month = lt->tm_mon + 1;
3932 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3936 day = strtol(dptr, &eptr, 10);
3946 year = lt->tm_year + 1900;
3948 else if (year < 100)
3957 month < -1 || month == 0 || month > 12 ||
3958 day < -1 || day == 0 || day > 31) return FALSE;
3960 t = date_to_time(year, month, day);
3961 if (t < 0) return FALSE;
3963 if (pw->layout == LAYOUT_CALENDAR)
3965 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
3971 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3972 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
3977 found = g_list_find(list, pw->search_pi);
3978 if (found && found->next)
3980 found = found->next;
3991 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
3993 pan_info_update(pw, NULL);
3994 pan_calendar_update(pw, pi);
3995 image_scroll_to_point(pw->imd,
3996 pi->x + pi->width / 2,
3997 pi->y + pi->height / 2, 0.5, 0.5);
4001 pan_info_update(pw, pi);
4002 image_scroll_to_point(pw->imd,
4003 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
4009 buf = date_value_string(t, DATE_LENGTH_MONTH);
4014 buf = g_strdup_printf("%d %s", day, tmp);
4020 buf = date_value_string(t, DATE_LENGTH_YEAR);
4025 buf_count = g_strdup_printf("( %d / %d )",
4026 g_list_index(list, pi) + 1,
4027 g_list_length(list));
4031 buf_count = g_strdup_printf("(%s)", _("no match"));
4034 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
4037 pan_search_status(pw, message);
4045 static void pan_search_activate_cb(const gchar *text, gpointer data)
4047 PanWindow *pw = data;
4051 tab_completion_append_to_history(pw->search_entry, text);
4053 if (pan_search_by_path(pw, text)) return;
4055 if ((pw->layout == LAYOUT_TIMELINE ||
4056 pw->layout == LAYOUT_CALENDAR) &&
4057 pan_search_by_date(pw, text))
4062 if (pan_search_by_partial(pw, text)) return;
4064 pan_search_status(pw, _("no match"));
4067 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
4069 PanWindow *pw = data;
4072 visible = GTK_WIDGET_VISIBLE(pw->search_box);
4073 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
4077 gtk_widget_hide(pw->search_box);
4078 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
4082 gtk_widget_show(pw->search_box);
4083 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
4084 gtk_widget_grab_focus(pw->search_entry);
4090 *-----------------------------------------------------------------------------
4091 * view window main routines
4092 *-----------------------------------------------------------------------------
4095 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
4097 PanWindow *pw = data;
4105 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
4106 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
4109 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
4112 switch (event->button)
4115 pan_info_update(pw, pi);
4117 if (!pi && pw->layout == LAYOUT_CALENDAR)
4119 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
4120 pan_calendar_update(pw, pi);
4126 pan_info_update(pw, pi);
4127 menu = pan_popup_menu(pw);
4128 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
4135 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
4138 PanWindow *pw = data;
4145 if (!(event->state & GDK_SHIFT_MASK))
4151 if (event->state & GDK_CONTROL_MASK)
4153 switch (event->direction)
4156 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
4157 (gint)event->x, (gint)event->y);
4159 case GDK_SCROLL_DOWN:
4160 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
4161 (gint)event->x, (gint)event->y);
4169 switch (event->direction)
4172 pixbuf_renderer_scroll(pr, 0, -h);
4174 case GDK_SCROLL_DOWN:
4175 pixbuf_renderer_scroll(pr, 0, h);
4177 case GDK_SCROLL_LEFT:
4178 pixbuf_renderer_scroll(pr, -w, 0);
4180 case GDK_SCROLL_RIGHT:
4181 pixbuf_renderer_scroll(pr, w, 0);
4189 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
4191 g_signal_connect(G_OBJECT(imd->pr), "clicked",
4192 G_CALLBACK(button_cb), pw);
4193 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
4194 G_CALLBACK(scroll_cb), pw);
4197 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
4199 PanWindow *pw = data;
4202 pw->imd = pw->imd_normal;
4205 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
4207 if (force_off && !pw->fs) return;
4211 fullscreen_stop(pw->fs);
4215 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
4216 pan_image_set_buttons(pw, pw->fs->imd);
4217 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
4218 G_CALLBACK(pan_window_key_press_cb), pw);
4220 pw->imd = pw->fs->imd;
4224 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
4226 PanWindow *pw = data;
4229 text = image_zoom_get_as_text(pw->imd);
4230 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4234 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
4236 PanWindow *pw = data;
4241 if (pr->scale == 0.0) return;
4243 pixbuf_renderer_get_visible_rect(pr, &rect);
4244 pixbuf_renderer_get_image_size(pr, &width, &height);
4246 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4247 adj->page_size = (gdouble)rect.width;
4248 adj->page_increment = adj->page_size / 2.0;
4249 adj->step_increment = 48.0 / pr->scale;
4251 adj->upper = MAX((gdouble)width, 1.0);
4252 adj->value = (gdouble)rect.x;
4254 pref_signal_block_data(pw->scrollbar_h, pw);
4255 gtk_adjustment_changed(adj);
4256 gtk_adjustment_value_changed(adj);
4257 pref_signal_unblock_data(pw->scrollbar_h, pw);
4259 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4260 adj->page_size = (gdouble)rect.height;
4261 adj->page_increment = adj->page_size / 2.0;
4262 adj->step_increment = 48.0 / pr->scale;
4264 adj->upper = MAX((gdouble)height, 1.0);
4265 adj->value = (gdouble)rect.y;
4267 pref_signal_block_data(pw->scrollbar_v, pw);
4268 gtk_adjustment_changed(adj);
4269 gtk_adjustment_value_changed(adj);
4270 pref_signal_unblock_data(pw->scrollbar_v, pw);
4273 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4275 PanWindow *pw = data;
4279 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4281 if (!pr->scale) return;
4283 x = (gint)gtk_range_get_value(range);
4285 pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
4288 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4290 PanWindow *pw = data;
4294 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4296 if (!pr->scale) return;
4298 y = (gint)gtk_range_get_value(range);
4300 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
4303 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4305 PanWindow *pw = data;
4307 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4308 pan_window_layout_update_idle(pw);
4311 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4313 PanWindow *pw = data;
4315 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4316 pan_window_layout_update_idle(pw);
4319 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4321 PanWindow *pw = data;
4324 path = remove_trailing_slash(new_text);
4325 parse_out_relatives(path);
4329 warning_dialog(_("Folder not found"),
4330 _("The entered path is not a folder"),
4331 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4335 tab_completion_append_to_history(pw->path_entry, path);
4338 pw->path = g_strdup(path);
4340 pan_window_layout_update_idle(pw);
4343 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4345 PanWindow *pw = data;
4348 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4350 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4351 pan_window_entry_activate_cb(text, pw);
4355 static void pan_window_close(PanWindow *pw)
4357 pan_window_list = g_list_remove(pan_window_list, pw);
4359 if (pw->idle_id != -1)
4361 g_source_remove(pw->idle_id);
4364 pan_fullscreen_toggle(pw, TRUE);
4365 gtk_widget_destroy(pw->window);
4367 pan_window_items_free(pw);
4374 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4376 PanWindow *pw = data;
4378 pan_window_close(pw);
4382 static void pan_window_new_real(const gchar *path)
4391 GdkGeometry geometry;
4393 pw = g_new0(PanWindow, 1);
4395 pw->path = g_strdup(path);
4396 pw->layout = LAYOUT_TIMELINE;
4397 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4398 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4399 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4403 pw->overlay_id = -1;
4406 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4408 geometry.min_width = 8;
4409 geometry.min_height = 8;
4410 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4412 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4413 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
4414 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4415 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4417 window_set_icon(pw->window, NULL, NULL);
4419 vbox = gtk_vbox_new(FALSE, 0);
4420 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4421 gtk_widget_show(vbox);
4423 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4425 pref_spacer(box, 0);
4426 pref_label_new(box, _("Location:"));
4427 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
4428 pan_window_entry_activate_cb, pw);
4429 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4430 G_CALLBACK(pan_window_entry_change_cb), pw);
4431 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4432 gtk_widget_show(combo);
4434 combo = gtk_combo_box_new_text();
4435 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4436 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4437 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4438 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4439 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4441 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4442 g_signal_connect(G_OBJECT(combo), "changed",
4443 G_CALLBACK(pan_window_layout_change_cb), pw);
4444 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4445 gtk_widget_show(combo);
4447 combo = gtk_combo_box_new_text();
4448 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4449 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4450 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4451 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4452 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4453 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4454 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4455 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4456 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4457 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4459 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4460 g_signal_connect(G_OBJECT(combo), "changed",
4461 G_CALLBACK(pan_window_layout_size_cb), pw);
4462 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4463 gtk_widget_show(combo);
4465 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4466 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4467 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4469 pw->imd = image_new(TRUE);
4470 pw->imd_normal = pw->imd;
4472 g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
4473 G_CALLBACK(pan_window_image_zoom_cb), pw);
4474 g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
4475 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4477 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4478 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4479 gtk_widget_show(GTK_WIDGET(pw->imd->widget));
4481 pan_window_dnd_init(pw);
4483 pan_image_set_buttons(pw, pw->imd);
4485 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4486 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4487 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4488 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4489 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4490 gtk_widget_show(pw->scrollbar_h);
4492 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4493 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4494 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4495 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4496 0, GTK_FILL | GTK_EXPAND, 0, 0);
4497 gtk_widget_show(pw->scrollbar_v);
4501 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4502 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4504 pref_spacer(pw->search_box, 0);
4505 pref_label_new(pw->search_box, _("Find:"));
4507 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4508 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4509 gtk_widget_show(hbox);
4511 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4512 pan_search_activate_cb, pw);
4513 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4514 gtk_widget_show(combo);
4516 pw->search_label = gtk_label_new("");
4517 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4518 gtk_widget_show(pw->search_label);
4522 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4524 frame = gtk_frame_new(NULL);
4525 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4526 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4527 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4528 gtk_widget_show(frame);
4530 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4531 gtk_container_add(GTK_CONTAINER(frame), hbox);
4532 gtk_widget_show(hbox);
4534 pref_spacer(hbox, 0);
4535 pw->label_message = pref_label_new(hbox, "");
4537 frame = gtk_frame_new(NULL);
4538 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4539 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4540 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4541 gtk_widget_show(frame);
4543 pw->label_zoom = gtk_label_new("");
4544 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4545 gtk_widget_show(pw->label_zoom);
4547 pw->search_button = gtk_toggle_button_new();
4548 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4549 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4550 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4551 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4552 gtk_widget_show(hbox);
4553 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4554 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4555 gtk_widget_show(pw->search_button_arrow);
4556 pref_label_new(hbox, _("Find"));
4558 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4559 gtk_widget_show(pw->search_button);
4560 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4561 G_CALLBACK(pan_search_toggle_cb), pw);
4563 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4564 G_CALLBACK(pan_window_delete_cb), pw);
4565 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4566 G_CALLBACK(pan_window_key_press_cb), pw);
4568 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4570 pan_window_layout_update_idle(pw);
4572 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4573 gtk_widget_show(pw->window);
4575 pan_window_list = g_list_append(pan_window_list, pw);
4579 *-----------------------------------------------------------------------------
4580 * peformance warnings
4581 *-----------------------------------------------------------------------------
4584 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4588 generic_dialog_close(gd);
4590 pan_window_new_real(path);
4594 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4598 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4599 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4602 static gint pan_warning(const gchar *path)
4608 GtkWidget *ct_button;
4611 if (enable_thumb_caching &&
4612 thumbnail_spec_standard) return FALSE;
4614 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4615 if (hide_dlg) return FALSE;
4617 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4619 gd->data = g_strdup(path);
4620 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4621 pan_warning_ok_cb, TRUE);
4623 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4624 _("Pan view performance may be poor."),
4625 _("To improve performance of thumbnails in the pan view the"
4626 " following options can be enabled. Note that both options"
4627 " must be enabled to notice a change in performance."));
4629 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4630 pref_spacer(group, PREF_PAD_INDENT);
4631 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4633 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4634 enable_thumb_caching, &enable_thumb_caching);
4635 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4636 thumbnail_spec_standard, &thumbnail_spec_standard);
4637 pref_checkbox_link_sensitivity(ct_button, button);
4641 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4642 G_CALLBACK(pan_warning_hide_cb), NULL);
4644 gtk_widget_show(gd->dialog);
4651 *-----------------------------------------------------------------------------
4653 *-----------------------------------------------------------------------------
4656 void pan_window_new(const gchar *path)
4658 if (pan_warning(path)) return;
4660 pan_window_new_real(path);
4664 *-----------------------------------------------------------------------------
4666 *-----------------------------------------------------------------------------
4669 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4671 PanWindow *pw = data;
4674 path = pan_menu_click_path(pw);
4677 pan_fullscreen_toggle(pw, TRUE);
4678 view_window_new(path);
4682 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4688 pw = submenu_item_get_data(widget);
4689 n = GPOINTER_TO_INT(data);
4692 path = pan_menu_click_path(pw);
4695 pan_fullscreen_toggle(pw, TRUE);
4696 start_editor_from_file(n, path);
4700 static void pan_info_cb(GtkWidget *widget, gpointer data)
4702 PanWindow *pw = data;
4705 path = pan_menu_click_path(pw);
4706 if (path) info_window_new(path, NULL);
4709 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4711 PanWindow *pw = data;
4713 image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
4716 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4718 PanWindow *pw = data;
4720 image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
4723 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4725 PanWindow *pw = data;
4727 image_zoom_set(pw->imd, 1.0);
4730 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4732 PanWindow *pw = data;
4735 path = pan_menu_click_path(pw);
4736 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4739 static void pan_move_cb(GtkWidget *widget, gpointer data)
4741 PanWindow *pw = data;
4744 path = pan_menu_click_path(pw);
4745 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4748 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4750 PanWindow *pw = data;
4753 path = pan_menu_click_path(pw);
4754 if (path) file_util_rename(path, NULL, pw->imd->widget);
4757 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4759 PanWindow *pw = data;
4762 path = pan_menu_click_path(pw);
4763 if (path) file_util_delete(path, NULL, pw->imd->widget);
4766 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4768 PanWindow *pw = data;
4770 pan_fullscreen_toggle(pw, FALSE);
4773 static void pan_close_cb(GtkWidget *widget, gpointer data)
4775 PanWindow *pw = data;
4777 pan_window_close(pw);
4780 static GtkWidget *pan_popup_menu(PanWindow *pw)
4786 active = (pw->click_pi != NULL);
4788 menu = popup_menu_short_lived();
4790 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4791 G_CALLBACK(pan_zoom_in_cb), pw);
4792 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4793 G_CALLBACK(pan_zoom_out_cb), pw);
4794 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4795 G_CALLBACK(pan_zoom_1_1_cb), pw);
4796 menu_item_add_divider(menu);
4798 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4799 gtk_widget_set_sensitive(item, active);
4801 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4802 G_CALLBACK(pan_info_cb), pw);
4804 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4805 G_CALLBACK(pan_new_window_cb), pw);
4807 menu_item_add_divider(menu);
4808 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4809 G_CALLBACK(pan_copy_cb), pw);
4810 menu_item_add_sensitive(menu, _("_Move..."), active,
4811 G_CALLBACK(pan_move_cb), pw);
4812 menu_item_add_sensitive(menu, _("_Rename..."), active,
4813 G_CALLBACK(pan_rename_cb), pw);
4814 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4815 G_CALLBACK(pan_delete_cb), pw);
4817 menu_item_add_divider(menu);
4821 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4825 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4828 menu_item_add_divider(menu);
4829 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4835 *-----------------------------------------------------------------------------
4837 *-----------------------------------------------------------------------------
4840 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4842 GtkSelectionData *selection_data, guint info,
4843 guint time, gpointer data)
4845 PanWindow *pw = data;
4847 if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
4849 if (info == TARGET_URI_LIST)
4853 list = uri_list_from_text(selection_data->data, TRUE);
4854 if (list && isdir((gchar *)list->data))
4856 gchar *path = list->data;
4859 pw->path = g_strdup(path);
4861 pan_window_layout_update_idle(pw);
4864 path_list_free(list);
4868 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4869 GtkSelectionData *selection_data, guint info,
4870 guint time, gpointer data)
4872 PanWindow *pw = data;
4875 path = pan_menu_click_path(pw);
4885 case TARGET_URI_LIST:
4888 case TARGET_TEXT_PLAIN:
4893 list = g_list_append(NULL, (gchar *)path);
4894 text = uri_text_from_list(list, &len, plain_text);
4898 gtk_selection_data_set (selection_data, selection_data->target,
4905 gtk_selection_data_set (selection_data, selection_data->target,
4910 static void pan_window_dnd_init(PanWindow *pw)
4914 widget = pw->imd->pr;
4916 gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
4917 dnd_file_drag_types, dnd_file_drag_types_count,
4918 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4919 g_signal_connect(G_OBJECT(widget), "drag_data_get",
4920 G_CALLBACK(pan_window_set_dnd_data), pw);
4922 gtk_drag_dest_set(widget,
4923 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4924 dnd_file_drop_types, dnd_file_drop_types_count,
4925 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4926 g_signal_connect(G_OBJECT(widget), "drag_data_received",
4927 G_CALLBACK(pan_window_get_dnd_data), pw);
4931 *-----------------------------------------------------------------------------
4932 * maintenance (for rename, move, remove)
4933 *-----------------------------------------------------------------------------