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 GList *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1179 gint ignore_case, gint partial)
1184 if (!path) return NULL;
1185 if (partial && path[0] == '/') return NULL;
1187 work = g_list_last(pw->list);
1193 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1199 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
1201 else if (pi->fd->name)
1209 haystack = g_utf8_strdown(pi->fd->name, -1);
1210 match = (strstr(haystack, path) != NULL);
1215 if (strstr(pi->fd->name, path)) match = TRUE;
1218 else if (ignore_case)
1220 if (strcasecmp(path, pi->fd->name) == 0) match = TRUE;
1224 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
1228 if (match) list = g_list_prepend(list, pi);
1233 return g_list_reverse(list);
1236 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y)
1240 if (x < 0 || x >= pw->imd->image_width ||
1241 y < 0 || y >= pw->imd->image_height) return NULL;
1249 if ((pi->type == type || type == ITEM_NONE) &&
1250 x >= pi->x && x < pi->x + pi->width &&
1251 y >= pi->y && y < pi->y + pi->height)
1262 *-----------------------------------------------------------------------------
1264 *-----------------------------------------------------------------------------
1267 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend)
1269 GList *flist = NULL;
1270 GList *dlist = NULL;
1274 filelist_read(path, &flist, &dlist);
1275 if (sort != SORT_NONE)
1277 flist = filelist_sort(flist, sort, ascend);
1278 dlist = filelist_sort(dlist, sort, ascend);
1288 folders = g_list_remove(folders, fd);
1290 if (filelist_read(fd->path, &flist, &dlist))
1292 if (sort != SORT_NONE)
1294 flist = filelist_sort(flist, sort, ascend);
1295 dlist = filelist_sort(dlist, sort, ascend);
1298 result = g_list_concat(result, flist);
1299 folders = g_list_concat(dlist, folders);
1308 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1316 list = pan_window_layout_list(path, SORT_NAME, TRUE);
1318 grid_size = (gint)sqrt((double)g_list_length(list));
1319 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1321 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1325 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1330 *width = PAN_FOLDER_BOX_BORDER * 2;
1331 *height = PAN_FOLDER_BOX_BORDER * 2;
1344 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1346 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1348 x += pi->width + PAN_THUMB_GAP;
1349 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1358 pi = pan_item_new_thumb(pw, fd, x, y);
1360 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1364 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1367 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1373 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1376 gint x1, y1, x2, y2;
1391 if (x1 > pi->x) x1 = pi->x;
1392 if (y1 > pi->y) y1 = pi->y;
1393 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1394 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1397 x1 -= PAN_FOLDER_BOX_BORDER;
1398 y1 -= PAN_FOLDER_BOX_BORDER;
1399 x2 += PAN_FOLDER_BOX_BORDER;
1400 y2 += PAN_FOLDER_BOX_BORDER;
1413 if (pi->type == ITEM_TRIANGLE && pi->data)
1427 if (width) *width = x2 - x1;
1428 if (height) *height = y2 - y1;
1431 typedef struct _FlowerGroup FlowerGroup;
1432 struct _FlowerGroup {
1445 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1449 work = group->items;
1467 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1468 gint *result_x, gint *result_y)
1474 radius = parent->circumference / (2*PI);
1475 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1477 a = 2*PI * group->diameter / parent->circumference;
1479 x = (gint)((double)radius * cos(parent->angle + a / 2));
1480 y = (gint)((double)radius * sin(parent->angle + a / 2));
1487 x += parent->width / 2;
1488 y += parent->height / 2;
1490 x -= group->width / 2;
1491 y -= group->height / 2;
1497 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1504 if (parent && parent->children)
1506 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1514 pan_window_layout_compute_folder_flower_move(group, x, y);
1519 gint px, py, gx, gy;
1520 gint x1, y1, x2, y2;
1522 px = parent->x + parent->width / 2;
1523 py = parent->y + parent->height / 2;
1525 gx = group->x + group->width / 2;
1526 gy = group->y + group->height / 2;
1531 x2 = MAX(px, gx + 5);
1532 y2 = MAX(py, gy + 5);
1534 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1535 px, py, gx, gy, gx + 5, gy + 5,
1537 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1541 pw->list = g_list_concat(group->items, pw->list);
1542 group->items = NULL;
1544 group->circumference = 0;
1545 work = group->children;
1553 group->circumference += child->diameter;
1556 work = g_list_last(group->children);
1564 pan_window_layout_compute_folder_flower_build(pw, child, group);
1567 g_list_free(group->children);
1571 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1584 if (!filelist_read(path, &f, &d)) return NULL;
1585 if (!f && !d) return NULL;
1587 f = filelist_sort(f, SORT_NAME, TRUE);
1588 d = filelist_sort(d, SORT_NAME, TRUE);
1590 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1591 PAN_TEXT_COLOR, 255);
1593 y += pi_box->height;
1595 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1597 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1598 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1599 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1600 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1602 x += PAN_FOLDER_BOX_BORDER;
1603 y += PAN_FOLDER_BOX_BORDER;
1605 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1619 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1621 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1622 x += pi->width + PAN_THUMB_GAP;
1623 if (pi->height > y_height) y_height = pi->height;
1627 pi = pan_item_new_thumb(pw, fd, x, y);
1628 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1629 y_height = PAN_THUMB_SIZE;
1633 if (grid_count >= grid_size)
1637 y += y_height + PAN_THUMB_GAP;
1641 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1646 group = g_new0(FlowerGroup, 1);
1647 group->items = pw->list;
1650 group->width = pi_box->width;
1651 group->height = pi_box->y + pi_box->height;
1652 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1654 group->children = NULL;
1665 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1666 if (child) group->children = g_list_prepend(group->children, child);
1674 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1675 gint *width, gint *height,
1676 gint *scroll_x, gint *scroll_y)
1681 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1682 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1684 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1686 list = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1689 PanItem *pi = list->data;
1690 *scroll_x = pi->x + pi->width / 2;
1691 *scroll_y = pi->y + pi->height / 2;
1696 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1697 gint *x, gint *y, gint *level,
1699 gint *width, gint *height)
1707 if (!filelist_read(path, &f, &d)) return;
1708 if (!f && !d) return;
1710 f = filelist_sort(f, SORT_NAME, TRUE);
1711 d = filelist_sort(d, SORT_NAME, TRUE);
1713 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1715 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1716 PAN_TEXT_COLOR, 255);
1718 *y += pi_box->height;
1720 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1722 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1723 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1724 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1725 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1727 *x += PAN_FOLDER_BOX_BORDER;
1728 *y += PAN_FOLDER_BOX_BORDER;
1739 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1741 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1742 *x += pi->width + PAN_THUMB_GAP;
1743 if (pi->height > y_height) y_height = pi->height;
1747 pi = pan_item_new_thumb(pw, fd, *x, *y);
1748 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1749 y_height = PAN_THUMB_SIZE;
1752 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1755 if (f) *y = pi_box->y + pi_box->height;
1767 *level = *level + 1;
1768 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1769 pi_box, width, height);
1770 *level = *level - 1;
1775 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1777 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1778 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1780 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1783 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1790 x = PAN_FOLDER_BOX_BORDER;
1791 y = PAN_FOLDER_BOX_BORDER;
1792 w = PAN_FOLDER_BOX_BORDER * 2;
1793 h = PAN_FOLDER_BOX_BORDER * 2;
1795 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1797 if (width) *width = w;
1798 if (height) *height = h;
1801 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
1809 PanItem *pi_month = NULL;
1810 PanItem *pi_day = NULL;
1816 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
1818 list = pan_window_layout_list(path, SORT_NONE, TRUE);
1819 list = filelist_sort(list, SORT_TIME, TRUE);
1821 *width = PAN_FOLDER_BOX_BORDER * 2;
1822 *height = PAN_FOLDER_BOX_BORDER * 2;
1827 day_start = month_start;
1842 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
1847 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
1853 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
1857 x = PAN_FOLDER_BOX_BORDER;
1860 y = PAN_FOLDER_BOX_BORDER;
1862 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
1863 pi = pan_item_new_text(pw, x, y, buf,
1864 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1865 PAN_TEXT_COLOR, 255);
1868 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
1870 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1871 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1872 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1874 x += PAN_FOLDER_BOX_BORDER;
1875 y += PAN_FOLDER_BOX_BORDER;
1879 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
1891 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
1893 needle = needle->next;
1902 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
1903 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
1904 PAN_TEXT_COLOR, 255);
1909 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
1910 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1911 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1912 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1914 x += PAN_FOLDER_BOX_BORDER;
1915 y += PAN_FOLDER_BOX_BORDER;
1919 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1921 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1922 if (pi->width > x_width) x_width = pi->width;
1923 y_height = pi->height;
1927 pi = pan_item_new_thumb(pw, fd, x, y);
1928 x_width = PAN_THUMB_SIZE;
1929 y_height = PAN_THUMB_SIZE;
1932 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
1933 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
1938 if (total > 0 && count < PAN_GROUP_MAX)
1940 y += y_height + PAN_THUMB_GAP;
1944 x += x_width + PAN_THUMB_GAP;
1954 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
1960 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
1961 gint *width, gint *height,
1962 gint *scroll_x, gint *scroll_y)
1964 pan_window_items_free(pw);
1968 case LAYOUT_SIZE_THUMB_DOTS:
1969 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
1970 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
1972 case LAYOUT_SIZE_THUMB_NONE:
1973 pw->thumb_size = PAN_THUMB_SIZE_NONE;
1974 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
1976 case LAYOUT_SIZE_THUMB_SMALL:
1977 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
1978 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
1980 case LAYOUT_SIZE_THUMB_NORMAL:
1982 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
1983 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
1985 case LAYOUT_SIZE_THUMB_LARGE:
1986 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
1987 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
1989 case LAYOUT_SIZE_10:
1990 pw->image_size = 10;
1991 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
1993 case LAYOUT_SIZE_25:
1994 pw->image_size = 25;
1995 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
1997 case LAYOUT_SIZE_33:
1998 pw->image_size = 33;
1999 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2001 case LAYOUT_SIZE_50:
2002 pw->image_size = 50;
2003 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2005 case LAYOUT_SIZE_100:
2006 pw->image_size = 100;
2007 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2020 pan_window_layout_compute_grid(pw, path, width, height);
2022 case LAYOUT_FOLDERS_LINEAR:
2023 pan_window_layout_compute_folders_linear(pw, path, width, height);
2025 case LAYOUT_FOLDERS_FLOWER:
2026 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2028 case LAYOUT_TIMELINE:
2029 pan_window_layout_compute_timeline(pw, path, width, height);
2035 printf("computed %d objects\n", g_list_length(pw->list));
2038 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2051 if (util_clip_region_test(x, y, width, height,
2052 pi->x, pi->y, pi->width, pi->height))
2054 list = g_list_prepend(list, pi);
2064 *-----------------------------------------------------------------------------
2066 *-----------------------------------------------------------------------------
2069 static gint pan_layout_queue_step(PanWindow *pw);
2072 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2074 PanWindow *pw = data;
2082 pw->queue_pi = NULL;
2086 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2087 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2090 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2094 thumb_loader_free(pw->tl);
2097 while (pan_layout_queue_step(pw));
2100 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2102 PanWindow *pw = data;
2110 pw->queue_pi = NULL;
2114 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2115 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2116 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2118 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2119 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2120 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2125 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2126 (GdkInterpType)zoom_quality);
2127 g_object_unref(tmp);
2131 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2135 image_loader_free(pw->il);
2138 while (pan_layout_queue_step(pw));
2142 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2143 guint width, guint height, gpointer data)
2145 PanWindow *pw = data;
2156 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2157 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2161 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2167 static gint pan_layout_queue_step(PanWindow *pw)
2171 if (!pw->queue) return FALSE;
2173 pi = pw->queue->data;
2174 pw->queue = g_list_remove(pw->queue, pi);
2177 image_loader_free(pw->il);
2179 thumb_loader_free(pw->tl);
2182 if (pi->type == ITEM_IMAGE)
2184 pw->il = image_loader_new(pi->fd->path);
2186 if (pw->size != LAYOUT_SIZE_100)
2188 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2192 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2194 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2196 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2198 image_loader_free(pw->il);
2201 else if (pi->type == ITEM_THUMB)
2203 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2205 if (!pw->tl->standard_loader)
2207 /* The classic loader will recreate a thumbnail any time we
2208 * request a different size than what exists. This view will
2209 * almost never use the user configured sizes so disable cache.
2211 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2214 thumb_loader_set_callbacks(pw->tl,
2215 pan_layout_queue_thumb_done_cb,
2216 pan_layout_queue_thumb_done_cb,
2219 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2221 thumb_loader_free(pw->tl);
2225 pw->queue_pi->queued = FALSE;
2226 pw->queue_pi = NULL;
2230 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2232 if (!pi || pi->queued || pi->pixbuf) return;
2233 if (pw->size <= LAYOUT_SIZE_THUMB_NONE) return;
2236 pw->queue = g_list_prepend(pw->queue, pi);
2238 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2241 static gint pan_window_request_tile_cb(ImageWindow *imd, gint x, gint y, gint width, gint height,
2242 GdkPixbuf *pixbuf, gpointer data)
2244 PanWindow *pw = data;
2249 pixbuf_draw_rect_fill(pixbuf,
2250 0, 0, width, height,
2251 PAN_BACKGROUND_COLOR, 255);
2253 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2255 gint rx, ry, rw, rh;
2257 if (util_clip_region(x, y, width, height,
2259 &rx, &ry, &rw, &rh))
2261 pixbuf_draw_rect_fill(pixbuf,
2262 rx - x, ry - y, rw, rh,
2263 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2266 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2268 gint rx, ry, rw, rh;
2270 if (util_clip_region(x, y, width, height,
2272 &rx, &ry, &rw, &rh))
2274 pixbuf_draw_rect_fill(pixbuf,
2275 rx - x, ry - y, rw, rh,
2276 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2280 list = pan_layout_intersect(pw, x, y, width, height);
2285 gint tx, ty, tw, th;
2286 gint rx, ry, rw, rh;
2293 if (pi->type == ITEM_THUMB && pi->pixbuf)
2295 tw = gdk_pixbuf_get_width(pi->pixbuf);
2296 th = gdk_pixbuf_get_height(pi->pixbuf);
2298 tx = pi->x + (pi->width - tw) / 2;
2299 ty = pi->y + (pi->height - th) / 2;
2301 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2303 if (util_clip_region(x, y, width, height,
2304 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2305 &rx, &ry, &rw, &rh))
2307 pixbuf_draw_shadow(pixbuf,
2308 rx - x, ry - y, rw, rh,
2309 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2311 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2316 if (util_clip_region(x, y, width, height,
2317 tx + tw, ty + PAN_SHADOW_OFFSET,
2318 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2319 &rx, &ry, &rw, &rh))
2321 pixbuf_draw_shadow(pixbuf,
2322 rx - x, ry - y, rw, rh,
2323 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2325 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2327 if (util_clip_region(x, y, width, height,
2328 tx + PAN_SHADOW_OFFSET, ty + th, tw, 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);
2339 if (util_clip_region(x, y, width, height,
2341 &rx, &ry, &rw, &rh))
2343 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2346 1.0, 1.0, GDK_INTERP_NEAREST,
2350 if (util_clip_region(x, y, width, height,
2351 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2352 &rx, &ry, &rw, &rh))
2354 pixbuf_draw_rect_fill(pixbuf,
2355 rx - x, ry - y, rw, rh,
2356 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2358 if (util_clip_region(x, y, width, height,
2359 tx, ty, PAN_OUTLINE_THICKNESS, th,
2360 &rx, &ry, &rw, &rh))
2362 pixbuf_draw_rect_fill(pixbuf,
2363 rx - x, ry - y, rw, rh,
2364 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2366 if (util_clip_region(x, y, width, height,
2367 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2368 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2369 &rx, &ry, &rw, &rh))
2371 pixbuf_draw_rect_fill(pixbuf,
2372 rx - x, ry - y, rw, rh,
2373 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2375 if (util_clip_region(x, y, width, height,
2376 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2377 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2378 &rx, &ry, &rw, &rh))
2380 pixbuf_draw_rect_fill(pixbuf,
2381 rx - x, ry - y, rw, rh,
2382 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2385 else if (pi->type == ITEM_THUMB)
2387 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2388 th = pi->height - PAN_SHADOW_OFFSET * 2;
2389 tx = pi->x + PAN_SHADOW_OFFSET;
2390 ty = pi->y + PAN_SHADOW_OFFSET;
2392 if (util_clip_region(x, y, width, height,
2394 &rx, &ry, &rw, &rh))
2398 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2399 pixbuf_draw_rect_fill(pixbuf,
2400 rx - x, ry - y, rw, rh,
2402 PAN_SHADOW_ALPHA / d);
2405 pan_layout_queue(pw, pi);
2407 else if (pi->type == ITEM_IMAGE)
2409 if (util_clip_region(x, y, width, height,
2410 pi->x, pi->y, pi->width, pi->height,
2411 &rx, &ry, &rw, &rh))
2415 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2418 1.0, 1.0, GDK_INTERP_NEAREST,
2423 pixbuf_draw_rect_fill(pixbuf,
2424 rx - x, ry - y, rw, rh,
2425 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2426 pan_layout_queue(pw, pi);
2430 else if (pi->type == ITEM_BOX)
2444 if (pi->color_a > 254)
2446 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2447 shadow[0], bh - shadow[0],
2448 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2450 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2451 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2453 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2455 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2460 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2461 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2463 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2465 PAN_SHADOW_COLOR, a);
2469 if (util_clip_region(x, y, width, height,
2470 pi->x, pi->y, bw, bh,
2471 &rx, &ry, &rw, &rh))
2473 pixbuf_draw_rect_fill(pixbuf,
2474 rx - x, ry - y, rw, rh,
2475 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2477 if (util_clip_region(x, y, width, height,
2478 pi->x, pi->y, bw, pi->border,
2479 &rx, &ry, &rw, &rh))
2481 pixbuf_draw_rect_fill(pixbuf,
2482 rx - x, ry - y, rw, rh,
2483 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2485 if (util_clip_region(x, y, width, height,
2486 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
2487 &rx, &ry, &rw, &rh))
2489 pixbuf_draw_rect_fill(pixbuf,
2490 rx - x, ry - y, rw, rh,
2491 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2493 if (util_clip_region(x, y, width, height,
2494 pi->x + bw - pi->border, pi->y + pi->border,
2495 pi->border, bh - pi->border * 2,
2496 &rx, &ry, &rw, &rh))
2498 pixbuf_draw_rect_fill(pixbuf,
2499 rx - x, ry - y, rw, rh,
2500 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2502 if (util_clip_region(x, y, width, height,
2503 pi->x, pi->y + bh - pi->border,
2505 &rx, &ry, &rw, &rh))
2507 pixbuf_draw_rect_fill(pixbuf,
2508 rx - x, ry - y, rw, rh,
2509 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2512 else if (pi->type == ITEM_TRIANGLE)
2514 if (util_clip_region(x, y, width, height,
2515 pi->x, pi->y, pi->width, pi->height,
2516 &rx, &ry, &rw, &rh) && pi->data)
2518 gint *coord = pi->data;
2519 pixbuf_draw_triangle(pixbuf,
2520 rx - x, ry - y, rw, rh,
2521 coord[0] - x, coord[1] - y,
2522 coord[2] - x, coord[3] - y,
2523 coord[4] - x, coord[5] - y,
2524 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2526 if (pi->border & BORDER_1)
2528 pixbuf_draw_line(pixbuf,
2529 rx - x, ry - y, rw, rh,
2530 coord[0] - x, coord[1] - y,
2531 coord[2] - x, coord[3] - y,
2532 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2534 if (pi->border & BORDER_2)
2536 pixbuf_draw_line(pixbuf,
2537 rx - x, ry - y, rw, rh,
2538 coord[2] - x, coord[3] - y,
2539 coord[4] - x, coord[5] - y,
2540 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2542 if (pi->border & BORDER_3)
2544 pixbuf_draw_line(pixbuf,
2545 rx - x, ry - y, rw, rh,
2546 coord[4] - x, coord[5] - y,
2547 coord[0] - x, coord[1] - y,
2548 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2552 else if (pi->type == ITEM_TEXT && pi->text)
2554 PangoLayout *layout;
2556 layout = pan_item_text_layout(pi, imd->image);
2557 pixbuf_draw_layout(pixbuf, layout, imd->image,
2558 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
2559 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2560 g_object_unref(G_OBJECT(layout));
2567 static gint count = 0;
2568 PangoLayout *layout;
2574 layout = gtk_widget_create_pango_layout(imd->image, NULL);
2576 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
2577 (x / imd->source_tile_width) +
2578 (y / imd->source_tile_height * (imd->image_width/imd->source_tile_width + 1)));
2579 pango_layout_set_text(layout, buf, -1);
2582 pango_layout_get_pixel_size(layout, &lw, &lh);
2584 pixmap = gdk_pixmap_new(imd->widget->window, lw, lh, -1);
2585 gdk_draw_rectangle(pixmap, imd->widget->style->black_gc, TRUE, 0, 0, lw, lh);
2586 gdk_draw_layout(pixmap, imd->widget->style->white_gc, 0, 0, layout);
2587 g_object_unref(G_OBJECT(layout));
2589 lx = MAX(0, width / 2 - lw / 2);
2590 ly = MAX(0, height / 2 - lh / 2);
2591 lw = MIN(lw, width - lx);
2592 lh = MIN(lh, height - ly);
2593 gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_drawable_get_colormap(imd->image->window),
2594 0, 0, lx, ly, lw, lh);
2595 g_object_unref(pixmap);
2603 static void pan_window_dispose_tile_cb(ImageWindow *imd, gint x, gint y, gint width, gint height,
2604 GdkPixbuf *pixbuf, gpointer data)
2606 PanWindow *pw = data;
2610 list = pan_layout_intersect(pw, x, y, width, height);
2619 if (pi->refcount > 0)
2623 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
2628 pw->queue = g_list_remove(pw->queue, pi);
2631 if (pw->queue_pi == pi) pw->queue_pi = NULL;
2634 g_object_unref(pi->pixbuf);
2646 *-----------------------------------------------------------------------------
2648 *-----------------------------------------------------------------------------
2651 static void pan_window_message(PanWindow *pw, const gchar *text)
2661 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
2674 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
2676 size += pi->fd->size;
2681 ss = text_from_size_abrev(size);
2682 buf = g_strdup_printf(_("%d images, %s"), count, ss);
2684 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
2688 static ImageWindow *pan_window_active_image(PanWindow *pw)
2690 if (pw->fs) return pw->fs->imd;
2695 static void pan_window_zoom_limit(PanWindow *pw)
2701 case LAYOUT_SIZE_THUMB_DOTS:
2702 case LAYOUT_SIZE_THUMB_NONE:
2703 case LAYOUT_SIZE_THUMB_SMALL:
2704 case LAYOUT_SIZE_THUMB_NORMAL:
2706 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
2710 case LAYOUT_SIZE_THUMB_LARGE:
2713 case LAYOUT_SIZE_10:
2714 case LAYOUT_SIZE_25:
2717 case LAYOUT_SIZE_33:
2718 case LAYOUT_SIZE_50:
2719 case LAYOUT_SIZE_100:
2725 image_zoom_set_limits(pw->imd, min, 32.0);
2728 static gint pan_window_layout_update_idle_cb(gpointer data)
2730 PanWindow *pw = data;
2736 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2738 if (!pw->cache_list && !pw->cache_todo)
2740 pan_cache_fill(pw, pw->path);
2743 pan_window_message(pw, _("Reading dimensions..."));
2747 if (pan_cache_step(pw))
2751 if (pw->cache_count == pw->cache_total)
2753 pan_window_message(pw, _("Sorting images..."));
2755 else if (pw->cache_tick > 9)
2759 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
2760 pw->cache_total - pw->cache_count);
2761 pan_window_message(pw, buf);
2771 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
2773 pan_window_zoom_limit(pw);
2775 if (width > 0 && height > 0)
2779 image_set_image_as_tiles(pw->imd, width, height,
2780 PAN_TILE_SIZE, PAN_TILE_SIZE, 8,
2781 pan_window_request_tile_cb,
2782 pan_window_dispose_tile_cb, pw, 1.0);
2784 if (scroll_x == 0 && scroll_y == 0)
2792 image_scroll_to_point(pw->imd, scroll_x, scroll_y, align, align);
2795 pan_window_message(pw, NULL);
2802 static void pan_window_layout_update_idle(PanWindow *pw)
2804 if (pw->idle_id == -1)
2806 pan_window_message(pw, _("Sorting images..."));
2807 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
2812 *-----------------------------------------------------------------------------
2813 * pan window keyboard
2814 *-----------------------------------------------------------------------------
2817 static const gchar *pan_menu_click_path(PanWindow *pw)
2819 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
2823 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
2825 PanWindow *pw = data;
2828 imd = pan_window_active_image(pw);
2829 gdk_window_get_origin(imd->image->window, x, y);
2830 popup_menu_position_clamp(menu, x, y, 0);
2833 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
2835 PanWindow *pw = data;
2838 gint stop_signal = FALSE;
2844 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(pw->imd->widget));
2846 imd = pan_window_active_image(pw);
2847 path = pan_menu_click_path(pw);
2851 switch (event->keyval)
2853 case GDK_Left: case GDK_KP_Left:
2857 case GDK_Right: case GDK_KP_Right:
2861 case GDK_Up: case GDK_KP_Up:
2865 case GDK_Down: case GDK_KP_Down:
2869 case GDK_Page_Up: case GDK_KP_Page_Up:
2870 image_scroll(imd, 0, 0-imd->vis_height / 2);
2872 case GDK_Page_Down: case GDK_KP_Page_Down:
2873 image_scroll(imd, 0, imd->vis_height / 2);
2875 case GDK_Home: case GDK_KP_Home:
2876 image_scroll(imd, 0-imd->vis_width / 2, 0);
2878 case GDK_End: case GDK_KP_End:
2879 image_scroll(imd, imd->vis_width / 2, 0);
2884 if (focused && !(event->state & GDK_CONTROL_MASK) )
2885 switch (event->keyval)
2887 case '+': case '=': case GDK_KP_Add:
2888 image_zoom_adjust(imd, ZOOM_INCREMENT);
2890 case '-': case GDK_KP_Subtract:
2891 image_zoom_adjust(imd, -ZOOM_INCREMENT);
2893 case 'Z': case 'z': case GDK_KP_Divide: case '1':
2894 image_zoom_set(imd, 1.0);
2897 image_zoom_set(imd, 2.0);
2900 image_zoom_set(imd, 3.0);
2903 image_zoom_set(imd, 4.0);
2906 image_zoom_set(imd, -4.0);
2909 image_zoom_set(imd, -3.0);
2912 image_zoom_set(imd, -2.0);
2916 pan_fullscreen_toggle(pw, FALSE);
2920 pan_overlay_toggle(pw);
2922 case GDK_Delete: case GDK_KP_Delete:
2927 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
2934 pan_fullscreen_toggle(pw, TRUE);
2937 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
2939 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
2945 menu = pan_popup_menu(pw);
2946 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
2951 if (event->state & GDK_CONTROL_MASK)
2954 switch (event->keyval)
2987 if (path) file_util_copy(path, NULL, NULL, imd->widget);
2990 if (path) file_util_move(path, NULL, NULL, imd->widget);
2993 if (path) file_util_rename(path, NULL, imd->widget);
2996 if (path) file_util_delete(path, NULL, imd->widget);
2999 if (path) info_window_new(path, NULL);
3002 pan_window_close(pw);
3005 if (n != -1 && path)
3007 pan_fullscreen_toggle(pw, TRUE);
3008 start_editor_from_file(n, path);
3012 else if (event->state & GDK_SHIFT_MASK)
3019 switch (event->keyval)
3024 pan_fullscreen_toggle(pw, TRUE);
3027 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3029 gtk_widget_grab_focus(pw->imd->widget);
3030 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3039 if (x != 0 || y!= 0)
3041 keyboard_scroll_calc(&x, &y, event);
3042 image_scroll(imd, x, y);
3049 *-----------------------------------------------------------------------------
3051 *-----------------------------------------------------------------------------
3054 static void pan_info_update(PanWindow *pw, PanItem *pi)
3060 gint x1, y1, x2, y2, x3, y3;
3063 if (pw->click_pi == pi) return;
3064 if (pi && !pi->fd) pi = NULL;
3066 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3071 printf("info set to %s\n", pi->fd->path);
3073 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3075 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3076 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3077 pan_item_set_key(pbox, "info");
3079 if (pi->type == ITEM_THUMB && pi->pixbuf)
3081 w = gdk_pixbuf_get_width(pi->pixbuf);
3082 h = gdk_pixbuf_get_height(pi->pixbuf);
3084 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3085 y1 = pi->y + (pi->height - h) / 2 + 8;
3089 x1 = pi->x + pi->width - 8;
3097 triangle_rect_region(x1, y1, x2, y2, x3, y3,
3100 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3101 x1, y1, x2, y2, x3, y3,
3102 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3103 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3104 pan_item_set_key(p, "info");
3105 pan_item_added(pw, p);
3107 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3108 _("Filename:"), TEXT_ATTR_BOLD,
3109 PAN_POPUP_TEXT_COLOR, 255);
3110 pan_item_set_key(plabel, "info");
3111 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3112 pi->fd->name, TEXT_ATTR_NONE,
3113 PAN_POPUP_TEXT_COLOR, 255);
3114 pan_item_set_key(p, "info");
3115 pan_item_size_by_item(pbox, p, 0);
3117 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3118 _("Date:"), TEXT_ATTR_BOLD,
3119 PAN_POPUP_TEXT_COLOR, 255);
3120 pan_item_set_key(plabel, "info");
3121 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3122 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3123 PAN_POPUP_TEXT_COLOR, 255);
3124 pan_item_set_key(p, "info");
3125 pan_item_size_by_item(pbox, p, 0);
3127 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3128 _("Size:"), TEXT_ATTR_BOLD,
3129 PAN_POPUP_TEXT_COLOR, 255);
3130 pan_item_set_key(plabel, "info");
3131 buf = text_from_size(pi->fd->size);
3132 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3133 buf, TEXT_ATTR_NONE,
3134 PAN_POPUP_TEXT_COLOR, 255);
3136 pan_item_set_key(p, "info");
3137 pan_item_size_by_item(pbox, p, 0);
3139 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3140 pan_item_added(pw, pbox);
3145 *-----------------------------------------------------------------------------
3147 *-----------------------------------------------------------------------------
3150 static void pan_search_status(PanWindow *pw, const gchar *text)
3152 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3155 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3163 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3165 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3166 if (!list) return FALSE;
3168 found = g_list_find(list, pw->click_pi);
3169 if (found && found->next)
3171 found = found->next;
3179 pan_info_update(pw, pi);
3180 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3182 buf = g_strdup_printf("%s ( %d / %d )",
3183 (path[0] == '/') ? _("path found") : _("filename found"),
3184 g_list_index(list, pi) + 1,
3185 g_list_length(list));
3186 pan_search_status(pw, buf);
3194 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3202 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3204 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3205 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3210 needle = g_utf8_strdown(text, -1);
3211 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3214 if (!list) return FALSE;
3216 found = g_list_find(list, pw->click_pi);
3217 if (found && found->next)
3219 found = found->next;
3227 pan_info_update(pw, pi);
3228 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3230 buf = g_strdup_printf("%s ( %d / %d )",
3232 g_list_index(list, pi) + 1,
3233 g_list_length(list));
3234 pan_search_status(pw, buf);
3242 static gint valid_date_separator(gchar c)
3244 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3247 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type, gint year, gint month, gint day)
3252 work = g_list_last(pw->list);
3260 if (pi->fd && (pi->type == type || type == ITEM_NONE))
3264 tl = localtime(&pi->fd->date);
3269 match = (tl->tm_year == year - 1900);
3270 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3271 if (match && day > 0) match = (tl->tm_mday == day);
3273 if (match) list = g_list_prepend(list, pi);
3278 return g_list_reverse(list);
3281 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3298 if (!text) return FALSE;
3300 ptr = (gchar *)text;
3301 while (*ptr != '\0')
3303 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3308 if (t == -1) return FALSE;
3310 if (!lt) return FALSE;
3312 if (valid_date_separator(*text))
3315 mptr = (gchar *)text;
3319 year = (gint)strtol(text, &mptr, 10);
3320 if (mptr == text) return FALSE;
3323 if (*mptr != '\0' && valid_date_separator(*mptr))
3328 month = strtol(mptr, &dptr, 10);
3331 if (valid_date_separator(*dptr))
3333 month = lt->tm_mon + 1;
3341 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3345 day = strtol(dptr, &eptr, 10);
3355 year = lt->tm_year + 1900;
3357 else if (year < 100)
3366 month < -1 || month == 0 || month > 12 ||
3367 day < -1 || day == 0 || day > 31) return FALSE;
3369 t = date_to_time(year, month, day);
3370 if (t < 0) return FALSE;
3372 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3374 list = pan_search_by_date_val(pw, type, year, month, day);
3377 found = g_list_find(list, pw->click_pi);
3378 if (found && found->next)
3380 found = found->next;
3391 pan_info_update(pw, pi);
3392 image_scroll_to_point(pw->imd,
3393 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3399 buf = date_value_string(t, DATE_LENGTH_MONTH);
3404 buf = g_strdup_printf("%d %s", day, tmp);
3410 buf = date_value_string(t, DATE_LENGTH_YEAR);
3415 buf_count = g_strdup_printf("( %d / %d )",
3416 g_list_index(list, pi) + 1,
3417 g_list_length(list));
3421 buf_count = g_strdup_printf("(%s)", _("no match"));
3424 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
3427 pan_search_status(pw, message);
3435 static void pan_search_activate_cb(const gchar *text, gpointer data)
3437 PanWindow *pw = data;
3441 tab_completion_append_to_history(pw->search_entry, text);
3443 if (pan_search_by_path(pw, text)) return;
3445 if (pw->layout == LAYOUT_TIMELINE && pan_search_by_date(pw, text)) return;
3447 if (pan_search_by_partial(pw, text)) return;
3449 pan_search_status(pw, _("no match"));
3452 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3454 PanWindow *pw = data;
3457 visible = GTK_WIDGET_VISIBLE(pw->search_box);
3458 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3462 gtk_widget_hide(pw->search_box);
3463 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3467 gtk_widget_show(pw->search_box);
3468 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3469 gtk_widget_grab_focus(pw->search_entry);
3475 *-----------------------------------------------------------------------------
3476 * view window main routines
3477 *-----------------------------------------------------------------------------
3480 static void button_cb(ImageWindow *imd, gint button, guint32 time,
3481 gdouble x, gdouble y, guint state, gpointer data)
3483 PanWindow *pw = data;
3489 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3490 (gint)((double)(pw->imd->x_scroll + x - pw->imd->x_offset) / pw->imd->scale),
3491 (gint)((double)(pw->imd->y_scroll + y - pw->imd->y_offset) / pw->imd->scale));
3497 pan_info_update(pw, pi);
3502 pan_info_update(pw, pi);
3503 menu = pan_popup_menu(pw);
3504 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, time);
3511 static void scroll_cb(ImageWindow *imd, GdkScrollDirection direction, guint32 time,
3512 gdouble x, gdouble y, guint state, gpointer data)
3515 PanWindow *pw = data;
3518 if (state & GDK_CONTROL_MASK)
3523 image_zoom_adjust_at_point(imd, ZOOM_INCREMENT, x, y);
3525 case GDK_SCROLL_DOWN:
3526 image_zoom_adjust_at_point(imd, -ZOOM_INCREMENT, x, y);
3532 else if ( (state & GDK_SHIFT_MASK) != (mousewheel_scrolls))
3537 image_scroll(imd, 0, -MOUSEWHEEL_SCROLL_SIZE);
3539 case GDK_SCROLL_DOWN:
3540 image_scroll(imd, 0, MOUSEWHEEL_SCROLL_SIZE);
3542 case GDK_SCROLL_LEFT:
3543 image_scroll(imd, -MOUSEWHEEL_SCROLL_SIZE, 0);
3545 case GDK_SCROLL_RIGHT:
3546 image_scroll(imd, MOUSEWHEEL_SCROLL_SIZE, 0);
3558 case GDK_SCROLL_DOWN:
3566 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
3568 image_set_button_func(imd, button_cb, pw);
3569 image_set_scroll_func(imd, scroll_cb, pw);
3572 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
3574 PanWindow *pw = data;
3579 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
3581 if (force_off && !pw->fs) return;
3585 fullscreen_stop(pw->fs);
3586 pw->imd = pw->imd_normal;
3590 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
3592 pan_image_set_buttons(pw, pw->fs->imd);
3593 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
3594 G_CALLBACK(pan_window_key_press_cb), pw);
3596 pw->imd = pw->fs->imd;
3600 static void pan_overlay_toggle(PanWindow *pw)
3604 imd = pan_window_active_image(pw);
3606 if (pw->overlay_id == -1)
3608 pw->overlay_id = image_overlay_info_enable(imd);
3612 image_overlay_info_disable(imd, pw->overlay_id);
3613 pw->overlay_id = -1;
3618 static void pan_window_image_update_cb(ImageWindow *imd, gpointer data)
3620 PanWindow *pw = data;
3623 text = image_zoom_get_as_text(imd);
3624 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
3628 static void pan_window_image_scroll_notify_cb(ImageWindow *imd, gint x, gint y,
3629 gint width, gint height, gpointer data)
3631 PanWindow *pw = data;
3634 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
3635 adj->page_size = (gdouble)imd->vis_width / imd->scale;
3636 adj->page_increment = adj->page_size / 2.0;
3637 adj->step_increment = 48.0 / imd->scale;
3639 adj->upper = MAX((gdouble)width + adj->page_size, 1.0);
3640 adj->value = (gdouble)x;
3642 pref_signal_block_data(pw->scrollbar_h, pw);
3643 gtk_adjustment_changed(adj);
3644 pref_signal_unblock_data(pw->scrollbar_h, pw);
3646 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
3647 adj->page_size = (gdouble)imd->vis_height / imd->scale;
3648 adj->page_increment = adj->page_size / 2.0;
3649 adj->step_increment = 48.0 / imd->scale;
3651 adj->upper = MAX((gdouble)height + adj->page_size, 1.0);
3652 adj->value = (gdouble)y;
3654 pref_signal_block_data(pw->scrollbar_v, pw);
3655 gtk_adjustment_changed(adj);
3656 pref_signal_unblock_data(pw->scrollbar_v, pw);
3658 // printf("scrolled to %d,%d @ %d x %d\n", x, y, width, height);
3661 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
3663 PanWindow *pw = data;
3666 if (!pw->imd->scale) return;
3668 x = (gint)gtk_range_get_value(range);
3670 image_scroll_to_point(pw->imd, x, (gint)((gdouble)pw->imd->y_scroll / pw->imd->scale), 0.0, 0.0);
3673 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
3675 PanWindow *pw = data;
3678 if (!pw->imd->scale) return;
3680 y = (gint)gtk_range_get_value(range);
3682 image_scroll_to_point(pw->imd, (gint)((gdouble)pw->imd->x_scroll / pw->imd->scale), y, 0.0, 0.0);
3685 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
3687 PanWindow *pw = data;
3689 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3690 pan_window_layout_update_idle(pw);
3693 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
3695 PanWindow *pw = data;
3697 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3698 pan_window_layout_update_idle(pw);
3701 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
3703 PanWindow *pw = data;
3706 path = remove_trailing_slash(new_text);
3707 parse_out_relatives(path);
3711 warning_dialog(_("Folder not found"),
3712 _("The entered path is not a folder"),
3713 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
3717 tab_completion_append_to_history(pw->path_entry, path);
3720 pw->path = g_strdup(path);
3722 pan_window_layout_update_idle(pw);
3725 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
3727 PanWindow *pw = data;
3730 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
3732 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
3733 pan_window_entry_activate_cb(text, pw);
3737 static void pan_window_close(PanWindow *pw)
3739 pan_window_list = g_list_remove(pan_window_list, pw);
3741 if (pw->idle_id != -1)
3743 g_source_remove(pw->idle_id);
3746 pan_fullscreen_toggle(pw, TRUE);
3747 gtk_widget_destroy(pw->window);
3749 pan_window_items_free(pw);
3756 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
3758 PanWindow *pw = data;
3760 pan_window_close(pw);
3764 static void pan_window_new_real(const gchar *path)
3773 GdkGeometry geometry;
3775 pw = g_new0(PanWindow, 1);
3777 pw->path = g_strdup(path);
3778 pw->layout = LAYOUT_TIMELINE;
3779 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
3780 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
3781 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
3785 pw->overlay_id = -1;
3788 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3790 geometry.min_width = 8;
3791 geometry.min_height = 8;
3792 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
3794 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
3795 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
3796 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
3797 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
3799 window_set_icon(pw->window, NULL, NULL);
3801 vbox = gtk_vbox_new(FALSE, 0);
3802 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
3803 gtk_widget_show(vbox);
3805 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3807 pref_spacer(box, 0);
3808 pref_label_new(box, _("Location:"));
3809 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
3810 pan_window_entry_activate_cb, pw);
3811 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
3812 G_CALLBACK(pan_window_entry_change_cb), pw);
3813 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
3814 gtk_widget_show(combo);
3816 combo = gtk_combo_box_new_text();
3817 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
3818 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
3819 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
3820 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
3822 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
3823 g_signal_connect(G_OBJECT(combo), "changed",
3824 G_CALLBACK(pan_window_layout_change_cb), pw);
3825 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
3826 gtk_widget_show(combo);
3828 combo = gtk_combo_box_new_text();
3829 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
3830 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
3831 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
3832 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
3833 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
3834 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
3835 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
3836 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
3837 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
3838 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
3840 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
3841 g_signal_connect(G_OBJECT(combo), "changed",
3842 G_CALLBACK(pan_window_layout_size_cb), pw);
3843 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
3844 gtk_widget_show(combo);
3846 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
3847 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
3848 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
3850 pw->imd = image_new(TRUE);
3851 pw->imd_normal = pw->imd;
3853 if (black_window_background) image_background_set_black(pw->imd, TRUE);
3854 image_set_update_func(pw->imd, pan_window_image_update_cb, pw);
3856 image_set_scroll_notify_func(pw->imd, pan_window_image_scroll_notify_cb, pw);
3859 gtk_box_pack_start(GTK_BOX(vbox), pw->imd->widget, TRUE, TRUE, 0);
3861 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
3862 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
3863 gtk_widget_show(pw->imd->widget);
3865 pan_window_dnd_init(pw);
3867 pan_image_set_buttons(pw, pw->imd);
3869 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
3870 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
3871 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
3872 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
3873 GTK_FILL | GTK_EXPAND, 0, 0, 0);
3874 gtk_widget_show(pw->scrollbar_h);
3876 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
3877 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
3878 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
3879 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
3880 0, GTK_FILL | GTK_EXPAND, 0, 0);
3881 gtk_widget_show(pw->scrollbar_v);
3885 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
3886 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
3888 pref_spacer(pw->search_box, 0);
3889 pref_label_new(pw->search_box, _("Find:"));
3891 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
3892 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
3893 gtk_widget_show(hbox);
3895 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
3896 pan_search_activate_cb, pw);
3897 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
3898 gtk_widget_show(combo);
3900 pw->search_label = gtk_label_new("");
3901 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
3902 gtk_widget_show(pw->search_label);
3906 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3908 frame = gtk_frame_new(NULL);
3909 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
3910 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
3911 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
3912 gtk_widget_show(frame);
3914 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
3915 gtk_container_add(GTK_CONTAINER(frame), hbox);
3916 gtk_widget_show(hbox);
3918 pref_spacer(hbox, 0);
3919 pw->label_message = pref_label_new(hbox, "");
3921 frame = gtk_frame_new(NULL);
3922 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
3923 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
3924 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
3925 gtk_widget_show(frame);
3927 pw->label_zoom = gtk_label_new("");
3928 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
3929 gtk_widget_show(pw->label_zoom);
3931 pw->search_button = gtk_toggle_button_new();
3932 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
3933 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
3934 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
3935 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
3936 gtk_widget_show(hbox);
3937 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
3938 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
3939 gtk_widget_show(pw->search_button_arrow);
3940 pref_label_new(hbox, _("Find"));
3942 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
3943 gtk_widget_show(pw->search_button);
3944 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
3945 G_CALLBACK(pan_search_toggle_cb), pw);
3947 g_signal_connect(G_OBJECT(pw->window), "delete_event",
3948 G_CALLBACK(pan_window_delete_cb), pw);
3949 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
3950 G_CALLBACK(pan_window_key_press_cb), pw);
3952 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
3954 pan_window_layout_update_idle(pw);
3956 gtk_widget_grab_focus(pw->imd->widget);
3957 gtk_widget_show(pw->window);
3959 pan_window_list = g_list_append(pan_window_list, pw);
3963 *-----------------------------------------------------------------------------
3964 * peformance warnings
3965 *-----------------------------------------------------------------------------
3968 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
3972 generic_dialog_close(gd);
3974 pan_window_new_real(path);
3978 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
3982 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
3983 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
3986 static gint pan_warning(const gchar *path)
3992 GtkWidget *ct_button;
3995 if (enable_thumb_caching &&
3996 thumbnail_spec_standard) return FALSE;
3998 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
3999 if (hide_dlg) return FALSE;
4001 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4003 gd->data = g_strdup(path);
4004 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4005 pan_warning_ok_cb, TRUE);
4007 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4008 _("Pan view performance may be poor."),
4009 _("To improve performance of thumbnails in the pan view the"
4010 " following options can be enabled. Note that both options"
4011 " must be enabled to notice a change in performance."));
4013 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4014 pref_spacer(group, PREF_PAD_INDENT);
4015 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4017 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4018 enable_thumb_caching, &enable_thumb_caching);
4019 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4020 thumbnail_spec_standard, &thumbnail_spec_standard);
4021 pref_checkbox_link_sensitivity(ct_button, button);
4025 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4026 G_CALLBACK(pan_warning_hide_cb), NULL);
4028 gtk_widget_show(gd->dialog);
4035 *-----------------------------------------------------------------------------
4037 *-----------------------------------------------------------------------------
4040 void pan_window_new(const gchar *path)
4042 if (pan_warning(path)) return;
4044 pan_window_new_real(path);
4048 *-----------------------------------------------------------------------------
4050 *-----------------------------------------------------------------------------
4053 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4055 PanWindow *pw = data;
4058 path = pan_menu_click_path(pw);
4061 pan_fullscreen_toggle(pw, TRUE);
4062 view_window_new(path);
4066 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4072 pw = submenu_item_get_data(widget);
4073 n = GPOINTER_TO_INT(data);
4076 path = pan_menu_click_path(pw);
4079 pan_fullscreen_toggle(pw, TRUE);
4080 start_editor_from_file(n, path);
4084 static void pan_info_cb(GtkWidget *widget, gpointer data)
4086 PanWindow *pw = data;
4089 path = pan_menu_click_path(pw);
4090 if (path) info_window_new(path, NULL);
4093 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4095 PanWindow *pw = data;
4097 image_zoom_adjust(pan_window_active_image(pw), ZOOM_INCREMENT);
4100 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4102 PanWindow *pw = data;
4104 image_zoom_adjust(pan_window_active_image(pw), -ZOOM_INCREMENT);
4107 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4109 PanWindow *pw = data;
4111 image_zoom_set(pan_window_active_image(pw), 1.0);
4114 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4116 PanWindow *pw = data;
4119 path = pan_menu_click_path(pw);
4120 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4123 static void pan_move_cb(GtkWidget *widget, gpointer data)
4125 PanWindow *pw = data;
4128 path = pan_menu_click_path(pw);
4129 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4132 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4134 PanWindow *pw = data;
4137 path = pan_menu_click_path(pw);
4138 if (path) file_util_rename(path, NULL, pw->imd->widget);
4141 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4143 PanWindow *pw = data;
4146 path = pan_menu_click_path(pw);
4147 if (path) file_util_delete(path, NULL, pw->imd->widget);
4150 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4152 PanWindow *pw = data;
4154 pan_fullscreen_toggle(pw, FALSE);
4157 static void pan_close_cb(GtkWidget *widget, gpointer data)
4159 PanWindow *pw = data;
4161 pan_window_close(pw);
4164 static GtkWidget *pan_popup_menu(PanWindow *pw)
4170 active = (pw->click_pi != NULL);
4172 menu = popup_menu_short_lived();
4174 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4175 G_CALLBACK(pan_zoom_in_cb), pw);
4176 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4177 G_CALLBACK(pan_zoom_out_cb), pw);
4178 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4179 G_CALLBACK(pan_zoom_1_1_cb), pw);
4180 menu_item_add_divider(menu);
4182 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4183 gtk_widget_set_sensitive(item, active);
4185 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4186 G_CALLBACK(pan_info_cb), pw);
4188 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4189 G_CALLBACK(pan_new_window_cb), pw);
4191 menu_item_add_divider(menu);
4192 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4193 G_CALLBACK(pan_copy_cb), pw);
4194 menu_item_add_sensitive(menu, _("_Move..."), active,
4195 G_CALLBACK(pan_move_cb), pw);
4196 menu_item_add_sensitive(menu, _("_Rename..."), active,
4197 G_CALLBACK(pan_rename_cb), pw);
4198 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4199 G_CALLBACK(pan_delete_cb), pw);
4201 menu_item_add_divider(menu);
4205 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4209 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4212 menu_item_add_divider(menu);
4213 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4219 *-----------------------------------------------------------------------------
4221 *-----------------------------------------------------------------------------
4224 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4226 GtkSelectionData *selection_data, guint info,
4227 guint time, gpointer data)
4229 PanWindow *pw = data;
4232 if (gtk_drag_get_source_widget(context) == pw->imd->image) return;
4236 if (info == TARGET_URI_LIST)
4240 list = uri_list_from_text(selection_data->data, TRUE);
4241 if (list && isdir((gchar *)list->data))
4243 gchar *path = list->data;
4246 pw->path = g_strdup(path);
4248 pan_window_layout_update_idle(pw);
4251 path_list_free(list);
4255 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4256 GtkSelectionData *selection_data, guint info,
4257 guint time, gpointer data)
4259 PanWindow *pw = data;
4262 path = pan_menu_click_path(pw);
4272 case TARGET_URI_LIST:
4275 case TARGET_TEXT_PLAIN:
4280 list = g_list_append(NULL, (gchar *)path);
4281 text = uri_text_from_list(list, &len, plain_text);
4285 gtk_selection_data_set (selection_data, selection_data->target,
4292 gtk_selection_data_set (selection_data, selection_data->target,
4297 static void pan_window_dnd_init(PanWindow *pw)
4303 gtk_drag_source_set(imd->image, GDK_BUTTON2_MASK,
4304 dnd_file_drag_types, dnd_file_drag_types_count,
4305 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4306 g_signal_connect(G_OBJECT(imd->image), "drag_data_get",
4307 G_CALLBACK(pan_window_set_dnd_data), pw);
4309 gtk_drag_dest_set(imd->image,
4310 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4311 dnd_file_drop_types, dnd_file_drop_types_count,
4312 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4313 g_signal_connect(G_OBJECT(imd->image), "drag_data_received",
4314 G_CALLBACK(pan_window_get_dnd_data), pw);
4318 *-----------------------------------------------------------------------------
4319 * maintenance (for rename, move, remove)
4320 *-----------------------------------------------------------------------------