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"
129 #define SORT_BY_EXIF_DATE 1
135 LAYOUT_FOLDERS_LINEAR,
136 LAYOUT_FOLDERS_FLOWER,
141 LAYOUT_SIZE_THUMB_DOTS = 0,
142 LAYOUT_SIZE_THUMB_NONE,
143 LAYOUT_SIZE_THUMB_SMALL,
144 LAYOUT_SIZE_THUMB_NORMAL,
145 LAYOUT_SIZE_THUMB_LARGE,
164 TEXT_ATTR_BOLD = 1 << 0,
165 TEXT_ATTR_HEADING = 1 << 1,
166 TEXT_ATTR_MARKUP = 1 << 2
177 typedef struct _PanItem PanItem;
192 TextAttrType text_attr;
210 typedef struct _PanWindow PanWindow;
215 ImageWindow *imd_normal;
218 GtkWidget *path_entry;
220 GtkWidget *label_message;
221 GtkWidget *label_zoom;
223 GtkWidget *search_box;
224 GtkWidget *search_entry;
225 GtkWidget *search_label;
226 GtkWidget *search_button;
227 GtkWidget *search_button_arrow;
229 GtkWidget *scrollbar_h;
230 GtkWidget *scrollbar_v;
250 CacheLoader *cache_cl;
263 typedef struct _PanGrid PanGrid;
272 typedef struct _PanCacheData PanCacheData;
273 struct _PanCacheData {
279 static GList *pan_window_list = NULL;
282 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend);
284 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
286 static void pan_window_layout_update_idle(PanWindow *pw);
288 static GtkWidget *pan_popup_menu(PanWindow *pw);
289 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off);
291 static void pan_window_close(PanWindow *pw);
293 static void pan_window_dnd_init(PanWindow *pw);
305 static gint date_compare(time_t a, time_t b, DateLengthType length)
310 if (length == DATE_LENGTH_EXACT) return (a == b);
312 if (!localtime_r(&a, &ta) ||
313 !localtime_r(&b, &tb)) return FALSE;
315 if (ta.tm_year != tb.tm_year) return FALSE;
316 if (length == DATE_LENGTH_YEAR) return TRUE;
318 if (ta.tm_mon != tb.tm_mon) return FALSE;
319 if (length == DATE_LENGTH_MONTH) return TRUE;
321 if (length == DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
323 if (ta.tm_mday != tb.tm_mday) return FALSE;
324 if (length == DATE_LENGTH_DAY) return TRUE;
326 return (ta.tm_hour == tb.tm_hour);
329 static gint date_value(time_t d, DateLengthType length)
333 if (!localtime_r(&d, &td)) return -1;
337 case DATE_LENGTH_DAY:
340 case DATE_LENGTH_WEEK:
343 case DATE_LENGTH_MONTH:
344 return td.tm_mon + 1;
346 case DATE_LENGTH_YEAR:
347 return td.tm_year + 1900;
349 case DATE_LENGTH_EXACT:
357 static gchar *date_value_string(time_t d, DateLengthType length)
361 gchar *format = NULL;
363 if (!localtime_r(&d, &td)) return g_strdup("");
367 case DATE_LENGTH_DAY:
368 return g_strdup_printf("%d", td.tm_mday);
370 case DATE_LENGTH_WEEK:
373 case DATE_LENGTH_MONTH:
376 case DATE_LENGTH_YEAR:
377 return g_strdup_printf("%d", td.tm_year + 1900);
379 case DATE_LENGTH_EXACT:
381 return g_strdup(text_from_time(d));
386 if (format && strftime(buf, sizeof(buf), format, &td) > 0)
388 gchar *ret = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
395 static time_t date_to_time(gint year, gint month, gint day)
402 lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
403 lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
404 lt.tm_year = year - 1900;
411 *-----------------------------------------------------------------------------
413 *-----------------------------------------------------------------------------
416 static void pan_cache_free(PanWindow *pw)
420 work = pw->cache_list;
428 cache_sim_data_free(pc->cd);
429 file_data_free((FileData *)pc);
432 g_list_free(pw->cache_list);
433 pw->cache_list = NULL;
435 filelist_free(pw->cache_todo);
436 pw->cache_todo = NULL;
442 cache_loader_free(pw->cache_cl);
446 static void pan_cache_fill(PanWindow *pw, const gchar *path)
452 list = pan_window_layout_list(path, SORT_NAME, TRUE);
453 pw->cache_todo = g_list_reverse(list);
455 pw->cache_total = g_list_length(pw->cache_todo);
458 static void pan_cache_step_done_cb(CacheLoader *cl, gint error, gpointer data)
460 PanWindow *pw = data;
465 pc = pw->cache_list->data;
474 cache_loader_free(cl);
477 pan_window_layout_update_idle(pw);
480 static gint pan_cache_step(PanWindow *pw)
484 CacheDataType load_mask;
486 if (!pw->cache_todo) return TRUE;
488 fd = pw->cache_todo->data;
489 pw->cache_todo = g_list_remove(pw->cache_todo, fd);
492 if (enable_thumb_caching)
496 found = cache_find_location(CACHE_TYPE_SIM, fd->path);
497 if (found && filetime(found) == fd->date)
499 cd = cache_sim_data_load(found);
504 if (!cd) cd = cache_sim_data_new();
508 cd->dimensions = image_load_dimensions(fd->path, &cd->width, &cd->height);
509 if (enable_thumb_caching &&
515 base = cache_get_location(CACHE_TYPE_SIM, fd->path, FALSE, &mode);
516 if (cache_ensure_dir_exists(base, mode))
519 cd->path = cache_get_location(CACHE_TYPE_SIM, fd->path, TRUE, NULL);
520 if (cache_sim_data_save(cd))
522 filetime_set(cd->path, filetime(fd->path));
531 pc = g_new0(PanCacheData, 1);
532 memcpy(pc, fd, sizeof(FileData));
537 pw->cache_list = g_list_prepend(pw->cache_list, pc);
539 cache_loader_free(pw->cache_cl);
541 load_mask = CACHE_LOADER_NONE;
542 if (pw->size > LAYOUT_SIZE_THUMB_LARGE) load_mask |= CACHE_LOADER_DIMENSIONS;
543 if (SORT_BY_EXIF_DATE) load_mask |= CACHE_LOADER_DATE;
544 pw->cache_cl = cache_loader_new(((FileData *)pc)->path, load_mask,
545 pan_cache_step_done_cb, pw);
546 return (pw->cache_cl == NULL);
549 /* This sync date function is optimized for lists with a common sort */
550 static void pan_cache_sync_date(PanWindow *pw, GList *list)
555 haystack = g_list_copy(pw->cache_list);
573 path = ((FileData *)pc)->path;
574 if (path && strcmp(path, fd->path) == 0)
576 if (pc->cd && pc->cd->have_date && pc->cd->date >= 0)
578 fd->date = pc->cd->date;
581 haystack = g_list_delete_link(haystack, needle);
586 needle = needle->next;
591 g_list_free(haystack);
595 *-----------------------------------------------------------------------------
597 *-----------------------------------------------------------------------------
600 static void pan_grid_clear(PanWindow *pw)
604 work = pw->list_grid;
612 g_list_free(pg->list);
616 g_list_free(pw->list_grid);
617 pw->list_grid = NULL;
619 pw->list = g_list_concat(pw->list, pw->list_static);
620 pw->list_static = NULL;
623 static void pan_grid_build(PanWindow *pw, gint width, gint height, gint grid_size)
636 l = g_list_length(pw->list);
640 total = (gdouble)width * (gdouble)height / (gdouble)l;
643 aw = (gdouble)width / s;
644 ah = (gdouble)height / s;
646 col = (gint)(sqrt((gdouble)l / grid_size) * width / height + 0.999);
647 col = CLAMP(col, 1, l / grid_size + 1);
648 row = (gint)((gdouble)l / grid_size / col);
649 if (row < 1) row = 1;
651 /* limit minimum size of grid so that a tile will always fit regardless of position */
652 cw = MAX((gint)ceil((gdouble)width / col), PAN_TILE_SIZE * 2);
653 ch = MAX((gint)ceil((gdouble)height / row), PAN_TILE_SIZE * 2);
658 printf("intersect speedup grid is %dx%d, based on %d average per grid\n", col, row, grid_size);
660 for (j = 0; j < row; j++)
661 for (i = 0; i < col; i++)
663 if ((i + 1) * cw / 2 < width && (j + 1) * ch / 2 < height)
667 pg = g_new0(PanGrid, 1);
674 pw->list_grid = g_list_prepend(pw->list_grid, pg);
676 if (debug) printf("grid section: %d,%d (%dx%d)\n", pg->x, pg->y, pg->w, pg->h);
689 grid = pw->list_grid;
698 if (util_clip_region(pi->x, pi->y, pi->width, pi->height,
699 pg->x, pg->y, pg->w, pg->h,
702 pg->list = g_list_prepend(pg->list, pi);
707 work = pw->list_grid;
715 pg->list = g_list_reverse(pg->list);
718 pw->list_static = pw->list;
725 *-----------------------------------------------------------------------------
727 *-----------------------------------------------------------------------------
730 static void pan_item_free(PanItem *pi)
734 if (pi->pixbuf) g_object_unref(pi->pixbuf);
735 if (pi->fd) file_data_free(pi->fd);
743 static void pan_window_items_free(PanWindow *pw)
752 PanItem *pi = work->data;
758 g_list_free(pw->list);
761 g_list_free(pw->queue);
765 image_loader_free(pw->il);
768 thumb_loader_free(pw->tl);
772 pw->search_pi = NULL;
775 static PanItem *pan_item_new_thumb(PanWindow *pw, FileData *fd, gint x, gint y)
779 pi = g_new0(PanItem, 1);
780 pi->type = ITEM_THUMB;
784 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
785 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
791 pw->list = g_list_prepend(pw->list, pi);
796 static PanItem *pan_item_new_box(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
798 guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
799 guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
803 pi = g_new0(PanItem, 1);
811 pi->color_r = base_r;
812 pi->color_g = base_g;
813 pi->color_b = base_b;
814 pi->color_a = base_a;
816 pi->color2_r = bord_r;
817 pi->color2_g = bord_g;
818 pi->color2_b = bord_b;
819 pi->color2_a = bord_a;
820 pi->border = border_size;
822 pw->list = g_list_prepend(pw->list, pi);
827 static void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
831 if (!pi || pi->type != ITEM_BOX) return;
836 pi->width -= shadow[0];
837 pi->height -= shadow[0];
840 shadow = g_new0(gint, 2);
845 pi->height += offset;
851 static PanItem *pan_item_new_tri(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
852 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
853 guint8 r, guint8 g, guint8 b, guint8 a)
858 pi = g_new0(PanItem, 1);
859 pi->type = ITEM_TRIANGLE;
870 coord = g_new0(gint, 6);
880 pi->border = BORDER_NONE;
882 pw->list = g_list_prepend(pw->list, pi);
887 static void pan_item_tri_border(PanItem *pi, gint borders,
888 guint8 r, guint8 g, guint8 b, guint8 a)
890 if (!pi || pi->type != ITEM_TRIANGLE) return;
892 pi->border = borders;
900 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
904 layout = gtk_widget_create_pango_layout(widget, NULL);
906 if (pi->text_attr & TEXT_ATTR_MARKUP)
908 pango_layout_set_markup(layout, pi->text, -1);
912 if (pi->text_attr & TEXT_ATTR_BOLD ||
913 pi->text_attr & TEXT_ATTR_HEADING)
918 pal = pango_attr_list_new();
919 if (pi->text_attr & TEXT_ATTR_BOLD)
921 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
923 pa->end_index = G_MAXINT;
924 pango_attr_list_insert(pal, pa);
926 if (pi->text_attr & TEXT_ATTR_HEADING)
928 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
930 pa->end_index = G_MAXINT;
931 pango_attr_list_insert(pal, pa);
933 pango_layout_set_attributes(layout, pal);
934 pango_attr_list_unref(pal);
937 pango_layout_set_text(layout, pi->text, -1);
941 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
945 if (!pi || !pi->text || !widget) return;
947 layout = pan_item_text_layout(pi, widget);
948 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
949 g_object_unref(G_OBJECT(layout));
951 pi->width += PAN_TEXT_BORDER_SIZE * 2;
952 pi->height += PAN_TEXT_BORDER_SIZE * 2;
955 static PanItem *pan_item_new_text(PanWindow *pw, gint x, gint y, const gchar *text, TextAttrType attr,
956 guint8 r, guint8 g, guint8 b, guint8 a)
960 pi = g_new0(PanItem, 1);
961 pi->type = ITEM_TEXT;
964 pi->text = g_strdup(text);
965 pi->text_attr = attr;
972 pan_item_text_compute_size(pi, pw->imd->pr);
974 pw->list = g_list_prepend(pw->list, pi);
979 static void pan_item_set_key(PanItem *pi, const gchar *key)
986 pi->key = g_strdup(key);
990 static void pan_item_added(PanWindow *pw, PanItem *pi)
993 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
996 static void pan_item_remove(PanWindow *pw, PanItem *pi)
1000 if (pw->click_pi == pi) pw->click_pi = NULL;
1001 if (pw->queue_pi == pi) pw->queue_pi = NULL;
1002 if (pw->search_pi == pi) pw->search_pi = NULL;
1003 pw->queue = g_list_remove(pw->queue, pi);
1005 pw->list = g_list_remove(pw->list, pi);
1006 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
1010 static void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
1012 if (!pi || !child) return;
1014 if (pi->x + pi->width < child->x + child->width + border)
1015 pi->width = child->x + child->width + border - pi->x;
1017 if (pi->y + pi->height < child->y + child->height + border)
1018 pi->height = child->y + child->height + border - pi->y;
1021 static void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
1025 if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
1026 if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
1029 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
1036 if (!pi->fd) return;
1038 work = pw->cache_list;
1047 path = ((FileData *)pc)->path;
1049 if (pc->cd && pc->cd->dimensions &&
1050 path && strcmp(path, pi->fd->path) == 0)
1052 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
1053 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
1055 pw->cache_list = g_list_remove(pw->cache_list, pc);
1056 cache_sim_data_free(pc->cd);
1057 file_data_free((FileData *)pc);
1063 static PanItem *pan_item_new_image(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
1067 pi = g_new0(PanItem, 1);
1068 pi->type = ITEM_IMAGE;
1073 pan_item_image_find_size(pw, pi, w, h);
1075 pw->list = g_list_prepend(pw->list, pi);
1080 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
1084 if (!key) return NULL;
1086 work = g_list_last(pw->list);
1092 if ((pi->type == type || type == ITEM_NONE) &&
1093 pi->key && strcmp(pi->key, key) == 0)
1099 work = g_list_last(pw->list_static);
1105 if ((pi->type == type || type == ITEM_NONE) &&
1106 pi->key && strcmp(pi->key, key) == 0)
1116 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1117 static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
1118 ItemType type, const gchar *path,
1119 gint ignore_case, gint partial)
1123 work = g_list_last(search_list);
1129 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1135 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
1137 else if (pi->fd->name)
1145 haystack = g_utf8_strdown(pi->fd->name, -1);
1146 match = (strstr(haystack, path) != NULL);
1151 if (strstr(pi->fd->name, path)) match = TRUE;
1154 else if (ignore_case)
1156 if (strcasecmp(path, pi->fd->name) == 0) match = TRUE;
1160 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
1164 if (match) list = g_list_prepend(list, pi);
1172 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1173 static GList *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1174 gint ignore_case, gint partial)
1178 if (!path) return NULL;
1179 if (partial && path[0] == '/') return NULL;
1181 list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
1182 list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
1184 return g_list_reverse(list);
1187 static PanItem *pan_item_find_by_coord_l(GList *list, ItemType type, gint x, gint y, const gchar *key)
1197 if ((pi->type == type || type == ITEM_NONE) &&
1198 x >= pi->x && x < pi->x + pi->width &&
1199 y >= pi->y && y < pi->y + pi->height &&
1200 (!key || (pi->key && strcmp(pi->key, key) == 0)))
1210 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y, const gchar *key)
1214 pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
1217 return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
1221 *-----------------------------------------------------------------------------
1223 *-----------------------------------------------------------------------------
1226 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend)
1228 GList *flist = NULL;
1229 GList *dlist = NULL;
1233 filelist_read(path, &flist, &dlist);
1234 if (sort != SORT_NONE)
1236 flist = filelist_sort(flist, sort, ascend);
1237 dlist = filelist_sort(dlist, sort, ascend);
1247 folders = g_list_remove(folders, fd);
1249 if (filelist_read(fd->path, &flist, &dlist))
1251 if (sort != SORT_NONE)
1253 flist = filelist_sort(flist, sort, ascend);
1254 dlist = filelist_sort(dlist, sort, ascend);
1257 result = g_list_concat(result, flist);
1258 folders = g_list_concat(dlist, folders);
1267 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1275 list = pan_window_layout_list(path, SORT_NAME, TRUE);
1277 grid_size = (gint)sqrt((double)g_list_length(list));
1278 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1280 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1284 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1289 *width = PAN_FOLDER_BOX_BORDER * 2;
1290 *height = PAN_FOLDER_BOX_BORDER * 2;
1303 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1305 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1307 x += pi->width + PAN_THUMB_GAP;
1308 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1317 pi = pan_item_new_thumb(pw, fd, x, y);
1319 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1323 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1326 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1332 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1335 gint x1, y1, x2, y2;
1350 if (x1 > pi->x) x1 = pi->x;
1351 if (y1 > pi->y) y1 = pi->y;
1352 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1353 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1356 x1 -= PAN_FOLDER_BOX_BORDER;
1357 y1 -= PAN_FOLDER_BOX_BORDER;
1358 x2 += PAN_FOLDER_BOX_BORDER;
1359 y2 += PAN_FOLDER_BOX_BORDER;
1372 if (pi->type == ITEM_TRIANGLE && pi->data)
1386 if (width) *width = x2 - x1;
1387 if (height) *height = y2 - y1;
1390 typedef struct _FlowerGroup FlowerGroup;
1391 struct _FlowerGroup {
1404 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1408 work = group->items;
1426 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1427 gint *result_x, gint *result_y)
1433 radius = parent->circumference / (2*PI);
1434 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1436 a = 2*PI * group->diameter / parent->circumference;
1438 x = (gint)((double)radius * cos(parent->angle + a / 2));
1439 y = (gint)((double)radius * sin(parent->angle + a / 2));
1446 x += parent->width / 2;
1447 y += parent->height / 2;
1449 x -= group->width / 2;
1450 y -= group->height / 2;
1456 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1463 if (parent && parent->children)
1465 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1473 pan_window_layout_compute_folder_flower_move(group, x, y);
1478 gint px, py, gx, gy;
1479 gint x1, y1, x2, y2;
1481 px = parent->x + parent->width / 2;
1482 py = parent->y + parent->height / 2;
1484 gx = group->x + group->width / 2;
1485 gy = group->y + group->height / 2;
1490 x2 = MAX(px, gx + 5);
1491 y2 = MAX(py, gy + 5);
1493 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1494 px, py, gx, gy, gx + 5, gy + 5,
1496 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1500 pw->list = g_list_concat(group->items, pw->list);
1501 group->items = NULL;
1503 group->circumference = 0;
1504 work = group->children;
1512 group->circumference += child->diameter;
1515 work = g_list_last(group->children);
1523 pan_window_layout_compute_folder_flower_build(pw, child, group);
1526 g_list_free(group->children);
1530 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1543 if (!filelist_read(path, &f, &d)) return NULL;
1544 if (!f && !d) return NULL;
1546 f = filelist_sort(f, SORT_NAME, TRUE);
1547 d = filelist_sort(d, SORT_NAME, TRUE);
1549 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1550 PAN_TEXT_COLOR, 255);
1552 y += pi_box->height;
1554 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1556 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1557 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1558 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1559 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1561 x += PAN_FOLDER_BOX_BORDER;
1562 y += PAN_FOLDER_BOX_BORDER;
1564 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1578 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1580 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1581 x += pi->width + PAN_THUMB_GAP;
1582 if (pi->height > y_height) y_height = pi->height;
1586 pi = pan_item_new_thumb(pw, fd, x, y);
1587 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1588 y_height = PAN_THUMB_SIZE;
1592 if (grid_count >= grid_size)
1596 y += y_height + PAN_THUMB_GAP;
1600 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1605 group = g_new0(FlowerGroup, 1);
1606 group->items = pw->list;
1609 group->width = pi_box->width;
1610 group->height = pi_box->y + pi_box->height;
1611 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1613 group->children = NULL;
1624 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1625 if (child) group->children = g_list_prepend(group->children, child);
1633 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1634 gint *width, gint *height,
1635 gint *scroll_x, gint *scroll_y)
1640 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1641 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1643 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1645 list = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1648 PanItem *pi = list->data;
1649 *scroll_x = pi->x + pi->width / 2;
1650 *scroll_y = pi->y + pi->height / 2;
1655 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1656 gint *x, gint *y, gint *level,
1658 gint *width, gint *height)
1666 if (!filelist_read(path, &f, &d)) return;
1667 if (!f && !d) return;
1669 f = filelist_sort(f, SORT_NAME, TRUE);
1670 d = filelist_sort(d, SORT_NAME, TRUE);
1672 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1674 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1675 PAN_TEXT_COLOR, 255);
1677 *y += pi_box->height;
1679 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1681 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1682 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1683 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1684 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1686 *x += PAN_FOLDER_BOX_BORDER;
1687 *y += PAN_FOLDER_BOX_BORDER;
1698 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1700 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1701 *x += pi->width + PAN_THUMB_GAP;
1702 if (pi->height > y_height) y_height = pi->height;
1706 pi = pan_item_new_thumb(pw, fd, *x, *y);
1707 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1708 y_height = PAN_THUMB_SIZE;
1711 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1714 if (f) *y = pi_box->y + pi_box->height;
1726 *level = *level + 1;
1727 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1728 pi_box, width, height);
1729 *level = *level - 1;
1734 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1736 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1737 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1739 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1742 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1749 x = PAN_FOLDER_BOX_BORDER;
1750 y = PAN_FOLDER_BOX_BORDER;
1751 w = PAN_FOLDER_BOX_BORDER * 2;
1752 h = PAN_FOLDER_BOX_BORDER * 2;
1754 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1756 if (width) *width = w;
1757 if (height) *height = h;
1761 *-----------------------------------------------------------------------------
1763 *-----------------------------------------------------------------------------
1766 static void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
1772 gint x1, y1, x2, y2, x3, y3;
1777 while ((pi = pan_item_find_by_key(pw, ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
1779 if (!pi_day || pi_day->type != ITEM_BOX ||
1780 !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
1782 list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
1794 if (dot->type != ITEM_BOX || !dot->fd ||
1795 !dot->key || strcmp(dot->key, "dot") != 0)
1797 list = g_list_delete_link(list, node);
1805 grid = (gint)(sqrt(g_list_length(list)) + 0.5);
1807 x = pi_day->x + pi_day->width + 4;
1811 if (y + grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4 > pw->pr->image_height)
1813 y = pw->pr->image_height - (grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4);
1817 pbox = pan_item_new_box(pw, NULL, x, y, PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1818 PAN_CAL_POPUP_BORDER,
1819 PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA,
1820 PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
1821 pan_item_set_key(pbox, "day_bubble");
1828 buf = date_value_string(pi_day->fd->date, DATE_LENGTH_WEEK);
1829 plabel = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1830 PAN_CAL_POPUP_TEXT_COLOR, 255);
1831 pan_item_set_key(plabel, "day_bubble");
1834 pan_item_size_by_item(pbox, plabel, 0);
1836 y += plabel->height;
1843 x += PAN_FOLDER_BOX_BORDER;
1844 y += PAN_FOLDER_BOX_BORDER;
1858 pimg = pan_item_new_thumb(pw, file_data_new_simple(dot->fd->path), x, y);
1859 pan_item_set_key(pimg, "day_bubble");
1861 pan_item_size_by_item(pbox, pimg, PAN_FOLDER_BOX_BORDER);
1866 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1871 x = pbox->x + PAN_FOLDER_BOX_BORDER;
1872 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1878 x1 = pi_day->x + pi_day->width - 8;
1881 y2 = pbox->y + MIN(42, pbox->height);
1883 y3 = MAX(pbox->y, y2 - 30);
1884 util_clip_triangle(x1, y1, x2, y2, x3, y3,
1887 pi = pan_item_new_tri(pw, NULL, x, y, w, h,
1888 x1, y1, x2, y2, x3, y3,
1889 PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA);
1890 pan_item_tri_border(pi, BORDER_1 | BORDER_3, PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
1891 pan_item_set_key(pi, "day_bubble");
1892 pan_item_added(pw, pi);
1894 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
1895 pan_item_added(pw, pbox);
1898 static void pan_window_layout_compute_calendar(PanWindow *pw, const gchar *path, gint *width, gint *height)
1914 list = pan_window_layout_list(path, SORT_NONE, TRUE);
1916 if (pw->cache_list && SORT_BY_EXIF_DATE)
1918 pw->cache_list = filelist_sort(pw->cache_list, SORT_NAME, TRUE);
1919 list = filelist_sort(list, SORT_NAME, TRUE);
1920 pan_cache_sync_date(pw, list);
1923 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
1924 list = filelist_sort(list, SORT_TIME, TRUE);
1937 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
1945 if (day_max < count) day_max = count;
1949 printf("biggest day contains %d images\n", day_max);
1951 grid = (gint)(sqrt((double)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
1952 day_width = MAX(PAN_CAL_DAY_WIDTH, grid);
1953 day_height = MAX(PAN_CAL_DAY_HEIGHT, grid);
1957 FileData *fd = list->data;
1959 year = date_value(fd->date, DATE_LENGTH_YEAR);
1960 month = date_value(fd->date, DATE_LENGTH_MONTH);
1963 work = g_list_last(list);
1966 FileData *fd = work->data;
1967 end_year = date_value(fd->date, DATE_LENGTH_YEAR);
1968 end_month = date_value(fd->date, DATE_LENGTH_MONTH);
1971 *width = PAN_FOLDER_BOX_BORDER * 2;
1972 *height = PAN_FOLDER_BOX_BORDER * 2;
1974 x = PAN_FOLDER_BOX_BORDER;
1975 y = PAN_FOLDER_BOX_BORDER;
1978 while (work && (year < end_year || (year == end_year && month <= end_month)))
1989 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
1991 days = date_value(dt, DATE_LENGTH_DAY);
1992 dt = date_to_time(year, month, 1);
1993 col = date_value(dt, DATE_LENGTH_WEEK);
1996 x = PAN_FOLDER_BOX_BORDER;
1998 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
1999 PAN_CAL_MONTH_BORDER,
2000 PAN_CAL_MONTH_COLOR, PAN_CAL_MONTH_ALPHA,
2001 PAN_CAL_MONTH_BORDER_COLOR, PAN_CAL_MONTH_ALPHA);
2002 buf = date_value_string(dt, DATE_LENGTH_MONTH);
2003 pi_text = pan_item_new_text(pw, x, y, buf,
2004 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2005 PAN_CAL_MONTH_TEXT_COLOR, 255);
2007 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
2009 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
2011 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
2012 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
2014 for (day = 1; day <= days; day++)
2021 dt = date_to_time(year, month, day);
2023 fd = g_new0(FileData, 1);
2024 /* path and name must be non NULL, so make them an invalid filename */
2025 fd->path = g_strdup("//");
2028 pi_day = pan_item_new_box(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
2030 PAN_CAL_DAY_COLOR, PAN_CAL_DAY_ALPHA,
2031 PAN_CAL_DAY_BORDER_COLOR, PAN_CAL_DAY_ALPHA);
2032 pan_item_set_key(pi_day, "day");
2034 dx = x + PAN_CAL_DOT_GAP * 2;
2035 dy = y + PAN_CAL_DOT_GAP * 2;
2037 fd = (work) ? work->data : NULL;
2038 while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
2042 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
2044 PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
2046 pan_item_set_key(pi, "dot");
2048 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2049 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
2051 dx = x + PAN_CAL_DOT_GAP * 2;
2052 dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2054 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
2056 /* must keep all dots within respective day even if it gets ugly */
2057 dy = y + PAN_CAL_DOT_GAP * 2;
2063 fd = (work) ? work->data : NULL;
2070 pi_day->color_r = MAX(pi_day->color_r - 61 - n * 3, 80);
2071 pi_day->color_g = pi_day->color_r;
2073 buf = g_strdup_printf("( %d )", n);
2074 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2075 PAN_CAL_DAY_TEXT_COLOR, 255);
2078 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
2079 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
2082 buf = g_strdup_printf("%d", day);
2083 pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2084 PAN_CAL_DAY_TEXT_COLOR, 255);
2088 pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
2095 x = PAN_FOLDER_BOX_BORDER;
2096 y += PAN_CAL_DAY_HEIGHT;
2100 x += PAN_CAL_DAY_WIDTH;
2104 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
2105 y += PAN_FOLDER_BOX_BORDER * 2;
2116 *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
2121 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
2129 PanItem *pi_month = NULL;
2130 PanItem *pi_day = NULL;
2136 list = pan_window_layout_list(path, SORT_NONE, TRUE);
2138 if (pw->cache_list && SORT_BY_EXIF_DATE)
2140 pw->cache_list = filelist_sort(pw->cache_list, SORT_NAME, TRUE);
2141 list = filelist_sort(list, SORT_NAME, TRUE);
2142 pan_cache_sync_date(pw, list);
2145 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2146 list = filelist_sort(list, SORT_TIME, TRUE);
2148 *width = PAN_FOLDER_BOX_BORDER * 2;
2149 *height = PAN_FOLDER_BOX_BORDER * 2;
2154 day_start = month_start;
2169 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2174 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
2180 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
2184 x = PAN_FOLDER_BOX_BORDER;
2187 y = PAN_FOLDER_BOX_BORDER;
2189 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
2190 pi = pan_item_new_text(pw, x, y, buf,
2191 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2192 PAN_TEXT_COLOR, 255);
2196 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
2198 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2199 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2200 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2202 x += PAN_FOLDER_BOX_BORDER;
2203 y += PAN_FOLDER_BOX_BORDER;
2207 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
2219 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
2221 needle = needle->next;
2230 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
2231 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2232 PAN_TEXT_COLOR, 255);
2237 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
2238 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2239 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2240 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2242 x += PAN_FOLDER_BOX_BORDER;
2243 y += PAN_FOLDER_BOX_BORDER;
2247 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2249 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
2250 if (pi->width > x_width) x_width = pi->width;
2251 y_height = pi->height;
2255 pi = pan_item_new_thumb(pw, fd, x, y);
2256 x_width = PAN_THUMB_SIZE;
2257 y_height = PAN_THUMB_SIZE;
2260 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
2261 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
2266 if (total > 0 && count < PAN_GROUP_MAX)
2268 y += y_height + PAN_THUMB_GAP;
2272 x += x_width + PAN_THUMB_GAP;
2282 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
2288 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
2289 gint *width, gint *height,
2290 gint *scroll_x, gint *scroll_y)
2292 pan_window_items_free(pw);
2296 case LAYOUT_SIZE_THUMB_DOTS:
2297 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2298 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2300 case LAYOUT_SIZE_THUMB_NONE:
2301 pw->thumb_size = PAN_THUMB_SIZE_NONE;
2302 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2304 case LAYOUT_SIZE_THUMB_SMALL:
2305 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2306 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2308 case LAYOUT_SIZE_THUMB_NORMAL:
2310 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2311 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2313 case LAYOUT_SIZE_THUMB_LARGE:
2314 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2315 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2317 case LAYOUT_SIZE_10:
2318 pw->image_size = 10;
2319 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2321 case LAYOUT_SIZE_25:
2322 pw->image_size = 25;
2323 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2325 case LAYOUT_SIZE_33:
2326 pw->image_size = 33;
2327 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2329 case LAYOUT_SIZE_50:
2330 pw->image_size = 50;
2331 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2333 case LAYOUT_SIZE_100:
2334 pw->image_size = 100;
2335 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2348 pan_window_layout_compute_grid(pw, path, width, height);
2350 case LAYOUT_FOLDERS_LINEAR:
2351 pan_window_layout_compute_folders_linear(pw, path, width, height);
2353 case LAYOUT_FOLDERS_FLOWER:
2354 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2356 case LAYOUT_CALENDAR:
2357 pan_window_layout_compute_calendar(pw, path, width, height);
2359 case LAYOUT_TIMELINE:
2360 pan_window_layout_compute_timeline(pw, path, width, height);
2366 printf("computed %d objects\n", g_list_length(pw->list));
2369 static GList *pan_layout_intersect_l(GList *list, GList *item_list,
2370 gint x, gint y, gint width, gint height)
2378 gint rx, ry, rw, rh;
2383 if (util_clip_region(x, y, width, height,
2384 pi->x, pi->y, pi->width, pi->height,
2385 &rx, &ry, &rw, &rh))
2387 list = g_list_prepend(list, pi);
2394 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2400 grid = pw->list_grid;
2406 if (x < pg->x || x + width > pg->x + pg->w ||
2407 y < pg->y || y + height > pg->y + pg->h)
2413 list = pan_layout_intersect_l(list, pw->list, x, y, width, height);
2417 list = pan_layout_intersect_l(list, pg->list, x, y, width, height);
2421 list = pan_layout_intersect_l(list, pw->list_static, x, y, width, height);
2428 *-----------------------------------------------------------------------------
2430 *-----------------------------------------------------------------------------
2433 static gint pan_layout_queue_step(PanWindow *pw);
2436 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2438 PanWindow *pw = data;
2446 pw->queue_pi = NULL;
2450 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2451 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2454 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2458 thumb_loader_free(pw->tl);
2461 while (pan_layout_queue_step(pw));
2464 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2466 PanWindow *pw = data;
2474 pw->queue_pi = NULL;
2478 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2479 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2480 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2482 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2483 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2484 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2489 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2490 (GdkInterpType)zoom_quality);
2491 g_object_unref(tmp);
2495 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2499 image_loader_free(pw->il);
2502 while (pan_layout_queue_step(pw));
2506 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2507 guint width, guint height, gpointer data)
2509 PanWindow *pw = data;
2520 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2521 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2525 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2531 static gint pan_layout_queue_step(PanWindow *pw)
2535 if (!pw->queue) return FALSE;
2537 pi = pw->queue->data;
2538 pw->queue = g_list_remove(pw->queue, pi);
2541 if (!pw->queue_pi->fd)
2543 pw->queue_pi->queued = FALSE;
2544 pw->queue_pi = NULL;
2548 image_loader_free(pw->il);
2550 thumb_loader_free(pw->tl);
2553 if (pi->type == ITEM_IMAGE)
2555 pw->il = image_loader_new(pi->fd->path);
2557 if (pw->size != LAYOUT_SIZE_100)
2559 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2563 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2565 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2567 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2569 image_loader_free(pw->il);
2572 else if (pi->type == ITEM_THUMB)
2574 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2576 if (!pw->tl->standard_loader)
2578 /* The classic loader will recreate a thumbnail any time we
2579 * request a different size than what exists. This view will
2580 * almost never use the user configured sizes so disable cache.
2582 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2585 thumb_loader_set_callbacks(pw->tl,
2586 pan_layout_queue_thumb_done_cb,
2587 pan_layout_queue_thumb_done_cb,
2590 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2592 thumb_loader_free(pw->tl);
2596 pw->queue_pi->queued = FALSE;
2597 pw->queue_pi = NULL;
2601 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2603 if (!pi || pi->queued || pi->pixbuf) return;
2604 if (pw->size <= LAYOUT_SIZE_THUMB_NONE) return;
2607 pw->queue = g_list_prepend(pw->queue, pi);
2609 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2612 static gint pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
2613 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2615 PanWindow *pw = data;
2620 pixbuf_set_rect_fill(pixbuf,
2621 0, 0, width, height,
2622 PAN_BACKGROUND_COLOR, 255);
2624 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2626 gint rx, ry, rw, rh;
2628 if (util_clip_region(x, y, width, height,
2630 &rx, &ry, &rw, &rh))
2632 pixbuf_draw_rect_fill(pixbuf,
2633 rx - x, ry - y, rw, rh,
2634 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2637 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2639 gint rx, ry, rw, rh;
2641 if (util_clip_region(x, y, width, height,
2643 &rx, &ry, &rw, &rh))
2645 pixbuf_draw_rect_fill(pixbuf,
2646 rx - x, ry - y, rw, rh,
2647 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2651 list = pan_layout_intersect(pw, x, y, width, height);
2656 gint tx, ty, tw, th;
2657 gint rx, ry, rw, rh;
2664 if (pi->type == ITEM_THUMB && pi->pixbuf)
2666 tw = gdk_pixbuf_get_width(pi->pixbuf);
2667 th = gdk_pixbuf_get_height(pi->pixbuf);
2669 tx = pi->x + (pi->width - tw) / 2;
2670 ty = pi->y + (pi->height - th) / 2;
2672 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2674 if (util_clip_region(x, y, width, height,
2675 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2676 &rx, &ry, &rw, &rh))
2678 pixbuf_draw_shadow(pixbuf,
2679 rx - x, ry - y, rw, rh,
2680 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2682 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2687 if (util_clip_region(x, y, width, height,
2688 tx + tw, ty + PAN_SHADOW_OFFSET,
2689 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2690 &rx, &ry, &rw, &rh))
2692 pixbuf_draw_shadow(pixbuf,
2693 rx - x, ry - y, rw, rh,
2694 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2696 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2698 if (util_clip_region(x, y, width, height,
2699 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2700 &rx, &ry, &rw, &rh))
2702 pixbuf_draw_shadow(pixbuf,
2703 rx - x, ry - y, rw, rh,
2704 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2706 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2710 if (util_clip_region(x, y, width, height,
2712 &rx, &ry, &rw, &rh))
2714 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2717 1.0, 1.0, GDK_INTERP_NEAREST,
2721 if (util_clip_region(x, y, width, height,
2722 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2723 &rx, &ry, &rw, &rh))
2725 pixbuf_draw_rect_fill(pixbuf,
2726 rx - x, ry - y, rw, rh,
2727 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2729 if (util_clip_region(x, y, width, height,
2730 tx, ty, PAN_OUTLINE_THICKNESS, th,
2731 &rx, &ry, &rw, &rh))
2733 pixbuf_draw_rect_fill(pixbuf,
2734 rx - x, ry - y, rw, rh,
2735 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2737 if (util_clip_region(x, y, width, height,
2738 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2739 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2740 &rx, &ry, &rw, &rh))
2742 pixbuf_draw_rect_fill(pixbuf,
2743 rx - x, ry - y, rw, rh,
2744 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2746 if (util_clip_region(x, y, width, height,
2747 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2748 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2749 &rx, &ry, &rw, &rh))
2751 pixbuf_draw_rect_fill(pixbuf,
2752 rx - x, ry - y, rw, rh,
2753 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2756 else if (pi->type == ITEM_THUMB)
2758 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2759 th = pi->height - PAN_SHADOW_OFFSET * 2;
2760 tx = pi->x + PAN_SHADOW_OFFSET;
2761 ty = pi->y + PAN_SHADOW_OFFSET;
2763 if (util_clip_region(x, y, width, height,
2765 &rx, &ry, &rw, &rh))
2769 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2770 pixbuf_draw_rect_fill(pixbuf,
2771 rx - x, ry - y, rw, rh,
2773 PAN_SHADOW_ALPHA / d);
2776 pan_layout_queue(pw, pi);
2778 else if (pi->type == ITEM_IMAGE)
2780 if (util_clip_region(x, y, width, height,
2781 pi->x, pi->y, pi->width, pi->height,
2782 &rx, &ry, &rw, &rh))
2786 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2789 1.0, 1.0, GDK_INTERP_NEAREST,
2794 pixbuf_draw_rect_fill(pixbuf,
2795 rx - x, ry - y, rw, rh,
2796 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2797 pan_layout_queue(pw, pi);
2801 else if (pi->type == ITEM_BOX)
2815 if (pi->color_a > 254)
2817 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2818 shadow[0], bh - shadow[0],
2819 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2821 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2822 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2824 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2826 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2831 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2832 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2834 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2836 PAN_SHADOW_COLOR, a);
2840 if (util_clip_region(x, y, width, height,
2841 pi->x, pi->y, bw, bh,
2842 &rx, &ry, &rw, &rh))
2844 pixbuf_draw_rect_fill(pixbuf,
2845 rx - x, ry - y, rw, rh,
2846 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2848 if (util_clip_region(x, y, width, height,
2849 pi->x, pi->y, bw, pi->border,
2850 &rx, &ry, &rw, &rh))
2852 pixbuf_draw_rect_fill(pixbuf,
2853 rx - x, ry - y, rw, rh,
2854 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2856 if (util_clip_region(x, y, width, height,
2857 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
2858 &rx, &ry, &rw, &rh))
2860 pixbuf_draw_rect_fill(pixbuf,
2861 rx - x, ry - y, rw, rh,
2862 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2864 if (util_clip_region(x, y, width, height,
2865 pi->x + bw - pi->border, pi->y + pi->border,
2866 pi->border, bh - pi->border * 2,
2867 &rx, &ry, &rw, &rh))
2869 pixbuf_draw_rect_fill(pixbuf,
2870 rx - x, ry - y, rw, rh,
2871 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2873 if (util_clip_region(x, y, width, height,
2874 pi->x, pi->y + bh - pi->border,
2876 &rx, &ry, &rw, &rh))
2878 pixbuf_draw_rect_fill(pixbuf,
2879 rx - x, ry - y, rw, rh,
2880 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2883 else if (pi->type == ITEM_TRIANGLE)
2885 if (util_clip_region(x, y, width, height,
2886 pi->x, pi->y, pi->width, pi->height,
2887 &rx, &ry, &rw, &rh) && pi->data)
2889 gint *coord = pi->data;
2890 pixbuf_draw_triangle(pixbuf,
2891 rx - x, ry - y, rw, rh,
2892 coord[0] - x, coord[1] - y,
2893 coord[2] - x, coord[3] - y,
2894 coord[4] - x, coord[5] - y,
2895 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2897 if (pi->border & BORDER_1)
2899 pixbuf_draw_line(pixbuf,
2900 rx - x, ry - y, rw, rh,
2901 coord[0] - x, coord[1] - y,
2902 coord[2] - x, coord[3] - y,
2903 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2905 if (pi->border & BORDER_2)
2907 pixbuf_draw_line(pixbuf,
2908 rx - x, ry - y, rw, rh,
2909 coord[2] - x, coord[3] - y,
2910 coord[4] - x, coord[5] - y,
2911 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2913 if (pi->border & BORDER_3)
2915 pixbuf_draw_line(pixbuf,
2916 rx - x, ry - y, rw, rh,
2917 coord[4] - x, coord[5] - y,
2918 coord[0] - x, coord[1] - y,
2919 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2923 else if (pi->type == ITEM_TEXT && pi->text)
2925 PangoLayout *layout;
2927 layout = pan_item_text_layout(pi, (GtkWidget *)pr);
2928 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
2929 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
2930 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2931 g_object_unref(G_OBJECT(layout));
2937 if (x%512 == 0 && y%512 == 0)
2939 PangoLayout *layout;
2942 layout = gtk_widget_create_pango_layout((GtkWidget *)pr, NULL);
2944 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
2945 (x / pr->source_tile_width) +
2946 (y / pr->source_tile_height * (pr->image_width/pr->source_tile_width + 1)));
2947 pango_layout_set_text(layout, buf, -1);
2950 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr, 0, 0, 0, 0, 0, 255);
2952 g_object_unref(G_OBJECT(layout));
2959 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
2960 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2962 PanWindow *pw = data;
2966 list = pan_layout_intersect(pw, x, y, width, height);
2975 if (pi->refcount > 0)
2979 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
2984 pw->queue = g_list_remove(pw->queue, pi);
2987 if (pw->queue_pi == pi) pw->queue_pi = NULL;
2990 g_object_unref(pi->pixbuf);
3002 *-----------------------------------------------------------------------------
3004 *-----------------------------------------------------------------------------
3007 static void pan_window_message(PanWindow *pw, const gchar *text)
3017 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
3021 work = pw->list_static;
3022 if (pw->layout == LAYOUT_CALENDAR)
3032 pi->type == ITEM_BOX &&
3033 pi->key && strcmp(pi->key, "dot") == 0)
3035 size += pi->fd->size;
3050 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
3052 size += pi->fd->size;
3058 ss = text_from_size_abrev(size);
3059 buf = g_strdup_printf(_("%d images, %s"), count, ss);
3061 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
3065 static void pan_window_zoom_limit(PanWindow *pw)
3071 case LAYOUT_SIZE_THUMB_DOTS:
3072 case LAYOUT_SIZE_THUMB_NONE:
3073 case LAYOUT_SIZE_THUMB_SMALL:
3074 case LAYOUT_SIZE_THUMB_NORMAL:
3076 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
3080 case LAYOUT_SIZE_THUMB_LARGE:
3083 case LAYOUT_SIZE_10:
3084 case LAYOUT_SIZE_25:
3087 case LAYOUT_SIZE_33:
3088 case LAYOUT_SIZE_50:
3089 case LAYOUT_SIZE_100:
3095 image_zoom_set_limits(pw->imd, min, 32.0);
3098 static gint pan_window_layout_update_idle_cb(gpointer data)
3100 PanWindow *pw = data;
3106 if (pw->size > LAYOUT_SIZE_THUMB_LARGE ||
3107 (SORT_BY_EXIF_DATE && (pw->layout == LAYOUT_TIMELINE || pw->layout == LAYOUT_CALENDAR)))
3109 if (!pw->cache_list && !pw->cache_todo)
3111 pan_cache_fill(pw, pw->path);
3114 pan_window_message(pw, _("Reading dimensions..."));
3122 if (pw->cache_count == pw->cache_total)
3124 pan_window_message(pw, _("Sorting images..."));
3126 else if (pw->cache_tick > 9)
3130 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
3131 pw->cache_total - pw->cache_count);
3132 pan_window_message(pw, buf);
3138 if (pan_cache_step(pw)) return TRUE;
3145 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3147 pan_window_zoom_limit(pw);
3149 if (width > 0 && height > 0)
3153 printf("Canvas size is %d x %d\n", width, height);
3155 pan_grid_build(pw, width, height, 1000);
3157 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
3158 PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
3159 pan_window_request_tile_cb,
3160 pan_window_dispose_tile_cb, pw, 1.0);
3162 if (scroll_x == 0 && scroll_y == 0)
3170 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
3173 pan_window_message(pw, NULL);
3179 static void pan_window_layout_update_idle(PanWindow *pw)
3181 if (pw->idle_id == -1)
3183 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3187 static void pan_window_layout_update(PanWindow *pw)
3189 pan_window_message(pw, _("Sorting images..."));
3190 pan_window_layout_update_idle(pw);
3194 *-----------------------------------------------------------------------------
3195 * pan window keyboard
3196 *-----------------------------------------------------------------------------
3199 static const gchar *pan_menu_click_path(PanWindow *pw)
3201 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3205 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3207 PanWindow *pw = data;
3209 gdk_window_get_origin(pw->imd->pr->window, x, y);
3210 popup_menu_position_clamp(menu, x, y, 0);
3213 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3215 PanWindow *pw = data;
3218 gint stop_signal = FALSE;
3224 pr = PIXBUF_RENDERER(pw->imd->pr);
3225 path = pan_menu_click_path(pw);
3227 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pw->imd->widget)));
3231 switch (event->keyval)
3233 case GDK_Left: case GDK_KP_Left:
3237 case GDK_Right: case GDK_KP_Right:
3241 case GDK_Up: case GDK_KP_Up:
3245 case GDK_Down: case GDK_KP_Down:
3249 case GDK_Page_Up: case GDK_KP_Page_Up:
3250 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
3252 case GDK_Page_Down: case GDK_KP_Page_Down:
3253 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
3255 case GDK_Home: case GDK_KP_Home:
3256 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
3258 case GDK_End: case GDK_KP_End:
3259 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
3264 if (focused && !(event->state & GDK_CONTROL_MASK) )
3265 switch (event->keyval)
3267 case '+': case '=': case GDK_KP_Add:
3268 pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
3270 case '-': case GDK_KP_Subtract:
3271 pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
3273 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3274 pixbuf_renderer_zoom_set(pr, 1.0);
3277 pixbuf_renderer_zoom_set(pr, 2.0);
3280 pixbuf_renderer_zoom_set(pr, 3.0);
3283 pixbuf_renderer_zoom_set(pr, 4.0);
3286 pixbuf_renderer_zoom_set(pr, -4.0);
3289 pixbuf_renderer_zoom_set(pr, -3.0);
3292 pixbuf_renderer_zoom_set(pr, -2.0);
3296 pan_fullscreen_toggle(pw, FALSE);
3301 pan_overlay_toggle(pw);
3304 case GDK_Delete: case GDK_KP_Delete:
3309 if (GTK_WIDGET_VISIBLE(pw->search_box))
3311 gtk_widget_grab_focus(pw->search_entry);
3315 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3323 pan_fullscreen_toggle(pw, TRUE);
3326 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3328 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3334 menu = pan_popup_menu(pw);
3335 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3340 if (event->state & GDK_CONTROL_MASK)
3343 switch (event->keyval)
3376 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3379 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3382 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3385 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3388 if (path) info_window_new(path, NULL);
3391 pan_window_close(pw);
3394 if (n != -1 && path)
3396 pan_fullscreen_toggle(pw, TRUE);
3397 start_editor_from_file(n, path);
3401 else if (event->state & GDK_SHIFT_MASK)
3408 switch (event->keyval)
3413 pan_fullscreen_toggle(pw, TRUE);
3416 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3418 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
3419 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3428 if (x != 0 || y!= 0)
3430 keyboard_scroll_calc(&x, &y, event);
3431 pixbuf_renderer_scroll(pr, x, y);
3438 *-----------------------------------------------------------------------------
3440 *-----------------------------------------------------------------------------
3443 static void pan_info_update(PanWindow *pw, PanItem *pi)
3449 gint x1, y1, x2, y2, x3, y3;
3452 if (pw->click_pi == pi) return;
3453 if (pi && !pi->fd) pi = NULL;
3455 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3460 if (debug) printf("info set to %s\n", pi->fd->path);
3462 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3464 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3465 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3466 pan_item_set_key(pbox, "info");
3468 if (pi->type == ITEM_THUMB && pi->pixbuf)
3470 w = gdk_pixbuf_get_width(pi->pixbuf);
3471 h = gdk_pixbuf_get_height(pi->pixbuf);
3473 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3474 y1 = pi->y + (pi->height - h) / 2 + 8;
3478 x1 = pi->x + pi->width - 8;
3486 util_clip_triangle(x1, y1, x2, y2, x3, y3,
3489 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3490 x1, y1, x2, y2, x3, y3,
3491 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3492 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3493 pan_item_set_key(p, "info");
3494 pan_item_added(pw, p);
3496 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3497 _("Filename:"), TEXT_ATTR_BOLD,
3498 PAN_POPUP_TEXT_COLOR, 255);
3499 pan_item_set_key(plabel, "info");
3500 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3501 pi->fd->name, TEXT_ATTR_NONE,
3502 PAN_POPUP_TEXT_COLOR, 255);
3503 pan_item_set_key(p, "info");
3504 pan_item_size_by_item(pbox, p, 0);
3506 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3507 _("Date:"), TEXT_ATTR_BOLD,
3508 PAN_POPUP_TEXT_COLOR, 255);
3509 pan_item_set_key(plabel, "info");
3510 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3511 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3512 PAN_POPUP_TEXT_COLOR, 255);
3513 pan_item_set_key(p, "info");
3514 pan_item_size_by_item(pbox, p, 0);
3516 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3517 _("Size:"), TEXT_ATTR_BOLD,
3518 PAN_POPUP_TEXT_COLOR, 255);
3519 pan_item_set_key(plabel, "info");
3520 buf = text_from_size(pi->fd->size);
3521 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3522 buf, TEXT_ATTR_NONE,
3523 PAN_POPUP_TEXT_COLOR, 255);
3525 pan_item_set_key(p, "info");
3526 pan_item_size_by_item(pbox, p, 0);
3528 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3529 pan_item_added(pw, pbox);
3534 *-----------------------------------------------------------------------------
3536 *-----------------------------------------------------------------------------
3539 static void pan_search_status(PanWindow *pw, const gchar *text)
3541 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3544 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3552 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3554 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3555 if (!list) return FALSE;
3557 found = g_list_find(list, pw->click_pi);
3558 if (found && found->next)
3560 found = found->next;
3568 pan_info_update(pw, pi);
3569 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3571 buf = g_strdup_printf("%s ( %d / %d )",
3572 (path[0] == '/') ? _("path found") : _("filename found"),
3573 g_list_index(list, pi) + 1,
3574 g_list_length(list));
3575 pan_search_status(pw, buf);
3583 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3591 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3593 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3594 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3599 needle = g_utf8_strdown(text, -1);
3600 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3603 if (!list) return FALSE;
3605 found = g_list_find(list, pw->click_pi);
3606 if (found && found->next)
3608 found = found->next;
3616 pan_info_update(pw, pi);
3617 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3619 buf = g_strdup_printf("%s ( %d / %d )",
3621 g_list_index(list, pi) + 1,
3622 g_list_length(list));
3623 pan_search_status(pw, buf);
3631 static gint valid_date_separator(gchar c)
3633 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3636 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
3637 gint year, gint month, gint day,
3643 work = g_list_last(pw->list_static);
3651 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
3652 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
3656 tl = localtime(&pi->fd->date);
3661 match = (tl->tm_year == year - 1900);
3662 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3663 if (match && day > 0) match = (tl->tm_mday == day);
3665 if (match) list = g_list_prepend(list, pi);
3670 return g_list_reverse(list);
3673 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3689 if (!text) return FALSE;
3691 ptr = (gchar *)text;
3692 while (*ptr != '\0')
3694 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3699 if (t == -1) return FALSE;
3701 if (!lt) return FALSE;
3703 if (valid_date_separator(*text))
3706 mptr = (gchar *)text;
3710 year = (gint)strtol(text, &mptr, 10);
3711 if (mptr == text) return FALSE;
3714 if (*mptr != '\0' && valid_date_separator(*mptr))
3719 month = strtol(mptr, &dptr, 10);
3722 if (valid_date_separator(*dptr))
3724 month = lt->tm_mon + 1;
3732 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3736 day = strtol(dptr, &eptr, 10);
3746 year = lt->tm_year + 1900;
3748 else if (year < 100)
3757 month < -1 || month == 0 || month > 12 ||
3758 day < -1 || day == 0 || day > 31) return FALSE;
3760 t = date_to_time(year, month, day);
3761 if (t < 0) return FALSE;
3763 if (pw->layout == LAYOUT_CALENDAR)
3765 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
3771 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3772 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
3777 found = g_list_find(list, pw->search_pi);
3778 if (found && found->next)
3780 found = found->next;
3791 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
3793 pan_info_update(pw, NULL);
3794 pan_calendar_update(pw, pi);
3795 image_scroll_to_point(pw->imd,
3796 pi->x + pi->width / 2,
3797 pi->y + pi->height / 2, 0.5, 0.5);
3801 pan_info_update(pw, pi);
3802 image_scroll_to_point(pw->imd,
3803 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3809 buf = date_value_string(t, DATE_LENGTH_MONTH);
3814 buf = g_strdup_printf("%d %s", day, tmp);
3820 buf = date_value_string(t, DATE_LENGTH_YEAR);
3825 buf_count = g_strdup_printf("( %d / %d )",
3826 g_list_index(list, pi) + 1,
3827 g_list_length(list));
3831 buf_count = g_strdup_printf("(%s)", _("no match"));
3834 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
3837 pan_search_status(pw, message);
3845 static void pan_search_activate_cb(const gchar *text, gpointer data)
3847 PanWindow *pw = data;
3851 tab_completion_append_to_history(pw->search_entry, text);
3853 if (pan_search_by_path(pw, text)) return;
3855 if ((pw->layout == LAYOUT_TIMELINE ||
3856 pw->layout == LAYOUT_CALENDAR) &&
3857 pan_search_by_date(pw, text))
3862 if (pan_search_by_partial(pw, text)) return;
3864 pan_search_status(pw, _("no match"));
3867 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3869 PanWindow *pw = data;
3872 visible = GTK_WIDGET_VISIBLE(pw->search_box);
3873 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3877 gtk_widget_hide(pw->search_box);
3878 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3882 gtk_widget_show(pw->search_box);
3883 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3884 gtk_widget_grab_focus(pw->search_entry);
3890 *-----------------------------------------------------------------------------
3891 * view window main routines
3892 *-----------------------------------------------------------------------------
3895 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
3897 PanWindow *pw = data;
3905 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
3906 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
3909 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3912 switch (event->button)
3915 pan_info_update(pw, pi);
3917 if (!pi && pw->layout == LAYOUT_CALENDAR)
3919 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
3920 pan_calendar_update(pw, pi);
3926 pan_info_update(pw, pi);
3927 menu = pan_popup_menu(pw);
3928 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
3935 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
3938 PanWindow *pw = data;
3945 if (!(event->state & GDK_SHIFT_MASK))
3951 if (event->state & GDK_CONTROL_MASK)
3953 switch (event->direction)
3956 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
3957 (gint)event->x, (gint)event->y);
3959 case GDK_SCROLL_DOWN:
3960 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
3961 (gint)event->x, (gint)event->y);
3969 switch (event->direction)
3972 pixbuf_renderer_scroll(pr, 0, -h);
3974 case GDK_SCROLL_DOWN:
3975 pixbuf_renderer_scroll(pr, 0, h);
3977 case GDK_SCROLL_LEFT:
3978 pixbuf_renderer_scroll(pr, -w, 0);
3980 case GDK_SCROLL_RIGHT:
3981 pixbuf_renderer_scroll(pr, w, 0);
3989 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
3991 g_signal_connect(G_OBJECT(imd->pr), "clicked",
3992 G_CALLBACK(button_cb), pw);
3993 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
3994 G_CALLBACK(scroll_cb), pw);
3997 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
3999 PanWindow *pw = data;
4002 pw->imd = pw->imd_normal;
4005 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
4007 if (force_off && !pw->fs) return;
4011 fullscreen_stop(pw->fs);
4015 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
4016 pan_image_set_buttons(pw, pw->fs->imd);
4017 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
4018 G_CALLBACK(pan_window_key_press_cb), pw);
4020 pw->imd = pw->fs->imd;
4024 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
4026 PanWindow *pw = data;
4029 text = image_zoom_get_as_text(pw->imd);
4030 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4034 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
4036 PanWindow *pw = data;
4041 if (pr->scale == 0.0) return;
4043 pixbuf_renderer_get_visible_rect(pr, &rect);
4044 pixbuf_renderer_get_image_size(pr, &width, &height);
4046 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4047 adj->page_size = (gdouble)rect.width;
4048 adj->page_increment = adj->page_size / 2.0;
4049 adj->step_increment = 48.0 / pr->scale;
4051 adj->upper = MAX((gdouble)width, 1.0);
4052 adj->value = (gdouble)rect.x;
4054 pref_signal_block_data(pw->scrollbar_h, pw);
4055 gtk_adjustment_changed(adj);
4056 gtk_adjustment_value_changed(adj);
4057 pref_signal_unblock_data(pw->scrollbar_h, pw);
4059 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4060 adj->page_size = (gdouble)rect.height;
4061 adj->page_increment = adj->page_size / 2.0;
4062 adj->step_increment = 48.0 / pr->scale;
4064 adj->upper = MAX((gdouble)height, 1.0);
4065 adj->value = (gdouble)rect.y;
4067 pref_signal_block_data(pw->scrollbar_v, pw);
4068 gtk_adjustment_changed(adj);
4069 gtk_adjustment_value_changed(adj);
4070 pref_signal_unblock_data(pw->scrollbar_v, pw);
4073 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4075 PanWindow *pw = data;
4079 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4081 if (!pr->scale) return;
4083 x = (gint)gtk_range_get_value(range);
4085 pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
4088 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4090 PanWindow *pw = data;
4094 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4096 if (!pr->scale) return;
4098 y = (gint)gtk_range_get_value(range);
4100 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
4103 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4105 PanWindow *pw = data;
4107 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4108 pan_window_layout_update(pw);
4111 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4113 PanWindow *pw = data;
4115 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4116 pan_window_layout_update(pw);
4119 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4121 PanWindow *pw = data;
4124 path = remove_trailing_slash(new_text);
4125 parse_out_relatives(path);
4129 warning_dialog(_("Folder not found"),
4130 _("The entered path is not a folder"),
4131 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4135 tab_completion_append_to_history(pw->path_entry, path);
4138 pw->path = g_strdup(path);
4140 pan_window_layout_update(pw);
4146 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4148 PanWindow *pw = data;
4151 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4153 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4154 pan_window_entry_activate_cb(text, pw);
4158 static void pan_window_close(PanWindow *pw)
4160 pan_window_list = g_list_remove(pan_window_list, pw);
4162 if (pw->idle_id != -1)
4164 g_source_remove(pw->idle_id);
4167 pan_fullscreen_toggle(pw, TRUE);
4168 gtk_widget_destroy(pw->window);
4170 pan_window_items_free(pw);
4178 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4180 PanWindow *pw = data;
4182 pan_window_close(pw);
4186 static void pan_window_new_real(const gchar *path)
4195 GdkGeometry geometry;
4197 pw = g_new0(PanWindow, 1);
4199 pw->path = g_strdup(path);
4200 pw->layout = LAYOUT_TIMELINE;
4201 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4202 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4203 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4206 pw->list_static = NULL;
4207 pw->list_grid = NULL;
4210 pw->overlay_id = -1;
4213 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4215 geometry.min_width = 8;
4216 geometry.min_height = 8;
4217 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4219 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4220 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
4221 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4222 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4224 window_set_icon(pw->window, NULL, NULL);
4226 vbox = gtk_vbox_new(FALSE, 0);
4227 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4228 gtk_widget_show(vbox);
4230 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4232 pref_spacer(box, 0);
4233 pref_label_new(box, _("Location:"));
4234 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
4235 pan_window_entry_activate_cb, pw);
4236 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4237 G_CALLBACK(pan_window_entry_change_cb), pw);
4238 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4239 gtk_widget_show(combo);
4241 combo = gtk_combo_box_new_text();
4242 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4243 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4244 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4245 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4246 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4248 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4249 g_signal_connect(G_OBJECT(combo), "changed",
4250 G_CALLBACK(pan_window_layout_change_cb), pw);
4251 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4252 gtk_widget_show(combo);
4254 combo = gtk_combo_box_new_text();
4255 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4256 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4257 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4258 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4259 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4260 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4261 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4262 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4263 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4264 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4266 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4267 g_signal_connect(G_OBJECT(combo), "changed",
4268 G_CALLBACK(pan_window_layout_size_cb), pw);
4269 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4270 gtk_widget_show(combo);
4272 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4273 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4274 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4276 pw->imd = image_new(TRUE);
4277 pw->imd_normal = pw->imd;
4279 g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
4280 G_CALLBACK(pan_window_image_zoom_cb), pw);
4281 g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
4282 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4284 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4285 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4286 gtk_widget_show(GTK_WIDGET(pw->imd->widget));
4288 pan_window_dnd_init(pw);
4290 pan_image_set_buttons(pw, pw->imd);
4292 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4293 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4294 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4295 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4296 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4297 gtk_widget_show(pw->scrollbar_h);
4299 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4300 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4301 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4302 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4303 0, GTK_FILL | GTK_EXPAND, 0, 0);
4304 gtk_widget_show(pw->scrollbar_v);
4308 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4309 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4311 pref_spacer(pw->search_box, 0);
4312 pref_label_new(pw->search_box, _("Find:"));
4314 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4315 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4316 gtk_widget_show(hbox);
4318 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4319 pan_search_activate_cb, pw);
4320 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4321 gtk_widget_show(combo);
4323 pw->search_label = gtk_label_new("");
4324 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4325 gtk_widget_show(pw->search_label);
4329 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4331 frame = gtk_frame_new(NULL);
4332 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4333 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4334 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4335 gtk_widget_show(frame);
4337 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4338 gtk_container_add(GTK_CONTAINER(frame), hbox);
4339 gtk_widget_show(hbox);
4341 pref_spacer(hbox, 0);
4342 pw->label_message = pref_label_new(hbox, "");
4344 frame = gtk_frame_new(NULL);
4345 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4346 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4347 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4348 gtk_widget_show(frame);
4350 pw->label_zoom = gtk_label_new("");
4351 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4352 gtk_widget_show(pw->label_zoom);
4354 pw->search_button = gtk_toggle_button_new();
4355 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4356 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4357 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4358 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4359 gtk_widget_show(hbox);
4360 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4361 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4362 gtk_widget_show(pw->search_button_arrow);
4363 pref_label_new(hbox, _("Find"));
4365 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4366 gtk_widget_show(pw->search_button);
4367 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4368 G_CALLBACK(pan_search_toggle_cb), pw);
4370 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4371 G_CALLBACK(pan_window_delete_cb), pw);
4372 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4373 G_CALLBACK(pan_window_key_press_cb), pw);
4375 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4377 pan_window_layout_update(pw);
4379 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4380 gtk_widget_show(pw->window);
4382 pan_window_list = g_list_append(pan_window_list, pw);
4386 *-----------------------------------------------------------------------------
4387 * peformance warnings
4388 *-----------------------------------------------------------------------------
4391 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4395 generic_dialog_close(gd);
4397 pan_window_new_real(path);
4401 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4405 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4406 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4409 static gint pan_warning(const gchar *path)
4415 GtkWidget *ct_button;
4418 if (enable_thumb_caching &&
4419 thumbnail_spec_standard) return FALSE;
4421 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4422 if (hide_dlg) return FALSE;
4424 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4426 gd->data = g_strdup(path);
4427 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4428 pan_warning_ok_cb, TRUE);
4430 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4431 _("Pan view performance may be poor."),
4432 _("To improve performance of thumbnails in the pan view the"
4433 " following options can be enabled. Note that both options"
4434 " must be enabled to notice a change in performance."));
4436 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4437 pref_spacer(group, PREF_PAD_INDENT);
4438 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4440 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4441 enable_thumb_caching, &enable_thumb_caching);
4442 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4443 thumbnail_spec_standard, &thumbnail_spec_standard);
4444 pref_checkbox_link_sensitivity(ct_button, button);
4448 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4449 G_CALLBACK(pan_warning_hide_cb), NULL);
4451 gtk_widget_show(gd->dialog);
4458 *-----------------------------------------------------------------------------
4460 *-----------------------------------------------------------------------------
4463 void pan_window_new(const gchar *path)
4465 if (pan_warning(path)) return;
4467 pan_window_new_real(path);
4471 *-----------------------------------------------------------------------------
4473 *-----------------------------------------------------------------------------
4476 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4478 PanWindow *pw = data;
4481 path = pan_menu_click_path(pw);
4484 pan_fullscreen_toggle(pw, TRUE);
4485 view_window_new(path);
4489 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4495 pw = submenu_item_get_data(widget);
4496 n = GPOINTER_TO_INT(data);
4499 path = pan_menu_click_path(pw);
4502 pan_fullscreen_toggle(pw, TRUE);
4503 start_editor_from_file(n, path);
4507 static void pan_info_cb(GtkWidget *widget, gpointer data)
4509 PanWindow *pw = data;
4512 path = pan_menu_click_path(pw);
4513 if (path) info_window_new(path, NULL);
4516 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4518 PanWindow *pw = data;
4520 image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
4523 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4525 PanWindow *pw = data;
4527 image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
4530 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4532 PanWindow *pw = data;
4534 image_zoom_set(pw->imd, 1.0);
4537 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4539 PanWindow *pw = data;
4542 path = pan_menu_click_path(pw);
4543 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4546 static void pan_move_cb(GtkWidget *widget, gpointer data)
4548 PanWindow *pw = data;
4551 path = pan_menu_click_path(pw);
4552 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4555 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4557 PanWindow *pw = data;
4560 path = pan_menu_click_path(pw);
4561 if (path) file_util_rename(path, NULL, pw->imd->widget);
4564 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4566 PanWindow *pw = data;
4569 path = pan_menu_click_path(pw);
4570 if (path) file_util_delete(path, NULL, pw->imd->widget);
4573 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4575 PanWindow *pw = data;
4577 pan_fullscreen_toggle(pw, FALSE);
4580 static void pan_close_cb(GtkWidget *widget, gpointer data)
4582 PanWindow *pw = data;
4584 pan_window_close(pw);
4587 static GtkWidget *pan_popup_menu(PanWindow *pw)
4593 active = (pw->click_pi != NULL);
4595 menu = popup_menu_short_lived();
4597 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4598 G_CALLBACK(pan_zoom_in_cb), pw);
4599 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4600 G_CALLBACK(pan_zoom_out_cb), pw);
4601 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4602 G_CALLBACK(pan_zoom_1_1_cb), pw);
4603 menu_item_add_divider(menu);
4605 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4606 gtk_widget_set_sensitive(item, active);
4608 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4609 G_CALLBACK(pan_info_cb), pw);
4611 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4612 G_CALLBACK(pan_new_window_cb), pw);
4614 menu_item_add_divider(menu);
4615 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4616 G_CALLBACK(pan_copy_cb), pw);
4617 menu_item_add_sensitive(menu, _("_Move..."), active,
4618 G_CALLBACK(pan_move_cb), pw);
4619 menu_item_add_sensitive(menu, _("_Rename..."), active,
4620 G_CALLBACK(pan_rename_cb), pw);
4621 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4622 G_CALLBACK(pan_delete_cb), pw);
4624 menu_item_add_divider(menu);
4628 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4632 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4635 menu_item_add_divider(menu);
4636 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4642 *-----------------------------------------------------------------------------
4644 *-----------------------------------------------------------------------------
4647 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4649 GtkSelectionData *selection_data, guint info,
4650 guint time, gpointer data)
4652 PanWindow *pw = data;
4654 if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
4656 if (info == TARGET_URI_LIST)
4660 list = uri_list_from_text(selection_data->data, TRUE);
4661 if (list && isdir((gchar *)list->data))
4663 gchar *path = list->data;
4666 pw->path = g_strdup(path);
4668 pan_window_layout_update(pw);
4671 path_list_free(list);
4675 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4676 GtkSelectionData *selection_data, guint info,
4677 guint time, gpointer data)
4679 PanWindow *pw = data;
4682 path = pan_menu_click_path(pw);
4692 case TARGET_URI_LIST:
4695 case TARGET_TEXT_PLAIN:
4700 list = g_list_append(NULL, (gchar *)path);
4701 text = uri_text_from_list(list, &len, plain_text);
4705 gtk_selection_data_set (selection_data, selection_data->target,
4712 gtk_selection_data_set (selection_data, selection_data->target,
4717 static void pan_window_dnd_init(PanWindow *pw)
4721 widget = pw->imd->pr;
4723 gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
4724 dnd_file_drag_types, dnd_file_drag_types_count,
4725 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4726 g_signal_connect(G_OBJECT(widget), "drag_data_get",
4727 G_CALLBACK(pan_window_set_dnd_data), pw);
4729 gtk_drag_dest_set(widget,
4730 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4731 dnd_file_drop_types, dnd_file_drop_types_count,
4732 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4733 g_signal_connect(G_OBJECT(widget), "drag_data_received",
4734 G_CALLBACK(pan_window_get_dnd_data), pw);
4738 *-----------------------------------------------------------------------------
4739 * maintenance (for rename, move, remove)
4740 *-----------------------------------------------------------------------------