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!
18 #include "cache-loader.h"
23 #include "fullscreen.h"
25 #include "image-load.h"
29 #include "pixbuf-renderer.h"
30 #include "pixbuf_util.h"
33 #include "ui_bookmark.h"
34 #include "ui_fileops.h"
37 #include "ui_tabcomp.h"
39 #include <gdk/gdkkeysyms.h> /* for keyboard values */
43 #define PAN_WINDOW_DEFAULT_WIDTH 720
44 #define PAN_WINDOW_DEFAULT_HEIGHT 500
46 #define PAN_TILE_SIZE 512
48 #define PAN_THUMB_SIZE_DOTS 4
49 #define PAN_THUMB_SIZE_NONE 24
50 #define PAN_THUMB_SIZE_SMALL 64
51 #define PAN_THUMB_SIZE_NORMAL 128
52 #define PAN_THUMB_SIZE_LARGE 256
53 #define PAN_THUMB_SIZE pw->thumb_size
55 #define PAN_THUMB_GAP_DOTS 2
56 #define PAN_THUMB_GAP_SMALL 14
57 #define PAN_THUMB_GAP_NORMAL 30
58 #define PAN_THUMB_GAP_LARGE 40
59 #define PAN_THUMB_GAP_HUGE 50
60 #define PAN_THUMB_GAP pw->thumb_gap
62 #define PAN_SHADOW_OFFSET 6
63 #define PAN_SHADOW_FADE 5
64 #define PAN_SHADOW_COLOR 0, 0, 0
65 #define PAN_SHADOW_ALPHA 64
67 #define PAN_OUTLINE_THICKNESS 1
68 #define PAN_OUTLINE_COLOR_1 255, 255, 255
69 #define PAN_OUTLINE_COLOR_2 64, 64, 64
70 #define PAN_OUTLINE_ALPHA 180
72 #define PAN_BACKGROUND_COLOR 150, 150, 150
74 #define PAN_GRID_SIZE 60
75 #define PAN_GRID_COLOR 0, 0, 0
76 #define PAN_GRID_ALPHA 20
78 #define PAN_FOLDER_BOX_COLOR 255, 255, 255
79 #define PAN_FOLDER_BOX_ALPHA 100
80 #define PAN_FOLDER_BOX_BORDER 20
82 #define PAN_FOLDER_BOX_OUTLINE_THICKNESS 4
83 #define PAN_FOLDER_BOX_OUTLINE_COLOR 0, 0, 0
84 #define PAN_FOLDER_BOX_OUTLINE_ALPHA 128
86 #define PAN_TEXT_BORDER_SIZE 4
87 #define PAN_TEXT_COLOR 0, 0, 0
89 #define PAN_POPUP_COLOR 255, 255, 225
90 #define PAN_POPUP_ALPHA 255
91 #define PAN_POPUP_BORDER 1
92 #define PAN_POPUP_BORDER_COLOR 0, 0, 0
93 #define PAN_POPUP_TEXT_COLOR 0, 0, 0
95 #define PAN_CAL_POPUP_COLOR 220, 220, 220
96 #define PAN_CAL_POPUP_ALPHA 255
97 #define PAN_CAL_POPUP_BORDER 1
98 #define PAN_CAL_POPUP_BORDER_COLOR 0, 0, 0
99 #define PAN_CAL_POPUP_TEXT_COLOR 0, 0, 0
101 #define PAN_CAL_DAY_WIDTH 100
102 #define PAN_CAL_DAY_HEIGHT 80
104 #define PAN_CAL_DAY_COLOR 255, 255, 255
105 #define PAN_CAL_DAY_ALPHA 220
106 #define PAN_CAL_DAY_BORDER 2
107 #define PAN_CAL_DAY_BORDER_COLOR 0, 0, 0
108 #define PAN_CAL_DAY_TEXT_COLOR 0, 0, 0
110 #define PAN_CAL_MONTH_COLOR 255, 255, 255
111 #define PAN_CAL_MONTH_ALPHA 200
112 #define PAN_CAL_MONTH_BORDER 4
113 #define PAN_CAL_MONTH_BORDER_COLOR 0, 0, 0
114 #define PAN_CAL_MONTH_TEXT_COLOR 0, 0, 0
116 #define PAN_CAL_DOT_SIZE 3
117 #define PAN_CAL_DOT_GAP 2
118 #define PAN_CAL_DOT_COLOR 128, 128, 128
119 #define PAN_CAL_DOT_ALPHA 128
122 #define PAN_GROUP_MAX 16
124 #define ZOOM_INCREMENT 1.0
125 #define ZOOM_LABEL_WIDTH 64
128 #define PAN_PREF_GROUP "pan_view_options"
129 #define PAN_PREF_HIDE_WARNING "hide_performance_warning"
130 #define PAN_PREF_EXIF_DATE "use_exif_date"
131 #define PAN_PREF_INFO_IMAGE "info_image_size"
132 #define PAN_PREF_INFO_EXIF "info_includes_exif"
138 LAYOUT_FOLDERS_LINEAR,
139 LAYOUT_FOLDERS_FLOWER,
144 LAYOUT_SIZE_THUMB_DOTS = 0,
145 LAYOUT_SIZE_THUMB_NONE,
146 LAYOUT_SIZE_THUMB_SMALL,
147 LAYOUT_SIZE_THUMB_NORMAL,
148 LAYOUT_SIZE_THUMB_LARGE,
167 TEXT_ATTR_BOLD = 1 << 0,
168 TEXT_ATTR_HEADING = 1 << 1,
169 TEXT_ATTR_MARKUP = 1 << 2
180 typedef struct _PanItem PanItem;
195 TextAttrType text_attr;
213 typedef struct _PanWindow PanWindow;
218 ImageWindow *imd_normal;
221 GtkWidget *path_entry;
223 GtkWidget *label_message;
224 GtkWidget *label_zoom;
226 GtkWidget *search_box;
227 GtkWidget *search_entry;
228 GtkWidget *search_label;
229 GtkWidget *search_button;
230 GtkWidget *search_button_arrow;
232 GtkWidget *date_button;
234 GtkWidget *scrollbar_h;
235 GtkWidget *scrollbar_v;
245 gint exif_date_enable;
247 gint info_image_size;
248 gint info_includes_exif;
250 gint ignore_symlinks;
261 CacheLoader *cache_cl;
274 typedef struct _PanGrid PanGrid;
283 typedef struct _PanCacheData PanCacheData;
284 struct _PanCacheData {
290 static GList *pan_window_list = NULL;
293 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend,
294 gint ignore_symlinks);
296 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
298 static void pan_layout_resize(PanWindow *pw);
300 static void pan_window_layout_update_idle(PanWindow *pw);
302 static GtkWidget *pan_popup_menu(PanWindow *pw);
303 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off);
305 static void pan_search_toggle_visible(PanWindow *pw, gint enable);
306 static void pan_search_activate(PanWindow *pw);
308 static void pan_window_close(PanWindow *pw);
310 static void pan_window_dnd_init(PanWindow *pw);
322 static gint date_compare(time_t a, time_t b, DateLengthType length)
327 if (length == DATE_LENGTH_EXACT) return (a == b);
329 if (!localtime_r(&a, &ta) ||
330 !localtime_r(&b, &tb)) return FALSE;
332 if (ta.tm_year != tb.tm_year) return FALSE;
333 if (length == DATE_LENGTH_YEAR) return TRUE;
335 if (ta.tm_mon != tb.tm_mon) return FALSE;
336 if (length == DATE_LENGTH_MONTH) return TRUE;
338 if (length == DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
340 if (ta.tm_mday != tb.tm_mday) return FALSE;
341 if (length == DATE_LENGTH_DAY) return TRUE;
343 return (ta.tm_hour == tb.tm_hour);
346 static gint date_value(time_t d, DateLengthType length)
350 if (!localtime_r(&d, &td)) return -1;
354 case DATE_LENGTH_DAY:
357 case DATE_LENGTH_WEEK:
360 case DATE_LENGTH_MONTH:
361 return td.tm_mon + 1;
363 case DATE_LENGTH_YEAR:
364 return td.tm_year + 1900;
366 case DATE_LENGTH_EXACT:
374 static gchar *date_value_string(time_t d, DateLengthType length)
378 gchar *format = NULL;
380 if (!localtime_r(&d, &td)) return g_strdup("");
384 case DATE_LENGTH_DAY:
385 return g_strdup_printf("%d", td.tm_mday);
387 case DATE_LENGTH_WEEK:
390 case DATE_LENGTH_MONTH:
393 case DATE_LENGTH_YEAR:
394 return g_strdup_printf("%d", td.tm_year + 1900);
396 case DATE_LENGTH_EXACT:
398 return g_strdup(text_from_time(d));
403 if (format && strftime(buf, sizeof(buf), format, &td) > 0)
405 gchar *ret = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
412 static time_t date_to_time(gint year, gint month, gint day)
419 lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
420 lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
421 lt.tm_year = year - 1900;
428 *-----------------------------------------------------------------------------
430 *-----------------------------------------------------------------------------
433 static void pan_cache_free(PanWindow *pw)
437 work = pw->cache_list;
445 cache_sim_data_free(pc->cd);
446 file_data_free((FileData *)pc);
449 g_list_free(pw->cache_list);
450 pw->cache_list = NULL;
452 filelist_free(pw->cache_todo);
453 pw->cache_todo = NULL;
459 cache_loader_free(pw->cache_cl);
463 static void pan_cache_fill(PanWindow *pw, const gchar *path)
469 list = pan_window_layout_list(path, SORT_NAME, TRUE, pw->ignore_symlinks);
470 pw->cache_todo = g_list_reverse(list);
472 pw->cache_total = g_list_length(pw->cache_todo);
475 static void pan_cache_step_done_cb(CacheLoader *cl, gint error, gpointer data)
477 PanWindow *pw = data;
482 pc = pw->cache_list->data;
491 cache_loader_free(cl);
494 pan_window_layout_update_idle(pw);
497 static gint pan_cache_step(PanWindow *pw)
501 CacheDataType load_mask;
503 if (!pw->cache_todo) return TRUE;
505 fd = pw->cache_todo->data;
506 pw->cache_todo = g_list_remove(pw->cache_todo, fd);
509 if (enable_thumb_caching)
513 found = cache_find_location(CACHE_TYPE_SIM, fd->path);
514 if (found && filetime(found) == fd->date)
516 cd = cache_sim_data_load(found);
521 if (!cd) cd = cache_sim_data_new();
525 cd->dimensions = image_load_dimensions(fd->path, &cd->width, &cd->height);
526 if (enable_thumb_caching &&
532 base = cache_get_location(CACHE_TYPE_SIM, fd->path, FALSE, &mode);
533 if (cache_ensure_dir_exists(base, mode))
536 cd->path = cache_get_location(CACHE_TYPE_SIM, fd->path, TRUE, NULL);
537 if (cache_sim_data_save(cd))
539 filetime_set(cd->path, filetime(fd->path));
548 pc = g_new0(PanCacheData, 1);
549 memcpy(pc, fd, sizeof(FileData));
554 pw->cache_list = g_list_prepend(pw->cache_list, pc);
556 cache_loader_free(pw->cache_cl);
558 load_mask = CACHE_LOADER_NONE;
559 if (pw->size > LAYOUT_SIZE_THUMB_LARGE) load_mask |= CACHE_LOADER_DIMENSIONS;
560 if (pw->exif_date_enable) load_mask |= CACHE_LOADER_DATE;
561 pw->cache_cl = cache_loader_new(((FileData *)pc)->path, load_mask,
562 pan_cache_step_done_cb, pw);
563 return (pw->cache_cl == NULL);
566 /* This sync date function is optimized for lists with a common sort */
567 static void pan_cache_sync_date(PanWindow *pw, GList *list)
572 haystack = g_list_copy(pw->cache_list);
590 path = ((FileData *)pc)->path;
591 if (path && strcmp(path, fd->path) == 0)
593 if (pc->cd && pc->cd->have_date && pc->cd->date >= 0)
595 fd->date = pc->cd->date;
598 haystack = g_list_delete_link(haystack, needle);
603 needle = needle->next;
608 g_list_free(haystack);
612 *-----------------------------------------------------------------------------
614 *-----------------------------------------------------------------------------
617 static void pan_grid_clear(PanWindow *pw)
621 work = pw->list_grid;
629 g_list_free(pg->list);
633 g_list_free(pw->list_grid);
634 pw->list_grid = NULL;
636 pw->list = g_list_concat(pw->list, pw->list_static);
637 pw->list_static = NULL;
640 static void pan_grid_build(PanWindow *pw, gint width, gint height, gint grid_size)
653 l = g_list_length(pw->list);
657 total = (gdouble)width * (gdouble)height / (gdouble)l;
660 aw = (gdouble)width / s;
661 ah = (gdouble)height / s;
663 col = (gint)(sqrt((gdouble)l / grid_size) * width / height + 0.999);
664 col = CLAMP(col, 1, l / grid_size + 1);
665 row = (gint)((gdouble)l / grid_size / col);
666 if (row < 1) row = 1;
668 /* limit minimum size of grid so that a tile will always fit regardless of position */
669 cw = MAX((gint)ceil((gdouble)width / col), PAN_TILE_SIZE * 2);
670 ch = MAX((gint)ceil((gdouble)height / row), PAN_TILE_SIZE * 2);
675 if (debug) printf("intersect speedup grid is %dx%d, based on %d average per grid\n", col, row, grid_size);
677 for (j = 0; j < row; j++)
678 for (i = 0; i < col; i++)
680 if ((i + 1) * cw / 2 < width && (j + 1) * ch / 2 < height)
684 pg = g_new0(PanGrid, 1);
691 pw->list_grid = g_list_prepend(pw->list_grid, pg);
693 if (debug) printf("grid section: %d,%d (%dx%d)\n", pg->x, pg->y, pg->w, pg->h);
706 grid = pw->list_grid;
715 if (util_clip_region(pi->x, pi->y, pi->width, pi->height,
716 pg->x, pg->y, pg->w, pg->h,
719 pg->list = g_list_prepend(pg->list, pi);
724 work = pw->list_grid;
732 pg->list = g_list_reverse(pg->list);
735 pw->list_static = pw->list;
742 *-----------------------------------------------------------------------------
744 *-----------------------------------------------------------------------------
747 static void pan_item_free(PanItem *pi)
751 if (pi->pixbuf) g_object_unref(pi->pixbuf);
752 if (pi->fd) file_data_free(pi->fd);
760 static void pan_window_items_free(PanWindow *pw)
769 PanItem *pi = work->data;
775 g_list_free(pw->list);
778 g_list_free(pw->queue);
782 image_loader_free(pw->il);
785 thumb_loader_free(pw->tl);
789 pw->search_pi = NULL;
792 static PanItem *pan_item_new_thumb(PanWindow *pw, FileData *fd, gint x, gint y)
796 pi = g_new0(PanItem, 1);
797 pi->type = ITEM_THUMB;
801 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
802 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
808 pw->list = g_list_prepend(pw->list, pi);
813 static PanItem *pan_item_new_box(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
815 guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
816 guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
820 pi = g_new0(PanItem, 1);
828 pi->color_r = base_r;
829 pi->color_g = base_g;
830 pi->color_b = base_b;
831 pi->color_a = base_a;
833 pi->color2_r = bord_r;
834 pi->color2_g = bord_g;
835 pi->color2_b = bord_b;
836 pi->color2_a = bord_a;
837 pi->border = border_size;
839 pw->list = g_list_prepend(pw->list, pi);
844 static void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
848 if (!pi || pi->type != ITEM_BOX) return;
853 pi->width -= shadow[0];
854 pi->height -= shadow[0];
857 shadow = g_new0(gint, 2);
862 pi->height += offset;
868 static PanItem *pan_item_new_tri(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
869 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
870 guint8 r, guint8 g, guint8 b, guint8 a)
875 pi = g_new0(PanItem, 1);
876 pi->type = ITEM_TRIANGLE;
887 coord = g_new0(gint, 6);
897 pi->border = BORDER_NONE;
899 pw->list = g_list_prepend(pw->list, pi);
904 static void pan_item_tri_border(PanItem *pi, gint borders,
905 guint8 r, guint8 g, guint8 b, guint8 a)
907 if (!pi || pi->type != ITEM_TRIANGLE) return;
909 pi->border = borders;
917 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
921 layout = gtk_widget_create_pango_layout(widget, NULL);
923 if (pi->text_attr & TEXT_ATTR_MARKUP)
925 pango_layout_set_markup(layout, pi->text, -1);
929 if (pi->text_attr & TEXT_ATTR_BOLD ||
930 pi->text_attr & TEXT_ATTR_HEADING)
935 pal = pango_attr_list_new();
936 if (pi->text_attr & TEXT_ATTR_BOLD)
938 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
940 pa->end_index = G_MAXINT;
941 pango_attr_list_insert(pal, pa);
943 if (pi->text_attr & TEXT_ATTR_HEADING)
945 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
947 pa->end_index = G_MAXINT;
948 pango_attr_list_insert(pal, pa);
950 pango_layout_set_attributes(layout, pal);
951 pango_attr_list_unref(pal);
954 pango_layout_set_text(layout, pi->text, -1);
958 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
962 if (!pi || !pi->text || !widget) return;
964 layout = pan_item_text_layout(pi, widget);
965 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
966 g_object_unref(G_OBJECT(layout));
968 pi->width += pi->border * 2;
969 pi->height += pi->border * 2;
972 static PanItem *pan_item_new_text(PanWindow *pw, gint x, gint y, const gchar *text, TextAttrType attr,
974 guint8 r, guint8 g, guint8 b, guint8 a)
978 pi = g_new0(PanItem, 1);
979 pi->type = ITEM_TEXT;
982 pi->text = g_strdup(text);
983 pi->text_attr = attr;
992 pan_item_text_compute_size(pi, pw->imd->pr);
994 pw->list = g_list_prepend(pw->list, pi);
999 static void pan_item_set_key(PanItem *pi, const gchar *key)
1006 pi->key = g_strdup(key);
1010 static void pan_item_added(PanWindow *pw, PanItem *pi)
1013 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1016 static void pan_item_remove(PanWindow *pw, PanItem *pi)
1020 if (pw->click_pi == pi) pw->click_pi = NULL;
1021 if (pw->queue_pi == pi) pw->queue_pi = NULL;
1022 if (pw->search_pi == pi) pw->search_pi = NULL;
1023 pw->queue = g_list_remove(pw->queue, pi);
1025 pw->list = g_list_remove(pw->list, pi);
1026 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1030 static void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
1032 if (!pi || !child) return;
1034 if (pi->x + pi->width < child->x + child->width + border)
1035 pi->width = child->x + child->width + border - pi->x;
1037 if (pi->y + pi->height < child->y + child->height + border)
1038 pi->height = child->y + child->height + border - pi->y;
1041 static void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
1045 if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
1046 if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
1049 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
1056 if (!pi->fd) return;
1058 work = pw->cache_list;
1067 path = ((FileData *)pc)->path;
1069 if (pc->cd && pc->cd->dimensions &&
1070 path && strcmp(path, pi->fd->path) == 0)
1072 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
1073 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
1075 pw->cache_list = g_list_remove(pw->cache_list, pc);
1076 cache_sim_data_free(pc->cd);
1077 file_data_free((FileData *)pc);
1083 static PanItem *pan_item_new_image(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
1087 pi = g_new0(PanItem, 1);
1088 pi->type = ITEM_IMAGE;
1098 pi->color2_a = PAN_SHADOW_ALPHA / 2;
1100 pan_item_image_find_size(pw, pi, w, h);
1102 pw->list = g_list_prepend(pw->list, pi);
1107 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
1111 if (!key) return NULL;
1113 work = g_list_last(pw->list);
1119 if ((pi->type == type || type == ITEM_NONE) &&
1120 pi->key && strcmp(pi->key, key) == 0)
1126 work = g_list_last(pw->list_static);
1132 if ((pi->type == type || type == ITEM_NONE) &&
1133 pi->key && strcmp(pi->key, key) == 0)
1143 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1144 static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
1145 ItemType type, const gchar *path,
1146 gint ignore_case, gint partial)
1150 work = g_list_last(search_list);
1156 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1162 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
1164 else if (pi->fd->name)
1172 haystack = g_utf8_strdown(pi->fd->name, -1);
1173 match = (strstr(haystack, path) != NULL);
1178 if (strstr(pi->fd->name, path)) match = TRUE;
1181 else if (ignore_case)
1183 if (strcasecmp(path, pi->fd->name) == 0) match = TRUE;
1187 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
1191 if (match) list = g_list_prepend(list, pi);
1199 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1200 static GList *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1201 gint ignore_case, gint partial)
1205 if (!path) return NULL;
1206 if (partial && path[0] == '/') return NULL;
1208 list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
1209 list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
1211 return g_list_reverse(list);
1214 static PanItem *pan_item_find_by_coord_l(GList *list, ItemType type, gint x, gint y, const gchar *key)
1224 if ((pi->type == type || type == ITEM_NONE) &&
1225 x >= pi->x && x < pi->x + pi->width &&
1226 y >= pi->y && y < pi->y + pi->height &&
1227 (!key || (pi->key && strcmp(pi->key, key) == 0)))
1237 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y, const gchar *key)
1241 pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
1244 return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
1248 *-----------------------------------------------------------------------------
1250 *-----------------------------------------------------------------------------
1253 static gint islink_loop(const gchar *s)
1259 sl = path_from_utf8(s);
1261 if (lstat(sl, &st) == 0 && S_ISLNK(st.st_mode))
1266 buf = g_malloc(st.st_size + 1);
1267 l = readlink(sl, buf, st.st_size);
1268 if (l == st.st_size)
1272 parse_out_relatives(buf);
1275 parse_out_relatives(sl);
1279 if (strncmp(sl, buf, l) == 0 &&
1280 (sl[l] == '\0' || sl[l] == '/' || l == 1)) ret = TRUE;
1286 link_path = concat_dir_and_file(sl, buf);
1287 parse_out_relatives(link_path);
1289 if (strncmp(sl, link_path, l) == 0 &&
1290 (sl[l] == '\0' || sl[l] == '/' || l == 1)) ret = TRUE;
1304 static gint is_ignored(const gchar *s, gint ignore_symlinks)
1309 if (!lstat_utf8(s, &st)) return TRUE;
1312 /* normal filesystems have directories with some size or block allocation,
1313 * special filesystems (like linux /proc) set both to zero.
1314 * enable this check if you enable listing the root "/" folder
1316 if (st.st_size == 0 && st.st_blocks == 0) return TRUE;
1319 if (S_ISLNK(st.st_mode) && (ignore_symlinks || islink_loop(s))) return TRUE;
1321 n = filename_from_path(s);
1322 if (n && strcmp(n, GQVIEW_RC_DIR) == 0) return TRUE;
1327 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend,
1328 gint ignore_symlinks)
1330 GList *flist = NULL;
1331 GList *dlist = NULL;
1335 filelist_read(path, &flist, &dlist);
1336 if (sort != SORT_NONE)
1338 flist = filelist_sort(flist, sort, ascend);
1339 dlist = filelist_sort(dlist, sort, ascend);
1349 folders = g_list_remove(folders, fd);
1351 if (!is_ignored(fd->path, ignore_symlinks) &&
1352 filelist_read(fd->path, &flist, &dlist))
1354 if (sort != SORT_NONE)
1356 flist = filelist_sort(flist, sort, ascend);
1357 dlist = filelist_sort(dlist, sort, ascend);
1360 result = g_list_concat(result, flist);
1361 folders = g_list_concat(dlist, folders);
1370 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1378 list = pan_window_layout_list(path, SORT_NAME, TRUE, pw->ignore_symlinks);
1380 grid_size = (gint)sqrt((double)g_list_length(list));
1381 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1383 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1387 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1392 *width = PAN_FOLDER_BOX_BORDER * 2;
1393 *height = PAN_FOLDER_BOX_BORDER * 2;
1406 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1408 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1410 x += pi->width + PAN_THUMB_GAP;
1411 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1420 pi = pan_item_new_thumb(pw, fd, x, y);
1422 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1426 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1429 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1435 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1438 gint x1, y1, x2, y2;
1453 if (x1 > pi->x) x1 = pi->x;
1454 if (y1 > pi->y) y1 = pi->y;
1455 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1456 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1459 x1 -= PAN_FOLDER_BOX_BORDER;
1460 y1 -= PAN_FOLDER_BOX_BORDER;
1461 x2 += PAN_FOLDER_BOX_BORDER;
1462 y2 += PAN_FOLDER_BOX_BORDER;
1475 if (pi->type == ITEM_TRIANGLE && pi->data)
1489 if (width) *width = x2 - x1;
1490 if (height) *height = y2 - y1;
1493 typedef struct _FlowerGroup FlowerGroup;
1494 struct _FlowerGroup {
1507 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1511 work = group->items;
1529 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1530 gint *result_x, gint *result_y)
1536 radius = parent->circumference / (2*PI);
1537 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1539 a = 2*PI * group->diameter / parent->circumference;
1541 x = (gint)((double)radius * cos(parent->angle + a / 2));
1542 y = (gint)((double)radius * sin(parent->angle + a / 2));
1549 x += parent->width / 2;
1550 y += parent->height / 2;
1552 x -= group->width / 2;
1553 y -= group->height / 2;
1559 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1566 if (parent && parent->children)
1568 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1576 pan_window_layout_compute_folder_flower_move(group, x, y);
1581 gint px, py, gx, gy;
1582 gint x1, y1, x2, y2;
1584 px = parent->x + parent->width / 2;
1585 py = parent->y + parent->height / 2;
1587 gx = group->x + group->width / 2;
1588 gy = group->y + group->height / 2;
1593 x2 = MAX(px, gx + 5);
1594 y2 = MAX(py, gy + 5);
1596 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1597 px, py, gx, gy, gx + 5, gy + 5,
1599 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1603 pw->list = g_list_concat(group->items, pw->list);
1604 group->items = NULL;
1606 group->circumference = 0;
1607 work = group->children;
1615 group->circumference += child->diameter;
1618 work = g_list_last(group->children);
1626 pan_window_layout_compute_folder_flower_build(pw, child, group);
1629 g_list_free(group->children);
1633 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1646 if (!filelist_read(path, &f, &d)) return NULL;
1647 if (!f && !d) return NULL;
1649 f = filelist_sort(f, SORT_NAME, TRUE);
1650 d = filelist_sort(d, SORT_NAME, TRUE);
1652 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1653 PAN_TEXT_BORDER_SIZE,
1654 PAN_TEXT_COLOR, 255);
1656 y += pi_box->height;
1658 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1660 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1661 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1662 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1663 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1665 x += PAN_FOLDER_BOX_BORDER;
1666 y += PAN_FOLDER_BOX_BORDER;
1668 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1682 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1684 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1685 x += pi->width + PAN_THUMB_GAP;
1686 if (pi->height > y_height) y_height = pi->height;
1690 pi = pan_item_new_thumb(pw, fd, x, y);
1691 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1692 y_height = PAN_THUMB_SIZE;
1696 if (grid_count >= grid_size)
1700 y += y_height + PAN_THUMB_GAP;
1704 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1707 group = g_new0(FlowerGroup, 1);
1708 group->items = pw->list;
1711 group->width = pi_box->width;
1712 group->height = pi_box->y + pi_box->height;
1713 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1715 group->children = NULL;
1726 if (!is_ignored(fd->path, pw->ignore_symlinks))
1728 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1729 if (child) group->children = g_list_prepend(group->children, child);
1733 if (!f && !group->children)
1735 work = group->items;
1746 g_list_free(group->items);
1757 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1758 gint *width, gint *height,
1759 gint *scroll_x, gint *scroll_y)
1764 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1765 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1767 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1769 list = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1772 PanItem *pi = list->data;
1773 *scroll_x = pi->x + pi->width / 2;
1774 *scroll_y = pi->y + pi->height / 2;
1779 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1780 gint *x, gint *y, gint *level,
1782 gint *width, gint *height)
1790 if (!filelist_read(path, &f, &d)) return;
1791 if (!f && !d) return;
1793 f = filelist_sort(f, SORT_NAME, TRUE);
1794 d = filelist_sort(d, SORT_NAME, TRUE);
1796 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1798 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1799 PAN_TEXT_BORDER_SIZE,
1800 PAN_TEXT_COLOR, 255);
1802 *y += pi_box->height;
1804 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1806 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1807 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1808 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1809 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1811 *x += PAN_FOLDER_BOX_BORDER;
1812 *y += PAN_FOLDER_BOX_BORDER;
1823 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1825 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1826 *x += pi->width + PAN_THUMB_GAP;
1827 if (pi->height > y_height) y_height = pi->height;
1831 pi = pan_item_new_thumb(pw, fd, *x, *y);
1832 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1833 y_height = PAN_THUMB_SIZE;
1836 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1839 if (f) *y = pi_box->y + pi_box->height;
1851 if (!is_ignored(fd->path, pw->ignore_symlinks))
1853 *level = *level + 1;
1854 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1855 pi_box, width, height);
1856 *level = *level - 1;
1862 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1864 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1865 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1867 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1870 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1877 x = PAN_FOLDER_BOX_BORDER;
1878 y = PAN_FOLDER_BOX_BORDER;
1879 w = PAN_FOLDER_BOX_BORDER * 2;
1880 h = PAN_FOLDER_BOX_BORDER * 2;
1882 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1884 if (width) *width = w;
1885 if (height) *height = h;
1889 *-----------------------------------------------------------------------------
1891 *-----------------------------------------------------------------------------
1894 static void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
1900 gint x1, y1, x2, y2, x3, y3;
1905 while ((pi = pan_item_find_by_key(pw, ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
1907 if (!pi_day || pi_day->type != ITEM_BOX ||
1908 !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
1910 list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
1922 if (dot->type != ITEM_BOX || !dot->fd ||
1923 !dot->key || strcmp(dot->key, "dot") != 0)
1925 list = g_list_delete_link(list, node);
1933 grid = (gint)(sqrt(g_list_length(list)) + 0.5);
1935 x = pi_day->x + pi_day->width + 4;
1939 if (y + grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4 > pw->pr->image_height)
1941 y = pw->pr->image_height - (grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4);
1945 pbox = pan_item_new_box(pw, NULL, x, y, PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1946 PAN_CAL_POPUP_BORDER,
1947 PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA,
1948 PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
1949 pan_item_set_key(pbox, "day_bubble");
1956 buf = date_value_string(pi_day->fd->date, DATE_LENGTH_WEEK);
1957 plabel = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1958 PAN_TEXT_BORDER_SIZE,
1959 PAN_CAL_POPUP_TEXT_COLOR, 255);
1960 pan_item_set_key(plabel, "day_bubble");
1963 pan_item_size_by_item(pbox, plabel, 0);
1965 y += plabel->height;
1972 x += PAN_FOLDER_BOX_BORDER;
1973 y += PAN_FOLDER_BOX_BORDER;
1987 pimg = pan_item_new_thumb(pw, file_data_new_simple(dot->fd->path), x, y);
1988 pan_item_set_key(pimg, "day_bubble");
1990 pan_item_size_by_item(pbox, pimg, PAN_FOLDER_BOX_BORDER);
1995 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
2000 x = pbox->x + PAN_FOLDER_BOX_BORDER;
2001 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
2007 x1 = pi_day->x + pi_day->width - 8;
2010 y2 = pbox->y + MIN(42, pbox->height);
2012 y3 = MAX(pbox->y, y2 - 30);
2013 util_clip_triangle(x1, y1, x2, y2, x3, y3,
2016 pi = pan_item_new_tri(pw, NULL, x, y, w, h,
2017 x1, y1, x2, y2, x3, y3,
2018 PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA);
2019 pan_item_tri_border(pi, BORDER_1 | BORDER_3, PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
2020 pan_item_set_key(pi, "day_bubble");
2021 pan_item_added(pw, pi);
2023 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
2024 pan_item_added(pw, pbox);
2026 pan_layout_resize(pw);
2029 static void pan_window_layout_compute_calendar(PanWindow *pw, const gchar *path, gint *width, gint *height)
2045 list = pan_window_layout_list(path, SORT_NONE, TRUE, pw->ignore_symlinks);
2047 if (pw->cache_list && pw->exif_date_enable)
2049 pw->cache_list = filelist_sort(pw->cache_list, SORT_NAME, TRUE);
2050 list = filelist_sort(list, SORT_NAME, TRUE);
2051 pan_cache_sync_date(pw, list);
2054 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2055 list = filelist_sort(list, SORT_TIME, TRUE);
2068 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2076 if (day_max < count) day_max = count;
2080 if (debug) printf("biggest day contains %d images\n", day_max);
2082 grid = (gint)(sqrt((double)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
2083 day_width = MAX(PAN_CAL_DAY_WIDTH, grid);
2084 day_height = MAX(PAN_CAL_DAY_HEIGHT, grid);
2088 FileData *fd = list->data;
2090 year = date_value(fd->date, DATE_LENGTH_YEAR);
2091 month = date_value(fd->date, DATE_LENGTH_MONTH);
2094 work = g_list_last(list);
2097 FileData *fd = work->data;
2098 end_year = date_value(fd->date, DATE_LENGTH_YEAR);
2099 end_month = date_value(fd->date, DATE_LENGTH_MONTH);
2102 *width = PAN_FOLDER_BOX_BORDER * 2;
2103 *height = PAN_FOLDER_BOX_BORDER * 2;
2105 x = PAN_FOLDER_BOX_BORDER;
2106 y = PAN_FOLDER_BOX_BORDER;
2109 while (work && (year < end_year || (year == end_year && month <= end_month)))
2120 /* figure last second of this month */
2121 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
2124 /* anything to show this month? */
2125 if (!date_compare(((FileData *)(work->data))->date, dt, DATE_LENGTH_MONTH))
2136 days = date_value(dt, DATE_LENGTH_DAY);
2137 dt = date_to_time(year, month, 1);
2138 col = date_value(dt, DATE_LENGTH_WEEK);
2141 x = PAN_FOLDER_BOX_BORDER;
2143 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
2144 PAN_CAL_MONTH_BORDER,
2145 PAN_CAL_MONTH_COLOR, PAN_CAL_MONTH_ALPHA,
2146 PAN_CAL_MONTH_BORDER_COLOR, PAN_CAL_MONTH_ALPHA);
2147 buf = date_value_string(dt, DATE_LENGTH_MONTH);
2148 pi_text = pan_item_new_text(pw, x, y, buf,
2149 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2150 PAN_TEXT_BORDER_SIZE,
2151 PAN_CAL_MONTH_TEXT_COLOR, 255);
2153 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
2155 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
2157 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
2158 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
2160 for (day = 1; day <= days; day++)
2167 dt = date_to_time(year, month, day);
2169 fd = g_new0(FileData, 1);
2170 /* path and name must be non NULL, so make them an invalid filename */
2171 fd->path = g_strdup("//");
2174 pi_day = pan_item_new_box(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
2176 PAN_CAL_DAY_COLOR, PAN_CAL_DAY_ALPHA,
2177 PAN_CAL_DAY_BORDER_COLOR, PAN_CAL_DAY_ALPHA);
2178 pan_item_set_key(pi_day, "day");
2180 dx = x + PAN_CAL_DOT_GAP * 2;
2181 dy = y + PAN_CAL_DOT_GAP * 2;
2183 fd = (work) ? work->data : NULL;
2184 while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
2188 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
2190 PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
2192 pan_item_set_key(pi, "dot");
2194 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2195 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
2197 dx = x + PAN_CAL_DOT_GAP * 2;
2198 dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2200 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
2202 /* must keep all dots within respective day even if it gets ugly */
2203 dy = y + PAN_CAL_DOT_GAP * 2;
2209 fd = (work) ? work->data : NULL;
2216 pi_day->color_r = MAX(pi_day->color_r - 61 - n * 3, 80);
2217 pi_day->color_g = pi_day->color_r;
2219 buf = g_strdup_printf("( %d )", n);
2220 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2221 PAN_TEXT_BORDER_SIZE,
2222 PAN_CAL_DAY_TEXT_COLOR, 255);
2225 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
2226 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
2229 buf = g_strdup_printf("%d", day);
2230 pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2231 PAN_TEXT_BORDER_SIZE,
2232 PAN_CAL_DAY_TEXT_COLOR, 255);
2236 pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
2243 x = PAN_FOLDER_BOX_BORDER;
2244 y += PAN_CAL_DAY_HEIGHT;
2248 x += PAN_CAL_DAY_WIDTH;
2252 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
2253 y += PAN_FOLDER_BOX_BORDER * 2;
2264 *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
2269 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
2277 PanItem *pi_month = NULL;
2278 PanItem *pi_day = NULL;
2284 list = pan_window_layout_list(path, SORT_NONE, TRUE, pw->ignore_symlinks);
2286 if (pw->cache_list && pw->exif_date_enable)
2288 pw->cache_list = filelist_sort(pw->cache_list, SORT_NAME, TRUE);
2289 list = filelist_sort(list, SORT_NAME, TRUE);
2290 pan_cache_sync_date(pw, list);
2293 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2294 list = filelist_sort(list, SORT_TIME, TRUE);
2296 *width = PAN_FOLDER_BOX_BORDER * 2;
2297 *height = PAN_FOLDER_BOX_BORDER * 2;
2302 day_start = month_start;
2317 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2322 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
2328 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
2332 x = PAN_FOLDER_BOX_BORDER;
2335 y = PAN_FOLDER_BOX_BORDER;
2337 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
2338 pi = pan_item_new_text(pw, x, y, buf,
2339 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2340 PAN_TEXT_BORDER_SIZE,
2341 PAN_TEXT_COLOR, 255);
2345 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
2347 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2348 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2349 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2351 x += PAN_FOLDER_BOX_BORDER;
2352 y += PAN_FOLDER_BOX_BORDER;
2356 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
2368 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
2370 needle = needle->next;
2379 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
2380 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2381 PAN_TEXT_BORDER_SIZE,
2382 PAN_TEXT_COLOR, 255);
2387 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
2388 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2389 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2390 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2392 x += PAN_FOLDER_BOX_BORDER;
2393 y += PAN_FOLDER_BOX_BORDER;
2397 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2399 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
2400 if (pi->width > x_width) x_width = pi->width;
2401 y_height = pi->height;
2405 pi = pan_item_new_thumb(pw, fd, x, y);
2406 x_width = PAN_THUMB_SIZE;
2407 y_height = PAN_THUMB_SIZE;
2410 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
2411 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
2416 if (total > 0 && count < PAN_GROUP_MAX)
2418 y += y_height + PAN_THUMB_GAP;
2422 x += x_width + PAN_THUMB_GAP;
2432 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
2438 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
2439 gint *width, gint *height,
2440 gint *scroll_x, gint *scroll_y)
2442 pan_window_items_free(pw);
2446 case LAYOUT_SIZE_THUMB_DOTS:
2447 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2448 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2450 case LAYOUT_SIZE_THUMB_NONE:
2451 pw->thumb_size = PAN_THUMB_SIZE_NONE;
2452 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2454 case LAYOUT_SIZE_THUMB_SMALL:
2455 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2456 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2458 case LAYOUT_SIZE_THUMB_NORMAL:
2460 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2461 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2463 case LAYOUT_SIZE_THUMB_LARGE:
2464 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2465 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2467 case LAYOUT_SIZE_10:
2468 pw->image_size = 10;
2469 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2471 case LAYOUT_SIZE_25:
2472 pw->image_size = 25;
2473 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2475 case LAYOUT_SIZE_33:
2476 pw->image_size = 33;
2477 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2479 case LAYOUT_SIZE_50:
2480 pw->image_size = 50;
2481 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2483 case LAYOUT_SIZE_100:
2484 pw->image_size = 100;
2485 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2498 pan_window_layout_compute_grid(pw, path, width, height);
2500 case LAYOUT_FOLDERS_LINEAR:
2501 pan_window_layout_compute_folders_linear(pw, path, width, height);
2503 case LAYOUT_FOLDERS_FLOWER:
2504 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2506 case LAYOUT_CALENDAR:
2507 pan_window_layout_compute_calendar(pw, path, width, height);
2509 case LAYOUT_TIMELINE:
2510 pan_window_layout_compute_timeline(pw, path, width, height);
2516 if (debug) printf("computed %d objects\n", g_list_length(pw->list));
2519 static GList *pan_layout_intersect_l(GList *list, GList *item_list,
2520 gint x, gint y, gint width, gint height)
2528 gint rx, ry, rw, rh;
2533 if (util_clip_region(x, y, width, height,
2534 pi->x, pi->y, pi->width, pi->height,
2535 &rx, &ry, &rw, &rh))
2537 list = g_list_prepend(list, pi);
2544 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2550 grid = pw->list_grid;
2556 if (x < pg->x || x + width > pg->x + pg->w ||
2557 y < pg->y || y + height > pg->y + pg->h)
2563 list = pan_layout_intersect_l(list, pw->list, x, y, width, height);
2567 list = pan_layout_intersect_l(list, pg->list, x, y, width, height);
2571 list = pan_layout_intersect_l(list, pw->list_static, x, y, width, height);
2577 static void pan_layout_resize(PanWindow *pw)
2592 if (width < pi->x + pi->width) width = pi->x + pi->width;
2593 if (height < pi->y + pi->height) height = pi->y + pi->height;
2595 work = pw->list_static;
2603 if (width < pi->x + pi->width) width = pi->x + pi->width;
2604 if (height < pi->y + pi->height) height = pi->y + pi->height;
2607 width += PAN_FOLDER_BOX_BORDER * 2;
2608 height += PAN_FOLDER_BOX_BORDER * 2;
2610 pr = PIXBUF_RENDERER(pw->imd->pr);
2611 if (width < pr->window_width) width = pr->window_width;
2612 if (height < pr->window_width) height = pr->window_height;
2614 pixbuf_renderer_set_tiles_size(PIXBUF_RENDERER(pw->imd->pr), width, height);
2618 *-----------------------------------------------------------------------------
2620 *-----------------------------------------------------------------------------
2623 static gint pan_layout_queue_step(PanWindow *pw);
2626 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2628 PanWindow *pw = data;
2636 pw->queue_pi = NULL;
2640 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2641 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2644 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2648 thumb_loader_free(pw->tl);
2651 while (pan_layout_queue_step(pw));
2654 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2656 PanWindow *pw = data;
2664 pw->queue_pi = NULL;
2668 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2669 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2670 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2672 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2673 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2674 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2679 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2680 (GdkInterpType)zoom_quality);
2681 g_object_unref(tmp);
2685 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2689 image_loader_free(pw->il);
2692 while (pan_layout_queue_step(pw));
2696 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2697 guint width, guint height, gpointer data)
2699 PanWindow *pw = data;
2710 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2711 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2715 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2721 static gint pan_layout_queue_step(PanWindow *pw)
2725 if (!pw->queue) return FALSE;
2727 pi = pw->queue->data;
2728 pw->queue = g_list_remove(pw->queue, pi);
2731 if (!pw->queue_pi->fd)
2733 pw->queue_pi->queued = FALSE;
2734 pw->queue_pi = NULL;
2738 image_loader_free(pw->il);
2740 thumb_loader_free(pw->tl);
2743 if (pi->type == ITEM_IMAGE)
2745 pw->il = image_loader_new(pi->fd->path);
2747 if (pw->size != LAYOUT_SIZE_100)
2749 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2753 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2755 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2757 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2759 image_loader_free(pw->il);
2762 else if (pi->type == ITEM_THUMB)
2764 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2766 if (!pw->tl->standard_loader)
2768 /* The classic loader will recreate a thumbnail any time we
2769 * request a different size than what exists. This view will
2770 * almost never use the user configured sizes so disable cache.
2772 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2775 thumb_loader_set_callbacks(pw->tl,
2776 pan_layout_queue_thumb_done_cb,
2777 pan_layout_queue_thumb_done_cb,
2780 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2782 thumb_loader_free(pw->tl);
2786 pw->queue_pi->queued = FALSE;
2787 pw->queue_pi = NULL;
2791 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2793 if (!pi || pi->queued || pi->pixbuf) return;
2794 if (pw->size <= LAYOUT_SIZE_THUMB_NONE &&
2795 (!pi->key || strcmp(pi->key, "info") != 0) )
2801 pw->queue = g_list_prepend(pw->queue, pi);
2803 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2806 static gint pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
2807 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2809 PanWindow *pw = data;
2814 pixbuf_set_rect_fill(pixbuf,
2815 0, 0, width, height,
2816 PAN_BACKGROUND_COLOR, 255);
2818 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2820 gint rx, ry, rw, rh;
2822 if (util_clip_region(x, y, width, height,
2824 &rx, &ry, &rw, &rh))
2826 pixbuf_draw_rect_fill(pixbuf,
2827 rx - x, ry - y, rw, rh,
2828 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2831 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2833 gint rx, ry, rw, rh;
2835 if (util_clip_region(x, y, width, height,
2837 &rx, &ry, &rw, &rh))
2839 pixbuf_draw_rect_fill(pixbuf,
2840 rx - x, ry - y, rw, rh,
2841 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2845 list = pan_layout_intersect(pw, x, y, width, height);
2850 gint tx, ty, tw, th;
2851 gint rx, ry, rw, rh;
2858 if (pi->type == ITEM_THUMB && pi->pixbuf)
2860 tw = gdk_pixbuf_get_width(pi->pixbuf);
2861 th = gdk_pixbuf_get_height(pi->pixbuf);
2863 tx = pi->x + (pi->width - tw) / 2;
2864 ty = pi->y + (pi->height - th) / 2;
2866 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2868 if (util_clip_region(x, y, width, height,
2869 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2870 &rx, &ry, &rw, &rh))
2872 pixbuf_draw_shadow(pixbuf,
2873 rx - x, ry - y, rw, rh,
2874 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2876 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2881 if (util_clip_region(x, y, width, height,
2882 tx + tw, ty + PAN_SHADOW_OFFSET,
2883 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2884 &rx, &ry, &rw, &rh))
2886 pixbuf_draw_shadow(pixbuf,
2887 rx - x, ry - y, rw, rh,
2888 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2890 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2892 if (util_clip_region(x, y, width, height,
2893 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2894 &rx, &ry, &rw, &rh))
2896 pixbuf_draw_shadow(pixbuf,
2897 rx - x, ry - y, rw, rh,
2898 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2900 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2904 if (util_clip_region(x, y, width, height,
2906 &rx, &ry, &rw, &rh))
2908 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2911 1.0, 1.0, GDK_INTERP_NEAREST,
2915 if (util_clip_region(x, y, width, height,
2916 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2917 &rx, &ry, &rw, &rh))
2919 pixbuf_draw_rect_fill(pixbuf,
2920 rx - x, ry - y, rw, rh,
2921 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2923 if (util_clip_region(x, y, width, height,
2924 tx, ty, PAN_OUTLINE_THICKNESS, th,
2925 &rx, &ry, &rw, &rh))
2927 pixbuf_draw_rect_fill(pixbuf,
2928 rx - x, ry - y, rw, rh,
2929 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2931 if (util_clip_region(x, y, width, height,
2932 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2933 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2934 &rx, &ry, &rw, &rh))
2936 pixbuf_draw_rect_fill(pixbuf,
2937 rx - x, ry - y, rw, rh,
2938 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2940 if (util_clip_region(x, y, width, height,
2941 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2942 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2943 &rx, &ry, &rw, &rh))
2945 pixbuf_draw_rect_fill(pixbuf,
2946 rx - x, ry - y, rw, rh,
2947 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2950 else if (pi->type == ITEM_THUMB)
2952 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2953 th = pi->height - PAN_SHADOW_OFFSET * 2;
2954 tx = pi->x + PAN_SHADOW_OFFSET;
2955 ty = pi->y + PAN_SHADOW_OFFSET;
2957 if (util_clip_region(x, y, width, height,
2959 &rx, &ry, &rw, &rh))
2963 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2964 pixbuf_draw_rect_fill(pixbuf,
2965 rx - x, ry - y, rw, rh,
2967 PAN_SHADOW_ALPHA / d);
2970 pan_layout_queue(pw, pi);
2972 else if (pi->type == ITEM_IMAGE)
2974 if (util_clip_region(x, y, width, height,
2975 pi->x, pi->y, pi->width, pi->height,
2976 &rx, &ry, &rw, &rh))
2980 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2983 1.0, 1.0, GDK_INTERP_NEAREST,
2988 pixbuf_draw_rect_fill(pixbuf,
2989 rx - x, ry - y, rw, rh,
2990 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2991 pan_layout_queue(pw, pi);
2995 else if (pi->type == ITEM_BOX)
3009 if (pi->color_a > 254)
3011 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
3012 shadow[0], bh - shadow[0],
3013 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
3015 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
3016 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
3018 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
3020 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
3025 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
3026 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
3028 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
3030 PAN_SHADOW_COLOR, a);
3034 if (util_clip_region(x, y, width, height,
3035 pi->x, pi->y, bw, bh,
3036 &rx, &ry, &rw, &rh))
3038 pixbuf_draw_rect_fill(pixbuf,
3039 rx - x, ry - y, rw, rh,
3040 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3042 if (util_clip_region(x, y, width, height,
3043 pi->x, pi->y, bw, pi->border,
3044 &rx, &ry, &rw, &rh))
3046 pixbuf_draw_rect_fill(pixbuf,
3047 rx - x, ry - y, rw, rh,
3048 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3050 if (util_clip_region(x, y, width, height,
3051 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
3052 &rx, &ry, &rw, &rh))
3054 pixbuf_draw_rect_fill(pixbuf,
3055 rx - x, ry - y, rw, rh,
3056 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3058 if (util_clip_region(x, y, width, height,
3059 pi->x + bw - pi->border, pi->y + pi->border,
3060 pi->border, bh - pi->border * 2,
3061 &rx, &ry, &rw, &rh))
3063 pixbuf_draw_rect_fill(pixbuf,
3064 rx - x, ry - y, rw, rh,
3065 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3067 if (util_clip_region(x, y, width, height,
3068 pi->x, pi->y + bh - pi->border,
3070 &rx, &ry, &rw, &rh))
3072 pixbuf_draw_rect_fill(pixbuf,
3073 rx - x, ry - y, rw, rh,
3074 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3077 else if (pi->type == ITEM_TRIANGLE)
3079 if (util_clip_region(x, y, width, height,
3080 pi->x, pi->y, pi->width, pi->height,
3081 &rx, &ry, &rw, &rh) && pi->data)
3083 gint *coord = pi->data;
3084 pixbuf_draw_triangle(pixbuf,
3085 rx - x, ry - y, rw, rh,
3086 coord[0] - x, coord[1] - y,
3087 coord[2] - x, coord[3] - y,
3088 coord[4] - x, coord[5] - y,
3089 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3091 if (pi->border & BORDER_1)
3093 pixbuf_draw_line(pixbuf,
3094 rx - x, ry - y, rw, rh,
3095 coord[0] - x, coord[1] - y,
3096 coord[2] - x, coord[3] - y,
3097 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3099 if (pi->border & BORDER_2)
3101 pixbuf_draw_line(pixbuf,
3102 rx - x, ry - y, rw, rh,
3103 coord[2] - x, coord[3] - y,
3104 coord[4] - x, coord[5] - y,
3105 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3107 if (pi->border & BORDER_3)
3109 pixbuf_draw_line(pixbuf,
3110 rx - x, ry - y, rw, rh,
3111 coord[4] - x, coord[5] - y,
3112 coord[0] - x, coord[1] - y,
3113 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3117 else if (pi->type == ITEM_TEXT && pi->text)
3119 PangoLayout *layout;
3121 layout = pan_item_text_layout(pi, (GtkWidget *)pr);
3122 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
3123 pi->x - x + pi->border, pi->y - y + pi->border,
3124 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3125 g_object_unref(G_OBJECT(layout));
3131 if (x%512 == 0 && y%512 == 0)
3133 PangoLayout *layout;
3136 layout = gtk_widget_create_pango_layout((GtkWidget *)pr, NULL);
3138 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
3139 (x / pr->source_tile_width) +
3140 (y / pr->source_tile_height * (pr->image_width/pr->source_tile_width + 1)));
3141 pango_layout_set_text(layout, buf, -1);
3144 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr, 0, 0, 0, 0, 0, 255);
3146 g_object_unref(G_OBJECT(layout));
3153 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
3154 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
3156 PanWindow *pw = data;
3160 list = pan_layout_intersect(pw, x, y, width, height);
3169 if (pi->refcount > 0)
3173 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
3178 pw->queue = g_list_remove(pw->queue, pi);
3181 if (pw->queue_pi == pi) pw->queue_pi = NULL;
3184 g_object_unref(pi->pixbuf);
3196 *-----------------------------------------------------------------------------
3198 *-----------------------------------------------------------------------------
3201 static void pan_window_message(PanWindow *pw, const gchar *text)
3211 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
3215 work = pw->list_static;
3216 if (pw->layout == LAYOUT_CALENDAR)
3226 pi->type == ITEM_BOX &&
3227 pi->key && strcmp(pi->key, "dot") == 0)
3229 size += pi->fd->size;
3244 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
3246 size += pi->fd->size;
3252 ss = text_from_size_abrev(size);
3253 buf = g_strdup_printf(_("%d images, %s"), count, ss);
3255 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
3259 static void pan_warning_folder(const gchar *path, GtkWidget *parent)
3263 message = g_strdup_printf(_("The pan view does not support the folder \"%s\"."), path);
3264 warning_dialog(_("Folder not supported"), message,
3265 GTK_STOCK_DIALOG_INFO, parent);
3269 static void pan_window_zoom_limit(PanWindow *pw)
3275 case LAYOUT_SIZE_THUMB_DOTS:
3276 case LAYOUT_SIZE_THUMB_NONE:
3277 case LAYOUT_SIZE_THUMB_SMALL:
3278 case LAYOUT_SIZE_THUMB_NORMAL:
3280 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
3284 case LAYOUT_SIZE_THUMB_LARGE:
3287 case LAYOUT_SIZE_10:
3288 case LAYOUT_SIZE_25:
3291 case LAYOUT_SIZE_33:
3292 case LAYOUT_SIZE_50:
3293 case LAYOUT_SIZE_100:
3299 image_zoom_set_limits(pw->imd, min, 32.0);
3302 static gint pan_window_layout_update_idle_cb(gpointer data)
3304 PanWindow *pw = data;
3310 if (pw->size > LAYOUT_SIZE_THUMB_LARGE ||
3311 (pw->exif_date_enable && (pw->layout == LAYOUT_TIMELINE || pw->layout == LAYOUT_CALENDAR)))
3313 if (!pw->cache_list && !pw->cache_todo)
3315 pan_cache_fill(pw, pw->path);
3318 pan_window_message(pw, _("Reading image data..."));
3326 if (pw->cache_count == pw->cache_total)
3328 pan_window_message(pw, _("Sorting..."));
3330 else if (pw->cache_tick > 9)
3334 buf = g_strdup_printf("%s %d / %d", _("Reading image data..."),
3335 pw->cache_count, pw->cache_total);
3336 pan_window_message(pw, buf);
3342 if (pan_cache_step(pw)) return TRUE;
3349 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3351 pan_window_zoom_limit(pw);
3353 if (width > 0 && height > 0)
3357 if (debug) printf("Canvas size is %d x %d\n", width, height);
3359 pan_grid_build(pw, width, height, 1000);
3361 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
3362 PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
3363 pan_window_request_tile_cb,
3364 pan_window_dispose_tile_cb, pw, 1.0);
3366 if (scroll_x == 0 && scroll_y == 0)
3374 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
3377 pan_window_message(pw, NULL);
3383 static void pan_window_layout_update_idle(PanWindow *pw)
3385 if (pw->idle_id == -1)
3387 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3391 static void pan_window_layout_update(PanWindow *pw)
3393 pan_window_message(pw, _("Sorting images..."));
3394 pan_window_layout_update_idle(pw);
3397 static void pan_window_layout_set_path(PanWindow *pw, const gchar *path)
3401 if (strcmp(path, "/") == 0)
3403 pan_warning_folder(path, pw->window);
3408 pw->path = g_strdup(path);
3410 pan_window_layout_update(pw);
3414 *-----------------------------------------------------------------------------
3415 * pan window keyboard
3416 *-----------------------------------------------------------------------------
3419 static const gchar *pan_menu_click_path(PanWindow *pw)
3421 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3425 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3427 PanWindow *pw = data;
3429 gdk_window_get_origin(pw->imd->pr->window, x, y);
3430 popup_menu_position_clamp(menu, x, y, 0);
3433 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3435 PanWindow *pw = data;
3438 gint stop_signal = FALSE;
3445 pr = PIXBUF_RENDERER(pw->imd->pr);
3446 path = pan_menu_click_path(pw);
3448 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pw->imd->widget)));
3449 on_entry = (GTK_WIDGET_HAS_FOCUS(pw->path_entry) ||
3450 GTK_WIDGET_HAS_FOCUS(pw->search_entry));
3455 switch (event->keyval)
3457 case GDK_Left: case GDK_KP_Left:
3460 case GDK_Right: case GDK_KP_Right:
3463 case GDK_Up: case GDK_KP_Up:
3466 case GDK_Down: case GDK_KP_Down:
3469 case GDK_Page_Up: case GDK_KP_Page_Up:
3470 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
3472 case GDK_Page_Down: case GDK_KP_Page_Down:
3473 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
3475 case GDK_Home: case GDK_KP_Home:
3476 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
3478 case GDK_End: case GDK_KP_End:
3479 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
3482 stop_signal = FALSE;
3486 if (x != 0 || y!= 0)
3488 if (event->state & GDK_SHIFT_MASK)
3493 keyboard_scroll_calc(&x, &y, event);
3494 pixbuf_renderer_scroll(pr, x, y);
3498 if (stop_signal) return stop_signal;
3500 if (event->state & GDK_CONTROL_MASK)
3505 switch (event->keyval)
3538 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3541 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3544 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3547 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3550 if (path) info_window_new(path, NULL);
3553 pan_search_toggle_visible(pw, TRUE);
3556 pan_search_activate(pw);
3559 pan_window_close(pw);
3562 stop_signal = FALSE;
3566 if (n != -1 && path)
3568 if (!editor_window_flag_set(n))
3570 pan_fullscreen_toggle(pw, TRUE);
3572 start_editor_from_file(n, path);
3578 switch (event->keyval)
3583 pan_fullscreen_toggle(pw, TRUE);
3587 pan_search_toggle_visible(pw, FALSE);
3591 stop_signal = FALSE;
3595 if (stop_signal) return stop_signal;
3600 switch (event->keyval)
3602 case '+': case '=': case GDK_KP_Add:
3603 pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
3605 case '-': case GDK_KP_Subtract:
3606 pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
3608 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3609 pixbuf_renderer_zoom_set(pr, 1.0);
3612 pixbuf_renderer_zoom_set(pr, 2.0);
3615 pixbuf_renderer_zoom_set(pr, 3.0);
3618 pixbuf_renderer_zoom_set(pr, 4.0);
3621 pixbuf_renderer_zoom_set(pr, -4.0);
3624 pixbuf_renderer_zoom_set(pr, -3.0);
3627 pixbuf_renderer_zoom_set(pr, -2.0);
3632 pan_fullscreen_toggle(pw, FALSE);
3636 pan_overlay_toggle(pw);
3639 case GDK_Delete: case GDK_KP_Delete:
3643 menu = pan_popup_menu(pw);
3644 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
3645 pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3648 pan_search_toggle_visible(pw, TRUE);
3651 stop_signal = FALSE;
3661 *-----------------------------------------------------------------------------
3663 *-----------------------------------------------------------------------------
3666 typedef struct _PanTextAlignment PanTextAlignment;
3667 struct _PanTextAlignment {
3679 static PanTextAlignment *pan_text_alignment_new(PanWindow *pw, gint x, gint y, const gchar *key)
3681 PanTextAlignment *ta;
3683 ta = g_new0(PanTextAlignment, 1);
3690 ta->key = g_strdup(key);
3695 static PanItem *pan_text_alignment_add(PanTextAlignment *ta,
3696 const gchar *label, const gchar *text)
3702 item = pan_item_new_text(ta->pw, ta->x, ta->y, label,
3704 PAN_POPUP_TEXT_COLOR, 255);
3705 pan_item_set_key(item, ta->key);
3711 ta->column1 = g_list_append(ta->column1, item);
3715 item = pan_item_new_text(ta->pw, ta->x, ta->y, text,
3717 PAN_POPUP_TEXT_COLOR, 255);
3718 pan_item_set_key(item, ta->key);
3724 ta->column2 = g_list_append(ta->column2, item);
3729 static void pan_text_alignment_calc(PanTextAlignment *ta, PanItem *box)
3739 work1 = ta->column1;
3745 work1 = work1->next;
3747 if (p && p->width > cw1) cw1 = p->width;
3750 work2 = ta->column2;
3756 work2 = work2->next;
3758 if (p && p->width > cw2) cw2 = p->width;
3763 work1 = ta->column1;
3764 work2 = ta->column2;
3765 while (work1 && work2)
3773 work1 = work1->next;
3774 work2 = work2->next;
3780 pan_item_size_by_item(box, p1, PREF_PAD_BORDER);
3781 height = p1->height;
3785 p2->x = x + cw1 + PREF_PAD_SPACE;
3787 pan_item_size_by_item(box, p2, PREF_PAD_BORDER);
3788 if (height < p2->height) height = p2->height;
3791 if (!p1 && !p2) height = PREF_PAD_GROUP;
3797 static void pan_text_alignment_free(PanTextAlignment *ta)
3801 g_list_free(ta->column1);
3802 g_list_free(ta->column2);
3807 static void pan_info_add_exif(PanTextAlignment *ta, FileData *fd)
3814 exif = exif_read(fd->path);
3817 pan_text_alignment_add(ta, NULL, NULL);
3819 for (i = 0; i < bar_exif_key_count; i++)
3824 label = g_strdup_printf("%s:", exif_get_description_by_key(bar_exif_key_list[i]));
3825 text = exif_get_data_as_text(exif, bar_exif_key_list[i]);
3826 text = bar_exif_validate_text(text);
3827 pan_text_alignment_add(ta, label, text);
3832 work = g_list_last(history_list_get_by_key("exif_extras"));
3833 if (work) pan_text_alignment_add(ta, "---", NULL);
3843 label = g_strdup_printf("%s:", name);
3844 text = exif_get_data_as_text(exif, name);
3845 text = bar_exif_validate_text(text);
3846 pan_text_alignment_add(ta, label, text);
3854 static void pan_info_update(PanWindow *pw, PanItem *pi)
3856 PanTextAlignment *ta;
3860 gint x1, y1, x2, y2, x3, y3;
3863 if (pw->click_pi == pi) return;
3864 if (pi && !pi->fd) pi = NULL;
3866 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3871 if (debug) printf("info set to %s\n", pi->fd->path);
3873 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3875 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3876 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3877 pan_item_set_key(pbox, "info");
3879 if (pi->type == ITEM_THUMB && pi->pixbuf)
3881 w = gdk_pixbuf_get_width(pi->pixbuf);
3882 h = gdk_pixbuf_get_height(pi->pixbuf);
3884 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3885 y1 = pi->y + (pi->height - h) / 2 + 8;
3889 x1 = pi->x + pi->width - 8;
3897 util_clip_triangle(x1, y1, x2, y2, x3, y3,
3900 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3901 x1, y1, x2, y2, x3, y3,
3902 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3903 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3904 pan_item_set_key(p, "info");
3905 pan_item_added(pw, p);
3907 ta = pan_text_alignment_new(pw, pbox->x + PREF_PAD_BORDER, pbox->y + PREF_PAD_BORDER, "info");
3909 pan_text_alignment_add(ta, _("Filename:"), pi->fd->name);
3910 buf = remove_level_from_path(pi->fd->path);
3911 pan_text_alignment_add(ta, _("Location:"), buf);
3913 pan_text_alignment_add(ta, _("Date:"), text_from_time(pi->fd->date));
3914 buf = text_from_size(pi->fd->size);
3915 pan_text_alignment_add(ta, _("Size:"), buf);
3918 if (pw->info_includes_exif)
3920 pan_info_add_exif(ta, pi->fd);
3923 pan_text_alignment_calc(ta, pbox);
3924 pan_text_alignment_free(ta);
3926 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3927 pan_item_added(pw, pbox);
3929 if (pw->info_image_size > LAYOUT_SIZE_THUMB_NONE)
3932 if (image_load_dimensions(pi->fd->path, &iw, &ih))
3936 switch (pw->info_image_size)
3938 case LAYOUT_SIZE_10:
3941 case LAYOUT_SIZE_25:
3944 case LAYOUT_SIZE_33:
3947 case LAYOUT_SIZE_50:
3950 case LAYOUT_SIZE_100:
3955 iw = MAX(1, iw * scale / 100);
3956 ih = MAX(1, ih * scale / 100);
3958 pbox = pan_item_new_box(pw, NULL, pbox->x, pbox->y + pbox->height + 8, 10, 10,
3960 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3961 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3962 pan_item_set_key(pbox, "info");
3964 p = pan_item_new_image(pw, file_data_new_simple(pi->fd->path),
3965 pbox->x + PREF_PAD_BORDER, pbox->y + PREF_PAD_BORDER, iw, ih);
3966 pan_item_set_key(p, "info");
3967 pan_item_size_by_item(pbox, p, PREF_PAD_BORDER);
3969 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3970 pan_item_added(pw, pbox);
3974 pan_layout_resize(pw);
3979 *-----------------------------------------------------------------------------
3981 *-----------------------------------------------------------------------------
3984 static void pan_search_status(PanWindow *pw, const gchar *text)
3986 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3989 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3997 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3999 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
4000 if (!list) return FALSE;
4002 found = g_list_find(list, pw->click_pi);
4003 if (found && found->next)
4005 found = found->next;
4013 pan_info_update(pw, pi);
4014 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
4016 buf = g_strdup_printf("%s ( %d / %d )",
4017 (path[0] == '/') ? _("path found") : _("filename found"),
4018 g_list_index(list, pi) + 1,
4019 g_list_length(list));
4020 pan_search_status(pw, buf);
4028 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
4036 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
4038 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
4039 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
4044 needle = g_utf8_strdown(text, -1);
4045 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
4048 if (!list) return FALSE;
4050 found = g_list_find(list, pw->click_pi);
4051 if (found && found->next)
4053 found = found->next;
4061 pan_info_update(pw, pi);
4062 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
4064 buf = g_strdup_printf("%s ( %d / %d )",
4066 g_list_index(list, pi) + 1,
4067 g_list_length(list));
4068 pan_search_status(pw, buf);
4076 static gint valid_date_separator(gchar c)
4078 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
4081 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
4082 gint year, gint month, gint day,
4088 work = g_list_last(pw->list_static);
4096 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
4097 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
4101 tl = localtime(&pi->fd->date);
4106 match = (tl->tm_year == year - 1900);
4107 if (match && month >= 0) match = (tl->tm_mon == month - 1);
4108 if (match && day > 0) match = (tl->tm_mday == day);
4110 if (match) list = g_list_prepend(list, pi);
4115 return g_list_reverse(list);
4118 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
4134 if (!text) return FALSE;
4136 ptr = (gchar *)text;
4137 while (*ptr != '\0')
4139 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
4144 if (t == -1) return FALSE;
4146 if (!lt) return FALSE;
4148 if (valid_date_separator(*text))
4151 mptr = (gchar *)text;
4155 year = (gint)strtol(text, &mptr, 10);
4156 if (mptr == text) return FALSE;
4159 if (*mptr != '\0' && valid_date_separator(*mptr))
4164 month = strtol(mptr, &dptr, 10);
4167 if (valid_date_separator(*dptr))
4169 month = lt->tm_mon + 1;
4177 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
4181 day = strtol(dptr, &eptr, 10);
4191 year = lt->tm_year + 1900;
4193 else if (year < 100)
4202 month < -1 || month == 0 || month > 12 ||
4203 day < -1 || day == 0 || day > 31) return FALSE;
4205 t = date_to_time(year, month, day);
4206 if (t < 0) return FALSE;
4208 if (pw->layout == LAYOUT_CALENDAR)
4210 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
4216 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
4217 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
4222 found = g_list_find(list, pw->search_pi);
4223 if (found && found->next)
4225 found = found->next;
4236 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
4238 pan_info_update(pw, NULL);
4239 pan_calendar_update(pw, pi);
4240 image_scroll_to_point(pw->imd,
4241 pi->x + pi->width / 2,
4242 pi->y + pi->height / 2, 0.5, 0.5);
4246 pan_info_update(pw, pi);
4247 image_scroll_to_point(pw->imd,
4248 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
4254 buf = date_value_string(t, DATE_LENGTH_MONTH);
4259 buf = g_strdup_printf("%d %s", day, tmp);
4265 buf = date_value_string(t, DATE_LENGTH_YEAR);
4270 buf_count = g_strdup_printf("( %d / %d )",
4271 g_list_index(list, pi) + 1,
4272 g_list_length(list));
4276 buf_count = g_strdup_printf("(%s)", _("no match"));
4279 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
4282 pan_search_status(pw, message);
4290 static void pan_search_activate_cb(const gchar *text, gpointer data)
4292 PanWindow *pw = data;
4296 tab_completion_append_to_history(pw->search_entry, text);
4298 if (pan_search_by_path(pw, text)) return;
4300 if ((pw->layout == LAYOUT_TIMELINE ||
4301 pw->layout == LAYOUT_CALENDAR) &&
4302 pan_search_by_date(pw, text))
4307 if (pan_search_by_partial(pw, text)) return;
4309 pan_search_status(pw, _("no match"));
4312 static void pan_search_activate(PanWindow *pw)
4317 if (!GTK_WIDGET_VISIBLE(pw->search_box))
4319 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
4323 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->search_entry)));
4324 pan_search_activate_cb(text, pw);
4328 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
4330 PanWindow *pw = data;
4333 visible = GTK_WIDGET_VISIBLE(pw->search_box);
4334 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
4338 gtk_widget_hide(pw->search_box);
4339 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
4343 gtk_widget_show(pw->search_box);
4344 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
4345 gtk_widget_grab_focus(pw->search_entry);
4349 static void pan_search_toggle_visible(PanWindow *pw, gint enable)
4355 if (GTK_WIDGET_VISIBLE(pw->search_box))
4357 gtk_widget_grab_focus(pw->search_entry);
4361 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
4366 if (GTK_WIDGET_VISIBLE(pw->search_entry))
4368 if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
4370 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4372 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
4379 *-----------------------------------------------------------------------------
4380 * view window main routines
4381 *-----------------------------------------------------------------------------
4384 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
4386 PanWindow *pw = data;
4394 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
4395 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
4398 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "info");
4399 if (pi && event->button == 1)
4401 pan_info_update(pw, NULL);
4405 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
4408 switch (event->button)
4411 pan_info_update(pw, pi);
4413 if (!pi && pw->layout == LAYOUT_CALENDAR)
4415 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
4416 pan_calendar_update(pw, pi);
4422 pan_info_update(pw, pi);
4423 menu = pan_popup_menu(pw);
4424 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
4431 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
4434 PanWindow *pw = data;
4441 if (!(event->state & GDK_SHIFT_MASK))
4447 if (event->state & GDK_CONTROL_MASK)
4449 switch (event->direction)
4452 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
4453 (gint)event->x, (gint)event->y);
4455 case GDK_SCROLL_DOWN:
4456 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
4457 (gint)event->x, (gint)event->y);
4465 switch (event->direction)
4468 pixbuf_renderer_scroll(pr, 0, -h);
4470 case GDK_SCROLL_DOWN:
4471 pixbuf_renderer_scroll(pr, 0, h);
4473 case GDK_SCROLL_LEFT:
4474 pixbuf_renderer_scroll(pr, -w, 0);
4476 case GDK_SCROLL_RIGHT:
4477 pixbuf_renderer_scroll(pr, w, 0);
4485 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
4487 g_signal_connect(G_OBJECT(imd->pr), "clicked",
4488 G_CALLBACK(button_cb), pw);
4489 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
4490 G_CALLBACK(scroll_cb), pw);
4493 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
4495 PanWindow *pw = data;
4498 pw->imd = pw->imd_normal;
4501 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
4503 if (force_off && !pw->fs) return;
4507 fullscreen_stop(pw->fs);
4511 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
4512 pan_image_set_buttons(pw, pw->fs->imd);
4513 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
4514 G_CALLBACK(pan_window_key_press_cb), pw);
4516 pw->imd = pw->fs->imd;
4520 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
4522 PanWindow *pw = data;
4525 text = image_zoom_get_as_text(pw->imd);
4526 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4530 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
4532 PanWindow *pw = data;
4537 if (pr->scale == 0.0) return;
4539 pixbuf_renderer_get_visible_rect(pr, &rect);
4540 pixbuf_renderer_get_image_size(pr, &width, &height);
4542 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4543 adj->page_size = (gdouble)rect.width;
4544 adj->page_increment = adj->page_size / 2.0;
4545 adj->step_increment = 48.0 / pr->scale;
4547 adj->upper = MAX((gdouble)width, 1.0);
4548 adj->value = (gdouble)rect.x;
4550 pref_signal_block_data(pw->scrollbar_h, pw);
4551 gtk_adjustment_changed(adj);
4552 gtk_adjustment_value_changed(adj);
4553 pref_signal_unblock_data(pw->scrollbar_h, pw);
4555 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4556 adj->page_size = (gdouble)rect.height;
4557 adj->page_increment = adj->page_size / 2.0;
4558 adj->step_increment = 48.0 / pr->scale;
4560 adj->upper = MAX((gdouble)height, 1.0);
4561 adj->value = (gdouble)rect.y;
4563 pref_signal_block_data(pw->scrollbar_v, pw);
4564 gtk_adjustment_changed(adj);
4565 gtk_adjustment_value_changed(adj);
4566 pref_signal_unblock_data(pw->scrollbar_v, pw);
4569 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4571 PanWindow *pw = data;
4575 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4577 if (!pr->scale) return;
4579 x = (gint)gtk_range_get_value(range);
4581 pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
4584 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4586 PanWindow *pw = data;
4590 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4592 if (!pr->scale) return;
4594 y = (gint)gtk_range_get_value(range);
4596 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
4599 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4601 PanWindow *pw = data;
4603 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4604 pan_window_layout_update(pw);
4607 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4609 PanWindow *pw = data;
4611 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4612 pan_window_layout_update(pw);
4616 static void pan_window_date_toggle_cb(GtkWidget *button, gpointer data)
4618 PanWindow *pw = data;
4620 pw->exif_date_enable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4621 pan_window_layout_update(pw);
4625 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4627 PanWindow *pw = data;
4630 path = remove_trailing_slash(new_text);
4631 parse_out_relatives(path);
4635 warning_dialog(_("Folder not found"),
4636 _("The entered path is not a folder"),
4637 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4641 tab_completion_append_to_history(pw->path_entry, path);
4643 pan_window_layout_set_path(pw, path);
4649 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4651 PanWindow *pw = data;
4654 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4656 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4657 pan_window_entry_activate_cb(text, pw);
4661 static void pan_window_close(PanWindow *pw)
4663 pan_window_list = g_list_remove(pan_window_list, pw);
4665 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_EXIF_DATE, pw->exif_date_enable);
4666 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_INFO_IMAGE, pw->info_image_size);
4667 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_INFO_EXIF, pw->info_includes_exif);
4669 if (pw->idle_id != -1)
4671 g_source_remove(pw->idle_id);
4674 pan_fullscreen_toggle(pw, TRUE);
4675 gtk_widget_destroy(pw->window);
4677 pan_window_items_free(pw);
4685 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4687 PanWindow *pw = data;
4689 pan_window_close(pw);
4693 static void pan_window_new_real(const gchar *path)
4702 GdkGeometry geometry;
4704 pw = g_new0(PanWindow, 1);
4706 pw->path = g_strdup(path);
4707 pw->layout = LAYOUT_TIMELINE;
4708 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4709 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4710 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4712 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_EXIF_DATE, &pw->exif_date_enable))
4714 pw->exif_date_enable = FALSE;
4716 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_INFO_IMAGE, &pw->info_image_size))
4718 pw->info_image_size = LAYOUT_SIZE_THUMB_NONE;
4720 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_INFO_EXIF, &pw->info_includes_exif))
4722 pw->info_includes_exif = TRUE;
4725 pw->ignore_symlinks = TRUE;
4728 pw->list_static = NULL;
4729 pw->list_grid = NULL;
4732 pw->overlay_id = -1;
4735 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4737 geometry.min_width = 8;
4738 geometry.min_height = 8;
4739 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4741 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4742 gtk_window_set_title (GTK_WINDOW(pw->window), _("Pan View - GQview"));
4743 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4744 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4746 window_set_icon(pw->window, NULL, NULL);
4748 vbox = gtk_vbox_new(FALSE, 0);
4749 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4750 gtk_widget_show(vbox);
4752 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4754 pref_spacer(box, 0);
4755 pref_label_new(box, _("Location:"));
4756 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view_path", -1,
4757 pan_window_entry_activate_cb, pw);
4758 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4759 G_CALLBACK(pan_window_entry_change_cb), pw);
4760 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4761 gtk_widget_show(combo);
4763 combo = gtk_combo_box_new_text();
4764 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4765 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4766 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4767 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4768 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4770 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4771 g_signal_connect(G_OBJECT(combo), "changed",
4772 G_CALLBACK(pan_window_layout_change_cb), pw);
4773 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4774 gtk_widget_show(combo);
4776 combo = gtk_combo_box_new_text();
4777 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4778 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4779 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4780 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4781 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4782 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4783 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4784 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4785 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4786 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4788 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4789 g_signal_connect(G_OBJECT(combo), "changed",
4790 G_CALLBACK(pan_window_layout_size_cb), pw);
4791 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4792 gtk_widget_show(combo);
4794 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4795 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4796 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4798 pw->imd = image_new(TRUE);
4799 pw->imd_normal = pw->imd;
4801 g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
4802 G_CALLBACK(pan_window_image_zoom_cb), pw);
4803 g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
4804 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4806 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4807 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4808 gtk_widget_show(GTK_WIDGET(pw->imd->widget));
4810 pan_window_dnd_init(pw);
4812 pan_image_set_buttons(pw, pw->imd);
4814 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4815 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4816 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4817 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4818 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4819 gtk_widget_show(pw->scrollbar_h);
4821 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4822 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4823 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4824 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4825 0, GTK_FILL | GTK_EXPAND, 0, 0);
4826 gtk_widget_show(pw->scrollbar_v);
4830 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4831 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4833 pref_spacer(pw->search_box, 0);
4834 pref_label_new(pw->search_box, _("Find:"));
4836 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4837 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4838 gtk_widget_show(hbox);
4840 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4841 pan_search_activate_cb, pw);
4842 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4843 gtk_widget_show(combo);
4845 pw->search_label = gtk_label_new("");
4846 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4847 gtk_widget_show(pw->search_label);
4851 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4853 frame = gtk_frame_new(NULL);
4854 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4855 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4856 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4857 gtk_widget_show(frame);
4859 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4860 gtk_container_add(GTK_CONTAINER(frame), hbox);
4861 gtk_widget_show(hbox);
4863 pref_spacer(hbox, 0);
4864 pw->label_message = pref_label_new(hbox, "");
4866 frame = gtk_frame_new(NULL);
4867 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4868 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4869 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4870 gtk_widget_show(frame);
4872 pw->label_zoom = gtk_label_new("");
4873 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4874 gtk_widget_show(pw->label_zoom);
4877 pw->date_button = pref_checkbox_new(box, _("Use Exif date"), pw->exif_date_enable,
4878 G_CALLBACK(pan_window_date_toggle_cb), pw);
4881 pw->search_button = gtk_toggle_button_new();
4882 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4883 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4884 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4885 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4886 gtk_widget_show(hbox);
4887 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4888 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4889 gtk_widget_show(pw->search_button_arrow);
4890 pref_label_new(hbox, _("Find"));
4892 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4893 gtk_widget_show(pw->search_button);
4894 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4895 G_CALLBACK(pan_search_toggle_cb), pw);
4897 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4898 G_CALLBACK(pan_window_delete_cb), pw);
4899 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4900 G_CALLBACK(pan_window_key_press_cb), pw);
4902 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4904 pan_window_layout_update(pw);
4906 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4907 gtk_widget_show(pw->window);
4909 pan_window_list = g_list_append(pan_window_list, pw);
4913 *-----------------------------------------------------------------------------
4914 * peformance warnings
4915 *-----------------------------------------------------------------------------
4918 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4922 generic_dialog_close(gd);
4924 pan_window_new_real(path);
4928 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4932 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4933 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4936 static gint pan_warning(const gchar *path)
4942 GtkWidget *ct_button;
4945 if (path && strcmp(path, "/") == 0)
4947 pan_warning_folder(path, NULL);
4951 if (enable_thumb_caching &&
4952 thumbnail_spec_standard) return FALSE;
4954 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4955 if (hide_dlg) return FALSE;
4957 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4959 gd->data = g_strdup(path);
4960 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4961 pan_warning_ok_cb, TRUE);
4963 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4964 _("Pan view performance may be poor."),
4965 _("To improve performance of thumbnails in the pan view the"
4966 " following options can be enabled. Note that both options"
4967 " must be enabled to notice a change in performance."));
4969 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4970 pref_spacer(group, PREF_PAD_INDENT);
4971 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4973 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4974 enable_thumb_caching, &enable_thumb_caching);
4975 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4976 thumbnail_spec_standard, &thumbnail_spec_standard);
4977 pref_checkbox_link_sensitivity(ct_button, button);
4981 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4982 G_CALLBACK(pan_warning_hide_cb), NULL);
4984 gtk_widget_show(gd->dialog);
4991 *-----------------------------------------------------------------------------
4993 *-----------------------------------------------------------------------------
4996 void pan_window_new(const gchar *path)
4998 if (pan_warning(path)) return;
5000 pan_window_new_real(path);
5004 *-----------------------------------------------------------------------------
5006 *-----------------------------------------------------------------------------
5009 #define INFO_IMAGE_SIZE_KEY "image_size_data"
5012 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
5014 PanWindow *pw = data;
5017 path = pan_menu_click_path(pw);
5020 pan_fullscreen_toggle(pw, TRUE);
5021 view_window_new(path);
5025 static void pan_edit_cb(GtkWidget *widget, gpointer data)
5031 pw = submenu_item_get_data(widget);
5032 n = GPOINTER_TO_INT(data);
5035 path = pan_menu_click_path(pw);
5038 if (!editor_window_flag_set(n))
5040 pan_fullscreen_toggle(pw, TRUE);
5042 start_editor_from_file(n, path);
5046 static void pan_info_cb(GtkWidget *widget, gpointer data)
5048 PanWindow *pw = data;
5051 path = pan_menu_click_path(pw);
5052 if (path) info_window_new(path, NULL);
5055 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
5057 PanWindow *pw = data;
5059 image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
5062 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
5064 PanWindow *pw = data;
5066 image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
5069 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
5071 PanWindow *pw = data;
5073 image_zoom_set(pw->imd, 1.0);
5076 static void pan_copy_cb(GtkWidget *widget, gpointer data)
5078 PanWindow *pw = data;
5081 path = pan_menu_click_path(pw);
5082 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
5085 static void pan_move_cb(GtkWidget *widget, gpointer data)
5087 PanWindow *pw = data;
5090 path = pan_menu_click_path(pw);
5091 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
5094 static void pan_rename_cb(GtkWidget *widget, gpointer data)
5096 PanWindow *pw = data;
5099 path = pan_menu_click_path(pw);
5100 if (path) file_util_rename(path, NULL, pw->imd->widget);
5103 static void pan_delete_cb(GtkWidget *widget, gpointer data)
5105 PanWindow *pw = data;
5108 path = pan_menu_click_path(pw);
5109 if (path) file_util_delete(path, NULL, pw->imd->widget);
5112 static void pan_exif_date_toggle_cb(GtkWidget *widget, gpointer data)
5114 PanWindow *pw = data;
5116 pw->exif_date_enable = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
5117 pan_window_layout_update(pw);
5120 static void pan_info_toggle_exif_cb(GtkWidget *widget, gpointer data)
5122 PanWindow *pw = data;
5124 pw->info_includes_exif = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
5125 /* fixme: sync info now */
5128 static void pan_info_toggle_image_cb(GtkWidget *widget, gpointer data)
5130 PanWindow *pw = data;
5132 pw->info_image_size = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), INFO_IMAGE_SIZE_KEY));
5133 /* fixme: sync info now */
5136 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
5138 PanWindow *pw = data;
5140 pan_fullscreen_toggle(pw, FALSE);
5143 static void pan_close_cb(GtkWidget *widget, gpointer data)
5145 PanWindow *pw = data;
5147 pan_window_close(pw);
5150 static GtkWidget *pan_popup_menu(PanWindow *pw)
5157 active = (pw->click_pi != NULL);
5159 menu = popup_menu_short_lived();
5161 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
5162 G_CALLBACK(pan_zoom_in_cb), pw);
5163 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
5164 G_CALLBACK(pan_zoom_out_cb), pw);
5165 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
5166 G_CALLBACK(pan_zoom_1_1_cb), pw);
5167 menu_item_add_divider(menu);
5169 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
5170 gtk_widget_set_sensitive(item, active);
5172 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
5173 G_CALLBACK(pan_info_cb), pw);
5175 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
5176 G_CALLBACK(pan_new_window_cb), pw);
5178 menu_item_add_divider(menu);
5179 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
5180 G_CALLBACK(pan_copy_cb), pw);
5181 menu_item_add_sensitive(menu, _("_Move..."), active,
5182 G_CALLBACK(pan_move_cb), pw);
5183 menu_item_add_sensitive(menu, _("_Rename..."), active,
5184 G_CALLBACK(pan_rename_cb), pw);
5185 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
5186 G_CALLBACK(pan_delete_cb), pw);
5188 menu_item_add_divider(menu);
5189 item = menu_item_add_check(menu, _("Sort by E_xif date"), pw->exif_date_enable,
5190 G_CALLBACK(pan_exif_date_toggle_cb), pw);
5191 gtk_widget_set_sensitive(item, (pw->layout == LAYOUT_TIMELINE || pw->layout == LAYOUT_CALENDAR));
5193 menu_item_add_divider(menu);
5195 menu_item_add_check(menu, _("_Show Exif information"), pw->info_includes_exif,
5196 G_CALLBACK(pan_info_toggle_exif_cb), pw);
5197 item = menu_item_add(menu, _("Show im_age"), NULL, NULL);
5198 submenu = gtk_menu_new();
5199 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
5201 item = menu_item_add_check(submenu, _("_None"), (pw->info_image_size == LAYOUT_SIZE_THUMB_NONE),
5202 G_CALLBACK(pan_info_toggle_image_cb), pw);
5203 g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(LAYOUT_SIZE_THUMB_NONE));
5205 item = menu_item_add_check(submenu, _("_Full size"), (pw->info_image_size == LAYOUT_SIZE_100),
5206 G_CALLBACK(pan_info_toggle_image_cb), pw);
5207 g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(LAYOUT_SIZE_100));
5209 item = menu_item_add_check(submenu, _("1:2 (50%)"), (pw->info_image_size == LAYOUT_SIZE_50),
5210 G_CALLBACK(pan_info_toggle_image_cb), pw);
5211 g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(LAYOUT_SIZE_50));
5213 item = menu_item_add_check(submenu, _("1:3 (33%)"), (pw->info_image_size == LAYOUT_SIZE_33),
5214 G_CALLBACK(pan_info_toggle_image_cb), pw);
5215 g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(LAYOUT_SIZE_33));
5217 item = menu_item_add_check(submenu, _("1:4 (25%)"), (pw->info_image_size == LAYOUT_SIZE_25),
5218 G_CALLBACK(pan_info_toggle_image_cb), pw);
5219 g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(LAYOUT_SIZE_25));
5221 item = menu_item_add_check(submenu, _("1:10 (10%)"), (pw->info_image_size == LAYOUT_SIZE_10),
5222 G_CALLBACK(pan_info_toggle_image_cb), pw);
5223 g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(LAYOUT_SIZE_10));
5227 menu_item_add_divider(menu);
5231 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
5235 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
5238 menu_item_add_divider(menu);
5239 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
5245 *-----------------------------------------------------------------------------
5247 *-----------------------------------------------------------------------------
5250 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
5252 GtkSelectionData *selection_data, guint info,
5253 guint time, gpointer data)
5255 PanWindow *pw = data;
5257 if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
5259 if (info == TARGET_URI_LIST)
5263 list = uri_list_from_text((gchar *)selection_data->data, TRUE);
5264 if (list && isdir((gchar *)list->data))
5266 gchar *path = list->data;
5268 pan_window_layout_set_path(pw, path);
5271 path_list_free(list);
5275 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
5276 GtkSelectionData *selection_data, guint info,
5277 guint time, gpointer data)
5279 PanWindow *pw = data;
5282 path = pan_menu_click_path(pw);
5292 case TARGET_URI_LIST:
5295 case TARGET_TEXT_PLAIN:
5300 list = g_list_append(NULL, (gchar *)path);
5301 text = uri_text_from_list(list, &len, plain_text);
5305 gtk_selection_data_set (selection_data, selection_data->target,
5306 8, (guchar *)text, len);
5312 gtk_selection_data_set (selection_data, selection_data->target,
5317 static void pan_window_dnd_init(PanWindow *pw)
5321 widget = pw->imd->pr;
5323 gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
5324 dnd_file_drag_types, dnd_file_drag_types_count,
5325 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
5326 g_signal_connect(G_OBJECT(widget), "drag_data_get",
5327 G_CALLBACK(pan_window_set_dnd_data), pw);
5329 gtk_drag_dest_set(widget,
5330 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
5331 dnd_file_drop_types, dnd_file_drop_types_count,
5332 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
5333 g_signal_connect(G_OBJECT(widget), "drag_data_received",
5334 G_CALLBACK(pan_window_get_dnd_data), pw);
5338 *-----------------------------------------------------------------------------
5339 * maintenance (for rename, move, remove)
5340 *-----------------------------------------------------------------------------