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!
20 #include "fullscreen.h"
22 #include "image-load.h"
26 #include "pixbuf-renderer.h"
27 #include "pixbuf_util.h"
30 #include "ui_bookmark.h"
31 #include "ui_fileops.h"
34 #include "ui_tabcomp.h"
36 #include <gdk/gdkkeysyms.h> /* for keyboard values */
40 #define PAN_WINDOW_DEFAULT_WIDTH 720
41 #define PAN_WINDOW_DEFAULT_HEIGHT 500
43 #define PAN_TILE_SIZE 512
45 #define PAN_THUMB_SIZE_DOTS 4
46 #define PAN_THUMB_SIZE_NONE 24
47 #define PAN_THUMB_SIZE_SMALL 64
48 #define PAN_THUMB_SIZE_NORMAL 128
49 #define PAN_THUMB_SIZE_LARGE 256
50 #define PAN_THUMB_SIZE pw->thumb_size
52 #define PAN_THUMB_GAP_DOTS 2
53 #define PAN_THUMB_GAP_SMALL 14
54 #define PAN_THUMB_GAP_NORMAL 30
55 #define PAN_THUMB_GAP_LARGE 40
56 #define PAN_THUMB_GAP_HUGE 50
57 #define PAN_THUMB_GAP pw->thumb_gap
59 #define PAN_SHADOW_OFFSET 6
60 #define PAN_SHADOW_FADE 5
61 #define PAN_SHADOW_COLOR 0, 0, 0
62 #define PAN_SHADOW_ALPHA 64
64 #define PAN_OUTLINE_THICKNESS 1
65 #define PAN_OUTLINE_COLOR_1 255, 255, 255
66 #define PAN_OUTLINE_COLOR_2 64, 64, 64
67 #define PAN_OUTLINE_ALPHA 180
69 #define PAN_BACKGROUND_COLOR 150, 150, 150
71 #define PAN_GRID_SIZE 60
72 #define PAN_GRID_COLOR 0, 0, 0
73 #define PAN_GRID_ALPHA 20
75 #define PAN_FOLDER_BOX_COLOR 255, 255, 255
76 #define PAN_FOLDER_BOX_ALPHA 100
77 #define PAN_FOLDER_BOX_BORDER 20
79 #define PAN_FOLDER_BOX_OUTLINE_THICKNESS 4
80 #define PAN_FOLDER_BOX_OUTLINE_COLOR 0, 0, 0
81 #define PAN_FOLDER_BOX_OUTLINE_ALPHA 128
83 #define PAN_TEXT_BORDER_SIZE 4
84 #define PAN_TEXT_COLOR 0, 0, 0
86 #define PAN_POPUP_COLOR 255, 255, 225
87 #define PAN_POPUP_ALPHA 255
88 #define PAN_POPUP_BORDER 1
89 #define PAN_POPUP_BORDER_COLOR 0, 0, 0
90 #define PAN_POPUP_TEXT_COLOR 0, 0, 0
92 #define PAN_CAL_POPUP_COLOR 220, 220, 220
93 #define PAN_CAL_POPUP_ALPHA 255
94 #define PAN_CAL_POPUP_BORDER 1
95 #define PAN_CAL_POPUP_BORDER_COLOR 0, 0, 0
96 #define PAN_CAL_POPUP_TEXT_COLOR 0, 0, 0
98 #define PAN_CAL_DAY_WIDTH 100
99 #define PAN_CAL_DAY_HEIGHT 80
101 #define PAN_CAL_DAY_COLOR 255, 255, 255
102 #define PAN_CAL_DAY_ALPHA 220
103 #define PAN_CAL_DAY_BORDER 2
104 #define PAN_CAL_DAY_BORDER_COLOR 0, 0, 0
105 #define PAN_CAL_DAY_TEXT_COLOR 0, 0, 0
107 #define PAN_CAL_MONTH_COLOR 255, 255, 255
108 #define PAN_CAL_MONTH_ALPHA 200
109 #define PAN_CAL_MONTH_BORDER 4
110 #define PAN_CAL_MONTH_BORDER_COLOR 0, 0, 0
111 #define PAN_CAL_MONTH_TEXT_COLOR 0, 0, 0
113 #define PAN_CAL_DOT_SIZE 3
114 #define PAN_CAL_DOT_GAP 2
115 #define PAN_CAL_DOT_COLOR 128, 128, 128
116 #define PAN_CAL_DOT_ALPHA 128
119 #define PAN_GROUP_MAX 16
121 #define ZOOM_INCREMENT 1.0
122 #define ZOOM_LABEL_WIDTH 64
125 #define PAN_PREF_GROUP "pan_view_options"
126 #define PAN_PREF_HIDE_WARNING "hide_performance_warning"
132 LAYOUT_FOLDERS_LINEAR,
133 LAYOUT_FOLDERS_FLOWER,
138 LAYOUT_SIZE_THUMB_DOTS = 0,
139 LAYOUT_SIZE_THUMB_NONE,
140 LAYOUT_SIZE_THUMB_SMALL,
141 LAYOUT_SIZE_THUMB_NORMAL,
142 LAYOUT_SIZE_THUMB_LARGE,
161 TEXT_ATTR_BOLD = 1 << 0,
162 TEXT_ATTR_HEADING = 1 << 1,
163 TEXT_ATTR_MARKUP = 1 << 2
174 typedef struct _PanItem PanItem;
189 TextAttrType text_attr;
207 typedef struct _PanWindow PanWindow;
212 ImageWindow *imd_normal;
215 GtkWidget *path_entry;
217 GtkWidget *label_message;
218 GtkWidget *label_zoom;
220 GtkWidget *search_box;
221 GtkWidget *search_entry;
222 GtkWidget *search_label;
223 GtkWidget *search_button;
224 GtkWidget *search_button_arrow;
226 GtkWidget *scrollbar_h;
227 GtkWidget *scrollbar_v;
259 typedef struct _PanGrid PanGrid;
268 typedef struct _PanCacheData PanCacheData;
269 struct _PanCacheData {
275 static GList *pan_window_list = NULL;
278 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend);
280 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
282 static GtkWidget *pan_popup_menu(PanWindow *pw);
283 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off);
285 static void pan_window_close(PanWindow *pw);
287 static void pan_window_dnd_init(PanWindow *pw);
299 static gint date_compare(time_t a, time_t b, DateLengthType length)
304 if (length == DATE_LENGTH_EXACT) return (a == b);
306 if (!localtime_r(&a, &ta) ||
307 !localtime_r(&b, &tb)) return FALSE;
309 if (ta.tm_year != tb.tm_year) return FALSE;
310 if (length == DATE_LENGTH_YEAR) return TRUE;
312 if (ta.tm_mon != tb.tm_mon) return FALSE;
313 if (length == DATE_LENGTH_MONTH) return TRUE;
315 if (length == DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
317 if (ta.tm_mday != tb.tm_mday) return FALSE;
318 if (length == DATE_LENGTH_DAY) return TRUE;
320 return (ta.tm_hour == tb.tm_hour);
323 static gint date_value(time_t d, DateLengthType length)
327 if (!localtime_r(&d, &td)) return -1;
331 case DATE_LENGTH_DAY:
334 case DATE_LENGTH_WEEK:
337 case DATE_LENGTH_MONTH:
338 return td.tm_mon + 1;
340 case DATE_LENGTH_YEAR:
341 return td.tm_year + 1900;
343 case DATE_LENGTH_EXACT:
351 static gchar *date_value_string(time_t d, DateLengthType length)
355 gchar *format = NULL;
357 if (!localtime_r(&d, &td)) return g_strdup("");
361 case DATE_LENGTH_DAY:
362 return g_strdup_printf("%d", td.tm_mday);
364 case DATE_LENGTH_WEEK:
367 case DATE_LENGTH_MONTH:
370 case DATE_LENGTH_YEAR:
371 return g_strdup_printf("%d", td.tm_year + 1900);
373 case DATE_LENGTH_EXACT:
375 return g_strdup(text_from_time(d));
380 if (format && strftime(buf, sizeof(buf), format, &td) > 0)
382 gchar *ret = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
389 static time_t date_to_time(gint year, gint month, gint day)
396 lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
397 lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
398 lt.tm_year = year - 1900;
405 *-----------------------------------------------------------------------------
407 *-----------------------------------------------------------------------------
410 static void pan_cache_free(PanWindow *pw)
414 work = pw->cache_list;
422 cache_sim_data_free(pc->cd);
423 file_data_free((FileData *)pc);
426 g_list_free(pw->cache_list);
427 pw->cache_list = NULL;
429 filelist_free(pw->cache_todo);
430 pw->cache_todo = NULL;
437 static void pan_cache_fill(PanWindow *pw, const gchar *path)
443 list = pan_window_layout_list(path, SORT_NAME, TRUE);
444 pw->cache_todo = g_list_reverse(list);
446 pw->cache_total = g_list_length(pw->cache_todo);
449 static gint pan_cache_step(PanWindow *pw)
453 CacheData *cd = NULL;
455 if (!pw->cache_todo) return FALSE;
457 fd = pw->cache_todo->data;
458 pw->cache_todo = g_list_remove(pw->cache_todo, fd);
460 if (enable_thumb_caching)
464 found = cache_find_location(CACHE_TYPE_SIM, fd->path);
465 if (found && filetime(found) == fd->date)
467 cd = cache_sim_data_load(found);
472 if (!cd) cd = cache_sim_data_new();
476 cd->dimensions = image_load_dimensions(fd->path, &cd->width, &cd->height);
477 if (enable_thumb_caching &&
483 base = cache_get_location(CACHE_TYPE_SIM, fd->path, FALSE, &mode);
484 if (cache_ensure_dir_exists(base, mode))
487 cd->path = cache_get_location(CACHE_TYPE_SIM, fd->path, TRUE, NULL);
488 if (cache_sim_data_save(cd))
490 filetime_set(cd->path, filetime(fd->path));
499 pc = g_new0(PanCacheData, 1);
500 memcpy(pc, fd, sizeof(FileData));
505 pw->cache_list = g_list_prepend(pw->cache_list, pc);
511 *-----------------------------------------------------------------------------
513 *-----------------------------------------------------------------------------
516 static void pan_grid_clear(PanWindow *pw)
520 work = pw->list_grid;
528 g_list_free(pg->list);
532 g_list_free(pw->list_grid);
533 pw->list_grid = NULL;
535 pw->list = g_list_concat(pw->list, pw->list_static);
536 pw->list_static = NULL;
539 static void pan_grid_build(PanWindow *pw, gint width, gint height, gint grid_size)
552 l = g_list_length(pw->list);
556 total = (gdouble)width * (gdouble)height / (gdouble)l;
559 aw = (gdouble)width / s;
560 ah = (gdouble)height / s;
562 col = (gint)(sqrt((gdouble)l / grid_size) * width / height + 0.999);
563 col = CLAMP(col, 1, l / grid_size + 1);
564 row = (gint)((gdouble)l / grid_size / col);
565 if (row < 1) row = 1;
567 /* limit minimum size of grid so that a tile will always fit regardless of position */
568 cw = MAX((gint)ceil((gdouble)width / col), PAN_TILE_SIZE * 2);
569 ch = MAX((gint)ceil((gdouble)height / row), PAN_TILE_SIZE * 2);
574 printf("intersect speedup grid is %dx%d, based on %d average per grid\n", col, row, grid_size);
576 for (j = 0; j < row; j++)
577 for (i = 0; i < col; i++)
579 if ((i + 1) * cw / 2 < width && (j + 1) * ch / 2 < height)
583 pg = g_new0(PanGrid, 1);
590 pw->list_grid = g_list_prepend(pw->list_grid, pg);
592 if (debug) printf("grid section: %d,%d (%dx%d)\n", pg->x, pg->y, pg->w, pg->h);
605 grid = pw->list_grid;
614 if (util_clip_region(pi->x, pi->y, pi->width, pi->height,
615 pg->x, pg->y, pg->w, pg->h,
618 pg->list = g_list_prepend(pg->list, pi);
623 work = pw->list_grid;
631 pg->list = g_list_reverse(pg->list);
634 pw->list_static = pw->list;
641 *-----------------------------------------------------------------------------
643 *-----------------------------------------------------------------------------
646 static void pan_item_free(PanItem *pi)
650 if (pi->pixbuf) g_object_unref(pi->pixbuf);
651 if (pi->fd) file_data_free(pi->fd);
659 static void pan_window_items_free(PanWindow *pw)
668 PanItem *pi = work->data;
674 g_list_free(pw->list);
677 g_list_free(pw->queue);
681 image_loader_free(pw->il);
684 thumb_loader_free(pw->tl);
688 pw->search_pi = NULL;
691 static PanItem *pan_item_new_thumb(PanWindow *pw, FileData *fd, gint x, gint y)
695 pi = g_new0(PanItem, 1);
696 pi->type = ITEM_THUMB;
700 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
701 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
707 pw->list = g_list_prepend(pw->list, pi);
712 static PanItem *pan_item_new_box(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
714 guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
715 guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
719 pi = g_new0(PanItem, 1);
727 pi->color_r = base_r;
728 pi->color_g = base_g;
729 pi->color_b = base_b;
730 pi->color_a = base_a;
732 pi->color2_r = bord_r;
733 pi->color2_g = bord_g;
734 pi->color2_b = bord_b;
735 pi->color2_a = bord_a;
736 pi->border = border_size;
738 pw->list = g_list_prepend(pw->list, pi);
743 static void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
747 if (!pi || pi->type != ITEM_BOX) return;
752 pi->width -= shadow[0];
753 pi->height -= shadow[0];
756 shadow = g_new0(gint, 2);
761 pi->height += offset;
767 static PanItem *pan_item_new_tri(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
768 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
769 guint8 r, guint8 g, guint8 b, guint8 a)
774 pi = g_new0(PanItem, 1);
775 pi->type = ITEM_TRIANGLE;
786 coord = g_new0(gint, 6);
796 pi->border = BORDER_NONE;
798 pw->list = g_list_prepend(pw->list, pi);
803 static void pan_item_tri_border(PanItem *pi, gint borders,
804 guint8 r, guint8 g, guint8 b, guint8 a)
806 if (!pi || pi->type != ITEM_TRIANGLE) return;
808 pi->border = borders;
816 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
820 layout = gtk_widget_create_pango_layout(widget, NULL);
822 if (pi->text_attr & TEXT_ATTR_MARKUP)
824 pango_layout_set_markup(layout, pi->text, -1);
828 if (pi->text_attr & TEXT_ATTR_BOLD ||
829 pi->text_attr & TEXT_ATTR_HEADING)
834 pal = pango_attr_list_new();
835 if (pi->text_attr & TEXT_ATTR_BOLD)
837 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
839 pa->end_index = G_MAXINT;
840 pango_attr_list_insert(pal, pa);
842 if (pi->text_attr & TEXT_ATTR_HEADING)
844 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
846 pa->end_index = G_MAXINT;
847 pango_attr_list_insert(pal, pa);
849 pango_layout_set_attributes(layout, pal);
850 pango_attr_list_unref(pal);
853 pango_layout_set_text(layout, pi->text, -1);
857 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
861 if (!pi || !pi->text || !widget) return;
863 layout = pan_item_text_layout(pi, widget);
864 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
865 g_object_unref(G_OBJECT(layout));
867 pi->width += PAN_TEXT_BORDER_SIZE * 2;
868 pi->height += PAN_TEXT_BORDER_SIZE * 2;
871 static PanItem *pan_item_new_text(PanWindow *pw, gint x, gint y, const gchar *text, TextAttrType attr,
872 guint8 r, guint8 g, guint8 b, guint8 a)
876 pi = g_new0(PanItem, 1);
877 pi->type = ITEM_TEXT;
880 pi->text = g_strdup(text);
881 pi->text_attr = attr;
888 pan_item_text_compute_size(pi, pw->imd->pr);
890 pw->list = g_list_prepend(pw->list, pi);
895 static void pan_item_set_key(PanItem *pi, const gchar *key)
902 pi->key = g_strdup(key);
906 static void pan_item_added(PanWindow *pw, PanItem *pi)
909 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
912 static void pan_item_remove(PanWindow *pw, PanItem *pi)
916 if (pw->click_pi == pi) pw->click_pi = NULL;
917 if (pw->queue_pi == pi) pw->queue_pi = NULL;
918 if (pw->search_pi == pi) pw->search_pi = NULL;
919 pw->queue = g_list_remove(pw->queue, pi);
921 pw->list = g_list_remove(pw->list, pi);
922 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
926 static void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
928 if (!pi || !child) return;
930 if (pi->x + pi->width < child->x + child->width + border)
931 pi->width = child->x + child->width + border - pi->x;
933 if (pi->y + pi->height < child->y + child->height + border)
934 pi->height = child->y + child->height + border - pi->y;
937 static void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
941 if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
942 if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
945 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
954 work = pw->cache_list;
963 path = ((FileData *)pc)->path;
965 if (pc->cd && pc->cd->dimensions &&
966 path && strcmp(path, pi->fd->path) == 0)
968 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
969 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
971 pw->cache_list = g_list_remove(pw->cache_list, pc);
972 cache_sim_data_free(pc->cd);
973 file_data_free((FileData *)pc);
979 static PanItem *pan_item_new_image(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
983 pi = g_new0(PanItem, 1);
984 pi->type = ITEM_IMAGE;
989 pan_item_image_find_size(pw, pi, w, h);
991 pw->list = g_list_prepend(pw->list, pi);
996 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
1000 if (!key) return NULL;
1002 work = g_list_last(pw->list);
1008 if ((pi->type == type || type == ITEM_NONE) &&
1009 pi->key && strcmp(pi->key, key) == 0)
1015 work = g_list_last(pw->list_static);
1021 if ((pi->type == type || type == ITEM_NONE) &&
1022 pi->key && strcmp(pi->key, key) == 0)
1032 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1033 static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
1034 ItemType type, const gchar *path,
1035 gint ignore_case, gint partial)
1039 work = g_list_last(search_list);
1045 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1051 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
1053 else if (pi->fd->name)
1061 haystack = g_utf8_strdown(pi->fd->name, -1);
1062 match = (strstr(haystack, path) != NULL);
1067 if (strstr(pi->fd->name, path)) match = TRUE;
1070 else if (ignore_case)
1072 if (strcasecmp(path, pi->fd->name) == 0) match = TRUE;
1076 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
1080 if (match) list = g_list_prepend(list, pi);
1088 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1089 static GList *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1090 gint ignore_case, gint partial)
1094 if (!path) return NULL;
1095 if (partial && path[0] == '/') return NULL;
1097 list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
1098 list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
1100 return g_list_reverse(list);
1103 static PanItem *pan_item_find_by_coord_l(GList *list, ItemType type, gint x, gint y, const gchar *key)
1113 if ((pi->type == type || type == ITEM_NONE) &&
1114 x >= pi->x && x < pi->x + pi->width &&
1115 y >= pi->y && y < pi->y + pi->height &&
1116 (!key || (pi->key && strcmp(pi->key, key) == 0)))
1126 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y, const gchar *key)
1130 pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
1133 return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
1137 *-----------------------------------------------------------------------------
1139 *-----------------------------------------------------------------------------
1142 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend)
1144 GList *flist = NULL;
1145 GList *dlist = NULL;
1149 filelist_read(path, &flist, &dlist);
1150 if (sort != SORT_NONE)
1152 flist = filelist_sort(flist, sort, ascend);
1153 dlist = filelist_sort(dlist, sort, ascend);
1163 folders = g_list_remove(folders, fd);
1165 if (filelist_read(fd->path, &flist, &dlist))
1167 if (sort != SORT_NONE)
1169 flist = filelist_sort(flist, sort, ascend);
1170 dlist = filelist_sort(dlist, sort, ascend);
1173 result = g_list_concat(result, flist);
1174 folders = g_list_concat(dlist, folders);
1183 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1191 list = pan_window_layout_list(path, SORT_NAME, TRUE);
1193 grid_size = (gint)sqrt((double)g_list_length(list));
1194 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1196 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1200 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1205 *width = PAN_FOLDER_BOX_BORDER * 2;
1206 *height = PAN_FOLDER_BOX_BORDER * 2;
1219 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1221 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1223 x += pi->width + PAN_THUMB_GAP;
1224 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1233 pi = pan_item_new_thumb(pw, fd, x, y);
1235 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1239 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1242 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1248 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1251 gint x1, y1, x2, y2;
1266 if (x1 > pi->x) x1 = pi->x;
1267 if (y1 > pi->y) y1 = pi->y;
1268 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1269 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1272 x1 -= PAN_FOLDER_BOX_BORDER;
1273 y1 -= PAN_FOLDER_BOX_BORDER;
1274 x2 += PAN_FOLDER_BOX_BORDER;
1275 y2 += PAN_FOLDER_BOX_BORDER;
1288 if (pi->type == ITEM_TRIANGLE && pi->data)
1302 if (width) *width = x2 - x1;
1303 if (height) *height = y2 - y1;
1306 typedef struct _FlowerGroup FlowerGroup;
1307 struct _FlowerGroup {
1320 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1324 work = group->items;
1342 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1343 gint *result_x, gint *result_y)
1349 radius = parent->circumference / (2*PI);
1350 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1352 a = 2*PI * group->diameter / parent->circumference;
1354 x = (gint)((double)radius * cos(parent->angle + a / 2));
1355 y = (gint)((double)radius * sin(parent->angle + a / 2));
1362 x += parent->width / 2;
1363 y += parent->height / 2;
1365 x -= group->width / 2;
1366 y -= group->height / 2;
1372 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1379 if (parent && parent->children)
1381 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1389 pan_window_layout_compute_folder_flower_move(group, x, y);
1394 gint px, py, gx, gy;
1395 gint x1, y1, x2, y2;
1397 px = parent->x + parent->width / 2;
1398 py = parent->y + parent->height / 2;
1400 gx = group->x + group->width / 2;
1401 gy = group->y + group->height / 2;
1406 x2 = MAX(px, gx + 5);
1407 y2 = MAX(py, gy + 5);
1409 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1410 px, py, gx, gy, gx + 5, gy + 5,
1412 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1416 pw->list = g_list_concat(group->items, pw->list);
1417 group->items = NULL;
1419 group->circumference = 0;
1420 work = group->children;
1428 group->circumference += child->diameter;
1431 work = g_list_last(group->children);
1439 pan_window_layout_compute_folder_flower_build(pw, child, group);
1442 g_list_free(group->children);
1446 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1459 if (!filelist_read(path, &f, &d)) return NULL;
1460 if (!f && !d) return NULL;
1462 f = filelist_sort(f, SORT_NAME, TRUE);
1463 d = filelist_sort(d, SORT_NAME, TRUE);
1465 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1466 PAN_TEXT_COLOR, 255);
1468 y += pi_box->height;
1470 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1472 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1473 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1474 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1475 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1477 x += PAN_FOLDER_BOX_BORDER;
1478 y += PAN_FOLDER_BOX_BORDER;
1480 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1494 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1496 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1497 x += pi->width + PAN_THUMB_GAP;
1498 if (pi->height > y_height) y_height = pi->height;
1502 pi = pan_item_new_thumb(pw, fd, x, y);
1503 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1504 y_height = PAN_THUMB_SIZE;
1508 if (grid_count >= grid_size)
1512 y += y_height + PAN_THUMB_GAP;
1516 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1521 group = g_new0(FlowerGroup, 1);
1522 group->items = pw->list;
1525 group->width = pi_box->width;
1526 group->height = pi_box->y + pi_box->height;
1527 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1529 group->children = NULL;
1540 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1541 if (child) group->children = g_list_prepend(group->children, child);
1549 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1550 gint *width, gint *height,
1551 gint *scroll_x, gint *scroll_y)
1556 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1557 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1559 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1561 list = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1564 PanItem *pi = list->data;
1565 *scroll_x = pi->x + pi->width / 2;
1566 *scroll_y = pi->y + pi->height / 2;
1571 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1572 gint *x, gint *y, gint *level,
1574 gint *width, gint *height)
1582 if (!filelist_read(path, &f, &d)) return;
1583 if (!f && !d) return;
1585 f = filelist_sort(f, SORT_NAME, TRUE);
1586 d = filelist_sort(d, SORT_NAME, TRUE);
1588 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1590 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1591 PAN_TEXT_COLOR, 255);
1593 *y += pi_box->height;
1595 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1597 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1598 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1599 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1600 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1602 *x += PAN_FOLDER_BOX_BORDER;
1603 *y += PAN_FOLDER_BOX_BORDER;
1614 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1616 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1617 *x += pi->width + PAN_THUMB_GAP;
1618 if (pi->height > y_height) y_height = pi->height;
1622 pi = pan_item_new_thumb(pw, fd, *x, *y);
1623 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1624 y_height = PAN_THUMB_SIZE;
1627 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1630 if (f) *y = pi_box->y + pi_box->height;
1642 *level = *level + 1;
1643 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1644 pi_box, width, height);
1645 *level = *level - 1;
1650 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1652 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1653 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1655 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1658 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1665 x = PAN_FOLDER_BOX_BORDER;
1666 y = PAN_FOLDER_BOX_BORDER;
1667 w = PAN_FOLDER_BOX_BORDER * 2;
1668 h = PAN_FOLDER_BOX_BORDER * 2;
1670 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1672 if (width) *width = w;
1673 if (height) *height = h;
1677 *-----------------------------------------------------------------------------
1679 *-----------------------------------------------------------------------------
1682 static void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
1688 gint x1, y1, x2, y2, x3, y3;
1693 while ((pi = pan_item_find_by_key(pw, ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
1695 if (!pi_day || pi_day->type != ITEM_BOX ||
1696 !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
1698 list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
1710 if (dot->type != ITEM_BOX || !dot->fd ||
1711 !dot->key || strcmp(dot->key, "dot") != 0)
1713 list = g_list_delete_link(list, node);
1721 grid = (gint)(sqrt(g_list_length(list)) + 0.5);
1723 x = pi_day->x + pi_day->width + 4;
1727 if (y + grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4 > pw->pr->image_height)
1729 y = pw->pr->image_height - (grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4);
1733 pbox = pan_item_new_box(pw, NULL, x, y, PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1734 PAN_CAL_POPUP_BORDER,
1735 PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA,
1736 PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
1737 pan_item_set_key(pbox, "day_bubble");
1744 buf = date_value_string(pi_day->fd->date, DATE_LENGTH_WEEK);
1745 plabel = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1746 PAN_CAL_POPUP_TEXT_COLOR, 255);
1747 pan_item_set_key(plabel, "day_bubble");
1750 pan_item_size_by_item(pbox, plabel, 0);
1752 y += plabel->height;
1759 x += PAN_FOLDER_BOX_BORDER;
1760 y += PAN_FOLDER_BOX_BORDER;
1774 pimg = pan_item_new_thumb(pw, file_data_new_simple(dot->fd->path), x, y);
1775 pan_item_set_key(pimg, "day_bubble");
1777 pan_item_size_by_item(pbox, pimg, PAN_FOLDER_BOX_BORDER);
1782 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1787 x = pbox->x + PAN_FOLDER_BOX_BORDER;
1788 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1794 x1 = pi_day->x + pi_day->width - 8;
1797 y2 = pbox->y + MIN(42, pbox->height);
1799 y3 = MAX(pbox->y, y2 - 30);
1800 util_clip_triangle(x1, y1, x2, y2, x3, y3,
1803 pi = pan_item_new_tri(pw, NULL, x, y, w, h,
1804 x1, y1, x2, y2, x3, y3,
1805 PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA);
1806 pan_item_tri_border(pi, BORDER_1 | BORDER_3, PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
1807 pan_item_set_key(pi, "day_bubble");
1808 pan_item_added(pw, pi);
1810 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
1811 pan_item_added(pw, pbox);
1814 static void pan_window_layout_compute_calendar(PanWindow *pw, const gchar *path, gint *width, gint *height)
1830 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
1832 list = pan_window_layout_list(path, SORT_NONE, TRUE);
1833 list = filelist_sort(list, SORT_TIME, TRUE);
1846 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
1854 if (day_max < count) day_max = count;
1858 printf("biggest day contains %d images\n", day_max);
1860 grid = (gint)(sqrt((double)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
1861 day_width = MAX(PAN_CAL_DAY_WIDTH, grid);
1862 day_height = MAX(PAN_CAL_DAY_HEIGHT, grid);
1866 FileData *fd = list->data;
1868 year = date_value(fd->date, DATE_LENGTH_YEAR);
1869 month = date_value(fd->date, DATE_LENGTH_MONTH);
1872 work = g_list_last(list);
1875 FileData *fd = work->data;
1876 end_year = date_value(fd->date, DATE_LENGTH_YEAR);
1877 end_month = date_value(fd->date, DATE_LENGTH_MONTH);
1880 *width = PAN_FOLDER_BOX_BORDER * 2;
1881 *height = PAN_FOLDER_BOX_BORDER * 2;
1883 x = PAN_FOLDER_BOX_BORDER;
1884 y = PAN_FOLDER_BOX_BORDER;
1887 while (work && (year < end_year || (year == end_year && month <= end_month)))
1898 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
1900 days = date_value(dt, DATE_LENGTH_DAY);
1901 dt = date_to_time(year, month, 1);
1902 col = date_value(dt, DATE_LENGTH_WEEK);
1905 x = PAN_FOLDER_BOX_BORDER;
1907 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
1908 PAN_CAL_MONTH_BORDER,
1909 PAN_CAL_MONTH_COLOR, PAN_CAL_MONTH_ALPHA,
1910 PAN_CAL_MONTH_BORDER_COLOR, PAN_CAL_MONTH_ALPHA);
1911 buf = date_value_string(dt, DATE_LENGTH_MONTH);
1912 pi_text = pan_item_new_text(pw, x, y, buf,
1913 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1914 PAN_CAL_MONTH_TEXT_COLOR, 255);
1916 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
1918 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
1920 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
1921 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
1923 for (day = 1; day <= days; day++)
1930 dt = date_to_time(year, month, day);
1932 fd = g_new0(FileData, 1);
1933 /* path and name must be non NULL, so make them an invalid filename */
1934 fd->path = g_strdup("//");
1937 pi_day = pan_item_new_box(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
1939 PAN_CAL_DAY_COLOR, PAN_CAL_DAY_ALPHA,
1940 PAN_CAL_DAY_BORDER_COLOR, PAN_CAL_DAY_ALPHA);
1941 pan_item_set_key(pi_day, "day");
1943 dx = x + PAN_CAL_DOT_GAP * 2;
1944 dy = y + PAN_CAL_DOT_GAP * 2;
1946 fd = (work) ? work->data : NULL;
1947 while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
1951 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
1953 PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
1955 pan_item_set_key(pi, "dot");
1957 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
1958 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
1960 dx = x + PAN_CAL_DOT_GAP * 2;
1961 dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
1963 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
1965 /* must keep all dots within respective day even if it gets ugly */
1966 dy = y + PAN_CAL_DOT_GAP * 2;
1972 fd = (work) ? work->data : NULL;
1979 pi_day->color_r = MAX(pi_day->color_r - 61 - n * 3, 80);
1980 pi_day->color_g = pi_day->color_r;
1982 buf = g_strdup_printf("( %d )", n);
1983 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
1984 PAN_CAL_DAY_TEXT_COLOR, 255);
1987 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
1988 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
1991 buf = g_strdup_printf("%d", day);
1992 pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1993 PAN_CAL_DAY_TEXT_COLOR, 255);
1997 pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
2004 x = PAN_FOLDER_BOX_BORDER;
2005 y += PAN_CAL_DAY_HEIGHT;
2009 x += PAN_CAL_DAY_WIDTH;
2013 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
2014 y += PAN_FOLDER_BOX_BORDER * 2;
2025 *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
2030 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
2038 PanItem *pi_month = NULL;
2039 PanItem *pi_day = NULL;
2045 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2047 list = pan_window_layout_list(path, SORT_NONE, TRUE);
2048 list = filelist_sort(list, SORT_TIME, TRUE);
2050 *width = PAN_FOLDER_BOX_BORDER * 2;
2051 *height = PAN_FOLDER_BOX_BORDER * 2;
2056 day_start = month_start;
2071 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2076 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
2082 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
2086 x = PAN_FOLDER_BOX_BORDER;
2089 y = PAN_FOLDER_BOX_BORDER;
2091 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
2092 pi = pan_item_new_text(pw, x, y, buf,
2093 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2094 PAN_TEXT_COLOR, 255);
2097 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
2099 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2100 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2101 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2103 x += PAN_FOLDER_BOX_BORDER;
2104 y += PAN_FOLDER_BOX_BORDER;
2108 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
2120 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
2122 needle = needle->next;
2131 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
2132 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2133 PAN_TEXT_COLOR, 255);
2138 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
2139 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2140 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2141 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2143 x += PAN_FOLDER_BOX_BORDER;
2144 y += PAN_FOLDER_BOX_BORDER;
2148 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2150 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
2151 if (pi->width > x_width) x_width = pi->width;
2152 y_height = pi->height;
2156 pi = pan_item_new_thumb(pw, fd, x, y);
2157 x_width = PAN_THUMB_SIZE;
2158 y_height = PAN_THUMB_SIZE;
2161 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
2162 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
2167 if (total > 0 && count < PAN_GROUP_MAX)
2169 y += y_height + PAN_THUMB_GAP;
2173 x += x_width + PAN_THUMB_GAP;
2183 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
2189 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
2190 gint *width, gint *height,
2191 gint *scroll_x, gint *scroll_y)
2193 pan_window_items_free(pw);
2197 case LAYOUT_SIZE_THUMB_DOTS:
2198 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2199 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2201 case LAYOUT_SIZE_THUMB_NONE:
2202 pw->thumb_size = PAN_THUMB_SIZE_NONE;
2203 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2205 case LAYOUT_SIZE_THUMB_SMALL:
2206 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2207 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2209 case LAYOUT_SIZE_THUMB_NORMAL:
2211 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2212 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2214 case LAYOUT_SIZE_THUMB_LARGE:
2215 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2216 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2218 case LAYOUT_SIZE_10:
2219 pw->image_size = 10;
2220 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2222 case LAYOUT_SIZE_25:
2223 pw->image_size = 25;
2224 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2226 case LAYOUT_SIZE_33:
2227 pw->image_size = 33;
2228 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2230 case LAYOUT_SIZE_50:
2231 pw->image_size = 50;
2232 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2234 case LAYOUT_SIZE_100:
2235 pw->image_size = 100;
2236 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2249 pan_window_layout_compute_grid(pw, path, width, height);
2251 case LAYOUT_FOLDERS_LINEAR:
2252 pan_window_layout_compute_folders_linear(pw, path, width, height);
2254 case LAYOUT_FOLDERS_FLOWER:
2255 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2257 case LAYOUT_CALENDAR:
2258 pan_window_layout_compute_calendar(pw, path, width, height);
2260 case LAYOUT_TIMELINE:
2261 pan_window_layout_compute_timeline(pw, path, width, height);
2267 printf("computed %d objects\n", g_list_length(pw->list));
2270 static GList *pan_layout_intersect_l(GList *list, GList *item_list,
2271 gint x, gint y, gint width, gint height)
2279 gint rx, ry, rw, rh;
2284 if (util_clip_region(x, y, width, height,
2285 pi->x, pi->y, pi->width, pi->height,
2286 &rx, &ry, &rw, &rh))
2288 list = g_list_prepend(list, pi);
2295 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2301 grid = pw->list_grid;
2307 if (x < pg->x || x + width > pg->x + pg->w ||
2308 y < pg->y || y + height > pg->y + pg->h)
2314 list = pan_layout_intersect_l(list, pw->list, x, y, width, height);
2318 list = pan_layout_intersect_l(list, pg->list, x, y, width, height);
2322 list = pan_layout_intersect_l(list, pw->list_static, x, y, width, height);
2329 *-----------------------------------------------------------------------------
2331 *-----------------------------------------------------------------------------
2334 static gint pan_layout_queue_step(PanWindow *pw);
2337 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2339 PanWindow *pw = data;
2347 pw->queue_pi = NULL;
2351 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2352 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2355 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2359 thumb_loader_free(pw->tl);
2362 while (pan_layout_queue_step(pw));
2365 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2367 PanWindow *pw = data;
2375 pw->queue_pi = NULL;
2379 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2380 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2381 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2383 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2384 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2385 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2390 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2391 (GdkInterpType)zoom_quality);
2392 g_object_unref(tmp);
2396 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2400 image_loader_free(pw->il);
2403 while (pan_layout_queue_step(pw));
2407 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2408 guint width, guint height, gpointer data)
2410 PanWindow *pw = data;
2421 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2422 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2426 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2432 static gint pan_layout_queue_step(PanWindow *pw)
2436 if (!pw->queue) return FALSE;
2438 pi = pw->queue->data;
2439 pw->queue = g_list_remove(pw->queue, pi);
2442 if (!pw->queue_pi->fd)
2444 pw->queue_pi->queued = FALSE;
2445 pw->queue_pi = NULL;
2449 image_loader_free(pw->il);
2451 thumb_loader_free(pw->tl);
2454 if (pi->type == ITEM_IMAGE)
2456 pw->il = image_loader_new(pi->fd->path);
2458 if (pw->size != LAYOUT_SIZE_100)
2460 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2464 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2466 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2468 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2470 image_loader_free(pw->il);
2473 else if (pi->type == ITEM_THUMB)
2475 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2477 if (!pw->tl->standard_loader)
2479 /* The classic loader will recreate a thumbnail any time we
2480 * request a different size than what exists. This view will
2481 * almost never use the user configured sizes so disable cache.
2483 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2486 thumb_loader_set_callbacks(pw->tl,
2487 pan_layout_queue_thumb_done_cb,
2488 pan_layout_queue_thumb_done_cb,
2491 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2493 thumb_loader_free(pw->tl);
2497 pw->queue_pi->queued = FALSE;
2498 pw->queue_pi = NULL;
2502 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2504 if (!pi || pi->queued || pi->pixbuf) return;
2505 if (pw->size <= LAYOUT_SIZE_THUMB_NONE) return;
2508 pw->queue = g_list_prepend(pw->queue, pi);
2510 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2513 static gint pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
2514 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2516 PanWindow *pw = data;
2521 pixbuf_set_rect_fill(pixbuf,
2522 0, 0, width, height,
2523 PAN_BACKGROUND_COLOR, 255);
2525 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2527 gint rx, ry, rw, rh;
2529 if (util_clip_region(x, y, width, height,
2531 &rx, &ry, &rw, &rh))
2533 pixbuf_draw_rect_fill(pixbuf,
2534 rx - x, ry - y, rw, rh,
2535 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2538 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2540 gint rx, ry, rw, rh;
2542 if (util_clip_region(x, y, width, height,
2544 &rx, &ry, &rw, &rh))
2546 pixbuf_draw_rect_fill(pixbuf,
2547 rx - x, ry - y, rw, rh,
2548 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2552 list = pan_layout_intersect(pw, x, y, width, height);
2557 gint tx, ty, tw, th;
2558 gint rx, ry, rw, rh;
2565 if (pi->type == ITEM_THUMB && pi->pixbuf)
2567 tw = gdk_pixbuf_get_width(pi->pixbuf);
2568 th = gdk_pixbuf_get_height(pi->pixbuf);
2570 tx = pi->x + (pi->width - tw) / 2;
2571 ty = pi->y + (pi->height - th) / 2;
2573 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2575 if (util_clip_region(x, y, width, height,
2576 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2577 &rx, &ry, &rw, &rh))
2579 pixbuf_draw_shadow(pixbuf,
2580 rx - x, ry - y, rw, rh,
2581 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2583 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2588 if (util_clip_region(x, y, width, height,
2589 tx + tw, ty + PAN_SHADOW_OFFSET,
2590 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2591 &rx, &ry, &rw, &rh))
2593 pixbuf_draw_shadow(pixbuf,
2594 rx - x, ry - y, rw, rh,
2595 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2597 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2599 if (util_clip_region(x, y, width, height,
2600 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2601 &rx, &ry, &rw, &rh))
2603 pixbuf_draw_shadow(pixbuf,
2604 rx - x, ry - y, rw, rh,
2605 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2607 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2611 if (util_clip_region(x, y, width, height,
2613 &rx, &ry, &rw, &rh))
2615 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2618 1.0, 1.0, GDK_INTERP_NEAREST,
2622 if (util_clip_region(x, y, width, height,
2623 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2624 &rx, &ry, &rw, &rh))
2626 pixbuf_draw_rect_fill(pixbuf,
2627 rx - x, ry - y, rw, rh,
2628 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2630 if (util_clip_region(x, y, width, height,
2631 tx, ty, PAN_OUTLINE_THICKNESS, th,
2632 &rx, &ry, &rw, &rh))
2634 pixbuf_draw_rect_fill(pixbuf,
2635 rx - x, ry - y, rw, rh,
2636 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2638 if (util_clip_region(x, y, width, height,
2639 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2640 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2641 &rx, &ry, &rw, &rh))
2643 pixbuf_draw_rect_fill(pixbuf,
2644 rx - x, ry - y, rw, rh,
2645 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2647 if (util_clip_region(x, y, width, height,
2648 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2649 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2650 &rx, &ry, &rw, &rh))
2652 pixbuf_draw_rect_fill(pixbuf,
2653 rx - x, ry - y, rw, rh,
2654 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2657 else if (pi->type == ITEM_THUMB)
2659 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2660 th = pi->height - PAN_SHADOW_OFFSET * 2;
2661 tx = pi->x + PAN_SHADOW_OFFSET;
2662 ty = pi->y + PAN_SHADOW_OFFSET;
2664 if (util_clip_region(x, y, width, height,
2666 &rx, &ry, &rw, &rh))
2670 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2671 pixbuf_draw_rect_fill(pixbuf,
2672 rx - x, ry - y, rw, rh,
2674 PAN_SHADOW_ALPHA / d);
2677 pan_layout_queue(pw, pi);
2679 else if (pi->type == ITEM_IMAGE)
2681 if (util_clip_region(x, y, width, height,
2682 pi->x, pi->y, pi->width, pi->height,
2683 &rx, &ry, &rw, &rh))
2687 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2690 1.0, 1.0, GDK_INTERP_NEAREST,
2695 pixbuf_draw_rect_fill(pixbuf,
2696 rx - x, ry - y, rw, rh,
2697 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2698 pan_layout_queue(pw, pi);
2702 else if (pi->type == ITEM_BOX)
2716 if (pi->color_a > 254)
2718 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2719 shadow[0], bh - shadow[0],
2720 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2722 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2723 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2725 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2727 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2732 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2733 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2735 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2737 PAN_SHADOW_COLOR, a);
2741 if (util_clip_region(x, y, width, height,
2742 pi->x, pi->y, bw, bh,
2743 &rx, &ry, &rw, &rh))
2745 pixbuf_draw_rect_fill(pixbuf,
2746 rx - x, ry - y, rw, rh,
2747 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2749 if (util_clip_region(x, y, width, height,
2750 pi->x, pi->y, bw, pi->border,
2751 &rx, &ry, &rw, &rh))
2753 pixbuf_draw_rect_fill(pixbuf,
2754 rx - x, ry - y, rw, rh,
2755 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2757 if (util_clip_region(x, y, width, height,
2758 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
2759 &rx, &ry, &rw, &rh))
2761 pixbuf_draw_rect_fill(pixbuf,
2762 rx - x, ry - y, rw, rh,
2763 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2765 if (util_clip_region(x, y, width, height,
2766 pi->x + bw - pi->border, pi->y + pi->border,
2767 pi->border, bh - pi->border * 2,
2768 &rx, &ry, &rw, &rh))
2770 pixbuf_draw_rect_fill(pixbuf,
2771 rx - x, ry - y, rw, rh,
2772 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2774 if (util_clip_region(x, y, width, height,
2775 pi->x, pi->y + bh - pi->border,
2777 &rx, &ry, &rw, &rh))
2779 pixbuf_draw_rect_fill(pixbuf,
2780 rx - x, ry - y, rw, rh,
2781 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2784 else if (pi->type == ITEM_TRIANGLE)
2786 if (util_clip_region(x, y, width, height,
2787 pi->x, pi->y, pi->width, pi->height,
2788 &rx, &ry, &rw, &rh) && pi->data)
2790 gint *coord = pi->data;
2791 pixbuf_draw_triangle(pixbuf,
2792 rx - x, ry - y, rw, rh,
2793 coord[0] - x, coord[1] - y,
2794 coord[2] - x, coord[3] - y,
2795 coord[4] - x, coord[5] - y,
2796 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2798 if (pi->border & BORDER_1)
2800 pixbuf_draw_line(pixbuf,
2801 rx - x, ry - y, rw, rh,
2802 coord[0] - x, coord[1] - y,
2803 coord[2] - x, coord[3] - y,
2804 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2806 if (pi->border & BORDER_2)
2808 pixbuf_draw_line(pixbuf,
2809 rx - x, ry - y, rw, rh,
2810 coord[2] - x, coord[3] - y,
2811 coord[4] - x, coord[5] - y,
2812 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2814 if (pi->border & BORDER_3)
2816 pixbuf_draw_line(pixbuf,
2817 rx - x, ry - y, rw, rh,
2818 coord[4] - x, coord[5] - y,
2819 coord[0] - x, coord[1] - y,
2820 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2824 else if (pi->type == ITEM_TEXT && pi->text)
2826 PangoLayout *layout;
2828 layout = pan_item_text_layout(pi, (GtkWidget *)pr);
2829 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
2830 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
2831 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2832 g_object_unref(G_OBJECT(layout));
2838 if (x%512 == 0 && y%512 == 0)
2840 PangoLayout *layout;
2843 layout = gtk_widget_create_pango_layout((GtkWidget *)pr, NULL);
2845 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
2846 (x / pr->source_tile_width) +
2847 (y / pr->source_tile_height * (pr->image_width/pr->source_tile_width + 1)));
2848 pango_layout_set_text(layout, buf, -1);
2851 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr, 0, 0, 0, 0, 0, 255);
2853 g_object_unref(G_OBJECT(layout));
2860 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
2861 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2863 PanWindow *pw = data;
2867 list = pan_layout_intersect(pw, x, y, width, height);
2876 if (pi->refcount > 0)
2880 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
2885 pw->queue = g_list_remove(pw->queue, pi);
2888 if (pw->queue_pi == pi) pw->queue_pi = NULL;
2891 g_object_unref(pi->pixbuf);
2903 *-----------------------------------------------------------------------------
2905 *-----------------------------------------------------------------------------
2908 static void pan_window_message(PanWindow *pw, const gchar *text)
2918 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
2922 work = pw->list_static;
2923 if (pw->layout == LAYOUT_CALENDAR)
2933 pi->type == ITEM_BOX &&
2934 pi->key && strcmp(pi->key, "dot") == 0)
2936 size += pi->fd->size;
2951 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
2953 size += pi->fd->size;
2959 ss = text_from_size_abrev(size);
2960 buf = g_strdup_printf(_("%d images, %s"), count, ss);
2962 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
2966 static void pan_window_zoom_limit(PanWindow *pw)
2972 case LAYOUT_SIZE_THUMB_DOTS:
2973 case LAYOUT_SIZE_THUMB_NONE:
2974 case LAYOUT_SIZE_THUMB_SMALL:
2975 case LAYOUT_SIZE_THUMB_NORMAL:
2977 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
2981 case LAYOUT_SIZE_THUMB_LARGE:
2984 case LAYOUT_SIZE_10:
2985 case LAYOUT_SIZE_25:
2988 case LAYOUT_SIZE_33:
2989 case LAYOUT_SIZE_50:
2990 case LAYOUT_SIZE_100:
2996 image_zoom_set_limits(pw->imd, min, 32.0);
2999 static gint pan_window_layout_update_idle_cb(gpointer data)
3001 PanWindow *pw = data;
3007 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
3009 if (!pw->cache_list && !pw->cache_todo)
3011 pan_cache_fill(pw, pw->path);
3014 pan_window_message(pw, _("Reading dimensions..."));
3018 if (pan_cache_step(pw))
3022 if (pw->cache_count == pw->cache_total)
3024 pan_window_message(pw, _("Sorting images..."));
3026 else if (pw->cache_tick > 9)
3030 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
3031 pw->cache_total - pw->cache_count);
3032 pan_window_message(pw, buf);
3042 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3044 pan_window_zoom_limit(pw);
3046 if (width > 0 && height > 0)
3050 printf("Canvas size is %d x %d\n", width, height);
3052 pan_grid_build(pw, width, height, 1000);
3054 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
3055 PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
3056 pan_window_request_tile_cb,
3057 pan_window_dispose_tile_cb, pw, 1.0);
3059 if (scroll_x == 0 && scroll_y == 0)
3067 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
3070 pan_window_message(pw, NULL);
3077 static void pan_window_layout_update_idle(PanWindow *pw)
3079 if (pw->idle_id == -1)
3081 pan_window_message(pw, _("Sorting images..."));
3082 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3087 *-----------------------------------------------------------------------------
3088 * pan window keyboard
3089 *-----------------------------------------------------------------------------
3092 static const gchar *pan_menu_click_path(PanWindow *pw)
3094 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3098 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3100 PanWindow *pw = data;
3102 gdk_window_get_origin(pw->imd->pr->window, x, y);
3103 popup_menu_position_clamp(menu, x, y, 0);
3106 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3108 PanWindow *pw = data;
3111 gint stop_signal = FALSE;
3117 pr = PIXBUF_RENDERER(pw->imd->pr);
3118 path = pan_menu_click_path(pw);
3120 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pw->imd->widget)));
3124 switch (event->keyval)
3126 case GDK_Left: case GDK_KP_Left:
3130 case GDK_Right: case GDK_KP_Right:
3134 case GDK_Up: case GDK_KP_Up:
3138 case GDK_Down: case GDK_KP_Down:
3142 case GDK_Page_Up: case GDK_KP_Page_Up:
3143 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
3145 case GDK_Page_Down: case GDK_KP_Page_Down:
3146 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
3148 case GDK_Home: case GDK_KP_Home:
3149 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
3151 case GDK_End: case GDK_KP_End:
3152 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
3157 if (focused && !(event->state & GDK_CONTROL_MASK) )
3158 switch (event->keyval)
3160 case '+': case '=': case GDK_KP_Add:
3161 pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
3163 case '-': case GDK_KP_Subtract:
3164 pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
3166 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3167 pixbuf_renderer_zoom_set(pr, 1.0);
3170 pixbuf_renderer_zoom_set(pr, 2.0);
3173 pixbuf_renderer_zoom_set(pr, 3.0);
3176 pixbuf_renderer_zoom_set(pr, 4.0);
3179 pixbuf_renderer_zoom_set(pr, -4.0);
3182 pixbuf_renderer_zoom_set(pr, -3.0);
3185 pixbuf_renderer_zoom_set(pr, -2.0);
3189 pan_fullscreen_toggle(pw, FALSE);
3194 pan_overlay_toggle(pw);
3197 case GDK_Delete: case GDK_KP_Delete:
3202 if (GTK_WIDGET_VISIBLE(pw->search_box))
3204 gtk_widget_grab_focus(pw->search_entry);
3208 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3216 pan_fullscreen_toggle(pw, TRUE);
3219 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3221 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3227 menu = pan_popup_menu(pw);
3228 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3233 if (event->state & GDK_CONTROL_MASK)
3236 switch (event->keyval)
3269 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3272 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3275 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3278 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3281 if (path) info_window_new(path, NULL);
3284 pan_window_close(pw);
3287 if (n != -1 && path)
3289 pan_fullscreen_toggle(pw, TRUE);
3290 start_editor_from_file(n, path);
3294 else if (event->state & GDK_SHIFT_MASK)
3301 switch (event->keyval)
3306 pan_fullscreen_toggle(pw, TRUE);
3309 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3311 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
3312 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3321 if (x != 0 || y!= 0)
3323 keyboard_scroll_calc(&x, &y, event);
3324 pixbuf_renderer_scroll(pr, x, y);
3331 *-----------------------------------------------------------------------------
3333 *-----------------------------------------------------------------------------
3336 static void pan_info_update(PanWindow *pw, PanItem *pi)
3342 gint x1, y1, x2, y2, x3, y3;
3345 if (pw->click_pi == pi) return;
3346 if (pi && !pi->fd) pi = NULL;
3348 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3353 if (debug) printf("info set to %s\n", pi->fd->path);
3355 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3357 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3358 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3359 pan_item_set_key(pbox, "info");
3361 if (pi->type == ITEM_THUMB && pi->pixbuf)
3363 w = gdk_pixbuf_get_width(pi->pixbuf);
3364 h = gdk_pixbuf_get_height(pi->pixbuf);
3366 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3367 y1 = pi->y + (pi->height - h) / 2 + 8;
3371 x1 = pi->x + pi->width - 8;
3379 util_clip_triangle(x1, y1, x2, y2, x3, y3,
3382 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3383 x1, y1, x2, y2, x3, y3,
3384 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3385 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3386 pan_item_set_key(p, "info");
3387 pan_item_added(pw, p);
3389 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3390 _("Filename:"), TEXT_ATTR_BOLD,
3391 PAN_POPUP_TEXT_COLOR, 255);
3392 pan_item_set_key(plabel, "info");
3393 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3394 pi->fd->name, TEXT_ATTR_NONE,
3395 PAN_POPUP_TEXT_COLOR, 255);
3396 pan_item_set_key(p, "info");
3397 pan_item_size_by_item(pbox, p, 0);
3399 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3400 _("Date:"), TEXT_ATTR_BOLD,
3401 PAN_POPUP_TEXT_COLOR, 255);
3402 pan_item_set_key(plabel, "info");
3403 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3404 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3405 PAN_POPUP_TEXT_COLOR, 255);
3406 pan_item_set_key(p, "info");
3407 pan_item_size_by_item(pbox, p, 0);
3409 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3410 _("Size:"), TEXT_ATTR_BOLD,
3411 PAN_POPUP_TEXT_COLOR, 255);
3412 pan_item_set_key(plabel, "info");
3413 buf = text_from_size(pi->fd->size);
3414 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3415 buf, TEXT_ATTR_NONE,
3416 PAN_POPUP_TEXT_COLOR, 255);
3418 pan_item_set_key(p, "info");
3419 pan_item_size_by_item(pbox, p, 0);
3421 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3422 pan_item_added(pw, pbox);
3427 *-----------------------------------------------------------------------------
3429 *-----------------------------------------------------------------------------
3432 static void pan_search_status(PanWindow *pw, const gchar *text)
3434 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3437 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3445 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3447 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3448 if (!list) return FALSE;
3450 found = g_list_find(list, pw->click_pi);
3451 if (found && found->next)
3453 found = found->next;
3461 pan_info_update(pw, pi);
3462 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3464 buf = g_strdup_printf("%s ( %d / %d )",
3465 (path[0] == '/') ? _("path found") : _("filename found"),
3466 g_list_index(list, pi) + 1,
3467 g_list_length(list));
3468 pan_search_status(pw, buf);
3476 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3484 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3486 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3487 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3492 needle = g_utf8_strdown(text, -1);
3493 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3496 if (!list) return FALSE;
3498 found = g_list_find(list, pw->click_pi);
3499 if (found && found->next)
3501 found = found->next;
3509 pan_info_update(pw, pi);
3510 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3512 buf = g_strdup_printf("%s ( %d / %d )",
3514 g_list_index(list, pi) + 1,
3515 g_list_length(list));
3516 pan_search_status(pw, buf);
3524 static gint valid_date_separator(gchar c)
3526 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3529 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
3530 gint year, gint month, gint day,
3536 work = g_list_last(pw->list_static);
3544 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
3545 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
3549 tl = localtime(&pi->fd->date);
3554 match = (tl->tm_year == year - 1900);
3555 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3556 if (match && day > 0) match = (tl->tm_mday == day);
3558 if (match) list = g_list_prepend(list, pi);
3563 return g_list_reverse(list);
3566 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3582 if (!text) return FALSE;
3584 ptr = (gchar *)text;
3585 while (*ptr != '\0')
3587 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3592 if (t == -1) return FALSE;
3594 if (!lt) return FALSE;
3596 if (valid_date_separator(*text))
3599 mptr = (gchar *)text;
3603 year = (gint)strtol(text, &mptr, 10);
3604 if (mptr == text) return FALSE;
3607 if (*mptr != '\0' && valid_date_separator(*mptr))
3612 month = strtol(mptr, &dptr, 10);
3615 if (valid_date_separator(*dptr))
3617 month = lt->tm_mon + 1;
3625 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3629 day = strtol(dptr, &eptr, 10);
3639 year = lt->tm_year + 1900;
3641 else if (year < 100)
3650 month < -1 || month == 0 || month > 12 ||
3651 day < -1 || day == 0 || day > 31) return FALSE;
3653 t = date_to_time(year, month, day);
3654 if (t < 0) return FALSE;
3656 if (pw->layout == LAYOUT_CALENDAR)
3658 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
3664 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3665 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
3670 found = g_list_find(list, pw->search_pi);
3671 if (found && found->next)
3673 found = found->next;
3684 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
3686 pan_info_update(pw, NULL);
3687 pan_calendar_update(pw, pi);
3688 image_scroll_to_point(pw->imd,
3689 pi->x + pi->width / 2,
3690 pi->y + pi->height / 2, 0.5, 0.5);
3694 pan_info_update(pw, pi);
3695 image_scroll_to_point(pw->imd,
3696 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3702 buf = date_value_string(t, DATE_LENGTH_MONTH);
3707 buf = g_strdup_printf("%d %s", day, tmp);
3713 buf = date_value_string(t, DATE_LENGTH_YEAR);
3718 buf_count = g_strdup_printf("( %d / %d )",
3719 g_list_index(list, pi) + 1,
3720 g_list_length(list));
3724 buf_count = g_strdup_printf("(%s)", _("no match"));
3727 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
3730 pan_search_status(pw, message);
3738 static void pan_search_activate_cb(const gchar *text, gpointer data)
3740 PanWindow *pw = data;
3744 tab_completion_append_to_history(pw->search_entry, text);
3746 if (pan_search_by_path(pw, text)) return;
3748 if ((pw->layout == LAYOUT_TIMELINE ||
3749 pw->layout == LAYOUT_CALENDAR) &&
3750 pan_search_by_date(pw, text))
3755 if (pan_search_by_partial(pw, text)) return;
3757 pan_search_status(pw, _("no match"));
3760 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3762 PanWindow *pw = data;
3765 visible = GTK_WIDGET_VISIBLE(pw->search_box);
3766 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3770 gtk_widget_hide(pw->search_box);
3771 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3775 gtk_widget_show(pw->search_box);
3776 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3777 gtk_widget_grab_focus(pw->search_entry);
3783 *-----------------------------------------------------------------------------
3784 * view window main routines
3785 *-----------------------------------------------------------------------------
3788 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
3790 PanWindow *pw = data;
3798 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
3799 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
3802 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3805 switch (event->button)
3808 pan_info_update(pw, pi);
3810 if (!pi && pw->layout == LAYOUT_CALENDAR)
3812 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
3813 pan_calendar_update(pw, pi);
3819 pan_info_update(pw, pi);
3820 menu = pan_popup_menu(pw);
3821 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
3828 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
3831 PanWindow *pw = data;
3838 if (!(event->state & GDK_SHIFT_MASK))
3844 if (event->state & GDK_CONTROL_MASK)
3846 switch (event->direction)
3849 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
3850 (gint)event->x, (gint)event->y);
3852 case GDK_SCROLL_DOWN:
3853 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
3854 (gint)event->x, (gint)event->y);
3862 switch (event->direction)
3865 pixbuf_renderer_scroll(pr, 0, -h);
3867 case GDK_SCROLL_DOWN:
3868 pixbuf_renderer_scroll(pr, 0, h);
3870 case GDK_SCROLL_LEFT:
3871 pixbuf_renderer_scroll(pr, -w, 0);
3873 case GDK_SCROLL_RIGHT:
3874 pixbuf_renderer_scroll(pr, w, 0);
3882 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
3884 g_signal_connect(G_OBJECT(imd->pr), "clicked",
3885 G_CALLBACK(button_cb), pw);
3886 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
3887 G_CALLBACK(scroll_cb), pw);
3890 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
3892 PanWindow *pw = data;
3895 pw->imd = pw->imd_normal;
3898 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
3900 if (force_off && !pw->fs) return;
3904 fullscreen_stop(pw->fs);
3908 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
3909 pan_image_set_buttons(pw, pw->fs->imd);
3910 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
3911 G_CALLBACK(pan_window_key_press_cb), pw);
3913 pw->imd = pw->fs->imd;
3917 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
3919 PanWindow *pw = data;
3922 text = image_zoom_get_as_text(pw->imd);
3923 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
3927 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
3929 PanWindow *pw = data;
3934 if (pr->scale == 0.0) return;
3936 pixbuf_renderer_get_visible_rect(pr, &rect);
3937 pixbuf_renderer_get_image_size(pr, &width, &height);
3939 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
3940 adj->page_size = (gdouble)rect.width;
3941 adj->page_increment = adj->page_size / 2.0;
3942 adj->step_increment = 48.0 / pr->scale;
3944 adj->upper = MAX((gdouble)width, 1.0);
3945 adj->value = (gdouble)rect.x;
3947 pref_signal_block_data(pw->scrollbar_h, pw);
3948 gtk_adjustment_changed(adj);
3949 gtk_adjustment_value_changed(adj);
3950 pref_signal_unblock_data(pw->scrollbar_h, pw);
3952 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
3953 adj->page_size = (gdouble)rect.height;
3954 adj->page_increment = adj->page_size / 2.0;
3955 adj->step_increment = 48.0 / pr->scale;
3957 adj->upper = MAX((gdouble)height, 1.0);
3958 adj->value = (gdouble)rect.y;
3960 pref_signal_block_data(pw->scrollbar_v, pw);
3961 gtk_adjustment_changed(adj);
3962 gtk_adjustment_value_changed(adj);
3963 pref_signal_unblock_data(pw->scrollbar_v, pw);
3966 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
3968 PanWindow *pw = data;
3972 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
3974 if (!pr->scale) return;
3976 x = (gint)gtk_range_get_value(range);
3978 pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
3981 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
3983 PanWindow *pw = data;
3987 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
3989 if (!pr->scale) return;
3991 y = (gint)gtk_range_get_value(range);
3993 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
3996 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
3998 PanWindow *pw = data;
4000 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4001 pan_window_layout_update_idle(pw);
4004 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
4006 PanWindow *pw = data;
4008 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
4009 pan_window_layout_update_idle(pw);
4012 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
4014 PanWindow *pw = data;
4017 path = remove_trailing_slash(new_text);
4018 parse_out_relatives(path);
4022 warning_dialog(_("Folder not found"),
4023 _("The entered path is not a folder"),
4024 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4028 tab_completion_append_to_history(pw->path_entry, path);
4031 pw->path = g_strdup(path);
4033 pan_window_layout_update_idle(pw);
4036 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4038 PanWindow *pw = data;
4041 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4043 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4044 pan_window_entry_activate_cb(text, pw);
4048 static void pan_window_close(PanWindow *pw)
4050 pan_window_list = g_list_remove(pan_window_list, pw);
4052 if (pw->idle_id != -1)
4054 g_source_remove(pw->idle_id);
4057 pan_fullscreen_toggle(pw, TRUE);
4058 gtk_widget_destroy(pw->window);
4060 pan_window_items_free(pw);
4067 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4069 PanWindow *pw = data;
4071 pan_window_close(pw);
4075 static void pan_window_new_real(const gchar *path)
4084 GdkGeometry geometry;
4086 pw = g_new0(PanWindow, 1);
4088 pw->path = g_strdup(path);
4089 pw->layout = LAYOUT_TIMELINE;
4090 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4091 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4092 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4095 pw->list_static = NULL;
4096 pw->list_grid = NULL;
4099 pw->overlay_id = -1;
4102 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4104 geometry.min_width = 8;
4105 geometry.min_height = 8;
4106 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4108 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4109 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
4110 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4111 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4113 window_set_icon(pw->window, NULL, NULL);
4115 vbox = gtk_vbox_new(FALSE, 0);
4116 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4117 gtk_widget_show(vbox);
4119 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4121 pref_spacer(box, 0);
4122 pref_label_new(box, _("Location:"));
4123 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
4124 pan_window_entry_activate_cb, pw);
4125 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4126 G_CALLBACK(pan_window_entry_change_cb), pw);
4127 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4128 gtk_widget_show(combo);
4130 combo = gtk_combo_box_new_text();
4131 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4132 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4133 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4134 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4135 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4137 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4138 g_signal_connect(G_OBJECT(combo), "changed",
4139 G_CALLBACK(pan_window_layout_change_cb), pw);
4140 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4141 gtk_widget_show(combo);
4143 combo = gtk_combo_box_new_text();
4144 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4145 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4146 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4147 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4148 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4149 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4150 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4151 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4152 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4153 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4155 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4156 g_signal_connect(G_OBJECT(combo), "changed",
4157 G_CALLBACK(pan_window_layout_size_cb), pw);
4158 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4159 gtk_widget_show(combo);
4161 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4162 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4163 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4165 pw->imd = image_new(TRUE);
4166 pw->imd_normal = pw->imd;
4168 g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
4169 G_CALLBACK(pan_window_image_zoom_cb), pw);
4170 g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
4171 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4173 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4174 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4175 gtk_widget_show(GTK_WIDGET(pw->imd->widget));
4177 pan_window_dnd_init(pw);
4179 pan_image_set_buttons(pw, pw->imd);
4181 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4182 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4183 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4184 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4185 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4186 gtk_widget_show(pw->scrollbar_h);
4188 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4189 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4190 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4191 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4192 0, GTK_FILL | GTK_EXPAND, 0, 0);
4193 gtk_widget_show(pw->scrollbar_v);
4197 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4198 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4200 pref_spacer(pw->search_box, 0);
4201 pref_label_new(pw->search_box, _("Find:"));
4203 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4204 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4205 gtk_widget_show(hbox);
4207 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4208 pan_search_activate_cb, pw);
4209 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4210 gtk_widget_show(combo);
4212 pw->search_label = gtk_label_new("");
4213 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4214 gtk_widget_show(pw->search_label);
4218 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4220 frame = gtk_frame_new(NULL);
4221 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4222 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4223 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4224 gtk_widget_show(frame);
4226 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4227 gtk_container_add(GTK_CONTAINER(frame), hbox);
4228 gtk_widget_show(hbox);
4230 pref_spacer(hbox, 0);
4231 pw->label_message = pref_label_new(hbox, "");
4233 frame = gtk_frame_new(NULL);
4234 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4235 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4236 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4237 gtk_widget_show(frame);
4239 pw->label_zoom = gtk_label_new("");
4240 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4241 gtk_widget_show(pw->label_zoom);
4243 pw->search_button = gtk_toggle_button_new();
4244 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4245 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4246 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4247 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4248 gtk_widget_show(hbox);
4249 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4250 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4251 gtk_widget_show(pw->search_button_arrow);
4252 pref_label_new(hbox, _("Find"));
4254 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4255 gtk_widget_show(pw->search_button);
4256 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4257 G_CALLBACK(pan_search_toggle_cb), pw);
4259 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4260 G_CALLBACK(pan_window_delete_cb), pw);
4261 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4262 G_CALLBACK(pan_window_key_press_cb), pw);
4264 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4266 pan_window_layout_update_idle(pw);
4268 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4269 gtk_widget_show(pw->window);
4271 pan_window_list = g_list_append(pan_window_list, pw);
4275 *-----------------------------------------------------------------------------
4276 * peformance warnings
4277 *-----------------------------------------------------------------------------
4280 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4284 generic_dialog_close(gd);
4286 pan_window_new_real(path);
4290 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4294 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4295 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4298 static gint pan_warning(const gchar *path)
4304 GtkWidget *ct_button;
4307 if (enable_thumb_caching &&
4308 thumbnail_spec_standard) return FALSE;
4310 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4311 if (hide_dlg) return FALSE;
4313 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4315 gd->data = g_strdup(path);
4316 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4317 pan_warning_ok_cb, TRUE);
4319 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4320 _("Pan view performance may be poor."),
4321 _("To improve performance of thumbnails in the pan view the"
4322 " following options can be enabled. Note that both options"
4323 " must be enabled to notice a change in performance."));
4325 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4326 pref_spacer(group, PREF_PAD_INDENT);
4327 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4329 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4330 enable_thumb_caching, &enable_thumb_caching);
4331 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4332 thumbnail_spec_standard, &thumbnail_spec_standard);
4333 pref_checkbox_link_sensitivity(ct_button, button);
4337 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4338 G_CALLBACK(pan_warning_hide_cb), NULL);
4340 gtk_widget_show(gd->dialog);
4347 *-----------------------------------------------------------------------------
4349 *-----------------------------------------------------------------------------
4352 void pan_window_new(const gchar *path)
4354 if (pan_warning(path)) return;
4356 pan_window_new_real(path);
4360 *-----------------------------------------------------------------------------
4362 *-----------------------------------------------------------------------------
4365 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4367 PanWindow *pw = data;
4370 path = pan_menu_click_path(pw);
4373 pan_fullscreen_toggle(pw, TRUE);
4374 view_window_new(path);
4378 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4384 pw = submenu_item_get_data(widget);
4385 n = GPOINTER_TO_INT(data);
4388 path = pan_menu_click_path(pw);
4391 pan_fullscreen_toggle(pw, TRUE);
4392 start_editor_from_file(n, path);
4396 static void pan_info_cb(GtkWidget *widget, gpointer data)
4398 PanWindow *pw = data;
4401 path = pan_menu_click_path(pw);
4402 if (path) info_window_new(path, NULL);
4405 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4407 PanWindow *pw = data;
4409 image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
4412 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4414 PanWindow *pw = data;
4416 image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
4419 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4421 PanWindow *pw = data;
4423 image_zoom_set(pw->imd, 1.0);
4426 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4428 PanWindow *pw = data;
4431 path = pan_menu_click_path(pw);
4432 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4435 static void pan_move_cb(GtkWidget *widget, gpointer data)
4437 PanWindow *pw = data;
4440 path = pan_menu_click_path(pw);
4441 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4444 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4446 PanWindow *pw = data;
4449 path = pan_menu_click_path(pw);
4450 if (path) file_util_rename(path, NULL, pw->imd->widget);
4453 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4455 PanWindow *pw = data;
4458 path = pan_menu_click_path(pw);
4459 if (path) file_util_delete(path, NULL, pw->imd->widget);
4462 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4464 PanWindow *pw = data;
4466 pan_fullscreen_toggle(pw, FALSE);
4469 static void pan_close_cb(GtkWidget *widget, gpointer data)
4471 PanWindow *pw = data;
4473 pan_window_close(pw);
4476 static GtkWidget *pan_popup_menu(PanWindow *pw)
4482 active = (pw->click_pi != NULL);
4484 menu = popup_menu_short_lived();
4486 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4487 G_CALLBACK(pan_zoom_in_cb), pw);
4488 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4489 G_CALLBACK(pan_zoom_out_cb), pw);
4490 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4491 G_CALLBACK(pan_zoom_1_1_cb), pw);
4492 menu_item_add_divider(menu);
4494 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4495 gtk_widget_set_sensitive(item, active);
4497 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4498 G_CALLBACK(pan_info_cb), pw);
4500 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4501 G_CALLBACK(pan_new_window_cb), pw);
4503 menu_item_add_divider(menu);
4504 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4505 G_CALLBACK(pan_copy_cb), pw);
4506 menu_item_add_sensitive(menu, _("_Move..."), active,
4507 G_CALLBACK(pan_move_cb), pw);
4508 menu_item_add_sensitive(menu, _("_Rename..."), active,
4509 G_CALLBACK(pan_rename_cb), pw);
4510 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4511 G_CALLBACK(pan_delete_cb), pw);
4513 menu_item_add_divider(menu);
4517 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4521 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4524 menu_item_add_divider(menu);
4525 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4531 *-----------------------------------------------------------------------------
4533 *-----------------------------------------------------------------------------
4536 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4538 GtkSelectionData *selection_data, guint info,
4539 guint time, gpointer data)
4541 PanWindow *pw = data;
4543 if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
4545 if (info == TARGET_URI_LIST)
4549 list = uri_list_from_text(selection_data->data, TRUE);
4550 if (list && isdir((gchar *)list->data))
4552 gchar *path = list->data;
4555 pw->path = g_strdup(path);
4557 pan_window_layout_update_idle(pw);
4560 path_list_free(list);
4564 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4565 GtkSelectionData *selection_data, guint info,
4566 guint time, gpointer data)
4568 PanWindow *pw = data;
4571 path = pan_menu_click_path(pw);
4581 case TARGET_URI_LIST:
4584 case TARGET_TEXT_PLAIN:
4589 list = g_list_append(NULL, (gchar *)path);
4590 text = uri_text_from_list(list, &len, plain_text);
4594 gtk_selection_data_set (selection_data, selection_data->target,
4601 gtk_selection_data_set (selection_data, selection_data->target,
4606 static void pan_window_dnd_init(PanWindow *pw)
4610 widget = pw->imd->pr;
4612 gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
4613 dnd_file_drag_types, dnd_file_drag_types_count,
4614 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4615 g_signal_connect(G_OBJECT(widget), "drag_data_get",
4616 G_CALLBACK(pan_window_set_dnd_data), pw);
4618 gtk_drag_dest_set(widget,
4619 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4620 dnd_file_drop_types, dnd_file_drop_types_count,
4621 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4622 g_signal_connect(G_OBJECT(widget), "drag_data_received",
4623 G_CALLBACK(pan_window_get_dnd_data), pw);
4627 *-----------------------------------------------------------------------------
4628 * maintenance (for rename, move, remove)
4629 *-----------------------------------------------------------------------------