7 * This software is released under the GNU General Public License (GNU GPL).
8 * Please read the included file COPYING for more information.
9 * This software comes with no warranty of any kind, use at your own risk!
20 #include "fullscreen.h"
22 #include "image-load.h"
26 #include "pixbuf_util.h"
29 #include "ui_bookmark.h"
30 #include "ui_fileops.h"
33 #include "ui_tabcomp.h"
35 #include <gdk/gdkkeysyms.h> /* for keyboard values */
39 #define PAN_WINDOW_DEFAULT_WIDTH 720
40 #define PAN_WINDOW_DEFAULT_HEIGHT 500
42 #define PAN_TILE_SIZE 512
44 #define PAN_THUMB_SIZE_DOTS 4
45 #define PAN_THUMB_SIZE_NONE 24
46 #define PAN_THUMB_SIZE_SMALL 64
47 #define PAN_THUMB_SIZE_NORMAL 128
48 #define PAN_THUMB_SIZE_LARGE 256
49 #define PAN_THUMB_SIZE pw->thumb_size
51 #define PAN_THUMB_GAP_DOTS 2
52 #define PAN_THUMB_GAP_SMALL 14
53 #define PAN_THUMB_GAP_NORMAL 30
54 #define PAN_THUMB_GAP_LARGE 40
55 #define PAN_THUMB_GAP_HUGE 50
56 #define PAN_THUMB_GAP pw->thumb_gap
58 #define PAN_SHADOW_OFFSET 6
59 #define PAN_SHADOW_FADE 5
60 #define PAN_SHADOW_COLOR 0, 0, 0
61 #define PAN_SHADOW_ALPHA 64
63 #define PAN_OUTLINE_THICKNESS 1
64 #define PAN_OUTLINE_COLOR_1 255, 255, 255
65 #define PAN_OUTLINE_COLOR_2 64, 64, 64
66 #define PAN_OUTLINE_ALPHA 180
68 #define PAN_BACKGROUND_COLOR 255, 255, 230
70 #define PAN_GRID_SIZE 10
71 #define PAN_GRID_COLOR 0, 0, 0
72 #define PAN_GRID_ALPHA 20
74 #define PAN_FOLDER_BOX_COLOR 0, 0, 255
75 #define PAN_FOLDER_BOX_ALPHA 10
76 #define PAN_FOLDER_BOX_BORDER 20
78 #define PAN_FOLDER_BOX_OUTLINE_THICKNESS 4
79 #define PAN_FOLDER_BOX_OUTLINE_COLOR 0, 0, 255
80 #define PAN_FOLDER_BOX_OUTLINE_ALPHA 64
82 #define PAN_TEXT_BORDER_SIZE 4
83 #define PAN_TEXT_COLOR 0, 0, 0
85 #define PAN_POPUP_COLOR 255, 255, 220
86 #define PAN_POPUP_ALPHA 255
87 #define PAN_POPUP_BORDER 1
88 #define PAN_POPUP_BORDER_COLOR 0, 0, 0
89 #define PAN_POPUP_TEXT_COLOR 0, 0, 0
91 #define PAN_GROUP_MAX 16
93 #define ZOOM_INCREMENT 1.0
94 #define ZOOM_LABEL_WIDTH 64
99 LAYOUT_FOLDERS_LINEAR,
100 LAYOUT_FOLDERS_FLOWER,
105 LAYOUT_SIZE_THUMB_DOTS = 0,
106 LAYOUT_SIZE_THUMB_NONE,
107 LAYOUT_SIZE_THUMB_SMALL,
108 LAYOUT_SIZE_THUMB_NORMAL,
109 LAYOUT_SIZE_THUMB_LARGE,
128 TEXT_ATTR_BOLD = 1 << 0,
129 TEXT_ATTR_HEADING = 1 << 1,
130 TEXT_ATTR_MARKUP = 1 << 2
141 typedef struct _PanItem PanItem;
156 TextAttrType text_attr;
174 typedef struct _PanWindow PanWindow;
179 ImageWindow *imd_normal;
182 GtkWidget *path_entry;
184 GtkWidget *label_message;
185 GtkWidget *label_zoom;
187 GtkWidget *search_box;
188 GtkWidget *search_entry;
189 GtkWidget *search_label;
190 GtkWidget *search_button;
191 GtkWidget *search_button_arrow;
193 GtkWidget *scrollbar_h;
194 GtkWidget *scrollbar_v;
223 typedef struct _PanCacheData PanCacheData;
224 struct _PanCacheData {
230 static GList *pan_window_list = NULL;
233 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend);
235 static GtkWidget *pan_popup_menu(PanWindow *pw);
236 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off);
237 static void pan_overlay_toggle(PanWindow *pw);
239 static void pan_window_close(PanWindow *pw);
241 static void pan_window_dnd_init(PanWindow *pw);
244 static gint util_clip_region(gint x, gint y, gint w, gint h,
245 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
246 gint *rx, gint *ry, gint *rw, gint *rh)
248 if (clip_x + clip_w <= x ||
250 clip_y + clip_h <= y ||
256 *rx = MAX(x, clip_x);
257 *rw = MIN((x + w), (clip_x + clip_w)) - *rx;
259 *ry = MAX(y, clip_y);
260 *rh = MIN((y + h), (clip_y + clip_h)) - *ry;
265 static gint util_clip_region_test(gint x, gint y, gint w, gint h,
266 gint clip_x, gint clip_y, gint clip_w, gint clip_h)
270 return util_clip_region(x, y, w, h,
271 clip_x, clip_y, clip_w, clip_h,
284 static gint date_compare(time_t a, time_t b, DateLengthType length)
289 if (length == DATE_LENGTH_EXACT) return (a == b);
291 if (!localtime_r(&a, &ta) ||
292 !localtime_r(&b, &tb)) return FALSE;
294 if (ta.tm_year != tb.tm_year) return FALSE;
295 if (length == DATE_LENGTH_YEAR) return TRUE;
297 if (ta.tm_mon != tb.tm_mon) return FALSE;
298 if (length == DATE_LENGTH_MONTH) return TRUE;
300 if (length == DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
302 if (ta.tm_mday != tb.tm_mday) return FALSE;
303 if (length == DATE_LENGTH_DAY) return TRUE;
305 return (ta.tm_hour == tb.tm_hour);
308 static gchar *date_value_string(time_t d, DateLengthType length)
312 gchar *format = NULL;
314 if (!localtime_r(&d, &td)) return g_strdup("");
318 case DATE_LENGTH_DAY:
319 return g_strdup_printf("%d", td.tm_mday);
321 case DATE_LENGTH_WEEK:
324 case DATE_LENGTH_MONTH:
327 case DATE_LENGTH_YEAR:
328 return g_strdup_printf("%d", td.tm_year + 1900);
330 case DATE_LENGTH_EXACT:
332 return g_strdup(text_from_time(d));
337 if (format && strftime(buf, sizeof(buf), format, &td) > 0)
339 gchar *ret = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
346 static time_t date_to_time(gint year, gint month, gint day)
353 lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
354 lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
355 lt.tm_year = year - 1900;
362 *-----------------------------------------------------------------------------
364 *-----------------------------------------------------------------------------
367 static void triangle_rect_region(gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
368 gint *rx, gint *ry, gint *rw, gint *rh)
376 tw = MAX(abs(x1 - x2), abs(x2 - x3));
377 tw = MAX(tw, abs(x3 - x1));
378 th = MAX(abs(y1 - y2), abs(y2 - y3));
379 th = MAX(th, abs(y3 - y1));
387 static void pixbuf_draw_triangle(GdkPixbuf *pb,
388 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
389 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
390 guint8 r, guint8 g, guint8 b, guint8 a)
406 pw = gdk_pixbuf_get_width(pb);
407 ph = gdk_pixbuf_get_height(pb);
409 if (!util_clip_region(0, 0, pw, ph,
410 clip_x, clip_y, clip_w, clip_h,
411 &rx, &ry, &rw, &rh)) return;
413 triangle_rect_region(x1, y1, x2, y2, x3, y3,
416 if (!util_clip_region(rx, ry, rw, rh,
418 &fx1, &fy1, &fw, &fh)) return;
422 p_alpha = gdk_pixbuf_get_has_alpha(pb);
423 prs = gdk_pixbuf_get_rowstride(pb);
424 p_pix = gdk_pixbuf_get_pixels(pb);
426 p_step = (p_alpha) ? 4 : 3;
427 for (i = fy1; i < fy2; i++)
429 pp = p_pix + i * prs + (fx1 * p_step);
430 for (j = fx1; j < fx2; j++)
434 z1 = (y1 - y2)*(j - x2) + (x2 - x1)*(i - y2);
435 z2 = (y2 - y3)*(j - x3) + (x3 - x2)*(i - y3);
438 z2 = (y3 - y1)*(j - x1) + (x1 - x3)*(i - y1);
441 pp[0] = (r * a + pp[0] * (256-a)) >> 8;
442 pp[1] = (g * a + pp[1] * (256-a)) >> 8;
443 pp[2] = (b * a + pp[2] * (256-a)) >> 8;
451 static void pixbuf_draw_line(GdkPixbuf *pb,
452 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
453 gint x1, gint y1, gint x2, gint y2,
454 guint8 r, guint8 g, guint8 b, guint8 a)
459 gint fx1, fy1, fx2, fy2;
465 gdouble xstep, ystep;
472 pw = gdk_pixbuf_get_width(pb);
473 ph = gdk_pixbuf_get_height(pb);
475 if (!util_clip_region(0, 0, pw, ph,
476 clip_x, clip_y, clip_w, clip_h,
477 &rx, &ry, &rw, &rh)) return;
489 if (xa == 0 && ya == 0) return;
491 nt = sqrt(xd * xd + yd * yd);
493 nt = (xa > ya) ? xa : ya;
494 xstep = (double)xd / nt;
495 ystep = (double)yd / nt;
497 p_alpha = gdk_pixbuf_get_has_alpha(pb);
498 prs = gdk_pixbuf_get_rowstride(pb);
499 p_pix = gdk_pixbuf_get_pixels(pb);
501 p_step = (p_alpha) ? 4 : 3;
505 for (n = 0; n < nt; n++)
510 if (x >= fx1 && x < fx2 &&
513 pp = p_pix + y * prs + x * p_step;
514 *pp = (r * a + *pp * (256-a)) >> 8;
516 *pp = (g * a + *pp * (256-a)) >> 8;
518 *pp = (b * a + *pp * (256-a)) >> 8;
525 static void pixbuf_draw_fade_linear(guchar *p_pix, gint prs, gint p_alpha,
526 gint s, gint vertical, gint border,
527 gint x1, gint y1, gint x2, gint y2,
528 guint8 r, guint8 g, guint8 b, guint8 a)
535 p_step = (p_alpha) ? 4 : 3;
536 for (j = y1; j < y2; j++)
538 pp = p_pix + j * prs + x1 * p_step;
539 if (!vertical) n = a - a * abs(j - s) / border;
540 for (i = x1; i < x2; i++)
542 if (vertical) n = a - a * abs(i - s) / border;
543 *pp = (r * n + *pp * (256-n)) >> 8;
545 *pp = (g * n + *pp * (256-n)) >> 8;
547 *pp = (b * n + *pp * (256-n)) >> 8;
554 static void pixbuf_draw_fade_radius(guchar *p_pix, gint prs, gint p_alpha,
555 gint sx, gint sy, gint border,
556 gint x1, gint y1, gint x2, gint y2,
557 guint8 r, guint8 g, guint8 b, guint8 a)
563 p_step = (p_alpha) ? 4 : 3;
564 for (j = y1; j < y2; j++)
566 pp = p_pix + j * prs + x1 * p_step;
567 for (i = x1; i < x2; i++)
572 r = MIN(border, (gint)sqrt((i-sx)*(i-sx) + (j-sy)*(j-sy)));
573 n = a - a * r / border;
574 *pp = (r * n + *pp * (256-n)) >> 8;
576 *pp = (g * n + *pp * (256-n)) >> 8;
578 *pp = (b * n + *pp * (256-n)) >> 8;
585 static void pixbuf_draw_shadow(GdkPixbuf *pb,
586 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
587 gint x, gint y, gint w, gint h, gint border,
588 guint8 r, guint8 g, guint8 b, guint8 a)
598 pw = gdk_pixbuf_get_width(pb);
599 ph = gdk_pixbuf_get_height(pb);
601 if (!util_clip_region(0, 0, pw, ph,
602 clip_x, clip_y, clip_w, clip_h,
603 &rx, &ry, &rw, &rh)) return;
605 p_alpha = gdk_pixbuf_get_has_alpha(pb);
606 prs = gdk_pixbuf_get_rowstride(pb);
607 p_pix = gdk_pixbuf_get_pixels(pb);
609 if (util_clip_region(x + border, y + border, w - border * 2, h - border * 2,
613 pixbuf_draw_rect_fill(pb, fx, fy, fw, fh, r, g, b, a);
616 if (border < 1) return;
618 if (util_clip_region(x, y + border, border, h - border * 2,
622 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
623 x + border, TRUE, border,
624 fx, fy, fx + fw, fy + fh,
627 if (util_clip_region(x + w - border, y + border, border, h - border * 2,
631 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
632 x + w - border, TRUE, border,
633 fx, fy, fx + fw, fy + fh,
636 if (util_clip_region(x + border, y, w - border * 2, border,
640 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
641 y + border, FALSE, border,
642 fx, fy, fx + fw, fy + fh,
645 if (util_clip_region(x + border, y + h - border, w - border * 2, border,
649 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
650 y + h - border, FALSE, border,
651 fx, fy, fx + fw, fy + fh,
654 if (util_clip_region(x, y, border, border,
658 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
659 x + border, y + border, border,
660 fx, fy, fx + fw, fy + fh,
663 if (util_clip_region(x + w - border, y, border, border,
667 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
668 x + w - border, y + border, border,
669 fx, fy, fx + fw, fy + fh,
672 if (util_clip_region(x, y + h - border, border, border,
676 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
677 x + border, y + h - border, border,
678 fx, fy, fx + fw, fy + fh,
681 if (util_clip_region(x + w - border, y + h - border, border, border,
685 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
686 x + w - border, y + h - border, border,
687 fx, fy, fx + fw, fy + fh,
694 *-----------------------------------------------------------------------------
696 *-----------------------------------------------------------------------------
699 static void pan_cache_free(PanWindow *pw)
703 work = pw->cache_list;
711 cache_sim_data_free(pc->cd);
712 file_data_free((FileData *)pc);
715 g_list_free(pw->cache_list);
716 pw->cache_list = NULL;
718 filelist_free(pw->cache_todo);
719 pw->cache_todo = NULL;
726 static void pan_cache_fill(PanWindow *pw, const gchar *path)
732 list = pan_window_layout_list(path, SORT_NAME, TRUE);
733 pw->cache_todo = g_list_reverse(list);
735 pw->cache_total = g_list_length(pw->cache_todo);
738 static gint pan_cache_step(PanWindow *pw)
742 CacheData *cd = NULL;
744 if (!pw->cache_todo) return FALSE;
746 fd = pw->cache_todo->data;
747 pw->cache_todo = g_list_remove(pw->cache_todo, fd);
749 if (enable_thumb_caching)
753 found = cache_find_location(CACHE_TYPE_SIM, fd->path);
754 if (found && filetime(found) == fd->date)
756 cd = cache_sim_data_load(found);
761 if (!cd) cd = cache_sim_data_new();
765 cd->dimensions = image_load_dimensions(fd->path, &cd->width, &cd->height);
766 if (enable_thumb_caching &&
772 base = cache_get_location(CACHE_TYPE_SIM, fd->path, FALSE, &mode);
773 if (cache_ensure_dir_exists(base, mode))
776 cd->path = cache_get_location(CACHE_TYPE_SIM, fd->path, TRUE, NULL);
777 if (cache_sim_data_save(cd))
779 filetime_set(cd->path, filetime(fd->path));
788 pc = g_new0(PanCacheData, 1);
789 memcpy(pc, fd, sizeof(FileData));
794 pw->cache_list = g_list_prepend(pw->cache_list, pc);
801 *-----------------------------------------------------------------------------
803 *-----------------------------------------------------------------------------
806 static void pan_item_free(PanItem *pi)
810 if (pi->pixbuf) g_object_unref(pi->pixbuf);
811 if (pi->fd) file_data_free(pi->fd);
819 static void pan_window_items_free(PanWindow *pw)
826 PanItem *pi = work->data;
832 g_list_free(pw->list);
835 g_list_free(pw->queue);
839 image_loader_free(pw->il);
842 thumb_loader_free(pw->tl);
848 static PanItem *pan_item_new_thumb(PanWindow *pw, FileData *fd, gint x, gint y)
852 pi = g_new0(PanItem, 1);
853 pi->type = ITEM_THUMB;
857 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
858 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
864 pw->list = g_list_prepend(pw->list, pi);
869 static PanItem *pan_item_new_box(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
871 guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
872 guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
876 pi = g_new0(PanItem, 1);
884 pi->color_r = base_r;
885 pi->color_g = base_g;
886 pi->color_b = base_b;
887 pi->color_a = base_a;
889 pi->color2_r = bord_r;
890 pi->color2_g = bord_g;
891 pi->color2_b = bord_b;
892 pi->color2_a = bord_a;
893 pi->border = border_size;
895 pw->list = g_list_prepend(pw->list, pi);
900 static void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
904 if (!pi || pi->type != ITEM_BOX) return;
909 pi->width -= shadow[0];
910 pi->height -= shadow[0];
913 shadow = g_new0(gint, 2);
918 pi->height += offset;
924 static PanItem *pan_item_new_tri(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
925 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
926 guint8 r, guint8 g, guint8 b, guint8 a)
931 pi = g_new0(PanItem, 1);
932 pi->type = ITEM_TRIANGLE;
943 coord = g_new0(gint, 6);
953 pi->border = BORDER_NONE;
955 pw->list = g_list_prepend(pw->list, pi);
960 static void pan_item_tri_border(PanItem *pi, gint borders,
961 guint8 r, guint8 g, guint8 b, guint8 a)
963 if (!pi || pi->type != ITEM_TRIANGLE) return;
965 pi->border = borders;
973 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
977 layout = gtk_widget_create_pango_layout(widget, NULL);
979 if (pi->text_attr & TEXT_ATTR_MARKUP)
981 pango_layout_set_markup(layout, pi->text, -1);
985 if (pi->text_attr & TEXT_ATTR_BOLD ||
986 pi->text_attr & TEXT_ATTR_HEADING)
991 pal = pango_attr_list_new();
992 if (pi->text_attr & TEXT_ATTR_BOLD)
994 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
996 pa->end_index = G_MAXINT;
997 pango_attr_list_insert(pal, pa);
999 if (pi->text_attr & TEXT_ATTR_HEADING)
1001 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
1002 pa->start_index = 0;
1003 pa->end_index = G_MAXINT;
1004 pango_attr_list_insert(pal, pa);
1006 pango_layout_set_attributes(layout, pal);
1007 pango_attr_list_unref(pal);
1010 pango_layout_set_text(layout, pi->text, -1);
1014 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
1016 PangoLayout *layout;
1018 if (!pi || !pi->text || !widget) return;
1020 layout = pan_item_text_layout(pi, widget);
1021 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
1022 g_object_unref(G_OBJECT(layout));
1024 pi->width += PAN_TEXT_BORDER_SIZE * 2;
1025 pi->height += PAN_TEXT_BORDER_SIZE * 2;
1028 static PanItem *pan_item_new_text(PanWindow *pw, gint x, gint y, const gchar *text, TextAttrType attr,
1029 guint8 r, guint8 g, guint8 b, guint8 a)
1033 pi = g_new0(PanItem, 1);
1034 pi->type = ITEM_TEXT;
1037 pi->text = g_strdup(text);
1038 pi->text_attr = attr;
1045 pan_item_text_compute_size(pi, pw->imd->widget);
1047 pw->list = g_list_prepend(pw->list, pi);
1052 static void pan_item_set_key(PanItem *pi, const gchar *key)
1059 pi->key = g_strdup(key);
1063 static void pan_item_added(PanWindow *pw, PanItem *pi)
1066 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1069 static void pan_item_remove(PanWindow *pw, PanItem *pi)
1073 if (pw->click_pi == pi) pw->click_pi = NULL;
1075 pw->list = g_list_remove(pw->list, pi);
1076 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1080 static void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
1082 if (!pi || !child) return;
1084 if (pi->x + pi->width < child->x + child->width + border)
1085 pi->width = child->x + child->width + border - pi->x;
1087 if (pi->y + pi->height < child->y + child->height + border)
1088 pi->height = child->y + child->height + border - pi->y;
1091 static void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
1095 if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
1096 if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
1099 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
1106 if (!pi->fd) return;
1108 work = pw->cache_list;
1117 path = ((FileData *)pc)->path;
1119 if (pc->cd && pc->cd->dimensions &&
1120 path && strcmp(path, pi->fd->path) == 0)
1122 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
1123 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
1125 pw->cache_list = g_list_remove(pw->cache_list, pc);
1126 cache_sim_data_free(pc->cd);
1127 file_data_free((FileData *)pc);
1133 static PanItem *pan_item_new_image(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
1137 pi = g_new0(PanItem, 1);
1138 pi->type = ITEM_IMAGE;
1143 pan_item_image_find_size(pw, pi, w, h);
1145 pw->list = g_list_prepend(pw->list, pi);
1150 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
1154 if (!key) return NULL;
1156 work = g_list_last(pw->list);
1162 if ((pi->type == type || type == ITEM_NONE) &&
1163 pi->key && strcmp(pi->key, key) == 0)
1173 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1174 static PanItem *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1175 gint ignore_case, gint partial)
1179 if (!path) return NULL;
1180 if (partial && path[0] == '/') return NULL;
1182 work = g_list_last(pw->list);
1188 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1192 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) return pi;
1194 else if (pi->fd->name)
1203 haystack = g_utf8_strdown(pi->fd->name, -1);
1204 match = (strstr(haystack, path) != NULL);
1206 if (match) return pi;
1210 if (strstr(pi->fd->name, path)) return pi;
1213 else if (ignore_case)
1215 if (strcasecmp(path, pi->fd->name) == 0) return pi;
1219 if (strcmp(path, pi->fd->name) == 0) return pi;
1229 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y)
1233 if (x < 0 || x >= pw->imd->image_width ||
1234 y < 0 || y >= pw->imd->image_height) return NULL;
1242 if ((pi->type == type || type == ITEM_NONE) &&
1243 x >= pi->x && x < pi->x + pi->width &&
1244 y >= pi->y && y < pi->y + pi->height)
1255 *-----------------------------------------------------------------------------
1257 *-----------------------------------------------------------------------------
1260 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend)
1262 GList *flist = NULL;
1263 GList *dlist = NULL;
1267 filelist_read(path, &flist, &dlist);
1268 if (sort != SORT_NONE)
1270 flist = filelist_sort(flist, sort, ascend);
1271 dlist = filelist_sort(dlist, sort, ascend);
1281 folders = g_list_remove(folders, fd);
1283 if (filelist_read(fd->path, &flist, &dlist))
1285 if (sort != SORT_NONE)
1287 flist = filelist_sort(flist, sort, ascend);
1288 dlist = filelist_sort(dlist, sort, ascend);
1291 result = g_list_concat(result, flist);
1292 folders = g_list_concat(dlist, folders);
1301 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1309 list = pan_window_layout_list(path, SORT_NAME, TRUE);
1311 grid_size = (gint)sqrt((double)g_list_length(list));
1312 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1314 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1318 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1323 *width = PAN_FOLDER_BOX_BORDER * 2;
1324 *height = PAN_FOLDER_BOX_BORDER * 2;
1337 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1339 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1341 x += pi->width + PAN_THUMB_GAP;
1342 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1351 pi = pan_item_new_thumb(pw, fd, x, y);
1353 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1357 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1360 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1366 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1369 gint x1, y1, x2, y2;
1384 if (x1 > pi->x) x1 = pi->x;
1385 if (y1 > pi->y) y1 = pi->y;
1386 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1387 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1390 x1 -= PAN_FOLDER_BOX_BORDER;
1391 y1 -= PAN_FOLDER_BOX_BORDER;
1392 x2 += PAN_FOLDER_BOX_BORDER;
1393 y2 += PAN_FOLDER_BOX_BORDER;
1406 if (pi->type == ITEM_TRIANGLE && pi->data)
1420 if (width) *width = x2 - x1;
1421 if (height) *height = y2 - y1;
1424 typedef struct _FlowerGroup FlowerGroup;
1425 struct _FlowerGroup {
1438 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1442 work = group->items;
1460 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1461 gint *result_x, gint *result_y)
1467 radius = parent->circumference / (2*PI);
1468 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1470 a = 2*PI * group->diameter / parent->circumference;
1472 x = (gint)((double)radius * cos(parent->angle + a / 2));
1473 y = (gint)((double)radius * sin(parent->angle + a / 2));
1480 x += parent->width / 2;
1481 y += parent->height / 2;
1483 x -= group->width / 2;
1484 y -= group->height / 2;
1490 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1497 if (parent && parent->children)
1499 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1507 pan_window_layout_compute_folder_flower_move(group, x, y);
1512 gint px, py, gx, gy;
1513 gint x1, y1, x2, y2;
1515 px = parent->x + parent->width / 2;
1516 py = parent->y + parent->height / 2;
1518 gx = group->x + group->width / 2;
1519 gy = group->y + group->height / 2;
1524 x2 = MAX(px, gx + 5);
1525 y2 = MAX(py, gy + 5);
1527 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1528 px, py, gx, gy, gx + 5, gy + 5,
1530 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1534 pw->list = g_list_concat(group->items, pw->list);
1535 group->items = NULL;
1537 group->circumference = 0;
1538 work = group->children;
1546 group->circumference += child->diameter;
1549 work = g_list_last(group->children);
1557 pan_window_layout_compute_folder_flower_build(pw, child, group);
1560 g_list_free(group->children);
1564 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1577 if (!filelist_read(path, &f, &d)) return NULL;
1578 if (!f && !d) return NULL;
1580 f = filelist_sort(f, SORT_NAME, TRUE);
1581 d = filelist_sort(d, SORT_NAME, TRUE);
1583 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1584 PAN_TEXT_COLOR, 255);
1586 y += pi_box->height;
1588 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1590 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1591 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1592 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1593 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1595 x += PAN_FOLDER_BOX_BORDER;
1596 y += PAN_FOLDER_BOX_BORDER;
1598 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1612 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1614 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1615 x += pi->width + PAN_THUMB_GAP;
1616 if (pi->height > y_height) y_height = pi->height;
1620 pi = pan_item_new_thumb(pw, fd, x, y);
1621 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1622 y_height = PAN_THUMB_SIZE;
1626 if (grid_count >= grid_size)
1630 y += y_height + PAN_THUMB_GAP;
1634 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1639 group = g_new0(FlowerGroup, 1);
1640 group->items = pw->list;
1643 group->width = pi_box->width;
1644 group->height = pi_box->y + pi_box->height;
1645 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1647 group->children = NULL;
1658 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1659 if (child) group->children = g_list_prepend(group->children, child);
1667 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1668 gint *width, gint *height,
1669 gint *scroll_x, gint *scroll_y)
1674 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1675 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1677 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1679 pi = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1682 *scroll_x = pi->x + pi->width / 2;
1683 *scroll_y = pi->y + pi->height / 2;
1687 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1688 gint *x, gint *y, gint *level,
1690 gint *width, gint *height)
1698 if (!filelist_read(path, &f, &d)) return;
1699 if (!f && !d) return;
1701 f = filelist_sort(f, SORT_NAME, TRUE);
1702 d = filelist_sort(d, SORT_NAME, TRUE);
1704 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1706 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1707 PAN_TEXT_COLOR, 255);
1709 *y += pi_box->height;
1711 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1713 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1714 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1715 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1716 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1718 *x += PAN_FOLDER_BOX_BORDER;
1719 *y += PAN_FOLDER_BOX_BORDER;
1730 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1732 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1733 *x += pi->width + PAN_THUMB_GAP;
1734 if (pi->height > y_height) y_height = pi->height;
1738 pi = pan_item_new_thumb(pw, fd, *x, *y);
1739 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1740 y_height = PAN_THUMB_SIZE;
1743 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1746 if (f) *y = pi_box->y + pi_box->height;
1758 *level = *level + 1;
1759 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1760 pi_box, width, height);
1761 *level = *level - 1;
1766 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1768 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1769 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1771 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1774 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1781 x = PAN_FOLDER_BOX_BORDER;
1782 y = PAN_FOLDER_BOX_BORDER;
1783 w = PAN_FOLDER_BOX_BORDER * 2;
1784 h = PAN_FOLDER_BOX_BORDER * 2;
1786 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1788 if (width) *width = w;
1789 if (height) *height = h;
1792 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
1800 PanItem *pi_month = NULL;
1801 PanItem *pi_day = NULL;
1807 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
1809 list = pan_window_layout_list(path, SORT_NONE, TRUE);
1810 list = filelist_sort(list, SORT_TIME, TRUE);
1812 *width = PAN_FOLDER_BOX_BORDER * 2;
1813 *height = PAN_FOLDER_BOX_BORDER * 2;
1818 day_start = month_start;
1833 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
1838 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
1844 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
1848 x = PAN_FOLDER_BOX_BORDER;
1851 y = PAN_FOLDER_BOX_BORDER;
1853 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
1854 pi = pan_item_new_text(pw, x, y, buf,
1855 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1856 PAN_TEXT_COLOR, 255);
1859 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
1861 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1862 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1863 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1865 x += PAN_FOLDER_BOX_BORDER;
1866 y += PAN_FOLDER_BOX_BORDER;
1870 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
1882 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
1884 needle = needle->next;
1893 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
1894 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
1895 PAN_TEXT_COLOR, 255);
1900 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
1901 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1902 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1903 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1905 x += PAN_FOLDER_BOX_BORDER;
1906 y += PAN_FOLDER_BOX_BORDER;
1910 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1912 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1913 if (pi->width > x_width) x_width = pi->width;
1914 y_height = pi->height;
1918 pi = pan_item_new_thumb(pw, fd, x, y);
1919 x_width = PAN_THUMB_SIZE;
1920 y_height = PAN_THUMB_SIZE;
1923 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
1924 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
1929 if (total > 0 && count < PAN_GROUP_MAX)
1931 y += y_height + PAN_THUMB_GAP;
1935 x += x_width + PAN_THUMB_GAP;
1945 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
1951 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
1952 gint *width, gint *height,
1953 gint *scroll_x, gint *scroll_y)
1955 pan_window_items_free(pw);
1959 case LAYOUT_SIZE_THUMB_DOTS:
1960 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
1961 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
1963 case LAYOUT_SIZE_THUMB_NONE:
1964 pw->thumb_size = PAN_THUMB_SIZE_NONE;
1965 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
1967 case LAYOUT_SIZE_THUMB_SMALL:
1968 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
1969 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
1971 case LAYOUT_SIZE_THUMB_NORMAL:
1973 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
1974 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
1976 case LAYOUT_SIZE_THUMB_LARGE:
1977 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
1978 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
1980 case LAYOUT_SIZE_10:
1981 pw->image_size = 10;
1982 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
1984 case LAYOUT_SIZE_25:
1985 pw->image_size = 25;
1986 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
1988 case LAYOUT_SIZE_33:
1989 pw->image_size = 33;
1990 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
1992 case LAYOUT_SIZE_50:
1993 pw->image_size = 50;
1994 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
1996 case LAYOUT_SIZE_100:
1997 pw->image_size = 100;
1998 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2011 pan_window_layout_compute_grid(pw, path, width, height);
2013 case LAYOUT_FOLDERS_LINEAR:
2014 pan_window_layout_compute_folders_linear(pw, path, width, height);
2016 case LAYOUT_FOLDERS_FLOWER:
2017 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2019 case LAYOUT_TIMELINE:
2020 pan_window_layout_compute_timeline(pw, path, width, height);
2026 printf("computed %d objects\n", g_list_length(pw->list));
2029 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2042 if (util_clip_region_test(x, y, width, height,
2043 pi->x, pi->y, pi->width, pi->height))
2045 list = g_list_prepend(list, pi);
2055 *-----------------------------------------------------------------------------
2057 *-----------------------------------------------------------------------------
2060 static gint pan_layout_queue_step(PanWindow *pw);
2063 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2065 PanWindow *pw = data;
2073 pw->queue_pi = NULL;
2077 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2078 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2081 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2085 thumb_loader_free(pw->tl);
2088 while (pan_layout_queue_step(pw));
2091 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2093 PanWindow *pw = data;
2101 pw->queue_pi = NULL;
2105 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2106 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2107 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2109 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2110 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2111 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2116 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2117 (GdkInterpType)zoom_quality);
2118 g_object_unref(tmp);
2122 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2126 image_loader_free(pw->il);
2129 while (pan_layout_queue_step(pw));
2133 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2134 guint width, guint height, gpointer data)
2136 PanWindow *pw = data;
2147 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2148 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2152 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2158 static gint pan_layout_queue_step(PanWindow *pw)
2162 if (!pw->queue) return FALSE;
2164 pi = pw->queue->data;
2165 pw->queue = g_list_remove(pw->queue, pi);
2168 image_loader_free(pw->il);
2170 thumb_loader_free(pw->tl);
2173 if (pi->type == ITEM_IMAGE)
2175 pw->il = image_loader_new(pi->fd->path);
2177 if (pw->size != LAYOUT_SIZE_100)
2179 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2183 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2185 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2187 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2189 image_loader_free(pw->il);
2192 else if (pi->type == ITEM_THUMB)
2194 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2196 if (!pw->tl->standard_loader)
2198 /* The classic loader will recreate a thumbnail any time we
2199 * request a different size than what exists. This view will
2200 * almost never use the user configured sizes so disable cache.
2202 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2205 thumb_loader_set_callbacks(pw->tl,
2206 pan_layout_queue_thumb_done_cb,
2207 pan_layout_queue_thumb_done_cb,
2210 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2212 thumb_loader_free(pw->tl);
2216 pw->queue_pi->queued = FALSE;
2217 pw->queue_pi = NULL;
2221 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2223 if (!pi || pi->queued || pi->pixbuf) return;
2224 if (pw->size <= LAYOUT_SIZE_THUMB_NONE) return;
2227 pw->queue = g_list_prepend(pw->queue, pi);
2229 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2232 static gint pan_window_request_tile_cb(ImageWindow *imd, gint x, gint y, gint width, gint height,
2233 GdkPixbuf *pixbuf, gpointer data)
2235 PanWindow *pw = data;
2240 pixbuf_draw_rect_fill(pixbuf,
2241 0, 0, width, height,
2242 PAN_BACKGROUND_COLOR, 255);
2244 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2246 gint rx, ry, rw, rh;
2248 if (util_clip_region(x, y, width, height,
2250 &rx, &ry, &rw, &rh))
2252 pixbuf_draw_rect_fill(pixbuf,
2253 rx - x, ry - y, rw, rh,
2254 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2257 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2259 gint rx, ry, rw, rh;
2261 if (util_clip_region(x, y, width, height,
2263 &rx, &ry, &rw, &rh))
2265 pixbuf_draw_rect_fill(pixbuf,
2266 rx - x, ry - y, rw, rh,
2267 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2271 list = pan_layout_intersect(pw, x, y, width, height);
2276 gint tx, ty, tw, th;
2277 gint rx, ry, rw, rh;
2284 if (pi->type == ITEM_THUMB && pi->pixbuf)
2286 tw = gdk_pixbuf_get_width(pi->pixbuf);
2287 th = gdk_pixbuf_get_height(pi->pixbuf);
2289 tx = pi->x + (pi->width - tw) / 2;
2290 ty = pi->y + (pi->height - th) / 2;
2292 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2294 if (util_clip_region(x, y, width, height,
2295 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2296 &rx, &ry, &rw, &rh))
2298 pixbuf_draw_shadow(pixbuf,
2299 rx - x, ry - y, rw, rh,
2300 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2302 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2307 if (util_clip_region(x, y, width, height,
2308 tx + tw, ty + PAN_SHADOW_OFFSET,
2309 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2310 &rx, &ry, &rw, &rh))
2312 pixbuf_draw_shadow(pixbuf,
2313 rx - x, ry - y, rw, rh,
2314 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2316 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2318 if (util_clip_region(x, y, width, height,
2319 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2320 &rx, &ry, &rw, &rh))
2322 pixbuf_draw_shadow(pixbuf,
2323 rx - x, ry - y, rw, rh,
2324 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2326 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2330 if (util_clip_region(x, y, width, height,
2332 &rx, &ry, &rw, &rh))
2334 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2337 1.0, 1.0, GDK_INTERP_NEAREST,
2341 if (util_clip_region(x, y, width, height,
2342 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2343 &rx, &ry, &rw, &rh))
2345 pixbuf_draw_rect_fill(pixbuf,
2346 rx - x, ry - y, rw, rh,
2347 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2349 if (util_clip_region(x, y, width, height,
2350 tx, ty, PAN_OUTLINE_THICKNESS, th,
2351 &rx, &ry, &rw, &rh))
2353 pixbuf_draw_rect_fill(pixbuf,
2354 rx - x, ry - y, rw, rh,
2355 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2357 if (util_clip_region(x, y, width, height,
2358 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2359 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2360 &rx, &ry, &rw, &rh))
2362 pixbuf_draw_rect_fill(pixbuf,
2363 rx - x, ry - y, rw, rh,
2364 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2366 if (util_clip_region(x, y, width, height,
2367 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2368 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2369 &rx, &ry, &rw, &rh))
2371 pixbuf_draw_rect_fill(pixbuf,
2372 rx - x, ry - y, rw, rh,
2373 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2376 else if (pi->type == ITEM_THUMB)
2378 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2379 th = pi->height - PAN_SHADOW_OFFSET * 2;
2380 tx = pi->x + PAN_SHADOW_OFFSET;
2381 ty = pi->y + PAN_SHADOW_OFFSET;
2383 if (util_clip_region(x, y, width, height,
2385 &rx, &ry, &rw, &rh))
2389 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2390 pixbuf_draw_rect_fill(pixbuf,
2391 rx - x, ry - y, rw, rh,
2393 PAN_SHADOW_ALPHA / d);
2396 pan_layout_queue(pw, pi);
2398 else if (pi->type == ITEM_IMAGE)
2400 if (util_clip_region(x, y, width, height,
2401 pi->x, pi->y, pi->width, pi->height,
2402 &rx, &ry, &rw, &rh))
2406 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2409 1.0, 1.0, GDK_INTERP_NEAREST,
2414 pixbuf_draw_rect_fill(pixbuf,
2415 rx - x, ry - y, rw, rh,
2416 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2417 pan_layout_queue(pw, pi);
2421 else if (pi->type == ITEM_BOX)
2435 if (pi->color_a > 254)
2437 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2438 shadow[0], bh - shadow[0],
2439 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2441 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2442 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2444 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2446 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2451 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2452 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2454 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2456 PAN_SHADOW_COLOR, a);
2460 if (util_clip_region(x, y, width, height,
2461 pi->x, pi->y, bw, bh,
2462 &rx, &ry, &rw, &rh))
2464 pixbuf_draw_rect_fill(pixbuf,
2465 rx - x, ry - y, rw, rh,
2466 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2468 if (util_clip_region(x, y, width, height,
2469 pi->x, pi->y, bw, pi->border,
2470 &rx, &ry, &rw, &rh))
2472 pixbuf_draw_rect_fill(pixbuf,
2473 rx - x, ry - y, rw, rh,
2474 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2476 if (util_clip_region(x, y, width, height,
2477 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
2478 &rx, &ry, &rw, &rh))
2480 pixbuf_draw_rect_fill(pixbuf,
2481 rx - x, ry - y, rw, rh,
2482 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2484 if (util_clip_region(x, y, width, height,
2485 pi->x + bw - pi->border, pi->y + pi->border,
2486 pi->border, bh - pi->border * 2,
2487 &rx, &ry, &rw, &rh))
2489 pixbuf_draw_rect_fill(pixbuf,
2490 rx - x, ry - y, rw, rh,
2491 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2493 if (util_clip_region(x, y, width, height,
2494 pi->x, pi->y + bh - pi->border,
2496 &rx, &ry, &rw, &rh))
2498 pixbuf_draw_rect_fill(pixbuf,
2499 rx - x, ry - y, rw, rh,
2500 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2503 else if (pi->type == ITEM_TRIANGLE)
2505 if (util_clip_region(x, y, width, height,
2506 pi->x, pi->y, pi->width, pi->height,
2507 &rx, &ry, &rw, &rh) && pi->data)
2509 gint *coord = pi->data;
2510 pixbuf_draw_triangle(pixbuf,
2511 rx - x, ry - y, rw, rh,
2512 coord[0] - x, coord[1] - y,
2513 coord[2] - x, coord[3] - y,
2514 coord[4] - x, coord[5] - y,
2515 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2517 if (pi->border & BORDER_1)
2519 pixbuf_draw_line(pixbuf,
2520 rx - x, ry - y, rw, rh,
2521 coord[0] - x, coord[1] - y,
2522 coord[2] - x, coord[3] - y,
2523 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2525 if (pi->border & BORDER_2)
2527 pixbuf_draw_line(pixbuf,
2528 rx - x, ry - y, rw, rh,
2529 coord[2] - x, coord[3] - y,
2530 coord[4] - x, coord[5] - y,
2531 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2533 if (pi->border & BORDER_3)
2535 pixbuf_draw_line(pixbuf,
2536 rx - x, ry - y, rw, rh,
2537 coord[4] - x, coord[5] - y,
2538 coord[0] - x, coord[1] - y,
2539 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2543 else if (pi->type == ITEM_TEXT && pi->text)
2545 PangoLayout *layout;
2547 layout = pan_item_text_layout(pi, imd->image);
2548 pixbuf_draw_layout(pixbuf, layout, imd->image,
2549 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
2550 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2551 g_object_unref(G_OBJECT(layout));
2558 static gint count = 0;
2559 PangoLayout *layout;
2565 layout = gtk_widget_create_pango_layout(imd->image, NULL);
2567 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
2568 (x / imd->source_tile_width) +
2569 (y / imd->source_tile_height * (imd->image_width/imd->source_tile_width + 1)));
2570 pango_layout_set_text(layout, buf, -1);
2573 pango_layout_get_pixel_size(layout, &lw, &lh);
2575 pixmap = gdk_pixmap_new(imd->widget->window, lw, lh, -1);
2576 gdk_draw_rectangle(pixmap, imd->widget->style->black_gc, TRUE, 0, 0, lw, lh);
2577 gdk_draw_layout(pixmap, imd->widget->style->white_gc, 0, 0, layout);
2578 g_object_unref(G_OBJECT(layout));
2580 lx = MAX(0, width / 2 - lw / 2);
2581 ly = MAX(0, height / 2 - lh / 2);
2582 lw = MIN(lw, width - lx);
2583 lh = MIN(lh, height - ly);
2584 gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_drawable_get_colormap(imd->image->window),
2585 0, 0, lx, ly, lw, lh);
2586 g_object_unref(pixmap);
2594 static void pan_window_dispose_tile_cb(ImageWindow *imd, gint x, gint y, gint width, gint height,
2595 GdkPixbuf *pixbuf, gpointer data)
2597 PanWindow *pw = data;
2601 list = pan_layout_intersect(pw, x, y, width, height);
2610 if (pi->refcount > 0)
2614 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
2619 pw->queue = g_list_remove(pw->queue, pi);
2622 if (pw->queue_pi == pi) pw->queue_pi = NULL;
2625 g_object_unref(pi->pixbuf);
2637 *-----------------------------------------------------------------------------
2639 *-----------------------------------------------------------------------------
2642 static void pan_window_message(PanWindow *pw, const gchar *text)
2652 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
2665 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
2667 size += pi->fd->size;
2672 ss = text_from_size_abrev(size);
2673 buf = g_strdup_printf(_("%d images, %s"), count, ss);
2675 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
2679 static ImageWindow *pan_window_active_image(PanWindow *pw)
2681 if (pw->fs) return pw->fs->imd;
2686 static void pan_window_zoom_limit(PanWindow *pw)
2692 case LAYOUT_SIZE_THUMB_DOTS:
2693 case LAYOUT_SIZE_THUMB_NONE:
2694 case LAYOUT_SIZE_THUMB_SMALL:
2695 case LAYOUT_SIZE_THUMB_NORMAL:
2697 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
2701 case LAYOUT_SIZE_THUMB_LARGE:
2704 case LAYOUT_SIZE_10:
2705 case LAYOUT_SIZE_25:
2708 case LAYOUT_SIZE_33:
2709 case LAYOUT_SIZE_50:
2710 case LAYOUT_SIZE_100:
2716 image_zoom_set_limits(pw->imd, min, 32.0);
2719 static gint pan_window_layout_update_idle_cb(gpointer data)
2721 PanWindow *pw = data;
2727 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2729 if (!pw->cache_list && !pw->cache_todo)
2731 pan_cache_fill(pw, pw->path);
2734 pan_window_message(pw, _("Reading dimensions..."));
2738 if (pan_cache_step(pw))
2742 if (pw->cache_count == pw->cache_total)
2744 pan_window_message(pw, _("Sorting images..."));
2746 else if (pw->cache_tick > 9)
2750 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
2751 pw->cache_total - pw->cache_count);
2752 pan_window_message(pw, buf);
2762 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
2764 pan_window_zoom_limit(pw);
2766 if (width > 0 && height > 0)
2770 image_set_image_as_tiles(pw->imd, width, height,
2771 PAN_TILE_SIZE, PAN_TILE_SIZE, 8,
2772 pan_window_request_tile_cb,
2773 pan_window_dispose_tile_cb, pw, 1.0);
2775 if (scroll_x == 0 && scroll_y == 0)
2783 image_scroll_to_point(pw->imd, scroll_x, scroll_y, align, align);
2786 pan_window_message(pw, NULL);
2793 static void pan_window_layout_update_idle(PanWindow *pw)
2795 if (pw->idle_id == -1)
2797 pan_window_message(pw, _("Sorting images..."));
2798 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
2803 *-----------------------------------------------------------------------------
2804 * pan window keyboard
2805 *-----------------------------------------------------------------------------
2808 static const gchar *pan_menu_click_path(PanWindow *pw)
2810 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
2814 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
2816 PanWindow *pw = data;
2819 imd = pan_window_active_image(pw);
2820 gdk_window_get_origin(imd->image->window, x, y);
2821 popup_menu_position_clamp(menu, x, y, 0);
2824 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
2826 PanWindow *pw = data;
2829 gint stop_signal = FALSE;
2835 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(pw->imd->widget));
2837 imd = pan_window_active_image(pw);
2838 path = pan_menu_click_path(pw);
2842 switch (event->keyval)
2844 case GDK_Left: case GDK_KP_Left:
2848 case GDK_Right: case GDK_KP_Right:
2852 case GDK_Up: case GDK_KP_Up:
2856 case GDK_Down: case GDK_KP_Down:
2860 case GDK_Page_Up: case GDK_KP_Page_Up:
2861 image_scroll(imd, 0, 0-imd->vis_height / 2);
2863 case GDK_Page_Down: case GDK_KP_Page_Down:
2864 image_scroll(imd, 0, imd->vis_height / 2);
2866 case GDK_Home: case GDK_KP_Home:
2867 image_scroll(imd, 0-imd->vis_width / 2, 0);
2869 case GDK_End: case GDK_KP_End:
2870 image_scroll(imd, imd->vis_width / 2, 0);
2875 if (focused && !(event->state & GDK_CONTROL_MASK) )
2876 switch (event->keyval)
2878 case '+': case '=': case GDK_KP_Add:
2879 image_zoom_adjust(imd, ZOOM_INCREMENT);
2881 case '-': case GDK_KP_Subtract:
2882 image_zoom_adjust(imd, -ZOOM_INCREMENT);
2884 case 'Z': case 'z': case GDK_KP_Divide: case '1':
2885 image_zoom_set(imd, 1.0);
2888 image_zoom_set(imd, 2.0);
2891 image_zoom_set(imd, 3.0);
2894 image_zoom_set(imd, 4.0);
2897 image_zoom_set(imd, -4.0);
2900 image_zoom_set(imd, -3.0);
2903 image_zoom_set(imd, -2.0);
2907 pan_fullscreen_toggle(pw, FALSE);
2911 pan_overlay_toggle(pw);
2913 case GDK_Delete: case GDK_KP_Delete:
2918 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
2925 pan_fullscreen_toggle(pw, TRUE);
2928 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
2930 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
2936 menu = pan_popup_menu(pw);
2937 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
2942 if (event->state & GDK_CONTROL_MASK)
2945 switch (event->keyval)
2978 if (path) file_util_copy(path, NULL, NULL, imd->widget);
2981 if (path) file_util_move(path, NULL, NULL, imd->widget);
2984 if (path) file_util_rename(path, NULL, imd->widget);
2987 if (path) file_util_delete(path, NULL, imd->widget);
2990 if (path) info_window_new(path, NULL);
2993 pan_window_close(pw);
2996 if (n != -1 && path)
2998 pan_fullscreen_toggle(pw, TRUE);
2999 start_editor_from_file(n, path);
3003 else if (event->state & GDK_SHIFT_MASK)
3010 switch (event->keyval)
3015 pan_fullscreen_toggle(pw, TRUE);
3018 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3020 gtk_widget_grab_focus(pw->imd->widget);
3021 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3030 if (x != 0 || y!= 0)
3032 keyboard_scroll_calc(&x, &y, event);
3033 image_scroll(imd, x, y);
3040 *-----------------------------------------------------------------------------
3042 *-----------------------------------------------------------------------------
3045 static void pan_info_update(PanWindow *pw, PanItem *pi)
3051 gint x1, y1, x2, y2, x3, y3;
3054 if (pw->click_pi == pi) return;
3055 if (pi && !pi->fd) pi = NULL;
3057 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3062 printf("info set to %s\n", pi->fd->path);
3064 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3066 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3067 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3068 pan_item_set_key(pbox, "info");
3070 if (pi->type == ITEM_THUMB && pi->pixbuf)
3072 w = gdk_pixbuf_get_width(pi->pixbuf);
3073 h = gdk_pixbuf_get_height(pi->pixbuf);
3075 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3076 y1 = pi->y + (pi->height - h) / 2 + 8;
3080 x1 = pi->x + pi->width - 8;
3088 triangle_rect_region(x1, y1, x2, y2, x3, y3,
3091 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3092 x1, y1, x2, y2, x3, y3,
3093 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3094 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3095 pan_item_set_key(p, "info");
3096 pan_item_added(pw, p);
3098 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3099 _("Filename:"), TEXT_ATTR_BOLD,
3100 PAN_POPUP_TEXT_COLOR, 255);
3101 pan_item_set_key(plabel, "info");
3102 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3103 pi->fd->name, TEXT_ATTR_NONE,
3104 PAN_POPUP_TEXT_COLOR, 255);
3105 pan_item_set_key(p, "info");
3106 pan_item_size_by_item(pbox, p, 0);
3108 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3109 _("Date:"), TEXT_ATTR_BOLD,
3110 PAN_POPUP_TEXT_COLOR, 255);
3111 pan_item_set_key(plabel, "info");
3112 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3113 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3114 PAN_POPUP_TEXT_COLOR, 255);
3115 pan_item_set_key(p, "info");
3116 pan_item_size_by_item(pbox, p, 0);
3118 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3119 _("Size:"), TEXT_ATTR_BOLD,
3120 PAN_POPUP_TEXT_COLOR, 255);
3121 pan_item_set_key(plabel, "info");
3122 buf = text_from_size(pi->fd->size);
3123 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3124 buf, TEXT_ATTR_NONE,
3125 PAN_POPUP_TEXT_COLOR, 255);
3127 pan_item_set_key(p, "info");
3128 pan_item_size_by_item(pbox, p, 0);
3130 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3131 pan_item_added(pw, pbox);
3136 *-----------------------------------------------------------------------------
3138 *-----------------------------------------------------------------------------
3141 static void pan_search_status(PanWindow *pw, const gchar *text)
3143 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3146 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3151 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3153 pi = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3154 if (!pi) return FALSE;
3156 pan_info_update(pw, pi);
3157 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3159 pan_search_status(pw, (path[0] == '/') ? _("path found") : _("filename found"));
3164 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3169 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3171 pi = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3172 if (!pi) pi = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3177 needle = g_utf8_strdown(text, -1);
3178 pi = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3181 if (!pi) return FALSE;
3183 pan_info_update(pw, pi);
3184 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3186 pan_search_status(pw, _("partial match"));
3191 static gint valid_date_separator(gchar c)
3193 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3196 static PanItem *pan_search_by_date_val(PanWindow *pw, ItemType type, gint year, gint month, gint day)
3200 work = g_list_last(pw->list);
3208 if (pi->fd && (pi->type == type || type == ITEM_NONE))
3212 tl = localtime(&pi->fd->date);
3217 match = (tl->tm_year == year - 1900);
3218 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3219 if (match && day > 0) match = (tl->tm_mday == day);
3221 if (match) return pi;
3229 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3243 if (!text) return FALSE;
3245 ptr = (gchar *)text;
3246 while (*ptr != '\0')
3248 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3253 if (t == -1) return FALSE;
3255 if (!lt) return FALSE;
3257 if (valid_date_separator(*text))
3260 mptr = (gchar *)text;
3264 year = (gint)strtol(text, &mptr, 10);
3265 if (mptr == text) return FALSE;
3268 if (*mptr != '\0' && valid_date_separator(*mptr))
3273 month = strtol(mptr, &dptr, 10);
3276 if (valid_date_separator(*dptr))
3278 month = lt->tm_mon + 1;
3286 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3290 day = strtol(dptr, &eptr, 10);
3300 year = lt->tm_year + 1900;
3302 else if (year < 100)
3311 month < -1 || month == 0 || month > 12 ||
3312 day < -1 || day == 0 || day > 31) return FALSE;
3314 t = date_to_time(year, month, day);
3315 if (t < 0) return FALSE;
3317 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3319 pi = pan_search_by_date_val(pw, type, year, month, day);
3322 pan_info_update(pw, pi);
3323 image_scroll_to_point(pw->imd,
3324 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3330 buf = date_value_string(t, DATE_LENGTH_MONTH);
3335 buf = g_strdup_printf("%d %s", day, tmp);
3341 buf = date_value_string(t, DATE_LENGTH_YEAR);
3343 message = g_strdup_printf("%s%s%s%s %s",
3344 (pi) ? "" : "(", (pi) ? "" : _("no match"), (pi) ? "" : ") " ,
3347 pan_search_status(pw, message);
3353 static void pan_search_activate_cb(const gchar *text, gpointer data)
3355 PanWindow *pw = data;
3359 tab_completion_append_to_history(pw->search_entry, text);
3361 if (pan_search_by_path(pw, text)) return;
3363 if (pw->layout == LAYOUT_TIMELINE && pan_search_by_date(pw, text)) return;
3365 if (pan_search_by_partial(pw, text)) return;
3367 pan_search_status(pw, _("no match"));
3370 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3372 PanWindow *pw = data;
3375 visible = GTK_WIDGET_VISIBLE(pw->search_box);
3376 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3380 gtk_widget_hide(pw->search_box);
3381 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3385 gtk_widget_show(pw->search_box);
3386 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3387 gtk_widget_grab_focus(pw->search_entry);
3393 *-----------------------------------------------------------------------------
3394 * view window main routines
3395 *-----------------------------------------------------------------------------
3398 static void button_cb(ImageWindow *imd, gint button, guint32 time,
3399 gdouble x, gdouble y, guint state, gpointer data)
3401 PanWindow *pw = data;
3407 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3408 (gint)((double)(pw->imd->x_scroll + x - pw->imd->x_offset) / pw->imd->scale),
3409 (gint)((double)(pw->imd->y_scroll + y - pw->imd->y_offset) / pw->imd->scale));
3415 pan_info_update(pw, pi);
3420 pan_info_update(pw, pi);
3421 menu = pan_popup_menu(pw);
3422 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, time);
3429 static void scroll_cb(ImageWindow *imd, GdkScrollDirection direction, guint32 time,
3430 gdouble x, gdouble y, guint state, gpointer data)
3433 PanWindow *pw = data;
3436 if (state & GDK_CONTROL_MASK)
3441 image_zoom_adjust_at_point(imd, ZOOM_INCREMENT, x, y);
3443 case GDK_SCROLL_DOWN:
3444 image_zoom_adjust_at_point(imd, -ZOOM_INCREMENT, x, y);
3450 else if ( (state & GDK_SHIFT_MASK) != (mousewheel_scrolls))
3455 image_scroll(imd, 0, -MOUSEWHEEL_SCROLL_SIZE);
3457 case GDK_SCROLL_DOWN:
3458 image_scroll(imd, 0, MOUSEWHEEL_SCROLL_SIZE);
3460 case GDK_SCROLL_LEFT:
3461 image_scroll(imd, -MOUSEWHEEL_SCROLL_SIZE, 0);
3463 case GDK_SCROLL_RIGHT:
3464 image_scroll(imd, MOUSEWHEEL_SCROLL_SIZE, 0);
3476 case GDK_SCROLL_DOWN:
3484 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
3486 image_set_button_func(imd, button_cb, pw);
3487 image_set_scroll_func(imd, scroll_cb, pw);
3490 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
3492 PanWindow *pw = data;
3497 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
3499 if (force_off && !pw->fs) return;
3503 fullscreen_stop(pw->fs);
3504 pw->imd = pw->imd_normal;
3508 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
3510 pan_image_set_buttons(pw, pw->fs->imd);
3511 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
3512 G_CALLBACK(pan_window_key_press_cb), pw);
3514 pw->imd = pw->fs->imd;
3518 static void pan_overlay_toggle(PanWindow *pw)
3522 imd = pan_window_active_image(pw);
3524 if (pw->overlay_id == -1)
3526 pw->overlay_id = image_overlay_info_enable(imd);
3530 image_overlay_info_disable(imd, pw->overlay_id);
3531 pw->overlay_id = -1;
3536 static void pan_window_image_update_cb(ImageWindow *imd, gpointer data)
3538 PanWindow *pw = data;
3541 text = image_zoom_get_as_text(imd);
3542 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
3546 static void pan_window_image_scroll_notify_cb(ImageWindow *imd, gint x, gint y,
3547 gint width, gint height, gpointer data)
3549 PanWindow *pw = data;
3552 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
3553 adj->page_size = (gdouble)imd->vis_width / imd->scale;
3554 adj->page_increment = adj->page_size / 2.0;
3555 adj->step_increment = 48.0 / imd->scale;
3557 adj->upper = MAX((gdouble)width + adj->page_size, 1.0);
3558 adj->value = (gdouble)x;
3560 pref_signal_block_data(pw->scrollbar_h, pw);
3561 gtk_adjustment_changed(adj);
3562 pref_signal_unblock_data(pw->scrollbar_h, pw);
3564 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
3565 adj->page_size = (gdouble)imd->vis_height / imd->scale;
3566 adj->page_increment = adj->page_size / 2.0;
3567 adj->step_increment = 48.0 / imd->scale;
3569 adj->upper = MAX((gdouble)height + adj->page_size, 1.0);
3570 adj->value = (gdouble)y;
3572 pref_signal_block_data(pw->scrollbar_v, pw);
3573 gtk_adjustment_changed(adj);
3574 pref_signal_unblock_data(pw->scrollbar_v, pw);
3576 // printf("scrolled to %d,%d @ %d x %d\n", x, y, width, height);
3579 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
3581 PanWindow *pw = data;
3584 if (!pw->imd->scale) return;
3586 x = (gint)gtk_range_get_value(range);
3588 image_scroll_to_point(pw->imd, x, (gint)((gdouble)pw->imd->y_scroll / pw->imd->scale), 0.0, 0.0);
3591 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
3593 PanWindow *pw = data;
3596 if (!pw->imd->scale) return;
3598 y = (gint)gtk_range_get_value(range);
3600 image_scroll_to_point(pw->imd, (gint)((gdouble)pw->imd->x_scroll / pw->imd->scale), y, 0.0, 0.0);
3603 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
3605 PanWindow *pw = data;
3607 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3608 pan_window_layout_update_idle(pw);
3611 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
3613 PanWindow *pw = data;
3615 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3616 pan_window_layout_update_idle(pw);
3619 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
3621 PanWindow *pw = data;
3624 path = remove_trailing_slash(new_text);
3625 parse_out_relatives(path);
3629 warning_dialog(_("Folder not found"),
3630 _("The entered path is not a folder"),
3631 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
3635 tab_completion_append_to_history(pw->path_entry, path);
3638 pw->path = g_strdup(path);
3640 pan_window_layout_update_idle(pw);
3643 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
3645 PanWindow *pw = data;
3648 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
3650 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
3651 pan_window_entry_activate_cb(text, pw);
3655 static void pan_window_close(PanWindow *pw)
3657 pan_window_list = g_list_remove(pan_window_list, pw);
3659 if (pw->idle_id != -1)
3661 g_source_remove(pw->idle_id);
3664 pan_fullscreen_toggle(pw, TRUE);
3665 gtk_widget_destroy(pw->window);
3667 pan_window_items_free(pw);
3674 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
3676 PanWindow *pw = data;
3678 pan_window_close(pw);
3682 void pan_window_new(const gchar *path)
3691 GdkGeometry geometry;
3693 pw = g_new0(PanWindow, 1);
3695 pw->path = g_strdup(path);
3696 pw->layout = LAYOUT_TIMELINE;
3697 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
3698 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
3699 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
3703 pw->overlay_id = -1;
3706 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3708 geometry.min_width = 8;
3709 geometry.min_height = 8;
3710 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
3712 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
3713 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
3714 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
3715 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
3717 window_set_icon(pw->window, NULL, NULL);
3719 vbox = gtk_vbox_new(FALSE, 0);
3720 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
3721 gtk_widget_show(vbox);
3723 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3725 pref_spacer(box, 0);
3726 pref_label_new(box, _("Location:"));
3727 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
3728 pan_window_entry_activate_cb, pw);
3729 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
3730 G_CALLBACK(pan_window_entry_change_cb), pw);
3731 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
3732 gtk_widget_show(combo);
3734 combo = gtk_combo_box_new_text();
3735 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
3736 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
3737 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
3738 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
3740 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
3741 g_signal_connect(G_OBJECT(combo), "changed",
3742 G_CALLBACK(pan_window_layout_change_cb), pw);
3743 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
3744 gtk_widget_show(combo);
3746 combo = gtk_combo_box_new_text();
3747 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
3748 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
3749 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
3750 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
3751 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
3752 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
3753 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
3754 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
3755 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
3756 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
3758 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
3759 g_signal_connect(G_OBJECT(combo), "changed",
3760 G_CALLBACK(pan_window_layout_size_cb), pw);
3761 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
3762 gtk_widget_show(combo);
3764 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
3765 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
3766 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
3768 pw->imd = image_new(TRUE);
3769 pw->imd_normal = pw->imd;
3771 if (black_window_background) image_background_set_black(pw->imd, TRUE);
3772 image_set_update_func(pw->imd, pan_window_image_update_cb, pw);
3774 image_set_scroll_notify_func(pw->imd, pan_window_image_scroll_notify_cb, pw);
3777 gtk_box_pack_start(GTK_BOX(vbox), pw->imd->widget, TRUE, TRUE, 0);
3779 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
3780 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
3781 gtk_widget_show(pw->imd->widget);
3783 pan_window_dnd_init(pw);
3785 pan_image_set_buttons(pw, pw->imd);
3787 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
3788 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
3789 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
3790 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
3791 GTK_FILL | GTK_EXPAND, 0, 0, 0);
3792 gtk_widget_show(pw->scrollbar_h);
3794 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
3795 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
3796 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
3797 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
3798 0, GTK_FILL | GTK_EXPAND, 0, 0);
3799 gtk_widget_show(pw->scrollbar_v);
3803 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
3804 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
3806 pref_spacer(pw->search_box, 0);
3807 pref_label_new(pw->search_box, _("Find:"));
3809 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
3810 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
3811 gtk_widget_show(hbox);
3813 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
3814 pan_search_activate_cb, pw);
3815 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
3816 gtk_widget_show(combo);
3818 pw->search_label = gtk_label_new("");
3819 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
3820 gtk_widget_show(pw->search_label);
3824 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3826 frame = gtk_frame_new(NULL);
3827 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
3828 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
3829 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
3830 gtk_widget_show(frame);
3832 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
3833 gtk_container_add(GTK_CONTAINER(frame), hbox);
3834 gtk_widget_show(hbox);
3836 pref_spacer(hbox, 0);
3837 pw->label_message = pref_label_new(hbox, "");
3839 frame = gtk_frame_new(NULL);
3840 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
3841 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
3842 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
3843 gtk_widget_show(frame);
3845 pw->label_zoom = gtk_label_new("");
3846 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
3847 gtk_widget_show(pw->label_zoom);
3849 pw->search_button = gtk_toggle_button_new();
3850 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
3851 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
3852 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
3853 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
3854 gtk_widget_show(hbox);
3855 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
3856 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
3857 gtk_widget_show(pw->search_button_arrow);
3858 pref_label_new(hbox, _("Find"));
3860 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
3861 gtk_widget_show(pw->search_button);
3862 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
3863 G_CALLBACK(pan_search_toggle_cb), pw);
3865 g_signal_connect(G_OBJECT(pw->window), "delete_event",
3866 G_CALLBACK(pan_window_delete_cb), pw);
3867 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
3868 G_CALLBACK(pan_window_key_press_cb), pw);
3870 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
3872 pan_window_layout_update_idle(pw);
3874 gtk_widget_grab_focus(pw->imd->widget);
3875 gtk_widget_show(pw->window);
3877 pan_window_list = g_list_append(pan_window_list, pw);
3881 *-----------------------------------------------------------------------------
3883 *-----------------------------------------------------------------------------
3887 *-----------------------------------------------------------------------------
3888 * view window menu routines and callbacks
3889 *-----------------------------------------------------------------------------
3892 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
3894 PanWindow *pw = data;
3897 path = pan_menu_click_path(pw);
3900 pan_fullscreen_toggle(pw, TRUE);
3901 view_window_new(path);
3905 static void pan_edit_cb(GtkWidget *widget, gpointer data)
3911 pw = submenu_item_get_data(widget);
3912 n = GPOINTER_TO_INT(data);
3915 path = pan_menu_click_path(pw);
3918 pan_fullscreen_toggle(pw, TRUE);
3919 start_editor_from_file(n, path);
3923 static void pan_info_cb(GtkWidget *widget, gpointer data)
3925 PanWindow *pw = data;
3928 path = pan_menu_click_path(pw);
3929 if (path) info_window_new(path, NULL);
3932 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
3934 PanWindow *pw = data;
3936 image_zoom_adjust(pan_window_active_image(pw), ZOOM_INCREMENT);
3939 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
3941 PanWindow *pw = data;
3943 image_zoom_adjust(pan_window_active_image(pw), -ZOOM_INCREMENT);
3946 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
3948 PanWindow *pw = data;
3950 image_zoom_set(pan_window_active_image(pw), 1.0);
3953 static void pan_copy_cb(GtkWidget *widget, gpointer data)
3955 PanWindow *pw = data;
3958 path = pan_menu_click_path(pw);
3959 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
3962 static void pan_move_cb(GtkWidget *widget, gpointer data)
3964 PanWindow *pw = data;
3967 path = pan_menu_click_path(pw);
3968 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
3971 static void pan_rename_cb(GtkWidget *widget, gpointer data)
3973 PanWindow *pw = data;
3976 path = pan_menu_click_path(pw);
3977 if (path) file_util_rename(path, NULL, pw->imd->widget);
3980 static void pan_delete_cb(GtkWidget *widget, gpointer data)
3982 PanWindow *pw = data;
3985 path = pan_menu_click_path(pw);
3986 if (path) file_util_delete(path, NULL, pw->imd->widget);
3989 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
3991 PanWindow *pw = data;
3993 pan_fullscreen_toggle(pw, FALSE);
3996 static void pan_close_cb(GtkWidget *widget, gpointer data)
3998 PanWindow *pw = data;
4000 pan_window_close(pw);
4003 static GtkWidget *pan_popup_menu(PanWindow *pw)
4009 active = (pw->click_pi != NULL);
4011 menu = popup_menu_short_lived();
4013 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4014 G_CALLBACK(pan_zoom_in_cb), pw);
4015 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4016 G_CALLBACK(pan_zoom_out_cb), pw);
4017 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4018 G_CALLBACK(pan_zoom_1_1_cb), pw);
4019 menu_item_add_divider(menu);
4021 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4022 gtk_widget_set_sensitive(item, active);
4024 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4025 G_CALLBACK(pan_info_cb), pw);
4027 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4028 G_CALLBACK(pan_new_window_cb), pw);
4030 menu_item_add_divider(menu);
4031 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4032 G_CALLBACK(pan_copy_cb), pw);
4033 menu_item_add_sensitive(menu, _("_Move..."), active,
4034 G_CALLBACK(pan_move_cb), pw);
4035 menu_item_add_sensitive(menu, _("_Rename..."), active,
4036 G_CALLBACK(pan_rename_cb), pw);
4037 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4038 G_CALLBACK(pan_delete_cb), pw);
4040 menu_item_add_divider(menu);
4044 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4048 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4051 menu_item_add_divider(menu);
4052 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4058 *-----------------------------------------------------------------------------
4059 * image drag and drop routines
4060 *-----------------------------------------------------------------------------
4063 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4065 GtkSelectionData *selection_data, guint info,
4066 guint time, gpointer data)
4068 PanWindow *pw = data;
4071 if (gtk_drag_get_source_widget(context) == pw->imd->image) return;
4075 if (info == TARGET_URI_LIST)
4079 list = uri_list_from_text(selection_data->data, TRUE);
4080 if (list && isdir((gchar *)list->data))
4082 gchar *path = list->data;
4085 pw->path = g_strdup(path);
4087 pan_window_layout_update_idle(pw);
4090 path_list_free(list);
4094 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4095 GtkSelectionData *selection_data, guint info,
4096 guint time, gpointer data)
4098 PanWindow *pw = data;
4101 path = pan_menu_click_path(pw);
4111 case TARGET_URI_LIST:
4114 case TARGET_TEXT_PLAIN:
4119 list = g_list_append(NULL, (gchar *)path);
4120 text = uri_text_from_list(list, &len, plain_text);
4124 gtk_selection_data_set (selection_data, selection_data->target,
4131 gtk_selection_data_set (selection_data, selection_data->target,
4136 static void pan_window_dnd_init(PanWindow *pw)
4142 gtk_drag_source_set(imd->image, GDK_BUTTON2_MASK,
4143 dnd_file_drag_types, dnd_file_drag_types_count,
4144 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4145 g_signal_connect(G_OBJECT(imd->image), "drag_data_get",
4146 G_CALLBACK(pan_window_set_dnd_data), pw);
4148 gtk_drag_dest_set(imd->image,
4149 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4150 dnd_file_drop_types, dnd_file_drop_types_count,
4151 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4152 g_signal_connect(G_OBJECT(imd->image), "drag_data_received",
4153 G_CALLBACK(pan_window_get_dnd_data), pw);
4157 *-----------------------------------------------------------------------------
4158 * maintenance (for rename, move, remove)
4159 *-----------------------------------------------------------------------------