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, gint border);
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;
1080 pan_item_image_find_size(pw, pi, w, h);
1082 pw->list = g_list_prepend(pw->list, pi);
1087 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
1091 if (!key) return NULL;
1093 work = g_list_last(pw->list);
1099 if ((pi->type == type || type == ITEM_NONE) &&
1100 pi->key && strcmp(pi->key, key) == 0)
1106 work = g_list_last(pw->list_static);
1112 if ((pi->type == type || type == ITEM_NONE) &&
1113 pi->key && strcmp(pi->key, key) == 0)
1123 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1124 static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
1125 ItemType type, const gchar *path,
1126 gint ignore_case, gint partial)
1130 work = g_list_last(search_list);
1136 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1142 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
1144 else if (pi->fd->name)
1152 haystack = g_utf8_strdown(pi->fd->name, -1);
1153 match = (strstr(haystack, path) != NULL);
1158 if (strstr(pi->fd->name, path)) match = TRUE;
1161 else if (ignore_case)
1163 if (strcasecmp(path, pi->fd->name) == 0) match = TRUE;
1167 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
1171 if (match) list = g_list_prepend(list, pi);
1179 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1180 static GList *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1181 gint ignore_case, gint partial)
1185 if (!path) return NULL;
1186 if (partial && path[0] == '/') return NULL;
1188 list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
1189 list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
1191 return g_list_reverse(list);
1194 static PanItem *pan_item_find_by_coord_l(GList *list, ItemType type, gint x, gint y, const gchar *key)
1204 if ((pi->type == type || type == ITEM_NONE) &&
1205 x >= pi->x && x < pi->x + pi->width &&
1206 y >= pi->y && y < pi->y + pi->height &&
1207 (!key || (pi->key && strcmp(pi->key, key) == 0)))
1217 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y, const gchar *key)
1221 pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
1224 return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
1228 *-----------------------------------------------------------------------------
1230 *-----------------------------------------------------------------------------
1233 static gint islink_loop(const gchar *s)
1239 sl = path_from_utf8(s);
1241 if (lstat(sl, &st) == 0 && S_ISLNK(st.st_mode))
1246 buf = g_malloc(st.st_size + 1);
1247 l = readlink(sl, buf, st.st_size);
1248 if (l == st.st_size)
1252 parse_out_relatives(buf);
1255 parse_out_relatives(sl);
1259 if (strncmp(sl, buf, l) == 0 &&
1260 (sl[l] == '\0' || sl[l] == '/' || l == 1)) ret = TRUE;
1266 link_path = concat_dir_and_file(sl, buf);
1267 parse_out_relatives(link_path);
1269 if (strncmp(sl, link_path, l) == 0 &&
1270 (sl[l] == '\0' || sl[l] == '/' || l == 1)) ret = TRUE;
1284 static gint is_ignored(const gchar *s, gint ignore_symlinks)
1289 if (!lstat_utf8(s, &st)) return TRUE;
1292 /* normal filesystems have directories with some size or block allocation,
1293 * special filesystems (like linux /proc) set both to zero.
1294 * enable this check if you enable listing the root "/" folder
1296 if (st.st_size == 0 && st.st_blocks == 0) return TRUE;
1299 if (S_ISLNK(st.st_mode) && (ignore_symlinks || islink_loop(s))) return TRUE;
1301 n = filename_from_path(s);
1302 if (n && strcmp(n, GQVIEW_RC_DIR) == 0) return TRUE;
1307 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend,
1308 gint ignore_symlinks)
1310 GList *flist = NULL;
1311 GList *dlist = NULL;
1315 filelist_read(path, &flist, &dlist);
1316 if (sort != SORT_NONE)
1318 flist = filelist_sort(flist, sort, ascend);
1319 dlist = filelist_sort(dlist, sort, ascend);
1329 folders = g_list_remove(folders, fd);
1331 if (!is_ignored(fd->path, ignore_symlinks) &&
1332 filelist_read(fd->path, &flist, &dlist))
1334 if (sort != SORT_NONE)
1336 flist = filelist_sort(flist, sort, ascend);
1337 dlist = filelist_sort(dlist, sort, ascend);
1340 result = g_list_concat(result, flist);
1341 folders = g_list_concat(dlist, folders);
1350 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1358 list = pan_window_layout_list(path, SORT_NAME, TRUE, pw->ignore_symlinks);
1360 grid_size = (gint)sqrt((double)g_list_length(list));
1361 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1363 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1367 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1372 *width = PAN_FOLDER_BOX_BORDER * 2;
1373 *height = PAN_FOLDER_BOX_BORDER * 2;
1386 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1388 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1390 x += pi->width + PAN_THUMB_GAP;
1391 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1400 pi = pan_item_new_thumb(pw, fd, x, y);
1402 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1406 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1409 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1415 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1418 gint x1, y1, x2, y2;
1433 if (x1 > pi->x) x1 = pi->x;
1434 if (y1 > pi->y) y1 = pi->y;
1435 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1436 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1439 x1 -= PAN_FOLDER_BOX_BORDER;
1440 y1 -= PAN_FOLDER_BOX_BORDER;
1441 x2 += PAN_FOLDER_BOX_BORDER;
1442 y2 += PAN_FOLDER_BOX_BORDER;
1455 if (pi->type == ITEM_TRIANGLE && pi->data)
1469 if (width) *width = x2 - x1;
1470 if (height) *height = y2 - y1;
1473 typedef struct _FlowerGroup FlowerGroup;
1474 struct _FlowerGroup {
1487 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1491 work = group->items;
1509 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1510 gint *result_x, gint *result_y)
1516 radius = parent->circumference / (2*PI);
1517 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1519 a = 2*PI * group->diameter / parent->circumference;
1521 x = (gint)((double)radius * cos(parent->angle + a / 2));
1522 y = (gint)((double)radius * sin(parent->angle + a / 2));
1529 x += parent->width / 2;
1530 y += parent->height / 2;
1532 x -= group->width / 2;
1533 y -= group->height / 2;
1539 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1546 if (parent && parent->children)
1548 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1556 pan_window_layout_compute_folder_flower_move(group, x, y);
1561 gint px, py, gx, gy;
1562 gint x1, y1, x2, y2;
1564 px = parent->x + parent->width / 2;
1565 py = parent->y + parent->height / 2;
1567 gx = group->x + group->width / 2;
1568 gy = group->y + group->height / 2;
1573 x2 = MAX(px, gx + 5);
1574 y2 = MAX(py, gy + 5);
1576 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1577 px, py, gx, gy, gx + 5, gy + 5,
1579 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1583 pw->list = g_list_concat(group->items, pw->list);
1584 group->items = NULL;
1586 group->circumference = 0;
1587 work = group->children;
1595 group->circumference += child->diameter;
1598 work = g_list_last(group->children);
1606 pan_window_layout_compute_folder_flower_build(pw, child, group);
1609 g_list_free(group->children);
1613 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1626 if (!filelist_read(path, &f, &d)) return NULL;
1627 if (!f && !d) return NULL;
1629 f = filelist_sort(f, SORT_NAME, TRUE);
1630 d = filelist_sort(d, SORT_NAME, TRUE);
1632 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1633 PAN_TEXT_COLOR, 255);
1635 y += pi_box->height;
1637 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1639 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1640 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1641 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1642 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1644 x += PAN_FOLDER_BOX_BORDER;
1645 y += PAN_FOLDER_BOX_BORDER;
1647 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1661 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1663 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1664 x += pi->width + PAN_THUMB_GAP;
1665 if (pi->height > y_height) y_height = pi->height;
1669 pi = pan_item_new_thumb(pw, fd, x, y);
1670 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1671 y_height = PAN_THUMB_SIZE;
1675 if (grid_count >= grid_size)
1679 y += y_height + PAN_THUMB_GAP;
1683 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1686 group = g_new0(FlowerGroup, 1);
1687 group->items = pw->list;
1690 group->width = pi_box->width;
1691 group->height = pi_box->y + pi_box->height;
1692 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1694 group->children = NULL;
1705 if (!is_ignored(fd->path, pw->ignore_symlinks))
1707 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1708 if (child) group->children = g_list_prepend(group->children, child);
1712 if (!f && !group->children)
1714 work = group->items;
1725 g_list_free(group->items);
1736 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1737 gint *width, gint *height,
1738 gint *scroll_x, gint *scroll_y)
1743 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1744 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1746 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1748 list = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1751 PanItem *pi = list->data;
1752 *scroll_x = pi->x + pi->width / 2;
1753 *scroll_y = pi->y + pi->height / 2;
1758 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1759 gint *x, gint *y, gint *level,
1761 gint *width, gint *height)
1769 if (!filelist_read(path, &f, &d)) return;
1770 if (!f && !d) return;
1772 f = filelist_sort(f, SORT_NAME, TRUE);
1773 d = filelist_sort(d, SORT_NAME, TRUE);
1775 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1777 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1778 PAN_TEXT_COLOR, 255);
1780 *y += pi_box->height;
1782 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1784 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1785 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1786 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1787 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1789 *x += PAN_FOLDER_BOX_BORDER;
1790 *y += PAN_FOLDER_BOX_BORDER;
1801 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1803 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1804 *x += pi->width + PAN_THUMB_GAP;
1805 if (pi->height > y_height) y_height = pi->height;
1809 pi = pan_item_new_thumb(pw, fd, *x, *y);
1810 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1811 y_height = PAN_THUMB_SIZE;
1814 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1817 if (f) *y = pi_box->y + pi_box->height;
1829 if (!is_ignored(fd->path, pw->ignore_symlinks))
1831 *level = *level + 1;
1832 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1833 pi_box, width, height);
1834 *level = *level - 1;
1840 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1842 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1843 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1845 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1848 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1855 x = PAN_FOLDER_BOX_BORDER;
1856 y = PAN_FOLDER_BOX_BORDER;
1857 w = PAN_FOLDER_BOX_BORDER * 2;
1858 h = PAN_FOLDER_BOX_BORDER * 2;
1860 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1862 if (width) *width = w;
1863 if (height) *height = h;
1867 *-----------------------------------------------------------------------------
1869 *-----------------------------------------------------------------------------
1872 static void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
1878 gint x1, y1, x2, y2, x3, y3;
1883 while ((pi = pan_item_find_by_key(pw, ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
1885 if (!pi_day || pi_day->type != ITEM_BOX ||
1886 !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
1888 list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
1900 if (dot->type != ITEM_BOX || !dot->fd ||
1901 !dot->key || strcmp(dot->key, "dot") != 0)
1903 list = g_list_delete_link(list, node);
1911 grid = (gint)(sqrt(g_list_length(list)) + 0.5);
1913 x = pi_day->x + pi_day->width + 4;
1917 if (y + grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4 > pw->pr->image_height)
1919 y = pw->pr->image_height - (grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4);
1923 pbox = pan_item_new_box(pw, NULL, x, y, PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1924 PAN_CAL_POPUP_BORDER,
1925 PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA,
1926 PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
1927 pan_item_set_key(pbox, "day_bubble");
1934 buf = date_value_string(pi_day->fd->date, DATE_LENGTH_WEEK);
1935 plabel = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1936 PAN_CAL_POPUP_TEXT_COLOR, 255);
1937 pan_item_set_key(plabel, "day_bubble");
1940 pan_item_size_by_item(pbox, plabel, 0);
1942 y += plabel->height;
1949 x += PAN_FOLDER_BOX_BORDER;
1950 y += PAN_FOLDER_BOX_BORDER;
1964 pimg = pan_item_new_thumb(pw, file_data_new_simple(dot->fd->path), x, y);
1965 pan_item_set_key(pimg, "day_bubble");
1967 pan_item_size_by_item(pbox, pimg, PAN_FOLDER_BOX_BORDER);
1972 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1977 x = pbox->x + PAN_FOLDER_BOX_BORDER;
1978 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1984 x1 = pi_day->x + pi_day->width - 8;
1987 y2 = pbox->y + MIN(42, pbox->height);
1989 y3 = MAX(pbox->y, y2 - 30);
1990 util_clip_triangle(x1, y1, x2, y2, x3, y3,
1993 pi = pan_item_new_tri(pw, NULL, x, y, w, h,
1994 x1, y1, x2, y2, x3, y3,
1995 PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA);
1996 pan_item_tri_border(pi, BORDER_1 | BORDER_3, PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
1997 pan_item_set_key(pi, "day_bubble");
1998 pan_item_added(pw, pi);
2000 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
2001 pan_item_added(pw, pbox);
2003 pan_layout_resize(pw, 100);
2006 static void pan_window_layout_compute_calendar(PanWindow *pw, const gchar *path, gint *width, gint *height)
2022 list = pan_window_layout_list(path, SORT_NONE, TRUE, pw->ignore_symlinks);
2024 if (pw->cache_list && pw->exif_date_enable)
2026 pw->cache_list = filelist_sort(pw->cache_list, SORT_NAME, TRUE);
2027 list = filelist_sort(list, SORT_NAME, TRUE);
2028 pan_cache_sync_date(pw, list);
2031 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2032 list = filelist_sort(list, SORT_TIME, TRUE);
2045 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2053 if (day_max < count) day_max = count;
2057 printf("biggest day contains %d images\n", day_max);
2059 grid = (gint)(sqrt((double)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
2060 day_width = MAX(PAN_CAL_DAY_WIDTH, grid);
2061 day_height = MAX(PAN_CAL_DAY_HEIGHT, grid);
2065 FileData *fd = list->data;
2067 year = date_value(fd->date, DATE_LENGTH_YEAR);
2068 month = date_value(fd->date, DATE_LENGTH_MONTH);
2071 work = g_list_last(list);
2074 FileData *fd = work->data;
2075 end_year = date_value(fd->date, DATE_LENGTH_YEAR);
2076 end_month = date_value(fd->date, DATE_LENGTH_MONTH);
2079 *width = PAN_FOLDER_BOX_BORDER * 2;
2080 *height = PAN_FOLDER_BOX_BORDER * 2;
2082 x = PAN_FOLDER_BOX_BORDER;
2083 y = PAN_FOLDER_BOX_BORDER;
2086 while (work && (year < end_year || (year == end_year && month <= end_month)))
2097 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
2099 days = date_value(dt, DATE_LENGTH_DAY);
2100 dt = date_to_time(year, month, 1);
2101 col = date_value(dt, DATE_LENGTH_WEEK);
2104 x = PAN_FOLDER_BOX_BORDER;
2106 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
2107 PAN_CAL_MONTH_BORDER,
2108 PAN_CAL_MONTH_COLOR, PAN_CAL_MONTH_ALPHA,
2109 PAN_CAL_MONTH_BORDER_COLOR, PAN_CAL_MONTH_ALPHA);
2110 buf = date_value_string(dt, DATE_LENGTH_MONTH);
2111 pi_text = pan_item_new_text(pw, x, y, buf,
2112 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2113 PAN_CAL_MONTH_TEXT_COLOR, 255);
2115 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
2117 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
2119 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
2120 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
2122 for (day = 1; day <= days; day++)
2129 dt = date_to_time(year, month, day);
2131 fd = g_new0(FileData, 1);
2132 /* path and name must be non NULL, so make them an invalid filename */
2133 fd->path = g_strdup("//");
2136 pi_day = pan_item_new_box(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
2138 PAN_CAL_DAY_COLOR, PAN_CAL_DAY_ALPHA,
2139 PAN_CAL_DAY_BORDER_COLOR, PAN_CAL_DAY_ALPHA);
2140 pan_item_set_key(pi_day, "day");
2142 dx = x + PAN_CAL_DOT_GAP * 2;
2143 dy = y + PAN_CAL_DOT_GAP * 2;
2145 fd = (work) ? work->data : NULL;
2146 while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
2150 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
2152 PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
2154 pan_item_set_key(pi, "dot");
2156 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2157 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
2159 dx = x + PAN_CAL_DOT_GAP * 2;
2160 dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2162 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
2164 /* must keep all dots within respective day even if it gets ugly */
2165 dy = y + PAN_CAL_DOT_GAP * 2;
2171 fd = (work) ? work->data : NULL;
2178 pi_day->color_r = MAX(pi_day->color_r - 61 - n * 3, 80);
2179 pi_day->color_g = pi_day->color_r;
2181 buf = g_strdup_printf("( %d )", n);
2182 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2183 PAN_CAL_DAY_TEXT_COLOR, 255);
2186 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
2187 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
2190 buf = g_strdup_printf("%d", day);
2191 pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2192 PAN_CAL_DAY_TEXT_COLOR, 255);
2196 pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
2203 x = PAN_FOLDER_BOX_BORDER;
2204 y += PAN_CAL_DAY_HEIGHT;
2208 x += PAN_CAL_DAY_WIDTH;
2212 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
2213 y += PAN_FOLDER_BOX_BORDER * 2;
2224 *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
2229 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
2237 PanItem *pi_month = NULL;
2238 PanItem *pi_day = NULL;
2244 list = pan_window_layout_list(path, SORT_NONE, TRUE, pw->ignore_symlinks);
2246 if (pw->cache_list && pw->exif_date_enable)
2248 pw->cache_list = filelist_sort(pw->cache_list, SORT_NAME, TRUE);
2249 list = filelist_sort(list, SORT_NAME, TRUE);
2250 pan_cache_sync_date(pw, list);
2253 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2254 list = filelist_sort(list, SORT_TIME, TRUE);
2256 *width = PAN_FOLDER_BOX_BORDER * 2;
2257 *height = PAN_FOLDER_BOX_BORDER * 2;
2262 day_start = month_start;
2277 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2282 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
2288 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
2292 x = PAN_FOLDER_BOX_BORDER;
2295 y = PAN_FOLDER_BOX_BORDER;
2297 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
2298 pi = pan_item_new_text(pw, x, y, buf,
2299 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2300 PAN_TEXT_COLOR, 255);
2304 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
2306 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2307 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2308 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2310 x += PAN_FOLDER_BOX_BORDER;
2311 y += PAN_FOLDER_BOX_BORDER;
2315 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
2327 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
2329 needle = needle->next;
2338 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
2339 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2340 PAN_TEXT_COLOR, 255);
2345 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
2346 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2347 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2348 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2350 x += PAN_FOLDER_BOX_BORDER;
2351 y += PAN_FOLDER_BOX_BORDER;
2355 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2357 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
2358 if (pi->width > x_width) x_width = pi->width;
2359 y_height = pi->height;
2363 pi = pan_item_new_thumb(pw, fd, x, y);
2364 x_width = PAN_THUMB_SIZE;
2365 y_height = PAN_THUMB_SIZE;
2368 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
2369 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
2374 if (total > 0 && count < PAN_GROUP_MAX)
2376 y += y_height + PAN_THUMB_GAP;
2380 x += x_width + PAN_THUMB_GAP;
2390 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
2396 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
2397 gint *width, gint *height,
2398 gint *scroll_x, gint *scroll_y)
2400 pan_window_items_free(pw);
2404 case LAYOUT_SIZE_THUMB_DOTS:
2405 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2406 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2408 case LAYOUT_SIZE_THUMB_NONE:
2409 pw->thumb_size = PAN_THUMB_SIZE_NONE;
2410 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2412 case LAYOUT_SIZE_THUMB_SMALL:
2413 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2414 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2416 case LAYOUT_SIZE_THUMB_NORMAL:
2418 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2419 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2421 case LAYOUT_SIZE_THUMB_LARGE:
2422 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2423 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2425 case LAYOUT_SIZE_10:
2426 pw->image_size = 10;
2427 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2429 case LAYOUT_SIZE_25:
2430 pw->image_size = 25;
2431 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2433 case LAYOUT_SIZE_33:
2434 pw->image_size = 33;
2435 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2437 case LAYOUT_SIZE_50:
2438 pw->image_size = 50;
2439 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2441 case LAYOUT_SIZE_100:
2442 pw->image_size = 100;
2443 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2456 pan_window_layout_compute_grid(pw, path, width, height);
2458 case LAYOUT_FOLDERS_LINEAR:
2459 pan_window_layout_compute_folders_linear(pw, path, width, height);
2461 case LAYOUT_FOLDERS_FLOWER:
2462 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2464 case LAYOUT_CALENDAR:
2465 pan_window_layout_compute_calendar(pw, path, width, height);
2467 case LAYOUT_TIMELINE:
2468 pan_window_layout_compute_timeline(pw, path, width, height);
2474 printf("computed %d objects\n", g_list_length(pw->list));
2477 static GList *pan_layout_intersect_l(GList *list, GList *item_list,
2478 gint x, gint y, gint width, gint height)
2486 gint rx, ry, rw, rh;
2491 if (util_clip_region(x, y, width, height,
2492 pi->x, pi->y, pi->width, pi->height,
2493 &rx, &ry, &rw, &rh))
2495 list = g_list_prepend(list, pi);
2502 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2508 grid = pw->list_grid;
2514 if (x < pg->x || x + width > pg->x + pg->w ||
2515 y < pg->y || y + height > pg->y + pg->h)
2521 list = pan_layout_intersect_l(list, pw->list, x, y, width, height);
2525 list = pan_layout_intersect_l(list, pg->list, x, y, width, height);
2529 list = pan_layout_intersect_l(list, pw->list_static, x, y, width, height);
2535 static void pan_layout_resize(PanWindow *pw, gint border)
2550 if (width < pi->x + pi->width) width = pi->x + pi->width;
2551 if (height < pi->y + pi->height) height = pi->y + pi->height;
2553 work = pw->list_static;
2561 if (width < pi->x + pi->width) width = pi->x + pi->width;
2562 if (height < pi->y + pi->height) height = pi->y + pi->height;
2568 pr = PIXBUF_RENDERER(pw->imd->pr);
2569 if (width < pr->window_width) width = pr->window_width;
2570 if (height < pr->window_width) height = pr->window_height;
2572 pixbuf_renderer_set_tiles_size(PIXBUF_RENDERER(pw->imd->pr), width, height);
2576 *-----------------------------------------------------------------------------
2578 *-----------------------------------------------------------------------------
2581 static gint pan_layout_queue_step(PanWindow *pw);
2584 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2586 PanWindow *pw = data;
2594 pw->queue_pi = NULL;
2598 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2599 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2602 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2606 thumb_loader_free(pw->tl);
2609 while (pan_layout_queue_step(pw));
2612 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2614 PanWindow *pw = data;
2622 pw->queue_pi = NULL;
2626 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2627 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2628 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2630 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2631 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2632 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2637 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2638 (GdkInterpType)zoom_quality);
2639 g_object_unref(tmp);
2643 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2647 image_loader_free(pw->il);
2650 while (pan_layout_queue_step(pw));
2654 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2655 guint width, guint height, gpointer data)
2657 PanWindow *pw = data;
2668 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2669 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2673 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2679 static gint pan_layout_queue_step(PanWindow *pw)
2683 if (!pw->queue) return FALSE;
2685 pi = pw->queue->data;
2686 pw->queue = g_list_remove(pw->queue, pi);
2689 if (!pw->queue_pi->fd)
2691 pw->queue_pi->queued = FALSE;
2692 pw->queue_pi = NULL;
2696 image_loader_free(pw->il);
2698 thumb_loader_free(pw->tl);
2701 if (pi->type == ITEM_IMAGE)
2703 pw->il = image_loader_new(pi->fd->path);
2705 if (pw->size != LAYOUT_SIZE_100)
2707 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2711 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2713 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2715 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2717 image_loader_free(pw->il);
2720 else if (pi->type == ITEM_THUMB)
2722 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2724 if (!pw->tl->standard_loader)
2726 /* The classic loader will recreate a thumbnail any time we
2727 * request a different size than what exists. This view will
2728 * almost never use the user configured sizes so disable cache.
2730 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2733 thumb_loader_set_callbacks(pw->tl,
2734 pan_layout_queue_thumb_done_cb,
2735 pan_layout_queue_thumb_done_cb,
2738 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2740 thumb_loader_free(pw->tl);
2744 pw->queue_pi->queued = FALSE;
2745 pw->queue_pi = NULL;
2749 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2751 if (!pi || pi->queued || pi->pixbuf) return;
2752 if (pw->size <= LAYOUT_SIZE_THUMB_NONE) return;
2755 pw->queue = g_list_prepend(pw->queue, pi);
2757 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2760 static gint pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
2761 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2763 PanWindow *pw = data;
2768 pixbuf_set_rect_fill(pixbuf,
2769 0, 0, width, height,
2770 PAN_BACKGROUND_COLOR, 255);
2772 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2774 gint rx, ry, rw, rh;
2776 if (util_clip_region(x, y, width, height,
2778 &rx, &ry, &rw, &rh))
2780 pixbuf_draw_rect_fill(pixbuf,
2781 rx - x, ry - y, rw, rh,
2782 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2785 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2787 gint rx, ry, rw, rh;
2789 if (util_clip_region(x, y, width, height,
2791 &rx, &ry, &rw, &rh))
2793 pixbuf_draw_rect_fill(pixbuf,
2794 rx - x, ry - y, rw, rh,
2795 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2799 list = pan_layout_intersect(pw, x, y, width, height);
2804 gint tx, ty, tw, th;
2805 gint rx, ry, rw, rh;
2812 if (pi->type == ITEM_THUMB && pi->pixbuf)
2814 tw = gdk_pixbuf_get_width(pi->pixbuf);
2815 th = gdk_pixbuf_get_height(pi->pixbuf);
2817 tx = pi->x + (pi->width - tw) / 2;
2818 ty = pi->y + (pi->height - th) / 2;
2820 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2822 if (util_clip_region(x, y, width, height,
2823 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2824 &rx, &ry, &rw, &rh))
2826 pixbuf_draw_shadow(pixbuf,
2827 rx - x, ry - y, rw, rh,
2828 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2830 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2835 if (util_clip_region(x, y, width, height,
2836 tx + tw, ty + PAN_SHADOW_OFFSET,
2837 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2838 &rx, &ry, &rw, &rh))
2840 pixbuf_draw_shadow(pixbuf,
2841 rx - x, ry - y, rw, rh,
2842 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2844 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2846 if (util_clip_region(x, y, width, height,
2847 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2848 &rx, &ry, &rw, &rh))
2850 pixbuf_draw_shadow(pixbuf,
2851 rx - x, ry - y, rw, rh,
2852 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2854 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2858 if (util_clip_region(x, y, width, height,
2860 &rx, &ry, &rw, &rh))
2862 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2865 1.0, 1.0, GDK_INTERP_NEAREST,
2869 if (util_clip_region(x, y, width, height,
2870 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2871 &rx, &ry, &rw, &rh))
2873 pixbuf_draw_rect_fill(pixbuf,
2874 rx - x, ry - y, rw, rh,
2875 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2877 if (util_clip_region(x, y, width, height,
2878 tx, ty, PAN_OUTLINE_THICKNESS, th,
2879 &rx, &ry, &rw, &rh))
2881 pixbuf_draw_rect_fill(pixbuf,
2882 rx - x, ry - y, rw, rh,
2883 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2885 if (util_clip_region(x, y, width, height,
2886 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2887 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2888 &rx, &ry, &rw, &rh))
2890 pixbuf_draw_rect_fill(pixbuf,
2891 rx - x, ry - y, rw, rh,
2892 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2894 if (util_clip_region(x, y, width, height,
2895 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2896 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2897 &rx, &ry, &rw, &rh))
2899 pixbuf_draw_rect_fill(pixbuf,
2900 rx - x, ry - y, rw, rh,
2901 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2904 else if (pi->type == ITEM_THUMB)
2906 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2907 th = pi->height - PAN_SHADOW_OFFSET * 2;
2908 tx = pi->x + PAN_SHADOW_OFFSET;
2909 ty = pi->y + PAN_SHADOW_OFFSET;
2911 if (util_clip_region(x, y, width, height,
2913 &rx, &ry, &rw, &rh))
2917 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2918 pixbuf_draw_rect_fill(pixbuf,
2919 rx - x, ry - y, rw, rh,
2921 PAN_SHADOW_ALPHA / d);
2924 pan_layout_queue(pw, pi);
2926 else if (pi->type == ITEM_IMAGE)
2928 if (util_clip_region(x, y, width, height,
2929 pi->x, pi->y, pi->width, pi->height,
2930 &rx, &ry, &rw, &rh))
2934 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2937 1.0, 1.0, GDK_INTERP_NEAREST,
2942 pixbuf_draw_rect_fill(pixbuf,
2943 rx - x, ry - y, rw, rh,
2944 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2945 pan_layout_queue(pw, pi);
2949 else if (pi->type == ITEM_BOX)
2963 if (pi->color_a > 254)
2965 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2966 shadow[0], bh - shadow[0],
2967 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2969 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2970 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2972 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2974 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2979 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2980 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2982 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2984 PAN_SHADOW_COLOR, a);
2988 if (util_clip_region(x, y, width, height,
2989 pi->x, pi->y, bw, bh,
2990 &rx, &ry, &rw, &rh))
2992 pixbuf_draw_rect_fill(pixbuf,
2993 rx - x, ry - y, rw, rh,
2994 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2996 if (util_clip_region(x, y, width, height,
2997 pi->x, pi->y, bw, pi->border,
2998 &rx, &ry, &rw, &rh))
3000 pixbuf_draw_rect_fill(pixbuf,
3001 rx - x, ry - y, rw, rh,
3002 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3004 if (util_clip_region(x, y, width, height,
3005 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
3006 &rx, &ry, &rw, &rh))
3008 pixbuf_draw_rect_fill(pixbuf,
3009 rx - x, ry - y, rw, rh,
3010 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3012 if (util_clip_region(x, y, width, height,
3013 pi->x + bw - pi->border, pi->y + pi->border,
3014 pi->border, bh - pi->border * 2,
3015 &rx, &ry, &rw, &rh))
3017 pixbuf_draw_rect_fill(pixbuf,
3018 rx - x, ry - y, rw, rh,
3019 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3021 if (util_clip_region(x, y, width, height,
3022 pi->x, pi->y + bh - pi->border,
3024 &rx, &ry, &rw, &rh))
3026 pixbuf_draw_rect_fill(pixbuf,
3027 rx - x, ry - y, rw, rh,
3028 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3031 else if (pi->type == ITEM_TRIANGLE)
3033 if (util_clip_region(x, y, width, height,
3034 pi->x, pi->y, pi->width, pi->height,
3035 &rx, &ry, &rw, &rh) && pi->data)
3037 gint *coord = pi->data;
3038 pixbuf_draw_triangle(pixbuf,
3039 rx - x, ry - y, rw, rh,
3040 coord[0] - x, coord[1] - y,
3041 coord[2] - x, coord[3] - y,
3042 coord[4] - x, coord[5] - y,
3043 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3045 if (pi->border & BORDER_1)
3047 pixbuf_draw_line(pixbuf,
3048 rx - x, ry - y, rw, rh,
3049 coord[0] - x, coord[1] - y,
3050 coord[2] - x, coord[3] - y,
3051 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3053 if (pi->border & BORDER_2)
3055 pixbuf_draw_line(pixbuf,
3056 rx - x, ry - y, rw, rh,
3057 coord[2] - x, coord[3] - y,
3058 coord[4] - x, coord[5] - y,
3059 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3061 if (pi->border & BORDER_3)
3063 pixbuf_draw_line(pixbuf,
3064 rx - x, ry - y, rw, rh,
3065 coord[4] - x, coord[5] - y,
3066 coord[0] - x, coord[1] - y,
3067 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
3071 else if (pi->type == ITEM_TEXT && pi->text)
3073 PangoLayout *layout;
3075 layout = pan_item_text_layout(pi, (GtkWidget *)pr);
3076 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
3077 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
3078 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
3079 g_object_unref(G_OBJECT(layout));
3085 if (x%512 == 0 && y%512 == 0)
3087 PangoLayout *layout;
3090 layout = gtk_widget_create_pango_layout((GtkWidget *)pr, NULL);
3092 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
3093 (x / pr->source_tile_width) +
3094 (y / pr->source_tile_height * (pr->image_width/pr->source_tile_width + 1)));
3095 pango_layout_set_text(layout, buf, -1);
3098 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr, 0, 0, 0, 0, 0, 255);
3100 g_object_unref(G_OBJECT(layout));
3107 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
3108 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
3110 PanWindow *pw = data;
3114 list = pan_layout_intersect(pw, x, y, width, height);
3123 if (pi->refcount > 0)
3127 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
3132 pw->queue = g_list_remove(pw->queue, pi);
3135 if (pw->queue_pi == pi) pw->queue_pi = NULL;
3138 g_object_unref(pi->pixbuf);
3150 *-----------------------------------------------------------------------------
3152 *-----------------------------------------------------------------------------
3155 static void pan_window_message(PanWindow *pw, const gchar *text)
3165 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
3169 work = pw->list_static;
3170 if (pw->layout == LAYOUT_CALENDAR)
3180 pi->type == ITEM_BOX &&
3181 pi->key && strcmp(pi->key, "dot") == 0)
3183 size += pi->fd->size;
3198 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
3200 size += pi->fd->size;
3206 ss = text_from_size_abrev(size);
3207 buf = g_strdup_printf(_("%d images, %s"), count, ss);
3209 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
3213 static void pan_warning_folder(const gchar *path, GtkWidget *parent)
3217 message = g_strdup_printf(_("The pan view does not support the folder \"%s\"."), path);
3218 warning_dialog(_("Folder not supported"), message,
3219 GTK_STOCK_DIALOG_INFO, parent);
3223 static void pan_window_zoom_limit(PanWindow *pw)
3229 case LAYOUT_SIZE_THUMB_DOTS:
3230 case LAYOUT_SIZE_THUMB_NONE:
3231 case LAYOUT_SIZE_THUMB_SMALL:
3232 case LAYOUT_SIZE_THUMB_NORMAL:
3234 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
3238 case LAYOUT_SIZE_THUMB_LARGE:
3241 case LAYOUT_SIZE_10:
3242 case LAYOUT_SIZE_25:
3245 case LAYOUT_SIZE_33:
3246 case LAYOUT_SIZE_50:
3247 case LAYOUT_SIZE_100:
3253 image_zoom_set_limits(pw->imd, min, 32.0);
3256 static gint pan_window_layout_update_idle_cb(gpointer data)
3258 PanWindow *pw = data;
3264 if (pw->size > LAYOUT_SIZE_THUMB_LARGE ||
3265 (pw->exif_date_enable && (pw->layout == LAYOUT_TIMELINE || pw->layout == LAYOUT_CALENDAR)))
3267 if (!pw->cache_list && !pw->cache_todo)
3269 pan_cache_fill(pw, pw->path);
3272 pan_window_message(pw, _("Reading image data..."));
3280 if (pw->cache_count == pw->cache_total)
3282 pan_window_message(pw, _("Sorting..."));
3284 else if (pw->cache_tick > 9)
3288 buf = g_strdup_printf("%s %d", _("Reading image data..."),
3289 pw->cache_total - pw->cache_count);
3290 pan_window_message(pw, buf);
3296 if (pan_cache_step(pw)) return TRUE;
3303 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3305 pan_window_zoom_limit(pw);
3307 if (width > 0 && height > 0)
3311 printf("Canvas size is %d x %d\n", width, height);
3313 pan_grid_build(pw, width, height, 1000);
3315 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
3316 PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
3317 pan_window_request_tile_cb,
3318 pan_window_dispose_tile_cb, pw, 1.0);
3320 if (scroll_x == 0 && scroll_y == 0)
3328 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
3331 pan_window_message(pw, NULL);
3337 static void pan_window_layout_update_idle(PanWindow *pw)
3339 if (pw->idle_id == -1)
3341 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3345 static void pan_window_layout_update(PanWindow *pw)
3347 pan_window_message(pw, _("Sorting images..."));
3348 pan_window_layout_update_idle(pw);
3351 static void pan_window_layout_set_path(PanWindow *pw, const gchar *path)
3355 if (strcmp(path, "/") == 0)
3357 pan_warning_folder(path, pw->window);
3362 pw->path = g_strdup(path);
3364 pan_window_layout_update(pw);
3368 *-----------------------------------------------------------------------------
3369 * pan window keyboard
3370 *-----------------------------------------------------------------------------
3373 static const gchar *pan_menu_click_path(PanWindow *pw)
3375 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3379 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3381 PanWindow *pw = data;
3383 gdk_window_get_origin(pw->imd->pr->window, x, y);
3384 popup_menu_position_clamp(menu, x, y, 0);
3387 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3389 PanWindow *pw = data;
3392 gint stop_signal = FALSE;
3398 pr = PIXBUF_RENDERER(pw->imd->pr);
3399 path = pan_menu_click_path(pw);
3401 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pw->imd->widget)));
3405 switch (event->keyval)
3407 case GDK_Left: case GDK_KP_Left:
3411 case GDK_Right: case GDK_KP_Right:
3415 case GDK_Up: case GDK_KP_Up:
3419 case GDK_Down: case GDK_KP_Down:
3423 case GDK_Page_Up: case GDK_KP_Page_Up:
3424 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
3426 case GDK_Page_Down: case GDK_KP_Page_Down:
3427 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
3429 case GDK_Home: case GDK_KP_Home:
3430 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
3432 case GDK_End: case GDK_KP_End:
3433 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
3438 if (focused && !(event->state & GDK_CONTROL_MASK) )
3439 switch (event->keyval)
3441 case '+': case '=': case GDK_KP_Add:
3442 pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
3444 case '-': case GDK_KP_Subtract:
3445 pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
3447 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3448 pixbuf_renderer_zoom_set(pr, 1.0);
3451 pixbuf_renderer_zoom_set(pr, 2.0);
3454 pixbuf_renderer_zoom_set(pr, 3.0);
3457 pixbuf_renderer_zoom_set(pr, 4.0);
3460 pixbuf_renderer_zoom_set(pr, -4.0);
3463 pixbuf_renderer_zoom_set(pr, -3.0);
3466 pixbuf_renderer_zoom_set(pr, -2.0);
3470 pan_fullscreen_toggle(pw, FALSE);
3475 pan_overlay_toggle(pw);
3478 case GDK_Delete: case GDK_KP_Delete:
3483 if (GTK_WIDGET_VISIBLE(pw->search_box))
3485 gtk_widget_grab_focus(pw->search_entry);
3489 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3497 pan_fullscreen_toggle(pw, TRUE);
3500 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3502 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3508 menu = pan_popup_menu(pw);
3509 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3514 if (event->state & GDK_CONTROL_MASK)
3517 switch (event->keyval)
3550 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3553 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3556 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3559 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3562 if (path) info_window_new(path, NULL);
3565 pan_window_close(pw);
3568 if (n != -1 && path)
3570 if (!editor_window_flag_set(n))
3572 pan_fullscreen_toggle(pw, TRUE);
3574 start_editor_from_file(n, path);
3578 else if (event->state & GDK_SHIFT_MASK)
3585 switch (event->keyval)
3590 pan_fullscreen_toggle(pw, TRUE);
3593 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3595 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
3596 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3605 if (x != 0 || y!= 0)
3607 keyboard_scroll_calc(&x, &y, event);
3608 pixbuf_renderer_scroll(pr, x, y);
3615 *-----------------------------------------------------------------------------
3617 *-----------------------------------------------------------------------------
3620 static void pan_info_update(PanWindow *pw, PanItem *pi)
3626 gint x1, y1, x2, y2, x3, y3;
3629 if (pw->click_pi == pi) return;
3630 if (pi && !pi->fd) pi = NULL;
3632 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3637 if (debug) printf("info set to %s\n", pi->fd->path);
3639 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3641 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3642 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3643 pan_item_set_key(pbox, "info");
3645 if (pi->type == ITEM_THUMB && pi->pixbuf)
3647 w = gdk_pixbuf_get_width(pi->pixbuf);
3648 h = gdk_pixbuf_get_height(pi->pixbuf);
3650 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3651 y1 = pi->y + (pi->height - h) / 2 + 8;
3655 x1 = pi->x + pi->width - 8;
3663 util_clip_triangle(x1, y1, x2, y2, x3, y3,
3666 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3667 x1, y1, x2, y2, x3, y3,
3668 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3669 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3670 pan_item_set_key(p, "info");
3671 pan_item_added(pw, p);
3673 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3674 _("Filename:"), TEXT_ATTR_BOLD,
3675 PAN_POPUP_TEXT_COLOR, 255);
3676 pan_item_set_key(plabel, "info");
3677 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3678 pi->fd->name, TEXT_ATTR_NONE,
3679 PAN_POPUP_TEXT_COLOR, 255);
3680 pan_item_set_key(p, "info");
3681 pan_item_size_by_item(pbox, p, 0);
3683 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3684 _("Date:"), TEXT_ATTR_BOLD,
3685 PAN_POPUP_TEXT_COLOR, 255);
3686 pan_item_set_key(plabel, "info");
3687 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3688 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3689 PAN_POPUP_TEXT_COLOR, 255);
3690 pan_item_set_key(p, "info");
3691 pan_item_size_by_item(pbox, p, 0);
3693 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3694 _("Size:"), TEXT_ATTR_BOLD,
3695 PAN_POPUP_TEXT_COLOR, 255);
3696 pan_item_set_key(plabel, "info");
3697 buf = text_from_size(pi->fd->size);
3698 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3699 buf, TEXT_ATTR_NONE,
3700 PAN_POPUP_TEXT_COLOR, 255);
3702 pan_item_set_key(p, "info");
3703 pan_item_size_by_item(pbox, p, 0);
3705 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3706 pan_item_added(pw, pbox);
3711 *-----------------------------------------------------------------------------
3713 *-----------------------------------------------------------------------------
3716 static void pan_search_status(PanWindow *pw, const gchar *text)
3718 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3721 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3729 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3731 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3732 if (!list) return FALSE;
3734 found = g_list_find(list, pw->click_pi);
3735 if (found && found->next)
3737 found = found->next;
3745 pan_info_update(pw, pi);
3746 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3748 buf = g_strdup_printf("%s ( %d / %d )",
3749 (path[0] == '/') ? _("path found") : _("filename found"),
3750 g_list_index(list, pi) + 1,
3751 g_list_length(list));
3752 pan_search_status(pw, buf);
3760 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3768 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3770 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3771 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3776 needle = g_utf8_strdown(text, -1);
3777 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3780 if (!list) return FALSE;
3782 found = g_list_find(list, pw->click_pi);
3783 if (found && found->next)
3785 found = found->next;
3793 pan_info_update(pw, pi);
3794 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3796 buf = g_strdup_printf("%s ( %d / %d )",
3798 g_list_index(list, pi) + 1,
3799 g_list_length(list));
3800 pan_search_status(pw, buf);
3808 static gint valid_date_separator(gchar c)
3810 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3813 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
3814 gint year, gint month, gint day,
3820 work = g_list_last(pw->list_static);
3828 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
3829 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
3833 tl = localtime(&pi->fd->date);
3838 match = (tl->tm_year == year - 1900);
3839 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3840 if (match && day > 0) match = (tl->tm_mday == day);
3842 if (match) list = g_list_prepend(list, pi);
3847 return g_list_reverse(list);
3850 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3866 if (!text) return FALSE;
3868 ptr = (gchar *)text;
3869 while (*ptr != '\0')
3871 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3876 if (t == -1) return FALSE;
3878 if (!lt) return FALSE;
3880 if (valid_date_separator(*text))
3883 mptr = (gchar *)text;
3887 year = (gint)strtol(text, &mptr, 10);
3888 if (mptr == text) return FALSE;
3891 if (*mptr != '\0' && valid_date_separator(*mptr))
3896 month = strtol(mptr, &dptr, 10);
3899 if (valid_date_separator(*dptr))
3901 month = lt->tm_mon + 1;
3909 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3913 day = strtol(dptr, &eptr, 10);
3923 year = lt->tm_year + 1900;
3925 else if (year < 100)
3934 month < -1 || month == 0 || month > 12 ||
3935 day < -1 || day == 0 || day > 31) return FALSE;
3937 t = date_to_time(year, month, day);
3938 if (t < 0) return FALSE;
3940 if (pw->layout == LAYOUT_CALENDAR)
3942 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
3948 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3949 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
3954 found = g_list_find(list, pw->search_pi);
3955 if (found && found->next)
3957 found = found->next;
3968 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
3970 pan_info_update(pw, NULL);
3971 pan_calendar_update(pw, pi);
3972 image_scroll_to_point(pw->imd,
3973 pi->x + pi->width / 2,
3974 pi->y + pi->height / 2, 0.5, 0.5);
3978 pan_info_update(pw, pi);
3979 image_scroll_to_point(pw->imd,
3980 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3986 buf = date_value_string(t, DATE_LENGTH_MONTH);
3991 buf = g_strdup_printf("%d %s", day, tmp);
3997 buf = date_value_string(t, DATE_LENGTH_YEAR);
4002 buf_count = g_strdup_printf("( %d / %d )",
4003 g_list_index(list, pi) + 1,
4004 g_list_length(list));
4008 buf_count = g_strdup_printf("(%s)", _("no match"));
4011 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
4014 pan_search_status(pw, message);
4022 static void pan_search_activate_cb(const gchar *text, gpointer data)
4024 PanWindow *pw = data;
4028 tab_completion_append_to_history(pw->search_entry, text);
4030 if (pan_search_by_path(pw, text)) return;
4032 if ((pw->layout == LAYOUT_TIMELINE ||
4033 pw->layout == LAYOUT_CALENDAR) &&
4034 pan_search_by_date(pw, text))
4039 if (pan_search_by_partial(pw, text)) return;
4041 pan_search_status(pw, _("no match"));
4044 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
4046 PanWindow *pw = data;
4049 visible = GTK_WIDGET_VISIBLE(pw->search_box);
4050 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
4054 gtk_widget_hide(pw->search_box);
4055 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
4059 gtk_widget_show(pw->search_box);
4060 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
4061 gtk_widget_grab_focus(pw->search_entry);
4067 *-----------------------------------------------------------------------------
4068 * view window main routines
4069 *-----------------------------------------------------------------------------
4072 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
4074 PanWindow *pw = data;
4082 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
4083 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
4086 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
4089 switch (event->button)
4092 pan_info_update(pw, pi);
4094 if (!pi && pw->layout == LAYOUT_CALENDAR)
4096 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
4097 pan_calendar_update(pw, pi);
4103 pan_info_update(pw, pi);
4104 menu = pan_popup_menu(pw);
4105 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
4112 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
4115 PanWindow *pw = data;
4122 if (!(event->state & GDK_SHIFT_MASK))
4128 if (event->state & GDK_CONTROL_MASK)
4130 switch (event->direction)
4133 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
4134 (gint)event->x, (gint)event->y);
4136 case GDK_SCROLL_DOWN:
4137 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
4138 (gint)event->x, (gint)event->y);
4146 switch (event->direction)
4149 pixbuf_renderer_scroll(pr, 0, -h);
4151 case GDK_SCROLL_DOWN:
4152 pixbuf_renderer_scroll(pr, 0, h);
4154 case GDK_SCROLL_LEFT:
4155 pixbuf_renderer_scroll(pr, -w, 0);
4157 case GDK_SCROLL_RIGHT:
4158 pixbuf_renderer_scroll(pr, w, 0);
4166 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
4168 g_signal_connect(G_OBJECT(imd->pr), "clicked",
4169 G_CALLBACK(button_cb), pw);
4170 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
4171 G_CALLBACK(scroll_cb), pw);
4174 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
4176 PanWindow *pw = data;
4179 pw->imd = pw->imd_normal;
4182 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
4184 if (force_off && !pw->fs) return;
4188 fullscreen_stop(pw->fs);
4192 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
4193 pan_image_set_buttons(pw, pw->fs->imd);
4194 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
4195 G_CALLBACK(pan_window_key_press_cb), pw);
4197 pw->imd = pw->fs->imd;
4201 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
4203 PanWindow *pw = data;
4206 text = image_zoom_get_as_text(pw->imd);
4207 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4211 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
4213 PanWindow *pw = data;
4218 if (pr->scale == 0.0) return;
4220 pixbuf_renderer_get_visible_rect(pr, &rect);
4221 pixbuf_renderer_get_image_size(pr, &width, &height);
4223 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4224 adj->page_size = (gdouble)rect.width;
4225 adj->page_increment = adj->page_size / 2.0;
4226 adj->step_increment = 48.0 / pr->scale;
4228 adj->upper = MAX((gdouble)width, 1.0);
4229 adj->value = (gdouble)rect.x;
4231 pref_signal_block_data(pw->scrollbar_h, pw);
4232 gtk_adjustment_changed(adj);
4233 gtk_adjustment_value_changed(adj);
4234 pref_signal_unblock_data(pw->scrollbar_h, pw);
4236 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4237 adj->page_size = (gdouble)rect.height;
4238 adj->page_increment = adj->page_size / 2.0;
4239 adj->step_increment = 48.0 / pr->scale;
4241 adj->upper = MAX((gdouble)height, 1.0);
4242 adj->value = (gdouble)rect.y;
4244 pref_signal_block_data(pw->scrollbar_v, pw);
4245 gtk_adjustment_changed(adj);
4246 gtk_adjustment_value_changed(adj);
4247 pref_signal_unblock_data(pw->scrollbar_v, pw);
4250 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4252 PanWindow *pw = data;
4256 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4258 if (!pr->scale) return;
4260 x = (gint)gtk_range_get_value(range);
4262 pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
4265 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4267 PanWindow *pw = data;
4271 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4273 if (!pr->scale) return;
4275 y = (gint)gtk_range_get_value(range);
4277 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
4280 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4282 PanWindow *pw = data;
4284 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4285 pan_window_layout_update(pw);
4288 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4290 PanWindow *pw = data;
4292 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4293 pan_window_layout_update(pw);
4297 static void pan_window_date_toggle_cb(GtkWidget *button, gpointer data)
4299 PanWindow *pw = data;
4301 pw->exif_date_enable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4302 pan_window_layout_update(pw);
4306 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4308 PanWindow *pw = data;
4311 path = remove_trailing_slash(new_text);
4312 parse_out_relatives(path);
4316 warning_dialog(_("Folder not found"),
4317 _("The entered path is not a folder"),
4318 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4322 tab_completion_append_to_history(pw->path_entry, path);
4324 pan_window_layout_set_path(pw, path);
4330 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4332 PanWindow *pw = data;
4335 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4337 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4338 pan_window_entry_activate_cb(text, pw);
4342 static void pan_window_close(PanWindow *pw)
4344 pan_window_list = g_list_remove(pan_window_list, pw);
4346 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_EXIF_DATE, pw->exif_date_enable);
4348 if (pw->idle_id != -1)
4350 g_source_remove(pw->idle_id);
4353 pan_fullscreen_toggle(pw, TRUE);
4354 gtk_widget_destroy(pw->window);
4356 pan_window_items_free(pw);
4364 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4366 PanWindow *pw = data;
4368 pan_window_close(pw);
4372 static void pan_window_new_real(const gchar *path)
4381 GdkGeometry geometry;
4383 pw = g_new0(PanWindow, 1);
4385 pw->path = g_strdup(path);
4386 pw->layout = LAYOUT_TIMELINE;
4387 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4388 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4389 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4391 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_EXIF_DATE, &pw->exif_date_enable))
4393 pw->exif_date_enable = FALSE;
4396 pw->ignore_symlinks = TRUE;
4399 pw->list_static = NULL;
4400 pw->list_grid = NULL;
4403 pw->overlay_id = -1;
4406 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4408 geometry.min_width = 8;
4409 geometry.min_height = 8;
4410 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4412 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4413 gtk_window_set_title (GTK_WINDOW(pw->window), _("Pan View - GQview"));
4414 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4415 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4417 window_set_icon(pw->window, NULL, NULL);
4419 vbox = gtk_vbox_new(FALSE, 0);
4420 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4421 gtk_widget_show(vbox);
4423 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4425 pref_spacer(box, 0);
4426 pref_label_new(box, _("Location:"));
4427 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view_path", -1,
4428 pan_window_entry_activate_cb, pw);
4429 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4430 G_CALLBACK(pan_window_entry_change_cb), pw);
4431 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4432 gtk_widget_show(combo);
4434 combo = gtk_combo_box_new_text();
4435 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4436 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4437 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4438 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4439 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4441 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4442 g_signal_connect(G_OBJECT(combo), "changed",
4443 G_CALLBACK(pan_window_layout_change_cb), pw);
4444 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4445 gtk_widget_show(combo);
4447 combo = gtk_combo_box_new_text();
4448 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4449 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4450 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4451 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4452 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4453 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4454 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4455 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4456 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4457 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4459 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4460 g_signal_connect(G_OBJECT(combo), "changed",
4461 G_CALLBACK(pan_window_layout_size_cb), pw);
4462 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4463 gtk_widget_show(combo);
4465 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4466 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4467 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4469 pw->imd = image_new(TRUE);
4470 pw->imd_normal = pw->imd;
4472 g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
4473 G_CALLBACK(pan_window_image_zoom_cb), pw);
4474 g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
4475 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4477 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4478 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4479 gtk_widget_show(GTK_WIDGET(pw->imd->widget));
4481 pan_window_dnd_init(pw);
4483 pan_image_set_buttons(pw, pw->imd);
4485 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4486 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4487 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4488 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4489 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4490 gtk_widget_show(pw->scrollbar_h);
4492 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4493 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4494 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4495 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4496 0, GTK_FILL | GTK_EXPAND, 0, 0);
4497 gtk_widget_show(pw->scrollbar_v);
4501 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4502 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4504 pref_spacer(pw->search_box, 0);
4505 pref_label_new(pw->search_box, _("Find:"));
4507 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4508 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4509 gtk_widget_show(hbox);
4511 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4512 pan_search_activate_cb, pw);
4513 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4514 gtk_widget_show(combo);
4516 pw->search_label = gtk_label_new("");
4517 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4518 gtk_widget_show(pw->search_label);
4522 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4524 frame = gtk_frame_new(NULL);
4525 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4526 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4527 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4528 gtk_widget_show(frame);
4530 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4531 gtk_container_add(GTK_CONTAINER(frame), hbox);
4532 gtk_widget_show(hbox);
4534 pref_spacer(hbox, 0);
4535 pw->label_message = pref_label_new(hbox, "");
4537 frame = gtk_frame_new(NULL);
4538 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4539 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4540 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4541 gtk_widget_show(frame);
4543 pw->label_zoom = gtk_label_new("");
4544 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4545 gtk_widget_show(pw->label_zoom);
4548 pw->date_button = pref_checkbox_new(box, _("Use Exif date"), pw->exif_date_enable,
4549 G_CALLBACK(pan_window_date_toggle_cb), pw);
4552 pw->search_button = gtk_toggle_button_new();
4553 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4554 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4555 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4556 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4557 gtk_widget_show(hbox);
4558 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4559 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4560 gtk_widget_show(pw->search_button_arrow);
4561 pref_label_new(hbox, _("Find"));
4563 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4564 gtk_widget_show(pw->search_button);
4565 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4566 G_CALLBACK(pan_search_toggle_cb), pw);
4568 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4569 G_CALLBACK(pan_window_delete_cb), pw);
4570 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4571 G_CALLBACK(pan_window_key_press_cb), pw);
4573 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4575 pan_window_layout_update(pw);
4577 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4578 gtk_widget_show(pw->window);
4580 pan_window_list = g_list_append(pan_window_list, pw);
4584 *-----------------------------------------------------------------------------
4585 * peformance warnings
4586 *-----------------------------------------------------------------------------
4589 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4593 generic_dialog_close(gd);
4595 pan_window_new_real(path);
4599 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4603 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4604 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4607 static gint pan_warning(const gchar *path)
4613 GtkWidget *ct_button;
4616 if (path && strcmp(path, "/") == 0)
4618 pan_warning_folder(path, NULL);
4622 if (enable_thumb_caching &&
4623 thumbnail_spec_standard) return FALSE;
4625 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4626 if (hide_dlg) return FALSE;
4628 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4630 gd->data = g_strdup(path);
4631 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4632 pan_warning_ok_cb, TRUE);
4634 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4635 _("Pan view performance may be poor."),
4636 _("To improve performance of thumbnails in the pan view the"
4637 " following options can be enabled. Note that both options"
4638 " must be enabled to notice a change in performance."));
4640 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4641 pref_spacer(group, PREF_PAD_INDENT);
4642 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4644 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4645 enable_thumb_caching, &enable_thumb_caching);
4646 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4647 thumbnail_spec_standard, &thumbnail_spec_standard);
4648 pref_checkbox_link_sensitivity(ct_button, button);
4652 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4653 G_CALLBACK(pan_warning_hide_cb), NULL);
4655 gtk_widget_show(gd->dialog);
4662 *-----------------------------------------------------------------------------
4664 *-----------------------------------------------------------------------------
4667 void pan_window_new(const gchar *path)
4669 if (pan_warning(path)) return;
4671 pan_window_new_real(path);
4675 *-----------------------------------------------------------------------------
4677 *-----------------------------------------------------------------------------
4680 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4682 PanWindow *pw = data;
4685 path = pan_menu_click_path(pw);
4688 pan_fullscreen_toggle(pw, TRUE);
4689 view_window_new(path);
4693 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4699 pw = submenu_item_get_data(widget);
4700 n = GPOINTER_TO_INT(data);
4703 path = pan_menu_click_path(pw);
4706 if (!editor_window_flag_set(n))
4708 pan_fullscreen_toggle(pw, TRUE);
4710 start_editor_from_file(n, path);
4714 static void pan_info_cb(GtkWidget *widget, gpointer data)
4716 PanWindow *pw = data;
4719 path = pan_menu_click_path(pw);
4720 if (path) info_window_new(path, NULL);
4723 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4725 PanWindow *pw = data;
4727 image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
4730 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4732 PanWindow *pw = data;
4734 image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
4737 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4739 PanWindow *pw = data;
4741 image_zoom_set(pw->imd, 1.0);
4744 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4746 PanWindow *pw = data;
4749 path = pan_menu_click_path(pw);
4750 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4753 static void pan_move_cb(GtkWidget *widget, gpointer data)
4755 PanWindow *pw = data;
4758 path = pan_menu_click_path(pw);
4759 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4762 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4764 PanWindow *pw = data;
4767 path = pan_menu_click_path(pw);
4768 if (path) file_util_rename(path, NULL, pw->imd->widget);
4771 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4773 PanWindow *pw = data;
4776 path = pan_menu_click_path(pw);
4777 if (path) file_util_delete(path, NULL, pw->imd->widget);
4780 static void pan_exif_date_toggle_cb(GtkWidget *widget, gpointer data)
4782 PanWindow *pw = data;
4784 pw->exif_date_enable = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
4785 pan_window_layout_update(pw);
4788 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4790 PanWindow *pw = data;
4792 pan_fullscreen_toggle(pw, FALSE);
4795 static void pan_close_cb(GtkWidget *widget, gpointer data)
4797 PanWindow *pw = data;
4799 pan_window_close(pw);
4802 static GtkWidget *pan_popup_menu(PanWindow *pw)
4808 active = (pw->click_pi != NULL);
4810 menu = popup_menu_short_lived();
4812 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4813 G_CALLBACK(pan_zoom_in_cb), pw);
4814 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4815 G_CALLBACK(pan_zoom_out_cb), pw);
4816 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4817 G_CALLBACK(pan_zoom_1_1_cb), pw);
4818 menu_item_add_divider(menu);
4820 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4821 gtk_widget_set_sensitive(item, active);
4823 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4824 G_CALLBACK(pan_info_cb), pw);
4826 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4827 G_CALLBACK(pan_new_window_cb), pw);
4829 menu_item_add_divider(menu);
4830 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4831 G_CALLBACK(pan_copy_cb), pw);
4832 menu_item_add_sensitive(menu, _("_Move..."), active,
4833 G_CALLBACK(pan_move_cb), pw);
4834 menu_item_add_sensitive(menu, _("_Rename..."), active,
4835 G_CALLBACK(pan_rename_cb), pw);
4836 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4837 G_CALLBACK(pan_delete_cb), pw);
4839 menu_item_add_divider(menu);
4840 item = menu_item_add_check(menu, _("Sort by E_xif date"), pw->exif_date_enable,
4841 G_CALLBACK(pan_exif_date_toggle_cb), pw);
4842 gtk_widget_set_sensitive(item, (pw->layout == LAYOUT_TIMELINE || pw->layout == LAYOUT_CALENDAR));
4844 menu_item_add_divider(menu);
4848 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4852 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4855 menu_item_add_divider(menu);
4856 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4862 *-----------------------------------------------------------------------------
4864 *-----------------------------------------------------------------------------
4867 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4869 GtkSelectionData *selection_data, guint info,
4870 guint time, gpointer data)
4872 PanWindow *pw = data;
4874 if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
4876 if (info == TARGET_URI_LIST)
4880 list = uri_list_from_text((gchar *)selection_data->data, TRUE);
4881 if (list && isdir((gchar *)list->data))
4883 gchar *path = list->data;
4885 pan_window_layout_set_path(pw, path);
4888 path_list_free(list);
4892 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4893 GtkSelectionData *selection_data, guint info,
4894 guint time, gpointer data)
4896 PanWindow *pw = data;
4899 path = pan_menu_click_path(pw);
4909 case TARGET_URI_LIST:
4912 case TARGET_TEXT_PLAIN:
4917 list = g_list_append(NULL, (gchar *)path);
4918 text = uri_text_from_list(list, &len, plain_text);
4922 gtk_selection_data_set (selection_data, selection_data->target,
4923 8, (guchar *)text, len);
4929 gtk_selection_data_set (selection_data, selection_data->target,
4934 static void pan_window_dnd_init(PanWindow *pw)
4938 widget = pw->imd->pr;
4940 gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
4941 dnd_file_drag_types, dnd_file_drag_types_count,
4942 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4943 g_signal_connect(G_OBJECT(widget), "drag_data_get",
4944 G_CALLBACK(pan_window_set_dnd_data), pw);
4946 gtk_drag_dest_set(widget,
4947 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4948 dnd_file_drop_types, dnd_file_drop_types_count,
4949 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4950 g_signal_connect(G_OBJECT(widget), "drag_data_received",
4951 G_CALLBACK(pan_window_get_dnd_data), pw);
4955 *-----------------------------------------------------------------------------
4956 * maintenance (for rename, move, remove)
4957 *-----------------------------------------------------------------------------