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 255, 255, 230
71 #define PAN_GRID_SIZE 10
72 #define PAN_GRID_COLOR 0, 0, 0
73 #define PAN_GRID_ALPHA 20
75 #define PAN_FOLDER_BOX_COLOR 0, 0, 255
76 #define PAN_FOLDER_BOX_ALPHA 10
77 #define PAN_FOLDER_BOX_BORDER 20
79 #define PAN_FOLDER_BOX_OUTLINE_THICKNESS 4
80 #define PAN_FOLDER_BOX_OUTLINE_COLOR 0, 0, 255
81 #define PAN_FOLDER_BOX_OUTLINE_ALPHA 64
83 #define PAN_TEXT_BORDER_SIZE 4
84 #define PAN_TEXT_COLOR 0, 0, 0
86 #define PAN_POPUP_COLOR 255, 255, 220
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_GROUP_MAX 16
94 #define ZOOM_INCREMENT 1.0
95 #define ZOOM_LABEL_WIDTH 64
98 #define PAN_PREF_GROUP "pan_view_options"
99 #define PAN_PREF_HIDE_WARNING "hide_performance_warning"
105 LAYOUT_FOLDERS_LINEAR,
106 LAYOUT_FOLDERS_FLOWER,
111 LAYOUT_SIZE_THUMB_DOTS = 0,
112 LAYOUT_SIZE_THUMB_NONE,
113 LAYOUT_SIZE_THUMB_SMALL,
114 LAYOUT_SIZE_THUMB_NORMAL,
115 LAYOUT_SIZE_THUMB_LARGE,
134 TEXT_ATTR_BOLD = 1 << 0,
135 TEXT_ATTR_HEADING = 1 << 1,
136 TEXT_ATTR_MARKUP = 1 << 2
147 typedef struct _PanItem PanItem;
162 TextAttrType text_attr;
180 typedef struct _PanWindow PanWindow;
185 ImageWindow *imd_normal;
188 GtkWidget *path_entry;
190 GtkWidget *label_message;
191 GtkWidget *label_zoom;
193 GtkWidget *search_box;
194 GtkWidget *search_entry;
195 GtkWidget *search_label;
196 GtkWidget *search_button;
197 GtkWidget *search_button_arrow;
199 GtkWidget *scrollbar_h;
200 GtkWidget *scrollbar_v;
232 typedef struct _PanGrid PanGrid;
241 typedef struct _PanCacheData PanCacheData;
242 struct _PanCacheData {
248 static GList *pan_window_list = NULL;
251 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend);
253 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
255 static GtkWidget *pan_popup_menu(PanWindow *pw);
256 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off);
258 static void pan_window_close(PanWindow *pw);
260 static void pan_window_dnd_init(PanWindow *pw);
272 static gint date_compare(time_t a, time_t b, DateLengthType length)
277 if (length == DATE_LENGTH_EXACT) return (a == b);
279 if (!localtime_r(&a, &ta) ||
280 !localtime_r(&b, &tb)) return FALSE;
282 if (ta.tm_year != tb.tm_year) return FALSE;
283 if (length == DATE_LENGTH_YEAR) return TRUE;
285 if (ta.tm_mon != tb.tm_mon) return FALSE;
286 if (length == DATE_LENGTH_MONTH) return TRUE;
288 if (length == DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
290 if (ta.tm_mday != tb.tm_mday) return FALSE;
291 if (length == DATE_LENGTH_DAY) return TRUE;
293 return (ta.tm_hour == tb.tm_hour);
296 static gint date_value(time_t d, DateLengthType length)
300 if (!localtime_r(&d, &td)) return -1;
304 case DATE_LENGTH_DAY:
307 case DATE_LENGTH_WEEK:
310 case DATE_LENGTH_MONTH:
311 return td.tm_mon + 1;
313 case DATE_LENGTH_YEAR:
314 return td.tm_year + 1900;
316 case DATE_LENGTH_EXACT:
324 static gchar *date_value_string(time_t d, DateLengthType length)
328 gchar *format = NULL;
330 if (!localtime_r(&d, &td)) return g_strdup("");
334 case DATE_LENGTH_DAY:
335 return g_strdup_printf("%d", td.tm_mday);
337 case DATE_LENGTH_WEEK:
340 case DATE_LENGTH_MONTH:
343 case DATE_LENGTH_YEAR:
344 return g_strdup_printf("%d", td.tm_year + 1900);
346 case DATE_LENGTH_EXACT:
348 return g_strdup(text_from_time(d));
353 if (format && strftime(buf, sizeof(buf), format, &td) > 0)
355 gchar *ret = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
362 static time_t date_to_time(gint year, gint month, gint day)
369 lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
370 lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
371 lt.tm_year = year - 1900;
378 *-----------------------------------------------------------------------------
380 *-----------------------------------------------------------------------------
383 static void pan_cache_free(PanWindow *pw)
387 work = pw->cache_list;
395 cache_sim_data_free(pc->cd);
396 file_data_free((FileData *)pc);
399 g_list_free(pw->cache_list);
400 pw->cache_list = NULL;
402 filelist_free(pw->cache_todo);
403 pw->cache_todo = NULL;
410 static void pan_cache_fill(PanWindow *pw, const gchar *path)
416 list = pan_window_layout_list(path, SORT_NAME, TRUE);
417 pw->cache_todo = g_list_reverse(list);
419 pw->cache_total = g_list_length(pw->cache_todo);
422 static gint pan_cache_step(PanWindow *pw)
426 CacheData *cd = NULL;
428 if (!pw->cache_todo) return FALSE;
430 fd = pw->cache_todo->data;
431 pw->cache_todo = g_list_remove(pw->cache_todo, fd);
433 if (enable_thumb_caching)
437 found = cache_find_location(CACHE_TYPE_SIM, fd->path);
438 if (found && filetime(found) == fd->date)
440 cd = cache_sim_data_load(found);
445 if (!cd) cd = cache_sim_data_new();
449 cd->dimensions = image_load_dimensions(fd->path, &cd->width, &cd->height);
450 if (enable_thumb_caching &&
456 base = cache_get_location(CACHE_TYPE_SIM, fd->path, FALSE, &mode);
457 if (cache_ensure_dir_exists(base, mode))
460 cd->path = cache_get_location(CACHE_TYPE_SIM, fd->path, TRUE, NULL);
461 if (cache_sim_data_save(cd))
463 filetime_set(cd->path, filetime(fd->path));
472 pc = g_new0(PanCacheData, 1);
473 memcpy(pc, fd, sizeof(FileData));
478 pw->cache_list = g_list_prepend(pw->cache_list, pc);
484 *-----------------------------------------------------------------------------
486 *-----------------------------------------------------------------------------
489 static void pan_grid_clear(PanWindow *pw)
493 work = pw->list_grid;
501 g_list_free(pg->list);
505 g_list_free(pw->list_grid);
506 pw->list_grid = NULL;
508 pw->list = g_list_concat(pw->list, pw->list_static);
509 pw->list_static = NULL;
512 static void pan_grid_build(PanWindow *pw, gint width, gint height, gint grid_size)
525 l = g_list_length(pw->list);
529 total = (gdouble)width * (gdouble)height / (gdouble)l;
532 aw = (gdouble)width / s;
533 ah = (gdouble)height / s;
535 col = (gint)(sqrt((gdouble)l / grid_size) * width / height + 0.999);
536 col = CLAMP(col, 1, l / grid_size + 1);
537 row = (gint)((gdouble)l / grid_size / col);
538 if (row < 1) row = 1;
540 /* limit minimum size of grid so that a tile will always fit regardless of position */
541 cw = MAX((gint)ceil((gdouble)width / col), PAN_TILE_SIZE * 2);
542 ch = MAX((gint)ceil((gdouble)height / row), PAN_TILE_SIZE * 2);
547 printf("intersect speedup grid is %dx%d, based on %d average per grid\n", col, row, grid_size);
549 for (j = 0; j < row; j++)
550 for (i = 0; i < col; i++)
552 if ((i + 1) * cw / 2 < width && (j + 1) * ch / 2 < height)
556 pg = g_new0(PanGrid, 1);
563 pw->list_grid = g_list_prepend(pw->list_grid, pg);
565 if (debug) printf("grid section: %d,%d (%dx%d)\n", pg->x, pg->y, pg->w, pg->h);
578 grid = pw->list_grid;
587 if (util_clip_region(pi->x, pi->y, pi->width, pi->height,
588 pg->x, pg->y, pg->w, pg->h,
591 pg->list = g_list_prepend(pg->list, pi);
596 work = pw->list_grid;
604 pg->list = g_list_reverse(pg->list);
607 pw->list_static = pw->list;
614 *-----------------------------------------------------------------------------
616 *-----------------------------------------------------------------------------
619 static void pan_item_free(PanItem *pi)
623 if (pi->pixbuf) g_object_unref(pi->pixbuf);
624 if (pi->fd) file_data_free(pi->fd);
632 static void pan_window_items_free(PanWindow *pw)
641 PanItem *pi = work->data;
647 g_list_free(pw->list);
650 g_list_free(pw->queue);
654 image_loader_free(pw->il);
657 thumb_loader_free(pw->tl);
661 pw->search_pi = NULL;
664 static PanItem *pan_item_new_thumb(PanWindow *pw, FileData *fd, gint x, gint y)
668 pi = g_new0(PanItem, 1);
669 pi->type = ITEM_THUMB;
673 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
674 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
680 pw->list = g_list_prepend(pw->list, pi);
685 static PanItem *pan_item_new_box(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
687 guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
688 guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
692 pi = g_new0(PanItem, 1);
700 pi->color_r = base_r;
701 pi->color_g = base_g;
702 pi->color_b = base_b;
703 pi->color_a = base_a;
705 pi->color2_r = bord_r;
706 pi->color2_g = bord_g;
707 pi->color2_b = bord_b;
708 pi->color2_a = bord_a;
709 pi->border = border_size;
711 pw->list = g_list_prepend(pw->list, pi);
716 static void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
720 if (!pi || pi->type != ITEM_BOX) return;
725 pi->width -= shadow[0];
726 pi->height -= shadow[0];
729 shadow = g_new0(gint, 2);
734 pi->height += offset;
740 static PanItem *pan_item_new_tri(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
741 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
742 guint8 r, guint8 g, guint8 b, guint8 a)
747 pi = g_new0(PanItem, 1);
748 pi->type = ITEM_TRIANGLE;
759 coord = g_new0(gint, 6);
769 pi->border = BORDER_NONE;
771 pw->list = g_list_prepend(pw->list, pi);
776 static void pan_item_tri_border(PanItem *pi, gint borders,
777 guint8 r, guint8 g, guint8 b, guint8 a)
779 if (!pi || pi->type != ITEM_TRIANGLE) return;
781 pi->border = borders;
789 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
793 layout = gtk_widget_create_pango_layout(widget, NULL);
795 if (pi->text_attr & TEXT_ATTR_MARKUP)
797 pango_layout_set_markup(layout, pi->text, -1);
801 if (pi->text_attr & TEXT_ATTR_BOLD ||
802 pi->text_attr & TEXT_ATTR_HEADING)
807 pal = pango_attr_list_new();
808 if (pi->text_attr & TEXT_ATTR_BOLD)
810 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
812 pa->end_index = G_MAXINT;
813 pango_attr_list_insert(pal, pa);
815 if (pi->text_attr & TEXT_ATTR_HEADING)
817 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
819 pa->end_index = G_MAXINT;
820 pango_attr_list_insert(pal, pa);
822 pango_layout_set_attributes(layout, pal);
823 pango_attr_list_unref(pal);
826 pango_layout_set_text(layout, pi->text, -1);
830 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
834 if (!pi || !pi->text || !widget) return;
836 layout = pan_item_text_layout(pi, widget);
837 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
838 g_object_unref(G_OBJECT(layout));
840 pi->width += PAN_TEXT_BORDER_SIZE * 2;
841 pi->height += PAN_TEXT_BORDER_SIZE * 2;
844 static PanItem *pan_item_new_text(PanWindow *pw, gint x, gint y, const gchar *text, TextAttrType attr,
845 guint8 r, guint8 g, guint8 b, guint8 a)
849 pi = g_new0(PanItem, 1);
850 pi->type = ITEM_TEXT;
853 pi->text = g_strdup(text);
854 pi->text_attr = attr;
861 pan_item_text_compute_size(pi, pw->imd->pr);
863 pw->list = g_list_prepend(pw->list, pi);
868 static void pan_item_set_key(PanItem *pi, const gchar *key)
875 pi->key = g_strdup(key);
879 static void pan_item_added(PanWindow *pw, PanItem *pi)
882 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
885 static void pan_item_remove(PanWindow *pw, PanItem *pi)
889 if (pw->click_pi == pi) pw->click_pi = NULL;
890 if (pw->queue_pi == pi) pw->queue_pi = NULL;
891 if (pw->search_pi == pi) pw->search_pi = NULL;
892 pw->queue = g_list_remove(pw->queue, pi);
894 pw->list = g_list_remove(pw->list, pi);
895 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
899 static void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
901 if (!pi || !child) return;
903 if (pi->x + pi->width < child->x + child->width + border)
904 pi->width = child->x + child->width + border - pi->x;
906 if (pi->y + pi->height < child->y + child->height + border)
907 pi->height = child->y + child->height + border - pi->y;
910 static void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
914 if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
915 if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
918 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
927 work = pw->cache_list;
936 path = ((FileData *)pc)->path;
938 if (pc->cd && pc->cd->dimensions &&
939 path && strcmp(path, pi->fd->path) == 0)
941 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
942 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
944 pw->cache_list = g_list_remove(pw->cache_list, pc);
945 cache_sim_data_free(pc->cd);
946 file_data_free((FileData *)pc);
952 static PanItem *pan_item_new_image(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
956 pi = g_new0(PanItem, 1);
957 pi->type = ITEM_IMAGE;
962 pan_item_image_find_size(pw, pi, w, h);
964 pw->list = g_list_prepend(pw->list, pi);
969 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
973 if (!key) return NULL;
975 work = g_list_last(pw->list);
981 if ((pi->type == type || type == ITEM_NONE) &&
982 pi->key && strcmp(pi->key, key) == 0)
988 work = g_list_last(pw->list_static);
994 if ((pi->type == type || type == ITEM_NONE) &&
995 pi->key && strcmp(pi->key, key) == 0)
1005 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1006 static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
1007 ItemType type, const gchar *path,
1008 gint ignore_case, gint partial)
1012 work = g_list_last(search_list);
1018 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
1024 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
1026 else if (pi->fd->name)
1034 haystack = g_utf8_strdown(pi->fd->name, -1);
1035 match = (strstr(haystack, path) != NULL);
1040 if (strstr(pi->fd->name, path)) match = TRUE;
1043 else if (ignore_case)
1045 if (strcasecmp(path, pi->fd->name) == 0) match = TRUE;
1049 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
1053 if (match) list = g_list_prepend(list, pi);
1061 /* when ignore_case and partial are TRUE, path should be converted to lower case */
1062 static GList *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
1063 gint ignore_case, gint partial)
1067 if (!path) return NULL;
1068 if (partial && path[0] == '/') return NULL;
1070 list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
1071 list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
1073 return g_list_reverse(list);
1076 static PanItem *pan_item_find_by_coord_l(GList *list, ItemType type, gint x, gint y, const gchar *key)
1086 if ((pi->type == type || type == ITEM_NONE) &&
1087 x >= pi->x && x < pi->x + pi->width &&
1088 y >= pi->y && y < pi->y + pi->height &&
1089 (!key || (pi->key && strcmp(pi->key, key) == 0)))
1099 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y, const gchar *key)
1103 pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
1106 return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
1110 *-----------------------------------------------------------------------------
1112 *-----------------------------------------------------------------------------
1115 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend)
1117 GList *flist = NULL;
1118 GList *dlist = NULL;
1122 filelist_read(path, &flist, &dlist);
1123 if (sort != SORT_NONE)
1125 flist = filelist_sort(flist, sort, ascend);
1126 dlist = filelist_sort(dlist, sort, ascend);
1136 folders = g_list_remove(folders, fd);
1138 if (filelist_read(fd->path, &flist, &dlist))
1140 if (sort != SORT_NONE)
1142 flist = filelist_sort(flist, sort, ascend);
1143 dlist = filelist_sort(dlist, sort, ascend);
1146 result = g_list_concat(result, flist);
1147 folders = g_list_concat(dlist, folders);
1156 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
1164 list = pan_window_layout_list(path, SORT_NAME, TRUE);
1166 grid_size = (gint)sqrt((double)g_list_length(list));
1167 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1169 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
1173 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1178 *width = PAN_FOLDER_BOX_BORDER * 2;
1179 *height = PAN_FOLDER_BOX_BORDER * 2;
1192 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1194 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1196 x += pi->width + PAN_THUMB_GAP;
1197 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1206 pi = pan_item_new_thumb(pw, fd, x, y);
1208 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1212 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1215 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1221 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1224 gint x1, y1, x2, y2;
1239 if (x1 > pi->x) x1 = pi->x;
1240 if (y1 > pi->y) y1 = pi->y;
1241 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1242 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1245 x1 -= PAN_FOLDER_BOX_BORDER;
1246 y1 -= PAN_FOLDER_BOX_BORDER;
1247 x2 += PAN_FOLDER_BOX_BORDER;
1248 y2 += PAN_FOLDER_BOX_BORDER;
1261 if (pi->type == ITEM_TRIANGLE && pi->data)
1275 if (width) *width = x2 - x1;
1276 if (height) *height = y2 - y1;
1279 typedef struct _FlowerGroup FlowerGroup;
1280 struct _FlowerGroup {
1293 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1297 work = group->items;
1315 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1316 gint *result_x, gint *result_y)
1322 radius = parent->circumference / (2*PI);
1323 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1325 a = 2*PI * group->diameter / parent->circumference;
1327 x = (gint)((double)radius * cos(parent->angle + a / 2));
1328 y = (gint)((double)radius * sin(parent->angle + a / 2));
1335 x += parent->width / 2;
1336 y += parent->height / 2;
1338 x -= group->width / 2;
1339 y -= group->height / 2;
1345 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1352 if (parent && parent->children)
1354 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1362 pan_window_layout_compute_folder_flower_move(group, x, y);
1367 gint px, py, gx, gy;
1368 gint x1, y1, x2, y2;
1370 px = parent->x + parent->width / 2;
1371 py = parent->y + parent->height / 2;
1373 gx = group->x + group->width / 2;
1374 gy = group->y + group->height / 2;
1379 x2 = MAX(px, gx + 5);
1380 y2 = MAX(py, gy + 5);
1382 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1383 px, py, gx, gy, gx + 5, gy + 5,
1385 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1389 pw->list = g_list_concat(group->items, pw->list);
1390 group->items = NULL;
1392 group->circumference = 0;
1393 work = group->children;
1401 group->circumference += child->diameter;
1404 work = g_list_last(group->children);
1412 pan_window_layout_compute_folder_flower_build(pw, child, group);
1415 g_list_free(group->children);
1419 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1432 if (!filelist_read(path, &f, &d)) return NULL;
1433 if (!f && !d) return NULL;
1435 f = filelist_sort(f, SORT_NAME, TRUE);
1436 d = filelist_sort(d, SORT_NAME, TRUE);
1438 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1439 PAN_TEXT_COLOR, 255);
1441 y += pi_box->height;
1443 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1445 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1446 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1447 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1448 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1450 x += PAN_FOLDER_BOX_BORDER;
1451 y += PAN_FOLDER_BOX_BORDER;
1453 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1467 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1469 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1470 x += pi->width + PAN_THUMB_GAP;
1471 if (pi->height > y_height) y_height = pi->height;
1475 pi = pan_item_new_thumb(pw, fd, x, y);
1476 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1477 y_height = PAN_THUMB_SIZE;
1481 if (grid_count >= grid_size)
1485 y += y_height + PAN_THUMB_GAP;
1489 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1494 group = g_new0(FlowerGroup, 1);
1495 group->items = pw->list;
1498 group->width = pi_box->width;
1499 group->height = pi_box->y + pi_box->height;
1500 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1502 group->children = NULL;
1513 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1514 if (child) group->children = g_list_prepend(group->children, child);
1522 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1523 gint *width, gint *height,
1524 gint *scroll_x, gint *scroll_y)
1529 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1530 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1532 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1534 list = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1537 PanItem *pi = list->data;
1538 *scroll_x = pi->x + pi->width / 2;
1539 *scroll_y = pi->y + pi->height / 2;
1544 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1545 gint *x, gint *y, gint *level,
1547 gint *width, gint *height)
1555 if (!filelist_read(path, &f, &d)) return;
1556 if (!f && !d) return;
1558 f = filelist_sort(f, SORT_NAME, TRUE);
1559 d = filelist_sort(d, SORT_NAME, TRUE);
1561 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1563 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1564 PAN_TEXT_COLOR, 255);
1566 *y += pi_box->height;
1568 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1570 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1571 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1572 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1573 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1575 *x += PAN_FOLDER_BOX_BORDER;
1576 *y += PAN_FOLDER_BOX_BORDER;
1587 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1589 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1590 *x += pi->width + PAN_THUMB_GAP;
1591 if (pi->height > y_height) y_height = pi->height;
1595 pi = pan_item_new_thumb(pw, fd, *x, *y);
1596 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1597 y_height = PAN_THUMB_SIZE;
1600 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1603 if (f) *y = pi_box->y + pi_box->height;
1615 *level = *level + 1;
1616 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1617 pi_box, width, height);
1618 *level = *level - 1;
1623 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1625 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1626 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1628 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1631 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1638 x = PAN_FOLDER_BOX_BORDER;
1639 y = PAN_FOLDER_BOX_BORDER;
1640 w = PAN_FOLDER_BOX_BORDER * 2;
1641 h = PAN_FOLDER_BOX_BORDER * 2;
1643 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1645 if (width) *width = w;
1646 if (height) *height = h;
1650 *-----------------------------------------------------------------------------
1652 *-----------------------------------------------------------------------------
1655 #define PAN_CAL_DAY_WIDTH 100
1656 #define PAN_CAL_DAY_HEIGHT 80
1657 #define PAN_CAL_DOT_SIZE 3
1658 #define PAN_CAL_DOT_GAP 2
1659 #define PAN_CAL_DOT_COLOR 0, 0, 0
1660 #define PAN_CAL_DOT_ALPHA 32
1662 static void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
1668 gint x1, y1, x2, y2, x3, y3;
1673 while ((pi = pan_item_find_by_key(pw, ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
1675 if (!pi_day || pi_day->type != ITEM_BOX ||
1676 !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
1678 list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
1690 if (dot->type != ITEM_BOX || !dot->fd ||
1691 !dot->key || strcmp(dot->key, "dot") != 0)
1693 list = g_list_delete_link(list, node);
1701 grid = (gint)(sqrt(g_list_length(list)) + 0.5);
1703 x = pi_day->x + pi_day->width + 4;
1707 if (y + grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4 > pw->pr->image_height)
1709 y = pw->pr->image_height - (grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4);
1713 pbox = pan_item_new_box(pw, NULL, x, y, PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1715 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
1716 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
1717 pan_item_set_key(pbox, "day_bubble");
1724 buf = date_value_string(pi_day->fd->date, DATE_LENGTH_WEEK);
1725 plabel = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1726 PAN_POPUP_TEXT_COLOR, 255);
1727 pan_item_set_key(plabel, "day_bubble");
1730 pan_item_size_by_item(pbox, plabel, 0);
1732 y += plabel->height;
1739 x += PAN_FOLDER_BOX_BORDER;
1740 y += PAN_FOLDER_BOX_BORDER;
1754 pimg = pan_item_new_thumb(pw, file_data_new_simple(dot->fd->path), x, y);
1755 pan_item_set_key(pimg, "day_bubble");
1757 pan_item_size_by_item(pbox, pimg, PAN_FOLDER_BOX_BORDER);
1762 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1767 x = pbox->x + PAN_FOLDER_BOX_BORDER;
1768 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1774 x1 = pi_day->x + pi_day->width - 8;
1777 y2 = pbox->y + MIN(42, pbox->height);
1779 y3 = MAX(pbox->y, y2 - 30);
1780 util_clip_triangle(x1, y1, x2, y2, x3, y3,
1783 pi = pan_item_new_tri(pw, NULL, x, y, w, h,
1784 x1, y1, x2, y2, x3, y3,
1785 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
1786 pan_item_tri_border(pi, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
1787 pan_item_set_key(pi, "day_bubble");
1788 pan_item_added(pw, pi);
1790 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
1791 pan_item_added(pw, pbox);
1794 static void pan_window_layout_compute_calendar(PanWindow *pw, const gchar *path, gint *width, gint *height)
1810 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
1812 list = pan_window_layout_list(path, SORT_NONE, TRUE);
1813 list = filelist_sort(list, SORT_TIME, TRUE);
1826 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
1834 if (day_max < count) day_max = count;
1838 printf("biggest day contains %d images\n", day_max);
1840 grid = (gint)(sqrt((double)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
1841 day_width = MAX(PAN_CAL_DAY_WIDTH, grid);
1842 day_height = MAX(PAN_CAL_DAY_HEIGHT, grid);
1846 FileData *fd = list->data;
1848 year = date_value(fd->date, DATE_LENGTH_YEAR);
1849 month = date_value(fd->date, DATE_LENGTH_MONTH);
1852 work = g_list_last(list);
1855 FileData *fd = work->data;
1856 end_year = date_value(fd->date, DATE_LENGTH_YEAR);
1857 end_month = date_value(fd->date, DATE_LENGTH_MONTH);
1860 *width = PAN_FOLDER_BOX_BORDER * 2;
1861 *height = PAN_FOLDER_BOX_BORDER * 2;
1863 x = PAN_FOLDER_BOX_BORDER;
1864 y = PAN_FOLDER_BOX_BORDER;
1867 while (work && (year < end_year || (year == end_year && month <= end_month)))
1878 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
1880 days = date_value(dt, DATE_LENGTH_DAY);
1881 dt = date_to_time(year, month, 1);
1882 col = date_value(dt, DATE_LENGTH_WEEK);
1885 x = PAN_FOLDER_BOX_BORDER;
1887 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
1888 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1889 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1890 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1891 buf = date_value_string(dt, DATE_LENGTH_MONTH);
1892 pi_text = pan_item_new_text(pw, x, y, buf,
1893 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1894 PAN_TEXT_COLOR, 255);
1896 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
1898 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
1900 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
1901 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
1903 for (day = 1; day <= days; day++)
1910 dt = date_to_time(year, month, day);
1912 fd = g_new0(FileData, 1);
1913 /* path and name must be non NULL, so make them an invalid filename */
1914 fd->path = g_strdup("//");
1917 pi_day = pan_item_new_box(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
1918 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1919 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1920 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1921 pan_item_set_key(pi_day, "day");
1923 dx = x + PAN_CAL_DOT_GAP * 2;
1924 dy = y + PAN_CAL_DOT_GAP * 2;
1926 fd = (work) ? work->data : NULL;
1927 while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
1931 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
1933 PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
1935 pan_item_set_key(pi, "dot");
1937 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
1938 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
1940 dx = x + PAN_CAL_DOT_GAP * 2;
1941 dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
1943 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
1945 /* must keep all dots within respective day even if it gets ugly */
1946 dy = y + PAN_CAL_DOT_GAP * 2;
1949 pi_day->color_a = MIN(PAN_FOLDER_BOX_ALPHA + 64 + n, 255);
1953 fd = (work) ? work->data : NULL;
1960 buf = g_strdup_printf("( %d )", n);
1961 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
1962 PAN_TEXT_COLOR, 255);
1965 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
1966 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
1969 buf = g_strdup_printf("%d", day);
1970 pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1971 PAN_TEXT_COLOR, 255);
1975 pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
1982 x = PAN_FOLDER_BOX_BORDER;
1983 y += PAN_CAL_DAY_HEIGHT;
1987 x += PAN_CAL_DAY_WIDTH;
1991 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
1992 y += PAN_FOLDER_BOX_BORDER * 2;
2003 *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
2008 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
2016 PanItem *pi_month = NULL;
2017 PanItem *pi_day = NULL;
2023 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
2025 list = pan_window_layout_list(path, SORT_NONE, TRUE);
2026 list = filelist_sort(list, SORT_TIME, TRUE);
2028 *width = PAN_FOLDER_BOX_BORDER * 2;
2029 *height = PAN_FOLDER_BOX_BORDER * 2;
2034 day_start = month_start;
2049 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
2054 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
2060 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
2064 x = PAN_FOLDER_BOX_BORDER;
2067 y = PAN_FOLDER_BOX_BORDER;
2069 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
2070 pi = pan_item_new_text(pw, x, y, buf,
2071 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
2072 PAN_TEXT_COLOR, 255);
2075 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
2077 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2078 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2079 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2081 x += PAN_FOLDER_BOX_BORDER;
2082 y += PAN_FOLDER_BOX_BORDER;
2086 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
2098 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
2100 needle = needle->next;
2109 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
2110 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
2111 PAN_TEXT_COLOR, 255);
2116 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
2117 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
2118 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
2119 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
2121 x += PAN_FOLDER_BOX_BORDER;
2122 y += PAN_FOLDER_BOX_BORDER;
2126 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2128 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
2129 if (pi->width > x_width) x_width = pi->width;
2130 y_height = pi->height;
2134 pi = pan_item_new_thumb(pw, fd, x, y);
2135 x_width = PAN_THUMB_SIZE;
2136 y_height = PAN_THUMB_SIZE;
2139 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
2140 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
2145 if (total > 0 && count < PAN_GROUP_MAX)
2147 y += y_height + PAN_THUMB_GAP;
2151 x += x_width + PAN_THUMB_GAP;
2161 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
2167 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
2168 gint *width, gint *height,
2169 gint *scroll_x, gint *scroll_y)
2171 pan_window_items_free(pw);
2175 case LAYOUT_SIZE_THUMB_DOTS:
2176 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2177 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2179 case LAYOUT_SIZE_THUMB_NONE:
2180 pw->thumb_size = PAN_THUMB_SIZE_NONE;
2181 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2183 case LAYOUT_SIZE_THUMB_SMALL:
2184 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2185 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2187 case LAYOUT_SIZE_THUMB_NORMAL:
2189 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2190 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2192 case LAYOUT_SIZE_THUMB_LARGE:
2193 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2194 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2196 case LAYOUT_SIZE_10:
2197 pw->image_size = 10;
2198 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2200 case LAYOUT_SIZE_25:
2201 pw->image_size = 25;
2202 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2204 case LAYOUT_SIZE_33:
2205 pw->image_size = 33;
2206 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2208 case LAYOUT_SIZE_50:
2209 pw->image_size = 50;
2210 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2212 case LAYOUT_SIZE_100:
2213 pw->image_size = 100;
2214 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2227 pan_window_layout_compute_grid(pw, path, width, height);
2229 case LAYOUT_FOLDERS_LINEAR:
2230 pan_window_layout_compute_folders_linear(pw, path, width, height);
2232 case LAYOUT_FOLDERS_FLOWER:
2233 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2235 case LAYOUT_CALENDAR:
2236 pan_window_layout_compute_calendar(pw, path, width, height);
2238 case LAYOUT_TIMELINE:
2239 pan_window_layout_compute_timeline(pw, path, width, height);
2245 printf("computed %d objects\n", g_list_length(pw->list));
2248 static GList *pan_layout_intersect_l(GList *list, GList *item_list,
2249 gint x, gint y, gint width, gint height)
2257 gint rx, ry, rw, rh;
2262 if (util_clip_region(x, y, width, height,
2263 pi->x, pi->y, pi->width, pi->height,
2264 &rx, &ry, &rw, &rh))
2266 list = g_list_prepend(list, pi);
2273 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2279 grid = pw->list_grid;
2285 if (x < pg->x || x + width > pg->x + pg->w ||
2286 y < pg->y || y + height > pg->y + pg->h)
2292 list = pan_layout_intersect_l(list, pw->list, x, y, width, height);
2296 list = pan_layout_intersect_l(list, pg->list, x, y, width, height);
2300 list = pan_layout_intersect_l(list, pw->list_static, x, y, width, height);
2307 *-----------------------------------------------------------------------------
2309 *-----------------------------------------------------------------------------
2312 static gint pan_layout_queue_step(PanWindow *pw);
2315 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2317 PanWindow *pw = data;
2325 pw->queue_pi = NULL;
2329 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2330 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2333 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2337 thumb_loader_free(pw->tl);
2340 while (pan_layout_queue_step(pw));
2343 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2345 PanWindow *pw = data;
2353 pw->queue_pi = NULL;
2357 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2358 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2359 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2361 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2362 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2363 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2368 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2369 (GdkInterpType)zoom_quality);
2370 g_object_unref(tmp);
2374 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2378 image_loader_free(pw->il);
2381 while (pan_layout_queue_step(pw));
2385 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2386 guint width, guint height, gpointer data)
2388 PanWindow *pw = data;
2399 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2400 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2404 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2410 static gint pan_layout_queue_step(PanWindow *pw)
2414 if (!pw->queue) return FALSE;
2416 pi = pw->queue->data;
2417 pw->queue = g_list_remove(pw->queue, pi);
2420 if (!pw->queue_pi->fd)
2422 pw->queue_pi->queued = FALSE;
2423 pw->queue_pi = NULL;
2427 image_loader_free(pw->il);
2429 thumb_loader_free(pw->tl);
2432 if (pi->type == ITEM_IMAGE)
2434 pw->il = image_loader_new(pi->fd->path);
2436 if (pw->size != LAYOUT_SIZE_100)
2438 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2442 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2444 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2446 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2448 image_loader_free(pw->il);
2451 else if (pi->type == ITEM_THUMB)
2453 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2455 if (!pw->tl->standard_loader)
2457 /* The classic loader will recreate a thumbnail any time we
2458 * request a different size than what exists. This view will
2459 * almost never use the user configured sizes so disable cache.
2461 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2464 thumb_loader_set_callbacks(pw->tl,
2465 pan_layout_queue_thumb_done_cb,
2466 pan_layout_queue_thumb_done_cb,
2469 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2471 thumb_loader_free(pw->tl);
2475 pw->queue_pi->queued = FALSE;
2476 pw->queue_pi = NULL;
2480 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2482 if (!pi || pi->queued || pi->pixbuf) return;
2483 if (pw->size <= LAYOUT_SIZE_THUMB_NONE) return;
2486 pw->queue = g_list_prepend(pw->queue, pi);
2488 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2491 static gint pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
2492 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2494 PanWindow *pw = data;
2499 pixbuf_set_rect_fill(pixbuf,
2500 0, 0, width, height,
2501 PAN_BACKGROUND_COLOR, 255);
2503 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2505 gint rx, ry, rw, rh;
2507 if (util_clip_region(x, y, width, height,
2509 &rx, &ry, &rw, &rh))
2511 pixbuf_draw_rect_fill(pixbuf,
2512 rx - x, ry - y, rw, rh,
2513 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2516 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2518 gint rx, ry, rw, rh;
2520 if (util_clip_region(x, y, width, height,
2522 &rx, &ry, &rw, &rh))
2524 pixbuf_draw_rect_fill(pixbuf,
2525 rx - x, ry - y, rw, rh,
2526 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2530 list = pan_layout_intersect(pw, x, y, width, height);
2535 gint tx, ty, tw, th;
2536 gint rx, ry, rw, rh;
2543 if (pi->type == ITEM_THUMB && pi->pixbuf)
2545 tw = gdk_pixbuf_get_width(pi->pixbuf);
2546 th = gdk_pixbuf_get_height(pi->pixbuf);
2548 tx = pi->x + (pi->width - tw) / 2;
2549 ty = pi->y + (pi->height - th) / 2;
2551 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2553 if (util_clip_region(x, y, width, height,
2554 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2555 &rx, &ry, &rw, &rh))
2557 pixbuf_draw_shadow(pixbuf,
2558 rx - x, ry - y, rw, rh,
2559 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2561 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2566 if (util_clip_region(x, y, width, height,
2567 tx + tw, ty + PAN_SHADOW_OFFSET,
2568 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2569 &rx, &ry, &rw, &rh))
2571 pixbuf_draw_shadow(pixbuf,
2572 rx - x, ry - y, rw, rh,
2573 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2575 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2577 if (util_clip_region(x, y, width, height,
2578 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2579 &rx, &ry, &rw, &rh))
2581 pixbuf_draw_shadow(pixbuf,
2582 rx - x, ry - y, rw, rh,
2583 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2585 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2589 if (util_clip_region(x, y, width, height,
2591 &rx, &ry, &rw, &rh))
2593 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2596 1.0, 1.0, GDK_INTERP_NEAREST,
2600 if (util_clip_region(x, y, width, height,
2601 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2602 &rx, &ry, &rw, &rh))
2604 pixbuf_draw_rect_fill(pixbuf,
2605 rx - x, ry - y, rw, rh,
2606 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2608 if (util_clip_region(x, y, width, height,
2609 tx, ty, PAN_OUTLINE_THICKNESS, th,
2610 &rx, &ry, &rw, &rh))
2612 pixbuf_draw_rect_fill(pixbuf,
2613 rx - x, ry - y, rw, rh,
2614 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2616 if (util_clip_region(x, y, width, height,
2617 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2618 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2619 &rx, &ry, &rw, &rh))
2621 pixbuf_draw_rect_fill(pixbuf,
2622 rx - x, ry - y, rw, rh,
2623 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2625 if (util_clip_region(x, y, width, height,
2626 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2627 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2628 &rx, &ry, &rw, &rh))
2630 pixbuf_draw_rect_fill(pixbuf,
2631 rx - x, ry - y, rw, rh,
2632 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2635 else if (pi->type == ITEM_THUMB)
2637 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2638 th = pi->height - PAN_SHADOW_OFFSET * 2;
2639 tx = pi->x + PAN_SHADOW_OFFSET;
2640 ty = pi->y + PAN_SHADOW_OFFSET;
2642 if (util_clip_region(x, y, width, height,
2644 &rx, &ry, &rw, &rh))
2648 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2649 pixbuf_draw_rect_fill(pixbuf,
2650 rx - x, ry - y, rw, rh,
2652 PAN_SHADOW_ALPHA / d);
2655 pan_layout_queue(pw, pi);
2657 else if (pi->type == ITEM_IMAGE)
2659 if (util_clip_region(x, y, width, height,
2660 pi->x, pi->y, pi->width, pi->height,
2661 &rx, &ry, &rw, &rh))
2665 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2668 1.0, 1.0, GDK_INTERP_NEAREST,
2673 pixbuf_draw_rect_fill(pixbuf,
2674 rx - x, ry - y, rw, rh,
2675 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2676 pan_layout_queue(pw, pi);
2680 else if (pi->type == ITEM_BOX)
2694 if (pi->color_a > 254)
2696 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2697 shadow[0], bh - shadow[0],
2698 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2700 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2701 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2703 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2705 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2710 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2711 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2713 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2715 PAN_SHADOW_COLOR, a);
2719 if (util_clip_region(x, y, width, height,
2720 pi->x, pi->y, bw, bh,
2721 &rx, &ry, &rw, &rh))
2723 pixbuf_draw_rect_fill(pixbuf,
2724 rx - x, ry - y, rw, rh,
2725 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2727 if (util_clip_region(x, y, width, height,
2728 pi->x, pi->y, bw, pi->border,
2729 &rx, &ry, &rw, &rh))
2731 pixbuf_draw_rect_fill(pixbuf,
2732 rx - x, ry - y, rw, rh,
2733 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2735 if (util_clip_region(x, y, width, height,
2736 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
2737 &rx, &ry, &rw, &rh))
2739 pixbuf_draw_rect_fill(pixbuf,
2740 rx - x, ry - y, rw, rh,
2741 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2743 if (util_clip_region(x, y, width, height,
2744 pi->x + bw - pi->border, pi->y + pi->border,
2745 pi->border, bh - pi->border * 2,
2746 &rx, &ry, &rw, &rh))
2748 pixbuf_draw_rect_fill(pixbuf,
2749 rx - x, ry - y, rw, rh,
2750 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2752 if (util_clip_region(x, y, width, height,
2753 pi->x, pi->y + bh - pi->border,
2755 &rx, &ry, &rw, &rh))
2757 pixbuf_draw_rect_fill(pixbuf,
2758 rx - x, ry - y, rw, rh,
2759 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2762 else if (pi->type == ITEM_TRIANGLE)
2764 if (util_clip_region(x, y, width, height,
2765 pi->x, pi->y, pi->width, pi->height,
2766 &rx, &ry, &rw, &rh) && pi->data)
2768 gint *coord = pi->data;
2769 pixbuf_draw_triangle(pixbuf,
2770 rx - x, ry - y, rw, rh,
2771 coord[0] - x, coord[1] - y,
2772 coord[2] - x, coord[3] - y,
2773 coord[4] - x, coord[5] - y,
2774 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2776 if (pi->border & BORDER_1)
2778 pixbuf_draw_line(pixbuf,
2779 rx - x, ry - y, rw, rh,
2780 coord[0] - x, coord[1] - y,
2781 coord[2] - x, coord[3] - y,
2782 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2784 if (pi->border & BORDER_2)
2786 pixbuf_draw_line(pixbuf,
2787 rx - x, ry - y, rw, rh,
2788 coord[2] - x, coord[3] - y,
2789 coord[4] - x, coord[5] - y,
2790 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2792 if (pi->border & BORDER_3)
2794 pixbuf_draw_line(pixbuf,
2795 rx - x, ry - y, rw, rh,
2796 coord[4] - x, coord[5] - y,
2797 coord[0] - x, coord[1] - y,
2798 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2802 else if (pi->type == ITEM_TEXT && pi->text)
2804 PangoLayout *layout;
2806 layout = pan_item_text_layout(pi, (GtkWidget *)pr);
2807 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
2808 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
2809 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2810 g_object_unref(G_OBJECT(layout));
2816 if (x%512 == 0 && y%512 == 0)
2818 PangoLayout *layout;
2821 layout = gtk_widget_create_pango_layout((GtkWidget *)pr, NULL);
2823 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
2824 (x / pr->source_tile_width) +
2825 (y / pr->source_tile_height * (pr->image_width/pr->source_tile_width + 1)));
2826 pango_layout_set_text(layout, buf, -1);
2829 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr, 0, 0, 0, 0, 0, 255);
2831 g_object_unref(G_OBJECT(layout));
2838 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
2839 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2841 PanWindow *pw = data;
2845 list = pan_layout_intersect(pw, x, y, width, height);
2854 if (pi->refcount > 0)
2858 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
2863 pw->queue = g_list_remove(pw->queue, pi);
2866 if (pw->queue_pi == pi) pw->queue_pi = NULL;
2869 g_object_unref(pi->pixbuf);
2881 *-----------------------------------------------------------------------------
2883 *-----------------------------------------------------------------------------
2886 static void pan_window_message(PanWindow *pw, const gchar *text)
2896 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
2900 work = pw->list_static;
2901 if (pw->layout == LAYOUT_CALENDAR)
2911 pi->type == ITEM_BOX &&
2912 pi->key && strcmp(pi->key, "dot") == 0)
2914 size += pi->fd->size;
2929 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
2931 size += pi->fd->size;
2937 ss = text_from_size_abrev(size);
2938 buf = g_strdup_printf(_("%d images, %s"), count, ss);
2940 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
2944 static void pan_window_zoom_limit(PanWindow *pw)
2950 case LAYOUT_SIZE_THUMB_DOTS:
2951 case LAYOUT_SIZE_THUMB_NONE:
2952 case LAYOUT_SIZE_THUMB_SMALL:
2953 case LAYOUT_SIZE_THUMB_NORMAL:
2955 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
2959 case LAYOUT_SIZE_THUMB_LARGE:
2962 case LAYOUT_SIZE_10:
2963 case LAYOUT_SIZE_25:
2966 case LAYOUT_SIZE_33:
2967 case LAYOUT_SIZE_50:
2968 case LAYOUT_SIZE_100:
2974 image_zoom_set_limits(pw->imd, min, 32.0);
2977 static gint pan_window_layout_update_idle_cb(gpointer data)
2979 PanWindow *pw = data;
2985 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2987 if (!pw->cache_list && !pw->cache_todo)
2989 pan_cache_fill(pw, pw->path);
2992 pan_window_message(pw, _("Reading dimensions..."));
2996 if (pan_cache_step(pw))
3000 if (pw->cache_count == pw->cache_total)
3002 pan_window_message(pw, _("Sorting images..."));
3004 else if (pw->cache_tick > 9)
3008 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
3009 pw->cache_total - pw->cache_count);
3010 pan_window_message(pw, buf);
3020 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
3022 pan_window_zoom_limit(pw);
3024 if (width > 0 && height > 0)
3028 printf("Canvas size is %d x %d\n", width, height);
3030 pan_grid_build(pw, width, height, 1000);
3032 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
3033 PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
3034 pan_window_request_tile_cb,
3035 pan_window_dispose_tile_cb, pw, 1.0);
3037 if (scroll_x == 0 && scroll_y == 0)
3045 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
3048 pan_window_message(pw, NULL);
3055 static void pan_window_layout_update_idle(PanWindow *pw)
3057 if (pw->idle_id == -1)
3059 pan_window_message(pw, _("Sorting images..."));
3060 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
3065 *-----------------------------------------------------------------------------
3066 * pan window keyboard
3067 *-----------------------------------------------------------------------------
3070 static const gchar *pan_menu_click_path(PanWindow *pw)
3072 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
3076 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
3078 PanWindow *pw = data;
3080 gdk_window_get_origin(pw->imd->pr->window, x, y);
3081 popup_menu_position_clamp(menu, x, y, 0);
3084 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
3086 PanWindow *pw = data;
3089 gint stop_signal = FALSE;
3095 pr = PIXBUF_RENDERER(pw->imd->pr);
3096 path = pan_menu_click_path(pw);
3098 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pw->imd->widget)));
3102 switch (event->keyval)
3104 case GDK_Left: case GDK_KP_Left:
3108 case GDK_Right: case GDK_KP_Right:
3112 case GDK_Up: case GDK_KP_Up:
3116 case GDK_Down: case GDK_KP_Down:
3120 case GDK_Page_Up: case GDK_KP_Page_Up:
3121 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
3123 case GDK_Page_Down: case GDK_KP_Page_Down:
3124 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
3126 case GDK_Home: case GDK_KP_Home:
3127 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
3129 case GDK_End: case GDK_KP_End:
3130 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
3135 if (focused && !(event->state & GDK_CONTROL_MASK) )
3136 switch (event->keyval)
3138 case '+': case '=': case GDK_KP_Add:
3139 pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
3141 case '-': case GDK_KP_Subtract:
3142 pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
3144 case 'Z': case 'z': case GDK_KP_Divide: case '1':
3145 pixbuf_renderer_zoom_set(pr, 1.0);
3148 pixbuf_renderer_zoom_set(pr, 2.0);
3151 pixbuf_renderer_zoom_set(pr, 3.0);
3154 pixbuf_renderer_zoom_set(pr, 4.0);
3157 pixbuf_renderer_zoom_set(pr, -4.0);
3160 pixbuf_renderer_zoom_set(pr, -3.0);
3163 pixbuf_renderer_zoom_set(pr, -2.0);
3167 pan_fullscreen_toggle(pw, FALSE);
3172 pan_overlay_toggle(pw);
3175 case GDK_Delete: case GDK_KP_Delete:
3180 if (GTK_WIDGET_VISIBLE(pw->search_box))
3182 gtk_widget_grab_focus(pw->search_entry);
3186 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
3194 pan_fullscreen_toggle(pw, TRUE);
3197 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
3199 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3205 menu = pan_popup_menu(pw);
3206 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
3211 if (event->state & GDK_CONTROL_MASK)
3214 switch (event->keyval)
3247 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3250 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3253 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3256 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3259 if (path) info_window_new(path, NULL);
3262 pan_window_close(pw);
3265 if (n != -1 && path)
3267 pan_fullscreen_toggle(pw, TRUE);
3268 start_editor_from_file(n, path);
3272 else if (event->state & GDK_SHIFT_MASK)
3279 switch (event->keyval)
3284 pan_fullscreen_toggle(pw, TRUE);
3287 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3289 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
3290 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3299 if (x != 0 || y!= 0)
3301 keyboard_scroll_calc(&x, &y, event);
3302 pixbuf_renderer_scroll(pr, x, y);
3309 *-----------------------------------------------------------------------------
3311 *-----------------------------------------------------------------------------
3314 static void pan_info_update(PanWindow *pw, PanItem *pi)
3320 gint x1, y1, x2, y2, x3, y3;
3323 if (pw->click_pi == pi) return;
3324 if (pi && !pi->fd) pi = NULL;
3326 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3331 if (debug) printf("info set to %s\n", pi->fd->path);
3333 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3335 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3336 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3337 pan_item_set_key(pbox, "info");
3339 if (pi->type == ITEM_THUMB && pi->pixbuf)
3341 w = gdk_pixbuf_get_width(pi->pixbuf);
3342 h = gdk_pixbuf_get_height(pi->pixbuf);
3344 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3345 y1 = pi->y + (pi->height - h) / 2 + 8;
3349 x1 = pi->x + pi->width - 8;
3357 util_clip_triangle(x1, y1, x2, y2, x3, y3,
3360 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3361 x1, y1, x2, y2, x3, y3,
3362 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3363 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3364 pan_item_set_key(p, "info");
3365 pan_item_added(pw, p);
3367 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3368 _("Filename:"), TEXT_ATTR_BOLD,
3369 PAN_POPUP_TEXT_COLOR, 255);
3370 pan_item_set_key(plabel, "info");
3371 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3372 pi->fd->name, TEXT_ATTR_NONE,
3373 PAN_POPUP_TEXT_COLOR, 255);
3374 pan_item_set_key(p, "info");
3375 pan_item_size_by_item(pbox, p, 0);
3377 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3378 _("Date:"), TEXT_ATTR_BOLD,
3379 PAN_POPUP_TEXT_COLOR, 255);
3380 pan_item_set_key(plabel, "info");
3381 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3382 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3383 PAN_POPUP_TEXT_COLOR, 255);
3384 pan_item_set_key(p, "info");
3385 pan_item_size_by_item(pbox, p, 0);
3387 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3388 _("Size:"), TEXT_ATTR_BOLD,
3389 PAN_POPUP_TEXT_COLOR, 255);
3390 pan_item_set_key(plabel, "info");
3391 buf = text_from_size(pi->fd->size);
3392 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3393 buf, TEXT_ATTR_NONE,
3394 PAN_POPUP_TEXT_COLOR, 255);
3396 pan_item_set_key(p, "info");
3397 pan_item_size_by_item(pbox, p, 0);
3399 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3400 pan_item_added(pw, pbox);
3405 *-----------------------------------------------------------------------------
3407 *-----------------------------------------------------------------------------
3410 static void pan_search_status(PanWindow *pw, const gchar *text)
3412 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3415 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3423 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3425 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3426 if (!list) return FALSE;
3428 found = g_list_find(list, pw->click_pi);
3429 if (found && found->next)
3431 found = found->next;
3439 pan_info_update(pw, pi);
3440 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3442 buf = g_strdup_printf("%s ( %d / %d )",
3443 (path[0] == '/') ? _("path found") : _("filename found"),
3444 g_list_index(list, pi) + 1,
3445 g_list_length(list));
3446 pan_search_status(pw, buf);
3454 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3462 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3464 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3465 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3470 needle = g_utf8_strdown(text, -1);
3471 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3474 if (!list) return FALSE;
3476 found = g_list_find(list, pw->click_pi);
3477 if (found && found->next)
3479 found = found->next;
3487 pan_info_update(pw, pi);
3488 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3490 buf = g_strdup_printf("%s ( %d / %d )",
3492 g_list_index(list, pi) + 1,
3493 g_list_length(list));
3494 pan_search_status(pw, buf);
3502 static gint valid_date_separator(gchar c)
3504 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3507 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
3508 gint year, gint month, gint day,
3514 work = g_list_last(pw->list_static);
3522 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
3523 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
3527 tl = localtime(&pi->fd->date);
3532 match = (tl->tm_year == year - 1900);
3533 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3534 if (match && day > 0) match = (tl->tm_mday == day);
3536 if (match) list = g_list_prepend(list, pi);
3541 return g_list_reverse(list);
3544 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3560 if (!text) return FALSE;
3562 ptr = (gchar *)text;
3563 while (*ptr != '\0')
3565 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3570 if (t == -1) return FALSE;
3572 if (!lt) return FALSE;
3574 if (valid_date_separator(*text))
3577 mptr = (gchar *)text;
3581 year = (gint)strtol(text, &mptr, 10);
3582 if (mptr == text) return FALSE;
3585 if (*mptr != '\0' && valid_date_separator(*mptr))
3590 month = strtol(mptr, &dptr, 10);
3593 if (valid_date_separator(*dptr))
3595 month = lt->tm_mon + 1;
3603 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3607 day = strtol(dptr, &eptr, 10);
3617 year = lt->tm_year + 1900;
3619 else if (year < 100)
3628 month < -1 || month == 0 || month > 12 ||
3629 day < -1 || day == 0 || day > 31) return FALSE;
3631 t = date_to_time(year, month, day);
3632 if (t < 0) return FALSE;
3634 if (pw->layout == LAYOUT_CALENDAR)
3636 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
3642 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3643 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
3648 found = g_list_find(list, pw->search_pi);
3649 if (found && found->next)
3651 found = found->next;
3662 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
3664 pan_info_update(pw, NULL);
3665 pan_calendar_update(pw, pi);
3666 image_scroll_to_point(pw->imd,
3667 pi->x + pi->width / 2,
3668 pi->y + pi->height / 2, 0.5, 0.5);
3672 pan_info_update(pw, pi);
3673 image_scroll_to_point(pw->imd,
3674 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3680 buf = date_value_string(t, DATE_LENGTH_MONTH);
3685 buf = g_strdup_printf("%d %s", day, tmp);
3691 buf = date_value_string(t, DATE_LENGTH_YEAR);
3696 buf_count = g_strdup_printf("( %d / %d )",
3697 g_list_index(list, pi) + 1,
3698 g_list_length(list));
3702 buf_count = g_strdup_printf("(%s)", _("no match"));
3705 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
3708 pan_search_status(pw, message);
3716 static void pan_search_activate_cb(const gchar *text, gpointer data)
3718 PanWindow *pw = data;
3722 tab_completion_append_to_history(pw->search_entry, text);
3724 if (pan_search_by_path(pw, text)) return;
3726 if ((pw->layout == LAYOUT_TIMELINE ||
3727 pw->layout == LAYOUT_CALENDAR) &&
3728 pan_search_by_date(pw, text))
3733 if (pan_search_by_partial(pw, text)) return;
3735 pan_search_status(pw, _("no match"));
3738 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3740 PanWindow *pw = data;
3743 visible = GTK_WIDGET_VISIBLE(pw->search_box);
3744 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3748 gtk_widget_hide(pw->search_box);
3749 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3753 gtk_widget_show(pw->search_box);
3754 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3755 gtk_widget_grab_focus(pw->search_entry);
3761 *-----------------------------------------------------------------------------
3762 * view window main routines
3763 *-----------------------------------------------------------------------------
3766 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
3768 PanWindow *pw = data;
3776 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
3777 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
3780 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3783 switch (event->button)
3786 pan_info_update(pw, pi);
3788 if (!pi && pw->layout == LAYOUT_CALENDAR)
3790 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
3791 pan_calendar_update(pw, pi);
3797 pan_info_update(pw, pi);
3798 menu = pan_popup_menu(pw);
3799 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
3806 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
3809 PanWindow *pw = data;
3816 if (!(event->state & GDK_SHIFT_MASK))
3822 if (event->state & GDK_CONTROL_MASK)
3824 switch (event->direction)
3827 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
3828 (gint)event->x, (gint)event->y);
3830 case GDK_SCROLL_DOWN:
3831 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
3832 (gint)event->x, (gint)event->y);
3840 switch (event->direction)
3843 pixbuf_renderer_scroll(pr, 0, -h);
3845 case GDK_SCROLL_DOWN:
3846 pixbuf_renderer_scroll(pr, 0, h);
3848 case GDK_SCROLL_LEFT:
3849 pixbuf_renderer_scroll(pr, -w, 0);
3851 case GDK_SCROLL_RIGHT:
3852 pixbuf_renderer_scroll(pr, w, 0);
3860 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
3862 g_signal_connect(G_OBJECT(imd->pr), "clicked",
3863 G_CALLBACK(button_cb), pw);
3864 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
3865 G_CALLBACK(scroll_cb), pw);
3868 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
3870 PanWindow *pw = data;
3873 pw->imd = pw->imd_normal;
3876 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
3878 if (force_off && !pw->fs) return;
3882 fullscreen_stop(pw->fs);
3886 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
3887 pan_image_set_buttons(pw, pw->fs->imd);
3888 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
3889 G_CALLBACK(pan_window_key_press_cb), pw);
3891 pw->imd = pw->fs->imd;
3895 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
3897 PanWindow *pw = data;
3900 text = image_zoom_get_as_text(pw->imd);
3901 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
3905 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
3907 PanWindow *pw = data;
3912 if (pr->scale == 0.0) return;
3914 pixbuf_renderer_get_visible_rect(pr, &rect);
3915 pixbuf_renderer_get_image_size(pr, &width, &height);
3917 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
3918 adj->page_size = (gdouble)rect.width;
3919 adj->page_increment = adj->page_size / 2.0;
3920 adj->step_increment = 48.0 / pr->scale;
3922 adj->upper = MAX((gdouble)width, 1.0);
3923 adj->value = (gdouble)rect.x;
3925 pref_signal_block_data(pw->scrollbar_h, pw);
3926 gtk_adjustment_changed(adj);
3927 gtk_adjustment_value_changed(adj);
3928 pref_signal_unblock_data(pw->scrollbar_h, pw);
3930 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
3931 adj->page_size = (gdouble)rect.height;
3932 adj->page_increment = adj->page_size / 2.0;
3933 adj->step_increment = 48.0 / pr->scale;
3935 adj->upper = MAX((gdouble)height, 1.0);
3936 adj->value = (gdouble)rect.y;
3938 pref_signal_block_data(pw->scrollbar_v, pw);
3939 gtk_adjustment_changed(adj);
3940 gtk_adjustment_value_changed(adj);
3941 pref_signal_unblock_data(pw->scrollbar_v, pw);
3944 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
3946 PanWindow *pw = data;
3950 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
3952 if (!pr->scale) return;
3954 x = (gint)gtk_range_get_value(range);
3956 pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
3959 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
3961 PanWindow *pw = data;
3965 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
3967 if (!pr->scale) return;
3969 y = (gint)gtk_range_get_value(range);
3971 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
3974 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
3976 PanWindow *pw = data;
3978 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3979 pan_window_layout_update_idle(pw);
3982 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
3984 PanWindow *pw = data;
3986 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3987 pan_window_layout_update_idle(pw);
3990 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
3992 PanWindow *pw = data;
3995 path = remove_trailing_slash(new_text);
3996 parse_out_relatives(path);
4000 warning_dialog(_("Folder not found"),
4001 _("The entered path is not a folder"),
4002 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
4006 tab_completion_append_to_history(pw->path_entry, path);
4009 pw->path = g_strdup(path);
4011 pan_window_layout_update_idle(pw);
4014 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
4016 PanWindow *pw = data;
4019 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
4021 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
4022 pan_window_entry_activate_cb(text, pw);
4026 static void pan_window_close(PanWindow *pw)
4028 pan_window_list = g_list_remove(pan_window_list, pw);
4030 if (pw->idle_id != -1)
4032 g_source_remove(pw->idle_id);
4035 pan_fullscreen_toggle(pw, TRUE);
4036 gtk_widget_destroy(pw->window);
4038 pan_window_items_free(pw);
4045 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
4047 PanWindow *pw = data;
4049 pan_window_close(pw);
4053 static void pan_window_new_real(const gchar *path)
4062 GdkGeometry geometry;
4064 pw = g_new0(PanWindow, 1);
4066 pw->path = g_strdup(path);
4067 pw->layout = LAYOUT_TIMELINE;
4068 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
4069 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
4070 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
4073 pw->list_static = NULL;
4074 pw->list_grid = NULL;
4077 pw->overlay_id = -1;
4080 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4082 geometry.min_width = 8;
4083 geometry.min_height = 8;
4084 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
4086 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
4087 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
4088 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
4089 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
4091 window_set_icon(pw->window, NULL, NULL);
4093 vbox = gtk_vbox_new(FALSE, 0);
4094 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
4095 gtk_widget_show(vbox);
4097 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
4099 pref_spacer(box, 0);
4100 pref_label_new(box, _("Location:"));
4101 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
4102 pan_window_entry_activate_cb, pw);
4103 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
4104 G_CALLBACK(pan_window_entry_change_cb), pw);
4105 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
4106 gtk_widget_show(combo);
4108 combo = gtk_combo_box_new_text();
4109 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
4110 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
4111 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
4112 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
4113 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
4115 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
4116 g_signal_connect(G_OBJECT(combo), "changed",
4117 G_CALLBACK(pan_window_layout_change_cb), pw);
4118 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4119 gtk_widget_show(combo);
4121 combo = gtk_combo_box_new_text();
4122 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
4123 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
4124 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
4125 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
4126 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
4127 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
4128 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
4129 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
4130 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
4131 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
4133 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
4134 g_signal_connect(G_OBJECT(combo), "changed",
4135 G_CALLBACK(pan_window_layout_size_cb), pw);
4136 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
4137 gtk_widget_show(combo);
4139 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
4140 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
4141 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
4143 pw->imd = image_new(TRUE);
4144 pw->imd_normal = pw->imd;
4146 g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
4147 G_CALLBACK(pan_window_image_zoom_cb), pw);
4148 g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
4149 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
4151 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
4152 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
4153 gtk_widget_show(GTK_WIDGET(pw->imd->widget));
4155 pan_window_dnd_init(pw);
4157 pan_image_set_buttons(pw, pw->imd);
4159 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
4160 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
4161 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
4162 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
4163 GTK_FILL | GTK_EXPAND, 0, 0, 0);
4164 gtk_widget_show(pw->scrollbar_h);
4166 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
4167 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
4168 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
4169 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
4170 0, GTK_FILL | GTK_EXPAND, 0, 0);
4171 gtk_widget_show(pw->scrollbar_v);
4175 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4176 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
4178 pref_spacer(pw->search_box, 0);
4179 pref_label_new(pw->search_box, _("Find:"));
4181 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
4182 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
4183 gtk_widget_show(hbox);
4185 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
4186 pan_search_activate_cb, pw);
4187 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
4188 gtk_widget_show(combo);
4190 pw->search_label = gtk_label_new("");
4191 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
4192 gtk_widget_show(pw->search_label);
4196 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4198 frame = gtk_frame_new(NULL);
4199 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4200 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4201 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
4202 gtk_widget_show(frame);
4204 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
4205 gtk_container_add(GTK_CONTAINER(frame), hbox);
4206 gtk_widget_show(hbox);
4208 pref_spacer(hbox, 0);
4209 pw->label_message = pref_label_new(hbox, "");
4211 frame = gtk_frame_new(NULL);
4212 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
4213 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
4214 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4215 gtk_widget_show(frame);
4217 pw->label_zoom = gtk_label_new("");
4218 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4219 gtk_widget_show(pw->label_zoom);
4221 pw->search_button = gtk_toggle_button_new();
4222 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4223 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4224 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4225 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4226 gtk_widget_show(hbox);
4227 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4228 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4229 gtk_widget_show(pw->search_button_arrow);
4230 pref_label_new(hbox, _("Find"));
4232 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4233 gtk_widget_show(pw->search_button);
4234 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4235 G_CALLBACK(pan_search_toggle_cb), pw);
4237 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4238 G_CALLBACK(pan_window_delete_cb), pw);
4239 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4240 G_CALLBACK(pan_window_key_press_cb), pw);
4242 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4244 pan_window_layout_update_idle(pw);
4246 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4247 gtk_widget_show(pw->window);
4249 pan_window_list = g_list_append(pan_window_list, pw);
4253 *-----------------------------------------------------------------------------
4254 * peformance warnings
4255 *-----------------------------------------------------------------------------
4258 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4262 generic_dialog_close(gd);
4264 pan_window_new_real(path);
4268 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4272 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4273 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4276 static gint pan_warning(const gchar *path)
4282 GtkWidget *ct_button;
4285 if (enable_thumb_caching &&
4286 thumbnail_spec_standard) return FALSE;
4288 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4289 if (hide_dlg) return FALSE;
4291 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4293 gd->data = g_strdup(path);
4294 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4295 pan_warning_ok_cb, TRUE);
4297 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4298 _("Pan view performance may be poor."),
4299 _("To improve performance of thumbnails in the pan view the"
4300 " following options can be enabled. Note that both options"
4301 " must be enabled to notice a change in performance."));
4303 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4304 pref_spacer(group, PREF_PAD_INDENT);
4305 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4307 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4308 enable_thumb_caching, &enable_thumb_caching);
4309 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4310 thumbnail_spec_standard, &thumbnail_spec_standard);
4311 pref_checkbox_link_sensitivity(ct_button, button);
4315 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4316 G_CALLBACK(pan_warning_hide_cb), NULL);
4318 gtk_widget_show(gd->dialog);
4325 *-----------------------------------------------------------------------------
4327 *-----------------------------------------------------------------------------
4330 void pan_window_new(const gchar *path)
4332 if (pan_warning(path)) return;
4334 pan_window_new_real(path);
4338 *-----------------------------------------------------------------------------
4340 *-----------------------------------------------------------------------------
4343 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4345 PanWindow *pw = data;
4348 path = pan_menu_click_path(pw);
4351 pan_fullscreen_toggle(pw, TRUE);
4352 view_window_new(path);
4356 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4362 pw = submenu_item_get_data(widget);
4363 n = GPOINTER_TO_INT(data);
4366 path = pan_menu_click_path(pw);
4369 pan_fullscreen_toggle(pw, TRUE);
4370 start_editor_from_file(n, path);
4374 static void pan_info_cb(GtkWidget *widget, gpointer data)
4376 PanWindow *pw = data;
4379 path = pan_menu_click_path(pw);
4380 if (path) info_window_new(path, NULL);
4383 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4385 PanWindow *pw = data;
4387 image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
4390 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4392 PanWindow *pw = data;
4394 image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
4397 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4399 PanWindow *pw = data;
4401 image_zoom_set(pw->imd, 1.0);
4404 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4406 PanWindow *pw = data;
4409 path = pan_menu_click_path(pw);
4410 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4413 static void pan_move_cb(GtkWidget *widget, gpointer data)
4415 PanWindow *pw = data;
4418 path = pan_menu_click_path(pw);
4419 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4422 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4424 PanWindow *pw = data;
4427 path = pan_menu_click_path(pw);
4428 if (path) file_util_rename(path, NULL, pw->imd->widget);
4431 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4433 PanWindow *pw = data;
4436 path = pan_menu_click_path(pw);
4437 if (path) file_util_delete(path, NULL, pw->imd->widget);
4440 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4442 PanWindow *pw = data;
4444 pan_fullscreen_toggle(pw, FALSE);
4447 static void pan_close_cb(GtkWidget *widget, gpointer data)
4449 PanWindow *pw = data;
4451 pan_window_close(pw);
4454 static GtkWidget *pan_popup_menu(PanWindow *pw)
4460 active = (pw->click_pi != NULL);
4462 menu = popup_menu_short_lived();
4464 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4465 G_CALLBACK(pan_zoom_in_cb), pw);
4466 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4467 G_CALLBACK(pan_zoom_out_cb), pw);
4468 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4469 G_CALLBACK(pan_zoom_1_1_cb), pw);
4470 menu_item_add_divider(menu);
4472 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4473 gtk_widget_set_sensitive(item, active);
4475 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4476 G_CALLBACK(pan_info_cb), pw);
4478 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4479 G_CALLBACK(pan_new_window_cb), pw);
4481 menu_item_add_divider(menu);
4482 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4483 G_CALLBACK(pan_copy_cb), pw);
4484 menu_item_add_sensitive(menu, _("_Move..."), active,
4485 G_CALLBACK(pan_move_cb), pw);
4486 menu_item_add_sensitive(menu, _("_Rename..."), active,
4487 G_CALLBACK(pan_rename_cb), pw);
4488 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4489 G_CALLBACK(pan_delete_cb), pw);
4491 menu_item_add_divider(menu);
4495 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4499 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4502 menu_item_add_divider(menu);
4503 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4509 *-----------------------------------------------------------------------------
4511 *-----------------------------------------------------------------------------
4514 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4516 GtkSelectionData *selection_data, guint info,
4517 guint time, gpointer data)
4519 PanWindow *pw = data;
4521 if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
4523 if (info == TARGET_URI_LIST)
4527 list = uri_list_from_text(selection_data->data, TRUE);
4528 if (list && isdir((gchar *)list->data))
4530 gchar *path = list->data;
4533 pw->path = g_strdup(path);
4535 pan_window_layout_update_idle(pw);
4538 path_list_free(list);
4542 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4543 GtkSelectionData *selection_data, guint info,
4544 guint time, gpointer data)
4546 PanWindow *pw = data;
4549 path = pan_menu_click_path(pw);
4559 case TARGET_URI_LIST:
4562 case TARGET_TEXT_PLAIN:
4567 list = g_list_append(NULL, (gchar *)path);
4568 text = uri_text_from_list(list, &len, plain_text);
4572 gtk_selection_data_set (selection_data, selection_data->target,
4579 gtk_selection_data_set (selection_data, selection_data->target,
4584 static void pan_window_dnd_init(PanWindow *pw)
4588 widget = pw->imd->pr;
4590 gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
4591 dnd_file_drag_types, dnd_file_drag_types_count,
4592 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4593 g_signal_connect(G_OBJECT(widget), "drag_data_get",
4594 G_CALLBACK(pan_window_set_dnd_data), pw);
4596 gtk_drag_dest_set(widget,
4597 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4598 dnd_file_drop_types, dnd_file_drop_types_count,
4599 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4600 g_signal_connect(G_OBJECT(widget), "drag_data_received",
4601 G_CALLBACK(pan_window_get_dnd_data), pw);
4605 *-----------------------------------------------------------------------------
4606 * maintenance (for rename, move, remove)
4607 *-----------------------------------------------------------------------------