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);
3568 pan_fullscreen_toggle(pw, FALSE);
3572 pan_overlay_toggle(pw);
3575 case GDK_Delete: case GDK_KP_Delete:
3580 if (GTK_WIDGET_VISIBLE(pw->search_box))
3582 gtk_widget_grab_focus(pw->search_entry);
3586 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3591 stop_signal = FALSE;
3597 pan_fullscreen_toggle(pw, TRUE);
3599 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3601 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3605 stop_signal = FALSE;
3610 menu = pan_popup_menu(pw);
3611 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3614 stop_signal = FALSE;
3621 switch (event->keyval)
3626 pan_fullscreen_toggle(pw, TRUE);
3628 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3630 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
3631 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3635 stop_signal = FALSE;
3639 stop_signal = FALSE;
3649 *-----------------------------------------------------------------------------
3651 *-----------------------------------------------------------------------------
3654 static void pan_info_update(PanWindow *pw, PanItem *pi)
3660 gint x1, y1, x2, y2, x3, y3;
3663 if (pw->click_pi == pi) return;
3664 if (pi && !pi->fd) pi = NULL;
3666 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3671 if (debug) printf("info set to %s\n", pi->fd->path);
3673 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3675 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3676 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3677 pan_item_set_key(pbox, "info");
3679 if (pi->type == ITEM_THUMB && pi->pixbuf)
3681 w = gdk_pixbuf_get_width(pi->pixbuf);
3682 h = gdk_pixbuf_get_height(pi->pixbuf);
3684 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3685 y1 = pi->y + (pi->height - h) / 2 + 8;
3689 x1 = pi->x + pi->width - 8;
3697 util_clip_triangle(x1, y1, x2, y2, x3, y3,
3700 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3701 x1, y1, x2, y2, x3, y3,
3702 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3703 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3704 pan_item_set_key(p, "info");
3705 pan_item_added(pw, p);
3707 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3708 _("Filename:"), TEXT_ATTR_BOLD,
3709 PAN_POPUP_TEXT_COLOR, 255);
3710 pan_item_set_key(plabel, "info");
3711 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3712 pi->fd->name, TEXT_ATTR_NONE,
3713 PAN_POPUP_TEXT_COLOR, 255);
3714 pan_item_set_key(p, "info");
3715 pan_item_size_by_item(pbox, p, 0);
3717 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3718 _("Date:"), TEXT_ATTR_BOLD,
3719 PAN_POPUP_TEXT_COLOR, 255);
3720 pan_item_set_key(plabel, "info");
3721 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3722 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3723 PAN_POPUP_TEXT_COLOR, 255);
3724 pan_item_set_key(p, "info");
3725 pan_item_size_by_item(pbox, p, 0);
3727 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3728 _("Size:"), TEXT_ATTR_BOLD,
3729 PAN_POPUP_TEXT_COLOR, 255);
3730 pan_item_set_key(plabel, "info");
3731 buf = text_from_size(pi->fd->size);
3732 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3733 buf, TEXT_ATTR_NONE,
3734 PAN_POPUP_TEXT_COLOR, 255);
3736 pan_item_set_key(p, "info");
3737 pan_item_size_by_item(pbox, p, 0);
3739 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3740 pan_item_added(pw, pbox);
3745 if (image_load_dimensions(pi->fd->path, &iw, &ih))
3747 pbox = pan_item_new_box(pw, NULL, pbox->x, pbox->y + pbox->height + 8, 10, 10,
3749 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3750 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3751 pan_item_set_key(pbox, "info");
3753 p = pan_item_new_image(pw, file_data_new_simple(pi->fd->path),
3754 pbox->x + 8, pbox->y + 8, iw, ih);
3755 pan_item_set_key(p, "info");
3756 pan_item_size_by_item(pbox, p, 8);
3758 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3759 pan_item_added(pw, pbox);
3761 pan_layout_resize(pw);
3768 *-----------------------------------------------------------------------------
3770 *-----------------------------------------------------------------------------
3773 static void pan_search_status(PanWindow *pw, const gchar *text)
3775 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3778 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3786 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3788 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3789 if (!list) return FALSE;
3791 found = g_list_find(list, pw->click_pi);
3792 if (found && found->next)
3794 found = found->next;
3802 pan_info_update(pw, pi);
3803 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3805 buf = g_strdup_printf("%s ( %d / %d )",
3806 (path[0] == '/') ? _("path found") : _("filename found"),
3807 g_list_index(list, pi) + 1,
3808 g_list_length(list));
3809 pan_search_status(pw, buf);
3817 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3825 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3827 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3828 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3833 needle = g_utf8_strdown(text, -1);
3834 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3837 if (!list) return FALSE;
3839 found = g_list_find(list, pw->click_pi);
3840 if (found && found->next)
3842 found = found->next;
3850 pan_info_update(pw, pi);
3851 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3853 buf = g_strdup_printf("%s ( %d / %d )",
3855 g_list_index(list, pi) + 1,
3856 g_list_length(list));
3857 pan_search_status(pw, buf);
3865 static gint valid_date_separator(gchar c)
3867 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3870 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
3871 gint year, gint month, gint day,
3877 work = g_list_last(pw->list_static);
3885 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
3886 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
3890 tl = localtime(&pi->fd->date);
3895 match = (tl->tm_year == year - 1900);
3896 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3897 if (match && day > 0) match = (tl->tm_mday == day);
3899 if (match) list = g_list_prepend(list, pi);
3904 return g_list_reverse(list);
3907 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3923 if (!text) return FALSE;
3925 ptr = (gchar *)text;
3926 while (*ptr != '\0')
3928 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3933 if (t == -1) return FALSE;
3935 if (!lt) return FALSE;
3937 if (valid_date_separator(*text))
3940 mptr = (gchar *)text;
3944 year = (gint)strtol(text, &mptr, 10);
3945 if (mptr == text) return FALSE;
3948 if (*mptr != '\0' && valid_date_separator(*mptr))
3953 month = strtol(mptr, &dptr, 10);
3956 if (valid_date_separator(*dptr))
3958 month = lt->tm_mon + 1;
3966 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3970 day = strtol(dptr, &eptr, 10);
3980 year = lt->tm_year + 1900;
3982 else if (year < 100)
3991 month < -1 || month == 0 || month > 12 ||
3992 day < -1 || day == 0 || day > 31) return FALSE;
3994 t = date_to_time(year, month, day);
3995 if (t < 0) return FALSE;
3997 if (pw->layout == LAYOUT_CALENDAR)
3999 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
4005 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
4006 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
4011 found = g_list_find(list, pw->search_pi);
4012 if (found && found->next)
4014 found = found->next;
4025 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
4027 pan_info_update(pw, NULL);
4028 pan_calendar_update(pw, pi);
4029 image_scroll_to_point(pw->imd,
4030 pi->x + pi->width / 2,
4031 pi->y + pi->height / 2, 0.5, 0.5);
4035 pan_info_update(pw, pi);
4036 image_scroll_to_point(pw->imd,
4037 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
4043 buf = date_value_string(t, DATE_LENGTH_MONTH);
4048 buf = g_strdup_printf("%d %s", day, tmp);
4054 buf = date_value_string(t, DATE_LENGTH_YEAR);
4059 buf_count = g_strdup_printf("( %d / %d )",
4060 g_list_index(list, pi) + 1,
4061 g_list_length(list));
4065 buf_count = g_strdup_printf("(%s)", _("no match"));
4068 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
4071 pan_search_status(pw, message);
4079 static void pan_search_activate_cb(const gchar *text, gpointer data)
4081 PanWindow *pw = data;
4085 tab_completion_append_to_history(pw->search_entry, text);
4087 if (pan_search_by_path(pw, text)) return;
4089 if ((pw->layout == LAYOUT_TIMELINE ||
4090 pw->layout == LAYOUT_CALENDAR) &&
4091 pan_search_by_date(pw, text))
4096 if (pan_search_by_partial(pw, text)) return;
4098 pan_search_status(pw, _("no match"));
4101 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
4103 PanWindow *pw = data;
4106 visible = GTK_WIDGET_VISIBLE(pw->search_box);
4107 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
4111 gtk_widget_hide(pw->search_box);
4112 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
4116 gtk_widget_show(pw->search_box);
4117 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
4118 gtk_widget_grab_focus(pw->search_entry);
4124 *-----------------------------------------------------------------------------
4125 * view window main routines
4126 *-----------------------------------------------------------------------------
4129 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
4131 PanWindow *pw = data;
4139 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
4140 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
4143 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "info");
4144 if (pi && event->button == 1)
4146 pan_info_update(pw, NULL);
4150 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
4153 switch (event->button)
4156 pan_info_update(pw, pi);
4158 if (!pi && pw->layout == LAYOUT_CALENDAR)
4160 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
4161 pan_calendar_update(pw, pi);
4167 pan_info_update(pw, pi);
4168 menu = pan_popup_menu(pw);
4169 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
4176 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
4179 PanWindow *pw = data;
4186 if (!(event->state & GDK_SHIFT_MASK))
4192 if (event->state & GDK_CONTROL_MASK)
4194 switch (event->direction)
4197 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
4198 (gint)event->x, (gint)event->y);
4200 case GDK_SCROLL_DOWN:
4201 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
4202 (gint)event->x, (gint)event->y);
4210 switch (event->direction)
4213 pixbuf_renderer_scroll(pr, 0, -h);
4215 case GDK_SCROLL_DOWN:
4216 pixbuf_renderer_scroll(pr, 0, h);
4218 case GDK_SCROLL_LEFT:
4219 pixbuf_renderer_scroll(pr, -w, 0);
4221 case GDK_SCROLL_RIGHT:
4222 pixbuf_renderer_scroll(pr, w, 0);
4230 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
4232 g_signal_connect(G_OBJECT(imd->pr), "clicked",
4233 G_CALLBACK(button_cb), pw);
4234 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
4235 G_CALLBACK(scroll_cb), pw);
4238 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
4240 PanWindow *pw = data;
4243 pw->imd = pw->imd_normal;
4246 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
4248 if (force_off && !pw->fs) return;
4252 fullscreen_stop(pw->fs);
4256 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
4257 pan_image_set_buttons(pw, pw->fs->imd);
4258 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
4259 G_CALLBACK(pan_window_key_press_cb), pw);
4261 pw->imd = pw->fs->imd;
4265 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
4267 PanWindow *pw = data;
4270 text = image_zoom_get_as_text(pw->imd);
4271 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4275 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
4277 PanWindow *pw = data;
4282 if (pr->scale == 0.0) return;
4284 pixbuf_renderer_get_visible_rect(pr, &rect);
4285 pixbuf_renderer_get_image_size(pr, &width, &height);
4287 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4288 adj->page_size = (gdouble)rect.width;
4289 adj->page_increment = adj->page_size / 2.0;
4290 adj->step_increment = 48.0 / pr->scale;
4292 adj->upper = MAX((gdouble)width, 1.0);
4293 adj->value = (gdouble)rect.x;
4295 pref_signal_block_data(pw->scrollbar_h, pw);
4296 gtk_adjustment_changed(adj);
4297 gtk_adjustment_value_changed(adj);
4298 pref_signal_unblock_data(pw->scrollbar_h, pw);
4300 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4301 adj->page_size = (gdouble)rect.height;
4302 adj->page_increment = adj->page_size / 2.0;
4303 adj->step_increment = 48.0 / pr->scale;
4305 adj->upper = MAX((gdouble)height, 1.0);
4306 adj->value = (gdouble)rect.y;
4308 pref_signal_block_data(pw->scrollbar_v, pw);
4309 gtk_adjustment_changed(adj);
4310 gtk_adjustment_value_changed(adj);
4311 pref_signal_unblock_data(pw->scrollbar_v, pw);
4314 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4316 PanWindow *pw = data;
4320 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4322 if (!pr->scale) return;
4324 x = (gint)gtk_range_get_value(range);
4326 pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
4329 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4331 PanWindow *pw = data;
4335 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4337 if (!pr->scale) return;
4339 y = (gint)gtk_range_get_value(range);
4341 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
4344 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4346 PanWindow *pw = data;
4348 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4349 pan_window_layout_update(pw);
4352 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4354 PanWindow *pw = data;
4356 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4357 pan_window_layout_update(pw);
4361 static void pan_window_date_toggle_cb(GtkWidget *button, gpointer data)
4363 PanWindow *pw = data;
4365 pw->exif_date_enable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4366 pan_window_layout_update(pw);
4370 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4372 PanWindow *pw = data;
4375 path = remove_trailing_slash(new_text);
4376 parse_out_relatives(path);
4380 warning_dialog(_("Folder not found"),
4381 _("The entered path is not a folder"),
4382 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4386 tab_completion_append_to_history(pw->path_entry, path);
4388 pan_window_layout_set_path(pw, path);
4394 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4396 PanWindow *pw = data;
4399 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4401 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4402 pan_window_entry_activate_cb(text, pw);
4406 static void pan_window_close(PanWindow *pw)
4408 pan_window_list = g_list_remove(pan_window_list, pw);
4410 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_EXIF_DATE, pw->exif_date_enable);
4412 if (pw->idle_id != -1)
4414 g_source_remove(pw->idle_id);
4417 pan_fullscreen_toggle(pw, TRUE);
4418 gtk_widget_destroy(pw->window);
4420 pan_window_items_free(pw);
4428 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4430 PanWindow *pw = data;
4432 pan_window_close(pw);
4436 static void pan_window_new_real(const gchar *path)
4445 GdkGeometry geometry;
4447 pw = g_new0(PanWindow, 1);
4449 pw->path = g_strdup(path);
4450 pw->layout = LAYOUT_TIMELINE;
4451 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4452 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4453 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4455 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_EXIF_DATE, &pw->exif_date_enable))
4457 pw->exif_date_enable = FALSE;
4460 pw->ignore_symlinks = TRUE;
4463 pw->list_static = NULL;
4464 pw->list_grid = NULL;
4467 pw->overlay_id = -1;
4470 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4472 geometry.min_width = 8;
4473 geometry.min_height = 8;
4474 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4476 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4477 gtk_window_set_title (GTK_WINDOW(pw->window), _("Pan View - GQview"));
4478 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4479 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4481 window_set_icon(pw->window, NULL, NULL);
4483 vbox = gtk_vbox_new(FALSE, 0);
4484 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4485 gtk_widget_show(vbox);
4487 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4489 pref_spacer(box, 0);
4490 pref_label_new(box, _("Location:"));
4491 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view_path", -1,
4492 pan_window_entry_activate_cb, pw);
4493 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4494 G_CALLBACK(pan_window_entry_change_cb), pw);
4495 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4496 gtk_widget_show(combo);
4498 combo = gtk_combo_box_new_text();
4499 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4500 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4501 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4502 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4503 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4505 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4506 g_signal_connect(G_OBJECT(combo), "changed",
4507 G_CALLBACK(pan_window_layout_change_cb), pw);
4508 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4509 gtk_widget_show(combo);
4511 combo = gtk_combo_box_new_text();
4512 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4513 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4514 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4515 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4516 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4517 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4518 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4519 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4520 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4521 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4523 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4524 g_signal_connect(G_OBJECT(combo), "changed",
4525 G_CALLBACK(pan_window_layout_size_cb), pw);
4526 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4527 gtk_widget_show(combo);
4529 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4530 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4531 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4533 pw->imd = image_new(TRUE);
4534 pw->imd_normal = pw->imd;
4536 g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
4537 G_CALLBACK(pan_window_image_zoom_cb), pw);
4538 g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
4539 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4541 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4542 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4543 gtk_widget_show(GTK_WIDGET(pw->imd->widget));
4545 pan_window_dnd_init(pw);
4547 pan_image_set_buttons(pw, pw->imd);
4549 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4550 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4551 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4552 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4553 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4554 gtk_widget_show(pw->scrollbar_h);
4556 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4557 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4558 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4559 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4560 0, GTK_FILL | GTK_EXPAND, 0, 0);
4561 gtk_widget_show(pw->scrollbar_v);
4565 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4566 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4568 pref_spacer(pw->search_box, 0);
4569 pref_label_new(pw->search_box, _("Find:"));
4571 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4572 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4573 gtk_widget_show(hbox);
4575 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4576 pan_search_activate_cb, pw);
4577 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4578 gtk_widget_show(combo);
4580 pw->search_label = gtk_label_new("");
4581 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4582 gtk_widget_show(pw->search_label);
4586 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4588 frame = gtk_frame_new(NULL);
4589 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4590 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4591 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4592 gtk_widget_show(frame);
4594 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4595 gtk_container_add(GTK_CONTAINER(frame), hbox);
4596 gtk_widget_show(hbox);
4598 pref_spacer(hbox, 0);
4599 pw->label_message = pref_label_new(hbox, "");
4601 frame = gtk_frame_new(NULL);
4602 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4603 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4604 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4605 gtk_widget_show(frame);
4607 pw->label_zoom = gtk_label_new("");
4608 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4609 gtk_widget_show(pw->label_zoom);
4612 pw->date_button = pref_checkbox_new(box, _("Use Exif date"), pw->exif_date_enable,
4613 G_CALLBACK(pan_window_date_toggle_cb), pw);
4616 pw->search_button = gtk_toggle_button_new();
4617 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4618 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4619 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4620 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4621 gtk_widget_show(hbox);
4622 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4623 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4624 gtk_widget_show(pw->search_button_arrow);
4625 pref_label_new(hbox, _("Find"));
4627 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4628 gtk_widget_show(pw->search_button);
4629 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4630 G_CALLBACK(pan_search_toggle_cb), pw);
4632 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4633 G_CALLBACK(pan_window_delete_cb), pw);
4634 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4635 G_CALLBACK(pan_window_key_press_cb), pw);
4637 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4639 pan_window_layout_update(pw);
4641 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4642 gtk_widget_show(pw->window);
4644 pan_window_list = g_list_append(pan_window_list, pw);
4648 *-----------------------------------------------------------------------------
4649 * peformance warnings
4650 *-----------------------------------------------------------------------------
4653 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4657 generic_dialog_close(gd);
4659 pan_window_new_real(path);
4663 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4667 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4668 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4671 static gint pan_warning(const gchar *path)
4677 GtkWidget *ct_button;
4680 if (path && strcmp(path, "/") == 0)
4682 pan_warning_folder(path, NULL);
4686 if (enable_thumb_caching &&
4687 thumbnail_spec_standard) return FALSE;
4689 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4690 if (hide_dlg) return FALSE;
4692 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4694 gd->data = g_strdup(path);
4695 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4696 pan_warning_ok_cb, TRUE);
4698 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4699 _("Pan view performance may be poor."),
4700 _("To improve performance of thumbnails in the pan view the"
4701 " following options can be enabled. Note that both options"
4702 " must be enabled to notice a change in performance."));
4704 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4705 pref_spacer(group, PREF_PAD_INDENT);
4706 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4708 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4709 enable_thumb_caching, &enable_thumb_caching);
4710 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4711 thumbnail_spec_standard, &thumbnail_spec_standard);
4712 pref_checkbox_link_sensitivity(ct_button, button);
4716 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4717 G_CALLBACK(pan_warning_hide_cb), NULL);
4719 gtk_widget_show(gd->dialog);
4726 *-----------------------------------------------------------------------------
4728 *-----------------------------------------------------------------------------
4731 void pan_window_new(const gchar *path)
4733 if (pan_warning(path)) return;
4735 pan_window_new_real(path);
4739 *-----------------------------------------------------------------------------
4741 *-----------------------------------------------------------------------------
4744 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4746 PanWindow *pw = data;
4749 path = pan_menu_click_path(pw);
4752 pan_fullscreen_toggle(pw, TRUE);
4753 view_window_new(path);
4757 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4763 pw = submenu_item_get_data(widget);
4764 n = GPOINTER_TO_INT(data);
4767 path = pan_menu_click_path(pw);
4770 if (!editor_window_flag_set(n))
4772 pan_fullscreen_toggle(pw, TRUE);
4774 start_editor_from_file(n, path);
4778 static void pan_info_cb(GtkWidget *widget, gpointer data)
4780 PanWindow *pw = data;
4783 path = pan_menu_click_path(pw);
4784 if (path) info_window_new(path, NULL);
4787 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4789 PanWindow *pw = data;
4791 image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
4794 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4796 PanWindow *pw = data;
4798 image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
4801 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4803 PanWindow *pw = data;
4805 image_zoom_set(pw->imd, 1.0);
4808 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4810 PanWindow *pw = data;
4813 path = pan_menu_click_path(pw);
4814 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4817 static void pan_move_cb(GtkWidget *widget, gpointer data)
4819 PanWindow *pw = data;
4822 path = pan_menu_click_path(pw);
4823 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4826 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4828 PanWindow *pw = data;
4831 path = pan_menu_click_path(pw);
4832 if (path) file_util_rename(path, NULL, pw->imd->widget);
4835 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4837 PanWindow *pw = data;
4840 path = pan_menu_click_path(pw);
4841 if (path) file_util_delete(path, NULL, pw->imd->widget);
4844 static void pan_exif_date_toggle_cb(GtkWidget *widget, gpointer data)
4846 PanWindow *pw = data;
4848 pw->exif_date_enable = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
4849 pan_window_layout_update(pw);
4852 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4854 PanWindow *pw = data;
4856 pan_fullscreen_toggle(pw, FALSE);
4859 static void pan_close_cb(GtkWidget *widget, gpointer data)
4861 PanWindow *pw = data;
4863 pan_window_close(pw);
4866 static GtkWidget *pan_popup_menu(PanWindow *pw)
4872 active = (pw->click_pi != NULL);
4874 menu = popup_menu_short_lived();
4876 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4877 G_CALLBACK(pan_zoom_in_cb), pw);
4878 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4879 G_CALLBACK(pan_zoom_out_cb), pw);
4880 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4881 G_CALLBACK(pan_zoom_1_1_cb), pw);
4882 menu_item_add_divider(menu);
4884 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4885 gtk_widget_set_sensitive(item, active);
4887 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4888 G_CALLBACK(pan_info_cb), pw);
4890 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4891 G_CALLBACK(pan_new_window_cb), pw);
4893 menu_item_add_divider(menu);
4894 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4895 G_CALLBACK(pan_copy_cb), pw);
4896 menu_item_add_sensitive(menu, _("_Move..."), active,
4897 G_CALLBACK(pan_move_cb), pw);
4898 menu_item_add_sensitive(menu, _("_Rename..."), active,
4899 G_CALLBACK(pan_rename_cb), pw);
4900 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4901 G_CALLBACK(pan_delete_cb), pw);
4903 menu_item_add_divider(menu);
4904 item = menu_item_add_check(menu, _("Sort by E_xif date"), pw->exif_date_enable,
4905 G_CALLBACK(pan_exif_date_toggle_cb), pw);
4906 gtk_widget_set_sensitive(item, (pw->layout == LAYOUT_TIMELINE || pw->layout == LAYOUT_CALENDAR));
4908 menu_item_add_divider(menu);
4912 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4916 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4919 menu_item_add_divider(menu);
4920 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4926 *-----------------------------------------------------------------------------
4928 *-----------------------------------------------------------------------------
4931 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4933 GtkSelectionData *selection_data, guint info,
4934 guint time, gpointer data)
4936 PanWindow *pw = data;
4938 if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
4940 if (info == TARGET_URI_LIST)
4944 list = uri_list_from_text((gchar *)selection_data->data, TRUE);
4945 if (list && isdir((gchar *)list->data))
4947 gchar *path = list->data;
4949 pan_window_layout_set_path(pw, path);
4952 path_list_free(list);
4956 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4957 GtkSelectionData *selection_data, guint info,
4958 guint time, gpointer data)
4960 PanWindow *pw = data;
4963 path = pan_menu_click_path(pw);
4973 case TARGET_URI_LIST:
4976 case TARGET_TEXT_PLAIN:
4981 list = g_list_append(NULL, (gchar *)path);
4982 text = uri_text_from_list(list, &len, plain_text);
4986 gtk_selection_data_set (selection_data, selection_data->target,
4987 8, (guchar *)text, len);
4993 gtk_selection_data_set (selection_data, selection_data->target,
4998 static void pan_window_dnd_init(PanWindow *pw)
5002 widget = pw->imd->pr;
5004 gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
5005 dnd_file_drag_types, dnd_file_drag_types_count,
5006 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
5007 g_signal_connect(G_OBJECT(widget), "drag_data_get",
5008 G_CALLBACK(pan_window_set_dnd_data), pw);
5010 gtk_drag_dest_set(widget,
5011 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
5012 dnd_file_drop_types, dnd_file_drop_types_count,
5013 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
5014 g_signal_connect(G_OBJECT(widget), "drag_data_received",
5015 G_CALLBACK(pan_window_get_dnd_data), pw);
5019 *-----------------------------------------------------------------------------
5020 * maintenance (for rename, move, remove)
5021 *-----------------------------------------------------------------------------