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_includes_image"
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_includes_image;
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 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 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
2122 days = date_value(dt, DATE_LENGTH_DAY);
2123 dt = date_to_time(year, month, 1);
2124 col = date_value(dt, DATE_LENGTH_WEEK);
2127 x = PAN_FOLDER_BOX_BORDER;
2129 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
2130 PAN_CAL_MONTH_BORDER,
2131 PAN_CAL_MONTH_COLOR, PAN_CAL_MONTH_ALPHA,
2132 PAN_CAL_MONTH_BORDER_COLOR, PAN_CAL_MONTH_ALPHA);
2133 buf = date_value_string(dt, DATE_LENGTH_MONTH);
2134 pi_text = pan_item_new_text(pw, x, y, buf,
2135 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2136 PAN_TEXT_BORDER_SIZE,
2137 PAN_CAL_MONTH_TEXT_COLOR, 255);
2139 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
2141 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
2143 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
2144 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
2146 for (day = 1; day <= days; day++)
2153 dt = date_to_time(year, month, day);
2155 fd = g_new0(FileData, 1);
2156 /* path and name must be non NULL, so make them an invalid filename */
2157 fd->path = g_strdup("//");
2160 pi_day = pan_item_new_box(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
2162 PAN_CAL_DAY_COLOR, PAN_CAL_DAY_ALPHA,
2163 PAN_CAL_DAY_BORDER_COLOR, PAN_CAL_DAY_ALPHA);
2164 pan_item_set_key(pi_day, "day");
2166 dx = x + PAN_CAL_DOT_GAP * 2;
2167 dy = y + PAN_CAL_DOT_GAP * 2;
2169 fd = (work) ? work->data : NULL;
2170 while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
2174 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
2176 PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
2178 pan_item_set_key(pi, "dot");
2180 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2181 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
2183 dx = x + PAN_CAL_DOT_GAP * 2;
2184 dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2186 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
2188 /* must keep all dots within respective day even if it gets ugly */
2189 dy = y + PAN_CAL_DOT_GAP * 2;
2195 fd = (work) ? work->data : NULL;
2202 pi_day->color_r = MAX(pi_day->color_r - 61 - n * 3, 80);
2203 pi_day->color_g = pi_day->color_r;
2205 buf = g_strdup_printf("( %d )", n);
2206 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2207 PAN_TEXT_BORDER_SIZE,
2208 PAN_CAL_DAY_TEXT_COLOR, 255);
2211 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
2212 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
2215 buf = g_strdup_printf("%d", day);
2216 pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2217 PAN_TEXT_BORDER_SIZE,
2218 PAN_CAL_DAY_TEXT_COLOR, 255);
2222 pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
2229 x = PAN_FOLDER_BOX_BORDER;
2230 y += PAN_CAL_DAY_HEIGHT;
2234 x += PAN_CAL_DAY_WIDTH;
2238 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
2239 y += PAN_FOLDER_BOX_BORDER * 2;
2250 *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
2255 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
2263 PanItem *pi_month = NULL;
2264 PanItem *pi_day = NULL;
2270 list = pan_window_layout_list(path, SORT_NONE, TRUE, pw->ignore_symlinks);
2272 if (pw->cache_list && pw->exif_date_enable)
2274 pw->cache_list = filelist_sort(pw->cache_list, SORT_NAME, TRUE);
2275 list = filelist_sort(list, SORT_NAME, TRUE);
2276 pan_cache_sync_date(pw, list);
2279 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2280 list = filelist_sort(list, SORT_TIME, TRUE);
2282 *width = PAN_FOLDER_BOX_BORDER * 2;
2283 *height = PAN_FOLDER_BOX_BORDER * 2;
2288 day_start = month_start;
2303 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2308 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
2314 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
2318 x = PAN_FOLDER_BOX_BORDER;
2321 y = PAN_FOLDER_BOX_BORDER;
2323 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
2324 pi = pan_item_new_text(pw, x, y, buf,
2325 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2326 PAN_TEXT_BORDER_SIZE,
2327 PAN_TEXT_COLOR, 255);
2331 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
2333 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2334 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2335 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2337 x += PAN_FOLDER_BOX_BORDER;
2338 y += PAN_FOLDER_BOX_BORDER;
2342 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
2354 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
2356 needle = needle->next;
2365 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
2366 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2367 PAN_TEXT_BORDER_SIZE,
2368 PAN_TEXT_COLOR, 255);
2373 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
2374 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2375 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2376 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2378 x += PAN_FOLDER_BOX_BORDER;
2379 y += PAN_FOLDER_BOX_BORDER;
2383 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2385 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
2386 if (pi->width > x_width) x_width = pi->width;
2387 y_height = pi->height;
2391 pi = pan_item_new_thumb(pw, fd, x, y);
2392 x_width = PAN_THUMB_SIZE;
2393 y_height = PAN_THUMB_SIZE;
2396 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
2397 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
2402 if (total > 0 && count < PAN_GROUP_MAX)
2404 y += y_height + PAN_THUMB_GAP;
2408 x += x_width + PAN_THUMB_GAP;
2418 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
2424 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
2425 gint *width, gint *height,
2426 gint *scroll_x, gint *scroll_y)
2428 pan_window_items_free(pw);
2432 case LAYOUT_SIZE_THUMB_DOTS:
2433 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2434 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2436 case LAYOUT_SIZE_THUMB_NONE:
2437 pw->thumb_size = PAN_THUMB_SIZE_NONE;
2438 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2440 case LAYOUT_SIZE_THUMB_SMALL:
2441 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2442 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2444 case LAYOUT_SIZE_THUMB_NORMAL:
2446 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2447 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2449 case LAYOUT_SIZE_THUMB_LARGE:
2450 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2451 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2453 case LAYOUT_SIZE_10:
2454 pw->image_size = 10;
2455 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2457 case LAYOUT_SIZE_25:
2458 pw->image_size = 25;
2459 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2461 case LAYOUT_SIZE_33:
2462 pw->image_size = 33;
2463 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2465 case LAYOUT_SIZE_50:
2466 pw->image_size = 50;
2467 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2469 case LAYOUT_SIZE_100:
2470 pw->image_size = 100;
2471 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2484 pan_window_layout_compute_grid(pw, path, width, height);
2486 case LAYOUT_FOLDERS_LINEAR:
2487 pan_window_layout_compute_folders_linear(pw, path, width, height);
2489 case LAYOUT_FOLDERS_FLOWER:
2490 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2492 case LAYOUT_CALENDAR:
2493 pan_window_layout_compute_calendar(pw, path, width, height);
2495 case LAYOUT_TIMELINE:
2496 pan_window_layout_compute_timeline(pw, path, width, height);
2502 if (debug) printf("computed %d objects\n", g_list_length(pw->list));
2505 static GList *pan_layout_intersect_l(GList *list, GList *item_list,
2506 gint x, gint y, gint width, gint height)
2514 gint rx, ry, rw, rh;
2519 if (util_clip_region(x, y, width, height,
2520 pi->x, pi->y, pi->width, pi->height,
2521 &rx, &ry, &rw, &rh))
2523 list = g_list_prepend(list, pi);
2530 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2536 grid = pw->list_grid;
2542 if (x < pg->x || x + width > pg->x + pg->w ||
2543 y < pg->y || y + height > pg->y + pg->h)
2549 list = pan_layout_intersect_l(list, pw->list, x, y, width, height);
2553 list = pan_layout_intersect_l(list, pg->list, x, y, width, height);
2557 list = pan_layout_intersect_l(list, pw->list_static, x, y, width, height);
2563 static void pan_layout_resize(PanWindow *pw)
2578 if (width < pi->x + pi->width) width = pi->x + pi->width;
2579 if (height < pi->y + pi->height) height = pi->y + pi->height;
2581 work = pw->list_static;
2589 if (width < pi->x + pi->width) width = pi->x + pi->width;
2590 if (height < pi->y + pi->height) height = pi->y + pi->height;
2593 width += PAN_FOLDER_BOX_BORDER * 2;
2594 height += PAN_FOLDER_BOX_BORDER * 2;
2596 pr = PIXBUF_RENDERER(pw->imd->pr);
2597 if (width < pr->window_width) width = pr->window_width;
2598 if (height < pr->window_width) height = pr->window_height;
2600 pixbuf_renderer_set_tiles_size(PIXBUF_RENDERER(pw->imd->pr), width, height);
2604 *-----------------------------------------------------------------------------
2606 *-----------------------------------------------------------------------------
2609 static gint pan_layout_queue_step(PanWindow *pw);
2612 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2614 PanWindow *pw = data;
2622 pw->queue_pi = NULL;
2626 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2627 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2630 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2634 thumb_loader_free(pw->tl);
2637 while (pan_layout_queue_step(pw));
2640 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2642 PanWindow *pw = data;
2650 pw->queue_pi = NULL;
2654 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2655 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2656 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2658 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2659 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2660 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2665 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2666 (GdkInterpType)zoom_quality);
2667 g_object_unref(tmp);
2671 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2675 image_loader_free(pw->il);
2678 while (pan_layout_queue_step(pw));
2682 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2683 guint width, guint height, gpointer data)
2685 PanWindow *pw = data;
2696 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2697 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2701 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2707 static gint pan_layout_queue_step(PanWindow *pw)
2711 if (!pw->queue) return FALSE;
2713 pi = pw->queue->data;
2714 pw->queue = g_list_remove(pw->queue, pi);
2717 if (!pw->queue_pi->fd)
2719 pw->queue_pi->queued = FALSE;
2720 pw->queue_pi = NULL;
2724 image_loader_free(pw->il);
2726 thumb_loader_free(pw->tl);
2729 if (pi->type == ITEM_IMAGE)
2731 pw->il = image_loader_new(pi->fd->path);
2733 if (pw->size != LAYOUT_SIZE_100)
2735 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2739 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2741 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2743 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2745 image_loader_free(pw->il);
2748 else if (pi->type == ITEM_THUMB)
2750 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2752 if (!pw->tl->standard_loader)
2754 /* The classic loader will recreate a thumbnail any time we
2755 * request a different size than what exists. This view will
2756 * almost never use the user configured sizes so disable cache.
2758 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2761 thumb_loader_set_callbacks(pw->tl,
2762 pan_layout_queue_thumb_done_cb,
2763 pan_layout_queue_thumb_done_cb,
2766 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2768 thumb_loader_free(pw->tl);
2772 pw->queue_pi->queued = FALSE;
2773 pw->queue_pi = NULL;
2777 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2779 if (!pi || pi->queued || pi->pixbuf) return;
2780 if (pw->size <= LAYOUT_SIZE_THUMB_NONE &&
2781 (!pi->key || strcmp(pi->key, "info") != 0) )
2787 pw->queue = g_list_prepend(pw->queue, pi);
2789 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2792 static gint pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
2793 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2795 PanWindow *pw = data;
2800 pixbuf_set_rect_fill(pixbuf,
2801 0, 0, width, height,
2802 PAN_BACKGROUND_COLOR, 255);
2804 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2806 gint rx, ry, rw, rh;
2808 if (util_clip_region(x, y, width, height,
2810 &rx, &ry, &rw, &rh))
2812 pixbuf_draw_rect_fill(pixbuf,
2813 rx - x, ry - y, rw, rh,
2814 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2817 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2819 gint rx, ry, rw, rh;
2821 if (util_clip_region(x, y, width, height,
2823 &rx, &ry, &rw, &rh))
2825 pixbuf_draw_rect_fill(pixbuf,
2826 rx - x, ry - y, rw, rh,
2827 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2831 list = pan_layout_intersect(pw, x, y, width, height);
2836 gint tx, ty, tw, th;
2837 gint rx, ry, rw, rh;
2844 if (pi->type == ITEM_THUMB && pi->pixbuf)
2846 tw = gdk_pixbuf_get_width(pi->pixbuf);
2847 th = gdk_pixbuf_get_height(pi->pixbuf);
2849 tx = pi->x + (pi->width - tw) / 2;
2850 ty = pi->y + (pi->height - th) / 2;
2852 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2854 if (util_clip_region(x, y, width, height,
2855 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2856 &rx, &ry, &rw, &rh))
2858 pixbuf_draw_shadow(pixbuf,
2859 rx - x, ry - y, rw, rh,
2860 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2862 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2867 if (util_clip_region(x, y, width, height,
2868 tx + tw, ty + PAN_SHADOW_OFFSET,
2869 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
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);
2878 if (util_clip_region(x, y, width, height,
2879 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2880 &rx, &ry, &rw, &rh))
2882 pixbuf_draw_shadow(pixbuf,
2883 rx - x, ry - y, rw, rh,
2884 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2886 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2890 if (util_clip_region(x, y, width, height,
2892 &rx, &ry, &rw, &rh))
2894 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2897 1.0, 1.0, GDK_INTERP_NEAREST,
2901 if (util_clip_region(x, y, width, height,
2902 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2903 &rx, &ry, &rw, &rh))
2905 pixbuf_draw_rect_fill(pixbuf,
2906 rx - x, ry - y, rw, rh,
2907 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2909 if (util_clip_region(x, y, width, height,
2910 tx, ty, PAN_OUTLINE_THICKNESS, th,
2911 &rx, &ry, &rw, &rh))
2913 pixbuf_draw_rect_fill(pixbuf,
2914 rx - x, ry - y, rw, rh,
2915 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2917 if (util_clip_region(x, y, width, height,
2918 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2919 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2920 &rx, &ry, &rw, &rh))
2922 pixbuf_draw_rect_fill(pixbuf,
2923 rx - x, ry - y, rw, rh,
2924 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2926 if (util_clip_region(x, y, width, height,
2927 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2928 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2929 &rx, &ry, &rw, &rh))
2931 pixbuf_draw_rect_fill(pixbuf,
2932 rx - x, ry - y, rw, rh,
2933 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2936 else if (pi->type == ITEM_THUMB)
2938 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2939 th = pi->height - PAN_SHADOW_OFFSET * 2;
2940 tx = pi->x + PAN_SHADOW_OFFSET;
2941 ty = pi->y + PAN_SHADOW_OFFSET;
2943 if (util_clip_region(x, y, width, height,
2945 &rx, &ry, &rw, &rh))
2949 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2950 pixbuf_draw_rect_fill(pixbuf,
2951 rx - x, ry - y, rw, rh,
2953 PAN_SHADOW_ALPHA / d);
2956 pan_layout_queue(pw, pi);
2958 else if (pi->type == ITEM_IMAGE)
2960 if (util_clip_region(x, y, width, height,
2961 pi->x, pi->y, pi->width, pi->height,
2962 &rx, &ry, &rw, &rh))
2966 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2969 1.0, 1.0, GDK_INTERP_NEAREST,
2974 pixbuf_draw_rect_fill(pixbuf,
2975 rx - x, ry - y, rw, rh,
2976 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2977 pan_layout_queue(pw, pi);
2981 else if (pi->type == ITEM_BOX)
2995 if (pi->color_a > 254)
2997 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2998 shadow[0], bh - shadow[0],
2999 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
3001 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
3002 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
3004 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
3006 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
3011 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
3012 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
3014 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
3016 PAN_SHADOW_COLOR, a);
3020 if (util_clip_region(x, y, width, height,
3021 pi->x, pi->y, bw, bh,
3022 &rx, &ry, &rw, &rh))
3024 pixbuf_draw_rect_fill(pixbuf,
3025 rx - x, ry - y, rw, rh,
3026 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3028 if (util_clip_region(x, y, width, height,
3029 pi->x, pi->y, bw, pi->border,
3030 &rx, &ry, &rw, &rh))
3032 pixbuf_draw_rect_fill(pixbuf,
3033 rx - x, ry - y, rw, rh,
3034 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3036 if (util_clip_region(x, y, width, height,
3037 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
3038 &rx, &ry, &rw, &rh))
3040 pixbuf_draw_rect_fill(pixbuf,
3041 rx - x, ry - y, rw, rh,
3042 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3044 if (util_clip_region(x, y, width, height,
3045 pi->x + bw - pi->border, pi->y + pi->border,
3046 pi->border, bh - pi->border * 2,
3047 &rx, &ry, &rw, &rh))
3049 pixbuf_draw_rect_fill(pixbuf,
3050 rx - x, ry - y, rw, rh,
3051 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3053 if (util_clip_region(x, y, width, height,
3054 pi->x, pi->y + bh - pi->border,
3056 &rx, &ry, &rw, &rh))
3058 pixbuf_draw_rect_fill(pixbuf,
3059 rx - x, ry - y, rw, rh,
3060 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3063 else if (pi->type == ITEM_TRIANGLE)
3065 if (util_clip_region(x, y, width, height,
3066 pi->x, pi->y, pi->width, pi->height,
3067 &rx, &ry, &rw, &rh) && pi->data)
3069 gint *coord = pi->data;
3070 pixbuf_draw_triangle(pixbuf,
3071 rx - x, ry - y, rw, rh,
3072 coord[0] - x, coord[1] - y,
3073 coord[2] - x, coord[3] - y,
3074 coord[4] - x, coord[5] - y,
3075 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3077 if (pi->border & BORDER_1)
3079 pixbuf_draw_line(pixbuf,
3080 rx - x, ry - y, rw, rh,
3081 coord[0] - x, coord[1] - y,
3082 coord[2] - x, coord[3] - y,
3083 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3085 if (pi->border & BORDER_2)
3087 pixbuf_draw_line(pixbuf,
3088 rx - x, ry - y, rw, rh,
3089 coord[2] - x, coord[3] - y,
3090 coord[4] - x, coord[5] - y,
3091 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3093 if (pi->border & BORDER_3)
3095 pixbuf_draw_line(pixbuf,
3096 rx - x, ry - y, rw, rh,
3097 coord[4] - x, coord[5] - y,
3098 coord[0] - x, coord[1] - y,
3099 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3103 else if (pi->type == ITEM_TEXT && pi->text)
3105 PangoLayout *layout;
3107 layout = pan_item_text_layout(pi, (GtkWidget *)pr);
3108 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
3109 pi->x - x + pi->border, pi->y - y + pi->border,
3110 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3111 g_object_unref(G_OBJECT(layout));
3117 if (x%512 == 0 && y%512 == 0)
3119 PangoLayout *layout;
3122 layout = gtk_widget_create_pango_layout((GtkWidget *)pr, NULL);
3124 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
3125 (x / pr->source_tile_width) +
3126 (y / pr->source_tile_height * (pr->image_width/pr->source_tile_width + 1)));
3127 pango_layout_set_text(layout, buf, -1);
3130 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr, 0, 0, 0, 0, 0, 255);
3132 g_object_unref(G_OBJECT(layout));
3139 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
3140 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
3142 PanWindow *pw = data;
3146 list = pan_layout_intersect(pw, x, y, width, height);
3155 if (pi->refcount > 0)
3159 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
3164 pw->queue = g_list_remove(pw->queue, pi);
3167 if (pw->queue_pi == pi) pw->queue_pi = NULL;
3170 g_object_unref(pi->pixbuf);
3182 *-----------------------------------------------------------------------------
3184 *-----------------------------------------------------------------------------
3187 static void pan_window_message(PanWindow *pw, const gchar *text)
3197 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
3201 work = pw->list_static;
3202 if (pw->layout == LAYOUT_CALENDAR)
3212 pi->type == ITEM_BOX &&
3213 pi->key && strcmp(pi->key, "dot") == 0)
3215 size += pi->fd->size;
3230 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
3232 size += pi->fd->size;
3238 ss = text_from_size_abrev(size);
3239 buf = g_strdup_printf(_("%d images, %s"), count, ss);
3241 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
3245 static void pan_warning_folder(const gchar *path, GtkWidget *parent)
3249 message = g_strdup_printf(_("The pan view does not support the folder \"%s\"."), path);
3250 warning_dialog(_("Folder not supported"), message,
3251 GTK_STOCK_DIALOG_INFO, parent);
3255 static void pan_window_zoom_limit(PanWindow *pw)
3261 case LAYOUT_SIZE_THUMB_DOTS:
3262 case LAYOUT_SIZE_THUMB_NONE:
3263 case LAYOUT_SIZE_THUMB_SMALL:
3264 case LAYOUT_SIZE_THUMB_NORMAL:
3266 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
3270 case LAYOUT_SIZE_THUMB_LARGE:
3273 case LAYOUT_SIZE_10:
3274 case LAYOUT_SIZE_25:
3277 case LAYOUT_SIZE_33:
3278 case LAYOUT_SIZE_50:
3279 case LAYOUT_SIZE_100:
3285 image_zoom_set_limits(pw->imd, min, 32.0);
3288 static gint pan_window_layout_update_idle_cb(gpointer data)
3290 PanWindow *pw = data;
3296 if (pw->size > LAYOUT_SIZE_THUMB_LARGE ||
3297 (pw->exif_date_enable && (pw->layout == LAYOUT_TIMELINE || pw->layout == LAYOUT_CALENDAR)))
3299 if (!pw->cache_list && !pw->cache_todo)
3301 pan_cache_fill(pw, pw->path);
3304 pan_window_message(pw, _("Reading image data..."));
3312 if (pw->cache_count == pw->cache_total)
3314 pan_window_message(pw, _("Sorting..."));
3316 else if (pw->cache_tick > 9)
3320 buf = g_strdup_printf("%s %d", _("Reading image data..."),
3321 pw->cache_total - pw->cache_count);
3322 pan_window_message(pw, buf);
3328 if (pan_cache_step(pw)) return TRUE;
3335 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3337 pan_window_zoom_limit(pw);
3339 if (width > 0 && height > 0)
3343 if (debug) printf("Canvas size is %d x %d\n", width, height);
3345 pan_grid_build(pw, width, height, 1000);
3347 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
3348 PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
3349 pan_window_request_tile_cb,
3350 pan_window_dispose_tile_cb, pw, 1.0);
3352 if (scroll_x == 0 && scroll_y == 0)
3360 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
3363 pan_window_message(pw, NULL);
3369 static void pan_window_layout_update_idle(PanWindow *pw)
3371 if (pw->idle_id == -1)
3373 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3377 static void pan_window_layout_update(PanWindow *pw)
3379 pan_window_message(pw, _("Sorting images..."));
3380 pan_window_layout_update_idle(pw);
3383 static void pan_window_layout_set_path(PanWindow *pw, const gchar *path)
3387 if (strcmp(path, "/") == 0)
3389 pan_warning_folder(path, pw->window);
3394 pw->path = g_strdup(path);
3396 pan_window_layout_update(pw);
3400 *-----------------------------------------------------------------------------
3401 * pan window keyboard
3402 *-----------------------------------------------------------------------------
3405 static const gchar *pan_menu_click_path(PanWindow *pw)
3407 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3411 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3413 PanWindow *pw = data;
3415 gdk_window_get_origin(pw->imd->pr->window, x, y);
3416 popup_menu_position_clamp(menu, x, y, 0);
3419 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3421 PanWindow *pw = data;
3424 gint stop_signal = FALSE;
3431 pr = PIXBUF_RENDERER(pw->imd->pr);
3432 path = pan_menu_click_path(pw);
3434 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pw->imd->widget)));
3435 on_entry = (GTK_WIDGET_HAS_FOCUS(pw->path_entry) ||
3436 GTK_WIDGET_HAS_FOCUS(pw->search_entry));
3441 switch (event->keyval)
3443 case GDK_Left: case GDK_KP_Left:
3446 case GDK_Right: case GDK_KP_Right:
3449 case GDK_Up: case GDK_KP_Up:
3452 case GDK_Down: case GDK_KP_Down:
3455 case GDK_Page_Up: case GDK_KP_Page_Up:
3456 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
3458 case GDK_Page_Down: case GDK_KP_Page_Down:
3459 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
3461 case GDK_Home: case GDK_KP_Home:
3462 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
3464 case GDK_End: case GDK_KP_End:
3465 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
3468 stop_signal = FALSE;
3472 if (x != 0 || y!= 0)
3474 if (event->state & GDK_SHIFT_MASK)
3479 keyboard_scroll_calc(&x, &y, event);
3480 pixbuf_renderer_scroll(pr, x, y);
3484 if (stop_signal) return stop_signal;
3486 if (event->state & GDK_CONTROL_MASK)
3491 switch (event->keyval)
3524 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3527 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3530 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3533 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3536 if (path) info_window_new(path, NULL);
3539 pan_search_toggle_visible(pw, TRUE);
3542 pan_search_activate(pw);
3545 pan_window_close(pw);
3548 stop_signal = FALSE;
3552 if (n != -1 && path)
3554 if (!editor_window_flag_set(n))
3556 pan_fullscreen_toggle(pw, TRUE);
3558 start_editor_from_file(n, path);
3564 switch (event->keyval)
3569 pan_fullscreen_toggle(pw, TRUE);
3573 pan_search_toggle_visible(pw, FALSE);
3577 stop_signal = FALSE;
3581 if (stop_signal) return stop_signal;
3586 switch (event->keyval)
3588 case '+': case '=': case GDK_KP_Add:
3589 pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
3591 case '-': case GDK_KP_Subtract:
3592 pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
3594 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3595 pixbuf_renderer_zoom_set(pr, 1.0);
3598 pixbuf_renderer_zoom_set(pr, 2.0);
3601 pixbuf_renderer_zoom_set(pr, 3.0);
3604 pixbuf_renderer_zoom_set(pr, 4.0);
3607 pixbuf_renderer_zoom_set(pr, -4.0);
3610 pixbuf_renderer_zoom_set(pr, -3.0);
3613 pixbuf_renderer_zoom_set(pr, -2.0);
3618 pan_fullscreen_toggle(pw, FALSE);
3622 pan_overlay_toggle(pw);
3625 case GDK_Delete: case GDK_KP_Delete:
3629 menu = pan_popup_menu(pw);
3630 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
3631 pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3634 pan_search_toggle_visible(pw, TRUE);
3637 stop_signal = FALSE;
3647 *-----------------------------------------------------------------------------
3649 *-----------------------------------------------------------------------------
3652 typedef struct _PanTextAlignment PanTextAlignment;
3653 struct _PanTextAlignment {
3665 static PanTextAlignment *pan_text_alignment_new(PanWindow *pw, gint x, gint y, const gchar *key)
3667 PanTextAlignment *ta;
3669 ta = g_new0(PanTextAlignment, 1);
3676 ta->key = g_strdup(key);
3681 static PanItem *pan_text_alignment_add(PanTextAlignment *ta,
3682 const gchar *label, const gchar *text)
3688 item = pan_item_new_text(ta->pw, ta->x, ta->y, label,
3690 PAN_POPUP_TEXT_COLOR, 255);
3691 pan_item_set_key(item, ta->key);
3697 ta->column1 = g_list_append(ta->column1, item);
3701 item = pan_item_new_text(ta->pw, ta->x, ta->y, text,
3703 PAN_POPUP_TEXT_COLOR, 255);
3704 pan_item_set_key(item, ta->key);
3710 ta->column2 = g_list_append(ta->column2, item);
3715 static void pan_text_alignment_calc(PanTextAlignment *ta, PanItem *box)
3725 work1 = ta->column1;
3731 work1 = work1->next;
3733 if (p && p->width > cw1) cw1 = p->width;
3736 work2 = ta->column2;
3742 work2 = work2->next;
3744 if (p && p->width > cw2) cw2 = p->width;
3749 work1 = ta->column1;
3750 work2 = ta->column2;
3751 while (work1 && work2)
3759 work1 = work1->next;
3760 work2 = work2->next;
3766 pan_item_size_by_item(box, p1, PREF_PAD_BORDER);
3767 height = p1->height;
3771 p2->x = x + cw1 + PREF_PAD_SPACE;
3773 pan_item_size_by_item(box, p2, PREF_PAD_BORDER);
3774 if (height < p2->height) height = p2->height;
3777 if (!p1 && !p2) height = PREF_PAD_GROUP;
3783 static void pan_text_alignment_free(PanTextAlignment *ta)
3787 g_list_free(ta->column1);
3788 g_list_free(ta->column2);
3793 static void pan_info_add_exif(PanTextAlignment *ta, FileData *fd)
3800 exif = exif_read(fd->path);
3803 pan_text_alignment_add(ta, NULL, NULL);
3805 for (i = 0; i < bar_exif_key_count; i++)
3810 label = g_strdup_printf("%s:", exif_get_description_by_key(bar_exif_key_list[i]));
3811 text = exif_get_data_as_text(exif, bar_exif_key_list[i]);
3812 text = bar_exif_validate_text(text);
3813 pan_text_alignment_add(ta, label, text);
3818 work = g_list_last(history_list_get_by_key("exif_extras"));
3819 if (work) pan_text_alignment_add(ta, "---", NULL);
3829 label = g_strdup_printf("%s:", name);
3830 text = exif_get_data_as_text(exif, name);
3831 text = bar_exif_validate_text(text);
3832 pan_text_alignment_add(ta, label, text);
3840 static void pan_info_update(PanWindow *pw, PanItem *pi)
3842 PanTextAlignment *ta;
3846 gint x1, y1, x2, y2, x3, y3;
3849 if (pw->click_pi == pi) return;
3850 if (pi && !pi->fd) pi = NULL;
3852 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3857 if (debug) printf("info set to %s\n", pi->fd->path);
3859 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3861 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3862 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3863 pan_item_set_key(pbox, "info");
3865 if (pi->type == ITEM_THUMB && pi->pixbuf)
3867 w = gdk_pixbuf_get_width(pi->pixbuf);
3868 h = gdk_pixbuf_get_height(pi->pixbuf);
3870 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3871 y1 = pi->y + (pi->height - h) / 2 + 8;
3875 x1 = pi->x + pi->width - 8;
3883 util_clip_triangle(x1, y1, x2, y2, x3, y3,
3886 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3887 x1, y1, x2, y2, x3, y3,
3888 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3889 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3890 pan_item_set_key(p, "info");
3891 pan_item_added(pw, p);
3893 ta = pan_text_alignment_new(pw, pbox->x + PREF_PAD_BORDER, pbox->y + PREF_PAD_BORDER, "info");
3895 pan_text_alignment_add(ta, _("Filename:"), pi->fd->name);
3896 buf = remove_level_from_path(pi->fd->path);
3897 pan_text_alignment_add(ta, _("Location:"), buf);
3899 pan_text_alignment_add(ta, _("Date:"), text_from_time(pi->fd->date));
3900 buf = text_from_size(pi->fd->size);
3901 pan_text_alignment_add(ta, _("Size:"), buf);
3904 if (pw->info_includes_exif)
3906 pan_info_add_exif(ta, pi->fd);
3909 pan_text_alignment_calc(ta, pbox);
3910 pan_text_alignment_free(ta);
3912 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3913 pan_item_added(pw, pbox);
3915 if (pw->info_includes_image)
3918 if (image_load_dimensions(pi->fd->path, &iw, &ih))
3920 pbox = pan_item_new_box(pw, NULL, pbox->x, pbox->y + pbox->height + 8, 10, 10,
3922 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3923 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3924 pan_item_set_key(pbox, "info");
3926 p = pan_item_new_image(pw, file_data_new_simple(pi->fd->path),
3927 pbox->x + PREF_PAD_BORDER, pbox->y + PREF_PAD_BORDER, iw, ih);
3928 pan_item_set_key(p, "info");
3929 pan_item_size_by_item(pbox, p, PREF_PAD_BORDER);
3931 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3932 pan_item_added(pw, pbox);
3936 pan_layout_resize(pw);
3941 *-----------------------------------------------------------------------------
3943 *-----------------------------------------------------------------------------
3946 static void pan_search_status(PanWindow *pw, const gchar *text)
3948 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3951 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3959 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3961 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3962 if (!list) return FALSE;
3964 found = g_list_find(list, pw->click_pi);
3965 if (found && found->next)
3967 found = found->next;
3975 pan_info_update(pw, pi);
3976 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3978 buf = g_strdup_printf("%s ( %d / %d )",
3979 (path[0] == '/') ? _("path found") : _("filename found"),
3980 g_list_index(list, pi) + 1,
3981 g_list_length(list));
3982 pan_search_status(pw, buf);
3990 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3998 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
4000 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
4001 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
4006 needle = g_utf8_strdown(text, -1);
4007 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
4010 if (!list) return FALSE;
4012 found = g_list_find(list, pw->click_pi);
4013 if (found && found->next)
4015 found = found->next;
4023 pan_info_update(pw, pi);
4024 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
4026 buf = g_strdup_printf("%s ( %d / %d )",
4028 g_list_index(list, pi) + 1,
4029 g_list_length(list));
4030 pan_search_status(pw, buf);
4038 static gint valid_date_separator(gchar c)
4040 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
4043 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
4044 gint year, gint month, gint day,
4050 work = g_list_last(pw->list_static);
4058 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
4059 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
4063 tl = localtime(&pi->fd->date);
4068 match = (tl->tm_year == year - 1900);
4069 if (match && month >= 0) match = (tl->tm_mon == month - 1);
4070 if (match && day > 0) match = (tl->tm_mday == day);
4072 if (match) list = g_list_prepend(list, pi);
4077 return g_list_reverse(list);
4080 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
4096 if (!text) return FALSE;
4098 ptr = (gchar *)text;
4099 while (*ptr != '\0')
4101 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
4106 if (t == -1) return FALSE;
4108 if (!lt) return FALSE;
4110 if (valid_date_separator(*text))
4113 mptr = (gchar *)text;
4117 year = (gint)strtol(text, &mptr, 10);
4118 if (mptr == text) return FALSE;
4121 if (*mptr != '\0' && valid_date_separator(*mptr))
4126 month = strtol(mptr, &dptr, 10);
4129 if (valid_date_separator(*dptr))
4131 month = lt->tm_mon + 1;
4139 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
4143 day = strtol(dptr, &eptr, 10);
4153 year = lt->tm_year + 1900;
4155 else if (year < 100)
4164 month < -1 || month == 0 || month > 12 ||
4165 day < -1 || day == 0 || day > 31) return FALSE;
4167 t = date_to_time(year, month, day);
4168 if (t < 0) return FALSE;
4170 if (pw->layout == LAYOUT_CALENDAR)
4172 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
4178 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
4179 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
4184 found = g_list_find(list, pw->search_pi);
4185 if (found && found->next)
4187 found = found->next;
4198 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
4200 pan_info_update(pw, NULL);
4201 pan_calendar_update(pw, pi);
4202 image_scroll_to_point(pw->imd,
4203 pi->x + pi->width / 2,
4204 pi->y + pi->height / 2, 0.5, 0.5);
4208 pan_info_update(pw, pi);
4209 image_scroll_to_point(pw->imd,
4210 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
4216 buf = date_value_string(t, DATE_LENGTH_MONTH);
4221 buf = g_strdup_printf("%d %s", day, tmp);
4227 buf = date_value_string(t, DATE_LENGTH_YEAR);
4232 buf_count = g_strdup_printf("( %d / %d )",
4233 g_list_index(list, pi) + 1,
4234 g_list_length(list));
4238 buf_count = g_strdup_printf("(%s)", _("no match"));
4241 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
4244 pan_search_status(pw, message);
4252 static void pan_search_activate_cb(const gchar *text, gpointer data)
4254 PanWindow *pw = data;
4258 tab_completion_append_to_history(pw->search_entry, text);
4260 if (pan_search_by_path(pw, text)) return;
4262 if ((pw->layout == LAYOUT_TIMELINE ||
4263 pw->layout == LAYOUT_CALENDAR) &&
4264 pan_search_by_date(pw, text))
4269 if (pan_search_by_partial(pw, text)) return;
4271 pan_search_status(pw, _("no match"));
4274 static void pan_search_activate(PanWindow *pw)
4279 if (!GTK_WIDGET_VISIBLE(pw->search_box))
4281 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
4285 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->search_entry)));
4286 pan_search_activate_cb(text, pw);
4290 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
4292 PanWindow *pw = data;
4295 visible = GTK_WIDGET_VISIBLE(pw->search_box);
4296 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
4300 gtk_widget_hide(pw->search_box);
4301 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
4305 gtk_widget_show(pw->search_box);
4306 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
4307 gtk_widget_grab_focus(pw->search_entry);
4311 static void pan_search_toggle_visible(PanWindow *pw, gint enable)
4317 if (GTK_WIDGET_VISIBLE(pw->search_box))
4319 gtk_widget_grab_focus(pw->search_entry);
4323 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
4328 if (GTK_WIDGET_VISIBLE(pw->search_entry))
4330 if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
4332 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4334 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
4341 *-----------------------------------------------------------------------------
4342 * view window main routines
4343 *-----------------------------------------------------------------------------
4346 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
4348 PanWindow *pw = data;
4356 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
4357 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
4360 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "info");
4361 if (pi && event->button == 1)
4363 pan_info_update(pw, NULL);
4367 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
4370 switch (event->button)
4373 pan_info_update(pw, pi);
4375 if (!pi && pw->layout == LAYOUT_CALENDAR)
4377 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
4378 pan_calendar_update(pw, pi);
4384 pan_info_update(pw, pi);
4385 menu = pan_popup_menu(pw);
4386 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
4393 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
4396 PanWindow *pw = data;
4403 if (!(event->state & GDK_SHIFT_MASK))
4409 if (event->state & GDK_CONTROL_MASK)
4411 switch (event->direction)
4414 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
4415 (gint)event->x, (gint)event->y);
4417 case GDK_SCROLL_DOWN:
4418 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
4419 (gint)event->x, (gint)event->y);
4427 switch (event->direction)
4430 pixbuf_renderer_scroll(pr, 0, -h);
4432 case GDK_SCROLL_DOWN:
4433 pixbuf_renderer_scroll(pr, 0, h);
4435 case GDK_SCROLL_LEFT:
4436 pixbuf_renderer_scroll(pr, -w, 0);
4438 case GDK_SCROLL_RIGHT:
4439 pixbuf_renderer_scroll(pr, w, 0);
4447 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
4449 g_signal_connect(G_OBJECT(imd->pr), "clicked",
4450 G_CALLBACK(button_cb), pw);
4451 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
4452 G_CALLBACK(scroll_cb), pw);
4455 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
4457 PanWindow *pw = data;
4460 pw->imd = pw->imd_normal;
4463 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
4465 if (force_off && !pw->fs) return;
4469 fullscreen_stop(pw->fs);
4473 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
4474 pan_image_set_buttons(pw, pw->fs->imd);
4475 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
4476 G_CALLBACK(pan_window_key_press_cb), pw);
4478 pw->imd = pw->fs->imd;
4482 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
4484 PanWindow *pw = data;
4487 text = image_zoom_get_as_text(pw->imd);
4488 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4492 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
4494 PanWindow *pw = data;
4499 if (pr->scale == 0.0) return;
4501 pixbuf_renderer_get_visible_rect(pr, &rect);
4502 pixbuf_renderer_get_image_size(pr, &width, &height);
4504 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4505 adj->page_size = (gdouble)rect.width;
4506 adj->page_increment = adj->page_size / 2.0;
4507 adj->step_increment = 48.0 / pr->scale;
4509 adj->upper = MAX((gdouble)width, 1.0);
4510 adj->value = (gdouble)rect.x;
4512 pref_signal_block_data(pw->scrollbar_h, pw);
4513 gtk_adjustment_changed(adj);
4514 gtk_adjustment_value_changed(adj);
4515 pref_signal_unblock_data(pw->scrollbar_h, pw);
4517 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4518 adj->page_size = (gdouble)rect.height;
4519 adj->page_increment = adj->page_size / 2.0;
4520 adj->step_increment = 48.0 / pr->scale;
4522 adj->upper = MAX((gdouble)height, 1.0);
4523 adj->value = (gdouble)rect.y;
4525 pref_signal_block_data(pw->scrollbar_v, pw);
4526 gtk_adjustment_changed(adj);
4527 gtk_adjustment_value_changed(adj);
4528 pref_signal_unblock_data(pw->scrollbar_v, pw);
4531 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4533 PanWindow *pw = data;
4537 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4539 if (!pr->scale) return;
4541 x = (gint)gtk_range_get_value(range);
4543 pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
4546 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4548 PanWindow *pw = data;
4552 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4554 if (!pr->scale) return;
4556 y = (gint)gtk_range_get_value(range);
4558 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
4561 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4563 PanWindow *pw = data;
4565 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4566 pan_window_layout_update(pw);
4569 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4571 PanWindow *pw = data;
4573 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4574 pan_window_layout_update(pw);
4578 static void pan_window_date_toggle_cb(GtkWidget *button, gpointer data)
4580 PanWindow *pw = data;
4582 pw->exif_date_enable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4583 pan_window_layout_update(pw);
4587 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4589 PanWindow *pw = data;
4592 path = remove_trailing_slash(new_text);
4593 parse_out_relatives(path);
4597 warning_dialog(_("Folder not found"),
4598 _("The entered path is not a folder"),
4599 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4603 tab_completion_append_to_history(pw->path_entry, path);
4605 pan_window_layout_set_path(pw, path);
4611 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4613 PanWindow *pw = data;
4616 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4618 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4619 pan_window_entry_activate_cb(text, pw);
4623 static void pan_window_close(PanWindow *pw)
4625 pan_window_list = g_list_remove(pan_window_list, pw);
4627 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_EXIF_DATE, pw->exif_date_enable);
4628 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_INFO_IMAGE, pw->info_includes_image);
4629 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_INFO_EXIF, pw->info_includes_exif);
4631 if (pw->idle_id != -1)
4633 g_source_remove(pw->idle_id);
4636 pan_fullscreen_toggle(pw, TRUE);
4637 gtk_widget_destroy(pw->window);
4639 pan_window_items_free(pw);
4647 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4649 PanWindow *pw = data;
4651 pan_window_close(pw);
4655 static void pan_window_new_real(const gchar *path)
4664 GdkGeometry geometry;
4666 pw = g_new0(PanWindow, 1);
4668 pw->path = g_strdup(path);
4669 pw->layout = LAYOUT_TIMELINE;
4670 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4671 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4672 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4674 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_EXIF_DATE, &pw->exif_date_enable))
4676 pw->exif_date_enable = FALSE;
4678 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_INFO_IMAGE, &pw->info_includes_image))
4680 pw->info_includes_image = FALSE;
4682 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_INFO_EXIF, &pw->info_includes_exif))
4684 pw->info_includes_exif = TRUE;
4687 pw->ignore_symlinks = TRUE;
4690 pw->list_static = NULL;
4691 pw->list_grid = NULL;
4694 pw->overlay_id = -1;
4697 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4699 geometry.min_width = 8;
4700 geometry.min_height = 8;
4701 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4703 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4704 gtk_window_set_title (GTK_WINDOW(pw->window), _("Pan View - GQview"));
4705 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4706 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4708 window_set_icon(pw->window, NULL, NULL);
4710 vbox = gtk_vbox_new(FALSE, 0);
4711 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4712 gtk_widget_show(vbox);
4714 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4716 pref_spacer(box, 0);
4717 pref_label_new(box, _("Location:"));
4718 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view_path", -1,
4719 pan_window_entry_activate_cb, pw);
4720 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4721 G_CALLBACK(pan_window_entry_change_cb), pw);
4722 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4723 gtk_widget_show(combo);
4725 combo = gtk_combo_box_new_text();
4726 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4727 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4728 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4729 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4730 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4732 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4733 g_signal_connect(G_OBJECT(combo), "changed",
4734 G_CALLBACK(pan_window_layout_change_cb), pw);
4735 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4736 gtk_widget_show(combo);
4738 combo = gtk_combo_box_new_text();
4739 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4740 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4741 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4742 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4743 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4744 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4745 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4746 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4747 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4748 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4750 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4751 g_signal_connect(G_OBJECT(combo), "changed",
4752 G_CALLBACK(pan_window_layout_size_cb), pw);
4753 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4754 gtk_widget_show(combo);
4756 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4757 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4758 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4760 pw->imd = image_new(TRUE);
4761 pw->imd_normal = pw->imd;
4763 g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
4764 G_CALLBACK(pan_window_image_zoom_cb), pw);
4765 g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
4766 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4768 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4769 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4770 gtk_widget_show(GTK_WIDGET(pw->imd->widget));
4772 pan_window_dnd_init(pw);
4774 pan_image_set_buttons(pw, pw->imd);
4776 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4777 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4778 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4779 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4780 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4781 gtk_widget_show(pw->scrollbar_h);
4783 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4784 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4785 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4786 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4787 0, GTK_FILL | GTK_EXPAND, 0, 0);
4788 gtk_widget_show(pw->scrollbar_v);
4792 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4793 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4795 pref_spacer(pw->search_box, 0);
4796 pref_label_new(pw->search_box, _("Find:"));
4798 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4799 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4800 gtk_widget_show(hbox);
4802 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4803 pan_search_activate_cb, pw);
4804 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4805 gtk_widget_show(combo);
4807 pw->search_label = gtk_label_new("");
4808 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4809 gtk_widget_show(pw->search_label);
4813 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4815 frame = gtk_frame_new(NULL);
4816 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4817 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4818 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4819 gtk_widget_show(frame);
4821 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4822 gtk_container_add(GTK_CONTAINER(frame), hbox);
4823 gtk_widget_show(hbox);
4825 pref_spacer(hbox, 0);
4826 pw->label_message = pref_label_new(hbox, "");
4828 frame = gtk_frame_new(NULL);
4829 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4830 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4831 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4832 gtk_widget_show(frame);
4834 pw->label_zoom = gtk_label_new("");
4835 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4836 gtk_widget_show(pw->label_zoom);
4839 pw->date_button = pref_checkbox_new(box, _("Use Exif date"), pw->exif_date_enable,
4840 G_CALLBACK(pan_window_date_toggle_cb), pw);
4843 pw->search_button = gtk_toggle_button_new();
4844 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4845 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4846 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4847 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4848 gtk_widget_show(hbox);
4849 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4850 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4851 gtk_widget_show(pw->search_button_arrow);
4852 pref_label_new(hbox, _("Find"));
4854 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4855 gtk_widget_show(pw->search_button);
4856 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4857 G_CALLBACK(pan_search_toggle_cb), pw);
4859 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4860 G_CALLBACK(pan_window_delete_cb), pw);
4861 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4862 G_CALLBACK(pan_window_key_press_cb), pw);
4864 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4866 pan_window_layout_update(pw);
4868 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4869 gtk_widget_show(pw->window);
4871 pan_window_list = g_list_append(pan_window_list, pw);
4875 *-----------------------------------------------------------------------------
4876 * peformance warnings
4877 *-----------------------------------------------------------------------------
4880 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4884 generic_dialog_close(gd);
4886 pan_window_new_real(path);
4890 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4894 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4895 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4898 static gint pan_warning(const gchar *path)
4904 GtkWidget *ct_button;
4907 if (path && strcmp(path, "/") == 0)
4909 pan_warning_folder(path, NULL);
4913 if (enable_thumb_caching &&
4914 thumbnail_spec_standard) return FALSE;
4916 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4917 if (hide_dlg) return FALSE;
4919 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4921 gd->data = g_strdup(path);
4922 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4923 pan_warning_ok_cb, TRUE);
4925 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4926 _("Pan view performance may be poor."),
4927 _("To improve performance of thumbnails in the pan view the"
4928 " following options can be enabled. Note that both options"
4929 " must be enabled to notice a change in performance."));
4931 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4932 pref_spacer(group, PREF_PAD_INDENT);
4933 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4935 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4936 enable_thumb_caching, &enable_thumb_caching);
4937 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4938 thumbnail_spec_standard, &thumbnail_spec_standard);
4939 pref_checkbox_link_sensitivity(ct_button, button);
4943 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4944 G_CALLBACK(pan_warning_hide_cb), NULL);
4946 gtk_widget_show(gd->dialog);
4953 *-----------------------------------------------------------------------------
4955 *-----------------------------------------------------------------------------
4958 void pan_window_new(const gchar *path)
4960 if (pan_warning(path)) return;
4962 pan_window_new_real(path);
4966 *-----------------------------------------------------------------------------
4968 *-----------------------------------------------------------------------------
4971 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4973 PanWindow *pw = data;
4976 path = pan_menu_click_path(pw);
4979 pan_fullscreen_toggle(pw, TRUE);
4980 view_window_new(path);
4984 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4990 pw = submenu_item_get_data(widget);
4991 n = GPOINTER_TO_INT(data);
4994 path = pan_menu_click_path(pw);
4997 if (!editor_window_flag_set(n))
4999 pan_fullscreen_toggle(pw, TRUE);
5001 start_editor_from_file(n, path);
5005 static void pan_info_cb(GtkWidget *widget, gpointer data)
5007 PanWindow *pw = data;
5010 path = pan_menu_click_path(pw);
5011 if (path) info_window_new(path, NULL);
5014 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
5016 PanWindow *pw = data;
5018 image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
5021 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
5023 PanWindow *pw = data;
5025 image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
5028 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
5030 PanWindow *pw = data;
5032 image_zoom_set(pw->imd, 1.0);
5035 static void pan_copy_cb(GtkWidget *widget, gpointer data)
5037 PanWindow *pw = data;
5040 path = pan_menu_click_path(pw);
5041 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
5044 static void pan_move_cb(GtkWidget *widget, gpointer data)
5046 PanWindow *pw = data;
5049 path = pan_menu_click_path(pw);
5050 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
5053 static void pan_rename_cb(GtkWidget *widget, gpointer data)
5055 PanWindow *pw = data;
5058 path = pan_menu_click_path(pw);
5059 if (path) file_util_rename(path, NULL, pw->imd->widget);
5062 static void pan_delete_cb(GtkWidget *widget, gpointer data)
5064 PanWindow *pw = data;
5067 path = pan_menu_click_path(pw);
5068 if (path) file_util_delete(path, NULL, pw->imd->widget);
5071 static void pan_exif_date_toggle_cb(GtkWidget *widget, gpointer data)
5073 PanWindow *pw = data;
5075 pw->exif_date_enable = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
5076 pan_window_layout_update(pw);
5079 static void pan_info_toggle_exif_cb(GtkWidget *widget, gpointer data)
5081 PanWindow *pw = data;
5083 pw->info_includes_exif = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
5084 /* fixme: sync info now */
5087 static void pan_info_toggle_image_cb(GtkWidget *widget, gpointer data)
5089 PanWindow *pw = data;
5091 pw->info_includes_image = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
5092 /* fixme: sync info now */
5095 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
5097 PanWindow *pw = data;
5099 pan_fullscreen_toggle(pw, FALSE);
5102 static void pan_close_cb(GtkWidget *widget, gpointer data)
5104 PanWindow *pw = data;
5106 pan_window_close(pw);
5109 static GtkWidget *pan_popup_menu(PanWindow *pw)
5115 active = (pw->click_pi != NULL);
5117 menu = popup_menu_short_lived();
5119 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
5120 G_CALLBACK(pan_zoom_in_cb), pw);
5121 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
5122 G_CALLBACK(pan_zoom_out_cb), pw);
5123 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
5124 G_CALLBACK(pan_zoom_1_1_cb), pw);
5125 menu_item_add_divider(menu);
5127 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
5128 gtk_widget_set_sensitive(item, active);
5130 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
5131 G_CALLBACK(pan_info_cb), pw);
5133 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
5134 G_CALLBACK(pan_new_window_cb), pw);
5136 menu_item_add_divider(menu);
5137 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
5138 G_CALLBACK(pan_copy_cb), pw);
5139 menu_item_add_sensitive(menu, _("_Move..."), active,
5140 G_CALLBACK(pan_move_cb), pw);
5141 menu_item_add_sensitive(menu, _("_Rename..."), active,
5142 G_CALLBACK(pan_rename_cb), pw);
5143 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
5144 G_CALLBACK(pan_delete_cb), pw);
5146 menu_item_add_divider(menu);
5147 item = menu_item_add_check(menu, _("Sort by E_xif date"), pw->exif_date_enable,
5148 G_CALLBACK(pan_exif_date_toggle_cb), pw);
5149 gtk_widget_set_sensitive(item, (pw->layout == LAYOUT_TIMELINE || pw->layout == LAYOUT_CALENDAR));
5151 menu_item_add_divider(menu);
5152 menu_item_add_check(menu, _("Show EXIF information"), pw->info_includes_exif,
5153 G_CALLBACK(pan_info_toggle_exif_cb), pw);
5154 menu_item_add_check(menu, _("Show full size image"), pw->info_includes_image,
5155 G_CALLBACK(pan_info_toggle_image_cb), pw);
5157 menu_item_add_divider(menu);
5161 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
5165 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
5168 menu_item_add_divider(menu);
5169 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
5175 *-----------------------------------------------------------------------------
5177 *-----------------------------------------------------------------------------
5180 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
5182 GtkSelectionData *selection_data, guint info,
5183 guint time, gpointer data)
5185 PanWindow *pw = data;
5187 if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
5189 if (info == TARGET_URI_LIST)
5193 list = uri_list_from_text((gchar *)selection_data->data, TRUE);
5194 if (list && isdir((gchar *)list->data))
5196 gchar *path = list->data;
5198 pan_window_layout_set_path(pw, path);
5201 path_list_free(list);
5205 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
5206 GtkSelectionData *selection_data, guint info,
5207 guint time, gpointer data)
5209 PanWindow *pw = data;
5212 path = pan_menu_click_path(pw);
5222 case TARGET_URI_LIST:
5225 case TARGET_TEXT_PLAIN:
5230 list = g_list_append(NULL, (gchar *)path);
5231 text = uri_text_from_list(list, &len, plain_text);
5235 gtk_selection_data_set (selection_data, selection_data->target,
5236 8, (guchar *)text, len);
5242 gtk_selection_data_set (selection_data, selection_data->target,
5247 static void pan_window_dnd_init(PanWindow *pw)
5251 widget = pw->imd->pr;
5253 gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
5254 dnd_file_drag_types, dnd_file_drag_types_count,
5255 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
5256 g_signal_connect(G_OBJECT(widget), "drag_data_get",
5257 G_CALLBACK(pan_window_set_dnd_data), pw);
5259 gtk_drag_dest_set(widget,
5260 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
5261 dnd_file_drop_types, dnd_file_drop_types_count,
5262 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
5263 g_signal_connect(G_OBJECT(widget), "drag_data_received",
5264 G_CALLBACK(pan_window_get_dnd_data), pw);
5268 *-----------------------------------------------------------------------------
5269 * maintenance (for rename, move, remove)
5270 *-----------------------------------------------------------------------------