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 static void pan_cache_sync_date(PanWindow *pw, GList *list)
554 haystack = g_list_copy(pw->cache_list);
572 path = ((FileData *)pc)->path;
573 if (path && strcmp(path, fd->path) == 0)
577 if (pc->cd && pc->cd->have_date && pc->cd->date >= 0)
579 fd->date = pc->cd->date;
583 needle = needle->next;
584 haystack = g_list_delete_link(haystack, tmp);
588 needle = needle->next;
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 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
1916 list = pan_window_layout_list(path, SORT_NONE, TRUE);
1917 list = filelist_sort(list, SORT_TIME, TRUE);
1919 if (pw->cache_list && SORT_BY_EXIF_DATE)
1921 pan_cache_sync_date(pw, list);
1922 list = filelist_sort(list, SORT_TIME, TRUE);
1936 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
1944 if (day_max < count) day_max = count;
1948 printf("biggest day contains %d images\n", day_max);
1950 grid = (gint)(sqrt((double)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
1951 day_width = MAX(PAN_CAL_DAY_WIDTH, grid);
1952 day_height = MAX(PAN_CAL_DAY_HEIGHT, grid);
1956 FileData *fd = list->data;
1958 year = date_value(fd->date, DATE_LENGTH_YEAR);
1959 month = date_value(fd->date, DATE_LENGTH_MONTH);
1962 work = g_list_last(list);
1965 FileData *fd = work->data;
1966 end_year = date_value(fd->date, DATE_LENGTH_YEAR);
1967 end_month = date_value(fd->date, DATE_LENGTH_MONTH);
1970 *width = PAN_FOLDER_BOX_BORDER * 2;
1971 *height = PAN_FOLDER_BOX_BORDER * 2;
1973 x = PAN_FOLDER_BOX_BORDER;
1974 y = PAN_FOLDER_BOX_BORDER;
1977 while (work && (year < end_year || (year == end_year && month <= end_month)))
1988 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
1990 days = date_value(dt, DATE_LENGTH_DAY);
1991 dt = date_to_time(year, month, 1);
1992 col = date_value(dt, DATE_LENGTH_WEEK);
1995 x = PAN_FOLDER_BOX_BORDER;
1997 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
1998 PAN_CAL_MONTH_BORDER,
1999 PAN_CAL_MONTH_COLOR, PAN_CAL_MONTH_ALPHA,
2000 PAN_CAL_MONTH_BORDER_COLOR, PAN_CAL_MONTH_ALPHA);
2001 buf = date_value_string(dt, DATE_LENGTH_MONTH);
2002 pi_text = pan_item_new_text(pw, x, y, buf,
2003 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2004 PAN_CAL_MONTH_TEXT_COLOR, 255);
2006 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
2008 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
2010 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
2011 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
2013 for (day = 1; day <= days; day++)
2020 dt = date_to_time(year, month, day);
2022 fd = g_new0(FileData, 1);
2023 /* path and name must be non NULL, so make them an invalid filename */
2024 fd->path = g_strdup("//");
2027 pi_day = pan_item_new_box(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
2029 PAN_CAL_DAY_COLOR, PAN_CAL_DAY_ALPHA,
2030 PAN_CAL_DAY_BORDER_COLOR, PAN_CAL_DAY_ALPHA);
2031 pan_item_set_key(pi_day, "day");
2033 dx = x + PAN_CAL_DOT_GAP * 2;
2034 dy = y + PAN_CAL_DOT_GAP * 2;
2036 fd = (work) ? work->data : NULL;
2037 while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
2041 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
2043 PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
2045 pan_item_set_key(pi, "dot");
2047 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2048 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
2050 dx = x + PAN_CAL_DOT_GAP * 2;
2051 dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
2053 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
2055 /* must keep all dots within respective day even if it gets ugly */
2056 dy = y + PAN_CAL_DOT_GAP * 2;
2062 fd = (work) ? work->data : NULL;
2069 pi_day->color_r = MAX(pi_day->color_r - 61 - n * 3, 80);
2070 pi_day->color_g = pi_day->color_r;
2072 buf = g_strdup_printf("( %d )", n);
2073 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2074 PAN_CAL_DAY_TEXT_COLOR, 255);
2077 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
2078 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
2081 buf = g_strdup_printf("%d", day);
2082 pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2083 PAN_CAL_DAY_TEXT_COLOR, 255);
2087 pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
2094 x = PAN_FOLDER_BOX_BORDER;
2095 y += PAN_CAL_DAY_HEIGHT;
2099 x += PAN_CAL_DAY_WIDTH;
2103 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
2104 y += PAN_FOLDER_BOX_BORDER * 2;
2115 *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
2120 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
2128 PanItem *pi_month = NULL;
2129 PanItem *pi_day = NULL;
2135 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2137 list = pan_window_layout_list(path, SORT_NONE, TRUE);
2138 list = filelist_sort(list, SORT_TIME, TRUE);
2140 if (pw->cache_list && SORT_BY_EXIF_DATE)
2142 pan_cache_sync_date(pw, list);
2143 list = filelist_sort(list, SORT_TIME, TRUE);
2146 *width = PAN_FOLDER_BOX_BORDER * 2;
2147 *height = PAN_FOLDER_BOX_BORDER * 2;
2152 day_start = month_start;
2167 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2172 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
2178 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
2182 x = PAN_FOLDER_BOX_BORDER;
2185 y = PAN_FOLDER_BOX_BORDER;
2187 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
2188 pi = pan_item_new_text(pw, x, y, buf,
2189 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2190 PAN_TEXT_COLOR, 255);
2194 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
2196 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2197 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2198 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2200 x += PAN_FOLDER_BOX_BORDER;
2201 y += PAN_FOLDER_BOX_BORDER;
2205 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
2217 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
2219 needle = needle->next;
2228 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
2229 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2230 PAN_TEXT_COLOR, 255);
2235 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
2236 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2237 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2238 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2240 x += PAN_FOLDER_BOX_BORDER;
2241 y += PAN_FOLDER_BOX_BORDER;
2245 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2247 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
2248 if (pi->width > x_width) x_width = pi->width;
2249 y_height = pi->height;
2253 pi = pan_item_new_thumb(pw, fd, x, y);
2254 x_width = PAN_THUMB_SIZE;
2255 y_height = PAN_THUMB_SIZE;
2258 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
2259 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
2264 if (total > 0 && count < PAN_GROUP_MAX)
2266 y += y_height + PAN_THUMB_GAP;
2270 x += x_width + PAN_THUMB_GAP;
2280 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
2286 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
2287 gint *width, gint *height,
2288 gint *scroll_x, gint *scroll_y)
2290 pan_window_items_free(pw);
2294 case LAYOUT_SIZE_THUMB_DOTS:
2295 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2296 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2298 case LAYOUT_SIZE_THUMB_NONE:
2299 pw->thumb_size = PAN_THUMB_SIZE_NONE;
2300 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2302 case LAYOUT_SIZE_THUMB_SMALL:
2303 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2304 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2306 case LAYOUT_SIZE_THUMB_NORMAL:
2308 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2309 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2311 case LAYOUT_SIZE_THUMB_LARGE:
2312 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2313 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2315 case LAYOUT_SIZE_10:
2316 pw->image_size = 10;
2317 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2319 case LAYOUT_SIZE_25:
2320 pw->image_size = 25;
2321 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2323 case LAYOUT_SIZE_33:
2324 pw->image_size = 33;
2325 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2327 case LAYOUT_SIZE_50:
2328 pw->image_size = 50;
2329 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2331 case LAYOUT_SIZE_100:
2332 pw->image_size = 100;
2333 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2346 pan_window_layout_compute_grid(pw, path, width, height);
2348 case LAYOUT_FOLDERS_LINEAR:
2349 pan_window_layout_compute_folders_linear(pw, path, width, height);
2351 case LAYOUT_FOLDERS_FLOWER:
2352 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2354 case LAYOUT_CALENDAR:
2355 pan_window_layout_compute_calendar(pw, path, width, height);
2357 case LAYOUT_TIMELINE:
2358 pan_window_layout_compute_timeline(pw, path, width, height);
2364 printf("computed %d objects\n", g_list_length(pw->list));
2367 static GList *pan_layout_intersect_l(GList *list, GList *item_list,
2368 gint x, gint y, gint width, gint height)
2376 gint rx, ry, rw, rh;
2381 if (util_clip_region(x, y, width, height,
2382 pi->x, pi->y, pi->width, pi->height,
2383 &rx, &ry, &rw, &rh))
2385 list = g_list_prepend(list, pi);
2392 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2398 grid = pw->list_grid;
2404 if (x < pg->x || x + width > pg->x + pg->w ||
2405 y < pg->y || y + height > pg->y + pg->h)
2411 list = pan_layout_intersect_l(list, pw->list, x, y, width, height);
2415 list = pan_layout_intersect_l(list, pg->list, x, y, width, height);
2419 list = pan_layout_intersect_l(list, pw->list_static, x, y, width, height);
2426 *-----------------------------------------------------------------------------
2428 *-----------------------------------------------------------------------------
2431 static gint pan_layout_queue_step(PanWindow *pw);
2434 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2436 PanWindow *pw = data;
2444 pw->queue_pi = NULL;
2448 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2449 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2452 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2456 thumb_loader_free(pw->tl);
2459 while (pan_layout_queue_step(pw));
2462 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2464 PanWindow *pw = data;
2472 pw->queue_pi = NULL;
2476 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2477 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2478 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2480 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2481 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2482 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2487 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2488 (GdkInterpType)zoom_quality);
2489 g_object_unref(tmp);
2493 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2497 image_loader_free(pw->il);
2500 while (pan_layout_queue_step(pw));
2504 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2505 guint width, guint height, gpointer data)
2507 PanWindow *pw = data;
2518 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2519 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2523 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2529 static gint pan_layout_queue_step(PanWindow *pw)
2533 if (!pw->queue) return FALSE;
2535 pi = pw->queue->data;
2536 pw->queue = g_list_remove(pw->queue, pi);
2539 if (!pw->queue_pi->fd)
2541 pw->queue_pi->queued = FALSE;
2542 pw->queue_pi = NULL;
2546 image_loader_free(pw->il);
2548 thumb_loader_free(pw->tl);
2551 if (pi->type == ITEM_IMAGE)
2553 pw->il = image_loader_new(pi->fd->path);
2555 if (pw->size != LAYOUT_SIZE_100)
2557 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2561 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2563 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2565 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2567 image_loader_free(pw->il);
2570 else if (pi->type == ITEM_THUMB)
2572 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2574 if (!pw->tl->standard_loader)
2576 /* The classic loader will recreate a thumbnail any time we
2577 * request a different size than what exists. This view will
2578 * almost never use the user configured sizes so disable cache.
2580 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2583 thumb_loader_set_callbacks(pw->tl,
2584 pan_layout_queue_thumb_done_cb,
2585 pan_layout_queue_thumb_done_cb,
2588 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2590 thumb_loader_free(pw->tl);
2594 pw->queue_pi->queued = FALSE;
2595 pw->queue_pi = NULL;
2599 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2601 if (!pi || pi->queued || pi->pixbuf) return;
2602 if (pw->size <= LAYOUT_SIZE_THUMB_NONE) return;
2605 pw->queue = g_list_prepend(pw->queue, pi);
2607 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2610 static gint pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
2611 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2613 PanWindow *pw = data;
2618 pixbuf_set_rect_fill(pixbuf,
2619 0, 0, width, height,
2620 PAN_BACKGROUND_COLOR, 255);
2622 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2624 gint rx, ry, rw, rh;
2626 if (util_clip_region(x, y, width, height,
2628 &rx, &ry, &rw, &rh))
2630 pixbuf_draw_rect_fill(pixbuf,
2631 rx - x, ry - y, rw, rh,
2632 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2635 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2637 gint rx, ry, rw, rh;
2639 if (util_clip_region(x, y, width, height,
2641 &rx, &ry, &rw, &rh))
2643 pixbuf_draw_rect_fill(pixbuf,
2644 rx - x, ry - y, rw, rh,
2645 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2649 list = pan_layout_intersect(pw, x, y, width, height);
2654 gint tx, ty, tw, th;
2655 gint rx, ry, rw, rh;
2662 if (pi->type == ITEM_THUMB && pi->pixbuf)
2664 tw = gdk_pixbuf_get_width(pi->pixbuf);
2665 th = gdk_pixbuf_get_height(pi->pixbuf);
2667 tx = pi->x + (pi->width - tw) / 2;
2668 ty = pi->y + (pi->height - th) / 2;
2670 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2672 if (util_clip_region(x, y, width, height,
2673 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2674 &rx, &ry, &rw, &rh))
2676 pixbuf_draw_shadow(pixbuf,
2677 rx - x, ry - y, rw, rh,
2678 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2680 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2685 if (util_clip_region(x, y, width, height,
2686 tx + tw, ty + PAN_SHADOW_OFFSET,
2687 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2688 &rx, &ry, &rw, &rh))
2690 pixbuf_draw_shadow(pixbuf,
2691 rx - x, ry - y, rw, rh,
2692 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2694 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2696 if (util_clip_region(x, y, width, height,
2697 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2698 &rx, &ry, &rw, &rh))
2700 pixbuf_draw_shadow(pixbuf,
2701 rx - x, ry - y, rw, rh,
2702 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2704 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2708 if (util_clip_region(x, y, width, height,
2710 &rx, &ry, &rw, &rh))
2712 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2715 1.0, 1.0, GDK_INTERP_NEAREST,
2719 if (util_clip_region(x, y, width, height,
2720 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2721 &rx, &ry, &rw, &rh))
2723 pixbuf_draw_rect_fill(pixbuf,
2724 rx - x, ry - y, rw, rh,
2725 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2727 if (util_clip_region(x, y, width, height,
2728 tx, ty, PAN_OUTLINE_THICKNESS, th,
2729 &rx, &ry, &rw, &rh))
2731 pixbuf_draw_rect_fill(pixbuf,
2732 rx - x, ry - y, rw, rh,
2733 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2735 if (util_clip_region(x, y, width, height,
2736 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2737 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2738 &rx, &ry, &rw, &rh))
2740 pixbuf_draw_rect_fill(pixbuf,
2741 rx - x, ry - y, rw, rh,
2742 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2744 if (util_clip_region(x, y, width, height,
2745 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2746 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2747 &rx, &ry, &rw, &rh))
2749 pixbuf_draw_rect_fill(pixbuf,
2750 rx - x, ry - y, rw, rh,
2751 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2754 else if (pi->type == ITEM_THUMB)
2756 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2757 th = pi->height - PAN_SHADOW_OFFSET * 2;
2758 tx = pi->x + PAN_SHADOW_OFFSET;
2759 ty = pi->y + PAN_SHADOW_OFFSET;
2761 if (util_clip_region(x, y, width, height,
2763 &rx, &ry, &rw, &rh))
2767 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2768 pixbuf_draw_rect_fill(pixbuf,
2769 rx - x, ry - y, rw, rh,
2771 PAN_SHADOW_ALPHA / d);
2774 pan_layout_queue(pw, pi);
2776 else if (pi->type == ITEM_IMAGE)
2778 if (util_clip_region(x, y, width, height,
2779 pi->x, pi->y, pi->width, pi->height,
2780 &rx, &ry, &rw, &rh))
2784 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2787 1.0, 1.0, GDK_INTERP_NEAREST,
2792 pixbuf_draw_rect_fill(pixbuf,
2793 rx - x, ry - y, rw, rh,
2794 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2795 pan_layout_queue(pw, pi);
2799 else if (pi->type == ITEM_BOX)
2813 if (pi->color_a > 254)
2815 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2816 shadow[0], bh - shadow[0],
2817 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2819 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2820 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2822 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2824 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2829 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2830 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2832 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2834 PAN_SHADOW_COLOR, a);
2838 if (util_clip_region(x, y, width, height,
2839 pi->x, pi->y, bw, bh,
2840 &rx, &ry, &rw, &rh))
2842 pixbuf_draw_rect_fill(pixbuf,
2843 rx - x, ry - y, rw, rh,
2844 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2846 if (util_clip_region(x, y, width, height,
2847 pi->x, pi->y, bw, pi->border,
2848 &rx, &ry, &rw, &rh))
2850 pixbuf_draw_rect_fill(pixbuf,
2851 rx - x, ry - y, rw, rh,
2852 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2854 if (util_clip_region(x, y, width, height,
2855 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
2856 &rx, &ry, &rw, &rh))
2858 pixbuf_draw_rect_fill(pixbuf,
2859 rx - x, ry - y, rw, rh,
2860 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2862 if (util_clip_region(x, y, width, height,
2863 pi->x + bw - pi->border, pi->y + pi->border,
2864 pi->border, bh - pi->border * 2,
2865 &rx, &ry, &rw, &rh))
2867 pixbuf_draw_rect_fill(pixbuf,
2868 rx - x, ry - y, rw, rh,
2869 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2871 if (util_clip_region(x, y, width, height,
2872 pi->x, pi->y + bh - pi->border,
2874 &rx, &ry, &rw, &rh))
2876 pixbuf_draw_rect_fill(pixbuf,
2877 rx - x, ry - y, rw, rh,
2878 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2881 else if (pi->type == ITEM_TRIANGLE)
2883 if (util_clip_region(x, y, width, height,
2884 pi->x, pi->y, pi->width, pi->height,
2885 &rx, &ry, &rw, &rh) && pi->data)
2887 gint *coord = pi->data;
2888 pixbuf_draw_triangle(pixbuf,
2889 rx - x, ry - y, rw, rh,
2890 coord[0] - x, coord[1] - y,
2891 coord[2] - x, coord[3] - y,
2892 coord[4] - x, coord[5] - y,
2893 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2895 if (pi->border & BORDER_1)
2897 pixbuf_draw_line(pixbuf,
2898 rx - x, ry - y, rw, rh,
2899 coord[0] - x, coord[1] - y,
2900 coord[2] - x, coord[3] - y,
2901 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2903 if (pi->border & BORDER_2)
2905 pixbuf_draw_line(pixbuf,
2906 rx - x, ry - y, rw, rh,
2907 coord[2] - x, coord[3] - y,
2908 coord[4] - x, coord[5] - y,
2909 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2911 if (pi->border & BORDER_3)
2913 pixbuf_draw_line(pixbuf,
2914 rx - x, ry - y, rw, rh,
2915 coord[4] - x, coord[5] - y,
2916 coord[0] - x, coord[1] - y,
2917 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2921 else if (pi->type == ITEM_TEXT && pi->text)
2923 PangoLayout *layout;
2925 layout = pan_item_text_layout(pi, (GtkWidget *)pr);
2926 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
2927 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
2928 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2929 g_object_unref(G_OBJECT(layout));
2935 if (x%512 == 0 && y%512 == 0)
2937 PangoLayout *layout;
2940 layout = gtk_widget_create_pango_layout((GtkWidget *)pr, NULL);
2942 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
2943 (x / pr->source_tile_width) +
2944 (y / pr->source_tile_height * (pr->image_width/pr->source_tile_width + 1)));
2945 pango_layout_set_text(layout, buf, -1);
2948 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr, 0, 0, 0, 0, 0, 255);
2950 g_object_unref(G_OBJECT(layout));
2957 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
2958 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2960 PanWindow *pw = data;
2964 list = pan_layout_intersect(pw, x, y, width, height);
2973 if (pi->refcount > 0)
2977 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
2982 pw->queue = g_list_remove(pw->queue, pi);
2985 if (pw->queue_pi == pi) pw->queue_pi = NULL;
2988 g_object_unref(pi->pixbuf);
3000 *-----------------------------------------------------------------------------
3002 *-----------------------------------------------------------------------------
3005 static void pan_window_message(PanWindow *pw, const gchar *text)
3015 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
3019 work = pw->list_static;
3020 if (pw->layout == LAYOUT_CALENDAR)
3030 pi->type == ITEM_BOX &&
3031 pi->key && strcmp(pi->key, "dot") == 0)
3033 size += pi->fd->size;
3048 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
3050 size += pi->fd->size;
3056 ss = text_from_size_abrev(size);
3057 buf = g_strdup_printf(_("%d images, %s"), count, ss);
3059 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
3063 static void pan_window_zoom_limit(PanWindow *pw)
3069 case LAYOUT_SIZE_THUMB_DOTS:
3070 case LAYOUT_SIZE_THUMB_NONE:
3071 case LAYOUT_SIZE_THUMB_SMALL:
3072 case LAYOUT_SIZE_THUMB_NORMAL:
3074 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
3078 case LAYOUT_SIZE_THUMB_LARGE:
3081 case LAYOUT_SIZE_10:
3082 case LAYOUT_SIZE_25:
3085 case LAYOUT_SIZE_33:
3086 case LAYOUT_SIZE_50:
3087 case LAYOUT_SIZE_100:
3093 image_zoom_set_limits(pw->imd, min, 32.0);
3096 static gint pan_window_layout_update_idle_cb(gpointer data)
3098 PanWindow *pw = data;
3104 if (pw->size > LAYOUT_SIZE_THUMB_LARGE ||
3105 (SORT_BY_EXIF_DATE && (pw->layout == LAYOUT_TIMELINE || pw->layout == LAYOUT_CALENDAR)))
3107 if (!pw->cache_list && !pw->cache_todo)
3109 pan_cache_fill(pw, pw->path);
3112 pan_window_message(pw, _("Reading dimensions..."));
3120 if (pw->cache_count == pw->cache_total)
3122 pan_window_message(pw, _("Sorting images..."));
3124 else if (pw->cache_tick > 9)
3128 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
3129 pw->cache_total - pw->cache_count);
3130 pan_window_message(pw, buf);
3136 if (pan_cache_step(pw)) return TRUE;
3143 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3145 pan_window_zoom_limit(pw);
3147 if (width > 0 && height > 0)
3151 printf("Canvas size is %d x %d\n", width, height);
3153 pan_grid_build(pw, width, height, 1000);
3155 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
3156 PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
3157 pan_window_request_tile_cb,
3158 pan_window_dispose_tile_cb, pw, 1.0);
3160 if (scroll_x == 0 && scroll_y == 0)
3168 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
3171 pan_window_message(pw, NULL);
3177 static void pan_window_layout_update_idle(PanWindow *pw)
3179 if (pw->idle_id == -1)
3181 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3185 static void pan_window_layout_update(PanWindow *pw)
3187 pan_window_message(pw, _("Sorting images..."));
3188 pan_window_layout_update_idle(pw);
3192 *-----------------------------------------------------------------------------
3193 * pan window keyboard
3194 *-----------------------------------------------------------------------------
3197 static const gchar *pan_menu_click_path(PanWindow *pw)
3199 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3203 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3205 PanWindow *pw = data;
3207 gdk_window_get_origin(pw->imd->pr->window, x, y);
3208 popup_menu_position_clamp(menu, x, y, 0);
3211 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3213 PanWindow *pw = data;
3216 gint stop_signal = FALSE;
3222 pr = PIXBUF_RENDERER(pw->imd->pr);
3223 path = pan_menu_click_path(pw);
3225 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pw->imd->widget)));
3229 switch (event->keyval)
3231 case GDK_Left: case GDK_KP_Left:
3235 case GDK_Right: case GDK_KP_Right:
3239 case GDK_Up: case GDK_KP_Up:
3243 case GDK_Down: case GDK_KP_Down:
3247 case GDK_Page_Up: case GDK_KP_Page_Up:
3248 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
3250 case GDK_Page_Down: case GDK_KP_Page_Down:
3251 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
3253 case GDK_Home: case GDK_KP_Home:
3254 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
3256 case GDK_End: case GDK_KP_End:
3257 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
3262 if (focused && !(event->state & GDK_CONTROL_MASK) )
3263 switch (event->keyval)
3265 case '+': case '=': case GDK_KP_Add:
3266 pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
3268 case '-': case GDK_KP_Subtract:
3269 pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
3271 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3272 pixbuf_renderer_zoom_set(pr, 1.0);
3275 pixbuf_renderer_zoom_set(pr, 2.0);
3278 pixbuf_renderer_zoom_set(pr, 3.0);
3281 pixbuf_renderer_zoom_set(pr, 4.0);
3284 pixbuf_renderer_zoom_set(pr, -4.0);
3287 pixbuf_renderer_zoom_set(pr, -3.0);
3290 pixbuf_renderer_zoom_set(pr, -2.0);
3294 pan_fullscreen_toggle(pw, FALSE);
3299 pan_overlay_toggle(pw);
3302 case GDK_Delete: case GDK_KP_Delete:
3307 if (GTK_WIDGET_VISIBLE(pw->search_box))
3309 gtk_widget_grab_focus(pw->search_entry);
3313 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3321 pan_fullscreen_toggle(pw, TRUE);
3324 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3326 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3332 menu = pan_popup_menu(pw);
3333 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3338 if (event->state & GDK_CONTROL_MASK)
3341 switch (event->keyval)
3374 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3377 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3380 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3383 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3386 if (path) info_window_new(path, NULL);
3389 pan_window_close(pw);
3392 if (n != -1 && path)
3394 pan_fullscreen_toggle(pw, TRUE);
3395 start_editor_from_file(n, path);
3399 else if (event->state & GDK_SHIFT_MASK)
3406 switch (event->keyval)
3411 pan_fullscreen_toggle(pw, TRUE);
3414 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3416 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
3417 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3426 if (x != 0 || y!= 0)
3428 keyboard_scroll_calc(&x, &y, event);
3429 pixbuf_renderer_scroll(pr, x, y);
3436 *-----------------------------------------------------------------------------
3438 *-----------------------------------------------------------------------------
3441 static void pan_info_update(PanWindow *pw, PanItem *pi)
3447 gint x1, y1, x2, y2, x3, y3;
3450 if (pw->click_pi == pi) return;
3451 if (pi && !pi->fd) pi = NULL;
3453 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3458 if (debug) printf("info set to %s\n", pi->fd->path);
3460 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3462 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3463 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3464 pan_item_set_key(pbox, "info");
3466 if (pi->type == ITEM_THUMB && pi->pixbuf)
3468 w = gdk_pixbuf_get_width(pi->pixbuf);
3469 h = gdk_pixbuf_get_height(pi->pixbuf);
3471 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3472 y1 = pi->y + (pi->height - h) / 2 + 8;
3476 x1 = pi->x + pi->width - 8;
3484 util_clip_triangle(x1, y1, x2, y2, x3, y3,
3487 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3488 x1, y1, x2, y2, x3, y3,
3489 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3490 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3491 pan_item_set_key(p, "info");
3492 pan_item_added(pw, p);
3494 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3495 _("Filename:"), TEXT_ATTR_BOLD,
3496 PAN_POPUP_TEXT_COLOR, 255);
3497 pan_item_set_key(plabel, "info");
3498 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3499 pi->fd->name, TEXT_ATTR_NONE,
3500 PAN_POPUP_TEXT_COLOR, 255);
3501 pan_item_set_key(p, "info");
3502 pan_item_size_by_item(pbox, p, 0);
3504 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3505 _("Date:"), TEXT_ATTR_BOLD,
3506 PAN_POPUP_TEXT_COLOR, 255);
3507 pan_item_set_key(plabel, "info");
3508 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3509 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3510 PAN_POPUP_TEXT_COLOR, 255);
3511 pan_item_set_key(p, "info");
3512 pan_item_size_by_item(pbox, p, 0);
3514 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3515 _("Size:"), TEXT_ATTR_BOLD,
3516 PAN_POPUP_TEXT_COLOR, 255);
3517 pan_item_set_key(plabel, "info");
3518 buf = text_from_size(pi->fd->size);
3519 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3520 buf, TEXT_ATTR_NONE,
3521 PAN_POPUP_TEXT_COLOR, 255);
3523 pan_item_set_key(p, "info");
3524 pan_item_size_by_item(pbox, p, 0);
3526 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3527 pan_item_added(pw, pbox);
3532 *-----------------------------------------------------------------------------
3534 *-----------------------------------------------------------------------------
3537 static void pan_search_status(PanWindow *pw, const gchar *text)
3539 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3542 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3550 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3552 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3553 if (!list) return FALSE;
3555 found = g_list_find(list, pw->click_pi);
3556 if (found && found->next)
3558 found = found->next;
3566 pan_info_update(pw, pi);
3567 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3569 buf = g_strdup_printf("%s ( %d / %d )",
3570 (path[0] == '/') ? _("path found") : _("filename found"),
3571 g_list_index(list, pi) + 1,
3572 g_list_length(list));
3573 pan_search_status(pw, buf);
3581 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3589 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3591 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3592 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3597 needle = g_utf8_strdown(text, -1);
3598 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3601 if (!list) return FALSE;
3603 found = g_list_find(list, pw->click_pi);
3604 if (found && found->next)
3606 found = found->next;
3614 pan_info_update(pw, pi);
3615 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3617 buf = g_strdup_printf("%s ( %d / %d )",
3619 g_list_index(list, pi) + 1,
3620 g_list_length(list));
3621 pan_search_status(pw, buf);
3629 static gint valid_date_separator(gchar c)
3631 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3634 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
3635 gint year, gint month, gint day,
3641 work = g_list_last(pw->list_static);
3649 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
3650 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
3654 tl = localtime(&pi->fd->date);
3659 match = (tl->tm_year == year - 1900);
3660 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3661 if (match && day > 0) match = (tl->tm_mday == day);
3663 if (match) list = g_list_prepend(list, pi);
3668 return g_list_reverse(list);
3671 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3687 if (!text) return FALSE;
3689 ptr = (gchar *)text;
3690 while (*ptr != '\0')
3692 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3697 if (t == -1) return FALSE;
3699 if (!lt) return FALSE;
3701 if (valid_date_separator(*text))
3704 mptr = (gchar *)text;
3708 year = (gint)strtol(text, &mptr, 10);
3709 if (mptr == text) return FALSE;
3712 if (*mptr != '\0' && valid_date_separator(*mptr))
3717 month = strtol(mptr, &dptr, 10);
3720 if (valid_date_separator(*dptr))
3722 month = lt->tm_mon + 1;
3730 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3734 day = strtol(dptr, &eptr, 10);
3744 year = lt->tm_year + 1900;
3746 else if (year < 100)
3755 month < -1 || month == 0 || month > 12 ||
3756 day < -1 || day == 0 || day > 31) return FALSE;
3758 t = date_to_time(year, month, day);
3759 if (t < 0) return FALSE;
3761 if (pw->layout == LAYOUT_CALENDAR)
3763 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
3769 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3770 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
3775 found = g_list_find(list, pw->search_pi);
3776 if (found && found->next)
3778 found = found->next;
3789 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
3791 pan_info_update(pw, NULL);
3792 pan_calendar_update(pw, pi);
3793 image_scroll_to_point(pw->imd,
3794 pi->x + pi->width / 2,
3795 pi->y + pi->height / 2, 0.5, 0.5);
3799 pan_info_update(pw, pi);
3800 image_scroll_to_point(pw->imd,
3801 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3807 buf = date_value_string(t, DATE_LENGTH_MONTH);
3812 buf = g_strdup_printf("%d %s", day, tmp);
3818 buf = date_value_string(t, DATE_LENGTH_YEAR);
3823 buf_count = g_strdup_printf("( %d / %d )",
3824 g_list_index(list, pi) + 1,
3825 g_list_length(list));
3829 buf_count = g_strdup_printf("(%s)", _("no match"));
3832 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
3835 pan_search_status(pw, message);
3843 static void pan_search_activate_cb(const gchar *text, gpointer data)
3845 PanWindow *pw = data;
3849 tab_completion_append_to_history(pw->search_entry, text);
3851 if (pan_search_by_path(pw, text)) return;
3853 if ((pw->layout == LAYOUT_TIMELINE ||
3854 pw->layout == LAYOUT_CALENDAR) &&
3855 pan_search_by_date(pw, text))
3860 if (pan_search_by_partial(pw, text)) return;
3862 pan_search_status(pw, _("no match"));
3865 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3867 PanWindow *pw = data;
3870 visible = GTK_WIDGET_VISIBLE(pw->search_box);
3871 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3875 gtk_widget_hide(pw->search_box);
3876 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3880 gtk_widget_show(pw->search_box);
3881 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3882 gtk_widget_grab_focus(pw->search_entry);
3888 *-----------------------------------------------------------------------------
3889 * view window main routines
3890 *-----------------------------------------------------------------------------
3893 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
3895 PanWindow *pw = data;
3903 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
3904 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
3907 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3910 switch (event->button)
3913 pan_info_update(pw, pi);
3915 if (!pi && pw->layout == LAYOUT_CALENDAR)
3917 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
3918 pan_calendar_update(pw, pi);
3924 pan_info_update(pw, pi);
3925 menu = pan_popup_menu(pw);
3926 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
3933 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
3936 PanWindow *pw = data;
3943 if (!(event->state & GDK_SHIFT_MASK))
3949 if (event->state & GDK_CONTROL_MASK)
3951 switch (event->direction)
3954 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
3955 (gint)event->x, (gint)event->y);
3957 case GDK_SCROLL_DOWN:
3958 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
3959 (gint)event->x, (gint)event->y);
3967 switch (event->direction)
3970 pixbuf_renderer_scroll(pr, 0, -h);
3972 case GDK_SCROLL_DOWN:
3973 pixbuf_renderer_scroll(pr, 0, h);
3975 case GDK_SCROLL_LEFT:
3976 pixbuf_renderer_scroll(pr, -w, 0);
3978 case GDK_SCROLL_RIGHT:
3979 pixbuf_renderer_scroll(pr, w, 0);
3987 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
3989 g_signal_connect(G_OBJECT(imd->pr), "clicked",
3990 G_CALLBACK(button_cb), pw);
3991 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
3992 G_CALLBACK(scroll_cb), pw);
3995 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
3997 PanWindow *pw = data;
4000 pw->imd = pw->imd_normal;
4003 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
4005 if (force_off && !pw->fs) return;
4009 fullscreen_stop(pw->fs);
4013 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
4014 pan_image_set_buttons(pw, pw->fs->imd);
4015 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
4016 G_CALLBACK(pan_window_key_press_cb), pw);
4018 pw->imd = pw->fs->imd;
4022 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
4024 PanWindow *pw = data;
4027 text = image_zoom_get_as_text(pw->imd);
4028 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
4032 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
4034 PanWindow *pw = data;
4039 if (pr->scale == 0.0) return;
4041 pixbuf_renderer_get_visible_rect(pr, &rect);
4042 pixbuf_renderer_get_image_size(pr, &width, &height);
4044 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
4045 adj->page_size = (gdouble)rect.width;
4046 adj->page_increment = adj->page_size / 2.0;
4047 adj->step_increment = 48.0 / pr->scale;
4049 adj->upper = MAX((gdouble)width, 1.0);
4050 adj->value = (gdouble)rect.x;
4052 pref_signal_block_data(pw->scrollbar_h, pw);
4053 gtk_adjustment_changed(adj);
4054 gtk_adjustment_value_changed(adj);
4055 pref_signal_unblock_data(pw->scrollbar_h, pw);
4057 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
4058 adj->page_size = (gdouble)rect.height;
4059 adj->page_increment = adj->page_size / 2.0;
4060 adj->step_increment = 48.0 / pr->scale;
4062 adj->upper = MAX((gdouble)height, 1.0);
4063 adj->value = (gdouble)rect.y;
4065 pref_signal_block_data(pw->scrollbar_v, pw);
4066 gtk_adjustment_changed(adj);
4067 gtk_adjustment_value_changed(adj);
4068 pref_signal_unblock_data(pw->scrollbar_v, pw);
4071 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
4073 PanWindow *pw = data;
4077 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4079 if (!pr->scale) return;
4081 x = (gint)gtk_range_get_value(range);
4083 pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
4086 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
4088 PanWindow *pw = data;
4092 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
4094 if (!pr->scale) return;
4096 y = (gint)gtk_range_get_value(range);
4098 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
4101 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
4103 PanWindow *pw = data;
4105 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4106 pan_window_layout_update(pw);
4109 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4111 PanWindow *pw = data;
4113 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4114 pan_window_layout_update(pw);
4117 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4119 PanWindow *pw = data;
4122 path = remove_trailing_slash(new_text);
4123 parse_out_relatives(path);
4127 warning_dialog(_("Folder not found"),
4128 _("The entered path is not a folder"),
4129 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4133 tab_completion_append_to_history(pw->path_entry, path);
4136 pw->path = g_strdup(path);
4138 pan_window_layout_update(pw);
4144 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4146 PanWindow *pw = data;
4149 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4151 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4152 pan_window_entry_activate_cb(text, pw);
4156 static void pan_window_close(PanWindow *pw)
4158 pan_window_list = g_list_remove(pan_window_list, pw);
4160 if (pw->idle_id != -1)
4162 g_source_remove(pw->idle_id);
4165 pan_fullscreen_toggle(pw, TRUE);
4166 gtk_widget_destroy(pw->window);
4168 pan_window_items_free(pw);
4176 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4178 PanWindow *pw = data;
4180 pan_window_close(pw);
4184 static void pan_window_new_real(const gchar *path)
4193 GdkGeometry geometry;
4195 pw = g_new0(PanWindow, 1);
4197 pw->path = g_strdup(path);
4198 pw->layout = LAYOUT_TIMELINE;
4199 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4200 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4201 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4204 pw->list_static = NULL;
4205 pw->list_grid = NULL;
4208 pw->overlay_id = -1;
4211 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4213 geometry.min_width = 8;
4214 geometry.min_height = 8;
4215 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4217 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4218 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
4219 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4220 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4222 window_set_icon(pw->window, NULL, NULL);
4224 vbox = gtk_vbox_new(FALSE, 0);
4225 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4226 gtk_widget_show(vbox);
4228 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4230 pref_spacer(box, 0);
4231 pref_label_new(box, _("Location:"));
4232 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
4233 pan_window_entry_activate_cb, pw);
4234 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4235 G_CALLBACK(pan_window_entry_change_cb), pw);
4236 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4237 gtk_widget_show(combo);
4239 combo = gtk_combo_box_new_text();
4240 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4241 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4242 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4243 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4244 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4246 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4247 g_signal_connect(G_OBJECT(combo), "changed",
4248 G_CALLBACK(pan_window_layout_change_cb), pw);
4249 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4250 gtk_widget_show(combo);
4252 combo = gtk_combo_box_new_text();
4253 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4254 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4255 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4256 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4257 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4258 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4259 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4260 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4261 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4262 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4264 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4265 g_signal_connect(G_OBJECT(combo), "changed",
4266 G_CALLBACK(pan_window_layout_size_cb), pw);
4267 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4268 gtk_widget_show(combo);
4270 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4271 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4272 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4274 pw->imd = image_new(TRUE);
4275 pw->imd_normal = pw->imd;
4277 g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
4278 G_CALLBACK(pan_window_image_zoom_cb), pw);
4279 g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
4280 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4282 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4283 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4284 gtk_widget_show(GTK_WIDGET(pw->imd->widget));
4286 pan_window_dnd_init(pw);
4288 pan_image_set_buttons(pw, pw->imd);
4290 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4291 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4292 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4293 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4294 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4295 gtk_widget_show(pw->scrollbar_h);
4297 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4298 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4299 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4300 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4301 0, GTK_FILL | GTK_EXPAND, 0, 0);
4302 gtk_widget_show(pw->scrollbar_v);
4306 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4307 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4309 pref_spacer(pw->search_box, 0);
4310 pref_label_new(pw->search_box, _("Find:"));
4312 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4313 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4314 gtk_widget_show(hbox);
4316 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4317 pan_search_activate_cb, pw);
4318 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4319 gtk_widget_show(combo);
4321 pw->search_label = gtk_label_new("");
4322 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4323 gtk_widget_show(pw->search_label);
4327 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4329 frame = gtk_frame_new(NULL);
4330 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4331 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4332 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4333 gtk_widget_show(frame);
4335 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4336 gtk_container_add(GTK_CONTAINER(frame), hbox);
4337 gtk_widget_show(hbox);
4339 pref_spacer(hbox, 0);
4340 pw->label_message = pref_label_new(hbox, "");
4342 frame = gtk_frame_new(NULL);
4343 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4344 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4345 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4346 gtk_widget_show(frame);
4348 pw->label_zoom = gtk_label_new("");
4349 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4350 gtk_widget_show(pw->label_zoom);
4352 pw->search_button = gtk_toggle_button_new();
4353 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4354 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4355 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4356 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4357 gtk_widget_show(hbox);
4358 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4359 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4360 gtk_widget_show(pw->search_button_arrow);
4361 pref_label_new(hbox, _("Find"));
4363 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4364 gtk_widget_show(pw->search_button);
4365 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4366 G_CALLBACK(pan_search_toggle_cb), pw);
4368 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4369 G_CALLBACK(pan_window_delete_cb), pw);
4370 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4371 G_CALLBACK(pan_window_key_press_cb), pw);
4373 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4375 pan_window_layout_update(pw);
4377 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4378 gtk_widget_show(pw->window);
4380 pan_window_list = g_list_append(pan_window_list, pw);
4384 *-----------------------------------------------------------------------------
4385 * peformance warnings
4386 *-----------------------------------------------------------------------------
4389 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4393 generic_dialog_close(gd);
4395 pan_window_new_real(path);
4399 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4403 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4404 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4407 static gint pan_warning(const gchar *path)
4413 GtkWidget *ct_button;
4416 if (enable_thumb_caching &&
4417 thumbnail_spec_standard) return FALSE;
4419 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4420 if (hide_dlg) return FALSE;
4422 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4424 gd->data = g_strdup(path);
4425 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4426 pan_warning_ok_cb, TRUE);
4428 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4429 _("Pan view performance may be poor."),
4430 _("To improve performance of thumbnails in the pan view the"
4431 " following options can be enabled. Note that both options"
4432 " must be enabled to notice a change in performance."));
4434 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4435 pref_spacer(group, PREF_PAD_INDENT);
4436 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4438 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4439 enable_thumb_caching, &enable_thumb_caching);
4440 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4441 thumbnail_spec_standard, &thumbnail_spec_standard);
4442 pref_checkbox_link_sensitivity(ct_button, button);
4446 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4447 G_CALLBACK(pan_warning_hide_cb), NULL);
4449 gtk_widget_show(gd->dialog);
4456 *-----------------------------------------------------------------------------
4458 *-----------------------------------------------------------------------------
4461 void pan_window_new(const gchar *path)
4463 if (pan_warning(path)) return;
4465 pan_window_new_real(path);
4469 *-----------------------------------------------------------------------------
4471 *-----------------------------------------------------------------------------
4474 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4476 PanWindow *pw = data;
4479 path = pan_menu_click_path(pw);
4482 pan_fullscreen_toggle(pw, TRUE);
4483 view_window_new(path);
4487 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4493 pw = submenu_item_get_data(widget);
4494 n = GPOINTER_TO_INT(data);
4497 path = pan_menu_click_path(pw);
4500 pan_fullscreen_toggle(pw, TRUE);
4501 start_editor_from_file(n, path);
4505 static void pan_info_cb(GtkWidget *widget, gpointer data)
4507 PanWindow *pw = data;
4510 path = pan_menu_click_path(pw);
4511 if (path) info_window_new(path, NULL);
4514 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4516 PanWindow *pw = data;
4518 image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
4521 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4523 PanWindow *pw = data;
4525 image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
4528 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4530 PanWindow *pw = data;
4532 image_zoom_set(pw->imd, 1.0);
4535 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4537 PanWindow *pw = data;
4540 path = pan_menu_click_path(pw);
4541 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4544 static void pan_move_cb(GtkWidget *widget, gpointer data)
4546 PanWindow *pw = data;
4549 path = pan_menu_click_path(pw);
4550 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4553 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4555 PanWindow *pw = data;
4558 path = pan_menu_click_path(pw);
4559 if (path) file_util_rename(path, NULL, pw->imd->widget);
4562 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4564 PanWindow *pw = data;
4567 path = pan_menu_click_path(pw);
4568 if (path) file_util_delete(path, NULL, pw->imd->widget);
4571 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4573 PanWindow *pw = data;
4575 pan_fullscreen_toggle(pw, FALSE);
4578 static void pan_close_cb(GtkWidget *widget, gpointer data)
4580 PanWindow *pw = data;
4582 pan_window_close(pw);
4585 static GtkWidget *pan_popup_menu(PanWindow *pw)
4591 active = (pw->click_pi != NULL);
4593 menu = popup_menu_short_lived();
4595 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4596 G_CALLBACK(pan_zoom_in_cb), pw);
4597 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4598 G_CALLBACK(pan_zoom_out_cb), pw);
4599 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4600 G_CALLBACK(pan_zoom_1_1_cb), pw);
4601 menu_item_add_divider(menu);
4603 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4604 gtk_widget_set_sensitive(item, active);
4606 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4607 G_CALLBACK(pan_info_cb), pw);
4609 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4610 G_CALLBACK(pan_new_window_cb), pw);
4612 menu_item_add_divider(menu);
4613 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4614 G_CALLBACK(pan_copy_cb), pw);
4615 menu_item_add_sensitive(menu, _("_Move..."), active,
4616 G_CALLBACK(pan_move_cb), pw);
4617 menu_item_add_sensitive(menu, _("_Rename..."), active,
4618 G_CALLBACK(pan_rename_cb), pw);
4619 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4620 G_CALLBACK(pan_delete_cb), pw);
4622 menu_item_add_divider(menu);
4626 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4630 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4633 menu_item_add_divider(menu);
4634 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4640 *-----------------------------------------------------------------------------
4642 *-----------------------------------------------------------------------------
4645 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4647 GtkSelectionData *selection_data, guint info,
4648 guint time, gpointer data)
4650 PanWindow *pw = data;
4652 if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
4654 if (info == TARGET_URI_LIST)
4658 list = uri_list_from_text(selection_data->data, TRUE);
4659 if (list && isdir((gchar *)list->data))
4661 gchar *path = list->data;
4664 pw->path = g_strdup(path);
4666 pan_window_layout_update(pw);
4669 path_list_free(list);
4673 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4674 GtkSelectionData *selection_data, guint info,
4675 guint time, gpointer data)
4677 PanWindow *pw = data;
4680 path = pan_menu_click_path(pw);
4690 case TARGET_URI_LIST:
4693 case TARGET_TEXT_PLAIN:
4698 list = g_list_append(NULL, (gchar *)path);
4699 text = uri_text_from_list(list, &len, plain_text);
4703 gtk_selection_data_set (selection_data, selection_data->target,
4710 gtk_selection_data_set (selection_data, selection_data->target,
4715 static void pan_window_dnd_init(PanWindow *pw)
4719 widget = pw->imd->pr;
4721 gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
4722 dnd_file_drag_types, dnd_file_drag_types_count,
4723 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4724 g_signal_connect(G_OBJECT(widget), "drag_data_get",
4725 G_CALLBACK(pan_window_set_dnd_data), pw);
4727 gtk_drag_dest_set(widget,
4728 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4729 dnd_file_drop_types, dnd_file_drop_types_count,
4730 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4731 g_signal_connect(G_OBJECT(widget), "drag_data_received",
4732 G_CALLBACK(pan_window_get_dnd_data), pw);
4736 *-----------------------------------------------------------------------------
4737 * maintenance (for rename, move, remove)
4738 *-----------------------------------------------------------------------------