changeset 2417:e5533195963f

Merge remote-tracking branch 'github/merge-requests/451' * github/merge-requests/451: Move pan-view components to their own subdir. Update the automake file with new headers Refactor pan-types.h into discrete header files for each pan-view code module Fix errors in -Werror (except GdkPixbuf deprecation warnings)
author Klaus Ethgen <Klaus@Ethgen.de>
date Sat, 24 Dec 2016 13:44:25 +0100
parents 0168ad13f7d9 (current diff) d2539dd6858c (diff)
children 8ff8a1df17c9 a2d4729a3b2d e8f835110f9c
files src/pan-calendar.c src/pan-folder.c src/pan-grid.c src/pan-item.c src/pan-timeline.c src/pan-types.h src/pan-util.c src/pan-view.c src/rcfile.c src/view_file.c
diffstat 32 files changed, 5834 insertions(+), 5583 deletions(-) [+]
line wrap: on
line diff
--- a/.gitignore	Tue Dec 20 21:36:52 2016 +0000
+++ b/.gitignore	Sat Dec 24 13:44:25 2016 +0100
@@ -69,6 +69,10 @@
 # /src/icons/
 /src/icons/icons_inline.h
 
+# /src/pan-view/
+/src/pan-view/.deps
+/src/pan-view/.dirstamp
+
 /build-stamp
 /debian/geeqie*
 /debian/files
--- a/configure.in	Tue Dec 20 21:36:52 2016 +0000
+++ b/configure.in	Sat Dec 24 13:44:25 2016 +0100
@@ -21,7 +21,8 @@
 AC_CONFIG_SRCDIR([src/main.c])
 
 AC_CONFIG_AUX_DIR(auxdir)
-AM_INIT_AUTOMAKE
+# Require Automake 1.14 for %reldir% support
+AM_INIT_AUTOMAKE([1.14.1 subdir-objects])
 
 AC_CONFIG_HEADER([config.h])
 
--- a/src/Makefile.am	Tue Dec 20 21:36:52 2016 +0000
+++ b/src/Makefile.am	Sat Dec 24 13:44:25 2016 +0100
@@ -80,10 +80,13 @@
 extra_SLIK = \
 	$(extra_ICONS)
 
+include $(srcdir)/pan-view/Makefile.am
+
 bin_PROGRAMS = geeqie
 
 geeqie_SOURCES = \
 	$(module_SLIK)	\
+	$(module_pan_view)	\
 	ClayRGB1998.icc \
 	ClayRGB1998_icc.h \
 	advanced_exif.c	\
@@ -202,14 +205,6 @@
 	misc.h		\
 	options.c	\
 	options.h	\
-	pan-calendar.c	\
-	pan-folder.c	\
-	pan-grid.c	\
-	pan-item.c	\
-	pan-timeline.c	\
-	pan-types.h	\
-	pan-util.c	\
-	pan-view.c	\
 	pan-view.h	\
 	pixbuf-renderer.c	\
 	pixbuf-renderer.h	\
--- a/src/fullscreen.c	Tue Dec 20 21:36:52 2016 +0000
+++ b/src/fullscreen.c	Sat Dec 24 13:44:25 2016 +0100
@@ -364,7 +364,7 @@
 
 	gtk_widget_destroy(fs->window);
 
-	gtk_window_present(fs->normal_window);
+	gtk_window_present(GTK_WINDOW(fs->normal_window));
 
 	g_free(fs);
 }
--- a/src/pan-calendar.c	Tue Dec 20 21:36:52 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,425 +0,0 @@
-/*
- * Copyright (C) 2006 John Ellis
- * Copyright (C) 2008 - 2016 The Geeqie Team
- *
- * Author: John Ellis
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "main.h"
-#include "pan-types.h"
-
-#include <glib/gprintf.h>
-#include <math.h>
-
-
-#define PAN_CAL_POPUP_COLOR 220, 220, 220
-#define PAN_CAL_POPUP_ALPHA 255
-#define PAN_CAL_POPUP_BORDER 1
-#define PAN_CAL_POPUP_BORDER_COLOR 0, 0, 0
-#define PAN_CAL_POPUP_TEXT_COLOR 0, 0, 0
-
-#define PAN_CAL_DAY_WIDTH 100
-#define PAN_CAL_DAY_HEIGHT 80
-
-#define PAN_CAL_DAY_COLOR 255, 255, 255
-#define PAN_CAL_DAY_ALPHA 220
-#define PAN_CAL_DAY_BORDER 2
-#define PAN_CAL_DAY_BORDER_COLOR 0, 0, 0
-#define PAN_CAL_DAY_TEXT_COLOR 0, 0, 0
-
-#define PAN_CAL_MONTH_COLOR 255, 255, 255
-#define PAN_CAL_MONTH_ALPHA 200
-#define PAN_CAL_MONTH_BORDER 4
-#define PAN_CAL_MONTH_BORDER_COLOR 0, 0, 0
-#define PAN_CAL_MONTH_TEXT_COLOR 0, 0, 0
-
-#define PAN_CAL_DOT_SIZE 3
-#define PAN_CAL_DOT_GAP 2
-#define PAN_CAL_DOT_COLOR 128, 128, 128
-#define PAN_CAL_DOT_ALPHA 128
-
-
-/*
- *-----------------------------------------------------------------------------
- * calendar
- *-----------------------------------------------------------------------------
- */
-
-void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
-{
-	PanItem *pbox;
-	PanItem *pi;
-	GList *list;
-	GList *work;
-	gint x1, y1, x2, y2, x3, y3;
-	gint x, y, w, h;
-	gint grid;
-	gint column;
-
-	while ((pi = pan_item_find_by_key(pw, PAN_ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
-
-	if (!pi_day || pi_day->type != PAN_ITEM_BOX ||
-	    !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
-
-	list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
-
-	work = list;
-	while (work)
-		{
-		PanItem *dot;
-		GList *node;
-
-		dot = work->data;
-		node = work;
-		work = work->next;
-
-		if (dot->type != PAN_ITEM_BOX || !dot->fd ||
-		    !dot->key || strcmp(dot->key, "dot") != 0)
-			{
-			list = g_list_delete_link(list, node);
-			}
-		}
-
-	grid = (gint)(sqrt(g_list_length(list)) + 0.5);
-
-	x = pi_day->x + pi_day->width + 4;
-	y = pi_day->y;
-
-	pbox = pan_item_box_new(pw, NULL, x, y, PAN_BOX_BORDER, PAN_BOX_BORDER,
-				PAN_CAL_POPUP_BORDER,
-				PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA,
-				PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
-	pan_item_set_key(pbox, "day_bubble");
-
-	if (pi_day->fd)
-		{
-		PanItem *plabel;
-		gchar *buf;
-
-		buf = pan_date_value_string(pi_day->fd->date, PAN_DATE_LENGTH_WEEK);
-		plabel = pan_item_text_new(pw, x, y, buf, PAN_TEXT_ATTR_BOLD | PAN_TEXT_ATTR_HEADING,
-					   PAN_TEXT_BORDER_SIZE,
-					   PAN_CAL_POPUP_TEXT_COLOR, 255);
-		pan_item_set_key(plabel, "day_bubble");
-		g_free(buf);
-
-		pan_item_size_by_item(pbox, plabel, 0);
-
-		y += plabel->height;
-		}
-
-	if (list)
-		{
-		column = 0;
-
-		x += PAN_BOX_BORDER;
-		y += PAN_BOX_BORDER;
-
-		work = list;
-		while (work)
-			{
-			PanItem *dot;
-
-			dot = work->data;
-			work = work->next;
-
-			if (dot->fd)
-				{
-				PanItem *pimg;
-
-				pimg = pan_item_thumb_new(pw, file_data_ref(dot->fd), x, y);
-				pan_item_set_key(pimg, "day_bubble");
-
-				pan_item_size_by_item(pbox, pimg, PAN_BOX_BORDER);
-
-				column++;
-				if (column < grid)
-					{
-					x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
-					}
-				else
-					{
-					column = 0;
-					x = pbox->x + PAN_BOX_BORDER;
-					y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
-					}
-				}
-			}
-		}
-
-	x1 = pi_day->x + pi_day->width - 8;
-	y1 = pi_day->y + 8;
-	x2 = pbox->x + 1;
-	y2 = pbox->y + MIN(42, pbox->height);
-	x3 = pbox->x + 1;
-	y3 = MAX(pbox->y, y2 - 30);
-	util_clip_triangle(x1, y1, x2, y2, x3, y3,
-			   &x, &y, &w, &h);
-
-	pi = pan_item_tri_new(pw, NULL, x, y, w, h,
-			      x1, y1, x2, y2, x3, y3,
-			      PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA);
-	pan_item_tri_border(pi, PAN_BORDER_1 | PAN_BORDER_3, PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
-	pan_item_set_key(pi, "day_bubble");
-	pan_item_added(pw, pi);
-
-	pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
-	pan_item_added(pw, pbox);
-
-	pan_layout_resize(pw);
-}
-
-void pan_calendar_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height)
-{
-	GList *list;
-	GList *work;
-	gint x, y;
-	time_t tc;
-	gint count;
-	gint day_max;
-	gint grid;
-	gint year = 0;
-	gint month = 0;
-	gint end_year = 0;
-	gint end_month = 0;
-
-	list = pan_list_tree(dir_fd, SORT_NONE, TRUE, pw->ignore_symlinks);
-
-	if (pw->cache_list && pw->exif_date_enable)
-		{
-		pw->cache_list = pan_cache_sort(pw->cache_list, SORT_NAME, TRUE);
-		list = filelist_sort(list, SORT_NAME, TRUE);
-		pan_cache_sync_date(pw, list);
-		}
-
-	pw->cache_list = pan_cache_sort(pw->cache_list, SORT_TIME, TRUE);
-	list = filelist_sort(list, SORT_TIME, TRUE);
-
-	day_max = 0;
-	count = 0;
-	tc = 0;
-	work = list;
-	while (work)
-		{
-		FileData *fd;
-
-		fd = work->data;
-		work = work->next;
-
-		if (!pan_date_compare(fd->date, tc, PAN_DATE_LENGTH_DAY))
-			{
-			count = 0;
-			tc = fd->date;
-			}
-		else
-			{
-			count++;
-			if (day_max < count) day_max = count;
-			}
-		}
-
-	DEBUG_1("biggest day contains %d images", day_max);
-
-	grid = (gint)(sqrt((gdouble)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
-
-	if (list)
-		{
-		FileData *fd = list->data;
-
-		year = pan_date_value(fd->date, PAN_DATE_LENGTH_YEAR);
-		month = pan_date_value(fd->date, PAN_DATE_LENGTH_MONTH);
-		}
-
-	work = g_list_last(list);
-	if (work)
-		{
-		FileData *fd = work->data;
-		end_year = pan_date_value(fd->date, PAN_DATE_LENGTH_YEAR);
-		end_month = pan_date_value(fd->date, PAN_DATE_LENGTH_MONTH);
-		}
-
-	*width = PAN_BOX_BORDER * 2;
-	*height = PAN_BOX_BORDER * 2;
-
-	x = PAN_BOX_BORDER;
-	y = PAN_BOX_BORDER;
-
-	work = list;
-	while (work && (year < end_year || (year == end_year && month <= end_month)))
-		{
-		PanItem *pi_month;
-		PanItem *pi_text;
-		gint day;
-		gint days;
-		gint col;
-		gint row;
-		time_t dt;
-		gchar *buf;
-
-		/* figure last second of this month */
-		dt = pan_date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
-		dt -= 60 * 60 * 24;
-
-		/* anything to show this month? */
-		if (!pan_date_compare(((FileData *)(work->data))->date, dt, PAN_DATE_LENGTH_MONTH))
-			{
-			month ++;
-			if (month > 12)
-				{
-				year++;
-				month = 1;
-				}
-			continue;
-			}
-
-		days = pan_date_value(dt, PAN_DATE_LENGTH_DAY);
-		dt = pan_date_to_time(year, month, 1);
-		col = pan_date_value(dt, PAN_DATE_LENGTH_WEEK);
-		row = 1;
-
-		x = PAN_BOX_BORDER;
-
-		pi_month = pan_item_box_new(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
-					    PAN_CAL_MONTH_BORDER,
-					    PAN_CAL_MONTH_COLOR, PAN_CAL_MONTH_ALPHA,
-					    PAN_CAL_MONTH_BORDER_COLOR, PAN_CAL_MONTH_ALPHA);
-		buf = pan_date_value_string(dt, PAN_DATE_LENGTH_MONTH);
-		pi_text = pan_item_text_new(pw, x, y, buf,
-					    PAN_TEXT_ATTR_BOLD | PAN_TEXT_ATTR_HEADING,
-					    PAN_TEXT_BORDER_SIZE,
-					    PAN_CAL_MONTH_TEXT_COLOR, 255);
-		g_free(buf);
-		pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
-
-		pi_month->height = pi_text->y + pi_text->height - pi_month->y;
-
-		x = PAN_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
-		y = pi_month->y + pi_month->height + PAN_BOX_BORDER;
-
-		for (day = 1; day <= days; day++)
-			{
-			FileData *fd;
-			PanItem *pi_day;
-			gint dx, dy;
-			gint n = 0;
-			gchar fake_path[20];
-
-			dt = pan_date_to_time(year, month, day);
-
-			/*
-			 * Create a FileData entry that represents the given day.
-			 * It does not correspond to any real file
-			 */
-
-			g_snprintf(fake_path, sizeof(fake_path), "//%04d-%02d-%02d", year, month, day);
-			fd = file_data_new_no_grouping(fake_path);
-			fd->date = dt;
-			pi_day = pan_item_box_new(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
-						  PAN_CAL_DAY_BORDER,
-						  PAN_CAL_DAY_COLOR, PAN_CAL_DAY_ALPHA,
-						  PAN_CAL_DAY_BORDER_COLOR, PAN_CAL_DAY_ALPHA);
-			pan_item_set_key(pi_day, "day");
-
-			dx = x + PAN_CAL_DOT_GAP * 2;
-			dy = y + PAN_CAL_DOT_GAP * 2;
-
-			fd = (work) ? work->data : NULL;
-			while (fd && pan_date_compare(fd->date, dt, PAN_DATE_LENGTH_DAY))
-				{
-				PanItem *pi;
-
-				pi = pan_item_box_new(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
-						      0,
-						      PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
-						      0, 0, 0, 0);
-				pan_item_set_key(pi, "dot");
-
-				dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
-				if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
-					{
-					dx = x + PAN_CAL_DOT_GAP * 2;
-					dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
-					}
-				if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
-					{
-					/* must keep all dots within respective day even if it gets ugly */
-					dy = y + PAN_CAL_DOT_GAP * 2;
-					}
-
-				n++;
-
-				work = work->next;
-				fd = (work) ? work->data : NULL;
-				}
-
-			if (n > 0)
-				{
-				PanItem *pi;
-
-				pi_day->color_r = MAX(pi_day->color_r - 61 - n * 3, 80);
-				pi_day->color_g = pi_day->color_r;
-
-				buf = g_strdup_printf("( %d )", n);
-				pi = pan_item_text_new(pw, x, y, buf, PAN_TEXT_ATTR_NONE,
-						       PAN_TEXT_BORDER_SIZE,
-						       PAN_CAL_DAY_TEXT_COLOR, 255);
-				g_free(buf);
-
-				pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
-				pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
-				}
-
-			buf = g_strdup_printf("%d", day);
-			pan_item_text_new(pw, x + 4, y + 4, buf, PAN_TEXT_ATTR_BOLD | PAN_TEXT_ATTR_HEADING,
-					  PAN_TEXT_BORDER_SIZE,
-					  PAN_CAL_DAY_TEXT_COLOR, 255);
-			g_free(buf);
-
-
-			pan_item_size_coordinates(pi_day, PAN_BOX_BORDER, width, height);
-
-			col++;
-			if (col > 6)
-				{
-				col = 0;
-				row++;
-				x = PAN_BOX_BORDER;
-				y += PAN_CAL_DAY_HEIGHT;
-				}
-			else
-				{
-				x += PAN_CAL_DAY_WIDTH;
-				}
-			}
-
-		if (col > 0) y += PAN_CAL_DAY_HEIGHT;
-		y += PAN_BOX_BORDER * 2;
-
-		month ++;
-		if (month > 12)
-			{
-			year++;
-			month = 1;
-			}
-		}
-
-	*width += grid;
-	*height = MAX(*height, grid + PAN_BOX_BORDER * 2 * 2);
-
-	g_list_free(list);
-}
-/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/pan-folder.c	Tue Dec 20 21:36:52 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,478 +0,0 @@
-/*
- * Copyright (C) 2006 John Ellis
- * Copyright (C) 2008 - 2016 The Geeqie Team
- *
- * Author: John Ellis
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "main.h"
-#include "pan-types.h"
-
-#include <math.h>
-
-
-static void pan_flower_size(PanWindow *pw, gint *width, gint *height)
-{
-	GList *work;
-	gint x1, y1, x2, y2;
-
-	x1 = 0;
-	y1 = 0;
-	x2 = 0;
-	y2 = 0;
-
-	work = pw->list;
-	while (work)
-		{
-		PanItem *pi;
-
-		pi = work->data;
-		work = work->next;
-
-		if (x1 > pi->x) x1 = pi->x;
-		if (y1 > pi->y) y1 = pi->y;
-		if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
-		if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
-		}
-
-	x1 -= PAN_BOX_BORDER;
-	y1 -= PAN_BOX_BORDER;
-	x2 += PAN_BOX_BORDER;
-	y2 += PAN_BOX_BORDER;
-
-	work = pw->list;
-	while (work)
-		{
-		PanItem *pi;
-
-		pi = work->data;
-		work = work->next;
-
-		pi->x -= x1;
-		pi->y -= y1;
-
-		if (pi->type == PAN_ITEM_TRIANGLE && pi->data)
-			{
-			gint *coord;
-
-			coord = pi->data;
-			coord[0] -= x1;
-			coord[1] -= y1;
-			coord[2] -= x1;
-			coord[3] -= y1;
-			coord[4] -= x1;
-			coord[5] -= y1;
-			}
-		}
-
-	if (width) *width = x2 - x1;
-	if (height) *height = y2 - y1;
-}
-
-typedef struct _FlowerGroup FlowerGroup;
-struct _FlowerGroup {
-	GList *items;
-	GList *children;
-	gint x;
-	gint y;
-	gint width;
-	gint height;
-
-	gdouble angle;
-	gint circumference;
-	gint diameter;
-};
-
-static void pan_flower_move(FlowerGroup *group, gint x, gint y)
-{
-	GList *work;
-
-	work = group->items;
-	while (work)
-		{
-		PanItem *pi;
-
-		pi = work->data;
-		work = work->next;
-
-		pi->x += x;
-		pi->y += y;
-		}
-
-	group->x += x;
-	group->y += y;
-}
-
-#define PI 3.14159265
-
-static void pan_flower_position(FlowerGroup *group, FlowerGroup *parent,
-							     gint *result_x, gint *result_y)
-{
-	gint x, y;
-	gint radius;
-	gdouble a;
-
-	radius = parent->circumference / (2*PI);
-	radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
-
-	a = 2*PI * group->diameter / parent->circumference;
-
-	x = (gint)((gdouble)radius * cos(parent->angle + a / 2));
-	y = (gint)((gdouble)radius * sin(parent->angle + a / 2));
-
-	parent->angle += a;
-
-	x += parent->x;
-	y += parent->y;
-
-	x += parent->width / 2;
-	y += parent->height / 2;
-
-	x -= group->width / 2;
-	y -= group->height / 2;
-
-	*result_x = x;
-	*result_y = y;
-}
-
-static void pan_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
-{
-	GList *work;
-	gint x, y;
-
-	if (!group) return;
-
-	if (parent && parent->children)
-		{
-		pan_flower_position(group, parent, &x, &y);
-		}
-	else
-		{
-		x = 0;
-		y = 0;
-		}
-
-	pan_flower_move(group, x, y);
-
-	if (parent)
-		{
-		PanItem *pi;
-		gint px, py, gx, gy;
-		gint x1, y1, x2, y2;
-
-		px = parent->x + parent->width / 2;
-		py = parent->y + parent->height / 2;
-
-		gx = group->x + group->width / 2;
-		gy = group->y + group->height / 2;
-
-		x1 = MIN(px, gx);
-		y1 = MIN(py, gy);
-
-		x2 = MAX(px, gx + 5);
-		y2 = MAX(py, gy + 5);
-
-		pi = pan_item_tri_new(pw, NULL, x1, y1, x2 - x1, y2 - y1,
-				      px, py, gx, gy, gx + 5, gy + 5,
-				      255, 40, 40, 128);
-		pan_item_tri_border(pi, PAN_BORDER_1 | PAN_BORDER_3,
-				    255, 0, 0, 128);
-		}
-
-	pw->list = g_list_concat(group->items, pw->list);
-	group->items = NULL;
-
-	group->circumference = 0;
-	work = group->children;
-	while (work)
-		{
-		FlowerGroup *child;
-
-		child = work->data;
-		work = work->next;
-
-		group->circumference += child->diameter;
-		}
-
-	work = g_list_last(group->children);
-	while (work)
-		{
-		FlowerGroup *child;
-
-		child = work->data;
-		work = work->prev;
-
-		pan_flower_build(pw, child, group);
-		}
-
-	g_list_free(group->children);
-	g_free(group);
-}
-
-static FlowerGroup *pan_flower_group(PanWindow *pw, FileData *dir_fd, gint x, gint y)
-{
-	FlowerGroup *group;
-	GList *f;
-	GList *d;
-	GList *work;
-	PanItem *pi_box;
-	gint x_start;
-	gint y_height;
-	gint grid_size;
-	gint grid_count;
-
-	if (!filelist_read(dir_fd, &f, &d)) return NULL;
-	if (!f && !d) return NULL;
-
-	f = filelist_sort(f, SORT_NAME, TRUE);
-	d = filelist_sort(d, SORT_NAME, TRUE);
-
-	pi_box = pan_item_text_new(pw, x, y, dir_fd->path, PAN_TEXT_ATTR_NONE,
-				   PAN_TEXT_BORDER_SIZE,
-				   PAN_TEXT_COLOR, 255);
-
-	y += pi_box->height;
-
-	pi_box = pan_item_box_new(pw, file_data_ref(dir_fd),
-				  x, y,
-				  PAN_BOX_BORDER * 2, PAN_BOX_BORDER * 2,
-				  PAN_BOX_OUTLINE_THICKNESS,
-				  PAN_BOX_COLOR, PAN_BOX_ALPHA,
-				  PAN_BOX_OUTLINE_COLOR, PAN_BOX_OUTLINE_ALPHA);
-
-	x += PAN_BOX_BORDER;
-	y += PAN_BOX_BORDER;
-
-	grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
-	grid_count = 0;
-	x_start = x;
-	y_height = y;
-
-	work = f;
-	while (work)
-		{
-		FileData *fd;
-		PanItem *pi;
-
-		fd = work->data;
-		work = work->next;
-
-		if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE)
-			{
-			pi = pan_item_image_new(pw, fd, x, y, 10, 10);
-			x += pi->width + PAN_THUMB_GAP;
-			if (pi->height > y_height) y_height = pi->height;
-			}
-		else
-			{
-			pi = pan_item_thumb_new(pw, fd, x, y);
-			x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
-			y_height = PAN_THUMB_SIZE;
-			}
-
-		grid_count++;
-		if (grid_count >= grid_size)
-			{
-			grid_count = 0;
-			x = x_start;
-			y += y_height + PAN_THUMB_GAP;
-			y_height = 0;
-			}
-
-		pan_item_size_by_item(pi_box, pi, PAN_BOX_BORDER);
-		}
-
-	group = g_new0(FlowerGroup, 1);
-	group->items = pw->list;
-	pw->list = NULL;
-
-	group->width = pi_box->width;
-	group->height = pi_box->y + pi_box->height;
-	group->diameter = (gint)sqrt(group->width * group->width + group->height * group->height);
-
-	group->children = NULL;
-
-	work = d;
-	while (work)
-		{
-		FileData *fd;
-		FlowerGroup *child;
-
-		fd = work->data;
-		work = work->next;
-
-		if (!pan_is_ignored(fd->path, pw->ignore_symlinks))
-			{
-			child = pan_flower_group(pw, fd, 0, 0);
-			if (child) group->children = g_list_prepend(group->children, child);
-			}
-		}
-
-	if (!f && !group->children)
-		{
-		work = group->items;
-		while (work)
-			{
-			PanItem *pi;
-
-			pi = work->data;
-			work = work->next;
-
-			pan_item_free(pi);
-			}
-
-		g_list_free(group->items);
-		g_free(group);
-		group = NULL;
-		}
-
-	g_list_free(f);
-	filelist_free(d);
-
-	return group;
-}
-
-void pan_flower_compute(PanWindow *pw, FileData *dir_fd,
-			gint *width, gint *height,
-			gint *scroll_x, gint *scroll_y)
-{
-	FlowerGroup *group;
-	GList *list;
-
-	group = pan_flower_group(pw, dir_fd, 0, 0);
-	pan_flower_build(pw, group, NULL);
-
-	pan_flower_size(pw, width, height);
-
-	list = pan_item_find_by_fd(pw, PAN_ITEM_BOX, dir_fd, FALSE, FALSE);
-	if (list)
-		{
-		PanItem *pi = list->data;
-		*scroll_x = pi->x + pi->width / 2;
-		*scroll_y = pi->y + pi->height / 2;
-		}
-	g_list_free(list);
-}
-
-static void pan_folder_tree_path(PanWindow *pw, FileData *dir_fd,
-				 gint *x, gint *y, gint *level,
-				 PanItem *parent,
-				 gint *width, gint *height)
-{
-	GList *f;
-	GList *d;
-	GList *work;
-	PanItem *pi_box;
-	gint y_height = 0;
-
-	if (!filelist_read(dir_fd, &f, &d)) return;
-	if (!f && !d) return;
-
-	f = filelist_sort(f, SORT_NAME, TRUE);
-	d = filelist_sort(d, SORT_NAME, TRUE);
-
-	*x = PAN_BOX_BORDER + ((*level) * MAX(PAN_BOX_BORDER, PAN_THUMB_GAP));
-
-	pi_box = pan_item_text_new(pw, *x, *y, dir_fd->path, PAN_TEXT_ATTR_NONE,
-				   PAN_TEXT_BORDER_SIZE,
-				   PAN_TEXT_COLOR, 255);
-
-	*y += pi_box->height;
-
-	pi_box = pan_item_box_new(pw, file_data_ref(dir_fd),
-				  *x, *y,
-				  PAN_BOX_BORDER, PAN_BOX_BORDER,
-				  PAN_BOX_OUTLINE_THICKNESS,
-				  PAN_BOX_COLOR, PAN_BOX_ALPHA,
-				  PAN_BOX_OUTLINE_COLOR, PAN_BOX_OUTLINE_ALPHA);
-
-	*x += PAN_BOX_BORDER;
-	*y += PAN_BOX_BORDER;
-
-	work = f;
-	while (work)
-		{
-		FileData *fd;
-		PanItem *pi;
-
-		fd = work->data;
-		work = work->next;
-
-		if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE)
-			{
-			pi = pan_item_image_new(pw, fd, *x, *y, 10, 10);
-			*x += pi->width + PAN_THUMB_GAP;
-			if (pi->height > y_height) y_height = pi->height;
-			}
-		else
-			{
-			pi = pan_item_thumb_new(pw, fd, *x, *y);
-			*x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
-			y_height = PAN_THUMB_SIZE;
-			}
-
-		pan_item_size_by_item(pi_box, pi, PAN_BOX_BORDER);
-		}
-
-	if (f) *y = pi_box->y + pi_box->height;
-
-	g_list_free(f);
-
-	work = d;
-	while (work)
-		{
-		FileData *fd;
-
-		fd = work->data;
-		work = work->next;
-
-		if (!pan_is_ignored(fd->path, pw->ignore_symlinks))
-			{
-			*level = *level + 1;
-			pan_folder_tree_path(pw, fd, x, y, level, pi_box, width, height);
-			*level = *level - 1;
-			}
-		}
-
-	filelist_free(d);
-
-	pan_item_size_by_item(parent, pi_box, PAN_BOX_BORDER);
-
-	if (*y < pi_box->y + pi_box->height + PAN_BOX_BORDER)
-		*y = pi_box->y + pi_box->height + PAN_BOX_BORDER;
-
-	pan_item_size_coordinates(pi_box, PAN_BOX_BORDER, width, height);
-}
-
-void pan_folder_tree_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height)
-{
-	gint x, y;
-	gint level;
-	gint w, h;
-
-	level = 0;
-	x = PAN_BOX_BORDER;
-	y = PAN_BOX_BORDER;
-	w = PAN_BOX_BORDER * 2;
-	h = PAN_BOX_BORDER * 2;
-
-	pan_folder_tree_path(pw, dir_fd, &x, &y, &level, NULL, &w, &h);
-
-	if (width) *width = w;
-	if (height) *height = h;
-}
-/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/pan-grid.c	Tue Dec 20 21:36:52 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2006 John Ellis
- * Copyright (C) 2008 - 2016 The Geeqie Team
- *
- * Author: John Ellis
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "main.h"
-#include "pan-types.h"
-
-#include <math.h>
-
-
-void pan_grid_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height)
-{
-	GList *list;
-	GList *work;
-	gint x, y;
-	gint grid_size;
-	gint next_y;
-
-	list = pan_list_tree(dir_fd, SORT_NAME, TRUE, pw->ignore_symlinks);
-
-	grid_size = (gint)sqrt((gdouble)g_list_length(list));
-	if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE)
-		{
-		grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
-		}
-	else
-		{
-		grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
-		}
-
-	next_y = 0;
-
-	*width = PAN_BOX_BORDER * 2;
-	*height = PAN_BOX_BORDER * 2;
-
-	x = PAN_THUMB_GAP;
-	y = PAN_THUMB_GAP;
-	work = list;
-	while (work)
-		{
-		FileData *fd;
-		PanItem *pi;
-
-		fd = work->data;
-		work = work->next;
-
-		if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE)
-			{
-			pi = pan_item_image_new(pw, fd, x, y, 10, 10);
-
-			x += pi->width + PAN_THUMB_GAP;
-			if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
-			if (x > grid_size)
-				{
-				x = PAN_THUMB_GAP;
-				y = next_y;
-				}
-			}
-		else
-			{
-			pi = pan_item_thumb_new(pw, fd, x, y);
-
-			x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
-			if (x > grid_size)
-				{
-				x = PAN_THUMB_GAP;
-				y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
-				}
-			}
-		pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
-		}
-
-	g_list_free(list);
-}
-/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/pan-item.c	Tue Dec 20 21:36:52 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,974 +0,0 @@
-/*
- * Copyright (C) 2006 John Ellis
- * Copyright (C) 2008 - 2016 The Geeqie Team
- *
- * Author: John Ellis
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "main.h"
-#include "pan-types.h"
-
-
-/*
- *-----------------------------------------------------------------------------
- * item base functions
- *-----------------------------------------------------------------------------
- */
-
-void pan_item_free(PanItem *pi)
-{
-	if (!pi) return;
-
-	if (pi->pixbuf) g_object_unref(pi->pixbuf);
-	if (pi->fd) file_data_unref(pi->fd);
-	g_free(pi->text);
-	g_free(pi->key);
-	g_free(pi->data);
-
-	g_free(pi);
-}
-
-void pan_item_set_key(PanItem *pi, const gchar *key)
-{
-	gchar *tmp;
-
-	if (!pi) return;
-
-	tmp = pi->key;
-	pi->key = g_strdup(key);
-	g_free(tmp);
-}
-
-void pan_item_added(PanWindow *pw, PanItem *pi)
-{
-	if (!pi) return;
-	image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
-}
-
-void pan_item_remove(PanWindow *pw, PanItem *pi)
-{
-	if (!pi) return;
-
-	if (pw->click_pi == pi) pw->click_pi = NULL;
-	if (pw->queue_pi == pi)	pw->queue_pi = NULL;
-	if (pw->search_pi == pi) pw->search_pi = NULL;
-	pw->queue = g_list_remove(pw->queue, pi);
-
-	pw->list = g_list_remove(pw->list, pi);
-	image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
-	pan_item_free(pi);
-}
-
-void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
-{
-	if (!pi || !child) return;
-
-	if (pi->x + pi->width < child->x + child->width + border)
-		pi->width = child->x + child->width + border - pi->x;
-
-	if (pi->y + pi->height < child->y + child->height + border)
-		pi->height = child->y + child->height + border - pi->y;
-}
-
-void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
-{
-	if (!pi) return;
-
-	if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
-	if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * item box type
- *-----------------------------------------------------------------------------
- */
-
-PanItem *pan_item_box_new(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
-			  gint border_size,
-			  guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
-			  guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
-{
-	PanItem *pi;
-
-	pi = g_new0(PanItem, 1);
-	pi->type = PAN_ITEM_BOX;
-	pi->fd = fd;
-	pi->x = x;
-	pi->y = y;
-	pi->width = width;
-	pi->height = height;
-
-	pi->color_r = base_r;
-	pi->color_g = base_g;
-	pi->color_b = base_b;
-	pi->color_a = base_a;
-
-	pi->color2_r = bord_r;
-	pi->color2_g = bord_g;
-	pi->color2_b = bord_b;
-	pi->color2_a = bord_a;
-	pi->border = border_size;
-
-	pw->list = g_list_prepend(pw->list, pi);
-
-	return pi;
-}
-
-void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
-{
-	gint *shadow;
-
-	if (!pi || pi->type != PAN_ITEM_BOX) return;
-
-	shadow = pi->data;
-	if (shadow)
-		{
-		pi->width -= shadow[0];
-		pi->height -= shadow[0];
-		}
-
-	shadow = g_new0(gint, 2);
-	shadow[0] = offset;
-	shadow[1] = fade;
-
-	pi->width += offset;
-	pi->height += offset;
-
-	g_free(pi->data);
-	pi->data = shadow;
-}
-
-gint pan_item_box_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
-		       gint x, gint y, gint width, gint height)
-{
-	gint bw, bh;
-	gint *shadow;
-	gint rx, ry, rw, rh;
-
-	bw = pi->width;
-	bh = pi->height;
-
-	shadow = pi->data;
-	if (shadow)
-		{
-		bw -= shadow[0];
-		bh -= shadow[0];
-
-		if (pi->color_a > 254)
-			{
-			pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
-					   shadow[0], bh - shadow[0],
-					   pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
-					   shadow[1],
-					   PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
-			pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
-					   bw, shadow[0],
-					   pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
-					   shadow[1],
-					   PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
-			}
-		else
-			{
-			gint a;
-			a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
-			pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
-					   bw, bh,
-					   pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
-					   shadow[1],
-					   PAN_SHADOW_COLOR, a);
-			}
-		}
-
-	if (util_clip_region(x, y, width, height,
-			     pi->x, pi->y, bw, bh,
-			     &rx, &ry, &rw, &rh))
-		{
-		pixbuf_draw_rect_fill(pixbuf,
-				      rx - x, ry - y, rw, rh,
-				      pi->color_r, pi->color_g, pi->color_b, pi->color_a);
-		}
-	if (util_clip_region(x, y, width, height,
-			     pi->x, pi->y, bw, pi->border,
-			     &rx, &ry, &rw, &rh))
-		{
-		pixbuf_draw_rect_fill(pixbuf,
-				      rx - x, ry - y, rw, rh,
-				      pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
-		}
-	if (util_clip_region(x, y, width, height,
-			     pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
-			     &rx, &ry, &rw, &rh))
-		{
-		pixbuf_draw_rect_fill(pixbuf,
-				      rx - x, ry - y, rw, rh,
-				      pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
-		}
-	if (util_clip_region(x, y, width, height,
-			     pi->x + bw - pi->border, pi->y + pi->border,
-			     pi->border, bh - pi->border * 2,
-			     &rx, &ry, &rw, &rh))
-		{
-		pixbuf_draw_rect_fill(pixbuf,
-				      rx - x, ry - y, rw, rh,
-				      pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
-		}
-	if (util_clip_region(x, y, width, height,
-			     pi->x, pi->y + bh - pi->border,
-			     bw,  pi->border,
-			     &rx, &ry, &rw, &rh))
-		{
-		pixbuf_draw_rect_fill(pixbuf,
-				      rx - x, ry - y, rw, rh,
-				      pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
-		}
-
-	return FALSE;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * item triangle type
- *-----------------------------------------------------------------------------
- */
-
-PanItem *pan_item_tri_new(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
-			  gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
-			  guint8 r, guint8 g, guint8 b, guint8 a)
-{
-	PanItem *pi;
-	gint *coord;
-
-	pi = g_new0(PanItem, 1);
-	pi->type = PAN_ITEM_TRIANGLE;
-	pi->x = x;
-	pi->y = y;
-	pi->width = width;
-	pi->height = height;
-
-	pi->color_r = r;
-	pi->color_g = g;
-	pi->color_b = b;
-	pi->color_a = a;
-
-	coord = g_new0(gint, 6);
-	coord[0] = x1;
-	coord[1] = y1;
-	coord[2] = x2;
-	coord[3] = y2;
-	coord[4] = x3;
-	coord[5] = y3;
-
-	pi->data = coord;
-
-	pi->border = PAN_BORDER_NONE;
-
-	pw->list = g_list_prepend(pw->list, pi);
-
-	return pi;
-}
-
-void pan_item_tri_border(PanItem *pi, gint borders,
-			 guint8 r, guint8 g, guint8 b, guint8 a)
-{
-	if (!pi || pi->type != PAN_ITEM_TRIANGLE) return;
-
-	pi->border = borders;
-
-	pi->color2_r = r;
-	pi->color2_g = g;
-	pi->color2_b = b;
-	pi->color2_a = a;
-}
-
-gint pan_item_tri_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
-		       gint x, gint y, gint width, gint height)
-{
-	gint rx, ry, rw, rh;
-
-	if (util_clip_region(x, y, width, height,
-			     pi->x, pi->y, pi->width, pi->height,
-			     &rx, &ry, &rw, &rh) && pi->data)
-		{
-		gint *coord = pi->data;
-		pixbuf_draw_triangle(pixbuf,
-				     rx - x, ry - y, rw, rh,
-				     coord[0] - x, coord[1] - y,
-				     coord[2] - x, coord[3] - y,
-				     coord[4] - x, coord[5] - y,
-				     pi->color_r, pi->color_g, pi->color_b, pi->color_a);
-
-		if (pi->border & PAN_BORDER_1)
-			{
-			pixbuf_draw_line(pixbuf,
-					 rx - x, ry - y, rw, rh,
-					 coord[0] - x, coord[1] - y,
-					 coord[2] - x, coord[3] - y,
-					 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
-			}
-		if (pi->border & PAN_BORDER_2)
-			{
-			pixbuf_draw_line(pixbuf,
-					 rx - x, ry - y, rw, rh,
-					 coord[2] - x, coord[3] - y,
-					 coord[4] - x, coord[5] - y,
-					 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
-			}
-		if (pi->border & PAN_BORDER_3)
-			{
-			pixbuf_draw_line(pixbuf,
-					 rx - x, ry - y, rw, rh,
-					 coord[4] - x, coord[5] - y,
-					 coord[0] - x, coord[1] - y,
-					 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
-			}
-		}
-
-	return FALSE;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * item text type
- *-----------------------------------------------------------------------------
- */
-
-static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
-{
-	PangoLayout *layout;
-
-	layout = gtk_widget_create_pango_layout(widget, NULL);
-
-	if (pi->text_attr & PAN_TEXT_ATTR_MARKUP)
-		{
-		pango_layout_set_markup(layout, pi->text, -1);
-		return layout;
-		}
-
-	if (pi->text_attr & PAN_TEXT_ATTR_BOLD ||
-	    pi->text_attr & PAN_TEXT_ATTR_HEADING)
-		{
-		PangoAttrList *pal;
-		PangoAttribute *pa;
-
-		pal = pango_attr_list_new();
-		if (pi->text_attr & PAN_TEXT_ATTR_BOLD)
-			{
-			pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
-			pa->start_index = 0;
-			pa->end_index = G_MAXINT;
-			pango_attr_list_insert(pal, pa);
-			}
-		if (pi->text_attr & PAN_TEXT_ATTR_HEADING)
-			{
-			pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
-			pa->start_index = 0;
-			pa->end_index = G_MAXINT;
-			pango_attr_list_insert(pal, pa);
-			}
-		pango_layout_set_attributes(layout, pal);
-		pango_attr_list_unref(pal);
-		}
-
-	pango_layout_set_text(layout, pi->text, -1);
-	return layout;
-}
-
-static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
-{
-	PangoLayout *layout;
-
-	if (!pi || !pi->text || !widget) return;
-
-	layout = pan_item_text_layout(pi, widget);
-	pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
-	g_object_unref(G_OBJECT(layout));
-
-	pi->width += pi->border * 2;
-	pi->height += pi->border * 2;
-}
-
-PanItem *pan_item_text_new(PanWindow *pw, gint x, gint y, const gchar *text,
-			   PanTextAttrType attr, PanBorderType border,
-			   guint8 r, guint8 g, guint8 b, guint8 a)
-{
-	PanItem *pi;
-
-	pi = g_new0(PanItem, 1);
-	pi->type = PAN_ITEM_TEXT;
-	pi->x = x;
-	pi->y = y;
-	pi->text = g_strdup(text);
-	pi->text_attr = attr;
-
-	pi->color_r = r;
-	pi->color_g = g;
-	pi->color_b = b;
-	pi->color_a = a;
-
-	pi->border = border;
-
-	pan_item_text_compute_size(pi, pw->imd->pr);
-
-	pw->list = g_list_prepend(pw->list, pi);
-
-	return pi;
-}
-
-gint pan_item_text_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
-			gint x, gint y, gint width, gint height)
-{
-	PangoLayout *layout;
-
-	layout = pan_item_text_layout(pi, (GtkWidget *)pr);
-	pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
-			   pi->x - x + pi->border, pi->y - y + pi->border,
-			   pi->color_r, pi->color_g, pi->color_b, pi->color_a);
-	g_object_unref(G_OBJECT(layout));
-
-	return FALSE;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * item thumbnail type
- *-----------------------------------------------------------------------------
- */
-
-PanItem *pan_item_thumb_new(PanWindow *pw, FileData *fd, gint x, gint y)
-{
-	PanItem *pi;
-
-	pi = g_new0(PanItem, 1);
-
-	pi->type = PAN_ITEM_THUMB;
-	pi->fd = fd;
-	pi->x = x;
-	pi->y = y;
-	pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
-	pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
-
-	pw->list = g_list_prepend(pw->list, pi);
-
-	return pi;
-}
-
-gint pan_item_thumb_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
-			 gint x, gint y, gint width, gint height)
-{
-	gint tx, ty, tw, th;
-	gint rx, ry, rw, rh;
-
-	if (pi->pixbuf)
-		{
-		tw = gdk_pixbuf_get_width(pi->pixbuf);
-		th = gdk_pixbuf_get_height(pi->pixbuf);
-
-		tx = pi->x + (pi->width - tw) / 2;
-		ty = pi->y + (pi->height - th) / 2;
-
-		if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
-			{
-			if (util_clip_region(x, y, width, height,
-					     tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
-					     &rx, &ry, &rw, &rh))
-				{
-				pixbuf_draw_shadow(pixbuf,
-						   rx - x, ry - y, rw, rh,
-						   tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
-						   PAN_SHADOW_FADE,
-						   PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
-				}
-			}
-		else
-			{
-			if (util_clip_region(x, y, width, height,
-					     tx + tw, ty + PAN_SHADOW_OFFSET,
-					     PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
-					     &rx, &ry, &rw, &rh))
-				{
-				pixbuf_draw_shadow(pixbuf,
-						   rx - x, ry - y, rw, rh,
-						   tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
-						   PAN_SHADOW_FADE,
-						   PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
-				}
-			if (util_clip_region(x, y, width, height,
-					     tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
-					     &rx, &ry, &rw, &rh))
-				{
-				pixbuf_draw_shadow(pixbuf,
-						   rx - x, ry - y, rw, rh,
-						   tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
-						   PAN_SHADOW_FADE,
-						   PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
-				}
-			}
-
-		if (util_clip_region(x, y, width, height,
-				     tx, ty, tw, th,
-				     &rx, &ry, &rw, &rh))
-			{
-			gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
-					     (gdouble) tx - x,
-					     (gdouble) ty - y,
-					     1.0, 1.0, GDK_INTERP_NEAREST,
-					     255);
-			}
-
-		if (util_clip_region(x, y, width, height,
-				     tx, ty, tw, PAN_OUTLINE_THICKNESS,
-				     &rx, &ry, &rw, &rh))
-			{
-			pixbuf_draw_rect_fill(pixbuf,
-					      rx - x, ry - y, rw, rh,
-					      PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
-			}
-		if (util_clip_region(x, y, width, height,
-				     tx, ty, PAN_OUTLINE_THICKNESS, th,
-				     &rx, &ry, &rw, &rh))
-			{
-			pixbuf_draw_rect_fill(pixbuf,
-					      rx - x, ry - y, rw, rh,
-					      PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
-			}
-		if (util_clip_region(x, y, width, height,
-				     tx + tw - PAN_OUTLINE_THICKNESS, ty +  PAN_OUTLINE_THICKNESS,
-				     PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
-				     &rx, &ry, &rw, &rh))
-			{
-			pixbuf_draw_rect_fill(pixbuf,
-					      rx - x, ry - y, rw, rh,
-					      PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
-			}
-		if (util_clip_region(x, y, width, height,
-				     tx +  PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
-				     tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
-				     &rx, &ry, &rw, &rh))
-			{
-			pixbuf_draw_rect_fill(pixbuf,
-					      rx - x, ry - y, rw, rh,
-					      PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
-			}
-		}
-	else
-		{
-		tw = pi->width - PAN_SHADOW_OFFSET * 2;
-		th = pi->height - PAN_SHADOW_OFFSET * 2;
-		tx = pi->x + PAN_SHADOW_OFFSET;
-		ty = pi->y + PAN_SHADOW_OFFSET;
-
-		if (util_clip_region(x, y, width, height,
-				     tx, ty, tw, th,
-				     &rx, &ry, &rw, &rh))
-			{
-			gint d;
-
-			d = (pw->size <= PAN_IMAGE_SIZE_THUMB_NONE) ? 2 : 8;
-			pixbuf_draw_rect_fill(pixbuf,
-					      rx - x, ry - y, rw, rh,
-					      PAN_SHADOW_COLOR,
-					      PAN_SHADOW_ALPHA / d);
-			}
-		}
-
-	return (pi->pixbuf == NULL);
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * item image type
- *-----------------------------------------------------------------------------
- */
-
-static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
-{
-	GList *work;
-
-	pi->width = w;
-	pi->height = h;
-
-	if (!pi->fd) return;
-
-	work = pw->cache_list;
-	while (work)
-		{
-		PanCacheData *pc;
-
-		pc = work->data;
-		work = work->next;
-
-		if (pc->cd && pc->cd->dimensions &&
-		    pc->fd && pc->fd == pi->fd)
-			{
-			pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
-			pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
-
-			pw->cache_list = g_list_remove(pw->cache_list, pc);
-			cache_sim_data_free(pc->cd);
-			file_data_unref(pc->fd);
-			g_free(pc);
-			return;
-			}
-		}
-}
-
-PanItem *pan_item_image_new(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
-{
-	PanItem *pi;
-
-	pi = g_new0(PanItem, 1);
-	pi->type = PAN_ITEM_IMAGE;
-	pi->fd = fd;
-	pi->x = x;
-	pi->y = y;
-
-	pi->color_a = 255;
-
-	pi->color2_r = 0;
-	pi->color2_g = 0;
-	pi->color2_b = 0;
-	pi->color2_a = PAN_SHADOW_ALPHA / 2;
-
-	pan_item_image_find_size(pw, pi, w, h);
-
-	pw->list = g_list_prepend(pw->list, pi);
-
-	return pi;
-}
-
-gint pan_item_image_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
-			 gint x, gint y, gint width, gint height)
-{
-	gint rx, ry, rw, rh;
-
-	if (util_clip_region(x, y, width, height,
-			     pi->x, pi->y, pi->width, pi->height,
-			     &rx, &ry, &rw, &rh))
-		{
-		if (pi->pixbuf)
-			{
-			gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
-					     (gdouble) pi->x - x,
-					     (gdouble) pi->y - y,
-					     1.0, 1.0, GDK_INTERP_NEAREST,
-					     pi->color_a);
-			}
-		else
-			{
-			pixbuf_draw_rect_fill(pixbuf,
-					      rx - x, ry - y, rw, rh,
-					      pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
-			}
-		}
-
-	return (pi->pixbuf == NULL);
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * item lookup/search
- *-----------------------------------------------------------------------------
- */
-
-PanItem *pan_item_find_by_key(PanWindow *pw, PanItemType type, const gchar *key)
-{
-	GList *work;
-
-	if (!key) return NULL;
-
-	work = g_list_last(pw->list);
-	while (work)
-		{
-		PanItem *pi;
-
-		pi = work->data;
-		if ((pi->type == type || type == PAN_ITEM_NONE) &&
-		     pi->key && strcmp(pi->key, key) == 0)
-			{
-			return pi;
-			}
-		work = work->prev;
-		}
-	work = g_list_last(pw->list_static);
-	while (work)
-		{
-		PanItem *pi;
-
-		pi = work->data;
-		if ((pi->type == type || type == PAN_ITEM_NONE) &&
-		     pi->key && strcmp(pi->key, key) == 0)
-			{
-			return pi;
-			}
-		work = work->prev;
-		}
-
-	return NULL;
-}
-
-/* when ignore_case and partial are TRUE, path should be converted to lower case */
-static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
-				      PanItemType type, const gchar *path,
-				      gboolean ignore_case, gboolean partial)
-{
-	GList *work;
-
-	work = g_list_last(search_list);
-	while (work)
-		{
-		PanItem *pi;
-
-		pi = work->data;
-		if ((pi->type == type || type == PAN_ITEM_NONE) && pi->fd)
-			{
-			gboolean match = FALSE;
-
-			if (path[0] == G_DIR_SEPARATOR)
-				{
-				if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
-				}
-			else if (pi->fd->name)
-				{
-				if (partial)
-					{
-					if (ignore_case)
-						{
-						gchar *haystack;
-
-						haystack = g_utf8_strdown(pi->fd->name, -1);
-						match = (strstr(haystack, path) != NULL);
-						g_free(haystack);
-						}
-					else
-						{
-						if (strstr(pi->fd->name, path)) match = TRUE;
-						}
-					}
-				else if (ignore_case)
-					{
-					if (g_ascii_strcasecmp(path, pi->fd->name) == 0) match = TRUE;
-					}
-				else
-					{
-					if (strcmp(path, pi->fd->name) == 0) match = TRUE;
-					}
-				}
-
-			if (match) list = g_list_prepend(list, pi);
-			}
-		work = work->prev;
-		}
-
-	return list;
-}
-
-/* when ignore_case and partial are TRUE, path should be converted to lower case */
-GList *pan_item_find_by_path(PanWindow *pw, PanItemType type, const gchar *path,
-			     gboolean ignore_case, gboolean partial)
-{
-	GList *list = NULL;
-
-	if (!path) return NULL;
-	if (partial && path[0] == G_DIR_SEPARATOR) return NULL;
-
-	list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
-	list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
-
-	return g_list_reverse(list);
-}
-
-GList *pan_item_find_by_fd(PanWindow *pw, PanItemType type, FileData *fd,
-			   gboolean ignore_case, gboolean partial)
-{
-	if (!fd) return NULL;
-	return pan_item_find_by_path(pw, type, fd->path, ignore_case, partial);
-}
-
-
-static PanItem *pan_item_find_by_coord_l(GList *list, PanItemType type, gint x, gint y, const gchar *key)
-{
-	GList *work;
-
-	work = list;
-	while (work)
-		{
-		PanItem *pi;
-
-		pi = work->data;
-		if ((pi->type == type || type == PAN_ITEM_NONE) &&
-		     x >= pi->x && x < pi->x + pi->width &&
-		     y >= pi->y && y < pi->y + pi->height &&
-		    (!key || (pi->key && strcmp(pi->key, key) == 0)))
-			{
-			return pi;
-			}
-		work = work->next;
-		}
-
-	return NULL;
-}
-
-PanItem *pan_item_find_by_coord(PanWindow *pw, PanItemType type,
-				gint x, gint y, const gchar *key)
-{
-	PanItem *pi;
-
-	pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
-	if (pi) return pi;
-
-	return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * text alignments
- *-----------------------------------------------------------------------------
- */
-
-PanTextAlignment *pan_text_alignment_new(PanWindow *pw, gint x, gint y, const gchar *key)
-{
-	PanTextAlignment *ta;
-
-	ta = g_new0(PanTextAlignment, 1);
-
-	ta->pw = pw;
-	ta->x = x;
-	ta->y = y;
-	ta->key = g_strdup(key);
-
-	return ta;
-}
-
-void pan_text_alignment_free(PanTextAlignment *ta)
-{
-	if (!ta) return;
-
-	g_list_free(ta->column1);
-	g_list_free(ta->column2);
-	g_free(ta->key);
-	g_free(ta);
-}
-
-PanItem *pan_text_alignment_add(PanTextAlignment *ta, const gchar *label, const gchar *text)
-{
-	PanItem *item;
-
-	if (label)
-		{
-		item = pan_item_text_new(ta->pw, ta->x, ta->y, label,
-					 PAN_TEXT_ATTR_BOLD, 0,
-					 PAN_POPUP_TEXT_COLOR, 255);
-		pan_item_set_key(item, ta->key);
-		}
-	else
-		{
-		item = NULL;
-		}
-	ta->column1 = g_list_append(ta->column1, item);
-
-	if (text)
-		{
-		item = pan_item_text_new(ta->pw, ta->x, ta->y, text,
-					 PAN_TEXT_ATTR_NONE, 0,
-					 PAN_POPUP_TEXT_COLOR, 255);
-		pan_item_set_key(item, ta->key);
-		}
-	else
-		{
-		item = NULL;
-		}
-	ta->column2 = g_list_append(ta->column2, item);
-
-	return item;
-}
-
-void pan_text_alignment_calc(PanTextAlignment *ta, PanItem *box)
-{
-	gint cw1, cw2;
-	gint x, y;
-	GList *work1;
-	GList *work2;
-
-	cw1 = 0;
-	cw2 = 0;
-
-	work1 = ta->column1;
-	while (work1)
-		{
-		PanItem *p;
-
-		p = work1->data;
-		work1 = work1->next;
-
-		if (p && p->width > cw1) cw1 = p->width;
-		}
-
-	work2 = ta->column2;
-	while (work2)
-		{
-		PanItem *p;
-
-		p = work2->data;
-		work2 = work2->next;
-
-		if (p && p->width > cw2) cw2 = p->width;
-		}
-
-	x = ta->x;
-	y = ta->y;
-	work1 = ta->column1;
-	work2 = ta->column2;
-	while (work1 && work2)
-		{
-		PanItem *p1;
-		PanItem *p2;
-		gint height = 0;
-
-		p1 = work1->data;
-		p2 = work2->data;
-		work1 = work1->next;
-		work2 = work2->next;
-
-		if (p1)
-			{
-			p1->x = x;
-			p1->y = y;
-			pan_item_size_by_item(box, p1, PREF_PAD_BORDER);
-			height = p1->height;
-			}
-		if (p2)
-			{
-			p2->x = x + cw1 + PREF_PAD_SPACE;
-			p2->y = y;
-			pan_item_size_by_item(box, p2, PREF_PAD_BORDER);
-			if (height < p2->height) height = p2->height;
-			}
-
-		if (!p1 && !p2) height = PREF_PAD_GROUP;
-
-		y += height;
-		}
-}
-/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/pan-timeline.c	Tue Dec 20 21:36:52 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2006 John Ellis
- * Copyright (C) 2008 - 2016 The Geeqie Team
- *
- * Author: John Ellis
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "main.h"
-#include "pan-types.h"
-
-
-void pan_timeline_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height)
-{
-	GList *list;
-	GList *work;
-	gint x, y;
-	time_t tc;
-	gint total;
-	gint count;
-	PanItem *pi_month = NULL;
-	PanItem *pi_day = NULL;
-	gint month_start;
-	gint day_start;
-	gint x_width;
-	gint y_height;
-
-	list = pan_list_tree(dir_fd, SORT_NONE, TRUE, pw->ignore_symlinks);
-
-	if (pw->cache_list && pw->exif_date_enable)
-		{
-		pw->cache_list = pan_cache_sort(pw->cache_list, SORT_NAME, TRUE);
-		list = filelist_sort(list, SORT_NAME, TRUE);
-		pan_cache_sync_date(pw, list);
-		}
-
-	pw->cache_list = pan_cache_sort(pw->cache_list, SORT_TIME, TRUE);
-	list = filelist_sort(list, SORT_TIME, TRUE);
-
-	*width = PAN_BOX_BORDER * 2;
-	*height = PAN_BOX_BORDER * 2;
-
-	x = 0;
-	y = 0;
-	month_start = y;
-	day_start = month_start;
-	x_width = 0;
-	y_height = 0;
-	tc = 0;
-	total = 0;
-	count = 0;
-	work = list;
-	while (work)
-		{
-		FileData *fd;
-		PanItem *pi;
-
-		fd = work->data;
-		work = work->next;
-
-		if (!pan_date_compare(fd->date, tc, PAN_DATE_LENGTH_DAY))
-			{
-			GList *needle;
-			gchar *buf;
-
-			if (!pan_date_compare(fd->date, tc, PAN_DATE_LENGTH_MONTH))
-				{
-				pi_day = NULL;
-
-				if (pi_month)
-					{
-					x = pi_month->x + pi_month->width + PAN_BOX_BORDER;
-					}
-				else
-					{
-					x = PAN_BOX_BORDER;
-					}
-
-				y = PAN_BOX_BORDER;
-
-				buf = pan_date_value_string(fd->date, PAN_DATE_LENGTH_MONTH);
-				pi = pan_item_text_new(pw, x, y, buf,
-						       PAN_TEXT_ATTR_BOLD | PAN_TEXT_ATTR_HEADING,
-						       PAN_TEXT_BORDER_SIZE,
-						       PAN_TEXT_COLOR, 255);
-				g_free(buf);
-				y += pi->height;
-
-				pi_month = pan_item_box_new(pw, file_data_ref(fd),
-							    x, y, 0, 0,
-							    PAN_BOX_OUTLINE_THICKNESS,
-							    PAN_BOX_COLOR, PAN_BOX_ALPHA,
-							    PAN_BOX_OUTLINE_COLOR, PAN_BOX_OUTLINE_ALPHA);
-
-				x += PAN_BOX_BORDER;
-				y += PAN_BOX_BORDER;
-				month_start = y;
-				}
-
-			if (pi_day) x = pi_day->x + pi_day->width + PAN_BOX_BORDER;
-
-			tc = fd->date;
-			total = 1;
-			count = 0;
-
-			needle = work;
-			while (needle)
-				{
-				FileData *nfd;
-
-				nfd = needle->data;
-				if (pan_date_compare(nfd->date, tc, PAN_DATE_LENGTH_DAY))
-					{
-					needle = needle->next;
-					total++;
-					}
-				else
-					{
-					needle = NULL;
-					}
-				}
-
-			buf = pan_date_value_string(fd->date, PAN_DATE_LENGTH_WEEK);
-			pi = pan_item_text_new(pw, x, y, buf, PAN_TEXT_ATTR_NONE,
-					       PAN_TEXT_BORDER_SIZE,
-					       PAN_TEXT_COLOR, 255);
-			g_free(buf);
-
-			y += pi->height;
-
-			pi_day = pan_item_box_new(pw, file_data_ref(fd), x, y, 0, 0,
-						  PAN_BOX_OUTLINE_THICKNESS,
-						  PAN_BOX_COLOR, PAN_BOX_ALPHA,
-						  PAN_BOX_OUTLINE_COLOR, PAN_BOX_OUTLINE_ALPHA);
-
-			x += PAN_BOX_BORDER;
-			y += PAN_BOX_BORDER;
-			day_start = y;
-			}
-
-		if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE)
-			{
-			pi = pan_item_image_new(pw, fd, x, y, 10, 10);
-			if (pi->width > x_width) x_width = pi->width;
-			y_height = pi->height;
-			}
-		else
-			{
-			pi = pan_item_thumb_new(pw, fd, x, y);
-			x_width = PAN_THUMB_SIZE;
-			y_height = PAN_THUMB_SIZE;
-			}
-
-		pan_item_size_by_item(pi_day, pi, PAN_BOX_BORDER);
-		pan_item_size_by_item(pi_month, pi_day, PAN_BOX_BORDER);
-
-		total--;
-		count++;
-
-		if (total > 0 && count < PAN_GROUP_MAX)
-			{
-			y += y_height + PAN_THUMB_GAP;
-			}
-		else
-			{
-			x += x_width + PAN_THUMB_GAP;
-			x_width = 0;
-			count = 0;
-
-			if (total > 0)
-				y = day_start;
-			else
-				y = month_start;
-			}
-
-		pan_item_size_coordinates(pi_month, PAN_BOX_BORDER, width, height);
-		}
-
-	g_list_free(list);
-}
-/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/pan-types.h	Tue Dec 20 21:36:52 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,368 +0,0 @@
-/*
- * Copyright (C) 2006 John Ellis
- * Copyright (C) 2008 - 2016 The Geeqie Team
- *
- * Author: John Ellis
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef PAN_TYPES_H
-#define PAN_TYPES_H
-
-#include "cache.h"
-#include "cache-loader.h"
-#include "filedata.h"
-#include "image.h"
-#include "image-load.h"
-#include "pixbuf_util.h"
-#include "pixbuf-renderer.h"
-#include "ui_misc.h"
-
-
-/* thumbnail sizes and spacing */
-
-#define PAN_THUMB_SIZE_DOTS 4
-#define PAN_THUMB_SIZE_NONE 24
-#define PAN_THUMB_SIZE_SMALL 64
-#define PAN_THUMB_SIZE_NORMAL 128
-#define PAN_THUMB_SIZE_LARGE 256
-#define PAN_THUMB_SIZE pw->thumb_size
-
-#define PAN_THUMB_GAP_DOTS 2
-#define PAN_THUMB_GAP_SMALL 14
-#define PAN_THUMB_GAP_NORMAL 30
-#define PAN_THUMB_GAP_LARGE 40
-#define PAN_THUMB_GAP_HUGE 50
-#define PAN_THUMB_GAP pw->thumb_gap
-
-/* basic sizes, colors, spacings */
-
-#define PAN_SHADOW_OFFSET 6
-#define PAN_SHADOW_FADE 5
-#define PAN_SHADOW_COLOR 0, 0, 0
-#define PAN_SHADOW_ALPHA 64
-
-#define PAN_OUTLINE_THICKNESS 1
-#define PAN_OUTLINE_COLOR_1 255, 255, 255
-#define PAN_OUTLINE_COLOR_2 64, 64, 64
-#define PAN_OUTLINE_ALPHA 180
-
-#define PAN_BACKGROUND_COLOR 150, 150, 150
-
-#define PAN_GRID_SIZE 60
-#define PAN_GRID_COLOR 0, 0, 0
-#define PAN_GRID_ALPHA 20
-
-#define PAN_BOX_COLOR 255, 255, 255
-#define PAN_BOX_ALPHA 100
-#define PAN_BOX_BORDER 20
-
-#define PAN_BOX_OUTLINE_THICKNESS 4
-#define PAN_BOX_OUTLINE_COLOR 0, 0, 0
-#define PAN_BOX_OUTLINE_ALPHA 128
-
-#define PAN_TEXT_BORDER_SIZE 4
-#define PAN_TEXT_COLOR 0, 0, 0
-
-/* popup info box */
-
-#define PAN_POPUP_COLOR 255, 255, 225
-#define PAN_POPUP_ALPHA 255
-#define PAN_POPUP_BORDER 1
-#define PAN_POPUP_BORDER_COLOR 0, 0, 0
-#define PAN_POPUP_TEXT_COLOR 0, 0, 0
-
-
-#define PAN_GROUP_MAX 16
-
-
-
-typedef enum {
-	PAN_LAYOUT_TIMELINE = 0,
-	PAN_LAYOUT_CALENDAR,
-	PAN_LAYOUT_FOLDERS_LINEAR,
-	PAN_LAYOUT_FOLDERS_FLOWER,
-	PAN_LAYOUT_GRID,
-	PAN_LAYOUT_COUNT
-} PanLayoutType;
-
-typedef enum {
-	PAN_IMAGE_SIZE_THUMB_DOTS = 0,
-	PAN_IMAGE_SIZE_THUMB_NONE,
-	PAN_IMAGE_SIZE_THUMB_SMALL,
-	PAN_IMAGE_SIZE_THUMB_NORMAL,
-	PAN_IMAGE_SIZE_THUMB_LARGE,
-	PAN_IMAGE_SIZE_10,
-	PAN_IMAGE_SIZE_25,
-	PAN_IMAGE_SIZE_33,
-	PAN_IMAGE_SIZE_50,
-	PAN_IMAGE_SIZE_100,
-	PAN_IMAGE_SIZE_COUNT
-} PanImageSize;
-
-typedef enum {
-	PAN_ITEM_NONE,
-	PAN_ITEM_THUMB,
-	PAN_ITEM_BOX,
-	PAN_ITEM_TRIANGLE,
-	PAN_ITEM_TEXT,
-	PAN_ITEM_IMAGE
-} PanItemType;
-
-typedef enum {
-	PAN_TEXT_ATTR_NONE = 0,
-	PAN_TEXT_ATTR_BOLD = 1 << 0,
-	PAN_TEXT_ATTR_HEADING = 1 << 1,
-	PAN_TEXT_ATTR_MARKUP = 1 << 2
-} PanTextAttrType;
-
-typedef enum {
-	PAN_BORDER_NONE = 0,
-	PAN_BORDER_1 = 1 << 0,
-	PAN_BORDER_2 = 1 << 1,
-	PAN_BORDER_3 = 1 << 2,
-	PAN_BORDER_4 = 1 << 3
-} PanBorderType;
-
-#define PAN_BORDER_TOP		PAN_BORDER_1
-#define PAN_BORDER_RIGHT		PAN_BORDER_2
-#define PAN_BORDER_BOTTOM	PAN_BORDER_3
-#define PAN_BORDER_LEFT		PAN_BORDER_4
-
-
-typedef struct _PanItem PanItem;
-struct _PanItem {
-	PanItemType type;
-	gint x;
-	gint y;
-	gint width;
-	gint height;
-	gchar *key;
-
-	FileData *fd;
-
-	GdkPixbuf *pixbuf;
-	gint refcount;
-
-	gchar *text;
-	PanTextAttrType text_attr;
-
-	guint8 color_r;
-	guint8 color_g;
-	guint8 color_b;
-	guint8 color_a;
-
-	guint8 color2_r;
-	guint8 color2_g;
-	guint8 color2_b;
-	guint8 color2_a;
-	gint border;
-
-	gpointer data;
-
-	gboolean queued;
-};
-
-typedef struct _PanWindow PanWindow;
-struct _PanWindow
-{
-	GtkWidget *window;
-	ImageWindow *imd;
-	ImageWindow *imd_normal;
-	FullScreenData *fs;
-
-	GtkWidget *path_entry;
-
-	GtkWidget *label_message;
-	GtkWidget *label_zoom;
-
-	GtkWidget *search_box;
-	GtkWidget *search_entry;
-	GtkWidget *search_label;
-	GtkWidget *search_button;
-	GtkWidget *search_button_arrow;
-
-	GtkWidget *date_button;
-
-	GtkWidget *scrollbar_h;
-	GtkWidget *scrollbar_v;
-
-	FileData *dir_fd;
-	PanLayoutType layout;
-	PanImageSize size;
-	gint thumb_size;
-	gint thumb_gap;
-	gint image_size;
-	gboolean exif_date_enable;
-
-	gint info_image_size;
-	gboolean info_includes_exif;
-
-	gboolean ignore_symlinks;
-
-	GList *list;
-	GList *list_static;
-	GList *list_grid;
-
-	GList *cache_list;
-	GList *cache_todo;
-	gint cache_count;
-	gint cache_total;
-	gint cache_tick;
-	CacheLoader *cache_cl;
-
-	ImageLoader *il;
-	ThumbLoader *tl;
-	PanItem *queue_pi;
-	GList *queue;
-
-	PanItem *click_pi;
-	PanItem *search_pi;
-
-	gint idle_id;
-};
-
-typedef struct _PanGrid PanGrid;
-struct _PanGrid {
-	gint x;
-	gint y;
-	gint w;
-	gint h;
-	GList *list;
-};
-
-typedef struct _PanCacheData PanCacheData;
-struct _PanCacheData {
-	FileData *fd;
-	CacheData *cd;
-};
-
-
-/* pan-view.c */
-
-GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
-void pan_layout_resize(PanWindow *pw);
-
-void pan_cache_sync_date(PanWindow *pw, GList *list);
-
-GList *pan_cache_sort(GList *list, SortType method, gboolean ascend);
-/* pan-item.c */
-
-void pan_item_free(PanItem *pi);
-
-void pan_item_set_key(PanItem *pi, const gchar *key);
-void pan_item_added(PanWindow *pw, PanItem *pi);
-void pan_item_remove(PanWindow *pw, PanItem *pi);
-
-void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border);
-void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h);
-
-
-PanItem *pan_item_find_by_key(PanWindow *pw, PanItemType type, const gchar *key);
-GList *pan_item_find_by_path(PanWindow *pw, PanItemType type, const gchar *path,
-			     gboolean ignore_case, gboolean partial);
-GList *pan_item_find_by_fd(PanWindow *pw, PanItemType type, FileData *fd,
-			     gboolean ignore_case, gboolean partial);
-PanItem *pan_item_find_by_coord(PanWindow *pw, PanItemType type,
-				gint x, gint y, const gchar *key);
-
-
-PanItem *pan_item_box_new(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
-			  gint border_size,
-			  guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
-			  guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a);
-void pan_item_box_shadow(PanItem *pi, gint offset, gint fade);
-gint pan_item_box_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
-		       gint x, gint y, gint width, gint height);
-
-PanItem *pan_item_tri_new(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
-			  gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
-			  guint8 r, guint8 g, guint8 b, guint8 a);
-void pan_item_tri_border(PanItem *pi, gint borders,
-			 guint8 r, guint8 g, guint8 b, guint8 a);
-gint pan_item_tri_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
-		       gint x, gint y, gint width, gint height);
-
-PanItem *pan_item_text_new(PanWindow *pw, gint x, gint y, const gchar *text,
-			   PanTextAttrType attr, PanBorderType border,
-			   guint8 r, guint8 g, guint8 b, guint8 a);
-gint pan_item_text_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
-			gint x, gint y, gint width, gint height);
-
-PanItem *pan_item_thumb_new(PanWindow *pw, FileData *fd, gint x, gint y);
-gint pan_item_thumb_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
-			 gint x, gint y, gint width, gint height);
-
-PanItem *pan_item_image_new(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h);
-gint pan_item_image_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
-			 gint x, gint y, gint width, gint height);
-
-
-typedef struct _PanTextAlignment PanTextAlignment;
-struct _PanTextAlignment {
-	PanWindow *pw;
-
-	GList *column1;
-	GList *column2;
-
-	gint x;
-	gint y;
-	gchar *key;
-};
-
-PanTextAlignment *pan_text_alignment_new(PanWindow *pw, gint x, gint y, const gchar *key);
-void pan_text_alignment_free(PanTextAlignment *ta);
-
-PanItem *pan_text_alignment_add(PanTextAlignment *ta, const gchar *label, const gchar *text);
-void pan_text_alignment_calc(PanTextAlignment *ta, PanItem *box);
-
-
-/* utils in pan-util.c */
-
-typedef enum {
-	PAN_DATE_LENGTH_EXACT,
-	PAN_DATE_LENGTH_HOUR,
-	PAN_DATE_LENGTH_DAY,
-	PAN_DATE_LENGTH_WEEK,
-	PAN_DATE_LENGTH_MONTH,
-	PAN_DATE_LENGTH_YEAR
-} PanDateLengthType;
-
-gboolean pan_date_compare(time_t a, time_t b, PanDateLengthType length);
-gint pan_date_value(time_t d, PanDateLengthType length);
-gchar *pan_date_value_string(time_t d,  PanDateLengthType length);
-time_t pan_date_to_time(gint year, gint month, gint day);
-
-gboolean pan_is_link_loop(const gchar *s);
-gboolean pan_is_ignored(const gchar *s, gboolean ignore_symlinks);
-GList *pan_list_tree(FileData *dir_fd, SortType sort, gboolean ascend,
-		     gboolean ignore_symlinks);
-
-
-/* the different view types */
-
-void pan_calendar_update(PanWindow *pw, PanItem *pi_day);
-void pan_calendar_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height);
-void pan_flower_compute(PanWindow *pw, FileData *dir_fd,
-			gint *width, gint *height,
-			gint *scroll_x, gint *scroll_y);
-void pan_folder_tree_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height);
-void pan_grid_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height);
-void pan_timeline_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height);
-
-
-#endif
-/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/pan-util.c	Tue Dec 20 21:36:52 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2006 John Ellis
- * Copyright (C) 2008 - 2016 The Geeqie Team
- *
- * Author: John Ellis
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "main.h"
-#include "pan-types.h"
-
-#include "ui_fileops.h"
-
-
-/*
- *-----------------------------------------------------------------------------
- * date functions
- *-----------------------------------------------------------------------------
- */
-
-gboolean pan_date_compare(time_t a, time_t b, PanDateLengthType length)
-{
-	struct tm ta;
-	struct tm tb;
-
-	if (length == PAN_DATE_LENGTH_EXACT) return (a == b);
-
-	if (!localtime_r(&a, &ta) ||
-	    !localtime_r(&b, &tb)) return FALSE;
-
-	if (ta.tm_year != tb.tm_year) return FALSE;
-	if (length == PAN_DATE_LENGTH_YEAR) return TRUE;
-
-	if (ta.tm_mon != tb.tm_mon) return FALSE;
-	if (length == PAN_DATE_LENGTH_MONTH) return TRUE;
-
-	if (length == PAN_DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
-
-	if (ta.tm_mday != tb.tm_mday) return FALSE;
-	if (length == PAN_DATE_LENGTH_DAY) return TRUE;
-
-	return (ta.tm_hour == tb.tm_hour);
-}
-
-gint pan_date_value(time_t d, PanDateLengthType length)
-{
-	struct tm td;
-
-	if (!localtime_r(&d, &td)) return -1;
-
-	switch (length)
-		{
-		case PAN_DATE_LENGTH_DAY:
-			return td.tm_mday;
-			break;
-		case PAN_DATE_LENGTH_WEEK:
-			return td.tm_wday;
-			break;
-		case PAN_DATE_LENGTH_MONTH:
-			return td.tm_mon + 1;
-			break;
-		case PAN_DATE_LENGTH_YEAR:
-			return td.tm_year + 1900;
-			break;
-		case PAN_DATE_LENGTH_EXACT:
-		default:
-			break;
-		}
-
-	return -1;
-}
-
-gchar *pan_date_value_string(time_t d, PanDateLengthType length)
-{
-	struct tm td;
-	gchar buf[128];
-	gchar *format = NULL;
-
-	if (!localtime_r(&d, &td)) return g_strdup("");
-
-	switch (length)
-		{
-		case PAN_DATE_LENGTH_DAY:
-			return g_strdup_printf("%d", td.tm_mday);
-			break;
-		case PAN_DATE_LENGTH_WEEK:
-			format = "%A %e";
-			break;
-		case PAN_DATE_LENGTH_MONTH:
-			format = "%B %Y";
-			break;
-		case PAN_DATE_LENGTH_YEAR:
-			return g_strdup_printf("%d", td.tm_year + 1900);
-			break;
-		case PAN_DATE_LENGTH_EXACT:
-		default:
-			return g_strdup(text_from_time(d));
-			break;
-		}
-
-
-	if (format && strftime(buf, sizeof(buf), format, &td) > 0)
-		{
-		gchar *ret = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
-		if (ret) return ret;
-		}
-
-	return g_strdup("");
-}
-
-time_t pan_date_to_time(gint year, gint month, gint day)
-{
-	struct tm lt;
-
-	lt.tm_sec = 0;
-	lt.tm_min = 0;
-	lt.tm_hour = 0;
-	lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
-	lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
-	lt.tm_year = year - 1900;
-	lt.tm_isdst = 0;
-
-	return mktime(&lt);
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * folder validation
- *-----------------------------------------------------------------------------
- */
-
-gboolean pan_is_link_loop(const gchar *s)
-{
-	gchar *sl;
-	struct stat st;
-	gboolean ret = FALSE;
-
-	sl = path_from_utf8(s);
-
-	if (lstat(sl, &st) == 0 && S_ISLNK(st.st_mode))
-		{
-		gchar *buf;
-		gint l;
-
-		buf = g_malloc(st.st_size + 1);
-		l = readlink(sl, buf, st.st_size);
-		if (l == st.st_size)
-			{
-			buf[l] = '\0';
-
-			parse_out_relatives(buf);
-			l = strlen(buf);
-
-			parse_out_relatives(sl);
-
-			if (buf[0] == G_DIR_SEPARATOR)
-				{
-				if (strncmp(sl, buf, l) == 0 &&
-				    (sl[l] == '\0' || sl[l] == G_DIR_SEPARATOR || l == 1)) ret = TRUE;
-				}
-			else
-				{
-				gchar *link_path;
-
-				link_path = g_build_filename(sl, buf, NULL);
-				parse_out_relatives(link_path);
-
-				if (strncmp(sl, link_path, l) == 0 &&
-				    (sl[l] == '\0' || sl[l] == G_DIR_SEPARATOR || l == 1)) ret = TRUE;
-
-				g_free(link_path);
-				}
-			}
-
-		g_free(buf);
-		}
-
-	g_free(sl);
-
-	return ret;
-}
-
-gboolean pan_is_ignored(const gchar *s, gboolean ignore_symlinks)
-{
-	struct stat st;
-	const gchar *n;
-
-	if (!lstat_utf8(s, &st)) return TRUE;
-
-#if 0
-	/* normal filesystems have directories with some size or block allocation,
-	 * special filesystems (like linux /proc) set both to zero.
-	 * enable this check if you enable listing the root "/" folder
-	 */
-	if (st.st_size == 0 && st.st_blocks == 0) return TRUE;
-#endif
-
-	if (S_ISLNK(st.st_mode) && (ignore_symlinks || pan_is_link_loop(s))) return TRUE;
-
-	n = filename_from_path(s);
-	if (n && strcmp(n, GQ_RC_DIR) == 0) return TRUE;
-
-	return FALSE;
-}
-
-GList *pan_list_tree(FileData *dir_fd, SortType sort, gboolean ascend,
-		     gboolean ignore_symlinks)
-{
-	GList *flist;
-	GList *dlist;
-	GList *result;
-	GList *folders;
-
-	filelist_read(dir_fd, &flist, &dlist);
-	if (sort != SORT_NONE)
-		{
-		flist = filelist_sort(flist, sort, ascend);
-		dlist = filelist_sort(dlist, sort, ascend);
-		}
-
-	result = flist;
-	folders = dlist;
-	while (folders)
-		{
-		FileData *fd;
-
-		fd = folders->data;
-		folders = g_list_remove(folders, fd);
-
-		if (!pan_is_ignored(fd->path, ignore_symlinks) &&
-		    filelist_read(fd, &flist, &dlist))
-			{
-			if (sort != SORT_NONE)
-				{
-				flist = filelist_sort(flist, sort, ascend);
-				dlist = filelist_sort(dlist, sort, ascend);
-				}
-
-			result = g_list_concat(result, flist);
-			folders = g_list_concat(dlist, folders);
-			}
-
-		file_data_unref(fd);
-		}
-
-	return result;
-}
-/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/pan-view.c	Tue Dec 20 21:36:52 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2771 +0,0 @@
-/*
- * Copyright (C) 2006 John Ellis
- * Copyright (C) 2008 - 2016 The Geeqie Team
- *
- * Author: John Ellis
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "main.h"
-#include "pan-view.h"
-
-#include "bar_exif.h"
-#include "dnd.h"
-#include "editors.h"
-#include "exif.h"
-#include "metadata.h"
-#include "fullscreen.h"
-#include "history_list.h"
-#include "img-view.h"
-#include "menu.h"
-#include "misc.h"
-#include "pan-types.h"
-#include "thumb.h"
-#include "ui_fileops.h"
-#include "ui_menu.h"
-#include "ui_tabcomp.h"
-#include "ui_utildlg.h"
-#include "uri_utils.h"
-#include "utilops.h"
-#include "window.h"
-
-#include <gdk/gdkkeysyms.h> /* for keyboard values */
-
-#include <math.h>
-
-
-#define PAN_WINDOW_DEFAULT_WIDTH 720
-#define PAN_WINDOW_DEFAULT_HEIGHT 500
-
-#define PAN_TILE_SIZE 512
-
-#define ZOOM_INCREMENT 1.0
-#define ZOOM_LABEL_WIDTH 64
-
-
-#define PAN_PREF_GROUP		"pan_view_options"
-#define PAN_PREF_HIDE_WARNING	"hide_performance_warning"
-#define PAN_PREF_EXIF_PAN_DATE	"use_exif_date"
-#define PAN_PREF_INFO_IMAGE	"info_image_size"
-#define PAN_PREF_INFO_EXIF	"info_includes_exif"
-
-
-static GList *pan_window_list = NULL;
-
-
-static void pan_layout_update_idle(PanWindow *pw);
-
-static void pan_fullscreen_toggle(PanWindow *pw, gboolean force_off);
-
-static void pan_search_toggle_visible(PanWindow *pw, gboolean enable);
-static void pan_search_activate(PanWindow *pw);
-
-static void pan_window_close(PanWindow *pw);
-
-static GtkWidget *pan_popup_menu(PanWindow *pw);
-
-static void pan_window_dnd_init(PanWindow *pw);
-
-
-/*
- *-----------------------------------------------------------------------------
- * the image/thumb loader queue
- *-----------------------------------------------------------------------------
- */
-
-static gboolean pan_queue_step(PanWindow *pw);
-
-
-static void pan_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
-{
-	PanWindow *pw = data;
-
-	if (pw->queue_pi)
-		{
-		PanItem *pi;
-		gint rc;
-
-		pi = pw->queue_pi;
-		pw->queue_pi = NULL;
-
-		pi->queued = FALSE;
-
-		if (pi->pixbuf) g_object_unref(pi->pixbuf);
-		pi->pixbuf = thumb_loader_get_pixbuf(tl);
-
-		rc = pi->refcount;
-		image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
-		pi->refcount = rc;
-		}
-
-	thumb_loader_free(pw->tl);
-	pw->tl = NULL;
-
-	while (pan_queue_step(pw));
-}
-
-static void pan_queue_image_done_cb(ImageLoader *il, gpointer data)
-{
-	PanWindow *pw = data;
-
-	if (pw->queue_pi)
-		{
-		PanItem *pi;
-		gint rc;
-
-		pi = pw->queue_pi;
-		pw->queue_pi = NULL;
-
-		pi->queued = FALSE;
-
-		if (pi->pixbuf) g_object_unref(pi->pixbuf);
-		pi->pixbuf = image_loader_get_pixbuf(pw->il);
-		if (pi->pixbuf) g_object_ref(pi->pixbuf);
-
-		if (pi->pixbuf && pw->size != PAN_IMAGE_SIZE_100 &&
-		    (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
-		     gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
-			{
-			GdkPixbuf *tmp;
-
-			tmp = pi->pixbuf;
-			pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
-							     (GdkInterpType)options->image.zoom_quality);
-			g_object_unref(tmp);
-			}
-
-		rc = pi->refcount;
-		image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
-		pi->refcount = rc;
-		}
-
-	image_loader_free(pw->il);
-	pw->il = NULL;
-
-	while (pan_queue_step(pw));
-}
-
-static gboolean pan_queue_step(PanWindow *pw)
-{
-	PanItem *pi;
-
-	if (!pw->queue) return FALSE;
-
-	pi = pw->queue->data;
-	pw->queue = g_list_remove(pw->queue, pi);
-	pw->queue_pi = pi;
-
-	if (!pw->queue_pi->fd)
-		{
-		pw->queue_pi->queued = FALSE;
-		pw->queue_pi = NULL;
-		return TRUE;
-		}
-
-	image_loader_free(pw->il);
-	pw->il = NULL;
-	thumb_loader_free(pw->tl);
-	pw->tl = NULL;
-
-	if (pi->type == PAN_ITEM_IMAGE)
-		{
-		pw->il = image_loader_new(pi->fd);
-
-		if (pw->size != PAN_IMAGE_SIZE_100)
-			{
-			image_loader_set_requested_size(pw->il, pi->width, pi->height);
-			}
-
-		g_signal_connect(G_OBJECT(pw->il), "error", (GCallback)pan_queue_image_done_cb, pw);
-		g_signal_connect(G_OBJECT(pw->il), "done", (GCallback)pan_queue_image_done_cb, pw);
-
-		if (image_loader_start(pw->il)) return FALSE;
-
-		image_loader_free(pw->il);
-		pw->il = NULL;
-		}
-	else if (pi->type == PAN_ITEM_THUMB)
-		{
-		pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
-
-		if (!pw->tl->standard_loader)
-			{
-			/* The classic loader will recreate a thumbnail any time we
-			 * request a different size than what exists. This view will
-			 * almost never use the user configured sizes so disable cache.
-			 */
-			thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
-			}
-
-		thumb_loader_set_callbacks(pw->tl,
-					   pan_queue_thumb_done_cb,
-					   pan_queue_thumb_done_cb,
-					   NULL, pw);
-
-		if (thumb_loader_start(pw->tl, pi->fd)) return FALSE;
-
-		thumb_loader_free(pw->tl);
-		pw->tl = NULL;
-		}
-
-	pw->queue_pi->queued = FALSE;
-	pw->queue_pi = NULL;
-	return TRUE;
-}
-
-static void pan_queue_add(PanWindow *pw, PanItem *pi)
-{
-	if (!pi || pi->queued || pi->pixbuf) return;
-	if (pw->size <= PAN_IMAGE_SIZE_THUMB_NONE &&
-	    (!pi->key || strcmp(pi->key, "info") != 0) )
-		{
-		return;
-		}
-
-	pi->queued = TRUE;
-	pw->queue = g_list_prepend(pw->queue, pi);
-
-	if (!pw->tl && !pw->il) while (pan_queue_step(pw));
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * tile request/dispose handlers
- *-----------------------------------------------------------------------------
- */
-
-static gboolean pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
-				       	   gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
-{
-	PanWindow *pw = data;
-	GList *list;
-	GList *work;
-	gint i;
-
-	pixbuf_set_rect_fill(pixbuf,
-			     0, 0, width, height,
-			     PAN_BACKGROUND_COLOR, 255);
-
-	for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
-		{
-		gint rx, ry, rw, rh;
-
-		if (util_clip_region(x, y, width, height,
-				     i, y, 1, height,
-				     &rx, &ry, &rw, &rh))
-			{
-			pixbuf_draw_rect_fill(pixbuf,
-					      rx - x, ry - y, rw, rh,
-					      PAN_GRID_COLOR, PAN_GRID_ALPHA);
-			}
-		}
-	for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
-		{
-		gint rx, ry, rw, rh;
-
-		if (util_clip_region(x, y, width, height,
-				     x, i, width, 1,
-				     &rx, &ry, &rw, &rh))
-			{
-			pixbuf_draw_rect_fill(pixbuf,
-					      rx - x, ry - y, rw, rh,
-					      PAN_GRID_COLOR, PAN_GRID_ALPHA);
-			}
-		}
-
-	list = pan_layout_intersect(pw, x, y, width, height);
-	work = list;
-	while (work)
-		{
-		PanItem *pi;
-		gboolean queue = FALSE;
-
-		pi = work->data;
-		work = work->next;
-
-		pi->refcount++;
-
-		switch (pi->type)
-			{
-			case PAN_ITEM_BOX:
-				queue = pan_item_box_draw(pw, pi, pixbuf, pr, x, y, width, height);
-				break;
-			case PAN_ITEM_TRIANGLE:
-				queue = pan_item_tri_draw(pw, pi, pixbuf, pr, x, y, width, height);
-				break;
-			case PAN_ITEM_TEXT:
-				queue = pan_item_text_draw(pw, pi, pixbuf, pr, x, y, width, height);
-				break;
-			case PAN_ITEM_THUMB:
-				queue = pan_item_thumb_draw(pw, pi, pixbuf, pr, x, y, width, height);
-				break;
-			case PAN_ITEM_IMAGE:
-				queue = pan_item_image_draw(pw, pi, pixbuf, pr, x, y, width, height);
-				break;
-			case PAN_ITEM_NONE:
-			default:
-				break;
-			}
-
-		if (queue) pan_queue_add(pw, pi);
-		}
-
-	g_list_free(list);
-
-	return TRUE;
-}
-
-static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
-				       gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
-{
-	PanWindow *pw = data;
-	GList *list;
-	GList *work;
-
-	list = pan_layout_intersect(pw, x, y, width, height);
-	work = list;
-	while (work)
-		{
-		PanItem *pi;
-
-		pi = work->data;
-		work = work->next;
-
-		if (pi->refcount > 0)
-			{
-			pi->refcount--;
-
-			if (pi->refcount == 0)
-				{
-				if (pi->queued)
-					{
-					pw->queue = g_list_remove(pw->queue, pi);
-					pi->queued = FALSE;
-					}
-				if (pw->queue_pi == pi) pw->queue_pi = NULL;
-				if (pi->pixbuf)
-					{
-					g_object_unref(pi->pixbuf);
-					pi->pixbuf = NULL;
-					}
-				}
-			}
-		}
-
-	g_list_free(list);
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * misc
- *-----------------------------------------------------------------------------
- */
-
-static void pan_window_message(PanWindow *pw, const gchar *text)
-{
-	GList *work;
-	gint count = 0;
-	gint64 size = 0;
-	gchar *ss;
-	gchar *buf;
-
-	if (text)
-		{
-		gtk_label_set_text(GTK_LABEL(pw->label_message), text);
-		return;
-		}
-
-	work = pw->list_static;
-	if (pw->layout == PAN_LAYOUT_CALENDAR)
-		{
-		while (work)
-			{
-			PanItem *pi;
-
-			pi = work->data;
-			work = work->next;
-
-			if (pi->fd &&
-			    pi->type == PAN_ITEM_BOX &&
-			    pi->key && strcmp(pi->key, "dot") == 0)
-				{
-				size += pi->fd->size;
-				count++;
-				}
-			}
-		}
-	else
-		{
-		while (work)
-			{
-			PanItem *pi;
-
-			pi = work->data;
-			work = work->next;
-
-			if (pi->fd &&
-			    (pi->type == PAN_ITEM_THUMB || pi->type == PAN_ITEM_IMAGE))
-				{
-				size += pi->fd->size;
-				count++;
-				}
-			}
-		}
-
-	ss = text_from_size_abrev(size);
-	buf = g_strdup_printf(_("%d images, %s"), count, ss);
-	g_free(ss);
-	gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
-	g_free(buf);
-}
-
-static void pan_warning_folder(const gchar *path, GtkWidget *parent)
-{
-	gchar *message;
-
-	message = g_strdup_printf(_("The pan view does not support the folder \"%s\"."), path);
-	warning_dialog(_("Folder not supported"), message,
-		      GTK_STOCK_DIALOG_INFO, parent);
-	g_free(message);
-}
-
-static void pan_window_zoom_limit(PanWindow *pw)
-{
-	gdouble min;
-
-	switch (pw->size)
-		{
-		case PAN_IMAGE_SIZE_THUMB_DOTS:
-		case PAN_IMAGE_SIZE_THUMB_NONE:
-		case PAN_IMAGE_SIZE_THUMB_SMALL:
-		case PAN_IMAGE_SIZE_THUMB_NORMAL:
-#if 0
-			/* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
-			min = -16.0;
-			break;
-#endif
-		case PAN_IMAGE_SIZE_THUMB_LARGE:
-			min = -6.0;
-			break;
-		case PAN_IMAGE_SIZE_10:
-		case PAN_IMAGE_SIZE_25:
-			min = -4.0;
-			break;
-		case PAN_IMAGE_SIZE_33:
-		case PAN_IMAGE_SIZE_50:
-		case PAN_IMAGE_SIZE_100:
-		default:
-			min = -2.0;
-			break;
-		}
-
-	image_zoom_set_limits(pw->imd, min, 32.0);
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * cache
- *-----------------------------------------------------------------------------
- */
-
-static gint pan_cache_sort_file_cb(gpointer a, gpointer b)
-{
-	PanCacheData *pca = a;
-	PanCacheData *pcb = b;
-	return filelist_sort_compare_filedata(pca->fd, pcb->fd);
-}
-GList *pan_cache_sort(GList *list, SortType method, gboolean ascend)
-{
-	return filelist_sort_full(list, method, ascend, (GCompareFunc) pan_cache_sort_file_cb);
-}
-
-
-static void pan_cache_free(PanWindow *pw)
-{
-	GList *work;
-
-	work = pw->cache_list;
-	while (work)
-		{
-		PanCacheData *pc;
-
-		pc = work->data;
-		work = work->next;
-
-		cache_sim_data_free(pc->cd);
-		file_data_unref(pc->fd);
-		g_free(pc);
-		}
-
-	g_list_free(pw->cache_list);
-	pw->cache_list = NULL;
-
-	filelist_free(pw->cache_todo);
-	pw->cache_todo = NULL;
-
-	pw->cache_count = 0;
-	pw->cache_total = 0;
-	pw->cache_tick = 0;
-
-	cache_loader_free(pw->cache_cl);
-	pw->cache_cl = NULL;
-}
-
-static void pan_cache_fill(PanWindow *pw, FileData *dir_fd)
-{
-	GList *list;
-
-	pan_cache_free(pw);
-
-	list = pan_list_tree(dir_fd, SORT_NAME, TRUE, pw->ignore_symlinks);
-	pw->cache_todo = g_list_reverse(list);
-
-	pw->cache_total = g_list_length(pw->cache_todo);
-}
-
-static void pan_cache_step_done_cb(CacheLoader *cl, gint error, gpointer data)
-{
-	PanWindow *pw = data;
-
-	if (pw->cache_list)
-		{
-		PanCacheData *pc;
-		pc = pw->cache_list->data;
-
-		if (!pc->cd)
-			{
-			pc->cd = cl->cd;
-			cl->cd = NULL;
-			}
-		}
-
-	cache_loader_free(cl);
-	pw->cache_cl = NULL;
-
-	pan_layout_update_idle(pw);
-}
-
-static gboolean pan_cache_step(PanWindow *pw)
-{
-	FileData *fd;
-	PanCacheData *pc;
-	CacheDataType load_mask;
-
-	if (!pw->cache_todo) return TRUE;
-
-	fd = pw->cache_todo->data;
-	pw->cache_todo = g_list_remove(pw->cache_todo, fd);
-
-	pc = g_new0(PanCacheData, 1);
-	pc->fd = file_data_ref(fd);
-
-	pc->cd = NULL;
-
-	pw->cache_list = g_list_prepend(pw->cache_list, pc);
-
-	cache_loader_free(pw->cache_cl);
-
-	load_mask = CACHE_LOADER_NONE;
-	if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) load_mask |= CACHE_LOADER_DIMENSIONS;
-	if (pw->exif_date_enable) load_mask |= CACHE_LOADER_DATE;
-	pw->cache_cl = cache_loader_new(pc->fd, load_mask,
-					pan_cache_step_done_cb, pw);
-	return (pw->cache_cl == NULL);
-}
-
-/* This sync date function is optimized for lists with a common sort */
-void pan_cache_sync_date(PanWindow *pw, GList *list)
-{
-	GList *haystack;
-	GList *work;
-
-	haystack = g_list_copy(pw->cache_list);
-
-	work = list;
-	while (work)
-		{
-		FileData *fd;
-		GList *needle;
-
-		fd = work->data;
-		work = work->next;
-
-		needle = haystack;
-		while (needle)
-			{
-			PanCacheData *pc;
-
-			pc = needle->data;
-			if (pc->fd == fd)
-				{
-				if (pc->cd && pc->cd->have_date && pc->cd->date >= 0)
-					{
-					fd->date = pc->cd->date;
-					}
-
-				haystack = g_list_delete_link(haystack, needle);
-				needle = NULL;
-				}
-			else
-				{
-				needle = needle->next;
-				}
-			}
-		}
-
-	g_list_free(haystack);
-}
-
-/*
- *-----------------------------------------------------------------------------
- * item grid
- *-----------------------------------------------------------------------------
- */
-
-static void pan_grid_clear(PanWindow *pw)
-{
-	GList *work;
-
-	work = pw->list_grid;
-	while (work)
-		{
-		PanGrid *pg;
-
-		pg = work->data;
-		work = work->next;
-
-		g_list_free(pg->list);
-		g_free(pg);
-		}
-
-	g_list_free(pw->list_grid);
-	pw->list_grid = NULL;
-
-	pw->list = g_list_concat(pw->list, pw->list_static);
-	pw->list_static = NULL;
-}
-
-static void pan_grid_build(PanWindow *pw, gint width, gint height, gint grid_size)
-{
-	GList *work;
-	gint col, row;
-	gint cw, ch;
-	gint l;
-	gint i, j;
-
-	pan_grid_clear(pw);
-
-	l = g_list_length(pw->list);
-
-	if (l < 1) return;
-
-	col = (gint)(sqrt((gdouble)l / grid_size) * width / height + 0.999);
-	col = CLAMP(col, 1, l / grid_size + 1);
-	row = (gint)((gdouble)l / grid_size / col);
-	if (row < 1) row = 1;
-
-	/* limit minimum size of grid so that a tile will always fit regardless of position */
-	cw = MAX((gint)ceil((gdouble)width / col), PAN_TILE_SIZE * 2);
-	ch = MAX((gint)ceil((gdouble)height / row), PAN_TILE_SIZE * 2);
-
-	row = row * 2 - 1;
-	col = col * 2 - 1;
-
-	DEBUG_1("intersect speedup grid is %dx%d, based on %d average per grid", col, row, grid_size);
-
-	for (j = 0; j < row; j++)
-	    for (i = 0; i < col; i++)
-		{
-		if ((i + 1) * cw / 2 < width && (j + 1) * ch / 2 < height)
-			{
-			PanGrid *pg;
-
-			pg = g_new0(PanGrid, 1);
-			pg->x = i * cw / 2;
-			pg->y = j * ch / 2;
-			pg->w = cw;
-			pg->h = ch;
-
-			pw->list_grid = g_list_prepend(pw->list_grid, pg);
-
-			DEBUG_1("grid section: %d,%d (%dx%d)", pg->x, pg->y, pg->w, pg->h);
-			}
-		}
-
-	work = pw->list;
-	while (work)
-		{
-		PanItem *pi;
-		GList *grid;
-
-		pi = work->data;
-		work = work->next;
-
-		grid = pw->list_grid;
-		while (grid)
-			{
-			PanGrid *pg;
-			gint rx, ry, rw, rh;
-
-			pg = grid->data;
-			grid = grid->next;
-
-			if (util_clip_region(pi->x, pi->y, pi->width, pi->height,
-					     pg->x, pg->y, pg->w, pg->h,
-					     &rx, &ry, &rw, &rh))
-				{
-				pg->list = g_list_prepend(pg->list, pi);
-				}
-			}
-		}
-
-	work = pw->list_grid;
-	while (work)
-		{
-		PanGrid *pg;
-
-		pg = work->data;
-		work = work->next;
-
-		pg->list = g_list_reverse(pg->list);
-		}
-
-	pw->list_static = pw->list;
-	pw->list = NULL;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * layout state reset
- *-----------------------------------------------------------------------------
- */
-
-static void pan_window_items_free(PanWindow *pw)
-{
-	GList *work;
-
-	pan_grid_clear(pw);
-
-	work = pw->list;
-	while (work)
-		{
-		PanItem *pi = work->data;
-		work = work->next;
-
-		pan_item_free(pi);
-		}
-
-	g_list_free(pw->list);
-	pw->list = NULL;
-
-	g_list_free(pw->queue);
-	pw->queue = NULL;
-	pw->queue_pi = NULL;
-
-	image_loader_free(pw->il);
-	pw->il = NULL;
-
-	thumb_loader_free(pw->tl);
-	pw->tl = NULL;
-
-	pw->click_pi = NULL;
-	pw->search_pi = NULL;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * layout generation, queries, sizing
- *-----------------------------------------------------------------------------
- */
-
-static void pan_layout_compute(PanWindow *pw, FileData *dir_fd,
-			       gint *width, gint *height,
-			       gint *scroll_x, gint *scroll_y)
-{
-	pan_window_items_free(pw);
-
-	switch (pw->size)
-		{
-		case PAN_IMAGE_SIZE_THUMB_DOTS:
-			pw->thumb_size = PAN_THUMB_SIZE_DOTS;
-			pw->thumb_gap = PAN_THUMB_GAP_DOTS;
-			break;
-		case PAN_IMAGE_SIZE_THUMB_NONE:
-			pw->thumb_size = PAN_THUMB_SIZE_NONE;
-			pw->thumb_gap = PAN_THUMB_GAP_SMALL;
-			break;
-		case PAN_IMAGE_SIZE_THUMB_SMALL:
-			pw->thumb_size = PAN_THUMB_SIZE_SMALL;
-			pw->thumb_gap = PAN_THUMB_GAP_SMALL;
-			break;
-		case PAN_IMAGE_SIZE_THUMB_NORMAL:
-		default:
-			pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
-			pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
-			break;
-		case PAN_IMAGE_SIZE_THUMB_LARGE:
-			pw->thumb_size = PAN_THUMB_SIZE_LARGE;
-			pw->thumb_gap = PAN_THUMB_GAP_LARGE;
-			break;
-		case PAN_IMAGE_SIZE_10:
-			pw->image_size = 10;
-			pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
-			break;
-		case PAN_IMAGE_SIZE_25:
-			pw->image_size = 25;
-			pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
-			break;
-		case PAN_IMAGE_SIZE_33:
-			pw->image_size = 33;
-			pw->thumb_gap = PAN_THUMB_GAP_LARGE;
-			break;
-		case PAN_IMAGE_SIZE_50:
-			pw->image_size = 50;
-			pw->thumb_gap = PAN_THUMB_GAP_HUGE;
-			break;
-		case PAN_IMAGE_SIZE_100:
-			pw->image_size = 100;
-			pw->thumb_gap = PAN_THUMB_GAP_HUGE;
-			break;
-		}
-
-	*width = 0;
-	*height = 0;
-	*scroll_x = 0;
-	*scroll_y = 0;
-
-	switch (pw->layout)
-		{
-		case PAN_LAYOUT_GRID:
-		default:
-			pan_grid_compute(pw, dir_fd, width, height);
-			break;
-		case PAN_LAYOUT_FOLDERS_LINEAR:
-			pan_folder_tree_compute(pw, dir_fd, width, height);
-			break;
-		case PAN_LAYOUT_FOLDERS_FLOWER:
-			pan_flower_compute(pw, dir_fd, width, height, scroll_x, scroll_y);
-			break;
-		case PAN_LAYOUT_CALENDAR:
-			pan_calendar_compute(pw, dir_fd, width, height);
-			break;
-		case PAN_LAYOUT_TIMELINE:
-			pan_timeline_compute(pw, dir_fd, width, height);
-			break;
-		}
-
-	pan_cache_free(pw);
-
-	DEBUG_1("computed %d objects", g_list_length(pw->list));
-}
-
-static GList *pan_layout_intersect_l(GList *list, GList *item_list,
-				     gint x, gint y, gint width, gint height)
-{
-	GList *work;
-
-	work = item_list;
-	while (work)
-		{
-		PanItem *pi;
-		gint rx, ry, rw, rh;
-
-		pi = work->data;
-		work = work->next;
-
-		if (util_clip_region(x, y, width, height,
-				     pi->x, pi->y, pi->width, pi->height,
-				     &rx, &ry, &rw, &rh))
-			{
-			list = g_list_prepend(list, pi);
-			}
-		}
-
-	return list;
-}
-
-GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
-{
-	GList *list = NULL;
-	GList *grid;
-	PanGrid *pg = NULL;
-
-	grid = pw->list_grid;
-	while (grid && !pg)
-		{
-		pg = grid->data;
-		grid = grid->next;
-
-		if (x < pg->x || x + width > pg->x + pg->w ||
-		    y < pg->y || y + height > pg->y + pg->h)
-			{
-			pg = NULL;
-			}
-		}
-
-	list = pan_layout_intersect_l(list, pw->list, x, y, width, height);
-
-	if (pg)
-		{
-		list = pan_layout_intersect_l(list, pg->list, x, y, width, height);
-		}
-	else
-		{
-		list = pan_layout_intersect_l(list, pw->list_static, x, y, width, height);
-		}
-
-	return list;
-}
-
-void pan_layout_resize(PanWindow *pw)
-{
-	gint width = 0;
-	gint height = 0;
-	GList *work;
-	PixbufRenderer *pr;
-
-	work = pw->list;
-	while (work)
-		{
-		PanItem *pi;
-
-		pi = work->data;
-		work = work->next;
-
-		if (width < pi->x + pi->width) width = pi->x + pi->width;
-		if (height < pi->y + pi->height) height = pi->y + pi->height;
-		}
-	work = pw->list_static;
-	while (work)
-		{
-		PanItem *pi;
-
-		pi = work->data;
-		work = work->next;
-
-		if (width < pi->x + pi->width) width = pi->x + pi->width;
-		if (height < pi->y + pi->height) height = pi->y + pi->height;
-		}
-
-	width += PAN_BOX_BORDER * 2;
-	height += PAN_BOX_BORDER * 2;
-
-	pr = PIXBUF_RENDERER(pw->imd->pr);
-	if (width < pr->window_width) width = pr->window_width;
-	if (height < pr->window_width) height = pr->window_height;
-
-	pixbuf_renderer_set_tiles_size(PIXBUF_RENDERER(pw->imd->pr), width, height);
-}
-
-static gint pan_layout_update_idle_cb(gpointer data)
-{
-	PanWindow *pw = data;
-	gint width;
-	gint height;
-	gint scroll_x;
-	gint scroll_y;
-
-	if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE ||
-	    (pw->exif_date_enable && (pw->layout == PAN_LAYOUT_TIMELINE || pw->layout == PAN_LAYOUT_CALENDAR)))
-		{
-		if (!pw->cache_list && !pw->cache_todo)
-			{
-			pan_cache_fill(pw, pw->dir_fd);
-			if (pw->cache_todo)
-				{
-				pan_window_message(pw, _("Reading image data..."));
-				return TRUE;
-				}
-			}
-		if (pw->cache_todo)
-			{
-			pw->cache_count++;
-			pw->cache_tick++;
-			if (pw->cache_count == pw->cache_total)
-				{
-				pan_window_message(pw, _("Sorting..."));
-				}
-			else if (pw->cache_tick > 9)
-				{
-				gchar *buf;
-
-				buf = g_strdup_printf("%s %d / %d", _("Reading image data..."),
-						      pw->cache_count, pw->cache_total);
-				pan_window_message(pw, buf);
-				g_free(buf);
-
-				pw->cache_tick = 0;
-				}
-
-			if (pan_cache_step(pw)) return TRUE;
-
-			pw->idle_id = 0;
-			return FALSE;
-			}
-		}
-
-	pan_layout_compute(pw, pw->dir_fd, &width, &height, &scroll_x, &scroll_y);
-
-	pan_window_zoom_limit(pw);
-
-	if (width > 0 && height > 0)
-		{
-		gdouble align;
-
-		DEBUG_1("Canvas size is %d x %d", width, height);
-
-		pan_grid_build(pw, width, height, 1000);
-
-		pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
-					  PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
-					  pan_window_request_tile_cb,
-					  pan_window_dispose_tile_cb, pw, 1.0);
-
-		if (scroll_x == 0 && scroll_y == 0)
-			{
-			align = 0.0;
-			}
-		else
-			{
-			align = 0.5;
-			}
-		pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
-		}
-
-	pan_window_message(pw, NULL);
-
-	pw->idle_id = 0;
-	return FALSE;
-}
-
-static void pan_layout_update_idle(PanWindow *pw)
-{
-	if (!pw->idle_id)
-		{
-		pw->idle_id = g_idle_add(pan_layout_update_idle_cb, pw);
-		}
-}
-
-static void pan_layout_update(PanWindow *pw)
-{
-	pan_window_message(pw, _("Sorting images..."));
-	pan_layout_update_idle(pw);
-}
-
-static void pan_layout_set_fd(PanWindow *pw, FileData *dir_fd)
-{
-	if (!dir_fd) return;
-
-	if (strcmp(dir_fd->path, G_DIR_SEPARATOR_S) == 0)
-		{
-		pan_warning_folder(dir_fd->path, pw->window);
-		return;
-		}
-
-	file_data_unref(pw->dir_fd);
-	pw->dir_fd = file_data_ref(dir_fd);
-
-	pan_layout_update(pw);
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * keyboard handlers
- *-----------------------------------------------------------------------------
- */
-
-FileData *pan_menu_click_fd(PanWindow *pw)
-{
-	if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd;
-	return NULL;
-}
-
-static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
-{
-	PanWindow *pw = data;
-
-	gdk_window_get_origin(gtk_widget_get_window(pw->imd->pr), x, y);
-	popup_menu_position_clamp(menu, x, y, 0);
-}
-
-static gboolean pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
-{
-	PanWindow *pw = data;
-	PixbufRenderer *pr;
-	FileData *fd;
-	gboolean stop_signal = FALSE;
-	GtkWidget *menu;
-	GtkWidget *imd_widget;
-	gint x = 0;
-	gint y = 0;
-	gint focused;
-	gint on_entry;
-
-	pr = PIXBUF_RENDERER(pw->imd->pr);
-	fd = pan_menu_click_fd(pw);
-
-	imd_widget = gtk_container_get_focus_child(GTK_CONTAINER(pw->imd->widget));
-	focused = (pw->fs || (imd_widget && gtk_widget_has_focus(imd_widget)));
-	on_entry = (gtk_widget_has_focus(pw->path_entry) ||
-		    gtk_widget_has_focus(pw->search_entry));
-
-	if (focused)
-		{
-		stop_signal = TRUE;
-		switch (event->keyval)
-			{
-			case GDK_KEY_Left: case GDK_KEY_KP_Left:
-				x -= 1;
-				break;
-			case GDK_KEY_Right: case GDK_KEY_KP_Right:
-				x += 1;
-				break;
-			case GDK_KEY_Up: case GDK_KEY_KP_Up:
-				y -= 1;
-				break;
-			case GDK_KEY_Down: case GDK_KEY_KP_Down:
-				y += 1;
-				break;
-			case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up:
-				pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
-				break;
-			case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down:
-				pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
-				break;
-			case GDK_KEY_Home: case GDK_KEY_KP_Home:
-				pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
-				break;
-			case GDK_KEY_End: case GDK_KEY_KP_End:
-				pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
-				break;
-			default:
-				stop_signal = FALSE;
-				break;
-			}
-
-		if (x != 0 || y!= 0)
-			{
-			if (event->state & GDK_SHIFT_MASK)
-				{
-				x *= 3;
-				y *= 3;
-				}
-			keyboard_scroll_calc(&x, &y, event);
-			pixbuf_renderer_scroll(pr, x, y);
-			}
-		}
-
-	if (stop_signal) return stop_signal;
-
-	if (event->state & GDK_CONTROL_MASK)
-		{
-		stop_signal = TRUE;
-		switch (event->keyval)
-			{
-			case '1':
-			case '2':
-			case '3':
-			case '4':
-			case '5':
-			case '6':
-			case '7':
-			case '8':
-			case '9':
-			case '0':
-				break;
-			case 'C': case 'c':
-				if (fd) file_util_copy(fd, NULL, NULL, GTK_WIDGET(pr));
-				break;
-			case 'M': case 'm':
-				if (fd) file_util_move(fd, NULL, NULL, GTK_WIDGET(pr));
-				break;
-			case 'R': case 'r':
-				if (fd) file_util_rename(fd, NULL, GTK_WIDGET(pr));
-				break;
-			case 'D': case 'd':
-				if (fd) file_util_delete(fd, NULL, GTK_WIDGET(pr));
-				break;
-			case 'F': case 'f':
-				pan_search_toggle_visible(pw, TRUE);
-				break;
-			case 'G': case 'g':
-				pan_search_activate(pw);
-				break;
-			case 'W': case 'w':
-				pan_window_close(pw);
-				break;
-			default:
-				stop_signal = FALSE;
-				break;
-			}
-		}
-	else
-		{
-		stop_signal = TRUE;
-		switch (event->keyval)
-			{
-			case GDK_KEY_Escape:
-				if (pw->fs)
-					{
-					pan_fullscreen_toggle(pw, TRUE);
-					}
-				else
-					{
-					pan_search_toggle_visible(pw, FALSE);
-					}
-				break;
-			default:
-				stop_signal = FALSE;
-				break;
-			}
-
-		if (stop_signal) return stop_signal;
-
-		if (!on_entry)
-			{
-			stop_signal = TRUE;
-			switch (event->keyval)
-				{
-				case '+': case '=': case GDK_KEY_KP_Add:
-					pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
-					break;
-				case '-': case GDK_KEY_KP_Subtract:
-					pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
-					break;
-				case 'Z': case 'z': case GDK_KEY_KP_Divide: case '1':
-					pixbuf_renderer_zoom_set(pr, 1.0);
-					break;
-				case '2':
-					pixbuf_renderer_zoom_set(pr, 2.0);
-					break;
-				case '3':
-					pixbuf_renderer_zoom_set(pr, 3.0);
-					break;
-				case '4':
-					pixbuf_renderer_zoom_set(pr, 4.0);
-					break;
-				case '7':
-					pixbuf_renderer_zoom_set(pr, -4.0);
-					break;
-				case '8':
-					pixbuf_renderer_zoom_set(pr, -3.0);
-					break;
-				case '9':
-					pixbuf_renderer_zoom_set(pr, -2.0);
-					break;
-				case 'F': case 'f':
-				case 'V': case 'v':
-				case GDK_KEY_F11:
-					pan_fullscreen_toggle(pw, FALSE);
-					break;
-				case 'I': case 'i':
-					break;
-				case GDK_KEY_Delete: case GDK_KEY_KP_Delete:
-					break;
-				case GDK_KEY_Menu:
-				case GDK_KEY_F10:
-					menu = pan_popup_menu(pw);
-					gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
-						       pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
-					break;
-				case '/':
-					pan_search_toggle_visible(pw, TRUE);
-					break;
-				default:
-					stop_signal = FALSE;
-					break;
-				}
-			}
-		}
-
-	return stop_signal;
-}
-
-/*
- *-----------------------------------------------------------------------------
- * info popup
- *-----------------------------------------------------------------------------
- */
-
-static void pan_info_add_exif(PanTextAlignment *ta, FileData *fd)
-{
-
-	if (!fd) return;
-
-	pan_text_alignment_add(ta, NULL, NULL);
-}
-
-
-static void pan_info_update(PanWindow *pw, PanItem *pi)
-{
-	PanTextAlignment *ta;
-	PanItem *pbox;
-	PanItem *p;
-	gchar *buf;
-	gint x1, y1, x2, y2, x3, y3;
-	gint x, y, w, h;
-
-	if (pw->click_pi == pi) return;
-	if (pi && !pi->fd) pi = NULL;
-
-	while ((p = pan_item_find_by_key(pw, PAN_ITEM_NONE, "info"))) pan_item_remove(pw, p);
-	pw->click_pi = pi;
-
-	if (!pi) return;
-
-	DEBUG_1("info set to %s", pi->fd->path);
-
-	pbox = pan_item_box_new(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
-				PAN_POPUP_BORDER,
-				PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
-				PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
-	pan_item_set_key(pbox, "info");
-
-	if (pi->type == PAN_ITEM_THUMB && pi->pixbuf)
-		{
-		w = gdk_pixbuf_get_width(pi->pixbuf);
-		h = gdk_pixbuf_get_height(pi->pixbuf);
-
-		x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
-		y1 = pi->y + (pi->height - h) / 2 + 8;
-		}
-	else
-		{
-		x1 = pi->x + pi->width - 8;
-		y1 = pi->y + 8;
-		}
-
-	x2 = pbox->x + 1;
-	y2 = pbox->y + 36;
-	x3 = pbox->x + 1;
-	y3 = pbox->y + 12;
-	util_clip_triangle(x1, y1, x2, y2, x3, y3,
-			   &x, &y, &w, &h);
-
-	p = pan_item_tri_new(pw, NULL, x, y, w, h,
-			     x1, y1, x2, y2, x3, y3,
-			     PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
-	pan_item_tri_border(p, PAN_BORDER_1 | PAN_BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
-	pan_item_set_key(p, "info");
-	pan_item_added(pw, p);
-
-	ta = pan_text_alignment_new(pw, pbox->x + PREF_PAD_BORDER, pbox->y + PREF_PAD_BORDER, "info");
-
-	pan_text_alignment_add(ta, _("Filename:"), pi->fd->name);
-	buf = remove_level_from_path(pi->fd->path);
-	pan_text_alignment_add(ta, _("Location:"), buf);
-	g_free(buf);
-	pan_text_alignment_add(ta, _("Date:"), text_from_time(pi->fd->date));
-	buf = text_from_size(pi->fd->size);
-	pan_text_alignment_add(ta, _("Size:"), buf);
-	g_free(buf);
-
-	if (pw->info_includes_exif)
-		{
-		pan_info_add_exif(ta, pi->fd);
-		}
-
-	pan_text_alignment_calc(ta, pbox);
-	pan_text_alignment_free(ta);
-
-	pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
-	pan_item_added(pw, pbox);
-
-	if (pw->info_image_size > PAN_IMAGE_SIZE_THUMB_NONE)
-		{
-		gint iw, ih;
-		if (image_load_dimensions(pi->fd, &iw, &ih))
-			{
-			gint scale = 25;
-
-			switch (pw->info_image_size)
-				{
-				case PAN_IMAGE_SIZE_10:
-					scale = 10;
-					break;
-				case PAN_IMAGE_SIZE_25:
-					scale = 25;
-					break;
-				case PAN_IMAGE_SIZE_33:
-					scale = 33;
-					break;
-				case PAN_IMAGE_SIZE_50:
-					scale = 50;
-					break;
-				case PAN_IMAGE_SIZE_100:
-					scale = 100;
-					break;
-				}
-
-			iw = MAX(1, iw * scale / 100);
-			ih = MAX(1, ih * scale / 100);
-
-			pbox = pan_item_box_new(pw, NULL, pbox->x, pbox->y + pbox->height + 8, 10, 10,
-						PAN_POPUP_BORDER,
-						PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
-						PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
-			pan_item_set_key(pbox, "info");
-
-			p = pan_item_image_new(pw, file_data_new_group(pi->fd->path),
-					       pbox->x + PREF_PAD_BORDER, pbox->y + PREF_PAD_BORDER, iw, ih);
-			pan_item_set_key(p, "info");
-			pan_item_size_by_item(pbox, p, PREF_PAD_BORDER);
-
-			pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
-			pan_item_added(pw, pbox);
-			}
-		}
-
-	pan_layout_resize(pw);
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * search
- *-----------------------------------------------------------------------------
- */
-
-static void pan_search_status(PanWindow *pw, const gchar *text)
-{
-	gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
-}
-
-static gint pan_search_by_path(PanWindow *pw, const gchar *path)
-{
-	PanItem *pi;
-	GList *list;
-	GList *found;
-	PanItemType type;
-	gchar *buf;
-
-	type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
-
-	list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
-	if (!list) return FALSE;
-
-	found = g_list_find(list, pw->click_pi);
-	if (found && found->next)
-		{
-		found = found->next;
-		pi = found->data;
-		}
-	else
-		{
-		pi = list->data;
-		}
-
-	pan_info_update(pw, pi);
-	image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
-
-	buf = g_strdup_printf("%s ( %d / %d )",
-			      (path[0] == G_DIR_SEPARATOR) ? _("path found") : _("filename found"),
-			      g_list_index(list, pi) + 1,
-			      g_list_length(list));
-	pan_search_status(pw, buf);
-	g_free(buf);
-
-	g_list_free(list);
-
-	return TRUE;
-}
-
-static gboolean pan_search_by_partial(PanWindow *pw, const gchar *text)
-{
-	PanItem *pi;
-	GList *list;
-	GList *found;
-	PanItemType type;
-	gchar *buf;
-
-	type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
-
-	list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
-	if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
-	if (!list)
-		{
-		gchar *needle;
-
-		needle = g_utf8_strdown(text, -1);
-		list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
-		g_free(needle);
-		}
-	if (!list) return FALSE;
-
-	found = g_list_find(list, pw->click_pi);
-	if (found && found->next)
-		{
-		found = found->next;
-		pi = found->data;
-		}
-	else
-		{
-		pi = list->data;
-		}
-
-	pan_info_update(pw, pi);
-	image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
-
-	buf = g_strdup_printf("%s ( %d / %d )",
-			      _("partial match"),
-			      g_list_index(list, pi) + 1,
-			      g_list_length(list));
-	pan_search_status(pw, buf);
-	g_free(buf);
-
-	g_list_free(list);
-
-	return TRUE;
-}
-
-static gboolean valid_date_separator(gchar c)
-{
-	return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
-}
-
-static GList *pan_search_by_date_val(PanWindow *pw, PanItemType type,
-				     gint year, gint month, gint day,
-				     const gchar *key)
-{
-	GList *list = NULL;
-	GList *work;
-
-	work = g_list_last(pw->list_static);
-	while (work)
-		{
-		PanItem *pi;
-
-		pi = work->data;
-		work = work->prev;
-
-		if (pi->fd && (pi->type == type || type == PAN_ITEM_NONE) &&
-		    ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
-			{
-			struct tm *tl;
-
-			tl = localtime(&pi->fd->date);
-			if (tl)
-				{
-				gint match;
-
-				match = (tl->tm_year == year - 1900);
-				if (match && month >= 0) match = (tl->tm_mon == month - 1);
-				if (match && day > 0) match = (tl->tm_mday == day);
-
-				if (match) list = g_list_prepend(list, pi);
-				}
-			}
-		}
-
-	return g_list_reverse(list);
-}
-
-static gboolean pan_search_by_date(PanWindow *pw, const gchar *text)
-{
-	PanItem *pi = NULL;
-	GList *list = NULL;
-	GList *found;
-	gint year;
-	gint month = -1;
-	gint day = -1;
-	gchar *ptr;
-	gchar *mptr;
-	struct tm *lt;
-	time_t t;
-	gchar *message;
-	gchar *buf;
-	gchar *buf_count;
-
-	if (!text) return FALSE;
-
-	ptr = (gchar *)text;
-	while (*ptr != '\0')
-		{
-		if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
-		ptr++;
-		}
-
-	t = time(NULL);
-	if (t == -1) return FALSE;
-	lt = localtime(&t);
-	if (!lt) return FALSE;
-
-	if (valid_date_separator(*text))
-		{
-		year = -1;
-		mptr = (gchar *)text;
-		}
-	else
-		{
-		year = (gint)strtol(text, &mptr, 10);
-		if (mptr == text) return FALSE;
-		}
-
-	if (*mptr != '\0' && valid_date_separator(*mptr))
-		{
-		gchar *dptr;
-
-		mptr++;
-		month = strtol(mptr, &dptr, 10);
-		if (dptr == mptr)
-			{
-			if (valid_date_separator(*dptr))
-				{
-				month = lt->tm_mon + 1;
-				dptr++;
-				}
-			else
-				{
-				month = -1;
-				}
-			}
-		if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
-			{
-			gchar *eptr;
-			dptr++;
-			day = strtol(dptr, &eptr, 10);
-			if (dptr == eptr)
-				{
-				day = lt->tm_mday;
-				}
-			}
-		}
-
-	if (year == -1)
-		{
-		year = lt->tm_year + 1900;
-		}
-	else if (year < 100)
-		{
-		if (year > 70)
-			year+= 1900;
-		else
-			year+= 2000;
-		}
-
-	if (year < 1970 ||
-	    month < -1 || month == 0 || month > 12 ||
-	    day < -1 || day == 0 || day > 31) return FALSE;
-
-	t = pan_date_to_time(year, month, day);
-	if (t < 0) return FALSE;
-
-	if (pw->layout == PAN_LAYOUT_CALENDAR)
-		{
-		list = pan_search_by_date_val(pw, PAN_ITEM_BOX, year, month, day, "day");
-		}
-	else
-		{
-		PanItemType type;
-
-		type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
-		list = pan_search_by_date_val(pw, type, year, month, day, NULL);
-		}
-
-	if (list)
-		{
-		found = g_list_find(list, pw->search_pi);
-		if (found && found->next)
-			{
-			found = found->next;
-			pi = found->data;
-			}
-		else
-			{
-			pi = list->data;
-			}
-		}
-
-	pw->search_pi = pi;
-
-	if (pw->layout == PAN_LAYOUT_CALENDAR && pi && pi->type == PAN_ITEM_BOX)
-		{
-		pan_info_update(pw, NULL);
-		pan_calendar_update(pw, pi);
-		image_scroll_to_point(pw->imd,
-				      pi->x + pi->width / 2,
-				      pi->y + pi->height / 2, 0.5, 0.5);
-		}
-	else if (pi)
-		{
-		pan_info_update(pw, pi);
-		image_scroll_to_point(pw->imd,
-				      pi->x - PAN_BOX_BORDER * 5 / 2,
-				      pi->y, 0.0, 0.5);
-		}
-
-	if (month > 0)
-		{
-		buf = pan_date_value_string(t, PAN_DATE_LENGTH_MONTH);
-		if (day > 0)
-			{
-			gchar *tmp;
-			tmp = buf;
-			buf = g_strdup_printf("%d %s", day, tmp);
-			g_free(tmp);
-			}
-		}
-	else
-		{
-		buf = pan_date_value_string(t, PAN_DATE_LENGTH_YEAR);
-		}
-
-	if (pi)
-		{
-		buf_count = g_strdup_printf("( %d / %d )",
-					    g_list_index(list, pi) + 1,
-					    g_list_length(list));
-		}
-	else
-		{
-		buf_count = g_strdup_printf("(%s)", _("no match"));
-		}
-
-	message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
-	g_free(buf);
-	g_free(buf_count);
-	pan_search_status(pw, message);
-	g_free(message);
-
-	g_list_free(list);
-
-	return TRUE;
-}
-
-static void pan_search_activate_cb(const gchar *text, gpointer data)
-{
-	PanWindow *pw = data;
-
-	if (!text) return;
-
-	tab_completion_append_to_history(pw->search_entry, text);
-
-	if (pan_search_by_path(pw, text)) return;
-
-	if ((pw->layout == PAN_LAYOUT_TIMELINE ||
-	     pw->layout == PAN_LAYOUT_CALENDAR) &&
-	    pan_search_by_date(pw, text))
-		{
-		return;
-		}
-
-	if (pan_search_by_partial(pw, text)) return;
-
-	pan_search_status(pw, _("no match"));
-}
-
-static void pan_search_activate(PanWindow *pw)
-{
-	gchar *text;
-
-	text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->search_entry)));
-	pan_search_activate_cb(text, pw);
-	g_free(text);
-}
-
-static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
-{
-	PanWindow *pw = data;
-	gboolean visible;
-
-	visible = gtk_widget_get_visible(pw->search_box);
-	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
-
-	if (visible)
-		{
-		gtk_widget_hide(pw->search_box);
-		gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
-		}
-	else
-		{
-		gtk_widget_show(pw->search_box);
-		gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
-		gtk_widget_grab_focus(pw->search_entry);
-		}
-}
-
-static void pan_search_toggle_visible(PanWindow *pw, gboolean enable)
-{
-	if (pw->fs) return;
-
-	if (enable)
-		{
-		if (gtk_widget_get_visible(pw->search_box))
-			{
-			gtk_widget_grab_focus(pw->search_entry);
-			}
-		else
-			{
-			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
-			}
-		}
-	else
-		{
-		if (gtk_widget_get_visible(pw->search_entry))
-			{
-			if (gtk_widget_has_focus(pw->search_entry))
-				{
-				gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
-				}
-			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
-			}
-		}
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * main window
- *-----------------------------------------------------------------------------
- */
-
-static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
-{
-	PanWindow *pw = data;
-	PanItem *pi = NULL;
-	GtkWidget *menu;
-	gint rx, ry;
-
-	rx = ry = 0;
-	if (pr->scale)
-		{
-		rx = (gdouble)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
-		ry = (gdouble)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
-		}
-
-	pi = pan_item_find_by_coord(pw, PAN_ITEM_BOX, rx, ry, "info");
-	if (pi && event->button == MOUSE_BUTTON_LEFT)
-		{
-		pan_info_update(pw, NULL);
-		return;
-		}
-
-	pi = pan_item_find_by_coord(pw, (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB,
-				    rx, ry, NULL);
-
-	switch (event->button)
-		{
-		case MOUSE_BUTTON_LEFT:
-			pan_info_update(pw, pi);
-
-			if (!pi && pw->layout == PAN_LAYOUT_CALENDAR)
-				{
-				pi = pan_item_find_by_coord(pw, PAN_ITEM_BOX, rx, ry, "day");
-				pan_calendar_update(pw, pi);
-				}
-			break;
-		case MOUSE_BUTTON_MIDDLE:
-			break;
-		case MOUSE_BUTTON_RIGHT:
-			pan_info_update(pw, pi);
-			menu = pan_popup_menu(pw);
-			gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
-			break;
-		default:
-			break;
-		}
-}
-
-static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
-{
-	gint w, h;
-
-	w = pr->vis_width;
-	h = pr->vis_height;
-
-	if (!(event->state & GDK_SHIFT_MASK))
-		{
-		w /= 3;
-		h /= 3;
-		}
-
-	if (event->state & GDK_CONTROL_MASK)
-		{
-		switch (event->direction)
-			{
-			case GDK_SCROLL_UP:
-				pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
-								     (gint)event->x, (gint)event->y);
-				break;
-			case GDK_SCROLL_DOWN:
-				pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
-								     (gint)event->x, (gint)event->y);
-				break;
-			default:
-				break;
-			}
-		}
-	else
-		{
-		switch (event->direction)
-			{
-			case GDK_SCROLL_UP:
-				pixbuf_renderer_scroll(pr, 0, -h);
-				break;
-			case GDK_SCROLL_DOWN:
-				pixbuf_renderer_scroll(pr, 0, h);
-				break;
-			case GDK_SCROLL_LEFT:
-				pixbuf_renderer_scroll(pr, -w, 0);
-				break;
-			case GDK_SCROLL_RIGHT:
-				pixbuf_renderer_scroll(pr, w, 0);
-				break;
-			default:
-				break;
-			}
-		}
-}
-
-static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
-{
-	g_signal_connect(G_OBJECT(imd->pr), "clicked",
-			 G_CALLBACK(button_cb), pw);
-	g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
-			 G_CALLBACK(scroll_cb), pw);
-}
-
-static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
-{
-	PanWindow *pw = data;
-
-	pw->fs = NULL;
-	pw->imd = pw->imd_normal;
-}
-
-static void pan_fullscreen_toggle(PanWindow *pw, gboolean force_off)
-{
-	if (force_off && !pw->fs) return;
-
-	if (pw->fs)
-		{
-		fullscreen_stop(pw->fs);
-		}
-	else
-		{
-		pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
-		pan_image_set_buttons(pw, pw->fs->imd);
-		g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
-				 G_CALLBACK(pan_window_key_press_cb), pw);
-
-		pw->imd = pw->fs->imd;
-		}
-}
-
-static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
-{
-	PanWindow *pw = data;
-	gchar *text;
-
-	text = image_zoom_get_as_text(pw->imd);
-	gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
-	g_free(text);
-}
-
-static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
-{
-	PanWindow *pw = data;
-	GtkAdjustment *adj;
-	GdkRectangle rect;
-	gint width, height;
-
-	if (pr->scale == 0.0) return;
-
-	pixbuf_renderer_get_visible_rect(pr, &rect);
-	pixbuf_renderer_get_image_size(pr, &width, &height);
-
-	adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
-	gtk_adjustment_set_page_size(adj, rect.width);
-	gtk_adjustment_set_page_increment(adj, gtk_adjustment_get_page_size(adj) / 2.0);
-	gtk_adjustment_set_step_increment(adj, 48.0 / pr->scale);
-	gtk_adjustment_set_lower(adj, 0.0);
-	gtk_adjustment_set_upper(adj, MAX((gdouble)width, 1.0));
-	gtk_adjustment_set_value(adj, (gdouble)rect.x);
-
-	pref_signal_block_data(pw->scrollbar_h, pw);
-	gtk_adjustment_changed(adj);
-	gtk_adjustment_value_changed(adj);
-	pref_signal_unblock_data(pw->scrollbar_h, pw);
-
-	adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
-	gtk_adjustment_set_page_size(adj, rect.height);
-	gtk_adjustment_set_page_increment(adj, gtk_adjustment_get_page_size(adj) / 2.0);
-	gtk_adjustment_set_step_increment(adj, 48.0 / pr->scale);
-	gtk_adjustment_set_lower(adj, 0.0);
-	gtk_adjustment_set_upper(adj, MAX((gdouble)height, 1.0));
-	gtk_adjustment_set_value(adj, (gdouble)rect.y);
-
-	pref_signal_block_data(pw->scrollbar_v, pw);
-	gtk_adjustment_changed(adj);
-	gtk_adjustment_value_changed(adj);
-	pref_signal_unblock_data(pw->scrollbar_v, pw);
-}
-
-static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
-{
-	PanWindow *pw = data;
-	PixbufRenderer *pr;
-	gint x;
-
-	pr = PIXBUF_RENDERER(pw->imd_normal->pr);
-
-	if (!pr->scale) return;
-
-	x = (gint)gtk_range_get_value(range);
-
-	pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
-}
-
-static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
-{
-	PanWindow *pw = data;
-	PixbufRenderer *pr;
-	gint y;
-
-	pr = PIXBUF_RENDERER(pw->imd_normal->pr);
-
-	if (!pr->scale) return;
-
-	y = (gint)gtk_range_get_value(range);
-
-	pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
-}
-
-static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
-{
-	PanWindow *pw = data;
-
-	pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
-	pan_layout_update(pw);
-}
-
-static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
-{
-	PanWindow *pw = data;
-
-	pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
-	pan_layout_update(pw);
-}
-
-static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
-{
-	PanWindow *pw = data;
-	gchar *path;
-
-	path = remove_trailing_slash(new_text);
-	parse_out_relatives(path);
-
-	if (!isdir(path))
-		{
-		warning_dialog(_("Folder not found"),
-			       _("The entered path is not a folder"),
-			       GTK_STOCK_DIALOG_WARNING, pw->path_entry);
-		}
-	else
-		{
-		FileData *dir_fd = file_data_new_dir(path);
-		tab_completion_append_to_history(pw->path_entry, path);
-
-		pan_layout_set_fd(pw, dir_fd);
-		file_data_unref(dir_fd);
-		}
-
-	g_free(path);
-}
-
-static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
-{
-	PanWindow *pw = data;
-	gchar *text;
-
-	if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
-
-	text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
-	pan_window_entry_activate_cb(text, pw);
-	g_free(text);
-}
-
-static void pan_window_close(PanWindow *pw)
-{
-	pan_window_list = g_list_remove(pan_window_list, pw);
-
-	pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_EXIF_PAN_DATE, pw->exif_date_enable);
-	pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_INFO_IMAGE, pw->info_image_size);
-	pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_INFO_EXIF, pw->info_includes_exif);
-
-	if (pw->idle_id)
-		{
-		g_source_remove(pw->idle_id);
-		}
-
-	pan_fullscreen_toggle(pw, TRUE);
-	gtk_widget_destroy(pw->window);
-
-	pan_window_items_free(pw);
-	pan_cache_free(pw);
-
-	file_data_unref(pw->dir_fd);
-
-	g_free(pw);
-}
-
-static gboolean pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
-{
-	PanWindow *pw = data;
-
-	pan_window_close(pw);
-	return TRUE;
-}
-
-static void pan_window_new_real(FileData *dir_fd)
-{
-	PanWindow *pw;
-	GtkWidget *vbox;
-	GtkWidget *box;
-	GtkWidget *combo;
-	GtkWidget *hbox;
-	GtkWidget *frame;
-	GtkWidget *table;
-	GdkGeometry geometry;
-
-	pw = g_new0(PanWindow, 1);
-
-	pw->dir_fd = file_data_ref(dir_fd);
-	pw->layout = PAN_LAYOUT_TIMELINE;
-	pw->size = PAN_IMAGE_SIZE_THUMB_NORMAL;
-	pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
-	pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
-
-	if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_EXIF_PAN_DATE, &pw->exif_date_enable))
-		{
-		pw->exif_date_enable = FALSE;
-		}
-	if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_INFO_IMAGE, &pw->info_image_size))
-		{
-		pw->info_image_size = PAN_IMAGE_SIZE_THUMB_NONE;
-		}
-	if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_INFO_EXIF, &pw->info_includes_exif))
-		{
-		pw->info_includes_exif = TRUE;
-		}
-
-	pw->ignore_symlinks = TRUE;
-
-	pw->idle_id = 0;
-
-	pw->window = window_new(GTK_WINDOW_TOPLEVEL, "panview", NULL, NULL, _("Pan View"));
-
-	geometry.min_width = DEFAULT_MINIMAL_WINDOW_SIZE;
-	geometry.min_height = DEFAULT_MINIMAL_WINDOW_SIZE;
-	gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
-
-	gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
-	gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
-
-	vbox = gtk_vbox_new(FALSE, 0);
-	gtk_container_add(GTK_CONTAINER(pw->window), vbox);
-	gtk_widget_show(vbox);
-
-	box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
-
-	pref_spacer(box, 0);
-	pref_label_new(box, _("Location:"));
-	combo = tab_completion_new_with_history(&pw->path_entry, dir_fd->path, "pan_view_path", -1,
-						pan_window_entry_activate_cb, pw);
-	g_signal_connect(G_OBJECT(gtk_widget_get_parent(pw->path_entry)), "changed",
-			 G_CALLBACK(pan_window_entry_change_cb), pw);
-	gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
-	gtk_widget_show(combo);
-
-	combo = gtk_combo_box_text_new();
-	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Timeline"));
-	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Calendar"));
-	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Folders"));
-	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Folders (flower)"));
-	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Grid"));
-
-	gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
-	g_signal_connect(G_OBJECT(combo), "changed",
-			 G_CALLBACK(pan_window_layout_change_cb), pw);
-	gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
-	gtk_widget_show(combo);
-
-	combo = gtk_combo_box_text_new();
-	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Dots"));
-	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("No Images"));
-	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Small Thumbnails"));
-	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Normal Thumbnails"));
-	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Large Thumbnails"));
-	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("1:10 (10%)"));
-	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("1:4 (25%)"));
-	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("1:3 (33%)"));
-	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("1:2 (50%)"));
-	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("1:1 (100%)"));
-
-	gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
-	g_signal_connect(G_OBJECT(combo), "changed",
-			 G_CALLBACK(pan_window_layout_size_cb), pw);
-	gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
-	gtk_widget_show(combo);
-
-	table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
-	gtk_table_set_row_spacings(GTK_TABLE(table), 2);
-	gtk_table_set_col_spacings(GTK_TABLE(table), 2);
-
-	pw->imd = image_new(TRUE);
-	pw->imd_normal = pw->imd;
-
-	g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
-			 G_CALLBACK(pan_window_image_zoom_cb), pw);
-	g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
-			 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
-
-	gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
-			 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
-	gtk_widget_show(GTK_WIDGET(pw->imd->widget));
-
-	pan_window_dnd_init(pw);
-
-	pan_image_set_buttons(pw, pw->imd);
-
-	pw->scrollbar_h = gtk_hscrollbar_new(NULL);
-	g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
-			 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
-	gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
-			 GTK_FILL | GTK_EXPAND, 0, 0, 0);
-	gtk_widget_show(pw->scrollbar_h);
-
-	pw->scrollbar_v = gtk_vscrollbar_new(NULL);
-	g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
-			 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
-	gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
-			 0, GTK_FILL | GTK_EXPAND, 0, 0);
-	gtk_widget_show(pw->scrollbar_v);
-
-	/* find bar */
-
-	pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
-
-	pref_spacer(pw->search_box, 0);
-	pref_label_new(pw->search_box, _("Find:"));
-
-	hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
-	gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
-	gtk_widget_show(hbox);
-
-	combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
-						pan_search_activate_cb, pw);
-	gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
-	gtk_widget_show(combo);
-
-	pw->search_label = gtk_label_new("");
-	gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
-	gtk_widget_show(pw->search_label);
-
-	/* status bar */
-
-	box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
-
-	frame = gtk_frame_new(NULL);
-	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
-	gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
-	gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
-	gtk_widget_show(frame);
-
-	hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
-	gtk_container_add(GTK_CONTAINER(frame), hbox);
-	gtk_widget_show(hbox);
-
-	pref_spacer(hbox, 0);
-	pw->label_message = pref_label_new(hbox, "");
-
-	frame = gtk_frame_new(NULL);
-	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
-	gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
-	gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
-	gtk_widget_show(frame);
-
-	pw->label_zoom = gtk_label_new("");
-	gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
-	gtk_widget_show(pw->label_zoom);
-
-	pw->search_button = gtk_toggle_button_new();
-	gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
-	gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
-	hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
-	gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
-	gtk_widget_show(hbox);
-	pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
-	gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
-	gtk_widget_show(pw->search_button_arrow);
-	pref_label_new(hbox, _("Find"));
-
-	gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
-	gtk_widget_show(pw->search_button);
-	g_signal_connect(G_OBJECT(pw->search_button), "clicked",
-			 G_CALLBACK(pan_search_toggle_cb), pw);
-
-	g_signal_connect(G_OBJECT(pw->window), "delete_event",
-			 G_CALLBACK(pan_window_delete_cb), pw);
-	g_signal_connect(G_OBJECT(pw->window), "key_press_event",
-			 G_CALLBACK(pan_window_key_press_cb), pw);
-
-	gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
-
-	pan_layout_update(pw);
-
-	gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
-	gtk_widget_show(pw->window);
-
-	pan_window_list = g_list_append(pan_window_list, pw);
-}
-
-/*
- *-----------------------------------------------------------------------------
- * peformance warnings
- *-----------------------------------------------------------------------------
- */
-
-static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
-{
-	FileData *dir_fd = data;
-
-	generic_dialog_close(gd);
-
-	pan_window_new_real(dir_fd);
-	file_data_unref(dir_fd);
-}
-
-static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
-{
-	gboolean hide_dlg;
-
-	hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
-	pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
-}
-
-static gboolean pan_warning(FileData *dir_fd)
-{
-	GenericDialog *gd;
-	GtkWidget *box;
-	GtkWidget *group;
-	GtkWidget *button;
-	GtkWidget *ct_button;
-	gboolean hide_dlg;
-
-	if (dir_fd && strcmp(dir_fd->path, G_DIR_SEPARATOR_S) == 0)
-		{
-		pan_warning_folder(dir_fd->path, NULL);
-		return TRUE;
-		}
-
-	if (options->thumbnails.enable_caching &&
-	    options->thumbnails.spec_standard) return FALSE;
-
-	if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
-	if (hide_dlg) return FALSE;
-
-	gd = generic_dialog_new(_("Pan View Performance"), "pan_view_warning", NULL, FALSE,
-				NULL, NULL);
-	gd->data = file_data_ref(dir_fd);
-	generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
-				  pan_warning_ok_cb, TRUE);
-
-	box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
-					 _("Pan view performance may be poor."),
-					 _("To improve performance of thumbnails in the pan view the"
-					   " following options can be enabled. Note that both options"
-					   " must be enabled to notice a change in performance."));
-
-	group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
-	pref_spacer(group, PREF_PAD_INDENT);
-	group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
-
-	ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
-					  options->thumbnails.enable_caching, &options->thumbnails.enable_caching);
-	button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
-				       options->thumbnails.spec_standard, &options->thumbnails.spec_standard);
-	pref_checkbox_link_sensitivity(ct_button, button);
-
-	pref_line(box, 0);
-
-	pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
-			  G_CALLBACK(pan_warning_hide_cb), NULL);
-
-	gtk_widget_show(gd->dialog);
-
-	return TRUE;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * entry point
- *-----------------------------------------------------------------------------
- */
-
-void pan_window_new(FileData *dir_fd)
-{
-	if (pan_warning(dir_fd)) return;
-
-	pan_window_new_real(dir_fd);
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * menus
- *-----------------------------------------------------------------------------
- */
-
-#define INFO_IMAGE_SIZE_KEY "image_size_data"
-
-
-static void pan_new_window_cb(GtkWidget *widget, gpointer data)
-{
-	PanWindow *pw = data;
-	FileData *fd;
-
-	fd = pan_menu_click_fd(pw);
-	if (fd)
-		{
-		pan_fullscreen_toggle(pw, TRUE);
-		view_window_new(fd);
-		}
-}
-
-static void pan_edit_cb(GtkWidget *widget, gpointer data)
-{
-	PanWindow *pw;
-	FileData *fd;
-	const gchar *key = data;
-
-	pw = submenu_item_get_data(widget);
-	if (!pw) return;
-
-	fd = pan_menu_click_fd(pw);
-	if (fd)
-		{
-		if (!editor_window_flag_set(key))
-			{
-			pan_fullscreen_toggle(pw, TRUE);
-			}
-		file_util_start_editor_from_file(key, fd, pw->imd->widget);
-		}
-}
-
-static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
-{
-	PanWindow *pw = data;
-
-	image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
-}
-
-static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
-{
-	PanWindow *pw = data;
-
-	image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
-}
-
-static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
-{
-	PanWindow *pw = data;
-
-	image_zoom_set(pw->imd, 1.0);
-}
-
-static void pan_copy_cb(GtkWidget *widget, gpointer data)
-{
-	PanWindow *pw = data;
-	FileData *fd;
-
-	fd = pan_menu_click_fd(pw);
-	if (fd) file_util_copy(fd, NULL, NULL, pw->imd->widget);
-}
-
-static void pan_move_cb(GtkWidget *widget, gpointer data)
-{
-	PanWindow *pw = data;
-	FileData *fd;
-
-	fd = pan_menu_click_fd(pw);
-	if (fd) file_util_move(fd, NULL, NULL, pw->imd->widget);
-}
-
-static void pan_rename_cb(GtkWidget *widget, gpointer data)
-{
-	PanWindow *pw = data;
-	FileData *fd;
-
-	fd = pan_menu_click_fd(pw);
-	if (fd) file_util_rename(fd, NULL, pw->imd->widget);
-}
-
-static void pan_delete_cb(GtkWidget *widget, gpointer data)
-{
-	PanWindow *pw = data;
-	FileData *fd;
-
-	fd = pan_menu_click_fd(pw);
-	if (fd) file_util_delete(fd, NULL, pw->imd->widget);
-}
-
-static void pan_copy_path_cb(GtkWidget *widget, gpointer data)
-{
-	PanWindow *pw = data;
-	FileData *fd;
-
-	fd = pan_menu_click_fd(pw);
-	if (fd) file_util_copy_path_to_clipboard(fd);
-}
-
-static void pan_exif_date_toggle_cb(GtkWidget *widget, gpointer data)
-{
-	PanWindow *pw = data;
-
-	pw->exif_date_enable = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
-	pan_layout_update(pw);
-}
-
-static void pan_info_toggle_exif_cb(GtkWidget *widget, gpointer data)
-{
-	PanWindow *pw = data;
-
-	pw->info_includes_exif = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
-	/* fixme: sync info now */
-}
-
-static void pan_info_toggle_image_cb(GtkWidget *widget, gpointer data)
-{
-	PanWindow *pw = data;
-
-	pw->info_image_size = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), INFO_IMAGE_SIZE_KEY));
-	/* fixme: sync info now */
-}
-
-static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
-{
-	PanWindow *pw = data;
-
-	pan_fullscreen_toggle(pw, FALSE);
-}
-
-static void pan_close_cb(GtkWidget *widget, gpointer data)
-{
-	PanWindow *pw = data;
-
-	pan_window_close(pw);
-}
-
-static void pan_popup_menu_destroy_cb(GtkWidget *widget, gpointer data)
-{
-	GList *editmenu_fd_list = data;
-
-	filelist_free(editmenu_fd_list);
-}
-
-static GList *pan_view_get_fd_list(PanWindow *pw)
-{
-	GList *list = NULL;
-	FileData *fd = pan_menu_click_fd(pw);
-
-	if (fd) list = g_list_prepend(filelist_copy(fd->sidecar_files), file_data_ref(fd));
-
-	return list;
-}
-
-static GtkWidget *pan_popup_menu(PanWindow *pw)
-{
-	GtkWidget *menu;
-	GtkWidget *submenu;
-	GtkWidget *item;
-	gboolean active;
-	GList *editmenu_fd_list;
-
-	active = (pw->click_pi != NULL);
-
-	menu = popup_menu_short_lived();
-
-	menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
-			    G_CALLBACK(pan_zoom_in_cb), pw);
-	menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
-			    G_CALLBACK(pan_zoom_out_cb), pw);
-	menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
-			    G_CALLBACK(pan_zoom_1_1_cb), pw);
-	menu_item_add_divider(menu);
-
-	editmenu_fd_list = pan_view_get_fd_list(pw);
-	g_signal_connect(G_OBJECT(menu), "destroy",
-			 G_CALLBACK(pan_popup_menu_destroy_cb), editmenu_fd_list);
-
-	submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw, editmenu_fd_list);
-	gtk_widget_set_sensitive(item, active);
-
-	menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
-				      G_CALLBACK(pan_new_window_cb), pw);
-
-	menu_item_add_divider(menu);
-	menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
-				      G_CALLBACK(pan_copy_cb), pw);
-	menu_item_add_sensitive(menu, _("_Move..."), active,
-				G_CALLBACK(pan_move_cb), pw);
-	menu_item_add_sensitive(menu, _("_Rename..."), active,
-				G_CALLBACK(pan_rename_cb), pw);
-	menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
-				      G_CALLBACK(pan_delete_cb), pw);
-	menu_item_add_sensitive(menu, _("_Copy path"), active,
-				G_CALLBACK(pan_copy_path_cb), pw);
-
-	menu_item_add_divider(menu);
-	item = menu_item_add_check(menu, _("Sort by E_xif date"), pw->exif_date_enable,
-				   G_CALLBACK(pan_exif_date_toggle_cb), pw);
-	gtk_widget_set_sensitive(item, (pw->layout == PAN_LAYOUT_TIMELINE || pw->layout == PAN_LAYOUT_CALENDAR));
-
-	menu_item_add_divider(menu);
-
-	menu_item_add_check(menu, _("_Show Exif information"), pw->info_includes_exif,
-			    G_CALLBACK(pan_info_toggle_exif_cb), pw);
-	item = menu_item_add(menu, _("Show im_age"), NULL, NULL);
-	submenu = gtk_menu_new();
-	gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
-
-	item = menu_item_add_check(submenu, _("_None"), (pw->info_image_size == PAN_IMAGE_SIZE_THUMB_NONE),
-				   G_CALLBACK(pan_info_toggle_image_cb), pw);
-	g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_THUMB_NONE));
-
-	item = menu_item_add_check(submenu, _("_Full size"), (pw->info_image_size == PAN_IMAGE_SIZE_100),
-				   G_CALLBACK(pan_info_toggle_image_cb), pw);
-	g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_100));
-
-	item = menu_item_add_check(submenu, _("1:2 (50%)"), (pw->info_image_size == PAN_IMAGE_SIZE_50),
-				   G_CALLBACK(pan_info_toggle_image_cb), pw);
-	g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_50));
-
-	item = menu_item_add_check(submenu, _("1:3 (33%)"), (pw->info_image_size == PAN_IMAGE_SIZE_33),
-				   G_CALLBACK(pan_info_toggle_image_cb), pw);
-	g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_33));
-
-	item = menu_item_add_check(submenu, _("1:4 (25%)"), (pw->info_image_size == PAN_IMAGE_SIZE_25),
-				   G_CALLBACK(pan_info_toggle_image_cb), pw);
-	g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_25));
-
-	item = menu_item_add_check(submenu, _("1:10 (10%)"), (pw->info_image_size == PAN_IMAGE_SIZE_10),
-				   G_CALLBACK(pan_info_toggle_image_cb), pw);
-	g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_10));
-
-
-
-	menu_item_add_divider(menu);
-
-	if (pw->fs)
-		{
-		menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
-		}
-	else
-		{
-		menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
-		}
-
-	menu_item_add_divider(menu);
-	menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
-
-	return menu;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * drag and drop
- *-----------------------------------------------------------------------------
- */
-
-static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
-				    gint x, gint y,
-				    GtkSelectionData *selection_data, guint info,
-				    guint time, gpointer data)
-{
-	PanWindow *pw = data;
-
-	if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
-
-	if (info == TARGET_URI_LIST)
-		{
-		GList *list;
-
-		list = uri_filelist_from_gtk_selection_data(selection_data);
-		if (list && isdir(((FileData *)list->data)->path))
-			{
-			FileData *fd = list->data;
-
-			pan_layout_set_fd(pw, fd);
-			}
-
-		filelist_free(list);
-		}
-}
-
-static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
-				    GtkSelectionData *selection_data, guint info,
-				    guint time, gpointer data)
-{
-	PanWindow *pw = data;
-	FileData *fd;
-
-	fd = pan_menu_click_fd(pw);
-	if (fd)
-		{
-		GList *list;
-
-		list = g_list_append(NULL, fd);
-		uri_selection_data_set_uris_from_filelist(selection_data, list);
-		g_list_free(list);
-		}
-	else
-		{
-		gtk_selection_data_set(selection_data, gtk_selection_data_get_target(selection_data),
-				       8, NULL, 0);
-		}
-}
-
-static void pan_window_dnd_init(PanWindow *pw)
-{
-	GtkWidget *widget;
-
-	widget = pw->imd->pr;
-
-	gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
-			    dnd_file_drag_types, dnd_file_drag_types_count,
-			    GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
-	g_signal_connect(G_OBJECT(widget), "drag_data_get",
-			 G_CALLBACK(pan_window_set_dnd_data), pw);
-
-	gtk_drag_dest_set(widget,
-			  GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
-			  dnd_file_drop_types, dnd_file_drop_types_count,
-			  GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
-	g_signal_connect(G_OBJECT(widget), "drag_data_received",
-			 G_CALLBACK(pan_window_get_dnd_data), pw);
-}
-
-/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/pan-view.h	Tue Dec 20 21:36:52 2016 +0000
+++ b/src/pan-view.h	Sat Dec 24 13:44:25 2016 +0100
@@ -22,9 +22,9 @@
 #ifndef PAN_VIEW_H
 #define PAN_VIEW_H
 
+#include "filedata.h"
 
 void pan_window_new(FileData *dir_fd);
 
-
 #endif
 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/Makefile.am	Sat Dec 24 13:44:25 2016 +0100
@@ -0,0 +1,16 @@
+module_pan_view = \
+	%D%/pan-calendar.c	\
+	%D%/pan-calendar.h	\
+	%D%/pan-folder.c	\
+	%D%/pan-folder.h	\
+	%D%/pan-grid.c	\
+	%D%/pan-grid.h	\
+	%D%/pan-item.c	\
+	%D%/pan-item.h	\
+	%D%/pan-timeline.c	\
+	%D%/pan-timeline.h	\
+	%D%/pan-types.h	\
+	%D%/pan-util.c	\
+	%D%/pan-util.h	\
+	%D%/pan-view.c	\
+	%D%/pan-view.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-calendar.c	Sat Dec 24 13:44:25 2016 +0100
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "pan-calendar.h"
+
+#include <glib/gprintf.h>
+#include <math.h>
+
+#include "pan-util.h"
+#include "pan-view.h"
+#include "pixbuf_util.h"
+
+#define PAN_CAL_POPUP_COLOR 220, 220, 220
+#define PAN_CAL_POPUP_ALPHA 255
+#define PAN_CAL_POPUP_BORDER 1
+#define PAN_CAL_POPUP_BORDER_COLOR 0, 0, 0
+#define PAN_CAL_POPUP_TEXT_COLOR 0, 0, 0
+
+#define PAN_CAL_DAY_WIDTH 100
+#define PAN_CAL_DAY_HEIGHT 80
+
+#define PAN_CAL_DAY_COLOR 255, 255, 255
+#define PAN_CAL_DAY_ALPHA 220
+#define PAN_CAL_DAY_BORDER 2
+#define PAN_CAL_DAY_BORDER_COLOR 0, 0, 0
+#define PAN_CAL_DAY_TEXT_COLOR 0, 0, 0
+
+#define PAN_CAL_MONTH_COLOR 255, 255, 255
+#define PAN_CAL_MONTH_ALPHA 200
+#define PAN_CAL_MONTH_BORDER 4
+#define PAN_CAL_MONTH_BORDER_COLOR 0, 0, 0
+#define PAN_CAL_MONTH_TEXT_COLOR 0, 0, 0
+
+#define PAN_CAL_DOT_SIZE 3
+#define PAN_CAL_DOT_GAP 2
+#define PAN_CAL_DOT_COLOR 128, 128, 128
+#define PAN_CAL_DOT_ALPHA 128
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * calendar
+ *-----------------------------------------------------------------------------
+ */
+
+void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
+{
+	PanItem *pbox;
+	PanItem *pi;
+	GList *list;
+	GList *work;
+	gint x1, y1, x2, y2, x3, y3;
+	gint x, y, w, h;
+	gint grid;
+	gint column;
+
+	while ((pi = pan_item_find_by_key(pw, PAN_ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
+
+	if (!pi_day || pi_day->type != PAN_ITEM_BOX ||
+	    !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
+
+	list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
+
+	work = list;
+	while (work)
+		{
+		PanItem *dot;
+		GList *node;
+
+		dot = work->data;
+		node = work;
+		work = work->next;
+
+		if (dot->type != PAN_ITEM_BOX || !dot->fd ||
+		    !dot->key || strcmp(dot->key, "dot") != 0)
+			{
+			list = g_list_delete_link(list, node);
+			}
+		}
+
+	grid = (gint)(sqrt(g_list_length(list)) + 0.5);
+
+	x = pi_day->x + pi_day->width + 4;
+	y = pi_day->y;
+
+	pbox = pan_item_box_new(pw, NULL, x, y, PAN_BOX_BORDER, PAN_BOX_BORDER,
+				PAN_CAL_POPUP_BORDER,
+				PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA,
+				PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
+	pan_item_set_key(pbox, "day_bubble");
+
+	if (pi_day->fd)
+		{
+		PanItem *plabel;
+		gchar *buf;
+
+		buf = pan_date_value_string(pi_day->fd->date, PAN_DATE_LENGTH_WEEK);
+		plabel = pan_item_text_new(pw, x, y, buf, PAN_TEXT_ATTR_BOLD | PAN_TEXT_ATTR_HEADING,
+					   PAN_TEXT_BORDER_SIZE,
+					   PAN_CAL_POPUP_TEXT_COLOR, 255);
+		pan_item_set_key(plabel, "day_bubble");
+		g_free(buf);
+
+		pan_item_size_by_item(pbox, plabel, 0);
+
+		y += plabel->height;
+		}
+
+	if (list)
+		{
+		column = 0;
+
+		x += PAN_BOX_BORDER;
+		y += PAN_BOX_BORDER;
+
+		work = list;
+		while (work)
+			{
+			PanItem *dot;
+
+			dot = work->data;
+			work = work->next;
+
+			if (dot->fd)
+				{
+				PanItem *pimg;
+
+				pimg = pan_item_thumb_new(pw, file_data_ref(dot->fd), x, y);
+				pan_item_set_key(pimg, "day_bubble");
+
+				pan_item_size_by_item(pbox, pimg, PAN_BOX_BORDER);
+
+				column++;
+				if (column < grid)
+					{
+					x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
+					}
+				else
+					{
+					column = 0;
+					x = pbox->x + PAN_BOX_BORDER;
+					y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
+					}
+				}
+			}
+		}
+
+	x1 = pi_day->x + pi_day->width - 8;
+	y1 = pi_day->y + 8;
+	x2 = pbox->x + 1;
+	y2 = pbox->y + MIN(42, pbox->height);
+	x3 = pbox->x + 1;
+	y3 = MAX(pbox->y, y2 - 30);
+	util_clip_triangle(x1, y1, x2, y2, x3, y3,
+			   &x, &y, &w, &h);
+
+	pi = pan_item_tri_new(pw, NULL, x, y, w, h,
+			      x1, y1, x2, y2, x3, y3,
+			      PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA);
+	pan_item_tri_border(pi, PAN_BORDER_1 | PAN_BORDER_3, PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
+	pan_item_set_key(pi, "day_bubble");
+	pan_item_added(pw, pi);
+
+	pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
+	pan_item_added(pw, pbox);
+
+	pan_layout_resize(pw);
+}
+
+void pan_calendar_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height)
+{
+	GList *list;
+	GList *work;
+	gint x, y;
+	time_t tc;
+	gint count;
+	gint day_max;
+	gint grid;
+	gint year = 0;
+	gint month = 0;
+	gint end_year = 0;
+	gint end_month = 0;
+
+	list = pan_list_tree(dir_fd, SORT_NONE, TRUE, pw->ignore_symlinks);
+
+	if (pw->cache_list && pw->exif_date_enable)
+		{
+		pw->cache_list = pan_cache_sort(pw->cache_list, SORT_NAME, TRUE);
+		list = filelist_sort(list, SORT_NAME, TRUE);
+		pan_cache_sync_date(pw, list);
+		}
+
+	pw->cache_list = pan_cache_sort(pw->cache_list, SORT_TIME, TRUE);
+	list = filelist_sort(list, SORT_TIME, TRUE);
+
+	day_max = 0;
+	count = 0;
+	tc = 0;
+	work = list;
+	while (work)
+		{
+		FileData *fd;
+
+		fd = work->data;
+		work = work->next;
+
+		if (!pan_date_compare(fd->date, tc, PAN_DATE_LENGTH_DAY))
+			{
+			count = 0;
+			tc = fd->date;
+			}
+		else
+			{
+			count++;
+			if (day_max < count) day_max = count;
+			}
+		}
+
+	DEBUG_1("biggest day contains %d images", day_max);
+
+	grid = (gint)(sqrt((gdouble)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
+
+	if (list)
+		{
+		FileData *fd = list->data;
+
+		year = pan_date_value(fd->date, PAN_DATE_LENGTH_YEAR);
+		month = pan_date_value(fd->date, PAN_DATE_LENGTH_MONTH);
+		}
+
+	work = g_list_last(list);
+	if (work)
+		{
+		FileData *fd = work->data;
+		end_year = pan_date_value(fd->date, PAN_DATE_LENGTH_YEAR);
+		end_month = pan_date_value(fd->date, PAN_DATE_LENGTH_MONTH);
+		}
+
+	*width = PAN_BOX_BORDER * 2;
+	*height = PAN_BOX_BORDER * 2;
+
+	x = PAN_BOX_BORDER;
+	y = PAN_BOX_BORDER;
+
+	work = list;
+	while (work && (year < end_year || (year == end_year && month <= end_month)))
+		{
+		PanItem *pi_month;
+		PanItem *pi_text;
+		gint day;
+		gint days;
+		gint col;
+		gint row;
+		time_t dt;
+		gchar *buf;
+
+		/* figure last second of this month */
+		dt = pan_date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
+		dt -= 60 * 60 * 24;
+
+		/* anything to show this month? */
+		if (!pan_date_compare(((FileData *)(work->data))->date, dt, PAN_DATE_LENGTH_MONTH))
+			{
+			month ++;
+			if (month > 12)
+				{
+				year++;
+				month = 1;
+				}
+			continue;
+			}
+
+		days = pan_date_value(dt, PAN_DATE_LENGTH_DAY);
+		dt = pan_date_to_time(year, month, 1);
+		col = pan_date_value(dt, PAN_DATE_LENGTH_WEEK);
+		row = 1;
+
+		x = PAN_BOX_BORDER;
+
+		pi_month = pan_item_box_new(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
+					    PAN_CAL_MONTH_BORDER,
+					    PAN_CAL_MONTH_COLOR, PAN_CAL_MONTH_ALPHA,
+					    PAN_CAL_MONTH_BORDER_COLOR, PAN_CAL_MONTH_ALPHA);
+		buf = pan_date_value_string(dt, PAN_DATE_LENGTH_MONTH);
+		pi_text = pan_item_text_new(pw, x, y, buf,
+					    PAN_TEXT_ATTR_BOLD | PAN_TEXT_ATTR_HEADING,
+					    PAN_TEXT_BORDER_SIZE,
+					    PAN_CAL_MONTH_TEXT_COLOR, 255);
+		g_free(buf);
+		pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
+
+		pi_month->height = pi_text->y + pi_text->height - pi_month->y;
+
+		x = PAN_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
+		y = pi_month->y + pi_month->height + PAN_BOX_BORDER;
+
+		for (day = 1; day <= days; day++)
+			{
+			FileData *fd;
+			PanItem *pi_day;
+			gint dx, dy;
+			gint n = 0;
+			gchar fake_path[20];
+
+			dt = pan_date_to_time(year, month, day);
+
+			/*
+			 * Create a FileData entry that represents the given day.
+			 * It does not correspond to any real file
+			 */
+
+			g_snprintf(fake_path, sizeof(fake_path), "//%04d-%02d-%02d", year, month, day);
+			fd = file_data_new_no_grouping(fake_path);
+			fd->date = dt;
+			pi_day = pan_item_box_new(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
+						  PAN_CAL_DAY_BORDER,
+						  PAN_CAL_DAY_COLOR, PAN_CAL_DAY_ALPHA,
+						  PAN_CAL_DAY_BORDER_COLOR, PAN_CAL_DAY_ALPHA);
+			pan_item_set_key(pi_day, "day");
+
+			dx = x + PAN_CAL_DOT_GAP * 2;
+			dy = y + PAN_CAL_DOT_GAP * 2;
+
+			fd = (work) ? work->data : NULL;
+			while (fd && pan_date_compare(fd->date, dt, PAN_DATE_LENGTH_DAY))
+				{
+				PanItem *pi;
+
+				pi = pan_item_box_new(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
+						      0,
+						      PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
+						      0, 0, 0, 0);
+				pan_item_set_key(pi, "dot");
+
+				dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
+				if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
+					{
+					dx = x + PAN_CAL_DOT_GAP * 2;
+					dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
+					}
+				if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
+					{
+					/* must keep all dots within respective day even if it gets ugly */
+					dy = y + PAN_CAL_DOT_GAP * 2;
+					}
+
+				n++;
+
+				work = work->next;
+				fd = (work) ? work->data : NULL;
+				}
+
+			if (n > 0)
+				{
+				PanItem *pi;
+
+				pi_day->color_r = MAX(pi_day->color_r - 61 - n * 3, 80);
+				pi_day->color_g = pi_day->color_r;
+
+				buf = g_strdup_printf("( %d )", n);
+				pi = pan_item_text_new(pw, x, y, buf, PAN_TEXT_ATTR_NONE,
+						       PAN_TEXT_BORDER_SIZE,
+						       PAN_CAL_DAY_TEXT_COLOR, 255);
+				g_free(buf);
+
+				pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
+				pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
+				}
+
+			buf = g_strdup_printf("%d", day);
+			pan_item_text_new(pw, x + 4, y + 4, buf, PAN_TEXT_ATTR_BOLD | PAN_TEXT_ATTR_HEADING,
+					  PAN_TEXT_BORDER_SIZE,
+					  PAN_CAL_DAY_TEXT_COLOR, 255);
+			g_free(buf);
+
+
+			pan_item_size_coordinates(pi_day, PAN_BOX_BORDER, width, height);
+
+			col++;
+			if (col > 6)
+				{
+				col = 0;
+				row++;
+				x = PAN_BOX_BORDER;
+				y += PAN_CAL_DAY_HEIGHT;
+				}
+			else
+				{
+				x += PAN_CAL_DAY_WIDTH;
+				}
+			}
+
+		if (col > 0) y += PAN_CAL_DAY_HEIGHT;
+		y += PAN_BOX_BORDER * 2;
+
+		month ++;
+		if (month > 12)
+			{
+			year++;
+			month = 1;
+			}
+		}
+
+	*width += grid;
+	*height = MAX(*height, grid + PAN_BOX_BORDER * 2 * 2);
+
+	g_list_free(list);
+}
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-calendar.h	Sat Dec 24 13:44:25 2016 +0100
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PAN_VIEW_PAN_CALENDAR_H
+#define PAN_VIEW_PAN_CALENDAR_H
+
+//#include "main.h"
+#include "pan-item.h"
+#include "pan-types.h"
+
+void pan_calendar_update(PanWindow *pw, PanItem *pi_day);
+void pan_calendar_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-folder.c	Sat Dec 24 13:44:25 2016 +0100
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "pan-folder.h"
+
+#include <math.h>
+
+#include "pan-item.h"
+#include "pan-util.h"
+
+static void pan_flower_size(PanWindow *pw, gint *width, gint *height)
+{
+	GList *work;
+	gint x1, y1, x2, y2;
+
+	x1 = 0;
+	y1 = 0;
+	x2 = 0;
+	y2 = 0;
+
+	work = pw->list;
+	while (work)
+		{
+		PanItem *pi;
+
+		pi = work->data;
+		work = work->next;
+
+		if (x1 > pi->x) x1 = pi->x;
+		if (y1 > pi->y) y1 = pi->y;
+		if (x2 < pi->x + pi->width) x2 = pi->x + pi->width;
+		if (y2 < pi->y + pi->height) y2 = pi->y + pi->height;
+		}
+
+	x1 -= PAN_BOX_BORDER;
+	y1 -= PAN_BOX_BORDER;
+	x2 += PAN_BOX_BORDER;
+	y2 += PAN_BOX_BORDER;
+
+	work = pw->list;
+	while (work)
+		{
+		PanItem *pi;
+
+		pi = work->data;
+		work = work->next;
+
+		pi->x -= x1;
+		pi->y -= y1;
+
+		if (pi->type == PAN_ITEM_TRIANGLE && pi->data)
+			{
+			gint *coord;
+
+			coord = pi->data;
+			coord[0] -= x1;
+			coord[1] -= y1;
+			coord[2] -= x1;
+			coord[3] -= y1;
+			coord[4] -= x1;
+			coord[5] -= y1;
+			}
+		}
+
+	if (width) *width = x2 - x1;
+	if (height) *height = y2 - y1;
+}
+
+typedef struct _FlowerGroup FlowerGroup;
+struct _FlowerGroup {
+	GList *items;
+	GList *children;
+	gint x;
+	gint y;
+	gint width;
+	gint height;
+
+	gdouble angle;
+	gint circumference;
+	gint diameter;
+};
+
+static void pan_flower_move(FlowerGroup *group, gint x, gint y)
+{
+	GList *work;
+
+	work = group->items;
+	while (work)
+		{
+		PanItem *pi;
+
+		pi = work->data;
+		work = work->next;
+
+		pi->x += x;
+		pi->y += y;
+		}
+
+	group->x += x;
+	group->y += y;
+}
+
+#define PI 3.14159265
+
+static void pan_flower_position(FlowerGroup *group, FlowerGroup *parent,
+							     gint *result_x, gint *result_y)
+{
+	gint x, y;
+	gint radius;
+	gdouble a;
+
+	radius = parent->circumference / (2*PI);
+	radius = MAX(radius, parent->diameter / 2 + group->diameter / 2);
+
+	a = 2*PI * group->diameter / parent->circumference;
+
+	x = (gint)((gdouble)radius * cos(parent->angle + a / 2));
+	y = (gint)((gdouble)radius * sin(parent->angle + a / 2));
+
+	parent->angle += a;
+
+	x += parent->x;
+	y += parent->y;
+
+	x += parent->width / 2;
+	y += parent->height / 2;
+
+	x -= group->width / 2;
+	y -= group->height / 2;
+
+	*result_x = x;
+	*result_y = y;
+}
+
+static void pan_flower_build(PanWindow *pw, FlowerGroup *group, FlowerGroup *parent)
+{
+	GList *work;
+	gint x, y;
+
+	if (!group) return;
+
+	if (parent && parent->children)
+		{
+		pan_flower_position(group, parent, &x, &y);
+		}
+	else
+		{
+		x = 0;
+		y = 0;
+		}
+
+	pan_flower_move(group, x, y);
+
+	if (parent)
+		{
+		PanItem *pi;
+		gint px, py, gx, gy;
+		gint x1, y1, x2, y2;
+
+		px = parent->x + parent->width / 2;
+		py = parent->y + parent->height / 2;
+
+		gx = group->x + group->width / 2;
+		gy = group->y + group->height / 2;
+
+		x1 = MIN(px, gx);
+		y1 = MIN(py, gy);
+
+		x2 = MAX(px, gx + 5);
+		y2 = MAX(py, gy + 5);
+
+		pi = pan_item_tri_new(pw, NULL, x1, y1, x2 - x1, y2 - y1,
+				      px, py, gx, gy, gx + 5, gy + 5,
+				      255, 40, 40, 128);
+		pan_item_tri_border(pi, PAN_BORDER_1 | PAN_BORDER_3,
+				    255, 0, 0, 128);
+		}
+
+	pw->list = g_list_concat(group->items, pw->list);
+	group->items = NULL;
+
+	group->circumference = 0;
+	work = group->children;
+	while (work)
+		{
+		FlowerGroup *child;
+
+		child = work->data;
+		work = work->next;
+
+		group->circumference += child->diameter;
+		}
+
+	work = g_list_last(group->children);
+	while (work)
+		{
+		FlowerGroup *child;
+
+		child = work->data;
+		work = work->prev;
+
+		pan_flower_build(pw, child, group);
+		}
+
+	g_list_free(group->children);
+	g_free(group);
+}
+
+static FlowerGroup *pan_flower_group(PanWindow *pw, FileData *dir_fd, gint x, gint y)
+{
+	FlowerGroup *group;
+	GList *f;
+	GList *d;
+	GList *work;
+	PanItem *pi_box;
+	gint x_start;
+	gint y_height;
+	gint grid_size;
+	gint grid_count;
+
+	if (!filelist_read(dir_fd, &f, &d)) return NULL;
+	if (!f && !d) return NULL;
+
+	f = filelist_sort(f, SORT_NAME, TRUE);
+	d = filelist_sort(d, SORT_NAME, TRUE);
+
+	pi_box = pan_item_text_new(pw, x, y, dir_fd->path, PAN_TEXT_ATTR_NONE,
+				   PAN_TEXT_BORDER_SIZE,
+				   PAN_TEXT_COLOR, 255);
+
+	y += pi_box->height;
+
+	pi_box = pan_item_box_new(pw, file_data_ref(dir_fd),
+				  x, y,
+				  PAN_BOX_BORDER * 2, PAN_BOX_BORDER * 2,
+				  PAN_BOX_OUTLINE_THICKNESS,
+				  PAN_BOX_COLOR, PAN_BOX_ALPHA,
+				  PAN_BOX_OUTLINE_COLOR, PAN_BOX_OUTLINE_ALPHA);
+
+	x += PAN_BOX_BORDER;
+	y += PAN_BOX_BORDER;
+
+	grid_size = (gint)(sqrt(g_list_length(f)) + 0.9);
+	grid_count = 0;
+	x_start = x;
+	y_height = y;
+
+	work = f;
+	while (work)
+		{
+		FileData *fd;
+		PanItem *pi;
+
+		fd = work->data;
+		work = work->next;
+
+		if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE)
+			{
+			pi = pan_item_image_new(pw, fd, x, y, 10, 10);
+			x += pi->width + PAN_THUMB_GAP;
+			if (pi->height > y_height) y_height = pi->height;
+			}
+		else
+			{
+			pi = pan_item_thumb_new(pw, fd, x, y);
+			x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
+			y_height = PAN_THUMB_SIZE;
+			}
+
+		grid_count++;
+		if (grid_count >= grid_size)
+			{
+			grid_count = 0;
+			x = x_start;
+			y += y_height + PAN_THUMB_GAP;
+			y_height = 0;
+			}
+
+		pan_item_size_by_item(pi_box, pi, PAN_BOX_BORDER);
+		}
+
+	group = g_new0(FlowerGroup, 1);
+	group->items = pw->list;
+	pw->list = NULL;
+
+	group->width = pi_box->width;
+	group->height = pi_box->y + pi_box->height;
+	group->diameter = (gint)sqrt(group->width * group->width + group->height * group->height);
+
+	group->children = NULL;
+
+	work = d;
+	while (work)
+		{
+		FileData *fd;
+		FlowerGroup *child;
+
+		fd = work->data;
+		work = work->next;
+
+		if (!pan_is_ignored(fd->path, pw->ignore_symlinks))
+			{
+			child = pan_flower_group(pw, fd, 0, 0);
+			if (child) group->children = g_list_prepend(group->children, child);
+			}
+		}
+
+	if (!f && !group->children)
+		{
+		work = group->items;
+		while (work)
+			{
+			PanItem *pi;
+
+			pi = work->data;
+			work = work->next;
+
+			pan_item_free(pi);
+			}
+
+		g_list_free(group->items);
+		g_free(group);
+		group = NULL;
+		}
+
+	g_list_free(f);
+	filelist_free(d);
+
+	return group;
+}
+
+void pan_flower_compute(PanWindow *pw, FileData *dir_fd,
+			gint *width, gint *height,
+			gint *scroll_x, gint *scroll_y)
+{
+	FlowerGroup *group;
+	GList *list;
+
+	group = pan_flower_group(pw, dir_fd, 0, 0);
+	pan_flower_build(pw, group, NULL);
+
+	pan_flower_size(pw, width, height);
+
+	list = pan_item_find_by_fd(pw, PAN_ITEM_BOX, dir_fd, FALSE, FALSE);
+	if (list)
+		{
+		PanItem *pi = list->data;
+		*scroll_x = pi->x + pi->width / 2;
+		*scroll_y = pi->y + pi->height / 2;
+		}
+	g_list_free(list);
+}
+
+static void pan_folder_tree_path(PanWindow *pw, FileData *dir_fd,
+				 gint *x, gint *y, gint *level,
+				 PanItem *parent,
+				 gint *width, gint *height)
+{
+	GList *f;
+	GList *d;
+	GList *work;
+	PanItem *pi_box;
+	gint y_height = 0;
+
+	if (!filelist_read(dir_fd, &f, &d)) return;
+	if (!f && !d) return;
+
+	f = filelist_sort(f, SORT_NAME, TRUE);
+	d = filelist_sort(d, SORT_NAME, TRUE);
+
+	*x = PAN_BOX_BORDER + ((*level) * MAX(PAN_BOX_BORDER, PAN_THUMB_GAP));
+
+	pi_box = pan_item_text_new(pw, *x, *y, dir_fd->path, PAN_TEXT_ATTR_NONE,
+				   PAN_TEXT_BORDER_SIZE,
+				   PAN_TEXT_COLOR, 255);
+
+	*y += pi_box->height;
+
+	pi_box = pan_item_box_new(pw, file_data_ref(dir_fd),
+				  *x, *y,
+				  PAN_BOX_BORDER, PAN_BOX_BORDER,
+				  PAN_BOX_OUTLINE_THICKNESS,
+				  PAN_BOX_COLOR, PAN_BOX_ALPHA,
+				  PAN_BOX_OUTLINE_COLOR, PAN_BOX_OUTLINE_ALPHA);
+
+	*x += PAN_BOX_BORDER;
+	*y += PAN_BOX_BORDER;
+
+	work = f;
+	while (work)
+		{
+		FileData *fd;
+		PanItem *pi;
+
+		fd = work->data;
+		work = work->next;
+
+		if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE)
+			{
+			pi = pan_item_image_new(pw, fd, *x, *y, 10, 10);
+			*x += pi->width + PAN_THUMB_GAP;
+			if (pi->height > y_height) y_height = pi->height;
+			}
+		else
+			{
+			pi = pan_item_thumb_new(pw, fd, *x, *y);
+			*x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
+			y_height = PAN_THUMB_SIZE;
+			}
+
+		pan_item_size_by_item(pi_box, pi, PAN_BOX_BORDER);
+		}
+
+	if (f) *y = pi_box->y + pi_box->height;
+
+	g_list_free(f);
+
+	work = d;
+	while (work)
+		{
+		FileData *fd;
+
+		fd = work->data;
+		work = work->next;
+
+		if (!pan_is_ignored(fd->path, pw->ignore_symlinks))
+			{
+			*level = *level + 1;
+			pan_folder_tree_path(pw, fd, x, y, level, pi_box, width, height);
+			*level = *level - 1;
+			}
+		}
+
+	filelist_free(d);
+
+	pan_item_size_by_item(parent, pi_box, PAN_BOX_BORDER);
+
+	if (*y < pi_box->y + pi_box->height + PAN_BOX_BORDER)
+		*y = pi_box->y + pi_box->height + PAN_BOX_BORDER;
+
+	pan_item_size_coordinates(pi_box, PAN_BOX_BORDER, width, height);
+}
+
+void pan_folder_tree_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height)
+{
+	gint x, y;
+	gint level;
+	gint w, h;
+
+	level = 0;
+	x = PAN_BOX_BORDER;
+	y = PAN_BOX_BORDER;
+	w = PAN_BOX_BORDER * 2;
+	h = PAN_BOX_BORDER * 2;
+
+	pan_folder_tree_path(pw, dir_fd, &x, &y, &level, NULL, &w, &h);
+
+	if (width) *width = w;
+	if (height) *height = h;
+}
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-folder.h	Sat Dec 24 13:44:25 2016 +0100
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PAN_VIEW_PAN_FOLDER_H
+#define PAN_VIEW_PAN_FOLDER_H
+
+#include "main.h"
+#include "pan-types.h"
+
+void pan_flower_compute(PanWindow *pw, FileData *dir_fd,
+			gint *width, gint *height,
+			gint *scroll_x, gint *scroll_y);
+void pan_folder_tree_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-grid.c	Sat Dec 24 13:44:25 2016 +0100
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "pan-grid.h"
+
+#include <math.h>
+
+#include "pan-item.h"
+#include "pan-util.h"
+
+void pan_grid_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height)
+{
+	GList *list;
+	GList *work;
+	gint x, y;
+	gint grid_size;
+	gint next_y;
+
+	list = pan_list_tree(dir_fd, SORT_NAME, TRUE, pw->ignore_symlinks);
+
+	grid_size = (gint)sqrt((gdouble)g_list_length(list));
+	if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE)
+		{
+		grid_size = grid_size * (512 + PAN_THUMB_GAP) * pw->image_size / 100;
+		}
+	else
+		{
+		grid_size = grid_size * (PAN_THUMB_SIZE + PAN_THUMB_GAP);
+		}
+
+	next_y = 0;
+
+	*width = PAN_BOX_BORDER * 2;
+	*height = PAN_BOX_BORDER * 2;
+
+	x = PAN_THUMB_GAP;
+	y = PAN_THUMB_GAP;
+	work = list;
+	while (work)
+		{
+		FileData *fd;
+		PanItem *pi;
+
+		fd = work->data;
+		work = work->next;
+
+		if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE)
+			{
+			pi = pan_item_image_new(pw, fd, x, y, 10, 10);
+
+			x += pi->width + PAN_THUMB_GAP;
+			if (y + pi->height + PAN_THUMB_GAP > next_y) next_y = y + pi->height + PAN_THUMB_GAP;
+			if (x > grid_size)
+				{
+				x = PAN_THUMB_GAP;
+				y = next_y;
+				}
+			}
+		else
+			{
+			pi = pan_item_thumb_new(pw, fd, x, y);
+
+			x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
+			if (x > grid_size)
+				{
+				x = PAN_THUMB_GAP;
+				y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
+				}
+			}
+		pan_item_size_coordinates(pi, PAN_THUMB_GAP, width, height);
+		}
+
+	g_list_free(list);
+}
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-grid.h	Sat Dec 24 13:44:25 2016 +0100
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PAN_VIEW_PAN_GRID_H
+#define PAN_VIEW_PAN_GRID_H
+
+#include "main.h"
+#include "pan-types.h"
+
+void pan_grid_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-item.c	Sat Dec 24 13:44:25 2016 +0100
@@ -0,0 +1,976 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "pan-item.h"
+
+#include "image.h"
+#include "pixbuf_util.h"
+#include "ui_misc.h"
+
+/*
+ *-----------------------------------------------------------------------------
+ * item base functions
+ *-----------------------------------------------------------------------------
+ */
+
+void pan_item_free(PanItem *pi)
+{
+	if (!pi) return;
+
+	if (pi->pixbuf) g_object_unref(pi->pixbuf);
+	if (pi->fd) file_data_unref(pi->fd);
+	g_free(pi->text);
+	g_free(pi->key);
+	g_free(pi->data);
+
+	g_free(pi);
+}
+
+void pan_item_set_key(PanItem *pi, const gchar *key)
+{
+	gchar *tmp;
+
+	if (!pi) return;
+
+	tmp = pi->key;
+	pi->key = g_strdup(key);
+	g_free(tmp);
+}
+
+void pan_item_added(PanWindow *pw, PanItem *pi)
+{
+	if (!pi) return;
+	image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
+}
+
+void pan_item_remove(PanWindow *pw, PanItem *pi)
+{
+	if (!pi) return;
+
+	if (pw->click_pi == pi) pw->click_pi = NULL;
+	if (pw->queue_pi == pi)	pw->queue_pi = NULL;
+	if (pw->search_pi == pi) pw->search_pi = NULL;
+	pw->queue = g_list_remove(pw->queue, pi);
+
+	pw->list = g_list_remove(pw->list, pi);
+	image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
+	pan_item_free(pi);
+}
+
+void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
+{
+	if (!pi || !child) return;
+
+	if (pi->x + pi->width < child->x + child->width + border)
+		pi->width = child->x + child->width + border - pi->x;
+
+	if (pi->y + pi->height < child->y + child->height + border)
+		pi->height = child->y + child->height + border - pi->y;
+}
+
+void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
+{
+	if (!pi) return;
+
+	if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
+	if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * item box type
+ *-----------------------------------------------------------------------------
+ */
+
+PanItem *pan_item_box_new(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
+			  gint border_size,
+			  guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
+			  guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
+{
+	PanItem *pi;
+
+	pi = g_new0(PanItem, 1);
+	pi->type = PAN_ITEM_BOX;
+	pi->fd = fd;
+	pi->x = x;
+	pi->y = y;
+	pi->width = width;
+	pi->height = height;
+
+	pi->color_r = base_r;
+	pi->color_g = base_g;
+	pi->color_b = base_b;
+	pi->color_a = base_a;
+
+	pi->color2_r = bord_r;
+	pi->color2_g = bord_g;
+	pi->color2_b = bord_b;
+	pi->color2_a = bord_a;
+	pi->border = border_size;
+
+	pw->list = g_list_prepend(pw->list, pi);
+
+	return pi;
+}
+
+void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
+{
+	gint *shadow;
+
+	if (!pi || pi->type != PAN_ITEM_BOX) return;
+
+	shadow = pi->data;
+	if (shadow)
+		{
+		pi->width -= shadow[0];
+		pi->height -= shadow[0];
+		}
+
+	shadow = g_new0(gint, 2);
+	shadow[0] = offset;
+	shadow[1] = fade;
+
+	pi->width += offset;
+	pi->height += offset;
+
+	g_free(pi->data);
+	pi->data = shadow;
+}
+
+gint pan_item_box_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
+		       gint x, gint y, gint width, gint height)
+{
+	gint bw, bh;
+	gint *shadow;
+	gint rx, ry, rw, rh;
+
+	bw = pi->width;
+	bh = pi->height;
+
+	shadow = pi->data;
+	if (shadow)
+		{
+		bw -= shadow[0];
+		bh -= shadow[0];
+
+		if (pi->color_a > 254)
+			{
+			pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
+					   shadow[0], bh - shadow[0],
+					   pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
+					   shadow[1],
+					   PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
+			pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
+					   bw, shadow[0],
+					   pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
+					   shadow[1],
+					   PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
+			}
+		else
+			{
+			gint a;
+			a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
+			pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
+					   bw, bh,
+					   pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
+					   shadow[1],
+					   PAN_SHADOW_COLOR, a);
+			}
+		}
+
+	if (util_clip_region(x, y, width, height,
+			     pi->x, pi->y, bw, bh,
+			     &rx, &ry, &rw, &rh))
+		{
+		pixbuf_draw_rect_fill(pixbuf,
+				      rx - x, ry - y, rw, rh,
+				      pi->color_r, pi->color_g, pi->color_b, pi->color_a);
+		}
+	if (util_clip_region(x, y, width, height,
+			     pi->x, pi->y, bw, pi->border,
+			     &rx, &ry, &rw, &rh))
+		{
+		pixbuf_draw_rect_fill(pixbuf,
+				      rx - x, ry - y, rw, rh,
+				      pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
+		}
+	if (util_clip_region(x, y, width, height,
+			     pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
+			     &rx, &ry, &rw, &rh))
+		{
+		pixbuf_draw_rect_fill(pixbuf,
+				      rx - x, ry - y, rw, rh,
+				      pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
+		}
+	if (util_clip_region(x, y, width, height,
+			     pi->x + bw - pi->border, pi->y + pi->border,
+			     pi->border, bh - pi->border * 2,
+			     &rx, &ry, &rw, &rh))
+		{
+		pixbuf_draw_rect_fill(pixbuf,
+				      rx - x, ry - y, rw, rh,
+				      pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
+		}
+	if (util_clip_region(x, y, width, height,
+			     pi->x, pi->y + bh - pi->border,
+			     bw,  pi->border,
+			     &rx, &ry, &rw, &rh))
+		{
+		pixbuf_draw_rect_fill(pixbuf,
+				      rx - x, ry - y, rw, rh,
+				      pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
+		}
+
+	return FALSE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * item triangle type
+ *-----------------------------------------------------------------------------
+ */
+
+PanItem *pan_item_tri_new(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
+			  gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
+			  guint8 r, guint8 g, guint8 b, guint8 a)
+{
+	PanItem *pi;
+	gint *coord;
+
+	pi = g_new0(PanItem, 1);
+	pi->type = PAN_ITEM_TRIANGLE;
+	pi->x = x;
+	pi->y = y;
+	pi->width = width;
+	pi->height = height;
+
+	pi->color_r = r;
+	pi->color_g = g;
+	pi->color_b = b;
+	pi->color_a = a;
+
+	coord = g_new0(gint, 6);
+	coord[0] = x1;
+	coord[1] = y1;
+	coord[2] = x2;
+	coord[3] = y2;
+	coord[4] = x3;
+	coord[5] = y3;
+
+	pi->data = coord;
+
+	pi->border = PAN_BORDER_NONE;
+
+	pw->list = g_list_prepend(pw->list, pi);
+
+	return pi;
+}
+
+void pan_item_tri_border(PanItem *pi, gint borders,
+			 guint8 r, guint8 g, guint8 b, guint8 a)
+{
+	if (!pi || pi->type != PAN_ITEM_TRIANGLE) return;
+
+	pi->border = borders;
+
+	pi->color2_r = r;
+	pi->color2_g = g;
+	pi->color2_b = b;
+	pi->color2_a = a;
+}
+
+gint pan_item_tri_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
+		       gint x, gint y, gint width, gint height)
+{
+	gint rx, ry, rw, rh;
+
+	if (util_clip_region(x, y, width, height,
+			     pi->x, pi->y, pi->width, pi->height,
+			     &rx, &ry, &rw, &rh) && pi->data)
+		{
+		gint *coord = pi->data;
+		pixbuf_draw_triangle(pixbuf,
+				     rx - x, ry - y, rw, rh,
+				     coord[0] - x, coord[1] - y,
+				     coord[2] - x, coord[3] - y,
+				     coord[4] - x, coord[5] - y,
+				     pi->color_r, pi->color_g, pi->color_b, pi->color_a);
+
+		if (pi->border & PAN_BORDER_1)
+			{
+			pixbuf_draw_line(pixbuf,
+					 rx - x, ry - y, rw, rh,
+					 coord[0] - x, coord[1] - y,
+					 coord[2] - x, coord[3] - y,
+					 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
+			}
+		if (pi->border & PAN_BORDER_2)
+			{
+			pixbuf_draw_line(pixbuf,
+					 rx - x, ry - y, rw, rh,
+					 coord[2] - x, coord[3] - y,
+					 coord[4] - x, coord[5] - y,
+					 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
+			}
+		if (pi->border & PAN_BORDER_3)
+			{
+			pixbuf_draw_line(pixbuf,
+					 rx - x, ry - y, rw, rh,
+					 coord[4] - x, coord[5] - y,
+					 coord[0] - x, coord[1] - y,
+					 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
+			}
+		}
+
+	return FALSE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * item text type
+ *-----------------------------------------------------------------------------
+ */
+
+static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
+{
+	PangoLayout *layout;
+
+	layout = gtk_widget_create_pango_layout(widget, NULL);
+
+	if (pi->text_attr & PAN_TEXT_ATTR_MARKUP)
+		{
+		pango_layout_set_markup(layout, pi->text, -1);
+		return layout;
+		}
+
+	if (pi->text_attr & PAN_TEXT_ATTR_BOLD ||
+	    pi->text_attr & PAN_TEXT_ATTR_HEADING)
+		{
+		PangoAttrList *pal;
+		PangoAttribute *pa;
+
+		pal = pango_attr_list_new();
+		if (pi->text_attr & PAN_TEXT_ATTR_BOLD)
+			{
+			pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
+			pa->start_index = 0;
+			pa->end_index = G_MAXINT;
+			pango_attr_list_insert(pal, pa);
+			}
+		if (pi->text_attr & PAN_TEXT_ATTR_HEADING)
+			{
+			pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
+			pa->start_index = 0;
+			pa->end_index = G_MAXINT;
+			pango_attr_list_insert(pal, pa);
+			}
+		pango_layout_set_attributes(layout, pal);
+		pango_attr_list_unref(pal);
+		}
+
+	pango_layout_set_text(layout, pi->text, -1);
+	return layout;
+}
+
+static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
+{
+	PangoLayout *layout;
+
+	if (!pi || !pi->text || !widget) return;
+
+	layout = pan_item_text_layout(pi, widget);
+	pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
+	g_object_unref(G_OBJECT(layout));
+
+	pi->width += pi->border * 2;
+	pi->height += pi->border * 2;
+}
+
+PanItem *pan_item_text_new(PanWindow *pw, gint x, gint y, const gchar *text,
+			   PanTextAttrType attr, PanBorderType border,
+			   guint8 r, guint8 g, guint8 b, guint8 a)
+{
+	PanItem *pi;
+
+	pi = g_new0(PanItem, 1);
+	pi->type = PAN_ITEM_TEXT;
+	pi->x = x;
+	pi->y = y;
+	pi->text = g_strdup(text);
+	pi->text_attr = attr;
+
+	pi->color_r = r;
+	pi->color_g = g;
+	pi->color_b = b;
+	pi->color_a = a;
+
+	pi->border = border;
+
+	pan_item_text_compute_size(pi, pw->imd->pr);
+
+	pw->list = g_list_prepend(pw->list, pi);
+
+	return pi;
+}
+
+gint pan_item_text_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
+			gint x, gint y, gint width, gint height)
+{
+	PangoLayout *layout;
+
+	layout = pan_item_text_layout(pi, (GtkWidget *)pr);
+	pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
+			   pi->x - x + pi->border, pi->y - y + pi->border,
+			   pi->color_r, pi->color_g, pi->color_b, pi->color_a);
+	g_object_unref(G_OBJECT(layout));
+
+	return FALSE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * item thumbnail type
+ *-----------------------------------------------------------------------------
+ */
+
+PanItem *pan_item_thumb_new(PanWindow *pw, FileData *fd, gint x, gint y)
+{
+	PanItem *pi;
+
+	pi = g_new0(PanItem, 1);
+
+	pi->type = PAN_ITEM_THUMB;
+	pi->fd = fd;
+	pi->x = x;
+	pi->y = y;
+	pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
+	pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
+
+	pw->list = g_list_prepend(pw->list, pi);
+
+	return pi;
+}
+
+gint pan_item_thumb_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
+			 gint x, gint y, gint width, gint height)
+{
+	gint tx, ty, tw, th;
+	gint rx, ry, rw, rh;
+
+	if (pi->pixbuf)
+		{
+		tw = gdk_pixbuf_get_width(pi->pixbuf);
+		th = gdk_pixbuf_get_height(pi->pixbuf);
+
+		tx = pi->x + (pi->width - tw) / 2;
+		ty = pi->y + (pi->height - th) / 2;
+
+		if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
+			{
+			if (util_clip_region(x, y, width, height,
+					     tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
+					     &rx, &ry, &rw, &rh))
+				{
+				pixbuf_draw_shadow(pixbuf,
+						   rx - x, ry - y, rw, rh,
+						   tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
+						   PAN_SHADOW_FADE,
+						   PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
+				}
+			}
+		else
+			{
+			if (util_clip_region(x, y, width, height,
+					     tx + tw, ty + PAN_SHADOW_OFFSET,
+					     PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
+					     &rx, &ry, &rw, &rh))
+				{
+				pixbuf_draw_shadow(pixbuf,
+						   rx - x, ry - y, rw, rh,
+						   tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
+						   PAN_SHADOW_FADE,
+						   PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
+				}
+			if (util_clip_region(x, y, width, height,
+					     tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
+					     &rx, &ry, &rw, &rh))
+				{
+				pixbuf_draw_shadow(pixbuf,
+						   rx - x, ry - y, rw, rh,
+						   tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
+						   PAN_SHADOW_FADE,
+						   PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
+				}
+			}
+
+		if (util_clip_region(x, y, width, height,
+				     tx, ty, tw, th,
+				     &rx, &ry, &rw, &rh))
+			{
+			gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
+					     (gdouble) tx - x,
+					     (gdouble) ty - y,
+					     1.0, 1.0, GDK_INTERP_NEAREST,
+					     255);
+			}
+
+		if (util_clip_region(x, y, width, height,
+				     tx, ty, tw, PAN_OUTLINE_THICKNESS,
+				     &rx, &ry, &rw, &rh))
+			{
+			pixbuf_draw_rect_fill(pixbuf,
+					      rx - x, ry - y, rw, rh,
+					      PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
+			}
+		if (util_clip_region(x, y, width, height,
+				     tx, ty, PAN_OUTLINE_THICKNESS, th,
+				     &rx, &ry, &rw, &rh))
+			{
+			pixbuf_draw_rect_fill(pixbuf,
+					      rx - x, ry - y, rw, rh,
+					      PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
+			}
+		if (util_clip_region(x, y, width, height,
+				     tx + tw - PAN_OUTLINE_THICKNESS, ty +  PAN_OUTLINE_THICKNESS,
+				     PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
+				     &rx, &ry, &rw, &rh))
+			{
+			pixbuf_draw_rect_fill(pixbuf,
+					      rx - x, ry - y, rw, rh,
+					      PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
+			}
+		if (util_clip_region(x, y, width, height,
+				     tx +  PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
+				     tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
+				     &rx, &ry, &rw, &rh))
+			{
+			pixbuf_draw_rect_fill(pixbuf,
+					      rx - x, ry - y, rw, rh,
+					      PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
+			}
+		}
+	else
+		{
+		tw = pi->width - PAN_SHADOW_OFFSET * 2;
+		th = pi->height - PAN_SHADOW_OFFSET * 2;
+		tx = pi->x + PAN_SHADOW_OFFSET;
+		ty = pi->y + PAN_SHADOW_OFFSET;
+
+		if (util_clip_region(x, y, width, height,
+				     tx, ty, tw, th,
+				     &rx, &ry, &rw, &rh))
+			{
+			gint d;
+
+			d = (pw->size <= PAN_IMAGE_SIZE_THUMB_NONE) ? 2 : 8;
+			pixbuf_draw_rect_fill(pixbuf,
+					      rx - x, ry - y, rw, rh,
+					      PAN_SHADOW_COLOR,
+					      PAN_SHADOW_ALPHA / d);
+			}
+		}
+
+	return (pi->pixbuf == NULL);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * item image type
+ *-----------------------------------------------------------------------------
+ */
+
+static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
+{
+	GList *work;
+
+	pi->width = w;
+	pi->height = h;
+
+	if (!pi->fd) return;
+
+	work = pw->cache_list;
+	while (work)
+		{
+		PanCacheData *pc;
+
+		pc = work->data;
+		work = work->next;
+
+		if (pc->cd && pc->cd->dimensions &&
+		    pc->fd && pc->fd == pi->fd)
+			{
+			pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
+			pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
+
+			pw->cache_list = g_list_remove(pw->cache_list, pc);
+			cache_sim_data_free(pc->cd);
+			file_data_unref(pc->fd);
+			g_free(pc);
+			return;
+			}
+		}
+}
+
+PanItem *pan_item_image_new(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
+{
+	PanItem *pi;
+
+	pi = g_new0(PanItem, 1);
+	pi->type = PAN_ITEM_IMAGE;
+	pi->fd = fd;
+	pi->x = x;
+	pi->y = y;
+
+	pi->color_a = 255;
+
+	pi->color2_r = 0;
+	pi->color2_g = 0;
+	pi->color2_b = 0;
+	pi->color2_a = PAN_SHADOW_ALPHA / 2;
+
+	pan_item_image_find_size(pw, pi, w, h);
+
+	pw->list = g_list_prepend(pw->list, pi);
+
+	return pi;
+}
+
+gint pan_item_image_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
+			 gint x, gint y, gint width, gint height)
+{
+	gint rx, ry, rw, rh;
+
+	if (util_clip_region(x, y, width, height,
+			     pi->x, pi->y, pi->width, pi->height,
+			     &rx, &ry, &rw, &rh))
+		{
+		if (pi->pixbuf)
+			{
+			gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
+					     (gdouble) pi->x - x,
+					     (gdouble) pi->y - y,
+					     1.0, 1.0, GDK_INTERP_NEAREST,
+					     pi->color_a);
+			}
+		else
+			{
+			pixbuf_draw_rect_fill(pixbuf,
+					      rx - x, ry - y, rw, rh,
+					      pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
+			}
+		}
+
+	return (pi->pixbuf == NULL);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * item lookup/search
+ *-----------------------------------------------------------------------------
+ */
+
+PanItem *pan_item_find_by_key(PanWindow *pw, PanItemType type, const gchar *key)
+{
+	GList *work;
+
+	if (!key) return NULL;
+
+	work = g_list_last(pw->list);
+	while (work)
+		{
+		PanItem *pi;
+
+		pi = work->data;
+		if ((pi->type == type || type == PAN_ITEM_NONE) &&
+		     pi->key && strcmp(pi->key, key) == 0)
+			{
+			return pi;
+			}
+		work = work->prev;
+		}
+	work = g_list_last(pw->list_static);
+	while (work)
+		{
+		PanItem *pi;
+
+		pi = work->data;
+		if ((pi->type == type || type == PAN_ITEM_NONE) &&
+		     pi->key && strcmp(pi->key, key) == 0)
+			{
+			return pi;
+			}
+		work = work->prev;
+		}
+
+	return NULL;
+}
+
+/* when ignore_case and partial are TRUE, path should be converted to lower case */
+static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
+				      PanItemType type, const gchar *path,
+				      gboolean ignore_case, gboolean partial)
+{
+	GList *work;
+
+	work = g_list_last(search_list);
+	while (work)
+		{
+		PanItem *pi;
+
+		pi = work->data;
+		if ((pi->type == type || type == PAN_ITEM_NONE) && pi->fd)
+			{
+			gboolean match = FALSE;
+
+			if (path[0] == G_DIR_SEPARATOR)
+				{
+				if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
+				}
+			else if (pi->fd->name)
+				{
+				if (partial)
+					{
+					if (ignore_case)
+						{
+						gchar *haystack;
+
+						haystack = g_utf8_strdown(pi->fd->name, -1);
+						match = (strstr(haystack, path) != NULL);
+						g_free(haystack);
+						}
+					else
+						{
+						if (strstr(pi->fd->name, path)) match = TRUE;
+						}
+					}
+				else if (ignore_case)
+					{
+					if (g_ascii_strcasecmp(path, pi->fd->name) == 0) match = TRUE;
+					}
+				else
+					{
+					if (strcmp(path, pi->fd->name) == 0) match = TRUE;
+					}
+				}
+
+			if (match) list = g_list_prepend(list, pi);
+			}
+		work = work->prev;
+		}
+
+	return list;
+}
+
+/* when ignore_case and partial are TRUE, path should be converted to lower case */
+GList *pan_item_find_by_path(PanWindow *pw, PanItemType type, const gchar *path,
+			     gboolean ignore_case, gboolean partial)
+{
+	GList *list = NULL;
+
+	if (!path) return NULL;
+	if (partial && path[0] == G_DIR_SEPARATOR) return NULL;
+
+	list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
+	list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
+
+	return g_list_reverse(list);
+}
+
+GList *pan_item_find_by_fd(PanWindow *pw, PanItemType type, FileData *fd,
+			   gboolean ignore_case, gboolean partial)
+{
+	if (!fd) return NULL;
+	return pan_item_find_by_path(pw, type, fd->path, ignore_case, partial);
+}
+
+
+static PanItem *pan_item_find_by_coord_l(GList *list, PanItemType type, gint x, gint y, const gchar *key)
+{
+	GList *work;
+
+	work = list;
+	while (work)
+		{
+		PanItem *pi;
+
+		pi = work->data;
+		if ((pi->type == type || type == PAN_ITEM_NONE) &&
+		     x >= pi->x && x < pi->x + pi->width &&
+		     y >= pi->y && y < pi->y + pi->height &&
+		    (!key || (pi->key && strcmp(pi->key, key) == 0)))
+			{
+			return pi;
+			}
+		work = work->next;
+		}
+
+	return NULL;
+}
+
+PanItem *pan_item_find_by_coord(PanWindow *pw, PanItemType type,
+				gint x, gint y, const gchar *key)
+{
+	PanItem *pi;
+
+	pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
+	if (pi) return pi;
+
+	return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * text alignments
+ *-----------------------------------------------------------------------------
+ */
+
+PanTextAlignment *pan_text_alignment_new(PanWindow *pw, gint x, gint y, const gchar *key)
+{
+	PanTextAlignment *ta;
+
+	ta = g_new0(PanTextAlignment, 1);
+
+	ta->pw = pw;
+	ta->x = x;
+	ta->y = y;
+	ta->key = g_strdup(key);
+
+	return ta;
+}
+
+void pan_text_alignment_free(PanTextAlignment *ta)
+{
+	if (!ta) return;
+
+	g_list_free(ta->column1);
+	g_list_free(ta->column2);
+	g_free(ta->key);
+	g_free(ta);
+}
+
+PanItem *pan_text_alignment_add(PanTextAlignment *ta, const gchar *label, const gchar *text)
+{
+	PanItem *item;
+
+	if (label)
+		{
+		item = pan_item_text_new(ta->pw, ta->x, ta->y, label,
+					 PAN_TEXT_ATTR_BOLD, 0,
+					 PAN_POPUP_TEXT_COLOR, 255);
+		pan_item_set_key(item, ta->key);
+		}
+	else
+		{
+		item = NULL;
+		}
+	ta->column1 = g_list_append(ta->column1, item);
+
+	if (text)
+		{
+		item = pan_item_text_new(ta->pw, ta->x, ta->y, text,
+					 PAN_TEXT_ATTR_NONE, 0,
+					 PAN_POPUP_TEXT_COLOR, 255);
+		pan_item_set_key(item, ta->key);
+		}
+	else
+		{
+		item = NULL;
+		}
+	ta->column2 = g_list_append(ta->column2, item);
+
+	return item;
+}
+
+void pan_text_alignment_calc(PanTextAlignment *ta, PanItem *box)
+{
+	gint cw1, cw2;
+	gint x, y;
+	GList *work1;
+	GList *work2;
+
+	cw1 = 0;
+	cw2 = 0;
+
+	work1 = ta->column1;
+	while (work1)
+		{
+		PanItem *p;
+
+		p = work1->data;
+		work1 = work1->next;
+
+		if (p && p->width > cw1) cw1 = p->width;
+		}
+
+	work2 = ta->column2;
+	while (work2)
+		{
+		PanItem *p;
+
+		p = work2->data;
+		work2 = work2->next;
+
+		if (p && p->width > cw2) cw2 = p->width;
+		}
+
+	x = ta->x;
+	y = ta->y;
+	work1 = ta->column1;
+	work2 = ta->column2;
+	while (work1 && work2)
+		{
+		PanItem *p1;
+		PanItem *p2;
+		gint height = 0;
+
+		p1 = work1->data;
+		p2 = work2->data;
+		work1 = work1->next;
+		work2 = work2->next;
+
+		if (p1)
+			{
+			p1->x = x;
+			p1->y = y;
+			pan_item_size_by_item(box, p1, PREF_PAD_BORDER);
+			height = p1->height;
+			}
+		if (p2)
+			{
+			p2->x = x + cw1 + PREF_PAD_SPACE;
+			p2->y = y;
+			pan_item_size_by_item(box, p2, PREF_PAD_BORDER);
+			if (height < p2->height) height = p2->height;
+			}
+
+		if (!p1 && !p2) height = PREF_PAD_GROUP;
+
+		y += height;
+		}
+}
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-item.h	Sat Dec 24 13:44:25 2016 +0100
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PAN_VIEW_PAN_ITEM_H
+#define PAN_VIEW_PAN_ITEM_H
+
+#include "main.h"
+#include "pan-types.h"
+#include "pixbuf-renderer.h"
+
+void pan_item_free(PanItem *pi);
+
+void pan_item_set_key(PanItem *pi, const gchar *key);
+void pan_item_added(PanWindow *pw, PanItem *pi);
+void pan_item_remove(PanWindow *pw, PanItem *pi);
+
+// Determine sizes
+void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border);
+void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h);
+
+// Find items
+PanItem *pan_item_find_by_key(PanWindow *pw, PanItemType type, const gchar *key);
+GList *pan_item_find_by_path(PanWindow *pw, PanItemType type, const gchar *path,
+			     gboolean ignore_case, gboolean partial);
+GList *pan_item_find_by_fd(PanWindow *pw, PanItemType type, FileData *fd,
+			   gboolean ignore_case, gboolean partial);
+PanItem *pan_item_find_by_coord(PanWindow *pw, PanItemType type,
+				gint x, gint y, const gchar *key);
+
+// Item box type
+PanItem *pan_item_box_new(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
+			  gint border_size,
+			  guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
+			  guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a);
+void pan_item_box_shadow(PanItem *pi, gint offset, gint fade);
+gint pan_item_box_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
+		       gint x, gint y, gint width, gint height);
+
+// Item triangle type
+PanItem *pan_item_tri_new(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
+			  gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
+			  guint8 r, guint8 g, guint8 b, guint8 a);
+void pan_item_tri_border(PanItem *pi, gint borders,
+			 guint8 r, guint8 g, guint8 b, guint8 a);
+gint pan_item_tri_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
+		       gint x, gint y, gint width, gint height);
+
+// Item text type
+PanItem *pan_item_text_new(PanWindow *pw, gint x, gint y, const gchar *text,
+			   PanTextAttrType attr, PanBorderType border,
+			   guint8 r, guint8 g, guint8 b, guint8 a);
+gint pan_item_text_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
+			gint x, gint y, gint width, gint height);
+
+// Item thumbnail type
+PanItem *pan_item_thumb_new(PanWindow *pw, FileData *fd, gint x, gint y);
+gint pan_item_thumb_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
+			 gint x, gint y, gint width, gint height);
+
+// Item image type
+PanItem *pan_item_image_new(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h);
+gint pan_item_image_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
+			 gint x, gint y, gint width, gint height);
+
+// Alignment
+typedef struct _PanTextAlignment PanTextAlignment;
+struct _PanTextAlignment {
+	PanWindow *pw;
+
+	GList *column1;
+	GList *column2;
+
+	gint x;
+	gint y;
+	gchar *key;
+};
+
+PanTextAlignment *pan_text_alignment_new(PanWindow *pw, gint x, gint y, const gchar *key);
+void pan_text_alignment_free(PanTextAlignment *ta);
+
+PanItem *pan_text_alignment_add(PanTextAlignment *ta, const gchar *label, const gchar *text);
+void pan_text_alignment_calc(PanTextAlignment *ta, PanItem *box);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-timeline.c	Sat Dec 24 13:44:25 2016 +0100
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "pan-timeline.h"
+
+#include "pan-item.h"
+#include "pan-util.h"
+#include "pan-view.h"
+
+void pan_timeline_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height)
+{
+	GList *list;
+	GList *work;
+	gint x, y;
+	time_t tc;
+	gint total;
+	gint count;
+	PanItem *pi_month = NULL;
+	PanItem *pi_day = NULL;
+	gint month_start;
+	gint day_start;
+	gint x_width;
+	gint y_height;
+
+	list = pan_list_tree(dir_fd, SORT_NONE, TRUE, pw->ignore_symlinks);
+
+	if (pw->cache_list && pw->exif_date_enable)
+		{
+		pw->cache_list = pan_cache_sort(pw->cache_list, SORT_NAME, TRUE);
+		list = filelist_sort(list, SORT_NAME, TRUE);
+		pan_cache_sync_date(pw, list);
+		}
+
+	pw->cache_list = pan_cache_sort(pw->cache_list, SORT_TIME, TRUE);
+	list = filelist_sort(list, SORT_TIME, TRUE);
+
+	*width = PAN_BOX_BORDER * 2;
+	*height = PAN_BOX_BORDER * 2;
+
+	x = 0;
+	y = 0;
+	month_start = y;
+	day_start = month_start;
+	x_width = 0;
+	y_height = 0;
+	tc = 0;
+	total = 0;
+	count = 0;
+	work = list;
+	while (work)
+		{
+		FileData *fd;
+		PanItem *pi;
+
+		fd = work->data;
+		work = work->next;
+
+		if (!pan_date_compare(fd->date, tc, PAN_DATE_LENGTH_DAY))
+			{
+			GList *needle;
+			gchar *buf;
+
+			if (!pan_date_compare(fd->date, tc, PAN_DATE_LENGTH_MONTH))
+				{
+				pi_day = NULL;
+
+				if (pi_month)
+					{
+					x = pi_month->x + pi_month->width + PAN_BOX_BORDER;
+					}
+				else
+					{
+					x = PAN_BOX_BORDER;
+					}
+
+				y = PAN_BOX_BORDER;
+
+				buf = pan_date_value_string(fd->date, PAN_DATE_LENGTH_MONTH);
+				pi = pan_item_text_new(pw, x, y, buf,
+						       PAN_TEXT_ATTR_BOLD | PAN_TEXT_ATTR_HEADING,
+						       PAN_TEXT_BORDER_SIZE,
+						       PAN_TEXT_COLOR, 255);
+				g_free(buf);
+				y += pi->height;
+
+				pi_month = pan_item_box_new(pw, file_data_ref(fd),
+							    x, y, 0, 0,
+							    PAN_BOX_OUTLINE_THICKNESS,
+							    PAN_BOX_COLOR, PAN_BOX_ALPHA,
+							    PAN_BOX_OUTLINE_COLOR, PAN_BOX_OUTLINE_ALPHA);
+
+				x += PAN_BOX_BORDER;
+				y += PAN_BOX_BORDER;
+				month_start = y;
+				}
+
+			if (pi_day) x = pi_day->x + pi_day->width + PAN_BOX_BORDER;
+
+			tc = fd->date;
+			total = 1;
+			count = 0;
+
+			needle = work;
+			while (needle)
+				{
+				FileData *nfd;
+
+				nfd = needle->data;
+				if (pan_date_compare(nfd->date, tc, PAN_DATE_LENGTH_DAY))
+					{
+					needle = needle->next;
+					total++;
+					}
+				else
+					{
+					needle = NULL;
+					}
+				}
+
+			buf = pan_date_value_string(fd->date, PAN_DATE_LENGTH_WEEK);
+			pi = pan_item_text_new(pw, x, y, buf, PAN_TEXT_ATTR_NONE,
+					       PAN_TEXT_BORDER_SIZE,
+					       PAN_TEXT_COLOR, 255);
+			g_free(buf);
+
+			y += pi->height;
+
+			pi_day = pan_item_box_new(pw, file_data_ref(fd), x, y, 0, 0,
+						  PAN_BOX_OUTLINE_THICKNESS,
+						  PAN_BOX_COLOR, PAN_BOX_ALPHA,
+						  PAN_BOX_OUTLINE_COLOR, PAN_BOX_OUTLINE_ALPHA);
+
+			x += PAN_BOX_BORDER;
+			y += PAN_BOX_BORDER;
+			day_start = y;
+			}
+
+		if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE)
+			{
+			pi = pan_item_image_new(pw, fd, x, y, 10, 10);
+			if (pi->width > x_width) x_width = pi->width;
+			y_height = pi->height;
+			}
+		else
+			{
+			pi = pan_item_thumb_new(pw, fd, x, y);
+			x_width = PAN_THUMB_SIZE;
+			y_height = PAN_THUMB_SIZE;
+			}
+
+		pan_item_size_by_item(pi_day, pi, PAN_BOX_BORDER);
+		pan_item_size_by_item(pi_month, pi_day, PAN_BOX_BORDER);
+
+		total--;
+		count++;
+
+		if (total > 0 && count < PAN_GROUP_MAX)
+			{
+			y += y_height + PAN_THUMB_GAP;
+			}
+		else
+			{
+			x += x_width + PAN_THUMB_GAP;
+			x_width = 0;
+			count = 0;
+
+			if (total > 0)
+				y = day_start;
+			else
+				y = month_start;
+			}
+
+		pan_item_size_coordinates(pi_month, PAN_BOX_BORDER, width, height);
+		}
+
+	g_list_free(list);
+}
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-timeline.h	Sat Dec 24 13:44:25 2016 +0100
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PAN_VIEW_PAN_TIMELINE_H
+#define PAN_VIEW_PAN_TIMELINE_H
+
+#include "main.h"
+#include "pan-types.h"
+
+void pan_timeline_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-types.h	Sat Dec 24 13:44:25 2016 +0100
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PAN_VIEW_PAN_TYPES_H
+#define PAN_VIEW_PAN_TYPES_H
+
+#include "cache-loader.h"
+#include "filedata.h"
+
+/* thumbnail sizes and spacing */
+
+#define PAN_THUMB_SIZE_DOTS 4
+#define PAN_THUMB_SIZE_NONE 24
+#define PAN_THUMB_SIZE_SMALL 64
+#define PAN_THUMB_SIZE_NORMAL 128
+#define PAN_THUMB_SIZE_LARGE 256
+#define PAN_THUMB_SIZE pw->thumb_size
+
+#define PAN_THUMB_GAP_DOTS 2
+#define PAN_THUMB_GAP_SMALL 14
+#define PAN_THUMB_GAP_NORMAL 30
+#define PAN_THUMB_GAP_LARGE 40
+#define PAN_THUMB_GAP_HUGE 50
+#define PAN_THUMB_GAP pw->thumb_gap
+
+/* basic sizes, colors, spacings */
+
+#define PAN_SHADOW_OFFSET 6
+#define PAN_SHADOW_FADE 5
+#define PAN_SHADOW_COLOR 0, 0, 0
+#define PAN_SHADOW_ALPHA 64
+
+#define PAN_OUTLINE_THICKNESS 1
+#define PAN_OUTLINE_COLOR_1 255, 255, 255
+#define PAN_OUTLINE_COLOR_2 64, 64, 64
+#define PAN_OUTLINE_ALPHA 180
+
+#define PAN_BACKGROUND_COLOR 150, 150, 150
+
+#define PAN_GRID_SIZE 60
+#define PAN_GRID_COLOR 0, 0, 0
+#define PAN_GRID_ALPHA 20
+
+#define PAN_BOX_COLOR 255, 255, 255
+#define PAN_BOX_ALPHA 100
+#define PAN_BOX_BORDER 20
+
+#define PAN_BOX_OUTLINE_THICKNESS 4
+#define PAN_BOX_OUTLINE_COLOR 0, 0, 0
+#define PAN_BOX_OUTLINE_ALPHA 128
+
+#define PAN_TEXT_BORDER_SIZE 4
+#define PAN_TEXT_COLOR 0, 0, 0
+
+/* popup info box */
+
+#define PAN_POPUP_COLOR 255, 255, 225
+#define PAN_POPUP_ALPHA 255
+#define PAN_POPUP_BORDER 1
+#define PAN_POPUP_BORDER_COLOR 0, 0, 0
+#define PAN_POPUP_TEXT_COLOR 0, 0, 0
+
+
+#define PAN_GROUP_MAX 16
+
+
+
+typedef enum {
+	PAN_LAYOUT_TIMELINE = 0,
+	PAN_LAYOUT_CALENDAR,
+	PAN_LAYOUT_FOLDERS_LINEAR,
+	PAN_LAYOUT_FOLDERS_FLOWER,
+	PAN_LAYOUT_GRID,
+	PAN_LAYOUT_COUNT
+} PanLayoutType;
+
+typedef enum {
+	PAN_IMAGE_SIZE_THUMB_DOTS = 0,
+	PAN_IMAGE_SIZE_THUMB_NONE,
+	PAN_IMAGE_SIZE_THUMB_SMALL,
+	PAN_IMAGE_SIZE_THUMB_NORMAL,
+	PAN_IMAGE_SIZE_THUMB_LARGE,
+	PAN_IMAGE_SIZE_10,
+	PAN_IMAGE_SIZE_25,
+	PAN_IMAGE_SIZE_33,
+	PAN_IMAGE_SIZE_50,
+	PAN_IMAGE_SIZE_100,
+	PAN_IMAGE_SIZE_COUNT
+} PanImageSize;
+
+typedef enum {
+	PAN_ITEM_NONE,
+	PAN_ITEM_THUMB,
+	PAN_ITEM_BOX,
+	PAN_ITEM_TRIANGLE,
+	PAN_ITEM_TEXT,
+	PAN_ITEM_IMAGE
+} PanItemType;
+
+typedef enum {
+	PAN_TEXT_ATTR_NONE = 0,
+	PAN_TEXT_ATTR_BOLD = 1 << 0,
+	PAN_TEXT_ATTR_HEADING = 1 << 1,
+	PAN_TEXT_ATTR_MARKUP = 1 << 2
+} PanTextAttrType;
+
+typedef enum {
+	PAN_BORDER_NONE = 0,
+	PAN_BORDER_1 = 1 << 0,
+	PAN_BORDER_2 = 1 << 1,
+	PAN_BORDER_3 = 1 << 2,
+	PAN_BORDER_4 = 1 << 3
+} PanBorderType;
+
+#define PAN_BORDER_TOP		PAN_BORDER_1
+#define PAN_BORDER_RIGHT		PAN_BORDER_2
+#define PAN_BORDER_BOTTOM	PAN_BORDER_3
+#define PAN_BORDER_LEFT		PAN_BORDER_4
+
+
+typedef struct _PanItem PanItem;
+struct _PanItem {
+	PanItemType type;
+	gint x;
+	gint y;
+	gint width;
+	gint height;
+	gchar *key;
+
+	FileData *fd;
+
+	GdkPixbuf *pixbuf;
+	gint refcount;
+
+	gchar *text;
+	PanTextAttrType text_attr;
+
+	guint8 color_r;
+	guint8 color_g;
+	guint8 color_b;
+	guint8 color_a;
+
+	guint8 color2_r;
+	guint8 color2_g;
+	guint8 color2_b;
+	guint8 color2_a;
+	gint border;
+
+	gpointer data;
+
+	gboolean queued;
+};
+
+typedef struct _PanWindow PanWindow;
+struct _PanWindow
+{
+	GtkWidget *window;
+	ImageWindow *imd;
+	ImageWindow *imd_normal;
+	FullScreenData *fs;
+
+	GtkWidget *path_entry;
+
+	GtkWidget *label_message;
+	GtkWidget *label_zoom;
+
+	GtkWidget *search_box;
+	GtkWidget *search_entry;
+	GtkWidget *search_label;
+	GtkWidget *search_button;
+	GtkWidget *search_button_arrow;
+
+	GtkWidget *date_button;
+
+	GtkWidget *scrollbar_h;
+	GtkWidget *scrollbar_v;
+
+	FileData *dir_fd;
+	PanLayoutType layout;
+	PanImageSize size;
+	gint thumb_size;
+	gint thumb_gap;
+	gint image_size;
+	gboolean exif_date_enable;
+
+	gint info_image_size;
+	gboolean info_includes_exif;
+
+	gboolean ignore_symlinks;
+
+	GList *list;
+	GList *list_static;
+	GList *list_grid;
+
+	GList *cache_list;
+	GList *cache_todo;
+	gint cache_count;
+	gint cache_total;
+	gint cache_tick;
+	CacheLoader *cache_cl;
+
+	ImageLoader *il;
+	ThumbLoader *tl;
+	PanItem *queue_pi;
+	GList *queue;
+
+	PanItem *click_pi;
+	PanItem *search_pi;
+
+	gint idle_id;
+};
+
+typedef struct _PanGrid PanGrid;
+struct _PanGrid {
+	gint x;
+	gint y;
+	gint w;
+	gint h;
+	GList *list;
+};
+
+typedef struct _PanCacheData PanCacheData;
+struct _PanCacheData {
+	FileData *fd;
+	CacheData *cd;
+};
+
+#endif
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-util.c	Sat Dec 24 13:44:25 2016 +0100
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "pan-util.h"
+
+#include "ui_fileops.h"
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * date functions
+ *-----------------------------------------------------------------------------
+ */
+
+gboolean pan_date_compare(time_t a, time_t b, PanDateLengthType length)
+{
+	struct tm ta;
+	struct tm tb;
+
+	if (length == PAN_DATE_LENGTH_EXACT) return (a == b);
+
+	if (!localtime_r(&a, &ta) ||
+	    !localtime_r(&b, &tb)) return FALSE;
+
+	if (ta.tm_year != tb.tm_year) return FALSE;
+	if (length == PAN_DATE_LENGTH_YEAR) return TRUE;
+
+	if (ta.tm_mon != tb.tm_mon) return FALSE;
+	if (length == PAN_DATE_LENGTH_MONTH) return TRUE;
+
+	if (length == PAN_DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
+
+	if (ta.tm_mday != tb.tm_mday) return FALSE;
+	if (length == PAN_DATE_LENGTH_DAY) return TRUE;
+
+	return (ta.tm_hour == tb.tm_hour);
+}
+
+gint pan_date_value(time_t d, PanDateLengthType length)
+{
+	struct tm td;
+
+	if (!localtime_r(&d, &td)) return -1;
+
+	switch (length)
+		{
+		case PAN_DATE_LENGTH_DAY:
+			return td.tm_mday;
+			break;
+		case PAN_DATE_LENGTH_WEEK:
+			return td.tm_wday;
+			break;
+		case PAN_DATE_LENGTH_MONTH:
+			return td.tm_mon + 1;
+			break;
+		case PAN_DATE_LENGTH_YEAR:
+			return td.tm_year + 1900;
+			break;
+		case PAN_DATE_LENGTH_EXACT:
+		default:
+			break;
+		}
+
+	return -1;
+}
+
+gchar *pan_date_value_string(time_t d, PanDateLengthType length)
+{
+	struct tm td;
+	gchar buf[128];
+	gchar *format = NULL;
+
+	if (!localtime_r(&d, &td)) return g_strdup("");
+
+	switch (length)
+		{
+		case PAN_DATE_LENGTH_DAY:
+			return g_strdup_printf("%d", td.tm_mday);
+			break;
+		case PAN_DATE_LENGTH_WEEK:
+			format = "%A %e";
+			break;
+		case PAN_DATE_LENGTH_MONTH:
+			format = "%B %Y";
+			break;
+		case PAN_DATE_LENGTH_YEAR:
+			return g_strdup_printf("%d", td.tm_year + 1900);
+			break;
+		case PAN_DATE_LENGTH_EXACT:
+		default:
+			return g_strdup(text_from_time(d));
+			break;
+		}
+
+
+	if (format && strftime(buf, sizeof(buf), format, &td) > 0)
+		{
+		gchar *ret = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
+		if (ret) return ret;
+		}
+
+	return g_strdup("");
+}
+
+time_t pan_date_to_time(gint year, gint month, gint day)
+{
+	struct tm lt;
+
+	lt.tm_sec = 0;
+	lt.tm_min = 0;
+	lt.tm_hour = 0;
+	lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
+	lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
+	lt.tm_year = year - 1900;
+	lt.tm_isdst = 0;
+
+	return mktime(&lt);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * folder validation
+ *-----------------------------------------------------------------------------
+ */
+
+gboolean pan_is_link_loop(const gchar *s)
+{
+	gchar *sl;
+	struct stat st;
+	gboolean ret = FALSE;
+
+	sl = path_from_utf8(s);
+
+	if (lstat(sl, &st) == 0 && S_ISLNK(st.st_mode))
+		{
+		gchar *buf;
+		gint l;
+
+		buf = g_malloc(st.st_size + 1);
+		l = readlink(sl, buf, st.st_size);
+		if (l == st.st_size)
+			{
+			buf[l] = '\0';
+
+			parse_out_relatives(buf);
+			l = strlen(buf);
+
+			parse_out_relatives(sl);
+
+			if (buf[0] == G_DIR_SEPARATOR)
+				{
+				if (strncmp(sl, buf, l) == 0 &&
+				    (sl[l] == '\0' || sl[l] == G_DIR_SEPARATOR || l == 1)) ret = TRUE;
+				}
+			else
+				{
+				gchar *link_path;
+
+				link_path = g_build_filename(sl, buf, NULL);
+				parse_out_relatives(link_path);
+
+				if (strncmp(sl, link_path, l) == 0 &&
+				    (sl[l] == '\0' || sl[l] == G_DIR_SEPARATOR || l == 1)) ret = TRUE;
+
+				g_free(link_path);
+				}
+			}
+
+		g_free(buf);
+		}
+
+	g_free(sl);
+
+	return ret;
+}
+
+gboolean pan_is_ignored(const gchar *s, gboolean ignore_symlinks)
+{
+	struct stat st;
+	const gchar *n;
+
+	if (!lstat_utf8(s, &st)) return TRUE;
+
+#if 0
+	/* normal filesystems have directories with some size or block allocation,
+	 * special filesystems (like linux /proc) set both to zero.
+	 * enable this check if you enable listing the root "/" folder
+	 */
+	if (st.st_size == 0 && st.st_blocks == 0) return TRUE;
+#endif
+
+	if (S_ISLNK(st.st_mode) && (ignore_symlinks || pan_is_link_loop(s))) return TRUE;
+
+	n = filename_from_path(s);
+	if (n && strcmp(n, GQ_RC_DIR) == 0) return TRUE;
+
+	return FALSE;
+}
+
+GList *pan_list_tree(FileData *dir_fd, SortType sort, gboolean ascend,
+		     gboolean ignore_symlinks)
+{
+	GList *flist;
+	GList *dlist;
+	GList *result;
+	GList *folders;
+
+	filelist_read(dir_fd, &flist, &dlist);
+	if (sort != SORT_NONE)
+		{
+		flist = filelist_sort(flist, sort, ascend);
+		dlist = filelist_sort(dlist, sort, ascend);
+		}
+
+	result = flist;
+	folders = dlist;
+	while (folders)
+		{
+		FileData *fd;
+
+		fd = folders->data;
+		folders = g_list_remove(folders, fd);
+
+		if (!pan_is_ignored(fd->path, ignore_symlinks) &&
+		    filelist_read(fd, &flist, &dlist))
+			{
+			if (sort != SORT_NONE)
+				{
+				flist = filelist_sort(flist, sort, ascend);
+				dlist = filelist_sort(dlist, sort, ascend);
+				}
+
+			result = g_list_concat(result, flist);
+			folders = g_list_concat(dlist, folders);
+			}
+
+		file_data_unref(fd);
+		}
+
+	return result;
+}
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-util.h	Sat Dec 24 13:44:25 2016 +0100
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PAN_VIEW_PAN_UTIL_H
+#define PAN_VIEW_PAN_UTIL_H
+
+#include "main.h"
+#include "pan-types.h"
+
+typedef enum {
+	PAN_DATE_LENGTH_EXACT,
+	PAN_DATE_LENGTH_HOUR,
+	PAN_DATE_LENGTH_DAY,
+	PAN_DATE_LENGTH_WEEK,
+	PAN_DATE_LENGTH_MONTH,
+	PAN_DATE_LENGTH_YEAR
+} PanDateLengthType;
+
+gboolean pan_date_compare(time_t a, time_t b, PanDateLengthType length);
+gint pan_date_value(time_t d, PanDateLengthType length);
+gchar *pan_date_value_string(time_t d,  PanDateLengthType length);
+time_t pan_date_to_time(gint year, gint month, gint day);
+
+gboolean pan_is_link_loop(const gchar *s);
+gboolean pan_is_ignored(const gchar *s, gboolean ignore_symlinks);
+GList *pan_list_tree(FileData *dir_fd, SortType sort, gboolean ascend,
+		     gboolean ignore_symlinks);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-view.c	Sat Dec 24 13:44:25 2016 +0100
@@ -0,0 +1,2779 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "pan-view.h"
+
+#include "bar_exif.h"
+#include "dnd.h"
+#include "editors.h"
+#include "exif.h"
+#include "fullscreen.h"
+#include "history_list.h"
+#include "image.h"
+#include "img-view.h"
+#include "menu.h"
+#include "metadata.h"
+#include "misc.h"
+#include "pan-calendar.h"
+#include "pan-folder.h"
+#include "pan-grid.h"
+#include "pan-item.h"
+#include "pan-timeline.h"
+#include "pan-util.h"
+#include "pixbuf-renderer.h"
+#include "pixbuf_util.h"
+#include "thumb.h"
+#include "ui_fileops.h"
+#include "ui_menu.h"
+#include "ui_misc.h"
+#include "ui_tabcomp.h"
+#include "ui_utildlg.h"
+#include "uri_utils.h"
+#include "utilops.h"
+#include "window.h"
+
+#include <gdk/gdkkeysyms.h> /* for keyboard values */
+
+#include <math.h>
+
+
+#define PAN_WINDOW_DEFAULT_WIDTH 720
+#define PAN_WINDOW_DEFAULT_HEIGHT 500
+
+#define PAN_TILE_SIZE 512
+
+#define ZOOM_INCREMENT 1.0
+#define ZOOM_LABEL_WIDTH 64
+
+
+#define PAN_PREF_GROUP		"pan_view_options"
+#define PAN_PREF_HIDE_WARNING	"hide_performance_warning"
+#define PAN_PREF_EXIF_PAN_DATE	"use_exif_date"
+#define PAN_PREF_INFO_IMAGE	"info_image_size"
+#define PAN_PREF_INFO_EXIF	"info_includes_exif"
+
+
+static GList *pan_window_list = NULL;
+
+
+static void pan_layout_update_idle(PanWindow *pw);
+
+static void pan_fullscreen_toggle(PanWindow *pw, gboolean force_off);
+
+static void pan_search_toggle_visible(PanWindow *pw, gboolean enable);
+static void pan_search_activate(PanWindow *pw);
+
+static void pan_window_close(PanWindow *pw);
+
+static GtkWidget *pan_popup_menu(PanWindow *pw);
+
+static void pan_window_dnd_init(PanWindow *pw);
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * the image/thumb loader queue
+ *-----------------------------------------------------------------------------
+ */
+
+static gboolean pan_queue_step(PanWindow *pw);
+
+
+static void pan_queue_thumb_done_cb(ThumbLoader *tl, gpointer data)
+{
+	PanWindow *pw = data;
+
+	if (pw->queue_pi)
+		{
+		PanItem *pi;
+		gint rc;
+
+		pi = pw->queue_pi;
+		pw->queue_pi = NULL;
+
+		pi->queued = FALSE;
+
+		if (pi->pixbuf) g_object_unref(pi->pixbuf);
+		pi->pixbuf = thumb_loader_get_pixbuf(tl);
+
+		rc = pi->refcount;
+		image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
+		pi->refcount = rc;
+		}
+
+	thumb_loader_free(pw->tl);
+	pw->tl = NULL;
+
+	while (pan_queue_step(pw));
+}
+
+static void pan_queue_image_done_cb(ImageLoader *il, gpointer data)
+{
+	PanWindow *pw = data;
+
+	if (pw->queue_pi)
+		{
+		PanItem *pi;
+		gint rc;
+
+		pi = pw->queue_pi;
+		pw->queue_pi = NULL;
+
+		pi->queued = FALSE;
+
+		if (pi->pixbuf) g_object_unref(pi->pixbuf);
+		pi->pixbuf = image_loader_get_pixbuf(pw->il);
+		if (pi->pixbuf) g_object_ref(pi->pixbuf);
+
+		if (pi->pixbuf && pw->size != PAN_IMAGE_SIZE_100 &&
+		    (gdk_pixbuf_get_width(pi->pixbuf) > pi->width ||
+		     gdk_pixbuf_get_height(pi->pixbuf) > pi->height))
+			{
+			GdkPixbuf *tmp;
+
+			tmp = pi->pixbuf;
+			pi->pixbuf = gdk_pixbuf_scale_simple(tmp, pi->width, pi->height,
+							     (GdkInterpType)options->image.zoom_quality);
+			g_object_unref(tmp);
+			}
+
+		rc = pi->refcount;
+		image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
+		pi->refcount = rc;
+		}
+
+	image_loader_free(pw->il);
+	pw->il = NULL;
+
+	while (pan_queue_step(pw));
+}
+
+static gboolean pan_queue_step(PanWindow *pw)
+{
+	PanItem *pi;
+
+	if (!pw->queue) return FALSE;
+
+	pi = pw->queue->data;
+	pw->queue = g_list_remove(pw->queue, pi);
+	pw->queue_pi = pi;
+
+	if (!pw->queue_pi->fd)
+		{
+		pw->queue_pi->queued = FALSE;
+		pw->queue_pi = NULL;
+		return TRUE;
+		}
+
+	image_loader_free(pw->il);
+	pw->il = NULL;
+	thumb_loader_free(pw->tl);
+	pw->tl = NULL;
+
+	if (pi->type == PAN_ITEM_IMAGE)
+		{
+		pw->il = image_loader_new(pi->fd);
+
+		if (pw->size != PAN_IMAGE_SIZE_100)
+			{
+			image_loader_set_requested_size(pw->il, pi->width, pi->height);
+			}
+
+		g_signal_connect(G_OBJECT(pw->il), "error", (GCallback)pan_queue_image_done_cb, pw);
+		g_signal_connect(G_OBJECT(pw->il), "done", (GCallback)pan_queue_image_done_cb, pw);
+
+		if (image_loader_start(pw->il)) return FALSE;
+
+		image_loader_free(pw->il);
+		pw->il = NULL;
+		}
+	else if (pi->type == PAN_ITEM_THUMB)
+		{
+		pw->tl = thumb_loader_new(PAN_THUMB_SIZE, PAN_THUMB_SIZE);
+
+		if (!pw->tl->standard_loader)
+			{
+			/* The classic loader will recreate a thumbnail any time we
+			 * request a different size than what exists. This view will
+			 * almost never use the user configured sizes so disable cache.
+			 */
+			thumb_loader_set_cache(pw->tl, FALSE, FALSE, FALSE);
+			}
+
+		thumb_loader_set_callbacks(pw->tl,
+					   pan_queue_thumb_done_cb,
+					   pan_queue_thumb_done_cb,
+					   NULL, pw);
+
+		if (thumb_loader_start(pw->tl, pi->fd)) return FALSE;
+
+		thumb_loader_free(pw->tl);
+		pw->tl = NULL;
+		}
+
+	pw->queue_pi->queued = FALSE;
+	pw->queue_pi = NULL;
+	return TRUE;
+}
+
+static void pan_queue_add(PanWindow *pw, PanItem *pi)
+{
+	if (!pi || pi->queued || pi->pixbuf) return;
+	if (pw->size <= PAN_IMAGE_SIZE_THUMB_NONE &&
+	    (!pi->key || strcmp(pi->key, "info") != 0) )
+		{
+		return;
+		}
+
+	pi->queued = TRUE;
+	pw->queue = g_list_prepend(pw->queue, pi);
+
+	if (!pw->tl && !pw->il) while (pan_queue_step(pw));
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * tile request/dispose handlers
+ *-----------------------------------------------------------------------------
+ */
+
+static gboolean pan_window_request_tile_cb(PixbufRenderer *pr, gint x, gint y,
+				       	   gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
+{
+	PanWindow *pw = data;
+	GList *list;
+	GList *work;
+	gint i;
+
+	pixbuf_set_rect_fill(pixbuf,
+			     0, 0, width, height,
+			     PAN_BACKGROUND_COLOR, 255);
+
+	for (i = (x / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < x + width; i += PAN_GRID_SIZE)
+		{
+		gint rx, ry, rw, rh;
+
+		if (util_clip_region(x, y, width, height,
+				     i, y, 1, height,
+				     &rx, &ry, &rw, &rh))
+			{
+			pixbuf_draw_rect_fill(pixbuf,
+					      rx - x, ry - y, rw, rh,
+					      PAN_GRID_COLOR, PAN_GRID_ALPHA);
+			}
+		}
+	for (i = (y / PAN_GRID_SIZE) * PAN_GRID_SIZE; i < y + height; i += PAN_GRID_SIZE)
+		{
+		gint rx, ry, rw, rh;
+
+		if (util_clip_region(x, y, width, height,
+				     x, i, width, 1,
+				     &rx, &ry, &rw, &rh))
+			{
+			pixbuf_draw_rect_fill(pixbuf,
+					      rx - x, ry - y, rw, rh,
+					      PAN_GRID_COLOR, PAN_GRID_ALPHA);
+			}
+		}
+
+	list = pan_layout_intersect(pw, x, y, width, height);
+	work = list;
+	while (work)
+		{
+		PanItem *pi;
+		gboolean queue = FALSE;
+
+		pi = work->data;
+		work = work->next;
+
+		pi->refcount++;
+
+		switch (pi->type)
+			{
+			case PAN_ITEM_BOX:
+				queue = pan_item_box_draw(pw, pi, pixbuf, pr, x, y, width, height);
+				break;
+			case PAN_ITEM_TRIANGLE:
+				queue = pan_item_tri_draw(pw, pi, pixbuf, pr, x, y, width, height);
+				break;
+			case PAN_ITEM_TEXT:
+				queue = pan_item_text_draw(pw, pi, pixbuf, pr, x, y, width, height);
+				break;
+			case PAN_ITEM_THUMB:
+				queue = pan_item_thumb_draw(pw, pi, pixbuf, pr, x, y, width, height);
+				break;
+			case PAN_ITEM_IMAGE:
+				queue = pan_item_image_draw(pw, pi, pixbuf, pr, x, y, width, height);
+				break;
+			case PAN_ITEM_NONE:
+			default:
+				break;
+			}
+
+		if (queue) pan_queue_add(pw, pi);
+		}
+
+	g_list_free(list);
+
+	return TRUE;
+}
+
+static void pan_window_dispose_tile_cb(PixbufRenderer *pr, gint x, gint y,
+				       gint width, gint height, GdkPixbuf *pixbuf, gpointer data)
+{
+	PanWindow *pw = data;
+	GList *list;
+	GList *work;
+
+	list = pan_layout_intersect(pw, x, y, width, height);
+	work = list;
+	while (work)
+		{
+		PanItem *pi;
+
+		pi = work->data;
+		work = work->next;
+
+		if (pi->refcount > 0)
+			{
+			pi->refcount--;
+
+			if (pi->refcount == 0)
+				{
+				if (pi->queued)
+					{
+					pw->queue = g_list_remove(pw->queue, pi);
+					pi->queued = FALSE;
+					}
+				if (pw->queue_pi == pi) pw->queue_pi = NULL;
+				if (pi->pixbuf)
+					{
+					g_object_unref(pi->pixbuf);
+					pi->pixbuf = NULL;
+					}
+				}
+			}
+		}
+
+	g_list_free(list);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * misc
+ *-----------------------------------------------------------------------------
+ */
+
+static void pan_window_message(PanWindow *pw, const gchar *text)
+{
+	GList *work;
+	gint count = 0;
+	gint64 size = 0;
+	gchar *ss;
+	gchar *buf;
+
+	if (text)
+		{
+		gtk_label_set_text(GTK_LABEL(pw->label_message), text);
+		return;
+		}
+
+	work = pw->list_static;
+	if (pw->layout == PAN_LAYOUT_CALENDAR)
+		{
+		while (work)
+			{
+			PanItem *pi;
+
+			pi = work->data;
+			work = work->next;
+
+			if (pi->fd &&
+			    pi->type == PAN_ITEM_BOX &&
+			    pi->key && strcmp(pi->key, "dot") == 0)
+				{
+				size += pi->fd->size;
+				count++;
+				}
+			}
+		}
+	else
+		{
+		while (work)
+			{
+			PanItem *pi;
+
+			pi = work->data;
+			work = work->next;
+
+			if (pi->fd &&
+			    (pi->type == PAN_ITEM_THUMB || pi->type == PAN_ITEM_IMAGE))
+				{
+				size += pi->fd->size;
+				count++;
+				}
+			}
+		}
+
+	ss = text_from_size_abrev(size);
+	buf = g_strdup_printf(_("%d images, %s"), count, ss);
+	g_free(ss);
+	gtk_label_set_text(GTK_LABEL(pw->label_message), buf);
+	g_free(buf);
+}
+
+static void pan_warning_folder(const gchar *path, GtkWidget *parent)
+{
+	gchar *message;
+
+	message = g_strdup_printf(_("The pan view does not support the folder \"%s\"."), path);
+	warning_dialog(_("Folder not supported"), message,
+		      GTK_STOCK_DIALOG_INFO, parent);
+	g_free(message);
+}
+
+static void pan_window_zoom_limit(PanWindow *pw)
+{
+	gdouble min;
+
+	switch (pw->size)
+		{
+		case PAN_IMAGE_SIZE_THUMB_DOTS:
+		case PAN_IMAGE_SIZE_THUMB_NONE:
+		case PAN_IMAGE_SIZE_THUMB_SMALL:
+		case PAN_IMAGE_SIZE_THUMB_NORMAL:
+#if 0
+			/* easily requires > 512mb ram when window size > 1024x768 and zoom is <= -8 */
+			min = -16.0;
+			break;
+#endif
+		case PAN_IMAGE_SIZE_THUMB_LARGE:
+			min = -6.0;
+			break;
+		case PAN_IMAGE_SIZE_10:
+		case PAN_IMAGE_SIZE_25:
+			min = -4.0;
+			break;
+		case PAN_IMAGE_SIZE_33:
+		case PAN_IMAGE_SIZE_50:
+		case PAN_IMAGE_SIZE_100:
+		default:
+			min = -2.0;
+			break;
+		}
+
+	image_zoom_set_limits(pw->imd, min, 32.0);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * cache
+ *-----------------------------------------------------------------------------
+ */
+
+static gint pan_cache_sort_file_cb(gpointer a, gpointer b)
+{
+	PanCacheData *pca = a;
+	PanCacheData *pcb = b;
+	return filelist_sort_compare_filedata(pca->fd, pcb->fd);
+}
+GList *pan_cache_sort(GList *list, SortType method, gboolean ascend)
+{
+	return filelist_sort_full(list, method, ascend, (GCompareFunc) pan_cache_sort_file_cb);
+}
+
+
+static void pan_cache_free(PanWindow *pw)
+{
+	GList *work;
+
+	work = pw->cache_list;
+	while (work)
+		{
+		PanCacheData *pc;
+
+		pc = work->data;
+		work = work->next;
+
+		cache_sim_data_free(pc->cd);
+		file_data_unref(pc->fd);
+		g_free(pc);
+		}
+
+	g_list_free(pw->cache_list);
+	pw->cache_list = NULL;
+
+	filelist_free(pw->cache_todo);
+	pw->cache_todo = NULL;
+
+	pw->cache_count = 0;
+	pw->cache_total = 0;
+	pw->cache_tick = 0;
+
+	cache_loader_free(pw->cache_cl);
+	pw->cache_cl = NULL;
+}
+
+static void pan_cache_fill(PanWindow *pw, FileData *dir_fd)
+{
+	GList *list;
+
+	pan_cache_free(pw);
+
+	list = pan_list_tree(dir_fd, SORT_NAME, TRUE, pw->ignore_symlinks);
+	pw->cache_todo = g_list_reverse(list);
+
+	pw->cache_total = g_list_length(pw->cache_todo);
+}
+
+static void pan_cache_step_done_cb(CacheLoader *cl, gint error, gpointer data)
+{
+	PanWindow *pw = data;
+
+	if (pw->cache_list)
+		{
+		PanCacheData *pc;
+		pc = pw->cache_list->data;
+
+		if (!pc->cd)
+			{
+			pc->cd = cl->cd;
+			cl->cd = NULL;
+			}
+		}
+
+	cache_loader_free(cl);
+	pw->cache_cl = NULL;
+
+	pan_layout_update_idle(pw);
+}
+
+static gboolean pan_cache_step(PanWindow *pw)
+{
+	FileData *fd;
+	PanCacheData *pc;
+	CacheDataType load_mask;
+
+	if (!pw->cache_todo) return TRUE;
+
+	fd = pw->cache_todo->data;
+	pw->cache_todo = g_list_remove(pw->cache_todo, fd);
+
+	pc = g_new0(PanCacheData, 1);
+	pc->fd = file_data_ref(fd);
+
+	pc->cd = NULL;
+
+	pw->cache_list = g_list_prepend(pw->cache_list, pc);
+
+	cache_loader_free(pw->cache_cl);
+
+	load_mask = CACHE_LOADER_NONE;
+	if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) load_mask |= CACHE_LOADER_DIMENSIONS;
+	if (pw->exif_date_enable) load_mask |= CACHE_LOADER_DATE;
+	pw->cache_cl = cache_loader_new(pc->fd, load_mask,
+					pan_cache_step_done_cb, pw);
+	return (pw->cache_cl == NULL);
+}
+
+/* This sync date function is optimized for lists with a common sort */
+void pan_cache_sync_date(PanWindow *pw, GList *list)
+{
+	GList *haystack;
+	GList *work;
+
+	haystack = g_list_copy(pw->cache_list);
+
+	work = list;
+	while (work)
+		{
+		FileData *fd;
+		GList *needle;
+
+		fd = work->data;
+		work = work->next;
+
+		needle = haystack;
+		while (needle)
+			{
+			PanCacheData *pc;
+
+			pc = needle->data;
+			if (pc->fd == fd)
+				{
+				if (pc->cd && pc->cd->have_date && pc->cd->date >= 0)
+					{
+					fd->date = pc->cd->date;
+					}
+
+				haystack = g_list_delete_link(haystack, needle);
+				needle = NULL;
+				}
+			else
+				{
+				needle = needle->next;
+				}
+			}
+		}
+
+	g_list_free(haystack);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * item grid
+ *-----------------------------------------------------------------------------
+ */
+
+static void pan_grid_clear(PanWindow *pw)
+{
+	GList *work;
+
+	work = pw->list_grid;
+	while (work)
+		{
+		PanGrid *pg;
+
+		pg = work->data;
+		work = work->next;
+
+		g_list_free(pg->list);
+		g_free(pg);
+		}
+
+	g_list_free(pw->list_grid);
+	pw->list_grid = NULL;
+
+	pw->list = g_list_concat(pw->list, pw->list_static);
+	pw->list_static = NULL;
+}
+
+static void pan_grid_build(PanWindow *pw, gint width, gint height, gint grid_size)
+{
+	GList *work;
+	gint col, row;
+	gint cw, ch;
+	gint l;
+	gint i, j;
+
+	pan_grid_clear(pw);
+
+	l = g_list_length(pw->list);
+
+	if (l < 1) return;
+
+	col = (gint)(sqrt((gdouble)l / grid_size) * width / height + 0.999);
+	col = CLAMP(col, 1, l / grid_size + 1);
+	row = (gint)((gdouble)l / grid_size / col);
+	if (row < 1) row = 1;
+
+	/* limit minimum size of grid so that a tile will always fit regardless of position */
+	cw = MAX((gint)ceil((gdouble)width / col), PAN_TILE_SIZE * 2);
+	ch = MAX((gint)ceil((gdouble)height / row), PAN_TILE_SIZE * 2);
+
+	row = row * 2 - 1;
+	col = col * 2 - 1;
+
+	DEBUG_1("intersect speedup grid is %dx%d, based on %d average per grid", col, row, grid_size);
+
+	for (j = 0; j < row; j++)
+	    for (i = 0; i < col; i++)
+		{
+		if ((i + 1) * cw / 2 < width && (j + 1) * ch / 2 < height)
+			{
+			PanGrid *pg;
+
+			pg = g_new0(PanGrid, 1);
+			pg->x = i * cw / 2;
+			pg->y = j * ch / 2;
+			pg->w = cw;
+			pg->h = ch;
+
+			pw->list_grid = g_list_prepend(pw->list_grid, pg);
+
+			DEBUG_1("grid section: %d,%d (%dx%d)", pg->x, pg->y, pg->w, pg->h);
+			}
+		}
+
+	work = pw->list;
+	while (work)
+		{
+		PanItem *pi;
+		GList *grid;
+
+		pi = work->data;
+		work = work->next;
+
+		grid = pw->list_grid;
+		while (grid)
+			{
+			PanGrid *pg;
+			gint rx, ry, rw, rh;
+
+			pg = grid->data;
+			grid = grid->next;
+
+			if (util_clip_region(pi->x, pi->y, pi->width, pi->height,
+					     pg->x, pg->y, pg->w, pg->h,
+					     &rx, &ry, &rw, &rh))
+				{
+				pg->list = g_list_prepend(pg->list, pi);
+				}
+			}
+		}
+
+	work = pw->list_grid;
+	while (work)
+		{
+		PanGrid *pg;
+
+		pg = work->data;
+		work = work->next;
+
+		pg->list = g_list_reverse(pg->list);
+		}
+
+	pw->list_static = pw->list;
+	pw->list = NULL;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * layout state reset
+ *-----------------------------------------------------------------------------
+ */
+
+static void pan_window_items_free(PanWindow *pw)
+{
+	GList *work;
+
+	pan_grid_clear(pw);
+
+	work = pw->list;
+	while (work)
+		{
+		PanItem *pi = work->data;
+		work = work->next;
+
+		pan_item_free(pi);
+		}
+
+	g_list_free(pw->list);
+	pw->list = NULL;
+
+	g_list_free(pw->queue);
+	pw->queue = NULL;
+	pw->queue_pi = NULL;
+
+	image_loader_free(pw->il);
+	pw->il = NULL;
+
+	thumb_loader_free(pw->tl);
+	pw->tl = NULL;
+
+	pw->click_pi = NULL;
+	pw->search_pi = NULL;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * layout generation, queries, sizing
+ *-----------------------------------------------------------------------------
+ */
+
+static void pan_layout_compute(PanWindow *pw, FileData *dir_fd,
+			       gint *width, gint *height,
+			       gint *scroll_x, gint *scroll_y)
+{
+	pan_window_items_free(pw);
+
+	switch (pw->size)
+		{
+		case PAN_IMAGE_SIZE_THUMB_DOTS:
+			pw->thumb_size = PAN_THUMB_SIZE_DOTS;
+			pw->thumb_gap = PAN_THUMB_GAP_DOTS;
+			break;
+		case PAN_IMAGE_SIZE_THUMB_NONE:
+			pw->thumb_size = PAN_THUMB_SIZE_NONE;
+			pw->thumb_gap = PAN_THUMB_GAP_SMALL;
+			break;
+		case PAN_IMAGE_SIZE_THUMB_SMALL:
+			pw->thumb_size = PAN_THUMB_SIZE_SMALL;
+			pw->thumb_gap = PAN_THUMB_GAP_SMALL;
+			break;
+		case PAN_IMAGE_SIZE_THUMB_NORMAL:
+		default:
+			pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
+			pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
+			break;
+		case PAN_IMAGE_SIZE_THUMB_LARGE:
+			pw->thumb_size = PAN_THUMB_SIZE_LARGE;
+			pw->thumb_gap = PAN_THUMB_GAP_LARGE;
+			break;
+		case PAN_IMAGE_SIZE_10:
+			pw->image_size = 10;
+			pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
+			break;
+		case PAN_IMAGE_SIZE_25:
+			pw->image_size = 25;
+			pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
+			break;
+		case PAN_IMAGE_SIZE_33:
+			pw->image_size = 33;
+			pw->thumb_gap = PAN_THUMB_GAP_LARGE;
+			break;
+		case PAN_IMAGE_SIZE_50:
+			pw->image_size = 50;
+			pw->thumb_gap = PAN_THUMB_GAP_HUGE;
+			break;
+		case PAN_IMAGE_SIZE_100:
+			pw->image_size = 100;
+			pw->thumb_gap = PAN_THUMB_GAP_HUGE;
+			break;
+		}
+
+	*width = 0;
+	*height = 0;
+	*scroll_x = 0;
+	*scroll_y = 0;
+
+	switch (pw->layout)
+		{
+		case PAN_LAYOUT_GRID:
+		default:
+			pan_grid_compute(pw, dir_fd, width, height);
+			break;
+		case PAN_LAYOUT_FOLDERS_LINEAR:
+			pan_folder_tree_compute(pw, dir_fd, width, height);
+			break;
+		case PAN_LAYOUT_FOLDERS_FLOWER:
+			pan_flower_compute(pw, dir_fd, width, height, scroll_x, scroll_y);
+			break;
+		case PAN_LAYOUT_CALENDAR:
+			pan_calendar_compute(pw, dir_fd, width, height);
+			break;
+		case PAN_LAYOUT_TIMELINE:
+			pan_timeline_compute(pw, dir_fd, width, height);
+			break;
+		}
+
+	pan_cache_free(pw);
+
+	DEBUG_1("computed %d objects", g_list_length(pw->list));
+}
+
+static GList *pan_layout_intersect_l(GList *list, GList *item_list,
+				     gint x, gint y, gint width, gint height)
+{
+	GList *work;
+
+	work = item_list;
+	while (work)
+		{
+		PanItem *pi;
+		gint rx, ry, rw, rh;
+
+		pi = work->data;
+		work = work->next;
+
+		if (util_clip_region(x, y, width, height,
+				     pi->x, pi->y, pi->width, pi->height,
+				     &rx, &ry, &rw, &rh))
+			{
+			list = g_list_prepend(list, pi);
+			}
+		}
+
+	return list;
+}
+
+GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height)
+{
+	GList *list = NULL;
+	GList *grid;
+	PanGrid *pg = NULL;
+
+	grid = pw->list_grid;
+	while (grid && !pg)
+		{
+		pg = grid->data;
+		grid = grid->next;
+
+		if (x < pg->x || x + width > pg->x + pg->w ||
+		    y < pg->y || y + height > pg->y + pg->h)
+			{
+			pg = NULL;
+			}
+		}
+
+	list = pan_layout_intersect_l(list, pw->list, x, y, width, height);
+
+	if (pg)
+		{
+		list = pan_layout_intersect_l(list, pg->list, x, y, width, height);
+		}
+	else
+		{
+		list = pan_layout_intersect_l(list, pw->list_static, x, y, width, height);
+		}
+
+	return list;
+}
+
+void pan_layout_resize(PanWindow *pw)
+{
+	gint width = 0;
+	gint height = 0;
+	GList *work;
+	PixbufRenderer *pr;
+
+	work = pw->list;
+	while (work)
+		{
+		PanItem *pi;
+
+		pi = work->data;
+		work = work->next;
+
+		if (width < pi->x + pi->width) width = pi->x + pi->width;
+		if (height < pi->y + pi->height) height = pi->y + pi->height;
+		}
+	work = pw->list_static;
+	while (work)
+		{
+		PanItem *pi;
+
+		pi = work->data;
+		work = work->next;
+
+		if (width < pi->x + pi->width) width = pi->x + pi->width;
+		if (height < pi->y + pi->height) height = pi->y + pi->height;
+		}
+
+	width += PAN_BOX_BORDER * 2;
+	height += PAN_BOX_BORDER * 2;
+
+	pr = PIXBUF_RENDERER(pw->imd->pr);
+	if (width < pr->window_width) width = pr->window_width;
+	if (height < pr->window_width) height = pr->window_height;
+
+	pixbuf_renderer_set_tiles_size(PIXBUF_RENDERER(pw->imd->pr), width, height);
+}
+
+static gint pan_layout_update_idle_cb(gpointer data)
+{
+	PanWindow *pw = data;
+	gint width;
+	gint height;
+	gint scroll_x;
+	gint scroll_y;
+
+	if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE ||
+	    (pw->exif_date_enable && (pw->layout == PAN_LAYOUT_TIMELINE || pw->layout == PAN_LAYOUT_CALENDAR)))
+		{
+		if (!pw->cache_list && !pw->cache_todo)
+			{
+			pan_cache_fill(pw, pw->dir_fd);
+			if (pw->cache_todo)
+				{
+				pan_window_message(pw, _("Reading image data..."));
+				return TRUE;
+				}
+			}
+		if (pw->cache_todo)
+			{
+			pw->cache_count++;
+			pw->cache_tick++;
+			if (pw->cache_count == pw->cache_total)
+				{
+				pan_window_message(pw, _("Sorting..."));
+				}
+			else if (pw->cache_tick > 9)
+				{
+				gchar *buf;
+
+				buf = g_strdup_printf("%s %d / %d", _("Reading image data..."),
+						      pw->cache_count, pw->cache_total);
+				pan_window_message(pw, buf);
+				g_free(buf);
+
+				pw->cache_tick = 0;
+				}
+
+			if (pan_cache_step(pw)) return TRUE;
+
+			pw->idle_id = 0;
+			return FALSE;
+			}
+		}
+
+	pan_layout_compute(pw, pw->dir_fd, &width, &height, &scroll_x, &scroll_y);
+
+	pan_window_zoom_limit(pw);
+
+	if (width > 0 && height > 0)
+		{
+		gdouble align;
+
+		DEBUG_1("Canvas size is %d x %d", width, height);
+
+		pan_grid_build(pw, width, height, 1000);
+
+		pixbuf_renderer_set_tiles(PIXBUF_RENDERER(pw->imd->pr), width, height,
+					  PAN_TILE_SIZE, PAN_TILE_SIZE, 10,
+					  pan_window_request_tile_cb,
+					  pan_window_dispose_tile_cb, pw, 1.0);
+
+		if (scroll_x == 0 && scroll_y == 0)
+			{
+			align = 0.0;
+			}
+		else
+			{
+			align = 0.5;
+			}
+		pixbuf_renderer_scroll_to_point(PIXBUF_RENDERER(pw->imd->pr), scroll_x, scroll_y, align, align);
+		}
+
+	pan_window_message(pw, NULL);
+
+	pw->idle_id = 0;
+	return FALSE;
+}
+
+static void pan_layout_update_idle(PanWindow *pw)
+{
+	if (!pw->idle_id)
+		{
+		pw->idle_id = g_idle_add(pan_layout_update_idle_cb, pw);
+		}
+}
+
+static void pan_layout_update(PanWindow *pw)
+{
+	pan_window_message(pw, _("Sorting images..."));
+	pan_layout_update_idle(pw);
+}
+
+static void pan_layout_set_fd(PanWindow *pw, FileData *dir_fd)
+{
+	if (!dir_fd) return;
+
+	if (strcmp(dir_fd->path, G_DIR_SEPARATOR_S) == 0)
+		{
+		pan_warning_folder(dir_fd->path, pw->window);
+		return;
+		}
+
+	file_data_unref(pw->dir_fd);
+	pw->dir_fd = file_data_ref(dir_fd);
+
+	pan_layout_update(pw);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * keyboard handlers
+ *-----------------------------------------------------------------------------
+ */
+
+FileData *pan_menu_click_fd(PanWindow *pw)
+{
+	if (pw->click_pi && pw->click_pi->fd) return pw->click_pi->fd;
+	return NULL;
+}
+
+static void pan_window_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
+{
+	PanWindow *pw = data;
+
+	gdk_window_get_origin(gtk_widget_get_window(pw->imd->pr), x, y);
+	popup_menu_position_clamp(menu, x, y, 0);
+}
+
+static gboolean pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
+{
+	PanWindow *pw = data;
+	PixbufRenderer *pr;
+	FileData *fd;
+	gboolean stop_signal = FALSE;
+	GtkWidget *menu;
+	GtkWidget *imd_widget;
+	gint x = 0;
+	gint y = 0;
+	gint focused;
+	gint on_entry;
+
+	pr = PIXBUF_RENDERER(pw->imd->pr);
+	fd = pan_menu_click_fd(pw);
+
+	imd_widget = gtk_container_get_focus_child(GTK_CONTAINER(pw->imd->widget));
+	focused = (pw->fs || (imd_widget && gtk_widget_has_focus(imd_widget)));
+	on_entry = (gtk_widget_has_focus(pw->path_entry) ||
+		    gtk_widget_has_focus(pw->search_entry));
+
+	if (focused)
+		{
+		stop_signal = TRUE;
+		switch (event->keyval)
+			{
+			case GDK_KEY_Left: case GDK_KEY_KP_Left:
+				x -= 1;
+				break;
+			case GDK_KEY_Right: case GDK_KEY_KP_Right:
+				x += 1;
+				break;
+			case GDK_KEY_Up: case GDK_KEY_KP_Up:
+				y -= 1;
+				break;
+			case GDK_KEY_Down: case GDK_KEY_KP_Down:
+				y += 1;
+				break;
+			case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up:
+				pixbuf_renderer_scroll(pr, 0, 0 - pr->vis_height / 2);
+				break;
+			case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down:
+				pixbuf_renderer_scroll(pr, 0, pr->vis_height / 2);
+				break;
+			case GDK_KEY_Home: case GDK_KEY_KP_Home:
+				pixbuf_renderer_scroll(pr, 0 - pr->vis_width / 2, 0);
+				break;
+			case GDK_KEY_End: case GDK_KEY_KP_End:
+				pixbuf_renderer_scroll(pr, pr->vis_width / 2, 0);
+				break;
+			default:
+				stop_signal = FALSE;
+				break;
+			}
+
+		if (x != 0 || y!= 0)
+			{
+			if (event->state & GDK_SHIFT_MASK)
+				{
+				x *= 3;
+				y *= 3;
+				}
+			keyboard_scroll_calc(&x, &y, event);
+			pixbuf_renderer_scroll(pr, x, y);
+			}
+		}
+
+	if (stop_signal) return stop_signal;
+
+	if (event->state & GDK_CONTROL_MASK)
+		{
+		stop_signal = TRUE;
+		switch (event->keyval)
+			{
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+			case '0':
+				break;
+			case 'C': case 'c':
+				if (fd) file_util_copy(fd, NULL, NULL, GTK_WIDGET(pr));
+				break;
+			case 'M': case 'm':
+				if (fd) file_util_move(fd, NULL, NULL, GTK_WIDGET(pr));
+				break;
+			case 'R': case 'r':
+				if (fd) file_util_rename(fd, NULL, GTK_WIDGET(pr));
+				break;
+			case 'D': case 'd':
+				if (fd) file_util_delete(fd, NULL, GTK_WIDGET(pr));
+				break;
+			case 'F': case 'f':
+				pan_search_toggle_visible(pw, TRUE);
+				break;
+			case 'G': case 'g':
+				pan_search_activate(pw);
+				break;
+			case 'W': case 'w':
+				pan_window_close(pw);
+				break;
+			default:
+				stop_signal = FALSE;
+				break;
+			}
+		}
+	else
+		{
+		stop_signal = TRUE;
+		switch (event->keyval)
+			{
+			case GDK_KEY_Escape:
+				if (pw->fs)
+					{
+					pan_fullscreen_toggle(pw, TRUE);
+					}
+				else
+					{
+					pan_search_toggle_visible(pw, FALSE);
+					}
+				break;
+			default:
+				stop_signal = FALSE;
+				break;
+			}
+
+		if (stop_signal) return stop_signal;
+
+		if (!on_entry)
+			{
+			stop_signal = TRUE;
+			switch (event->keyval)
+				{
+				case '+': case '=': case GDK_KEY_KP_Add:
+					pixbuf_renderer_zoom_adjust(pr, ZOOM_INCREMENT);
+					break;
+				case '-': case GDK_KEY_KP_Subtract:
+					pixbuf_renderer_zoom_adjust(pr, -ZOOM_INCREMENT);
+					break;
+				case 'Z': case 'z': case GDK_KEY_KP_Divide: case '1':
+					pixbuf_renderer_zoom_set(pr, 1.0);
+					break;
+				case '2':
+					pixbuf_renderer_zoom_set(pr, 2.0);
+					break;
+				case '3':
+					pixbuf_renderer_zoom_set(pr, 3.0);
+					break;
+				case '4':
+					pixbuf_renderer_zoom_set(pr, 4.0);
+					break;
+				case '7':
+					pixbuf_renderer_zoom_set(pr, -4.0);
+					break;
+				case '8':
+					pixbuf_renderer_zoom_set(pr, -3.0);
+					break;
+				case '9':
+					pixbuf_renderer_zoom_set(pr, -2.0);
+					break;
+				case 'F': case 'f':
+				case 'V': case 'v':
+				case GDK_KEY_F11:
+					pan_fullscreen_toggle(pw, FALSE);
+					break;
+				case 'I': case 'i':
+					break;
+				case GDK_KEY_Delete: case GDK_KEY_KP_Delete:
+					break;
+				case GDK_KEY_Menu:
+				case GDK_KEY_F10:
+					menu = pan_popup_menu(pw);
+					gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
+						       pan_window_menu_pos_cb, pw, 0, GDK_CURRENT_TIME);
+					break;
+				case '/':
+					pan_search_toggle_visible(pw, TRUE);
+					break;
+				default:
+					stop_signal = FALSE;
+					break;
+				}
+			}
+		}
+
+	return stop_signal;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * info popup
+ *-----------------------------------------------------------------------------
+ */
+
+static void pan_info_add_exif(PanTextAlignment *ta, FileData *fd)
+{
+
+	if (!fd) return;
+
+	pan_text_alignment_add(ta, NULL, NULL);
+}
+
+
+static void pan_info_update(PanWindow *pw, PanItem *pi)
+{
+	PanTextAlignment *ta;
+	PanItem *pbox;
+	PanItem *p;
+	gchar *buf;
+	gint x1, y1, x2, y2, x3, y3;
+	gint x, y, w, h;
+
+	if (pw->click_pi == pi) return;
+	if (pi && !pi->fd) pi = NULL;
+
+	while ((p = pan_item_find_by_key(pw, PAN_ITEM_NONE, "info"))) pan_item_remove(pw, p);
+	pw->click_pi = pi;
+
+	if (!pi) return;
+
+	DEBUG_1("info set to %s", pi->fd->path);
+
+	pbox = pan_item_box_new(pw, NULL, pi->x + pi->width + 4, pi->y, 10, 10,
+				PAN_POPUP_BORDER,
+				PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
+				PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
+	pan_item_set_key(pbox, "info");
+
+	if (pi->type == PAN_ITEM_THUMB && pi->pixbuf)
+		{
+		w = gdk_pixbuf_get_width(pi->pixbuf);
+		h = gdk_pixbuf_get_height(pi->pixbuf);
+
+		x1 = pi->x + pi->width - (pi->width - w) / 2 - 8;
+		y1 = pi->y + (pi->height - h) / 2 + 8;
+		}
+	else
+		{
+		x1 = pi->x + pi->width - 8;
+		y1 = pi->y + 8;
+		}
+
+	x2 = pbox->x + 1;
+	y2 = pbox->y + 36;
+	x3 = pbox->x + 1;
+	y3 = pbox->y + 12;
+	util_clip_triangle(x1, y1, x2, y2, x3, y3,
+			   &x, &y, &w, &h);
+
+	p = pan_item_tri_new(pw, NULL, x, y, w, h,
+			     x1, y1, x2, y2, x3, y3,
+			     PAN_POPUP_COLOR, PAN_POPUP_ALPHA);
+	pan_item_tri_border(p, PAN_BORDER_1 | PAN_BORDER_3, PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
+	pan_item_set_key(p, "info");
+	pan_item_added(pw, p);
+
+	ta = pan_text_alignment_new(pw, pbox->x + PREF_PAD_BORDER, pbox->y + PREF_PAD_BORDER, "info");
+
+	pan_text_alignment_add(ta, _("Filename:"), pi->fd->name);
+	buf = remove_level_from_path(pi->fd->path);
+	pan_text_alignment_add(ta, _("Location:"), buf);
+	g_free(buf);
+	pan_text_alignment_add(ta, _("Date:"), text_from_time(pi->fd->date));
+	buf = text_from_size(pi->fd->size);
+	pan_text_alignment_add(ta, _("Size:"), buf);
+	g_free(buf);
+
+	if (pw->info_includes_exif)
+		{
+		pan_info_add_exif(ta, pi->fd);
+		}
+
+	pan_text_alignment_calc(ta, pbox);
+	pan_text_alignment_free(ta);
+
+	pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
+	pan_item_added(pw, pbox);
+
+	if (pw->info_image_size > PAN_IMAGE_SIZE_THUMB_NONE)
+		{
+		gint iw, ih;
+		if (image_load_dimensions(pi->fd, &iw, &ih))
+			{
+			gint scale = 25;
+
+			switch (pw->info_image_size)
+				{
+				case PAN_IMAGE_SIZE_10:
+					scale = 10;
+					break;
+				case PAN_IMAGE_SIZE_25:
+					scale = 25;
+					break;
+				case PAN_IMAGE_SIZE_33:
+					scale = 33;
+					break;
+				case PAN_IMAGE_SIZE_50:
+					scale = 50;
+					break;
+				case PAN_IMAGE_SIZE_100:
+					scale = 100;
+					break;
+				}
+
+			iw = MAX(1, iw * scale / 100);
+			ih = MAX(1, ih * scale / 100);
+
+			pbox = pan_item_box_new(pw, NULL, pbox->x, pbox->y + pbox->height + 8, 10, 10,
+						PAN_POPUP_BORDER,
+						PAN_POPUP_COLOR, PAN_POPUP_ALPHA,
+						PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
+			pan_item_set_key(pbox, "info");
+
+			p = pan_item_image_new(pw, file_data_new_group(pi->fd->path),
+					       pbox->x + PREF_PAD_BORDER, pbox->y + PREF_PAD_BORDER, iw, ih);
+			pan_item_set_key(p, "info");
+			pan_item_size_by_item(pbox, p, PREF_PAD_BORDER);
+
+			pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
+			pan_item_added(pw, pbox);
+			}
+		}
+
+	pan_layout_resize(pw);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * search
+ *-----------------------------------------------------------------------------
+ */
+
+static void pan_search_status(PanWindow *pw, const gchar *text)
+{
+	gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
+}
+
+static gint pan_search_by_path(PanWindow *pw, const gchar *path)
+{
+	PanItem *pi;
+	GList *list;
+	GList *found;
+	PanItemType type;
+	gchar *buf;
+
+	type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
+
+	list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
+	if (!list) return FALSE;
+
+	found = g_list_find(list, pw->click_pi);
+	if (found && found->next)
+		{
+		found = found->next;
+		pi = found->data;
+		}
+	else
+		{
+		pi = list->data;
+		}
+
+	pan_info_update(pw, pi);
+	image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
+
+	buf = g_strdup_printf("%s ( %d / %d )",
+			      (path[0] == G_DIR_SEPARATOR) ? _("path found") : _("filename found"),
+			      g_list_index(list, pi) + 1,
+			      g_list_length(list));
+	pan_search_status(pw, buf);
+	g_free(buf);
+
+	g_list_free(list);
+
+	return TRUE;
+}
+
+static gboolean pan_search_by_partial(PanWindow *pw, const gchar *text)
+{
+	PanItem *pi;
+	GList *list;
+	GList *found;
+	PanItemType type;
+	gchar *buf;
+
+	type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
+
+	list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
+	if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
+	if (!list)
+		{
+		gchar *needle;
+
+		needle = g_utf8_strdown(text, -1);
+		list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
+		g_free(needle);
+		}
+	if (!list) return FALSE;
+
+	found = g_list_find(list, pw->click_pi);
+	if (found && found->next)
+		{
+		found = found->next;
+		pi = found->data;
+		}
+	else
+		{
+		pi = list->data;
+		}
+
+	pan_info_update(pw, pi);
+	image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
+
+	buf = g_strdup_printf("%s ( %d / %d )",
+			      _("partial match"),
+			      g_list_index(list, pi) + 1,
+			      g_list_length(list));
+	pan_search_status(pw, buf);
+	g_free(buf);
+
+	g_list_free(list);
+
+	return TRUE;
+}
+
+static gboolean valid_date_separator(gchar c)
+{
+	return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
+}
+
+static GList *pan_search_by_date_val(PanWindow *pw, PanItemType type,
+				     gint year, gint month, gint day,
+				     const gchar *key)
+{
+	GList *list = NULL;
+	GList *work;
+
+	work = g_list_last(pw->list_static);
+	while (work)
+		{
+		PanItem *pi;
+
+		pi = work->data;
+		work = work->prev;
+
+		if (pi->fd && (pi->type == type || type == PAN_ITEM_NONE) &&
+		    ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
+			{
+			struct tm *tl;
+
+			tl = localtime(&pi->fd->date);
+			if (tl)
+				{
+				gint match;
+
+				match = (tl->tm_year == year - 1900);
+				if (match && month >= 0) match = (tl->tm_mon == month - 1);
+				if (match && day > 0) match = (tl->tm_mday == day);
+
+				if (match) list = g_list_prepend(list, pi);
+				}
+			}
+		}
+
+	return g_list_reverse(list);
+}
+
+static gboolean pan_search_by_date(PanWindow *pw, const gchar *text)
+{
+	PanItem *pi = NULL;
+	GList *list = NULL;
+	GList *found;
+	gint year;
+	gint month = -1;
+	gint day = -1;
+	gchar *ptr;
+	gchar *mptr;
+	struct tm *lt;
+	time_t t;
+	gchar *message;
+	gchar *buf;
+	gchar *buf_count;
+
+	if (!text) return FALSE;
+
+	ptr = (gchar *)text;
+	while (*ptr != '\0')
+		{
+		if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
+		ptr++;
+		}
+
+	t = time(NULL);
+	if (t == -1) return FALSE;
+	lt = localtime(&t);
+	if (!lt) return FALSE;
+
+	if (valid_date_separator(*text))
+		{
+		year = -1;
+		mptr = (gchar *)text;
+		}
+	else
+		{
+		year = (gint)strtol(text, &mptr, 10);
+		if (mptr == text) return FALSE;
+		}
+
+	if (*mptr != '\0' && valid_date_separator(*mptr))
+		{
+		gchar *dptr;
+
+		mptr++;
+		month = strtol(mptr, &dptr, 10);
+		if (dptr == mptr)
+			{
+			if (valid_date_separator(*dptr))
+				{
+				month = lt->tm_mon + 1;
+				dptr++;
+				}
+			else
+				{
+				month = -1;
+				}
+			}
+		if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
+			{
+			gchar *eptr;
+			dptr++;
+			day = strtol(dptr, &eptr, 10);
+			if (dptr == eptr)
+				{
+				day = lt->tm_mday;
+				}
+			}
+		}
+
+	if (year == -1)
+		{
+		year = lt->tm_year + 1900;
+		}
+	else if (year < 100)
+		{
+		if (year > 70)
+			year+= 1900;
+		else
+			year+= 2000;
+		}
+
+	if (year < 1970 ||
+	    month < -1 || month == 0 || month > 12 ||
+	    day < -1 || day == 0 || day > 31) return FALSE;
+
+	t = pan_date_to_time(year, month, day);
+	if (t < 0) return FALSE;
+
+	if (pw->layout == PAN_LAYOUT_CALENDAR)
+		{
+		list = pan_search_by_date_val(pw, PAN_ITEM_BOX, year, month, day, "day");
+		}
+	else
+		{
+		PanItemType type;
+
+		type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
+		list = pan_search_by_date_val(pw, type, year, month, day, NULL);
+		}
+
+	if (list)
+		{
+		found = g_list_find(list, pw->search_pi);
+		if (found && found->next)
+			{
+			found = found->next;
+			pi = found->data;
+			}
+		else
+			{
+			pi = list->data;
+			}
+		}
+
+	pw->search_pi = pi;
+
+	if (pw->layout == PAN_LAYOUT_CALENDAR && pi && pi->type == PAN_ITEM_BOX)
+		{
+		pan_info_update(pw, NULL);
+		pan_calendar_update(pw, pi);
+		image_scroll_to_point(pw->imd,
+				      pi->x + pi->width / 2,
+				      pi->y + pi->height / 2, 0.5, 0.5);
+		}
+	else if (pi)
+		{
+		pan_info_update(pw, pi);
+		image_scroll_to_point(pw->imd,
+				      pi->x - PAN_BOX_BORDER * 5 / 2,
+				      pi->y, 0.0, 0.5);
+		}
+
+	if (month > 0)
+		{
+		buf = pan_date_value_string(t, PAN_DATE_LENGTH_MONTH);
+		if (day > 0)
+			{
+			gchar *tmp;
+			tmp = buf;
+			buf = g_strdup_printf("%d %s", day, tmp);
+			g_free(tmp);
+			}
+		}
+	else
+		{
+		buf = pan_date_value_string(t, PAN_DATE_LENGTH_YEAR);
+		}
+
+	if (pi)
+		{
+		buf_count = g_strdup_printf("( %d / %d )",
+					    g_list_index(list, pi) + 1,
+					    g_list_length(list));
+		}
+	else
+		{
+		buf_count = g_strdup_printf("(%s)", _("no match"));
+		}
+
+	message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
+	g_free(buf);
+	g_free(buf_count);
+	pan_search_status(pw, message);
+	g_free(message);
+
+	g_list_free(list);
+
+	return TRUE;
+}
+
+static void pan_search_activate_cb(const gchar *text, gpointer data)
+{
+	PanWindow *pw = data;
+
+	if (!text) return;
+
+	tab_completion_append_to_history(pw->search_entry, text);
+
+	if (pan_search_by_path(pw, text)) return;
+
+	if ((pw->layout == PAN_LAYOUT_TIMELINE ||
+	     pw->layout == PAN_LAYOUT_CALENDAR) &&
+	    pan_search_by_date(pw, text))
+		{
+		return;
+		}
+
+	if (pan_search_by_partial(pw, text)) return;
+
+	pan_search_status(pw, _("no match"));
+}
+
+static void pan_search_activate(PanWindow *pw)
+{
+	gchar *text;
+
+	text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->search_entry)));
+	pan_search_activate_cb(text, pw);
+	g_free(text);
+}
+
+static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
+{
+	PanWindow *pw = data;
+	gboolean visible;
+
+	visible = gtk_widget_get_visible(pw->search_box);
+	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
+
+	if (visible)
+		{
+		gtk_widget_hide(pw->search_box);
+		gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
+		}
+	else
+		{
+		gtk_widget_show(pw->search_box);
+		gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+		gtk_widget_grab_focus(pw->search_entry);
+		}
+}
+
+static void pan_search_toggle_visible(PanWindow *pw, gboolean enable)
+{
+	if (pw->fs) return;
+
+	if (enable)
+		{
+		if (gtk_widget_get_visible(pw->search_box))
+			{
+			gtk_widget_grab_focus(pw->search_entry);
+			}
+		else
+			{
+			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
+			}
+		}
+	else
+		{
+		if (gtk_widget_get_visible(pw->search_entry))
+			{
+			if (gtk_widget_has_focus(pw->search_entry))
+				{
+				gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
+				}
+			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
+			}
+		}
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * main window
+ *-----------------------------------------------------------------------------
+ */
+
+static void button_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
+{
+	PanWindow *pw = data;
+	PanItem *pi = NULL;
+	GtkWidget *menu;
+	gint rx, ry;
+
+	rx = ry = 0;
+	if (pr->scale)
+		{
+		rx = (gdouble)(pr->x_scroll + event->x - pr->x_offset) / pr->scale;
+		ry = (gdouble)(pr->y_scroll + event->y - pr->y_offset) / pr->scale;
+		}
+
+	pi = pan_item_find_by_coord(pw, PAN_ITEM_BOX, rx, ry, "info");
+	if (pi && event->button == MOUSE_BUTTON_LEFT)
+		{
+		pan_info_update(pw, NULL);
+		return;
+		}
+
+	pi = pan_item_find_by_coord(pw, (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB,
+				    rx, ry, NULL);
+
+	switch (event->button)
+		{
+		case MOUSE_BUTTON_LEFT:
+			pan_info_update(pw, pi);
+
+			if (!pi && pw->layout == PAN_LAYOUT_CALENDAR)
+				{
+				pi = pan_item_find_by_coord(pw, PAN_ITEM_BOX, rx, ry, "day");
+				pan_calendar_update(pw, pi);
+				}
+			break;
+		case MOUSE_BUTTON_MIDDLE:
+			break;
+		case MOUSE_BUTTON_RIGHT:
+			pan_info_update(pw, pi);
+			menu = pan_popup_menu(pw);
+			gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
+			break;
+		default:
+			break;
+		}
+}
+
+static void scroll_cb(PixbufRenderer *pr, GdkEventScroll *event, gpointer data)
+{
+	gint w, h;
+
+	w = pr->vis_width;
+	h = pr->vis_height;
+
+	if (!(event->state & GDK_SHIFT_MASK))
+		{
+		w /= 3;
+		h /= 3;
+		}
+
+	if (event->state & GDK_CONTROL_MASK)
+		{
+		switch (event->direction)
+			{
+			case GDK_SCROLL_UP:
+				pixbuf_renderer_zoom_adjust_at_point(pr, ZOOM_INCREMENT,
+								     (gint)event->x, (gint)event->y);
+				break;
+			case GDK_SCROLL_DOWN:
+				pixbuf_renderer_zoom_adjust_at_point(pr, -ZOOM_INCREMENT,
+								     (gint)event->x, (gint)event->y);
+				break;
+			default:
+				break;
+			}
+		}
+	else
+		{
+		switch (event->direction)
+			{
+			case GDK_SCROLL_UP:
+				pixbuf_renderer_scroll(pr, 0, -h);
+				break;
+			case GDK_SCROLL_DOWN:
+				pixbuf_renderer_scroll(pr, 0, h);
+				break;
+			case GDK_SCROLL_LEFT:
+				pixbuf_renderer_scroll(pr, -w, 0);
+				break;
+			case GDK_SCROLL_RIGHT:
+				pixbuf_renderer_scroll(pr, w, 0);
+				break;
+			default:
+				break;
+			}
+		}
+}
+
+static void pan_image_set_buttons(PanWindow *pw, ImageWindow *imd)
+{
+	g_signal_connect(G_OBJECT(imd->pr), "clicked",
+			 G_CALLBACK(button_cb), pw);
+	g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
+			 G_CALLBACK(scroll_cb), pw);
+}
+
+static void pan_fullscreen_stop_func(FullScreenData *fs, gpointer data)
+{
+	PanWindow *pw = data;
+
+	pw->fs = NULL;
+	pw->imd = pw->imd_normal;
+}
+
+static void pan_fullscreen_toggle(PanWindow *pw, gboolean force_off)
+{
+	if (force_off && !pw->fs) return;
+
+	if (pw->fs)
+		{
+		fullscreen_stop(pw->fs);
+		}
+	else
+		{
+		pw->fs = fullscreen_start(pw->window, pw->imd, pan_fullscreen_stop_func, pw);
+		pan_image_set_buttons(pw, pw->fs->imd);
+		g_signal_connect(G_OBJECT(pw->fs->window), "key_press_event",
+				 G_CALLBACK(pan_window_key_press_cb), pw);
+
+		pw->imd = pw->fs->imd;
+		}
+}
+
+static void pan_window_image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
+{
+	PanWindow *pw = data;
+	gchar *text;
+
+	text = image_zoom_get_as_text(pw->imd);
+	gtk_label_set_text(GTK_LABEL(pw->label_zoom), text);
+	g_free(text);
+}
+
+static void pan_window_image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
+{
+	PanWindow *pw = data;
+	GtkAdjustment *adj;
+	GdkRectangle rect;
+	gint width, height;
+
+	if (pr->scale == 0.0) return;
+
+	pixbuf_renderer_get_visible_rect(pr, &rect);
+	pixbuf_renderer_get_image_size(pr, &width, &height);
+
+	adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_h));
+	gtk_adjustment_set_page_size(adj, rect.width);
+	gtk_adjustment_set_page_increment(adj, gtk_adjustment_get_page_size(adj) / 2.0);
+	gtk_adjustment_set_step_increment(adj, 48.0 / pr->scale);
+	gtk_adjustment_set_lower(adj, 0.0);
+	gtk_adjustment_set_upper(adj, MAX((gdouble)width, 1.0));
+	gtk_adjustment_set_value(adj, (gdouble)rect.x);
+
+	pref_signal_block_data(pw->scrollbar_h, pw);
+	gtk_adjustment_changed(adj);
+	gtk_adjustment_value_changed(adj);
+	pref_signal_unblock_data(pw->scrollbar_h, pw);
+
+	adj = gtk_range_get_adjustment(GTK_RANGE(pw->scrollbar_v));
+	gtk_adjustment_set_page_size(adj, rect.height);
+	gtk_adjustment_set_page_increment(adj, gtk_adjustment_get_page_size(adj) / 2.0);
+	gtk_adjustment_set_step_increment(adj, 48.0 / pr->scale);
+	gtk_adjustment_set_lower(adj, 0.0);
+	gtk_adjustment_set_upper(adj, MAX((gdouble)height, 1.0));
+	gtk_adjustment_set_value(adj, (gdouble)rect.y);
+
+	pref_signal_block_data(pw->scrollbar_v, pw);
+	gtk_adjustment_changed(adj);
+	gtk_adjustment_value_changed(adj);
+	pref_signal_unblock_data(pw->scrollbar_v, pw);
+}
+
+static void pan_window_scrollbar_h_value_cb(GtkRange *range, gpointer data)
+{
+	PanWindow *pw = data;
+	PixbufRenderer *pr;
+	gint x;
+
+	pr = PIXBUF_RENDERER(pw->imd_normal->pr);
+
+	if (!pr->scale) return;
+
+	x = (gint)gtk_range_get_value(range);
+
+	pixbuf_renderer_scroll_to_point(pr, x, (gint)((gdouble)pr->y_scroll / pr->scale), 0.0, 0.0);
+}
+
+static void pan_window_scrollbar_v_value_cb(GtkRange *range, gpointer data)
+{
+	PanWindow *pw = data;
+	PixbufRenderer *pr;
+	gint y;
+
+	pr = PIXBUF_RENDERER(pw->imd_normal->pr);
+
+	if (!pr->scale) return;
+
+	y = (gint)gtk_range_get_value(range);
+
+	pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)pr->x_scroll / pr->scale), y, 0.0, 0.0);
+}
+
+static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
+{
+	PanWindow *pw = data;
+
+	pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
+	pan_layout_update(pw);
+}
+
+static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
+{
+	PanWindow *pw = data;
+
+	pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
+	pan_layout_update(pw);
+}
+
+static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
+{
+	PanWindow *pw = data;
+	gchar *path;
+
+	path = remove_trailing_slash(new_text);
+	parse_out_relatives(path);
+
+	if (!isdir(path))
+		{
+		warning_dialog(_("Folder not found"),
+			       _("The entered path is not a folder"),
+			       GTK_STOCK_DIALOG_WARNING, pw->path_entry);
+		}
+	else
+		{
+		FileData *dir_fd = file_data_new_dir(path);
+		tab_completion_append_to_history(pw->path_entry, path);
+
+		pan_layout_set_fd(pw, dir_fd);
+		file_data_unref(dir_fd);
+		}
+
+	g_free(path);
+}
+
+static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
+{
+	PanWindow *pw = data;
+	gchar *text;
+
+	if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) return;
+
+	text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
+	pan_window_entry_activate_cb(text, pw);
+	g_free(text);
+}
+
+static void pan_window_close(PanWindow *pw)
+{
+	pan_window_list = g_list_remove(pan_window_list, pw);
+
+	pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_EXIF_PAN_DATE, pw->exif_date_enable);
+	pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_INFO_IMAGE, pw->info_image_size);
+	pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_INFO_EXIF, pw->info_includes_exif);
+
+	if (pw->idle_id)
+		{
+		g_source_remove(pw->idle_id);
+		}
+
+	pan_fullscreen_toggle(pw, TRUE);
+	gtk_widget_destroy(pw->window);
+
+	pan_window_items_free(pw);
+	pan_cache_free(pw);
+
+	file_data_unref(pw->dir_fd);
+
+	g_free(pw);
+}
+
+static gboolean pan_window_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data)
+{
+	PanWindow *pw = data;
+
+	pan_window_close(pw);
+	return TRUE;
+}
+
+static void pan_window_new_real(FileData *dir_fd)
+{
+	PanWindow *pw;
+	GtkWidget *vbox;
+	GtkWidget *box;
+	GtkWidget *combo;
+	GtkWidget *hbox;
+	GtkWidget *frame;
+	GtkWidget *table;
+	GdkGeometry geometry;
+
+	pw = g_new0(PanWindow, 1);
+
+	pw->dir_fd = file_data_ref(dir_fd);
+	pw->layout = PAN_LAYOUT_TIMELINE;
+	pw->size = PAN_IMAGE_SIZE_THUMB_NORMAL;
+	pw->thumb_size = PAN_THUMB_SIZE_NORMAL;
+	pw->thumb_gap = PAN_THUMB_GAP_NORMAL;
+
+	if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_EXIF_PAN_DATE, &pw->exif_date_enable))
+		{
+		pw->exif_date_enable = FALSE;
+		}
+	if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_INFO_IMAGE, &pw->info_image_size))
+		{
+		pw->info_image_size = PAN_IMAGE_SIZE_THUMB_NONE;
+		}
+	if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_INFO_EXIF, &pw->info_includes_exif))
+		{
+		pw->info_includes_exif = TRUE;
+		}
+
+	pw->ignore_symlinks = TRUE;
+
+	pw->idle_id = 0;
+
+	pw->window = window_new(GTK_WINDOW_TOPLEVEL, "panview", NULL, NULL, _("Pan View"));
+
+	geometry.min_width = DEFAULT_MINIMAL_WINDOW_SIZE;
+	geometry.min_height = DEFAULT_MINIMAL_WINDOW_SIZE;
+	gtk_window_set_geometry_hints(GTK_WINDOW(pw->window), NULL, &geometry, GDK_HINT_MIN_SIZE);
+
+	gtk_window_set_resizable(GTK_WINDOW(pw->window), TRUE);
+	gtk_container_set_border_width(GTK_CONTAINER(pw->window), 0);
+
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_container_add(GTK_CONTAINER(pw->window), vbox);
+	gtk_widget_show(vbox);
+
+	box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
+
+	pref_spacer(box, 0);
+	pref_label_new(box, _("Location:"));
+	combo = tab_completion_new_with_history(&pw->path_entry, dir_fd->path, "pan_view_path", -1,
+						pan_window_entry_activate_cb, pw);
+	g_signal_connect(G_OBJECT(gtk_widget_get_parent(pw->path_entry)), "changed",
+			 G_CALLBACK(pan_window_entry_change_cb), pw);
+	gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
+	gtk_widget_show(combo);
+
+	combo = gtk_combo_box_text_new();
+	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Timeline"));
+	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Calendar"));
+	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Folders"));
+	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Folders (flower)"));
+	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Grid"));
+
+	gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->layout);
+	g_signal_connect(G_OBJECT(combo), "changed",
+			 G_CALLBACK(pan_window_layout_change_cb), pw);
+	gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
+	gtk_widget_show(combo);
+
+	combo = gtk_combo_box_text_new();
+	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Dots"));
+	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("No Images"));
+	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Small Thumbnails"));
+	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Normal Thumbnails"));
+	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Large Thumbnails"));
+	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("1:10 (10%)"));
+	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("1:4 (25%)"));
+	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("1:3 (33%)"));
+	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("1:2 (50%)"));
+	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("1:1 (100%)"));
+
+	gtk_combo_box_set_active(GTK_COMBO_BOX(combo), pw->size);
+	g_signal_connect(G_OBJECT(combo), "changed",
+			 G_CALLBACK(pan_window_layout_size_cb), pw);
+	gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0);
+	gtk_widget_show(combo);
+
+	table = pref_table_new(vbox, 2, 2, FALSE, TRUE);
+	gtk_table_set_row_spacings(GTK_TABLE(table), 2);
+	gtk_table_set_col_spacings(GTK_TABLE(table), 2);
+
+	pw->imd = image_new(TRUE);
+	pw->imd_normal = pw->imd;
+
+	g_signal_connect(G_OBJECT(pw->imd->pr), "zoom",
+			 G_CALLBACK(pan_window_image_zoom_cb), pw);
+	g_signal_connect(G_OBJECT(pw->imd->pr), "scroll_notify",
+			 G_CALLBACK(pan_window_image_scroll_notify_cb), pw);
+
+	gtk_table_attach(GTK_TABLE(table), pw->imd->widget, 0, 1, 0, 1,
+			 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
+	gtk_widget_show(GTK_WIDGET(pw->imd->widget));
+
+	pan_window_dnd_init(pw);
+
+	pan_image_set_buttons(pw, pw->imd);
+
+	pw->scrollbar_h = gtk_hscrollbar_new(NULL);
+	g_signal_connect(G_OBJECT(pw->scrollbar_h), "value_changed",
+			 G_CALLBACK(pan_window_scrollbar_h_value_cb), pw);
+	gtk_table_attach(GTK_TABLE(table), pw->scrollbar_h, 0, 1, 1, 2,
+			 GTK_FILL | GTK_EXPAND, 0, 0, 0);
+	gtk_widget_show(pw->scrollbar_h);
+
+	pw->scrollbar_v = gtk_vscrollbar_new(NULL);
+	g_signal_connect(G_OBJECT(pw->scrollbar_v), "value_changed",
+			 G_CALLBACK(pan_window_scrollbar_v_value_cb), pw);
+	gtk_table_attach(GTK_TABLE(table), pw->scrollbar_v, 1, 2, 0, 1,
+			 0, GTK_FILL | GTK_EXPAND, 0, 0);
+	gtk_widget_show(pw->scrollbar_v);
+
+	/* find bar */
+
+	pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
+	gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
+
+	pref_spacer(pw->search_box, 0);
+	pref_label_new(pw->search_box, _("Find:"));
+
+	hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
+	gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
+	gtk_widget_show(hbox);
+
+	combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
+						pan_search_activate_cb, pw);
+	gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
+	gtk_widget_show(combo);
+
+	pw->search_label = gtk_label_new("");
+	gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
+	gtk_widget_show(pw->search_label);
+
+	/* status bar */
+
+	box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
+
+	frame = gtk_frame_new(NULL);
+	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
+	gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
+	gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
+	gtk_widget_show(frame);
+
+	hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
+	gtk_container_add(GTK_CONTAINER(frame), hbox);
+	gtk_widget_show(hbox);
+
+	pref_spacer(hbox, 0);
+	pw->label_message = pref_label_new(hbox, "");
+
+	frame = gtk_frame_new(NULL);
+	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
+	gtk_widget_set_size_request(frame, ZOOM_LABEL_WIDTH, -1);
+	gtk_box_pack_end(GTK_BOX(box), frame, FALSE, FALSE, 0);
+	gtk_widget_show(frame);
+
+	pw->label_zoom = gtk_label_new("");
+	gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
+	gtk_widget_show(pw->label_zoom);
+
+	pw->search_button = gtk_toggle_button_new();
+	gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
+	gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
+	hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
+	gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
+	gtk_widget_show(hbox);
+	pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
+	gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
+	gtk_widget_show(pw->search_button_arrow);
+	pref_label_new(hbox, _("Find"));
+
+	gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
+	gtk_widget_show(pw->search_button);
+	g_signal_connect(G_OBJECT(pw->search_button), "clicked",
+			 G_CALLBACK(pan_search_toggle_cb), pw);
+
+	g_signal_connect(G_OBJECT(pw->window), "delete_event",
+			 G_CALLBACK(pan_window_delete_cb), pw);
+	g_signal_connect(G_OBJECT(pw->window), "key_press_event",
+			 G_CALLBACK(pan_window_key_press_cb), pw);
+
+	gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
+
+	pan_layout_update(pw);
+
+	gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
+	gtk_widget_show(pw->window);
+
+	pan_window_list = g_list_append(pan_window_list, pw);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * peformance warnings
+ *-----------------------------------------------------------------------------
+ */
+
+static void pan_warning_ok_cb(GenericDialog *gd, gpointer data)
+{
+	FileData *dir_fd = data;
+
+	generic_dialog_close(gd);
+
+	pan_window_new_real(dir_fd);
+	file_data_unref(dir_fd);
+}
+
+static void pan_warning_hide_cb(GtkWidget *button, gpointer data)
+{
+	gboolean hide_dlg;
+
+	hide_dlg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
+	pref_list_int_set(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, hide_dlg);
+}
+
+static gboolean pan_warning(FileData *dir_fd)
+{
+	GenericDialog *gd;
+	GtkWidget *box;
+	GtkWidget *group;
+	GtkWidget *button;
+	GtkWidget *ct_button;
+	gboolean hide_dlg;
+
+	if (dir_fd && strcmp(dir_fd->path, G_DIR_SEPARATOR_S) == 0)
+		{
+		pan_warning_folder(dir_fd->path, NULL);
+		return TRUE;
+		}
+
+	if (options->thumbnails.enable_caching &&
+	    options->thumbnails.spec_standard) return FALSE;
+
+	if (!pref_list_int_get(PAN_PREF_GROUP, PAN_PREF_HIDE_WARNING, &hide_dlg)) hide_dlg = FALSE;
+	if (hide_dlg) return FALSE;
+
+	gd = generic_dialog_new(_("Pan View Performance"), "pan_view_warning", NULL, FALSE,
+				NULL, NULL);
+	gd->data = file_data_ref(dir_fd);
+	generic_dialog_add_button(gd, GTK_STOCK_OK, NULL,
+				  pan_warning_ok_cb, TRUE);
+
+	box = generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
+					 _("Pan view performance may be poor."),
+					 _("To improve performance of thumbnails in the pan view the"
+					   " following options can be enabled. Note that both options"
+					   " must be enabled to notice a change in performance."));
+
+	group = pref_box_new(box, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
+	pref_spacer(group, PREF_PAD_INDENT);
+	group = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
+
+	ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
+					  options->thumbnails.enable_caching, &options->thumbnails.enable_caching);
+	button = pref_checkbox_new_int(group, _("Use shared thumbnail cache"),
+				       options->thumbnails.spec_standard, &options->thumbnails.spec_standard);
+	pref_checkbox_link_sensitivity(ct_button, button);
+
+	pref_line(box, 0);
+
+	pref_checkbox_new(box, _("Do not show this dialog again"), hide_dlg,
+			  G_CALLBACK(pan_warning_hide_cb), NULL);
+
+	gtk_widget_show(gd->dialog);
+
+	return TRUE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * entry point
+ *-----------------------------------------------------------------------------
+ */
+
+void pan_window_new(FileData *dir_fd)
+{
+	if (pan_warning(dir_fd)) return;
+
+	pan_window_new_real(dir_fd);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * menus
+ *-----------------------------------------------------------------------------
+ */
+
+#define INFO_IMAGE_SIZE_KEY "image_size_data"
+
+
+static void pan_new_window_cb(GtkWidget *widget, gpointer data)
+{
+	PanWindow *pw = data;
+	FileData *fd;
+
+	fd = pan_menu_click_fd(pw);
+	if (fd)
+		{
+		pan_fullscreen_toggle(pw, TRUE);
+		view_window_new(fd);
+		}
+}
+
+static void pan_edit_cb(GtkWidget *widget, gpointer data)
+{
+	PanWindow *pw;
+	FileData *fd;
+	const gchar *key = data;
+
+	pw = submenu_item_get_data(widget);
+	if (!pw) return;
+
+	fd = pan_menu_click_fd(pw);
+	if (fd)
+		{
+		if (!editor_window_flag_set(key))
+			{
+			pan_fullscreen_toggle(pw, TRUE);
+			}
+		file_util_start_editor_from_file(key, fd, pw->imd->widget);
+		}
+}
+
+static void pan_zoom_in_cb(GtkWidget *widget, gpointer data)
+{
+	PanWindow *pw = data;
+
+	image_zoom_adjust(pw->imd, ZOOM_INCREMENT);
+}
+
+static void pan_zoom_out_cb(GtkWidget *widget, gpointer data)
+{
+	PanWindow *pw = data;
+
+	image_zoom_adjust(pw->imd, -ZOOM_INCREMENT);
+}
+
+static void pan_zoom_1_1_cb(GtkWidget *widget, gpointer data)
+{
+	PanWindow *pw = data;
+
+	image_zoom_set(pw->imd, 1.0);
+}
+
+static void pan_copy_cb(GtkWidget *widget, gpointer data)
+{
+	PanWindow *pw = data;
+	FileData *fd;
+
+	fd = pan_menu_click_fd(pw);
+	if (fd) file_util_copy(fd, NULL, NULL, pw->imd->widget);
+}
+
+static void pan_move_cb(GtkWidget *widget, gpointer data)
+{
+	PanWindow *pw = data;
+	FileData *fd;
+
+	fd = pan_menu_click_fd(pw);
+	if (fd) file_util_move(fd, NULL, NULL, pw->imd->widget);
+}
+
+static void pan_rename_cb(GtkWidget *widget, gpointer data)
+{
+	PanWindow *pw = data;
+	FileData *fd;
+
+	fd = pan_menu_click_fd(pw);
+	if (fd) file_util_rename(fd, NULL, pw->imd->widget);
+}
+
+static void pan_delete_cb(GtkWidget *widget, gpointer data)
+{
+	PanWindow *pw = data;
+	FileData *fd;
+
+	fd = pan_menu_click_fd(pw);
+	if (fd) file_util_delete(fd, NULL, pw->imd->widget);
+}
+
+static void pan_copy_path_cb(GtkWidget *widget, gpointer data)
+{
+	PanWindow *pw = data;
+	FileData *fd;
+
+	fd = pan_menu_click_fd(pw);
+	if (fd) file_util_copy_path_to_clipboard(fd);
+}
+
+static void pan_exif_date_toggle_cb(GtkWidget *widget, gpointer data)
+{
+	PanWindow *pw = data;
+
+	pw->exif_date_enable = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
+	pan_layout_update(pw);
+}
+
+static void pan_info_toggle_exif_cb(GtkWidget *widget, gpointer data)
+{
+	PanWindow *pw = data;
+
+	pw->info_includes_exif = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
+	/* fixme: sync info now */
+}
+
+static void pan_info_toggle_image_cb(GtkWidget *widget, gpointer data)
+{
+	PanWindow *pw = data;
+
+	pw->info_image_size = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), INFO_IMAGE_SIZE_KEY));
+	/* fixme: sync info now */
+}
+
+static void pan_fullscreen_cb(GtkWidget *widget, gpointer data)
+{
+	PanWindow *pw = data;
+
+	pan_fullscreen_toggle(pw, FALSE);
+}
+
+static void pan_close_cb(GtkWidget *widget, gpointer data)
+{
+	PanWindow *pw = data;
+
+	pan_window_close(pw);
+}
+
+static void pan_popup_menu_destroy_cb(GtkWidget *widget, gpointer data)
+{
+	GList *editmenu_fd_list = data;
+
+	filelist_free(editmenu_fd_list);
+}
+
+static GList *pan_view_get_fd_list(PanWindow *pw)
+{
+	GList *list = NULL;
+	FileData *fd = pan_menu_click_fd(pw);
+
+	if (fd) list = g_list_prepend(filelist_copy(fd->sidecar_files), file_data_ref(fd));
+
+	return list;
+}
+
+static GtkWidget *pan_popup_menu(PanWindow *pw)
+{
+	GtkWidget *menu;
+	GtkWidget *submenu;
+	GtkWidget *item;
+	gboolean active;
+	GList *editmenu_fd_list;
+
+	active = (pw->click_pi != NULL);
+
+	menu = popup_menu_short_lived();
+
+	menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN,
+			    G_CALLBACK(pan_zoom_in_cb), pw);
+	menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT,
+			    G_CALLBACK(pan_zoom_out_cb), pw);
+	menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100,
+			    G_CALLBACK(pan_zoom_1_1_cb), pw);
+	menu_item_add_divider(menu);
+
+	editmenu_fd_list = pan_view_get_fd_list(pw);
+	g_signal_connect(G_OBJECT(menu), "destroy",
+			 G_CALLBACK(pan_popup_menu_destroy_cb), editmenu_fd_list);
+
+	submenu_add_edit(menu, &item, G_CALLBACK(pan_edit_cb), pw, editmenu_fd_list);
+	gtk_widget_set_sensitive(item, active);
+
+	menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
+				      G_CALLBACK(pan_new_window_cb), pw);
+
+	menu_item_add_divider(menu);
+	menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
+				      G_CALLBACK(pan_copy_cb), pw);
+	menu_item_add_sensitive(menu, _("_Move..."), active,
+				G_CALLBACK(pan_move_cb), pw);
+	menu_item_add_sensitive(menu, _("_Rename..."), active,
+				G_CALLBACK(pan_rename_cb), pw);
+	menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
+				      G_CALLBACK(pan_delete_cb), pw);
+	menu_item_add_sensitive(menu, _("_Copy path"), active,
+				G_CALLBACK(pan_copy_path_cb), pw);
+
+	menu_item_add_divider(menu);
+	item = menu_item_add_check(menu, _("Sort by E_xif date"), pw->exif_date_enable,
+				   G_CALLBACK(pan_exif_date_toggle_cb), pw);
+	gtk_widget_set_sensitive(item, (pw->layout == PAN_LAYOUT_TIMELINE || pw->layout == PAN_LAYOUT_CALENDAR));
+
+	menu_item_add_divider(menu);
+
+	menu_item_add_check(menu, _("_Show Exif information"), pw->info_includes_exif,
+			    G_CALLBACK(pan_info_toggle_exif_cb), pw);
+	item = menu_item_add(menu, _("Show im_age"), NULL, NULL);
+	submenu = gtk_menu_new();
+	gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
+
+	item = menu_item_add_check(submenu, _("_None"), (pw->info_image_size == PAN_IMAGE_SIZE_THUMB_NONE),
+				   G_CALLBACK(pan_info_toggle_image_cb), pw);
+	g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_THUMB_NONE));
+
+	item = menu_item_add_check(submenu, _("_Full size"), (pw->info_image_size == PAN_IMAGE_SIZE_100),
+				   G_CALLBACK(pan_info_toggle_image_cb), pw);
+	g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_100));
+
+	item = menu_item_add_check(submenu, _("1:2 (50%)"), (pw->info_image_size == PAN_IMAGE_SIZE_50),
+				   G_CALLBACK(pan_info_toggle_image_cb), pw);
+	g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_50));
+
+	item = menu_item_add_check(submenu, _("1:3 (33%)"), (pw->info_image_size == PAN_IMAGE_SIZE_33),
+				   G_CALLBACK(pan_info_toggle_image_cb), pw);
+	g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_33));
+
+	item = menu_item_add_check(submenu, _("1:4 (25%)"), (pw->info_image_size == PAN_IMAGE_SIZE_25),
+				   G_CALLBACK(pan_info_toggle_image_cb), pw);
+	g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_25));
+
+	item = menu_item_add_check(submenu, _("1:10 (10%)"), (pw->info_image_size == PAN_IMAGE_SIZE_10),
+				   G_CALLBACK(pan_info_toggle_image_cb), pw);
+	g_object_set_data(G_OBJECT(item), INFO_IMAGE_SIZE_KEY, GINT_TO_POINTER(PAN_IMAGE_SIZE_10));
+
+
+
+	menu_item_add_divider(menu);
+
+	if (pw->fs)
+		{
+		menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
+		}
+	else
+		{
+		menu_item_add(menu, _("_Full screen"), G_CALLBACK(pan_fullscreen_cb), pw);
+		}
+
+	menu_item_add_divider(menu);
+	menu_item_add_stock(menu, _("C_lose window"), GTK_STOCK_CLOSE, G_CALLBACK(pan_close_cb), pw);
+
+	return menu;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * drag and drop
+ *-----------------------------------------------------------------------------
+ */
+
+static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
+				    gint x, gint y,
+				    GtkSelectionData *selection_data, guint info,
+				    guint time, gpointer data)
+{
+	PanWindow *pw = data;
+
+	if (gtk_drag_get_source_widget(context) == pw->imd->pr) return;
+
+	if (info == TARGET_URI_LIST)
+		{
+		GList *list;
+
+		list = uri_filelist_from_gtk_selection_data(selection_data);
+		if (list && isdir(((FileData *)list->data)->path))
+			{
+			FileData *fd = list->data;
+
+			pan_layout_set_fd(pw, fd);
+			}
+
+		filelist_free(list);
+		}
+}
+
+static void pan_window_set_dnd_data(GtkWidget *widget, GdkDragContext *context,
+				    GtkSelectionData *selection_data, guint info,
+				    guint time, gpointer data)
+{
+	PanWindow *pw = data;
+	FileData *fd;
+
+	fd = pan_menu_click_fd(pw);
+	if (fd)
+		{
+		GList *list;
+
+		list = g_list_append(NULL, fd);
+		uri_selection_data_set_uris_from_filelist(selection_data, list);
+		g_list_free(list);
+		}
+	else
+		{
+		gtk_selection_data_set(selection_data, gtk_selection_data_get_target(selection_data),
+				       8, NULL, 0);
+		}
+}
+
+static void pan_window_dnd_init(PanWindow *pw)
+{
+	GtkWidget *widget;
+
+	widget = pw->imd->pr;
+
+	gtk_drag_source_set(widget, GDK_BUTTON2_MASK,
+			    dnd_file_drag_types, dnd_file_drag_types_count,
+			    GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
+	g_signal_connect(G_OBJECT(widget), "drag_data_get",
+			 G_CALLBACK(pan_window_set_dnd_data), pw);
+
+	gtk_drag_dest_set(widget,
+			  GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
+			  dnd_file_drop_types, dnd_file_drop_types_count,
+			  GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
+	g_signal_connect(G_OBJECT(widget), "drag_data_received",
+			 G_CALLBACK(pan_window_get_dnd_data), pw);
+}
+
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-view/pan-view.h	Sat Dec 24 13:44:25 2016 +0100
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PAN_VIEW_PAN_VIEW_H
+#define PAN_VIEW_PAN_VIEW_H
+
+#include "main.h"
+#include "pan-types.h"
+
+GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
+void pan_layout_resize(PanWindow *pw);
+
+void pan_cache_sync_date(PanWindow *pw, GList *list);
+
+GList *pan_cache_sort(GList *list, SortType method, gboolean ascend);
+
+
+#endif
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/rcfile.c	Tue Dec 20 21:36:52 2016 +0000
+++ b/src/rcfile.c	Sat Dec 24 13:44:25 2016 +0100
@@ -157,6 +157,31 @@
 	return TRUE;
 }
 
+void write_ushort_option(GString *str, gint indent, const gchar *label, guint16 n)
+{
+	g_string_append_printf(str, "%s = \"%uh\" ", label, n);
+}
+
+gboolean read_ushort_option(const gchar *option, const gchar *label, const gchar *value, guint16 *n)
+{
+	if (g_ascii_strcasecmp(option, label) != 0) return FALSE;
+	if (!n) return FALSE;
+
+	if (g_ascii_isdigit(value[0]))
+		{
+		*n = strtoul(value, NULL, 10);
+		}
+	else
+		{
+		if (g_ascii_strcasecmp(value, "true") == 0)
+			*n = 1;
+		else
+			*n = 0;
+		}
+
+	return TRUE;
+}
+
 void write_uint_option(GString *str, gint indent, const gchar *label, guint n)
 {
 	g_string_append_printf(str, "%s = \"%u\" ", label, n);
@@ -644,14 +669,14 @@
 		if (READ_CHAR(*options, image_overlay.template_string)) continue;
 		if (READ_INT(*options, image_overlay.x)) continue;
 		if (READ_INT(*options, image_overlay.y)) continue;
-		if (READ_INT(*options, image_overlay.text_red)) continue;
-		if (READ_INT(*options, image_overlay.text_green)) continue;
-		if (READ_INT(*options, image_overlay.text_blue)) continue;
-		if (READ_INT(*options, image_overlay.text_alpha)) continue;
-		if (READ_INT(*options, image_overlay.background_red)) continue;
-		if (READ_INT(*options, image_overlay.background_green)) continue;
-		if (READ_INT(*options, image_overlay.background_blue)) continue;
-		if (READ_INT(*options, image_overlay.background_alpha)) continue;
+		if (READ_USHORT(*options, image_overlay.text_red)) continue;
+		if (READ_USHORT(*options, image_overlay.text_green)) continue;
+		if (READ_USHORT(*options, image_overlay.text_blue)) continue;
+		if (READ_USHORT(*options, image_overlay.text_alpha)) continue;
+		if (READ_USHORT(*options, image_overlay.background_red)) continue;
+		if (READ_USHORT(*options, image_overlay.background_green)) continue;
+		if (READ_USHORT(*options, image_overlay.background_blue)) continue;
+		if (READ_USHORT(*options, image_overlay.background_alpha)) continue;
 		if (READ_CHAR(*options, image_overlay.font)) continue;
 
 		/* Slideshow options */
--- a/src/rcfile.h	Tue Dec 20 21:36:52 2016 +0000
+++ b/src/rcfile.h	Sat Dec 24 13:44:25 2016 +0100
@@ -31,6 +31,8 @@
 gboolean read_color_option(const gchar *option, const gchar *label, const gchar *value, GdkColor *color);
 void write_int_option(GString *str, gint indent, const gchar *label, gint n);
 gboolean read_int_option(const gchar *option, const gchar *label, const gchar *value, gint *n);
+void write_ushort_option(GString *str, gint indent, const gchar *label, guint16 n);
+gboolean read_ushort_option(const gchar *option, const gchar *label, const gchar *value, guint16 *n);
 void write_uint_option(GString *str, gint indent, const gchar *label, guint n);
 gboolean read_uint_option(const gchar *option, const gchar *label, const gchar *value, guint *n);
 gboolean read_uint_option_clamp(const gchar *option, const gchar *label, const gchar *value, guint *n, guint min, guint max);
@@ -54,6 +56,7 @@
 #define READ_BOOL(_target_, _name_) read_bool_option(option, #_name_, value, &(_target_)._name_)
 #define READ_INT(_target_, _name_) read_int_option(option, #_name_, value, &(_target_)._name_)
 #define READ_UINT(_target_, _name_) read_uint_option(option, #_name_, value, &(_target_)._name_)
+#define READ_USHORT(_target_, _name_) read_ushort_option(option, #_name_, value, &(_target_)._name_)
 #define READ_INT_CLAMP(_target_, _name_, _min_, _max_) read_int_option_clamp(option, #_name_, value, &(_target_)._name_, _min_, _max_)
 #define READ_UINT_CLAMP(_target_, _name_, _min_, _max_) read_uint_option_clamp(option, #_name_, value, &(_target_)._name_, _min_, _max_)
 #define READ_INT_UNIT(_target_, _name_, _unit_) read_int_unit_option(option, #_name_, value, &(_target_)._name_, _unit_)
--- a/src/view_file.c	Tue Dec 20 21:36:52 2016 +0000
+++ b/src/view_file.c	Sat Dec 24 13:44:25 2016 +0100
@@ -22,6 +22,8 @@
 #include "view_file.h"
 
 #include "dupe.h"
+#include "collect.h"
+#include "collect-table.h"
 #include "editors.h"
 #include "layout.h"
 #include "menu.h"