7 * This software is released under the GNU General Public License (GNU GPL).
8 * Please read the included file COPYING for more information.
9 * This software comes with no warranty of any kind, use at your own risk!
17 #include "cache-loader.h"
21 #include "fullscreen.h"
23 #include "image-load.h"
27 #include "pixbuf-renderer.h"
28 #include "pixbuf_util.h"
31 #include "ui_bookmark.h"
32 #include "ui_fileops.h"
35 #include "ui_tabcomp.h"
37 #include <gdk/gdkkeysyms.h> /* for keyboard values */
41 #define PAN_WINDOW_DEFAULT_WIDTH 720
42 #define PAN_WINDOW_DEFAULT_HEIGHT 500
44 #define PAN_TILE_SIZE 512
46 #define PAN_THUMB_SIZE_DOTS 4
47 #define PAN_THUMB_SIZE_NONE 24
48 #define PAN_THUMB_SIZE_SMALL 64
49 #define PAN_THUMB_SIZE_NORMAL 128
50 #define PAN_THUMB_SIZE_LARGE 256
51 #define PAN_THUMB_SIZE pw->thumb_size
53 #define PAN_THUMB_GAP_DOTS 2
54 #define PAN_THUMB_GAP_SMALL 14
55 #define PAN_THUMB_GAP_NORMAL 30
56 #define PAN_THUMB_GAP_LARGE 40
57 #define PAN_THUMB_GAP_HUGE 50
58 #define PAN_THUMB_GAP pw->thumb_gap
60 #define PAN_SHADOW_OFFSET 6
61 #define PAN_SHADOW_FADE 5
62 #define PAN_SHADOW_COLOR 0, 0, 0
63 #define PAN_SHADOW_ALPHA 64
65 #define PAN_OUTLINE_THICKNESS 1
66 #define PAN_OUTLINE_COLOR_1 255, 255, 255
67 #define PAN_OUTLINE_COLOR_2 64, 64, 64
68 #define PAN_OUTLINE_ALPHA 180
70 #define PAN_BACKGROUND_COLOR 150, 150, 150
72 #define PAN_GRID_SIZE 60
73 #define PAN_GRID_COLOR 0, 0, 0
74 #define PAN_GRID_ALPHA 20
76 #define PAN_FOLDER_BOX_COLOR 255, 255, 255
77 #define PAN_FOLDER_BOX_ALPHA 100
78 #define PAN_FOLDER_BOX_BORDER 20
80 #define PAN_FOLDER_BOX_OUTLINE_THICKNESS 4
81 #define PAN_FOLDER_BOX_OUTLINE_COLOR 0, 0, 0
82 #define PAN_FOLDER_BOX_OUTLINE_ALPHA 128
84 #define PAN_TEXT_BORDER_SIZE 4
85 #define PAN_TEXT_COLOR 0, 0, 0
87 #define PAN_POPUP_COLOR 255, 255, 225
88 #define PAN_POPUP_ALPHA 255
89 #define PAN_POPUP_BORDER 1
90 #define PAN_POPUP_BORDER_COLOR 0, 0, 0
91 #define PAN_POPUP_TEXT_COLOR 0, 0, 0
93 #define PAN_CAL_POPUP_COLOR 220, 220, 220
94 #define PAN_CAL_POPUP_ALPHA 255
95 #define PAN_CAL_POPUP_BORDER 1
96 #define PAN_CAL_POPUP_BORDER_COLOR 0, 0, 0
97 #define PAN_CAL_POPUP_TEXT_COLOR 0, 0, 0
99 #define PAN_CAL_DAY_WIDTH 100
100 #define PAN_CAL_DAY_HEIGHT 80
102 #define PAN_CAL_DAY_COLOR 255, 255, 255
103 #define PAN_CAL_DAY_ALPHA 220
104 #define PAN_CAL_DAY_BORDER 2
105 #define PAN_CAL_DAY_BORDER_COLOR 0, 0, 0
106 #define PAN_CAL_DAY_TEXT_COLOR 0, 0, 0
108 #define PAN_CAL_MONTH_COLOR 255, 255, 255
109 #define PAN_CAL_MONTH_ALPHA 200
110 #define PAN_CAL_MONTH_BORDER 4
111 #define PAN_CAL_MONTH_BORDER_COLOR 0, 0, 0
112 #define PAN_CAL_MONTH_TEXT_COLOR 0, 0, 0
114 #define PAN_CAL_DOT_SIZE 3
115 #define PAN_CAL_DOT_GAP 2
116 #define PAN_CAL_DOT_COLOR 128, 128, 128
117 #define PAN_CAL_DOT_ALPHA 128
120 #define PAN_GROUP_MAX 16
122 #define ZOOM_INCREMENT 1.0
123 #define ZOOM_LABEL_WIDTH 64
126 #define PAN_PREF_GROUP "pan_view_options"
127 #define PAN_PREF_HIDE_WARNING "hide_performance_warning"
128 #define PAN_PREF_EXIF_DATE "use_exif_date"
134 LAYOUT_FOLDERS_LINEAR,
135 LAYOUT_FOLDERS_FLOWER,
140 LAYOUT_SIZE_THUMB_DOTS = 0,
141 LAYOUT_SIZE_THUMB_NONE,
142 LAYOUT_SIZE_THUMB_SMALL,
143 LAYOUT_SIZE_THUMB_NORMAL,
144 LAYOUT_SIZE_THUMB_LARGE,
163 TEXT_ATTR_BOLD = 1 << 0,
164 TEXT_ATTR_HEADING = 1 << 1,
165 TEXT_ATTR_MARKUP = 1 << 2
176 typedef struct _PanItem PanItem;
191 TextAttrType text_attr;
209 typedef struct _PanWindow PanWindow;
214 ImageWindow *imd_normal;
217 GtkWidget *path_entry;
219 GtkWidget *label_message;
220 GtkWidget *label_zoom;
222 GtkWidget *search_box;
223 GtkWidget *search_entry;
224 GtkWidget *search_label;
225 GtkWidget *search_button;
226 GtkWidget *search_button_arrow;
228 GtkWidget *date_button;
230 GtkWidget *scrollbar_h;
231 GtkWidget *scrollbar_v;
241 gint exif_date_enable;
243 gint ignore_symlinks;
254 CacheLoader *cache_cl;
267 typedef struct _PanGrid PanGrid;
276 typedef struct _PanCacheData PanCacheData;
277 struct _PanCacheData {
283 static GList *pan_window_list = NULL;
286 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend,
287 gint ignore_symlinks);
289 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
291 static void pan_layout_resize(PanWindow *pw);
293 static void pan_window_layout_update_idle(PanWindow *pw);
295 static GtkWidget *pan_popup_menu(PanWindow *pw);
296 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off);
298 static void pan_window_close(PanWindow *pw);
300 static void pan_window_dnd_init(PanWindow *pw);
312 static gint date_compare(time_t a, time_t b, DateLengthType length)
317 if (length == DATE_LENGTH_EXACT) return (a == b);
319 if (!localtime_r(&a, &ta) ||
320 !localtime_r(&b, &tb)) return FALSE;
322 if (ta.tm_year != tb.tm_year) return FALSE;
323 if (length == DATE_LENGTH_YEAR) return TRUE;
325 if (ta.tm_mon != tb.tm_mon) return FALSE;
326 if (length == DATE_LENGTH_MONTH) return TRUE;
328 if (length == DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
330 if (ta.tm_mday != tb.tm_mday) return FALSE;
331 if (length == DATE_LENGTH_DAY) return TRUE;
333 return (ta.tm_hour == tb.tm_hour);
336 static gint date_value(time_t d, DateLengthType length)
340 if (!localtime_r(&d, &td)) return -1;
344 case DATE_LENGTH_DAY:
347 case DATE_LENGTH_WEEK:
350 case DATE_LENGTH_MONTH:
351 return td.tm_mon + 1;
353 case DATE_LENGTH_YEAR:
354 return td.tm_year + 1900;
356 case DATE_LENGTH_EXACT:
364 static gchar *date_value_string(time_t d, DateLengthType length)
368 gchar *format = NULL;
370 if (!localtime_r(&d, &td)) return g_strdup("");
374 case DATE_LENGTH_DAY:
375 return g_strdup_printf("%d", td.tm_mday);
377 case DATE_LENGTH_WEEK:
380 case DATE_LENGTH_MONTH:
383 case DATE_LENGTH_YEAR:
384 return g_strdup_printf("%d", td.tm_year + 1900);
386 case DATE_LENGTH_EXACT:
388 return g_strdup(text_from_time(d));
393 if (format && strftime(buf, sizeof(buf), format, &td) > 0)
395 gchar *ret = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
402 static time_t date_to_time(gint year, gint month, gint day)
409 lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
410 lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
411 lt.tm_year = year - 1900;
418 *-----------------------------------------------------------------------------
420 *-----------------------------------------------------------------------------
423 static void pan_cache_free(PanWindow *pw)
427 work = pw->cache_list;
435 cache_sim_data_free(pc->cd);
436 file_data_free((FileData *)pc);
439 g_list_free(pw->cache_list);
440 pw->cache_list = NULL;
442 filelist_free(pw->cache_todo);
443 pw->cache_todo = NULL;
449 cache_loader_free(pw->cache_cl);
453 static void pan_cache_fill(PanWindow *pw, const gchar *path)
459 list = pan_window_layout_list(path, SORT_NAME, TRUE, pw->ignore_symlinks);
460 pw->cache_todo = g_list_reverse(list);
462 pw->cache_total = g_list_length(pw->cache_todo);
465 static void pan_cache_step_done_cb(CacheLoader *cl, gint error, gpointer data)
467 PanWindow *pw = data;
472 pc = pw->cache_list->data;
481 cache_loader_free(cl);
484 pan_window_layout_update_idle(pw);
487 static gint pan_cache_step(PanWindow *pw)
491 CacheDataType load_mask;
493 if (!pw->cache_todo) return TRUE;
495 fd = pw->cache_todo->data;
496 pw->cache_todo = g_list_remove(pw->cache_todo, fd);
499 if (enable_thumb_caching)
503 found = cache_find_location(CACHE_TYPE_SIM, fd->path);
504 if (found && filetime(found) == fd->date)
506 cd = cache_sim_data_load(found);
511 if (!cd) cd = cache_sim_data_new();
515 cd->dimensions = image_load_dimensions(fd->path, &cd->width, &cd->height);
516 if (enable_thumb_caching &&
522 base = cache_get_location(CACHE_TYPE_SIM, fd->path, FALSE, &mode);
523 if (cache_ensure_dir_exists(base, mode))
526 cd->path = cache_get_location(CACHE_TYPE_SIM, fd->path, TRUE, NULL);
527 if (cache_sim_data_save(cd))
529 filetime_set(cd->path, filetime(fd->path));
538 pc = g_new0(PanCacheData, 1);
539 memcpy(pc, fd, sizeof(FileData));
544 pw->cache_list = g_list_prepend(pw->cache_list, pc);
546 cache_loader_free(pw->cache_cl);
548 load_mask = CACHE_LOADER_NONE;
549 if (pw->size > LAYOUT_SIZE_THUMB_LARGE) load_mask |= CACHE_LOADER_DIMENSIONS;
550 if (pw->exif_date_enable) load_mask |= CACHE_LOADER_DATE;
551 pw->cache_cl = cache_loader_new(((FileData *)pc)->path, load_mask,
552 pan_cache_step_done_cb, pw);
553 return (pw->cache_cl == NULL);
556 /* This sync date function is optimized for lists with a common sort */
557 static void pan_cache_sync_date(PanWindow *pw, GList *list)
562 haystack = g_list_copy(pw->cache_list);
580 path = ((FileData *)pc)->path;
581 if (path && strcmp(path, fd->path) == 0)
583 if (pc->cd && pc->cd->have_date && pc->cd->date >= 0)
585 fd->date = pc->cd->date;
588 haystack = g_list_delete_link(haystack, needle);
593 needle = needle->next;
598 g_list_free(haystack);
602 *-----------------------------------------------------------------------------
604 *-----------------------------------------------------------------------------
607 static void pan_grid_clear(PanWindow *pw)
611 work = pw->list_grid;
619 g_list_free(pg->list);
623 g_list_free(pw->list_grid);
624 pw->list_grid = NULL;
626 pw->list = g_list_concat(pw->list, pw->list_static);
627 pw->list_static = NULL;
630 static void pan_grid_build(PanWindow *pw, gint width, gint height, gint grid_size)
643 l = g_list_length(pw->list);
647 total = (gdouble)width * (gdouble)height / (gdouble)l;
650 aw = (gdouble)width / s;
651 ah = (gdouble)height / s;
653 col = (gint)(sqrt((gdouble)l / grid_size) * width / height + 0.999);
654 col = CLAMP(col, 1, l / grid_size + 1);
655 row = (gint)((gdouble)l / grid_size / col);
656 if (row < 1) row = 1;
658 /* limit minimum size of grid so that a tile will always fit regardless of position */
659 cw = MAX((gint)ceil((gdouble)width / col), PAN_TILE_SIZE * 2);
660 ch = MAX((gint)ceil((gdouble)height / row), PAN_TILE_SIZE * 2);
665 printf("intersect speedup grid is %dx%d, based on %d average per grid\n", col, row, grid_size);
667 for (j = 0; j < row; j++)
668 for (i = 0; i < col; i++)
670 if ((i + 1) * cw / 2 < width && (j + 1) * ch / 2 < height)
674 pg = g_new0(PanGrid, 1);
681 pw->list_grid = g_list_prepend(pw->list_grid, pg);
683 if (debug) printf("grid section: %d,%d (%dx%d)\n", pg->x, pg->y, pg->w, pg->h);
696 grid = pw->list_grid;
705 if (util_clip_region(pi->x, pi->y, pi->width, pi->height,
706 pg->x, pg->y, pg->w, pg->h,
709 pg->list = g_list_prepend(pg->list, pi);
714 work = pw->list_grid;
722 pg->list = g_list_reverse(pg->list);
725 pw->list_static = pw->list;
732 *-----------------------------------------------------------------------------
734 *-----------------------------------------------------------------------------
737 static void pan_item_free(PanItem *pi)
741 if (pi->pixbuf) g_object_unref(pi->pixbuf);
742 if (pi->fd) file_data_free(pi->fd);
750 static void pan_window_items_free(PanWindow *pw)
759 PanItem *pi = work->data;
765 g_list_free(pw->list);
768 g_list_free(pw->queue);
772 image_loader_free(pw->il);
775 thumb_loader_free(pw->tl);
779 pw->search_pi = NULL;
782 static PanItem *pan_item_new_thumb(PanWindow *pw, FileData *fd, gint x, gint y)
786 pi = g_new0(PanItem, 1);
787 pi->type = ITEM_THUMB;
791 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
792 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
798 pw->list = g_list_prepend(pw->list, pi);
803 static PanItem *pan_item_new_box(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
805 guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
806 guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
810 pi = g_new0(PanItem, 1);
818 pi->color_r = base_r;
819 pi->color_g = base_g;
820 pi->color_b = base_b;
821 pi->color_a = base_a;
823 pi->color2_r = bord_r;
824 pi->color2_g = bord_g;
825 pi->color2_b = bord_b;
826 pi->color2_a = bord_a;
827 pi->border = border_size;
829 pw->list = g_list_prepend(pw->list, pi);
834 static void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
838 if (!pi || pi->type != ITEM_BOX) return;
843 pi->width -= shadow[0];
844 pi->height -= shadow[0];
847 shadow = g_new0(gint, 2);
852 pi->height += offset;
858 static PanItem *pan_item_new_tri(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
859 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
860 guint8 r, guint8 g, guint8 b, guint8 a)
865 pi = g_new0(PanItem, 1);
866 pi->type = ITEM_TRIANGLE;
877 coord = g_new0(gint, 6);
887 pi->border = BORDER_NONE;
889 pw->list = g_list_prepend(pw->list, pi);
894 static void pan_item_tri_border(PanItem *pi, gint borders,
895 guint8 r, guint8 g, guint8 b, guint8 a)
897 if (!pi || pi->type != ITEM_TRIANGLE) return;
899 pi->border = borders;
907 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
911 layout = gtk_widget_create_pango_layout(widget, NULL);
913 if (pi->text_attr & TEXT_ATTR_MARKUP)
915 pango_layout_set_markup(layout, pi->text, -1);
919 if (pi->text_attr & TEXT_ATTR_BOLD ||
920 pi->text_attr & TEXT_ATTR_HEADING)
925 pal = pango_attr_list_new();
926 if (pi->text_attr & TEXT_ATTR_BOLD)
928 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
930 pa->end_index = G_MAXINT;
931 pango_attr_list_insert(pal, pa);
933 if (pi->text_attr & TEXT_ATTR_HEADING)
935 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
937 pa->end_index = G_MAXINT;
938 pango_attr_list_insert(pal, pa);
940 pango_layout_set_attributes(layout, pal);
941 pango_attr_list_unref(pal);
944 pango_layout_set_text(layout, pi->text, -1);
948 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
952 if (!pi || !pi->text || !widget) return;
954 layout = pan_item_text_layout(pi, widget);
955 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
956 g_object_unref(G_OBJECT(layout));
958 pi->width += PAN_TEXT_BORDER_SIZE * 2;
959 pi->height += PAN_TEXT_BORDER_SIZE * 2;
962 static PanItem *pan_item_new_text(PanWindow *pw, gint x, gint y, const gchar *text, TextAttrType attr,
963 guint8 r, guint8 g, guint8 b, guint8 a)
967 pi = g_new0(PanItem, 1);
968 pi->type = ITEM_TEXT;
971 pi->text = g_strdup(text);
972 pi->text_attr = attr;
979 pan_item_text_compute_size(pi, pw->imd->pr);
981 pw->list = g_list_prepend(pw->list, pi);
986 static void pan_item_set_key(PanItem *pi, const gchar *key)
993 pi->key = g_strdup(key);
997 static void pan_item_added(PanWindow *pw, PanItem *pi)
1000 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1003 static void pan_item_remove(PanWindow *pw, PanItem *pi)
1007 if (pw->click_pi == pi) pw->click_pi = NULL;
1008 if (pw->queue_pi == pi) pw->queue_pi = NULL;
1009 if (pw->search_pi == pi) pw->search_pi = NULL;
1010 pw->queue = g_list_remove(pw->queue, pi);
1012 pw->list = g_list_remove(pw->list, pi);
1013 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1017 static void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
1019 if (!pi || !child) return;
1021 if (pi->x + pi->width < child->x + child->width + border)
1022 pi->width = child->x + child->width + border - pi->x;
1024 if (pi->y + pi->height < child->y + child->height + border)
1025 pi->height = child->y + child->height + border - pi->y;
1028 static void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
1032 if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
1033 if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
1036 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
1043 if (!pi->fd) return;
1045 work = pw->cache_list;
1054 path = ((FileData *)pc)->path;
1056 if (pc->cd && pc->cd->dimensions &&
1057 path && strcmp(path, pi->fd->path) == 0)
1059 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
1060 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
1062 pw->cache_list = g_list_remove(pw->cache_list, pc);
1063 cache_sim_data_free(pc->cd);
1064 file_data_free((FileData *)pc);
1070 static PanItem *pan_item_new_image(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
1074 pi = g_new0(PanItem, 1);
1075 pi->type = ITEM_IMAGE;
1085 pi->color2_a = PAN_SHADOW_ALPHA / 2;
1087 pan_item_image_find_size(pw, pi, w, h);
1089 pw->list = g_list_prepend(pw->list, pi);
1094 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
1098 if (!key) return NULL;
1100 work = g_list_last(pw->list);
1106 if ((pi->type == type || type == ITEM_NONE) &&
1107 pi->key && strcmp(pi->key, key) == 0)
1113 work = g_list_last(pw->list_static);
1119 if ((pi->type == type || type == ITEM_NONE) &&
1120 pi->key && strcmp(pi->key, key) == 0)
1130 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1131 static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
1132 ItemType type, const gchar *path,
1133 gint ignore_case, gint partial)
1137 work = g_list_last(search_list);
1143 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1149 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
1151 else if (pi->fd->name)
1159 haystack = g_utf8_strdown(pi->fd->name, -1);
1160 match = (strstr(haystack, path) != NULL);
1165 if (strstr(pi->fd->name, path)) match = TRUE;
1168 else if (ignore_case)
1170 if (strcasecmp(path, pi->fd->name) == 0) match = TRUE;
1174 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
1178 if (match) list = g_list_prepend(list, pi);
1186 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1187 static GList *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1188 gint ignore_case, gint partial)
1192 if (!path) return NULL;
1193 if (partial && path[0] == '/') return NULL;
1195 list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
1196 list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
1198 return g_list_reverse(list);
1201 static PanItem *pan_item_find_by_coord_l(GList *list, ItemType type, gint x, gint y, const gchar *key)
1211 if ((pi->type == type || type == ITEM_NONE) &&
1212 x >= pi->x && x < pi->x + pi->width &&
1213 y >= pi->y && y < pi->y + pi->height &&
1214 (!key || (pi->key && strcmp(pi->key, key) == 0)))
1224 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y, const gchar *key)
1228 pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
1231 return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
1235 *-----------------------------------------------------------------------------
1237 *-----------------------------------------------------------------------------
1240 static gint islink_loop(const gchar *s)
1246 sl = path_from_utf8(s);
1248 if (lstat(sl, &st) == 0 && S_ISLNK(st.st_mode))
1253 buf = g_malloc(st.st_size + 1);
1254 l = readlink(sl, buf, st.st_size);
1255 if (l == st.st_size)
1259 parse_out_relatives(buf);
1262 parse_out_relatives(sl);
1266 if (strncmp(sl, buf, l) == 0 &&
1267 (sl[l] == '\0' || sl[l] == '/' || l == 1)) ret = TRUE;
1273 link_path = concat_dir_and_file(sl, buf);
1274 parse_out_relatives(link_path);
1276 if (strncmp(sl, link_path, l) == 0 &&
1277 (sl[l] == '\0' || sl[l] == '/' || l == 1)) ret = TRUE;
1291 static gint is_ignored(const gchar *s, gint ignore_symlinks)
1296 if (!lstat_utf8(s, &st)) return TRUE;
1299 /* normal filesystems have directories with some size or block allocation,
1300 * special filesystems (like linux /proc) set both to zero.
1301 * enable this check if you enable listing the root "/" folder
1303 if (st.st_size == 0 && st.st_blocks == 0) return TRUE;
1306 if (S_ISLNK(st.st_mode) && (ignore_symlinks || islink_loop(s))) return TRUE;
1308 n = filename_from_path(s);
1309 if (n && strcmp(n, GQVIEW_RC_DIR) == 0) return TRUE;
1314 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend,
1315 gint ignore_symlinks)
1317 GList *flist = NULL;
1318 GList *dlist = NULL;
1322 filelist_read(path, &flist, &dlist);
1323 if (sort != SORT_NONE)
1325 flist = filelist_sort(flist, sort, ascend);
1326 dlist = filelist_sort(dlist, sort, ascend);
1336 folders = g_list_remove(folders, fd);
1338 if (!is_ignored(fd->path, ignore_symlinks) &&
1339 filelist_read(fd->path, &flist, &dlist))
1341 if (sort != SORT_NONE)
1343 flist = filelist_sort(flist, sort, ascend);
1344 dlist = filelist_sort(dlist, sort, ascend);
1347 result = g_list_concat(result, flist);
1348 folders = g_list_concat(dlist, folders);
1357 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1365 list = pan_window_layout_list(path, SORT_NAME, TRUE, pw->ignore_symlinks);
1367 grid_size = (gint)sqrt((double)g_list_length(list));
1368 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1370 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1374 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1379 *width = PAN_FOLDER_BOX_BORDER * 2;
1380 *height = PAN_FOLDER_BOX_BORDER * 2;
1393 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1395 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1397 x += pi->width + PAN_THUMB_GAP;
1398 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1407 pi = pan_item_new_thumb(pw, fd, x, y);
1409 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1413 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1416 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1422 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1425 gint x1, y1, x2, y2;
1440 if (x1 > pi->x) x1 = pi->x;
1441 if (y1 > pi->y) y1 = pi->y;
1442 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1443 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1446 x1 -= PAN_FOLDER_BOX_BORDER;
1447 y1 -= PAN_FOLDER_BOX_BORDER;
1448 x2 += PAN_FOLDER_BOX_BORDER;
1449 y2 += PAN_FOLDER_BOX_BORDER;
1462 if (pi->type == ITEM_TRIANGLE && pi->data)
1476 if (width) *width = x2 - x1;
1477 if (height) *height = y2 - y1;
1480 typedef struct _FlowerGroup FlowerGroup;
1481 struct _FlowerGroup {
1494 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1498 work = group->items;
1516 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1517 gint *result_x, gint *result_y)
1523 radius = parent->circumference / (2*PI);
1524 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1526 a = 2*PI * group->diameter / parent->circumference;
1528 x = (gint)((double)radius * cos(parent->angle + a / 2));
1529 y = (gint)((double)radius * sin(parent->angle + a / 2));
1536 x += parent->width / 2;
1537 y += parent->height / 2;
1539 x -= group->width / 2;
1540 y -= group->height / 2;
1546 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1553 if (parent && parent->children)
1555 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1563 pan_window_layout_compute_folder_flower_move(group, x, y);
1568 gint px, py, gx, gy;
1569 gint x1, y1, x2, y2;
1571 px = parent->x + parent->width / 2;
1572 py = parent->y + parent->height / 2;
1574 gx = group->x + group->width / 2;
1575 gy = group->y + group->height / 2;
1580 x2 = MAX(px, gx + 5);
1581 y2 = MAX(py, gy + 5);
1583 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1584 px, py, gx, gy, gx + 5, gy + 5,
1586 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1590 pw->list = g_list_concat(group->items, pw->list);
1591 group->items = NULL;
1593 group->circumference = 0;
1594 work = group->children;
1602 group->circumference += child->diameter;
1605 work = g_list_last(group->children);
1613 pan_window_layout_compute_folder_flower_build(pw, child, group);
1616 g_list_free(group->children);
1620 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1633 if (!filelist_read(path, &f, &d)) return NULL;
1634 if (!f && !d) return NULL;
1636 f = filelist_sort(f, SORT_NAME, TRUE);
1637 d = filelist_sort(d, SORT_NAME, TRUE);
1639 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1640 PAN_TEXT_COLOR, 255);
1642 y += pi_box->height;
1644 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1646 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1647 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1648 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1649 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1651 x += PAN_FOLDER_BOX_BORDER;
1652 y += PAN_FOLDER_BOX_BORDER;
1654 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1668 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1670 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1671 x += pi->width + PAN_THUMB_GAP;
1672 if (pi->height > y_height) y_height = pi->height;
1676 pi = pan_item_new_thumb(pw, fd, x, y);
1677 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1678 y_height = PAN_THUMB_SIZE;
1682 if (grid_count >= grid_size)
1686 y += y_height + PAN_THUMB_GAP;
1690 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1693 group = g_new0(FlowerGroup, 1);
1694 group->items = pw->list;
1697 group->width = pi_box->width;
1698 group->height = pi_box->y + pi_box->height;
1699 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1701 group->children = NULL;
1712 if (!is_ignored(fd->path, pw->ignore_symlinks))
1714 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1715 if (child) group->children = g_list_prepend(group->children, child);
1719 if (!f && !group->children)
1721 work = group->items;
1732 g_list_free(group->items);
1743 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1744 gint *width, gint *height,
1745 gint *scroll_x, gint *scroll_y)
1750 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1751 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1753 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1755 list = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1758 PanItem *pi = list->data;
1759 *scroll_x = pi->x + pi->width / 2;
1760 *scroll_y = pi->y + pi->height / 2;
1765 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1766 gint *x, gint *y, gint *level,
1768 gint *width, gint *height)
1776 if (!filelist_read(path, &f, &d)) return;
1777 if (!f && !d) return;
1779 f = filelist_sort(f, SORT_NAME, TRUE);
1780 d = filelist_sort(d, SORT_NAME, TRUE);
1782 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1784 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1785 PAN_TEXT_COLOR, 255);
1787 *y += pi_box->height;
1789 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1791 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1792 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1793 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1794 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1796 *x += PAN_FOLDER_BOX_BORDER;
1797 *y += PAN_FOLDER_BOX_BORDER;
1808 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1810 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1811 *x += pi->width + PAN_THUMB_GAP;
1812 if (pi->height > y_height) y_height = pi->height;
1816 pi = pan_item_new_thumb(pw, fd, *x, *y);
1817 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1818 y_height = PAN_THUMB_SIZE;
1821 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1824 if (f) *y = pi_box->y + pi_box->height;
1836 if (!is_ignored(fd->path, pw->ignore_symlinks))
1838 *level = *level + 1;
1839 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1840 pi_box, width, height);
1841 *level = *level - 1;
1847 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1849 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1850 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1852 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1855 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1862 x = PAN_FOLDER_BOX_BORDER;
1863 y = PAN_FOLDER_BOX_BORDER;
1864 w = PAN_FOLDER_BOX_BORDER * 2;
1865 h = PAN_FOLDER_BOX_BORDER * 2;
1867 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1869 if (width) *width = w;
1870 if (height) *height = h;
1874 *-----------------------------------------------------------------------------
1876 *-----------------------------------------------------------------------------
1879 static void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
1885 gint x1, y1, x2, y2, x3, y3;
1890 while ((pi = pan_item_find_by_key(pw, ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
1892 if (!pi_day || pi_day->type != ITEM_BOX ||
1893 !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
1895 list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
1907 if (dot->type != ITEM_BOX || !dot->fd ||
1908 !dot->key || strcmp(dot->key, "dot") != 0)
1910 list = g_list_delete_link(list, node);
1918 grid = (gint)(sqrt(g_list_length(list)) + 0.5);
1920 x = pi_day->x + pi_day->width + 4;
1924 if (y + grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4 > pw->pr->image_height)
1926 y = pw->pr->image_height - (grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4);
1930 pbox = pan_item_new_box(pw, NULL, x, y, PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1931 PAN_CAL_POPUP_BORDER,
1932 PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA,
1933 PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
1934 pan_item_set_key(pbox, "day_bubble");
1941 buf = date_value_string(pi_day->fd->date, DATE_LENGTH_WEEK);
1942 plabel = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1943 PAN_CAL_POPUP_TEXT_COLOR, 255);
1944 pan_item_set_key(plabel, "day_bubble");
1947 pan_item_size_by_item(pbox, plabel, 0);
1949 y += plabel->height;
1956 x += PAN_FOLDER_BOX_BORDER;
1957 y += PAN_FOLDER_BOX_BORDER;
1971 pimg = pan_item_new_thumb(pw, file_data_new_simple(dot->fd->path), x, y);
1972 pan_item_set_key(pimg, "day_bubble");
1974 pan_item_size_by_item(pbox, pimg, PAN_FOLDER_BOX_BORDER);
1979 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1984 x = pbox->x + PAN_FOLDER_BOX_BORDER;
1985 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1991 x1 = pi_day->x + pi_day->width - 8;
1994 y2 = pbox->y + MIN(42, pbox->height);
1996 y3 = MAX(pbox->y, y2 - 30);
1997 util_clip_triangle(x1, y1, x2, y2, x3, y3,
2000 pi = pan_item_new_tri(pw, NULL, x, y, w, h,
2001 x1, y1, x2, y2, x3, y3,
2002 PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA);
2003 pan_item_tri_border(pi, BORDER_1 | BORDER_3, PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
2004 pan_item_set_key(pi, "day_bubble");
2005 pan_item_added(pw, pi);
2007 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
2008 pan_item_added(pw, pbox);
2010 pan_layout_resize(pw);
2013 static void pan_window_layout_compute_calendar(PanWindow *pw, const gchar *path, gint *width, gint *height)
2029 list = pan_window_layout_list(path, SORT_NONE, TRUE, pw->ignore_symlinks);
2031 if (pw->cache_list && pw->exif_date_enable)
2033 pw->cache_list = filelist_sort(pw->cache_list, SORT_NAME, TRUE);
2034 list = filelist_sort(list, SORT_NAME, TRUE);
2035 pan_cache_sync_date(pw, list);
2038 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2039 list = filelist_sort(list, SORT_TIME, TRUE);
2052 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2060 if (day_max < count) day_max = count;
2064 printf("biggest day contains %d images\n", day_max);
2066 grid = (gint)(sqrt((double)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
2067 day_width = MAX(PAN_CAL_DAY_WIDTH, grid);
2068 day_height = MAX(PAN_CAL_DAY_HEIGHT, grid);
2072 FileData *fd = list->data;
2074 year = date_value(fd->date, DATE_LENGTH_YEAR);
2075 month = date_value(fd->date, DATE_LENGTH_MONTH);
2078 work = g_list_last(list);
2081 FileData *fd = work->data;
2082 end_year = date_value(fd->date, DATE_LENGTH_YEAR);
2083 end_month = date_value(fd->date, DATE_LENGTH_MONTH);
2086 *width = PAN_FOLDER_BOX_BORDER * 2;
2087 *height = PAN_FOLDER_BOX_BORDER * 2;
2089 x = PAN_FOLDER_BOX_BORDER;
2090 y = PAN_FOLDER_BOX_BORDER;
2093 while (work && (year < end_year || (year == end_year && month <= end_month)))
2104 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
2106 days = date_value(dt, DATE_LENGTH_DAY);
2107 dt = date_to_time(year, month, 1);
2108 col = date_value(dt, DATE_LENGTH_WEEK);
2111 x = PAN_FOLDER_BOX_BORDER;
2113 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
2114 PAN_CAL_MONTH_BORDER,
2115 PAN_CAL_MONTH_COLOR, PAN_CAL_MONTH_ALPHA,
2116 PAN_CAL_MONTH_BORDER_COLOR, PAN_CAL_MONTH_ALPHA);
2117 buf = date_value_string(dt, DATE_LENGTH_MONTH);
2118 pi_text = pan_item_new_text(pw, x, y, buf,
2119 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2120 PAN_CAL_MONTH_TEXT_COLOR, 255);
2122 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
2124 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
2126 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
2127 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
2129 for (day = 1; day <= days; day++)
2136 dt = date_to_time(year, month, day);
2138 fd = g_new0(FileData, 1);
2139 /* path and name must be non NULL, so make them an invalid filename */
2140 fd->path = g_strdup("//");
2143 pi_day = pan_item_new_box(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
2145 PAN_CAL_DAY_COLOR, PAN_CAL_DAY_ALPHA,
2146 PAN_CAL_DAY_BORDER_COLOR, PAN_CAL_DAY_ALPHA);
2147 pan_item_set_key(pi_day, "day");
2149 dx = x + PAN_CAL_DOT_GAP * 2;
2150 dy = y + PAN_CAL_DOT_GAP * 2;
2152 fd = (work) ? work->data : NULL;
2153 while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
2157 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
2159 PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
2161 pan_item_set_key(pi, "dot");
2163 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2164 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
2166 dx = x + PAN_CAL_DOT_GAP * 2;
2167 dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2169 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
2171 /* must keep all dots within respective day even if it gets ugly */
2172 dy = y + PAN_CAL_DOT_GAP * 2;
2178 fd = (work) ? work->data : NULL;
2185 pi_day->color_r = MAX(pi_day->color_r - 61 - n * 3, 80);
2186 pi_day->color_g = pi_day->color_r;
2188 buf = g_strdup_printf("( %d )", n);
2189 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2190 PAN_CAL_DAY_TEXT_COLOR, 255);
2193 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
2194 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
2197 buf = g_strdup_printf("%d", day);
2198 pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2199 PAN_CAL_DAY_TEXT_COLOR, 255);
2203 pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
2210 x = PAN_FOLDER_BOX_BORDER;
2211 y += PAN_CAL_DAY_HEIGHT;
2215 x += PAN_CAL_DAY_WIDTH;
2219 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
2220 y += PAN_FOLDER_BOX_BORDER * 2;
2231 *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
2236 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
2244 PanItem *pi_month = NULL;
2245 PanItem *pi_day = NULL;
2251 list = pan_window_layout_list(path, SORT_NONE, TRUE, pw->ignore_symlinks);
2253 if (pw->cache_list && pw->exif_date_enable)
2255 pw->cache_list = filelist_sort(pw->cache_list, SORT_NAME, TRUE);
2256 list = filelist_sort(list, SORT_NAME, TRUE);
2257 pan_cache_sync_date(pw, list);
2260 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2261 list = filelist_sort(list, SORT_TIME, TRUE);
2263 *width = PAN_FOLDER_BOX_BORDER * 2;
2264 *height = PAN_FOLDER_BOX_BORDER * 2;
2269 day_start = month_start;
2284 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2289 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
2295 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
2299 x = PAN_FOLDER_BOX_BORDER;
2302 y = PAN_FOLDER_BOX_BORDER;
2304 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
2305 pi = pan_item_new_text(pw, x, y, buf,
2306 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2307 PAN_TEXT_COLOR, 255);
2311 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
2313 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2314 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2315 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2317 x += PAN_FOLDER_BOX_BORDER;
2318 y += PAN_FOLDER_BOX_BORDER;
2322 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
2334 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
2336 needle = needle->next;
2345 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
2346 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2347 PAN_TEXT_COLOR, 255);
2352 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
2353 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2354 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2355 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2357 x += PAN_FOLDER_BOX_BORDER;
2358 y += PAN_FOLDER_BOX_BORDER;
2362 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2364 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
2365 if (pi->width > x_width) x_width = pi->width;
2366 y_height = pi->height;
2370 pi = pan_item_new_thumb(pw, fd, x, y);
2371 x_width = PAN_THUMB_SIZE;
2372 y_height = PAN_THUMB_SIZE;
2375 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
2376 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
2381 if (total > 0 && count < PAN_GROUP_MAX)
2383 y += y_height + PAN_THUMB_GAP;
2387 x += x_width + PAN_THUMB_GAP;
2397 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
2403 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
2404 gint *width, gint *height,
2405 gint *scroll_x, gint *scroll_y)
2407 pan_window_items_free(pw);
2411 case LAYOUT_SIZE_THUMB_DOTS:
2412 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2413 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2415 case LAYOUT_SIZE_THUMB_NONE:
2416 pw->thumb_size = PAN_THUMB_SIZE_NONE;
2417 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2419 case LAYOUT_SIZE_THUMB_SMALL:
2420 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2421 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2423 case LAYOUT_SIZE_THUMB_NORMAL:
2425 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2426 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2428 case LAYOUT_SIZE_THUMB_LARGE:
2429 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2430 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2432 case LAYOUT_SIZE_10:
2433 pw->image_size = 10;
2434 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2436 case LAYOUT_SIZE_25:
2437 pw->image_size = 25;
2438 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2440 case LAYOUT_SIZE_33:
2441 pw->image_size = 33;
2442 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2444 case LAYOUT_SIZE_50:
2445 pw->image_size = 50;
2446 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2448 case LAYOUT_SIZE_100:
2449 pw->image_size = 100;
2450 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2463 pan_window_layout_compute_grid(pw, path, width, height);
2465 case LAYOUT_FOLDERS_LINEAR:
2466 pan_window_layout_compute_folders_linear(pw, path, width, height);
2468 case LAYOUT_FOLDERS_FLOWER:
2469 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2471 case LAYOUT_CALENDAR:
2472 pan_window_layout_compute_calendar(pw, path, width, height);
2474 case LAYOUT_TIMELINE:
2475 pan_window_layout_compute_timeline(pw, path, width, height);
2481 printf("computed %d objects\n", g_list_length(pw->list));
2484 static GList *pan_layout_intersect_l(GList *list, GList *item_list,
2485 gint x, gint y, gint width, gint height)
2493 gint rx, ry, rw, rh;
2498 if (util_clip_region(x, y, width, height,
2499 pi->x, pi->y, pi->width, pi->height,
2500 &rx, &ry, &rw, &rh))
2502 list = g_list_prepend(list, pi);
2509 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2515 grid = pw->list_grid;
2521 if (x < pg->x || x + width > pg->x + pg->w ||
2522 y < pg->y || y + height > pg->y + pg->h)
2528 list = pan_layout_intersect_l(list, pw->list, x, y, width, height);
2532 list = pan_layout_intersect_l(list, pg->list, x, y, width, height);
2536 list = pan_layout_intersect_l(list, pw->list_static, x, y, width, height);
2542 static void pan_layout_resize(PanWindow *pw)
2557 if (width < pi->x + pi->width) width = pi->x + pi->width;
2558 if (height < pi->y + pi->height) height = pi->y + pi->height;
2560 work = pw->list_static;
2568 if (width < pi->x + pi->width) width = pi->x + pi->width;
2569 if (height < pi->y + pi->height) height = pi->y + pi->height;
2572 width += PAN_FOLDER_BOX_BORDER * 2;
2573 height += PAN_FOLDER_BOX_BORDER * 2;
2575 pr = PIXBUF_RENDERER(pw->imd->pr);
2576 if (width < pr->window_width) width = pr->window_width;
2577 if (height < pr->window_width) height = pr->window_height;
2579 pixbuf_renderer_set_tiles_size(PIXBUF_RENDERER(pw->imd->pr), width, height);
2583 *-----------------------------------------------------------------------------
2585 *-----------------------------------------------------------------------------
2588 static gint pan_layout_queue_step(PanWindow *pw);
2591 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2593 PanWindow *pw = data;
2601 pw->queue_pi = NULL;
2605 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2606 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2609 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2613 thumb_loader_free(pw->tl);
2616 while (pan_layout_queue_step(pw));
2619 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2621 PanWindow *pw = data;
2629 pw->queue_pi = NULL;
2633 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2634 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2635 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2637 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2638 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2639 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2644 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2645 (GdkInterpType)zoom_quality);
2646 g_object_unref(tmp);
2650 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2654 image_loader_free(pw->il);
2657 while (pan_layout_queue_step(pw));
2661 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2662 guint width, guint height, gpointer data)
2664 PanWindow *pw = data;
2675 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2676 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2680 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2686 static gint pan_layout_queue_step(PanWindow *pw)
2690 if (!pw->queue) return FALSE;
2692 pi = pw->queue->data;
2693 pw->queue = g_list_remove(pw->queue, pi);
2696 if (!pw->queue_pi->fd)
2698 pw->queue_pi->queued = FALSE;
2699 pw->queue_pi = NULL;
2703 image_loader_free(pw->il);
2705 thumb_loader_free(pw->tl);
2708 if (pi->type == ITEM_IMAGE)
2710 pw->il = image_loader_new(pi->fd->path);
2712 if (pw->size != LAYOUT_SIZE_100)
2714 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2718 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2720 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2722 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2724 image_loader_free(pw->il);
2727 else if (pi->type == ITEM_THUMB)
2729 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2731 if (!pw->tl->standard_loader)
2733 /* The classic loader will recreate a thumbnail any time we
2734 * request a different size than what exists. This view will
2735 * almost never use the user configured sizes so disable cache.
2737 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2740 thumb_loader_set_callbacks(pw->tl,
2741 pan_layout_queue_thumb_done_cb,
2742 pan_layout_queue_thumb_done_cb,
2745 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2747 thumb_loader_free(pw->tl);
2751 pw->queue_pi->queued = FALSE;
2752 pw->queue_pi = NULL;
2756 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2758 if (!pi || pi->queued || pi->pixbuf) return;
2759 if (pw->size <= LAYOUT_SIZE_THUMB_NONE &&
2760 (!pi->key || strcmp(pi->key, "info") != 0) )
2766 pw->queue = g_list_prepend(pw->queue, pi);
2768 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2771 static gint pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
2772 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2774 PanWindow *pw = data;
2779 pixbuf_set_rect_fill(pixbuf,
2780 0, 0, width, height,
2781 PAN_BACKGROUND_COLOR, 255);
2783 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2785 gint rx, ry, rw, rh;
2787 if (util_clip_region(x, y, width, height,
2789 &rx, &ry, &rw, &rh))
2791 pixbuf_draw_rect_fill(pixbuf,
2792 rx - x, ry - y, rw, rh,
2793 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2796 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2798 gint rx, ry, rw, rh;
2800 if (util_clip_region(x, y, width, height,
2802 &rx, &ry, &rw, &rh))
2804 pixbuf_draw_rect_fill(pixbuf,
2805 rx - x, ry - y, rw, rh,
2806 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2810 list = pan_layout_intersect(pw, x, y, width, height);
2815 gint tx, ty, tw, th;
2816 gint rx, ry, rw, rh;
2823 if (pi->type == ITEM_THUMB && pi->pixbuf)
2825 tw = gdk_pixbuf_get_width(pi->pixbuf);
2826 th = gdk_pixbuf_get_height(pi->pixbuf);
2828 tx = pi->x + (pi->width - tw) / 2;
2829 ty = pi->y + (pi->height - th) / 2;
2831 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2833 if (util_clip_region(x, y, width, height,
2834 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2835 &rx, &ry, &rw, &rh))
2837 pixbuf_draw_shadow(pixbuf,
2838 rx - x, ry - y, rw, rh,
2839 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2841 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2846 if (util_clip_region(x, y, width, height,
2847 tx + tw, ty + PAN_SHADOW_OFFSET,
2848 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2849 &rx, &ry, &rw, &rh))
2851 pixbuf_draw_shadow(pixbuf,
2852 rx - x, ry - y, rw, rh,
2853 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2855 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2857 if (util_clip_region(x, y, width, height,
2858 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2859 &rx, &ry, &rw, &rh))
2861 pixbuf_draw_shadow(pixbuf,
2862 rx - x, ry - y, rw, rh,
2863 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2865 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2869 if (util_clip_region(x, y, width, height,
2871 &rx, &ry, &rw, &rh))
2873 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2876 1.0, 1.0, GDK_INTERP_NEAREST,
2880 if (util_clip_region(x, y, width, height,
2881 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2882 &rx, &ry, &rw, &rh))
2884 pixbuf_draw_rect_fill(pixbuf,
2885 rx - x, ry - y, rw, rh,
2886 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2888 if (util_clip_region(x, y, width, height,
2889 tx, ty, PAN_OUTLINE_THICKNESS, th,
2890 &rx, &ry, &rw, &rh))
2892 pixbuf_draw_rect_fill(pixbuf,
2893 rx - x, ry - y, rw, rh,
2894 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2896 if (util_clip_region(x, y, width, height,
2897 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2898 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2899 &rx, &ry, &rw, &rh))
2901 pixbuf_draw_rect_fill(pixbuf,
2902 rx - x, ry - y, rw, rh,
2903 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2905 if (util_clip_region(x, y, width, height,
2906 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2907 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2908 &rx, &ry, &rw, &rh))
2910 pixbuf_draw_rect_fill(pixbuf,
2911 rx - x, ry - y, rw, rh,
2912 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2915 else if (pi->type == ITEM_THUMB)
2917 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2918 th = pi->height - PAN_SHADOW_OFFSET * 2;
2919 tx = pi->x + PAN_SHADOW_OFFSET;
2920 ty = pi->y + PAN_SHADOW_OFFSET;
2922 if (util_clip_region(x, y, width, height,
2924 &rx, &ry, &rw, &rh))
2928 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2929 pixbuf_draw_rect_fill(pixbuf,
2930 rx - x, ry - y, rw, rh,
2932 PAN_SHADOW_ALPHA / d);
2935 pan_layout_queue(pw, pi);
2937 else if (pi->type == ITEM_IMAGE)
2939 if (util_clip_region(x, y, width, height,
2940 pi->x, pi->y, pi->width, pi->height,
2941 &rx, &ry, &rw, &rh))
2945 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2948 1.0, 1.0, GDK_INTERP_NEAREST,
2953 pixbuf_draw_rect_fill(pixbuf,
2954 rx - x, ry - y, rw, rh,
2955 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2956 pan_layout_queue(pw, pi);
2960 else if (pi->type == ITEM_BOX)
2974 if (pi->color_a > 254)
2976 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2977 shadow[0], bh - shadow[0],
2978 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2980 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2981 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2983 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2985 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2990 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2991 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2993 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2995 PAN_SHADOW_COLOR, a);
2999 if (util_clip_region(x, y, width, height,
3000 pi->x, pi->y, bw, bh,
3001 &rx, &ry, &rw, &rh))
3003 pixbuf_draw_rect_fill(pixbuf,
3004 rx - x, ry - y, rw, rh,
3005 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3007 if (util_clip_region(x, y, width, height,
3008 pi->x, pi->y, bw, pi->border,
3009 &rx, &ry, &rw, &rh))
3011 pixbuf_draw_rect_fill(pixbuf,
3012 rx - x, ry - y, rw, rh,
3013 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3015 if (util_clip_region(x, y, width, height,
3016 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
3017 &rx, &ry, &rw, &rh))
3019 pixbuf_draw_rect_fill(pixbuf,
3020 rx - x, ry - y, rw, rh,
3021 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3023 if (util_clip_region(x, y, width, height,
3024 pi->x + bw - pi->border, pi->y + pi->border,
3025 pi->border, bh - pi->border * 2,
3026 &rx, &ry, &rw, &rh))
3028 pixbuf_draw_rect_fill(pixbuf,
3029 rx - x, ry - y, rw, rh,
3030 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3032 if (util_clip_region(x, y, width, height,
3033 pi->x, pi->y + bh - pi->border,
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);
3042 else if (pi->type == ITEM_TRIANGLE)
3044 if (util_clip_region(x, y, width, height,
3045 pi->x, pi->y, pi->width, pi->height,
3046 &rx, &ry, &rw, &rh) && pi->data)
3048 gint *coord = pi->data;
3049 pixbuf_draw_triangle(pixbuf,
3050 rx - x, ry - y, rw, rh,
3051 coord[0] - x, coord[1] - y,
3052 coord[2] - x, coord[3] - y,
3053 coord[4] - x, coord[5] - y,
3054 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3056 if (pi->border & BORDER_1)
3058 pixbuf_draw_line(pixbuf,
3059 rx - x, ry - y, rw, rh,
3060 coord[0] - x, coord[1] - y,
3061 coord[2] - x, coord[3] - y,
3062 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3064 if (pi->border & BORDER_2)
3066 pixbuf_draw_line(pixbuf,
3067 rx - x, ry - y, rw, rh,
3068 coord[2] - x, coord[3] - y,
3069 coord[4] - x, coord[5] - y,
3070 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3072 if (pi->border & BORDER_3)
3074 pixbuf_draw_line(pixbuf,
3075 rx - x, ry - y, rw, rh,
3076 coord[4] - x, coord[5] - y,
3077 coord[0] - x, coord[1] - y,
3078 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3082 else if (pi->type == ITEM_TEXT && pi->text)
3084 PangoLayout *layout;
3086 layout = pan_item_text_layout(pi, (GtkWidget *)pr);
3087 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
3088 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
3089 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3090 g_object_unref(G_OBJECT(layout));
3096 if (x%512 == 0 && y%512 == 0)
3098 PangoLayout *layout;
3101 layout = gtk_widget_create_pango_layout((GtkWidget *)pr, NULL);
3103 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
3104 (x / pr->source_tile_width) +
3105 (y / pr->source_tile_height * (pr->image_width/pr->source_tile_width + 1)));
3106 pango_layout_set_text(layout, buf, -1);
3109 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr, 0, 0, 0, 0, 0, 255);
3111 g_object_unref(G_OBJECT(layout));
3118 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
3119 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
3121 PanWindow *pw = data;
3125 list = pan_layout_intersect(pw, x, y, width, height);
3134 if (pi->refcount > 0)
3138 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
3143 pw->queue = g_list_remove(pw->queue, pi);
3146 if (pw->queue_pi == pi) pw->queue_pi = NULL;
3149 g_object_unref(pi->pixbuf);
3161 *-----------------------------------------------------------------------------
3163 *-----------------------------------------------------------------------------
3166 static void pan_window_message(PanWindow *pw, const gchar *text)
3176 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
3180 work = pw->list_static;
3181 if (pw->layout == LAYOUT_CALENDAR)
3191 pi->type == ITEM_BOX &&
3192 pi->key && strcmp(pi->key, "dot") == 0)
3194 size += pi->fd->size;
3209 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
3211 size += pi->fd->size;
3217 ss = text_from_size_abrev(size);
3218 buf = g_strdup_printf(_("%d images, %s"), count, ss);
3220 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
3224 static void pan_warning_folder(const gchar *path, GtkWidget *parent)
3228 message = g_strdup_printf(_("The pan view does not support the folder \"%s\"."), path);
3229 warning_dialog(_("Folder not supported"), message,
3230 GTK_STOCK_DIALOG_INFO, parent);
3234 static void pan_window_zoom_limit(PanWindow *pw)
3240 case LAYOUT_SIZE_THUMB_DOTS:
3241 case LAYOUT_SIZE_THUMB_NONE:
3242 case LAYOUT_SIZE_THUMB_SMALL:
3243 case LAYOUT_SIZE_THUMB_NORMAL:
3245 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
3249 case LAYOUT_SIZE_THUMB_LARGE:
3252 case LAYOUT_SIZE_10:
3253 case LAYOUT_SIZE_25:
3256 case LAYOUT_SIZE_33:
3257 case LAYOUT_SIZE_50:
3258 case LAYOUT_SIZE_100:
3264 image_zoom_set_limits(pw->imd, min, 32.0);
3267 static gint pan_window_layout_update_idle_cb(gpointer data)
3269 PanWindow *pw = data;
3275 if (pw->size > LAYOUT_SIZE_THUMB_LARGE ||
3276 (pw->exif_date_enable && (pw->layout == LAYOUT_TIMELINE || pw->layout == LAYOUT_CALENDAR)))
3278 if (!pw->cache_list && !pw->cache_todo)
3280 pan_cache_fill(pw, pw->path);
3283 pan_window_message(pw, _("Reading image data..."));
3291 if (pw->cache_count == pw->cache_total)
3293 pan_window_message(pw, _("Sorting..."));
3295 else if (pw->cache_tick > 9)
3299 buf = g_strdup_printf("%s %d", _("Reading image data..."),
3300 pw->cache_total - pw->cache_count);
3301 pan_window_message(pw, buf);
3307 if (pan_cache_step(pw)) return TRUE;
3314 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3316 pan_window_zoom_limit(pw);
3318 if (width > 0 && height > 0)
3322 printf("Canvas size is %d x %d\n", width, height);
3324 pan_grid_build(pw, width, height, 1000);
3326 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
3327 PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
3328 pan_window_request_tile_cb,
3329 pan_window_dispose_tile_cb, pw, 1.0);
3331 if (scroll_x == 0 && scroll_y == 0)
3339 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
3342 pan_window_message(pw, NULL);
3348 static void pan_window_layout_update_idle(PanWindow *pw)
3350 if (pw->idle_id == -1)
3352 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3356 static void pan_window_layout_update(PanWindow *pw)
3358 pan_window_message(pw, _("Sorting images..."));
3359 pan_window_layout_update_idle(pw);
3362 static void pan_window_layout_set_path(PanWindow *pw, const gchar *path)
3366 if (strcmp(path, "/") == 0)
3368 pan_warning_folder(path, pw->window);
3373 pw->path = g_strdup(path);
3375 pan_window_layout_update(pw);
3379 *-----------------------------------------------------------------------------
3380 * pan window keyboard
3381 *-----------------------------------------------------------------------------
3384 static const gchar *pan_menu_click_path(PanWindow *pw)
3386 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3390 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3392 PanWindow *pw = data;
3394 gdk_window_get_origin(pw->imd->pr->window, x, y);
3395 popup_menu_position_clamp(menu, x, y, 0);
3398 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3400 PanWindow *pw = data;
3403 gint stop_signal = FALSE;
3409 pr = PIXBUF_RENDERER(pw->imd->pr);
3410 path = pan_menu_click_path(pw);
3412 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pw->imd->widget)));
3417 switch (event->keyval)
3419 case GDK_Left: case GDK_KP_Left:
3422 case GDK_Right: case GDK_KP_Right:
3425 case GDK_Up: case GDK_KP_Up:
3428 case GDK_Down: case GDK_KP_Down:
3431 case GDK_Page_Up: case GDK_KP_Page_Up:
3432 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
3434 case GDK_Page_Down: case GDK_KP_Page_Down:
3435 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
3437 case GDK_Home: case GDK_KP_Home:
3438 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
3440 case GDK_End: case GDK_KP_End:
3441 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
3444 stop_signal = FALSE;
3448 if (x != 0 || y!= 0)
3450 if (event->state & GDK_SHIFT_MASK)
3455 keyboard_scroll_calc(&x, &y, event);
3456 pixbuf_renderer_scroll(pr, x, y);
3460 if (stop_signal) return stop_signal;
3462 if (event->state & GDK_CONTROL_MASK)
3467 switch (event->keyval)
3500 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3503 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3506 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3509 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3512 if (path) info_window_new(path, NULL);
3515 pan_window_close(pw);
3518 stop_signal = FALSE;
3522 if (n != -1 && path)
3524 if (!editor_window_flag_set(n))
3526 pan_fullscreen_toggle(pw, TRUE);
3528 start_editor_from_file(n, path);
3536 switch (event->keyval)
3538 case '+': case '=': case GDK_KP_Add:
3539 pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
3541 case '-': case GDK_KP_Subtract:
3542 pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
3544 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3545 pixbuf_renderer_zoom_set(pr, 1.0);
3548 pixbuf_renderer_zoom_set(pr, 2.0);
3551 pixbuf_renderer_zoom_set(pr, 3.0);
3554 pixbuf_renderer_zoom_set(pr, 4.0);
3557 pixbuf_renderer_zoom_set(pr, -4.0);
3560 pixbuf_renderer_zoom_set(pr, -3.0);
3563 pixbuf_renderer_zoom_set(pr, -2.0);
3567 pan_fullscreen_toggle(pw, FALSE);
3571 pan_overlay_toggle(pw);
3574 case GDK_Delete: case GDK_KP_Delete:
3579 if (GTK_WIDGET_VISIBLE(pw->search_box))
3581 gtk_widget_grab_focus(pw->search_entry);
3585 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3590 stop_signal = FALSE;
3596 pan_fullscreen_toggle(pw, TRUE);
3598 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3600 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3604 stop_signal = FALSE;
3609 menu = pan_popup_menu(pw);
3610 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3613 stop_signal = FALSE;
3620 switch (event->keyval)
3625 pan_fullscreen_toggle(pw, TRUE);
3627 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3629 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
3630 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3634 stop_signal = FALSE;
3638 stop_signal = FALSE;
3648 *-----------------------------------------------------------------------------
3650 *-----------------------------------------------------------------------------
3653 static void pan_info_update(PanWindow *pw, PanItem *pi)
3659 gint x1, y1, x2, y2, x3, y3;
3662 if (pw->click_pi == pi) return;
3663 if (pi && !pi->fd) pi = NULL;
3665 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3670 if (debug) printf("info set to %s\n", pi->fd->path);
3672 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3674 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3675 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3676 pan_item_set_key(pbox, "info");
3678 if (pi->type == ITEM_THUMB && pi->pixbuf)
3680 w = gdk_pixbuf_get_width(pi->pixbuf);
3681 h = gdk_pixbuf_get_height(pi->pixbuf);
3683 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3684 y1 = pi->y + (pi->height - h) / 2 + 8;
3688 x1 = pi->x + pi->width - 8;
3696 util_clip_triangle(x1, y1, x2, y2, x3, y3,
3699 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3700 x1, y1, x2, y2, x3, y3,
3701 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3702 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3703 pan_item_set_key(p, "info");
3704 pan_item_added(pw, p);
3706 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3707 _("Filename:"), TEXT_ATTR_BOLD,
3708 PAN_POPUP_TEXT_COLOR, 255);
3709 pan_item_set_key(plabel, "info");
3710 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3711 pi->fd->name, TEXT_ATTR_NONE,
3712 PAN_POPUP_TEXT_COLOR, 255);
3713 pan_item_set_key(p, "info");
3714 pan_item_size_by_item(pbox, p, 0);
3716 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3717 _("Date:"), TEXT_ATTR_BOLD,
3718 PAN_POPUP_TEXT_COLOR, 255);
3719 pan_item_set_key(plabel, "info");
3720 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3721 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3722 PAN_POPUP_TEXT_COLOR, 255);
3723 pan_item_set_key(p, "info");
3724 pan_item_size_by_item(pbox, p, 0);
3726 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3727 _("Size:"), TEXT_ATTR_BOLD,
3728 PAN_POPUP_TEXT_COLOR, 255);
3729 pan_item_set_key(plabel, "info");
3730 buf = text_from_size(pi->fd->size);
3731 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3732 buf, TEXT_ATTR_NONE,
3733 PAN_POPUP_TEXT_COLOR, 255);
3735 pan_item_set_key(p, "info");
3736 pan_item_size_by_item(pbox, p, 0);
3738 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3739 pan_item_added(pw, pbox);
3744 if (image_load_dimensions(pi->fd->path, &iw, &ih))
3746 pbox = pan_item_new_box(pw, NULL, pbox->x, pbox->y + pbox->height + 8, 10, 10,
3748 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3749 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3750 pan_item_set_key(pbox, "info");
3752 p = pan_item_new_image(pw, file_data_new_simple(pi->fd->path),
3753 pbox->x + 8, pbox->y + 8, iw, ih);
3754 pan_item_set_key(p, "info");
3755 pan_item_size_by_item(pbox, p, 8);
3757 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3758 pan_item_added(pw, pbox);
3760 pan_layout_resize(pw);
3767 *-----------------------------------------------------------------------------
3769 *-----------------------------------------------------------------------------
3772 static void pan_search_status(PanWindow *pw, const gchar *text)
3774 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3777 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3785 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3787 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3788 if (!list) return FALSE;
3790 found = g_list_find(list, pw->click_pi);
3791 if (found && found->next)
3793 found = found->next;
3801 pan_info_update(pw, pi);
3802 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3804 buf = g_strdup_printf("%s ( %d / %d )",
3805 (path[0] == '/') ? _("path found") : _("filename found"),
3806 g_list_index(list, pi) + 1,
3807 g_list_length(list));
3808 pan_search_status(pw, buf);
3816 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3824 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3826 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3827 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3832 needle = g_utf8_strdown(text, -1);
3833 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3836 if (!list) return FALSE;
3838 found = g_list_find(list, pw->click_pi);
3839 if (found && found->next)
3841 found = found->next;
3849 pan_info_update(pw, pi);
3850 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3852 buf = g_strdup_printf("%s ( %d / %d )",
3854 g_list_index(list, pi) + 1,
3855 g_list_length(list));
3856 pan_search_status(pw, buf);
3864 static gint valid_date_separator(gchar c)
3866 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3869 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
3870 gint year, gint month, gint day,
3876 work = g_list_last(pw->list_static);
3884 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
3885 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
3889 tl = localtime(&pi->fd->date);
3894 match = (tl->tm_year == year - 1900);
3895 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3896 if (match && day > 0) match = (tl->tm_mday == day);
3898 if (match) list = g_list_prepend(list, pi);
3903 return g_list_reverse(list);
3906 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3922 if (!text) return FALSE;
3924 ptr = (gchar *)text;
3925 while (*ptr != '\0')
3927 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3932 if (t == -1) return FALSE;
3934 if (!lt) return FALSE;
3936 if (valid_date_separator(*text))
3939 mptr = (gchar *)text;
3943 year = (gint)strtol(text, &mptr, 10);
3944 if (mptr == text) return FALSE;
3947 if (*mptr != '\0' && valid_date_separator(*mptr))
3952 month = strtol(mptr, &dptr, 10);
3955 if (valid_date_separator(*dptr))
3957 month = lt->tm_mon + 1;
3965 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3969 day = strtol(dptr, &eptr, 10);
3979 year = lt->tm_year + 1900;
3981 else if (year < 100)
3990 month < -1 || month == 0 || month > 12 ||
3991 day < -1 || day == 0 || day > 31) return FALSE;
3993 t = date_to_time(year, month, day);
3994 if (t < 0) return FALSE;
3996 if (pw->layout == LAYOUT_CALENDAR)
3998 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
4004 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
4005 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
4010 found = g_list_find(list, pw->search_pi);
4011 if (found && found->next)
4013 found = found->next;
4024 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
4026 pan_info_update(pw, NULL);
4027 pan_calendar_update(pw, pi);
4028 image_scroll_to_point(pw->imd,
4029 pi->x + pi->width / 2,
4030 pi->y + pi->height / 2, 0.5, 0.5);
4034 pan_info_update(pw, pi);
4035 image_scroll_to_point(pw->imd,
4036 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
4042 buf = date_value_string(t, DATE_LENGTH_MONTH);
4047 buf = g_strdup_printf("%d %s", day, tmp);
4053 buf = date_value_string(t, DATE_LENGTH_YEAR);
4058 buf_count = g_strdup_printf("( %d / %d )",
4059 g_list_index(list, pi) + 1,
4060 g_list_length(list));
4064 buf_count = g_strdup_printf("(%s)", _("no match"));
4067 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
4070 pan_search_status(pw, message);
4078 static void pan_search_activate_cb(const gchar *text, gpointer data)
4080 PanWindow *pw = data;
4084 tab_completion_append_to_history(pw->search_entry, text);
4086 if (pan_search_by_path(pw, text)) return;
4088 if ((pw->layout == LAYOUT_TIMELINE ||
4089 pw->layout == LAYOUT_CALENDAR) &&
4090 pan_search_by_date(pw, text))
4095 if (pan_search_by_partial(pw, text)) return;
4097 pan_search_status(pw, _("no match"));
4100 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
4102 PanWindow *pw = data;
4105 visible = GTK_WIDGET_VISIBLE(pw->search_box);
4106 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
4110 gtk_widget_hide(pw->search_box);
4111 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
4115 gtk_widget_show(pw->search_box);
4116 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
4117 gtk_widget_grab_focus(pw->search_entry);
4123 *-----------------------------------------------------------------------------
4124 * view window main routines
4125 *-----------------------------------------------------------------------------
4128 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
4130 PanWindow *pw = data;
4138 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
4139 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
4142 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "info");
4143 if (pi && event->button == 1)
4145 pan_info_update(pw, NULL);
4149 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
4152 switch (event->button)
4155 pan_info_update(pw, pi);
4157 if (!pi && pw->layout == LAYOUT_CALENDAR)
4159 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
4160 pan_calendar_update(pw, pi);
4166 pan_info_update(pw, pi);
4167 menu = pan_popup_menu(pw);
4168 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
4175 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
4178 PanWindow *pw = data;
4185 if (!(event->state & GDK_SHIFT_MASK))
4191 if (event->state & GDK_CONTROL_MASK)
4193 switch (event->direction)
4196 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
4197 (gint)event->x, (gint)event->y);
4199 case GDK_SCROLL_DOWN:
4200 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
4201 (gint)event->x, (gint)event->y);
4209 switch (event->direction)
4212 pixbuf_renderer_scroll(pr, 0, -h);
4214 case GDK_SCROLL_DOWN:
4215 pixbuf_renderer_scroll(pr, 0, h);
4217 case GDK_SCROLL_LEFT:
4218 pixbuf_renderer_scroll(pr, -w, 0);
4220 case GDK_SCROLL_RIGHT:
4221 pixbuf_renderer_scroll(pr, w, 0);
4229 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
4231 g_signal_connect(G_OBJECT(imd->pr), "clicked",
4232 G_CALLBACK(button_cb), pw);
4233 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
4234 G_CALLBACK(scroll_cb), pw);
4237 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
4239 PanWindow *pw = data;
4242 pw->imd = pw->imd_normal;
4245 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
4247 if (force_off && !pw->fs) return;
4251 fullscreen_stop(pw->fs);
4255 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
4256 pan_image_set_buttons(pw, pw->fs->imd);
4257 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
4258 G_CALLBACK(pan_window_key_press_cb), pw);
4260 pw->imd = pw->fs->imd;
4264 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
4266 PanWindow *pw = data;
4269 text = image_zoom_get_as_text(pw->imd);
4270 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4274 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
4276 PanWindow *pw = data;
4281 if (pr->scale == 0.0) return;
4283 pixbuf_renderer_get_visible_rect(pr, &rect);
4284 pixbuf_renderer_get_image_size(pr, &width, &height);
4286 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4287 adj->page_size = (gdouble)rect.width;
4288 adj->page_increment = adj->page_size / 2.0;
4289 adj->step_increment = 48.0 / pr->scale;
4291 adj->upper = MAX((gdouble)width, 1.0);
4292 adj->value = (gdouble)rect.x;
4294 pref_signal_block_data(pw->scrollbar_h, pw);
4295 gtk_adjustment_changed(adj);
4296 gtk_adjustment_value_changed(adj);
4297 pref_signal_unblock_data(pw->scrollbar_h, pw);
4299 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4300 adj->page_size = (gdouble)rect.height;
4301 adj->page_increment = adj->page_size / 2.0;
4302 adj->step_increment = 48.0 / pr->scale;
4304 adj->upper = MAX((gdouble)height, 1.0);
4305 adj->value = (gdouble)rect.y;
4307 pref_signal_block_data(pw->scrollbar_v, pw);
4308 gtk_adjustment_changed(adj);
4309 gtk_adjustment_value_changed(adj);
4310 pref_signal_unblock_data(pw->scrollbar_v, pw);
4313 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4315 PanWindow *pw = data;
4319 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4321 if (!pr->scale) return;
4323 x = (gint)gtk_range_get_value(range);
4325 pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
4328 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4330 PanWindow *pw = data;
4334 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4336 if (!pr->scale) return;
4338 y = (gint)gtk_range_get_value(range);
4340 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
4343 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4345 PanWindow *pw = data;
4347 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4348 pan_window_layout_update(pw);
4351 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4353 PanWindow *pw = data;
4355 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4356 pan_window_layout_update(pw);
4360 static void pan_window_date_toggle_cb(GtkWidget *button, gpointer data)
4362 PanWindow *pw = data;
4364 pw->exif_date_enable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4365 pan_window_layout_update(pw);
4369 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4371 PanWindow *pw = data;
4374 path = remove_trailing_slash(new_text);
4375 parse_out_relatives(path);
4379 warning_dialog(_("Folder not found"),
4380 _("The entered path is not a folder"),
4381 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4385 tab_completion_append_to_history(pw->path_entry, path);
4387 pan_window_layout_set_path(pw, path);
4393 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4395 PanWindow *pw = data;
4398 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4400 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4401 pan_window_entry_activate_cb(text, pw);
4405 static void pan_window_close(PanWindow *pw)
4407 pan_window_list = g_list_remove(pan_window_list, pw);
4409 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_EXIF_DATE, pw->exif_date_enable);
4411 if (pw->idle_id != -1)
4413 g_source_remove(pw->idle_id);
4416 pan_fullscreen_toggle(pw, TRUE);
4417 gtk_widget_destroy(pw->window);
4419 pan_window_items_free(pw);
4427 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4429 PanWindow *pw = data;
4431 pan_window_close(pw);
4435 static void pan_window_new_real(const gchar *path)
4444 GdkGeometry geometry;
4446 pw = g_new0(PanWindow, 1);
4448 pw->path = g_strdup(path);
4449 pw->layout = LAYOUT_TIMELINE;
4450 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4451 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4452 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4454 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_EXIF_DATE, &pw->exif_date_enable))
4456 pw->exif_date_enable = FALSE;
4459 pw->ignore_symlinks = TRUE;
4462 pw->list_static = NULL;
4463 pw->list_grid = NULL;
4466 pw->overlay_id = -1;
4469 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4471 geometry.min_width = 8;
4472 geometry.min_height = 8;
4473 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4475 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4476 gtk_window_set_title (GTK_WINDOW(pw->window), _("Pan View - GQview"));
4477 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4478 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4480 window_set_icon(pw->window, NULL, NULL);
4482 vbox = gtk_vbox_new(FALSE, 0);
4483 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4484 gtk_widget_show(vbox);
4486 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4488 pref_spacer(box, 0);
4489 pref_label_new(box, _("Location:"));
4490 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view_path", -1,
4491 pan_window_entry_activate_cb, pw);
4492 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4493 G_CALLBACK(pan_window_entry_change_cb), pw);
4494 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4495 gtk_widget_show(combo);
4497 combo = gtk_combo_box_new_text();
4498 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4499 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4500 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4501 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4502 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4504 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4505 g_signal_connect(G_OBJECT(combo), "changed",
4506 G_CALLBACK(pan_window_layout_change_cb), pw);
4507 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4508 gtk_widget_show(combo);
4510 combo = gtk_combo_box_new_text();
4511 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4512 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4513 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4514 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4515 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4516 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4517 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4518 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4519 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4520 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4522 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4523 g_signal_connect(G_OBJECT(combo), "changed",
4524 G_CALLBACK(pan_window_layout_size_cb), pw);
4525 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4526 gtk_widget_show(combo);
4528 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4529 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4530 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4532 pw->imd = image_new(TRUE);
4533 pw->imd_normal = pw->imd;
4535 g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
4536 G_CALLBACK(pan_window_image_zoom_cb), pw);
4537 g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
4538 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4540 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4541 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4542 gtk_widget_show(GTK_WIDGET(pw->imd->widget));
4544 pan_window_dnd_init(pw);
4546 pan_image_set_buttons(pw, pw->imd);
4548 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4549 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4550 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4551 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4552 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4553 gtk_widget_show(pw->scrollbar_h);
4555 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4556 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4557 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4558 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4559 0, GTK_FILL | GTK_EXPAND, 0, 0);
4560 gtk_widget_show(pw->scrollbar_v);
4564 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4565 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4567 pref_spacer(pw->search_box, 0);
4568 pref_label_new(pw->search_box, _("Find:"));
4570 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4571 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4572 gtk_widget_show(hbox);
4574 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4575 pan_search_activate_cb, pw);
4576 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4577 gtk_widget_show(combo);
4579 pw->search_label = gtk_label_new("");
4580 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4581 gtk_widget_show(pw->search_label);
4585 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4587 frame = gtk_frame_new(NULL);
4588 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4589 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4590 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4591 gtk_widget_show(frame);
4593 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4594 gtk_container_add(GTK_CONTAINER(frame), hbox);
4595 gtk_widget_show(hbox);
4597 pref_spacer(hbox, 0);
4598 pw->label_message = pref_label_new(hbox, "");
4600 frame = gtk_frame_new(NULL);
4601 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4602 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4603 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4604 gtk_widget_show(frame);
4606 pw->label_zoom = gtk_label_new("");
4607 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4608 gtk_widget_show(pw->label_zoom);
4611 pw->date_button = pref_checkbox_new(box, _("Use Exif date"), pw->exif_date_enable,
4612 G_CALLBACK(pan_window_date_toggle_cb), pw);
4615 pw->search_button = gtk_toggle_button_new();
4616 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4617 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4618 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4619 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4620 gtk_widget_show(hbox);
4621 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4622 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4623 gtk_widget_show(pw->search_button_arrow);
4624 pref_label_new(hbox, _("Find"));
4626 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4627 gtk_widget_show(pw->search_button);
4628 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4629 G_CALLBACK(pan_search_toggle_cb), pw);
4631 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4632 G_CALLBACK(pan_window_delete_cb), pw);
4633 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4634 G_CALLBACK(pan_window_key_press_cb), pw);
4636 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4638 pan_window_layout_update(pw);
4640 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4641 gtk_widget_show(pw->window);
4643 pan_window_list = g_list_append(pan_window_list, pw);
4647 *-----------------------------------------------------------------------------
4648 * peformance warnings
4649 *-----------------------------------------------------------------------------
4652 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4656 generic_dialog_close(gd);
4658 pan_window_new_real(path);
4662 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4666 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4667 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4670 static gint pan_warning(const gchar *path)
4676 GtkWidget *ct_button;
4679 if (path && strcmp(path, "/") == 0)
4681 pan_warning_folder(path, NULL);
4685 if (enable_thumb_caching &&
4686 thumbnail_spec_standard) return FALSE;
4688 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4689 if (hide_dlg) return FALSE;
4691 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4693 gd->data = g_strdup(path);
4694 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4695 pan_warning_ok_cb, TRUE);
4697 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4698 _("Pan view performance may be poor."),
4699 _("To improve performance of thumbnails in the pan view the"
4700 " following options can be enabled. Note that both options"
4701 " must be enabled to notice a change in performance."));
4703 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4704 pref_spacer(group, PREF_PAD_INDENT);
4705 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4707 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4708 enable_thumb_caching, &enable_thumb_caching);
4709 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4710 thumbnail_spec_standard, &thumbnail_spec_standard);
4711 pref_checkbox_link_sensitivity(ct_button, button);
4715 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4716 G_CALLBACK(pan_warning_hide_cb), NULL);
4718 gtk_widget_show(gd->dialog);
4725 *-----------------------------------------------------------------------------
4727 *-----------------------------------------------------------------------------
4730 void pan_window_new(const gchar *path)
4732 if (pan_warning(path)) return;
4734 pan_window_new_real(path);
4738 *-----------------------------------------------------------------------------
4740 *-----------------------------------------------------------------------------
4743 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4745 PanWindow *pw = data;
4748 path = pan_menu_click_path(pw);
4751 pan_fullscreen_toggle(pw, TRUE);
4752 view_window_new(path);
4756 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4762 pw = submenu_item_get_data(widget);
4763 n = GPOINTER_TO_INT(data);
4766 path = pan_menu_click_path(pw);
4769 if (!editor_window_flag_set(n))
4771 pan_fullscreen_toggle(pw, TRUE);
4773 start_editor_from_file(n, path);
4777 static void pan_info_cb(GtkWidget *widget, gpointer data)
4779 PanWindow *pw = data;
4782 path = pan_menu_click_path(pw);
4783 if (path) info_window_new(path, NULL);
4786 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4788 PanWindow *pw = data;
4790 image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
4793 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4795 PanWindow *pw = data;
4797 image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
4800 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4802 PanWindow *pw = data;
4804 image_zoom_set(pw->imd, 1.0);
4807 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4809 PanWindow *pw = data;
4812 path = pan_menu_click_path(pw);
4813 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4816 static void pan_move_cb(GtkWidget *widget, gpointer data)
4818 PanWindow *pw = data;
4821 path = pan_menu_click_path(pw);
4822 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4825 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4827 PanWindow *pw = data;
4830 path = pan_menu_click_path(pw);
4831 if (path) file_util_rename(path, NULL, pw->imd->widget);
4834 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4836 PanWindow *pw = data;
4839 path = pan_menu_click_path(pw);
4840 if (path) file_util_delete(path, NULL, pw->imd->widget);
4843 static void pan_exif_date_toggle_cb(GtkWidget *widget, gpointer data)
4845 PanWindow *pw = data;
4847 pw->exif_date_enable = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
4848 pan_window_layout_update(pw);
4851 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4853 PanWindow *pw = data;
4855 pan_fullscreen_toggle(pw, FALSE);
4858 static void pan_close_cb(GtkWidget *widget, gpointer data)
4860 PanWindow *pw = data;
4862 pan_window_close(pw);
4865 static GtkWidget *pan_popup_menu(PanWindow *pw)
4871 active = (pw->click_pi != NULL);
4873 menu = popup_menu_short_lived();
4875 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4876 G_CALLBACK(pan_zoom_in_cb), pw);
4877 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4878 G_CALLBACK(pan_zoom_out_cb), pw);
4879 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4880 G_CALLBACK(pan_zoom_1_1_cb), pw);
4881 menu_item_add_divider(menu);
4883 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4884 gtk_widget_set_sensitive(item, active);
4886 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4887 G_CALLBACK(pan_info_cb), pw);
4889 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4890 G_CALLBACK(pan_new_window_cb), pw);
4892 menu_item_add_divider(menu);
4893 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4894 G_CALLBACK(pan_copy_cb), pw);
4895 menu_item_add_sensitive(menu, _("_Move..."), active,
4896 G_CALLBACK(pan_move_cb), pw);
4897 menu_item_add_sensitive(menu, _("_Rename..."), active,
4898 G_CALLBACK(pan_rename_cb), pw);
4899 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4900 G_CALLBACK(pan_delete_cb), pw);
4902 menu_item_add_divider(menu);
4903 item = menu_item_add_check(menu, _("Sort by E_xif date"), pw->exif_date_enable,
4904 G_CALLBACK(pan_exif_date_toggle_cb), pw);
4905 gtk_widget_set_sensitive(item, (pw->layout == LAYOUT_TIMELINE || pw->layout == LAYOUT_CALENDAR));
4907 menu_item_add_divider(menu);
4911 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4915 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4918 menu_item_add_divider(menu);
4919 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4925 *-----------------------------------------------------------------------------
4927 *-----------------------------------------------------------------------------
4930 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4932 GtkSelectionData *selection_data, guint info,
4933 guint time, gpointer data)
4935 PanWindow *pw = data;
4937 if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
4939 if (info == TARGET_URI_LIST)
4943 list = uri_list_from_text((gchar *)selection_data->data, TRUE);
4944 if (list && isdir((gchar *)list->data))
4946 gchar *path = list->data;
4948 pan_window_layout_set_path(pw, path);
4951 path_list_free(list);
4955 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4956 GtkSelectionData *selection_data, guint info,
4957 guint time, gpointer data)
4959 PanWindow *pw = data;
4962 path = pan_menu_click_path(pw);
4972 case TARGET_URI_LIST:
4975 case TARGET_TEXT_PLAIN:
4980 list = g_list_append(NULL, (gchar *)path);
4981 text = uri_text_from_list(list, &len, plain_text);
4985 gtk_selection_data_set (selection_data, selection_data->target,
4986 8, (guchar *)text, len);
4992 gtk_selection_data_set (selection_data, selection_data->target,
4997 static void pan_window_dnd_init(PanWindow *pw)
5001 widget = pw->imd->pr;
5003 gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
5004 dnd_file_drag_types, dnd_file_drag_types_count,
5005 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
5006 g_signal_connect(G_OBJECT(widget), "drag_data_get",
5007 G_CALLBACK(pan_window_set_dnd_data), pw);
5009 gtk_drag_dest_set(widget,
5010 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
5011 dnd_file_drop_types, dnd_file_drop_types_count,
5012 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
5013 g_signal_connect(G_OBJECT(widget), "drag_data_received",
5014 G_CALLBACK(pan_window_get_dnd_data), pw);
5018 *-----------------------------------------------------------------------------
5019 * maintenance (for rename, move, remove)
5020 *-----------------------------------------------------------------------------