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_window_close(PanWindow *pw);
307 static void pan_window_dnd_init(PanWindow *pw);
319 static gint date_compare(time_t a, time_t b, DateLengthType length)
324 if (length == DATE_LENGTH_EXACT) return (a == b);
326 if (!localtime_r(&a, &ta) ||
327 !localtime_r(&b, &tb)) return FALSE;
329 if (ta.tm_year != tb.tm_year) return FALSE;
330 if (length == DATE_LENGTH_YEAR) return TRUE;
332 if (ta.tm_mon != tb.tm_mon) return FALSE;
333 if (length == DATE_LENGTH_MONTH) return TRUE;
335 if (length == DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
337 if (ta.tm_mday != tb.tm_mday) return FALSE;
338 if (length == DATE_LENGTH_DAY) return TRUE;
340 return (ta.tm_hour == tb.tm_hour);
343 static gint date_value(time_t d, DateLengthType length)
347 if (!localtime_r(&d, &td)) return -1;
351 case DATE_LENGTH_DAY:
354 case DATE_LENGTH_WEEK:
357 case DATE_LENGTH_MONTH:
358 return td.tm_mon + 1;
360 case DATE_LENGTH_YEAR:
361 return td.tm_year + 1900;
363 case DATE_LENGTH_EXACT:
371 static gchar *date_value_string(time_t d, DateLengthType length)
375 gchar *format = NULL;
377 if (!localtime_r(&d, &td)) return g_strdup("");
381 case DATE_LENGTH_DAY:
382 return g_strdup_printf("%d", td.tm_mday);
384 case DATE_LENGTH_WEEK:
387 case DATE_LENGTH_MONTH:
390 case DATE_LENGTH_YEAR:
391 return g_strdup_printf("%d", td.tm_year + 1900);
393 case DATE_LENGTH_EXACT:
395 return g_strdup(text_from_time(d));
400 if (format && strftime(buf, sizeof(buf), format, &td) > 0)
402 gchar *ret = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
409 static time_t date_to_time(gint year, gint month, gint day)
416 lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
417 lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
418 lt.tm_year = year - 1900;
425 *-----------------------------------------------------------------------------
427 *-----------------------------------------------------------------------------
430 static void pan_cache_free(PanWindow *pw)
434 work = pw->cache_list;
442 cache_sim_data_free(pc->cd);
443 file_data_free((FileData *)pc);
446 g_list_free(pw->cache_list);
447 pw->cache_list = NULL;
449 filelist_free(pw->cache_todo);
450 pw->cache_todo = NULL;
456 cache_loader_free(pw->cache_cl);
460 static void pan_cache_fill(PanWindow *pw, const gchar *path)
466 list = pan_window_layout_list(path, SORT_NAME, TRUE, pw->ignore_symlinks);
467 pw->cache_todo = g_list_reverse(list);
469 pw->cache_total = g_list_length(pw->cache_todo);
472 static void pan_cache_step_done_cb(CacheLoader *cl, gint error, gpointer data)
474 PanWindow *pw = data;
479 pc = pw->cache_list->data;
488 cache_loader_free(cl);
491 pan_window_layout_update_idle(pw);
494 static gint pan_cache_step(PanWindow *pw)
498 CacheDataType load_mask;
500 if (!pw->cache_todo) return TRUE;
502 fd = pw->cache_todo->data;
503 pw->cache_todo = g_list_remove(pw->cache_todo, fd);
506 if (enable_thumb_caching)
510 found = cache_find_location(CACHE_TYPE_SIM, fd->path);
511 if (found && filetime(found) == fd->date)
513 cd = cache_sim_data_load(found);
518 if (!cd) cd = cache_sim_data_new();
522 cd->dimensions = image_load_dimensions(fd->path, &cd->width, &cd->height);
523 if (enable_thumb_caching &&
529 base = cache_get_location(CACHE_TYPE_SIM, fd->path, FALSE, &mode);
530 if (cache_ensure_dir_exists(base, mode))
533 cd->path = cache_get_location(CACHE_TYPE_SIM, fd->path, TRUE, NULL);
534 if (cache_sim_data_save(cd))
536 filetime_set(cd->path, filetime(fd->path));
545 pc = g_new0(PanCacheData, 1);
546 memcpy(pc, fd, sizeof(FileData));
551 pw->cache_list = g_list_prepend(pw->cache_list, pc);
553 cache_loader_free(pw->cache_cl);
555 load_mask = CACHE_LOADER_NONE;
556 if (pw->size > LAYOUT_SIZE_THUMB_LARGE) load_mask |= CACHE_LOADER_DIMENSIONS;
557 if (pw->exif_date_enable) load_mask |= CACHE_LOADER_DATE;
558 pw->cache_cl = cache_loader_new(((FileData *)pc)->path, load_mask,
559 pan_cache_step_done_cb, pw);
560 return (pw->cache_cl == NULL);
563 /* This sync date function is optimized for lists with a common sort */
564 static void pan_cache_sync_date(PanWindow *pw, GList *list)
569 haystack = g_list_copy(pw->cache_list);
587 path = ((FileData *)pc)->path;
588 if (path && strcmp(path, fd->path) == 0)
590 if (pc->cd && pc->cd->have_date && pc->cd->date >= 0)
592 fd->date = pc->cd->date;
595 haystack = g_list_delete_link(haystack, needle);
600 needle = needle->next;
605 g_list_free(haystack);
609 *-----------------------------------------------------------------------------
611 *-----------------------------------------------------------------------------
614 static void pan_grid_clear(PanWindow *pw)
618 work = pw->list_grid;
626 g_list_free(pg->list);
630 g_list_free(pw->list_grid);
631 pw->list_grid = NULL;
633 pw->list = g_list_concat(pw->list, pw->list_static);
634 pw->list_static = NULL;
637 static void pan_grid_build(PanWindow *pw, gint width, gint height, gint grid_size)
650 l = g_list_length(pw->list);
654 total = (gdouble)width * (gdouble)height / (gdouble)l;
657 aw = (gdouble)width / s;
658 ah = (gdouble)height / s;
660 col = (gint)(sqrt((gdouble)l / grid_size) * width / height + 0.999);
661 col = CLAMP(col, 1, l / grid_size + 1);
662 row = (gint)((gdouble)l / grid_size / col);
663 if (row < 1) row = 1;
665 /* limit minimum size of grid so that a tile will always fit regardless of position */
666 cw = MAX((gint)ceil((gdouble)width / col), PAN_TILE_SIZE * 2);
667 ch = MAX((gint)ceil((gdouble)height / row), PAN_TILE_SIZE * 2);
672 printf("intersect speedup grid is %dx%d, based on %d average per grid\n", col, row, grid_size);
674 for (j = 0; j < row; j++)
675 for (i = 0; i < col; i++)
677 if ((i + 1) * cw / 2 < width && (j + 1) * ch / 2 < height)
681 pg = g_new0(PanGrid, 1);
688 pw->list_grid = g_list_prepend(pw->list_grid, pg);
690 if (debug) printf("grid section: %d,%d (%dx%d)\n", pg->x, pg->y, pg->w, pg->h);
703 grid = pw->list_grid;
712 if (util_clip_region(pi->x, pi->y, pi->width, pi->height,
713 pg->x, pg->y, pg->w, pg->h,
716 pg->list = g_list_prepend(pg->list, pi);
721 work = pw->list_grid;
729 pg->list = g_list_reverse(pg->list);
732 pw->list_static = pw->list;
739 *-----------------------------------------------------------------------------
741 *-----------------------------------------------------------------------------
744 static void pan_item_free(PanItem *pi)
748 if (pi->pixbuf) g_object_unref(pi->pixbuf);
749 if (pi->fd) file_data_free(pi->fd);
757 static void pan_window_items_free(PanWindow *pw)
766 PanItem *pi = work->data;
772 g_list_free(pw->list);
775 g_list_free(pw->queue);
779 image_loader_free(pw->il);
782 thumb_loader_free(pw->tl);
786 pw->search_pi = NULL;
789 static PanItem *pan_item_new_thumb(PanWindow *pw, FileData *fd, gint x, gint y)
793 pi = g_new0(PanItem, 1);
794 pi->type = ITEM_THUMB;
798 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
799 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
805 pw->list = g_list_prepend(pw->list, pi);
810 static PanItem *pan_item_new_box(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
812 guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
813 guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
817 pi = g_new0(PanItem, 1);
825 pi->color_r = base_r;
826 pi->color_g = base_g;
827 pi->color_b = base_b;
828 pi->color_a = base_a;
830 pi->color2_r = bord_r;
831 pi->color2_g = bord_g;
832 pi->color2_b = bord_b;
833 pi->color2_a = bord_a;
834 pi->border = border_size;
836 pw->list = g_list_prepend(pw->list, pi);
841 static void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
845 if (!pi || pi->type != ITEM_BOX) return;
850 pi->width -= shadow[0];
851 pi->height -= shadow[0];
854 shadow = g_new0(gint, 2);
859 pi->height += offset;
865 static PanItem *pan_item_new_tri(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
866 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
867 guint8 r, guint8 g, guint8 b, guint8 a)
872 pi = g_new0(PanItem, 1);
873 pi->type = ITEM_TRIANGLE;
884 coord = g_new0(gint, 6);
894 pi->border = BORDER_NONE;
896 pw->list = g_list_prepend(pw->list, pi);
901 static void pan_item_tri_border(PanItem *pi, gint borders,
902 guint8 r, guint8 g, guint8 b, guint8 a)
904 if (!pi || pi->type != ITEM_TRIANGLE) return;
906 pi->border = borders;
914 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
918 layout = gtk_widget_create_pango_layout(widget, NULL);
920 if (pi->text_attr & TEXT_ATTR_MARKUP)
922 pango_layout_set_markup(layout, pi->text, -1);
926 if (pi->text_attr & TEXT_ATTR_BOLD ||
927 pi->text_attr & TEXT_ATTR_HEADING)
932 pal = pango_attr_list_new();
933 if (pi->text_attr & TEXT_ATTR_BOLD)
935 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
937 pa->end_index = G_MAXINT;
938 pango_attr_list_insert(pal, pa);
940 if (pi->text_attr & TEXT_ATTR_HEADING)
942 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
944 pa->end_index = G_MAXINT;
945 pango_attr_list_insert(pal, pa);
947 pango_layout_set_attributes(layout, pal);
948 pango_attr_list_unref(pal);
951 pango_layout_set_text(layout, pi->text, -1);
955 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
959 if (!pi || !pi->text || !widget) return;
961 layout = pan_item_text_layout(pi, widget);
962 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
963 g_object_unref(G_OBJECT(layout));
965 pi->width += pi->border * 2;
966 pi->height += pi->border * 2;
969 static PanItem *pan_item_new_text(PanWindow *pw, gint x, gint y, const gchar *text, TextAttrType attr,
971 guint8 r, guint8 g, guint8 b, guint8 a)
975 pi = g_new0(PanItem, 1);
976 pi->type = ITEM_TEXT;
979 pi->text = g_strdup(text);
980 pi->text_attr = attr;
989 pan_item_text_compute_size(pi, pw->imd->pr);
991 pw->list = g_list_prepend(pw->list, pi);
996 static void pan_item_set_key(PanItem *pi, const gchar *key)
1003 pi->key = g_strdup(key);
1007 static void pan_item_added(PanWindow *pw, PanItem *pi)
1010 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1013 static void pan_item_remove(PanWindow *pw, PanItem *pi)
1017 if (pw->click_pi == pi) pw->click_pi = NULL;
1018 if (pw->queue_pi == pi) pw->queue_pi = NULL;
1019 if (pw->search_pi == pi) pw->search_pi = NULL;
1020 pw->queue = g_list_remove(pw->queue, pi);
1022 pw->list = g_list_remove(pw->list, pi);
1023 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1027 static void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
1029 if (!pi || !child) return;
1031 if (pi->x + pi->width < child->x + child->width + border)
1032 pi->width = child->x + child->width + border - pi->x;
1034 if (pi->y + pi->height < child->y + child->height + border)
1035 pi->height = child->y + child->height + border - pi->y;
1038 static void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
1042 if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
1043 if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
1046 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
1053 if (!pi->fd) return;
1055 work = pw->cache_list;
1064 path = ((FileData *)pc)->path;
1066 if (pc->cd && pc->cd->dimensions &&
1067 path && strcmp(path, pi->fd->path) == 0)
1069 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
1070 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
1072 pw->cache_list = g_list_remove(pw->cache_list, pc);
1073 cache_sim_data_free(pc->cd);
1074 file_data_free((FileData *)pc);
1080 static PanItem *pan_item_new_image(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
1084 pi = g_new0(PanItem, 1);
1085 pi->type = ITEM_IMAGE;
1095 pi->color2_a = PAN_SHADOW_ALPHA / 2;
1097 pan_item_image_find_size(pw, pi, w, h);
1099 pw->list = g_list_prepend(pw->list, pi);
1104 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
1108 if (!key) return NULL;
1110 work = g_list_last(pw->list);
1116 if ((pi->type == type || type == ITEM_NONE) &&
1117 pi->key && strcmp(pi->key, key) == 0)
1123 work = g_list_last(pw->list_static);
1129 if ((pi->type == type || type == ITEM_NONE) &&
1130 pi->key && strcmp(pi->key, key) == 0)
1140 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1141 static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
1142 ItemType type, const gchar *path,
1143 gint ignore_case, gint partial)
1147 work = g_list_last(search_list);
1153 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1159 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
1161 else if (pi->fd->name)
1169 haystack = g_utf8_strdown(pi->fd->name, -1);
1170 match = (strstr(haystack, path) != NULL);
1175 if (strstr(pi->fd->name, path)) match = TRUE;
1178 else if (ignore_case)
1180 if (strcasecmp(path, pi->fd->name) == 0) match = TRUE;
1184 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
1188 if (match) list = g_list_prepend(list, pi);
1196 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1197 static GList *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1198 gint ignore_case, gint partial)
1202 if (!path) return NULL;
1203 if (partial && path[0] == '/') return NULL;
1205 list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
1206 list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
1208 return g_list_reverse(list);
1211 static PanItem *pan_item_find_by_coord_l(GList *list, ItemType type, gint x, gint y, const gchar *key)
1221 if ((pi->type == type || type == ITEM_NONE) &&
1222 x >= pi->x && x < pi->x + pi->width &&
1223 y >= pi->y && y < pi->y + pi->height &&
1224 (!key || (pi->key && strcmp(pi->key, key) == 0)))
1234 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y, const gchar *key)
1238 pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
1241 return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
1245 *-----------------------------------------------------------------------------
1247 *-----------------------------------------------------------------------------
1250 static gint islink_loop(const gchar *s)
1256 sl = path_from_utf8(s);
1258 if (lstat(sl, &st) == 0 && S_ISLNK(st.st_mode))
1263 buf = g_malloc(st.st_size + 1);
1264 l = readlink(sl, buf, st.st_size);
1265 if (l == st.st_size)
1269 parse_out_relatives(buf);
1272 parse_out_relatives(sl);
1276 if (strncmp(sl, buf, l) == 0 &&
1277 (sl[l] == '\0' || sl[l] == '/' || l == 1)) ret = TRUE;
1283 link_path = concat_dir_and_file(sl, buf);
1284 parse_out_relatives(link_path);
1286 if (strncmp(sl, link_path, l) == 0 &&
1287 (sl[l] == '\0' || sl[l] == '/' || l == 1)) ret = TRUE;
1301 static gint is_ignored(const gchar *s, gint ignore_symlinks)
1306 if (!lstat_utf8(s, &st)) return TRUE;
1309 /* normal filesystems have directories with some size or block allocation,
1310 * special filesystems (like linux /proc) set both to zero.
1311 * enable this check if you enable listing the root "/" folder
1313 if (st.st_size == 0 && st.st_blocks == 0) return TRUE;
1316 if (S_ISLNK(st.st_mode) && (ignore_symlinks || islink_loop(s))) return TRUE;
1318 n = filename_from_path(s);
1319 if (n && strcmp(n, GQVIEW_RC_DIR) == 0) return TRUE;
1324 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend,
1325 gint ignore_symlinks)
1327 GList *flist = NULL;
1328 GList *dlist = NULL;
1332 filelist_read(path, &flist, &dlist);
1333 if (sort != SORT_NONE)
1335 flist = filelist_sort(flist, sort, ascend);
1336 dlist = filelist_sort(dlist, sort, ascend);
1346 folders = g_list_remove(folders, fd);
1348 if (!is_ignored(fd->path, ignore_symlinks) &&
1349 filelist_read(fd->path, &flist, &dlist))
1351 if (sort != SORT_NONE)
1353 flist = filelist_sort(flist, sort, ascend);
1354 dlist = filelist_sort(dlist, sort, ascend);
1357 result = g_list_concat(result, flist);
1358 folders = g_list_concat(dlist, folders);
1367 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1375 list = pan_window_layout_list(path, SORT_NAME, TRUE, pw->ignore_symlinks);
1377 grid_size = (gint)sqrt((double)g_list_length(list));
1378 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1380 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1384 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1389 *width = PAN_FOLDER_BOX_BORDER * 2;
1390 *height = PAN_FOLDER_BOX_BORDER * 2;
1403 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1405 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1407 x += pi->width + PAN_THUMB_GAP;
1408 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1417 pi = pan_item_new_thumb(pw, fd, x, y);
1419 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1423 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1426 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1432 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1435 gint x1, y1, x2, y2;
1450 if (x1 > pi->x) x1 = pi->x;
1451 if (y1 > pi->y) y1 = pi->y;
1452 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1453 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1456 x1 -= PAN_FOLDER_BOX_BORDER;
1457 y1 -= PAN_FOLDER_BOX_BORDER;
1458 x2 += PAN_FOLDER_BOX_BORDER;
1459 y2 += PAN_FOLDER_BOX_BORDER;
1472 if (pi->type == ITEM_TRIANGLE && pi->data)
1486 if (width) *width = x2 - x1;
1487 if (height) *height = y2 - y1;
1490 typedef struct _FlowerGroup FlowerGroup;
1491 struct _FlowerGroup {
1504 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1508 work = group->items;
1526 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1527 gint *result_x, gint *result_y)
1533 radius = parent->circumference / (2*PI);
1534 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1536 a = 2*PI * group->diameter / parent->circumference;
1538 x = (gint)((double)radius * cos(parent->angle + a / 2));
1539 y = (gint)((double)radius * sin(parent->angle + a / 2));
1546 x += parent->width / 2;
1547 y += parent->height / 2;
1549 x -= group->width / 2;
1550 y -= group->height / 2;
1556 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1563 if (parent && parent->children)
1565 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1573 pan_window_layout_compute_folder_flower_move(group, x, y);
1578 gint px, py, gx, gy;
1579 gint x1, y1, x2, y2;
1581 px = parent->x + parent->width / 2;
1582 py = parent->y + parent->height / 2;
1584 gx = group->x + group->width / 2;
1585 gy = group->y + group->height / 2;
1590 x2 = MAX(px, gx + 5);
1591 y2 = MAX(py, gy + 5);
1593 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1594 px, py, gx, gy, gx + 5, gy + 5,
1596 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1600 pw->list = g_list_concat(group->items, pw->list);
1601 group->items = NULL;
1603 group->circumference = 0;
1604 work = group->children;
1612 group->circumference += child->diameter;
1615 work = g_list_last(group->children);
1623 pan_window_layout_compute_folder_flower_build(pw, child, group);
1626 g_list_free(group->children);
1630 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1643 if (!filelist_read(path, &f, &d)) return NULL;
1644 if (!f && !d) return NULL;
1646 f = filelist_sort(f, SORT_NAME, TRUE);
1647 d = filelist_sort(d, SORT_NAME, TRUE);
1649 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1650 PAN_TEXT_BORDER_SIZE,
1651 PAN_TEXT_COLOR, 255);
1653 y += pi_box->height;
1655 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1657 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1658 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1659 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1660 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1662 x += PAN_FOLDER_BOX_BORDER;
1663 y += PAN_FOLDER_BOX_BORDER;
1665 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1679 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1681 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1682 x += pi->width + PAN_THUMB_GAP;
1683 if (pi->height > y_height) y_height = pi->height;
1687 pi = pan_item_new_thumb(pw, fd, x, y);
1688 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1689 y_height = PAN_THUMB_SIZE;
1693 if (grid_count >= grid_size)
1697 y += y_height + PAN_THUMB_GAP;
1701 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1704 group = g_new0(FlowerGroup, 1);
1705 group->items = pw->list;
1708 group->width = pi_box->width;
1709 group->height = pi_box->y + pi_box->height;
1710 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1712 group->children = NULL;
1723 if (!is_ignored(fd->path, pw->ignore_symlinks))
1725 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1726 if (child) group->children = g_list_prepend(group->children, child);
1730 if (!f && !group->children)
1732 work = group->items;
1743 g_list_free(group->items);
1754 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1755 gint *width, gint *height,
1756 gint *scroll_x, gint *scroll_y)
1761 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1762 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1764 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1766 list = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1769 PanItem *pi = list->data;
1770 *scroll_x = pi->x + pi->width / 2;
1771 *scroll_y = pi->y + pi->height / 2;
1776 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1777 gint *x, gint *y, gint *level,
1779 gint *width, gint *height)
1787 if (!filelist_read(path, &f, &d)) return;
1788 if (!f && !d) return;
1790 f = filelist_sort(f, SORT_NAME, TRUE);
1791 d = filelist_sort(d, SORT_NAME, TRUE);
1793 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1795 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1796 PAN_TEXT_BORDER_SIZE,
1797 PAN_TEXT_COLOR, 255);
1799 *y += pi_box->height;
1801 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1803 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1804 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1805 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1806 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1808 *x += PAN_FOLDER_BOX_BORDER;
1809 *y += PAN_FOLDER_BOX_BORDER;
1820 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1822 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1823 *x += pi->width + PAN_THUMB_GAP;
1824 if (pi->height > y_height) y_height = pi->height;
1828 pi = pan_item_new_thumb(pw, fd, *x, *y);
1829 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1830 y_height = PAN_THUMB_SIZE;
1833 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1836 if (f) *y = pi_box->y + pi_box->height;
1848 if (!is_ignored(fd->path, pw->ignore_symlinks))
1850 *level = *level + 1;
1851 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1852 pi_box, width, height);
1853 *level = *level - 1;
1859 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1861 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1862 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1864 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1867 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1874 x = PAN_FOLDER_BOX_BORDER;
1875 y = PAN_FOLDER_BOX_BORDER;
1876 w = PAN_FOLDER_BOX_BORDER * 2;
1877 h = PAN_FOLDER_BOX_BORDER * 2;
1879 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1881 if (width) *width = w;
1882 if (height) *height = h;
1886 *-----------------------------------------------------------------------------
1888 *-----------------------------------------------------------------------------
1891 static void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
1897 gint x1, y1, x2, y2, x3, y3;
1902 while ((pi = pan_item_find_by_key(pw, ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
1904 if (!pi_day || pi_day->type != ITEM_BOX ||
1905 !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
1907 list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
1919 if (dot->type != ITEM_BOX || !dot->fd ||
1920 !dot->key || strcmp(dot->key, "dot") != 0)
1922 list = g_list_delete_link(list, node);
1930 grid = (gint)(sqrt(g_list_length(list)) + 0.5);
1932 x = pi_day->x + pi_day->width + 4;
1936 if (y + grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4 > pw->pr->image_height)
1938 y = pw->pr->image_height - (grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4);
1942 pbox = pan_item_new_box(pw, NULL, x, y, PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1943 PAN_CAL_POPUP_BORDER,
1944 PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA,
1945 PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
1946 pan_item_set_key(pbox, "day_bubble");
1953 buf = date_value_string(pi_day->fd->date, DATE_LENGTH_WEEK);
1954 plabel = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1955 PAN_TEXT_BORDER_SIZE,
1956 PAN_CAL_POPUP_TEXT_COLOR, 255);
1957 pan_item_set_key(plabel, "day_bubble");
1960 pan_item_size_by_item(pbox, plabel, 0);
1962 y += plabel->height;
1969 x += PAN_FOLDER_BOX_BORDER;
1970 y += PAN_FOLDER_BOX_BORDER;
1984 pimg = pan_item_new_thumb(pw, file_data_new_simple(dot->fd->path), x, y);
1985 pan_item_set_key(pimg, "day_bubble");
1987 pan_item_size_by_item(pbox, pimg, PAN_FOLDER_BOX_BORDER);
1992 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1997 x = pbox->x + PAN_FOLDER_BOX_BORDER;
1998 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
2004 x1 = pi_day->x + pi_day->width - 8;
2007 y2 = pbox->y + MIN(42, pbox->height);
2009 y3 = MAX(pbox->y, y2 - 30);
2010 util_clip_triangle(x1, y1, x2, y2, x3, y3,
2013 pi = pan_item_new_tri(pw, NULL, x, y, w, h,
2014 x1, y1, x2, y2, x3, y3,
2015 PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA);
2016 pan_item_tri_border(pi, BORDER_1 | BORDER_3, PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
2017 pan_item_set_key(pi, "day_bubble");
2018 pan_item_added(pw, pi);
2020 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
2021 pan_item_added(pw, pbox);
2023 pan_layout_resize(pw);
2026 static void pan_window_layout_compute_calendar(PanWindow *pw, const gchar *path, gint *width, gint *height)
2042 list = pan_window_layout_list(path, SORT_NONE, TRUE, pw->ignore_symlinks);
2044 if (pw->cache_list && pw->exif_date_enable)
2046 pw->cache_list = filelist_sort(pw->cache_list, SORT_NAME, TRUE);
2047 list = filelist_sort(list, SORT_NAME, TRUE);
2048 pan_cache_sync_date(pw, list);
2051 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2052 list = filelist_sort(list, SORT_TIME, TRUE);
2065 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2073 if (day_max < count) day_max = count;
2077 printf("biggest day contains %d images\n", day_max);
2079 grid = (gint)(sqrt((double)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
2080 day_width = MAX(PAN_CAL_DAY_WIDTH, grid);
2081 day_height = MAX(PAN_CAL_DAY_HEIGHT, grid);
2085 FileData *fd = list->data;
2087 year = date_value(fd->date, DATE_LENGTH_YEAR);
2088 month = date_value(fd->date, DATE_LENGTH_MONTH);
2091 work = g_list_last(list);
2094 FileData *fd = work->data;
2095 end_year = date_value(fd->date, DATE_LENGTH_YEAR);
2096 end_month = date_value(fd->date, DATE_LENGTH_MONTH);
2099 *width = PAN_FOLDER_BOX_BORDER * 2;
2100 *height = PAN_FOLDER_BOX_BORDER * 2;
2102 x = PAN_FOLDER_BOX_BORDER;
2103 y = PAN_FOLDER_BOX_BORDER;
2106 while (work && (year < end_year || (year == end_year && month <= end_month)))
2117 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
2119 days = date_value(dt, DATE_LENGTH_DAY);
2120 dt = date_to_time(year, month, 1);
2121 col = date_value(dt, DATE_LENGTH_WEEK);
2124 x = PAN_FOLDER_BOX_BORDER;
2126 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
2127 PAN_CAL_MONTH_BORDER,
2128 PAN_CAL_MONTH_COLOR, PAN_CAL_MONTH_ALPHA,
2129 PAN_CAL_MONTH_BORDER_COLOR, PAN_CAL_MONTH_ALPHA);
2130 buf = date_value_string(dt, DATE_LENGTH_MONTH);
2131 pi_text = pan_item_new_text(pw, x, y, buf,
2132 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2133 PAN_TEXT_BORDER_SIZE,
2134 PAN_CAL_MONTH_TEXT_COLOR, 255);
2136 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
2138 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
2140 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
2141 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
2143 for (day = 1; day <= days; day++)
2150 dt = date_to_time(year, month, day);
2152 fd = g_new0(FileData, 1);
2153 /* path and name must be non NULL, so make them an invalid filename */
2154 fd->path = g_strdup("//");
2157 pi_day = pan_item_new_box(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
2159 PAN_CAL_DAY_COLOR, PAN_CAL_DAY_ALPHA,
2160 PAN_CAL_DAY_BORDER_COLOR, PAN_CAL_DAY_ALPHA);
2161 pan_item_set_key(pi_day, "day");
2163 dx = x + PAN_CAL_DOT_GAP * 2;
2164 dy = y + PAN_CAL_DOT_GAP * 2;
2166 fd = (work) ? work->data : NULL;
2167 while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
2171 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
2173 PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
2175 pan_item_set_key(pi, "dot");
2177 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2178 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
2180 dx = x + PAN_CAL_DOT_GAP * 2;
2181 dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2183 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
2185 /* must keep all dots within respective day even if it gets ugly */
2186 dy = y + PAN_CAL_DOT_GAP * 2;
2192 fd = (work) ? work->data : NULL;
2199 pi_day->color_r = MAX(pi_day->color_r - 61 - n * 3, 80);
2200 pi_day->color_g = pi_day->color_r;
2202 buf = g_strdup_printf("( %d )", n);
2203 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2204 PAN_TEXT_BORDER_SIZE,
2205 PAN_CAL_DAY_TEXT_COLOR, 255);
2208 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
2209 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
2212 buf = g_strdup_printf("%d", day);
2213 pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2214 PAN_TEXT_BORDER_SIZE,
2215 PAN_CAL_DAY_TEXT_COLOR, 255);
2219 pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
2226 x = PAN_FOLDER_BOX_BORDER;
2227 y += PAN_CAL_DAY_HEIGHT;
2231 x += PAN_CAL_DAY_WIDTH;
2235 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
2236 y += PAN_FOLDER_BOX_BORDER * 2;
2247 *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
2252 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
2260 PanItem *pi_month = NULL;
2261 PanItem *pi_day = NULL;
2267 list = pan_window_layout_list(path, SORT_NONE, TRUE, pw->ignore_symlinks);
2269 if (pw->cache_list && pw->exif_date_enable)
2271 pw->cache_list = filelist_sort(pw->cache_list, SORT_NAME, TRUE);
2272 list = filelist_sort(list, SORT_NAME, TRUE);
2273 pan_cache_sync_date(pw, list);
2276 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2277 list = filelist_sort(list, SORT_TIME, TRUE);
2279 *width = PAN_FOLDER_BOX_BORDER * 2;
2280 *height = PAN_FOLDER_BOX_BORDER * 2;
2285 day_start = month_start;
2300 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2305 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
2311 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
2315 x = PAN_FOLDER_BOX_BORDER;
2318 y = PAN_FOLDER_BOX_BORDER;
2320 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
2321 pi = pan_item_new_text(pw, x, y, buf,
2322 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2323 PAN_TEXT_BORDER_SIZE,
2324 PAN_TEXT_COLOR, 255);
2328 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
2330 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2331 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2332 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2334 x += PAN_FOLDER_BOX_BORDER;
2335 y += PAN_FOLDER_BOX_BORDER;
2339 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
2351 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
2353 needle = needle->next;
2362 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
2363 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2364 PAN_TEXT_BORDER_SIZE,
2365 PAN_TEXT_COLOR, 255);
2370 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
2371 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2372 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2373 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2375 x += PAN_FOLDER_BOX_BORDER;
2376 y += PAN_FOLDER_BOX_BORDER;
2380 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2382 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
2383 if (pi->width > x_width) x_width = pi->width;
2384 y_height = pi->height;
2388 pi = pan_item_new_thumb(pw, fd, x, y);
2389 x_width = PAN_THUMB_SIZE;
2390 y_height = PAN_THUMB_SIZE;
2393 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
2394 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
2399 if (total > 0 && count < PAN_GROUP_MAX)
2401 y += y_height + PAN_THUMB_GAP;
2405 x += x_width + PAN_THUMB_GAP;
2415 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
2421 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
2422 gint *width, gint *height,
2423 gint *scroll_x, gint *scroll_y)
2425 pan_window_items_free(pw);
2429 case LAYOUT_SIZE_THUMB_DOTS:
2430 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2431 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2433 case LAYOUT_SIZE_THUMB_NONE:
2434 pw->thumb_size = PAN_THUMB_SIZE_NONE;
2435 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2437 case LAYOUT_SIZE_THUMB_SMALL:
2438 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2439 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2441 case LAYOUT_SIZE_THUMB_NORMAL:
2443 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2444 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2446 case LAYOUT_SIZE_THUMB_LARGE:
2447 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2448 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2450 case LAYOUT_SIZE_10:
2451 pw->image_size = 10;
2452 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2454 case LAYOUT_SIZE_25:
2455 pw->image_size = 25;
2456 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2458 case LAYOUT_SIZE_33:
2459 pw->image_size = 33;
2460 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2462 case LAYOUT_SIZE_50:
2463 pw->image_size = 50;
2464 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2466 case LAYOUT_SIZE_100:
2467 pw->image_size = 100;
2468 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2481 pan_window_layout_compute_grid(pw, path, width, height);
2483 case LAYOUT_FOLDERS_LINEAR:
2484 pan_window_layout_compute_folders_linear(pw, path, width, height);
2486 case LAYOUT_FOLDERS_FLOWER:
2487 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2489 case LAYOUT_CALENDAR:
2490 pan_window_layout_compute_calendar(pw, path, width, height);
2492 case LAYOUT_TIMELINE:
2493 pan_window_layout_compute_timeline(pw, path, width, height);
2499 printf("computed %d objects\n", g_list_length(pw->list));
2502 static GList *pan_layout_intersect_l(GList *list, GList *item_list,
2503 gint x, gint y, gint width, gint height)
2511 gint rx, ry, rw, rh;
2516 if (util_clip_region(x, y, width, height,
2517 pi->x, pi->y, pi->width, pi->height,
2518 &rx, &ry, &rw, &rh))
2520 list = g_list_prepend(list, pi);
2527 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2533 grid = pw->list_grid;
2539 if (x < pg->x || x + width > pg->x + pg->w ||
2540 y < pg->y || y + height > pg->y + pg->h)
2546 list = pan_layout_intersect_l(list, pw->list, x, y, width, height);
2550 list = pan_layout_intersect_l(list, pg->list, x, y, width, height);
2554 list = pan_layout_intersect_l(list, pw->list_static, x, y, width, height);
2560 static void pan_layout_resize(PanWindow *pw)
2575 if (width < pi->x + pi->width) width = pi->x + pi->width;
2576 if (height < pi->y + pi->height) height = pi->y + pi->height;
2578 work = pw->list_static;
2586 if (width < pi->x + pi->width) width = pi->x + pi->width;
2587 if (height < pi->y + pi->height) height = pi->y + pi->height;
2590 width += PAN_FOLDER_BOX_BORDER * 2;
2591 height += PAN_FOLDER_BOX_BORDER * 2;
2593 pr = PIXBUF_RENDERER(pw->imd->pr);
2594 if (width < pr->window_width) width = pr->window_width;
2595 if (height < pr->window_width) height = pr->window_height;
2597 pixbuf_renderer_set_tiles_size(PIXBUF_RENDERER(pw->imd->pr), width, height);
2601 *-----------------------------------------------------------------------------
2603 *-----------------------------------------------------------------------------
2606 static gint pan_layout_queue_step(PanWindow *pw);
2609 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2611 PanWindow *pw = data;
2619 pw->queue_pi = NULL;
2623 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2624 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2627 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2631 thumb_loader_free(pw->tl);
2634 while (pan_layout_queue_step(pw));
2637 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2639 PanWindow *pw = data;
2647 pw->queue_pi = NULL;
2651 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2652 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2653 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2655 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2656 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2657 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2662 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2663 (GdkInterpType)zoom_quality);
2664 g_object_unref(tmp);
2668 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2672 image_loader_free(pw->il);
2675 while (pan_layout_queue_step(pw));
2679 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2680 guint width, guint height, gpointer data)
2682 PanWindow *pw = data;
2693 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2694 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2698 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2704 static gint pan_layout_queue_step(PanWindow *pw)
2708 if (!pw->queue) return FALSE;
2710 pi = pw->queue->data;
2711 pw->queue = g_list_remove(pw->queue, pi);
2714 if (!pw->queue_pi->fd)
2716 pw->queue_pi->queued = FALSE;
2717 pw->queue_pi = NULL;
2721 image_loader_free(pw->il);
2723 thumb_loader_free(pw->tl);
2726 if (pi->type == ITEM_IMAGE)
2728 pw->il = image_loader_new(pi->fd->path);
2730 if (pw->size != LAYOUT_SIZE_100)
2732 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2736 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2738 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2740 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2742 image_loader_free(pw->il);
2745 else if (pi->type == ITEM_THUMB)
2747 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2749 if (!pw->tl->standard_loader)
2751 /* The classic loader will recreate a thumbnail any time we
2752 * request a different size than what exists. This view will
2753 * almost never use the user configured sizes so disable cache.
2755 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2758 thumb_loader_set_callbacks(pw->tl,
2759 pan_layout_queue_thumb_done_cb,
2760 pan_layout_queue_thumb_done_cb,
2763 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2765 thumb_loader_free(pw->tl);
2769 pw->queue_pi->queued = FALSE;
2770 pw->queue_pi = NULL;
2774 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2776 if (!pi || pi->queued || pi->pixbuf) return;
2777 if (pw->size <= LAYOUT_SIZE_THUMB_NONE &&
2778 (!pi->key || strcmp(pi->key, "info") != 0) )
2784 pw->queue = g_list_prepend(pw->queue, pi);
2786 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2789 static gint pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
2790 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2792 PanWindow *pw = data;
2797 pixbuf_set_rect_fill(pixbuf,
2798 0, 0, width, height,
2799 PAN_BACKGROUND_COLOR, 255);
2801 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2803 gint rx, ry, rw, rh;
2805 if (util_clip_region(x, y, width, height,
2807 &rx, &ry, &rw, &rh))
2809 pixbuf_draw_rect_fill(pixbuf,
2810 rx - x, ry - y, rw, rh,
2811 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2814 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2816 gint rx, ry, rw, rh;
2818 if (util_clip_region(x, y, width, height,
2820 &rx, &ry, &rw, &rh))
2822 pixbuf_draw_rect_fill(pixbuf,
2823 rx - x, ry - y, rw, rh,
2824 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2828 list = pan_layout_intersect(pw, x, y, width, height);
2833 gint tx, ty, tw, th;
2834 gint rx, ry, rw, rh;
2841 if (pi->type == ITEM_THUMB && pi->pixbuf)
2843 tw = gdk_pixbuf_get_width(pi->pixbuf);
2844 th = gdk_pixbuf_get_height(pi->pixbuf);
2846 tx = pi->x + (pi->width - tw) / 2;
2847 ty = pi->y + (pi->height - th) / 2;
2849 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2851 if (util_clip_region(x, y, width, height,
2852 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2853 &rx, &ry, &rw, &rh))
2855 pixbuf_draw_shadow(pixbuf,
2856 rx - x, ry - y, rw, rh,
2857 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2859 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2864 if (util_clip_region(x, y, width, height,
2865 tx + tw, ty + PAN_SHADOW_OFFSET,
2866 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2867 &rx, &ry, &rw, &rh))
2869 pixbuf_draw_shadow(pixbuf,
2870 rx - x, ry - y, rw, rh,
2871 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2873 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2875 if (util_clip_region(x, y, width, height,
2876 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2877 &rx, &ry, &rw, &rh))
2879 pixbuf_draw_shadow(pixbuf,
2880 rx - x, ry - y, rw, rh,
2881 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2883 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2887 if (util_clip_region(x, y, width, height,
2889 &rx, &ry, &rw, &rh))
2891 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2894 1.0, 1.0, GDK_INTERP_NEAREST,
2898 if (util_clip_region(x, y, width, height,
2899 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2900 &rx, &ry, &rw, &rh))
2902 pixbuf_draw_rect_fill(pixbuf,
2903 rx - x, ry - y, rw, rh,
2904 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2906 if (util_clip_region(x, y, width, height,
2907 tx, ty, PAN_OUTLINE_THICKNESS, th,
2908 &rx, &ry, &rw, &rh))
2910 pixbuf_draw_rect_fill(pixbuf,
2911 rx - x, ry - y, rw, rh,
2912 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2914 if (util_clip_region(x, y, width, height,
2915 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2916 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2917 &rx, &ry, &rw, &rh))
2919 pixbuf_draw_rect_fill(pixbuf,
2920 rx - x, ry - y, rw, rh,
2921 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2923 if (util_clip_region(x, y, width, height,
2924 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2925 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2926 &rx, &ry, &rw, &rh))
2928 pixbuf_draw_rect_fill(pixbuf,
2929 rx - x, ry - y, rw, rh,
2930 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2933 else if (pi->type == ITEM_THUMB)
2935 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2936 th = pi->height - PAN_SHADOW_OFFSET * 2;
2937 tx = pi->x + PAN_SHADOW_OFFSET;
2938 ty = pi->y + PAN_SHADOW_OFFSET;
2940 if (util_clip_region(x, y, width, height,
2942 &rx, &ry, &rw, &rh))
2946 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2947 pixbuf_draw_rect_fill(pixbuf,
2948 rx - x, ry - y, rw, rh,
2950 PAN_SHADOW_ALPHA / d);
2953 pan_layout_queue(pw, pi);
2955 else if (pi->type == ITEM_IMAGE)
2957 if (util_clip_region(x, y, width, height,
2958 pi->x, pi->y, pi->width, pi->height,
2959 &rx, &ry, &rw, &rh))
2963 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2966 1.0, 1.0, GDK_INTERP_NEAREST,
2971 pixbuf_draw_rect_fill(pixbuf,
2972 rx - x, ry - y, rw, rh,
2973 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2974 pan_layout_queue(pw, pi);
2978 else if (pi->type == ITEM_BOX)
2992 if (pi->color_a > 254)
2994 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2995 shadow[0], bh - shadow[0],
2996 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2998 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2999 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
3001 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
3003 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
3008 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
3009 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
3011 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
3013 PAN_SHADOW_COLOR, a);
3017 if (util_clip_region(x, y, width, height,
3018 pi->x, pi->y, bw, bh,
3019 &rx, &ry, &rw, &rh))
3021 pixbuf_draw_rect_fill(pixbuf,
3022 rx - x, ry - y, rw, rh,
3023 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3025 if (util_clip_region(x, y, width, height,
3026 pi->x, pi->y, bw, pi->border,
3027 &rx, &ry, &rw, &rh))
3029 pixbuf_draw_rect_fill(pixbuf,
3030 rx - x, ry - y, rw, rh,
3031 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3033 if (util_clip_region(x, y, width, height,
3034 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
3035 &rx, &ry, &rw, &rh))
3037 pixbuf_draw_rect_fill(pixbuf,
3038 rx - x, ry - y, rw, rh,
3039 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3041 if (util_clip_region(x, y, width, height,
3042 pi->x + bw - pi->border, pi->y + pi->border,
3043 pi->border, bh - pi->border * 2,
3044 &rx, &ry, &rw, &rh))
3046 pixbuf_draw_rect_fill(pixbuf,
3047 rx - x, ry - y, rw, rh,
3048 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3050 if (util_clip_region(x, y, width, height,
3051 pi->x, pi->y + bh - pi->border,
3053 &rx, &ry, &rw, &rh))
3055 pixbuf_draw_rect_fill(pixbuf,
3056 rx - x, ry - y, rw, rh,
3057 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3060 else if (pi->type == ITEM_TRIANGLE)
3062 if (util_clip_region(x, y, width, height,
3063 pi->x, pi->y, pi->width, pi->height,
3064 &rx, &ry, &rw, &rh) && pi->data)
3066 gint *coord = pi->data;
3067 pixbuf_draw_triangle(pixbuf,
3068 rx - x, ry - y, rw, rh,
3069 coord[0] - x, coord[1] - y,
3070 coord[2] - x, coord[3] - y,
3071 coord[4] - x, coord[5] - y,
3072 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3074 if (pi->border & BORDER_1)
3076 pixbuf_draw_line(pixbuf,
3077 rx - x, ry - y, rw, rh,
3078 coord[0] - x, coord[1] - y,
3079 coord[2] - x, coord[3] - y,
3080 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3082 if (pi->border & BORDER_2)
3084 pixbuf_draw_line(pixbuf,
3085 rx - x, ry - y, rw, rh,
3086 coord[2] - x, coord[3] - y,
3087 coord[4] - x, coord[5] - y,
3088 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3090 if (pi->border & BORDER_3)
3092 pixbuf_draw_line(pixbuf,
3093 rx - x, ry - y, rw, rh,
3094 coord[4] - x, coord[5] - y,
3095 coord[0] - x, coord[1] - y,
3096 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3100 else if (pi->type == ITEM_TEXT && pi->text)
3102 PangoLayout *layout;
3104 layout = pan_item_text_layout(pi, (GtkWidget *)pr);
3105 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
3106 pi->x - x + pi->border, pi->y - y + pi->border,
3107 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3108 g_object_unref(G_OBJECT(layout));
3114 if (x%512 == 0 && y%512 == 0)
3116 PangoLayout *layout;
3119 layout = gtk_widget_create_pango_layout((GtkWidget *)pr, NULL);
3121 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
3122 (x / pr->source_tile_width) +
3123 (y / pr->source_tile_height * (pr->image_width/pr->source_tile_width + 1)));
3124 pango_layout_set_text(layout, buf, -1);
3127 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr, 0, 0, 0, 0, 0, 255);
3129 g_object_unref(G_OBJECT(layout));
3136 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
3137 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
3139 PanWindow *pw = data;
3143 list = pan_layout_intersect(pw, x, y, width, height);
3152 if (pi->refcount > 0)
3156 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
3161 pw->queue = g_list_remove(pw->queue, pi);
3164 if (pw->queue_pi == pi) pw->queue_pi = NULL;
3167 g_object_unref(pi->pixbuf);
3179 *-----------------------------------------------------------------------------
3181 *-----------------------------------------------------------------------------
3184 static void pan_window_message(PanWindow *pw, const gchar *text)
3194 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
3198 work = pw->list_static;
3199 if (pw->layout == LAYOUT_CALENDAR)
3209 pi->type == ITEM_BOX &&
3210 pi->key && strcmp(pi->key, "dot") == 0)
3212 size += pi->fd->size;
3227 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
3229 size += pi->fd->size;
3235 ss = text_from_size_abrev(size);
3236 buf = g_strdup_printf(_("%d images, %s"), count, ss);
3238 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
3242 static void pan_warning_folder(const gchar *path, GtkWidget *parent)
3246 message = g_strdup_printf(_("The pan view does not support the folder \"%s\"."), path);
3247 warning_dialog(_("Folder not supported"), message,
3248 GTK_STOCK_DIALOG_INFO, parent);
3252 static void pan_window_zoom_limit(PanWindow *pw)
3258 case LAYOUT_SIZE_THUMB_DOTS:
3259 case LAYOUT_SIZE_THUMB_NONE:
3260 case LAYOUT_SIZE_THUMB_SMALL:
3261 case LAYOUT_SIZE_THUMB_NORMAL:
3263 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
3267 case LAYOUT_SIZE_THUMB_LARGE:
3270 case LAYOUT_SIZE_10:
3271 case LAYOUT_SIZE_25:
3274 case LAYOUT_SIZE_33:
3275 case LAYOUT_SIZE_50:
3276 case LAYOUT_SIZE_100:
3282 image_zoom_set_limits(pw->imd, min, 32.0);
3285 static gint pan_window_layout_update_idle_cb(gpointer data)
3287 PanWindow *pw = data;
3293 if (pw->size > LAYOUT_SIZE_THUMB_LARGE ||
3294 (pw->exif_date_enable && (pw->layout == LAYOUT_TIMELINE || pw->layout == LAYOUT_CALENDAR)))
3296 if (!pw->cache_list && !pw->cache_todo)
3298 pan_cache_fill(pw, pw->path);
3301 pan_window_message(pw, _("Reading image data..."));
3309 if (pw->cache_count == pw->cache_total)
3311 pan_window_message(pw, _("Sorting..."));
3313 else if (pw->cache_tick > 9)
3317 buf = g_strdup_printf("%s %d", _("Reading image data..."),
3318 pw->cache_total - pw->cache_count);
3319 pan_window_message(pw, buf);
3325 if (pan_cache_step(pw)) return TRUE;
3332 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3334 pan_window_zoom_limit(pw);
3336 if (width > 0 && height > 0)
3340 printf("Canvas size is %d x %d\n", width, height);
3342 pan_grid_build(pw, width, height, 1000);
3344 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
3345 PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
3346 pan_window_request_tile_cb,
3347 pan_window_dispose_tile_cb, pw, 1.0);
3349 if (scroll_x == 0 && scroll_y == 0)
3357 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
3360 pan_window_message(pw, NULL);
3366 static void pan_window_layout_update_idle(PanWindow *pw)
3368 if (pw->idle_id == -1)
3370 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3374 static void pan_window_layout_update(PanWindow *pw)
3376 pan_window_message(pw, _("Sorting images..."));
3377 pan_window_layout_update_idle(pw);
3380 static void pan_window_layout_set_path(PanWindow *pw, const gchar *path)
3384 if (strcmp(path, "/") == 0)
3386 pan_warning_folder(path, pw->window);
3391 pw->path = g_strdup(path);
3393 pan_window_layout_update(pw);
3397 *-----------------------------------------------------------------------------
3398 * pan window keyboard
3399 *-----------------------------------------------------------------------------
3402 static const gchar *pan_menu_click_path(PanWindow *pw)
3404 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3408 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3410 PanWindow *pw = data;
3412 gdk_window_get_origin(pw->imd->pr->window, x, y);
3413 popup_menu_position_clamp(menu, x, y, 0);
3416 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3418 PanWindow *pw = data;
3421 gint stop_signal = FALSE;
3427 pr = PIXBUF_RENDERER(pw->imd->pr);
3428 path = pan_menu_click_path(pw);
3430 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pw->imd->widget)));
3435 switch (event->keyval)
3437 case GDK_Left: case GDK_KP_Left:
3440 case GDK_Right: case GDK_KP_Right:
3443 case GDK_Up: case GDK_KP_Up:
3446 case GDK_Down: case GDK_KP_Down:
3449 case GDK_Page_Up: case GDK_KP_Page_Up:
3450 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
3452 case GDK_Page_Down: case GDK_KP_Page_Down:
3453 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
3455 case GDK_Home: case GDK_KP_Home:
3456 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
3458 case GDK_End: case GDK_KP_End:
3459 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
3462 stop_signal = FALSE;
3466 if (x != 0 || y!= 0)
3468 if (event->state & GDK_SHIFT_MASK)
3473 keyboard_scroll_calc(&x, &y, event);
3474 pixbuf_renderer_scroll(pr, x, y);
3478 if (stop_signal) return stop_signal;
3480 if (event->state & GDK_CONTROL_MASK)
3485 switch (event->keyval)
3518 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3521 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3524 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3527 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3530 if (path) info_window_new(path, NULL);
3533 pan_window_close(pw);
3536 stop_signal = FALSE;
3540 if (n != -1 && path)
3542 if (!editor_window_flag_set(n))
3544 pan_fullscreen_toggle(pw, TRUE);
3546 start_editor_from_file(n, path);
3554 switch (event->keyval)
3556 case '+': case '=': case GDK_KP_Add:
3557 pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
3559 case '-': case GDK_KP_Subtract:
3560 pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
3562 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3563 pixbuf_renderer_zoom_set(pr, 1.0);
3566 pixbuf_renderer_zoom_set(pr, 2.0);
3569 pixbuf_renderer_zoom_set(pr, 3.0);
3572 pixbuf_renderer_zoom_set(pr, 4.0);
3575 pixbuf_renderer_zoom_set(pr, -4.0);
3578 pixbuf_renderer_zoom_set(pr, -3.0);
3581 pixbuf_renderer_zoom_set(pr, -2.0);
3586 pan_fullscreen_toggle(pw, FALSE);
3590 pan_overlay_toggle(pw);
3593 case GDK_Delete: case GDK_KP_Delete:
3598 if (GTK_WIDGET_VISIBLE(pw->search_box))
3600 gtk_widget_grab_focus(pw->search_entry);
3604 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3609 stop_signal = FALSE;
3615 pan_fullscreen_toggle(pw, TRUE);
3617 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3619 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3623 stop_signal = FALSE;
3628 menu = pan_popup_menu(pw);
3629 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3632 stop_signal = FALSE;
3639 switch (event->keyval)
3644 pan_fullscreen_toggle(pw, TRUE);
3646 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3648 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
3649 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3653 stop_signal = FALSE;
3657 stop_signal = FALSE;
3667 *-----------------------------------------------------------------------------
3669 *-----------------------------------------------------------------------------
3672 typedef struct _PanTextAlignment PanTextAlignment;
3673 struct _PanTextAlignment {
3685 static PanTextAlignment *pan_text_alignment_new(PanWindow *pw, gint x, gint y, const gchar *key)
3687 PanTextAlignment *ta;
3689 ta = g_new0(PanTextAlignment, 1);
3696 ta->key = g_strdup(key);
3701 static PanItem *pan_text_alignment_add(PanTextAlignment *ta,
3702 const gchar *label, const gchar *text)
3708 item = pan_item_new_text(ta->pw, ta->x, ta->y, label,
3710 PAN_POPUP_TEXT_COLOR, 255);
3711 pan_item_set_key(item, ta->key);
3717 ta->column1 = g_list_append(ta->column1, item);
3721 item = pan_item_new_text(ta->pw, ta->x, ta->y, text,
3723 PAN_POPUP_TEXT_COLOR, 255);
3724 pan_item_set_key(item, ta->key);
3730 ta->column2 = g_list_append(ta->column2, item);
3735 static void pan_text_alignment_calc(PanTextAlignment *ta, PanItem *box)
3745 work1 = ta->column1;
3751 work1 = work1->next;
3753 if (p && p->width > cw1) cw1 = p->width;
3756 work2 = ta->column2;
3762 work2 = work2->next;
3764 if (p && p->width > cw2) cw2 = p->width;
3769 work1 = ta->column1;
3770 work2 = ta->column2;
3771 while (work1 && work2)
3779 work1 = work1->next;
3780 work2 = work2->next;
3786 pan_item_size_by_item(box, p1, PREF_PAD_BORDER);
3787 height = p1->height;
3791 p2->x = x + cw1 + PREF_PAD_SPACE;
3793 pan_item_size_by_item(box, p2, PREF_PAD_BORDER);
3794 if (height < p2->height) height = p2->height;
3797 if (!p1 && !p2) height = PREF_PAD_GROUP;
3803 static void pan_text_alignment_free(PanTextAlignment *ta)
3807 g_list_free(ta->column1);
3808 g_list_free(ta->column2);
3813 static void pan_info_add_exif(PanTextAlignment *ta, FileData *fd)
3820 exif = exif_read(fd->path);
3823 pan_text_alignment_add(ta, NULL, NULL);
3825 for (i = 0; i < bar_exif_key_count; i++)
3830 label = g_strdup_printf("%s:", exif_get_description_by_key(bar_exif_key_list[i]));
3831 text = exif_get_data_as_text(exif, bar_exif_key_list[i]);
3832 text = bar_exif_validate_text(text);
3833 pan_text_alignment_add(ta, label, text);
3838 work = g_list_last(history_list_get_by_key("exif_extras"));
3839 if (work) pan_text_alignment_add(ta, "---", NULL);
3849 label = g_strdup_printf("%s:", name);
3850 text = exif_get_data_as_text(exif, name);
3851 text = bar_exif_validate_text(text);
3852 pan_text_alignment_add(ta, label, text);
3860 static void pan_info_update(PanWindow *pw, PanItem *pi)
3862 PanTextAlignment *ta;
3866 gint x1, y1, x2, y2, x3, y3;
3869 if (pw->click_pi == pi) return;
3870 if (pi && !pi->fd) pi = NULL;
3872 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3877 if (debug) printf("info set to %s\n", pi->fd->path);
3879 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3881 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3882 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3883 pan_item_set_key(pbox, "info");
3885 if (pi->type == ITEM_THUMB && pi->pixbuf)
3887 w = gdk_pixbuf_get_width(pi->pixbuf);
3888 h = gdk_pixbuf_get_height(pi->pixbuf);
3890 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3891 y1 = pi->y + (pi->height - h) / 2 + 8;
3895 x1 = pi->x + pi->width - 8;
3903 util_clip_triangle(x1, y1, x2, y2, x3, y3,
3906 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3907 x1, y1, x2, y2, x3, y3,
3908 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3909 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3910 pan_item_set_key(p, "info");
3911 pan_item_added(pw, p);
3913 ta = pan_text_alignment_new(pw, pbox->x + PREF_PAD_BORDER, pbox->y + PREF_PAD_BORDER, "info");
3915 pan_text_alignment_add(ta, _("Filename:"), pi->fd->name);
3916 pan_text_alignment_add(ta, _("Date:"), text_from_time(pi->fd->date));
3917 buf = text_from_size(pi->fd->size);
3918 pan_text_alignment_add(ta, _("Size:"), buf);
3921 if (pw->info_includes_exif)
3923 pan_info_add_exif(ta, pi->fd);
3926 pan_text_alignment_calc(ta, pbox);
3927 pan_text_alignment_free(ta);
3929 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3930 pan_item_added(pw, pbox);
3932 if (pw->info_includes_image)
3935 if (image_load_dimensions(pi->fd->path, &iw, &ih))
3937 pbox = pan_item_new_box(pw, NULL, pbox->x, pbox->y + pbox->height + 8, 10, 10,
3939 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3940 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3941 pan_item_set_key(pbox, "info");
3943 p = pan_item_new_image(pw, file_data_new_simple(pi->fd->path),
3944 pbox->x + PREF_PAD_BORDER, pbox->y + PREF_PAD_BORDER, iw, ih);
3945 pan_item_set_key(p, "info");
3946 pan_item_size_by_item(pbox, p, PREF_PAD_BORDER);
3948 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3949 pan_item_added(pw, pbox);
3951 pan_layout_resize(pw);
3958 *-----------------------------------------------------------------------------
3960 *-----------------------------------------------------------------------------
3963 static void pan_search_status(PanWindow *pw, const gchar *text)
3965 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3968 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3976 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3978 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3979 if (!list) return FALSE;
3981 found = g_list_find(list, pw->click_pi);
3982 if (found && found->next)
3984 found = found->next;
3992 pan_info_update(pw, pi);
3993 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3995 buf = g_strdup_printf("%s ( %d / %d )",
3996 (path[0] == '/') ? _("path found") : _("filename found"),
3997 g_list_index(list, pi) + 1,
3998 g_list_length(list));
3999 pan_search_status(pw, buf);
4007 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
4015 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
4017 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
4018 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
4023 needle = g_utf8_strdown(text, -1);
4024 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
4027 if (!list) return FALSE;
4029 found = g_list_find(list, pw->click_pi);
4030 if (found && found->next)
4032 found = found->next;
4040 pan_info_update(pw, pi);
4041 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
4043 buf = g_strdup_printf("%s ( %d / %d )",
4045 g_list_index(list, pi) + 1,
4046 g_list_length(list));
4047 pan_search_status(pw, buf);
4055 static gint valid_date_separator(gchar c)
4057 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
4060 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
4061 gint year, gint month, gint day,
4067 work = g_list_last(pw->list_static);
4075 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
4076 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
4080 tl = localtime(&pi->fd->date);
4085 match = (tl->tm_year == year - 1900);
4086 if (match && month >= 0) match = (tl->tm_mon == month - 1);
4087 if (match && day > 0) match = (tl->tm_mday == day);
4089 if (match) list = g_list_prepend(list, pi);
4094 return g_list_reverse(list);
4097 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
4113 if (!text) return FALSE;
4115 ptr = (gchar *)text;
4116 while (*ptr != '\0')
4118 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
4123 if (t == -1) return FALSE;
4125 if (!lt) return FALSE;
4127 if (valid_date_separator(*text))
4130 mptr = (gchar *)text;
4134 year = (gint)strtol(text, &mptr, 10);
4135 if (mptr == text) return FALSE;
4138 if (*mptr != '\0' && valid_date_separator(*mptr))
4143 month = strtol(mptr, &dptr, 10);
4146 if (valid_date_separator(*dptr))
4148 month = lt->tm_mon + 1;
4156 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
4160 day = strtol(dptr, &eptr, 10);
4170 year = lt->tm_year + 1900;
4172 else if (year < 100)
4181 month < -1 || month == 0 || month > 12 ||
4182 day < -1 || day == 0 || day > 31) return FALSE;
4184 t = date_to_time(year, month, day);
4185 if (t < 0) return FALSE;
4187 if (pw->layout == LAYOUT_CALENDAR)
4189 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
4195 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
4196 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
4201 found = g_list_find(list, pw->search_pi);
4202 if (found && found->next)
4204 found = found->next;
4215 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
4217 pan_info_update(pw, NULL);
4218 pan_calendar_update(pw, pi);
4219 image_scroll_to_point(pw->imd,
4220 pi->x + pi->width / 2,
4221 pi->y + pi->height / 2, 0.5, 0.5);
4225 pan_info_update(pw, pi);
4226 image_scroll_to_point(pw->imd,
4227 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
4233 buf = date_value_string(t, DATE_LENGTH_MONTH);
4238 buf = g_strdup_printf("%d %s", day, tmp);
4244 buf = date_value_string(t, DATE_LENGTH_YEAR);
4249 buf_count = g_strdup_printf("( %d / %d )",
4250 g_list_index(list, pi) + 1,
4251 g_list_length(list));
4255 buf_count = g_strdup_printf("(%s)", _("no match"));
4258 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
4261 pan_search_status(pw, message);
4269 static void pan_search_activate_cb(const gchar *text, gpointer data)
4271 PanWindow *pw = data;
4275 tab_completion_append_to_history(pw->search_entry, text);
4277 if (pan_search_by_path(pw, text)) return;
4279 if ((pw->layout == LAYOUT_TIMELINE ||
4280 pw->layout == LAYOUT_CALENDAR) &&
4281 pan_search_by_date(pw, text))
4286 if (pan_search_by_partial(pw, text)) return;
4288 pan_search_status(pw, _("no match"));
4291 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
4293 PanWindow *pw = data;
4296 visible = GTK_WIDGET_VISIBLE(pw->search_box);
4297 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
4301 gtk_widget_hide(pw->search_box);
4302 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
4306 gtk_widget_show(pw->search_box);
4307 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
4308 gtk_widget_grab_focus(pw->search_entry);
4314 *-----------------------------------------------------------------------------
4315 * view window main routines
4316 *-----------------------------------------------------------------------------
4319 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
4321 PanWindow *pw = data;
4329 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
4330 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
4333 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "info");
4334 if (pi && event->button == 1)
4336 pan_info_update(pw, NULL);
4340 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
4343 switch (event->button)
4346 pan_info_update(pw, pi);
4348 if (!pi && pw->layout == LAYOUT_CALENDAR)
4350 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
4351 pan_calendar_update(pw, pi);
4357 pan_info_update(pw, pi);
4358 menu = pan_popup_menu(pw);
4359 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
4366 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
4369 PanWindow *pw = data;
4376 if (!(event->state & GDK_SHIFT_MASK))
4382 if (event->state & GDK_CONTROL_MASK)
4384 switch (event->direction)
4387 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
4388 (gint)event->x, (gint)event->y);
4390 case GDK_SCROLL_DOWN:
4391 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
4392 (gint)event->x, (gint)event->y);
4400 switch (event->direction)
4403 pixbuf_renderer_scroll(pr, 0, -h);
4405 case GDK_SCROLL_DOWN:
4406 pixbuf_renderer_scroll(pr, 0, h);
4408 case GDK_SCROLL_LEFT:
4409 pixbuf_renderer_scroll(pr, -w, 0);
4411 case GDK_SCROLL_RIGHT:
4412 pixbuf_renderer_scroll(pr, w, 0);
4420 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
4422 g_signal_connect(G_OBJECT(imd->pr), "clicked",
4423 G_CALLBACK(button_cb), pw);
4424 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
4425 G_CALLBACK(scroll_cb), pw);
4428 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
4430 PanWindow *pw = data;
4433 pw->imd = pw->imd_normal;
4436 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
4438 if (force_off && !pw->fs) return;
4442 fullscreen_stop(pw->fs);
4446 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
4447 pan_image_set_buttons(pw, pw->fs->imd);
4448 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
4449 G_CALLBACK(pan_window_key_press_cb), pw);
4451 pw->imd = pw->fs->imd;
4455 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
4457 PanWindow *pw = data;
4460 text = image_zoom_get_as_text(pw->imd);
4461 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4465 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
4467 PanWindow *pw = data;
4472 if (pr->scale == 0.0) return;
4474 pixbuf_renderer_get_visible_rect(pr, &rect);
4475 pixbuf_renderer_get_image_size(pr, &width, &height);
4477 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4478 adj->page_size = (gdouble)rect.width;
4479 adj->page_increment = adj->page_size / 2.0;
4480 adj->step_increment = 48.0 / pr->scale;
4482 adj->upper = MAX((gdouble)width, 1.0);
4483 adj->value = (gdouble)rect.x;
4485 pref_signal_block_data(pw->scrollbar_h, pw);
4486 gtk_adjustment_changed(adj);
4487 gtk_adjustment_value_changed(adj);
4488 pref_signal_unblock_data(pw->scrollbar_h, pw);
4490 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4491 adj->page_size = (gdouble)rect.height;
4492 adj->page_increment = adj->page_size / 2.0;
4493 adj->step_increment = 48.0 / pr->scale;
4495 adj->upper = MAX((gdouble)height, 1.0);
4496 adj->value = (gdouble)rect.y;
4498 pref_signal_block_data(pw->scrollbar_v, pw);
4499 gtk_adjustment_changed(adj);
4500 gtk_adjustment_value_changed(adj);
4501 pref_signal_unblock_data(pw->scrollbar_v, pw);
4504 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4506 PanWindow *pw = data;
4510 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4512 if (!pr->scale) return;
4514 x = (gint)gtk_range_get_value(range);
4516 pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
4519 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4521 PanWindow *pw = data;
4525 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4527 if (!pr->scale) return;
4529 y = (gint)gtk_range_get_value(range);
4531 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
4534 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4536 PanWindow *pw = data;
4538 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4539 pan_window_layout_update(pw);
4542 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4544 PanWindow *pw = data;
4546 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4547 pan_window_layout_update(pw);
4551 static void pan_window_date_toggle_cb(GtkWidget *button, gpointer data)
4553 PanWindow *pw = data;
4555 pw->exif_date_enable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4556 pan_window_layout_update(pw);
4560 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4562 PanWindow *pw = data;
4565 path = remove_trailing_slash(new_text);
4566 parse_out_relatives(path);
4570 warning_dialog(_("Folder not found"),
4571 _("The entered path is not a folder"),
4572 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4576 tab_completion_append_to_history(pw->path_entry, path);
4578 pan_window_layout_set_path(pw, path);
4584 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4586 PanWindow *pw = data;
4589 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4591 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4592 pan_window_entry_activate_cb(text, pw);
4596 static void pan_window_close(PanWindow *pw)
4598 pan_window_list = g_list_remove(pan_window_list, pw);
4600 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_EXIF_DATE, pw->exif_date_enable);
4601 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_INFO_IMAGE, pw->info_includes_image);
4602 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_INFO_EXIF, pw->info_includes_exif);
4604 if (pw->idle_id != -1)
4606 g_source_remove(pw->idle_id);
4609 pan_fullscreen_toggle(pw, TRUE);
4610 gtk_widget_destroy(pw->window);
4612 pan_window_items_free(pw);
4620 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4622 PanWindow *pw = data;
4624 pan_window_close(pw);
4628 static void pan_window_new_real(const gchar *path)
4637 GdkGeometry geometry;
4639 pw = g_new0(PanWindow, 1);
4641 pw->path = g_strdup(path);
4642 pw->layout = LAYOUT_TIMELINE;
4643 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4644 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4645 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4647 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_EXIF_DATE, &pw->exif_date_enable))
4649 pw->exif_date_enable = FALSE;
4651 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_INFO_IMAGE, &pw->info_includes_image))
4653 pw->info_includes_image = FALSE;
4655 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_INFO_EXIF, &pw->info_includes_exif))
4657 pw->info_includes_exif = TRUE;
4660 pw->ignore_symlinks = TRUE;
4663 pw->list_static = NULL;
4664 pw->list_grid = NULL;
4667 pw->overlay_id = -1;
4670 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4672 geometry.min_width = 8;
4673 geometry.min_height = 8;
4674 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4676 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4677 gtk_window_set_title (GTK_WINDOW(pw->window), _("Pan View - GQview"));
4678 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4679 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4681 window_set_icon(pw->window, NULL, NULL);
4683 vbox = gtk_vbox_new(FALSE, 0);
4684 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4685 gtk_widget_show(vbox);
4687 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4689 pref_spacer(box, 0);
4690 pref_label_new(box, _("Location:"));
4691 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view_path", -1,
4692 pan_window_entry_activate_cb, pw);
4693 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4694 G_CALLBACK(pan_window_entry_change_cb), pw);
4695 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4696 gtk_widget_show(combo);
4698 combo = gtk_combo_box_new_text();
4699 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4700 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4701 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4702 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4703 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4705 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4706 g_signal_connect(G_OBJECT(combo), "changed",
4707 G_CALLBACK(pan_window_layout_change_cb), pw);
4708 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4709 gtk_widget_show(combo);
4711 combo = gtk_combo_box_new_text();
4712 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4713 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4714 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4715 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4716 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4717 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4718 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4719 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4720 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4721 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4723 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4724 g_signal_connect(G_OBJECT(combo), "changed",
4725 G_CALLBACK(pan_window_layout_size_cb), pw);
4726 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4727 gtk_widget_show(combo);
4729 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4730 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4731 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4733 pw->imd = image_new(TRUE);
4734 pw->imd_normal = pw->imd;
4736 g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
4737 G_CALLBACK(pan_window_image_zoom_cb), pw);
4738 g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
4739 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4741 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4742 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4743 gtk_widget_show(GTK_WIDGET(pw->imd->widget));
4745 pan_window_dnd_init(pw);
4747 pan_image_set_buttons(pw, pw->imd);
4749 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4750 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4751 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4752 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4753 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4754 gtk_widget_show(pw->scrollbar_h);
4756 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4757 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4758 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4759 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4760 0, GTK_FILL | GTK_EXPAND, 0, 0);
4761 gtk_widget_show(pw->scrollbar_v);
4765 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4766 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4768 pref_spacer(pw->search_box, 0);
4769 pref_label_new(pw->search_box, _("Find:"));
4771 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4772 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4773 gtk_widget_show(hbox);
4775 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4776 pan_search_activate_cb, pw);
4777 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4778 gtk_widget_show(combo);
4780 pw->search_label = gtk_label_new("");
4781 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4782 gtk_widget_show(pw->search_label);
4786 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4788 frame = gtk_frame_new(NULL);
4789 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4790 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4791 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4792 gtk_widget_show(frame);
4794 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4795 gtk_container_add(GTK_CONTAINER(frame), hbox);
4796 gtk_widget_show(hbox);
4798 pref_spacer(hbox, 0);
4799 pw->label_message = pref_label_new(hbox, "");
4801 frame = gtk_frame_new(NULL);
4802 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4803 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4804 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4805 gtk_widget_show(frame);
4807 pw->label_zoom = gtk_label_new("");
4808 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4809 gtk_widget_show(pw->label_zoom);
4812 pw->date_button = pref_checkbox_new(box, _("Use Exif date"), pw->exif_date_enable,
4813 G_CALLBACK(pan_window_date_toggle_cb), pw);
4816 pw->search_button = gtk_toggle_button_new();
4817 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4818 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4819 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4820 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4821 gtk_widget_show(hbox);
4822 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4823 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4824 gtk_widget_show(pw->search_button_arrow);
4825 pref_label_new(hbox, _("Find"));
4827 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4828 gtk_widget_show(pw->search_button);
4829 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4830 G_CALLBACK(pan_search_toggle_cb), pw);
4832 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4833 G_CALLBACK(pan_window_delete_cb), pw);
4834 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4835 G_CALLBACK(pan_window_key_press_cb), pw);
4837 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4839 pan_window_layout_update(pw);
4841 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4842 gtk_widget_show(pw->window);
4844 pan_window_list = g_list_append(pan_window_list, pw);
4848 *-----------------------------------------------------------------------------
4849 * peformance warnings
4850 *-----------------------------------------------------------------------------
4853 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4857 generic_dialog_close(gd);
4859 pan_window_new_real(path);
4863 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4867 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4868 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4871 static gint pan_warning(const gchar *path)
4877 GtkWidget *ct_button;
4880 if (path && strcmp(path, "/") == 0)
4882 pan_warning_folder(path, NULL);
4886 if (enable_thumb_caching &&
4887 thumbnail_spec_standard) return FALSE;
4889 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4890 if (hide_dlg) return FALSE;
4892 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4894 gd->data = g_strdup(path);
4895 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4896 pan_warning_ok_cb, TRUE);
4898 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4899 _("Pan view performance may be poor."),
4900 _("To improve performance of thumbnails in the pan view the"
4901 " following options can be enabled. Note that both options"
4902 " must be enabled to notice a change in performance."));
4904 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4905 pref_spacer(group, PREF_PAD_INDENT);
4906 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4908 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4909 enable_thumb_caching, &enable_thumb_caching);
4910 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4911 thumbnail_spec_standard, &thumbnail_spec_standard);
4912 pref_checkbox_link_sensitivity(ct_button, button);
4916 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4917 G_CALLBACK(pan_warning_hide_cb), NULL);
4919 gtk_widget_show(gd->dialog);
4926 *-----------------------------------------------------------------------------
4928 *-----------------------------------------------------------------------------
4931 void pan_window_new(const gchar *path)
4933 if (pan_warning(path)) return;
4935 pan_window_new_real(path);
4939 *-----------------------------------------------------------------------------
4941 *-----------------------------------------------------------------------------
4944 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4946 PanWindow *pw = data;
4949 path = pan_menu_click_path(pw);
4952 pan_fullscreen_toggle(pw, TRUE);
4953 view_window_new(path);
4957 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4963 pw = submenu_item_get_data(widget);
4964 n = GPOINTER_TO_INT(data);
4967 path = pan_menu_click_path(pw);
4970 if (!editor_window_flag_set(n))
4972 pan_fullscreen_toggle(pw, TRUE);
4974 start_editor_from_file(n, path);
4978 static void pan_info_cb(GtkWidget *widget, gpointer data)
4980 PanWindow *pw = data;
4983 path = pan_menu_click_path(pw);
4984 if (path) info_window_new(path, NULL);
4987 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4989 PanWindow *pw = data;
4991 image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
4994 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4996 PanWindow *pw = data;
4998 image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
5001 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
5003 PanWindow *pw = data;
5005 image_zoom_set(pw->imd, 1.0);
5008 static void pan_copy_cb(GtkWidget *widget, gpointer data)
5010 PanWindow *pw = data;
5013 path = pan_menu_click_path(pw);
5014 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
5017 static void pan_move_cb(GtkWidget *widget, gpointer data)
5019 PanWindow *pw = data;
5022 path = pan_menu_click_path(pw);
5023 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
5026 static void pan_rename_cb(GtkWidget *widget, gpointer data)
5028 PanWindow *pw = data;
5031 path = pan_menu_click_path(pw);
5032 if (path) file_util_rename(path, NULL, pw->imd->widget);
5035 static void pan_delete_cb(GtkWidget *widget, gpointer data)
5037 PanWindow *pw = data;
5040 path = pan_menu_click_path(pw);
5041 if (path) file_util_delete(path, NULL, pw->imd->widget);
5044 static void pan_exif_date_toggle_cb(GtkWidget *widget, gpointer data)
5046 PanWindow *pw = data;
5048 pw->exif_date_enable = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
5049 pan_window_layout_update(pw);
5052 static void pan_info_toggle_exif_cb(GtkWidget *widget, gpointer data)
5054 PanWindow *pw = data;
5056 pw->info_includes_exif = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
5057 /* fixme: sync info now */
5060 static void pan_info_toggle_image_cb(GtkWidget *widget, gpointer data)
5062 PanWindow *pw = data;
5064 pw->info_includes_image = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
5065 /* fixme: sync info now */
5068 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
5070 PanWindow *pw = data;
5072 pan_fullscreen_toggle(pw, FALSE);
5075 static void pan_close_cb(GtkWidget *widget, gpointer data)
5077 PanWindow *pw = data;
5079 pan_window_close(pw);
5082 static GtkWidget *pan_popup_menu(PanWindow *pw)
5088 active = (pw->click_pi != NULL);
5090 menu = popup_menu_short_lived();
5092 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
5093 G_CALLBACK(pan_zoom_in_cb), pw);
5094 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
5095 G_CALLBACK(pan_zoom_out_cb), pw);
5096 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
5097 G_CALLBACK(pan_zoom_1_1_cb), pw);
5098 menu_item_add_divider(menu);
5100 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
5101 gtk_widget_set_sensitive(item, active);
5103 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
5104 G_CALLBACK(pan_info_cb), pw);
5106 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
5107 G_CALLBACK(pan_new_window_cb), pw);
5109 menu_item_add_divider(menu);
5110 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
5111 G_CALLBACK(pan_copy_cb), pw);
5112 menu_item_add_sensitive(menu, _("_Move..."), active,
5113 G_CALLBACK(pan_move_cb), pw);
5114 menu_item_add_sensitive(menu, _("_Rename..."), active,
5115 G_CALLBACK(pan_rename_cb), pw);
5116 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
5117 G_CALLBACK(pan_delete_cb), pw);
5119 menu_item_add_divider(menu);
5120 item = menu_item_add_check(menu, _("Sort by E_xif date"), pw->exif_date_enable,
5121 G_CALLBACK(pan_exif_date_toggle_cb), pw);
5122 gtk_widget_set_sensitive(item, (pw->layout == LAYOUT_TIMELINE || pw->layout == LAYOUT_CALENDAR));
5124 menu_item_add_divider(menu);
5125 menu_item_add_check(menu, _("Show EXIF information"), pw->info_includes_exif,
5126 G_CALLBACK(pan_info_toggle_exif_cb), pw);
5127 menu_item_add_check(menu, _("Show full size image"), pw->info_includes_image,
5128 G_CALLBACK(pan_info_toggle_image_cb), pw);
5130 menu_item_add_divider(menu);
5134 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
5138 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
5141 menu_item_add_divider(menu);
5142 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
5148 *-----------------------------------------------------------------------------
5150 *-----------------------------------------------------------------------------
5153 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
5155 GtkSelectionData *selection_data, guint info,
5156 guint time, gpointer data)
5158 PanWindow *pw = data;
5160 if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
5162 if (info == TARGET_URI_LIST)
5166 list = uri_list_from_text((gchar *)selection_data->data, TRUE);
5167 if (list && isdir((gchar *)list->data))
5169 gchar *path = list->data;
5171 pan_window_layout_set_path(pw, path);
5174 path_list_free(list);
5178 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
5179 GtkSelectionData *selection_data, guint info,
5180 guint time, gpointer data)
5182 PanWindow *pw = data;
5185 path = pan_menu_click_path(pw);
5195 case TARGET_URI_LIST:
5198 case TARGET_TEXT_PLAIN:
5203 list = g_list_append(NULL, (gchar *)path);
5204 text = uri_text_from_list(list, &len, plain_text);
5208 gtk_selection_data_set (selection_data, selection_data->target,
5209 8, (guchar *)text, len);
5215 gtk_selection_data_set (selection_data, selection_data->target,
5220 static void pan_window_dnd_init(PanWindow *pw)
5224 widget = pw->imd->pr;
5226 gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
5227 dnd_file_drag_types, dnd_file_drag_types_count,
5228 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
5229 g_signal_connect(G_OBJECT(widget), "drag_data_get",
5230 G_CALLBACK(pan_window_set_dnd_data), pw);
5232 gtk_drag_dest_set(widget,
5233 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
5234 dnd_file_drop_types, dnd_file_drop_types_count,
5235 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
5236 g_signal_connect(G_OBJECT(widget), "drag_data_received",
5237 G_CALLBACK(pan_window_get_dnd_data), pw);
5241 *-----------------------------------------------------------------------------
5242 * maintenance (for rename, move, remove)
5243 *-----------------------------------------------------------------------------