7 * This software is released under the GNU General Public License (GNU GPL).
8 * Please read the included file COPYING for more information.
9 * This software comes with no warranty of any kind, use at your own risk!
20 #include "fullscreen.h"
22 #include "image-load.h"
26 #include "pixbuf_util.h"
29 #include "ui_bookmark.h"
30 #include "ui_fileops.h"
33 #include "ui_tabcomp.h"
35 #include <gdk/gdkkeysyms.h> /* for keyboard values */
39 #define PAN_WINDOW_DEFAULT_WIDTH 720
40 #define PAN_WINDOW_DEFAULT_HEIGHT 500
42 #define PAN_TILE_SIZE 512
44 #define PAN_THUMB_SIZE_DOTS 4
45 #define PAN_THUMB_SIZE_NONE 24
46 #define PAN_THUMB_SIZE_SMALL 64
47 #define PAN_THUMB_SIZE_NORMAL 128
48 #define PAN_THUMB_SIZE_LARGE 256
49 #define PAN_THUMB_SIZE pw->thumb_size
51 #define PAN_THUMB_GAP_DOTS 2
52 #define PAN_THUMB_GAP_SMALL 14
53 #define PAN_THUMB_GAP_NORMAL 30
54 #define PAN_THUMB_GAP_LARGE 40
55 #define PAN_THUMB_GAP_HUGE 50
56 #define PAN_THUMB_GAP pw->thumb_gap
58 #define PAN_SHADOW_OFFSET 6
59 #define PAN_SHADOW_FADE 5
60 #define PAN_SHADOW_COLOR 0, 0, 0
61 #define PAN_SHADOW_ALPHA 64
63 #define PAN_OUTLINE_THICKNESS 1
64 #define PAN_OUTLINE_COLOR_1 255, 255, 255
65 #define PAN_OUTLINE_COLOR_2 64, 64, 64
66 #define PAN_OUTLINE_ALPHA 180
68 #define PAN_BACKGROUND_COLOR 255, 255, 230
70 #define PAN_GRID_SIZE 10
71 #define PAN_GRID_COLOR 0, 0, 0
72 #define PAN_GRID_ALPHA 20
74 #define PAN_FOLDER_BOX_COLOR 0, 0, 255
75 #define PAN_FOLDER_BOX_ALPHA 10
76 #define PAN_FOLDER_BOX_BORDER 20
78 #define PAN_FOLDER_BOX_OUTLINE_THICKNESS 4
79 #define PAN_FOLDER_BOX_OUTLINE_COLOR 0, 0, 255
80 #define PAN_FOLDER_BOX_OUTLINE_ALPHA 64
82 #define PAN_TEXT_BORDER_SIZE 4
83 #define PAN_TEXT_COLOR 0, 0, 0
85 #define PAN_POPUP_COLOR 255, 255, 220
86 #define PAN_POPUP_ALPHA 255
87 #define PAN_POPUP_BORDER 1
88 #define PAN_POPUP_BORDER_COLOR 0, 0, 0
89 #define PAN_POPUP_TEXT_COLOR 0, 0, 0
91 #define PAN_GROUP_MAX 16
93 #define ZOOM_INCREMENT 1.0
94 #define ZOOM_LABEL_WIDTH 64
97 #define PAN_PREF_GROUP "pan_view_options"
98 #define PAN_PREF_HIDE_WARNING "hide_performance_warning"
103 LAYOUT_FOLDERS_LINEAR,
104 LAYOUT_FOLDERS_FLOWER,
109 LAYOUT_SIZE_THUMB_DOTS = 0,
110 LAYOUT_SIZE_THUMB_NONE,
111 LAYOUT_SIZE_THUMB_SMALL,
112 LAYOUT_SIZE_THUMB_NORMAL,
113 LAYOUT_SIZE_THUMB_LARGE,
132 TEXT_ATTR_BOLD = 1 << 0,
133 TEXT_ATTR_HEADING = 1 << 1,
134 TEXT_ATTR_MARKUP = 1 << 2
145 typedef struct _PanItem PanItem;
160 TextAttrType text_attr;
178 typedef struct _PanWindow PanWindow;
183 ImageWindow *imd_normal;
186 GtkWidget *path_entry;
188 GtkWidget *label_message;
189 GtkWidget *label_zoom;
191 GtkWidget *search_box;
192 GtkWidget *search_entry;
193 GtkWidget *search_label;
194 GtkWidget *search_button;
195 GtkWidget *search_button_arrow;
197 GtkWidget *scrollbar_h;
198 GtkWidget *scrollbar_v;
227 typedef struct _PanCacheData PanCacheData;
228 struct _PanCacheData {
234 static GList *pan_window_list = NULL;
237 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend);
239 static GtkWidget *pan_popup_menu(PanWindow *pw);
240 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off);
241 static void pan_overlay_toggle(PanWindow *pw);
243 static void pan_window_close(PanWindow *pw);
245 static void pan_window_dnd_init(PanWindow *pw);
248 static gint util_clip_region(gint x, gint y, gint w, gint h,
249 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
250 gint *rx, gint *ry, gint *rw, gint *rh)
252 if (clip_x + clip_w <= x ||
254 clip_y + clip_h <= y ||
260 *rx = MAX(x, clip_x);
261 *rw = MIN((x + w), (clip_x + clip_w)) - *rx;
263 *ry = MAX(y, clip_y);
264 *rh = MIN((y + h), (clip_y + clip_h)) - *ry;
269 static gint util_clip_region_test(gint x, gint y, gint w, gint h,
270 gint clip_x, gint clip_y, gint clip_w, gint clip_h)
274 return util_clip_region(x, y, w, h,
275 clip_x, clip_y, clip_w, clip_h,
288 static gint date_compare(time_t a, time_t b, DateLengthType length)
293 if (length == DATE_LENGTH_EXACT) return (a == b);
295 if (!localtime_r(&a, &ta) ||
296 !localtime_r(&b, &tb)) return FALSE;
298 if (ta.tm_year != tb.tm_year) return FALSE;
299 if (length == DATE_LENGTH_YEAR) return TRUE;
301 if (ta.tm_mon != tb.tm_mon) return FALSE;
302 if (length == DATE_LENGTH_MONTH) return TRUE;
304 if (length == DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
306 if (ta.tm_mday != tb.tm_mday) return FALSE;
307 if (length == DATE_LENGTH_DAY) return TRUE;
309 return (ta.tm_hour == tb.tm_hour);
312 static gchar *date_value_string(time_t d, DateLengthType length)
316 gchar *format = NULL;
318 if (!localtime_r(&d, &td)) return g_strdup("");
322 case DATE_LENGTH_DAY:
323 return g_strdup_printf("%d", td.tm_mday);
325 case DATE_LENGTH_WEEK:
328 case DATE_LENGTH_MONTH:
331 case DATE_LENGTH_YEAR:
332 return g_strdup_printf("%d", td.tm_year + 1900);
334 case DATE_LENGTH_EXACT:
336 return g_strdup(text_from_time(d));
341 if (format && strftime(buf, sizeof(buf), format, &td) > 0)
343 gchar *ret = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
350 static time_t date_to_time(gint year, gint month, gint day)
357 lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
358 lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
359 lt.tm_year = year - 1900;
366 *-----------------------------------------------------------------------------
368 *-----------------------------------------------------------------------------
371 static void triangle_rect_region(gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
372 gint *rx, gint *ry, gint *rw, gint *rh)
380 tw = MAX(abs(x1 - x2), abs(x2 - x3));
381 tw = MAX(tw, abs(x3 - x1));
382 th = MAX(abs(y1 - y2), abs(y2 - y3));
383 th = MAX(th, abs(y3 - y1));
391 static void pixbuf_draw_triangle(GdkPixbuf *pb,
392 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
393 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
394 guint8 r, guint8 g, guint8 b, guint8 a)
410 pw = gdk_pixbuf_get_width(pb);
411 ph = gdk_pixbuf_get_height(pb);
413 if (!util_clip_region(0, 0, pw, ph,
414 clip_x, clip_y, clip_w, clip_h,
415 &rx, &ry, &rw, &rh)) return;
417 triangle_rect_region(x1, y1, x2, y2, x3, y3,
420 if (!util_clip_region(rx, ry, rw, rh,
422 &fx1, &fy1, &fw, &fh)) return;
426 p_alpha = gdk_pixbuf_get_has_alpha(pb);
427 prs = gdk_pixbuf_get_rowstride(pb);
428 p_pix = gdk_pixbuf_get_pixels(pb);
430 p_step = (p_alpha) ? 4 : 3;
431 for (i = fy1; i < fy2; i++)
433 pp = p_pix + i * prs + (fx1 * p_step);
434 for (j = fx1; j < fx2; j++)
438 z1 = (y1 - y2)*(j - x2) + (x2 - x1)*(i - y2);
439 z2 = (y2 - y3)*(j - x3) + (x3 - x2)*(i - y3);
442 z2 = (y3 - y1)*(j - x1) + (x1 - x3)*(i - y1);
445 pp[0] = (r * a + pp[0] * (256-a)) >> 8;
446 pp[1] = (g * a + pp[1] * (256-a)) >> 8;
447 pp[2] = (b * a + pp[2] * (256-a)) >> 8;
455 static void pixbuf_draw_line(GdkPixbuf *pb,
456 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
457 gint x1, gint y1, gint x2, gint y2,
458 guint8 r, guint8 g, guint8 b, guint8 a)
463 gint fx1, fy1, fx2, fy2;
469 gdouble xstep, ystep;
476 pw = gdk_pixbuf_get_width(pb);
477 ph = gdk_pixbuf_get_height(pb);
479 if (!util_clip_region(0, 0, pw, ph,
480 clip_x, clip_y, clip_w, clip_h,
481 &rx, &ry, &rw, &rh)) return;
493 if (xa == 0 && ya == 0) return;
495 nt = sqrt(xd * xd + yd * yd);
497 nt = (xa > ya) ? xa : ya;
498 xstep = (double)xd / nt;
499 ystep = (double)yd / nt;
501 p_alpha = gdk_pixbuf_get_has_alpha(pb);
502 prs = gdk_pixbuf_get_rowstride(pb);
503 p_pix = gdk_pixbuf_get_pixels(pb);
505 p_step = (p_alpha) ? 4 : 3;
509 for (n = 0; n < nt; n++)
514 if (x >= fx1 && x < fx2 &&
517 pp = p_pix + y * prs + x * p_step;
518 *pp = (r * a + *pp * (256-a)) >> 8;
520 *pp = (g * a + *pp * (256-a)) >> 8;
522 *pp = (b * a + *pp * (256-a)) >> 8;
529 static void pixbuf_draw_fade_linear(guchar *p_pix, gint prs, gint p_alpha,
530 gint s, gint vertical, gint border,
531 gint x1, gint y1, gint x2, gint y2,
532 guint8 r, guint8 g, guint8 b, guint8 a)
539 p_step = (p_alpha) ? 4 : 3;
540 for (j = y1; j < y2; j++)
542 pp = p_pix + j * prs + x1 * p_step;
543 if (!vertical) n = a - a * abs(j - s) / border;
544 for (i = x1; i < x2; i++)
546 if (vertical) n = a - a * abs(i - s) / border;
547 *pp = (r * n + *pp * (256-n)) >> 8;
549 *pp = (g * n + *pp * (256-n)) >> 8;
551 *pp = (b * n + *pp * (256-n)) >> 8;
558 static void pixbuf_draw_fade_radius(guchar *p_pix, gint prs, gint p_alpha,
559 gint sx, gint sy, gint border,
560 gint x1, gint y1, gint x2, gint y2,
561 guint8 r, guint8 g, guint8 b, guint8 a)
567 p_step = (p_alpha) ? 4 : 3;
568 for (j = y1; j < y2; j++)
570 pp = p_pix + j * prs + x1 * p_step;
571 for (i = x1; i < x2; i++)
576 r = MIN(border, (gint)sqrt((i-sx)*(i-sx) + (j-sy)*(j-sy)));
577 n = a - a * r / border;
578 *pp = (r * n + *pp * (256-n)) >> 8;
580 *pp = (g * n + *pp * (256-n)) >> 8;
582 *pp = (b * n + *pp * (256-n)) >> 8;
589 static void pixbuf_draw_shadow(GdkPixbuf *pb,
590 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
591 gint x, gint y, gint w, gint h, gint border,
592 guint8 r, guint8 g, guint8 b, guint8 a)
602 pw = gdk_pixbuf_get_width(pb);
603 ph = gdk_pixbuf_get_height(pb);
605 if (!util_clip_region(0, 0, pw, ph,
606 clip_x, clip_y, clip_w, clip_h,
607 &rx, &ry, &rw, &rh)) return;
609 p_alpha = gdk_pixbuf_get_has_alpha(pb);
610 prs = gdk_pixbuf_get_rowstride(pb);
611 p_pix = gdk_pixbuf_get_pixels(pb);
613 if (util_clip_region(x + border, y + border, w - border * 2, h - border * 2,
617 pixbuf_draw_rect_fill(pb, fx, fy, fw, fh, r, g, b, a);
620 if (border < 1) return;
622 if (util_clip_region(x, y + border, border, h - border * 2,
626 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
627 x + border, TRUE, border,
628 fx, fy, fx + fw, fy + fh,
631 if (util_clip_region(x + w - border, y + border, border, h - border * 2,
635 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
636 x + w - border, TRUE, border,
637 fx, fy, fx + fw, fy + fh,
640 if (util_clip_region(x + border, y, w - border * 2, border,
644 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
645 y + border, FALSE, border,
646 fx, fy, fx + fw, fy + fh,
649 if (util_clip_region(x + border, y + h - border, w - border * 2, border,
653 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
654 y + h - border, FALSE, border,
655 fx, fy, fx + fw, fy + fh,
658 if (util_clip_region(x, y, border, border,
662 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
663 x + border, y + border, border,
664 fx, fy, fx + fw, fy + fh,
667 if (util_clip_region(x + w - border, y, border, border,
671 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
672 x + w - border, y + border, border,
673 fx, fy, fx + fw, fy + fh,
676 if (util_clip_region(x, y + h - border, border, border,
680 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
681 x + border, y + h - border, border,
682 fx, fy, fx + fw, fy + fh,
685 if (util_clip_region(x + w - border, y + h - border, border, border,
689 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
690 x + w - border, y + h - border, border,
691 fx, fy, fx + fw, fy + fh,
698 *-----------------------------------------------------------------------------
700 *-----------------------------------------------------------------------------
703 static void pan_cache_free(PanWindow *pw)
707 work = pw->cache_list;
715 cache_sim_data_free(pc->cd);
716 file_data_free((FileData *)pc);
719 g_list_free(pw->cache_list);
720 pw->cache_list = NULL;
722 filelist_free(pw->cache_todo);
723 pw->cache_todo = NULL;
730 static void pan_cache_fill(PanWindow *pw, const gchar *path)
736 list = pan_window_layout_list(path, SORT_NAME, TRUE);
737 pw->cache_todo = g_list_reverse(list);
739 pw->cache_total = g_list_length(pw->cache_todo);
742 static gint pan_cache_step(PanWindow *pw)
746 CacheData *cd = NULL;
748 if (!pw->cache_todo) return FALSE;
750 fd = pw->cache_todo->data;
751 pw->cache_todo = g_list_remove(pw->cache_todo, fd);
753 if (enable_thumb_caching)
757 found = cache_find_location(CACHE_TYPE_SIM, fd->path);
758 if (found && filetime(found) == fd->date)
760 cd = cache_sim_data_load(found);
765 if (!cd) cd = cache_sim_data_new();
769 cd->dimensions = image_load_dimensions(fd->path, &cd->width, &cd->height);
770 if (enable_thumb_caching &&
776 base = cache_get_location(CACHE_TYPE_SIM, fd->path, FALSE, &mode);
777 if (cache_ensure_dir_exists(base, mode))
780 cd->path = cache_get_location(CACHE_TYPE_SIM, fd->path, TRUE, NULL);
781 if (cache_sim_data_save(cd))
783 filetime_set(cd->path, filetime(fd->path));
792 pc = g_new0(PanCacheData, 1);
793 memcpy(pc, fd, sizeof(FileData));
798 pw->cache_list = g_list_prepend(pw->cache_list, pc);
805 *-----------------------------------------------------------------------------
807 *-----------------------------------------------------------------------------
810 static void pan_item_free(PanItem *pi)
814 if (pi->pixbuf) g_object_unref(pi->pixbuf);
815 if (pi->fd) file_data_free(pi->fd);
823 static void pan_window_items_free(PanWindow *pw)
830 PanItem *pi = work->data;
836 g_list_free(pw->list);
839 g_list_free(pw->queue);
843 image_loader_free(pw->il);
846 thumb_loader_free(pw->tl);
852 static PanItem *pan_item_new_thumb(PanWindow *pw, FileData *fd, gint x, gint y)
856 pi = g_new0(PanItem, 1);
857 pi->type = ITEM_THUMB;
861 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
862 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
868 pw->list = g_list_prepend(pw->list, pi);
873 static PanItem *pan_item_new_box(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
875 guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
876 guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
880 pi = g_new0(PanItem, 1);
888 pi->color_r = base_r;
889 pi->color_g = base_g;
890 pi->color_b = base_b;
891 pi->color_a = base_a;
893 pi->color2_r = bord_r;
894 pi->color2_g = bord_g;
895 pi->color2_b = bord_b;
896 pi->color2_a = bord_a;
897 pi->border = border_size;
899 pw->list = g_list_prepend(pw->list, pi);
904 static void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
908 if (!pi || pi->type != ITEM_BOX) return;
913 pi->width -= shadow[0];
914 pi->height -= shadow[0];
917 shadow = g_new0(gint, 2);
922 pi->height += offset;
928 static PanItem *pan_item_new_tri(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
929 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
930 guint8 r, guint8 g, guint8 b, guint8 a)
935 pi = g_new0(PanItem, 1);
936 pi->type = ITEM_TRIANGLE;
947 coord = g_new0(gint, 6);
957 pi->border = BORDER_NONE;
959 pw->list = g_list_prepend(pw->list, pi);
964 static void pan_item_tri_border(PanItem *pi, gint borders,
965 guint8 r, guint8 g, guint8 b, guint8 a)
967 if (!pi || pi->type != ITEM_TRIANGLE) return;
969 pi->border = borders;
977 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
981 layout = gtk_widget_create_pango_layout(widget, NULL);
983 if (pi->text_attr & TEXT_ATTR_MARKUP)
985 pango_layout_set_markup(layout, pi->text, -1);
989 if (pi->text_attr & TEXT_ATTR_BOLD ||
990 pi->text_attr & TEXT_ATTR_HEADING)
995 pal = pango_attr_list_new();
996 if (pi->text_attr & TEXT_ATTR_BOLD)
998 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
1000 pa->end_index = G_MAXINT;
1001 pango_attr_list_insert(pal, pa);
1003 if (pi->text_attr & TEXT_ATTR_HEADING)
1005 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
1006 pa->start_index = 0;
1007 pa->end_index = G_MAXINT;
1008 pango_attr_list_insert(pal, pa);
1010 pango_layout_set_attributes(layout, pal);
1011 pango_attr_list_unref(pal);
1014 pango_layout_set_text(layout, pi->text, -1);
1018 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
1020 PangoLayout *layout;
1022 if (!pi || !pi->text || !widget) return;
1024 layout = pan_item_text_layout(pi, widget);
1025 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
1026 g_object_unref(G_OBJECT(layout));
1028 pi->width += PAN_TEXT_BORDER_SIZE * 2;
1029 pi->height += PAN_TEXT_BORDER_SIZE * 2;
1032 static PanItem *pan_item_new_text(PanWindow *pw, gint x, gint y, const gchar *text, TextAttrType attr,
1033 guint8 r, guint8 g, guint8 b, guint8 a)
1037 pi = g_new0(PanItem, 1);
1038 pi->type = ITEM_TEXT;
1041 pi->text = g_strdup(text);
1042 pi->text_attr = attr;
1049 pan_item_text_compute_size(pi, pw->imd->widget);
1051 pw->list = g_list_prepend(pw->list, pi);
1056 static void pan_item_set_key(PanItem *pi, const gchar *key)
1063 pi->key = g_strdup(key);
1067 static void pan_item_added(PanWindow *pw, PanItem *pi)
1070 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1073 static void pan_item_remove(PanWindow *pw, PanItem *pi)
1077 if (pw->click_pi == pi) pw->click_pi = NULL;
1079 pw->list = g_list_remove(pw->list, pi);
1080 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1084 static void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
1086 if (!pi || !child) return;
1088 if (pi->x + pi->width < child->x + child->width + border)
1089 pi->width = child->x + child->width + border - pi->x;
1091 if (pi->y + pi->height < child->y + child->height + border)
1092 pi->height = child->y + child->height + border - pi->y;
1095 static void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
1099 if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
1100 if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
1103 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
1110 if (!pi->fd) return;
1112 work = pw->cache_list;
1121 path = ((FileData *)pc)->path;
1123 if (pc->cd && pc->cd->dimensions &&
1124 path && strcmp(path, pi->fd->path) == 0)
1126 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
1127 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
1129 pw->cache_list = g_list_remove(pw->cache_list, pc);
1130 cache_sim_data_free(pc->cd);
1131 file_data_free((FileData *)pc);
1137 static PanItem *pan_item_new_image(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
1141 pi = g_new0(PanItem, 1);
1142 pi->type = ITEM_IMAGE;
1147 pan_item_image_find_size(pw, pi, w, h);
1149 pw->list = g_list_prepend(pw->list, pi);
1154 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
1158 if (!key) return NULL;
1160 work = g_list_last(pw->list);
1166 if ((pi->type == type || type == ITEM_NONE) &&
1167 pi->key && strcmp(pi->key, key) == 0)
1177 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1178 static PanItem *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1179 gint ignore_case, gint partial)
1183 if (!path) return NULL;
1184 if (partial && path[0] == '/') return NULL;
1186 work = g_list_last(pw->list);
1192 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1196 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) return pi;
1198 else if (pi->fd->name)
1207 haystack = g_utf8_strdown(pi->fd->name, -1);
1208 match = (strstr(haystack, path) != NULL);
1210 if (match) return pi;
1214 if (strstr(pi->fd->name, path)) return pi;
1217 else if (ignore_case)
1219 if (strcasecmp(path, pi->fd->name) == 0) return pi;
1223 if (strcmp(path, pi->fd->name) == 0) return pi;
1233 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y)
1237 if (x < 0 || x >= pw->imd->image_width ||
1238 y < 0 || y >= pw->imd->image_height) return NULL;
1246 if ((pi->type == type || type == ITEM_NONE) &&
1247 x >= pi->x && x < pi->x + pi->width &&
1248 y >= pi->y && y < pi->y + pi->height)
1259 *-----------------------------------------------------------------------------
1261 *-----------------------------------------------------------------------------
1264 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend)
1266 GList *flist = NULL;
1267 GList *dlist = NULL;
1271 filelist_read(path, &flist, &dlist);
1272 if (sort != SORT_NONE)
1274 flist = filelist_sort(flist, sort, ascend);
1275 dlist = filelist_sort(dlist, sort, ascend);
1285 folders = g_list_remove(folders, fd);
1287 if (filelist_read(fd->path, &flist, &dlist))
1289 if (sort != SORT_NONE)
1291 flist = filelist_sort(flist, sort, ascend);
1292 dlist = filelist_sort(dlist, sort, ascend);
1295 result = g_list_concat(result, flist);
1296 folders = g_list_concat(dlist, folders);
1305 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1313 list = pan_window_layout_list(path, SORT_NAME, TRUE);
1315 grid_size = (gint)sqrt((double)g_list_length(list));
1316 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1318 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1322 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1327 *width = PAN_FOLDER_BOX_BORDER * 2;
1328 *height = PAN_FOLDER_BOX_BORDER * 2;
1341 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1343 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1345 x += pi->width + PAN_THUMB_GAP;
1346 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1355 pi = pan_item_new_thumb(pw, fd, x, y);
1357 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1361 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1364 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1370 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1373 gint x1, y1, x2, y2;
1388 if (x1 > pi->x) x1 = pi->x;
1389 if (y1 > pi->y) y1 = pi->y;
1390 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1391 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1394 x1 -= PAN_FOLDER_BOX_BORDER;
1395 y1 -= PAN_FOLDER_BOX_BORDER;
1396 x2 += PAN_FOLDER_BOX_BORDER;
1397 y2 += PAN_FOLDER_BOX_BORDER;
1410 if (pi->type == ITEM_TRIANGLE && pi->data)
1424 if (width) *width = x2 - x1;
1425 if (height) *height = y2 - y1;
1428 typedef struct _FlowerGroup FlowerGroup;
1429 struct _FlowerGroup {
1442 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1446 work = group->items;
1464 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1465 gint *result_x, gint *result_y)
1471 radius = parent->circumference / (2*PI);
1472 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1474 a = 2*PI * group->diameter / parent->circumference;
1476 x = (gint)((double)radius * cos(parent->angle + a / 2));
1477 y = (gint)((double)radius * sin(parent->angle + a / 2));
1484 x += parent->width / 2;
1485 y += parent->height / 2;
1487 x -= group->width / 2;
1488 y -= group->height / 2;
1494 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1501 if (parent && parent->children)
1503 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1511 pan_window_layout_compute_folder_flower_move(group, x, y);
1516 gint px, py, gx, gy;
1517 gint x1, y1, x2, y2;
1519 px = parent->x + parent->width / 2;
1520 py = parent->y + parent->height / 2;
1522 gx = group->x + group->width / 2;
1523 gy = group->y + group->height / 2;
1528 x2 = MAX(px, gx + 5);
1529 y2 = MAX(py, gy + 5);
1531 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1532 px, py, gx, gy, gx + 5, gy + 5,
1534 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1538 pw->list = g_list_concat(group->items, pw->list);
1539 group->items = NULL;
1541 group->circumference = 0;
1542 work = group->children;
1550 group->circumference += child->diameter;
1553 work = g_list_last(group->children);
1561 pan_window_layout_compute_folder_flower_build(pw, child, group);
1564 g_list_free(group->children);
1568 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1581 if (!filelist_read(path, &f, &d)) return NULL;
1582 if (!f && !d) return NULL;
1584 f = filelist_sort(f, SORT_NAME, TRUE);
1585 d = filelist_sort(d, SORT_NAME, TRUE);
1587 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1588 PAN_TEXT_COLOR, 255);
1590 y += pi_box->height;
1592 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1594 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1595 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1596 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1597 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1599 x += PAN_FOLDER_BOX_BORDER;
1600 y += PAN_FOLDER_BOX_BORDER;
1602 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1616 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1618 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1619 x += pi->width + PAN_THUMB_GAP;
1620 if (pi->height > y_height) y_height = pi->height;
1624 pi = pan_item_new_thumb(pw, fd, x, y);
1625 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1626 y_height = PAN_THUMB_SIZE;
1630 if (grid_count >= grid_size)
1634 y += y_height + PAN_THUMB_GAP;
1638 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1643 group = g_new0(FlowerGroup, 1);
1644 group->items = pw->list;
1647 group->width = pi_box->width;
1648 group->height = pi_box->y + pi_box->height;
1649 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1651 group->children = NULL;
1662 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1663 if (child) group->children = g_list_prepend(group->children, child);
1671 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1672 gint *width, gint *height,
1673 gint *scroll_x, gint *scroll_y)
1678 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1679 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1681 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1683 pi = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1686 *scroll_x = pi->x + pi->width / 2;
1687 *scroll_y = pi->y + pi->height / 2;
1691 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1692 gint *x, gint *y, gint *level,
1694 gint *width, gint *height)
1702 if (!filelist_read(path, &f, &d)) return;
1703 if (!f && !d) return;
1705 f = filelist_sort(f, SORT_NAME, TRUE);
1706 d = filelist_sort(d, SORT_NAME, TRUE);
1708 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1710 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1711 PAN_TEXT_COLOR, 255);
1713 *y += pi_box->height;
1715 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1717 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1718 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1719 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1720 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1722 *x += PAN_FOLDER_BOX_BORDER;
1723 *y += PAN_FOLDER_BOX_BORDER;
1734 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1736 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1737 *x += pi->width + PAN_THUMB_GAP;
1738 if (pi->height > y_height) y_height = pi->height;
1742 pi = pan_item_new_thumb(pw, fd, *x, *y);
1743 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1744 y_height = PAN_THUMB_SIZE;
1747 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1750 if (f) *y = pi_box->y + pi_box->height;
1762 *level = *level + 1;
1763 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1764 pi_box, width, height);
1765 *level = *level - 1;
1770 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1772 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1773 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1775 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1778 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1785 x = PAN_FOLDER_BOX_BORDER;
1786 y = PAN_FOLDER_BOX_BORDER;
1787 w = PAN_FOLDER_BOX_BORDER * 2;
1788 h = PAN_FOLDER_BOX_BORDER * 2;
1790 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1792 if (width) *width = w;
1793 if (height) *height = h;
1796 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
1804 PanItem *pi_month = NULL;
1805 PanItem *pi_day = NULL;
1811 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
1813 list = pan_window_layout_list(path, SORT_NONE, TRUE);
1814 list = filelist_sort(list, SORT_TIME, TRUE);
1816 *width = PAN_FOLDER_BOX_BORDER * 2;
1817 *height = PAN_FOLDER_BOX_BORDER * 2;
1822 day_start = month_start;
1837 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
1842 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
1848 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
1852 x = PAN_FOLDER_BOX_BORDER;
1855 y = PAN_FOLDER_BOX_BORDER;
1857 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
1858 pi = pan_item_new_text(pw, x, y, buf,
1859 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1860 PAN_TEXT_COLOR, 255);
1863 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
1865 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1866 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1867 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1869 x += PAN_FOLDER_BOX_BORDER;
1870 y += PAN_FOLDER_BOX_BORDER;
1874 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
1886 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
1888 needle = needle->next;
1897 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
1898 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
1899 PAN_TEXT_COLOR, 255);
1904 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
1905 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1906 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1907 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1909 x += PAN_FOLDER_BOX_BORDER;
1910 y += PAN_FOLDER_BOX_BORDER;
1914 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1916 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1917 if (pi->width > x_width) x_width = pi->width;
1918 y_height = pi->height;
1922 pi = pan_item_new_thumb(pw, fd, x, y);
1923 x_width = PAN_THUMB_SIZE;
1924 y_height = PAN_THUMB_SIZE;
1927 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
1928 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
1933 if (total > 0 && count < PAN_GROUP_MAX)
1935 y += y_height + PAN_THUMB_GAP;
1939 x += x_width + PAN_THUMB_GAP;
1949 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
1955 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
1956 gint *width, gint *height,
1957 gint *scroll_x, gint *scroll_y)
1959 pan_window_items_free(pw);
1963 case LAYOUT_SIZE_THUMB_DOTS:
1964 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
1965 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
1967 case LAYOUT_SIZE_THUMB_NONE:
1968 pw->thumb_size = PAN_THUMB_SIZE_NONE;
1969 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
1971 case LAYOUT_SIZE_THUMB_SMALL:
1972 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
1973 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
1975 case LAYOUT_SIZE_THUMB_NORMAL:
1977 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
1978 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
1980 case LAYOUT_SIZE_THUMB_LARGE:
1981 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
1982 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
1984 case LAYOUT_SIZE_10:
1985 pw->image_size = 10;
1986 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
1988 case LAYOUT_SIZE_25:
1989 pw->image_size = 25;
1990 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
1992 case LAYOUT_SIZE_33:
1993 pw->image_size = 33;
1994 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
1996 case LAYOUT_SIZE_50:
1997 pw->image_size = 50;
1998 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2000 case LAYOUT_SIZE_100:
2001 pw->image_size = 100;
2002 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2015 pan_window_layout_compute_grid(pw, path, width, height);
2017 case LAYOUT_FOLDERS_LINEAR:
2018 pan_window_layout_compute_folders_linear(pw, path, width, height);
2020 case LAYOUT_FOLDERS_FLOWER:
2021 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2023 case LAYOUT_TIMELINE:
2024 pan_window_layout_compute_timeline(pw, path, width, height);
2030 printf("computed %d objects\n", g_list_length(pw->list));
2033 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2046 if (util_clip_region_test(x, y, width, height,
2047 pi->x, pi->y, pi->width, pi->height))
2049 list = g_list_prepend(list, pi);
2059 *-----------------------------------------------------------------------------
2061 *-----------------------------------------------------------------------------
2064 static gint pan_layout_queue_step(PanWindow *pw);
2067 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2069 PanWindow *pw = data;
2077 pw->queue_pi = NULL;
2081 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2082 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2085 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2089 thumb_loader_free(pw->tl);
2092 while (pan_layout_queue_step(pw));
2095 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2097 PanWindow *pw = data;
2105 pw->queue_pi = NULL;
2109 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2110 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2111 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2113 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2114 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2115 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2120 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2121 (GdkInterpType)zoom_quality);
2122 g_object_unref(tmp);
2126 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2130 image_loader_free(pw->il);
2133 while (pan_layout_queue_step(pw));
2137 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2138 guint width, guint height, gpointer data)
2140 PanWindow *pw = data;
2151 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2152 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2156 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2162 static gint pan_layout_queue_step(PanWindow *pw)
2166 if (!pw->queue) return FALSE;
2168 pi = pw->queue->data;
2169 pw->queue = g_list_remove(pw->queue, pi);
2172 image_loader_free(pw->il);
2174 thumb_loader_free(pw->tl);
2177 if (pi->type == ITEM_IMAGE)
2179 pw->il = image_loader_new(pi->fd->path);
2181 if (pw->size != LAYOUT_SIZE_100)
2183 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2187 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2189 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2191 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2193 image_loader_free(pw->il);
2196 else if (pi->type == ITEM_THUMB)
2198 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2200 if (!pw->tl->standard_loader)
2202 /* The classic loader will recreate a thumbnail any time we
2203 * request a different size than what exists. This view will
2204 * almost never use the user configured sizes so disable cache.
2206 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2209 thumb_loader_set_callbacks(pw->tl,
2210 pan_layout_queue_thumb_done_cb,
2211 pan_layout_queue_thumb_done_cb,
2214 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2216 thumb_loader_free(pw->tl);
2220 pw->queue_pi->queued = FALSE;
2221 pw->queue_pi = NULL;
2225 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2227 if (!pi || pi->queued || pi->pixbuf) return;
2228 if (pw->size <= LAYOUT_SIZE_THUMB_NONE) return;
2231 pw->queue = g_list_prepend(pw->queue, pi);
2233 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2236 static gint pan_window_request_tile_cb(ImageWindow *imd, gint x, gint y, gint width, gint height,
2237 GdkPixbuf *pixbuf, gpointer data)
2239 PanWindow *pw = data;
2244 pixbuf_draw_rect_fill(pixbuf,
2245 0, 0, width, height,
2246 PAN_BACKGROUND_COLOR, 255);
2248 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2250 gint rx, ry, rw, rh;
2252 if (util_clip_region(x, y, width, height,
2254 &rx, &ry, &rw, &rh))
2256 pixbuf_draw_rect_fill(pixbuf,
2257 rx - x, ry - y, rw, rh,
2258 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2261 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2263 gint rx, ry, rw, rh;
2265 if (util_clip_region(x, y, width, height,
2267 &rx, &ry, &rw, &rh))
2269 pixbuf_draw_rect_fill(pixbuf,
2270 rx - x, ry - y, rw, rh,
2271 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2275 list = pan_layout_intersect(pw, x, y, width, height);
2280 gint tx, ty, tw, th;
2281 gint rx, ry, rw, rh;
2288 if (pi->type == ITEM_THUMB && pi->pixbuf)
2290 tw = gdk_pixbuf_get_width(pi->pixbuf);
2291 th = gdk_pixbuf_get_height(pi->pixbuf);
2293 tx = pi->x + (pi->width - tw) / 2;
2294 ty = pi->y + (pi->height - th) / 2;
2296 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2298 if (util_clip_region(x, y, width, height,
2299 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2300 &rx, &ry, &rw, &rh))
2302 pixbuf_draw_shadow(pixbuf,
2303 rx - x, ry - y, rw, rh,
2304 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2306 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2311 if (util_clip_region(x, y, width, height,
2312 tx + tw, ty + PAN_SHADOW_OFFSET,
2313 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2314 &rx, &ry, &rw, &rh))
2316 pixbuf_draw_shadow(pixbuf,
2317 rx - x, ry - y, rw, rh,
2318 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2320 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2322 if (util_clip_region(x, y, width, height,
2323 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2324 &rx, &ry, &rw, &rh))
2326 pixbuf_draw_shadow(pixbuf,
2327 rx - x, ry - y, rw, rh,
2328 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2330 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2334 if (util_clip_region(x, y, width, height,
2336 &rx, &ry, &rw, &rh))
2338 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2341 1.0, 1.0, GDK_INTERP_NEAREST,
2345 if (util_clip_region(x, y, width, height,
2346 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2347 &rx, &ry, &rw, &rh))
2349 pixbuf_draw_rect_fill(pixbuf,
2350 rx - x, ry - y, rw, rh,
2351 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2353 if (util_clip_region(x, y, width, height,
2354 tx, ty, PAN_OUTLINE_THICKNESS, th,
2355 &rx, &ry, &rw, &rh))
2357 pixbuf_draw_rect_fill(pixbuf,
2358 rx - x, ry - y, rw, rh,
2359 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2361 if (util_clip_region(x, y, width, height,
2362 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2363 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2364 &rx, &ry, &rw, &rh))
2366 pixbuf_draw_rect_fill(pixbuf,
2367 rx - x, ry - y, rw, rh,
2368 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2370 if (util_clip_region(x, y, width, height,
2371 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2372 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2373 &rx, &ry, &rw, &rh))
2375 pixbuf_draw_rect_fill(pixbuf,
2376 rx - x, ry - y, rw, rh,
2377 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2380 else if (pi->type == ITEM_THUMB)
2382 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2383 th = pi->height - PAN_SHADOW_OFFSET * 2;
2384 tx = pi->x + PAN_SHADOW_OFFSET;
2385 ty = pi->y + PAN_SHADOW_OFFSET;
2387 if (util_clip_region(x, y, width, height,
2389 &rx, &ry, &rw, &rh))
2393 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2394 pixbuf_draw_rect_fill(pixbuf,
2395 rx - x, ry - y, rw, rh,
2397 PAN_SHADOW_ALPHA / d);
2400 pan_layout_queue(pw, pi);
2402 else if (pi->type == ITEM_IMAGE)
2404 if (util_clip_region(x, y, width, height,
2405 pi->x, pi->y, pi->width, pi->height,
2406 &rx, &ry, &rw, &rh))
2410 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2413 1.0, 1.0, GDK_INTERP_NEAREST,
2418 pixbuf_draw_rect_fill(pixbuf,
2419 rx - x, ry - y, rw, rh,
2420 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2421 pan_layout_queue(pw, pi);
2425 else if (pi->type == ITEM_BOX)
2439 if (pi->color_a > 254)
2441 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2442 shadow[0], bh - shadow[0],
2443 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2445 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2446 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2448 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2450 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2455 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2456 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2458 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2460 PAN_SHADOW_COLOR, a);
2464 if (util_clip_region(x, y, width, height,
2465 pi->x, pi->y, bw, bh,
2466 &rx, &ry, &rw, &rh))
2468 pixbuf_draw_rect_fill(pixbuf,
2469 rx - x, ry - y, rw, rh,
2470 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2472 if (util_clip_region(x, y, width, height,
2473 pi->x, pi->y, bw, pi->border,
2474 &rx, &ry, &rw, &rh))
2476 pixbuf_draw_rect_fill(pixbuf,
2477 rx - x, ry - y, rw, rh,
2478 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2480 if (util_clip_region(x, y, width, height,
2481 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
2482 &rx, &ry, &rw, &rh))
2484 pixbuf_draw_rect_fill(pixbuf,
2485 rx - x, ry - y, rw, rh,
2486 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2488 if (util_clip_region(x, y, width, height,
2489 pi->x + bw - pi->border, pi->y + pi->border,
2490 pi->border, bh - pi->border * 2,
2491 &rx, &ry, &rw, &rh))
2493 pixbuf_draw_rect_fill(pixbuf,
2494 rx - x, ry - y, rw, rh,
2495 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2497 if (util_clip_region(x, y, width, height,
2498 pi->x, pi->y + bh - pi->border,
2500 &rx, &ry, &rw, &rh))
2502 pixbuf_draw_rect_fill(pixbuf,
2503 rx - x, ry - y, rw, rh,
2504 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2507 else if (pi->type == ITEM_TRIANGLE)
2509 if (util_clip_region(x, y, width, height,
2510 pi->x, pi->y, pi->width, pi->height,
2511 &rx, &ry, &rw, &rh) && pi->data)
2513 gint *coord = pi->data;
2514 pixbuf_draw_triangle(pixbuf,
2515 rx - x, ry - y, rw, rh,
2516 coord[0] - x, coord[1] - y,
2517 coord[2] - x, coord[3] - y,
2518 coord[4] - x, coord[5] - y,
2519 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2521 if (pi->border & BORDER_1)
2523 pixbuf_draw_line(pixbuf,
2524 rx - x, ry - y, rw, rh,
2525 coord[0] - x, coord[1] - y,
2526 coord[2] - x, coord[3] - y,
2527 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2529 if (pi->border & BORDER_2)
2531 pixbuf_draw_line(pixbuf,
2532 rx - x, ry - y, rw, rh,
2533 coord[2] - x, coord[3] - y,
2534 coord[4] - x, coord[5] - y,
2535 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2537 if (pi->border & BORDER_3)
2539 pixbuf_draw_line(pixbuf,
2540 rx - x, ry - y, rw, rh,
2541 coord[4] - x, coord[5] - y,
2542 coord[0] - x, coord[1] - y,
2543 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2547 else if (pi->type == ITEM_TEXT && pi->text)
2549 PangoLayout *layout;
2551 layout = pan_item_text_layout(pi, imd->image);
2552 pixbuf_draw_layout(pixbuf, layout, imd->image,
2553 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
2554 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2555 g_object_unref(G_OBJECT(layout));
2562 static gint count = 0;
2563 PangoLayout *layout;
2569 layout = gtk_widget_create_pango_layout(imd->image, NULL);
2571 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
2572 (x / imd->source_tile_width) +
2573 (y / imd->source_tile_height * (imd->image_width/imd->source_tile_width + 1)));
2574 pango_layout_set_text(layout, buf, -1);
2577 pango_layout_get_pixel_size(layout, &lw, &lh);
2579 pixmap = gdk_pixmap_new(imd->widget->window, lw, lh, -1);
2580 gdk_draw_rectangle(pixmap, imd->widget->style->black_gc, TRUE, 0, 0, lw, lh);
2581 gdk_draw_layout(pixmap, imd->widget->style->white_gc, 0, 0, layout);
2582 g_object_unref(G_OBJECT(layout));
2584 lx = MAX(0, width / 2 - lw / 2);
2585 ly = MAX(0, height / 2 - lh / 2);
2586 lw = MIN(lw, width - lx);
2587 lh = MIN(lh, height - ly);
2588 gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_drawable_get_colormap(imd->image->window),
2589 0, 0, lx, ly, lw, lh);
2590 g_object_unref(pixmap);
2598 static void pan_window_dispose_tile_cb(ImageWindow *imd, gint x, gint y, gint width, gint height,
2599 GdkPixbuf *pixbuf, gpointer data)
2601 PanWindow *pw = data;
2605 list = pan_layout_intersect(pw, x, y, width, height);
2614 if (pi->refcount > 0)
2618 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
2623 pw->queue = g_list_remove(pw->queue, pi);
2626 if (pw->queue_pi == pi) pw->queue_pi = NULL;
2629 g_object_unref(pi->pixbuf);
2641 *-----------------------------------------------------------------------------
2643 *-----------------------------------------------------------------------------
2646 static void pan_window_message(PanWindow *pw, const gchar *text)
2656 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
2669 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
2671 size += pi->fd->size;
2676 ss = text_from_size_abrev(size);
2677 buf = g_strdup_printf(_("%d images, %s"), count, ss);
2679 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
2683 static ImageWindow *pan_window_active_image(PanWindow *pw)
2685 if (pw->fs) return pw->fs->imd;
2690 static void pan_window_zoom_limit(PanWindow *pw)
2696 case LAYOUT_SIZE_THUMB_DOTS:
2697 case LAYOUT_SIZE_THUMB_NONE:
2698 case LAYOUT_SIZE_THUMB_SMALL:
2699 case LAYOUT_SIZE_THUMB_NORMAL:
2701 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
2705 case LAYOUT_SIZE_THUMB_LARGE:
2708 case LAYOUT_SIZE_10:
2709 case LAYOUT_SIZE_25:
2712 case LAYOUT_SIZE_33:
2713 case LAYOUT_SIZE_50:
2714 case LAYOUT_SIZE_100:
2720 image_zoom_set_limits(pw->imd, min, 32.0);
2723 static gint pan_window_layout_update_idle_cb(gpointer data)
2725 PanWindow *pw = data;
2731 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2733 if (!pw->cache_list && !pw->cache_todo)
2735 pan_cache_fill(pw, pw->path);
2738 pan_window_message(pw, _("Reading dimensions..."));
2742 if (pan_cache_step(pw))
2746 if (pw->cache_count == pw->cache_total)
2748 pan_window_message(pw, _("Sorting images..."));
2750 else if (pw->cache_tick > 9)
2754 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
2755 pw->cache_total - pw->cache_count);
2756 pan_window_message(pw, buf);
2766 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
2768 pan_window_zoom_limit(pw);
2770 if (width > 0 && height > 0)
2774 image_set_image_as_tiles(pw->imd, width, height,
2775 PAN_TILE_SIZE, PAN_TILE_SIZE, 8,
2776 pan_window_request_tile_cb,
2777 pan_window_dispose_tile_cb, pw, 1.0);
2779 if (scroll_x == 0 && scroll_y == 0)
2787 image_scroll_to_point(pw->imd, scroll_x, scroll_y, align, align);
2790 pan_window_message(pw, NULL);
2797 static void pan_window_layout_update_idle(PanWindow *pw)
2799 if (pw->idle_id == -1)
2801 pan_window_message(pw, _("Sorting images..."));
2802 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
2807 *-----------------------------------------------------------------------------
2808 * pan window keyboard
2809 *-----------------------------------------------------------------------------
2812 static const gchar *pan_menu_click_path(PanWindow *pw)
2814 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
2818 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
2820 PanWindow *pw = data;
2823 imd = pan_window_active_image(pw);
2824 gdk_window_get_origin(imd->image->window, x, y);
2825 popup_menu_position_clamp(menu, x, y, 0);
2828 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
2830 PanWindow *pw = data;
2833 gint stop_signal = FALSE;
2839 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(pw->imd->widget));
2841 imd = pan_window_active_image(pw);
2842 path = pan_menu_click_path(pw);
2846 switch (event->keyval)
2848 case GDK_Left: case GDK_KP_Left:
2852 case GDK_Right: case GDK_KP_Right:
2856 case GDK_Up: case GDK_KP_Up:
2860 case GDK_Down: case GDK_KP_Down:
2864 case GDK_Page_Up: case GDK_KP_Page_Up:
2865 image_scroll(imd, 0, 0-imd->vis_height / 2);
2867 case GDK_Page_Down: case GDK_KP_Page_Down:
2868 image_scroll(imd, 0, imd->vis_height / 2);
2870 case GDK_Home: case GDK_KP_Home:
2871 image_scroll(imd, 0-imd->vis_width / 2, 0);
2873 case GDK_End: case GDK_KP_End:
2874 image_scroll(imd, imd->vis_width / 2, 0);
2879 if (focused && !(event->state & GDK_CONTROL_MASK) )
2880 switch (event->keyval)
2882 case '+': case '=': case GDK_KP_Add:
2883 image_zoom_adjust(imd, ZOOM_INCREMENT);
2885 case '-': case GDK_KP_Subtract:
2886 image_zoom_adjust(imd, -ZOOM_INCREMENT);
2888 case 'Z': case 'z': case GDK_KP_Divide: case '1':
2889 image_zoom_set(imd, 1.0);
2892 image_zoom_set(imd, 2.0);
2895 image_zoom_set(imd, 3.0);
2898 image_zoom_set(imd, 4.0);
2901 image_zoom_set(imd, -4.0);
2904 image_zoom_set(imd, -3.0);
2907 image_zoom_set(imd, -2.0);
2911 pan_fullscreen_toggle(pw, FALSE);
2915 pan_overlay_toggle(pw);
2917 case GDK_Delete: case GDK_KP_Delete:
2922 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
2929 pan_fullscreen_toggle(pw, TRUE);
2932 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
2934 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
2940 menu = pan_popup_menu(pw);
2941 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
2946 if (event->state & GDK_CONTROL_MASK)
2949 switch (event->keyval)
2982 if (path) file_util_copy(path, NULL, NULL, imd->widget);
2985 if (path) file_util_move(path, NULL, NULL, imd->widget);
2988 if (path) file_util_rename(path, NULL, imd->widget);
2991 if (path) file_util_delete(path, NULL, imd->widget);
2994 if (path) info_window_new(path, NULL);
2997 pan_window_close(pw);
3000 if (n != -1 && path)
3002 pan_fullscreen_toggle(pw, TRUE);
3003 start_editor_from_file(n, path);
3007 else if (event->state & GDK_SHIFT_MASK)
3014 switch (event->keyval)
3019 pan_fullscreen_toggle(pw, TRUE);
3022 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3024 gtk_widget_grab_focus(pw->imd->widget);
3025 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3034 if (x != 0 || y!= 0)
3036 keyboard_scroll_calc(&x, &y, event);
3037 image_scroll(imd, x, y);
3044 *-----------------------------------------------------------------------------
3046 *-----------------------------------------------------------------------------
3049 static void pan_info_update(PanWindow *pw, PanItem *pi)
3055 gint x1, y1, x2, y2, x3, y3;
3058 if (pw->click_pi == pi) return;
3059 if (pi && !pi->fd) pi = NULL;
3061 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3066 printf("info set to %s\n", pi->fd->path);
3068 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3070 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3071 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3072 pan_item_set_key(pbox, "info");
3074 if (pi->type == ITEM_THUMB && pi->pixbuf)
3076 w = gdk_pixbuf_get_width(pi->pixbuf);
3077 h = gdk_pixbuf_get_height(pi->pixbuf);
3079 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3080 y1 = pi->y + (pi->height - h) / 2 + 8;
3084 x1 = pi->x + pi->width - 8;
3092 triangle_rect_region(x1, y1, x2, y2, x3, y3,
3095 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3096 x1, y1, x2, y2, x3, y3,
3097 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3098 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3099 pan_item_set_key(p, "info");
3100 pan_item_added(pw, p);
3102 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3103 _("Filename:"), TEXT_ATTR_BOLD,
3104 PAN_POPUP_TEXT_COLOR, 255);
3105 pan_item_set_key(plabel, "info");
3106 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3107 pi->fd->name, TEXT_ATTR_NONE,
3108 PAN_POPUP_TEXT_COLOR, 255);
3109 pan_item_set_key(p, "info");
3110 pan_item_size_by_item(pbox, p, 0);
3112 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3113 _("Date:"), TEXT_ATTR_BOLD,
3114 PAN_POPUP_TEXT_COLOR, 255);
3115 pan_item_set_key(plabel, "info");
3116 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3117 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3118 PAN_POPUP_TEXT_COLOR, 255);
3119 pan_item_set_key(p, "info");
3120 pan_item_size_by_item(pbox, p, 0);
3122 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3123 _("Size:"), TEXT_ATTR_BOLD,
3124 PAN_POPUP_TEXT_COLOR, 255);
3125 pan_item_set_key(plabel, "info");
3126 buf = text_from_size(pi->fd->size);
3127 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3128 buf, TEXT_ATTR_NONE,
3129 PAN_POPUP_TEXT_COLOR, 255);
3131 pan_item_set_key(p, "info");
3132 pan_item_size_by_item(pbox, p, 0);
3134 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3135 pan_item_added(pw, pbox);
3140 *-----------------------------------------------------------------------------
3142 *-----------------------------------------------------------------------------
3145 static void pan_search_status(PanWindow *pw, const gchar *text)
3147 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3150 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3155 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3157 pi = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3158 if (!pi) return FALSE;
3160 pan_info_update(pw, pi);
3161 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3163 pan_search_status(pw, (path[0] == '/') ? _("path found") : _("filename found"));
3168 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3173 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3175 pi = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3176 if (!pi) pi = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3181 needle = g_utf8_strdown(text, -1);
3182 pi = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3185 if (!pi) return FALSE;
3187 pan_info_update(pw, pi);
3188 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3190 pan_search_status(pw, _("partial match"));
3195 static gint valid_date_separator(gchar c)
3197 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3200 static PanItem *pan_search_by_date_val(PanWindow *pw, ItemType type, gint year, gint month, gint day)
3204 work = g_list_last(pw->list);
3212 if (pi->fd && (pi->type == type || type == ITEM_NONE))
3216 tl = localtime(&pi->fd->date);
3221 match = (tl->tm_year == year - 1900);
3222 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3223 if (match && day > 0) match = (tl->tm_mday == day);
3225 if (match) return pi;
3233 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3247 if (!text) return FALSE;
3249 ptr = (gchar *)text;
3250 while (*ptr != '\0')
3252 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3257 if (t == -1) return FALSE;
3259 if (!lt) return FALSE;
3261 if (valid_date_separator(*text))
3264 mptr = (gchar *)text;
3268 year = (gint)strtol(text, &mptr, 10);
3269 if (mptr == text) return FALSE;
3272 if (*mptr != '\0' && valid_date_separator(*mptr))
3277 month = strtol(mptr, &dptr, 10);
3280 if (valid_date_separator(*dptr))
3282 month = lt->tm_mon + 1;
3290 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3294 day = strtol(dptr, &eptr, 10);
3304 year = lt->tm_year + 1900;
3306 else if (year < 100)
3315 month < -1 || month == 0 || month > 12 ||
3316 day < -1 || day == 0 || day > 31) return FALSE;
3318 t = date_to_time(year, month, day);
3319 if (t < 0) return FALSE;
3321 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3323 pi = pan_search_by_date_val(pw, type, year, month, day);
3326 pan_info_update(pw, pi);
3327 image_scroll_to_point(pw->imd,
3328 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3334 buf = date_value_string(t, DATE_LENGTH_MONTH);
3339 buf = g_strdup_printf("%d %s", day, tmp);
3345 buf = date_value_string(t, DATE_LENGTH_YEAR);
3347 message = g_strdup_printf("%s%s%s%s %s",
3348 (pi) ? "" : "(", (pi) ? "" : _("no match"), (pi) ? "" : ") " ,
3351 pan_search_status(pw, message);
3357 static void pan_search_activate_cb(const gchar *text, gpointer data)
3359 PanWindow *pw = data;
3363 tab_completion_append_to_history(pw->search_entry, text);
3365 if (pan_search_by_path(pw, text)) return;
3367 if (pw->layout == LAYOUT_TIMELINE && pan_search_by_date(pw, text)) return;
3369 if (pan_search_by_partial(pw, text)) return;
3371 pan_search_status(pw, _("no match"));
3374 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3376 PanWindow *pw = data;
3379 visible = GTK_WIDGET_VISIBLE(pw->search_box);
3380 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3384 gtk_widget_hide(pw->search_box);
3385 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3389 gtk_widget_show(pw->search_box);
3390 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3391 gtk_widget_grab_focus(pw->search_entry);
3397 *-----------------------------------------------------------------------------
3398 * view window main routines
3399 *-----------------------------------------------------------------------------
3402 static void button_cb(ImageWindow *imd, gint button, guint32 time,
3403 gdouble x, gdouble y, guint state, gpointer data)
3405 PanWindow *pw = data;
3411 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3412 (gint)((double)(pw->imd->x_scroll + x - pw->imd->x_offset) / pw->imd->scale),
3413 (gint)((double)(pw->imd->y_scroll + y - pw->imd->y_offset) / pw->imd->scale));
3419 pan_info_update(pw, pi);
3424 pan_info_update(pw, pi);
3425 menu = pan_popup_menu(pw);
3426 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, time);
3433 static void scroll_cb(ImageWindow *imd, GdkScrollDirection direction, guint32 time,
3434 gdouble x, gdouble y, guint state, gpointer data)
3437 PanWindow *pw = data;
3440 if (state & GDK_CONTROL_MASK)
3445 image_zoom_adjust_at_point(imd, ZOOM_INCREMENT, x, y);
3447 case GDK_SCROLL_DOWN:
3448 image_zoom_adjust_at_point(imd, -ZOOM_INCREMENT, x, y);
3454 else if ( (state & GDK_SHIFT_MASK) != (mousewheel_scrolls))
3459 image_scroll(imd, 0, -MOUSEWHEEL_SCROLL_SIZE);
3461 case GDK_SCROLL_DOWN:
3462 image_scroll(imd, 0, MOUSEWHEEL_SCROLL_SIZE);
3464 case GDK_SCROLL_LEFT:
3465 image_scroll(imd, -MOUSEWHEEL_SCROLL_SIZE, 0);
3467 case GDK_SCROLL_RIGHT:
3468 image_scroll(imd, MOUSEWHEEL_SCROLL_SIZE, 0);
3480 case GDK_SCROLL_DOWN:
3488 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
3490 image_set_button_func(imd, button_cb, pw);
3491 image_set_scroll_func(imd, scroll_cb, pw);
3494 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
3496 PanWindow *pw = data;
3501 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
3503 if (force_off && !pw->fs) return;
3507 fullscreen_stop(pw->fs);
3508 pw->imd = pw->imd_normal;
3512 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
3514 pan_image_set_buttons(pw, pw->fs->imd);
3515 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
3516 G_CALLBACK(pan_window_key_press_cb), pw);
3518 pw->imd = pw->fs->imd;
3522 static void pan_overlay_toggle(PanWindow *pw)
3526 imd = pan_window_active_image(pw);
3528 if (pw->overlay_id == -1)
3530 pw->overlay_id = image_overlay_info_enable(imd);
3534 image_overlay_info_disable(imd, pw->overlay_id);
3535 pw->overlay_id = -1;
3540 static void pan_window_image_update_cb(ImageWindow *imd, gpointer data)
3542 PanWindow *pw = data;
3545 text = image_zoom_get_as_text(imd);
3546 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
3550 static void pan_window_image_scroll_notify_cb(ImageWindow *imd, gint x, gint y,
3551 gint width, gint height, gpointer data)
3553 PanWindow *pw = data;
3556 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
3557 adj->page_size = (gdouble)imd->vis_width / imd->scale;
3558 adj->page_increment = adj->page_size / 2.0;
3559 adj->step_increment = 48.0 / imd->scale;
3561 adj->upper = MAX((gdouble)width + adj->page_size, 1.0);
3562 adj->value = (gdouble)x;
3564 pref_signal_block_data(pw->scrollbar_h, pw);
3565 gtk_adjustment_changed(adj);
3566 pref_signal_unblock_data(pw->scrollbar_h, pw);
3568 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
3569 adj->page_size = (gdouble)imd->vis_height / imd->scale;
3570 adj->page_increment = adj->page_size / 2.0;
3571 adj->step_increment = 48.0 / imd->scale;
3573 adj->upper = MAX((gdouble)height + adj->page_size, 1.0);
3574 adj->value = (gdouble)y;
3576 pref_signal_block_data(pw->scrollbar_v, pw);
3577 gtk_adjustment_changed(adj);
3578 pref_signal_unblock_data(pw->scrollbar_v, pw);
3580 // printf("scrolled to %d,%d @ %d x %d\n", x, y, width, height);
3583 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
3585 PanWindow *pw = data;
3588 if (!pw->imd->scale) return;
3590 x = (gint)gtk_range_get_value(range);
3592 image_scroll_to_point(pw->imd, x, (gint)((gdouble)pw->imd->y_scroll / pw->imd->scale), 0.0, 0.0);
3595 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
3597 PanWindow *pw = data;
3600 if (!pw->imd->scale) return;
3602 y = (gint)gtk_range_get_value(range);
3604 image_scroll_to_point(pw->imd, (gint)((gdouble)pw->imd->x_scroll / pw->imd->scale), y, 0.0, 0.0);
3607 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
3609 PanWindow *pw = data;
3611 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3612 pan_window_layout_update_idle(pw);
3615 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
3617 PanWindow *pw = data;
3619 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3620 pan_window_layout_update_idle(pw);
3623 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
3625 PanWindow *pw = data;
3628 path = remove_trailing_slash(new_text);
3629 parse_out_relatives(path);
3633 warning_dialog(_("Folder not found"),
3634 _("The entered path is not a folder"),
3635 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
3639 tab_completion_append_to_history(pw->path_entry, path);
3642 pw->path = g_strdup(path);
3644 pan_window_layout_update_idle(pw);
3647 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
3649 PanWindow *pw = data;
3652 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
3654 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
3655 pan_window_entry_activate_cb(text, pw);
3659 static void pan_window_close(PanWindow *pw)
3661 pan_window_list = g_list_remove(pan_window_list, pw);
3663 if (pw->idle_id != -1)
3665 g_source_remove(pw->idle_id);
3668 pan_fullscreen_toggle(pw, TRUE);
3669 gtk_widget_destroy(pw->window);
3671 pan_window_items_free(pw);
3678 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
3680 PanWindow *pw = data;
3682 pan_window_close(pw);
3686 static void pan_window_new_real(const gchar *path)
3695 GdkGeometry geometry;
3697 pw = g_new0(PanWindow, 1);
3699 pw->path = g_strdup(path);
3700 pw->layout = LAYOUT_TIMELINE;
3701 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
3702 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
3703 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
3707 pw->overlay_id = -1;
3710 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3712 geometry.min_width = 8;
3713 geometry.min_height = 8;
3714 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
3716 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
3717 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
3718 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
3719 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
3721 window_set_icon(pw->window, NULL, NULL);
3723 vbox = gtk_vbox_new(FALSE, 0);
3724 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
3725 gtk_widget_show(vbox);
3727 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3729 pref_spacer(box, 0);
3730 pref_label_new(box, _("Location:"));
3731 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
3732 pan_window_entry_activate_cb, pw);
3733 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
3734 G_CALLBACK(pan_window_entry_change_cb), pw);
3735 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
3736 gtk_widget_show(combo);
3738 combo = gtk_combo_box_new_text();
3739 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
3740 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
3741 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
3742 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
3744 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
3745 g_signal_connect(G_OBJECT(combo), "changed",
3746 G_CALLBACK(pan_window_layout_change_cb), pw);
3747 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
3748 gtk_widget_show(combo);
3750 combo = gtk_combo_box_new_text();
3751 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
3752 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
3753 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
3754 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
3755 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
3756 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
3757 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
3758 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
3759 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
3760 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
3762 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
3763 g_signal_connect(G_OBJECT(combo), "changed",
3764 G_CALLBACK(pan_window_layout_size_cb), pw);
3765 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
3766 gtk_widget_show(combo);
3768 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
3769 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
3770 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
3772 pw->imd = image_new(TRUE);
3773 pw->imd_normal = pw->imd;
3775 if (black_window_background) image_background_set_black(pw->imd, TRUE);
3776 image_set_update_func(pw->imd, pan_window_image_update_cb, pw);
3778 image_set_scroll_notify_func(pw->imd, pan_window_image_scroll_notify_cb, pw);
3781 gtk_box_pack_start(GTK_BOX(vbox), pw->imd->widget, TRUE, TRUE, 0);
3783 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
3784 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
3785 gtk_widget_show(pw->imd->widget);
3787 pan_window_dnd_init(pw);
3789 pan_image_set_buttons(pw, pw->imd);
3791 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
3792 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
3793 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
3794 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
3795 GTK_FILL | GTK_EXPAND, 0, 0, 0);
3796 gtk_widget_show(pw->scrollbar_h);
3798 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
3799 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
3800 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
3801 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
3802 0, GTK_FILL | GTK_EXPAND, 0, 0);
3803 gtk_widget_show(pw->scrollbar_v);
3807 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
3808 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
3810 pref_spacer(pw->search_box, 0);
3811 pref_label_new(pw->search_box, _("Find:"));
3813 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
3814 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
3815 gtk_widget_show(hbox);
3817 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
3818 pan_search_activate_cb, pw);
3819 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
3820 gtk_widget_show(combo);
3822 pw->search_label = gtk_label_new("");
3823 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
3824 gtk_widget_show(pw->search_label);
3828 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3830 frame = gtk_frame_new(NULL);
3831 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
3832 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
3833 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
3834 gtk_widget_show(frame);
3836 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
3837 gtk_container_add(GTK_CONTAINER(frame), hbox);
3838 gtk_widget_show(hbox);
3840 pref_spacer(hbox, 0);
3841 pw->label_message = pref_label_new(hbox, "");
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_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
3847 gtk_widget_show(frame);
3849 pw->label_zoom = gtk_label_new("");
3850 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
3851 gtk_widget_show(pw->label_zoom);
3853 pw->search_button = gtk_toggle_button_new();
3854 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
3855 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
3856 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
3857 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
3858 gtk_widget_show(hbox);
3859 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
3860 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
3861 gtk_widget_show(pw->search_button_arrow);
3862 pref_label_new(hbox, _("Find"));
3864 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
3865 gtk_widget_show(pw->search_button);
3866 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
3867 G_CALLBACK(pan_search_toggle_cb), pw);
3869 g_signal_connect(G_OBJECT(pw->window), "delete_event",
3870 G_CALLBACK(pan_window_delete_cb), pw);
3871 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
3872 G_CALLBACK(pan_window_key_press_cb), pw);
3874 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
3876 pan_window_layout_update_idle(pw);
3878 gtk_widget_grab_focus(pw->imd->widget);
3879 gtk_widget_show(pw->window);
3881 pan_window_list = g_list_append(pan_window_list, pw);
3885 *-----------------------------------------------------------------------------
3886 * peformance warnings
3887 *-----------------------------------------------------------------------------
3890 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
3894 generic_dialog_close(gd);
3896 pan_window_new_real(path);
3900 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
3904 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
3905 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
3908 static gint pan_warning(const gchar *path)
3914 GtkWidget *ct_button;
3917 if (enable_thumb_caching &&
3918 thumbnail_spec_standard) return FALSE;
3920 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
3921 if (hide_dlg) return FALSE;
3923 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
3925 gd->data = g_strdup(path);
3926 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
3927 pan_warning_ok_cb, TRUE);
3929 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
3930 _("Pan view performance may be poor."),
3931 _("To improve performance of thumbnails in the pan view the"
3932 " following options can be enabled. Note that both options"
3933 " must be enabled to notice a change in performance."));
3935 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3936 pref_spacer(group, PREF_PAD_INDENT);
3937 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3939 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
3940 enable_thumb_caching, &enable_thumb_caching);
3941 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
3942 thumbnail_spec_standard, &thumbnail_spec_standard);
3943 pref_checkbox_link_sensitivity(ct_button, button);
3947 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
3948 G_CALLBACK(pan_warning_hide_cb), NULL);
3950 gtk_widget_show(gd->dialog);
3957 *-----------------------------------------------------------------------------
3959 *-----------------------------------------------------------------------------
3962 void pan_window_new(const gchar *path)
3964 if (pan_warning(path)) return;
3966 pan_window_new_real(path);
3970 *-----------------------------------------------------------------------------
3972 *-----------------------------------------------------------------------------
3975 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
3977 PanWindow *pw = data;
3980 path = pan_menu_click_path(pw);
3983 pan_fullscreen_toggle(pw, TRUE);
3984 view_window_new(path);
3988 static void pan_edit_cb(GtkWidget *widget, gpointer data)
3994 pw = submenu_item_get_data(widget);
3995 n = GPOINTER_TO_INT(data);
3998 path = pan_menu_click_path(pw);
4001 pan_fullscreen_toggle(pw, TRUE);
4002 start_editor_from_file(n, path);
4006 static void pan_info_cb(GtkWidget *widget, gpointer data)
4008 PanWindow *pw = data;
4011 path = pan_menu_click_path(pw);
4012 if (path) info_window_new(path, NULL);
4015 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4017 PanWindow *pw = data;
4019 image_zoom_adjust(pan_window_active_image(pw), ZOOM_INCREMENT);
4022 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4024 PanWindow *pw = data;
4026 image_zoom_adjust(pan_window_active_image(pw), -ZOOM_INCREMENT);
4029 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4031 PanWindow *pw = data;
4033 image_zoom_set(pan_window_active_image(pw), 1.0);
4036 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4038 PanWindow *pw = data;
4041 path = pan_menu_click_path(pw);
4042 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4045 static void pan_move_cb(GtkWidget *widget, gpointer data)
4047 PanWindow *pw = data;
4050 path = pan_menu_click_path(pw);
4051 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4054 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4056 PanWindow *pw = data;
4059 path = pan_menu_click_path(pw);
4060 if (path) file_util_rename(path, NULL, pw->imd->widget);
4063 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4065 PanWindow *pw = data;
4068 path = pan_menu_click_path(pw);
4069 if (path) file_util_delete(path, NULL, pw->imd->widget);
4072 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4074 PanWindow *pw = data;
4076 pan_fullscreen_toggle(pw, FALSE);
4079 static void pan_close_cb(GtkWidget *widget, gpointer data)
4081 PanWindow *pw = data;
4083 pan_window_close(pw);
4086 static GtkWidget *pan_popup_menu(PanWindow *pw)
4092 active = (pw->click_pi != NULL);
4094 menu = popup_menu_short_lived();
4096 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4097 G_CALLBACK(pan_zoom_in_cb), pw);
4098 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4099 G_CALLBACK(pan_zoom_out_cb), pw);
4100 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4101 G_CALLBACK(pan_zoom_1_1_cb), pw);
4102 menu_item_add_divider(menu);
4104 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4105 gtk_widget_set_sensitive(item, active);
4107 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4108 G_CALLBACK(pan_info_cb), pw);
4110 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4111 G_CALLBACK(pan_new_window_cb), pw);
4113 menu_item_add_divider(menu);
4114 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4115 G_CALLBACK(pan_copy_cb), pw);
4116 menu_item_add_sensitive(menu, _("_Move..."), active,
4117 G_CALLBACK(pan_move_cb), pw);
4118 menu_item_add_sensitive(menu, _("_Rename..."), active,
4119 G_CALLBACK(pan_rename_cb), pw);
4120 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4121 G_CALLBACK(pan_delete_cb), pw);
4123 menu_item_add_divider(menu);
4127 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4131 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4134 menu_item_add_divider(menu);
4135 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4141 *-----------------------------------------------------------------------------
4143 *-----------------------------------------------------------------------------
4146 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4148 GtkSelectionData *selection_data, guint info,
4149 guint time, gpointer data)
4151 PanWindow *pw = data;
4154 if (gtk_drag_get_source_widget(context) == pw->imd->image) return;
4158 if (info == TARGET_URI_LIST)
4162 list = uri_list_from_text(selection_data->data, TRUE);
4163 if (list && isdir((gchar *)list->data))
4165 gchar *path = list->data;
4168 pw->path = g_strdup(path);
4170 pan_window_layout_update_idle(pw);
4173 path_list_free(list);
4177 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4178 GtkSelectionData *selection_data, guint info,
4179 guint time, gpointer data)
4181 PanWindow *pw = data;
4184 path = pan_menu_click_path(pw);
4194 case TARGET_URI_LIST:
4197 case TARGET_TEXT_PLAIN:
4202 list = g_list_append(NULL, (gchar *)path);
4203 text = uri_text_from_list(list, &len, plain_text);
4207 gtk_selection_data_set (selection_data, selection_data->target,
4214 gtk_selection_data_set (selection_data, selection_data->target,
4219 static void pan_window_dnd_init(PanWindow *pw)
4225 gtk_drag_source_set(imd->image, GDK_BUTTON2_MASK,
4226 dnd_file_drag_types, dnd_file_drag_types_count,
4227 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4228 g_signal_connect(G_OBJECT(imd->image), "drag_data_get",
4229 G_CALLBACK(pan_window_set_dnd_data), pw);
4231 gtk_drag_dest_set(imd->image,
4232 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4233 dnd_file_drop_types, dnd_file_drop_types_count,
4234 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4235 g_signal_connect(G_OBJECT(imd->image), "drag_data_received",
4236 G_CALLBACK(pan_window_get_dnd_data), pw);
4240 *-----------------------------------------------------------------------------
4241 * maintenance (for rename, move, remove)
4242 *-----------------------------------------------------------------------------