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_NONE 24
45 #define PAN_THUMB_SIZE_SMALL 64
46 #define PAN_THUMB_SIZE_NORMAL 128
47 #define PAN_THUMB_SIZE_LARGE 256
48 #define PAN_THUMB_SIZE pw->thumb_size
50 #define PAN_THUMB_GAP_SMALL 14
51 #define PAN_THUMB_GAP_NORMAL 30
52 #define PAN_THUMB_GAP_LARGE 40
53 #define PAN_THUMB_GAP_HUGE 50
54 #define PAN_THUMB_GAP pw->thumb_gap
56 #define PAN_SHADOW_OFFSET 6
57 #define PAN_SHADOW_FADE 5
58 #define PAN_SHADOW_COLOR 0, 0, 0
59 #define PAN_SHADOW_ALPHA 64
61 #define PAN_OUTLINE_THICKNESS 1
62 #define PAN_OUTLINE_COLOR_1 255, 255, 255
63 #define PAN_OUTLINE_COLOR_2 64, 64, 64
64 #define PAN_OUTLINE_ALPHA 180
66 #define PAN_BACKGROUND_COLOR 255, 255, 230
68 #define PAN_GRID_SIZE 10
69 #define PAN_GRID_COLOR 0, 0, 0
70 #define PAN_GRID_ALPHA 20
72 #define PAN_FOLDER_BOX_COLOR 0, 0, 255
73 #define PAN_FOLDER_BOX_ALPHA 10
74 #define PAN_FOLDER_BOX_BORDER 20
76 #define PAN_FOLDER_BOX_OUTLINE_THICKNESS 4
77 #define PAN_FOLDER_BOX_OUTLINE_COLOR 0, 0, 255
78 #define PAN_FOLDER_BOX_OUTLINE_ALPHA 64
80 #define PAN_TEXT_BORDER_SIZE 4
81 #define PAN_TEXT_COLOR 0, 0, 0
83 #define PAN_POPUP_COLOR 255, 255, 220
84 #define PAN_POPUP_ALPHA 255
85 #define PAN_POPUP_BORDER 1
86 #define PAN_POPUP_BORDER_COLOR 0, 0, 0
87 #define PAN_POPUP_TEXT_COLOR 0, 0, 0
89 #define PAN_GROUP_MAX 16
91 #define ZOOM_INCREMENT 1.0
92 #define ZOOM_LABEL_WIDTH 64
97 LAYOUT_FOLDERS_LINEAR,
98 LAYOUT_FOLDERS_FLOWER,
103 LAYOUT_SIZE_THUMB_NONE = 0,
104 LAYOUT_SIZE_THUMB_SMALL,
105 LAYOUT_SIZE_THUMB_NORMAL,
106 LAYOUT_SIZE_THUMB_LARGE,
125 TEXT_ATTR_BOLD = 1 << 0,
126 TEXT_ATTR_HEADING = 1 << 1,
127 TEXT_ATTR_MARKUP = 1 << 2
138 typedef struct _PanItem PanItem;
153 TextAttrType text_attr;
171 typedef struct _PanWindow PanWindow;
176 ImageWindow *imd_normal;
179 GtkWidget *path_entry;
181 GtkWidget *label_message;
182 GtkWidget *label_zoom;
184 GtkWidget *search_box;
185 GtkWidget *search_entry;
186 GtkWidget *search_label;
187 GtkWidget *search_button;
188 GtkWidget *search_button_arrow;
190 GtkWidget *scrollbar_h;
191 GtkWidget *scrollbar_v;
220 typedef struct _PanCacheData PanCacheData;
221 struct _PanCacheData {
227 static GList *pan_window_list = NULL;
230 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend);
232 static GtkWidget *pan_popup_menu(PanWindow *pw);
233 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off);
234 static void pan_overlay_toggle(PanWindow *pw);
236 static void pan_window_close(PanWindow *pw);
238 static void pan_window_dnd_init(PanWindow *pw);
241 static gint util_clip_region(gint x, gint y, gint w, gint h,
242 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
243 gint *rx, gint *ry, gint *rw, gint *rh)
245 if (clip_x + clip_w <= x ||
247 clip_y + clip_h <= y ||
253 *rx = MAX(x, clip_x);
254 *rw = MIN((x + w), (clip_x + clip_w)) - *rx;
256 *ry = MAX(y, clip_y);
257 *rh = MIN((y + h), (clip_y + clip_h)) - *ry;
262 static gint util_clip_region_test(gint x, gint y, gint w, gint h,
263 gint clip_x, gint clip_y, gint clip_w, gint clip_h)
267 return util_clip_region(x, y, w, h,
268 clip_x, clip_y, clip_w, clip_h,
281 static gint date_compare(time_t a, time_t b, DateLengthType length)
286 if (length == DATE_LENGTH_EXACT) return (a == b);
288 if (!localtime_r(&a, &ta) ||
289 !localtime_r(&b, &tb)) return FALSE;
291 if (ta.tm_year != tb.tm_year) return FALSE;
292 if (length == DATE_LENGTH_YEAR) return TRUE;
294 if (ta.tm_mon != tb.tm_mon) return FALSE;
295 if (length == DATE_LENGTH_MONTH) return TRUE;
297 if (length == DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
299 if (ta.tm_mday != tb.tm_mday) return FALSE;
300 if (length == DATE_LENGTH_DAY) return TRUE;
302 return (ta.tm_hour == tb.tm_hour);
305 static gchar *date_value_string(time_t d, DateLengthType length)
309 gchar *format = NULL;
311 if (!localtime_r(&d, &td)) return g_strdup("");
315 case DATE_LENGTH_DAY:
316 return g_strdup_printf("%d", td.tm_mday);
318 case DATE_LENGTH_WEEK:
321 case DATE_LENGTH_MONTH:
324 case DATE_LENGTH_YEAR:
325 return g_strdup_printf("%d", td.tm_year + 1900);
327 case DATE_LENGTH_EXACT:
329 return g_strdup(text_from_time(d));
334 if (format && strftime(buf, sizeof(buf), format, &td) > 0)
336 gchar *ret = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
343 static time_t date_to_time(gint year, gint month, gint day)
350 lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
351 lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
352 lt.tm_year = year - 1900;
359 *-----------------------------------------------------------------------------
361 *-----------------------------------------------------------------------------
364 static void triangle_rect_region(gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
365 gint *rx, gint *ry, gint *rw, gint *rh)
373 tw = MAX(abs(x1 - x2), abs(x2 - x3));
374 tw = MAX(tw, abs(x3 - x1));
375 th = MAX(abs(y1 - y2), abs(y2 - y3));
376 th = MAX(th, abs(y3 - y1));
384 static void pixbuf_draw_triangle(GdkPixbuf *pb,
385 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
386 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
387 guint8 r, guint8 g, guint8 b, guint8 a)
403 pw = gdk_pixbuf_get_width(pb);
404 ph = gdk_pixbuf_get_height(pb);
406 if (!util_clip_region(0, 0, pw, ph,
407 clip_x, clip_y, clip_w, clip_h,
408 &rx, &ry, &rw, &rh)) return;
410 triangle_rect_region(x1, y1, x2, y2, x3, y3,
413 if (!util_clip_region(rx, ry, rw, rh,
415 &fx1, &fy1, &fw, &fh)) return;
419 p_alpha = gdk_pixbuf_get_has_alpha(pb);
420 prs = gdk_pixbuf_get_rowstride(pb);
421 p_pix = gdk_pixbuf_get_pixels(pb);
423 p_step = (p_alpha) ? 4 : 3;
424 for (i = fy1; i < fy2; i++)
426 pp = p_pix + i * prs + (fx1 * p_step);
427 for (j = fx1; j < fx2; j++)
431 z1 = (y1 - y2)*(j - x2) + (x2 - x1)*(i - y2);
432 z2 = (y2 - y3)*(j - x3) + (x3 - x2)*(i - y3);
435 z2 = (y3 - y1)*(j - x1) + (x1 - x3)*(i - y1);
438 pp[0] = (r * a + pp[0] * (256-a)) >> 8;
439 pp[1] = (g * a + pp[1] * (256-a)) >> 8;
440 pp[2] = (b * a + pp[2] * (256-a)) >> 8;
448 static void pixbuf_draw_line(GdkPixbuf *pb,
449 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
450 gint x1, gint y1, gint x2, gint y2,
451 guint8 r, guint8 g, guint8 b, guint8 a)
456 gint fx1, fy1, fx2, fy2;
462 gdouble xstep, ystep;
469 pw = gdk_pixbuf_get_width(pb);
470 ph = gdk_pixbuf_get_height(pb);
472 if (!util_clip_region(0, 0, pw, ph,
473 clip_x, clip_y, clip_w, clip_h,
474 &rx, &ry, &rw, &rh)) return;
486 if (xa == 0 && ya == 0) return;
488 nt = sqrt(xd * xd + yd * yd);
490 nt = (xa > ya) ? xa : ya;
491 xstep = (double)xd / nt;
492 ystep = (double)yd / nt;
494 p_alpha = gdk_pixbuf_get_has_alpha(pb);
495 prs = gdk_pixbuf_get_rowstride(pb);
496 p_pix = gdk_pixbuf_get_pixels(pb);
498 p_step = (p_alpha) ? 4 : 3;
502 for (n = 0; n < nt; n++)
507 if (x >= fx1 && x < fx2 &&
510 pp = p_pix + y * prs + x * p_step;
511 *pp = (r * a + *pp * (256-a)) >> 8;
513 *pp = (g * a + *pp * (256-a)) >> 8;
515 *pp = (b * a + *pp * (256-a)) >> 8;
522 static void pixbuf_draw_fade_linear(guchar *p_pix, gint prs, gint p_alpha,
523 gint s, gint vertical, gint border,
524 gint x1, gint y1, gint x2, gint y2,
525 guint8 r, guint8 g, guint8 b, guint8 a)
532 p_step = (p_alpha) ? 4 : 3;
533 for (j = y1; j < y2; j++)
535 pp = p_pix + j * prs + x1 * p_step;
536 if (!vertical) n = a - a * abs(j - s) / border;
537 for (i = x1; i < x2; i++)
539 if (vertical) n = a - a * abs(i - s) / border;
540 *pp = (r * n + *pp * (256-n)) >> 8;
542 *pp = (g * n + *pp * (256-n)) >> 8;
544 *pp = (b * n + *pp * (256-n)) >> 8;
551 static void pixbuf_draw_fade_radius(guchar *p_pix, gint prs, gint p_alpha,
552 gint sx, gint sy, gint border,
553 gint x1, gint y1, gint x2, gint y2,
554 guint8 r, guint8 g, guint8 b, guint8 a)
560 p_step = (p_alpha) ? 4 : 3;
561 for (j = y1; j < y2; j++)
563 pp = p_pix + j * prs + x1 * p_step;
564 for (i = x1; i < x2; i++)
569 r = MIN(border, (gint)sqrt((i-sx)*(i-sx) + (j-sy)*(j-sy)));
570 n = a - a * r / border;
571 *pp = (r * n + *pp * (256-n)) >> 8;
573 *pp = (g * n + *pp * (256-n)) >> 8;
575 *pp = (b * n + *pp * (256-n)) >> 8;
582 static void pixbuf_draw_shadow(GdkPixbuf *pb,
583 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
584 gint x, gint y, gint w, gint h, gint border,
585 guint8 r, guint8 g, guint8 b, guint8 a)
595 pw = gdk_pixbuf_get_width(pb);
596 ph = gdk_pixbuf_get_height(pb);
598 if (!util_clip_region(0, 0, pw, ph,
599 clip_x, clip_y, clip_w, clip_h,
600 &rx, &ry, &rw, &rh)) return;
602 p_alpha = gdk_pixbuf_get_has_alpha(pb);
603 prs = gdk_pixbuf_get_rowstride(pb);
604 p_pix = gdk_pixbuf_get_pixels(pb);
606 if (util_clip_region(x + border, y + border, w - border * 2, h - border * 2,
610 pixbuf_draw_rect_fill(pb, fx, fy, fw, fh, r, g, b, a);
613 if (border < 1) return;
615 if (util_clip_region(x, y + border, border, h - border * 2,
619 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
620 x + border, TRUE, border,
621 fx, fy, fx + fw, fy + fh,
624 if (util_clip_region(x + w - border, y + border, border, h - border * 2,
628 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
629 x + w - border, TRUE, border,
630 fx, fy, fx + fw, fy + fh,
633 if (util_clip_region(x + border, y, w - border * 2, border,
637 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
638 y + border, FALSE, border,
639 fx, fy, fx + fw, fy + fh,
642 if (util_clip_region(x + border, y + h - border, w - border * 2, border,
646 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
647 y + h - border, FALSE, border,
648 fx, fy, fx + fw, fy + fh,
651 if (util_clip_region(x, y, border, border,
655 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
656 x + border, y + border, border,
657 fx, fy, fx + fw, fy + fh,
660 if (util_clip_region(x + w - border, y, border, border,
664 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
665 x + w - border, y + border, border,
666 fx, fy, fx + fw, fy + fh,
669 if (util_clip_region(x, y + h - border, border, border,
673 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
674 x + border, y + h - border, border,
675 fx, fy, fx + fw, fy + fh,
678 if (util_clip_region(x + w - border, y + h - border, border, border,
682 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
683 x + w - border, y + h - border, border,
684 fx, fy, fx + fw, fy + fh,
691 *-----------------------------------------------------------------------------
693 *-----------------------------------------------------------------------------
696 static void pan_cache_free(PanWindow *pw)
700 work = pw->cache_list;
708 cache_sim_data_free(pc->cd);
709 file_data_free((FileData *)pc);
712 g_list_free(pw->cache_list);
713 pw->cache_list = NULL;
715 filelist_free(pw->cache_todo);
716 pw->cache_todo = NULL;
723 static void pan_cache_fill(PanWindow *pw, const gchar *path)
729 list = pan_window_layout_list(path, SORT_NAME, TRUE);
730 pw->cache_todo = g_list_reverse(list);
732 pw->cache_total = g_list_length(pw->cache_todo);
735 static gint pan_cache_step(PanWindow *pw)
739 CacheData *cd = NULL;
741 if (!pw->cache_todo) return FALSE;
743 fd = pw->cache_todo->data;
744 pw->cache_todo = g_list_remove(pw->cache_todo, fd);
746 if (enable_thumb_caching)
750 found = cache_find_location(CACHE_TYPE_SIM, fd->path);
751 if (found && filetime(found) == fd->date)
753 cd = cache_sim_data_load(found);
758 if (!cd) cd = cache_sim_data_new();
762 cd->dimensions = image_load_dimensions(fd->path, &cd->width, &cd->height);
763 if (enable_thumb_caching &&
769 base = cache_get_location(CACHE_TYPE_SIM, fd->path, FALSE, &mode);
770 if (cache_ensure_dir_exists(base, mode))
773 cd->path = cache_get_location(CACHE_TYPE_SIM, fd->path, TRUE, NULL);
774 if (cache_sim_data_save(cd))
776 filetime_set(cd->path, filetime(fd->path));
785 pc = g_new0(PanCacheData, 1);
786 memcpy(pc, fd, sizeof(FileData));
791 pw->cache_list = g_list_prepend(pw->cache_list, pc);
798 *-----------------------------------------------------------------------------
800 *-----------------------------------------------------------------------------
803 static void pan_item_free(PanItem *pi)
807 if (pi->pixbuf) g_object_unref(pi->pixbuf);
808 if (pi->fd) file_data_free(pi->fd);
816 static void pan_window_items_free(PanWindow *pw)
823 PanItem *pi = work->data;
829 g_list_free(pw->list);
832 g_list_free(pw->queue);
836 image_loader_free(pw->il);
839 thumb_loader_free(pw->tl);
845 static PanItem *pan_item_new_thumb(PanWindow *pw, FileData *fd, gint x, gint y)
849 pi = g_new0(PanItem, 1);
850 pi->type = ITEM_THUMB;
854 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
855 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
861 pw->list = g_list_prepend(pw->list, pi);
866 static PanItem *pan_item_new_box(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
868 guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
869 guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
873 pi = g_new0(PanItem, 1);
881 pi->color_r = base_r;
882 pi->color_g = base_g;
883 pi->color_b = base_b;
884 pi->color_a = base_a;
886 pi->color2_r = bord_r;
887 pi->color2_g = bord_g;
888 pi->color2_b = bord_b;
889 pi->color2_a = bord_a;
890 pi->border = border_size;
892 pw->list = g_list_prepend(pw->list, pi);
897 static void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
901 if (!pi || pi->type != ITEM_BOX) return;
906 pi->width -= shadow[0];
907 pi->height -= shadow[0];
910 shadow = g_new0(gint, 2);
915 pi->height += offset;
921 static PanItem *pan_item_new_tri(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
922 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
923 guint8 r, guint8 g, guint8 b, guint8 a)
928 pi = g_new0(PanItem, 1);
929 pi->type = ITEM_TRIANGLE;
940 coord = g_new0(gint, 6);
950 pi->border = BORDER_NONE;
952 pw->list = g_list_prepend(pw->list, pi);
957 static void pan_item_tri_border(PanItem *pi, gint borders,
958 guint8 r, guint8 g, guint8 b, guint8 a)
960 if (!pi || pi->type != ITEM_TRIANGLE) return;
962 pi->border = borders;
970 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
974 layout = gtk_widget_create_pango_layout(widget, NULL);
976 if (pi->text_attr & TEXT_ATTR_MARKUP)
978 pango_layout_set_markup(layout, pi->text, -1);
982 if (pi->text_attr & TEXT_ATTR_BOLD ||
983 pi->text_attr & TEXT_ATTR_HEADING)
988 pal = pango_attr_list_new();
989 if (pi->text_attr & TEXT_ATTR_BOLD)
991 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
993 pa->end_index = G_MAXINT;
994 pango_attr_list_insert(pal, pa);
996 if (pi->text_attr & TEXT_ATTR_HEADING)
998 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
1000 pa->end_index = G_MAXINT;
1001 pango_attr_list_insert(pal, pa);
1003 pango_layout_set_attributes(layout, pal);
1004 pango_attr_list_unref(pal);
1007 pango_layout_set_text(layout, pi->text, -1);
1011 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
1013 PangoLayout *layout;
1015 if (!pi || !pi->text || !widget) return;
1017 layout = pan_item_text_layout(pi, widget);
1018 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
1019 g_object_unref(G_OBJECT(layout));
1021 pi->width += PAN_TEXT_BORDER_SIZE * 2;
1022 pi->height += PAN_TEXT_BORDER_SIZE * 2;
1025 static PanItem *pan_item_new_text(PanWindow *pw, gint x, gint y, const gchar *text, TextAttrType attr,
1026 guint8 r, guint8 g, guint8 b, guint8 a)
1030 pi = g_new0(PanItem, 1);
1031 pi->type = ITEM_TEXT;
1034 pi->text = g_strdup(text);
1035 pi->text_attr = attr;
1042 pan_item_text_compute_size(pi, pw->imd->widget);
1044 pw->list = g_list_prepend(pw->list, pi);
1049 static void pan_item_set_key(PanItem *pi, const gchar *key)
1056 pi->key = g_strdup(key);
1060 static void pan_item_added(PanWindow *pw, PanItem *pi)
1063 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1066 static void pan_item_remove(PanWindow *pw, PanItem *pi)
1070 if (pw->click_pi == pi) pw->click_pi = NULL;
1072 pw->list = g_list_remove(pw->list, pi);
1073 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1077 static void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
1079 if (!pi || !child) return;
1081 if (pi->x + pi->width < child->x + child->width + border)
1082 pi->width = child->x + child->width + border - pi->x;
1084 if (pi->y + pi->height < child->y + child->height + border)
1085 pi->height = child->y + child->height + border - pi->y;
1088 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
1095 if (!pi->fd) return;
1097 work = pw->cache_list;
1106 path = ((FileData *)pc)->path;
1108 if (pc->cd && pc->cd->dimensions &&
1109 path && strcmp(path, pi->fd->path) == 0)
1111 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
1112 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
1114 pw->cache_list = g_list_remove(pw->cache_list, pc);
1115 cache_sim_data_free(pc->cd);
1116 file_data_free((FileData *)pc);
1122 static PanItem *pan_item_new_image(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
1126 pi = g_new0(PanItem, 1);
1127 pi->type = ITEM_IMAGE;
1132 pan_item_image_find_size(pw, pi, w, h);
1134 pw->list = g_list_prepend(pw->list, pi);
1139 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
1143 if (!key) return NULL;
1145 work = g_list_last(pw->list);
1151 if ((pi->type == type || type == ITEM_NONE) &&
1152 pi->key && strcmp(pi->key, key) == 0)
1162 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1163 static PanItem *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1164 gint ignore_case, gint partial)
1168 if (!path) return NULL;
1169 if (partial && path[0] == '/') return NULL;
1171 work = g_list_last(pw->list);
1177 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1181 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) return pi;
1183 else if (pi->fd->name)
1192 haystack = g_utf8_strdown(pi->fd->name, -1);
1193 match = (strstr(haystack, path) != NULL);
1195 if (match) return pi;
1199 if (strstr(pi->fd->name, path)) return pi;
1202 else if (ignore_case)
1204 if (strcasecmp(path, pi->fd->name) == 0) return pi;
1208 if (strcmp(path, pi->fd->name) == 0) return pi;
1218 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y)
1222 if (x < 0 || x >= pw->imd->image_width ||
1223 y < 0 || y >= pw->imd->image_height) return NULL;
1231 if ((pi->type == type || type == ITEM_NONE) &&
1232 x >= pi->x && x < pi->x + pi->width &&
1233 y >= pi->y && y < pi->y + pi->height)
1244 *-----------------------------------------------------------------------------
1246 *-----------------------------------------------------------------------------
1249 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend)
1251 GList *flist = NULL;
1252 GList *dlist = NULL;
1256 filelist_read(path, &flist, &dlist);
1257 if (sort != SORT_NONE)
1259 flist = filelist_sort(flist, sort, ascend);
1260 dlist = filelist_sort(dlist, sort, ascend);
1270 folders = g_list_remove(folders, fd);
1272 if (filelist_read(fd->path, &flist, &dlist))
1274 if (sort != SORT_NONE)
1276 flist = filelist_sort(flist, sort, ascend);
1277 dlist = filelist_sort(dlist, sort, ascend);
1280 result = g_list_concat(result, flist);
1281 folders = g_list_concat(dlist, folders);
1290 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1299 list = pan_window_layout_list(path, SORT_NAME, TRUE);
1301 grid_size = (gint)sqrt((double)g_list_length(list));
1302 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1304 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1308 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1313 w = PAN_THUMB_GAP * 2;
1314 h = PAN_THUMB_GAP * 2;
1327 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1329 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1331 x += pi->width + PAN_THUMB_GAP;
1332 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1341 pi = pan_item_new_thumb(pw, fd, x, y);
1343 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1347 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1351 if (w < pi->x + pi->width + PAN_THUMB_GAP) w = pi->x + pi->width + PAN_THUMB_GAP;
1352 if (h < pi->y + pi->height + PAN_THUMB_GAP) h = pi->y + pi->height + PAN_THUMB_GAP;
1355 if (width) *width = w;
1356 if (height) *height = h;
1361 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1364 gint x1, y1, x2, y2;
1379 if (x1 > pi->x) x1 = pi->x;
1380 if (y1 > pi->y) y1 = pi->y;
1381 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1382 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1385 x1 -= PAN_FOLDER_BOX_BORDER;
1386 y1 -= PAN_FOLDER_BOX_BORDER;
1387 x2 += PAN_FOLDER_BOX_BORDER;
1388 y2 += PAN_FOLDER_BOX_BORDER;
1401 if (pi->type == ITEM_TRIANGLE && pi->data)
1415 if (width) *width = x2 - x1;
1416 if (height) *height = y2 - y1;
1419 typedef struct _FlowerGroup FlowerGroup;
1420 struct _FlowerGroup {
1433 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1437 work = group->items;
1455 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1456 gint *result_x, gint *result_y)
1462 radius = parent->circumference / (2*PI);
1463 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1465 a = 2*PI * group->diameter / parent->circumference;
1467 x = (gint)((double)radius * cos(parent->angle + a / 2));
1468 y = (gint)((double)radius * sin(parent->angle + a / 2));
1475 x += parent->width / 2;
1476 y += parent->height / 2;
1478 x -= group->width / 2;
1479 y -= group->height / 2;
1485 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1492 if (parent && parent->children)
1494 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1502 pan_window_layout_compute_folder_flower_move(group, x, y);
1507 gint px, py, gx, gy;
1508 gint x1, y1, x2, y2;
1510 px = parent->x + parent->width / 2;
1511 py = parent->y + parent->height / 2;
1513 gx = group->x + group->width / 2;
1514 gy = group->y + group->height / 2;
1519 x2 = MAX(px, gx + 5);
1520 y2 = MAX(py, gy + 5);
1522 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1523 px, py, gx, gy, gx + 5, gy + 5,
1525 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1529 pw->list = g_list_concat(group->items, pw->list);
1530 group->items = NULL;
1532 group->circumference = 0;
1533 work = group->children;
1541 group->circumference += child->diameter;
1544 work = g_list_last(group->children);
1552 pan_window_layout_compute_folder_flower_build(pw, child, group);
1555 g_list_free(group->children);
1559 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1572 if (!filelist_read(path, &f, &d)) return NULL;
1573 if (!f && !d) return NULL;
1575 f = filelist_sort(f, SORT_NAME, TRUE);
1576 d = filelist_sort(d, SORT_NAME, TRUE);
1578 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1579 PAN_TEXT_COLOR, 255);
1581 y += pi_box->height;
1583 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1585 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1586 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1587 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1588 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1590 x += PAN_FOLDER_BOX_BORDER;
1591 y += PAN_FOLDER_BOX_BORDER;
1593 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1607 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1609 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1610 x += pi->width + PAN_THUMB_GAP;
1611 if (pi->height > y_height) y_height = pi->height;
1615 pi = pan_item_new_thumb(pw, fd, x, y);
1616 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1617 y_height = PAN_THUMB_SIZE;
1621 if (grid_count >= grid_size)
1625 y += y_height + PAN_THUMB_GAP;
1629 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1634 group = g_new0(FlowerGroup, 1);
1635 group->items = pw->list;
1638 group->width = pi_box->width;
1639 group->height = pi_box->y + pi_box->height;
1640 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1642 group->children = NULL;
1653 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1654 if (child) group->children = g_list_prepend(group->children, child);
1662 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1663 gint *width, gint *height,
1664 gint *scroll_x, gint *scroll_y)
1669 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1670 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1672 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1674 pi = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1677 *scroll_x = pi->x - PAN_FOLDER_BOX_BORDER;
1678 *scroll_y = pi->y - PAN_FOLDER_BOX_BORDER;
1682 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1683 gint *x, gint *y, gint *level,
1685 gint *width, gint *height)
1693 if (!filelist_read(path, &f, &d)) return;
1694 if (!f && !d) return;
1696 f = filelist_sort(f, SORT_NAME, TRUE);
1697 d = filelist_sort(d, SORT_NAME, TRUE);
1699 *x = PAN_THUMB_GAP + ((*level) * (PAN_THUMB_GAP * 2));
1701 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1702 PAN_TEXT_COLOR, 255);
1704 *y += pi_box->height;
1706 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1708 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1709 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1710 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1711 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1713 *x += PAN_FOLDER_BOX_BORDER;
1714 *y += PAN_FOLDER_BOX_BORDER;
1725 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1727 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1728 *x += pi->width + PAN_THUMB_GAP;
1729 if (pi->height > y_height) y_height = pi->height;
1733 pi = pan_item_new_thumb(pw, fd, *x, *y);
1734 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1735 y_height = PAN_THUMB_SIZE;
1738 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1741 if (f) *y = pi_box->y + pi_box->height;
1753 *level = *level + 1;
1754 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1755 pi_box, width, height);
1756 *level = *level - 1;
1761 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1763 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1764 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1766 if (*width < pi_box->x + pi_box->width + PAN_FOLDER_BOX_BORDER)
1767 *width = pi_box->x + pi_box->width + PAN_FOLDER_BOX_BORDER;
1768 if (*height < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1769 *height = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1772 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1779 x = PAN_FOLDER_BOX_BORDER;
1780 y = PAN_FOLDER_BOX_BORDER;
1781 w = PAN_FOLDER_BOX_BORDER * 2;
1782 h = PAN_FOLDER_BOX_BORDER * 2;
1784 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1786 if (width) *width = w;
1787 if (height) *height = h;
1790 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
1799 PanItem *pi_month = NULL;
1800 PanItem *pi_day = NULL;
1806 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
1808 list = pan_window_layout_list(path, SORT_NONE, TRUE);
1809 list = filelist_sort(list, SORT_TIME, TRUE);
1811 w = PAN_FOLDER_BOX_BORDER * 2;
1812 h = PAN_FOLDER_BOX_BORDER * 2;
1817 day_start = month_start;
1832 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
1837 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
1843 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
1847 x = PAN_FOLDER_BOX_BORDER;
1850 y = PAN_FOLDER_BOX_BORDER;
1852 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
1853 pi = pan_item_new_text(pw, x, y, buf,
1854 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1855 PAN_TEXT_COLOR, 255);
1858 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
1860 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1861 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1862 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1864 x += PAN_FOLDER_BOX_BORDER;
1865 y += PAN_FOLDER_BOX_BORDER;
1869 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
1881 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
1883 needle = needle->next;
1892 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
1893 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
1894 PAN_TEXT_COLOR, 255);
1899 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
1900 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1901 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1902 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1904 x += PAN_FOLDER_BOX_BORDER;
1905 y += PAN_FOLDER_BOX_BORDER;
1909 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1911 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1912 if (pi->width > x_width) x_width = pi->width;
1913 y_height = pi->height;
1917 pi = pan_item_new_thumb(pw, fd, x, y);
1918 x_width = PAN_THUMB_SIZE;
1919 y_height = PAN_THUMB_SIZE;
1922 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
1923 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
1927 if (pi->x + pi->width + PAN_FOLDER_BOX_BORDER > pi_day->x + pi_day->width)
1928 pi_day->width = pi->x + pi->width + PAN_FOLDER_BOX_BORDER - pi_day->x;
1929 if (pi->y + pi->height + PAN_FOLDER_BOX_BORDER > pi_day->y + pi_day->height)
1930 pi_day->height = pi->y + pi->height + PAN_FOLDER_BOX_BORDER - pi_day->y;
1933 if (pi_month && pi_day)
1935 if (pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER > pi_month->x + pi_month->width)
1936 pi_month->width = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER - pi_month->x;
1937 if (pi_day->y + pi_day->height + PAN_FOLDER_BOX_BORDER > pi_month->y + pi_month->height)
1938 pi_month->height = pi_day->y + pi_day->height + PAN_FOLDER_BOX_BORDER - pi_month->y;
1945 if (total > 0 && count < PAN_GROUP_MAX)
1947 y += y_height + PAN_THUMB_GAP;
1951 x += x_width + PAN_THUMB_GAP;
1961 if (w < pi->x + pi->width + PAN_THUMB_GAP) w = pi->x + pi->width + PAN_THUMB_GAP;
1962 if (h < pi->y + pi->height + PAN_THUMB_GAP) h = pi->y + pi->height + PAN_THUMB_GAP;
1965 w += PAN_FOLDER_BOX_BORDER;
1966 h += PAN_FOLDER_BOX_BORDER;
1968 if (width) *width = w;
1969 if (height) *height = h;
1974 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
1975 gint *width, gint *height,
1976 gint *scroll_x, gint *scroll_y)
1978 pan_window_items_free(pw);
1982 case LAYOUT_SIZE_THUMB_NONE:
1983 pw->thumb_size = PAN_THUMB_SIZE_NONE;
1984 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
1986 case LAYOUT_SIZE_THUMB_SMALL:
1987 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
1988 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
1990 case LAYOUT_SIZE_THUMB_NORMAL:
1992 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
1993 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
1995 case LAYOUT_SIZE_THUMB_LARGE:
1996 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
1997 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
1999 case LAYOUT_SIZE_10:
2000 pw->image_size = 10;
2001 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2003 case LAYOUT_SIZE_25:
2004 pw->image_size = 25;
2005 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2007 case LAYOUT_SIZE_33:
2008 pw->image_size = 33;
2009 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2011 case LAYOUT_SIZE_50:
2012 pw->image_size = 50;
2013 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2015 case LAYOUT_SIZE_100:
2016 pw->image_size = 100;
2017 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2030 pan_window_layout_compute_grid(pw, path, width, height);
2032 case LAYOUT_FOLDERS_LINEAR:
2033 pan_window_layout_compute_folders_linear(pw, path, width, height);
2035 case LAYOUT_FOLDERS_FLOWER:
2036 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2038 case LAYOUT_TIMELINE:
2039 pan_window_layout_compute_timeline(pw, path, width, height);
2045 printf("computed %d objects\n", g_list_length(pw->list));
2048 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2061 if (util_clip_region_test(x, y, width, height,
2062 pi->x, pi->y, pi->width, pi->height))
2064 list = g_list_prepend(list, pi);
2074 *-----------------------------------------------------------------------------
2076 *-----------------------------------------------------------------------------
2079 static gint pan_layout_queue_step(PanWindow *pw);
2082 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2084 PanWindow *pw = data;
2092 pw->queue_pi = NULL;
2096 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2097 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2100 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2104 thumb_loader_free(pw->tl);
2107 while (pan_layout_queue_step(pw));
2110 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2112 PanWindow *pw = data;
2120 pw->queue_pi = NULL;
2124 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2125 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2126 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2128 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2129 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2130 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2135 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2136 (GdkInterpType)zoom_quality);
2137 g_object_unref(tmp);
2141 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2145 image_loader_free(pw->il);
2148 while (pan_layout_queue_step(pw));
2152 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2153 guint width, guint height, gpointer data)
2155 PanWindow *pw = data;
2166 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2167 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2171 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2177 static gint pan_layout_queue_step(PanWindow *pw)
2181 if (!pw->queue) return FALSE;
2183 pi = pw->queue->data;
2184 pw->queue = g_list_remove(pw->queue, pi);
2187 image_loader_free(pw->il);
2189 thumb_loader_free(pw->tl);
2192 if (pi->type == ITEM_IMAGE)
2194 pw->il = image_loader_new(pi->fd->path);
2196 if (pw->size != LAYOUT_SIZE_100)
2198 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2202 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2204 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2206 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2208 image_loader_free(pw->il);
2211 else if (pi->type == ITEM_THUMB)
2213 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2215 if (!pw->tl->standard_loader)
2217 /* The classic loader will recreate a thumbnail any time we
2218 * request a different size than what exists. This view will
2219 * almost never use the user configured sizes so disable cache.
2221 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2224 thumb_loader_set_callbacks(pw->tl,
2225 pan_layout_queue_thumb_done_cb,
2226 pan_layout_queue_thumb_done_cb,
2229 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2231 thumb_loader_free(pw->tl);
2235 pw->queue_pi->queued = FALSE;
2236 pw->queue_pi = NULL;
2240 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2242 if (!pi || pi->queued || pi->pixbuf) return;
2243 if (pw->size == LAYOUT_SIZE_THUMB_NONE) return;
2246 pw->queue = g_list_prepend(pw->queue, pi);
2248 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2251 static gint pan_window_request_tile_cb(ImageWindow *imd, gint x, gint y, gint width, gint height,
2252 GdkPixbuf *pixbuf, gpointer data)
2254 PanWindow *pw = data;
2259 pixbuf_draw_rect_fill(pixbuf,
2260 0, 0, width, height,
2261 PAN_BACKGROUND_COLOR, 255);
2263 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2265 gint rx, ry, rw, rh;
2267 if (util_clip_region(x, y, width, height,
2269 &rx, &ry, &rw, &rh))
2271 pixbuf_draw_rect_fill(pixbuf,
2272 rx - x, ry - y, rw, rh,
2273 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2276 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2278 gint rx, ry, rw, rh;
2280 if (util_clip_region(x, y, width, height,
2282 &rx, &ry, &rw, &rh))
2284 pixbuf_draw_rect_fill(pixbuf,
2285 rx - x, ry - y, rw, rh,
2286 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2290 list = pan_layout_intersect(pw, x, y, width, height);
2295 gint tx, ty, tw, th;
2296 gint rx, ry, rw, rh;
2303 if (pi->type == ITEM_THUMB && pi->pixbuf)
2305 tw = gdk_pixbuf_get_width(pi->pixbuf);
2306 th = gdk_pixbuf_get_height(pi->pixbuf);
2308 tx = pi->x + (pi->width - tw) / 2;
2309 ty = pi->y + (pi->height - th) / 2;
2311 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2313 if (util_clip_region(x, y, width, height,
2314 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2315 &rx, &ry, &rw, &rh))
2317 pixbuf_draw_shadow(pixbuf,
2318 rx - x, ry - y, rw, rh,
2319 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2321 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2326 if (util_clip_region(x, y, width, height,
2327 tx + tw, ty + PAN_SHADOW_OFFSET,
2328 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2329 &rx, &ry, &rw, &rh))
2331 pixbuf_draw_shadow(pixbuf,
2332 rx - x, ry - y, rw, rh,
2333 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2335 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2337 if (util_clip_region(x, y, width, height,
2338 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2339 &rx, &ry, &rw, &rh))
2341 pixbuf_draw_shadow(pixbuf,
2342 rx - x, ry - y, rw, rh,
2343 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2345 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2349 if (util_clip_region(x, y, width, height,
2351 &rx, &ry, &rw, &rh))
2353 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2356 1.0, 1.0, GDK_INTERP_NEAREST,
2360 if (util_clip_region(x, y, width, height,
2361 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2362 &rx, &ry, &rw, &rh))
2364 pixbuf_draw_rect_fill(pixbuf,
2365 rx - x, ry - y, rw, rh,
2366 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2368 if (util_clip_region(x, y, width, height,
2369 tx, ty, PAN_OUTLINE_THICKNESS, th,
2370 &rx, &ry, &rw, &rh))
2372 pixbuf_draw_rect_fill(pixbuf,
2373 rx - x, ry - y, rw, rh,
2374 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2376 if (util_clip_region(x, y, width, height,
2377 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2378 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2379 &rx, &ry, &rw, &rh))
2381 pixbuf_draw_rect_fill(pixbuf,
2382 rx - x, ry - y, rw, rh,
2383 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2385 if (util_clip_region(x, y, width, height,
2386 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2387 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2388 &rx, &ry, &rw, &rh))
2390 pixbuf_draw_rect_fill(pixbuf,
2391 rx - x, ry - y, rw, rh,
2392 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2395 else if (pi->type == ITEM_THUMB)
2397 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2398 th = pi->height - PAN_SHADOW_OFFSET * 2;
2399 tx = pi->x + PAN_SHADOW_OFFSET;
2400 ty = pi->y + PAN_SHADOW_OFFSET;
2402 if (util_clip_region(x, y, width, height,
2404 &rx, &ry, &rw, &rh))
2408 d = (pw->size == LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2409 pixbuf_draw_rect_fill(pixbuf,
2410 rx - x, ry - y, rw, rh,
2412 PAN_SHADOW_ALPHA / d);
2415 pan_layout_queue(pw, pi);
2417 else if (pi->type == ITEM_IMAGE)
2419 if (util_clip_region(x, y, width, height,
2420 pi->x, pi->y, pi->width, pi->height,
2421 &rx, &ry, &rw, &rh))
2425 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2428 1.0, 1.0, GDK_INTERP_NEAREST,
2433 pixbuf_draw_rect_fill(pixbuf,
2434 rx - x, ry - y, rw, rh,
2435 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2436 pan_layout_queue(pw, pi);
2440 else if (pi->type == ITEM_BOX)
2454 if (pi->color_a > 254)
2456 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2457 shadow[0], bh - shadow[0],
2458 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2460 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2461 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2463 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2465 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2470 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2471 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2473 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2475 PAN_SHADOW_COLOR, a);
2479 if (util_clip_region(x, y, width, height,
2480 pi->x, pi->y, bw, bh,
2481 &rx, &ry, &rw, &rh))
2483 pixbuf_draw_rect_fill(pixbuf,
2484 rx - x, ry - y, rw, rh,
2485 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2487 if (util_clip_region(x, y, width, height,
2488 pi->x, pi->y, bw, pi->border,
2489 &rx, &ry, &rw, &rh))
2491 pixbuf_draw_rect_fill(pixbuf,
2492 rx - x, ry - y, rw, rh,
2493 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2495 if (util_clip_region(x, y, width, height,
2496 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
2497 &rx, &ry, &rw, &rh))
2499 pixbuf_draw_rect_fill(pixbuf,
2500 rx - x, ry - y, rw, rh,
2501 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2503 if (util_clip_region(x, y, width, height,
2504 pi->x + bw - pi->border, pi->y + pi->border,
2505 pi->border, bh - pi->border * 2,
2506 &rx, &ry, &rw, &rh))
2508 pixbuf_draw_rect_fill(pixbuf,
2509 rx - x, ry - y, rw, rh,
2510 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2512 if (util_clip_region(x, y, width, height,
2513 pi->x, pi->y + bh - pi->border,
2515 &rx, &ry, &rw, &rh))
2517 pixbuf_draw_rect_fill(pixbuf,
2518 rx - x, ry - y, rw, rh,
2519 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2522 else if (pi->type == ITEM_TRIANGLE)
2524 if (util_clip_region(x, y, width, height,
2525 pi->x, pi->y, pi->width, pi->height,
2526 &rx, &ry, &rw, &rh) && pi->data)
2528 gint *coord = pi->data;
2529 pixbuf_draw_triangle(pixbuf,
2530 rx - x, ry - y, rw, rh,
2531 coord[0] - x, coord[1] - y,
2532 coord[2] - x, coord[3] - y,
2533 coord[4] - x, coord[5] - y,
2534 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2536 if (pi->border & BORDER_1)
2538 pixbuf_draw_line(pixbuf,
2539 rx - x, ry - y, rw, rh,
2540 coord[0] - x, coord[1] - y,
2541 coord[2] - x, coord[3] - y,
2542 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2544 if (pi->border & BORDER_2)
2546 pixbuf_draw_line(pixbuf,
2547 rx - x, ry - y, rw, rh,
2548 coord[2] - x, coord[3] - y,
2549 coord[4] - x, coord[5] - y,
2550 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2552 if (pi->border & BORDER_3)
2554 pixbuf_draw_line(pixbuf,
2555 rx - x, ry - y, rw, rh,
2556 coord[4] - x, coord[5] - y,
2557 coord[0] - x, coord[1] - y,
2558 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2562 else if (pi->type == ITEM_TEXT && pi->text)
2564 PangoLayout *layout;
2566 layout = pan_item_text_layout(pi, imd->image);
2567 pixbuf_draw_layout(pixbuf, layout, imd->image,
2568 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
2569 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2570 g_object_unref(G_OBJECT(layout));
2577 static gint count = 0;
2578 PangoLayout *layout;
2584 layout = gtk_widget_create_pango_layout(imd->image, NULL);
2586 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
2587 (x / imd->source_tile_width) +
2588 (y / imd->source_tile_height * (imd->image_width/imd->source_tile_width + 1)));
2589 pango_layout_set_text(layout, buf, -1);
2592 pango_layout_get_pixel_size(layout, &lw, &lh);
2594 pixmap = gdk_pixmap_new(imd->widget->window, lw, lh, -1);
2595 gdk_draw_rectangle(pixmap, imd->widget->style->black_gc, TRUE, 0, 0, lw, lh);
2596 gdk_draw_layout(pixmap, imd->widget->style->white_gc, 0, 0, layout);
2597 g_object_unref(G_OBJECT(layout));
2599 lx = MAX(0, width / 2 - lw / 2);
2600 ly = MAX(0, height / 2 - lh / 2);
2601 lw = MIN(lw, width - lx);
2602 lh = MIN(lh, height - ly);
2603 gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_drawable_get_colormap(imd->image->window),
2604 0, 0, lx, ly, lw, lh);
2605 g_object_unref(pixmap);
2613 static void pan_window_dispose_tile_cb(ImageWindow *imd, gint x, gint y, gint width, gint height,
2614 GdkPixbuf *pixbuf, gpointer data)
2616 PanWindow *pw = data;
2620 list = pan_layout_intersect(pw, x, y, width, height);
2629 if (pi->refcount > 0)
2633 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
2638 pw->queue = g_list_remove(pw->queue, pi);
2641 if (pw->queue_pi == pi) pw->queue_pi = NULL;
2644 g_object_unref(pi->pixbuf);
2656 *-----------------------------------------------------------------------------
2658 *-----------------------------------------------------------------------------
2661 static void pan_window_message(PanWindow *pw, const gchar *text)
2671 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
2684 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
2686 size += pi->fd->size;
2691 ss = text_from_size_abrev(size);
2692 buf = g_strdup_printf(_("%d images, %s"), count, ss);
2694 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
2698 static ImageWindow *pan_window_active_image(PanWindow *pw)
2700 if (pw->fs) return pw->fs->imd;
2705 static void pan_window_zoom_limit(PanWindow *pw)
2711 case LAYOUT_SIZE_THUMB_NONE:
2712 case LAYOUT_SIZE_THUMB_SMALL:
2713 case LAYOUT_SIZE_THUMB_NORMAL:
2715 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
2719 case LAYOUT_SIZE_THUMB_LARGE:
2722 case LAYOUT_SIZE_10:
2723 case LAYOUT_SIZE_25:
2726 case LAYOUT_SIZE_33:
2727 case LAYOUT_SIZE_50:
2728 case LAYOUT_SIZE_100:
2734 image_zoom_set_limits(pw->imd, min, 32.0);
2737 static gint pan_window_layout_update_idle_cb(gpointer data)
2739 PanWindow *pw = data;
2745 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2747 if (!pw->cache_list && !pw->cache_todo)
2749 pan_cache_fill(pw, pw->path);
2752 pan_window_message(pw, _("Reading dimensions..."));
2756 if (pan_cache_step(pw))
2760 if (pw->cache_count == pw->cache_total)
2762 pan_window_message(pw, _("Sorting images..."));
2764 else if (pw->cache_tick > 9)
2768 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
2769 pw->cache_total - pw->cache_count);
2770 pan_window_message(pw, buf);
2780 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
2782 pan_window_zoom_limit(pw);
2784 if (width > 0 && height > 0)
2786 image_set_image_as_tiles(pw->imd, width, height,
2787 PAN_TILE_SIZE, PAN_TILE_SIZE, 8,
2788 pan_window_request_tile_cb,
2789 pan_window_dispose_tile_cb, pw, 1.0);
2790 image_scroll_to_point(pw->imd, scroll_x, scroll_y);
2793 pan_window_message(pw, NULL);
2800 static void pan_window_layout_update_idle(PanWindow *pw)
2802 if (pw->idle_id == -1)
2804 pan_window_message(pw, _("Sorting images..."));
2805 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
2810 *-----------------------------------------------------------------------------
2811 * pan window keyboard
2812 *-----------------------------------------------------------------------------
2815 static const gchar *pan_menu_click_path(PanWindow *pw)
2817 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
2821 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
2823 PanWindow *pw = data;
2826 imd = pan_window_active_image(pw);
2827 gdk_window_get_origin(imd->image->window, x, y);
2828 popup_menu_position_clamp(menu, x, y, 0);
2831 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
2833 PanWindow *pw = data;
2836 gint stop_signal = FALSE;
2842 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(pw->imd->widget));
2844 imd = pan_window_active_image(pw);
2845 path = pan_menu_click_path(pw);
2849 switch (event->keyval)
2851 case GDK_Left: case GDK_KP_Left:
2855 case GDK_Right: case GDK_KP_Right:
2859 case GDK_Up: case GDK_KP_Up:
2863 case GDK_Down: case GDK_KP_Down:
2867 case GDK_Page_Up: case GDK_KP_Page_Up:
2868 image_scroll(imd, 0, 0-imd->vis_height / 2);
2870 case GDK_Page_Down: case GDK_KP_Page_Down:
2871 image_scroll(imd, 0, imd->vis_height / 2);
2873 case GDK_Home: case GDK_KP_Home:
2874 image_scroll(imd, 0-imd->vis_width / 2, 0);
2876 case GDK_End: case GDK_KP_End:
2877 image_scroll(imd, imd->vis_width / 2, 0);
2882 if (focused && !(event->state & GDK_CONTROL_MASK) )
2883 switch (event->keyval)
2885 case '+': case '=': case GDK_KP_Add:
2886 image_zoom_adjust(imd, ZOOM_INCREMENT);
2888 case '-': case GDK_KP_Subtract:
2889 image_zoom_adjust(imd, -ZOOM_INCREMENT);
2891 case 'Z': case 'z': case GDK_KP_Divide: case '1':
2892 image_zoom_set(imd, 1.0);
2895 image_zoom_set(imd, 2.0);
2898 image_zoom_set(imd, 3.0);
2901 image_zoom_set(imd, 4.0);
2904 image_zoom_set(imd, -4.0);
2907 image_zoom_set(imd, -3.0);
2910 image_zoom_set(imd, -2.0);
2914 pan_fullscreen_toggle(pw, FALSE);
2918 pan_overlay_toggle(pw);
2920 case GDK_Delete: case GDK_KP_Delete:
2925 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
2932 pan_fullscreen_toggle(pw, TRUE);
2935 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
2937 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
2943 menu = pan_popup_menu(pw);
2944 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
2949 if (event->state & GDK_CONTROL_MASK)
2952 switch (event->keyval)
2985 if (path) file_util_copy(path, NULL, NULL, imd->widget);
2988 if (path) file_util_move(path, NULL, NULL, imd->widget);
2991 if (path) file_util_rename(path, NULL, imd->widget);
2994 if (path) file_util_delete(path, NULL, imd->widget);
2997 if (path) info_window_new(path, NULL);
3000 pan_window_close(pw);
3003 if (n != -1 && path)
3005 pan_fullscreen_toggle(pw, TRUE);
3006 start_editor_from_file(n, path);
3010 else if (event->state & GDK_SHIFT_MASK)
3017 switch (event->keyval)
3022 pan_fullscreen_toggle(pw, TRUE);
3025 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3027 gtk_widget_grab_focus(pw->imd->widget);
3028 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3037 if (x != 0 || y!= 0)
3039 keyboard_scroll_calc(&x, &y, event);
3040 image_scroll(imd, x, y);
3047 *-----------------------------------------------------------------------------
3049 *-----------------------------------------------------------------------------
3052 static void pan_info_update(PanWindow *pw, PanItem *pi)
3058 gint x1, y1, x2, y2, x3, y3;
3061 if (pw->click_pi == pi) return;
3062 if (pi && !pi->fd) pi = NULL;
3064 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3069 printf("info set to %s\n", pi->fd->path);
3071 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3073 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3074 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3075 pan_item_set_key(pbox, "info");
3077 if (pi->type == ITEM_THUMB && pi->pixbuf)
3079 w = gdk_pixbuf_get_width(pi->pixbuf);
3080 h = gdk_pixbuf_get_height(pi->pixbuf);
3082 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3083 y1 = pi->y + (pi->height - h) / 2 + 8;
3087 x1 = pi->x + pi->width - 8;
3095 triangle_rect_region(x1, y1, x2, y2, x3, y3,
3098 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3099 x1, y1, x2, y2, x3, y3,
3100 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3101 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3102 pan_item_set_key(p, "info");
3103 pan_item_added(pw, p);
3105 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3106 _("Filename:"), TEXT_ATTR_BOLD,
3107 PAN_POPUP_TEXT_COLOR, 255);
3108 pan_item_set_key(plabel, "info");
3109 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3110 pi->fd->name, TEXT_ATTR_NONE,
3111 PAN_POPUP_TEXT_COLOR, 255);
3112 pan_item_set_key(p, "info");
3113 pan_item_size_by_item(pbox, p, 0);
3115 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3116 _("Date:"), TEXT_ATTR_BOLD,
3117 PAN_POPUP_TEXT_COLOR, 255);
3118 pan_item_set_key(plabel, "info");
3119 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3120 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3121 PAN_POPUP_TEXT_COLOR, 255);
3122 pan_item_set_key(p, "info");
3123 pan_item_size_by_item(pbox, p, 0);
3125 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3126 _("Size:"), TEXT_ATTR_BOLD,
3127 PAN_POPUP_TEXT_COLOR, 255);
3128 pan_item_set_key(plabel, "info");
3129 buf = text_from_size(pi->fd->size);
3130 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3131 buf, TEXT_ATTR_NONE,
3132 PAN_POPUP_TEXT_COLOR, 255);
3134 pan_item_set_key(p, "info");
3135 pan_item_size_by_item(pbox, p, 0);
3137 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3138 pan_item_added(pw, pbox);
3143 *-----------------------------------------------------------------------------
3145 *-----------------------------------------------------------------------------
3148 static void pan_search_status(PanWindow *pw, const gchar *text)
3150 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3153 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3158 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3160 pi = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3161 if (!pi) return FALSE;
3163 pan_info_update(pw, pi);
3164 image_scroll_to_point(pw->imd, pi->x - PAN_THUMB_GAP, pi->y - PAN_THUMB_GAP);
3166 pan_search_status(pw, (path[0] == '/') ? _("path found") : _("filename found"));
3171 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3176 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3178 pi = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3179 if (!pi) pi = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3184 needle = g_utf8_strdown(text, -1);
3185 pi = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3188 if (!pi) return FALSE;
3190 pan_info_update(pw, pi);
3191 image_scroll_to_point(pw->imd, pi->x - PAN_THUMB_GAP, pi->y - PAN_THUMB_GAP);
3193 pan_search_status(pw, _("partial match"));
3198 static gint valid_date_separator(gchar c)
3200 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3203 static PanItem *pan_search_by_date_val(PanWindow *pw, gint year, gint month, gint day)
3207 work = g_list_last(pw->list);
3219 tl = localtime(&pi->fd->date);
3224 match = (tl->tm_year == year - 1900);
3225 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3226 if (match && day > 0) match = (tl->tm_mday == day);
3228 if (match) return pi;
3236 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3249 if (!text) return FALSE;
3251 ptr = (gchar *)text;
3252 while (*ptr != '\0')
3254 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3259 if (t == -1) return FALSE;
3261 if (!lt) return FALSE;
3263 if (valid_date_separator(*text))
3266 mptr = (gchar *)text;
3270 year = (gint)strtol(text, &mptr, 10);
3271 if (mptr == text) return FALSE;
3274 if (*mptr != '\0' && valid_date_separator(*mptr))
3279 month = strtol(mptr, &dptr, 10);
3282 if (valid_date_separator(*dptr))
3284 month = lt->tm_mon + 1;
3292 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3296 day = strtol(dptr, &eptr, 10);
3306 year = lt->tm_year + 1900;
3308 else if (year < 100)
3317 month < -1 || month == 0 || month > 12 ||
3318 day < -1 || day == 0 || day > 31) return FALSE;
3320 t = date_to_time(year, month, day);
3321 if (t < 0) return FALSE;
3323 pi = pan_search_by_date_val(pw, year, month, day);
3326 pan_info_update(pw, pi);
3327 image_scroll_to_point(pw->imd, pi->x - PAN_THUMB_GAP, pi->y - PAN_THUMB_GAP);
3332 buf = date_value_string(t, DATE_LENGTH_MONTH);
3337 buf = g_strdup_printf("%d %s", day, tmp);
3343 buf = date_value_string(t, DATE_LENGTH_YEAR);
3345 message = g_strdup_printf("%s%s%s%s %s",
3346 (pi) ? "" : "(", (pi) ? "" : _("no match"), (pi) ? "" : ") " ,
3349 pan_search_status(pw, message);
3355 static void pan_search_activate_cb(const gchar *text, gpointer data)
3357 PanWindow *pw = data;
3361 tab_completion_append_to_history(pw->search_entry, text);
3363 if (pan_search_by_path(pw, text)) return;
3365 if (pw->layout == LAYOUT_TIMELINE && pan_search_by_date(pw, text)) return;
3367 if (pan_search_by_partial(pw, text)) return;
3369 pan_search_status(pw, _("no match"));
3372 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3374 PanWindow *pw = data;
3377 visible = GTK_WIDGET_VISIBLE(pw->search_box);
3378 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3382 gtk_widget_hide(pw->search_box);
3383 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3387 gtk_widget_show(pw->search_box);
3388 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3389 gtk_widget_grab_focus(pw->search_entry);
3395 *-----------------------------------------------------------------------------
3396 * view window main routines
3397 *-----------------------------------------------------------------------------
3400 static void button_cb(ImageWindow *imd, gint button, guint32 time,
3401 gdouble x, gdouble y, guint state, gpointer data)
3403 PanWindow *pw = data;
3409 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3410 (gint)((double)(pw->imd->x_scroll + x - pw->imd->x_offset) / pw->imd->scale),
3411 (gint)((double)(pw->imd->y_scroll + y - pw->imd->y_offset) / pw->imd->scale));
3417 pan_info_update(pw, pi);
3422 pan_info_update(pw, pi);
3423 menu = pan_popup_menu(pw);
3424 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, time);
3431 static void scroll_cb(ImageWindow *imd, GdkScrollDirection direction, guint32 time,
3432 gdouble x, gdouble y, guint state, gpointer data)
3435 PanWindow *pw = data;
3438 if (state & GDK_CONTROL_MASK)
3443 image_zoom_adjust_at_point(imd, ZOOM_INCREMENT, x, y);
3445 case GDK_SCROLL_DOWN:
3446 image_zoom_adjust_at_point(imd, -ZOOM_INCREMENT, x, y);
3452 else if ( (state & GDK_SHIFT_MASK) != (mousewheel_scrolls))
3457 image_scroll(imd, 0, -MOUSEWHEEL_SCROLL_SIZE);
3459 case GDK_SCROLL_DOWN:
3460 image_scroll(imd, 0, MOUSEWHEEL_SCROLL_SIZE);
3462 case GDK_SCROLL_LEFT:
3463 image_scroll(imd, -MOUSEWHEEL_SCROLL_SIZE, 0);
3465 case GDK_SCROLL_RIGHT:
3466 image_scroll(imd, MOUSEWHEEL_SCROLL_SIZE, 0);
3478 case GDK_SCROLL_DOWN:
3486 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
3488 image_set_button_func(imd, button_cb, pw);
3489 image_set_scroll_func(imd, scroll_cb, pw);
3492 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
3494 PanWindow *pw = data;
3499 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
3501 if (force_off && !pw->fs) return;
3505 fullscreen_stop(pw->fs);
3506 pw->imd = pw->imd_normal;
3510 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
3512 pan_image_set_buttons(pw, pw->fs->imd);
3513 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
3514 G_CALLBACK(pan_window_key_press_cb), pw);
3516 pw->imd = pw->fs->imd;
3520 static void pan_overlay_toggle(PanWindow *pw)
3524 imd = pan_window_active_image(pw);
3526 if (pw->overlay_id == -1)
3528 pw->overlay_id = image_overlay_info_enable(imd);
3532 image_overlay_info_disable(imd, pw->overlay_id);
3533 pw->overlay_id = -1;
3538 static void pan_window_image_update_cb(ImageWindow *imd, gpointer data)
3540 PanWindow *pw = data;
3543 text = image_zoom_get_as_text(imd);
3544 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
3548 static void pan_window_image_scroll_notify_cb(ImageWindow *imd, gint x, gint y,
3549 gint width, gint height, gpointer data)
3551 PanWindow *pw = data;
3554 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
3555 adj->page_size = (gdouble)imd->vis_width / imd->scale;
3556 adj->page_increment = adj->page_size / 2.0;
3557 adj->step_increment = 48.0 / imd->scale;
3559 adj->upper = MAX((gdouble)width + adj->page_size, 1.0);
3560 adj->value = (gdouble)x;
3562 pref_signal_block_data(pw->scrollbar_h, pw);
3563 gtk_adjustment_changed(adj);
3564 pref_signal_unblock_data(pw->scrollbar_h, pw);
3566 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
3567 adj->page_size = (gdouble)imd->vis_height / imd->scale;
3568 adj->page_increment = adj->page_size / 2.0;
3569 adj->step_increment = 48.0 / imd->scale;
3571 adj->upper = MAX((gdouble)height + adj->page_size, 1.0);
3572 adj->value = (gdouble)y;
3574 pref_signal_block_data(pw->scrollbar_v, pw);
3575 gtk_adjustment_changed(adj);
3576 pref_signal_unblock_data(pw->scrollbar_v, pw);
3578 // printf("scrolled to %d,%d @ %d x %d\n", x, y, width, height);
3581 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
3583 PanWindow *pw = data;
3586 if (!pw->imd->scale) return;
3588 x = (gint)gtk_range_get_value(range);
3590 image_scroll_to_point(pw->imd, x, (gint)((gdouble)pw->imd->y_scroll / pw->imd->scale));
3593 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
3595 PanWindow *pw = data;
3598 if (!pw->imd->scale) return;
3600 y = (gint)gtk_range_get_value(range);
3602 image_scroll_to_point(pw->imd, (gint)((gdouble)pw->imd->x_scroll / pw->imd->scale), y);
3605 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
3607 PanWindow *pw = data;
3609 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3610 pan_window_layout_update_idle(pw);
3613 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
3615 PanWindow *pw = data;
3617 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3618 pan_window_layout_update_idle(pw);
3621 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
3623 PanWindow *pw = data;
3626 path = remove_trailing_slash(new_text);
3627 parse_out_relatives(path);
3631 warning_dialog(_("Folder not found"),
3632 _("The entered path is not a folder"),
3633 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
3637 tab_completion_append_to_history(pw->path_entry, path);
3640 pw->path = g_strdup(path);
3642 pan_window_layout_update_idle(pw);
3645 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
3647 PanWindow *pw = data;
3650 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
3652 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
3653 pan_window_entry_activate_cb(text, pw);
3657 static void pan_window_close(PanWindow *pw)
3659 pan_window_list = g_list_remove(pan_window_list, pw);
3661 if (pw->idle_id != -1)
3663 g_source_remove(pw->idle_id);
3666 pan_fullscreen_toggle(pw, TRUE);
3667 gtk_widget_destroy(pw->window);
3669 pan_window_items_free(pw);
3676 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
3678 PanWindow *pw = data;
3680 pan_window_close(pw);
3684 void pan_window_new(const gchar *path)
3693 GdkGeometry geometry;
3695 pw = g_new0(PanWindow, 1);
3697 pw->path = g_strdup(path);
3698 pw->layout = LAYOUT_TIMELINE;
3699 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
3700 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
3701 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
3705 pw->overlay_id = -1;
3708 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3710 geometry.min_width = 8;
3711 geometry.min_height = 8;
3712 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
3714 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
3715 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
3716 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
3717 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
3719 window_set_icon(pw->window, NULL, NULL);
3721 vbox = gtk_vbox_new(FALSE, 0);
3722 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
3723 gtk_widget_show(vbox);
3725 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3727 pref_spacer(box, 0);
3728 pref_label_new(box, _("Location:"));
3729 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
3730 pan_window_entry_activate_cb, pw);
3731 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
3732 G_CALLBACK(pan_window_entry_change_cb), pw);
3733 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
3734 gtk_widget_show(combo);
3736 combo = gtk_combo_box_new_text();
3737 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
3738 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
3739 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
3740 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
3742 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
3743 g_signal_connect(G_OBJECT(combo), "changed",
3744 G_CALLBACK(pan_window_layout_change_cb), pw);
3745 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
3746 gtk_widget_show(combo);
3748 combo = gtk_combo_box_new_text();
3749 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
3750 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
3751 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
3752 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
3753 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
3754 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
3755 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
3756 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
3757 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
3759 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
3760 g_signal_connect(G_OBJECT(combo), "changed",
3761 G_CALLBACK(pan_window_layout_size_cb), pw);
3762 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
3763 gtk_widget_show(combo);
3765 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
3766 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
3767 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
3769 pw->imd = image_new(TRUE);
3770 pw->imd_normal = pw->imd;
3772 if (black_window_background) image_background_set_black(pw->imd, TRUE);
3773 image_set_update_func(pw->imd, pan_window_image_update_cb, pw);
3775 image_set_scroll_notify_func(pw->imd, pan_window_image_scroll_notify_cb, pw);
3778 gtk_box_pack_start(GTK_BOX(vbox), pw->imd->widget, TRUE, TRUE, 0);
3780 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
3781 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
3782 gtk_widget_show(pw->imd->widget);
3784 pan_window_dnd_init(pw);
3786 pan_image_set_buttons(pw, pw->imd);
3788 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
3789 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
3790 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
3791 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
3792 GTK_FILL | GTK_EXPAND, 0, 0, 0);
3793 gtk_widget_show(pw->scrollbar_h);
3795 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
3796 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
3797 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
3798 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
3799 0, GTK_FILL | GTK_EXPAND, 0, 0);
3800 gtk_widget_show(pw->scrollbar_v);
3804 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
3805 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
3807 pref_spacer(pw->search_box, 0);
3808 pref_label_new(pw->search_box, _("Find:"));
3810 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
3811 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
3812 gtk_widget_show(hbox);
3814 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
3815 pan_search_activate_cb, pw);
3816 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
3817 gtk_widget_show(combo);
3819 pw->search_label = gtk_label_new("");
3820 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
3821 gtk_widget_show(pw->search_label);
3825 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3827 frame = gtk_frame_new(NULL);
3828 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
3829 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
3830 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
3831 gtk_widget_show(frame);
3833 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
3834 gtk_container_add(GTK_CONTAINER(frame), hbox);
3835 gtk_widget_show(hbox);
3837 pref_spacer(hbox, PREF_PAD_SPACE);
3838 pw->label_message = pref_label_new(hbox, "");
3840 frame = gtk_frame_new(NULL);
3841 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
3842 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
3843 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
3844 gtk_widget_show(frame);
3846 pw->label_zoom = gtk_label_new("");
3847 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
3848 gtk_widget_show(pw->label_zoom);
3850 pw->search_button = gtk_toggle_button_new();
3851 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
3852 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
3853 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
3854 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
3855 gtk_widget_show(hbox);
3856 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
3857 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
3858 gtk_widget_show(pw->search_button_arrow);
3859 pref_label_new(hbox, _("Find"));
3861 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
3862 gtk_widget_show(pw->search_button);
3863 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
3864 G_CALLBACK(pan_search_toggle_cb), pw);
3866 g_signal_connect(G_OBJECT(pw->window), "delete_event",
3867 G_CALLBACK(pan_window_delete_cb), pw);
3868 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
3869 G_CALLBACK(pan_window_key_press_cb), pw);
3871 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
3873 pan_window_layout_update_idle(pw);
3875 gtk_widget_grab_focus(pw->imd->widget);
3876 gtk_widget_show(pw->window);
3878 pan_window_list = g_list_append(pan_window_list, pw);
3882 *-----------------------------------------------------------------------------
3884 *-----------------------------------------------------------------------------
3888 *-----------------------------------------------------------------------------
3889 * view window menu routines and callbacks
3890 *-----------------------------------------------------------------------------
3893 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
3895 PanWindow *pw = data;
3898 path = pan_menu_click_path(pw);
3901 pan_fullscreen_toggle(pw, TRUE);
3902 view_window_new(path);
3906 static void pan_edit_cb(GtkWidget *widget, gpointer data)
3912 pw = submenu_item_get_data(widget);
3913 n = GPOINTER_TO_INT(data);
3916 path = pan_menu_click_path(pw);
3919 pan_fullscreen_toggle(pw, TRUE);
3920 start_editor_from_file(n, path);
3924 static void pan_info_cb(GtkWidget *widget, gpointer data)
3926 PanWindow *pw = data;
3929 path = pan_menu_click_path(pw);
3930 if (path) info_window_new(path, NULL);
3933 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
3935 PanWindow *pw = data;
3937 image_zoom_adjust(pan_window_active_image(pw), ZOOM_INCREMENT);
3940 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
3942 PanWindow *pw = data;
3944 image_zoom_adjust(pan_window_active_image(pw), -ZOOM_INCREMENT);
3947 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
3949 PanWindow *pw = data;
3951 image_zoom_set(pan_window_active_image(pw), 1.0);
3954 static void pan_copy_cb(GtkWidget *widget, gpointer data)
3956 PanWindow *pw = data;
3959 path = pan_menu_click_path(pw);
3960 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
3963 static void pan_move_cb(GtkWidget *widget, gpointer data)
3965 PanWindow *pw = data;
3968 path = pan_menu_click_path(pw);
3969 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
3972 static void pan_rename_cb(GtkWidget *widget, gpointer data)
3974 PanWindow *pw = data;
3977 path = pan_menu_click_path(pw);
3978 if (path) file_util_rename(path, NULL, pw->imd->widget);
3981 static void pan_delete_cb(GtkWidget *widget, gpointer data)
3983 PanWindow *pw = data;
3986 path = pan_menu_click_path(pw);
3987 if (path) file_util_delete(path, NULL, pw->imd->widget);
3990 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
3992 PanWindow *pw = data;
3994 pan_fullscreen_toggle(pw, FALSE);
3997 static void pan_close_cb(GtkWidget *widget, gpointer data)
3999 PanWindow *pw = data;
4001 pan_window_close(pw);
4004 static GtkWidget *pan_popup_menu(PanWindow *pw)
4010 active = (pw->click_pi != NULL);
4012 menu = popup_menu_short_lived();
4014 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4015 G_CALLBACK(pan_zoom_in_cb), pw);
4016 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4017 G_CALLBACK(pan_zoom_out_cb), pw);
4018 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4019 G_CALLBACK(pan_zoom_1_1_cb), pw);
4020 menu_item_add_divider(menu);
4022 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4023 gtk_widget_set_sensitive(item, active);
4025 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4026 G_CALLBACK(pan_info_cb), pw);
4028 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4029 G_CALLBACK(pan_new_window_cb), pw);
4031 menu_item_add_divider(menu);
4032 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4033 G_CALLBACK(pan_copy_cb), pw);
4034 menu_item_add_sensitive(menu, _("_Move..."), active,
4035 G_CALLBACK(pan_move_cb), pw);
4036 menu_item_add_sensitive(menu, _("_Rename..."), active,
4037 G_CALLBACK(pan_rename_cb), pw);
4038 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4039 G_CALLBACK(pan_delete_cb), pw);
4041 menu_item_add_divider(menu);
4045 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4049 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4052 menu_item_add_divider(menu);
4053 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4059 *-----------------------------------------------------------------------------
4060 * image drag and drop routines
4061 *-----------------------------------------------------------------------------
4064 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4066 GtkSelectionData *selection_data, guint info,
4067 guint time, gpointer data)
4069 PanWindow *pw = data;
4072 if (gtk_drag_get_source_widget(context) == pw->imd->image) return;
4076 if (info == TARGET_URI_LIST)
4080 list = uri_list_from_text(selection_data->data, TRUE);
4081 if (list && isdir((gchar *)list->data))
4083 printf("FIXME: change to this folder: %s\n", (gchar *)list->data);
4086 path_list_free(list);
4090 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4091 GtkSelectionData *selection_data, guint info,
4092 guint time, gpointer data)
4094 printf("FIXME: set dnd data\n");
4097 static void pan_window_dnd_init(PanWindow *pw)
4103 gtk_drag_source_set(imd->image, GDK_BUTTON2_MASK,
4104 dnd_file_drag_types, dnd_file_drag_types_count,
4105 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4106 g_signal_connect(G_OBJECT(imd->image), "drag_data_get",
4107 G_CALLBACK(pan_window_set_dnd_data), pw);
4109 gtk_drag_dest_set(imd->image,
4110 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4111 dnd_file_drop_types, dnd_file_drop_types_count,
4112 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4113 g_signal_connect(G_OBJECT(imd->image), "drag_data_received",
4114 G_CALLBACK(pan_window_get_dnd_data), pw);
4118 *-----------------------------------------------------------------------------
4119 * maintenance (for rename, move, remove)
4120 *-----------------------------------------------------------------------------