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 + pi->width / 2;
1678 *scroll_y = pi->y + pi->height / 2;
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)
2788 image_set_image_as_tiles(pw->imd, width, height,
2789 PAN_TILE_SIZE, PAN_TILE_SIZE, 8,
2790 pan_window_request_tile_cb,
2791 pan_window_dispose_tile_cb, pw, 1.0);
2793 if (scroll_x == 0 && scroll_y == 0)
2801 image_scroll_to_point(pw->imd, scroll_x, scroll_y, align, align);
2804 pan_window_message(pw, NULL);
2811 static void pan_window_layout_update_idle(PanWindow *pw)
2813 if (pw->idle_id == -1)
2815 pan_window_message(pw, _("Sorting images..."));
2816 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
2821 *-----------------------------------------------------------------------------
2822 * pan window keyboard
2823 *-----------------------------------------------------------------------------
2826 static const gchar *pan_menu_click_path(PanWindow *pw)
2828 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
2832 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
2834 PanWindow *pw = data;
2837 imd = pan_window_active_image(pw);
2838 gdk_window_get_origin(imd->image->window, x, y);
2839 popup_menu_position_clamp(menu, x, y, 0);
2842 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
2844 PanWindow *pw = data;
2847 gint stop_signal = FALSE;
2853 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(pw->imd->widget));
2855 imd = pan_window_active_image(pw);
2856 path = pan_menu_click_path(pw);
2860 switch (event->keyval)
2862 case GDK_Left: case GDK_KP_Left:
2866 case GDK_Right: case GDK_KP_Right:
2870 case GDK_Up: case GDK_KP_Up:
2874 case GDK_Down: case GDK_KP_Down:
2878 case GDK_Page_Up: case GDK_KP_Page_Up:
2879 image_scroll(imd, 0, 0-imd->vis_height / 2);
2881 case GDK_Page_Down: case GDK_KP_Page_Down:
2882 image_scroll(imd, 0, imd->vis_height / 2);
2884 case GDK_Home: case GDK_KP_Home:
2885 image_scroll(imd, 0-imd->vis_width / 2, 0);
2887 case GDK_End: case GDK_KP_End:
2888 image_scroll(imd, imd->vis_width / 2, 0);
2893 if (focused && !(event->state & GDK_CONTROL_MASK) )
2894 switch (event->keyval)
2896 case '+': case '=': case GDK_KP_Add:
2897 image_zoom_adjust(imd, ZOOM_INCREMENT);
2899 case '-': case GDK_KP_Subtract:
2900 image_zoom_adjust(imd, -ZOOM_INCREMENT);
2902 case 'Z': case 'z': case GDK_KP_Divide: case '1':
2903 image_zoom_set(imd, 1.0);
2906 image_zoom_set(imd, 2.0);
2909 image_zoom_set(imd, 3.0);
2912 image_zoom_set(imd, 4.0);
2915 image_zoom_set(imd, -4.0);
2918 image_zoom_set(imd, -3.0);
2921 image_zoom_set(imd, -2.0);
2925 pan_fullscreen_toggle(pw, FALSE);
2929 pan_overlay_toggle(pw);
2931 case GDK_Delete: case GDK_KP_Delete:
2936 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
2943 pan_fullscreen_toggle(pw, TRUE);
2946 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
2948 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
2954 menu = pan_popup_menu(pw);
2955 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
2960 if (event->state & GDK_CONTROL_MASK)
2963 switch (event->keyval)
2996 if (path) file_util_copy(path, NULL, NULL, imd->widget);
2999 if (path) file_util_move(path, NULL, NULL, imd->widget);
3002 if (path) file_util_rename(path, NULL, imd->widget);
3005 if (path) file_util_delete(path, NULL, imd->widget);
3008 if (path) info_window_new(path, NULL);
3011 pan_window_close(pw);
3014 if (n != -1 && path)
3016 pan_fullscreen_toggle(pw, TRUE);
3017 start_editor_from_file(n, path);
3021 else if (event->state & GDK_SHIFT_MASK)
3028 switch (event->keyval)
3033 pan_fullscreen_toggle(pw, TRUE);
3036 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3038 gtk_widget_grab_focus(pw->imd->widget);
3039 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3048 if (x != 0 || y!= 0)
3050 keyboard_scroll_calc(&x, &y, event);
3051 image_scroll(imd, x, y);
3058 *-----------------------------------------------------------------------------
3060 *-----------------------------------------------------------------------------
3063 static void pan_info_update(PanWindow *pw, PanItem *pi)
3069 gint x1, y1, x2, y2, x3, y3;
3072 if (pw->click_pi == pi) return;
3073 if (pi && !pi->fd) pi = NULL;
3075 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3080 printf("info set to %s\n", pi->fd->path);
3082 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3084 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3085 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3086 pan_item_set_key(pbox, "info");
3088 if (pi->type == ITEM_THUMB && pi->pixbuf)
3090 w = gdk_pixbuf_get_width(pi->pixbuf);
3091 h = gdk_pixbuf_get_height(pi->pixbuf);
3093 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3094 y1 = pi->y + (pi->height - h) / 2 + 8;
3098 x1 = pi->x + pi->width - 8;
3106 triangle_rect_region(x1, y1, x2, y2, x3, y3,
3109 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3110 x1, y1, x2, y2, x3, y3,
3111 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3112 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3113 pan_item_set_key(p, "info");
3114 pan_item_added(pw, p);
3116 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3117 _("Filename:"), TEXT_ATTR_BOLD,
3118 PAN_POPUP_TEXT_COLOR, 255);
3119 pan_item_set_key(plabel, "info");
3120 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3121 pi->fd->name, TEXT_ATTR_NONE,
3122 PAN_POPUP_TEXT_COLOR, 255);
3123 pan_item_set_key(p, "info");
3124 pan_item_size_by_item(pbox, p, 0);
3126 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3127 _("Date:"), TEXT_ATTR_BOLD,
3128 PAN_POPUP_TEXT_COLOR, 255);
3129 pan_item_set_key(plabel, "info");
3130 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3131 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3132 PAN_POPUP_TEXT_COLOR, 255);
3133 pan_item_set_key(p, "info");
3134 pan_item_size_by_item(pbox, p, 0);
3136 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3137 _("Size:"), TEXT_ATTR_BOLD,
3138 PAN_POPUP_TEXT_COLOR, 255);
3139 pan_item_set_key(plabel, "info");
3140 buf = text_from_size(pi->fd->size);
3141 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3142 buf, TEXT_ATTR_NONE,
3143 PAN_POPUP_TEXT_COLOR, 255);
3145 pan_item_set_key(p, "info");
3146 pan_item_size_by_item(pbox, p, 0);
3148 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3149 pan_item_added(pw, pbox);
3154 *-----------------------------------------------------------------------------
3156 *-----------------------------------------------------------------------------
3159 static void pan_search_status(PanWindow *pw, const gchar *text)
3161 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3164 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3169 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3171 pi = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3172 if (!pi) return FALSE;
3174 pan_info_update(pw, pi);
3175 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3177 pan_search_status(pw, (path[0] == '/') ? _("path found") : _("filename found"));
3182 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3187 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3189 pi = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3190 if (!pi) pi = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3195 needle = g_utf8_strdown(text, -1);
3196 pi = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3199 if (!pi) return FALSE;
3201 pan_info_update(pw, pi);
3202 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3204 pan_search_status(pw, _("partial match"));
3209 static gint valid_date_separator(gchar c)
3211 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3214 static PanItem *pan_search_by_date_val(PanWindow *pw, ItemType type, gint year, gint month, gint day)
3218 work = g_list_last(pw->list);
3226 if (pi->fd && (pi->type == type || type == ITEM_NONE))
3230 tl = localtime(&pi->fd->date);
3235 match = (tl->tm_year == year - 1900);
3236 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3237 if (match && day > 0) match = (tl->tm_mday == day);
3239 if (match) return pi;
3247 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3261 if (!text) return FALSE;
3263 ptr = (gchar *)text;
3264 while (*ptr != '\0')
3266 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3271 if (t == -1) return FALSE;
3273 if (!lt) return FALSE;
3275 if (valid_date_separator(*text))
3278 mptr = (gchar *)text;
3282 year = (gint)strtol(text, &mptr, 10);
3283 if (mptr == text) return FALSE;
3286 if (*mptr != '\0' && valid_date_separator(*mptr))
3291 month = strtol(mptr, &dptr, 10);
3294 if (valid_date_separator(*dptr))
3296 month = lt->tm_mon + 1;
3304 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3308 day = strtol(dptr, &eptr, 10);
3318 year = lt->tm_year + 1900;
3320 else if (year < 100)
3329 month < -1 || month == 0 || month > 12 ||
3330 day < -1 || day == 0 || day > 31) return FALSE;
3332 t = date_to_time(year, month, day);
3333 if (t < 0) return FALSE;
3335 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3337 pi = pan_search_by_date_val(pw, type, year, month, day);
3340 pan_info_update(pw, pi);
3341 image_scroll_to_point(pw->imd,
3342 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3348 buf = date_value_string(t, DATE_LENGTH_MONTH);
3353 buf = g_strdup_printf("%d %s", day, tmp);
3359 buf = date_value_string(t, DATE_LENGTH_YEAR);
3361 message = g_strdup_printf("%s%s%s%s %s",
3362 (pi) ? "" : "(", (pi) ? "" : _("no match"), (pi) ? "" : ") " ,
3365 pan_search_status(pw, message);
3371 static void pan_search_activate_cb(const gchar *text, gpointer data)
3373 PanWindow *pw = data;
3377 tab_completion_append_to_history(pw->search_entry, text);
3379 if (pan_search_by_path(pw, text)) return;
3381 if (pw->layout == LAYOUT_TIMELINE && pan_search_by_date(pw, text)) return;
3383 if (pan_search_by_partial(pw, text)) return;
3385 pan_search_status(pw, _("no match"));
3388 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3390 PanWindow *pw = data;
3393 visible = GTK_WIDGET_VISIBLE(pw->search_box);
3394 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3398 gtk_widget_hide(pw->search_box);
3399 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3403 gtk_widget_show(pw->search_box);
3404 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3405 gtk_widget_grab_focus(pw->search_entry);
3411 *-----------------------------------------------------------------------------
3412 * view window main routines
3413 *-----------------------------------------------------------------------------
3416 static void button_cb(ImageWindow *imd, gint button, guint32 time,
3417 gdouble x, gdouble y, guint state, gpointer data)
3419 PanWindow *pw = data;
3425 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3426 (gint)((double)(pw->imd->x_scroll + x - pw->imd->x_offset) / pw->imd->scale),
3427 (gint)((double)(pw->imd->y_scroll + y - pw->imd->y_offset) / pw->imd->scale));
3433 pan_info_update(pw, pi);
3438 pan_info_update(pw, pi);
3439 menu = pan_popup_menu(pw);
3440 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, time);
3447 static void scroll_cb(ImageWindow *imd, GdkScrollDirection direction, guint32 time,
3448 gdouble x, gdouble y, guint state, gpointer data)
3451 PanWindow *pw = data;
3454 if (state & GDK_CONTROL_MASK)
3459 image_zoom_adjust_at_point(imd, ZOOM_INCREMENT, x, y);
3461 case GDK_SCROLL_DOWN:
3462 image_zoom_adjust_at_point(imd, -ZOOM_INCREMENT, x, y);
3468 else if ( (state & GDK_SHIFT_MASK) != (mousewheel_scrolls))
3473 image_scroll(imd, 0, -MOUSEWHEEL_SCROLL_SIZE);
3475 case GDK_SCROLL_DOWN:
3476 image_scroll(imd, 0, MOUSEWHEEL_SCROLL_SIZE);
3478 case GDK_SCROLL_LEFT:
3479 image_scroll(imd, -MOUSEWHEEL_SCROLL_SIZE, 0);
3481 case GDK_SCROLL_RIGHT:
3482 image_scroll(imd, MOUSEWHEEL_SCROLL_SIZE, 0);
3494 case GDK_SCROLL_DOWN:
3502 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
3504 image_set_button_func(imd, button_cb, pw);
3505 image_set_scroll_func(imd, scroll_cb, pw);
3508 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
3510 PanWindow *pw = data;
3515 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
3517 if (force_off && !pw->fs) return;
3521 fullscreen_stop(pw->fs);
3522 pw->imd = pw->imd_normal;
3526 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
3528 pan_image_set_buttons(pw, pw->fs->imd);
3529 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
3530 G_CALLBACK(pan_window_key_press_cb), pw);
3532 pw->imd = pw->fs->imd;
3536 static void pan_overlay_toggle(PanWindow *pw)
3540 imd = pan_window_active_image(pw);
3542 if (pw->overlay_id == -1)
3544 pw->overlay_id = image_overlay_info_enable(imd);
3548 image_overlay_info_disable(imd, pw->overlay_id);
3549 pw->overlay_id = -1;
3554 static void pan_window_image_update_cb(ImageWindow *imd, gpointer data)
3556 PanWindow *pw = data;
3559 text = image_zoom_get_as_text(imd);
3560 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
3564 static void pan_window_image_scroll_notify_cb(ImageWindow *imd, gint x, gint y,
3565 gint width, gint height, gpointer data)
3567 PanWindow *pw = data;
3570 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
3571 adj->page_size = (gdouble)imd->vis_width / imd->scale;
3572 adj->page_increment = adj->page_size / 2.0;
3573 adj->step_increment = 48.0 / imd->scale;
3575 adj->upper = MAX((gdouble)width + adj->page_size, 1.0);
3576 adj->value = (gdouble)x;
3578 pref_signal_block_data(pw->scrollbar_h, pw);
3579 gtk_adjustment_changed(adj);
3580 pref_signal_unblock_data(pw->scrollbar_h, pw);
3582 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
3583 adj->page_size = (gdouble)imd->vis_height / imd->scale;
3584 adj->page_increment = adj->page_size / 2.0;
3585 adj->step_increment = 48.0 / imd->scale;
3587 adj->upper = MAX((gdouble)height + adj->page_size, 1.0);
3588 adj->value = (gdouble)y;
3590 pref_signal_block_data(pw->scrollbar_v, pw);
3591 gtk_adjustment_changed(adj);
3592 pref_signal_unblock_data(pw->scrollbar_v, pw);
3594 // printf("scrolled to %d,%d @ %d x %d\n", x, y, width, height);
3597 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
3599 PanWindow *pw = data;
3602 if (!pw->imd->scale) return;
3604 x = (gint)gtk_range_get_value(range);
3606 image_scroll_to_point(pw->imd, x, (gint)((gdouble)pw->imd->y_scroll / pw->imd->scale), 0.0, 0.0);
3609 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
3611 PanWindow *pw = data;
3614 if (!pw->imd->scale) return;
3616 y = (gint)gtk_range_get_value(range);
3618 image_scroll_to_point(pw->imd, (gint)((gdouble)pw->imd->x_scroll / pw->imd->scale), y, 0.0, 0.0);
3621 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
3623 PanWindow *pw = data;
3625 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3626 pan_window_layout_update_idle(pw);
3629 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
3631 PanWindow *pw = data;
3633 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3634 pan_window_layout_update_idle(pw);
3637 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
3639 PanWindow *pw = data;
3642 path = remove_trailing_slash(new_text);
3643 parse_out_relatives(path);
3647 warning_dialog(_("Folder not found"),
3648 _("The entered path is not a folder"),
3649 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
3653 tab_completion_append_to_history(pw->path_entry, path);
3656 pw->path = g_strdup(path);
3658 pan_window_layout_update_idle(pw);
3661 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
3663 PanWindow *pw = data;
3666 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
3668 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
3669 pan_window_entry_activate_cb(text, pw);
3673 static void pan_window_close(PanWindow *pw)
3675 pan_window_list = g_list_remove(pan_window_list, pw);
3677 if (pw->idle_id != -1)
3679 g_source_remove(pw->idle_id);
3682 pan_fullscreen_toggle(pw, TRUE);
3683 gtk_widget_destroy(pw->window);
3685 pan_window_items_free(pw);
3692 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
3694 PanWindow *pw = data;
3696 pan_window_close(pw);
3700 void pan_window_new(const gchar *path)
3709 GdkGeometry geometry;
3711 pw = g_new0(PanWindow, 1);
3713 pw->path = g_strdup(path);
3714 pw->layout = LAYOUT_TIMELINE;
3715 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
3716 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
3717 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
3721 pw->overlay_id = -1;
3724 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3726 geometry.min_width = 8;
3727 geometry.min_height = 8;
3728 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
3730 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
3731 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
3732 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
3733 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
3735 window_set_icon(pw->window, NULL, NULL);
3737 vbox = gtk_vbox_new(FALSE, 0);
3738 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
3739 gtk_widget_show(vbox);
3741 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3743 pref_spacer(box, 0);
3744 pref_label_new(box, _("Location:"));
3745 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
3746 pan_window_entry_activate_cb, pw);
3747 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
3748 G_CALLBACK(pan_window_entry_change_cb), pw);
3749 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
3750 gtk_widget_show(combo);
3752 combo = gtk_combo_box_new_text();
3753 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
3754 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
3755 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
3756 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
3758 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
3759 g_signal_connect(G_OBJECT(combo), "changed",
3760 G_CALLBACK(pan_window_layout_change_cb), pw);
3761 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
3762 gtk_widget_show(combo);
3764 combo = gtk_combo_box_new_text();
3765 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
3766 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
3767 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
3768 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
3769 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
3770 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
3771 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
3772 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
3773 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
3775 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
3776 g_signal_connect(G_OBJECT(combo), "changed",
3777 G_CALLBACK(pan_window_layout_size_cb), pw);
3778 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
3779 gtk_widget_show(combo);
3781 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
3782 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
3783 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
3785 pw->imd = image_new(TRUE);
3786 pw->imd_normal = pw->imd;
3788 if (black_window_background) image_background_set_black(pw->imd, TRUE);
3789 image_set_update_func(pw->imd, pan_window_image_update_cb, pw);
3791 image_set_scroll_notify_func(pw->imd, pan_window_image_scroll_notify_cb, pw);
3794 gtk_box_pack_start(GTK_BOX(vbox), pw->imd->widget, TRUE, TRUE, 0);
3796 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
3797 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
3798 gtk_widget_show(pw->imd->widget);
3800 pan_window_dnd_init(pw);
3802 pan_image_set_buttons(pw, pw->imd);
3804 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
3805 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
3806 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
3807 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
3808 GTK_FILL | GTK_EXPAND, 0, 0, 0);
3809 gtk_widget_show(pw->scrollbar_h);
3811 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
3812 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
3813 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
3814 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
3815 0, GTK_FILL | GTK_EXPAND, 0, 0);
3816 gtk_widget_show(pw->scrollbar_v);
3820 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
3821 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
3823 pref_spacer(pw->search_box, 0);
3824 pref_label_new(pw->search_box, _("Find:"));
3826 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
3827 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
3828 gtk_widget_show(hbox);
3830 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
3831 pan_search_activate_cb, pw);
3832 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
3833 gtk_widget_show(combo);
3835 pw->search_label = gtk_label_new("");
3836 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
3837 gtk_widget_show(pw->search_label);
3841 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3843 frame = gtk_frame_new(NULL);
3844 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
3845 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
3846 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
3847 gtk_widget_show(frame);
3849 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
3850 gtk_container_add(GTK_CONTAINER(frame), hbox);
3851 gtk_widget_show(hbox);
3853 pref_spacer(hbox, PREF_PAD_SPACE);
3854 pw->label_message = pref_label_new(hbox, "");
3856 frame = gtk_frame_new(NULL);
3857 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
3858 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
3859 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
3860 gtk_widget_show(frame);
3862 pw->label_zoom = gtk_label_new("");
3863 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
3864 gtk_widget_show(pw->label_zoom);
3866 pw->search_button = gtk_toggle_button_new();
3867 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
3868 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
3869 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
3870 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
3871 gtk_widget_show(hbox);
3872 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
3873 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
3874 gtk_widget_show(pw->search_button_arrow);
3875 pref_label_new(hbox, _("Find"));
3877 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
3878 gtk_widget_show(pw->search_button);
3879 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
3880 G_CALLBACK(pan_search_toggle_cb), pw);
3882 g_signal_connect(G_OBJECT(pw->window), "delete_event",
3883 G_CALLBACK(pan_window_delete_cb), pw);
3884 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
3885 G_CALLBACK(pan_window_key_press_cb), pw);
3887 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
3889 pan_window_layout_update_idle(pw);
3891 gtk_widget_grab_focus(pw->imd->widget);
3892 gtk_widget_show(pw->window);
3894 pan_window_list = g_list_append(pan_window_list, pw);
3898 *-----------------------------------------------------------------------------
3900 *-----------------------------------------------------------------------------
3904 *-----------------------------------------------------------------------------
3905 * view window menu routines and callbacks
3906 *-----------------------------------------------------------------------------
3909 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
3911 PanWindow *pw = data;
3914 path = pan_menu_click_path(pw);
3917 pan_fullscreen_toggle(pw, TRUE);
3918 view_window_new(path);
3922 static void pan_edit_cb(GtkWidget *widget, gpointer data)
3928 pw = submenu_item_get_data(widget);
3929 n = GPOINTER_TO_INT(data);
3932 path = pan_menu_click_path(pw);
3935 pan_fullscreen_toggle(pw, TRUE);
3936 start_editor_from_file(n, path);
3940 static void pan_info_cb(GtkWidget *widget, gpointer data)
3942 PanWindow *pw = data;
3945 path = pan_menu_click_path(pw);
3946 if (path) info_window_new(path, NULL);
3949 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
3951 PanWindow *pw = data;
3953 image_zoom_adjust(pan_window_active_image(pw), ZOOM_INCREMENT);
3956 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
3958 PanWindow *pw = data;
3960 image_zoom_adjust(pan_window_active_image(pw), -ZOOM_INCREMENT);
3963 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
3965 PanWindow *pw = data;
3967 image_zoom_set(pan_window_active_image(pw), 1.0);
3970 static void pan_copy_cb(GtkWidget *widget, gpointer data)
3972 PanWindow *pw = data;
3975 path = pan_menu_click_path(pw);
3976 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
3979 static void pan_move_cb(GtkWidget *widget, gpointer data)
3981 PanWindow *pw = data;
3984 path = pan_menu_click_path(pw);
3985 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
3988 static void pan_rename_cb(GtkWidget *widget, gpointer data)
3990 PanWindow *pw = data;
3993 path = pan_menu_click_path(pw);
3994 if (path) file_util_rename(path, NULL, pw->imd->widget);
3997 static void pan_delete_cb(GtkWidget *widget, gpointer data)
3999 PanWindow *pw = data;
4002 path = pan_menu_click_path(pw);
4003 if (path) file_util_delete(path, NULL, pw->imd->widget);
4006 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4008 PanWindow *pw = data;
4010 pan_fullscreen_toggle(pw, FALSE);
4013 static void pan_close_cb(GtkWidget *widget, gpointer data)
4015 PanWindow *pw = data;
4017 pan_window_close(pw);
4020 static GtkWidget *pan_popup_menu(PanWindow *pw)
4026 active = (pw->click_pi != NULL);
4028 menu = popup_menu_short_lived();
4030 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4031 G_CALLBACK(pan_zoom_in_cb), pw);
4032 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4033 G_CALLBACK(pan_zoom_out_cb), pw);
4034 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4035 G_CALLBACK(pan_zoom_1_1_cb), pw);
4036 menu_item_add_divider(menu);
4038 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4039 gtk_widget_set_sensitive(item, active);
4041 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4042 G_CALLBACK(pan_info_cb), pw);
4044 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4045 G_CALLBACK(pan_new_window_cb), pw);
4047 menu_item_add_divider(menu);
4048 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4049 G_CALLBACK(pan_copy_cb), pw);
4050 menu_item_add_sensitive(menu, _("_Move..."), active,
4051 G_CALLBACK(pan_move_cb), pw);
4052 menu_item_add_sensitive(menu, _("_Rename..."), active,
4053 G_CALLBACK(pan_rename_cb), pw);
4054 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4055 G_CALLBACK(pan_delete_cb), pw);
4057 menu_item_add_divider(menu);
4061 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4065 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4068 menu_item_add_divider(menu);
4069 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4075 *-----------------------------------------------------------------------------
4076 * image drag and drop routines
4077 *-----------------------------------------------------------------------------
4080 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4082 GtkSelectionData *selection_data, guint info,
4083 guint time, gpointer data)
4085 PanWindow *pw = data;
4088 if (gtk_drag_get_source_widget(context) == pw->imd->image) return;
4092 if (info == TARGET_URI_LIST)
4096 list = uri_list_from_text(selection_data->data, TRUE);
4097 if (list && isdir((gchar *)list->data))
4099 printf("FIXME: change to this folder: %s\n", (gchar *)list->data);
4102 path_list_free(list);
4106 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4107 GtkSelectionData *selection_data, guint info,
4108 guint time, gpointer data)
4110 printf("FIXME: set dnd data\n");
4113 static void pan_window_dnd_init(PanWindow *pw)
4119 gtk_drag_source_set(imd->image, GDK_BUTTON2_MASK,
4120 dnd_file_drag_types, dnd_file_drag_types_count,
4121 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4122 g_signal_connect(G_OBJECT(imd->image), "drag_data_get",
4123 G_CALLBACK(pan_window_set_dnd_data), pw);
4125 gtk_drag_dest_set(imd->image,
4126 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4127 dnd_file_drop_types, dnd_file_drop_types_count,
4128 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4129 g_signal_connect(G_OBJECT(imd->image), "drag_data_received",
4130 G_CALLBACK(pan_window_get_dnd_data), pw);
4134 *-----------------------------------------------------------------------------
4135 * maintenance (for rename, move, remove)
4136 *-----------------------------------------------------------------------------