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;
230 typedef struct _PanCacheData PanCacheData;
231 struct _PanCacheData {
237 static GList *pan_window_list = NULL;
240 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend);
242 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
244 static GtkWidget *pan_popup_menu(PanWindow *pw);
245 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off);
247 static void pan_window_close(PanWindow *pw);
249 static void pan_window_dnd_init(PanWindow *pw);
261 static gint date_compare(time_t a, time_t b, DateLengthType length)
266 if (length == DATE_LENGTH_EXACT) return (a == b);
268 if (!localtime_r(&a, &ta) ||
269 !localtime_r(&b, &tb)) return FALSE;
271 if (ta.tm_year != tb.tm_year) return FALSE;
272 if (length == DATE_LENGTH_YEAR) return TRUE;
274 if (ta.tm_mon != tb.tm_mon) return FALSE;
275 if (length == DATE_LENGTH_MONTH) return TRUE;
277 if (length == DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
279 if (ta.tm_mday != tb.tm_mday) return FALSE;
280 if (length == DATE_LENGTH_DAY) return TRUE;
282 return (ta.tm_hour == tb.tm_hour);
285 static gint date_value(time_t d, DateLengthType length)
289 if (!localtime_r(&d, &td)) return -1;
293 case DATE_LENGTH_DAY:
296 case DATE_LENGTH_WEEK:
299 case DATE_LENGTH_MONTH:
300 return td.tm_mon + 1;
302 case DATE_LENGTH_YEAR:
303 return td.tm_year + 1900;
305 case DATE_LENGTH_EXACT:
313 static gchar *date_value_string(time_t d, DateLengthType length)
317 gchar *format = NULL;
319 if (!localtime_r(&d, &td)) return g_strdup("");
323 case DATE_LENGTH_DAY:
324 return g_strdup_printf("%d", td.tm_mday);
326 case DATE_LENGTH_WEEK:
329 case DATE_LENGTH_MONTH:
332 case DATE_LENGTH_YEAR:
333 return g_strdup_printf("%d", td.tm_year + 1900);
335 case DATE_LENGTH_EXACT:
337 return g_strdup(text_from_time(d));
342 if (format && strftime(buf, sizeof(buf), format, &td) > 0)
344 gchar *ret = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
351 static time_t date_to_time(gint year, gint month, gint day)
358 lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
359 lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
360 lt.tm_year = year - 1900;
367 *-----------------------------------------------------------------------------
369 *-----------------------------------------------------------------------------
372 static void pan_cache_free(PanWindow *pw)
376 work = pw->cache_list;
384 cache_sim_data_free(pc->cd);
385 file_data_free((FileData *)pc);
388 g_list_free(pw->cache_list);
389 pw->cache_list = NULL;
391 filelist_free(pw->cache_todo);
392 pw->cache_todo = NULL;
399 static void pan_cache_fill(PanWindow *pw, const gchar *path)
405 list = pan_window_layout_list(path, SORT_NAME, TRUE);
406 pw->cache_todo = g_list_reverse(list);
408 pw->cache_total = g_list_length(pw->cache_todo);
411 static gint pan_cache_step(PanWindow *pw)
415 CacheData *cd = NULL;
417 if (!pw->cache_todo) return FALSE;
419 fd = pw->cache_todo->data;
420 pw->cache_todo = g_list_remove(pw->cache_todo, fd);
422 if (enable_thumb_caching)
426 found = cache_find_location(CACHE_TYPE_SIM, fd->path);
427 if (found && filetime(found) == fd->date)
429 cd = cache_sim_data_load(found);
434 if (!cd) cd = cache_sim_data_new();
438 cd->dimensions = image_load_dimensions(fd->path, &cd->width, &cd->height);
439 if (enable_thumb_caching &&
445 base = cache_get_location(CACHE_TYPE_SIM, fd->path, FALSE, &mode);
446 if (cache_ensure_dir_exists(base, mode))
449 cd->path = cache_get_location(CACHE_TYPE_SIM, fd->path, TRUE, NULL);
450 if (cache_sim_data_save(cd))
452 filetime_set(cd->path, filetime(fd->path));
461 pc = g_new0(PanCacheData, 1);
462 memcpy(pc, fd, sizeof(FileData));
467 pw->cache_list = g_list_prepend(pw->cache_list, pc);
474 *-----------------------------------------------------------------------------
476 *-----------------------------------------------------------------------------
479 static void pan_item_free(PanItem *pi)
483 if (pi->pixbuf) g_object_unref(pi->pixbuf);
484 if (pi->fd) file_data_free(pi->fd);
492 static void pan_window_items_free(PanWindow *pw)
499 PanItem *pi = work->data;
505 g_list_free(pw->list);
508 g_list_free(pw->queue);
512 image_loader_free(pw->il);
515 thumb_loader_free(pw->tl);
519 pw->search_pi = NULL;
522 static PanItem *pan_item_new_thumb(PanWindow *pw, FileData *fd, gint x, gint y)
526 pi = g_new0(PanItem, 1);
527 pi->type = ITEM_THUMB;
531 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
532 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
538 pw->list = g_list_prepend(pw->list, pi);
543 static PanItem *pan_item_new_box(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
545 guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
546 guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
550 pi = g_new0(PanItem, 1);
558 pi->color_r = base_r;
559 pi->color_g = base_g;
560 pi->color_b = base_b;
561 pi->color_a = base_a;
563 pi->color2_r = bord_r;
564 pi->color2_g = bord_g;
565 pi->color2_b = bord_b;
566 pi->color2_a = bord_a;
567 pi->border = border_size;
569 pw->list = g_list_prepend(pw->list, pi);
574 static void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
578 if (!pi || pi->type != ITEM_BOX) return;
583 pi->width -= shadow[0];
584 pi->height -= shadow[0];
587 shadow = g_new0(gint, 2);
592 pi->height += offset;
598 static PanItem *pan_item_new_tri(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
599 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
600 guint8 r, guint8 g, guint8 b, guint8 a)
605 pi = g_new0(PanItem, 1);
606 pi->type = ITEM_TRIANGLE;
617 coord = g_new0(gint, 6);
627 pi->border = BORDER_NONE;
629 pw->list = g_list_prepend(pw->list, pi);
634 static void pan_item_tri_border(PanItem *pi, gint borders,
635 guint8 r, guint8 g, guint8 b, guint8 a)
637 if (!pi || pi->type != ITEM_TRIANGLE) return;
639 pi->border = borders;
647 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
651 layout = gtk_widget_create_pango_layout(widget, NULL);
653 if (pi->text_attr & TEXT_ATTR_MARKUP)
655 pango_layout_set_markup(layout, pi->text, -1);
659 if (pi->text_attr & TEXT_ATTR_BOLD ||
660 pi->text_attr & TEXT_ATTR_HEADING)
665 pal = pango_attr_list_new();
666 if (pi->text_attr & TEXT_ATTR_BOLD)
668 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
670 pa->end_index = G_MAXINT;
671 pango_attr_list_insert(pal, pa);
673 if (pi->text_attr & TEXT_ATTR_HEADING)
675 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
677 pa->end_index = G_MAXINT;
678 pango_attr_list_insert(pal, pa);
680 pango_layout_set_attributes(layout, pal);
681 pango_attr_list_unref(pal);
684 pango_layout_set_text(layout, pi->text, -1);
688 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
692 if (!pi || !pi->text || !widget) return;
694 layout = pan_item_text_layout(pi, widget);
695 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
696 g_object_unref(G_OBJECT(layout));
698 pi->width += PAN_TEXT_BORDER_SIZE * 2;
699 pi->height += PAN_TEXT_BORDER_SIZE * 2;
702 static PanItem *pan_item_new_text(PanWindow *pw, gint x, gint y, const gchar *text, TextAttrType attr,
703 guint8 r, guint8 g, guint8 b, guint8 a)
707 pi = g_new0(PanItem, 1);
708 pi->type = ITEM_TEXT;
711 pi->text = g_strdup(text);
712 pi->text_attr = attr;
719 pan_item_text_compute_size(pi, pw->imd->pr);
721 pw->list = g_list_prepend(pw->list, pi);
726 static void pan_item_set_key(PanItem *pi, const gchar *key)
733 pi->key = g_strdup(key);
737 static void pan_item_added(PanWindow *pw, PanItem *pi)
740 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
743 static void pan_item_remove(PanWindow *pw, PanItem *pi)
747 if (pw->click_pi == pi) pw->click_pi = NULL;
748 if (pw->queue_pi == pi) pw->queue_pi = NULL;
749 if (pw->search_pi == pi) pw->search_pi = NULL;
750 pw->queue = g_list_remove(pw->queue, pi);
752 pw->list = g_list_remove(pw->list, pi);
753 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
757 static void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
759 if (!pi || !child) return;
761 if (pi->x + pi->width < child->x + child->width + border)
762 pi->width = child->x + child->width + border - pi->x;
764 if (pi->y + pi->height < child->y + child->height + border)
765 pi->height = child->y + child->height + border - pi->y;
768 static void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
772 if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
773 if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
776 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
785 work = pw->cache_list;
794 path = ((FileData *)pc)->path;
796 if (pc->cd && pc->cd->dimensions &&
797 path && strcmp(path, pi->fd->path) == 0)
799 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
800 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
802 pw->cache_list = g_list_remove(pw->cache_list, pc);
803 cache_sim_data_free(pc->cd);
804 file_data_free((FileData *)pc);
810 static PanItem *pan_item_new_image(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
814 pi = g_new0(PanItem, 1);
815 pi->type = ITEM_IMAGE;
820 pan_item_image_find_size(pw, pi, w, h);
822 pw->list = g_list_prepend(pw->list, pi);
827 static PanItem *pan_item_find_by_key(PanWindow *pw, ItemType type, const gchar *key)
831 if (!key) return NULL;
833 work = g_list_last(pw->list);
839 if ((pi->type == type || type == ITEM_NONE) &&
840 pi->key && strcmp(pi->key, key) == 0)
850 /* when ignore_case and partial are TRUE, path should be converted to lower case */
851 static GList *pan_item_find_by_path(PanWindow *pw, ItemType type, const gchar *path,
852 gint ignore_case, gint partial)
857 if (!path) return NULL;
858 if (partial && path[0] == '/') return NULL;
860 work = g_list_last(pw->list);
866 if ((pi->type == type || type == ITEM_NONE) && pi->fd)
872 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
874 else if (pi->fd->name)
882 haystack = g_utf8_strdown(pi->fd->name, -1);
883 match = (strstr(haystack, path) != NULL);
888 if (strstr(pi->fd->name, path)) match = TRUE;
891 else if (ignore_case)
893 if (strcasecmp(path, pi->fd->name) == 0) match = TRUE;
897 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
901 if (match) list = g_list_prepend(list, pi);
906 return g_list_reverse(list);
909 static PanItem *pan_item_find_by_coord(PanWindow *pw, ItemType type, gint x, gint y, const gchar *key)
919 if ((pi->type == type || type == ITEM_NONE) &&
920 x >= pi->x && x < pi->x + pi->width &&
921 y >= pi->y && y < pi->y + pi->height &&
922 (!key || (pi->key && strcmp(pi->key, key) == 0)))
933 *-----------------------------------------------------------------------------
935 *-----------------------------------------------------------------------------
938 static GList *pan_window_layout_list(const gchar *path, SortType sort, gint ascend)
945 filelist_read(path, &flist, &dlist);
946 if (sort != SORT_NONE)
948 flist = filelist_sort(flist, sort, ascend);
949 dlist = filelist_sort(dlist, sort, ascend);
959 folders = g_list_remove(folders, fd);
961 if (filelist_read(fd->path, &flist, &dlist))
963 if (sort != SORT_NONE)
965 flist = filelist_sort(flist, sort, ascend);
966 dlist = filelist_sort(dlist, sort, ascend);
969 result = g_list_concat(result, flist);
970 folders = g_list_concat(dlist, folders);
979 static void pan_window_layout_compute_grid(PanWindow *pw, const gchar *path, gint *width, gint *height)
987 list = pan_window_layout_list(path, SORT_NAME, TRUE);
989 grid_size = (gint)sqrt((double)g_list_length(list));
990 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
992 grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
996 grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
1001 *width = PAN_FOLDER_BOX_BORDER * 2;
1002 *height = PAN_FOLDER_BOX_BORDER * 2;
1015 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1017 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1019 x += pi->width + PAN_THUMB_GAP;
1020 if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
1029 pi = pan_item_new_thumb(pw, fd, x, y);
1031 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1035 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1038 pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
1044 static void pan_window_Layout_compute_folders_flower_size(PanWindow *pw, gint *width, gint *height)
1047 gint x1, y1, x2, y2;
1062 if (x1 > pi->x) x1 = pi->x;
1063 if (y1 > pi->y) y1 = pi->y;
1064 if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
1065 if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
1068 x1 -= PAN_FOLDER_BOX_BORDER;
1069 y1 -= PAN_FOLDER_BOX_BORDER;
1070 x2 += PAN_FOLDER_BOX_BORDER;
1071 y2 += PAN_FOLDER_BOX_BORDER;
1084 if (pi->type == ITEM_TRIANGLE && pi->data)
1098 if (width) *width = x2 - x1;
1099 if (height) *height = y2 - y1;
1102 typedef struct _FlowerGroup FlowerGroup;
1103 struct _FlowerGroup {
1116 static void pan_window_layout_compute_folder_flower_move(FlowerGroup *group, gint x, gint y)
1120 work = group->items;
1138 static void pan_window_layout_compute_folder_flower_position(FlowerGroup *group, FlowerGroup *parent,
1139 gint *result_x, gint *result_y)
1145 radius = parent->circumference / (2*PI);
1146 radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
1148 a = 2*PI * group->diameter / parent->circumference;
1150 x = (gint)((double)radius * cos(parent->angle + a / 2));
1151 y = (gint)((double)radius * sin(parent->angle + a / 2));
1158 x += parent->width / 2;
1159 y += parent->height / 2;
1161 x -= group->width / 2;
1162 y -= group->height / 2;
1168 static void pan_window_layout_compute_folder_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
1175 if (parent && parent->children)
1177 pan_window_layout_compute_folder_flower_position(group, parent, &x, &y);
1185 pan_window_layout_compute_folder_flower_move(group, x, y);
1190 gint px, py, gx, gy;
1191 gint x1, y1, x2, y2;
1193 px = parent->x + parent->width / 2;
1194 py = parent->y + parent->height / 2;
1196 gx = group->x + group->width / 2;
1197 gy = group->y + group->height / 2;
1202 x2 = MAX(px, gx + 5);
1203 y2 = MAX(py, gy + 5);
1205 pi = pan_item_new_tri(pw, NULL, x1, y1, x2 - x1, y2 - y1,
1206 px, py, gx, gy, gx + 5, gy + 5,
1208 pan_item_tri_border(pi, BORDER_1 | BORDER_3,
1212 pw->list = g_list_concat(group->items, pw->list);
1213 group->items = NULL;
1215 group->circumference = 0;
1216 work = group->children;
1224 group->circumference += child->diameter;
1227 work = g_list_last(group->children);
1235 pan_window_layout_compute_folder_flower_build(pw, child, group);
1238 g_list_free(group->children);
1242 static FlowerGroup *pan_window_layout_compute_folders_flower_path(PanWindow *pw, const gchar *path,
1255 if (!filelist_read(path, &f, &d)) return NULL;
1256 if (!f && !d) return NULL;
1258 f = filelist_sort(f, SORT_NAME, TRUE);
1259 d = filelist_sort(d, SORT_NAME, TRUE);
1261 pi_box = pan_item_new_text(pw, x, y, path, TEXT_ATTR_NONE,
1262 PAN_TEXT_COLOR, 255);
1264 y += pi_box->height;
1266 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1268 PAN_FOLDER_BOX_BORDER * 2, PAN_FOLDER_BOX_BORDER * 2,
1269 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1270 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1271 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1273 x += PAN_FOLDER_BOX_BORDER;
1274 y += PAN_FOLDER_BOX_BORDER;
1276 grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
1290 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1292 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1293 x += pi->width + PAN_THUMB_GAP;
1294 if (pi->height > y_height) y_height = pi->height;
1298 pi = pan_item_new_thumb(pw, fd, x, y);
1299 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1300 y_height = PAN_THUMB_SIZE;
1304 if (grid_count >= grid_size)
1308 y += y_height + PAN_THUMB_GAP;
1312 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1317 group = g_new0(FlowerGroup, 1);
1318 group->items = pw->list;
1321 group->width = pi_box->width;
1322 group->height = pi_box->y + pi_box->height;
1323 group->diameter = (int)sqrt(group->width * group->width + group->height * group->height);
1325 group->children = NULL;
1336 child = pan_window_layout_compute_folders_flower_path(pw, fd->path, 0, 0);
1337 if (child) group->children = g_list_prepend(group->children, child);
1345 static void pan_window_layout_compute_folders_flower(PanWindow *pw, const gchar *path,
1346 gint *width, gint *height,
1347 gint *scroll_x, gint *scroll_y)
1352 group = pan_window_layout_compute_folders_flower_path(pw, path, 0, 0);
1353 pan_window_layout_compute_folder_flower_build(pw, group, NULL);
1355 pan_window_Layout_compute_folders_flower_size(pw, width, height);
1357 list = pan_item_find_by_path(pw, ITEM_BOX, path, FALSE, FALSE);
1360 PanItem *pi = list->data;
1361 *scroll_x = pi->x + pi->width / 2;
1362 *scroll_y = pi->y + pi->height / 2;
1367 static void pan_window_layout_compute_folders_linear_path(PanWindow *pw, const gchar *path,
1368 gint *x, gint *y, gint *level,
1370 gint *width, gint *height)
1378 if (!filelist_read(path, &f, &d)) return;
1379 if (!f && !d) return;
1381 f = filelist_sort(f, SORT_NAME, TRUE);
1382 d = filelist_sort(d, SORT_NAME, TRUE);
1384 *x = PAN_FOLDER_BOX_BORDER + ((*level) * MAX(PAN_FOLDER_BOX_BORDER, PAN_THUMB_GAP));
1386 pi_box = pan_item_new_text(pw, *x, *y, path, TEXT_ATTR_NONE,
1387 PAN_TEXT_COLOR, 255);
1389 *y += pi_box->height;
1391 pi_box = pan_item_new_box(pw, file_data_new_simple(path),
1393 PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1394 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1395 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1396 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1398 *x += PAN_FOLDER_BOX_BORDER;
1399 *y += PAN_FOLDER_BOX_BORDER;
1410 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1412 pi = pan_item_new_image(pw, fd, *x, *y, 10, 10);
1413 *x += pi->width + PAN_THUMB_GAP;
1414 if (pi->height > y_height) y_height = pi->height;
1418 pi = pan_item_new_thumb(pw, fd, *x, *y);
1419 *x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1420 y_height = PAN_THUMB_SIZE;
1423 pan_item_size_by_item(pi_box, pi, PAN_FOLDER_BOX_BORDER);
1426 if (f) *y = pi_box->y + pi_box->height;
1438 *level = *level + 1;
1439 pan_window_layout_compute_folders_linear_path(pw, fd->path, x, y, level,
1440 pi_box, width, height);
1441 *level = *level - 1;
1446 pan_item_size_by_item(parent, pi_box, PAN_FOLDER_BOX_BORDER);
1448 if (*y < pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER)
1449 *y = pi_box->y + pi_box->height + PAN_FOLDER_BOX_BORDER;
1451 pan_item_size_coordinates(pi_box, PAN_FOLDER_BOX_BORDER, width, height);
1454 static void pan_window_layout_compute_folders_linear(PanWindow *pw, const gchar *path, gint *width, gint *height)
1461 x = PAN_FOLDER_BOX_BORDER;
1462 y = PAN_FOLDER_BOX_BORDER;
1463 w = PAN_FOLDER_BOX_BORDER * 2;
1464 h = PAN_FOLDER_BOX_BORDER * 2;
1466 pan_window_layout_compute_folders_linear_path(pw, path, &x, &y, &level, NULL, &w, &h);
1468 if (width) *width = w;
1469 if (height) *height = h;
1473 *-----------------------------------------------------------------------------
1475 *-----------------------------------------------------------------------------
1478 #define PAN_CAL_DAY_WIDTH 100
1479 #define PAN_CAL_DAY_HEIGHT 80
1480 #define PAN_CAL_DOT_SIZE 3
1481 #define PAN_CAL_DOT_GAP 2
1482 #define PAN_CAL_DOT_COLOR 0, 0, 0
1483 #define PAN_CAL_DOT_ALPHA 32
1485 static void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
1491 gint x1, y1, x2, y2, x3, y3;
1496 while ((pi = pan_item_find_by_key(pw, ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
1498 if (!pi_day || pi_day->type != ITEM_BOX ||
1499 !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
1501 list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
1513 if (dot->type != ITEM_BOX || !dot->fd ||
1514 !dot->key || strcmp(dot->key, "dot") != 0)
1516 list = g_list_delete_link(list, node);
1524 grid = (gint)(sqrt(g_list_length(list)) + 0.5);
1526 x = pi_day->x + pi_day->width + 4;
1530 if (y + grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4 > pw->pr->image_height)
1532 y = pw->pr->image_height - (grid * (PAN_THUMB_SIZE + PAN_THUMB_GAP) + PAN_FOLDER_BOX_BORDER * 4);
1536 pbox = pan_item_new_box(pw, NULL, x, y, PAN_FOLDER_BOX_BORDER, PAN_FOLDER_BOX_BORDER,
1538 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
1539 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
1540 pan_item_set_key(pbox, "day_bubble");
1547 buf = date_value_string(pi_day->fd->date, DATE_LENGTH_WEEK);
1548 plabel = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1549 PAN_POPUP_TEXT_COLOR, 255);
1550 pan_item_set_key(plabel, "day_bubble");
1553 pan_item_size_by_item(pbox, plabel, 0);
1555 y += plabel->height;
1562 x += PAN_FOLDER_BOX_BORDER;
1563 y += PAN_FOLDER_BOX_BORDER;
1577 pimg = pan_item_new_thumb(pw, file_data_new_simple(dot->fd->path), x, y);
1578 pan_item_set_key(pimg, "day_bubble");
1580 pan_item_size_by_item(pbox, pimg, PAN_FOLDER_BOX_BORDER);
1585 x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1590 x = pbox->x + PAN_FOLDER_BOX_BORDER;
1591 y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
1597 x1 = pi_day->x + pi_day->width - 8;
1600 y2 = pbox->y + MIN(42, pbox->height);
1602 y3 = MAX(pbox->y, y2 - 30);
1603 util_clip_triangle(x1, y1, x2, y2, x3, y3,
1606 pi = pan_item_new_tri(pw, NULL, x, y, w, h,
1607 x1, y1, x2, y2, x3, y3,
1608 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
1609 pan_item_tri_border(pi, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
1610 pan_item_set_key(pi, "day_bubble");
1611 pan_item_added(pw, pi);
1613 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
1614 pan_item_added(pw, pbox);
1617 static void pan_window_layout_compute_calendar(PanWindow *pw, const gchar *path, gint *width, gint *height)
1633 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
1635 list = pan_window_layout_list(path, SORT_NONE, TRUE);
1636 list = filelist_sort(list, SORT_TIME, TRUE);
1649 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
1657 if (day_max < count) day_max = count;
1661 printf("biggest day contains %d images\n", day_max);
1663 grid = (gint)(sqrt((double)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
1664 day_width = MAX(PAN_CAL_DAY_WIDTH, grid);
1665 day_height = MAX(PAN_CAL_DAY_HEIGHT, grid);
1669 FileData *fd = list->data;
1671 year = date_value(fd->date, DATE_LENGTH_YEAR);
1672 month = date_value(fd->date, DATE_LENGTH_MONTH);
1675 work = g_list_last(list);
1678 FileData *fd = work->data;
1679 end_year = date_value(fd->date, DATE_LENGTH_YEAR);
1680 end_month = date_value(fd->date, DATE_LENGTH_MONTH);
1683 *width = PAN_FOLDER_BOX_BORDER * 2;
1684 *height = PAN_FOLDER_BOX_BORDER * 2;
1686 x = PAN_FOLDER_BOX_BORDER;
1687 y = PAN_FOLDER_BOX_BORDER;
1690 while (work && (year < end_year || (year == end_year && month <= end_month)))
1701 dt = date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
1703 days = date_value(dt, DATE_LENGTH_DAY);
1704 dt = date_to_time(year, month, 1);
1705 col = date_value(dt, DATE_LENGTH_WEEK);
1708 x = PAN_FOLDER_BOX_BORDER;
1710 pi_month = pan_item_new_box(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
1711 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1712 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1713 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1714 buf = date_value_string(dt, DATE_LENGTH_MONTH);
1715 pi_text = pan_item_new_text(pw, x, y, buf,
1716 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1717 PAN_TEXT_COLOR, 255);
1719 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
1721 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
1723 x = PAN_FOLDER_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
1724 y = pi_month->y + pi_month->height + PAN_FOLDER_BOX_BORDER;
1726 for (day = 1; day <= days; day++)
1733 dt = date_to_time(year, month, day);
1735 fd = g_new0(FileData, 1);
1736 /* path and name must be non NULL, so make them an invalid filename */
1737 fd->path = g_strdup("//");
1740 pi_day = pan_item_new_box(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
1741 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1742 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1743 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1744 pan_item_set_key(pi_day, "day");
1746 dx = x + PAN_CAL_DOT_GAP * 2;
1747 dy = y + PAN_CAL_DOT_GAP * 2;
1749 fd = (work) ? work->data : NULL;
1750 while (fd && date_compare(fd->date, dt, DATE_LENGTH_DAY))
1754 pi = pan_item_new_box(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
1756 PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
1758 pan_item_set_key(pi, "dot");
1760 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
1761 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
1763 dx = x + PAN_CAL_DOT_GAP * 2;
1764 dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
1766 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
1768 /* must keep all dots within respective day even if it gets ugly */
1769 dy = y + PAN_CAL_DOT_GAP * 2;
1772 pi_day->color_a = MIN(PAN_FOLDER_BOX_ALPHA + 64 + n, 255);
1776 fd = (work) ? work->data : NULL;
1783 buf = g_strdup_printf("( %d )", n);
1784 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
1785 PAN_TEXT_COLOR, 255);
1788 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
1789 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
1792 buf = g_strdup_printf("%d", day);
1793 pan_item_new_text(pw, x + 4, y + 4, buf, TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1794 PAN_TEXT_COLOR, 255);
1798 pan_item_size_coordinates(pi_day, PAN_FOLDER_BOX_BORDER, width, height);
1805 x = PAN_FOLDER_BOX_BORDER;
1806 y += PAN_CAL_DAY_HEIGHT;
1810 x += PAN_CAL_DAY_WIDTH;
1814 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
1815 y += PAN_FOLDER_BOX_BORDER * 2;
1826 *height = MAX(*height, grid + PAN_FOLDER_BOX_BORDER * 2 * 2);
1831 static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path, gint *width, gint *height)
1839 PanItem *pi_month = NULL;
1840 PanItem *pi_day = NULL;
1846 pw->cache_list = filelist_sort(pw->cache_list, SORT_TIME, TRUE);
1848 list = pan_window_layout_list(path, SORT_NONE, TRUE);
1849 list = filelist_sort(list, SORT_TIME, TRUE);
1851 *width = PAN_FOLDER_BOX_BORDER * 2;
1852 *height = PAN_FOLDER_BOX_BORDER * 2;
1857 day_start = month_start;
1872 if (!date_compare(fd->date, tc, DATE_LENGTH_DAY))
1877 if (!date_compare(fd->date, tc, DATE_LENGTH_MONTH))
1883 x = pi_month->x + pi_month->width + PAN_FOLDER_BOX_BORDER;
1887 x = PAN_FOLDER_BOX_BORDER;
1890 y = PAN_FOLDER_BOX_BORDER;
1892 buf = date_value_string(fd->date, DATE_LENGTH_MONTH);
1893 pi = pan_item_new_text(pw, x, y, buf,
1894 TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
1895 PAN_TEXT_COLOR, 255);
1898 pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
1900 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1901 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1902 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1904 x += PAN_FOLDER_BOX_BORDER;
1905 y += PAN_FOLDER_BOX_BORDER;
1909 if (pi_day) x = pi_day->x + pi_day->width + PAN_FOLDER_BOX_BORDER;
1921 if (date_compare(nfd->date, tc, DATE_LENGTH_DAY))
1923 needle = needle->next;
1932 buf = date_value_string(fd->date, DATE_LENGTH_WEEK);
1933 pi = pan_item_new_text(pw, x, y, buf, TEXT_ATTR_NONE,
1934 PAN_TEXT_COLOR, 255);
1939 pi_day = pan_item_new_box(pw, file_data_new_simple(fd->path), x, y, 0, 0,
1940 PAN_FOLDER_BOX_OUTLINE_THICKNESS,
1941 PAN_FOLDER_BOX_COLOR, PAN_FOLDER_BOX_ALPHA,
1942 PAN_FOLDER_BOX_OUTLINE_COLOR, PAN_FOLDER_BOX_OUTLINE_ALPHA);
1944 x += PAN_FOLDER_BOX_BORDER;
1945 y += PAN_FOLDER_BOX_BORDER;
1949 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
1951 pi = pan_item_new_image(pw, fd, x, y, 10, 10);
1952 if (pi->width > x_width) x_width = pi->width;
1953 y_height = pi->height;
1957 pi = pan_item_new_thumb(pw, fd, x, y);
1958 x_width = PAN_THUMB_SIZE;
1959 y_height = PAN_THUMB_SIZE;
1962 pan_item_size_by_item(pi_day, pi, PAN_FOLDER_BOX_BORDER);
1963 pan_item_size_by_item(pi_month, pi_day, PAN_FOLDER_BOX_BORDER);
1968 if (total > 0 && count < PAN_GROUP_MAX)
1970 y += y_height + PAN_THUMB_GAP;
1974 x += x_width + PAN_THUMB_GAP;
1984 pan_item_size_coordinates(pi_month, PAN_FOLDER_BOX_BORDER, width, height);
1990 static void pan_window_layout_compute(PanWindow *pw, const gchar *path,
1991 gint *width, gint *height,
1992 gint *scroll_x, gint *scroll_y)
1994 pan_window_items_free(pw);
1998 case LAYOUT_SIZE_THUMB_DOTS:
1999 pw->thumb_size = PAN_THUMB_SIZE_DOTS;
2000 pw->thumb_gap = PAN_THUMB_GAP_DOTS;
2002 case LAYOUT_SIZE_THUMB_NONE:
2003 pw->thumb_size = PAN_THUMB_SIZE_NONE;
2004 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2006 case LAYOUT_SIZE_THUMB_SMALL:
2007 pw->thumb_size = PAN_THUMB_SIZE_SMALL;
2008 pw->thumb_gap = PAN_THUMB_GAP_SMALL;
2010 case LAYOUT_SIZE_THUMB_NORMAL:
2012 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
2013 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2015 case LAYOUT_SIZE_THUMB_LARGE:
2016 pw->thumb_size = PAN_THUMB_SIZE_LARGE;
2017 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2019 case LAYOUT_SIZE_10:
2020 pw->image_size = 10;
2021 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2023 case LAYOUT_SIZE_25:
2024 pw->image_size = 25;
2025 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
2027 case LAYOUT_SIZE_33:
2028 pw->image_size = 33;
2029 pw->thumb_gap = PAN_THUMB_GAP_LARGE;
2031 case LAYOUT_SIZE_50:
2032 pw->image_size = 50;
2033 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2035 case LAYOUT_SIZE_100:
2036 pw->image_size = 100;
2037 pw->thumb_gap = PAN_THUMB_GAP_HUGE;
2050 pan_window_layout_compute_grid(pw, path, width, height);
2052 case LAYOUT_FOLDERS_LINEAR:
2053 pan_window_layout_compute_folders_linear(pw, path, width, height);
2055 case LAYOUT_FOLDERS_FLOWER:
2056 pan_window_layout_compute_folders_flower(pw, path, width, height, scroll_x, scroll_y);
2058 case LAYOUT_CALENDAR:
2059 pan_window_layout_compute_calendar(pw, path, width, height);
2061 case LAYOUT_TIMELINE:
2062 pan_window_layout_compute_timeline(pw, path, width, height);
2068 printf("computed %d objects\n", g_list_length(pw->list));
2071 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
2080 gint rx, ry, rw, rh;
2085 if (util_clip_region(x, y, width, height,
2086 pi->x, pi->y, pi->width, pi->height,
2087 &rx, &ry, &rw, &rh))
2089 list = g_list_prepend(list, pi);
2097 *-----------------------------------------------------------------------------
2099 *-----------------------------------------------------------------------------
2102 static gint pan_layout_queue_step(PanWindow *pw);
2105 static void pan_layout_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
2107 PanWindow *pw = data;
2115 pw->queue_pi = NULL;
2119 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2120 pi->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
2123 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2127 thumb_loader_free(pw->tl);
2130 while (pan_layout_queue_step(pw));
2133 static void pan_layout_queue_image_done_cb(ImageLoader *il, gpointer data)
2135 PanWindow *pw = data;
2143 pw->queue_pi = NULL;
2147 if (pi->pixbuf) g_object_unref(pi->pixbuf);
2148 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2149 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2151 if (pi->pixbuf && pw->size != LAYOUT_SIZE_100 &&
2152 (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
2153 gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
2158 pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
2159 (GdkInterpType)zoom_quality);
2160 g_object_unref(tmp);
2164 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
2168 image_loader_free(pw->il);
2171 while (pan_layout_queue_step(pw));
2175 static void pan_layout_queue_image_area_cb(ImageLoader *il, guint x, guint y,
2176 guint width, guint height, gpointer data)
2178 PanWindow *pw = data;
2189 pi->pixbuf = image_loader_get_pixbuf(pw->il);
2190 if (pi->pixbuf) g_object_ref(pi->pixbuf);
2194 image_area_changed(pw->imd, pi->x + x, pi->y + y, width, height);
2200 static gint pan_layout_queue_step(PanWindow *pw)
2204 if (!pw->queue) return FALSE;
2206 pi = pw->queue->data;
2207 pw->queue = g_list_remove(pw->queue, pi);
2210 if (!pw->queue_pi->fd)
2212 pw->queue_pi->queued = FALSE;
2213 pw->queue_pi = NULL;
2217 image_loader_free(pw->il);
2219 thumb_loader_free(pw->tl);
2222 if (pi->type == ITEM_IMAGE)
2224 pw->il = image_loader_new(pi->fd->path);
2226 if (pw->size != LAYOUT_SIZE_100)
2228 image_loader_set_requested_size(pw->il, pi->width, pi->height);
2232 image_loader_set_area_ready_func(pw->il, pan_layout_queue_image_area_cb, pw);
2234 image_loader_set_error_func(pw->il, pan_layout_queue_image_done_cb, pw);
2236 if (image_loader_start(pw->il, pan_layout_queue_image_done_cb, pw)) return FALSE;
2238 image_loader_free(pw->il);
2241 else if (pi->type == ITEM_THUMB)
2243 pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
2245 if (!pw->tl->standard_loader)
2247 /* The classic loader will recreate a thumbnail any time we
2248 * request a different size than what exists. This view will
2249 * almost never use the user configured sizes so disable cache.
2251 thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
2254 thumb_loader_set_callbacks(pw->tl,
2255 pan_layout_queue_thumb_done_cb,
2256 pan_layout_queue_thumb_done_cb,
2259 if (thumb_loader_start(pw->tl, pi->fd->path)) return FALSE;
2261 thumb_loader_free(pw->tl);
2265 pw->queue_pi->queued = FALSE;
2266 pw->queue_pi = NULL;
2270 static void pan_layout_queue(PanWindow *pw, PanItem *pi)
2272 if (!pi || pi->queued || pi->pixbuf) return;
2273 if (pw->size <= LAYOUT_SIZE_THUMB_NONE) return;
2276 pw->queue = g_list_prepend(pw->queue, pi);
2278 if (!pw->tl && !pw->il) while(pan_layout_queue_step(pw));
2281 static gint pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
2282 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2284 PanWindow *pw = data;
2289 pixbuf_set_rect_fill(pixbuf,
2290 0, 0, width, height,
2291 PAN_BACKGROUND_COLOR, 255);
2293 for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
2295 gint rx, ry, rw, rh;
2297 if (util_clip_region(x, y, width, height,
2299 &rx, &ry, &rw, &rh))
2301 pixbuf_draw_rect_fill(pixbuf,
2302 rx - x, ry - y, rw, rh,
2303 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2306 for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
2308 gint rx, ry, rw, rh;
2310 if (util_clip_region(x, y, width, height,
2312 &rx, &ry, &rw, &rh))
2314 pixbuf_draw_rect_fill(pixbuf,
2315 rx - x, ry - y, rw, rh,
2316 PAN_GRID_COLOR, PAN_GRID_ALPHA);
2320 list = pan_layout_intersect(pw, x, y, width, height);
2325 gint tx, ty, tw, th;
2326 gint rx, ry, rw, rh;
2333 if (pi->type == ITEM_THUMB && pi->pixbuf)
2335 tw = gdk_pixbuf_get_width(pi->pixbuf);
2336 th = gdk_pixbuf_get_height(pi->pixbuf);
2338 tx = pi->x + (pi->width - tw) / 2;
2339 ty = pi->y + (pi->height - th) / 2;
2341 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
2343 if (util_clip_region(x, y, width, height,
2344 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
2345 &rx, &ry, &rw, &rh))
2347 pixbuf_draw_shadow(pixbuf,
2348 rx - x, ry - y, rw, rh,
2349 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2351 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2356 if (util_clip_region(x, y, width, height,
2357 tx + tw, ty + PAN_SHADOW_OFFSET,
2358 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
2359 &rx, &ry, &rw, &rh))
2361 pixbuf_draw_shadow(pixbuf,
2362 rx - x, ry - y, rw, rh,
2363 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2365 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2367 if (util_clip_region(x, y, width, height,
2368 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
2369 &rx, &ry, &rw, &rh))
2371 pixbuf_draw_shadow(pixbuf,
2372 rx - x, ry - y, rw, rh,
2373 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
2375 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2379 if (util_clip_region(x, y, width, height,
2381 &rx, &ry, &rw, &rh))
2383 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2386 1.0, 1.0, GDK_INTERP_NEAREST,
2390 if (util_clip_region(x, y, width, height,
2391 tx, ty, tw, PAN_OUTLINE_THICKNESS,
2392 &rx, &ry, &rw, &rh))
2394 pixbuf_draw_rect_fill(pixbuf,
2395 rx - x, ry - y, rw, rh,
2396 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2398 if (util_clip_region(x, y, width, height,
2399 tx, ty, PAN_OUTLINE_THICKNESS, th,
2400 &rx, &ry, &rw, &rh))
2402 pixbuf_draw_rect_fill(pixbuf,
2403 rx - x, ry - y, rw, rh,
2404 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
2406 if (util_clip_region(x, y, width, height,
2407 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
2408 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
2409 &rx, &ry, &rw, &rh))
2411 pixbuf_draw_rect_fill(pixbuf,
2412 rx - x, ry - y, rw, rh,
2413 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2415 if (util_clip_region(x, y, width, height,
2416 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
2417 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
2418 &rx, &ry, &rw, &rh))
2420 pixbuf_draw_rect_fill(pixbuf,
2421 rx - x, ry - y, rw, rh,
2422 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
2425 else if (pi->type == ITEM_THUMB)
2427 tw = pi->width - PAN_SHADOW_OFFSET * 2;
2428 th = pi->height - PAN_SHADOW_OFFSET * 2;
2429 tx = pi->x + PAN_SHADOW_OFFSET;
2430 ty = pi->y + PAN_SHADOW_OFFSET;
2432 if (util_clip_region(x, y, width, height,
2434 &rx, &ry, &rw, &rh))
2438 d = (pw->size <= LAYOUT_SIZE_THUMB_NONE) ? 2 : 8;
2439 pixbuf_draw_rect_fill(pixbuf,
2440 rx - x, ry - y, rw, rh,
2442 PAN_SHADOW_ALPHA / d);
2445 pan_layout_queue(pw, pi);
2447 else if (pi->type == ITEM_IMAGE)
2449 if (util_clip_region(x, y, width, height,
2450 pi->x, pi->y, pi->width, pi->height,
2451 &rx, &ry, &rw, &rh))
2455 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
2458 1.0, 1.0, GDK_INTERP_NEAREST,
2463 pixbuf_draw_rect_fill(pixbuf,
2464 rx - x, ry - y, rw, rh,
2465 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA / 2);
2466 pan_layout_queue(pw, pi);
2470 else if (pi->type == ITEM_BOX)
2484 if (pi->color_a > 254)
2486 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
2487 shadow[0], bh - shadow[0],
2488 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2490 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2491 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
2493 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2495 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
2500 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
2501 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
2503 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
2505 PAN_SHADOW_COLOR, a);
2509 if (util_clip_region(x, y, width, height,
2510 pi->x, pi->y, bw, bh,
2511 &rx, &ry, &rw, &rh))
2513 pixbuf_draw_rect_fill(pixbuf,
2514 rx - x, ry - y, rw, rh,
2515 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2517 if (util_clip_region(x, y, width, height,
2518 pi->x, pi->y, bw, pi->border,
2519 &rx, &ry, &rw, &rh))
2521 pixbuf_draw_rect_fill(pixbuf,
2522 rx - x, ry - y, rw, rh,
2523 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2525 if (util_clip_region(x, y, width, height,
2526 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
2527 &rx, &ry, &rw, &rh))
2529 pixbuf_draw_rect_fill(pixbuf,
2530 rx - x, ry - y, rw, rh,
2531 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2533 if (util_clip_region(x, y, width, height,
2534 pi->x + bw - pi->border, pi->y + pi->border,
2535 pi->border, bh - pi->border * 2,
2536 &rx, &ry, &rw, &rh))
2538 pixbuf_draw_rect_fill(pixbuf,
2539 rx - x, ry - y, rw, rh,
2540 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2542 if (util_clip_region(x, y, width, height,
2543 pi->x, pi->y + bh - pi->border,
2545 &rx, &ry, &rw, &rh))
2547 pixbuf_draw_rect_fill(pixbuf,
2548 rx - x, ry - y, rw, rh,
2549 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2552 else if (pi->type == ITEM_TRIANGLE)
2554 if (util_clip_region(x, y, width, height,
2555 pi->x, pi->y, pi->width, pi->height,
2556 &rx, &ry, &rw, &rh) && pi->data)
2558 gint *coord = pi->data;
2559 pixbuf_draw_triangle(pixbuf,
2560 rx - x, ry - y, rw, rh,
2561 coord[0] - x, coord[1] - y,
2562 coord[2] - x, coord[3] - y,
2563 coord[4] - x, coord[5] - y,
2564 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2566 if (pi->border & BORDER_1)
2568 pixbuf_draw_line(pixbuf,
2569 rx - x, ry - y, rw, rh,
2570 coord[0] - x, coord[1] - y,
2571 coord[2] - x, coord[3] - y,
2572 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2574 if (pi->border & BORDER_2)
2576 pixbuf_draw_line(pixbuf,
2577 rx - x, ry - y, rw, rh,
2578 coord[2] - x, coord[3] - y,
2579 coord[4] - x, coord[5] - y,
2580 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2582 if (pi->border & BORDER_3)
2584 pixbuf_draw_line(pixbuf,
2585 rx - x, ry - y, rw, rh,
2586 coord[4] - x, coord[5] - y,
2587 coord[0] - x, coord[1] - y,
2588 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
2592 else if (pi->type == ITEM_TEXT && pi->text)
2594 PangoLayout *layout;
2596 layout = pan_item_text_layout(pi, (GtkWidget *)pr);
2597 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
2598 pi->x - x + PAN_TEXT_BORDER_SIZE, pi->y - y + PAN_TEXT_BORDER_SIZE,
2599 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
2600 g_object_unref(G_OBJECT(layout));
2606 if (x%512 == 0 && y%512 == 0)
2608 PangoLayout *layout;
2611 layout = gtk_widget_create_pango_layout((GtkWidget *)pr, NULL);
2613 buf = g_strdup_printf("%d,%d\n(#%d)", x, y,
2614 (x / pr->source_tile_width) +
2615 (y / pr->source_tile_height * (pr->image_width/pr->source_tile_width + 1)));
2616 pango_layout_set_text(layout, buf, -1);
2619 pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr, 0, 0, 0, 0, 0, 255);
2621 g_object_unref(G_OBJECT(layout));
2628 static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
2629 gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
2631 PanWindow *pw = data;
2635 list = pan_layout_intersect(pw, x, y, width, height);
2644 if (pi->refcount > 0)
2648 if ((pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE) &&
2653 pw->queue = g_list_remove(pw->queue, pi);
2656 if (pw->queue_pi == pi) pw->queue_pi = NULL;
2659 g_object_unref(pi->pixbuf);
2671 *-----------------------------------------------------------------------------
2673 *-----------------------------------------------------------------------------
2676 static void pan_window_message(PanWindow *pw, const gchar *text)
2686 gtk_label_set_text(GTK_LABEL(pw->label_message), text);
2691 if (pw->layout == LAYOUT_CALENDAR)
2701 pi->type == ITEM_BOX &&
2702 pi->key && strcmp(pi->key, "dot") == 0)
2704 size += pi->fd->size;
2719 (pi->type == ITEM_THUMB || pi->type == ITEM_IMAGE))
2721 size += pi->fd->size;
2727 ss = text_from_size_abrev(size);
2728 buf = g_strdup_printf(_("%d images, %s"), count, ss);
2730 gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
2734 static void pan_window_zoom_limit(PanWindow *pw)
2740 case LAYOUT_SIZE_THUMB_DOTS:
2741 case LAYOUT_SIZE_THUMB_NONE:
2742 case LAYOUT_SIZE_THUMB_SMALL:
2743 case LAYOUT_SIZE_THUMB_NORMAL:
2745 /* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
2749 case LAYOUT_SIZE_THUMB_LARGE:
2752 case LAYOUT_SIZE_10:
2753 case LAYOUT_SIZE_25:
2756 case LAYOUT_SIZE_33:
2757 case LAYOUT_SIZE_50:
2758 case LAYOUT_SIZE_100:
2764 image_zoom_set_limits(pw->imd, min, 32.0);
2767 static gint pan_window_layout_update_idle_cb(gpointer data)
2769 PanWindow *pw = data;
2775 if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
2777 if (!pw->cache_list && !pw->cache_todo)
2779 pan_cache_fill(pw, pw->path);
2782 pan_window_message(pw, _("Reading dimensions..."));
2786 if (pan_cache_step(pw))
2790 if (pw->cache_count == pw->cache_total)
2792 pan_window_message(pw, _("Sorting images..."));
2794 else if (pw->cache_tick > 9)
2798 buf = g_strdup_printf("%s %d", _("Reading dimensions..."),
2799 pw->cache_total - pw->cache_count);
2800 pan_window_message(pw, buf);
2810 pan_window_layout_compute(pw, pw->path, &width, &height, &scroll_x, &scroll_y);
2812 pan_window_zoom_limit(pw);
2814 if (width > 0 && height > 0)
2818 printf("Canvas size is %d x %d\n", width, height);
2820 pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
2821 PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
2822 pan_window_request_tile_cb,
2823 pan_window_dispose_tile_cb, pw, 1.0);
2825 if (scroll_x == 0 && scroll_y == 0)
2833 pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
2836 pan_window_message(pw, NULL);
2843 static void pan_window_layout_update_idle(PanWindow *pw)
2845 if (pw->idle_id == -1)
2847 pan_window_message(pw, _("Sorting images..."));
2848 pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
2853 *-----------------------------------------------------------------------------
2854 * pan window keyboard
2855 *-----------------------------------------------------------------------------
2858 static const gchar *pan_menu_click_path(PanWindow *pw)
2860 if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd->path;
2864 static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
2866 PanWindow *pw = data;
2868 gdk_window_get_origin(pw->imd->pr->window, x, y);
2869 popup_menu_position_clamp(menu, x, y, 0);
2872 static gint pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
2874 PanWindow *pw = data;
2877 gint stop_signal = FALSE;
2883 pr = PIXBUF_RENDERER(pw->imd->pr);
2884 path = pan_menu_click_path(pw);
2886 focused = (pw->fs || GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(pw->imd->widget)));
2890 switch (event->keyval)
2892 case GDK_Left: case GDK_KP_Left:
2896 case GDK_Right: case GDK_KP_Right:
2900 case GDK_Up: case GDK_KP_Up:
2904 case GDK_Down: case GDK_KP_Down:
2908 case GDK_Page_Up: case GDK_KP_Page_Up:
2909 pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
2911 case GDK_Page_Down: case GDK_KP_Page_Down:
2912 pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
2914 case GDK_Home: case GDK_KP_Home:
2915 pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
2917 case GDK_End: case GDK_KP_End:
2918 pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
2923 if (focused && !(event->state & GDK_CONTROL_MASK) )
2924 switch (event->keyval)
2926 case '+': case '=': case GDK_KP_Add:
2927 pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
2929 case '-': case GDK_KP_Subtract:
2930 pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
2932 case 'Z': case 'z': case GDK_KP_Divide: case '1':
2933 pixbuf_renderer_zoom_set(pr, 1.0);
2936 pixbuf_renderer_zoom_set(pr, 2.0);
2939 pixbuf_renderer_zoom_set(pr, 3.0);
2942 pixbuf_renderer_zoom_set(pr, 4.0);
2945 pixbuf_renderer_zoom_set(pr, -4.0);
2948 pixbuf_renderer_zoom_set(pr, -3.0);
2951 pixbuf_renderer_zoom_set(pr, -2.0);
2955 pan_fullscreen_toggle(pw, FALSE);
2960 pan_overlay_toggle(pw);
2963 case GDK_Delete: case GDK_KP_Delete:
2968 if (GTK_WIDGET_VISIBLE(pw->search_box))
2970 gtk_widget_grab_focus(pw->search_entry);
2974 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
2982 pan_fullscreen_toggle(pw, TRUE);
2985 else if (GTK_WIDGET_VISIBLE(pw->search_entry))
2987 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
2993 menu = pan_popup_menu(pw);
2994 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
2999 if (event->state & GDK_CONTROL_MASK)
3002 switch (event->keyval)
3035 if (path) file_util_copy(path, NULL, NULL, GTK_WIDGET(pr));
3038 if (path) file_util_move(path, NULL, NULL, GTK_WIDGET(pr));
3041 if (path) file_util_rename(path, NULL, GTK_WIDGET(pr));
3044 if (path) file_util_delete(path, NULL, GTK_WIDGET(pr));
3047 if (path) info_window_new(path, NULL);
3050 pan_window_close(pw);
3053 if (n != -1 && path)
3055 pan_fullscreen_toggle(pw, TRUE);
3056 start_editor_from_file(n, path);
3060 else if (event->state & GDK_SHIFT_MASK)
3067 switch (event->keyval)
3072 pan_fullscreen_toggle(pw, TRUE);
3075 else if (GTK_WIDGET_HAS_FOCUS(pw->search_entry))
3077 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
3078 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
3087 if (x != 0 || y!= 0)
3089 keyboard_scroll_calc(&x, &y, event);
3090 pixbuf_renderer_scroll(pr, x, y);
3097 *-----------------------------------------------------------------------------
3099 *-----------------------------------------------------------------------------
3102 static void pan_info_update(PanWindow *pw, PanItem *pi)
3108 gint x1, y1, x2, y2, x3, y3;
3111 if (pw->click_pi == pi) return;
3112 if (pi && !pi->fd) pi = NULL;
3114 while ((p = pan_item_find_by_key(pw, ITEM_NONE, "info"))) pan_item_remove(pw, p);
3119 printf("info set to %s\n", pi->fd->path);
3121 pbox = pan_item_new_box(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
3123 PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
3124 PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3125 pan_item_set_key(pbox, "info");
3127 if (pi->type == ITEM_THUMB && pi->pixbuf)
3129 w = gdk_pixbuf_get_width(pi->pixbuf);
3130 h = gdk_pixbuf_get_height(pi->pixbuf);
3132 x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
3133 y1 = pi->y + (pi->height - h) / 2 + 8;
3137 x1 = pi->x + pi->width - 8;
3145 util_clip_triangle(x1, y1, x2, y2, x3, y3,
3148 p = pan_item_new_tri(pw, NULL, x, y, w, h,
3149 x1, y1, x2, y2, x3, y3,
3150 PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
3151 pan_item_tri_border(p, BORDER_1 | BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
3152 pan_item_set_key(p, "info");
3153 pan_item_added(pw, p);
3155 plabel = pan_item_new_text(pw, pbox->x, pbox->y,
3156 _("Filename:"), TEXT_ATTR_BOLD,
3157 PAN_POPUP_TEXT_COLOR, 255);
3158 pan_item_set_key(plabel, "info");
3159 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3160 pi->fd->name, TEXT_ATTR_NONE,
3161 PAN_POPUP_TEXT_COLOR, 255);
3162 pan_item_set_key(p, "info");
3163 pan_item_size_by_item(pbox, p, 0);
3165 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3166 _("Date:"), TEXT_ATTR_BOLD,
3167 PAN_POPUP_TEXT_COLOR, 255);
3168 pan_item_set_key(plabel, "info");
3169 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3170 text_from_time(pi->fd->date), TEXT_ATTR_NONE,
3171 PAN_POPUP_TEXT_COLOR, 255);
3172 pan_item_set_key(p, "info");
3173 pan_item_size_by_item(pbox, p, 0);
3175 plabel = pan_item_new_text(pw, plabel->x, plabel->y + plabel->height,
3176 _("Size:"), TEXT_ATTR_BOLD,
3177 PAN_POPUP_TEXT_COLOR, 255);
3178 pan_item_set_key(plabel, "info");
3179 buf = text_from_size(pi->fd->size);
3180 p = pan_item_new_text(pw, plabel->x + plabel->width, plabel->y,
3181 buf, TEXT_ATTR_NONE,
3182 PAN_POPUP_TEXT_COLOR, 255);
3184 pan_item_set_key(p, "info");
3185 pan_item_size_by_item(pbox, p, 0);
3187 pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
3188 pan_item_added(pw, pbox);
3193 *-----------------------------------------------------------------------------
3195 *-----------------------------------------------------------------------------
3198 static void pan_search_status(PanWindow *pw, const gchar *text)
3200 gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
3203 static gint pan_search_by_path(PanWindow *pw, const gchar *path)
3211 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3213 list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
3214 if (!list) return FALSE;
3216 found = g_list_find(list, pw->click_pi);
3217 if (found && found->next)
3219 found = found->next;
3227 pan_info_update(pw, pi);
3228 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3230 buf = g_strdup_printf("%s ( %d / %d )",
3231 (path[0] == '/') ? _("path found") : _("filename found"),
3232 g_list_index(list, pi) + 1,
3233 g_list_length(list));
3234 pan_search_status(pw, buf);
3242 static gint pan_search_by_partial(PanWindow *pw, const gchar *text)
3250 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3252 list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
3253 if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
3258 needle = g_utf8_strdown(text, -1);
3259 list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
3262 if (!list) return FALSE;
3264 found = g_list_find(list, pw->click_pi);
3265 if (found && found->next)
3267 found = found->next;
3275 pan_info_update(pw, pi);
3276 image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
3278 buf = g_strdup_printf("%s ( %d / %d )",
3280 g_list_index(list, pi) + 1,
3281 g_list_length(list));
3282 pan_search_status(pw, buf);
3290 static gint valid_date_separator(gchar c)
3292 return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
3295 static GList *pan_search_by_date_val(PanWindow *pw, ItemType type,
3296 gint year, gint month, gint day,
3302 work = g_list_last(pw->list);
3310 if (pi->fd && (pi->type == type || type == ITEM_NONE) &&
3311 ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
3315 tl = localtime(&pi->fd->date);
3320 match = (tl->tm_year == year - 1900);
3321 if (match && month >= 0) match = (tl->tm_mon == month - 1);
3322 if (match && day > 0) match = (tl->tm_mday == day);
3324 if (match) list = g_list_prepend(list, pi);
3329 return g_list_reverse(list);
3332 static gint pan_search_by_date(PanWindow *pw, const gchar *text)
3348 if (!text) return FALSE;
3350 ptr = (gchar *)text;
3351 while (*ptr != '\0')
3353 if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
3358 if (t == -1) return FALSE;
3360 if (!lt) return FALSE;
3362 if (valid_date_separator(*text))
3365 mptr = (gchar *)text;
3369 year = (gint)strtol(text, &mptr, 10);
3370 if (mptr == text) return FALSE;
3373 if (*mptr != '\0' && valid_date_separator(*mptr))
3378 month = strtol(mptr, &dptr, 10);
3381 if (valid_date_separator(*dptr))
3383 month = lt->tm_mon + 1;
3391 if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
3395 day = strtol(dptr, &eptr, 10);
3405 year = lt->tm_year + 1900;
3407 else if (year < 100)
3416 month < -1 || month == 0 || month > 12 ||
3417 day < -1 || day == 0 || day > 31) return FALSE;
3419 t = date_to_time(year, month, day);
3420 if (t < 0) return FALSE;
3422 if (pw->layout == LAYOUT_CALENDAR)
3424 list = pan_search_by_date_val(pw, ITEM_BOX, year, month, day, "day");
3430 type = (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB;
3431 list = pan_search_by_date_val(pw, type, year, month, day, NULL);
3436 found = g_list_find(list, pw->search_pi);
3437 if (found && found->next)
3439 found = found->next;
3450 if (pw->layout == LAYOUT_CALENDAR && pi && pi->type == ITEM_BOX)
3452 pan_info_update(pw, NULL);
3453 pan_calendar_update(pw, pi);
3454 image_scroll_to_point(pw->imd,
3455 pi->x + pi->width / 2,
3456 pi->y + pi->height / 2, 0.5, 0.5);
3460 pan_info_update(pw, pi);
3461 image_scroll_to_point(pw->imd,
3462 pi->x - PAN_FOLDER_BOX_BORDER * 5 / 2,
3468 buf = date_value_string(t, DATE_LENGTH_MONTH);
3473 buf = g_strdup_printf("%d %s", day, tmp);
3479 buf = date_value_string(t, DATE_LENGTH_YEAR);
3484 buf_count = g_strdup_printf("( %d / %d )",
3485 g_list_index(list, pi) + 1,
3486 g_list_length(list));
3490 buf_count = g_strdup_printf("(%s)", _("no match"));
3493 message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
3496 pan_search_status(pw, message);
3504 static void pan_search_activate_cb(const gchar *text, gpointer data)
3506 PanWindow *pw = data;
3510 tab_completion_append_to_history(pw->search_entry, text);
3512 if (pan_search_by_path(pw, text)) return;
3514 if ((pw->layout == LAYOUT_TIMELINE ||
3515 pw->layout == LAYOUT_CALENDAR) &&
3516 pan_search_by_date(pw, text))
3521 if (pan_search_by_partial(pw, text)) return;
3523 pan_search_status(pw, _("no match"));
3526 static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
3528 PanWindow *pw = data;
3531 visible = GTK_WIDGET_VISIBLE(pw->search_box);
3532 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
3536 gtk_widget_hide(pw->search_box);
3537 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
3541 gtk_widget_show(pw->search_box);
3542 gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3543 gtk_widget_grab_focus(pw->search_entry);
3549 *-----------------------------------------------------------------------------
3550 * view window main routines
3551 *-----------------------------------------------------------------------------
3554 static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
3556 PanWindow *pw = data;
3564 rx = (double)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
3565 ry = (double)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
3568 pi = pan_item_find_by_coord(pw, (pw->size > LAYOUT_SIZE_THUMB_LARGE) ? ITEM_IMAGE : ITEM_THUMB,
3571 switch (event->button)
3574 pan_info_update(pw, pi);
3576 if (!pi && pw->layout == LAYOUT_CALENDAR)
3578 pi = pan_item_find_by_coord(pw, ITEM_BOX, rx, ry, "day");
3579 pan_calendar_update(pw, pi);
3585 pan_info_update(pw, pi);
3586 menu = pan_popup_menu(pw);
3587 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
3594 static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
3597 PanWindow *pw = data;
3604 if (!(event->state & GDK_SHIFT_MASK))
3610 if (event->state & GDK_CONTROL_MASK)
3612 switch (event->direction)
3615 pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
3616 (gint)event->x, (gint)event->y);
3618 case GDK_SCROLL_DOWN:
3619 pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
3620 (gint)event->x, (gint)event->y);
3628 switch (event->direction)
3631 pixbuf_renderer_scroll(pr, 0, -h);
3633 case GDK_SCROLL_DOWN:
3634 pixbuf_renderer_scroll(pr, 0, h);
3636 case GDK_SCROLL_LEFT:
3637 pixbuf_renderer_scroll(pr, -w, 0);
3639 case GDK_SCROLL_RIGHT:
3640 pixbuf_renderer_scroll(pr, w, 0);
3648 static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
3650 g_signal_connect(G_OBJECT(imd->pr), "clicked",
3651 G_CALLBACK(button_cb), pw);
3652 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
3653 G_CALLBACK(scroll_cb), pw);
3656 static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
3658 PanWindow *pw = data;
3661 pw->imd = pw->imd_normal;
3664 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off)
3666 if (force_off && !pw->fs) return;
3670 fullscreen_stop(pw->fs);
3674 pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
3675 pan_image_set_buttons(pw, pw->fs->imd);
3676 g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
3677 G_CALLBACK(pan_window_key_press_cb), pw);
3679 pw->imd = pw->fs->imd;
3683 static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
3685 PanWindow *pw = data;
3688 text = image_zoom_get_as_text(pw->imd);
3689 gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
3693 static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
3695 PanWindow *pw = data;
3700 if (pr->scale == 0.0) return;
3702 pixbuf_renderer_get_visible_rect(pr, &rect);
3703 pixbuf_renderer_get_image_size(pr, &width, &height);
3705 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
3706 adj->page_size = (gdouble)rect.width;
3707 adj->page_increment = adj->page_size / 2.0;
3708 adj->step_increment = 48.0 / pr->scale;
3710 adj->upper = MAX((gdouble)width, 1.0);
3711 adj->value = (gdouble)rect.x;
3713 pref_signal_block_data(pw->scrollbar_h, pw);
3714 gtk_adjustment_changed(adj);
3715 gtk_adjustment_value_changed(adj);
3716 pref_signal_unblock_data(pw->scrollbar_h, pw);
3718 adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
3719 adj->page_size = (gdouble)rect.height;
3720 adj->page_increment = adj->page_size / 2.0;
3721 adj->step_increment = 48.0 / pr->scale;
3723 adj->upper = MAX((gdouble)height, 1.0);
3724 adj->value = (gdouble)rect.y;
3726 pref_signal_block_data(pw->scrollbar_v, pw);
3727 gtk_adjustment_changed(adj);
3728 gtk_adjustment_value_changed(adj);
3729 pref_signal_unblock_data(pw->scrollbar_v, pw);
3732 static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
3734 PanWindow *pw = data;
3738 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
3740 if (!pr->scale) return;
3742 x = (gint)gtk_range_get_value(range);
3744 pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
3747 static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
3749 PanWindow *pw = data;
3753 pr = PIXBUF_RENDERER(pw->imd_normal->pr);
3755 if (!pr->scale) return;
3757 y = (gint)gtk_range_get_value(range);
3759 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
3762 static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
3764 PanWindow *pw = data;
3766 pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3767 pan_window_layout_update_idle(pw);
3770 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
3772 PanWindow *pw = data;
3774 pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3775 pan_window_layout_update_idle(pw);
3778 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
3780 PanWindow *pw = data;
3783 path = remove_trailing_slash(new_text);
3784 parse_out_relatives(path);
3788 warning_dialog(_("Folder not found"),
3789 _("The entered path is not a folder"),
3790 GTK_STOCK_DIALOG_WARNING, pw->path_entry);
3794 tab_completion_append_to_history(pw->path_entry, path);
3797 pw->path = g_strdup(path);
3799 pan_window_layout_update_idle(pw);
3802 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
3804 PanWindow *pw = data;
3807 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
3809 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
3810 pan_window_entry_activate_cb(text, pw);
3814 static void pan_window_close(PanWindow *pw)
3816 pan_window_list = g_list_remove(pan_window_list, pw);
3818 if (pw->idle_id != -1)
3820 g_source_remove(pw->idle_id);
3823 pan_fullscreen_toggle(pw, TRUE);
3824 gtk_widget_destroy(pw->window);
3826 pan_window_items_free(pw);
3833 static gint pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
3835 PanWindow *pw = data;
3837 pan_window_close(pw);
3841 static void pan_window_new_real(const gchar *path)
3850 GdkGeometry geometry;
3852 pw = g_new0(PanWindow, 1);
3854 pw->path = g_strdup(path);
3855 pw->layout = LAYOUT_TIMELINE;
3856 pw->size = LAYOUT_SIZE_THUMB_NORMAL;
3857 pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
3858 pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
3862 pw->overlay_id = -1;
3865 pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3867 geometry.min_width = 8;
3868 geometry.min_height = 8;
3869 gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
3871 gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
3872 gtk_window_set_title (GTK_WINDOW(pw->window), "Pan View - GQview");
3873 gtk_window_set_wmclass(GTK_WINDOW(pw->window), "view", "GQview");
3874 gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
3876 window_set_icon(pw->window, NULL, NULL);
3878 vbox = gtk_vbox_new(FALSE, 0);
3879 gtk_container_add(GTK_CONTAINER(pw->window), vbox);
3880 gtk_widget_show(vbox);
3882 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3884 pref_spacer(box, 0);
3885 pref_label_new(box, _("Location:"));
3886 combo = tab_completion_new_with_history(&pw->path_entry, path, "pan_view", -1,
3887 pan_window_entry_activate_cb, pw);
3888 g_signal_connect(G_OBJECT(pw->path_entry->parent), "changed",
3889 G_CALLBACK(pan_window_entry_change_cb), pw);
3890 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
3891 gtk_widget_show(combo);
3893 combo = gtk_combo_box_new_text();
3894 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Timeline"));
3895 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Calendar"));
3896 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders"));
3897 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Folders (flower)"));
3898 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Grid"));
3900 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
3901 g_signal_connect(G_OBJECT(combo), "changed",
3902 G_CALLBACK(pan_window_layout_change_cb), pw);
3903 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
3904 gtk_widget_show(combo);
3906 combo = gtk_combo_box_new_text();
3907 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Dots"));
3908 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("No Images"));
3909 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Small Thumbnails"));
3910 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Normal Thumbnails"));
3911 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Large Thumbnails"));
3912 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:10 (10%)"));
3913 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:4 (25%)"));
3914 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:3 (33%)"));
3915 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:2 (50%)"));
3916 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("1:1 (100%)"));
3918 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
3919 g_signal_connect(G_OBJECT(combo), "changed",
3920 G_CALLBACK(pan_window_layout_size_cb), pw);
3921 gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
3922 gtk_widget_show(combo);
3924 table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
3925 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
3926 gtk_table_set_col_spacings(GTK_TABLE(table), 2);
3928 pw->imd = image_new(TRUE);
3929 pw->imd_normal = pw->imd;
3931 g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
3932 G_CALLBACK(pan_window_image_zoom_cb), pw);
3933 g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
3934 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
3936 gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
3937 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
3938 gtk_widget_show(GTK_WIDGET(pw->imd->widget));
3940 pan_window_dnd_init(pw);
3942 pan_image_set_buttons(pw, pw->imd);
3944 pw->scrollbar_h = gtk_hscrollbar_new(NULL);
3945 g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
3946 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
3947 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
3948 GTK_FILL | GTK_EXPAND, 0, 0, 0);
3949 gtk_widget_show(pw->scrollbar_h);
3951 pw->scrollbar_v = gtk_vscrollbar_new(NULL);
3952 g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
3953 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
3954 gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
3955 0, GTK_FILL | GTK_EXPAND, 0, 0);
3956 gtk_widget_show(pw->scrollbar_v);
3960 pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
3961 gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
3963 pref_spacer(pw->search_box, 0);
3964 pref_label_new(pw->search_box, _("Find:"));
3966 hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
3967 gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
3968 gtk_widget_show(hbox);
3970 combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
3971 pan_search_activate_cb, pw);
3972 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
3973 gtk_widget_show(combo);
3975 pw->search_label = gtk_label_new("");
3976 gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
3977 gtk_widget_show(pw->search_label);
3981 box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3983 frame = gtk_frame_new(NULL);
3984 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
3985 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
3986 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
3987 gtk_widget_show(frame);
3989 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
3990 gtk_container_add(GTK_CONTAINER(frame), hbox);
3991 gtk_widget_show(hbox);
3993 pref_spacer(hbox, 0);
3994 pw->label_message = pref_label_new(hbox, "");
3996 frame = gtk_frame_new(NULL);
3997 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
3998 gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
3999 gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
4000 gtk_widget_show(frame);
4002 pw->label_zoom = gtk_label_new("");
4003 gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
4004 gtk_widget_show(pw->label_zoom);
4006 pw->search_button = gtk_toggle_button_new();
4007 gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
4008 gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
4009 hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
4010 gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
4011 gtk_widget_show(hbox);
4012 pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
4013 gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
4014 gtk_widget_show(pw->search_button_arrow);
4015 pref_label_new(hbox, _("Find"));
4017 gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
4018 gtk_widget_show(pw->search_button);
4019 g_signal_connect(G_OBJECT(pw->search_button), "clicked",
4020 G_CALLBACK(pan_search_toggle_cb), pw);
4022 g_signal_connect(G_OBJECT(pw->window), "delete_event",
4023 G_CALLBACK(pan_window_delete_cb), pw);
4024 g_signal_connect(G_OBJECT(pw->window), "key_press_event",
4025 G_CALLBACK(pan_window_key_press_cb), pw);
4027 gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
4029 pan_window_layout_update_idle(pw);
4031 gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
4032 gtk_widget_show(pw->window);
4034 pan_window_list = g_list_append(pan_window_list, pw);
4038 *-----------------------------------------------------------------------------
4039 * peformance warnings
4040 *-----------------------------------------------------------------------------
4043 static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
4047 generic_dialog_close(gd);
4049 pan_window_new_real(path);
4053 static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
4057 hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
4058 pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
4061 static gint pan_warning(const gchar *path)
4067 GtkWidget *ct_button;
4070 if (enable_thumb_caching &&
4071 thumbnail_spec_standard) return FALSE;
4073 if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
4074 if (hide_dlg) return FALSE;
4076 gd = generic_dialog_new(_("Pan View Performance"), "GQview", "pan_view_warning", NULL, FALSE,
4078 gd->data = g_strdup(path);
4079 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
4080 pan_warning_ok_cb, TRUE);
4082 box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
4083 _("Pan view performance may be poor."),
4084 _("To improve performance of thumbnails in the pan view the"
4085 " following options can be enabled. Note that both options"
4086 " must be enabled to notice a change in performance."));
4088 group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
4089 pref_spacer(group, PREF_PAD_INDENT);
4090 group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4092 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
4093 enable_thumb_caching, &enable_thumb_caching);
4094 button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
4095 thumbnail_spec_standard, &thumbnail_spec_standard);
4096 pref_checkbox_link_sensitivity(ct_button, button);
4100 pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
4101 G_CALLBACK(pan_warning_hide_cb), NULL);
4103 gtk_widget_show(gd->dialog);
4110 *-----------------------------------------------------------------------------
4112 *-----------------------------------------------------------------------------
4115 void pan_window_new(const gchar *path)
4117 if (pan_warning(path)) return;
4119 pan_window_new_real(path);
4123 *-----------------------------------------------------------------------------
4125 *-----------------------------------------------------------------------------
4128 static void pan_new_window_cb(GtkWidget *widget, gpointer data)
4130 PanWindow *pw = data;
4133 path = pan_menu_click_path(pw);
4136 pan_fullscreen_toggle(pw, TRUE);
4137 view_window_new(path);
4141 static void pan_edit_cb(GtkWidget *widget, gpointer data)
4147 pw = submenu_item_get_data(widget);
4148 n = GPOINTER_TO_INT(data);
4151 path = pan_menu_click_path(pw);
4154 pan_fullscreen_toggle(pw, TRUE);
4155 start_editor_from_file(n, path);
4159 static void pan_info_cb(GtkWidget *widget, gpointer data)
4161 PanWindow *pw = data;
4164 path = pan_menu_click_path(pw);
4165 if (path) info_window_new(path, NULL);
4168 static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
4170 PanWindow *pw = data;
4172 image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
4175 static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
4177 PanWindow *pw = data;
4179 image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
4182 static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
4184 PanWindow *pw = data;
4186 image_zoom_set(pw->imd, 1.0);
4189 static void pan_copy_cb(GtkWidget *widget, gpointer data)
4191 PanWindow *pw = data;
4194 path = pan_menu_click_path(pw);
4195 if (path) file_util_copy(path, NULL, NULL, pw->imd->widget);
4198 static void pan_move_cb(GtkWidget *widget, gpointer data)
4200 PanWindow *pw = data;
4203 path = pan_menu_click_path(pw);
4204 if (path) file_util_move(path, NULL, NULL, pw->imd->widget);
4207 static void pan_rename_cb(GtkWidget *widget, gpointer data)
4209 PanWindow *pw = data;
4212 path = pan_menu_click_path(pw);
4213 if (path) file_util_rename(path, NULL, pw->imd->widget);
4216 static void pan_delete_cb(GtkWidget *widget, gpointer data)
4218 PanWindow *pw = data;
4221 path = pan_menu_click_path(pw);
4222 if (path) file_util_delete(path, NULL, pw->imd->widget);
4225 static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
4227 PanWindow *pw = data;
4229 pan_fullscreen_toggle(pw, FALSE);
4232 static void pan_close_cb(GtkWidget *widget, gpointer data)
4234 PanWindow *pw = data;
4236 pan_window_close(pw);
4239 static GtkWidget *pan_popup_menu(PanWindow *pw)
4245 active = (pw->click_pi != NULL);
4247 menu = popup_menu_short_lived();
4249 menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
4250 G_CALLBACK(pan_zoom_in_cb), pw);
4251 menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
4252 G_CALLBACK(pan_zoom_out_cb), pw);
4253 menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
4254 G_CALLBACK(pan_zoom_1_1_cb), pw);
4255 menu_item_add_divider(menu);
4257 submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw);
4258 gtk_widget_set_sensitive(item, active);
4260 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
4261 G_CALLBACK(pan_info_cb), pw);
4263 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
4264 G_CALLBACK(pan_new_window_cb), pw);
4266 menu_item_add_divider(menu);
4267 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
4268 G_CALLBACK(pan_copy_cb), pw);
4269 menu_item_add_sensitive(menu, _("_Move..."), active,
4270 G_CALLBACK(pan_move_cb), pw);
4271 menu_item_add_sensitive(menu, _("_Rename..."), active,
4272 G_CALLBACK(pan_rename_cb), pw);
4273 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
4274 G_CALLBACK(pan_delete_cb), pw);
4276 menu_item_add_divider(menu);
4280 menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4284 menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
4287 menu_item_add_divider(menu);
4288 menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
4294 *-----------------------------------------------------------------------------
4296 *-----------------------------------------------------------------------------
4299 static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
4301 GtkSelectionData *selection_data, guint info,
4302 guint time, gpointer data)
4304 PanWindow *pw = data;
4306 if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
4308 if (info == TARGET_URI_LIST)
4312 list = uri_list_from_text(selection_data->data, TRUE);
4313 if (list && isdir((gchar *)list->data))
4315 gchar *path = list->data;
4318 pw->path = g_strdup(path);
4320 pan_window_layout_update_idle(pw);
4323 path_list_free(list);
4327 static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
4328 GtkSelectionData *selection_data, guint info,
4329 guint time, gpointer data)
4331 PanWindow *pw = data;
4334 path = pan_menu_click_path(pw);
4344 case TARGET_URI_LIST:
4347 case TARGET_TEXT_PLAIN:
4352 list = g_list_append(NULL, (gchar *)path);
4353 text = uri_text_from_list(list, &len, plain_text);
4357 gtk_selection_data_set (selection_data, selection_data->target,
4364 gtk_selection_data_set (selection_data, selection_data->target,
4369 static void pan_window_dnd_init(PanWindow *pw)
4373 widget = pw->imd->pr;
4375 gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
4376 dnd_file_drag_types, dnd_file_drag_types_count,
4377 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4378 g_signal_connect(G_OBJECT(widget), "drag_data_get",
4379 G_CALLBACK(pan_window_set_dnd_data), pw);
4381 gtk_drag_dest_set(widget,
4382 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
4383 dnd_file_drop_types, dnd_file_drop_types_count,
4384 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
4385 g_signal_connect(G_OBJECT(widget), "drag_data_received",
4386 G_CALLBACK(pan_window_get_dnd_data), pw);
4390 *-----------------------------------------------------------------------------
4391 * maintenance (for rename, move, remove)
4392 *-----------------------------------------------------------------------------