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!
17 #include "cache-loader.h"
21 #include "fullscreen.h"
23 #include "image-load.h"
27 #include "pixbuf-renderer.h"
28 #include "pixbuf_util.h"
31 #include "ui_bookmark.h"
32 #include "ui_fileops.h"
35 #include "ui_tabcomp.h"
37 #include <gdk/gdkkeysyms.h> /* for keyboard values */
41 #define PAN_WINDOW_DEFAULT_WIDTH 720
42 #define PAN_WINDOW_DEFAULT_HEIGHT 500
44 #define PAN_TILE_SIZE 512
46 #define PAN_THUMB_SIZE_DOTS 4
47 #define PAN_THUMB_SIZE_NONE 24
48 #define PAN_THUMB_SIZE_SMALL 64
49 #define PAN_THUMB_SIZE_NORMAL 128
50 #define PAN_THUMB_SIZE_LARGE 256
51 #define PAN_THUMB_SIZE pw->thumb_size
53 #define PAN_THUMB_GAP_DOTS 2
54 #define PAN_THUMB_GAP_SMALL 14
55 #define PAN_THUMB_GAP_NORMAL 30
56 #define PAN_THUMB_GAP_LARGE 40
57 #define PAN_THUMB_GAP_HUGE 50
58 #define PAN_THUMB_GAP pw->thumb_gap
60 #define PAN_SHADOW_OFFSET 6
61 #define PAN_SHADOW_FADE 5
62 #define PAN_SHADOW_COLOR 0, 0, 0
63 #define PAN_SHADOW_ALPHA 64
65 #define PAN_OUTLINE_THICKNESS 1
66 #define PAN_OUTLINE_COLOR_1 255, 255, 255
67 #define PAN_OUTLINE_COLOR_2 64, 64, 64
68 #define PAN_OUTLINE_ALPHA 180
70 #define PAN_BACKGROUND_COLOR 150, 150, 150
72 #define PAN_GRID_SIZE 60
73 #define PAN_GRID_COLOR 0, 0, 0
74 #define PAN_GRID_ALPHA 20
76 #define PAN_FOLDER_BOX_COLOR 255, 255, 255
77 #define PAN_FOLDER_BOX_ALPHA 100
78 #define PAN_FOLDER_BOX_BORDER 20
80 #define PAN_FOLDER_BOX_OUTLINE_THICKNESS 4
81 #define PAN_FOLDER_BOX_OUTLINE_COLOR 0, 0, 0
82 #define PAN_FOLDER_BOX_OUTLINE_ALPHA 128
84 #define PAN_TEXT_BORDER_SIZE 4
85 #define PAN_TEXT_COLOR 0, 0, 0
87 #define PAN_POPUP_COLOR 255, 255, 225
88 #define PAN_POPUP_ALPHA 255
89 #define PAN_POPUP_BORDER 1
90 #define PAN_POPUP_BORDER_COLOR 0, 0, 0
91 #define PAN_POPUP_TEXT_COLOR 0, 0, 0
93 #define PAN_CAL_POPUP_COLOR 220, 220, 220
94 #define PAN_CAL_POPUP_ALPHA 255
95 #define PAN_CAL_POPUP_BORDER 1
96 #define PAN_CAL_POPUP_BORDER_COLOR 0, 0, 0
97 #define PAN_CAL_POPUP_TEXT_COLOR 0, 0, 0
99 #define PAN_CAL_DAY_WIDTH 100
100 #define PAN_CAL_DAY_HEIGHT 80
102 #define PAN_CAL_DAY_COLOR 255, 255, 255
103 #define PAN_CAL_DAY_ALPHA 220
104 #define PAN_CAL_DAY_BORDER 2
105 #define PAN_CAL_DAY_BORDER_COLOR 0, 0, 0
106 #define PAN_CAL_DAY_TEXT_COLOR 0, 0, 0
108 #define PAN_CAL_MONTH_COLOR 255, 255, 255
109 #define PAN_CAL_MONTH_ALPHA 200
110 #define PAN_CAL_MONTH_BORDER 4
111 #define PAN_CAL_MONTH_BORDER_COLOR 0, 0, 0
112 #define PAN_CAL_MONTH_TEXT_COLOR 0, 0, 0
114 #define PAN_CAL_DOT_SIZE 3
115 #define PAN_CAL_DOT_GAP 2
116 #define PAN_CAL_DOT_COLOR 128, 128, 128
117 #define PAN_CAL_DOT_ALPHA 128
120 #define PAN_GROUP_MAX 16
122 #define ZOOM_INCREMENT 1.0
123 #define ZOOM_LABEL_WIDTH 64
126 #define PAN_PREF_GROUP "pan_view_options"
127 #define PAN_PREF_HIDE_WARNING "hide_performance_warning"
128 #define PAN_PREF_EXIF_DATE "use_exif_date"
134 LAYOUT_FOLDERS_LINEAR,
135 LAYOUT_FOLDERS_FLOWER,
140 LAYOUT_SIZE_THUMB_DOTS = 0,
141 LAYOUT_SIZE_THUMB_NONE,
142 LAYOUT_SIZE_THUMB_SMALL,
143 LAYOUT_SIZE_THUMB_NORMAL,
144 LAYOUT_SIZE_THUMB_LARGE,
163 TEXT_ATTR_BOLD = 1 << 0,
164 TEXT_ATTR_HEADING = 1 << 1,
165 TEXT_ATTR_MARKUP = 1 << 2
176 typedef struct _PanItem PanItem;
191 TextAttrType text_attr;
209 typedef struct _PanWindow PanWindow;
214 ImageWindow *imd_normal;
217 GtkWidget *path_entry;
219 GtkWidget *label_message;
220 GtkWidget *label_zoom;
222 GtkWidget *search_box;
223 GtkWidget *search_entry;
224 GtkWidget *search_label;
225 GtkWidget *search_button;
226 GtkWidget *search_button_arrow;
228 GtkWidget *date_button;
230 GtkWidget *scrollbar_h;
231 GtkWidget *scrollbar_v;
241 gint exif_date_enable;
243 gint ignore_symlinks;
254 CacheLoader *cache_cl;
267 typedef struct _PanGrid PanGrid;
276 typedef struct _PanCacheData PanCacheData;
277 struct _PanCacheData {
283 static GList *pan_window_list = NULL;
286 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend,
287 gint ignore_symlinks);
289 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
291 static void pan_window_layout_update_idle(PanWindow *pw);
293 static GtkWidget *pan_popup_menu(PanWindow *pw);
294 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off);
296 static void pan_window_close(PanWindow *pw);
298 static void pan_window_dnd_init(PanWindow *pw);
310 static gint date_compare(time_t a, time_t b, DateLengthType length)
315 if (length == DATE_LENGTH_EXACT) return (a == b);
317 if (!localtime_r(&a, &ta) ||
318 !localtime_r(&b, &tb)) return FALSE;
320 if (ta.tm_year != tb.tm_year) return FALSE;
321 if (length == DATE_LENGTH_YEAR) return TRUE;
323 if (ta.tm_mon != tb.tm_mon) return FALSE;
324 if (length == DATE_LENGTH_MONTH) return TRUE;
326 if (length == DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
328 if (ta.tm_mday != tb.tm_mday) return FALSE;
329 if (length == DATE_LENGTH_DAY) return TRUE;
331 return (ta.tm_hour == tb.tm_hour);
334 static gint date_value(time_t d, DateLengthType length)
338 if (!localtime_r(&d, &td)) return -1;
342 case DATE_LENGTH_DAY:
345 case DATE_LENGTH_WEEK:
348 case DATE_LENGTH_MONTH:
349 return td.tm_mon + 1;
351 case DATE_LENGTH_YEAR:
352 return td.tm_year + 1900;
354 case DATE_LENGTH_EXACT:
362 static gchar *date_value_string(time_t d, DateLengthType length)
366 gchar *format = NULL;
368 if (!localtime_r(&d, &td)) return g_strdup("");
372 case DATE_LENGTH_DAY:
373 return g_strdup_printf("%d", td.tm_mday);
375 case DATE_LENGTH_WEEK:
378 case DATE_LENGTH_MONTH:
381 case DATE_LENGTH_YEAR:
382 return g_strdup_printf("%d", td.tm_year + 1900);
384 case DATE_LENGTH_EXACT:
386 return g_strdup(text_from_time(d));
391 if (format && strftime(buf, sizeof(buf), format, &td) > 0)
393 gchar *ret = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
400 static time_t date_to_time(gint year, gint month, gint day)
407 lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
408 lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
409 lt.tm_year = year - 1900;
416 *-----------------------------------------------------------------------------
418 *-----------------------------------------------------------------------------
421 static void pan_cache_free(PanWindow *pw)
425 work = pw->cache_list;
433 cache_sim_data_free(pc->cd);
434 file_data_free((FileData *)pc);
437 g_list_free(pw->cache_list);
438 pw->cache_list = NULL;
440 filelist_free(pw->cache_todo);
441 pw->cache_todo = NULL;
447 cache_loader_free(pw->cache_cl);
451 static void pan_cache_fill(PanWindow *pw, const gchar *path)
457 list = pan_window_layout_list(path, SORT_NAME, TRUE, pw->ignore_symlinks);
458 pw->cache_todo = g_list_reverse(list);
460 pw->cache_total = g_list_length(pw->cache_todo);
463 static void pan_cache_step_done_cb(CacheLoader *cl, gint error, gpointer data)
465 PanWindow *pw = data;
470 pc = pw->cache_list->data;
479 cache_loader_free(cl);
482 pan_window_layout_update_idle(pw);
485 static gint pan_cache_step(PanWindow *pw)
489 CacheDataType load_mask;
491 if (!pw->cache_todo) return TRUE;
493 fd = pw->cache_todo->data;
494 pw->cache_todo = g_list_remove(pw->cache_todo, fd);
497 if (enable_thumb_caching)
501 found = cache_find_location(CACHE_TYPE_SIM, fd->path);
502 if (found && filetime(found) == fd->date)
504 cd = cache_sim_data_load(found);
509 if (!cd) cd = cache_sim_data_new();
513 cd->dimensions = image_load_dimensions(fd->path, &cd->width, &cd->height);
514 if (enable_thumb_caching &&
520 base = cache_get_location(CACHE_TYPE_SIM, fd->path, FALSE, &mode);
521 if (cache_ensure_dir_exists(base, mode))
524 cd->path = cache_get_location(CACHE_TYPE_SIM, fd->path, TRUE, NULL);
525 if (cache_sim_data_save(cd))
527 filetime_set(cd->path, filetime(fd->path));
536 pc = g_new0(PanCacheData, 1);
537 memcpy(pc, fd, sizeof(FileData));
542 pw->cache_list = g_list_prepend(pw->cache_list, pc);
544 cache_loader_free(pw->cache_cl);
546 load_mask = CACHE_LOADER_NONE;
547 if (pw->size > LAYOUT_SIZE_THUMB_LARGE) load_mask |= CACHE_LOADER_DIMENSIONS;
548 if (pw->exif_date_enable) load_mask |= CACHE_LOADER_DATE;
549 pw->cache_cl = cache_loader_new(((FileData *)pc)->path, load_mask,
550 pan_cache_step_done_cb, pw);
551 return (pw->cache_cl == NULL);
554 /* This sync date function is optimized for lists with a common sort */
555 static void pan_cache_sync_date(PanWindow *pw, GList *list)
560 haystack = g_list_copy(pw->cache_list);
578 path = ((FileData *)pc)->path;
579 if (path && strcmp(path, fd->path) == 0)
581 if (pc->cd && pc->cd->have_date && pc->cd->date >= 0)
583 fd->date = pc->cd->date;
586 haystack = g_list_delete_link(haystack, needle);
591 needle = needle->next;
596 g_list_free(haystack);
600 *-----------------------------------------------------------------------------
602 *-----------------------------------------------------------------------------
605 static void pan_grid_clear(PanWindow *pw)
609 work = pw->list_grid;
617 g_list_free(pg->list);
621 g_list_free(pw->list_grid);
622 pw->list_grid = NULL;
624 pw->list = g_list_concat(pw->list, pw->list_static);
625 pw->list_static = NULL;
628 static void pan_grid_build(PanWindow *pw, gint width, gint height, gint grid_size)
641 l = g_list_length(pw->list);
645 total = (gdouble)width * (gdouble)height / (gdouble)l;
648 aw = (gdouble)width / s;
649 ah = (gdouble)height / s;
651 col = (gint)(sqrt((gdouble)l / grid_size) * width / height + 0.999);
652 col = CLAMP(col, 1, l / grid_size + 1);
653 row = (gint)((gdouble)l / grid_size / col);
654 if (row < 1) row = 1;
656 /* limit minimum size of grid so that a tile will always fit regardless of position */
657 cw = MAX((gint)ceil((gdouble)width / col), PAN_TILE_SIZE * 2);
658 ch = MAX((gint)ceil((gdouble)height / row), PAN_TILE_SIZE * 2);
663 printf("intersect speedup grid is %dx%d, based on %d average per grid\n", col, row, grid_size);
665 for (j = 0; j < row; j++)
666 for (i = 0; i < col; i++)
668 if ((i + 1) * cw / 2 < width && (j + 1) * ch / 2 < height)
672 pg = g_new0(PanGrid, 1);
679 pw->list_grid = g_list_prepend(pw->list_grid, pg);
681 if (debug) printf("grid section: %d,%d (%dx%d)\n", pg->x, pg->y, pg->w, pg->h);
694 grid = pw->list_grid;
703 if (util_clip_region(pi->x, pi->y, pi->width, pi->height,
704 pg->x, pg->y, pg->w, pg->h,
707 pg->list = g_list_prepend(pg->list, pi);
712 work = pw->list_grid;
720 pg->list = g_list_reverse(pg->list);
723 pw->list_static = pw->list;
730 *-----------------------------------------------------------------------------
732 *-----------------------------------------------------------------------------
735 static void pan_item_free(PanItem *pi)
739 if (pi->pixbuf) g_object_unref(pi->pixbuf);
740 if (pi->fd) file_data_free(pi->fd);
748 static void pan_window_items_free(PanWindow *pw)
757 PanItem *pi = work->data;
763 g_list_free(pw->list);
766 g_list_free(pw->queue);
770 image_loader_free(pw->il);
773 thumb_loader_free(pw->tl);
777 pw->search_pi = NULL;
780 static PanItem *pan_item_new_thumb(PanWindow *pw, FileData *fd, gint x, gint y)
784 pi = g_new0(PanItem, 1);
785 pi->type = ITEM_THUMB;
789 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
790 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
796 pw->list = g_list_prepend(pw->list, pi);
801 static PanItem *pan_item_new_box(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
803 guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
804 guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
808 pi = g_new0(PanItem, 1);
816 pi->color_r = base_r;
817 pi->color_g = base_g;
818 pi->color_b = base_b;
819 pi->color_a = base_a;
821 pi->color2_r = bord_r;
822 pi->color2_g = bord_g;
823 pi->color2_b = bord_b;
824 pi->color2_a = bord_a;
825 pi->border = border_size;
827 pw->list = g_list_prepend(pw->list, pi);
832 static void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
836 if (!pi || pi->type != ITEM_BOX) return;
841 pi->width -= shadow[0];
842 pi->height -= shadow[0];
845 shadow = g_new0(gint, 2);
850 pi->height += offset;
856 static PanItem *pan_item_new_tri(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
857 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
858 guint8 r, guint8 g, guint8 b, guint8 a)
863 pi = g_new0(PanItem, 1);
864 pi->type = ITEM_TRIANGLE;
875 coord = g_new0(gint, 6);
885 pi->border = BORDER_NONE;
887 pw->list = g_list_prepend(pw->list, pi);
892 static void pan_item_tri_border(PanItem *pi, gint borders,
893 guint8 r, guint8 g, guint8 b, guint8 a)
895 if (!pi || pi->type != ITEM_TRIANGLE) return;
897 pi->border = borders;
905 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
909 layout = gtk_widget_create_pango_layout(widget, NULL);
911 if (pi->text_attr & TEXT_ATTR_MARKUP)
913 pango_layout_set_markup(layout, pi->text, -1);
917 if (pi->text_attr & TEXT_ATTR_BOLD ||
918 pi->text_attr & TEXT_ATTR_HEADING)
923 pal = pango_attr_list_new();
924 if (pi->text_attr & TEXT_ATTR_BOLD)
926 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
928 pa->end_index = G_MAXINT;
929 pango_attr_list_insert(pal, pa);
931 if (pi->text_attr & TEXT_ATTR_HEADING)
933 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
935 pa->end_index = G_MAXINT;
936 pango_attr_list_insert(pal, pa);
938 pango_layout_set_attributes(layout, pal);
939 pango_attr_list_unref(pal);
942 pango_layout_set_text(layout, pi->text, -1);
946 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
950 if (!pi || !pi->text || !widget) return;
952 layout = pan_item_text_layout(pi, widget);
953 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
954 g_object_unref(G_OBJECT(layout));
956 pi->width += PAN_TEXT_BORDER_SIZE * 2;
957 pi->height += PAN_TEXT_BORDER_SIZE * 2;
960 static PanItem *pan_item_new_text(PanWindow *pw, gint x, gint y, const gchar *text, TextAttrType attr,
961 guint8 r, guint8 g, guint8 b, guint8 a)
965 pi = g_new0(PanItem, 1);
966 pi->type = ITEM_TEXT;
969 pi->text = g_strdup(text);
970 pi->text_attr = attr;
977 pan_item_text_compute_size(pi, pw->imd->pr);
979 pw->list = g_list_prepend(pw->list, pi);
984 static void pan_item_set_key(PanItem *pi, const gchar *key)
991 pi->key = g_strdup(key);
995 static void pan_item_added(PanWindow *pw, PanItem *pi)
998 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1001 static void pan_item_remove(PanWindow *pw, PanItem *pi)
1005 if (pw->click_pi == pi) pw->click_pi = NULL;
1006 if (pw->queue_pi == pi) pw->queue_pi = NULL;
1007 if (pw->search_pi == pi) pw->search_pi = NULL;
1008 pw->queue = g_list_remove(pw->queue, pi);
1010 pw->list = g_list_remove(pw->list, pi);
1011 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1015 static void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
1017 if (!pi || !child) return;
1019 if (pi->x + pi->width < child->x + child->width + border)
1020 pi->width = child->x + child->width + border - pi->x;
1022 if (pi->y + pi->height < child->y + child->height + border)
1023 pi->height = child->y + child->height + border - pi->y;
1026 static void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
1030 if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
1031 if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
1034 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
1041 if (!pi->fd) return;
1043 work = pw->cache_list;
1052 path = ((FileData *)pc)->path;
1054 if (pc->cd && pc->cd->dimensions &&
1055 path && strcmp(path, pi->fd->path) == 0)
1057 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
1058 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
1060 pw->cache_list = g_list_remove(pw->cache_list, pc);
1061 cache_sim_data_free(pc->cd);
1062 file_data_free((FileData *)pc);
1068 static PanItem *pan_item_new_image(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
1072 pi = g_new0(PanItem, 1);
1073 pi->type = ITEM_IMAGE;
1078 pan_item_image_find_size(pw, pi, w, h);
1080 pw->list = g_list_prepend(pw->list, pi);
1085 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
1089 if (!key) return NULL;
1091 work = g_list_last(pw->list);
1097 if ((pi->type == type || type == ITEM_NONE) &&
1098 pi->key && strcmp(pi->key, key) == 0)
1104 work = g_list_last(pw->list_static);
1110 if ((pi->type == type || type == ITEM_NONE) &&
1111 pi->key && strcmp(pi->key, key) == 0)
1121 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1122 static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
1123 ItemType type, const gchar *path,
1124 gint ignore_case, gint partial)
1128 work = g_list_last(search_list);
1134 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1140 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
1142 else if (pi->fd->name)
1150 haystack = g_utf8_strdown(pi->fd->name, -1);
1151 match = (strstr(haystack, path) != NULL);
1156 if (strstr(pi->fd->name, path)) match = TRUE;
1159 else if (ignore_case)
1161 if (strcasecmp(path, pi->fd->name) == 0) match = TRUE;
1165 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
1169 if (match) list = g_list_prepend(list, pi);
1177 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1178 static GList *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1179 gint ignore_case, gint partial)
1183 if (!path) return NULL;
1184 if (partial && path[0] == '/') return NULL;
1186 list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
1187 list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
1189 return g_list_reverse(list);
1192 static PanItem *pan_item_find_by_coord_l(GList *list, ItemType type, gint x, gint y, const gchar *key)
1202 if ((pi->type == type || type == ITEM_NONE) &&
1203 x >= pi->x && x < pi->x + pi->width &&
1204 y >= pi->y && y < pi->y + pi->height &&
1205 (!key || (pi->key && strcmp(pi->key, key) == 0)))
1215 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y, const gchar *key)
1219 pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
1222 return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
1226 *-----------------------------------------------------------------------------
1228 *-----------------------------------------------------------------------------
1231 static gint islink_loop(const gchar *s)
1237 sl = path_from_utf8(s);
1239 if (lstat(sl, &st) == 0 && S_ISLNK(st.st_mode))
1244 buf = g_malloc(st.st_size + 1);
1245 l = readlink(sl, buf, st.st_size);
1246 if (l == st.st_size)
1250 parse_out_relatives(buf);
1253 parse_out_relatives(sl);
1257 if (strncmp(sl, buf, l) == 0 &&
1258 (sl[l] == '\0' || sl[l] == '/' || l == 1)) ret = TRUE;
1264 link_path = concat_dir_and_file(sl, buf);
1265 parse_out_relatives(link_path);
1267 if (strncmp(sl, link_path, l) == 0 &&
1268 (sl[l] == '\0' || sl[l] == '/' || l == 1)) ret = TRUE;
1282 static gint is_ignored(const gchar *s, gint ignore_symlinks)
1287 if (!lstat_utf8(s, &st)) return TRUE;
1290 /* normal filesystems have directories with some size or block allocation,
1291 * special filesystems (like linux /proc) set both to zero.
1292 * enable this check if you enable listing the root "/" folder
1294 if (st.st_size == 0 && st.st_blocks == 0) return TRUE;
1297 if (S_ISLNK(st.st_mode) && (ignore_symlinks || islink_loop(s))) return TRUE;
1299 n = filename_from_path(s);
1300 if (n && strcmp(n, GQVIEW_RC_DIR) == 0) return TRUE;
1305 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend,
1306 gint ignore_symlinks)
1308 GList *flist = NULL;
1309 GList *dlist = NULL;
1313 filelist_read(path, &flist, &dlist);
1314 if (sort != SORT_NONE)
1316 flist = filelist_sort(flist, sort, ascend);
1317 dlist = filelist_sort(dlist, sort, ascend);
1327 folders = g_list_remove(folders, fd);
1329 if (!is_ignored(fd->path, ignore_symlinks) &&
1330 filelist_read(fd->path, &flist, &dlist))
1332 if (sort != SORT_NONE)
1334 flist = filelist_sort(flist, sort, ascend);
1335 dlist = filelist_sort(dlist, sort, ascend);
1338 result = g_list_concat(result, flist);
1339 folders = g_list_concat(dlist, folders);
1348 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1356 list = pan_window_layout_list(path, SORT_NAME, TRUE, pw->ignore_symlinks);
1358 grid_size = (gint)sqrt((double)g_list_length(list));
1359 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1361 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1365 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1370 *width = PAN_FOLDER_BOX_BORDER * 2;
1371 *height = PAN_FOLDER_BOX_BORDER * 2;
1384 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1386 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1388 x += pi->width + PAN_THUMB_GAP;
1389 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1398 pi = pan_item_new_thumb(pw, fd, x, y);
1400 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1404 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1407 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1413 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1416 gint x1, y1, x2, y2;
1431 if (x1 > pi->x) x1 = pi->x;
1432 if (y1 > pi->y) y1 = pi->y;
1433 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1434 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1437 x1 -= PAN_FOLDER_BOX_BORDER;
1438 y1 -= PAN_FOLDER_BOX_BORDER;
1439 x2 += PAN_FOLDER_BOX_BORDER;
1440 y2 += PAN_FOLDER_BOX_BORDER;
1453 if (pi->type == ITEM_TRIANGLE && pi->data)
1467 if (width) *width = x2 - x1;
1468 if (height) *height = y2 - y1;
1471 typedef struct _FlowerGroup FlowerGroup;
1472 struct _FlowerGroup {
1485 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1489 work = group->items;
1507 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1508 gint *result_x, gint *result_y)
1514 radius = parent->circumference / (2*PI);
1515 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1517 a = 2*PI * group->diameter / parent->circumference;
1519 x = (gint)((double)radius * cos(parent->angle + a / 2));
1520 y = (gint)((double)radius * sin(parent->angle + a / 2));
1527 x += parent->width / 2;
1528 y += parent->height / 2;
1530 x -= group->width / 2;
1531 y -= group->height / 2;
1537 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1544 if (parent && parent->children)
1546 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1554 pan_window_layout_compute_folder_flower_move(group, x, y);
1559 gint px, py, gx, gy;
1560 gint x1, y1, x2, y2;
1562 px = parent->x + parent->width / 2;
1563 py = parent->y + parent->height / 2;
1565 gx = group->x + group->width / 2;
1566 gy = group->y + group->height / 2;
1571 x2 = MAX(px, gx + 5);
1572 y2 = MAX(py, gy + 5);
1574 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1575 px, py, gx, gy, gx + 5, gy + 5,
1577 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1581 pw->list = g_list_concat(group->items, pw->list);
1582 group->items = NULL;
1584 group->circumference = 0;
1585 work = group->children;
1593 group->circumference += child->diameter;
1596 work = g_list_last(group->children);
1604 pan_window_layout_compute_folder_flower_build(pw, child, group);
1607 g_list_free(group->children);
1611 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1624 if (!filelist_read(path, &f, &d)) return NULL;
1625 if (!f && !d) return NULL;
1627 f = filelist_sort(f, SORT_NAME, TRUE);
1628 d = filelist_sort(d, SORT_NAME, TRUE);
1630 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1631 PAN_TEXT_COLOR, 255);
1633 y += pi_box->height;
1635 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1637 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1638 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1639 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1640 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1642 x += PAN_FOLDER_BOX_BORDER;
1643 y += PAN_FOLDER_BOX_BORDER;
1645 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1659 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1661 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1662 x += pi->width + PAN_THUMB_GAP;
1663 if (pi->height > y_height) y_height = pi->height;
1667 pi = pan_item_new_thumb(pw, fd, x, y);
1668 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1669 y_height = PAN_THUMB_SIZE;
1673 if (grid_count >= grid_size)
1677 y += y_height + PAN_THUMB_GAP;
1681 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1684 group = g_new0(FlowerGroup, 1);
1685 group->items = pw->list;
1688 group->width = pi_box->width;
1689 group->height = pi_box->y + pi_box->height;
1690 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1692 group->children = NULL;
1703 if (!is_ignored(fd->path, pw->ignore_symlinks))
1705 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1706 if (child) group->children = g_list_prepend(group->children, child);
1710 if (!f && !group->children)
1712 work = group->items;
1723 g_list_free(group->items);
1734 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1735 gint *width, gint *height,
1736 gint *scroll_x, gint *scroll_y)
1741 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1742 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1744 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1746 list = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1749 PanItem *pi = list->data;
1750 *scroll_x = pi->x + pi->width / 2;
1751 *scroll_y = pi->y + pi->height / 2;
1756 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1757 gint *x, gint *y, gint *level,
1759 gint *width, gint *height)
1767 if (!filelist_read(path, &f, &d)) return;
1768 if (!f && !d) return;
1770 f = filelist_sort(f, SORT_NAME, TRUE);
1771 d = filelist_sort(d, SORT_NAME, TRUE);
1773 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1775 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1776 PAN_TEXT_COLOR, 255);
1778 *y += pi_box->height;
1780 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1782 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1783 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1784 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1785 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1787 *x += PAN_FOLDER_BOX_BORDER;
1788 *y += PAN_FOLDER_BOX_BORDER;
1799 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1801 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1802 *x += pi->width + PAN_THUMB_GAP;
1803 if (pi->height > y_height) y_height = pi->height;
1807 pi = pan_item_new_thumb(pw, fd, *x, *y);
1808 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1809 y_height = PAN_THUMB_SIZE;
1812 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1815 if (f) *y = pi_box->y + pi_box->height;
1827 if (!is_ignored(fd->path, pw->ignore_symlinks))
1829 *level = *level + 1;
1830 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1831 pi_box, width, height);
1832 *level = *level - 1;
1838 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1840 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1841 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1843 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1846 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1853 x = PAN_FOLDER_BOX_BORDER;
1854 y = PAN_FOLDER_BOX_BORDER;
1855 w = PAN_FOLDER_BOX_BORDER * 2;
1856 h = PAN_FOLDER_BOX_BORDER * 2;
1858 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1860 if (width) *width = w;
1861 if (height) *height = h;
1865 *-----------------------------------------------------------------------------
1867 *-----------------------------------------------------------------------------
1870 static void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
1876 gint x1, y1, x2, y2, x3, y3;
1881 while ((pi = pan_item_find_by_key(pw, ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
1883 if (!pi_day || pi_day->type != ITEM_BOX ||
1884 !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
1886 list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
1898 if (dot->type != ITEM_BOX || !dot->fd ||
1899 !dot->key || strcmp(dot->key, "dot") != 0)
1901 list = g_list_delete_link(list, node);
1909 grid = (gint)(sqrt(g_list_length(list)) + 0.5);
1911 x = pi_day->x + pi_day->width + 4;
1915 if (y + grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4 > pw->pr->image_height)
1917 y = pw->pr->image_height - (grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4);
1921 pbox = pan_item_new_box(pw, NULL, x, y, PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1922 PAN_CAL_POPUP_BORDER,
1923 PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA,
1924 PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
1925 pan_item_set_key(pbox, "day_bubble");
1932 buf = date_value_string(pi_day->fd->date, DATE_LENGTH_WEEK);
1933 plabel = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1934 PAN_CAL_POPUP_TEXT_COLOR, 255);
1935 pan_item_set_key(plabel, "day_bubble");
1938 pan_item_size_by_item(pbox, plabel, 0);
1940 y += plabel->height;
1947 x += PAN_FOLDER_BOX_BORDER;
1948 y += PAN_FOLDER_BOX_BORDER;
1962 pimg = pan_item_new_thumb(pw, file_data_new_simple(dot->fd->path), x, y);
1963 pan_item_set_key(pimg, "day_bubble");
1965 pan_item_size_by_item(pbox, pimg, PAN_FOLDER_BOX_BORDER);
1970 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1975 x = pbox->x + PAN_FOLDER_BOX_BORDER;
1976 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1982 x1 = pi_day->x + pi_day->width - 8;
1985 y2 = pbox->y + MIN(42, pbox->height);
1987 y3 = MAX(pbox->y, y2 - 30);
1988 util_clip_triangle(x1, y1, x2, y2, x3, y3,
1991 pi = pan_item_new_tri(pw, NULL, x, y, w, h,
1992 x1, y1, x2, y2, x3, y3,
1993 PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA);
1994 pan_item_tri_border(pi, BORDER_1 | BORDER_3, PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
1995 pan_item_set_key(pi, "day_bubble");
1996 pan_item_added(pw, pi);
1998 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
1999 pan_item_added(pw, pbox);
2002 static void pan_window_layout_compute_calendar(PanWindow *pw, const gchar *path, gint *width, gint *height)
2018 list = pan_window_layout_list(path, SORT_NONE, TRUE, pw->ignore_symlinks);
2020 if (pw->cache_list && pw->exif_date_enable)
2022 pw->cache_list = filelist_sort(pw->cache_list, SORT_NAME, TRUE);
2023 list = filelist_sort(list, SORT_NAME, TRUE);
2024 pan_cache_sync_date(pw, list);
2027 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2028 list = filelist_sort(list, SORT_TIME, TRUE);
2041 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2049 if (day_max < count) day_max = count;
2053 printf("biggest day contains %d images\n", day_max);
2055 grid = (gint)(sqrt((double)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
2056 day_width = MAX(PAN_CAL_DAY_WIDTH, grid);
2057 day_height = MAX(PAN_CAL_DAY_HEIGHT, grid);
2061 FileData *fd = list->data;
2063 year = date_value(fd->date, DATE_LENGTH_YEAR);
2064 month = date_value(fd->date, DATE_LENGTH_MONTH);
2067 work = g_list_last(list);
2070 FileData *fd = work->data;
2071 end_year = date_value(fd->date, DATE_LENGTH_YEAR);
2072 end_month = date_value(fd->date, DATE_LENGTH_MONTH);
2075 *width = PAN_FOLDER_BOX_BORDER * 2;
2076 *height = PAN_FOLDER_BOX_BORDER * 2;
2078 x = PAN_FOLDER_BOX_BORDER;
2079 y = PAN_FOLDER_BOX_BORDER;
2082 while (work && (year < end_year || (year == end_year && month <= end_month)))
2093 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
2095 days = date_value(dt, DATE_LENGTH_DAY);
2096 dt = date_to_time(year, month, 1);
2097 col = date_value(dt, DATE_LENGTH_WEEK);
2100 x = PAN_FOLDER_BOX_BORDER;
2102 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
2103 PAN_CAL_MONTH_BORDER,
2104 PAN_CAL_MONTH_COLOR, PAN_CAL_MONTH_ALPHA,
2105 PAN_CAL_MONTH_BORDER_COLOR, PAN_CAL_MONTH_ALPHA);
2106 buf = date_value_string(dt, DATE_LENGTH_MONTH);
2107 pi_text = pan_item_new_text(pw, x, y, buf,
2108 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2109 PAN_CAL_MONTH_TEXT_COLOR, 255);
2111 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
2113 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
2115 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
2116 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
2118 for (day = 1; day <= days; day++)
2125 dt = date_to_time(year, month, day);
2127 fd = g_new0(FileData, 1);
2128 /* path and name must be non NULL, so make them an invalid filename */
2129 fd->path = g_strdup("//");
2132 pi_day = pan_item_new_box(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
2134 PAN_CAL_DAY_COLOR, PAN_CAL_DAY_ALPHA,
2135 PAN_CAL_DAY_BORDER_COLOR, PAN_CAL_DAY_ALPHA);
2136 pan_item_set_key(pi_day, "day");
2138 dx = x + PAN_CAL_DOT_GAP * 2;
2139 dy = y + PAN_CAL_DOT_GAP * 2;
2141 fd = (work) ? work->data : NULL;
2142 while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
2146 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
2148 PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
2150 pan_item_set_key(pi, "dot");
2152 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2153 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
2155 dx = x + PAN_CAL_DOT_GAP * 2;
2156 dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2158 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
2160 /* must keep all dots within respective day even if it gets ugly */
2161 dy = y + PAN_CAL_DOT_GAP * 2;
2167 fd = (work) ? work->data : NULL;
2174 pi_day->color_r = MAX(pi_day->color_r - 61 - n * 3, 80);
2175 pi_day->color_g = pi_day->color_r;
2177 buf = g_strdup_printf("( %d )", n);
2178 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2179 PAN_CAL_DAY_TEXT_COLOR, 255);
2182 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
2183 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
2186 buf = g_strdup_printf("%d", day);
2187 pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2188 PAN_CAL_DAY_TEXT_COLOR, 255);
2192 pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
2199 x = PAN_FOLDER_BOX_BORDER;
2200 y += PAN_CAL_DAY_HEIGHT;
2204 x += PAN_CAL_DAY_WIDTH;
2208 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
2209 y += PAN_FOLDER_BOX_BORDER * 2;
2220 *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
2225 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
2233 PanItem *pi_month = NULL;
2234 PanItem *pi_day = NULL;
2240 list = pan_window_layout_list(path, SORT_NONE, TRUE, pw->ignore_symlinks);
2242 if (pw->cache_list && pw->exif_date_enable)
2244 pw->cache_list = filelist_sort(pw->cache_list, SORT_NAME, TRUE);
2245 list = filelist_sort(list, SORT_NAME, TRUE);
2246 pan_cache_sync_date(pw, list);
2249 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2250 list = filelist_sort(list, SORT_TIME, TRUE);
2252 *width = PAN_FOLDER_BOX_BORDER * 2;
2253 *height = PAN_FOLDER_BOX_BORDER * 2;
2258 day_start = month_start;
2273 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2278 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
2284 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
2288 x = PAN_FOLDER_BOX_BORDER;
2291 y = PAN_FOLDER_BOX_BORDER;
2293 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
2294 pi = pan_item_new_text(pw, x, y, buf,
2295 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2296 PAN_TEXT_COLOR, 255);
2300 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
2302 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2303 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2304 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2306 x += PAN_FOLDER_BOX_BORDER;
2307 y += PAN_FOLDER_BOX_BORDER;
2311 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
2323 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
2325 needle = needle->next;
2334 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
2335 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2336 PAN_TEXT_COLOR, 255);
2341 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
2342 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2343 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2344 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2346 x += PAN_FOLDER_BOX_BORDER;
2347 y += PAN_FOLDER_BOX_BORDER;
2351 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2353 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
2354 if (pi->width > x_width) x_width = pi->width;
2355 y_height = pi->height;
2359 pi = pan_item_new_thumb(pw, fd, x, y);
2360 x_width = PAN_THUMB_SIZE;
2361 y_height = PAN_THUMB_SIZE;
2364 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
2365 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
2370 if (total > 0 && count < PAN_GROUP_MAX)
2372 y += y_height + PAN_THUMB_GAP;
2376 x += x_width + PAN_THUMB_GAP;
2386 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
2392 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
2393 gint *width, gint *height,
2394 gint *scroll_x, gint *scroll_y)
2396 pan_window_items_free(pw);
2400 case LAYOUT_SIZE_THUMB_DOTS:
2401 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2402 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2404 case LAYOUT_SIZE_THUMB_NONE:
2405 pw->thumb_size = PAN_THUMB_SIZE_NONE;
2406 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2408 case LAYOUT_SIZE_THUMB_SMALL:
2409 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2410 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2412 case LAYOUT_SIZE_THUMB_NORMAL:
2414 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2415 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2417 case LAYOUT_SIZE_THUMB_LARGE:
2418 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2419 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2421 case LAYOUT_SIZE_10:
2422 pw->image_size = 10;
2423 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2425 case LAYOUT_SIZE_25:
2426 pw->image_size = 25;
2427 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2429 case LAYOUT_SIZE_33:
2430 pw->image_size = 33;
2431 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2433 case LAYOUT_SIZE_50:
2434 pw->image_size = 50;
2435 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2437 case LAYOUT_SIZE_100:
2438 pw->image_size = 100;
2439 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2452 pan_window_layout_compute_grid(pw, path, width, height);
2454 case LAYOUT_FOLDERS_LINEAR:
2455 pan_window_layout_compute_folders_linear(pw, path, width, height);
2457 case LAYOUT_FOLDERS_FLOWER:
2458 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2460 case LAYOUT_CALENDAR:
2461 pan_window_layout_compute_calendar(pw, path, width, height);
2463 case LAYOUT_TIMELINE:
2464 pan_window_layout_compute_timeline(pw, path, width, height);
2470 printf("computed %d objects\n", g_list_length(pw->list));
2473 static GList *pan_layout_intersect_l(GList *list, GList *item_list,
2474 gint x, gint y, gint width, gint height)
2482 gint rx, ry, rw, rh;
2487 if (util_clip_region(x, y, width, height,
2488 pi->x, pi->y, pi->width, pi->height,
2489 &rx, &ry, &rw, &rh))
2491 list = g_list_prepend(list, pi);
2498 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2504 grid = pw->list_grid;
2510 if (x < pg->x || x + width > pg->x + pg->w ||
2511 y < pg->y || y + height > pg->y + pg->h)
2517 list = pan_layout_intersect_l(list, pw->list, x, y, width, height);
2521 list = pan_layout_intersect_l(list, pg->list, x, y, width, height);
2525 list = pan_layout_intersect_l(list, pw->list_static, x, y, width, height);
2532 *-----------------------------------------------------------------------------
2534 *-----------------------------------------------------------------------------
2537 static gint pan_layout_queue_step(PanWindow *pw);
2540 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2542 PanWindow *pw = data;
2550 pw->queue_pi = NULL;
2554 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2555 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2558 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2562 thumb_loader_free(pw->tl);
2565 while (pan_layout_queue_step(pw));
2568 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2570 PanWindow *pw = data;
2578 pw->queue_pi = NULL;
2582 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2583 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2584 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2586 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2587 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2588 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2593 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2594 (GdkInterpType)zoom_quality);
2595 g_object_unref(tmp);
2599 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2603 image_loader_free(pw->il);
2606 while (pan_layout_queue_step(pw));
2610 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2611 guint width, guint height, gpointer data)
2613 PanWindow *pw = data;
2624 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2625 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2629 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2635 static gint pan_layout_queue_step(PanWindow *pw)
2639 if (!pw->queue) return FALSE;
2641 pi = pw->queue->data;
2642 pw->queue = g_list_remove(pw->queue, pi);
2645 if (!pw->queue_pi->fd)
2647 pw->queue_pi->queued = FALSE;
2648 pw->queue_pi = NULL;
2652 image_loader_free(pw->il);
2654 thumb_loader_free(pw->tl);
2657 if (pi->type == ITEM_IMAGE)
2659 pw->il = image_loader_new(pi->fd->path);
2661 if (pw->size != LAYOUT_SIZE_100)
2663 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2667 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2669 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2671 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2673 image_loader_free(pw->il);
2676 else if (pi->type == ITEM_THUMB)
2678 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2680 if (!pw->tl->standard_loader)
2682 /* The classic loader will recreate a thumbnail any time we
2683 * request a different size than what exists. This view will
2684 * almost never use the user configured sizes so disable cache.
2686 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2689 thumb_loader_set_callbacks(pw->tl,
2690 pan_layout_queue_thumb_done_cb,
2691 pan_layout_queue_thumb_done_cb,
2694 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2696 thumb_loader_free(pw->tl);
2700 pw->queue_pi->queued = FALSE;
2701 pw->queue_pi = NULL;
2705 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2707 if (!pi || pi->queued || pi->pixbuf) return;
2708 if (pw->size <= LAYOUT_SIZE_THUMB_NONE) return;
2711 pw->queue = g_list_prepend(pw->queue, pi);
2713 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2716 static gint pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
2717 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2719 PanWindow *pw = data;
2724 pixbuf_set_rect_fill(pixbuf,
2725 0, 0, width, height,
2726 PAN_BACKGROUND_COLOR, 255);
2728 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2730 gint rx, ry, rw, rh;
2732 if (util_clip_region(x, y, width, height,
2734 &rx, &ry, &rw, &rh))
2736 pixbuf_draw_rect_fill(pixbuf,
2737 rx - x, ry - y, rw, rh,
2738 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2741 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2743 gint rx, ry, rw, rh;
2745 if (util_clip_region(x, y, width, height,
2747 &rx, &ry, &rw, &rh))
2749 pixbuf_draw_rect_fill(pixbuf,
2750 rx - x, ry - y, rw, rh,
2751 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2755 list = pan_layout_intersect(pw, x, y, width, height);
2760 gint tx, ty, tw, th;
2761 gint rx, ry, rw, rh;
2768 if (pi->type == ITEM_THUMB && pi->pixbuf)
2770 tw = gdk_pixbuf_get_width(pi->pixbuf);
2771 th = gdk_pixbuf_get_height(pi->pixbuf);
2773 tx = pi->x + (pi->width - tw) / 2;
2774 ty = pi->y + (pi->height - th) / 2;
2776 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2778 if (util_clip_region(x, y, width, height,
2779 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2780 &rx, &ry, &rw, &rh))
2782 pixbuf_draw_shadow(pixbuf,
2783 rx - x, ry - y, rw, rh,
2784 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2786 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2791 if (util_clip_region(x, y, width, height,
2792 tx + tw, ty + PAN_SHADOW_OFFSET,
2793 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2794 &rx, &ry, &rw, &rh))
2796 pixbuf_draw_shadow(pixbuf,
2797 rx - x, ry - y, rw, rh,
2798 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2800 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2802 if (util_clip_region(x, y, width, height,
2803 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2804 &rx, &ry, &rw, &rh))
2806 pixbuf_draw_shadow(pixbuf,
2807 rx - x, ry - y, rw, rh,
2808 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2810 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2814 if (util_clip_region(x, y, width, height,
2816 &rx, &ry, &rw, &rh))
2818 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2821 1.0, 1.0, GDK_INTERP_NEAREST,
2825 if (util_clip_region(x, y, width, height,
2826 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2827 &rx, &ry, &rw, &rh))
2829 pixbuf_draw_rect_fill(pixbuf,
2830 rx - x, ry - y, rw, rh,
2831 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2833 if (util_clip_region(x, y, width, height,
2834 tx, ty, PAN_OUTLINE_THICKNESS, th,
2835 &rx, &ry, &rw, &rh))
2837 pixbuf_draw_rect_fill(pixbuf,
2838 rx - x, ry - y, rw, rh,
2839 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2841 if (util_clip_region(x, y, width, height,
2842 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2843 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2844 &rx, &ry, &rw, &rh))
2846 pixbuf_draw_rect_fill(pixbuf,
2847 rx - x, ry - y, rw, rh,
2848 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2850 if (util_clip_region(x, y, width, height,
2851 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2852 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2853 &rx, &ry, &rw, &rh))
2855 pixbuf_draw_rect_fill(pixbuf,
2856 rx - x, ry - y, rw, rh,
2857 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2860 else if (pi->type == ITEM_THUMB)
2862 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2863 th = pi->height - PAN_SHADOW_OFFSET * 2;
2864 tx = pi->x + PAN_SHADOW_OFFSET;
2865 ty = pi->y + PAN_SHADOW_OFFSET;
2867 if (util_clip_region(x, y, width, height,
2869 &rx, &ry, &rw, &rh))
2873 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2874 pixbuf_draw_rect_fill(pixbuf,
2875 rx - x, ry - y, rw, rh,
2877 PAN_SHADOW_ALPHA / d);
2880 pan_layout_queue(pw, pi);
2882 else if (pi->type == ITEM_IMAGE)
2884 if (util_clip_region(x, y, width, height,
2885 pi->x, pi->y, pi->width, pi->height,
2886 &rx, &ry, &rw, &rh))
2890 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2893 1.0, 1.0, GDK_INTERP_NEAREST,
2898 pixbuf_draw_rect_fill(pixbuf,
2899 rx - x, ry - y, rw, rh,
2900 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2901 pan_layout_queue(pw, pi);
2905 else if (pi->type == ITEM_BOX)
2919 if (pi->color_a > 254)
2921 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2922 shadow[0], bh - shadow[0],
2923 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2925 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2926 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2928 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2930 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2935 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2936 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2938 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2940 PAN_SHADOW_COLOR, a);
2944 if (util_clip_region(x, y, width, height,
2945 pi->x, pi->y, bw, bh,
2946 &rx, &ry, &rw, &rh))
2948 pixbuf_draw_rect_fill(pixbuf,
2949 rx - x, ry - y, rw, rh,
2950 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2952 if (util_clip_region(x, y, width, height,
2953 pi->x, pi->y, bw, pi->border,
2954 &rx, &ry, &rw, &rh))
2956 pixbuf_draw_rect_fill(pixbuf,
2957 rx - x, ry - y, rw, rh,
2958 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2960 if (util_clip_region(x, y, width, height,
2961 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
2962 &rx, &ry, &rw, &rh))
2964 pixbuf_draw_rect_fill(pixbuf,
2965 rx - x, ry - y, rw, rh,
2966 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2968 if (util_clip_region(x, y, width, height,
2969 pi->x + bw - pi->border, pi->y + pi->border,
2970 pi->border, bh - pi->border * 2,
2971 &rx, &ry, &rw, &rh))
2973 pixbuf_draw_rect_fill(pixbuf,
2974 rx - x, ry - y, rw, rh,
2975 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2977 if (util_clip_region(x, y, width, height,
2978 pi->x, pi->y + bh - pi->border,
2980 &rx, &ry, &rw, &rh))
2982 pixbuf_draw_rect_fill(pixbuf,
2983 rx - x, ry - y, rw, rh,
2984 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2987 else if (pi->type == ITEM_TRIANGLE)
2989 if (util_clip_region(x, y, width, height,
2990 pi->x, pi->y, pi->width, pi->height,
2991 &rx, &ry, &rw, &rh) && pi->data)
2993 gint *coord = pi->data;
2994 pixbuf_draw_triangle(pixbuf,
2995 rx - x, ry - y, rw, rh,
2996 coord[0] - x, coord[1] - y,
2997 coord[2] - x, coord[3] - y,
2998 coord[4] - x, coord[5] - y,
2999 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3001 if (pi->border & BORDER_1)
3003 pixbuf_draw_line(pixbuf,
3004 rx - x, ry - y, rw, rh,
3005 coord[0] - x, coord[1] - y,
3006 coord[2] - x, coord[3] - y,
3007 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3009 if (pi->border & BORDER_2)
3011 pixbuf_draw_line(pixbuf,
3012 rx - x, ry - y, rw, rh,
3013 coord[2] - x, coord[3] - y,
3014 coord[4] - x, coord[5] - y,
3015 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3017 if (pi->border & BORDER_3)
3019 pixbuf_draw_line(pixbuf,
3020 rx - x, ry - y, rw, rh,
3021 coord[4] - x, coord[5] - y,
3022 coord[0] - x, coord[1] - y,
3023 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3027 else if (pi->type == ITEM_TEXT && pi->text)
3029 PangoLayout *layout;
3031 layout = pan_item_text_layout(pi, (GtkWidget *)pr);
3032 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
3033 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
3034 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3035 g_object_unref(G_OBJECT(layout));
3041 if (x%512 == 0 && y%512 == 0)
3043 PangoLayout *layout;
3046 layout = gtk_widget_create_pango_layout((GtkWidget *)pr, NULL);
3048 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
3049 (x / pr->source_tile_width) +
3050 (y / pr->source_tile_height * (pr->image_width/pr->source_tile_width + 1)));
3051 pango_layout_set_text(layout, buf, -1);
3054 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr, 0, 0, 0, 0, 0, 255);
3056 g_object_unref(G_OBJECT(layout));
3063 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
3064 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
3066 PanWindow *pw = data;
3070 list = pan_layout_intersect(pw, x, y, width, height);
3079 if (pi->refcount > 0)
3083 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
3088 pw->queue = g_list_remove(pw->queue, pi);
3091 if (pw->queue_pi == pi) pw->queue_pi = NULL;
3094 g_object_unref(pi->pixbuf);
3106 *-----------------------------------------------------------------------------
3108 *-----------------------------------------------------------------------------
3111 static void pan_window_message(PanWindow *pw, const gchar *text)
3121 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
3125 work = pw->list_static;
3126 if (pw->layout == LAYOUT_CALENDAR)
3136 pi->type == ITEM_BOX &&
3137 pi->key && strcmp(pi->key, "dot") == 0)
3139 size += pi->fd->size;
3154 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
3156 size += pi->fd->size;
3162 ss = text_from_size_abrev(size);
3163 buf = g_strdup_printf(_("%d images, %s"), count, ss);
3165 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
3169 static void pan_warning_folder(const gchar *path, GtkWidget *parent)
3173 message = g_strdup_printf(_("The pan view does not support the folder \"%s\"."), path);
3174 warning_dialog(_("Folder not supported"), message,
3175 GTK_STOCK_DIALOG_INFO, parent);
3179 static void pan_window_zoom_limit(PanWindow *pw)
3185 case LAYOUT_SIZE_THUMB_DOTS:
3186 case LAYOUT_SIZE_THUMB_NONE:
3187 case LAYOUT_SIZE_THUMB_SMALL:
3188 case LAYOUT_SIZE_THUMB_NORMAL:
3190 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
3194 case LAYOUT_SIZE_THUMB_LARGE:
3197 case LAYOUT_SIZE_10:
3198 case LAYOUT_SIZE_25:
3201 case LAYOUT_SIZE_33:
3202 case LAYOUT_SIZE_50:
3203 case LAYOUT_SIZE_100:
3209 image_zoom_set_limits(pw->imd, min, 32.0);
3212 static gint pan_window_layout_update_idle_cb(gpointer data)
3214 PanWindow *pw = data;
3220 if (pw->size > LAYOUT_SIZE_THUMB_LARGE ||
3221 (pw->exif_date_enable && (pw->layout == LAYOUT_TIMELINE || pw->layout == LAYOUT_CALENDAR)))
3223 if (!pw->cache_list && !pw->cache_todo)
3225 pan_cache_fill(pw, pw->path);
3228 pan_window_message(pw, _("Reading image data..."));
3236 if (pw->cache_count == pw->cache_total)
3238 pan_window_message(pw, _("Sorting..."));
3240 else if (pw->cache_tick > 9)
3244 buf = g_strdup_printf("%s %d", _("Reading image data..."),
3245 pw->cache_total - pw->cache_count);
3246 pan_window_message(pw, buf);
3252 if (pan_cache_step(pw)) return TRUE;
3259 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3261 pan_window_zoom_limit(pw);
3263 if (width > 0 && height > 0)
3267 printf("Canvas size is %d x %d\n", width, height);
3269 pan_grid_build(pw, width, height, 1000);
3271 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
3272 PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
3273 pan_window_request_tile_cb,
3274 pan_window_dispose_tile_cb, pw, 1.0);
3276 if (scroll_x == 0 && scroll_y == 0)
3284 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
3287 pan_window_message(pw, NULL);
3293 static void pan_window_layout_update_idle(PanWindow *pw)
3295 if (pw->idle_id == -1)
3297 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3301 static void pan_window_layout_update(PanWindow *pw)
3303 pan_window_message(pw, _("Sorting images..."));
3304 pan_window_layout_update_idle(pw);
3307 static void pan_window_layout_set_path(PanWindow *pw, const gchar *path)
3311 if (strcmp(path, "/") == 0)
3313 pan_warning_folder(path, pw->window);
3318 pw->path = g_strdup(path);
3320 pan_window_layout_update(pw);
3324 *-----------------------------------------------------------------------------
3325 * pan window keyboard
3326 *-----------------------------------------------------------------------------
3329 static const gchar *pan_menu_click_path(PanWindow *pw)
3331 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3335 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3337 PanWindow *pw = data;
3339 gdk_window_get_origin(pw->imd->pr->window, x, y);
3340 popup_menu_position_clamp(menu, x, y, 0);
3343 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3345 PanWindow *pw = data;
3348 gint stop_signal = FALSE;
3354 pr = PIXBUF_RENDERER(pw->imd->pr);
3355 path = pan_menu_click_path(pw);
3357 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pw->imd->widget)));
3361 switch (event->keyval)
3363 case GDK_Left: case GDK_KP_Left:
3367 case GDK_Right: case GDK_KP_Right:
3371 case GDK_Up: case GDK_KP_Up:
3375 case GDK_Down: case GDK_KP_Down:
3379 case GDK_Page_Up: case GDK_KP_Page_Up:
3380 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
3382 case GDK_Page_Down: case GDK_KP_Page_Down:
3383 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
3385 case GDK_Home: case GDK_KP_Home:
3386 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
3388 case GDK_End: case GDK_KP_End:
3389 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
3394 if (focused && !(event->state & GDK_CONTROL_MASK) )
3395 switch (event->keyval)
3397 case '+': case '=': case GDK_KP_Add:
3398 pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
3400 case '-': case GDK_KP_Subtract:
3401 pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
3403 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3404 pixbuf_renderer_zoom_set(pr, 1.0);
3407 pixbuf_renderer_zoom_set(pr, 2.0);
3410 pixbuf_renderer_zoom_set(pr, 3.0);
3413 pixbuf_renderer_zoom_set(pr, 4.0);
3416 pixbuf_renderer_zoom_set(pr, -4.0);
3419 pixbuf_renderer_zoom_set(pr, -3.0);
3422 pixbuf_renderer_zoom_set(pr, -2.0);
3426 pan_fullscreen_toggle(pw, FALSE);
3431 pan_overlay_toggle(pw);
3434 case GDK_Delete: case GDK_KP_Delete:
3439 if (GTK_WIDGET_VISIBLE(pw->search_box))
3441 gtk_widget_grab_focus(pw->search_entry);
3445 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3453 pan_fullscreen_toggle(pw, TRUE);
3456 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3458 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3464 menu = pan_popup_menu(pw);
3465 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3470 if (event->state & GDK_CONTROL_MASK)
3473 switch (event->keyval)
3506 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3509 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3512 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3515 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3518 if (path) info_window_new(path, NULL);
3521 pan_window_close(pw);
3524 if (n != -1 && path)
3526 if (!editor_window_flag_set(n))
3528 pan_fullscreen_toggle(pw, TRUE);
3530 start_editor_from_file(n, path);
3534 else if (event->state & GDK_SHIFT_MASK)
3541 switch (event->keyval)
3546 pan_fullscreen_toggle(pw, TRUE);
3549 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3551 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
3552 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3561 if (x != 0 || y!= 0)
3563 keyboard_scroll_calc(&x, &y, event);
3564 pixbuf_renderer_scroll(pr, x, y);
3571 *-----------------------------------------------------------------------------
3573 *-----------------------------------------------------------------------------
3576 static void pan_info_update(PanWindow *pw, PanItem *pi)
3582 gint x1, y1, x2, y2, x3, y3;
3585 if (pw->click_pi == pi) return;
3586 if (pi && !pi->fd) pi = NULL;
3588 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3593 if (debug) printf("info set to %s\n", pi->fd->path);
3595 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3597 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3598 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3599 pan_item_set_key(pbox, "info");
3601 if (pi->type == ITEM_THUMB && pi->pixbuf)
3603 w = gdk_pixbuf_get_width(pi->pixbuf);
3604 h = gdk_pixbuf_get_height(pi->pixbuf);
3606 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3607 y1 = pi->y + (pi->height - h) / 2 + 8;
3611 x1 = pi->x + pi->width - 8;
3619 util_clip_triangle(x1, y1, x2, y2, x3, y3,
3622 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3623 x1, y1, x2, y2, x3, y3,
3624 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3625 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3626 pan_item_set_key(p, "info");
3627 pan_item_added(pw, p);
3629 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3630 _("Filename:"), TEXT_ATTR_BOLD,
3631 PAN_POPUP_TEXT_COLOR, 255);
3632 pan_item_set_key(plabel, "info");
3633 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3634 pi->fd->name, TEXT_ATTR_NONE,
3635 PAN_POPUP_TEXT_COLOR, 255);
3636 pan_item_set_key(p, "info");
3637 pan_item_size_by_item(pbox, p, 0);
3639 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3640 _("Date:"), TEXT_ATTR_BOLD,
3641 PAN_POPUP_TEXT_COLOR, 255);
3642 pan_item_set_key(plabel, "info");
3643 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3644 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3645 PAN_POPUP_TEXT_COLOR, 255);
3646 pan_item_set_key(p, "info");
3647 pan_item_size_by_item(pbox, p, 0);
3649 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3650 _("Size:"), TEXT_ATTR_BOLD,
3651 PAN_POPUP_TEXT_COLOR, 255);
3652 pan_item_set_key(plabel, "info");
3653 buf = text_from_size(pi->fd->size);
3654 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3655 buf, TEXT_ATTR_NONE,
3656 PAN_POPUP_TEXT_COLOR, 255);
3658 pan_item_set_key(p, "info");
3659 pan_item_size_by_item(pbox, p, 0);
3661 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3662 pan_item_added(pw, pbox);
3667 *-----------------------------------------------------------------------------
3669 *-----------------------------------------------------------------------------
3672 static void pan_search_status(PanWindow *pw, const gchar *text)
3674 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3677 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3685 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3687 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3688 if (!list) return FALSE;
3690 found = g_list_find(list, pw->click_pi);
3691 if (found && found->next)
3693 found = found->next;
3701 pan_info_update(pw, pi);
3702 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3704 buf = g_strdup_printf("%s ( %d / %d )",
3705 (path[0] == '/') ? _("path found") : _("filename found"),
3706 g_list_index(list, pi) + 1,
3707 g_list_length(list));
3708 pan_search_status(pw, buf);
3716 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3724 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3726 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3727 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3732 needle = g_utf8_strdown(text, -1);
3733 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3736 if (!list) return FALSE;
3738 found = g_list_find(list, pw->click_pi);
3739 if (found && found->next)
3741 found = found->next;
3749 pan_info_update(pw, pi);
3750 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3752 buf = g_strdup_printf("%s ( %d / %d )",
3754 g_list_index(list, pi) + 1,
3755 g_list_length(list));
3756 pan_search_status(pw, buf);
3764 static gint valid_date_separator(gchar c)
3766 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3769 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
3770 gint year, gint month, gint day,
3776 work = g_list_last(pw->list_static);
3784 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
3785 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
3789 tl = localtime(&pi->fd->date);
3794 match = (tl->tm_year == year - 1900);
3795 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3796 if (match && day > 0) match = (tl->tm_mday == day);
3798 if (match) list = g_list_prepend(list, pi);
3803 return g_list_reverse(list);
3806 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3822 if (!text) return FALSE;
3824 ptr = (gchar *)text;
3825 while (*ptr != '\0')
3827 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3832 if (t == -1) return FALSE;
3834 if (!lt) return FALSE;
3836 if (valid_date_separator(*text))
3839 mptr = (gchar *)text;
3843 year = (gint)strtol(text, &mptr, 10);
3844 if (mptr == text) return FALSE;
3847 if (*mptr != '\0' && valid_date_separator(*mptr))
3852 month = strtol(mptr, &dptr, 10);
3855 if (valid_date_separator(*dptr))
3857 month = lt->tm_mon + 1;
3865 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3869 day = strtol(dptr, &eptr, 10);
3879 year = lt->tm_year + 1900;
3881 else if (year < 100)
3890 month < -1 || month == 0 || month > 12 ||
3891 day < -1 || day == 0 || day > 31) return FALSE;
3893 t = date_to_time(year, month, day);
3894 if (t < 0) return FALSE;
3896 if (pw->layout == LAYOUT_CALENDAR)
3898 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
3904 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3905 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
3910 found = g_list_find(list, pw->search_pi);
3911 if (found && found->next)
3913 found = found->next;
3924 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
3926 pan_info_update(pw, NULL);
3927 pan_calendar_update(pw, pi);
3928 image_scroll_to_point(pw->imd,
3929 pi->x + pi->width / 2,
3930 pi->y + pi->height / 2, 0.5, 0.5);
3934 pan_info_update(pw, pi);
3935 image_scroll_to_point(pw->imd,
3936 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3942 buf = date_value_string(t, DATE_LENGTH_MONTH);
3947 buf = g_strdup_printf("%d %s", day, tmp);
3953 buf = date_value_string(t, DATE_LENGTH_YEAR);
3958 buf_count = g_strdup_printf("( %d / %d )",
3959 g_list_index(list, pi) + 1,
3960 g_list_length(list));
3964 buf_count = g_strdup_printf("(%s)", _("no match"));
3967 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
3970 pan_search_status(pw, message);
3978 static void pan_search_activate_cb(const gchar *text, gpointer data)
3980 PanWindow *pw = data;
3984 tab_completion_append_to_history(pw->search_entry, text);
3986 if (pan_search_by_path(pw, text)) return;
3988 if ((pw->layout == LAYOUT_TIMELINE ||
3989 pw->layout == LAYOUT_CALENDAR) &&
3990 pan_search_by_date(pw, text))
3995 if (pan_search_by_partial(pw, text)) return;
3997 pan_search_status(pw, _("no match"));
4000 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
4002 PanWindow *pw = data;
4005 visible = GTK_WIDGET_VISIBLE(pw->search_box);
4006 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
4010 gtk_widget_hide(pw->search_box);
4011 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
4015 gtk_widget_show(pw->search_box);
4016 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
4017 gtk_widget_grab_focus(pw->search_entry);
4023 *-----------------------------------------------------------------------------
4024 * view window main routines
4025 *-----------------------------------------------------------------------------
4028 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
4030 PanWindow *pw = data;
4038 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
4039 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
4042 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
4045 switch (event->button)
4048 pan_info_update(pw, pi);
4050 if (!pi && pw->layout == LAYOUT_CALENDAR)
4052 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
4053 pan_calendar_update(pw, pi);
4059 pan_info_update(pw, pi);
4060 menu = pan_popup_menu(pw);
4061 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
4068 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
4071 PanWindow *pw = data;
4078 if (!(event->state & GDK_SHIFT_MASK))
4084 if (event->state & GDK_CONTROL_MASK)
4086 switch (event->direction)
4089 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
4090 (gint)event->x, (gint)event->y);
4092 case GDK_SCROLL_DOWN:
4093 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
4094 (gint)event->x, (gint)event->y);
4102 switch (event->direction)
4105 pixbuf_renderer_scroll(pr, 0, -h);
4107 case GDK_SCROLL_DOWN:
4108 pixbuf_renderer_scroll(pr, 0, h);
4110 case GDK_SCROLL_LEFT:
4111 pixbuf_renderer_scroll(pr, -w, 0);
4113 case GDK_SCROLL_RIGHT:
4114 pixbuf_renderer_scroll(pr, w, 0);
4122 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
4124 g_signal_connect(G_OBJECT(imd->pr), "clicked",
4125 G_CALLBACK(button_cb), pw);
4126 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
4127 G_CALLBACK(scroll_cb), pw);
4130 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
4132 PanWindow *pw = data;
4135 pw->imd = pw->imd_normal;
4138 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
4140 if (force_off && !pw->fs) return;
4144 fullscreen_stop(pw->fs);
4148 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
4149 pan_image_set_buttons(pw, pw->fs->imd);
4150 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
4151 G_CALLBACK(pan_window_key_press_cb), pw);
4153 pw->imd = pw->fs->imd;
4157 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
4159 PanWindow *pw = data;
4162 text = image_zoom_get_as_text(pw->imd);
4163 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4167 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
4169 PanWindow *pw = data;
4174 if (pr->scale == 0.0) return;
4176 pixbuf_renderer_get_visible_rect(pr, &rect);
4177 pixbuf_renderer_get_image_size(pr, &width, &height);
4179 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4180 adj->page_size = (gdouble)rect.width;
4181 adj->page_increment = adj->page_size / 2.0;
4182 adj->step_increment = 48.0 / pr->scale;
4184 adj->upper = MAX((gdouble)width, 1.0);
4185 adj->value = (gdouble)rect.x;
4187 pref_signal_block_data(pw->scrollbar_h, pw);
4188 gtk_adjustment_changed(adj);
4189 gtk_adjustment_value_changed(adj);
4190 pref_signal_unblock_data(pw->scrollbar_h, pw);
4192 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4193 adj->page_size = (gdouble)rect.height;
4194 adj->page_increment = adj->page_size / 2.0;
4195 adj->step_increment = 48.0 / pr->scale;
4197 adj->upper = MAX((gdouble)height, 1.0);
4198 adj->value = (gdouble)rect.y;
4200 pref_signal_block_data(pw->scrollbar_v, pw);
4201 gtk_adjustment_changed(adj);
4202 gtk_adjustment_value_changed(adj);
4203 pref_signal_unblock_data(pw->scrollbar_v, pw);
4206 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4208 PanWindow *pw = data;
4212 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4214 if (!pr->scale) return;
4216 x = (gint)gtk_range_get_value(range);
4218 pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
4221 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4223 PanWindow *pw = data;
4227 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4229 if (!pr->scale) return;
4231 y = (gint)gtk_range_get_value(range);
4233 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
4236 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4238 PanWindow *pw = data;
4240 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4241 pan_window_layout_update(pw);
4244 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4246 PanWindow *pw = data;
4248 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4249 pan_window_layout_update(pw);
4253 static void pan_window_date_toggle_cb(GtkWidget *button, gpointer data)
4255 PanWindow *pw = data;
4257 pw->exif_date_enable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4258 pan_window_layout_update(pw);
4262 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4264 PanWindow *pw = data;
4267 path = remove_trailing_slash(new_text);
4268 parse_out_relatives(path);
4272 warning_dialog(_("Folder not found"),
4273 _("The entered path is not a folder"),
4274 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4278 tab_completion_append_to_history(pw->path_entry, path);
4280 pan_window_layout_set_path(pw, path);
4286 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4288 PanWindow *pw = data;
4291 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4293 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4294 pan_window_entry_activate_cb(text, pw);
4298 static void pan_window_close(PanWindow *pw)
4300 pan_window_list = g_list_remove(pan_window_list, pw);
4302 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_EXIF_DATE, pw->exif_date_enable);
4304 if (pw->idle_id != -1)
4306 g_source_remove(pw->idle_id);
4309 pan_fullscreen_toggle(pw, TRUE);
4310 gtk_widget_destroy(pw->window);
4312 pan_window_items_free(pw);
4320 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4322 PanWindow *pw = data;
4324 pan_window_close(pw);
4328 static void pan_window_new_real(const gchar *path)
4337 GdkGeometry geometry;
4339 pw = g_new0(PanWindow, 1);
4341 pw->path = g_strdup(path);
4342 pw->layout = LAYOUT_TIMELINE;
4343 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4344 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4345 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4347 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_EXIF_DATE, &pw->exif_date_enable))
4349 pw->exif_date_enable = FALSE;
4352 pw->ignore_symlinks = TRUE;
4355 pw->list_static = NULL;
4356 pw->list_grid = NULL;
4359 pw->overlay_id = -1;
4362 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4364 geometry.min_width = 8;
4365 geometry.min_height = 8;
4366 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4368 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4369 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
4370 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4371 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4373 window_set_icon(pw->window, NULL, NULL);
4375 vbox = gtk_vbox_new(FALSE, 0);
4376 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4377 gtk_widget_show(vbox);
4379 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4381 pref_spacer(box, 0);
4382 pref_label_new(box, _("Location:"));
4383 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view_path", -1,
4384 pan_window_entry_activate_cb, pw);
4385 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4386 G_CALLBACK(pan_window_entry_change_cb), pw);
4387 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4388 gtk_widget_show(combo);
4390 combo = gtk_combo_box_new_text();
4391 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4392 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4393 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4394 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4395 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4397 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4398 g_signal_connect(G_OBJECT(combo), "changed",
4399 G_CALLBACK(pan_window_layout_change_cb), pw);
4400 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4401 gtk_widget_show(combo);
4403 combo = gtk_combo_box_new_text();
4404 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4405 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4406 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4407 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4408 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4409 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4410 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4411 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4412 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4413 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4415 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4416 g_signal_connect(G_OBJECT(combo), "changed",
4417 G_CALLBACK(pan_window_layout_size_cb), pw);
4418 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4419 gtk_widget_show(combo);
4421 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4422 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4423 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4425 pw->imd = image_new(TRUE);
4426 pw->imd_normal = pw->imd;
4428 g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
4429 G_CALLBACK(pan_window_image_zoom_cb), pw);
4430 g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
4431 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4433 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4434 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4435 gtk_widget_show(GTK_WIDGET(pw->imd->widget));
4437 pan_window_dnd_init(pw);
4439 pan_image_set_buttons(pw, pw->imd);
4441 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4442 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4443 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4444 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4445 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4446 gtk_widget_show(pw->scrollbar_h);
4448 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4449 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4450 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4451 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4452 0, GTK_FILL | GTK_EXPAND, 0, 0);
4453 gtk_widget_show(pw->scrollbar_v);
4457 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4458 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4460 pref_spacer(pw->search_box, 0);
4461 pref_label_new(pw->search_box, _("Find:"));
4463 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4464 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4465 gtk_widget_show(hbox);
4467 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4468 pan_search_activate_cb, pw);
4469 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4470 gtk_widget_show(combo);
4472 pw->search_label = gtk_label_new("");
4473 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4474 gtk_widget_show(pw->search_label);
4478 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4480 frame = gtk_frame_new(NULL);
4481 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4482 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4483 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4484 gtk_widget_show(frame);
4486 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4487 gtk_container_add(GTK_CONTAINER(frame), hbox);
4488 gtk_widget_show(hbox);
4490 pref_spacer(hbox, 0);
4491 pw->label_message = pref_label_new(hbox, "");
4493 frame = gtk_frame_new(NULL);
4494 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4495 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4496 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4497 gtk_widget_show(frame);
4499 pw->label_zoom = gtk_label_new("");
4500 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4501 gtk_widget_show(pw->label_zoom);
4504 pw->date_button = pref_checkbox_new(box, _("Use Exif date"), pw->exif_date_enable,
4505 G_CALLBACK(pan_window_date_toggle_cb), pw);
4508 pw->search_button = gtk_toggle_button_new();
4509 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4510 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4511 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4512 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4513 gtk_widget_show(hbox);
4514 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4515 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4516 gtk_widget_show(pw->search_button_arrow);
4517 pref_label_new(hbox, _("Find"));
4519 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4520 gtk_widget_show(pw->search_button);
4521 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4522 G_CALLBACK(pan_search_toggle_cb), pw);
4524 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4525 G_CALLBACK(pan_window_delete_cb), pw);
4526 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4527 G_CALLBACK(pan_window_key_press_cb), pw);
4529 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4531 pan_window_layout_update(pw);
4533 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4534 gtk_widget_show(pw->window);
4536 pan_window_list = g_list_append(pan_window_list, pw);
4540 *-----------------------------------------------------------------------------
4541 * peformance warnings
4542 *-----------------------------------------------------------------------------
4545 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4549 generic_dialog_close(gd);
4551 pan_window_new_real(path);
4555 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4559 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4560 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4563 static gint pan_warning(const gchar *path)
4569 GtkWidget *ct_button;
4572 if (path && strcmp(path, "/") == 0)
4574 pan_warning_folder(path, NULL);
4578 if (enable_thumb_caching &&
4579 thumbnail_spec_standard) return FALSE;
4581 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4582 if (hide_dlg) return FALSE;
4584 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4586 gd->data = g_strdup(path);
4587 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4588 pan_warning_ok_cb, TRUE);
4590 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4591 _("Pan view performance may be poor."),
4592 _("To improve performance of thumbnails in the pan view the"
4593 " following options can be enabled. Note that both options"
4594 " must be enabled to notice a change in performance."));
4596 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4597 pref_spacer(group, PREF_PAD_INDENT);
4598 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4600 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4601 enable_thumb_caching, &enable_thumb_caching);
4602 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4603 thumbnail_spec_standard, &thumbnail_spec_standard);
4604 pref_checkbox_link_sensitivity(ct_button, button);
4608 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4609 G_CALLBACK(pan_warning_hide_cb), NULL);
4611 gtk_widget_show(gd->dialog);
4618 *-----------------------------------------------------------------------------
4620 *-----------------------------------------------------------------------------
4623 void pan_window_new(const gchar *path)
4625 if (pan_warning(path)) return;
4627 pan_window_new_real(path);
4631 *-----------------------------------------------------------------------------
4633 *-----------------------------------------------------------------------------
4636 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4638 PanWindow *pw = data;
4641 path = pan_menu_click_path(pw);
4644 pan_fullscreen_toggle(pw, TRUE);
4645 view_window_new(path);
4649 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4655 pw = submenu_item_get_data(widget);
4656 n = GPOINTER_TO_INT(data);
4659 path = pan_menu_click_path(pw);
4662 if (!editor_window_flag_set(n))
4664 pan_fullscreen_toggle(pw, TRUE);
4666 start_editor_from_file(n, path);
4670 static void pan_info_cb(GtkWidget *widget, gpointer data)
4672 PanWindow *pw = data;
4675 path = pan_menu_click_path(pw);
4676 if (path) info_window_new(path, NULL);
4679 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4681 PanWindow *pw = data;
4683 image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
4686 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4688 PanWindow *pw = data;
4690 image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
4693 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4695 PanWindow *pw = data;
4697 image_zoom_set(pw->imd, 1.0);
4700 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4702 PanWindow *pw = data;
4705 path = pan_menu_click_path(pw);
4706 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4709 static void pan_move_cb(GtkWidget *widget, gpointer data)
4711 PanWindow *pw = data;
4714 path = pan_menu_click_path(pw);
4715 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4718 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4720 PanWindow *pw = data;
4723 path = pan_menu_click_path(pw);
4724 if (path) file_util_rename(path, NULL, pw->imd->widget);
4727 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4729 PanWindow *pw = data;
4732 path = pan_menu_click_path(pw);
4733 if (path) file_util_delete(path, NULL, pw->imd->widget);
4736 static void pan_exif_date_toggle_cb(GtkWidget *widget, gpointer data)
4738 PanWindow *pw = data;
4740 pw->exif_date_enable = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
4741 pan_window_layout_update(pw);
4744 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4746 PanWindow *pw = data;
4748 pan_fullscreen_toggle(pw, FALSE);
4751 static void pan_close_cb(GtkWidget *widget, gpointer data)
4753 PanWindow *pw = data;
4755 pan_window_close(pw);
4758 static GtkWidget *pan_popup_menu(PanWindow *pw)
4764 active = (pw->click_pi != NULL);
4766 menu = popup_menu_short_lived();
4768 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4769 G_CALLBACK(pan_zoom_in_cb), pw);
4770 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4771 G_CALLBACK(pan_zoom_out_cb), pw);
4772 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4773 G_CALLBACK(pan_zoom_1_1_cb), pw);
4774 menu_item_add_divider(menu);
4776 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4777 gtk_widget_set_sensitive(item, active);
4779 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4780 G_CALLBACK(pan_info_cb), pw);
4782 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4783 G_CALLBACK(pan_new_window_cb), pw);
4785 menu_item_add_divider(menu);
4786 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4787 G_CALLBACK(pan_copy_cb), pw);
4788 menu_item_add_sensitive(menu, _("_Move..."), active,
4789 G_CALLBACK(pan_move_cb), pw);
4790 menu_item_add_sensitive(menu, _("_Rename..."), active,
4791 G_CALLBACK(pan_rename_cb), pw);
4792 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4793 G_CALLBACK(pan_delete_cb), pw);
4795 menu_item_add_divider(menu);
4796 item = menu_item_add_check(menu, _("Sort by E_xif date"), pw->exif_date_enable,
4797 G_CALLBACK(pan_exif_date_toggle_cb), pw);
4798 gtk_widget_set_sensitive(item, (pw->layout == LAYOUT_TIMELINE || pw->layout == LAYOUT_CALENDAR));
4800 menu_item_add_divider(menu);
4804 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4808 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4811 menu_item_add_divider(menu);
4812 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4818 *-----------------------------------------------------------------------------
4820 *-----------------------------------------------------------------------------
4823 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4825 GtkSelectionData *selection_data, guint info,
4826 guint time, gpointer data)
4828 PanWindow *pw = data;
4830 if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
4832 if (info == TARGET_URI_LIST)
4836 list = uri_list_from_text(selection_data->data, TRUE);
4837 if (list && isdir((gchar *)list->data))
4839 gchar *path = list->data;
4841 pan_window_layout_set_path(pw, path);
4844 path_list_free(list);
4848 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4849 GtkSelectionData *selection_data, guint info,
4850 guint time, gpointer data)
4852 PanWindow *pw = data;
4855 path = pan_menu_click_path(pw);
4865 case TARGET_URI_LIST:
4868 case TARGET_TEXT_PLAIN:
4873 list = g_list_append(NULL, (gchar *)path);
4874 text = uri_text_from_list(list, &len, plain_text);
4878 gtk_selection_data_set (selection_data, selection_data->target,
4885 gtk_selection_data_set (selection_data, selection_data->target,
4890 static void pan_window_dnd_init(PanWindow *pw)
4894 widget = pw->imd->pr;
4896 gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
4897 dnd_file_drag_types, dnd_file_drag_types_count,
4898 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4899 g_signal_connect(G_OBJECT(widget), "drag_data_get",
4900 G_CALLBACK(pan_window_set_dnd_data), pw);
4902 gtk_drag_dest_set(widget,
4903 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4904 dnd_file_drop_types, dnd_file_drop_types_count,
4905 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4906 g_signal_connect(G_OBJECT(widget), "drag_data_received",
4907 G_CALLBACK(pan_window_get_dnd_data), pw);
4911 *-----------------------------------------------------------------------------
4912 * maintenance (for rename, move, remove)
4913 *-----------------------------------------------------------------------------